Imported Upstream version 8.9.0+debian~rc4

This commit is contained in:
Praveen Arimbrathodiyil 2016-06-16 23:09:34 +05:30
parent 886c2c3fe4
commit 4e6e56d4f7
1485 changed files with 26862 additions and 9882 deletions

3
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,3 @@
Were closing our issue tracker on GitHub so we can focus on the GitLab.com project and respond to issues more quickly.
We encourage you to open an issue on the [GitLab.com issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues). You can log into GitLab.com using your GitHub account.

3
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,3 @@
Thank you for taking the time to contribute back to GitLab!
Please open a merge request [on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests), we look forward to reviewing your contribution! You can log into GitLab.com using your GitHub account.

78
.gitignore vendored
View file

@ -4,46 +4,46 @@
.bundle .bundle
.chef .chef
.directory .directory
.envrc /.envrc
.gitlab_shell_secret /.gitlab_shell_secret
.idea .idea
.rbenv-version /.rbenv-version
.rbx/ .rbx/
.ruby-gemset /.ruby-gemset
.ruby-version /.ruby-version
.rvmrc /.rvmrc
.sass-cache/ .sass-cache/
.secret /.secret
.vagrant /.vagrant
.byebug_history /.byebug_history
Vagrantfile /Vagrantfile
backups/* /backups/*
config/aws.yml /config/aws.yml
config/database.yml /config/database.yml
config/gitlab.yml /config/gitlab.yml
config/gitlab_ci.yml /config/gitlab_ci.yml
config/initializers/rack_attack.rb /config/initializers/rack_attack.rb
config/initializers/smtp_settings.rb /config/initializers/smtp_settings.rb
config/initializers/relative_url.rb /config/initializers/relative_url.rb
config/resque.yml /config/resque.yml
config/unicorn.rb /config/unicorn.rb
config/secrets.yml /config/secrets.yml
config/sidekiq.yml /config/sidekiq.yml
coverage/* /coverage/*
db/*.sqlite3 /db/*.sqlite3
db/*.sqlite3-journal /db/*.sqlite3-journal
db/data.yml /db/data.yml
doc/code/* /doc/code/*
dump.rdb /dump.rdb
log/*.log* /log/*.log*
nohup.out /nohup.out
public/assets/ /public/assets/
public/uploads.* /public/uploads.*
public/uploads/ /public/uploads/
shared/artifacts/ /shared/artifacts/
rails_best_practices_output.html /rails_best_practices_output.html
/tags /tags
tmp/ /tmp/*
vendor/bundle/* /vendor/bundle/*
builds/* /builds/*
shared/* /shared/*

View file

@ -2,111 +2,199 @@ image: "ruby:2.1"
services: services:
- mysql:latest - mysql:latest
- redis:latest - redis:alpine
cache: cache:
key: "ruby21" key: "ruby21"
paths: paths:
- vendor - vendor/apt
- vendor/ruby
variables: variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1" MYSQL_ALLOW_EMPTY_PASSWORD: "1"
# retry tests only in CI environment # retry tests only in CI environment
RSPEC_RETRY_RETRY_COUNT: "3" RSPEC_RETRY_RETRY_COUNT: "3"
RAILS_ENV: "test"
SIMPLECOV: "true"
USE_DB: "true"
USE_BUNDLE_INSTALL: "true"
before_script: before_script:
- source ./scripts/prepare_build.sh - source ./scripts/prepare_build.sh
- ruby -v
- which ruby
- retry gem install bundler --no-ri --no-rdoc
- cp config/gitlab.yml.example config/gitlab.yml - cp config/gitlab.yml.example config/gitlab.yml
- touch log/application.log - bundle --version
- touch log/test.log - '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"'
- retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" - retry gem install knapsack
- RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate - '[ "$USE_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate'
stages: stages:
- prepare
- test - test
- notifications - post-test
spec:feature: # Prepare and merge knapsack tests
.knapsack-state: &knapsack-state
services: []
variables:
USE_DB: "false"
USE_BUNDLE_INSTALL: "false"
cache:
key: "knapsack"
paths:
- knapsack/
artifacts:
paths:
- knapsack/
knapsack:
<<: *knapsack-state
stage: prepare
script:
- mkdir -p knapsack/
- '[[ -f knapsack/rspec_report.json ]] || echo "{}" > knapsack/rspec_report.json'
- '[[ -f knapsack/spinach_report.json ]] || echo "{}" > knapsack/spinach_report.json'
update-knapsack:
<<: *knapsack-state
stage: post-test
script:
- scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json
- scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
- rm -f knapsack/*_node_*.json
only:
- master
# Execute all testing suites
.rspec-knapsack: &rspec-knapsack
stage: test stage: test
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature - JOB_NAME=( $CI_BUILD_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]}
- 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
artifacts:
paths:
- knapsack/
spec:api: .spinach-knapsack: &spinach-knapsack
stage: test stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api - bundle exec rake assets:precompile 2>/dev/null
- JOB_NAME=( $CI_BUILD_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true
- cp knapsack/spinach_report.json ${KNAPSACK_REPORT_PATH}
- knapsack spinach "-r rerun" || retry '[ ! -e tmp/spinach-rerun.txt ] || bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
artifacts:
paths:
- knapsack/
spec:models: rspec 0 20: *rspec-knapsack
rspec 1 20: *rspec-knapsack
rspec 2 20: *rspec-knapsack
rspec 3 20: *rspec-knapsack
rspec 4 20: *rspec-knapsack
rspec 5 20: *rspec-knapsack
rspec 6 20: *rspec-knapsack
rspec 7 20: *rspec-knapsack
rspec 8 20: *rspec-knapsack
rspec 9 20: *rspec-knapsack
rspec 10 20: *rspec-knapsack
rspec 11 20: *rspec-knapsack
rspec 12 20: *rspec-knapsack
rspec 13 20: *rspec-knapsack
rspec 14 20: *rspec-knapsack
rspec 15 20: *rspec-knapsack
rspec 16 20: *rspec-knapsack
rspec 17 20: *rspec-knapsack
rspec 18 20: *rspec-knapsack
rspec 19 20: *rspec-knapsack
spinach 0 10: *spinach-knapsack
spinach 1 10: *spinach-knapsack
spinach 2 10: *spinach-knapsack
spinach 3 10: *spinach-knapsack
spinach 4 10: *spinach-knapsack
spinach 5 10: *spinach-knapsack
spinach 6 10: *spinach-knapsack
spinach 7 10: *spinach-knapsack
spinach 8 10: *spinach-knapsack
spinach 9 10: *spinach-knapsack
# Execute all testing suites against Ruby 2.2
.ruby-22: &ruby-22
image: "ruby:2.2"
only:
- master
cache:
key: "ruby22"
paths:
- vendor
.rspec-knapsack-ruby22: &rspec-knapsack-ruby22
<<: *rspec-knapsack
<<: *ruby-22
.spinach-knapsack-ruby22: &spinach-knapsack-ruby22
<<: *spinach-knapsack
<<: *ruby-22
rspec 0 20 ruby22: *rspec-knapsack-ruby22
rspec 1 20 ruby22: *rspec-knapsack-ruby22
rspec 2 20 ruby22: *rspec-knapsack-ruby22
rspec 3 20 ruby22: *rspec-knapsack-ruby22
rspec 4 20 ruby22: *rspec-knapsack-ruby22
rspec 5 20 ruby22: *rspec-knapsack-ruby22
rspec 6 20 ruby22: *rspec-knapsack-ruby22
rspec 7 20 ruby22: *rspec-knapsack-ruby22
rspec 8 20 ruby22: *rspec-knapsack-ruby22
rspec 9 20 ruby22: *rspec-knapsack-ruby22
rspec 10 20 ruby22: *rspec-knapsack-ruby22
rspec 11 20 ruby22: *rspec-knapsack-ruby22
rspec 12 20 ruby22: *rspec-knapsack-ruby22
rspec 13 20 ruby22: *rspec-knapsack-ruby22
rspec 14 20 ruby22: *rspec-knapsack-ruby22
rspec 15 20 ruby22: *rspec-knapsack-ruby22
rspec 16 20 ruby22: *rspec-knapsack-ruby22
rspec 17 20 ruby22: *rspec-knapsack-ruby22
rspec 18 20 ruby22: *rspec-knapsack-ruby22
rspec 19 20 ruby22: *rspec-knapsack-ruby22
spinach 0 10 ruby22: *spinach-knapsack-ruby22
spinach 1 10 ruby22: *spinach-knapsack-ruby22
spinach 2 10 ruby22: *spinach-knapsack-ruby22
spinach 3 10 ruby22: *spinach-knapsack-ruby22
spinach 4 10 ruby22: *spinach-knapsack-ruby22
spinach 5 10 ruby22: *spinach-knapsack-ruby22
spinach 6 10 ruby22: *spinach-knapsack-ruby22
spinach 7 10 ruby22: *spinach-knapsack-ruby22
spinach 8 10 ruby22: *spinach-knapsack-ruby22
spinach 9 10 ruby22: *spinach-knapsack-ruby22
# Other generic tests
.exec: &exec
stage: test stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models - bundle exec $CI_BUILD_NAME
spec:lib: teaspoon: *exec
stage: test rubocop: *exec
script: rake scss_lint: *exec
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib rake brakeman: *exec
rake flog: *exec
spec:services: rake flay: *exec
stage: test rake db:migrate:reset: *exec
script: license_finder: *exec
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
spec:other:
stage: test
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
spinach:project:half:
stage: test
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
spinach:project:rest:
stage: test
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
spinach:other:
stage: test
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
teaspoon:
stage: test
script:
- RAILS_ENV=test bundle exec teaspoon
rubocop:
stage: test
script:
- bundle exec rubocop
scss-lint:
stage: test
script:
- bundle exec rake scss_lint
brakeman:
stage: test
script:
- bundle exec rake brakeman
flog:
stage: test
script:
- bundle exec rake flog
flay:
stage: test
script:
- bundle exec rake flay
bundler:audit: bundler:audit:
stage: test stage: test
@ -115,127 +203,10 @@ bundler:audit:
script: script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941" - "bundle exec bundle-audit check --update --ignore OSVDB-115941"
db-migrate-reset: # Notify slack in the end
stage: test
script:
- RAILS_ENV=test bundle exec rake db:migrate:reset
# Ruby 2.2 jobs
spec:feature:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
cache:
key: "ruby22"
paths:
- vendor
spec:api:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
cache:
key: "ruby22"
paths:
- vendor
spec:models:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
cache:
key: "ruby22"
paths:
- vendor
spec:lib:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
cache:
key: "ruby22"
paths:
- vendor
spec:services:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
cache:
key: "ruby22"
paths:
- vendor
spec:other:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
cache:
key: "ruby22"
paths:
- vendor
spinach:project:half:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
cache:
key: "ruby22"
paths:
- vendor
spinach:project:rest:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
cache:
key: "ruby22"
paths:
- vendor
spinach:other:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
cache:
key: "ruby22"
paths:
- vendor
notify:slack: notify:slack:
stage: notifications stage: post-test
script: script:
- ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>" - ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
when: on_failure when: on_failure

View file

@ -1,3 +1,5 @@
require: rubocop-rspec
AllCops: AllCops:
TargetRubyVersion: 2.1 TargetRubyVersion: 2.1
# Cop names are not displayed in offense messages by default. Change behavior # Cop names are not displayed in offense messages by default. Change behavior
@ -11,7 +13,8 @@ AllCops:
# Exclude some GitLab files # Exclude some GitLab files
Exclude: Exclude:
- 'vendor/**/*' - 'vendor/**/*'
- 'db/**/*' - 'db/*'
- 'db/fixtures/**/*'
- 'tmp/**/*' - 'tmp/**/*'
- 'bin/**/*' - 'bin/**/*'
- 'lib/backup/**/*' - 'lib/backup/**/*'
@ -57,7 +60,7 @@ Style/AndOr:
# Use `Array#join` instead of `Array#*`. # Use `Array#join` instead of `Array#*`.
Style/ArrayJoin: Style/ArrayJoin:
Enabled: false Enabled: true
# Use only ascii symbols in comments. # Use only ascii symbols in comments.
Style/AsciiComments: Style/AsciiComments:
@ -69,7 +72,7 @@ Style/AsciiIdentifiers:
# Checks for uses of Module#attr. # Checks for uses of Module#attr.
Style/Attr: Style/Attr:
Enabled: false Enabled: true
# Avoid the use of BEGIN blocks. # Avoid the use of BEGIN blocks.
Style/BeginBlock: Style/BeginBlock:
@ -81,7 +84,7 @@ Style/BarePercentLiterals:
# Do not use block comments. # Do not use block comments.
Style/BlockComments: Style/BlockComments:
Enabled: false Enabled: true
# Put end statement of multiline block on its own line. # Put end statement of multiline block on its own line.
Style/BlockEndNewline: Style/BlockEndNewline:
@ -122,7 +125,7 @@ Style/ClassCheck:
# Use self when defining module/class methods. # Use self when defining module/class methods.
Style/ClassMethods: Style/ClassMethods:
Enabled: false Enabled: true
# Avoid the use of class variables. # Avoid the use of class variables.
Style/ClassVars: Style/ClassVars:
@ -152,7 +155,7 @@ Style/ConstantName:
# Use def with parentheses when there are arguments. # Use def with parentheses when there are arguments.
Style/DefWithParentheses: Style/DefWithParentheses:
Enabled: false Enabled: true
# Checks for use of deprecated Hash methods. # Checks for use of deprecated Hash methods.
Style/DeprecatedHashMethods: Style/DeprecatedHashMethods:
@ -192,7 +195,7 @@ Style/EmptyLines:
# Keep blank lines around access modifiers. # Keep blank lines around access modifiers.
Style/EmptyLinesAroundAccessModifier: Style/EmptyLinesAroundAccessModifier:
Enabled: false Enabled: true
# Keeps track of empty lines around block bodies. # Keeps track of empty lines around block bodies.
Style/EmptyLinesAroundBlockBody: Style/EmptyLinesAroundBlockBody:
@ -216,15 +219,15 @@ Style/EmptyLiteral:
# Avoid the use of END blocks. # Avoid the use of END blocks.
Style/EndBlock: Style/EndBlock:
Enabled: false Enabled: true
# Use Unix-style line endings. # Use Unix-style line endings.
Style/EndOfLine: Style/EndOfLine:
Enabled: false Enabled: true
# Favor the use of Fixnum#even? && Fixnum#odd? # Favor the use of Fixnum#even? && Fixnum#odd?
Style/EvenOdd: Style/EvenOdd:
Enabled: false Enabled: true
# Do not use unnecessary spacing. # Do not use unnecessary spacing.
Style/ExtraSpacing: Style/ExtraSpacing:
@ -232,15 +235,20 @@ Style/ExtraSpacing:
# Use snake_case for source file names. # Use snake_case for source file names.
Style/FileName: Style/FileName:
Enabled: false Enabled: true
# Checks for a line break before the first parameter in a multi-line method
# parameter definition.
Style/FirstMethodParameterLineBreak:
Enabled: true
# Checks for flip flops. # Checks for flip flops.
Style/FlipFlop: Style/FlipFlop:
Enabled: false Enabled: true
# Checks use of for or each in multiline loops. # Checks use of for or each in multiline loops.
Style/For: Style/For:
Enabled: false Enabled: true
# Enforce the use of Kernel#sprintf, Kernel#format or String#%. # Enforce the use of Kernel#sprintf, Kernel#format or String#%.
Style/FormatString: Style/FormatString:
@ -248,7 +256,7 @@ Style/FormatString:
# Do not introduce global variables. # Do not introduce global variables.
Style/GlobalVars: Style/GlobalVars:
Enabled: false Enabled: true
# Check for conditionals that can be replaced with guard clauses. # Check for conditionals that can be replaced with guard clauses.
Style/GuardClause: Style/GuardClause:
@ -269,7 +277,7 @@ Style/IfUnlessModifier:
# Do not use if x; .... Use the ternary operator instead. # Do not use if x; .... Use the ternary operator instead.
Style/IfWithSemicolon: Style/IfWithSemicolon:
Enabled: false Enabled: true
# Checks that conditional statements do not have an identical line at the # Checks that conditional statements do not have an identical line at the
# end of each branch, which can validly be moved out of the conditional. # end of each branch, which can validly be moved out of the conditional.
@ -279,7 +287,7 @@ Style/IdenticalConditionalBranches:
# Checks the indentation of the first line of the right-hand-side of a # Checks the indentation of the first line of the right-hand-side of a
# multi-line assignment. # multi-line assignment.
Style/IndentAssignment: Style/IndentAssignment:
Enabled: false Enabled: true
# Keep indentation straight. # Keep indentation straight.
Style/IndentationConsistency: Style/IndentationConsistency:
@ -299,7 +307,7 @@ Style/IndentHash:
# Use Kernel#loop for infinite loops. # Use Kernel#loop for infinite loops.
Style/InfiniteLoop: Style/InfiniteLoop:
Enabled: false Enabled: true
# Use the new lambda literal syntax for single-line blocks. # Use the new lambda literal syntax for single-line blocks.
Style/Lambda: Style/Lambda:
@ -307,11 +315,11 @@ Style/Lambda:
# Use lambda.call(...) instead of lambda.(...). # Use lambda.call(...) instead of lambda.(...).
Style/LambdaCall: Style/LambdaCall:
Enabled: false Enabled: true
# Comments should start with a space. # Comments should start with a space.
Style/LeadingCommentSpace: Style/LeadingCommentSpace:
Enabled: false Enabled: true
# Use \ instead of + or << to concatenate two string literals at line end. # Use \ instead of + or << to concatenate two string literals at line end.
Style/LineEndConcatenation: Style/LineEndConcatenation:
@ -323,33 +331,56 @@ Style/MethodCallParentheses:
# Checks if the method definitions have or don't have parentheses. # Checks if the method definitions have or don't have parentheses.
Style/MethodDefParentheses: Style/MethodDefParentheses:
Enabled: false Enabled: true
# Use the configured style when naming methods. # Use the configured style when naming methods.
Style/MethodName: Style/MethodName:
Enabled: false Enabled: true
# Checks for usage of `extend self` in modules. # Checks for usage of `extend self` in modules.
Style/ModuleFunction: Style/ModuleFunction:
Enabled: false Enabled: false
# Checks that the closing brace in an array literal is either on the same line
# as the last array element, or a new line.
Style/MultilineArrayBraceLayout:
Enabled: false
EnforcedStyle: symmetrical
# Avoid multi-line chains of blocks. # Avoid multi-line chains of blocks.
Style/MultilineBlockChain: Style/MultilineBlockChain:
Enabled: false Enabled: true
# Ensures newlines after multiline block do statements. # Ensures newlines after multiline block do statements.
Style/MultilineBlockLayout: Style/MultilineBlockLayout:
Enabled: true Enabled: true
# Checks that the closing brace in a hash literal is either on the same line as
# the last hash element, or a new line.
Style/MultilineHashBraceLayout:
Enabled: false
EnforcedStyle: symmetrical
# Do not use then for multi-line if/unless. # Do not use then for multi-line if/unless.
Style/MultilineIfThen: Style/MultilineIfThen:
Enabled: true
# Checks that the closing brace in a method call is either on the same line as
# the last method argument, or a new line.
Style/MultilineMethodCallBraceLayout:
Enabled: false Enabled: false
EnforcedStyle: symmetrical
# Checks indentation of method calls with the dot operator that span more than # Checks indentation of method calls with the dot operator that span more than
# one line. # one line.
Style/MultilineMethodCallIndentation: Style/MultilineMethodCallIndentation:
Enabled: false Enabled: false
# Checks that the closing brace in a method definition is symmetrical with
# respect to the opening brace and the method parameters.
Style/MultilineMethodDefinitionBraceLayout:
Enabled: false
# Checks indentation of binary operations that span more than one line. # Checks indentation of binary operations that span more than one line.
Style/MultilineOperationIndentation: Style/MultilineOperationIndentation:
Enabled: false Enabled: false
@ -364,7 +395,7 @@ Style/MutableConstant:
# Favor unless over if for negative conditions (or control flow or). # Favor unless over if for negative conditions (or control flow or).
Style/NegatedIf: Style/NegatedIf:
Enabled: false Enabled: true
# Favor until over while for negative conditions. # Favor until over while for negative conditions.
Style/NegatedWhile: Style/NegatedWhile:
@ -372,7 +403,7 @@ Style/NegatedWhile:
# Avoid using nested modifiers. # Avoid using nested modifiers.
Style/NestedModifier: Style/NestedModifier:
Enabled: false Enabled: true
# Parenthesize method calls which are nested inside the argument list of # Parenthesize method calls which are nested inside the argument list of
# another parenthesized method call. # another parenthesized method call.
@ -409,7 +440,7 @@ Style/OneLineConditional:
# When defining binary operators, name the argument other. # When defining binary operators, name the argument other.
Style/OpMethod: Style/OpMethod:
Enabled: false Enabled: true
# Check for simple usages of parallel assignment. It will only warn when # Check for simple usages of parallel assignment. It will only warn when
# the number of variables matches on both sides of the assignment. # the number of variables matches on both sides of the assignment.
@ -456,10 +487,9 @@ Style/RedundantException:
Style/RedundantFreeze: Style/RedundantFreeze:
Enabled: false Enabled: false
# TODO: Enable RedundantParentheses Cop.
# Checks for parentheses that seem not to serve any purpose. # Checks for parentheses that seem not to serve any purpose.
Style/RedundantParentheses: Style/RedundantParentheses:
Enabled: false Enabled: true
# Don't use return where it's not required. # Don't use return where it's not required.
Style/RedundantReturn: Style/RedundantReturn:
@ -485,11 +515,12 @@ Style/SelfAssignment:
# Don't use semicolons to terminate expressions. # Don't use semicolons to terminate expressions.
Style/Semicolon: Style/Semicolon:
Enabled: false Enabled: true
# Checks for proper usage of fail and raise. # Checks for proper usage of fail and raise.
Style/SignalException: Style/SignalException:
Enabled: false EnforcedStyle: only_raise
Enabled: true
# Enforces the names of some block params. # Enforces the names of some block params.
Style/SingleLineBlockParams: Style/SingleLineBlockParams:
@ -510,25 +541,24 @@ Style/SpaceAfterComma:
# Do not put a space between a method name and the opening parenthesis in a # Do not put a space between a method name and the opening parenthesis in a
# method definition. # method definition.
Style/SpaceAfterMethodName: Style/SpaceAfterMethodName:
Enabled: false Enabled: true
# Tracks redundant space after the ! operator. # Tracks redundant space after the ! operator.
Style/SpaceAfterNot: Style/SpaceAfterNot:
Enabled: false Enabled: true
# Use spaces after semicolons. # Use spaces after semicolons.
Style/SpaceAfterSemicolon: Style/SpaceAfterSemicolon:
Enabled: false Enabled: true
# Checks that the equals signs in parameter default assignments have or don't # Checks that the equals signs in parameter default assignments have or don't
# have surrounding space depending on configuration. # have surrounding space depending on configuration.
Style/SpaceAroundEqualsInParameterDefault: Style/SpaceAroundEqualsInParameterDefault:
Enabled: false Enabled: false
# TODO: Enable SpaceAroundKeyword Cop.
# Use a space around keywords if appropriate. # Use a space around keywords if appropriate.
Style/SpaceAroundKeyword: Style/SpaceAroundKeyword:
Enabled: false Enabled: true
# Use a single space around operators. # Use a single space around operators.
Style/SpaceAroundOperators: Style/SpaceAroundOperators:
@ -540,11 +570,11 @@ Style/SpaceBeforeBlockBraces:
# No spaces before commas. # No spaces before commas.
Style/SpaceBeforeComma: Style/SpaceBeforeComma:
Enabled: false Enabled: true
# Checks for missing space between code and a comment on the same line. # Checks for missing space between code and a comment on the same line.
Style/SpaceBeforeComment: Style/SpaceBeforeComment:
Enabled: false Enabled: true
# Checks that exactly one space is used between a method name and the first # Checks that exactly one space is used between a method name and the first
# argument for method calls without parentheses. # argument for method calls without parentheses.
@ -553,7 +583,7 @@ Style/SpaceBeforeFirstArg:
# No spaces before semicolons. # No spaces before semicolons.
Style/SpaceBeforeSemicolon: Style/SpaceBeforeSemicolon:
Enabled: false Enabled: true
# Checks that block braces have or don't have surrounding space. # Checks that block braces have or don't have surrounding space.
# For blocks taking parameters, checks that the left brace has or doesn't # For blocks taking parameters, checks that the left brace has or doesn't
@ -575,11 +605,12 @@ Style/SpaceInsideParens:
# No spaces inside range literals. # No spaces inside range literals.
Style/SpaceInsideRangeLiteral: Style/SpaceInsideRangeLiteral:
Enabled: false Enabled: true
# Checks for padding/surrounding spaces inside string interpolation. # Checks for padding/surrounding spaces inside string interpolation.
Style/SpaceInsideStringInterpolation: Style/SpaceInsideStringInterpolation:
Enabled: false EnforcedStyle: no_space
Enabled: true
# Avoid Perl-style global variables. # Avoid Perl-style global variables.
Style/SpecialGlobalVars: Style/SpecialGlobalVars:
@ -587,7 +618,8 @@ Style/SpecialGlobalVars:
# Check for the usage of parentheses around stabby lambda arguments. # Check for the usage of parentheses around stabby lambda arguments.
Style/StabbyLambdaParentheses: Style/StabbyLambdaParentheses:
Enabled: false EnforcedStyle: require_parentheses
Enabled: true
# Checks if uses of quotes match the configured preference. # Checks if uses of quotes match the configured preference.
Style/StringLiterals: Style/StringLiterals:
@ -600,7 +632,9 @@ Style/StringLiteralsInInterpolation:
# Checks if configured preferred methods are used over non-preferred. # Checks if configured preferred methods are used over non-preferred.
Style/StringMethods: Style/StringMethods:
Enabled: false PreferredMethods:
intern: to_sym
Enabled: true
# Use %i or %I for arrays of symbols. # Use %i or %I for arrays of symbols.
Style/SymbolArray: Style/SymbolArray:
@ -658,23 +692,24 @@ Style/UnneededPercentQ:
# Don't interpolate global, instance and class variables directly in strings. # Don't interpolate global, instance and class variables directly in strings.
Style/VariableInterpolation: Style/VariableInterpolation:
Enabled: false Enabled: true
# Use the configured style when naming variables. # Use the configured style when naming variables.
Style/VariableName: Style/VariableName:
Enabled: false EnforcedStyle: snake_case
Enabled: true
# Use when x then ... for one-line cases. # Use when x then ... for one-line cases.
Style/WhenThen: Style/WhenThen:
Enabled: false Enabled: true
# Checks for redundant do after while or until. # Checks for redundant do after while or until.
Style/WhileUntilDo: Style/WhileUntilDo:
Enabled: false Enabled: true
# Favor modifier while/until usage when you have a single-line body. # Favor modifier while/until usage when you have a single-line body.
Style/WhileUntilModifier: Style/WhileUntilModifier:
Enabled: false Enabled: true
# Use %w or %W for arrays of words. # Use %w or %W for arrays of words.
Style/WordArray: Style/WordArray:
@ -737,7 +772,7 @@ Metrics/PerceivedComplexity:
# Checks for ambiguous operators in the first argument of a method invocation # Checks for ambiguous operators in the first argument of a method invocation
# without parentheses. # without parentheses.
Lint/AmbiguousOperator: Lint/AmbiguousOperator:
Enabled: false Enabled: true
# Checks for ambiguous regexp literals in the first argument of a method # Checks for ambiguous regexp literals in the first argument of a method
# invocation without parentheses. # invocation without parentheses.
@ -750,24 +785,24 @@ Lint/AssignmentInCondition:
# Align block ends correctly. # Align block ends correctly.
Lint/BlockAlignment: Lint/BlockAlignment:
Enabled: false Enabled: true
# Default values in optional keyword arguments and optional ordinal arguments # Default values in optional keyword arguments and optional ordinal arguments
# should not refer back to the name of the argument. # should not refer back to the name of the argument.
Lint/CircularArgumentReference: Lint/CircularArgumentReference:
Enabled: false Enabled: true
# Checks for condition placed in a confusing position relative to the keyword. # Checks for condition placed in a confusing position relative to the keyword.
Lint/ConditionPosition: Lint/ConditionPosition:
Enabled: false Enabled: true
# Check for debugger calls. # Check for debugger calls.
Lint/Debugger: Lint/Debugger:
Enabled: false Enabled: true
# Align ends corresponding to defs correctly. # Align ends corresponding to defs correctly.
Lint/DefEndAlignment: Lint/DefEndAlignment:
Enabled: false Enabled: true
# Check for deprecated class method calls. # Check for deprecated class method calls.
Lint/DeprecatedClassMethods: Lint/DeprecatedClassMethods:
@ -783,15 +818,15 @@ Lint/DuplicatedKey:
# Check for immutable argument given to each_with_object. # Check for immutable argument given to each_with_object.
Lint/EachWithObjectArgument: Lint/EachWithObjectArgument:
Enabled: false Enabled: true
# Check for odd code arrangement in an else block. # Check for odd code arrangement in an else block.
Lint/ElseLayout: Lint/ElseLayout:
Enabled: false Enabled: true
# Checks for empty ensure block. # Checks for empty ensure block.
Lint/EmptyEnsure: Lint/EmptyEnsure:
Enabled: false Enabled: true
# Checks for empty string interpolation. # Checks for empty string interpolation.
Lint/EmptyInterpolation: Lint/EmptyInterpolation:
@ -799,37 +834,36 @@ Lint/EmptyInterpolation:
# Align ends correctly. # Align ends correctly.
Lint/EndAlignment: Lint/EndAlignment:
Enabled: false Enabled: true
# END blocks should not be placed inside method definitions. # END blocks should not be placed inside method definitions.
Lint/EndInMethod: Lint/EndInMethod:
Enabled: false Enabled: true
# Do not use return in an ensure block. # Do not use return in an ensure block.
Lint/EnsureReturn: Lint/EnsureReturn:
Enabled: false Enabled: true
# The use of eval represents a serious security risk. # The use of eval represents a serious security risk.
Lint/Eval: Lint/Eval:
Enabled: false Enabled: true
# Catches floating-point literals too large or small for Ruby to represent. # Catches floating-point literals too large or small for Ruby to represent.
Lint/FloatOutOfRange: Lint/FloatOutOfRange:
Enabled: false Enabled: true
# The number of parameters to format/sprint must match the fields. # The number of parameters to format/sprint must match the fields.
Lint/FormatParameterMismatch: Lint/FormatParameterMismatch:
Enabled: false Enabled: true
# Don't suppress exception. # Don't suppress exception.
Lint/HandleExceptions: Lint/HandleExceptions:
Enabled: false Enabled: false
# TODO: Enable ImplicitStringConcatenation Cop.
# Checks for adjacent string literals on the same line, which could better be # Checks for adjacent string literals on the same line, which could better be
# represented as a single string literal. # represented as a single string literal.
Lint/ImplicitStringConcatenation: Lint/ImplicitStringConcatenation:
Enabled: false Enabled: true
# TODO: Enable IneffectiveAccessModifier Cop. # TODO: Enable IneffectiveAccessModifier Cop.
# Checks for attempts to use `private` or `protected` to set the visibility # Checks for attempts to use `private` or `protected` to set the visibility
@ -840,15 +874,15 @@ Lint/IneffectiveAccessModifier:
# Checks for invalid character literals with a non-escaped whitespace # Checks for invalid character literals with a non-escaped whitespace
# character. # character.
Lint/InvalidCharacterLiteral: Lint/InvalidCharacterLiteral:
Enabled: false Enabled: true
# Checks of literals used in conditions. # Checks of literals used in conditions.
Lint/LiteralInCondition: Lint/LiteralInCondition:
Enabled: false Enabled: true
# Checks for literals used in interpolation. # Checks for literals used in interpolation.
Lint/LiteralInInterpolation: Lint/LiteralInInterpolation:
Enabled: false Enabled: true
# Use Kernel#loop with break rather than begin/end/until or begin/end/while # Use Kernel#loop with break rather than begin/end/until or begin/end/while
# for post-loop tests. # for post-loop tests.
@ -857,11 +891,11 @@ Lint/Loop:
# Do not use nested method definitions. # Do not use nested method definitions.
Lint/NestedMethodDefinition: Lint/NestedMethodDefinition:
Enabled: false Enabled: true
# Do not omit the accumulator when calling `next` in a `reduce`/`inject` block. # Do not omit the accumulator when calling `next` in a `reduce`/`inject` block.
Lint/NextWithoutAccumulator: Lint/NextWithoutAccumulator:
Enabled: false Enabled: true
# Checks for method calls with a space before the opening parenthesis. # Checks for method calls with a space before the opening parenthesis.
Lint/ParenthesesAsGroupedExpression: Lint/ParenthesesAsGroupedExpression:
@ -870,11 +904,11 @@ Lint/ParenthesesAsGroupedExpression:
# Checks for `rand(1)` calls. Such calls always return `0` and most likely # Checks for `rand(1)` calls. Such calls always return `0` and most likely
# a mistake. # a mistake.
Lint/RandOne: Lint/RandOne:
Enabled: false Enabled: true
# Use parentheses in the method call to avoid confusion about precedence. # Use parentheses in the method call to avoid confusion about precedence.
Lint/RequireParentheses: Lint/RequireParentheses:
Enabled: false Enabled: true
# Avoid rescuing the Exception class. # Avoid rescuing the Exception class.
Lint/RescueException: Lint/RescueException:
@ -909,7 +943,7 @@ Lint/UnusedMethodArgument:
# Unreachable code. # Unreachable code.
Lint/UnreachableCode: Lint/UnreachableCode:
Enabled: false Enabled: true
# Checks for useless access modifiers. # Checks for useless access modifiers.
Lint/UselessAccessModifier: Lint/UselessAccessModifier:
@ -921,19 +955,19 @@ Lint/UselessAssignment:
# Checks for comparison of something with itself. # Checks for comparison of something with itself.
Lint/UselessComparison: Lint/UselessComparison:
Enabled: false Enabled: true
# Checks for useless `else` in `begin..end` without `rescue`. # Checks for useless `else` in `begin..end` without `rescue`.
Lint/UselessElseWithoutRescue: Lint/UselessElseWithoutRescue:
Enabled: false Enabled: true
# Checks for useless setter call to a local variable. # Checks for useless setter call to a local variable.
Lint/UselessSetterCall: Lint/UselessSetterCall:
Enabled: false Enabled: true
# Possible use of operator/literal/variable in void context. # Possible use of operator/literal/variable in void context.
Lint/Void: Lint/Void:
Enabled: false Enabled: true
##################### Performance ############################ ##################### Performance ############################
@ -942,11 +976,10 @@ Lint/Void:
Performance/Casecmp: Performance/Casecmp:
Enabled: true Enabled: true
# TODO: Enable DoubleStartEndWith Cop.
# Use `str.{start,end}_with?(x, ..., y, ...)` instead of # Use `str.{start,end}_with?(x, ..., y, ...)` instead of
# `str.{start,end}_with?(x, ...) || str.{start,end}_with?(y, ...)`. # `str.{start,end}_with?(x, ...) || str.{start,end}_with?(y, ...)`.
Performance/DoubleStartEndWith: Performance/DoubleStartEndWith:
Enabled: false Enabled: true
# TODO: Enable EndWith Cop. # TODO: Enable EndWith Cop.
# Use `end_with?` instead of a regex match anchored to the end of a string. # Use `end_with?` instead of a regex match anchored to the end of a string.
@ -957,10 +990,9 @@ Performance/EndWith:
Performance/LstripRstrip: Performance/LstripRstrip:
Enabled: true Enabled: true
# TODO: Enable RangeInclude Cop.
# Use `Range#cover?` instead of `Range#include?`. # Use `Range#cover?` instead of `Range#include?`.
Performance/RangeInclude: Performance/RangeInclude:
Enabled: false Enabled: true
# TODO: Enable RedundantBlockCall Cop. # TODO: Enable RedundantBlockCall Cop.
# Use `yield` instead of `block.call`. # Use `yield` instead of `block.call`.
@ -980,16 +1012,14 @@ Performance/RedundantMerge:
MaxKeyValuePairs: 2 MaxKeyValuePairs: 2
Enabled: false Enabled: false
# TODO: Enable RedundantSortBy Cop.
# Use `sort` instead of `sort_by { |x| x }`. # Use `sort` instead of `sort_by { |x| x }`.
Performance/RedundantSortBy: Performance/RedundantSortBy:
Enabled: false Enabled: true
# TODO: Enable StartWith Cop.
# Use `start_with?` instead of a regex match anchored to the beginning of a # Use `start_with?` instead of a regex match anchored to the beginning of a
# string. # string.
Performance/StartWith: Performance/StartWith:
Enabled: false Enabled: true
# Use `tr` instead of `gsub` when you are replacing the same number of # Use `tr` instead of `gsub` when you are replacing the same number of
# characters. Use `delete` instead of `gsub` when you are deleting # characters. Use `delete` instead of `gsub` when you are deleting
@ -997,10 +1027,9 @@ Performance/StartWith:
Performance/StringReplacement: Performance/StringReplacement:
Enabled: true Enabled: true
# TODO: Enable TimesMap Cop.
# Checks for `.times.map` calls. # Checks for `.times.map` calls.
Performance/TimesMap: Performance/TimesMap:
Enabled: false Enabled: true
##################### Rails ################################## ##################### Rails ##################################
@ -1025,11 +1054,11 @@ Rails/Delegate:
# Prefer `find_by` over `where.first`. # Prefer `find_by` over `where.first`.
Rails/FindBy: Rails/FindBy:
Enabled: false Enabled: true
# Prefer `all.find_each` over `all.find`. # Prefer `all.find_each` over `all.find`.
Rails/FindEach: Rails/FindEach:
Enabled: false Enabled: true
# Prefer has_many :through to has_and_belongs_to_many. # Prefer has_many :through to has_and_belongs_to_many.
Rails/HasAndBelongsToMany: Rails/HasAndBelongsToMany:
@ -1041,7 +1070,7 @@ Rails/Output:
# Checks for incorrect grammar when using methods like `3.day.ago`. # Checks for incorrect grammar when using methods like `3.day.ago`.
Rails/PluralizationGrammar: Rails/PluralizationGrammar:
Enabled: false Enabled: true
# Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`. # Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`.
Rails/ReadWriteAttribute: Rails/ReadWriteAttribute:
@ -1049,7 +1078,7 @@ Rails/ReadWriteAttribute:
# Checks the arguments of ActiveRecord scopes. # Checks the arguments of ActiveRecord scopes.
Rails/ScopeArgs: Rails/ScopeArgs:
Enabled: false Enabled: true
# Checks the correct usage of time zone aware methods. # Checks the correct usage of time zone aware methods.
# http://danilenko.org/2012/7/6/rails_timezones # http://danilenko.org/2012/7/6/rails_timezones
@ -1059,3 +1088,68 @@ Rails/TimeZone:
# Use validates :attribute, hash of validations. # Use validates :attribute, hash of validations.
Rails/Validation: Rails/Validation:
Enabled: false Enabled: false
Rails/UniqBeforePluck:
Enabled: false
##################### RSpec ##################################
# Check that instances are not being stubbed globally.
RSpec/AnyInstance:
Enabled: false
# Check that the first argument to the top level describe is the tested class or
# module.
RSpec/DescribeClass:
Enabled: false
# Use `described_class` for tested class / module.
RSpec/DescribeMethod:
Enabled: false
# Checks that the second argument to top level describe is the tested method
# name.
RSpec/DescribedClass:
Enabled: false
# Checks for long example.
RSpec/ExampleLength:
Enabled: false
Max: 5
# Do not use should when describing your tests.
RSpec/ExampleWording:
Enabled: false
CustomTransform:
be: is
have: has
not: does not
IgnoredWords: []
# Checks the file and folder naming of the spec file.
RSpec/FilePath:
Enabled: false
CustomTransform:
RuboCop: rubocop
RSpec: rspec
# Checks if there are focused specs.
RSpec/Focus:
Enabled: true
# Checks for the usage of instance variables.
RSpec/InstanceVariable:
Enabled: false
# Checks for multiple top-level describes.
RSpec/MultipleDescribes:
Enabled: false
# Enforces the usage of the same method on all negative message expectations.
RSpec/NotToNot:
EnforcedStyle: not_to
Enabled: true
# Prefer using verifying doubles over normal doubles.
RSpec/VerifiedDoubles:
Enabled: false

0
.vagrant_enabled Normal file
View file

144
CHANGELOG
View file

@ -1,10 +1,140 @@
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.9.0 (unreleased)
- Fix Error 500 when using closes_issues API with an external issue tracker
- Add more information into RSS feed for issues (Alexander Matyushentsev)
- Bulk assign/unassign labels to issues.
- Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
- Fix endless redirections when accessing user OAuth applications when they are disabled
- Allow enabling wiki page events from Webhook management UI
- Bump rouge to 1.11.0
- Fix issue with arrow keys not working in search autocomplete dropdown
- Fix an issue where note polling stopped working if a window was in the
background during a refresh.
- Make EmailsOnPushWorker use Sidekiq mailers queue
- Fix wiki page events' webhook to point to the wiki repository
- Don't show tags for revert and cherry-pick operations
- Fix issue todo not remove when leave project !4150 (Long Nguyen)
- Allow customisable text on the 'nearly there' page after a user signs up
- Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
- Fix SVG sanitizer to allow more elements
- Allow forking projects with restricted visibility level
- Added descriptions to notification settings dropdown
- Improve note validation to prevent errors when creating invalid note via API
- Reduce number of fog gem dependencies
- Remove project notification settings associated with deleted projects
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects
- Add a metric for the number of new Redis connections created by a transaction
- Fix Error 500 when viewing a blob with binary characters after the 1024-byte mark
- Redesign navigation for project pages
- Fix groups API to list only user's accessible projects
- Add Environments and Deployments
- Redesign account and email confirmation emails
- Don't fail builds for projects that are deleted
- Support Docker Registry manifest v1
- `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix
- Bump nokogiri to 1.6.8
- Use gitlab-shell v3.0.0
- Upgrade to jQuery 2
- Use Knapsack to evenly distribute tests across multiple nodes
- Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged
- Don't allow MRs to be merged when commits were added since the last review / page load
- Add DB index on users.state
- Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database
- Changed the Slack build message to use the singular duration if necessary (Aran Koning)
- Links from a wiki page to other wiki pages should be rewritten as expected
- Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
- Fix issues filter when ordering by milestone
- Added artifacts:when to .gitlab-ci.yml - this requires GitLab Runner 1.3
- Bamboo Service: Fix missing credentials & URL handling when base URL contains a path (Benjamin Schmid)
- TeamCity Service: Fix URL handling when base URL contains a path
- Todos will display target state if issuable target is 'Closed' or 'Merged'
- Fix bug when sorting issues by milestone due date and filtering by two or more labels
- Add support for using Yubikeys (U2F) for two-factor authentication
- Link to blank group icon doesn't throw a 404 anymore
- Remove 'main language' feature
- Pipelines can be canceled only when there are running builds
- Use downcased path to container repository as this is expected path by Docker
- Projects pending deletion will render a 404 page
- Measure queue duration between gitlab-workhorse and Rails
- Make Omniauth providers specs to not modify global configuration
- Make authentication service for Container Registry to be compatible with < Docker 1.11
- Add Application Setting to configure Container Registry token expire delay (default 5min)
- Cache assigned issue and merge request counts in sidebar nav
- Use Knapsack only in CI environment
- Cache project build count in sidebar nav
- Add milestone expire date to the right sidebar
- Manually mark a issue or merge request as a todo
- Fix markdown_spec to use before instead of before(:all) to properly cleanup database after testing
- Reduce number of queries needed to render issue labels in the sidebar
- Improve error handling importing projects
- Remove duplicated notification settings
- Put project Files and Commits tabs under Code tab
- Decouple global notification level from user model
- Replace Colorize with Rainbow for coloring console output in Rake tasks.
- Add workhorse controller and API helpers
- An indicator is now displayed at the top of the comment field for confidential issues.
- RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
- Improve issuables APIs performance when accessing notes !4471
- External links now open in a new tab
- Prevent default actions of disabled buttons and links
- Markdown editor now correctly resets the input value on edit cancellation !4175
- Toggling a task list item in a issue/mr description does not creates a Todo for mentions
- Improved UX of date pickers on issue & milestone forms
- Cache on the database if a project has an active external issue tracker.
- Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
- All classes in the Banzai::ReferenceParser namespace are now instrumented
- Remove deprecated issues_tracker and issues_tracker_id from project model
- Allow users to create confidential issues in private projects
- Measure CPU time for instrumented methods
- Instrument private methods and private instance methods by default instead just public methods
- Only show notes through JSON on confidential issues that the user has access to
- Updated the allocations Gem to version 1.0.5
- The background sampler now ignores classes without names
- Update design for `Close` buttons
- New custom icons for navigation
- Horizontally scrolling navigation on project, group, and profile settings pages
- Hide global side navigation by default
- Fix project Star/Unstar project button tooltip
- Remove tanuki logo from side navigation; center on top nav
- Include user relationships when retrieving award_emoji
- Various associations are now eager loaded when parsing issue references to reduce the number of queries executed
v 8.8.5 (unreleased)
- Ensure branch cleanup regardless of whether the GitHub import process succeeds
- Fix todos page throwing errors when you have a project pending deletion
- Reduce number of SQL queries when rendering user references
- Import GitHub repositories respecting the API rate limit
- Fix importer for GitHub comments on diff
- Disable Webhooks before proceeding with the GitHub import
- Fix incremental trace upload API when using multi-byte UTF-8 chars in trace
v 8.8.4
- Fix LDAP-based login for users with 2FA enabled. !4493
v 8.8.3
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
- Fixed JS error when trying to remove discussion form. !4303
- Fixed issue with button color when no CI enabled. !4287
- Fixed potential issue with 2 CI status polling events happening. !3869
- Improve design of Pipeline view. !4230
- Fix gitlab importer failing to import new projects due to missing credentials. !4301
- Fix import URL migration not rescuing with the correct Error. !4321
- Fix health check access token changing due to old application settings being used. !4332
- Make authentication service for Container Registry to be compatible with Docker versions before 1.11. !4363
- Add Application Setting to configure Container Registry token expire delay (default 5 min). !4364
- Pass the "Remember me" value to the 2FA token form. !4369
- Fix incorrect links on pipeline page when merge request created from fork. !4376
- Use downcased path to container repository as this is expected path by Docker. !4420
- Fix wiki project clone address error (chujinjin). !4429
- Fix serious performance bug with rendering Markdown with InlineDiffFilter. !4392
- Fix missing number on generated ordered list element. !4437
- Prevent disclosure of notes on confidential issues in search results.
v 8.8.2 v 8.8.2
- Added remove due date button. !4209 - Added remove due date button. !4209
- Fix access to Pipelines by Anonymous user. !4233
- Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources. !4242 - Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources. !4242
- Fix Error 500 in CI charts by gracefully handling commits with no durations. !4245 - Fix Error 500 in CI charts by gracefully handling commits with no durations. !4245
- Fix table UI on CI builds page. !4249 - Fix table UI on CI builds page. !4249
- Fix backups if registry is disabled. !4263 - Fix backups if registry is disabled. !4263
- Fixed issue with merge button color. !4211 - Fixed issue with merge button color. !4211
@ -14,7 +144,7 @@ v 8.8.2
v 8.8.1 v 8.8.1
- Add documentation for the "Health Check" feature - Add documentation for the "Health Check" feature
- Allow anonymous users to access a public project's pipelines - Allow anonymous users to access a public project's pipelines !4233
- Fix MySQL compatibility in zero downtime migrations helpers - Fix MySQL compatibility in zero downtime migrations helpers
- Fix the CI login to Container Registry (the gitlab-ci-token user) - Fix the CI login to Container Registry (the gitlab-ci-token user)
@ -31,6 +161,7 @@ v 8.8.0
- Added inline diff styling for `change_title` system notes. (Adam Butler) - Added inline diff styling for `change_title` system notes. (Adam Butler)
- Project#open_branches has been cleaned up and no longer loads entire records into memory. - Project#open_branches has been cleaned up and no longer loads entire records into memory.
- Escape HTML in commit titles in system note messages - Escape HTML in commit titles in system note messages
- Improve design of Pipeline View
- Fix scope used when accessing container registry - Fix scope used when accessing container registry
- Fix creation of Ci::Commit object which can lead to pending, failed in some scenarios - Fix creation of Ci::Commit object which can lead to pending, failed in some scenarios
- Improve multiple branch push performance by memoizing permission checking - Improve multiple branch push performance by memoizing permission checking
@ -90,11 +221,18 @@ v 8.8.0
- Improve Issue formatting for the Slack Service (Jeroen van Baarsen) - Improve Issue formatting for the Slack Service (Jeroen van Baarsen)
- Fixed advice on invalid permissions on upload path !2948 (Ludovic Perrine) - Fixed advice on invalid permissions on upload path !2948 (Ludovic Perrine)
- Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs) - Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs)
- When creating a .gitignore file a dropdown with templates will be provided
- Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562
v 8.7.7
- Fix import by `Any Git URL` broken if the URL contains a space
v 8.7.6 v 8.7.6
- Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko) - Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko)
- Fix import from GitLab.com to a private instance failure. !4181 - Fix import from GitLab.com to a private instance failure. !4181
- Fix external imports not finding the import data. !4106 - Fix external imports not finding the import data. !4106
- Fix notification delay when changing status of an issue
- Bump Workhorse to 0.7.5 so it can serve raw diffs
v 8.7.5 v 8.7.5
- Fix relative links in wiki pages. !4050 - Fix relative links in wiki pages. !4050

View file

@ -96,7 +96,7 @@ 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 [free Antetype viewer (Mac OSX only)] or grab an exported PNG from the design
(the PNG is 1:1). (the PNG is 1:1).
The current designs can be found in the [`gitlab1.atype` file]. The current designs can be found in the [`gitlab8.atype` file].
### UI development kit ### UI development kit
@ -308,16 +308,14 @@ tests are least likely to receive timely feedback. The workflow to make a merge
request is as follows: request is as follows:
1. Fork the project into your personal space on GitLab.com 1. Fork the project into your personal space on GitLab.com
1. Create a feature branch 1. Create a feature branch, branch away from `master`.
1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code 1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
1. Add your changes to the [CHANGELOG](CHANGELOG) 1. Add your changes to the [CHANGELOG](CHANGELOG)
1. If you are changing the README, some documentation or other things which 1. If you are writing documentation, make sure to read the [documentation styleguide][doc-styleguide]
have no effect on the tests, add `[ci skip]` somewhere in the commit message
and make sure to read the [documentation styleguide][doc-styleguide]
1. If you have multiple commits please combine them into one commit by 1. If you have multiple commits please combine them into one commit by
[squashing them][git-squash] [squashing them][git-squash]
1. Push the commit(s) to your fork 1. Push the commit(s) to your fork
1. Submit a merge request (MR) to the master branch 1. Submit a merge request (MR) to the `master` branch
1. The MR title should describe the change you want to make 1. The MR title should describe the change you want to make
1. The MR description should give a motive for your change and the method you 1. The MR description should give a motive for your change and the method you
used to achieve it, see the [merge request description format] used to achieve it, see the [merge request description format]
@ -407,6 +405,7 @@ description area. Copy-paste it to retain the markdown format.
entire line to follow it. This prevents linting tools from generating warnings. entire line to follow it. This prevents linting tools from generating warnings.
- Don't touch neighbouring lines. As an exception, automatic mass - Don't touch neighbouring lines. As an exception, automatic mass
refactoring modifications may leave style non-compliant. refactoring modifications may leave style non-compliant.
1. If the merge request adds any new libraries (gems, JavaScript libraries, etc.), they should conform to our [Licensing guidelines][license-finder-doc]. See the instructions in that document for help if your MR fails the "license-finder" test with a "Dependencies that need approval" error.
## Changes for Stable Releases ## Changes for Stable Releases
@ -532,4 +531,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide" [scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design [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 [free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab1.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/gitlab1.atype/ [`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
[license-finder-doc]: doc/development/licensing.md

View file

@ -1 +1 @@
2.7.2 3.0.0

View file

@ -1 +1 @@
0.7.1 0.7.5

47
Gemfile
View file

@ -18,9 +18,8 @@ gem "mysql2", '~> 0.3.16', group: :mysql
gem "pg", '~> 0.18.2', group: :postgres gem "pg", '~> 0.18.2', group: :postgres
# Authentication libraries # Authentication libraries
gem 'devise', '~> 3.5.4' gem 'devise', '~> 4.0'
gem 'doorkeeper', '~> 3.1' gem 'doorkeeper', '~> 3.1'
gem 'devise-async', '~> 0.9.0'
gem 'omniauth', '~> 1.3.1' gem 'omniauth', '~> 1.3.1'
gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6' gem 'omniauth-azure-oauth2', '~> 0.0.6'
@ -39,16 +38,17 @@ gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt' gem 'jwt'
# Spam and anti-bot protection # Spam and anti-bot protection
gem 'recaptcha', require: 'recaptcha/rails' gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails'
gem 'akismet', '~> 2.0' gem 'akismet', '~> 2.0'
# Two-factor authentication # Two-factor authentication
gem 'devise-two-factor', '~> 2.0.0' gem 'devise-two-factor', '~> 3.0.0'
gem 'rqrcode-rails3', '~> 0.1.7' gem 'rqrcode-rails3', '~> 0.1.7'
gem 'attr_encrypted', '~> 1.3.4' gem 'attr_encrypted', '~> 3.0.0'
gem 'u2f', '~> 0.2.1'
# Browser detection # Browser detection
gem "browser", '~> 1.0.0' gem "browser", '~> 2.0.3'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
@ -73,7 +73,7 @@ gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Pagination # Pagination
gem "kaminari", "~> 0.16.3" gem "kaminari", "~> 0.17.0"
# HAML # HAML
gem "haml-rails", '~> 0.9.0' gem "haml-rails", '~> 0.9.0'
@ -84,8 +84,15 @@ gem "carrierwave", '~> 0.10.0'
# Drag and Drop UI # Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1' gem 'dropzonejs-rails', '~> 0.7.1'
# for backups
gem 'fog-aws', '~> 0.9'
gem 'fog-azure', '~> 0.0'
gem 'fog-core', '~> 1.40'
gem 'fog-local', '~> 0.3'
gem 'fog-google', '~> 0.3'
gem 'fog-openstack', '~> 0.1'
# for aws storage # for aws storage
gem "fog", "~> 1.36.0"
gem "unf", '~> 0.1.4' gem "unf", '~> 0.1.4'
# Authorization # Authorization
@ -105,7 +112,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.10.1' gem 'rouge', '~> 1.11'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
@ -121,7 +128,7 @@ group :unicorn do
end end
# State machine # State machine
gem "state_machines-activerecord", '~> 0.3.0' gem "state_machines-activerecord", '~> 0.4.0'
# Run events after state machine commits # Run events after state machine commits
gem 'after_commit_queue' gem 'after_commit_queue'
@ -138,7 +145,7 @@ gem 'redis-namespace'
gem "httparty", '~> 0.13.3' gem "httparty", '~> 0.13.3'
# Colored output to console # Colored output to console
gem "colorize", '~> 0.7.0' gem "rainbow", '~> 2.1.0'
# GitLab settings # GitLab settings
gem 'settingslogic', '~> 2.0.9' gem 'settingslogic', '~> 2.0.9'
@ -178,9 +185,6 @@ gem 'ruby-fogbugz', '~> 0.2.1'
# d3 # d3
gem 'd3_rails', '~> 3.5.0' gem 'd3_rails', '~> 3.5.0'
#cal-heatmap
gem 'cal-heatmap-rails', '~> 3.6.0'
# underscore-rails # underscore-rails
gem "underscore-rails", "~> 1.8.0" gem "underscore-rails", "~> 1.8.0"
@ -206,6 +210,9 @@ gem 'mousetrap-rails', '~> 1.4.6'
# Detect and convert string character encoding # Detect and convert string character encoding
gem 'charlock_holmes', '~> 0.7.3' gem 'charlock_holmes', '~> 0.7.3'
# Parse duration
gem 'chronic_duration', '~> 0.10.6'
gem "sass-rails", '~> 5.0.0' gem "sass-rails", '~> 5.0.0'
gem "coffee-rails", '~> 4.1.0' gem "coffee-rails", '~> 4.1.0'
gem "uglifier", '~> 2.7.2' gem "uglifier", '~> 2.7.2'
@ -241,7 +248,7 @@ end
group :development do group :development do
gem "foreman" gem "foreman"
gem 'brakeman', '~> 3.2.0', require: false gem 'brakeman', '~> 3.3.0', require: false
gem 'letter_opener_web', '~> 1.3.0' gem 'letter_opener_web', '~> 1.3.0'
gem 'quiet_assets', '~> 1.0.2' gem 'quiet_assets', '~> 1.0.2'
@ -293,15 +300,19 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0' gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.38.0', require: false gem 'rubocop', '~> 0.40.0', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'coveralls', '~> 0.8.2', require: false gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.11.0', require: false gem 'simplecov', '~> 0.11.0', require: false
gem 'flog', require: false gem 'flog', require: false
gem 'flay', require: false gem 'flay', require: false
gem 'bundler-audit', require: false gem 'bundler-audit', require: false
gem 'benchmark-ips', require: false gem 'benchmark-ips', require: false
gem "license_finder", require: false
gem 'knapsack'
end end
group :test do group :test do
@ -325,7 +336,7 @@ gem "mail_room", "~> 0.7"
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'
## CI ## CI
gem 'activerecord-session_store', '~> 0.1.0' gem 'activerecord-session_store', '~> 1.0.0'
gem "nested_form", '~> 0.3.2' gem "nested_form", '~> 0.3.2'
# OAuth # OAuth

View file

@ -1,7 +1,6 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
CFPropertyList (2.3.2)
RedCloth (4.2.9) RedCloth (4.2.9)
ace-rails-ap (4.0.2) ace-rails-ap (4.0.2)
actionmailer (4.2.6) actionmailer (4.2.6)
@ -33,10 +32,12 @@ GEM
activemodel (= 4.2.6) activemodel (= 4.2.6)
activesupport (= 4.2.6) activesupport (= 4.2.6)
arel (~> 6.0) arel (~> 6.0)
activerecord-session_store (0.1.2) activerecord-session_store (1.0.0)
actionpack (>= 4.0.0, < 5) actionpack (>= 4.0, < 5.1)
activerecord (>= 4.0.0, < 5) activerecord (>= 4.0, < 5.1)
railties (>= 4.0.0, < 5) multi_json (~> 1.11, >= 1.11.2)
rack (>= 1.5.2, < 3)
railties (>= 4.0, < 5.1)
activesupport (4.2.6) activesupport (4.2.6)
i18n (~> 0.7) i18n (~> 0.7)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
@ -49,7 +50,7 @@ GEM
after_commit_queue (1.3.0) after_commit_queue (1.3.0)
activerecord (>= 3.0) activerecord (>= 3.0)
akismet (2.0.0) akismet (2.0.0)
allocations (1.0.4) allocations (1.0.5)
arel (6.0.3) arel (6.0.3)
asana (0.4.0) asana (0.4.0)
faraday (~> 0.9) faraday (~> 0.9)
@ -58,8 +59,8 @@ GEM
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.3) asciidoctor (1.5.3)
ast (2.2.0) ast (2.2.0)
attr_encrypted (1.3.4) attr_encrypted (3.0.1)
encryptor (>= 1.3.0) encryptor (~> 3.0.0)
attr_required (1.0.0) attr_required (1.0.0)
autoprefixer-rails (6.2.3) autoprefixer-rails (6.2.3)
execjs execjs
@ -69,9 +70,24 @@ GEM
descendants_tracker (~> 0.0.4) descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0) ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
azure (0.7.5)
addressable (~> 2.3)
azure-core (~> 0.1)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
json (~> 1.8)
mime-types (>= 1, < 3.0)
nokogiri (~> 1.6)
systemu (~> 2.6)
thor (~> 0.19)
uuid (~> 2.0)
azure-core (0.1.2)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
nokogiri (~> 1.6)
babosa (1.0.2) babosa (1.0.2)
base32 (0.3.2) base32 (0.3.2)
bcrypt (3.1.10) bcrypt (3.1.11)
benchmark-ips (2.3.0) benchmark-ips (2.3.0)
better_errors (1.0.1) better_errors (1.0.1)
coderay (>= 1.0.0) coderay (>= 1.0.0)
@ -81,17 +97,8 @@ GEM
bootstrap-sass (3.3.6) bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4) sass (>= 3.3.4)
brakeman (3.2.1) brakeman (3.3.2)
erubis (~> 2.6) browser (2.0.3)
haml (>= 3.0, < 5.0)
highline (>= 1.6.20, < 2.0)
ruby2ruby (~> 2.3.0)
ruby_parser (~> 3.8.1)
safe_yaml (>= 1.0)
sass (~> 3.0)
slim (>= 1.3.6, < 4.0)
terminal-table (~> 1.4)
browser (1.0.1)
builder (3.2.2) builder (3.2.2)
bullet (5.0.0) bullet (5.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
@ -100,7 +107,6 @@ GEM
bundler (~> 1.2) bundler (~> 1.2)
thor (~> 0.18) thor (~> 0.18)
byebug (8.2.1) byebug (8.2.1)
cal-heatmap-rails (3.6.0)
capybara (2.6.2) capybara (2.6.2)
addressable addressable
mime-types (>= 1.16) mime-types (>= 1.16)
@ -118,6 +124,8 @@ GEM
mime-types (>= 1.16) mime-types (>= 1.16)
cause (0.1) cause (0.1)
charlock_holmes (0.7.3) charlock_holmes (0.7.3)
chronic_duration (0.10.6)
numerizer (~> 0.1.1)
chunky_png (1.3.5) chunky_png (1.3.5)
cliver (0.3.2) cliver (0.3.2)
coderay (1.1.0) coderay (1.1.0)
@ -154,21 +162,18 @@ GEM
activerecord (>= 3.2.0, < 5.0) activerecord (>= 3.2.0, < 5.0)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
devise (3.5.4) devise (4.1.1)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5) railties (>= 4.1.0, < 5.1)
responders responders
thread_safe (~> 0.1)
warden (~> 1.2.3) warden (~> 1.2.3)
devise-async (0.9.0) devise-two-factor (3.0.0)
devise (~> 3.2)
devise-two-factor (2.0.1)
activesupport activesupport
attr_encrypted (~> 1.3.2) attr_encrypted (>= 1.3, < 4, != 2)
devise (~> 3.5.0) devise (~> 4.0)
railties railties
rotp (~> 2) rotp (~> 2.0)
diff-lcs (1.2.5) diff-lcs (1.2.5)
diffy (3.0.7) diffy (3.0.7)
docile (1.1.5) docile (1.1.5)
@ -180,12 +185,12 @@ GEM
email_spec (1.6.0) email_spec (1.6.0)
launchy (~> 2.1) launchy (~> 2.1)
mail (~> 2.2) mail (~> 2.2)
encryptor (1.3.0) encryptor (3.0.0)
equalizer (0.0.11) equalizer (0.0.11)
erubis (2.7.0) erubis (2.7.0)
escape_utils (1.1.1) escape_utils (1.1.1)
eventmachine (1.0.8) eventmachine (1.0.8)
excon (0.45.4) excon (0.49.0)
execjs (2.6.0) execjs (2.6.0)
expression_parser (0.9.0) expression_parser (0.9.0)
factory_girl (4.5.0) factory_girl (4.5.0)
@ -202,8 +207,6 @@ GEM
multi_json multi_json
ffaker (2.0.0) ffaker (2.0.0)
ffi (1.9.10) ffi (1.9.10)
fission (0.5.0)
CFPropertyList (~> 2.2)
flay (2.6.1) flay (2.6.1)
ruby_parser (~> 3.0) ruby_parser (~> 3.0)
sexp_processor (~> 4.0) sexp_processor (~> 4.0)
@ -213,109 +216,33 @@ GEM
flowdock (0.7.1) flowdock (0.7.1)
httparty (~> 0.7) httparty (~> 0.7)
multi_json multi_json
fog (1.36.0) fog-aws (0.9.2)
fog-aliyun (>= 0.1.0)
fog-atmos
fog-aws (>= 0.6.0)
fog-brightbox (~> 0.4)
fog-core (~> 1.32)
fog-dynect (~> 0.0.2)
fog-ecloud (~> 0.1)
fog-google (<= 0.1.0)
fog-json
fog-local
fog-powerdns (>= 0.1.1)
fog-profitbricks
fog-radosgw (>= 0.0.2)
fog-riakcs
fog-sakuracloud (>= 0.0.4)
fog-serverlove
fog-softlayer
fog-storm_on_demand
fog-terremark
fog-vmfusion
fog-voxel
fog-xenserver
fog-xml (~> 0.1.1)
ipaddress (~> 0.5)
nokogiri (~> 1.5, >= 1.5.11)
fog-aliyun (0.1.0)
fog-core (~> 1.27)
fog-json (~> 1.0)
ipaddress (~> 0.8)
xml-simple (~> 1.1)
fog-atmos (0.1.0)
fog-core
fog-xml
fog-aws (0.8.1)
fog-core (~> 1.27) fog-core (~> 1.27)
fog-json (~> 1.0) fog-json (~> 1.0)
fog-xml (~> 0.1) fog-xml (~> 0.1)
ipaddress (~> 0.8) ipaddress (~> 0.8)
fog-brightbox (0.10.1) fog-azure (0.0.2)
fog-core (~> 1.22) azure (~> 0.6)
fog-json fog-core (~> 1.27)
inflecto (~> 0.0.2) fog-json (~> 1.0)
fog-core (1.35.0) fog-xml (~> 0.1)
fog-core (1.40.0)
builder builder
excon (~> 0.45) excon (~> 0.49)
formatador (~> 0.2) formatador (~> 0.2)
fog-dynect (0.0.2) fog-google (0.3.2)
fog-core
fog-json
fog-xml
fog-ecloud (0.3.0)
fog-core
fog-xml
fog-google (0.1.0)
fog-core fog-core
fog-json fog-json
fog-xml fog-xml
fog-json (1.0.2) fog-json (1.0.2)
fog-core (~> 1.0) fog-core (~> 1.0)
multi_json (~> 1.10) multi_json (~> 1.10)
fog-local (0.2.1) fog-local (0.3.0)
fog-core (~> 1.27) fog-core (~> 1.27)
fog-powerdns (0.1.1) fog-openstack (0.1.6)
fog-core (~> 1.27) fog-core (>= 1.39)
fog-json (~> 1.0) fog-json (>= 1.0)
fog-xml (~> 0.1) ipaddress (>= 0.8)
fog-profitbricks (0.0.5)
fog-core
fog-xml
nokogiri
fog-radosgw (0.0.5)
fog-core (>= 1.21.0)
fog-json
fog-xml (>= 0.0.1)
fog-riakcs (0.1.0)
fog-core
fog-json
fog-xml
fog-sakuracloud (1.7.5)
fog-core
fog-json
fog-serverlove (0.1.2)
fog-core
fog-json
fog-softlayer (1.0.3)
fog-core
fog-json
fog-storm_on_demand (0.1.1)
fog-core
fog-json
fog-terremark (0.1.0)
fog-core
fog-xml
fog-vmfusion (0.1.0)
fission
fog-core
fog-voxel (0.1.0)
fog-core
fog-xml
fog-xenserver (0.2.2)
fog-core
fog-xml
fog-xml (0.1.2) fog-xml (0.1.2)
fog-core fog-core
nokogiri (~> 1.5, >= 1.5.11) nokogiri (~> 1.5, >= 1.5.11)
@ -350,7 +277,7 @@ GEM
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab_emoji (0.3.1) gitlab_emoji (0.3.1)
gemojione (~> 2.2, >= 2.2.1) gemojione (~> 2.2, >= 2.2.1)
gitlab_git (10.1.0) gitlab_git (10.1.3)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
@ -404,7 +331,6 @@ GEM
hashie (3.4.3) hashie (3.4.3)
health_check (1.5.1) health_check (1.5.1)
rails (>= 2.3.0) rails (>= 2.3.0)
highline (1.7.8)
hipchat (1.5.2) hipchat (1.5.2)
httparty httparty
mimemagic mimemagic
@ -424,11 +350,10 @@ GEM
httpclient (2.7.0.1) httpclient (2.7.0.1)
i18n (0.7.0) i18n (0.7.0)
ice_nine (0.11.1) ice_nine (0.11.1)
inflecto (0.0.2)
influxdb (0.2.3) influxdb (0.2.3)
cause cause
json json
ipaddress (0.8.2) ipaddress (0.8.3)
jquery-atwho-rails (1.3.2) jquery-atwho-rails (1.3.2)
jquery-rails (4.1.1) jquery-rails (4.1.1)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
@ -441,10 +366,13 @@ GEM
railties (>= 3.2.16) railties (>= 3.2.16)
json (1.8.3) json (1.8.3)
jwt (1.5.2) jwt (1.5.2)
kaminari (0.16.3) kaminari (0.17.0)
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
kgio (2.10.0) kgio (2.10.0)
knapsack (1.11.0)
rake
timecop (>= 0.1.0)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.4.1) letter_opener (1.4.1)
@ -453,6 +381,12 @@ GEM
actionmailer (>= 3.2) actionmailer (>= 3.2)
letter_opener (~> 1.0) letter_opener (~> 1.0)
railties (>= 3.2) railties (>= 3.2)
license_finder (2.1.0)
bundler
httparty
rubyzip
thor
xml-simple
licensee (8.0.0) licensee (8.0.0)
rugged (>= 0.24b) rugged (>= 0.24b)
listen (3.0.5) listen (3.0.5)
@ -466,9 +400,9 @@ GEM
mime-types (>= 1.16, < 4) mime-types (>= 1.16, < 4)
mail_room (0.7.0) mail_room (0.7.0)
method_source (0.8.2) method_source (0.8.2)
mime-types (2.99.1) mime-types (2.99.2)
mimemagic (0.3.0) mimemagic (0.3.0)
mini_portile2 (2.0.0) mini_portile2 (2.1.0)
minitest (5.7.0) minitest (5.7.0)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
multi_json (1.11.2) multi_json (1.11.2)
@ -479,8 +413,10 @@ GEM
net-ldap (0.12.1) net-ldap (0.12.1)
net-ssh (3.0.1) net-ssh (3.0.1)
newrelic_rpm (3.14.1.311) newrelic_rpm (3.14.1.311)
nokogiri (1.6.7.2) nokogiri (1.6.8)
mini_portile2 (~> 2.0.0.rc2) mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
numerizer (0.1.1)
oauth (0.4.7) oauth (0.4.7)
oauth2 (1.0.0) oauth2 (1.0.0)
faraday (>= 0.8, < 0.10) faraday (>= 0.8, < 0.10)
@ -549,9 +485,10 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
paranoia (2.1.4) paranoia (2.1.4)
activerecord (~> 4.0) activerecord (~> 4.0)
parser (2.3.0.6) parser (2.3.1.0)
ast (~> 2.2) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
pkg-config (1.1.7)
poltergeist (1.9.0) poltergeist (1.9.0)
capybara (~> 2.1) capybara (~> 2.1)
cliver (~> 0.3.1) cliver (~> 0.3.1)
@ -627,7 +564,7 @@ GEM
debugger-ruby_core_source (~> 1.3) debugger-ruby_core_source (~> 1.3)
rdoc (3.12.2) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
recaptcha (1.0.2) recaptcha (3.0.0)
json json
redcarpet (3.3.3) redcarpet (3.3.3)
redis (3.3.0) redis (3.3.0)
@ -655,8 +592,8 @@ GEM
responders (2.1.1) responders (2.1.1)
railties (>= 4.2.0, < 5.1) railties (>= 4.2.0, < 5.1)
rinku (1.7.3) rinku (1.7.3)
rotp (2.1.1) rotp (2.1.2)
rouge (1.10.1) rouge (1.11.0)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
@ -684,31 +621,31 @@ GEM
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.4.1) rspec-support (3.4.1)
rubocop (0.38.0) rubocop (0.40.0)
parser (>= 2.3.0.6, < 3.0) parser (>= 2.3.1.0, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1) unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-rspec (1.5.0)
rubocop (>= 0.40.0)
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-progressbar (1.7.5) ruby-progressbar (1.8.1)
ruby-saml (1.1.2) ruby-saml (1.1.2)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
uuid (~> 2.3) uuid (~> 2.3)
ruby2ruby (2.3.0) ruby_parser (3.8.2)
ruby_parser (~> 3.1)
sexp_processor (~> 4.0)
ruby_parser (3.8.1)
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.2) rubyntlm (0.5.2)
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.2.0)
rufus-scheduler (3.1.10) rufus-scheduler (3.1.10)
rugged (0.24.0) rugged (0.24.0)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
sass (3.4.21) sass (3.4.22)
sass-rails (5.0.4) sass-rails (5.0.4)
railties (>= 4.0.0, < 5.0) railties (>= 4.0.0, < 5.0)
sass (~> 3.1) sass (~> 3.1)
@ -757,9 +694,6 @@ GEM
tilt (>= 1.3, < 3) tilt (>= 1.3, < 3)
six (0.2.0) six (0.2.0)
slack-notifier (1.2.1) slack-notifier (1.2.1)
slim (3.0.6)
temple (~> 0.7.3)
tilt (>= 1.3.3, < 2.1)
slop (3.6.0) slop (3.6.0)
spinach (0.8.10) spinach (0.8.10)
colorize colorize
@ -786,11 +720,11 @@ GEM
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
state_machines (0.4.0) state_machines (0.4.0)
state_machines-activemodel (0.3.0) state_machines-activemodel (0.4.0)
activemodel (~> 4.1) activemodel (>= 4.1, < 5.1)
state_machines (>= 0.4.0) state_machines (>= 0.4.0)
state_machines-activerecord (0.3.0) state_machines-activerecord (0.4.0)
activerecord (~> 4.1) activerecord (>= 4.1, < 5.1)
state_machines-activemodel (>= 0.3.0) state_machines-activemodel (>= 0.3.0)
stringex (2.5.2) stringex (2.5.2)
systemu (2.6.5) systemu (2.6.5)
@ -800,10 +734,8 @@ GEM
railties (>= 3.2.5, < 6) railties (>= 3.2.5, < 6)
teaspoon-jasmine (2.2.0) teaspoon-jasmine (2.2.0)
teaspoon (>= 1.0.0) teaspoon (>= 1.0.0)
temple (0.7.6)
term-ansicolor (1.3.2) term-ansicolor (1.3.2)
tins (~> 1.0) tins (~> 1.0)
terminal-table (1.5.2)
test_after_commit (0.4.2) test_after_commit (0.4.2)
activerecord (>= 3.2) activerecord (>= 3.2)
thin (1.6.4) thin (1.6.4)
@ -812,7 +744,8 @@ GEM
rack (~> 1.0) rack (~> 1.0)
thor (0.19.1) thor (0.19.1)
thread_safe (0.3.5) thread_safe (0.3.5)
tilt (2.0.2) tilt (2.0.5)
timecop (0.8.1)
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
tinder (1.10.1) tinder (1.10.1)
eventmachine (~> 1.0) eventmachine (~> 1.0)
@ -832,6 +765,7 @@ GEM
simple_oauth (~> 0.1.4) simple_oauth (~> 0.1.4)
tzinfo (1.2.2) tzinfo (1.2.2)
thread_safe (~> 0.1) thread_safe (~> 0.1)
u2f (0.2.1)
uglifier (2.7.2) uglifier (2.7.2)
execjs (>= 0.3.0) execjs (>= 0.3.0)
json (>= 1.8.0) json (>= 1.8.0)
@ -839,7 +773,7 @@ GEM
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.2) unf_ext (0.0.7.2)
unicode-display_width (1.0.2) unicode-display_width (1.0.5)
unicorn (4.9.0) unicorn (4.9.0)
kgio (~> 2.6) kgio (~> 2.6)
rack rack
@ -856,7 +790,7 @@ GEM
coercible (~> 1.0) coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3) descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9) equalizer (~> 0.0, >= 0.0.9)
warden (1.2.4) warden (1.2.6)
rack (>= 1.0) rack (>= 1.0)
web-console (2.3.0) web-console (2.3.0)
activemodel (>= 4.0) activemodel (>= 4.0)
@ -883,7 +817,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
RedCloth (~> 4.2.9) RedCloth (~> 4.2.9)
ace-rails-ap (~> 4.0.2) ace-rails-ap (~> 4.0.2)
activerecord-session_store (~> 0.1.0) activerecord-session_store (~> 1.0.0)
acts-as-taggable-on (~> 3.4) acts-as-taggable-on (~> 3.4)
addressable (~> 2.3.8) addressable (~> 2.3.8)
after_commit_queue after_commit_queue
@ -891,7 +825,7 @@ DEPENDENCIES
allocations (~> 1.0) allocations (~> 1.0)
asana (~> 0.4.0) asana (~> 0.4.0)
asciidoctor (~> 1.5.2) asciidoctor (~> 1.5.2)
attr_encrypted (~> 1.3.4) attr_encrypted (~> 3.0.0)
awesome_print (~> 1.2.0) awesome_print (~> 1.2.0)
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0) base32 (~> 0.3.0)
@ -899,27 +833,25 @@ DEPENDENCIES
better_errors (~> 1.0.1) better_errors (~> 1.0.1)
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0) bootstrap-sass (~> 3.3.0)
brakeman (~> 3.2.0) brakeman (~> 3.3.0)
browser (~> 1.0.0) browser (~> 2.0.3)
bullet bullet
bundler-audit bundler-audit
byebug byebug
cal-heatmap-rails (~> 3.6.0)
capybara (~> 2.6.2) capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
carrierwave (~> 0.10.0) carrierwave (~> 0.10.0)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
chronic_duration (~> 0.10.6)
coffee-rails (~> 4.1.0) coffee-rails (~> 4.1.0)
colorize (~> 0.7.0)
connection_pool (~> 2.0) connection_pool (~> 2.0)
coveralls (~> 0.8.2) coveralls (~> 0.8.2)
creole (~> 0.5.0) creole (~> 0.5.0)
d3_rails (~> 3.5.0) d3_rails (~> 3.5.0)
database_cleaner (~> 1.4.0) database_cleaner (~> 1.4.0)
default_value_for (~> 3.0.0) default_value_for (~> 3.0.0)
devise (~> 3.5.4) devise (~> 4.0)
devise-async (~> 0.9.0) devise-two-factor (~> 3.0.0)
devise-two-factor (~> 2.0.0)
diffy (~> 3.0.3) diffy (~> 3.0.3)
doorkeeper (~> 3.1) doorkeeper (~> 3.1)
dropzonejs-rails (~> 0.7.1) dropzonejs-rails (~> 0.7.1)
@ -929,7 +861,12 @@ DEPENDENCIES
ffaker (~> 2.0.0) ffaker (~> 2.0.0)
flay flay
flog flog
fog (~> 1.36.0) fog-aws (~> 0.9)
fog-azure (~> 0.0)
fog-core (~> 1.40)
fog-google (~> 0.3)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
font-awesome-rails (~> 4.2) font-awesome-rails (~> 4.2)
foreman foreman
fuubar (~> 2.0.0) fuubar (~> 2.0.0)
@ -957,8 +894,10 @@ DEPENDENCIES
jquery-turbolinks (~> 2.1.0) jquery-turbolinks (~> 2.1.0)
jquery-ui-rails (~> 5.0.0) jquery-ui-rails (~> 5.0.0)
jwt jwt
kaminari (~> 0.16.3) kaminari (~> 0.17.0)
knapsack
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.0)
license_finder
licensee (~> 8.0.0) licensee (~> 8.0.0)
loofah (~> 2.0.3) loofah (~> 2.0.3)
mail_room (~> 0.7) mail_room (~> 0.7)
@ -998,10 +937,11 @@ DEPENDENCIES
rack-oauth2 (~> 1.2.1) rack-oauth2 (~> 1.2.1)
rails (= 4.2.6) rails (= 4.2.6)
rails-deprecated_sanitizer (~> 1.0.3) rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0)
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
rblineprof rblineprof
rdoc (~> 3.6) rdoc (~> 3.6)
recaptcha recaptcha (~> 3.0)
redcarpet (~> 3.3.3) redcarpet (~> 3.3.3)
redis (~> 3.2) redis (~> 3.2)
redis-namespace redis-namespace
@ -1009,11 +949,12 @@ DEPENDENCIES
request_store (~> 1.3.0) request_store (~> 1.3.0)
rerun (~> 0.11.0) rerun (~> 0.11.0)
responders (~> 2.0) responders (~> 2.0)
rouge (~> 1.10.1) rouge (~> 1.11)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.4.0) rspec-rails (~> 3.4.0)
rspec-retry rspec-retry
rubocop (~> 0.38.0) rubocop (~> 0.40.0)
rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 5.0.0) sass-rails (~> 5.0.0)
@ -1038,7 +979,7 @@ DEPENDENCIES
spring-commands-spinach (~> 1.1.0) spring-commands-spinach (~> 1.1.0)
spring-commands-teaspoon (~> 0.0.2) spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 3.6.0) sprockets (~> 3.6.0)
state_machines-activerecord (~> 0.3.0) state_machines-activerecord (~> 0.4.0)
task_list (~> 1.0.2) task_list (~> 1.0.2)
teaspoon (~> 1.1.0) teaspoon (~> 1.1.0)
teaspoon-jasmine (~> 2.2.0) teaspoon-jasmine (~> 2.2.0)
@ -1046,6 +987,7 @@ DEPENDENCIES
thin (~> 1.6.1) thin (~> 1.6.1)
tinder (~> 1.10.0) tinder (~> 1.10.0)
turbolinks (~> 2.5.0) turbolinks (~> 2.5.0)
u2f (~> 0.2.1)
uglifier (~> 2.7.2) uglifier (~> 2.7.2)
underscore-rails (~> 1.8.0) underscore-rails (~> 1.8.0)
unf (~> 0.1.4) unf (~> 0.1.4)
@ -1058,4 +1000,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.12.3 1.12.5

View file

@ -1,9 +1,7 @@
# GitLab # GitLab
[![build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) [![build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![Build Status](https://semaphoreci.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/400484/shields_badge.svg)](https://semaphoreci.com/gitlabhq/gitlabhq)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master)
## Canonical source ## Canonical source

View file

@ -8,3 +8,5 @@ relative_url_conf = File.expand_path('../config/initializers/relative_url', __FI
require relative_url_conf if File.exist?("#{relative_url_conf}.rb") require relative_url_conf if File.exist?("#{relative_url_conf}.rb")
Gitlab::Application.load_tasks Gitlab::Application.load_tasks
Knapsack.load_tasks if defined?(Knapsack)

View file

@ -1 +1 @@
8.8.2 8.9.0-rc4

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,84 @@
class @LabelManager
errorMessage: 'Unable to update label prioritization at this time'
constructor: (opts = {}) ->
# Defaults
{
@togglePriorityButton = $('.js-toggle-priority')
@prioritizedLabels = $('.js-prioritized-labels')
@otherLabels = $('.js-other-labels')
} = opts
@prioritizedLabels.sortable(
items: 'li'
placeholder: 'list-placeholder'
axis: 'y'
update: @onPrioritySortUpdate.bind(@)
)
@bindEvents()
bindEvents: ->
@togglePriorityButton.on 'click', @, @onTogglePriorityClick
onTogglePriorityClick: (e) ->
e.preventDefault()
_this = e.data
$btn = $(e.currentTarget)
$label = $("##{$btn.data('domId')}")
action = if $btn.parents('.js-prioritized-labels').length then 'remove' else 'add'
_this.toggleLabelPriority($label, action)
toggleLabelPriority: ($label, action, persistState = true) ->
_this = @
url = $label.find('.js-toggle-priority').data 'url'
$target = @prioritizedLabels
$from = @otherLabels
# Optimistic update
if action is 'remove'
$target = @otherLabels
$from = @prioritizedLabels
if $from.find('li').length is 1
$from.find('.empty-message').show()
if not $target.find('li').length
$target.find('.empty-message').hide()
$label.detach().appendTo($target)
# Return if we are not persisting state
return unless persistState
if action is 'remove'
xhr = $.ajax url: url, type: 'DELETE'
else
xhr = @savePrioritySort($label, action)
xhr.fail @rollbackLabelPosition.bind(@, $label, action)
onPrioritySortUpdate: ->
xhr = @savePrioritySort()
xhr.fail ->
new Flash(@errorMessage, 'alert')
savePrioritySort: () ->
$.post
url: @prioritizedLabels.data('url')
data:
label_ids: @getSortedLabelsIds()
rollbackLabelPosition: ($label, originalAction)->
action = if originalAction is 'remove' then 'add' else 'remove'
@toggleLabelPriority($label, action, false)
new Flash(@errorMessage, 'alert')
getSortedLabelsIds: ->
sortedIds = []
@prioritizedLabels.find('li').each ->
sortedIds.push $(@).data 'id'
sortedIds

View file

@ -1,11 +1,14 @@
class @Activities class @Activities
constructor: -> constructor: ->
Pager.init 20, true Pager.init 20, true, false, @updateTooltips
$(".event-filter-link").on "click", (event) => $(".event-filter-link").on "click", (event) =>
event.preventDefault() event.preventDefault()
@toggleFilter($(event.currentTarget)) @toggleFilter($(event.currentTarget))
@reloadActivities() @reloadActivities()
updateTooltips: ->
gl.utils.localTimeAgo($('.js-timeago', '#activity'))
reloadActivities: -> reloadActivities: ->
$(".content_list").html '' $(".content_list").html ''
Pager.init 20, true Pager.init 20, true

View file

@ -4,7 +4,7 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file. # the compiled file.
# #
#= require jquery #= require jquery2
#= require jquery-ui/autocomplete #= require jquery-ui/autocomplete
#= require jquery-ui/datepicker #= require jquery-ui/datepicker
#= require jquery-ui/draggable #= require jquery-ui/draggable
@ -18,8 +18,6 @@
#= require jquery.atwho #= require jquery.atwho
#= require jquery.scrollTo #= require jquery.scrollTo
#= require jquery.turbolinks #= require jquery.turbolinks
#= require d3
#= require cal-heatmap
#= require turbolinks #= require turbolinks
#= require autosave #= require autosave
#= require bootstrap/affix #= require bootstrap/affix
@ -37,7 +35,6 @@
#= require raphael #= require raphael
#= require g.raphael #= require g.raphael
#= require g.bar #= require g.bar
#= require Chart
#= require branch-graph #= require branch-graph
#= require ace/ace #= require ace/ace
#= require ace/ext-searchbox #= require ace/ext-searchbox
@ -52,9 +49,17 @@
#= require shortcuts_network #= require shortcuts_network
#= require jquery.nicescroll #= require jquery.nicescroll
#= require date.format #= require date.format
#= require_tree . #= require_directory ./behaviors
#= require_directory ./blob
#= require_directory ./ci
#= require_directory ./commit
#= require_directory ./extensions
#= require_directory ./lib
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus #= require fuzzaldrin-plus
#= require cropper #= require cropper
#= require u2f
window.slugify = (text) -> window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
@ -120,6 +125,7 @@ window.onload = ->
setTimeout shiftWindow, 100 setTimeout shiftWindow, 100
$ -> $ ->
gl.utils.preventDisabledButtons()
bootstrapBreakpoint = bp.getBreakpointSize() bootstrapBreakpoint = bp.getBreakpointSize()
$(".nicescroll").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF") $(".nicescroll").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
@ -157,19 +163,6 @@ $ ->
$el.data('placement') || 'bottom' $el.data('placement') || 'bottom'
) )
$('.header-logo .home').tooltip(
placement: (_, el) ->
$el = $(el)
if $('.page-with-sidebar').hasClass('page-sidebar-collapsed') then 'right' else 'bottom'
container: 'body'
)
$('.page-with-sidebar').tooltip(
selector: '.sidebar-collapsed .nav-sidebar a, .sidebar-collapsed a.sidebar-user'
placement: 'right'
container: 'body'
)
# Form submitter # Form submitter
$('.trigger-submit').on 'change', -> $('.trigger-submit').on 'change', ->
$(@).parents('form').submit() $(@).parents('form').submit()
@ -202,6 +195,7 @@ $ ->
$('.navbar-toggle').on 'click', -> $('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle() $('.header-content .title').toggle()
$('.header-content .header-logo').toggle()
$('.header-content .navbar-collapse').toggle() $('.header-content .navbar-collapse').toggle()
$('.navbar-toggle').toggleClass('active') $('.navbar-toggle').toggleClass('active')
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left") $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
@ -220,6 +214,10 @@ $ ->
form = btn.closest("form") form = btn.closest("form")
new ConfirmDangerModal(form, text) new ConfirmDangerModal(form, text)
$(document).on 'click', 'button', ->
$(this).blur()
$('input[type="search"]').each -> $('input[type="search"]').each ->
$this = $(this) $this = $(this)
$this.attr 'value', $this.val() $this.attr 'value', $this.val()
@ -232,7 +230,6 @@ $ ->
$this.attr 'value', $this.val() $this.attr 'value', $this.val()
$sidebarGutterToggle = $('.js-sidebar-toggle') $sidebarGutterToggle = $('.js-sidebar-toggle')
$navIconToggle = $('.toggle-nav-collapse')
$(document) $(document)
.off 'breakpoint:change' .off 'breakpoint:change'
@ -242,42 +239,6 @@ $ ->
if $gutterIcon.hasClass('fa-angle-double-right') if $gutterIcon.hasClass('fa-angle-double-right')
$sidebarGutterToggle.trigger('click') $sidebarGutterToggle.trigger('click')
$navIcon = $navIconToggle.find('.fa')
if $navIcon.hasClass('fa-angle-left')
$navIconToggle.trigger('click')
$(document)
.off 'click', '.js-sidebar-toggle'
.on 'click', '.js-sidebar-toggle', (e, triggered) ->
e.preventDefault()
$this = $(this)
$thisIcon = $this.find 'i'
$allGutterToggleIcons = $('.js-sidebar-toggle i')
if $thisIcon.hasClass('fa-angle-double-right')
$allGutterToggleIcons
.removeClass('fa-angle-double-right')
.addClass('fa-angle-double-left')
$('aside.right-sidebar')
.removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed')
$('.page-with-sidebar')
.removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed')
else
$allGutterToggleIcons
.removeClass('fa-angle-double-left')
.addClass('fa-angle-double-right')
$('aside.right-sidebar')
.removeClass('right-sidebar-collapsed')
.addClass('right-sidebar-expanded')
$('.page-with-sidebar')
.removeClass('right-sidebar-collapsed')
.addClass('right-sidebar-expanded')
if not triggered
$.cookie("collapsed_gutter",
$('.right-sidebar')
.hasClass('right-sidebar-collapsed'), { path: '/' })
fitSidebarForSize = -> fitSidebarForSize = ->
oldBootstrapBreakpoint = bootstrapBreakpoint oldBootstrapBreakpoint = bootstrapBreakpoint
bootstrapBreakpoint = bp.getBreakpointSize() bootstrapBreakpoint = bp.getBreakpointSize()
@ -290,9 +251,10 @@ $ ->
$(document).trigger('breakpoint:change', [bootstrapBreakpoint]) $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$(window) $(window)
.off "resize" .off "resize.app"
.on "resize", (e) -> .on "resize.app", (e) ->
fitSidebarForSize() fitSidebarForSize()
gl.awardsHandler = new AwardsHandler()
checkInitialSidebarSize() checkInitialSidebarSize()
new Aside() new Aside()

View file

@ -1,201 +1,354 @@
class @AwardsHandler class @AwardsHandler
constructor: (@getEmojisUrl, @postEmojiUrl, @noteableType, @noteableId, @unicodes) ->
$('.js-add-award').on 'click', (event) =>
event.stopPropagation()
event.preventDefault()
@showEmojiMenu() constructor: ->
$('html').on 'click', (event) -> @aliases = gl.emojiAliases()
if !$(event.target).closest('.emoji-menu').length
$(document)
.off 'click', '.js-add-award'
.on 'click', '.js-add-award', (e) =>
e.stopPropagation()
e.preventDefault()
@showEmojiMenu $(e.currentTarget)
$('html').on 'click', (e) ->
$target = $ e.target
unless $target.closest('.emoji-menu-content').length
$('.js-awards-block.current').removeClass 'current'
unless $target.closest('.emoji-menu').length
if $('.emoji-menu').is(':visible') if $('.emoji-menu').is(':visible')
$('.js-add-award.is-active').removeClass 'is-active'
$('.emoji-menu').removeClass 'is-visible' $('.emoji-menu').removeClass 'is-visible'
$('.awards') $(document)
.off 'click' .off 'click', '.js-emoji-btn'
.on 'click', '.js-emoji-btn', @handleClick .on 'click', '.js-emoji-btn', (e) =>
e.preventDefault()
@renderFrequentlyUsedBlock() $target = $ e.currentTarget
emoji = $target.find('.icon').data 'emoji'
handleClick: (e) -> $target.closest('.js-awards-block').addClass 'current'
e.preventDefault() @addAward @getVotesBlock(), @getAwardUrl(), emoji
emoji = $(this)
.find('.icon')
.data 'emoji'
if emoji is 'thumbsup' and awardsHandler.didUserClickEmoji $(this), 'thumbsdown'
awardsHandler.addAward 'thumbsdown'
else if emoji is 'thumbsdown' and awardsHandler.didUserClickEmoji $(this), 'thumbsup' showEmojiMenu: ($addBtn) ->
awardsHandler.addAward 'thumbsup'
awardsHandler.addAward emoji $menu = $ '.emoji-menu'
$(this).trigger 'blur' if $addBtn.hasClass 'js-note-emoji'
$addBtn.parents('.note').find('.js-awards-block').addClass 'current'
else
$addBtn.closest('.js-awards-block').addClass 'current'
didUserClickEmoji: (that, emoji) -> if $menu.length
if $(that).siblings("button:has([data-emoji=#{emoji}])").attr('data-original-title') $holder = $addBtn.closest('.js-award-holder')
$(that).siblings("button:has([data-emoji=#{emoji}])").attr('data-original-title').indexOf('me') > -1
showEmojiMenu: -> if $menu.is '.is-visible'
if $('.emoji-menu').length $addBtn.removeClass 'is-active'
if $('.emoji-menu').is '.is-visible' $menu.removeClass 'is-visible'
$('.emoji-menu').removeClass 'is-visible'
$('#emoji_search').blur() $('#emoji_search').blur()
else else
$('.emoji-menu').addClass 'is-visible' $addBtn.addClass 'is-active'
@positionMenu($menu, $addBtn)
$menu.addClass 'is-visible'
$('#emoji_search').focus() $('#emoji_search').focus()
else else
$('.js-add-award').addClass 'is-loading' $addBtn.addClass 'is-loading is-active'
$.get @getEmojisUrl, (response) => url = @getAwardMenuUrl()
$('.js-add-award').removeClass 'is-loading'
$('.js-award-holder').append response @createEmojiMenu url, =>
$addBtn.removeClass 'is-loading'
$menu = $('.emoji-menu')
@positionMenu($menu, $addBtn)
@renderFrequentlyUsedBlock() unless @frequentEmojiBlockRendered
setTimeout => setTimeout =>
$('.emoji-menu').addClass 'is-visible' $menu.addClass 'is-visible'
$('#emoji_search').focus() $('#emoji_search').focus()
@setupSearch() @setupSearch()
, 200 , 200
addAward: (emoji) ->
@postEmoji emoji, => createEmojiMenu: (awardMenuUrl, callback) ->
@addAwardToEmojiBar(emoji)
$.get awardMenuUrl, (response) ->
$('body').append response
callback()
positionMenu: ($menu, $addBtn) ->
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"
if position? and position is 'right'
css.left = "#{($addBtn.offset().left - $menu.outerWidth()) + 20}px"
$menu.addClass 'is-aligned-right'
else
css.left = "#{$addBtn.offset().left}px"
$menu.removeClass 'is-aligned-right'
$menu.css(css)
addAward: (votesBlock, awardUrl, emoji, checkMutuality = true, callback) ->
emoji = @normilizeEmojiName emoji
@postEmoji awardUrl, emoji, =>
@addAwardToEmojiBar votesBlock, emoji, checkMutuality
callback?()
$('.emoji-menu').removeClass 'is-visible' $('.emoji-menu').removeClass 'is-visible'
addAwardToEmojiBar: (emoji) ->
@addEmojiToFrequentlyUsedList(emoji)
if @exist(emoji) addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = true) ->
if @isActive(emoji)
@decrementCounter(emoji) @checkMutuality votesBlock, emoji if checkForMutuality
@addEmojiToFrequentlyUsedList emoji
emoji = @normilizeEmojiName emoji
$emojiButton = @findEmojiIcon(votesBlock, emoji).parent()
if $emojiButton.length > 0
if @isActive $emojiButton
@decrementCounter $emojiButton, emoji
else else
counter = @findEmojiIcon(emoji).siblings('.js-counter') counter = $emojiButton.find '.js-counter'
counter.text(parseInt(counter.text()) + 1) counter.text parseInt(counter.text()) + 1
counter.parent().addClass('active') $emojiButton.addClass 'active'
@addMeToAuthorList(emoji) @addMeToUserList votesBlock, emoji
@animateEmoji $emojiButton
else else
@createEmoji(emoji) votesBlock.removeClass 'hidden'
@createEmoji votesBlock, emoji
exist: (emoji) ->
@findEmojiIcon(emoji).length > 0
isActive: (emoji) -> getVotesBlock: ->
@findEmojiIcon(emoji).parent().hasClass('active')
decrementCounter: (emoji) -> currentBlock = $ '.js-awards-block.current'
counter = @findEmojiIcon(emoji).siblings('.js-counter') return if currentBlock.length then currentBlock else $('.js-awards-block').eq 0
emojiIcon = counter.parent()
if parseInt(counter.text()) > 1
counter.text(parseInt(counter.text()) - 1) getAwardUrl: -> return @getVotesBlock().data 'award-url'
emojiIcon.removeClass('active')
@removeMeFromAuthorList(emoji)
else if emoji == 'thumbsup' || emoji == 'thumbsdown' checkMutuality: (votesBlock, emoji) ->
emojiIcon.tooltip('destroy')
counter.text(0) awardUrl = @getAwardUrl()
emojiIcon.removeClass('active')
@removeMeFromAuthorList(emoji) if emoji in [ 'thumbsup', 'thumbsdown' ]
mutualVote = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup'
$emojiButton = votesBlock.find("[data-emoji=#{mutualVote}]").parent()
isAlreadyVoted = $emojiButton.hasClass 'active'
if isAlreadyVoted
@showEmojiLoader $emojiButton
@addAward votesBlock, awardUrl, mutualVote, false, ->
$emojiButton.removeClass 'is-loading'
showEmojiLoader: ($emojiButton) ->
$loader = $emojiButton.find '.fa-spinner'
unless $loader.length
$emojiButton.append '<i class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>'
$emojiButton.addClass 'is-loading'
isActive: ($emojiButton) -> $emojiButton.hasClass 'active'
decrementCounter: ($emojiButton, emoji) ->
counter = $ '.js-counter', $emojiButton
counterNumber = parseInt counter.text(), 10
if counterNumber > 1
counter.text counterNumber - 1
@removeMeFromUserList $emojiButton, emoji
else if emoji is 'thumbsup' or emoji is 'thumbsdown'
$emojiButton.tooltip 'destroy'
counter.text '0'
@removeMeFromUserList $emojiButton, emoji
@removeEmoji $emojiButton if $emojiButton.parents('.note').length
else else
emojiIcon.tooltip('destroy') @removeEmoji $emojiButton
emojiIcon.remove()
$emojiButton.removeClass 'active'
removeEmoji: ($emojiButton) ->
$emojiButton.tooltip('destroy')
$emojiButton.remove()
$votesBlock = @getVotesBlock()
if $votesBlock.find('.js-emoji-btn').length is 0
$votesBlock.addClass 'hidden'
getAwardTooltip: ($awardBlock) ->
return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title') or ''
removeMeFromUserList: ($emojiButton, emoji) ->
awardBlock = $emojiButton
originalTitle = @getAwardTooltip awardBlock
authors = originalTitle.split ', '
authors.splice authors.indexOf('me'), 1
newAuthors = authors.join ', '
removeMeFromAuthorList: (emoji) ->
awardBlock = @findEmojiIcon(emoji).parent()
authors = awardBlock
.attr('data-original-title')
.split(', ')
authors.splice(authors.indexOf('me'),1)
awardBlock awardBlock
.closest('.js-emoji-btn') .closest '.js-emoji-btn'
.attr('data-original-title', authors.join(', ')) .removeData 'original-title'
@resetTooltip(awardBlock) .attr 'data-original-title', newAuthors
@resetTooltip awardBlock
addMeToUserList: (votesBlock, emoji) ->
awardBlock = @findEmojiIcon(votesBlock, emoji).parent()
origTitle = @getAwardTooltip awardBlock
users = []
addMeToAuthorList: (emoji) ->
awardBlock = @findEmojiIcon(emoji).parent()
origTitle = awardBlock.attr('data-original-title').trim()
authors = []
if origTitle if origTitle
authors = origTitle.split(', ') users = origTitle.trim().split ', '
authors.push('me')
awardBlock.attr('data-original-title', authors.join(', ')) users.push 'me'
@resetTooltip(awardBlock) awardBlock.attr 'title', users.join ', '
@resetTooltip awardBlock
resetTooltip: (award) -> resetTooltip: (award) ->
award.tooltip('destroy')
# "destroy" call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout. award.tooltip 'destroy'
setTimeout (->
award.tooltip() # 'destroy' call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout.
), 200 cb = -> award.tooltip()
setTimeout cb, 200
createEmoji: (emoji) -> createEmoji_: (votesBlock, emoji) ->
emojiCssClass = @resolveNameToCssClass(emoji)
nodes = [] emojiCssClass = @resolveNameToCssClass emoji
nodes.push( buttonHtml = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'>
"<button class='btn award-control js-emoji-btn has-tooltip active' data-original-title='me'>", <div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>
"<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>", <span class='award-control-text js-counter'>1</span>
"<span class='award-control-text js-counter'>1</span>", </button>"
"</button>"
)
$(nodes.join("\n")) $emojiButton = $ buttonHtml
.insertBefore('.js-award-holder') $emojiButton
.find('.emoji-icon') .insertBefore votesBlock.find '.js-award-holder'
.data('emoji', emoji) .find '.emoji-icon'
.data 'emoji', emoji
@animateEmoji $emojiButton
$('.award-control').tooltip() $('.award-control').tooltip()
votesBlock.removeClass 'current'
animateEmoji: ($emoji) ->
className = 'pulse animated'
$emoji.addClass className
setTimeout (-> $emoji.removeClass className), 321
createEmoji: (votesBlock, emoji) ->
if $('.emoji-menu').length
return @createEmoji_ votesBlock, emoji
@createEmojiMenu @getAwardMenuUrl(), => @createEmoji_ votesBlock, emoji
getAwardMenuUrl: -> return gon.award_menu_url
resolveNameToCssClass: (emoji) -> resolveNameToCssClass: (emoji) ->
emojiIcon = $(".emoji-menu-content [data-emoji='#{emoji}']")
emojiIcon = $ ".emoji-menu-content [data-emoji='#{emoji}']"
if emojiIcon.length > 0 if emojiIcon.length > 0
unicodeName = emojiIcon.data('unicode-name') unicodeName = emojiIcon.data 'unicode-name'
else else
# Find by alias # Find by alias
unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data('unicode-name') unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data 'unicode-name'
"emoji-#{unicodeName}" return "emoji-#{unicodeName}"
postEmoji: (emoji, callback) ->
$.post @postEmojiUrl, { note: {
note: ":#{emoji}:"
noteable_type: @noteableType
noteable_id: @noteableId
}},(data) ->
if data.ok
callback.call()
findEmojiIcon: (emoji) -> postEmoji: (awardUrl, emoji, callback) ->
$(".awards > .js-emoji-btn [data-emoji='#{emoji}']")
$.post awardUrl, { name: emoji }, (data) ->
callback() if data.ok
findEmojiIcon: (votesBlock, emoji) ->
return votesBlock.find ".js-emoji-btn [data-emoji='#{emoji}']"
scrollToAwards: -> scrollToAwards: ->
$('body, html').animate({
scrollTop: $('.awards').offset().top - 80 options = scrollTop: $('.awards').offset().top - 110
}, 200) $('body, html').animate options, 200
normilizeEmojiName: (emoji) -> return @aliases[emoji] or emoji
addEmojiToFrequentlyUsedList: (emoji) -> addEmojiToFrequentlyUsedList: (emoji) ->
frequentlyUsedEmojis = @getFrequentlyUsedEmojis() frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
frequentlyUsedEmojis.push(emoji) frequentlyUsedEmojis.push emoji
$.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }) $.cookie 'frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }
getFrequentlyUsedEmojis: -> getFrequentlyUsedEmojis: ->
frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(',')
_.compact(_.uniq(frequentlyUsedEmojis)) frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') or '').split(',')
return _.compact _.uniq frequentlyUsedEmojis
renderFrequentlyUsedBlock: -> renderFrequentlyUsedBlock: ->
if $.cookie('frequently_used_emojis')
if $.cookie 'frequently_used_emojis'
frequentlyUsedEmojis = @getFrequentlyUsedEmojis() frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
ul = $('<ul>') ul = $("<ul class='clearfix emoji-menu-list frequent-emojis'>")
for emoji in frequentlyUsedEmojis for emoji in frequentlyUsedEmojis
do (emoji) -> $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
$(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
$('input.emoji-search').after(ul).after($('<h5>').text('Frequently used')) $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used'))
@frequentEmojiBlockRendered = true
setupSearch: -> setupSearch: ->
$('input.emoji-search').keyup (ev) =>
$('input.emoji-search').on 'keyup', (ev) =>
term = $(ev.target).val() term = $(ev.target).val()
# Clean previous search results # Clean previous search results
@ -204,12 +357,14 @@ class @AwardsHandler
if term if term
# Generate a search result block # Generate a search result block
h5 = $('<h5>').text('Search results').addClass('emoji-search') h5 = $('<h5>').text('Search results').addClass('emoji-search')
foundEmojis = @searchEmojis(term).show() found_emojis = @searchEmojis(term).show()
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis) ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis)
$('.emoji-menu-content ul, .emoji-menu-content h5').hide() $('.emoji-menu-content ul, .emoji-menu-content h5').hide()
$('.emoji-menu-content').append(h5).append(ul) $('.emoji-menu-content').append(h5).append(ul)
else else
$('.emoji-menu-content').children().show() $('.emoji-menu-content').children().show()
searchEmojis: (term)->
$(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone() searchEmojis: (term) ->
$(".emoji-menu-list:not(.frequent-emojis) [data-emoji*='#{term}']").closest('li').clone()

View file

@ -1,34 +0,0 @@
class @Calendar
constructor: (timestamps, starting_year, starting_month, calendar_activities_path) ->
cal = new CalHeatMap()
cal.init
itemName: ["contribution"]
data: timestamps
start: new Date(starting_year, starting_month)
domainLabelFormat: "%b"
id: "cal-heatmap"
domain: "month"
subDomain: "day"
range: 12
tooltip: true
label:
position: "top"
legend: [
0
10
20
30
]
legendCellPadding: 3
cellSize: $('.user-calendar').width() / 73
onClick: (date, count) ->
formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
$.ajax
url: calendar_activities_path
data:
date: formated_date
cache: false
dataType: "html"
success: (data) ->
$(".user-calendar-activities").html data

View file

@ -1,19 +1,33 @@
class CiBuild class @CiBuild
@interval: null @interval: null
@state: null @state: null
constructor: (build_url, build_status, build_state) -> constructor: (@build_url, @build_status, @state) ->
clearInterval(CiBuild.interval) clearInterval(CiBuild.interval)
@state = build_state # Init breakpoint checker
@bp = Breakpoints.get()
@hideSidebar()
$('.js-build-sidebar').niceScroll()
$(document)
.off 'click', '.js-sidebar-build-toggle'
.on 'click', '.js-sidebar-build-toggle', @toggleSidebar
@initScrollButtonAffix() $(window)
.off 'resize.build'
.on 'resize.build', @hideSidebar
if build_status == "running" || build_status == "pending" @updateArtifactRemoveDate()
if $('#build-trace').length
@getInitialBuildTrace()
@initScrollButtonAffix()
if @build_status is "running" or @build_status is "pending"
# #
# Bind autoscroll button to follow build output # Bind autoscroll button to follow build output
# #
$("#autoscroll-button").bind "click", -> $('#autoscroll-button').on 'click', ->
state = $(this).data("state") state = $(this).data("state")
if "enabled" is state if "enabled" is state
$(this).data "state", "disabled" $(this).data "state", "disabled"
@ -27,26 +41,37 @@ class CiBuild
# Only valid for runnig build when output changes during time # Only valid for runnig build when output changes during time
# #
CiBuild.interval = setInterval => CiBuild.interval = setInterval =>
if window.location.href.split("#").first() is build_url if window.location.href.split("#").first() is @build_url
last_state = @state @getBuildTrace()
$.ajax
url: build_url + "/trace.json?state=" + encodeURIComponent(@state)
dataType: "json"
success: (log) =>
return unless last_state is @state
if log.state and log.status is "running"
@state = log.state
if log.append
$('.fa-refresh').before log.html
else
$('#build-trace code').html log.html
$('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
@checkAutoscroll()
else if log.status isnt build_status
Turbolinks.visit build_url
, 4000 , 4000
getInitialBuildTrace: ->
$.ajax
url: @build_url
dataType: 'json'
success: (build_data) ->
$('.js-build-output').html build_data.trace_html
if build_data.status is 'success' or build_data.status is 'failed'
$('.js-build-refresh').remove()
getBuildTrace: ->
$.ajax
url: "#{@build_url}/trace.json?state=#{encodeURIComponent(@state)}"
dataType: "json"
success: (log) =>
if log.state
@state = log.state
if log.status is "running"
if log.append
$('.js-build-output').append log.html
else
$('.js-build-output').html log.html
@checkAutoscroll()
else if log.status isnt @build_status
Turbolinks.visit @build_url
checkAutoscroll: -> checkAutoscroll: ->
$("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state") $("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state")
@ -61,4 +86,29 @@ class CiBuild
$body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top) $body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top)
) )
@CiBuild = CiBuild shouldHideSidebar: ->
bootstrapBreakpoint = @bp.getBreakpointSize()
bootstrapBreakpoint is 'xs' or bootstrapBreakpoint is 'sm'
toggleSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.toggleClass 'right-sidebar-expanded right-sidebar-collapsed'
hideSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.removeClass 'right-sidebar-expanded'
.addClass 'right-sidebar-collapsed'
else
$('.js-build-sidebar')
.removeClass 'right-sidebar-collapsed'
.addClass 'right-sidebar-expanded'
updateArtifactRemoveDate: ->
$date = $('.js-artifacts-remove')
if $date.length
date = $date.text()
$date.text $.timefor(new Date(date), ' ')

View file

@ -16,8 +16,8 @@ class Dispatcher
shortcut_handler = null shortcut_handler = null
switch page switch page
when 'projects:issues:index' when 'projects:issues:index'
Issues.init()
Issuable.init() Issuable.init()
new IssuableBulkActions()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'projects:issues:show' when 'projects:issues:show'
new Issue() new Issue()
@ -53,9 +53,13 @@ class Dispatcher
new Diff() new Diff()
shortcut_handler = new ShortcutsIssuable(true) shortcut_handler = new ShortcutsIssuable(true)
new ZenMode() new ZenMode()
new MergedButtons()
when 'projects:merge_requests:commits', 'projects:merge_requests:builds'
new MergedButtons()
when "projects:merge_requests:diffs" when "projects:merge_requests:diffs"
new Diff() new Diff()
new ZenMode() new ZenMode()
new MergedButtons()
when 'projects:merge_requests:index' when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
Issuable.init() Issuable.init()
@ -98,6 +102,8 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'projects:labels:new', 'projects:labels:edit' when 'projects:labels:new', 'projects:labels:edit'
new Labels() new Labels()
when 'projects:labels:index'
new LabelManager() if $('.prioritized-labels').length
when 'projects:network:show' when 'projects:network:show'
# Ensure we don't create a particular shortcut handler here. This is # Ensure we don't create a particular shortcut handler here. This is
# already created, where the network graph is created. # already created, where the network graph is created.
@ -119,7 +125,7 @@ class Dispatcher
new UsersSelect() new UsersSelect()
when 'projects' when 'projects'
new NamespaceSelect() new NamespaceSelect()
when 'dashboard' when 'dashboard', 'root'
shortcut_handler = new ShortcutsDashboardNavigation() shortcut_handler = new ShortcutsDashboardNavigation()
when 'profiles' when 'profiles'
new Profile() new Profile()

View file

@ -21,7 +21,7 @@ class @DueDateSelect
$dropdown.glDropdown( $dropdown.glDropdown(
hidden: -> hidden: ->
$selectbox.hide() $selectbox.hide()
$value.removeAttr('style') $value.css('display', '')
) )
addDueDate = (isDropdown) -> addDueDate = (isDropdown) ->
@ -42,12 +42,13 @@ class @DueDateSelect
type: 'PUT' type: 'PUT'
url: issueUpdateURL url: issueUpdateURL
data: data data: data
dataType: 'json'
beforeSend: -> beforeSend: ->
$loading.fadeIn() $loading.fadeIn()
if isDropdown if isDropdown
$dropdown.trigger('loading.gl.dropdown') $dropdown.trigger('loading.gl.dropdown')
$selectbox.hide() $selectbox.hide()
$value.removeAttr('style') $value.css('display', '')
$valueContent.html(mediumDate) $valueContent.html(mediumDate)
$sidebarValue.html(mediumDate) $sidebarValue.html(mediumDate)

View file

@ -1,5 +1,5 @@
class @Flash class @Flash
constructor: (message, type)-> constructor: (message, type = 'alert')->
@flash = $(".flash-container") @flash = $(".flash-container")
@flash.html("") @flash.html("")

View file

@ -3,6 +3,7 @@
window.GitLab ?= {} window.GitLab ?= {}
GitLab.GfmAutoComplete = GitLab.GfmAutoComplete =
dataLoading: false dataLoading: false
dataLoaded: false
dataSource: '' dataSource: ''
@ -22,6 +23,24 @@ GitLab.GfmAutoComplete =
Milestones: Milestones:
template: '<li>${title}</li>' template: '<li>${title}</li>'
Loading:
template: '<li><i class="fa fa-refresh fa-spin"></i> Loading...</li>'
DefaultOptions:
sorter: (query, items, searchKey) ->
return items if items[0].name? and items[0].name is 'loading'
$.fn.atwho.default.callbacks.sorter(query, items, searchKey)
filter: (query, data, searchKey) ->
return data if data[0] is 'loading'
$.fn.atwho.default.callbacks.filter(query, data, searchKey)
beforeInsert: (value) ->
if not GitLab.GfmAutoComplete.dataLoaded
@at
else
value
# Add GFM auto-completion to all input fields, that accept GFM input. # Add GFM auto-completion to all input fields, that accept GFM input.
setup: (wrap) -> setup: (wrap) ->
@input = $('.js-gfm-input') @input = $('.js-gfm-input')
@ -53,18 +72,37 @@ GitLab.GfmAutoComplete =
# Emoji # Emoji
@input.atwho @input.atwho
at: ':' at: ':'
displayTpl: @Emoji.template displayTpl: (value) =>
if value.path?
@Emoji.template
else
@Loading.template
insertTpl: ':${name}:' insertTpl: ':${name}:'
data: ['loading']
callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
# Team Members # Team Members
@input.atwho @input.atwho
at: '@' at: '@'
displayTpl: @Members.template displayTpl: (value) =>
if value.username?
@Members.template
else
@Loading.template
insertTpl: '${atwho-at}${username}' insertTpl: '${atwho-at}${username}'
searchKey: 'search' searchKey: 'search'
data: ['loading']
callbacks: callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
beforeSave: (members) -> beforeSave: (members) ->
$.map members, (m) -> $.map members, (m) ->
return m if not m.username?
title = m.name title = m.name
title += " (#{m.count})" if m.count title += " (#{m.count})" if m.count
@ -76,11 +114,21 @@ GitLab.GfmAutoComplete =
at: '#' at: '#'
alias: 'issues' alias: 'issues'
searchKey: 'search' searchKey: 'search'
displayTpl: @Issues.template displayTpl: (value) =>
if value.title?
@Issues.template
else
@Loading.template
data: ['loading']
insertTpl: '${atwho-at}${id}' insertTpl: '${atwho-at}${id}'
callbacks: callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
beforeSave: (issues) -> beforeSave: (issues) ->
$.map issues, (i) -> $.map issues, (i) ->
return i if not i.title?
id: i.iid id: i.iid
title: sanitize(i.title) title: sanitize(i.title)
search: "#{i.iid} #{i.title}" search: "#{i.iid} #{i.title}"
@ -89,11 +137,18 @@ GitLab.GfmAutoComplete =
at: '%' at: '%'
alias: 'milestones' alias: 'milestones'
searchKey: 'search' searchKey: 'search'
displayTpl: @Milestones.template displayTpl: (value) =>
if value.title?
@Milestones.template
else
@Loading.template
insertTpl: '${atwho-at}"${title}"' insertTpl: '${atwho-at}"${title}"'
data: ['loading']
callbacks: callbacks:
beforeSave: (milestones) -> beforeSave: (milestones) ->
$.map milestones, (m) -> $.map milestones, (m) ->
return m if not m.title?
id: m.iid id: m.iid
title: sanitize(m.title) title: sanitize(m.title)
search: "#{m.title}" search: "#{m.title}"
@ -102,11 +157,21 @@ GitLab.GfmAutoComplete =
at: '!' at: '!'
alias: 'mergerequests' alias: 'mergerequests'
searchKey: 'search' searchKey: 'search'
displayTpl: @Issues.template displayTpl: (value) =>
if value.title?
@Issues.template
else
@Loading.template
data: ['loading']
insertTpl: '${atwho-at}${id}' insertTpl: '${atwho-at}${id}'
callbacks: callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
beforeSave: (merges) -> beforeSave: (merges) ->
$.map merges, (m) -> $.map merges, (m) ->
return m if not m.title?
id: m.iid id: m.iid
title: sanitize(m.title) title: sanitize(m.title)
search: "#{m.iid} #{m.title}" search: "#{m.iid} #{m.title}"
@ -118,6 +183,8 @@ GitLab.GfmAutoComplete =
$.getJSON(dataSource) $.getJSON(dataSource)
loadData: (data) -> loadData: (data) ->
@dataLoaded = true
# load members # load members
@input.atwho 'load', '@', data.members @input.atwho 'load', '@', data.members
# load issues # load issues
@ -128,3 +195,7 @@ GitLab.GfmAutoComplete =
@input.atwho 'load', 'mergerequests', data.mergerequests @input.atwho 'load', 'mergerequests', data.mergerequests
# load emojis # load emojis
@input.atwho 'load', ':', data.emojis @input.atwho 'load', ':', data.emojis
# This trigger at.js again
# otherwise we would be stuck with loading until the user types
$(':focus').trigger('keyup')

View file

@ -11,6 +11,8 @@ class GitLabDropdownFilter
$inputContainer = @input.parent() $inputContainer = @input.parent()
$clearButton = $inputContainer.find('.js-dropdown-input-clear') $clearButton = $inputContainer.find('.js-dropdown-input-clear')
@indeterminateIds = []
# Clear click # Clear click
$clearButton.on 'click', (e) => $clearButton.on 'click', (e) =>
e.preventDefault() e.preventDefault()
@ -35,20 +37,20 @@ class GitLabDropdownFilter
if keyCode is 13 if keyCode is 13
return false return false
clearTimeout timeout # Only filter asynchronously only if option remote is set
timeout = setTimeout => if @options.remote
blur_field = @shouldBlur keyCode clearTimeout timeout
search_text = @input.val() timeout = setTimeout =>
blur_field = @shouldBlur keyCode
if blur_field and @filterInputBlur if blur_field and @filterInputBlur
@input.blur() @input.blur()
if @options.remote @options.query @input.val(), (data) =>
@options.query search_text, (data) =>
@options.callback(data) @options.callback(data)
else , 250
@filter search_text else
, 250 @filter @input.val()
shouldBlur: (keyCode) -> shouldBlur: (keyCode) ->
return BLUR_KEYCODES.indexOf(keyCode) >= 0 return BLUR_KEYCODES.indexOf(keyCode) >= 0
@ -142,6 +144,7 @@ class GitLabDropdown
LOADING_CLASS = "is-loading" LOADING_CLASS = "is-loading"
PAGE_TWO_CLASS = "is-page-two" PAGE_TWO_CLASS = "is-page-two"
ACTIVE_CLASS = "is-active" ACTIVE_CLASS = "is-active"
INDETERMINATE_CLASS = "is-indeterminate"
currentIndex = -1 currentIndex = -1
FILTER_INPUT = '.dropdown-input .dropdown-input-field' FILTER_INPUT = '.dropdown-input .dropdown-input-field'
@ -182,9 +185,6 @@ class GitLabDropdown
@fullData = data @fullData = data
@parseData @fullData @parseData @fullData
if @options.filterable
@filterInput.trigger 'keyup'
} }
# Init filterable # Init filterable
@ -211,6 +211,7 @@ class GitLabDropdown
@dropdown.on "shown.bs.dropdown", @opened @dropdown.on "shown.bs.dropdown", @opened
@dropdown.on "hidden.bs.dropdown", @hidden @dropdown.on "hidden.bs.dropdown", @hidden
$(@el).on "update.label", @updateLabel
@dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate
@dropdown.on 'keyup', (e) => @dropdown.on 'keyup', (e) =>
if e.which is 27 # Escape key if e.which is 27 # Escape key
@ -298,6 +299,13 @@ class GitLabDropdown
opened: => opened: =>
@addArrowKeyEvent() @addArrowKeyEvent()
if @options.setIndeterminateIds
@options.setIndeterminateIds.call(@)
# Makes indeterminate items effective
if @fullData and @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
@parseData @fullData
contentHtml = $('.dropdown-content', @dropdown).html() contentHtml = $('.dropdown-content', @dropdown).html()
if @remote && contentHtml is "" if @remote && contentHtml is ""
@remote.execute() @remote.execute()
@ -309,12 +317,18 @@ class GitLabDropdown
hidden: (e) => hidden: (e) =>
@removeArrayKeyEvent() @removeArrayKeyEvent()
$input = @dropdown.find(".dropdown-input-field")
if @options.filterable if @options.filterable
@dropdown $input
.find(".dropdown-input-field")
.blur() .blur()
.val("") .val("")
.trigger("keyup")
# 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 not @options.persistWhenHide
$input.trigger("keyup")
if @dropdown.find(".dropdown-toggle-page").length if @dropdown.find(".dropdown-toggle-page").length
$('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS $('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS
@ -358,7 +372,7 @@ class GitLabDropdown
if @options.renderRow if @options.renderRow
# Call the render function # Call the render function
html = @options.renderRow(data) html = @options.renderRow.call(@options, data, @)
else else
if not selected if not selected
value = if @options.id then @options.id(data) else data.id value = if @options.id then @options.id(data) else data.id
@ -440,9 +454,20 @@ class GitLabDropdown
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel @updateLabel()
else else
selectedObject selectedObject
else if el.hasClass(INDETERMINATE_CLASS)
el.addClass ACTIVE_CLASS
el.removeClass INDETERMINATE_CLASS
if not value?
field.remove()
if not field.length and fieldName
@addInput(fieldName, value)
return selectedObject
else else
if not @options.multiSelect or el.hasClass('dropdown-clear-active') if not @options.multiSelect or el.hasClass('dropdown-clear-active')
@dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
@ -456,34 +481,45 @@ class GitLabDropdown
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject, el) @updateLabel(selectedObject, el)
if value? if value?
if !field.length and fieldName if !field.length and fieldName
# Create hidden input for form @addInput(fieldName, value)
input = "<input type='hidden' name='#{fieldName}' value='#{value}' />"
if @options.inputId?
input = $(input)
.attr('id', @options.inputId)
@dropdown.before input
else else
field.val value field.val value
return selectedObject return selectedObject
selectRowAtIndex: (index) -> addInput: (fieldName, value)->
selector = ".dropdown-content li:not(.divider):eq(#{index}) a" # Create hidden input for form
$input = $('<input>').attr('type', 'hidden')
.attr('name', fieldName)
.val(value)
if @options.inputId?
$input.attr('id', @options.inputId)
@dropdown.before $input
selectRowAtIndex: (e, index) ->
selector = ".dropdown-content li:not(.divider,.dropdown-header,.separator):eq(#{index}) a"
if @dropdown.find(".dropdown-toggle-page").length if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one #{selector}" selector = ".dropdown-page-one #{selector}"
# simulate a click on the first link # simulate a click on the first link
$(selector, @dropdown).trigger "click" $el = $(selector, @dropdown)
if $el.length
e.preventDefault()
e.stopImmediatePropagation()
$(selector, @dropdown)[0].click()
addArrowKeyEvent: -> addArrowKeyEvent: ->
ARROW_KEY_CODES = [38, 40] ARROW_KEY_CODES = [38, 40]
$input = @dropdown.find(".dropdown-input-field") $input = @dropdown.find(".dropdown-input-field")
selector = '.dropdown-content li:not(.divider)' selector = '.dropdown-content li:not(.divider,.dropdown-header,.separator)'
if @dropdown.find(".dropdown-toggle-page").length if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one #{selector}" selector = ".dropdown-page-one #{selector}"
@ -511,8 +547,8 @@ class GitLabDropdown
return false return false
if currentKeyCode is 13 if currentKeyCode is 13 and currentIndex isnt -1
@selectRowAtIndex if currentIndex < 0 then 0 else currentIndex @selectRowAtIndex e, currentIndex
removeArrayKeyEvent: -> removeArrayKeyEvent: ->
$('body').off 'keydown' $('body').off 'keydown'
@ -544,6 +580,9 @@ class GitLabDropdown
# Scroll the dropdown content up # Scroll the dropdown content up
$dropdownContent.scrollTop(listItemTop - dropdownContentTop) $dropdownContent.scrollTop(listItemTop - dropdownContentTop)
updateLabel: (selected = null, el = null) =>
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el)
$.fn.glDropdown = (opts) -> $.fn.glDropdown = (opts) ->
return @.each -> return @.each ->
if (!$.data @, 'glDropdown') if (!$.data @, 'glDropdown')

View file

@ -0,0 +1,8 @@
# 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 Chart
#= require_tree .

View file

@ -1,5 +1,4 @@
#= require d3 #= require d3
#= require stat_graph_contributors_util
class @ContributorsStatGraph class @ContributorsStatGraph
init: (log) -> init: (log) ->

View file

@ -1,6 +1,4 @@
#= require d3 #= require d3
#= require jquery
#= require underscore
class @ContributorsGraph class @ContributorsGraph
MARGIN: MARGIN:

View file

@ -1,13 +1,23 @@
issuable_created = false
@Issuable = @Issuable =
init: -> init: ->
Issuable.initTemplates() unless issuable_created
Issuable.initSearch() issuable_created = true
Issuable.initTemplates()
Issuable.initSearch()
Issuable.initChecks()
Issuable.initLabelFilterRemove()
initTemplates: -> initTemplates: ->
Issuable.labelRow = _.template( Issuable.labelRow = _.template(
'<% _.each(labels, function(label){ %> '<% _.each(labels, function(label){ %>
<span class="label-row"> <span class="label-row btn-group" role="group" aria-label="<%= _.escape(label.title) %>" style="color: <%= label.text_color %>;">
<a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%= label.color %>;" title="<%= _.escape(label.description) %>" data-container="body">
<%= _.escape(label.title) %>
</a>
<button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%= label.color %>;" data-label="<%= _.escape(label.title) %>">
<i class="fa fa-times"></i>
</button>
</span> </span>
<% }); %>' <% }); %>'
) )
@ -19,15 +29,32 @@
.on 'keyup', -> .on 'keyup', ->
clearTimeout(@timer) clearTimeout(@timer)
@timer = setTimeout( -> @timer = setTimeout( ->
Issuable.filterResults $('#issue_search_form') $search = $('#issue_search')
$form = $('.js-filter-form')
$input = $("input[name='#{$search.attr('name')}']", $form)
if $input.length is 0
$form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>"
else
$input.val $search.val()
Issuable.filterResults $form
, 500) , 500)
toggleLabelFilters: -> initLabelFilterRemove: ->
$filteredLabels = $('.filtered-labels') $(document)
if $filteredLabels.find('.label-row').length > 0 .off 'click', '.js-label-filter-remove'
$filteredLabels.removeClass('hidden') .on 'click', '.js-label-filter-remove', (e) ->
else $button = $(@)
$filteredLabels.addClass('hidden')
# Remove the label input box
$('input[name="label_name[]"]')
.filter -> @value is $button.data('label')
.remove()
# Submit the form to get new data
Issuable.filterResults $('.filter-form')
$('.js-label-select').trigger('update.label')
filterResults: (form) => filterResults: (form) =>
formData = form.serialize() formData = form.serialize()
@ -37,48 +64,27 @@
issuesUrl = formAction issuesUrl = formAction
issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}") issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}")
issuesUrl += formData issuesUrl += formData
$.ajax
type: 'GET'
url: formAction
data: formData
complete: ->
$('.issues-holder, .merge-requests-holder').css('opacity', '1.0')
success: (data) ->
$('.issues-holder, .merge-requests-holder').html(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: issuesUrl}, document.title, issuesUrl
Issuable.reload()
Issuable.updateStateFilters()
$filteredLabels = $('.filtered-labels')
if typeof Issuable.labelRow is 'function' Turbolinks.visit(issuesUrl);
$filteredLabels.html(Issuable.labelRow(data))
Issuable.toggleLabelFilters() initChecks: ->
$('.check_all_issues').off('click').on('click', ->
$('.selected_issue').prop('checked', @checked)
Issuable.checkChanged()
)
dataType: "json" $('.selected_issue').off('change').on('change', Issuable.checkChanged)
reload: -> checkChanged: ->
if Issues.created checked_issues = $('.selected_issue:checked')
Issues.initChecks() if checked_issues.length > 0
ids = $.map checked_issues, (value) ->
$(value).data('id')
$('#filter_issue_search').val($('#issue_search').val()) $('#update_issues_ids').val ids
$('.issues-other-filters').hide()
updateStateFilters: -> $('.issues_bulk_update').show()
stateFilters = $('.issues-state-filters') else
newParams = {} $('#update_issues_ids').val []
paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search'] $('.issues_bulk_update').hide()
$('.issues-other-filters').show()
for paramKey in paramKeys
newParams[paramKey] = gl.utils.getParameterValues(paramKey)[0] or ''
if stateFilters.length
stateFilters.find('a').each ->
initialUrl = gl.utils.removeParamQueryString($(this).attr('href'), 'label_name[]')
labelNameValues = gl.utils.getParameterValues('label_name[]')
if labelNameValues
labelNameQueryString = ("label_name[]=#{value}" for value in labelNameValues).join('&')
newUrl = "#{gl.utils.mergeUrlParams(newParams, initialUrl)}&#{labelNameQueryString}"
else
newUrl = gl.utils.mergeUrlParams(newParams, initialUrl)
$(this).attr 'href', newUrl

View file

@ -19,6 +19,16 @@ class @IssuableForm
@form.on "click", ".btn-cancel", @resetAutosave @form.on "click", ".btn-cancel", @resetAutosave
@initWip() @initWip()
@initMoveDropdown()
$issuableDueDate = $('#issuable-due-date')
if $issuableDueDate.length
$('.datepicker').datepicker(
dateFormat: 'yy-mm-dd',
onSelect: (dateText, inst) ->
$issuableDueDate.val dateText
).datepicker 'setDate', $.datepicker.parseDate('yy-mm-dd', $issuableDueDate.val())
initAutosave: -> initAutosave: ->
new Autosave @titleField, [ new Autosave @titleField, [
@ -80,3 +90,23 @@ class @IssuableForm
addWip: -> addWip: ->
@titleField.val "WIP: #{@titleField.val()}" @titleField.val "WIP: #{@titleField.val()}"
initMoveDropdown: ->
$moveDropdown = $('.js-move-dropdown')
if $moveDropdown.length
$('.js-move-dropdown').select2
ajax:
url: $moveDropdown.data('projects-url')
results: (data) ->
return {
results: data
}
data: (query) ->
{
search: query
}
formatResult: (project) ->
project.name_with_namespace
formatSelection: (project) ->
project.name_with_namespace

View file

@ -0,0 +1,121 @@
class @IssuableBulkActions
constructor: (opts = {}) ->
# Set defaults
{
@container = $('.content')
@form = @getElement('.bulk-update')
@issues = @getElement('.issues-list .issue')
} = opts
@bindEvents()
# Fixes bulk-assign not working when navigating through pages
Issuable.initChecks();
getElement: (selector) ->
@container.find selector
bindEvents: ->
@form.off('submit').on('submit', @onFormSubmit.bind(@))
onFormSubmit: (e) ->
e.preventDefault()
@submit()
submit: ->
_this = @
xhr = $.ajax
url: @form.attr 'action'
method: @form.attr 'method'
dataType: 'JSON',
data: @getFormDataAsObject()
xhr.done (response, status, xhr) ->
location.reload()
xhr.fail ->
new Flash("Issue update failed")
xhr.always @onFormSubmitAlways.bind(@)
onFormSubmitAlways: ->
@form.find('[type="submit"]').enable()
getSelectedIssues: ->
@issues.has('.selected_issue:checked')
getLabelsFromSelection: ->
labels = []
@getSelectedIssues().map ->
_labels = $(@).data('labels')
if _labels
_labels.map (labelId) ->
labels.push(labelId) if labels.indexOf(labelId) is -1
labels
###*
* Will return only labels that were marked previously and the user has unmarked
* @return {Array} Label IDs
###
getUnmarkedIndeterminedLabels: ->
result = []
labelsToKeep = []
for el in @getElement('.labels-filter .is-indeterminate')
labelsToKeep.push $(el).data('labelId')
for id in @getLabelsFromSelection()
# Only the ones that we are not going to keep
result.push(id) if labelsToKeep.indexOf(id) is -1
result
###*
* Simple form serialization, it will return just what we need
* Returns key/value pairs from form data
###
getFormDataAsObject: ->
formData =
update:
state_event : @form.find('input[name="update[state_event]"]').val()
assignee_id : @form.find('input[name="update[assignee_id]"]').val()
milestone_id : @form.find('input[name="update[milestone_id]"]').val()
issues_ids : @form.find('input[name="update[issues_ids]"]').val()
add_label_ids : []
remove_label_ids : []
@getLabelsToApply().map (id) ->
formData.update.add_label_ids.push id
@getLabelsToRemove().map (id) ->
formData.update.remove_label_ids.push id
formData
getLabelsToApply: ->
labelIds = []
$labels = @form.find('.labels-filter input[name="update[label_ids][]"]')
$labels.each (k, label) ->
labelIds.push parseInt($(label).val()) if label
labelIds
###*
* Returns Label IDs that will be removed from issue selection
* @return {Array} Array of labels IDs
###
getLabelsToRemove: ->
result = []
indeterminatedLabels = @getUnmarkedIndeterminedLabels()
labelsToApply = @getLabelsToApply()
indeterminatedLabels.map (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
result.push(id) if labelsToApply.indexOf(id) is -1
result

View file

@ -1,38 +0,0 @@
@Issues =
init: ->
Issues.created = true
Issues.initChecks()
$("body").on "ajax:success", ".close_issue, .reopen_issue", ->
t = $(this)
totalIssues = undefined
reopen = t.hasClass("reopen_issue")
$(".issue_counter").each ->
issue = $(this)
totalIssues = parseInt($(this).html(), 10)
if reopen and issue.closest(".main_menu").length
$(this).html totalIssues + 1
else
$(this).html totalIssues - 1
initChecks: ->
$(".check_all_issues").click ->
$(".selected_issue").prop("checked", @checked)
Issues.checkChanged()
$(".selected_issue").bind "change", Issues.checkChanged
checkChanged: ->
checked_issues = $(".selected_issue:checked")
if checked_issues.length > 0
ids = []
$.each checked_issues, (index, value) ->
ids.push $(value).attr("data-id")
$("#update_issues_ids").val ids
$(".issues-other-filters").hide()
$(".issues_bulk_update").show()
else
$("#update_issues_ids").val []
$(".issues_bulk_update").hide()
$(".issues-other-filters").show()

View file

@ -1,5 +1,7 @@
class @LabelsSelect class @LabelsSelect
constructor: -> constructor: ->
_this = @
$('.js-label-select').each (i, dropdown) -> $('.js-label-select').each (i, dropdown) ->
$dropdown = $(dropdown) $dropdown = $(dropdown)
projectId = $dropdown.data('project-id') projectId = $dropdown.data('project-id')
@ -93,8 +95,11 @@ class @LabelsSelect
$newLabelCreateButton.enable() $newLabelCreateButton.enable()
if label.message? if label.message?
errors = _.map label.message, (value, key) ->
"#{key} #{value[0]}"
$newLabelError $newLabelError
.text label.message .html errors.join("<br/>")
.show() .show()
else else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click' $('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
@ -196,10 +201,18 @@ class @LabelsSelect
callback data callback data
renderRow: (label) -> renderRow: (label, instance) ->
removesAll = label.id is 0 or not label.id? $li = $('<li>')
$a = $('<a href="#">')
selectedClass = [] selectedClass = []
removesAll = label.id is 0 or not label.id?
if $dropdown.hasClass('js-filter-bulk-update')
indeterminate = instance.indeterminateIds
if indeterminate.indexOf(label.id) isnt -1
selectedClass.push 'is-indeterminate'
if $form.find("input[type='hidden']\ if $form.find("input[type='hidden']\
[name='#{$dropdown.data('fieldName')}']\ [name='#{$dropdown.data('fieldName')}']\
[value='#{this.id(label)}']").length [value='#{this.id(label)}']").length
@ -230,17 +243,21 @@ class @LabelsSelect
else else
colorEl = '' colorEl = ''
"<li> # We need to identify which items are actually labels
<a href='#' class='#{selectedClass.join(' ')}'> if label.id
#{colorEl} selectedClass.push('label-item')
#{_.escape(label.title)} $a.attr('data-label-id', label.id)
</a>
</li>" $a.addClass(selectedClass.join(' '))
filterable: true .html("#{colorEl} #{_.escape(label.title)}")
# Return generated html
$li.html($a).prop('outerHTML')
persistWhenHide: $dropdown.data('persistWhenHide')
search: search:
fields: ['title'] fields: ['title']
selectable: true selectable: true
filterable: true
toggleLabel: (selected, el) -> toggleLabel: (selected, el) ->
selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active') selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
@ -280,10 +297,19 @@ class @LabelsSelect
else if $dropdown.hasClass('js-filter-submit') else if $dropdown.hasClass('js-filter-submit')
$dropdown.closest('form').submit() $dropdown.closest('form').submit()
else else
saveLabelData() if not $dropdown.hasClass 'js-filter-bulk-update'
saveLabelData()
if $dropdown.hasClass('js-filter-bulk-update')
# If we are persisting state we need the classes
if not @options.persistWhenHide
$dropdown.parent().find('.is-active, .is-indeterminate').removeClass()
multiSelect: $dropdown.hasClass 'js-multiselect' multiSelect: $dropdown.hasClass 'js-multiselect'
clicked: (label) -> clicked: (label) ->
if $dropdown.hasClass('js-filter-bulk-update')
return
page = $('body').data 'page' page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index' isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is 'projects:merge_requests:index' isMRIndex = page is 'projects:merge_requests:index'
@ -298,4 +324,31 @@ class @LabelsSelect
return return
else else
saveLabelData() saveLabelData()
setIndeterminateIds: ->
if @dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')
@indeterminateIds = _this.getIndeterminateIds()
) )
@bindEvents()
bindEvents: ->
$('body').on 'change', '.selected_issue', @onSelectCheckboxIssue
onSelectCheckboxIssue: ->
return if $('.selected_issue:checked').length
# Remove inputs
$('.issues_bulk_update .labels-filter input[type="hidden"]').remove()
# Also restore button text
$('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label')
getIndeterminateIds: ->
label_ids = []
$('.selected_issue:checked').each (i, el) ->
issue_id = $(el).data('id')
label_ids.push $("#issue_#{issue_id}").data('labels')
_.flatten(label_ids)

View file

@ -0,0 +1,25 @@
hideEndFade = ($scrollingTabs) ->
$scrollingTabs.each ->
$this = $(@)
$this
.find('.fade-right')
.toggleClass('end-scroll', $this.width() is $this.prop('scrollWidth'))
$ ->
$('.fade-left').addClass('end-scroll')
hideEndFade($('.scrolling-tabs'))
$(window)
.off 'resize.nav'
.on 'resize.nav', ->
hideEndFade($('.scrolling-tabs'))
$('.scrolling-tabs').on 'scroll', (event) ->
$this = $(this)
currentPosition = $this.scrollLeft()
maxPosition = $this.prop('scrollWidth') - $this.outerWidth()
$this.find('.fade-left').toggleClass('end-scroll', currentPosition is 0)
$this.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition)

View file

@ -0,0 +1,43 @@
((w) ->
window.gl or= {}
window.gl.utils or= {}
jQuery.timefor = (time, suffix, expiredLabel) ->
return '' unless time
suffix or= 'remaining'
expiredLabel or= 'Past due'
jQuery.timeago.settings.allowFuture = yes
{ suffixFromNow } = jQuery.timeago.settings.strings
jQuery.timeago.settings.strings.suffixFromNow = suffix
timefor = $.timeago time
if timefor.indexOf('ago') > -1
timefor = expiredLabel
jQuery.timeago.settings.strings.suffixFromNow = suffixFromNow
return timefor
gl.utils.updateTooltipTitle = ($tooltipEl, newTitle) ->
$tooltipEl
.tooltip 'destroy'
.attr 'title', newTitle
.tooltip 'fixTitle'
gl.utils.preventDisabledButtons = ->
$('.btn').click (e) ->
if $(this).hasClass 'disabled'
e.preventDefault()
e.stopImmediatePropagation()
return false
) window

View file

@ -12,6 +12,13 @@
$el.attr('title', gl.utils.formatDate($el.attr('datetime'))) $el.attr('title', gl.utils.formatDate($el.attr('datetime')))
) )
$timeagoEls.timeago() if setTimeago if setTimeago
$timeagoEls.timeago()
$timeagoEls.tooltip('destroy')
# Recreate with custom template
$timeagoEls.tooltip(
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
)
) window ) window

View file

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

View file

@ -26,10 +26,19 @@
newUrl = decodeURIComponent(url) newUrl = decodeURIComponent(url)
for paramName, paramValue of params for paramName, paramValue of params
pattern = new RegExp "\\b(#{paramName}=).*?(&|$)" pattern = new RegExp "\\b(#{paramName}=).*?(&|$)"
if url.search(pattern) >= 0 if not paramValue?
newUrl = newUrl.replace pattern, ''
else if url.search(pattern) isnt -1
newUrl = newUrl.replace pattern, "$1#{paramValue}$2" newUrl = newUrl.replace pattern, "$1#{paramValue}$2"
else else
newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}" newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}"
# Remove a trailing ampersand
lastChar = newUrl[newUrl.length - 1]
if lastChar is '&'
newUrl = newUrl.slice 0, -1
newUrl newUrl
# removes parameter query string from url. returns the modified url # removes parameter query string from url. returns the modified url

View file

@ -42,9 +42,3 @@ work = ->
$(document).on('page:fetch', start) $(document).on('page:fetch', start)
$(document).on('page:change', stop) $(document).on('page:change', stop)
$ ->
# Make logo clickable as part of a workaround for Safari visited
# link behaviour (See !2690).
$('#logo').on 'click', ->
$('#js-shortcuts-home').get(0).click()

View file

@ -75,6 +75,9 @@ class @MergeRequestTabs
@loadDiff($target.attr('href')) @loadDiff($target.attr('href'))
if bp? and bp.getBreakpointSize() isnt 'lg' if bp? and bp.getBreakpointSize() isnt 'lg'
@shrinkView() @shrinkView()
navBarHeight = $('.navbar-gitlab').outerHeight()
$.scrollTo(".merge-request-details .merge-request-tabs", offset: -navBarHeight)
else if action == 'builds' else if action == 'builds'
@loadBuilds($target.attr('href')) @loadBuilds($target.attr('href'))
@expandView() @expandView()

View file

@ -10,6 +10,7 @@ class @MergeRequestWidget
$('#modal_merge_info').modal(show: false) $('#modal_merge_info').modal(show: false)
@firstCICheck = true @firstCICheck = true
@readyForCICheck = false @readyForCICheck = false
@cancel = false
clearInterval @fetchBuildStatusInterval clearInterval @fetchBuildStatusInterval
@clearEventListeners() @clearEventListeners()
@ -21,10 +22,16 @@ class @MergeRequestWidget
clearEventListeners: -> clearEventListeners: ->
$(document).off 'page:change.merge_request' $(document).off 'page:change.merge_request'
cancelPolling: ->
@cancel = true
addEventListeners: -> addEventListeners: ->
allowedPages = ['show', 'commits', 'builds', 'changes']
$(document).on 'page:change.merge_request', => $(document).on 'page:change.merge_request', =>
if $('body').data('page') isnt 'projects:merge_requests:show' page = $('body').data('page').split(':').last()
if allowedPages.indexOf(page) < 0
clearInterval @fetchBuildStatusInterval clearInterval @fetchBuildStatusInterval
@cancelPolling()
@clearEventListeners() @clearEventListeners()
mergeInProgress: (deleteSourceBranch = false)-> mergeInProgress: (deleteSourceBranch = false)->
@ -67,6 +74,7 @@ class @MergeRequestWidget
$('.ci-widget-fetching').show() $('.ci-widget-fetching').show()
$.getJSON @opts.ci_status_url, (data) => $.getJSON @opts.ci_status_url, (data) =>
return if @cancel
@readyForCICheck = true @readyForCICheck = true
if data.status is '' if data.status is ''
@ -106,6 +114,7 @@ class @MergeRequestWidget
@firstCICheck = false @firstCICheck = false
showCIStatus: (state) -> showCIStatus: (state) ->
return if not state?
$('.ci_widget').hide() $('.ci_widget').hide()
allowed_states = ["failed", "canceled", "running", "pending", "success", "skipped", "not_found"] allowed_states = ["failed", "canceled", "running", "pending", "success", "skipped", "not_found"]
if state in allowed_states if state in allowed_states
@ -126,6 +135,6 @@ class @MergeRequestWidget
$('.ci_widget:visible .ci-coverage').text(text) $('.ci_widget:visible .ci-coverage').text(text)
setMergeButtonClass: (css_class) -> setMergeButtonClass: (css_class) ->
$('.js-merge-button') $('.js-merge-button,.accept-action .dropdown-toggle')
.removeClass('btn-danger btn-warning btn-create') .removeClass('btn-danger btn-warning btn-create')
.addClass(css_class) .addClass(css_class)

View file

@ -0,0 +1,30 @@
class @MergedButtons
constructor: ->
@$removeBranchWidget = $('.remove_source_branch_widget')
@$removeBranchProgress = $('.remove_source_branch_in_progress')
@$removeBranchFailed = $('.remove_source_branch_widget.failed')
@cleanEventListeners()
@initEventListeners()
cleanEventListeners: ->
$(document).off 'click', '.remove_source_branch'
$(document).off 'ajax:success', '.remove_source_branch'
$(document).off 'ajax:error', '.remove_source_branch'
initEventListeners: ->
$(document).on 'click', '.remove_source_branch', @removeSourceBranch
$(document).on 'ajax:success', '.remove_source_branch', @removeBranchSuccess
$(document).on 'ajax:error', '.remove_source_branch', @removeBranchError
removeSourceBranch: =>
@$removeBranchWidget.hide()
@$removeBranchProgress.show()
removeBranchSuccess: ->
location.reload()
removeBranchError: ->
@$removeBranchWidget.hide()
@$removeBranchProgress.hide()
@$removeBranchFailed.show()

View file

@ -24,11 +24,21 @@ class @MilestoneSelect
if issueUpdateURL if issueUpdateURL
milestoneLinkTemplate = _.template( milestoneLinkTemplate = _.template(
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= _.escape(title) %></a>' '<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>">
<span class="has-tooltip" data-container="body" title="<%= remaining %>">
<%= _.escape(title) %>
</span>
</a>'
) )
milestoneLinkNoneTemplate = '<div class="light">None</div>' milestoneLinkNoneTemplate = '<div class="light">None</div>'
collapsedSidebarLabelTemplate = _.template(
'<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left">
<%= _.escape(title) %>
</span>'
)
$dropdown.glDropdown( $dropdown.glDropdown(
data: (term, callback) -> data: (term, callback) ->
$.ajax( $.ajax(
@ -83,7 +93,7 @@ class @MilestoneSelect
$selectbox.hide() $selectbox.hide()
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.removeAttr('style') $value.css('display', '')
clicked: (selected) -> clicked: (selected) ->
page = $('body').data 'page' page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index' isIssueIndex = page is 'projects:issues:index'
@ -106,7 +116,7 @@ class @MilestoneSelect
.val() .val()
data = {} data = {}
data[abilityName] = {} data[abilityName] = {}
data[abilityName].milestone_id = selected data[abilityName].milestone_id = if selected? then selected else null
$loading $loading
.fadeIn() .fadeIn()
$dropdown.trigger('loading.gl.dropdown') $dropdown.trigger('loading.gl.dropdown')
@ -118,12 +128,13 @@ class @MilestoneSelect
$dropdown.trigger('loaded.gl.dropdown') $dropdown.trigger('loaded.gl.dropdown')
$loading.fadeOut() $loading.fadeOut()
$selectbox.hide() $selectbox.hide()
$value.removeAttr('style') $value.css('display', '')
if data.milestone? if data.milestone?
data.milestone.namespace = _this.currentProject.namespace data.milestone.namespace = _this.currentProject.namespace
data.milestone.path = _this.currentProject.path data.milestone.path = _this.currentProject.path
data.milestone.remaining = $.timefor data.milestone.due_date
$value.html(milestoneLinkTemplate(data.milestone)) $value.html(milestoneLinkTemplate(data.milestone))
$sidebarCollapsedValue.find('span').text(data.milestone.title) $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone))
else else
$value.html(milestoneLinkNoneTemplate) $value.html(milestoneLinkNoneTemplate)
$sidebarCollapsedValue.find('span').text('No') $sidebarCollapsedValue.find('span').text('No')

View file

@ -114,13 +114,15 @@ class @Notes
@refresh() @refresh()
, @pollingInterval , @pollingInterval
refresh: -> refresh: =>
return if @refreshing is true
refreshing = true
if not document.hidden and document.URL.indexOf(@noteable_url) is 0 if not document.hidden and document.URL.indexOf(@noteable_url) is 0
@getContent() @getContent()
getContent: -> getContent: ->
return if @refreshing
@refreshing = true
$.ajax $.ajax
url: @notes_url url: @notes_url
data: "last_fetched_at=" + @last_fetched_at data: "last_fetched_at=" + @last_fetched_at
@ -134,8 +136,8 @@ class @Notes
@renderDiscussionNote(note) @renderDiscussionNote(note)
else else
@renderNote(note) @renderNote(note)
always: => .always () =>
@refreshing = false @refreshing = false
### ###
Increase @pollingInterval up to 120 seconds on every function call, Increase @pollingInterval up to 120 seconds on every function call,
@ -162,13 +164,14 @@ class @Notes
renderNote: (note) -> renderNote: (note) ->
unless note.valid unless note.valid
if note.award if note.award
flash = new Flash('You have already used this award emoji!', 'alert') flash = new Flash('You have already awarded this emoji!', 'alert')
flash.pinTo('.header-content') flash.pinTo('.header-content')
return return
if note.award if note.award
awardsHandler.addAwardToEmojiBar(note.note) votesBlock = $('.js-awards-block').eq 0
awardsHandler.scrollToAwards() gl.awardsHandler.addAwardToEmojiBar votesBlock, note.name
gl.awardsHandler.scrollToAwards()
# render note if it not present in loaded list # render note if it not present in loaded list
# or skip if rendered # or skip if rendered
@ -329,7 +332,7 @@ class @Notes
@renderDiscussionNote(note) @renderDiscussionNote(note)
# cleanup after successfully creating a diff/discussion note # cleanup after successfully creating a diff/discussion note
@removeDiscussionNoteForm($("#new-discussion-note-form-#{note.discussion_id}")) @removeDiscussionNoteForm($(xhr.target))
### ###
Called in response to the edit note form being submitted Called in response to the edit note form being submitted
@ -353,8 +356,7 @@ class @Notes
Called in response to clicking the edit note link Called in response to clicking the edit note link
Replaces the note text with the note edit form Replaces the note text with the note edit form
Adds a hidden div with the original content of the note to fill the edit note form with Adds a data attribute to the form with the original content of the note for cancellations
if the user cancels
### ###
showEditForm: (e, scrollTo, myLastNote) -> showEditForm: (e, scrollTo, myLastNote) ->
e.preventDefault() e.preventDefault()
@ -370,6 +372,8 @@ class @Notes
done = ($noteText) -> done = ($noteText) ->
# Neat little trick to put the cursor at the end # Neat little trick to put the cursor at the end
noteTextVal = $noteText.val() 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
$noteText.val('').val(noteTextVal); $noteText.val('').val(noteTextVal);
new GLForm form new GLForm form
@ -392,14 +396,16 @@ class @Notes
### ###
Called in response to clicking the edit note link Called in response to clicking the edit note link
Hides edit form Hides edit form and restores the original note text to the editor textarea.
### ###
cancelEdit: (e) -> cancelEdit: (e) ->
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
form = note.find(".current-note-edit-form")
note.removeClass "is-editting" note.removeClass "is-editting"
note.find(".current-note-edit-form") form.removeClass("current-note-edit-form")
.removeClass("current-note-edit-form") # Replace markdown textarea text with original note text.
form.find(".js-note-text").val(form.find('form.edit-note').data('original-note'))
### ###
Called in response to deleting a note of any kind. Called in response to deleting a note of any kind.

View file

@ -1,5 +1,5 @@
@Pager = @Pager =
init: (@limit = 0, preload, @disable = false) -> init: (@limit = 0, preload, @disable = false, @callback = $.noop) ->
@loading = $('.loading').first() @loading = $('.loading').first()
if preload if preload
@ -19,6 +19,7 @@
@loading.hide() @loading.hide()
success: (data) -> success: (data) ->
Pager.append(data.count, data.html) Pager.append(data.count, data.html)
Pager.callback()
dataType: "json" dataType: "json"
append: (count, html) -> append: (count, html) ->

View file

@ -7,12 +7,17 @@ class @ProjectNew
@toggleSettingsOnclick() @toggleSettingsOnclick()
toggleSettings: -> toggleSettings: =>
checked = $("#project_builds_enabled").prop("checked") @_showOrHide('#project_builds_enabled', '.builds-feature')
if checked @_showOrHide('#project_merge_requests_enabled', '.merge-requests-feature')
$('.builds-feature').show()
else
$('.builds-feature').hide()
toggleSettingsOnclick: -> toggleSettingsOnclick: ->
$("#project_builds_enabled").on 'click', @toggleSettings $('#project_builds_enabled, #project_merge_requests_enabled').on 'click', @toggleSettings
_showOrHide: (checkElement, container) ->
$container = $(container)
if $(checkElement).prop('checked')
$container.show()
else
$container.hide()

View file

@ -10,6 +10,89 @@ class @Sidebar
$('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading) $('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading)
$('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded) $('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded)
$(document)
.off 'click', '.js-sidebar-toggle'
.on 'click', '.js-sidebar-toggle', (e, triggered) ->
e.preventDefault()
$this = $(this)
$thisIcon = $this.find 'i'
$allGutterToggleIcons = $('.js-sidebar-toggle i')
if $thisIcon.hasClass('fa-angle-double-right')
$allGutterToggleIcons
.removeClass('fa-angle-double-right')
.addClass('fa-angle-double-left')
$('aside.right-sidebar')
.removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed')
$('.page-with-sidebar')
.removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed')
else
$allGutterToggleIcons
.removeClass('fa-angle-double-left')
.addClass('fa-angle-double-right')
$('aside.right-sidebar')
.removeClass('right-sidebar-collapsed')
.addClass('right-sidebar-expanded')
$('.page-with-sidebar')
.removeClass('right-sidebar-collapsed')
.addClass('right-sidebar-expanded')
if not triggered
$.cookie("collapsed_gutter",
$('.right-sidebar')
.hasClass('right-sidebar-collapsed'), { path: '/' })
$(document)
.off 'click', '.js-issuable-todo'
.on 'click', '.js-issuable-todo', @toggleTodo
toggleTodo: (e) =>
$this = $(e.currentTarget)
$todoLoading = $('.js-issuable-todo-loading')
$btnText = $('.js-issuable-todo-text', $this)
ajaxType = if $this.attr('data-id') then 'PATCH' else 'POST'
ajaxUrlExtra = if $this.attr('data-id') then "/#{$this.attr('data-id')}" else ''
$.ajax(
url: "#{$this.data('url')}#{ajaxUrlExtra}"
type: ajaxType
dataType: 'json'
data:
issuable_id: $this.data('issuable')
issuable_type: $this.data('issuable-type')
beforeSend: =>
@beforeTodoSend($this, $todoLoading)
).done (data) =>
@todoUpdateDone(data, $this, $btnText, $todoLoading)
beforeTodoSend: ($btn, $todoLoading) ->
$btn.disable()
$todoLoading.removeClass 'hidden'
todoUpdateDone: (data, $btn, $btnText, $todoLoading) ->
$todoPendingCount = $('.todos-pending-count')
$todoPendingCount.text data.count
$btn.enable()
$todoLoading.addClass 'hidden'
if data.count is 0
$todoPendingCount.addClass 'hidden'
else
$todoPendingCount.removeClass 'hidden'
if data.todo?
$btn
.attr 'aria-label', $btn.data('mark-text')
.attr 'data-id', data.todo.id
$btnText.text $btn.data('mark-text')
else
$btn
.attr 'aria-label', $btn.data('todo-text')
.removeAttr 'data-id'
$btnText.text $btn.data('todo-text')
sidebarDropdownLoading: (e) -> sidebarDropdownLoading: (e) ->
$sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon') $sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon')
img = $sidebarCollapsedIcon.find('img') img = $sidebarCollapsedIcon.find('img')
@ -76,12 +159,10 @@ class @Sidebar
@triggerOpenSidebar() if not @isOpen() @triggerOpenSidebar() if not @isOpen()
if action is 'hide' if action is 'hide'
@triggerOpenSidebar() is @isOpen() @triggerOpenSidebar() if @isOpen()
isOpen: -> isOpen: ->
@sidebar.is('.right-sidebar-expanded') @sidebar.is('.right-sidebar-expanded')
getBlock: (name) -> getBlock: (name) ->
@sidebar.find(".block.#{name}") @sidebar.find(".block.#{name}")

View file

@ -20,8 +20,7 @@ class @SearchAutocomplete
@dropdown = @wrap.find('.dropdown') @dropdown = @wrap.find('.dropdown')
@dropdownContent = @dropdown.find('.dropdown-content') @dropdownContent = @dropdown.find('.dropdown-content')
@locationBadgeEl = @getElement('.search-location-badge') @locationBadgeEl = @getElement('.location-badge')
@locationText = @getElement('.location-text')
@scopeInputEl = @getElement('#scope') @scopeInputEl = @getElement('#scope')
@searchInput = @getElement('.search-input') @searchInput = @getElement('.search-input')
@projectInputEl = @getElement('#search_project_id') @projectInputEl = @getElement('#search_project_id')
@ -133,7 +132,7 @@ class @SearchAutocomplete
scope: @scopeInputEl.val() scope: @scopeInputEl.val()
# Location badge # Location badge
_location: @locationText.text() _location: @locationBadgeEl.text()
} }
bindEvents: -> bindEvents: ->
@ -143,23 +142,28 @@ class @SearchAutocomplete
@searchInput.on 'click', @onSearchInputClick @searchInput.on 'click', @onSearchInputClick
@searchInput.on 'focus', @onSearchInputFocus @searchInput.on 'focus', @onSearchInputFocus
@clearInput.on 'click', @onClearInputClick @clearInput.on 'click', @onClearInputClick
@locationBadgeEl.on 'click', =>
@searchInput.focus()
onDocumentClick: (e) => onDocumentClick: (e) =>
# If clicking outside the search box # If clicking outside the search box
# And search input is not focused # And search input is not focused
# And we are not clicking inside a suggestion # And we are not clicking inside a suggestion
if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).parents('ul').length if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).closest('.search-form').length
@onSearchInputBlur() @onSearchInputBlur()
enableAutocomplete: -> enableAutocomplete: ->
# No need to enable anything if user is not logged in # No need to enable anything if user is not logged in
return if !gon.current_user_id return if !gon.current_user_id
_this = @ unless @dropdown.hasClass('open')
@loadingSuggestions = false _this = @
@loadingSuggestions = false
@dropdown.addClass('open') @dropdown
@searchInput.removeClass('disabled') .addClass('open')
.trigger('shown.bs.dropdown')
@searchInput.removeClass('disabled')
onSearchInputKeyDown: => onSearchInputKeyDown: =>
# Saves last length of the entered text # Saves last length of the entered text
@ -190,7 +194,7 @@ class @SearchAutocomplete
@disableAutocomplete() @disableAutocomplete()
else else
# We should display the menu only when input is not empty # We should display the menu only when input is not empty
@enableAutocomplete() @enableAutocomplete() if e.keyCode isnt KEYCODE.ENTER
@wrap.toggleClass 'has-value', !!e.target.value @wrap.toggleClass 'has-value', !!e.target.value
@ -221,10 +225,8 @@ class @SearchAutocomplete
category = if item.category? then "#{item.category}: " else '' category = if item.category? then "#{item.category}: " else ''
value = if item.value? then item.value else '' value = if item.value? then item.value else ''
html = "<span class='location-badge'> badgeText = "#{category}#{value}"
<i class='location-text'>#{category}#{value}</i> @locationBadgeEl.text(badgeText).show()
</span>"
@locationBadgeEl.html(html)
@wrap.addClass('has-location-badge') @wrap.addClass('has-location-badge')
restoreOriginalState: -> restoreOriginalState: ->
@ -233,9 +235,8 @@ class @SearchAutocomplete
for input in inputs for input in inputs
@getElement("##{input}").val(@originalState[input]) @getElement("##{input}").val(@originalState[input])
if @originalState._location is '' if @originalState._location is ''
@locationBadgeEl.empty() @locationBadgeEl.hide()
else else
@addLocationBadge( @addLocationBadge(
value: @originalState._location value: @originalState._location
@ -244,7 +245,7 @@ class @SearchAutocomplete
@dropdown.removeClass 'open' @dropdown.removeClass 'open'
badgePresent: -> badgePresent: ->
@locationBadgeEl.children().length @locationBadgeEl.length
resetSearchState: -> resetSearchState: ->
inputs = Object.keys @originalState inputs = Object.keys @originalState
@ -257,7 +258,7 @@ class @SearchAutocomplete
@getElement("##{input}").val('') @getElement("##{input}").val('')
removeLocationBadge: -> removeLocationBadge: ->
@locationBadgeEl.empty() @locationBadgeEl.hide()
# Reset state # Reset state
@resetSearchState() @resetSearchState()

View file

@ -3,10 +3,10 @@
class @ShortcutsDashboardNavigation extends Shortcuts class @ShortcutsDashboardNavigation extends Shortcuts
constructor: -> constructor: ->
super() super()
Mousetrap.bind('g a', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-activity')) Mousetrap.bind('g a', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-activity'))
Mousetrap.bind('g i', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-issues')) Mousetrap.bind('g i', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-issues'))
Mousetrap.bind('g m', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-merge_requests')) Mousetrap.bind('g m', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-merge_requests'))
Mousetrap.bind('g p', -> ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-projects')) Mousetrap.bind('g p', -> ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-projects'))
@findAndFollowLink: (selector) -> @findAndFollowLink: (selector) ->
link = $(selector).attr('href') link = $(selector).attr('href')

View file

@ -10,14 +10,6 @@ class @ShortcutsIssuable extends ShortcutsNavigation
@replyWithSelectedText() @replyWithSelectedText()
return false return false
) )
Mousetrap.bind('j', =>
@prevIssue()
return false
)
Mousetrap.bind('k', =>
@nextIssue()
return false
)
Mousetrap.bind('e', => Mousetrap.bind('e', =>
@editIssue() @editIssue()
return false return false
@ -29,16 +21,6 @@ class @ShortcutsIssuable extends ShortcutsNavigation
else else
@enabledHelp.push('.hidden-shortcut.issues') @enabledHelp.push('.hidden-shortcut.issues')
prevIssue: ->
$prevBtn = $('.prev-btn')
if not $prevBtn.hasClass('disabled')
Turbolinks.visit($prevBtn.attr('href'))
nextIssue: ->
$nextBtn = $('.next-btn')
if not $nextBtn.hasClass('disabled')
Turbolinks.visit($nextBtn.attr('href'))
replyWithSelectedText: -> replyWithSelectedText: ->
if window.getSelection if window.getSelection
selected = window.getSelection().toString() selected = window.getSelection().toString()

View file

@ -4,8 +4,6 @@ expanded = 'page-sidebar-expanded'
toggleSidebar = -> toggleSidebar = ->
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}") $('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
$('header').toggleClass("header-collapsed header-expanded") $('header').toggleClass("header-collapsed header-expanded")
$('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
$.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
setTimeout ( -> setTimeout ( ->
niceScrollBars = $('.nicescroll').niceScroll(); niceScrollBars = $('.nicescroll').niceScroll();
@ -17,10 +15,3 @@ $(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) ->
toggleSidebar() toggleSidebar()
) )
$ ->
size = bp.getBreakpointSize()
if size is "xs" or size is "sm"
if $('.page-with-sidebar').hasClass(expanded)
toggleSidebar()

View file

@ -9,9 +9,11 @@ class @Star
$this.parent().find('.star-count').text data.star_count $this.parent().find('.star-count').text data.star_count
if isStarred if isStarred
$starSpan.removeClass('starred').text 'Star' $starSpan.removeClass('starred').text 'Star'
gl.utils.updateTooltipTitle $this, 'Star project'
$starIcon.removeClass('fa-star').addClass 'fa-star-o' $starIcon.removeClass('fa-star').addClass 'fa-star-o'
else else
$starSpan.addClass('starred').text 'Unstar' $starSpan.addClass('starred').text 'Unstar'
gl.utils.updateTooltipTitle $this, 'Unstar project'
$starIcon.removeClass('fa-star-o').addClass 'fa-star' $starIcon.removeClass('fa-star-o').addClass 'fa-star'
return return

View file

@ -19,3 +19,8 @@ class @Subscription
action = if status == 'subscribed' then 'Unsubscribe' else 'Subscribe' action = if status == 'subscribed' then 'Unsubscribe' else 'Subscribe'
btn.find('span').text(action) btn.find('span').text(action)
@subscription_status.find('>div').toggleClass('hidden') @subscription_status.find('>div').toggleClass('hidden')
if btn.attr('data-original-title')
btn.tooltip('hide')
.attr('data-original-title', action)
.tooltip('fixTitle')

View file

@ -0,0 +1,63 @@
# Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
#
# State Flow #1: setup -> in_progress -> authenticated -> POST to server
# State Flow #2: setup -> in_progress -> error -> setup
class @U2FAuthenticate
constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id
@challenges = u2fParams.challenges
@signRequests = u2fParams.sign_requests
start: () =>
if U2FUtil.isU2FSupported()
@renderSetup()
else
@renderNotSupported()
authenticate: () =>
u2f.sign(@appId, @challenges, @signRequests, (response) =>
if response.errorCode
error = new U2FError(response.errorCode)
@renderError(error);
else
@renderAuthenticated(JSON.stringify(response))
, 10)
#############
# Rendering #
#############
templates: {
"notSupported": "#js-authenticate-u2f-not-supported",
"setup": '#js-authenticate-u2f-setup',
"inProgress": '#js-authenticate-u2f-in-progress',
"error": '#js-authenticate-u2f-error',
"authenticated": '#js-authenticate-u2f-authenticated'
}
renderTemplate: (name, params) =>
templateString = $(@templates[name]).html()
template = _.template(templateString)
@container.html(template(params))
renderSetup: () =>
@renderTemplate('setup')
@container.find('#js-login-u2f-device').on('click', @renderInProgress)
renderInProgress: () =>
@renderTemplate('inProgress')
@authenticate()
renderError: (error) =>
@renderTemplate('error', {error_message: error.message()})
@container.find('#js-u2f-try-again').on('click', @renderSetup)
renderAuthenticated: (deviceResponse) =>
@renderTemplate('authenticated')
# Prefer to do this instead of interpolating using Underscore templates
# because of JSON escaping issues.
@container.find("#js-device-response").val(deviceResponse)
renderNotSupported: () =>
@renderTemplate('notSupported')

View file

@ -0,0 +1,13 @@
class @U2FError
constructor: (@errorCode) ->
@httpsDisabled = (window.location.protocol isnt 'https:')
console.error("U2F Error Code: #{@errorCode}")
message: () =>
switch
when (@errorCode is u2f.ErrorCodes.BAD_REQUEST and @httpsDisabled)
"U2F only works with HTTPS-enabled websites. Contact your administrator for more details."
when @errorCode is u2f.ErrorCodes.DEVICE_INELIGIBLE
"This device has already been registered with us."
else
"There was a problem communicating with your device."

View file

@ -0,0 +1,63 @@
# Register U2F (universal 2nd factor) devices for users to authenticate with.
#
# State Flow #1: setup -> in_progress -> registered -> POST to server
# State Flow #2: setup -> in_progress -> error -> setup
class @U2FRegister
constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id
@registerRequests = u2fParams.register_requests
@signRequests = u2fParams.sign_requests
start: () =>
if U2FUtil.isU2FSupported()
@renderSetup()
else
@renderNotSupported()
register: () =>
u2f.register(@appId, @registerRequests, @signRequests, (response) =>
if response.errorCode
error = new U2FError(response.errorCode)
@renderError(error);
else
@renderRegistered(JSON.stringify(response))
, 10)
#############
# Rendering #
#############
templates: {
"notSupported": "#js-register-u2f-not-supported",
"setup": '#js-register-u2f-setup',
"inProgress": '#js-register-u2f-in-progress',
"error": '#js-register-u2f-error',
"registered": '#js-register-u2f-registered'
}
renderTemplate: (name, params) =>
templateString = $(@templates[name]).html()
template = _.template(templateString)
@container.html(template(params))
renderSetup: () =>
@renderTemplate('setup')
@container.find('#js-setup-u2f-device').on('click', @renderInProgress)
renderInProgress: () =>
@renderTemplate('inProgress')
@register()
renderError: (error) =>
@renderTemplate('error', {error_message: error.message()})
@container.find('#js-u2f-try-again').on('click', @renderSetup)
renderRegistered: (deviceResponse) =>
@renderTemplate('registered')
# Prefer to do this instead of interpolating using Underscore templates
# because of JSON escaping issues.
@container.find("#js-device-response").val(deviceResponse)
renderNotSupported: () =>
@renderTemplate('notSupported')

View file

@ -0,0 +1,15 @@
# Helper class for U2F (universal 2nd factor) device registration and authentication.
class @U2FUtil
@isU2FSupported: ->
if @testMode
true
else
gon.u2f.browser_supports_u2f
@enableTestMode: ->
@testMode = true
<% if Rails.env.test? %>
U2FUtil.enableTestMode();
<% end %>

View file

@ -122,6 +122,9 @@ class @UserTabs
@parentEl.find(tabSelector).html(data.html) @parentEl.find(tabSelector).html(data.html)
@loaded[action] = true @loaded[action] = true
# Fix tooltips
gl.utils.localTimeAgo($('.js-timeago', tabSelector))
loadActivities: (source) -> loadActivities: (source) ->
return if @loaded['activity'] is true return if @loaded['activity'] is true

View file

@ -0,0 +1,8 @@
# 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 d3
#= require_tree .

View file

@ -0,0 +1,198 @@
class @Calendar
constructor: (timestamps, @calendar_activities_path) ->
@currentSelectedDate = ''
@daySpace = 1
@daySize = 15
@daySizeWithSpace = @daySize + (@daySpace * 2)
@monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
@months = []
@highestValue = 0
# Get the highest value from the timestampes
_.each timestamps, (count) =>
if count > @highestValue
@highestValue = count
# Loop through the timestamps to create a group of objects
# The group of objects will be grouped based on the day of the week they are
@timestampsTmp = []
i = 0
group = 0
_.each timestamps, (count, date) =>
newDate = new Date parseInt(date) * 1000
day = newDate.getDay()
# Create a new group array if this is the first day of the week
# or if is first object
if (day is 0 and i isnt 0) or i is 0
@timestampsTmp.push []
group++
innerArray = @timestampsTmp[group-1]
# Push to the inner array the values that will be used to render map
innerArray.push
count: count
date: newDate
day: day
i++
# Init color functions
@color = @initColor()
@colorKey = @initColorKey()
# Init the svg element
@renderSvg(group)
@renderDays()
@renderMonths()
@renderDayTitles()
@renderKey()
@initTooltips()
renderSvg: (group) ->
@svg = d3.select '.js-contrib-calendar'
.append 'svg'
.attr 'width', (group + 1) * @daySizeWithSpace
.attr 'height', 167
.attr 'class', 'contrib-calendar'
renderDays: ->
@svg.selectAll 'g'
.data @timestampsTmp
.enter()
.append 'g'
.attr 'transform', (group, i) =>
_.each group, (stamp, a) =>
if a is 0 and stamp.day is 0
month = stamp.date.getMonth()
x = (@daySizeWithSpace * i + 1) + @daySizeWithSpace
lastMonth = _.last(@months)
if lastMonth?
lastMonthX = lastMonth.x
if !lastMonth?
@months.push
month: month
x: x
else if month isnt lastMonth.month and x - @daySizeWithSpace isnt lastMonthX
@months.push
month: month
x: x
"translate(#{(@daySizeWithSpace * i + 1) + @daySizeWithSpace}, 18)"
.selectAll 'rect'
.data (stamp) ->
stamp
.enter()
.append 'rect'
.attr 'x', '0'
.attr 'y', (stamp, i) =>
(@daySizeWithSpace * stamp.day)
.attr 'width', @daySize
.attr 'height', @daySize
.attr 'title', (stamp) =>
contribText = 'No contributions'
if stamp.count > 0
contribText = "#{stamp.count} contribution#{if stamp.count > 1 then 's' else ''}"
date = dateFormat(stamp.date, 'mmm d, yyyy')
"#{contribText}<br />#{date}"
.attr 'class', 'user-contrib-cell js-tooltip'
.attr 'fill', (stamp) =>
if stamp.count isnt 0
@color(stamp.count)
else
'#ededed'
.attr 'data-container', 'body'
.on 'click', @clickDay
renderDayTitles: ->
days = [{
text: 'M'
y: 29 + (@daySizeWithSpace * 1)
}, {
text: 'W'
y: 29 + (@daySizeWithSpace * 3)
}, {
text: 'F'
y: 29 + (@daySizeWithSpace * 5)
}]
@svg.append 'g'
.selectAll 'text'
.data days
.enter()
.append 'text'
.attr 'text-anchor', 'middle'
.attr 'x', 8
.attr 'y', (day) ->
day.y
.text (day) ->
day.text
.attr 'class', 'user-contrib-text'
renderMonths: ->
@svg.append 'g'
.selectAll 'text'
.data @months
.enter()
.append 'text'
.attr 'x', (date) ->
date.x
.attr 'y', 10
.attr 'class', 'user-contrib-text'
.text (date) =>
@monthNames[date.month]
renderKey: ->
keyColors = ['#ededed', @colorKey(0), @colorKey(1), @colorKey(2), @colorKey(3)]
@svg.append 'g'
.attr 'transform', "translate(18, #{@daySizeWithSpace * 8 + 16})"
.selectAll 'rect'
.data keyColors
.enter()
.append 'rect'
.attr 'width', @daySize
.attr 'height', @daySize
.attr 'x', (color, i) =>
@daySizeWithSpace * i
.attr 'y', 0
.attr 'fill', (color) ->
color
initColor: ->
d3.scale
.linear()
.range(['#acd5f2', '#254e77'])
.domain([0, @highestValue])
initColorKey: ->
d3.scale
.linear()
.range(['#acd5f2', '#254e77'])
.domain([0, 3])
clickDay: (stamp) =>
if @currentSelectedDate isnt stamp.date
@currentSelectedDate = stamp.date
formatted_date = @currentSelectedDate.getFullYear() + "-" + (@currentSelectedDate.getMonth()+1) + "-" + @currentSelectedDate.getDate()
$.ajax
url: @calendar_activities_path
data:
date: formatted_date
cache: false
dataType: 'html'
beforeSend: ->
$('.user-calendar-activities').html '<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>'
success: (data) ->
$('.user-calendar-activities').html data
else
$('.user-calendar-activities').html ''
initTooltips: ->
$('.js-contrib-calendar .js-tooltip').tooltip
html: true

View file

@ -31,7 +31,7 @@ class @UsersSelect
assignTo = (selected) -> assignTo = (selected) ->
data = {} data = {}
data[abilityName] = {} data[abilityName] = {}
data[abilityName].assignee_id = selected data[abilityName].assignee_id = if selected? then selected else null
$loading $loading
.fadeIn() .fadeIn()
$dropdown.trigger('loading.gl.dropdown') $dropdown.trigger('loading.gl.dropdown')
@ -93,6 +93,8 @@ class @UsersSelect
$dropdown.glDropdown( $dropdown.glDropdown(
data: (term, callback) => data: (term, callback) =>
isAuthorFilter = $('.js-author-search')
@users term, (users) => @users term, (users) =>
if term.length is 0 if term.length is 0
showDivider = 0 showDivider = 0
@ -138,7 +140,7 @@ class @UsersSelect
toggleLabel: (selected) -> toggleLabel: (selected) ->
if selected && 'id' of selected if selected && 'id' of selected
selected.name if selected.text then selected.text else selected.name
else else
defaultLabel defaultLabel
@ -147,7 +149,7 @@ class @UsersSelect
hidden: (e) -> hidden: (e) ->
$selectbox.hide() $selectbox.hide()
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.removeAttr('style') $value.css('display', '')
clicked: (user) -> clicked: (user) ->
page = $('body').data 'page' page = $('body').data 'page'

View file

@ -8,7 +8,6 @@
*= require select2 *= require select2
*= require_self *= require_self
*= require dropzone/basic *= require dropzone/basic
*= require cal-heatmap
*= require cropper.css *= require cropper.css
*/ */

View file

@ -45,6 +45,7 @@
&.s32 { font-size: 20px; line-height: 32px; } &.s32 { font-size: 20px; line-height: 32px; }
&.s40 { font-size: 16px; line-height: 40px; } &.s40 { font-size: 16px; line-height: 40px; }
&.s60 { font-size: 32px; line-height: 60px; } &.s60 { font-size: 32px; line-height: 60px; }
&.s70 { font-size: 34px; line-height: 70px; }
&.s90 { font-size: 36px; line-height: 90px; } &.s90 { font-size: 36px; line-height: 90px; }
&.s110 { font-size: 40px; line-height: 112px; font-weight: 300; } &.s110 { font-size: 40px; line-height: 112px; font-weight: 300; }
&.s140 { font-size: 72px; line-height: 140px; } &.s140 { font-size: 72px; line-height: 140px; }

View file

@ -24,8 +24,8 @@
background-color: $background-color; background-color: $background-color;
padding: $gl-padding; padding: $gl-padding;
margin-bottom: 0; margin-bottom: 0;
border-top: 1px solid $border-color; border-top: 1px solid $white-dark;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $white-dark;
color: $gl-gray; color: $gl-gray;
&.oneline-block { &.oneline-block {
@ -61,6 +61,11 @@
margin-bottom: -$gl-padding; margin-bottom: -$gl-padding;
} }
&.content-component-block {
padding: 11px 0;
background-color: $white-light;
}
.title { .title {
color: $gl-text-color; color: $gl-text-color;
} }
@ -110,9 +115,9 @@
.cover-title { .cover-title {
color: $gl-header-color; color: $gl-header-color;
margin: 0; margin: 0;
font-size: 23px; font-size: 24px;
font-weight: normal; font-weight: normal;
margin: 16px 0 5px; margin-bottom: 5px;
color: #4c4e54; color: #4c4e54;
font-size: 23px; font-size: 23px;
line-height: 1.1; line-height: 1.1;
@ -137,7 +142,6 @@
} }
.cover-desc { .cover-desc {
padding: 0 $gl-padding 3px;
color: $gl-text-color; color: $gl-text-color;
&.username:last-child { &.username:last-child {
@ -205,7 +209,7 @@
.content-block { .content-block {
padding: $gl-padding 0; padding: $gl-padding 0;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $white-dark;
&.oneline-block { &.oneline-block {
line-height: 36px; line-height: 36px;

View file

@ -16,6 +16,19 @@
@include btn-default; @include btn-default;
} }
@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border) {
background-color: $background;
color: $text;
border-color: $border;
&:hover,
&:focus {
background-color: $hover-background;
color: $hover-text;
border-color: $hover-border;;
}
}
@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) { @mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) {
background-color: $light; background-color: $light;
border-color: $border-light; border-color: $border-light;
@ -66,6 +79,23 @@
@include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active); @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
} }
@mixin btn-with-margin {
margin-left: $btn-side-margin;
float: left;
&.inline {
float: none;
}
&.btn-sm {
margin-left: $btn-sm-side-margin;
}
&.btn-xs {
margin-left: $btn-xs-side-margin;
}
}
.btn { .btn {
@include btn-default; @include btn-default;
@include btn-white; @include btn-white;
@ -106,11 +136,14 @@
@include btn-blue; @include btn-blue;
} }
&.btn-close,
&.btn-warning { &.btn-warning {
@include btn-orange; @include btn-orange;
} }
&.btn-close {
@include btn-outline($white-light, $orange-normal, $orange-normal, $orange-light, $white-light, $orange-light);
}
&.btn-danger, &.btn-danger,
&.btn-remove, &.btn-remove,
&.btn-red { &.btn-red {
@ -126,15 +159,9 @@
} }
&.btn-grouped { &.btn-grouped {
margin-right: 7px; @include btn-with-margin;
float: left;
&:last-child {
margin-right: 0;
}
&.btn-xs {
margin-right: 3px;
}
} }
&.disabled { &.disabled {
pointer-events: auto !important; pointer-events: auto !important;
} }
@ -176,11 +203,7 @@
.btn-group { .btn-group {
&.btn-grouped { &.btn-grouped {
margin-right: 7px; @include btn-with-margin;
float: left;
&:last-child {
margin-right: 0;
}
} }
} }

View file

@ -1,70 +1,44 @@
.calender-block { .calender-block {
padding-left: 0;
padding-right: 0;
@media (min-width: $screen-sm-min) and (max-width: $screen-lg-min) { @media (min-width: $screen-sm-min) and (max-width: $screen-lg-min) {
overflow-x: scroll; overflow-x: scroll;
} }
} }
.user-calendar-activities { .user-calendar-activities {
.calendar_onclick_hr {
padding: 0;
margin: 10px 0;
}
.str-truncated { .str-truncated {
max-width: 70%; max-width: 70%;
} }
.text-expander { .user-calendar-activities-loading {
background: #eee; font-size: 24px;
color: #555; }
padding: 0 5px; }
.user-calendar {
text-align: center;
.calendar {
display: inline-block;
}
}
.user-contrib-cell {
&:hover {
cursor: pointer; cursor: pointer;
margin-left: 4px; stroke: #000;
&:hover {
background-color: #ddd;
}
} }
} }
/** .user-contrib-text {
* This overwrites the default values of the cal-heatmap gem font-size: 12px;
*/ fill: #959494;
.calendar { }
.qi {
fill: #fff; .calendar-hint {
} margin-top: -23px;
float: right;
.q1 { font-size: 12px;
fill: #ededed !important;
}
.q2 {
fill: #acd5f2 !important;
}
.q3 {
fill: #7fa8d1 !important;
}
.q4 {
fill: #49729b !important;
}
.q5 {
fill: #254e77 !important;
}
.future {
visibility: hidden;
}
.domain-background {
fill: none;
shape-rendering: crispedges;
}
.ch-tooltip {
padding: 3px;
font-weight: 550;
}
} }

View file

@ -122,10 +122,9 @@
a { a {
display: block; display: block;
position: relative; position: relative;
padding-left: 10px; padding: 5px 10px;
padding-right: 10px;
color: $dropdown-link-color; color: $dropdown-link-color;
line-height: 34px; line-height: initial;
text-overflow: ellipsis; text-overflow: ellipsis;
border-radius: 2px; border-radius: 2px;
white-space: nowrap; white-space: nowrap;
@ -154,7 +153,7 @@
color: $dropdown-header-color; color: $dropdown-header-color;
font-size: 13px; font-size: 13px;
line-height: 22px; line-height: 22px;
padding: 0 10px 10px; padding: 0 10px;
} }
.separator + .dropdown-header { .separator + .dropdown-header {
@ -162,6 +161,20 @@
} }
} }
.dropdown-menu-large {
width: 340px;
}
.dropdown-menu-no-wrap {
a {
white-space: normal;
}
}
.dropdown-menu-full-width {
width: 100%;
}
.dropdown-menu-paging { .dropdown-menu-paging {
.dropdown-page-two, .dropdown-page-two,
.dropdown-menu-back { .dropdown-menu-back {
@ -228,13 +241,11 @@
a { a {
padding-left: 25px; padding-left: 25px;
&.is-active { &.is-indeterminate, &.is-active {
&::before { &::before {
content: "\f00c";
position: absolute; position: absolute;
left: 5px; left: 5px;
top: 50%; top: 8px;
margin-top: -7px;
font: normal normal normal 14px/1 FontAwesome; font: normal normal normal 14px/1 FontAwesome;
font-size: inherit; font-size: inherit;
text-rendering: auto; text-rendering: auto;
@ -242,6 +253,14 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
} }
&.is-indeterminate::before {
content: "\f068";
}
&.is-active::before {
content: "\f00c";
}
} }
} }
@ -521,3 +540,14 @@
background-color: $calendar-unselectable-bg; background-color: $calendar-unselectable-bg;
} }
} }
.dropdown-menu-inner-title {
display: block;
color: $gl-title-color;
font-weight: 600;
}
.dropdown-menu-inner-content {
display: block;
color: $gl-placeholder-color;
}

View file

@ -5,6 +5,10 @@
.file-holder { .file-holder {
border: 1px solid $border-color; border: 1px solid $border-color;
&.file-holder-no-border {
border: 0;
}
&.readme-holder { &.readme-holder {
margin: $gl-padding-top 0; margin: $gl-padding-top 0;
} }
@ -23,8 +27,17 @@
word-wrap: break-word; word-wrap: break-word;
border-radius: 3px 3px 0 0; border-radius: 3px 3px 0 0;
&.file-title-clear {
padding-left: 0;
padding-right: 0;
background-color: transparent;
.file-actions {
right: 0;
}
}
.file-actions { .file-actions {
float: right;
position: absolute; position: absolute;
top: 5px; top: 5px;
right: 15px; right: 15px;

View file

@ -28,10 +28,6 @@ input[type='text'].danger {
} }
label { label {
&.control-label {
@extend .col-sm-2;
}
&.inline-label { &.inline-label {
margin: 0; margin: 0;
} }
@ -41,6 +37,10 @@ label {
} }
} }
.control-label {
@extend .col-sm-2;
}
.inline-input-group { .inline-input-group {
width: 250px; width: 250px;
} }
@ -76,6 +76,7 @@ label {
.form-control { .form-control {
@include box-shadow(none); @include box-shadow(none);
border-radius: 3px; border-radius: 3px;
padding: $gl-vert-padding $gl-input-padding;
} }
.select-wrapper { .select-wrapper {

View file

@ -8,32 +8,14 @@
*/ */
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) { @mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
.page-with-sidebar { .page-with-sidebar {
.header-logo {
background: $color-darker;
a {
color: $color-light;
h3 {
color: $color-light;
}
}
&:hover {
background-color: $color-dark;
a {
color: #fff;
h3 {
color: #fff;
}
}
}
}
.collapse-nav a { .collapse-nav a {
color: #fff; color: $color-light;
background: $color; background: $color;
&:hover {
color: $white-light;
}
} }
.sidebar-wrapper { .sidebar-wrapper {
@ -45,7 +27,7 @@
&:hover { &:hover {
background-color: $color-dark; background-color: $color-dark;
color: #fff; color: $white-light;
text-decoration: none; text-decoration: none;
} }
} }
@ -63,10 +45,20 @@
color: $color-light; color: $color-light;
} }
path,
polygon {
fill: $color-light;
}
.count { .count {
color: $color-light; color: $color-light;
background: $color-dark; background: $color-dark;
} }
svg {
position: relative;
top: 3px;
}
} }
&.separate-item { &.separate-item {
@ -74,7 +66,7 @@
} }
&.active a { &.active a {
color: #fff; color: $white-light;
background: $color-dark; background: $color-dark;
&.no-highlight { &.no-highlight {
@ -82,15 +74,23 @@
} }
i { i {
color: #fff color: $white-light
}
path,
polygon {
fill: $white-light;
} }
} }
} }
} }
} }
$theme-blue: #2980b9;
$theme-charcoal: #3d454d; $theme-charcoal: #3d454d;
$theme-charcoal-dark: #383f45;
$theme-charcoal-text: #b9bbbe;
$theme-blue: #2980b9;
$theme-graphite: #666; $theme-graphite: #666;
$theme-gray: #373737; $theme-gray: #373737;
$theme-green: #019875; $theme-green: #019875;
@ -102,7 +102,7 @@ body {
} }
&.ui_charcoal { &.ui_charcoal {
@include gitlab-theme(#d6d7d9, #485157, $theme-charcoal, #353b41); @include gitlab-theme($theme-charcoal-text, #485157, $theme-charcoal, $theme-charcoal-dark);
} }
&.ui_graphite { &.ui_graphite {

View file

@ -79,6 +79,10 @@ header {
&.header-collapsed { &.header-collapsed {
padding: 0 16px; padding: 0 16px;
.side-nav-toggle {
display: block;
}
} }
.side-nav-toggle { .side-nav-toggle {
@ -86,6 +90,7 @@ header {
position: absolute; position: absolute;
left: -10px; left: -10px;
margin: 6px 0; margin: 6px 0;
font-size: 18px;
padding: 6px 10px; padding: 6px 10px;
border: none; border: none;
background-color: $background-color; background-color: $background-color;
@ -97,10 +102,6 @@ header {
&:focus { &:focus {
outline: none; outline: none;
} }
@media (max-width: $screen-xs-min) {
display: block;
}
} }
} }
@ -108,10 +109,8 @@ header {
position: relative; position: relative;
height: $header-height; height: $header-height;
padding-right: 40px; padding-right: 40px;
padding-left: 30px;
@media (max-width: $screen-xs-min) { transition-duration: .3s;
padding-left: 40px;
}
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
padding-right: 0; padding-right: 0;
@ -121,9 +120,29 @@ header {
margin-top: -5px; margin-top: -5px;
} }
.header-logo {
position: absolute;
left: 50%;
margin-left: -18px;
top: 7px;
transition-duration: .3s;
z-index: 999;
&:hover {
cursor: pointer;
}
@media (max-width: $screen-xs-max) {
right: 25px;
left: auto;
}
}
.title { .title {
margin: 0; margin: 0;
font-size: 19px; font-size: 19px;
max-width: 400px;
display: inline-block;
line-height: $header-height; line-height: $header-height;
font-weight: normal; font-weight: normal;
color: $gl-text-color; color: $gl-text-color;
@ -132,6 +151,10 @@ header {
vertical-align: top; vertical-align: top;
white-space: nowrap; white-space: nowrap;
@media (max-width: $screen-sm-max) {
max-width: 190px;
}
a { a {
color: $gl-text-color; color: $gl-text-color;
&:hover { &:hover {
@ -159,6 +182,10 @@ header {
.navbar-collapse { .navbar-collapse {
float: right; float: right;
border-top: none; border-top: none;
@media (max-width: $screen-xs-max) {
float: none;
}
} }
} }
@ -171,31 +198,24 @@ header {
} }
} }
@mixin collapsed-header {
margin-left: $sidebar_collapsed_width;
}
.header-collapsed { .header-collapsed {
margin-left: $sidebar_collapsed_width; margin-left: 0;
@media (min-width: $screen-md-min) { .header-content {
@include collapsed-header;
}
@media (max-width: $screen-xs-min) { @media (min-width: $screen-sm-max) {
margin-left: 0; padding-left: 30px;
transition-duration: .3s;
}
} }
} }
.header-expanded { .tanuki-shape {
margin-left: $sidebar_collapsed_width; transition: all 0.8s;
@media (min-width: $screen-md-min) { &:hover, &.highlight {
margin-left: $sidebar_width; fill: rgb(255, 255, 255);
} transition: all 0.1s;
@media (max-width: $screen-xs-min) {
margin-left: 0;
} }
} }

View file

@ -2,6 +2,7 @@
font-family: $regular_font; font-family: $regular_font;
font-size: $font-size-base; font-size: $font-size-base;
&.ui-datepicker,
&.ui-datepicker-inline { &.ui-datepicker-inline {
border: 1px solid #ddd; border: 1px solid #ddd;
padding: 10px; padding: 10px;
@ -10,6 +11,25 @@
.ui-datepicker-header { .ui-datepicker-header {
background: #fff; background: #fff;
border-color: #ddd; border-color: #ddd;
.ui-datepicker-prev,
.ui-datepicker-next {
top: 4px;
}
.ui-datepicker-prev {
left: 2px;
}
.ui-datepicker-next {
right: 2px;
}
.ui-state-hover {
background: transparent;
border: 0;
cursor: pointer;
}
} }
.ui-datepicker-calendar td a { .ui-datepicker-calendar td a {
@ -36,21 +56,18 @@
} }
.ui-state-highlight { .ui-state-highlight {
border: 1px solid #eee; border: 0;
background: #eee; background: transparent;
} }
.ui-state-active { .ui-datepicker-calendar {
border: 1px solid $gl-primary; .ui-state-active,
background: $gl-primary; .ui-state-hover,
color: #fff; .ui-state-focus {
} border: 1px solid $gl-primary;
background: $gl-primary;
.ui-state-hover, color: #fff;
.ui-state-focus { }
border: 1px solid $row-hover;
background: $row-hover;
color: #333;
} }
} }

View file

@ -137,10 +137,30 @@ ul.content-list {
padding-top: 1px; padding-top: 1px;
float: right; float: right;
.btn { > .btn,
padding: 10px 14px; > .btn-group {
margin-right: $gl-padding-top;
display: inline-block;
margin-top: 4px;
margin-bottom: 4px;
&:last-child {
margin-right: 0;
}
} }
} }
// When dragging a list item
&.ui-sortable-helper {
border-bottom: none;
}
&.list-placeholder {
background-color: $gray-light;
border: dotted 1px $gray-dark;
margin: 1px 0;
min-height: 30px;
}
} }
} }

View file

@ -2,18 +2,10 @@
* Generic mixins * Generic mixins
*/ */
@mixin box-shadow($shadow) { @mixin box-shadow($shadow) {
-webkit-box-shadow: $shadow;
-moz-box-shadow: $shadow;
-ms-box-shadow: $shadow;
-o-box-shadow: $shadow;
box-shadow: $shadow; box-shadow: $shadow;
} }
@mixin border-radius($radius) { @mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
-ms-border-radius: $radius;
-o-border-radius: $radius;
border-radius: $radius; border-radius: $radius;
} }

View file

@ -48,10 +48,6 @@
display: block; display: block;
} }
.project-home-desc {
font-size: 21px;
}
.project-repo-buttons, .project-repo-buttons,
.git-clone-holder { .git-clone-holder {
display: none; display: none;
@ -70,10 +66,6 @@
display: none; display: none;
} }
%ul.notes .note-role, .note-actions {
display: none;
}
.nav-links, .nav-links { .nav-links, .nav-links {
li a { li a {
font-size: 14px; font-size: 14px;

View file

@ -1,3 +1,35 @@
@mixin fade($gradient-direction, $rgba, $gradient-color) {
visibility: visible;
opacity: 1;
z-index: 2;
position: absolute;
bottom: 12px;
width: 43px;
height: 30px;
transition-duration: .3s;
-webkit-transform: translateZ(0);
background: -webkit-linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
background: -o-linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
background: -moz-linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
background: linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
&.end-scroll {
visibility: hidden;
opacity: 0;
transition-duration: .3s;
}
}
@mixin scrolling-links() {
white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
}
}
.nav-links { .nav-links {
padding: 0; padding: 0;
margin: 0; margin: 0;
@ -10,8 +42,7 @@
a { a {
display: inline-block; display: inline-block;
padding: 14px; padding: $gl-btn-padding;
padding-top: $gl-padding;
padding-bottom: 11px; padding-bottom: 11px;
margin-bottom: -1px; margin-bottom: -1px;
font-size: 15px; font-size: 15px;
@ -36,6 +67,29 @@
color: #78a; color: #78a;
} }
} }
&.sub-nav {
text-align: center;
background-color: $background-color;
.container-fluid {
background-color: $background-color;
margin-bottom: 0;
}
li {
a {
margin: 0;
padding: 11px 10px 9px;
}
&.active a {
border-bottom: none;
color: $link-underline-blue;
}
}
}
} }
.top-area { .top-area {
@ -50,6 +104,10 @@
width: 50%; width: 50%;
line-height: 28px; line-height: 28px;
&.wiki-page {
padding: 16px 10px 11px;
}
/* Small devices (phones, tablets, 768px and lower) */ /* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-sm-min) { @media (max-width: $screen-sm-min) {
width: 100%; width: 100%;
@ -73,6 +131,10 @@
margin-bottom: 0; margin-bottom: 0;
border-bottom: none; border-bottom: none;
li a {
padding: 16px 10px 11px;
}
/* Small devices (phones, tablets, 768px and lower) */ /* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-sm-max) { @media (max-width: $screen-sm-max) {
width: 100%; width: 100%;
@ -119,7 +181,7 @@
} }
input { input {
height: 34px; height: 35px;
display: inline-block; display: inline-block;
position: relative; position: relative;
top: 2px; top: 2px;
@ -148,7 +210,7 @@
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
padding-bottom: 0; padding-bottom: 0;
width: 100%;
.btn, form, .dropdown, .dropdown-menu-toggle, .form-control { .btn, form, .dropdown, .dropdown-menu-toggle, .form-control {
margin: 0 0 10px; margin: 0 0 10px;
display: block; display: block;
@ -179,16 +241,6 @@
margin: 0; margin: 0;
} }
} }
/* Small devices (tablets, 768px and lower) */
@media (max-width: $screen-sm-max) {
width: 100%;
text-align: left;
input {
width: 300px;
}
}
} }
} }
@ -196,10 +248,11 @@
position: fixed; position: fixed;
top: $header-height; top: $header-height;
width: 100%; width: 100%;
z-index: 1; z-index: 11;
background: $background-color; background: $background-color;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
transition-duration: .3s; transition-duration: .3s;
text-align: center;
.container-fluid { .container-fluid {
position: relative; position: relative;
@ -209,13 +262,8 @@
float: right; float: right;
padding: 7px 0 0; padding: 7px 0 0;
@media (max-width: $screen-xs-min) { @media (max-width: $screen-xs-max) {
float: none; display: none;
padding: 0 9px;
.dropdown-new {
width: 100%;
}
} }
i { i {
@ -233,19 +281,44 @@
} }
.dropdown { .dropdown {
margin-left: 7px; position: absolute;
top: 7px;
right: 15px;
z-index: 2;
@media (max-width: $screen-xs-min) { li.active {
margin-left: 0; font-weight: bold;
} }
} }
} }
.nav-links { .nav-links {
@include scrolling-links();
border-bottom: none; border-bottom: none;
height: 51px; height: 51px;
white-space: nowrap;
overflow-x: auto; svg {
position: relative;
top: 2px;
margin-right: 2px;
height: 15px;
width: auto;
path,
polygon {
fill: $layout-link-gray;
}
}
.fade-right {
@include fade(left, rgba(250, 250, 250, 0.4), $background-color);
right: 0;
}
.fade-left {
@include fade(right, rgba(250, 250, 250, 0.4), $background-color);
left: 0;
}
li { li {
@ -258,9 +331,17 @@
} }
&.active { &.active {
a, i { a, i {
color: $black; color: $black;
} }
svg {
path,
polygon {
fill: $black;
}
}
} }
.badge { .badge {
@ -269,14 +350,80 @@
} }
} }
} .nav-control {
.page-with-layout-nav { .fade-right {
margin-top: 50px; @media (min-width: $screen-xs-max) {
right: 68px;
&.controls-dropdown-visible { }
@media (max-width: $screen-xs-min) { @media (max-width: $screen-xs-min) {
margin-top: 96px; right: 0;
}
}
}
}
.scrolling-tabs-container {
position: relative;
.nav-links {
@include scrolling-links();
.fade-right {
@include fade(left, rgba(255, 255, 255, 0.4), $background-color);
right: 0;
}
.fade-left {
@include fade(right, rgba(255, 255, 255, 0.4), $background-color);
left: 0;
}
}
}
.nav-block {
position: relative;
.nav-links {
@include scrolling-links();
.fade-right {
@include fade(left, rgba(255, 255, 255, 0.4), $white-light);
right: 0;
}
.fade-left {
@include fade(right, rgba(255, 255, 255, 0.4), $white-light);
left: 0;
}
&.event-filter {
.fade-right {
visibility: hidden;
@media (max-width: $screen-xs-max) {
visibility: visible;
}
}
}
}
}
.page-with-layout-nav {
margin-top: $header-height + 2;
.right-sidebar {
top: ($header-height * 2) + 2;
}
}
.activities {
.nav-block {
border-bottom: 1px solid $border-color;
.nav-links {
border-bottom: none;
} }
} }
} }

View file

@ -8,7 +8,7 @@
background: #fff; background: #fff;
border-color: $input-border; border-color: $input-border;
height: 35px; height: 35px;
padding: $gl-vert-padding $gl-btn-padding; padding: $gl-vert-padding $gl-input-padding;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 1.42857143; line-height: 1.42857143;
border-radius: $border-radius-base; border-radius: $border-radius-base;

View file

@ -1,11 +1,3 @@
#logo {
z-index: 2;
position: absolute;
width: 58px;
cursor: pointer;
margin-top: 8px;
}
.page-with-sidebar { .page-with-sidebar {
padding-top: $header-height; padding-top: $header-height;
transition-duration: .3s; transition-duration: .3s;
@ -20,12 +12,6 @@
height: 100%; height: 100%;
transition-duration: .3s; transition-duration: .3s;
} }
.gitlab-text-container-link {
z-index: 1;
position: absolute;
left: 0;
}
} }
.sidebar-wrapper { .sidebar-wrapper {
@ -49,58 +35,11 @@
} }
.sidebar-wrapper { .sidebar-wrapper {
.header-logo {
border-bottom: 1px solid transparent;
float: left;
height: $header-height;
width: $sidebar_width;
position: fixed;
z-index: 999;
overflow: hidden;
transition-duration: .3s;
a {
float: left;
height: $header-height;
width: 100%;
padding-left: 22px;
overflow: hidden;
outline: none;
transition-duration: .3s;
img {
width: 36px;
height: 36px;
}
#tanuki-logo, img {
float: left;
}
.gitlab-text-container {
width: 230px;
h3 {
width: 158px;
float: left;
margin: 0;
margin-left: 50px;
font-size: 19px;
line-height: 50px;
font-weight: normal;
}
}
}
&:hover {
background-color: #eee;
}
}
.sidebar-user { .sidebar-user {
padding: 7px 22px; padding: 15px 22px;
position: fixed; position: fixed;
bottom: 40px; bottom: 0;
width: $sidebar_width; width: $sidebar_width;
overflow: hidden; overflow: hidden;
transition-duration: .3s; transition-duration: .3s;
@ -126,8 +65,8 @@
.nav-sidebar { .nav-sidebar {
margin-top: 14 + $header-height; margin-top: 22 + $header-height;
margin-bottom: 100px; margin-bottom: 116px;
transition-duration: .3s; transition-duration: .3s;
list-style: none; list-style: none;
overflow: hidden; overflow: hidden;
@ -144,14 +83,19 @@
margin-top: 10px; margin-top: 10px;
} }
.icon-container {
width: 34px;
display: inline-block;
text-align: center;
}
a { a {
padding: 7px 15px; width: $sidebar_width;
padding: 7px 15px 7px 23px;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 24px; line-height: 24px;
color: $gray;
display: block; display: block;
text-decoration: none; text-decoration: none;
padding-left: 23px;
font-weight: normal; font-weight: normal;
outline: none; outline: none;
@ -164,16 +108,12 @@
} }
i { i {
width: 16px; font-size: 16px;
color: $gray-light;
margin-right: 13px;
} }
.count { i,
float: right; svg {
background: #eee; margin-right: 13px;
padding: 0 8px;
@include border-radius(6px);
} }
&.back-link i { &.back-link i {
@ -181,6 +121,12 @@
} }
} }
} }
.count {
float: right;
padding: 0 8px;
@include border-radius(6px);
}
} }
.sidebar-subnav { .sidebar-subnav {
@ -195,11 +141,12 @@
.collapse-nav a { .collapse-nav a {
width: $sidebar_width; width: $sidebar_width;
position: fixed; position: fixed;
bottom: 0; top: 0;
left: 0; left: 0;
font-size: 13px; padding: 5px 0;
font-size: 18px;
background: transparent; background: transparent;
height: 40px; height: 50px;
text-align: center; text-align: center;
line-height: 40px; line-height: 40px;
transition-duration: .3s; transition-duration: .3s;
@ -217,37 +164,13 @@
} }
.page-sidebar-collapsed { .page-sidebar-collapsed {
padding-left: $sidebar_collapsed_width; padding-left: 0;
@media (max-width: $screen-xs-min) {
padding-left: 0;
}
.sidebar-wrapper { .sidebar-wrapper {
width: $sidebar_collapsed_width; width: 0;
@media (max-width: $screen-xs-min) {
width: 0;
}
.header-logo {
width: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
width: 0;
}
a {
padding-left: ($sidebar_collapsed_width - 36) / 2;
.gitlab-text-container {
display: none;
}
}
}
.nav-sidebar { .nav-sidebar {
width: $sidebar_collapsed_width; width: 0;
li { li {
width: auto; width: auto;
@ -261,46 +184,28 @@
} }
.collapse-nav a { .collapse-nav a {
width: $sidebar_collapsed_width; width: 0;
@media (max-width: $screen-xs-min) { i {
width: 0; display: none;
} }
} }
.sidebar-user { .sidebar-user {
padding-left: ($sidebar_collapsed_width - 36) / 2; width: 0;
width: $sidebar_collapsed_width; padding-left: 0;
padding-right: 0;
@media (max-width: $screen-xs-min) {
width: 0;
padding-left: 0;
padding-right: 0;
}
.username { .username {
display: none; display: none;
} }
} }
} }
.layout-nav {
padding-right: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
padding-right: 0;;
}
}
} }
.page-sidebar-expanded { .page-sidebar-expanded {
padding-left: $sidebar_collapsed_width;
@media (min-width: $screen-md-min) { @media (max-width: $screen-sm-max) {
padding-left: $sidebar_width;
}
@media (max-width: $screen-xs-min) {
padding-left: 0; padding-left: 0;
} }
@ -321,20 +226,6 @@
} }
} }
} }
.layout-nav {
@media (max-width: $screen-xs-min) {
padding-right: 0;;
}
@media (min-width: $screen-xs-min) and (max-width: $screen-md-min) {
padding-right: 62px;
}
@media (min-width: $screen-md-min) {
padding-right: $sidebar_width;
}
}
} }
.right-sidebar-collapsed { .right-sidebar-collapsed {
@ -353,7 +244,9 @@
padding-right: 0; padding-right: 0;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
padding-right: $sidebar_collapsed_width; &:not(.build-sidebar) {
padding-right: $sidebar_collapsed_width;
}
} }
@media (min-width: $screen-md-min) { @media (min-width: $screen-md-min) {

View file

@ -5,7 +5,7 @@
padding: 0; padding: 0;
.timeline-entry { .timeline-entry {
padding: $gl-padding $gl-btn-padding; padding: $gl-padding $gl-btn-padding 11px;
border-color: $table-border-color; border-color: $table-border-color;
color: $gl-gray; color: $gl-gray;
border-bottom: 1px solid $border-white-light; border-bottom: 1px solid $border-white-light;

View file

@ -192,3 +192,8 @@
.text-info:hover { .text-info:hover {
color: $brand-info; color: $brand-info;
} }
// Prevent datetimes on tooltips to break into two lines
.local-timeago {
white-space: nowrap;
}

View file

@ -57,13 +57,15 @@ $code_line_height: 1.5;
*/ */
$gl-padding: 16px; $gl-padding: 16px;
$gl-btn-padding: 10px; $gl-btn-padding: 10px;
$gl-input-padding: 10px;
$gl-vert-padding: 6px; $gl-vert-padding: 6px;
$gl-padding-top: 10px; $gl-padding-top: 10px;
/* /*
* Misc * Misc
*/ */
$row-hover: #f4f8fe; $row-hover: #f7faff;
$row-hover-border: #b2d7ff;
$progress-color: #c0392b; $progress-color: #c0392b;
$avatar_radius: 50%; $avatar_radius: 50%;
$header-height: 50px; $header-height: 50px;
@ -78,6 +80,9 @@ $provider-btn-not-active-color: #4688f1;
$link-underline-blue: #4a8bee; $link-underline-blue: #4a8bee;
$layout-link-gray: #7e7c7c; $layout-link-gray: #7e7c7c;
$todo-alert-blue: #428bca; $todo-alert-blue: #428bca;
$btn-side-margin: 10px;
$btn-sm-side-margin: 7px;
$btn-xs-side-margin: 5px;
/* /*
* Color schema * Color schema
@ -104,7 +109,7 @@ $blue-medium-light: #3498cb;
$blue-medium: #2f8ebf; $blue-medium: #2f8ebf;
$blue-medium-dark: #2d86b4; $blue-medium-dark: #2d86b4;
$orange-light: rgba(252, 109, 38, 0.80); $orange-light: #fc8a51;
$orange-normal: #e75e40; $orange-normal: #e75e40;
$orange-dark: #ce5237; $orange-dark: #ce5237;
@ -119,8 +124,8 @@ $border-white-light: #f1f2f4;
$border-white-normal: #d6dae2; $border-white-normal: #d6dae2;
$border-white-dark: #c6cacf; $border-white-dark: #c6cacf;
$border-gray-light: rgba(0, 0, 0, 0.06); $border-gray-light: #dcdcdc;
$border-gray-normal: rgba(0, 0, 0, 0.10);; $border-gray-normal: #d7d7d7;
$border-gray-dark: #c6cacf; $border-gray-dark: #c6cacf;
$border-green-light: #2faa60; $border-green-light: #2faa60;
@ -255,3 +260,6 @@ $calendar-header-color: #b8b8b8;
$calendar-hover-bg: #ecf3fe; $calendar-hover-bg: #ecf3fe;
$calendar-border-color: rgba(#000, .1); $calendar-border-color: rgba(#000, .1);
$calendar-unselectable-bg: #faf9f9; $calendar-unselectable-bg: #faf9f9;
$ci-output-bg: #1d1f21;
$ci-text-color: #c5c8c6;

View file

@ -0,0 +1,134 @@
// NOTE: This stylesheet is for the exclusive use of the `devise_mailer` layout
// used for Devise email templates, and _should not_ be included in any
// application stylesheets.
//
// Styles defined here are embedded directly into the resulting email HTML via
// the `premailer` gem.
$body-background-color: #363636;
$message-background-color: #fafafa;
$header-color: #6b4fbb;
$body-color: #444;
$cta-color: #e14329;
$footer-link-color: #7e7e7e;
$font-family: Helvetica, Arial, sans-serif;
body {
background-color: $body-background-color;
font-family: $font-family;
margin: 0;
padding: 0;
}
table {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
border: 0;
border-collapse: separate;
&#wrapper {
background-color: $body-background-color;
width: 100%;
}
&#header {
margin: 0 auto;
text-align: left;
width: 600px;
}
&#body {
background-color: $message-background-color;
border: 1px solid #000;
border-radius: 4px;
margin: 0 auto;
width: 600px;
}
&#footer {
color: $footer-link-color;
font-size: 14px;
text-align: center;
width: 100%;
}
td {
&#body-container {
padding: 20px 40px;
}
}
}
.center {
text-align: center;
}
#logo {
border: none;
outline: none;
min-height: 88px;
width: 134px;
}
#content {
h2 {
color: $header-color;
font-size: 30px;
font-weight: 400;
line-height: 34px;
margin-top: 0;
}
p {
color: $body-color;
font-size: 17px;
line-height: 24px;
margin-bottom: 0;
}
}
#cta {
border: 1px solid $cta-color;
border-radius: 3px;
display: inline-block;
margin: 20px 0;
padding: 12px 24px;
a {
background-color: $message-background-color;
color: $cta-color;
display: inline-block;
text-decoration: none;
}
}
#tanuki {
padding: 40px 0 0;
img {
border: none;
outline: none;
width: 37px;
min-height: 36px;
}
}
#tagline {
font-size: 22px;
font-weight: 100;
padding: 4px 0 40px;
}
#social {
padding: 0 10px 20px;
width: 600px;
word-spacing: 20px;
a {
color: $footer-link-color;
text-decoration: none;
}
}

View file

@ -1,5 +1,15 @@
@import "framework/variables"; @import "framework/variables";
// This file is largely copied from `highlight/white.scss`, but modified to
// avoid all descendant selectors (`table td`). This is because the CSS inlining
// we use performs dramatically worse on descendant selectors than the
// alternatives.
// <https://gitlab.com/gitlab-org/gitlab-ee/issues/490#note_12283632>
//
// DO NOT ADD ANY DESCENDANT SELECTORS TO THIS FILE. Instead, use (in order of
// preference): plain class selectors, type (element name) selectors, or
// explicit child selectors.
table.code { table.code {
width: 100%; width: 100%;
font-family: monospace; font-family: monospace;
@ -11,33 +21,162 @@ table.code {
-premailer-cellspacing: 0; -premailer-cellspacing: 0;
-premailer-width: 100%; -premailer-width: 100%;
td { > tr > td {
line-height: $code_line_height; line-height: $code_line_height;
font-family: monospace; font-family: monospace;
font-size: $code_font_size; font-size: $code_font_size;
}
td.diff-line-num { &.diff-line-num {
margin: 0; margin: 0;
padding: 0; padding: 0;
border: none; border: none;
background: $background-color; padding: 0 5px;
color: rgba(0, 0, 0, 0.3); border-right: 1px solid;
padding: 0 5px; text-align: right;
border-right: 1px solid $border-color; min-width: 35px;
text-align: right; max-width: 50px;
min-width: 35px; width: 35px;
max-width: 50px; }
width: 35px;
}
td.line_content { &.line_content {
display: block; display: block;
margin: 0; margin: 0;
padding: 0 0.5em; padding: 0 0.5em;
border: none; border: none;
white-space: pre; white-space: pre;
}
} }
} }
@import "highlight/white"; .line-numbers, .diff-line-num {
background-color: $background-color;
}
.diff-line-num, .diff-line-num a {
color: $black-transparent;
}
pre.code, .diff-line-num {
border-color: $table-border-gray;
}
.code.white, pre.code, .line_content {
background-color: #fff;
color: #333;
}
.diff-line-num {
&.old {
background-color: $line-number-old;
border-color: $line-removed-dark;
}
&.new {
background-color: $line-number-new;
border-color: $line-added-dark;
}
&.hll:not(.empty-cell) {
background-color: $line-number-select;
border-color: $line-select-yellow-dark;
}
}
.line_content {
&.old {
background-color: $line-removed;
> .line > span.idiff, > .line > span > span.idiff {
background-color: $line-removed-dark;
}
}
&.new {
background-color: $line-added;
> .line > span.idiff, > .line > span > span.idiff {
background-color: $line-added-dark;
}
}
&.match {
color: $black-transparent;
background-color: $match-line;
}
&.hll:not(.empty-cell) {
background-color: $line-select-yellow;
}
}
pre > .hll {
background-color: #f8eec7 !important;
}
span.highlight_word {
background-color: #fafe3d !important;
}
.hll { background-color: #f8f8f8 }
.c { color: #998; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; }
.k { font-weight: bold; }
.o { font-weight: bold; }
.cm { color: #998; font-style: italic; }
.cp { color: #999; font-weight: bold; }
.c1 { color: #998; font-style: italic; }
.cs { color: #999; font-weight: bold; font-style: italic; }
.gd { color: #000; background-color: #fdd; }
.gd .x { color: #000; background-color: #faa; }
.ge { font-style: italic; }
.gr { color: #a00; }
.gh { color: #999; }
.gi { color: #000; background-color: #dfd; }
.gi .x { color: #000; background-color: #afa; }
.go { color: #888; }
.gp { color: #555; }
.gs { font-weight: bold; }
.gu { color: #800080; font-weight: bold; }
.gt { color: #a00; }
.kc { font-weight: bold; }
.kd { font-weight: bold; }
.kn { font-weight: bold; }
.kp { font-weight: bold; }
.kr { font-weight: bold; }
.kt { color: #458; font-weight: bold; }
.m { color: #099; }
.s { color: #d14; }
.n { color: #333; }
.na { color: teal; }
.nb { color: #0086b3; }
.nc { color: #458; font-weight: bold; }
.no { color: teal; }
.ni { color: purple; }
.ne { color: #900; font-weight: bold; }
.nf { color: #900; font-weight: bold; }
.nn { color: #555; }
.nt { color: navy; }
.nv { color: teal; }
.ow { font-weight: bold; }
.w { color: #bbb; }
.mf { color: #099; }
.mh { color: #099; }
.mi { color: #099; }
.mo { color: #099; }
.sb { color: #d14; }
.sc { color: #d14; }
.sd { color: #d14; }
.s2 { color: #d14; }
.se { color: #d14; }
.sh { color: #d14; }
.si { color: #d14; }
.sx { color: #d14; }
.sr { color: #009926; }
.s1 { color: #d14; }
.ss { color: #990073; }
.bp { color: #999; }
.vc { color: teal; }
.vg { color: teal; }
.vi { color: teal; }
.il { color: #099; }
.gc { color: #999; background-color: #eaf2f5; }

View file

@ -6,19 +6,19 @@ p.details {
font-style: italic; font-style: italic;
color: #777 color: #777
} }
.footer p { .footer > p {
font-size: small; font-size: small;
color: #777 color: #777
} }
pre.commit-message { pre.commit-message {
white-space: pre-wrap; white-space: pre-wrap;
} }
.file-stats a { .file-stats > a {
text-decoration: none; text-decoration: none;
} > .new-file {
.file-stats .new-file { color: #090;
color: #090; }
} > .deleted-file {
.file-stats .deleted-file { color: #b00;
color: #b00; }
} }

View file

@ -1,6 +1,4 @@
.awards { .awards {
line-height: 34px;
.emoji-icon { .emoji-icon {
width: 20px; width: 20px;
height: 20px; height: 20px;
@ -9,8 +7,6 @@
.emoji-menu { .emoji-menu {
position: absolute; position: absolute;
top: 100%;
left: 0;
margin-top: 3px; margin-top: 3px;
z-index: 1000; z-index: 1000;
min-width: 160px; min-width: 160px;
@ -23,7 +19,12 @@
opacity: 0; opacity: 0;
transform: scale(.2); transform: scale(.2);
transform-origin: 0 -45px; transform-origin: 0 -45px;
transition: all .3s cubic-bezier(.87,-.41,.19,1.44); transition: .3s cubic-bezier(.87,-.41,.19,1.44);
transition-property: transform, opacity;
&.is-aligned-right {
transform-origin: 100% -45px;
}
&.is-visible { &.is-visible {
pointer-events: all; pointer-events: all;
@ -94,20 +95,30 @@
.award-control { .award-control {
margin-right: 5px; margin-right: 5px;
margin-bottom: 5px;
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
line-height: 20px; line-height: 20px;
outline: 0; outline: 0;
&:hover,
&.active, &.active,
&:active { &:active {
background-color: $white-dark; background-color: $row-hover;
border-color: $row-hover-border;
box-shadow: none; box-shadow: none;
outline: 0; outline: 0;
} }
&.btn {
&:focus {
outline: 0;
}
}
&.is-loading { &.is-loading {
.award-control-icon { .award-control-icon-normal,
.emoji-icon {
display: none; display: none;
} }

View file

@ -3,12 +3,7 @@
background: #111; background: #111;
color: #fff; color: #fff;
font-family: $monospace_font; font-family: $monospace_font;
white-space: pre; white-space: pre-wrap;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
overflow: auto; overflow: auto;
overflow-y: hidden; overflow-y: hidden;
font-size: 12px; font-size: 12px;
@ -58,37 +53,92 @@
left: 70px; left: 70px;
} }
} }
}
.build-widget { .build-header {
padding: 10px; position: relative;
background: $background-color; padding-right: 40px;
margin-bottom: 20px;
border-radius: 4px;
.title { @media (min-width: $screen-sm-min) {
margin-top: 0; padding-right: 0;
color: #666; }
line-height: 1.5;
} a {
.attr-name { color: $gl-gray;
color: #777;
&:hover {
color: $gl-link-color;
text-decoration: none;
} }
} }
.alert-disabled { code {
background: $background-color; color: $code-color;
}
a { .avatar {
color: #3084bb !important; float: none;
} margin-right: 2px;
margin-left: 2px;
} }
} }
table.builds { table.builds {
.build-link { .build-link {
a { a {
color: $gl-dark-link-color; color: $gl-dark-link-color;
} }
} }
} }
.build-trace {
background: $ci-output-bg;
color: $ci-text-color;
white-space: pre;
overflow-x: auto;
font-size: 12px;
.fa-refresh {
font-size: 24px;
}
.bash {
display: block;
}
}
.right-sidebar.build-sidebar {
padding-top: $gl-padding;
padding-bottom: $gl-padding;
&.right-sidebar-collapsed {
display: none;
}
.block {
width: 100%;
}
.build-sidebar-header {
padding-top: 0;
.gutter-toggle {
margin-top: 0;
}
}
}
.build-detail-row {
margin-bottom: 5px;
}
.build-light-text {
color: $gl-placeholder-color;
}
.build-gutter-toggle {
position: absolute;
top: 50%;
right: 0;
margin-top: -17px;
}

View file

@ -26,8 +26,28 @@
.commit-info-row { .commit-info-row {
margin-bottom: 10px; margin-bottom: 10px;
&.commit-info-row-header {
line-height: 34px;
@media (min-width: $screen-sm-min) {
margin-bottom: 0;
}
.commit-options-dropdown-caret {
@media (max-width: $screen-sm) {
margin-left: 0;
}
}
}
.avatar { .avatar {
@extend .avatar-inline; @extend .avatar-inline;
margin-left: 0;
@media (min-width: $screen-sm-min) {
margin-left: 4px;
}
} }
.commit-committer-link, .commit-committer-link,
.commit-author-link { .commit-author-link {
@ -35,10 +55,6 @@
font-weight: bold; font-weight: bold;
} }
.time_ago {
margin-left: 8px;
}
.fa-clipboard { .fa-clipboard {
color: $dropdown-title-btn-color; color: $dropdown-title-btn-color;
} }

View file

@ -2,13 +2,21 @@
margin-bottom: 20px; margin-bottom: 20px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
> h1 { > h1, h2, h3, h4, h5, h6 {
font-weight: 400; font-weight: 400;
} }
.lead { .lead {
margin-bottom: 20px; margin-bottom: 20px;
} }
ul, ol {
padding-left: 0;
}
li {
list-style-type: none;
}
} }
.confirmation-content { .confirmation-content {

View file

@ -29,8 +29,6 @@
margin-top: 6px; margin-top: 6px;
p { p {
overflow-x: auto;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }

View file

@ -0,0 +1,5 @@
.environments {
.commit-title {
margin: 0;
}
}

View file

@ -39,3 +39,20 @@
} }
} }
} }
.groups-cover-block {
.container-fluid {
position: relative;
}
.access-request-button {
@include btn-gray;
position: absolute;
right: 16px;
bottom: 32px;
padding: 3px 10px;
text-transform: none;
background-color: $background-color;
}
}

View file

@ -29,11 +29,15 @@
} }
} }
.issuable-sidebar { .right-sidebar {
a { a {
color: inherit; color: inherit;
} }
.issuable-header-text {
margin-top: 7px;
}
.block { .block {
@include clearfix; @include clearfix;
padding: $gl-padding 0; padding: $gl-padding 0;
@ -60,10 +64,6 @@
margin-top: 0; margin-top: 0;
} }
.issuable-count {
margin-top: 7px;
}
.gutter-toggle { .gutter-toggle {
margin-left: 20px; margin-left: 20px;
padding-left: 10px; padding-left: 10px;
@ -74,6 +74,10 @@
} }
} }
.block-first {
padding-top: 0;
}
.title { .title {
color: $gl-text-color; color: $gl-text-color;
margin-bottom: 10px; margin-bottom: 10px;
@ -246,7 +250,7 @@
} }
} }
.issuable-pager { .issuable-header-btn {
background: $gray-normal; background: $gray-normal;
border: 1px solid $border-gray-normal; border: 1px solid $border-gray-normal;
&:hover { &:hover {
@ -259,7 +263,7 @@
} }
} }
a:not(.issuable-pager) { a {
&:hover { &:hover {
color: $md-link-color; color: $md-link-color;
text-decoration: none; text-decoration: none;

View file

@ -40,11 +40,6 @@
} }
} }
.issue-search-form {
margin: 0;
height: 24px;
}
form.edit-issue { form.edit-issue {
margin: 0; margin: 0;
} }
@ -96,8 +91,3 @@ form.edit-issue {
.issue-form .select2-container { .issue-form .select2-container {
width: 250px !important; width: 250px !important;
} }
.issue-closed-by-widget {
color: $gl-text-color;
margin-left: 52px;
}

View file

@ -50,11 +50,26 @@
.label-row { .label-row {
.label-name { .label-name {
display: inline-block; display: block;
width: 200px; margin-bottom: 10px;
@media (max-width: $screen-xs-min) { @media (min-width: $screen-sm-min) {
display: block; display: inline-block;
width: 200px;
margin-bottom: 0;
}
}
.label-description {
display: block;
margin-bottom: 10px;
@media (min-width: $screen-sm-min) {
display: inline-block;
width: 40%;
margin-left: 10px;
margin-bottom: 0;
vertical-align: middle;
} }
} }
@ -68,10 +83,6 @@
padding: 3px 4px; padding: 3px 4px;
} }
.label-subscription {
display: inline-block;
}
.dropdown-labels-error { .dropdown-labels-error {
padding: 5px 10px; padding: 5px 10px;
margin-bottom: 10px; margin-bottom: 10px;
@ -79,62 +90,95 @@
color: $white-light; color: $white-light;
} }
@mixin labels-mobile {
@media (max-width: $screen-xs-min) {
display: block;
width: 100%;
margin-left: 0;
padding: 10px 0;
}
}
.manage-labels-list { .manage-labels-list {
.btn-action {
color: $gl-dark-link-color;
.prepend-left-10, .prepend-description-left { .fa {
display: inline-block; font-size: 18px;
width: 40%; vertical-align: middle;
vertical-align: middle; }
@include labels-mobile; &:hover {
color: $gl-link-color;
&.remove-row {
color: $gl-danger;
}
}
} }
.prepend-description-left { .dropdown {
width: 57%; @media (min-width: $screen-sm-min) {
float: right;
@include labels-mobile; }
} }
}
.pull-info-right {
float: right; .prioritized-labels {
margin-bottom: 30px;
@media (max-width: $screen-xs-min) {
float: none; .add-priority {
} display: none;
color: $gray-light;
.action-buttons { }
border-color: transparent; }
padding: 6px;
color: $gl-text-color; .other-labels {
.remove-priority {
&.label-subscribe-button { display: none;
padding-left: 0; }
} }
}
.toggle-priority {
i { display: inline-block;
color: $gl-text-color; vertical-align: middle;
}
button {
.append-right-20 { border-color: transparent;
a { padding: 5px 8px;
color: $gl-text-color; vertical-align: top;
} font-size: 14px;
@media (max-width: $screen-xs-min) { &:hover {
display: block; border-color: transparent;
margin-bottom: 10px; }
} }
}
.filtered-labels {
.label-row {
&:not(:last-child) {
margin-right: 5px;
}
}
.label-remove {
border-left: 1px solid rgba(0, 0, 0, .1);
z-index: 3;
}
.btn {
color: inherit;
}
}
.label-options-toggle {
width: 100%;
}
.label-subscribe-button {
.label-subscribe-button-loading {
display: none;
}
&.disabled {
.label-subscribe-button-icon {
display: none;
}
.label-subscribe-button-loading {
display: block;
} }
} }
} }

View file

@ -41,7 +41,7 @@
margin: 0; margin: 0;
margin-left: 20px; margin-left: 20px;
padding: 5px; padding: 5px;
padding-top: 12px; padding-top: 8px;
line-height: 20px; line-height: 20px;
&.right { &.right {
@ -79,11 +79,14 @@
} }
&.ci-failed, &.ci-failed,
&.ci-canceled,
&.ci-error { &.ci-error {
color: $gl-danger; color: $gl-danger;
} }
&.ci-canceled {
color: $gl-gray;
}
a.monospace { a.monospace {
color: inherit; color: inherit;
} }
@ -105,11 +108,39 @@
font-size: 17px; font-size: 17px;
margin: 5px 0; margin: 5px 0;
color: $gl-gray-dark; color: $gl-gray-dark;
&.has-conflicts .fa-exclamation-triangle {
color: $gl-warning;
}
} }
p:last-child { p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@media (max-width: $screen-sm-max) {
h4 {
font-size: 15px;
}
p {
font-size: 13px;
}
.btn,
.btn-group,
.accept-action {
width: 100%;
margin-bottom: 4px;
}
.accept-control {
width: 100%;
text-align: center;
margin: 0;
}
}
} }
.mr-widget-footer { .mr-widget-footer {
@ -280,11 +311,15 @@
background-color: $white-light; background-color: $white-light;
color: $gl-placeholder-color; color: $gl-placeholder-color;
} }
th,
td {
padding: 16px;
}
} }
} }
.merged-buttons {
.btn {
float: left;
&:not(:last-child) {
margin-right: 10px;
}
}
}

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