Commit fa890604 authored by Timothy Andrew's avatar Timothy Andrew

Merge remote-tracking branch 'origin/master' into 21170-cycle-analytics

parents 0b97b42d 95b9421a
*.erb *.erb
lib/gitlab/sanitizers/svg/whitelist.rb
...@@ -48,3 +48,4 @@ ...@@ -48,3 +48,4 @@
/vendor/bundle/* /vendor/bundle/*
/builds/* /builds/*
/shared/* /shared/*
/.gitlab_workhorse_secret
...@@ -82,7 +82,7 @@ update-knapsack: ...@@ -82,7 +82,7 @@ update-knapsack:
- export KNAPSACK_REPORT_PATH=knapsack/rspec_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export KNAPSACK_REPORT_PATH=knapsack/rspec_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true - export KNAPSACK_GENERATE_REPORT=true
- cp knapsack/rspec_report.json ${KNAPSACK_REPORT_PATH} - cp knapsack/rspec_report.json ${KNAPSACK_REPORT_PATH}
- knapsack rspec - knapsack rspec "--color --format documentation"
artifacts: artifacts:
expire_in: 31d expire_in: 31d
paths: paths:
...@@ -206,10 +206,15 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21 ...@@ -206,10 +206,15 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21
- bundle exec $CI_BUILD_NAME - bundle exec $CI_BUILD_NAME
rubocop: *exec rubocop: *exec
rake haml_lint: *exec
rake scss_lint: *exec rake scss_lint: *exec
rake brakeman: *exec rake brakeman: *exec
rake flog: *exec rake flog:
rake flay: *exec <<: *exec
allow_failure: yes
rake flay:
<<: *exec
allow_failure: yes
license_finder: *exec license_finder: *exec
rake downtime_check: *exec rake downtime_check: *exec
......
# Whether to ignore frontmatter at the beginning of HAML documents for
# frameworks such as Jekyll/Middleman
skip_frontmatter: false
exclude:
- 'vendor/**/*'
- 'spec/**/*'
linters:
AltText:
enabled: false
ClassAttributeWithStaticValue:
enabled: false
ClassesBeforeIds:
enabled: false
ConsecutiveComments:
enabled: false
ConsecutiveSilentScripts:
enabled: false
max_consecutive: 2
EmptyObjectReference:
enabled: true
EmptyScript:
enabled: true
FinalNewline:
enabled: false
present: true
HtmlAttributes:
enabled: false
ImplicitDiv:
enabled: false
LeadingCommentSpace:
enabled: false
LineLength:
enabled: false
max: 80
MultilinePipe:
enabled: false
MultilineScript:
enabled: true
ObjectReferenceAttributes:
enabled: true
RuboCop:
enabled: false
# These cops are incredibly noisy when it comes to HAML templates, so we
# ignore them.
ignored_cops:
- Lint/BlockAlignment
- Lint/EndAlignment
- Lint/Void
- Metrics/LineLength
- Style/AlignParameters
- Style/BlockNesting
- Style/ElseAlignment
- Style/FileName
- Style/FinalNewline
- Style/FrozenStringLiteralComment
- Style/IfUnlessModifier
- Style/IndentationWidth
- Style/Next
- Style/TrailingBlankLines
- Style/TrailingWhitespace
- Style/WhileUntilModifier
RubyComments:
enabled: false
SpaceBeforeScript:
enabled: false
SpaceInsideHashAttributes:
enabled: false
style: space
Indentation:
enabled: true
character: space # or tab
TagName:
enabled: true
TrailingWhitespace:
enabled: false
UnnecessaryInterpolation:
enabled: false
UnnecessaryStringOutput:
enabled: false
...@@ -767,26 +767,33 @@ Rails/ScopeArgs: ...@@ -767,26 +767,33 @@ Rails/ScopeArgs:
RSpec/AnyInstance: RSpec/AnyInstance:
Enabled: false Enabled: false
# Check that the first argument to the top level describe is the tested class or # Check for expectations where `be(...)` can replace `eql(...)`.
# module. RSpec/BeEql:
Enabled: false
# Check that the first argument to the top level describe is a constant.
RSpec/DescribeClass: RSpec/DescribeClass:
Enabled: false Enabled: false
# Use `described_class` for tested class / module. # Checks that tests use `described_class`.
RSpec/DescribedClass:
Enabled: false
# Checks that the second argument to `describe` specifies a method.
RSpec/DescribeMethod: RSpec/DescribeMethod:
Enabled: false Enabled: false
# Checks that the second argument to top level describe is the tested method # Checks if an example group does not include any tests.
# name. RSpec/EmptyExampleGroup:
RSpec/DescribedClass:
Enabled: false Enabled: false
CustomIncludeMethods: []
# Checks for long example. # Checks for long examples.
RSpec/ExampleLength: RSpec/ExampleLength:
Enabled: false Enabled: false
Max: 5 Max: 5
# Do not use should when describing your tests. # Checks that example descriptions do not start with "should".
RSpec/ExampleWording: RSpec/ExampleWording:
Enabled: false Enabled: false
CustomTransform: CustomTransform:
...@@ -795,6 +802,10 @@ RSpec/ExampleWording: ...@@ -795,6 +802,10 @@ RSpec/ExampleWording:
not: does not not: does not
IgnoredWords: [] IgnoredWords: []
# Checks for `expect(...)` calls containing literal values.
RSpec/ExpectActual:
Enabled: false
# Checks the file and folder naming of the spec file. # Checks the file and folder naming of the spec file.
RSpec/FilePath: RSpec/FilePath:
Enabled: false Enabled: false
...@@ -806,19 +817,65 @@ RSpec/FilePath: ...@@ -806,19 +817,65 @@ RSpec/FilePath:
RSpec/Focus: RSpec/Focus:
Enabled: true Enabled: true
# Checks the arguments passed to `before`, `around`, and `after`.
RSpec/HookArgument:
Enabled: false
EnforcedStyle: implicit
# Check that a consistent implict expectation style is used.
# TODO (rspeicher): Available in rubocop-rspec 1.8.0
# RSpec/ImplicitExpect:
# Enabled: true
# EnforcedStyle: is_expected
# Checks for the usage of instance variables. # Checks for the usage of instance variables.
RSpec/InstanceVariable: RSpec/InstanceVariable:
Enabled: false Enabled: false
# Checks for multiple top-level describes. # Checks for `subject` definitions that come after `let` definitions.
RSpec/LeadingSubject:
Enabled: false
# Checks unreferenced `let!` calls being used for test setup.
RSpec/LetSetup:
Enabled: false
# Check that chains of messages are not being stubbed.
RSpec/MessageChain:
Enabled: false
# Checks for consistent message expectation style.
RSpec/MessageExpectation:
Enabled: false
EnforcedStyle: allow
# Checks for multiple top level describes.
RSpec/MultipleDescribes: RSpec/MultipleDescribes:
Enabled: false Enabled: false
# Enforces the usage of the same method on all negative message expectations. # Checks if examples contain too many `expect` calls.
RSpec/MultipleExpectations:
Enabled: false
Max: 1
# Checks for explicitly referenced test subjects.
RSpec/NamedSubject:
Enabled: false
# Checks for nested example groups.
RSpec/NestedGroups:
Enabled: false
MaxNesting: 2
# Checks for consistent method usage for negating expectations.
RSpec/NotToNot: RSpec/NotToNot:
EnforcedStyle: not_to EnforcedStyle: not_to
Enabled: true Enabled: true
# Checks for stubbed test subjects.
RSpec/SubjectStub:
Enabled: false
# Prefer using verifying doubles over normal doubles. # Prefer using verifying doubles over normal doubles.
RSpec/VerifiedDoubles: RSpec/VerifiedDoubles:
Enabled: false Enabled: false
This diff is collapsed.
This diff is collapsed.
...@@ -91,19 +91,7 @@ This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs]. ...@@ -91,19 +91,7 @@ This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs].
## Implement design & UI elements ## Implement design & UI elements
### Design reference Please see the [UI Guide for building GitLab].
The GitLab design reference can be found in the [gitlab-design] project.
The designs are made using Antetype (`.atype` files). You can use the
[free Antetype viewer (Mac OSX only)] or grab an exported PNG from the design
(the PNG is 1:1).
The current designs can be found in the [`gitlab8.atype` file].
### UI development kit
Implemented UI elements can also be found at https://gitlab.com/help/ui. Please
note that this page isn't comprehensive at this time.
## Issue tracker ## Issue tracker
...@@ -289,6 +277,8 @@ request is as follows: ...@@ -289,6 +277,8 @@ request is as follows:
1. For more complex migrations, write tests. 1. For more complex migrations, write tests.
1. Merge requests **must** adhere to the [merge request performance 1. Merge requests **must** adhere to the [merge request performance
guidelines](doc/development/merge_request_performance_guidelines.md). guidelines](doc/development/merge_request_performance_guidelines.md).
1. For tests that use Capybara or PhantomJS, see this [article on how
to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
The **official merge window** is in the beginning of the month from the 1st to The **official merge window** is in the beginning of the month from the 1st to
the 7th day of the month. This is the best time to submit an MR and get the 7th day of the month. This is the best time to submit an MR and get
...@@ -489,7 +479,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor ...@@ -489,7 +479,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide" [doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide" [scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide" [newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design [UI Guide for building GitLab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/ui_guide.md
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
[license-finder-doc]: doc/development/licensing.md [license-finder-doc]: doc/development/licensing.md
...@@ -26,7 +26,7 @@ gem 'omniauth-auth0', '~> 1.4.1' ...@@ -26,7 +26,7 @@ gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6' gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-cas3', '~> 1.1.2' gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-facebook', '~> 3.0.0' gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-google-oauth2', '~> 0.4.1' gem 'omniauth-google-oauth2', '~> 0.4.1'
...@@ -53,7 +53,7 @@ gem 'browser', '~> 2.2' ...@@ -53,7 +53,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.6.3' gem 'gitlab_git', '~> 10.6.6'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes # GitLab fork with several improvements to original library. For full list of changes
...@@ -206,6 +206,9 @@ gem 'mousetrap-rails', '~> 1.4.6' ...@@ -206,6 +206,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'
# Faster JSON
gem 'oj', '~> 2.17.4'
# Parse time & duration # Parse time & duration
gem 'chronic', '~> 0.10.2' gem 'chronic', '~> 0.10.2'
gem 'chronic_duration', '~> 0.10.6' gem 'chronic_duration', '~> 0.10.6'
...@@ -295,9 +298,10 @@ group :development, :test do ...@@ -295,9 +298,10 @@ 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.41.2', require: false gem 'rubocop', '~> 0.42.0', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false gem 'rubocop-rspec', '~> 1.7.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'haml_lint', '~> 0.18.2', require: false
gem 'simplecov', '0.12.0', require: false gem 'simplecov', '0.12.0', require: false
gem 'flog', '~> 4.3.2', require: false gem 'flog', '~> 4.3.2', require: false
gem 'flay', '~> 2.6.1', require: false gem 'flay', '~> 2.6.1', require: false
......
...@@ -189,7 +189,7 @@ GEM ...@@ -189,7 +189,7 @@ GEM
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.49.0) excon (0.52.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)
...@@ -215,8 +215,8 @@ GEM ...@@ -215,8 +215,8 @@ GEM
flowdock (0.7.1) flowdock (0.7.1)
httparty (~> 0.7) httparty (~> 0.7)
multi_json multi_json
fog-aws (0.9.2) fog-aws (0.11.0)
fog-core (~> 1.27) fog-core (~> 1.38)
fog-json (~> 1.0) fog-json (~> 1.0)
fog-xml (~> 0.1) fog-xml (~> 0.1)
ipaddress (~> 0.8) ipaddress (~> 0.8)
...@@ -225,7 +225,7 @@ GEM ...@@ -225,7 +225,7 @@ GEM
fog-core (~> 1.27) fog-core (~> 1.27)
fog-json (~> 1.0) fog-json (~> 1.0)
fog-xml (~> 0.1) fog-xml (~> 0.1)
fog-core (1.40.0) fog-core (1.42.0)
builder builder
excon (~> 0.49) excon (~> 0.49)
formatador (~> 0.2) formatador (~> 0.2)
...@@ -279,7 +279,7 @@ GEM ...@@ -279,7 +279,7 @@ GEM
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab_git (10.6.3) gitlab_git (10.6.6)
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)
...@@ -322,11 +322,18 @@ GEM ...@@ -322,11 +322,18 @@ GEM
grape-entity (0.4.8) grape-entity (0.4.8)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
haml (4.0.7)
tilt
haml_lint (0.18.2)
haml (~> 4.0)
rake (>= 10, < 12)
rubocop (>= 0.36.0)
sysexits (~> 1.1)
hamlit (2.6.1) hamlit (2.6.1)
temple (~> 0.7.6) temple (~> 0.7.6)
thor thor
tilt tilt
hashie (3.4.3) hashie (3.4.4)
health_check (2.1.0) health_check (2.1.0)
rails (>= 4.0) rails (>= 4.0)
hipchat (1.5.2) hipchat (1.5.2)
...@@ -394,7 +401,7 @@ GEM ...@@ -394,7 +401,7 @@ GEM
mime-types (>= 1.16, < 4) mime-types (>= 1.16, < 4)
mail_room (0.8.0) mail_room (0.8.0)
method_source (0.8.2) method_source (0.8.2)
mime-types (2.99.2) mime-types (2.99.3)
mimemagic (0.3.0) mimemagic (0.3.0)
mini_portile2 (2.1.0) mini_portile2 (2.1.0)
minitest (5.7.0) minitest (5.7.0)
...@@ -420,6 +427,7 @@ GEM ...@@ -420,6 +427,7 @@ GEM
rack (>= 1.2, < 3) rack (>= 1.2, < 3)
octokit (4.3.0) octokit (4.3.0)
sawyer (~> 0.7.0, >= 0.5.3) sawyer (~> 0.7.0, >= 0.5.3)
oj (2.17.4)
omniauth (1.3.1) omniauth (1.3.1)
hashie (>= 1.2, < 4) hashie (>= 1.2, < 4)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
...@@ -437,7 +445,7 @@ GEM ...@@ -437,7 +445,7 @@ GEM
addressable (~> 2.3) addressable (~> 2.3)
nokogiri (~> 1.6.6) nokogiri (~> 1.6.6)
omniauth (~> 1.2) omniauth (~> 1.2)
omniauth-facebook (3.0.0) omniauth-facebook (4.0.0)
omniauth-oauth2 (~> 1.2) omniauth-oauth2 (~> 1.2)
omniauth-github (1.1.2) omniauth-github (1.1.2)
omniauth (~> 1.0) omniauth (~> 1.0)
...@@ -584,7 +592,7 @@ GEM ...@@ -584,7 +592,7 @@ GEM
railties (>= 4.2.0, < 5.1) railties (>= 4.2.0, < 5.1)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
rouge (2.0.5) rouge (2.0.6)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
...@@ -612,14 +620,14 @@ GEM ...@@ -612,14 +620,14 @@ GEM
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.5.0) rspec-support (3.5.0)
rubocop (0.41.2) rubocop (0.42.0)
parser (>= 2.3.1.1, < 3.0) parser (>= 2.3.1.1, < 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-rspec (1.7.0)
rubocop (>= 0.40.0) rubocop (>= 0.42.0)
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-prof (0.15.9) ruby-prof (0.15.9)
...@@ -723,6 +731,7 @@ GEM ...@@ -723,6 +731,7 @@ GEM
stringex (2.5.2) stringex (2.5.2)
sys-filesystem (1.1.6) sys-filesystem (1.1.6)
ffi ffi
sysexits (1.2.0)
systemu (2.6.5) systemu (2.6.5)
task_list (1.0.2) task_list (1.0.2)
html-pipeline html-pipeline
...@@ -754,7 +763,7 @@ GEM ...@@ -754,7 +763,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.1.0) unicode-display_width (1.1.1)
unicorn (4.9.0) unicorn (4.9.0)
kgio (~> 2.6) kgio (~> 2.6)
rack rack
...@@ -858,7 +867,7 @@ DEPENDENCIES ...@@ -858,7 +867,7 @@ DEPENDENCIES
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
github-markup (~> 1.4) github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.6.3) gitlab_git (~> 10.6.6)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
...@@ -866,6 +875,7 @@ DEPENDENCIES ...@@ -866,6 +875,7 @@ DEPENDENCIES
gon (~> 6.1.0) gon (~> 6.1.0)
grape (~> 0.15.0) grape (~> 0.15.0)
grape-entity (~> 0.4.2) grape-entity (~> 0.4.2)
haml_lint (~> 0.18.2)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
health_check (~> 2.1.0) health_check (~> 2.1.0)
hipchat (~> 1.5.0) hipchat (~> 1.5.0)
...@@ -895,12 +905,13 @@ DEPENDENCIES ...@@ -895,12 +905,13 @@ DEPENDENCIES
nokogiri (~> 1.6.7, >= 1.6.7.2) nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.2.0) oauth2 (~> 1.2.0)
octokit (~> 4.3.0) octokit (~> 4.3.0)
oj (~> 2.17.4)
omniauth (~> 1.3.1) omniauth (~> 1.3.1)
omniauth-auth0 (~> 1.4.1) omniauth-auth0 (~> 1.4.1)
omniauth-azure-oauth2 (~> 0.0.6) omniauth-azure-oauth2 (~> 0.0.6)
omniauth-bitbucket (~> 0.0.2) omniauth-bitbucket (~> 0.0.2)
omniauth-cas3 (~> 1.1.2) omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 3.0.0) omniauth-facebook (~> 4.0.0)
omniauth-github (~> 1.1.1) omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.0) omniauth-gitlab (~> 1.0.0)
omniauth-google-oauth2 (~> 0.4.1) omniauth-google-oauth2 (~> 0.4.1)
...@@ -935,8 +946,8 @@ DEPENDENCIES ...@@ -935,8 +946,8 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0) rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5) rspec-retry (~> 0.4.5)
rubocop (~> 0.41.2) rubocop (~> 0.42.0)
rubocop-rspec (~> 1.5.0) rubocop-rspec (~> 1.7.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.15.9) ruby-prof (~> 0.15.9)
sanitize (~> 2.0) sanitize (~> 2.0)
......
...@@ -3,10 +3,11 @@ ...@@ -3,10 +3,11 @@
[![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)
[![coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) [![coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
## Canonical source ## Canonical source
The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible. The cannonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/).
## Open source software to collaborate on code ## Open source software to collaborate on code
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
LabelManager.prototype.errorMessage = 'Unable to update label prioritization at this time'; LabelManager.prototype.errorMessage = 'Unable to update label prioritization at this time';
function LabelManager(opts) { function LabelManager(opts) {
// Defaults
var ref, ref1, ref2; var ref, ref1, ref2;
if (opts == null) { if (opts == null) {
opts = {}; opts = {};
...@@ -28,6 +29,7 @@ ...@@ -28,6 +29,7 @@
$btn = $(e.currentTarget); $btn = $(e.currentTarget);
$label = $("#" + ($btn.data('domId'))); $label = $("#" + ($btn.data('domId')));
action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add'; action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
// Make sure tooltip will hide
$tooltip = $("#" + ($btn.find('.has-tooltip:visible').attr('aria-describedby'))); $tooltip = $("#" + ($btn.find('.has-tooltip:visible').attr('aria-describedby')));
$tooltip.tooltip('destroy'); $tooltip.tooltip('destroy');
return _this.toggleLabelPriority($label, action); return _this.toggleLabelPriority($label, action);
...@@ -42,6 +44,7 @@ ...@@ -42,6 +44,7 @@
url = $label.find('.js-toggle-priority').data('url'); url = $label.find('.js-toggle-priority').data('url');
$target = this.prioritizedLabels; $target = this.prioritizedLabels;
$from = this.otherLabels; $from = this.otherLabels;
// Optimistic update
if (action === 'remove') { if (action === 'remove') {
$target = this.otherLabels; $target = this.otherLabels;
$from = this.prioritizedLabels; $from = this.prioritizedLabels;
...@@ -53,6 +56,7 @@ ...@@ -53,6 +56,7 @@
$target.find('.empty-message').addClass('hidden'); $target.find('.empty-message').addClass('hidden');
} }
$label.detach().appendTo($target); $label.detach().appendTo($target);
// Return if we are not persisting state
if (!persistState) { if (!persistState) {
return; return;
} }
...@@ -61,6 +65,7 @@ ...@@ -61,6 +65,7 @@
url: url, url: url,
type: 'DELETE' type: 'DELETE'
}); });
// Restore empty message
if (!$from.find('li').length) { if (!$from.find('li').length) {
$from.find('.empty-message').removeClass('hidden'); $from.find('.empty-message').removeClass('hidden');
} }
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
return callback(group); return callback(group);
}); });
}, },
// Return groups list. Filtered by query
// Only active groups retrieved
groups: function(query, skip_ldap, callback) { groups: function(query, skip_ldap, callback) {
var url = Api.buildUrl(Api.groupsPath); var url = Api.buildUrl(Api.groupsPath);
return $.ajax({ return $.ajax({
...@@ -38,6 +40,7 @@ ...@@ -38,6 +40,7 @@
return callback(groups); return callback(groups);
}); });
}, },
// Return namespaces list. Filtered by query
namespaces: function(query, callback) { namespaces: function(query, callback) {
var url = Api.buildUrl(Api.namespacesPath); var url = Api.buildUrl(Api.namespacesPath);
return $.ajax({ return $.ajax({
...@@ -52,6 +55,7 @@ ...@@ -52,6 +55,7 @@
return callback(namespaces); return callback(namespaces);
}); });
}, },
// Return projects list. Filtered by query
projects: function(query, order, callback) { projects: function(query, order, callback) {
var url = Api.buildUrl(Api.projectsPath); var url = Api.buildUrl(Api.projectsPath);
return $.ajax({ return $.ajax({
...@@ -82,6 +86,7 @@ ...@@ -82,6 +86,7 @@
return callback(message.responseJSON); return callback(message.responseJSON);
}); });
}, },
// Return group projects list. Filtered by query
groupProjects: function(group_id, query, callback) { groupProjects: function(group_id, query, callback) {
var url = Api.buildUrl(Api.groupProjectsPath) var url = Api.buildUrl(Api.groupProjectsPath)
.replace(':id', group_id); .replace(':id', group_id);
...@@ -97,6 +102,7 @@ ...@@ -97,6 +102,7 @@
return callback(projects); return callback(projects);
}); });
}, },
// Return text for a specific license
licenseText: function(key, data, callback) { licenseText: function(key, data, callback) {
var url = Api.buildUrl(Api.licensePath) var url = Api.buildUrl(Api.licensePath)
.replace(':key', key); .replace(':key', key);
......
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
/*= require jquery2 */ /*= require jquery2 */
/*= require jquery-ui/autocomplete */ /*= require jquery-ui/autocomplete */
/*= require jquery-ui/datepicker */ /*= require jquery-ui/datepicker */
...@@ -76,6 +82,7 @@ ...@@ -76,6 +82,7 @@
} }
}; };
// Disable button if text field is empty
window.disableButtonIfEmptyField = function(field_selector, button_selector) { window.disableButtonIfEmptyField = function(field_selector, button_selector) {
var closest_submit, field; var closest_submit, field;
field = $(field_selector); field = $(field_selector);
...@@ -92,6 +99,7 @@ ...@@ -92,6 +99,7 @@
}); });
}; };
// Disable button if any input field with given selector is empty
window.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) { window.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
var closest_submit, updateButtons; var closest_submit, updateButtons;
closest_submit = form.find(button_selector); closest_submit = form.find(button_selector);
...@@ -128,6 +136,8 @@ ...@@ -128,6 +136,8 @@
window.addEventListener("hashchange", shiftWindow); window.addEventListener("hashchange", shiftWindow);
window.onload = function() { window.onload = function() {
// Scroll the window to avoid the topnav bar
// https://github.com/twitter/bootstrap/issues/1768
if (location.hash) { if (location.hash) {
return setTimeout(shiftWindow, 100); return setTimeout(shiftWindow, 100);
} }
...@@ -149,6 +159,8 @@ ...@@ -149,6 +159,8 @@
return $(this).select().one('mouseup', function(e) { return $(this).select().one('mouseup', function(e) {
return e.preventDefault(); return e.preventDefault();
}); });
// Click a .js-select-on-focus field, select the contents
// Prevent a mouseup event from deselecting the input
}); });
$('.remove-row').bind('ajax:success', function() { $('.remove-row').bind('ajax:success', function() {
$(this).tooltip('destroy') $(this).tooltip('destroy')
...@@ -163,6 +175,7 @@ ...@@ -163,6 +175,7 @@
}); });
$('select.select2').select2({ $('select.select2').select2({
width: 'resolve', width: 'resolve',
// Initialize select2 selects
dropdownAutoWidth: true dropdownAutoWidth: true
}); });
$('.js-select2').bind('select2-close', function() { $('.js-select2').bind('select2-close', function() {
...@@ -170,25 +183,28 @@ ...@@ -170,25 +183,28 @@
$('.select2-container-active').removeClass('select2-container-active'); $('.select2-container-active').removeClass('select2-container-active');
return $(':focus').blur(); return $(':focus').blur();
}), 1); }), 1);
// Close select2 on escape
}); });
// Initialize tooltips
$body.tooltip({ $body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]', selector: '.has-tooltip, [data-toggle="tooltip"]',
placement: function(_, el) { placement: function(_, el) {
var $el; return $(el).data('placement') || 'bottom';
$el = $(el);
return $el.data('placement') || 'bottom';
} }
}); });
$('.trigger-submit').on('change', function() { $('.trigger-submit').on('change', function() {
return $(this).parents('form').submit(); return $(this).parents('form').submit();
// Form submitter
}); });
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true); gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
// Flash
if ((flash = $(".flash-container")).length > 0) { if ((flash = $(".flash-container")).length > 0) {
flash.click(function() { flash.click(function() {
return $(this).fadeOut(); return $(this).fadeOut();
}); });
flash.show(); flash.show();
} }
// Disable form buttons while a form is submitting
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function(e) { $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function(e) {
var buttons; var buttons;
buttons = $('[type="submit"]', this); buttons = $('[type="submit"]', this);
...@@ -209,6 +225,7 @@ ...@@ -209,6 +225,7 @@
} }
}); });
$('.account-box').hover(function() { $('.account-box').hover(function() {
// Show/Hide the profile menu when hovering the account box
return $(this).toggleClass('hover'); return $(this).toggleClass('hover');
}); });
$document.on('click', '.diff-content .js-show-suppressed-diff', function() { $document.on('click', '.diff-content .js-show-suppressed-diff', function() {
...@@ -216,6 +233,7 @@ ...@@ -216,6 +233,7 @@
$container = $(this).parent(); $container = $(this).parent();
$container.next('table').show(); $container.next('table').show();
return $container.remove(); return $container.remove();
// Commit show suppressed diff
}); });
$('.navbar-toggle').on('click', function() { $('.navbar-toggle').on('click', function() {
$('.header-content .title').toggle(); $('.header-content .title').toggle();
...@@ -223,6 +241,7 @@ ...@@ -223,6 +241,7 @@
$('.header-content .navbar-collapse').toggle(); $('.header-content .navbar-collapse').toggle();
return $('.navbar-toggle').toggleClass('active'); return $('.navbar-toggle').toggleClass('active');
}); });
// Show/hide comments on diff
$body.on("click", ".js-toggle-diff-comments", function(e) { $body.on("click", ".js-toggle-diff-comments", function(e) {
var $this = $(this); var $this = $(this);
$this.toggleClass('active'); $this.toggleClass('active');
...@@ -232,6 +251,7 @@ ...@@ -232,6 +251,7 @@
} else { } else {
notesHolders.hide(); notesHolders.hide();
} }
$this.trigger('blur');
return e.preventDefault(); return e.preventDefault();
}); });
$document.off("click", '.js-confirm-danger'); $document.off("click", '.js-confirm-danger');
...@@ -286,42 +306,9 @@ ...@@ -286,42 +306,9 @@
gl.awardsHandler = new AwardsHandler(); gl.awardsHandler = new AwardsHandler();
checkInitialSidebarSize(); checkInitialSidebarSize();
new Aside(); new Aside();
if ($window.width() < 1024 && $.cookie('pin_nav') === 'true') {
$.cookie('pin_nav', 'false', { // bind sidebar events
path: gon.relative_url_root || '/', new gl.Sidebar();
expires: 365 * 10
});
$('.page-with-sidebar').toggleClass('page-sidebar-collapsed page-sidebar-expanded').removeClass('page-sidebar-pinned');
$('.navbar-fixed-top').removeClass('header-pinned-nav');
}
$document.off('click', '.js-nav-pin').on('click', '.js-nav-pin', function(e) {
var $page, $pinBtn, $tooltip, $topNav, doPinNav, tooltipText;
e.preventDefault();
$pinBtn = $(e.currentTarget);
$page = $('.page-with-sidebar');
$topNav = $('.navbar-fixed-top');
$tooltip = $("#" + ($pinBtn.attr('aria-describedby')));
doPinNav = !$page.is('.page-sidebar-pinned');
tooltipText = 'Pin navigation';
$(this).toggleClass('is-active');
if (doPinNav) {
$page.addClass('page-sidebar-pinned');
$topNav.addClass('header-pinned-nav');
} else {
$tooltip.remove();
$page.removeClass('page-sidebar-pinned').toggleClass('page-sidebar-collapsed page-sidebar-expanded');
$topNav.removeClass('header-pinned-nav').toggleClass('header-collapsed header-expanded');
}
$.cookie('pin_nav', doPinNav, {
path: gon.relative_url_root || '/',
expires: 365 * 10
});
if ($.cookie('pin_nav') === 'true' || doPinNav) {
tooltipText = 'Unpin navigation';
}
$tooltip.find('.tooltip-inner').text(tooltipText);
return $pinBtn.attr('title', tooltipText).tooltip('fixTitle');
});
// Custom time ago // Custom time ago
gl.utils.shortTimeAgo($('.js-short-timeago')); gl.utils.shortTimeAgo($('.js-short-timeago'));
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
} }
Autosave.prototype.restore = function() { Autosave.prototype.restore = function() {
var e, error, text; var e, text;
if (window.localStorage == null) { if (window.localStorage == null) {
return; return;
} }
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
if ((text != null ? text.length : void 0) > 0) { if ((text != null ? text.length : void 0) > 0) {
try { try {
return window.localStorage.setItem(this.key, text); return window.localStorage.setItem(this.key, text);
} catch (undefined) {} } catch (error) {}
} else { } else {
return this.reset(); return this.reset();
} }
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
} }
try { try {
return window.localStorage.removeItem(this.key); return window.localStorage.removeItem(this.key);
} catch (undefined) {} } catch (error) {}
}; };
return Autosave; return Autosave;
......
...@@ -86,6 +86,8 @@ ...@@ -86,6 +86,8 @@
AwardsHandler.prototype.positionMenu = function($menu, $addBtn) { AwardsHandler.prototype.positionMenu = function($menu, $addBtn) {
var css, position; var css, position;
position = $addBtn.data('position'); position = $addBtn.data('position');
// The menu could potentially be off-screen or in a hidden overflow element
// So we position the element absolute in the body
css = { css = {
top: ($addBtn.offset().top + $addBtn.outerHeight()) + "px" top: ($addBtn.offset().top + $addBtn.outerHeight()) + "px"
}; };
...@@ -255,12 +257,12 @@ ...@@ -255,12 +257,12 @@
}; };
AwardsHandler.prototype.animateEmoji = function($emoji) { AwardsHandler.prototype.animateEmoji = function($emoji) {
var className; var className = 'pulse animated once short';
className = 'pulse animated';
$emoji.addClass(className); $emoji.addClass(className);
return setTimeout((function() {
return $emoji.removeClass(className); $emoji.on('webkitAnimationEnd animationEnd', function() {
}), 321); $(this).removeClass(className);
});
}; };
AwardsHandler.prototype.createEmoji = function(votesBlock, emoji) { AwardsHandler.prototype.createEmoji = function(votesBlock, emoji) {
...@@ -284,6 +286,7 @@ ...@@ -284,6 +286,7 @@
if (emojiIcon.length > 0) { if (emojiIcon.length > 0) {
unicodeName = emojiIcon.data('unicode-name'); unicodeName = emojiIcon.data('unicode-name');
} else { } else {
// Find by alias
unicodeName = $(".emoji-menu-content [data-aliases*=':" + emoji + ":']").data('unicode-name'); unicodeName = $(".emoji-menu-content [data-aliases*=':" + emoji + ":']").data('unicode-name');
} }
return "emoji-" + unicodeName; return "emoji-" + unicodeName;
...@@ -350,8 +353,10 @@ ...@@ -350,8 +353,10 @@
return function(ev) { return function(ev) {
var found_emojis, h5, term, ul; var found_emojis, h5, term, ul;
term = $(ev.target).val(); term = $(ev.target).val();
// Clean previous search results
$('ul.emoji-menu-search, h5.emoji-search').remove(); $('ul.emoji-menu-search, h5.emoji-search').remove();
if (term) { if (term) {
// Generate a search result block
h5 = $('<h5>').text('Search results'); h5 = $('<h5>').text('Search results');
found_emojis = _this.searchEmojis(term).show(); found_emojis = _this.searchEmojis(term).show();
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis); ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis);
......
/*= require jquery.ba-resize */ /*= require jquery.ba-resize */
/*= require autosize */ /*= require autosize */
(function() { (function() {
......
...@@ -5,6 +5,12 @@ ...@@ -5,6 +5,12 @@
container = $(this).closest(".js-details-container"); container = $(this).closest(".js-details-container");
return container.toggleClass("open"); return container.toggleClass("open");
}); });
// Show details content. Hides link after click.
//
// %div
// %a.js-details-expand
// %div.js-details-content
//
return $("body").on("click", ".js-details-expand", function(e) { return $("body").on("click", ".js-details-expand", function(e) {
$(this).next('.js-details-content').removeClass("hide"); $(this).next('.js-details-content').removeClass("hide");
$(this).hide(); $(this).hide();
......
// Quick Submit behavior
//
// When a child field of a form with a `js-quick-submit` class receives a
// "Meta+Enter" (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, the form
// is submitted.
//
/*= require extensions/jquery */ /*= require extensions/jquery */
//
// ### Example Markup
//
// <form action="/foo" class="js-quick-submit">
// <input type="text" />
// <textarea></textarea>
// <input type="submit" value="Submit" />
// </form>
//
(function() { (function() {
var isMac, keyCodeIs; var isMac, keyCodeIs;
...@@ -17,6 +31,7 @@ ...@@ -17,6 +31,7 @@
$(document).on('keydown.quick_submit', '.js-quick-submit', function(e) { $(document).on('keydown.quick_submit', '.js-quick-submit', function(e) {
var $form, $submit_button; var $form, $submit_button;
// Enter
if (!keyCodeIs(e, 13)) { if (!keyCodeIs(e, 13)) {
return; return;
} }
...@@ -33,8 +48,11 @@ ...@@ -33,8 +48,11 @@
return $form.submit(); return $form.submit();
}); });
// If the user tabs to a submit button on a `js-quick-submit` form, display a
// tooltip to let them know they could've used the hotkey
$(document).on('keyup.quick_submit', '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]', function(e) { $(document).on('keyup.quick_submit', '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]', function(e) {
var $this, title; var $this, title;
// Tab
if (!keyCodeIs(e, 9)) { if (!keyCodeIs(e, 9)) {
return; return;
} }
......
// Requires Input behavior
//
// When called on a form with input fields with the `required` attribute, the
// form's submit button will be disabled until all required fields have values.
//
/*= require extensions/jquery */ /*= require extensions/jquery */
//
// ### Example Markup
//
// <form class="js-requires-input">
// <input type="text" required="required">
// <input type="submit" value="Submit">
// </form>
//
(function() { (function() {
$.fn.requiresInput = function() { $.fn.requiresInput = function() {
var $button, $form, fieldSelector, requireInput, required; var $button, $form, fieldSelector, requireInput, required;
...@@ -11,14 +23,17 @@ ...@@ -11,14 +23,17 @@
requireInput = function() { requireInput = function() {
var values; var values;
values = _.map($(fieldSelector, $form), function(field) { values = _.map($(fieldSelector, $form), function(field) {
// Collect the input values of *all* required fields
return field.value; return field.value;
}); });
// Disable the button if any required fields are empty
if (values.length && _.any(values, _.isEmpty)) { if (values.length && _.any(values, _.isEmpty)) {
return $button.disable(); return $button.disable();
} else { } else {
return $button.enable(); return $button.enable();
} }
}; };
// Set initial button state
requireInput(); requireInput();
return $form.on('change input', fieldSelector, requireInput); return $form.on('change input', fieldSelector, requireInput);
}; };
...@@ -27,6 +42,8 @@ ...@@ -27,6 +42,8 @@
var $form, hideOrShowHelpBlock; var $form, hideOrShowHelpBlock;
$form = $('form.js-requires-input'); $form = $('form.js-requires-input');
$form.requiresInput(); $form.requiresInput();
// Hide or Show the help block when creating a new project
// based on the option selected
hideOrShowHelpBlock = function(form) { hideOrShowHelpBlock = function(form) {
var selected; var selected;
selected = $('.js-select-namespace option:selected'); selected = $('.js-select-namespace option:selected');
......
(function(w) { (function(w) {
$(function() { $(function() {
// Toggle button. Show/hide content inside parent container.
// Button does not change visibility. If button has icon - it changes chevron style.
//
// %div.js-toggle-container
// %a.js-toggle-button
// %div.js-toggle-content
//
$('body').on('click', '.js-toggle-button', function(e) { $('body').on('click', '.js-toggle-button', function(e) {
e.preventDefault(); e.preventDefault();
$(this) $(this)
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
autoDiscover: false, autoDiscover: false,
autoProcessQueue: false, autoProcessQueue: false,
url: form.attr('action'), url: form.attr('action'),
// Rails uses a hidden input field for PUT
// http://stackoverflow.com/questions/21056482/how-to-set-method-put-in-form-tag-in-rails
method: method, method: method,
clickable: true, clickable: true,
uploadMultiple: false, uploadMultiple: false,
...@@ -36,6 +38,7 @@ ...@@ -36,6 +38,7 @@
formData.append('commit_message', form.find('.js-commit-message').val()); formData.append('commit_message', form.find('.js-commit-message').val());
}); });
}, },
// Override behavior of adding error underneath preview
error: function(file, errorMessage) { error: function(file, errorMessage) {
var stripped; var stripped;
stripped = $("<div/>").html(errorMessage).text(); stripped = $("<div/>").html(errorMessage).text();
......
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
this.buildDropdown(); this.buildDropdown();
this.bindEvents(); this.bindEvents();
this.onFilenameUpdate(); this.onFilenameUpdate();
this.autosizeUpdateEvent = document.createEvent('Event');
this.autosizeUpdateEvent.initEvent('autosize:update', true, false);
} }
TemplateSelector.prototype.buildDropdown = function() { TemplateSelector.prototype.buildDropdown = function() {
...@@ -66,9 +69,16 @@ ...@@ -66,9 +69,16 @@
// be added by all subclasses. // be added by all subclasses.
}; };
// To be implemented on the extending class
// e.g.
// Api.gitignoreText item.name, @requestFileSuccess.bind(@)
TemplateSelector.prototype.requestFileSuccess = function(file, skipFocus) { TemplateSelector.prototype.requestFileSuccess = function(file, skipFocus) {
this.editor.setValue(file.content, 1); this.editor.setValue(file.content, 1);
if (!skipFocus) this.editor.focus(); if (!skipFocus) this.editor.focus();
if (this.editor instanceof jQuery) {
this.editor.get(0).dispatchEvent(this.autosizeUpdateEvent);
}
}; };
TemplateSelector.prototype.startLoadingSpinner = function() { TemplateSelector.prototype.startLoadingSpinner = function() {
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
return function() { return function() {
return $("#file-content").val(_this.editor.getValue()); return $("#file-content").val(_this.editor.getValue());
}; };
// Before a form submission, move the content from the Ace editor into the
// submitted textarea
})(this)); })(this));
this.initModePanesAndLinks(); this.initModePanesAndLinks();
new BlobLicenseSelectors({ new BlobLicenseSelectors({
......
...@@ -34,6 +34,11 @@ ...@@ -34,6 +34,11 @@
}, },
issues () { issues () {
this.$nextTick(() => { this.$nextTick(() => {
if (this.scrollHeight() <= this.listHeight() && this.list.issuesSize > this.list.issues.length) {
this.list.page++;
this.list.getIssues(false);
}
if (this.scrollHeight() > this.listHeight()) { if (this.scrollHeight() > this.listHeight()) {
this.showCount = true; this.showCount = true;
} else { } else {
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
if ($(allDeviceSelector.join(",")).length) { if ($(allDeviceSelector.join(",")).length) {
return; return;
} }
// Create all the elements
els = $.map(BREAKPOINTS, function(breakpoint) { els = $.map(BREAKPOINTS, function(breakpoint) {
return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>"; return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>";
}); });
...@@ -40,6 +41,7 @@ ...@@ -40,6 +41,7 @@
BreakpointInstance.prototype.getBreakpointSize = function() { BreakpointInstance.prototype.getBreakpointSize = function() {
var $visibleDevice; var $visibleDevice;
$visibleDevice = this.visibleDevice; $visibleDevice = this.visibleDevice;
// the page refreshed via turbolinks
if (!$visibleDevice().length) { if (!$visibleDevice().length) {
this.setup(); this.setup();
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
this.toggleSidebar = bind(this.toggleSidebar, this); this.toggleSidebar = bind(this.toggleSidebar, this);
this.updateDropdown = bind(this.updateDropdown, this); this.updateDropdown = bind(this.updateDropdown, this);
clearInterval(Build.interval); clearInterval(Build.interval);
// Init breakpoint checker
this.bp = Breakpoints.get(); this.bp = Breakpoints.get();
$('.js-build-sidebar').niceScroll(); $('.js-build-sidebar').niceScroll();
...@@ -26,10 +27,11 @@ ...@@ -26,10 +27,11 @@
$(document).off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar); $(document).off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
$(window).off('resize.build').on('resize.build', this.hideSidebar); $(window).off('resize.build').on('resize.build', this.hideSidebar);
$(document).off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown); $(document).off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
$('#js-build-scroll > a').off('click').on('click', this.stepTrace);
this.updateArtifactRemoveDate(); this.updateArtifactRemoveDate();
if ($('#build-trace').length) { if ($('#build-trace').length) {
this.getInitialBuildTrace(); this.getInitialBuildTrace();
this.initScrollButtonAffix(); this.initScrollButtons();
} }
if (this.build_status === "running" || this.build_status === "pending") { if (this.build_status === "running" || this.build_status === "pending") {
$('#autoscroll-button').on('click', function() { $('#autoscroll-button').on('click', function() {
...@@ -42,6 +44,9 @@ ...@@ -42,6 +44,9 @@
$(this).data("state", "enabled"); $(this).data("state", "enabled");
return $(this).text("disable autoscroll"); return $(this).text("disable autoscroll");
} }
//
// Bind autoscroll button to follow build output
//
}); });
Build.interval = setInterval((function(_this) { Build.interval = setInterval((function(_this) {
return function() { return function() {
...@@ -49,6 +54,10 @@ ...@@ -49,6 +54,10 @@
return _this.getBuildTrace(); return _this.getBuildTrace();
} }
}; };
//
// Check for new build output if user still watching build page
// Only valid for runnig build when output changes during time
//
})(this), 4000); })(this), 4000);
} }
} }
...@@ -98,7 +107,7 @@ ...@@ -98,7 +107,7 @@
} }
}; };
Build.prototype.initScrollButtonAffix = function() { Build.prototype.initScrollButtons = function() {
var $body, $buildScroll, $buildTrace; var $body, $buildScroll, $buildTrace;
$buildScroll = $('#js-build-scroll'); $buildScroll = $('#js-build-scroll');
$body = $('body'); $body = $('body');
...@@ -157,6 +166,14 @@ ...@@ -157,6 +166,14 @@
this.populateJobs(stage); this.populateJobs(stage);
}; };
Build.prototype.stepTrace = function(e) {
e.preventDefault();
$currentTarget = $(e.currentTarget);
$.scrollTo($currentTarget.attr('href'), {
offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
});
};
return Build; return Build;
})(); })();
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
this.ImageFile = (function() { this.ImageFile = (function() {
var prepareFrames; var prepareFrames;
// Width where images must fits in, for 2-up this gets divided by 2
ImageFile.availWidth = 900; ImageFile.availWidth = 900;
ImageFile.viewModes = ['two-up', 'swipe']; ImageFile.viewModes = ['two-up', 'swipe'];
...@@ -9,6 +10,7 @@ ...@@ -9,6 +10,7 @@
function ImageFile(file) { function ImageFile(file) {
this.file = file; this.file = file;
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) { this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
// Determine if old and new file has same dimensions, if not show 'two-up' view
return function(deletedWidth, deletedHeight) { return function(deletedWidth, deletedHeight) {
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) { return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
if (width === deletedWidth && height === deletedHeight) { if (width === deletedWidth && height === deletedHeight) {
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
CommitsList.content.html(data.html); CommitsList.content.html(data.html);
return history.replaceState({ return history.replaceState({
page: commitsUrl page: commitsUrl
// Change url so if user reload a page - search results are saved
}, document.title, commitsUrl); }, document.title, commitsUrl);
}, },
dataType: "json" dataType: "json"
......
...@@ -6,14 +6,19 @@ ...@@ -6,14 +6,19 @@
genericSuccess = function(e) { genericSuccess = function(e) {
showTooltip(e.trigger, 'Copied!'); showTooltip(e.trigger, 'Copied!');
// Clear the selection and blur the trigger so it loses its border
e.clearSelection(); e.clearSelection();
return $(e.trigger).blur(); return $(e.trigger).blur();
}; };
// Safari doesn't support `execCommand`, so instead we inform the user to
// copy manually.
//
// See http://clipboardjs.com/#browser-support
genericError = function(e) { genericError = function(e) {
var key; var key;
if (/Mac/i.test(navigator.userAgent)) { if (/Mac/i.test(navigator.userAgent)) {
key = '&#8984;'; key = '&#8984;'; // Command
} else { } else {
key = 'Ctrl'; key = 'Ctrl';
} }
......
...@@ -39,6 +39,9 @@ ...@@ -39,6 +39,9 @@
bottom: unfoldBottom, bottom: unfoldBottom,
offset: offset, offset: offset,
unfold: unfold, unfold: unfold,
// indent is used to compensate for single space indent to fit
// '+' and '-' prepended to diff lines,
// see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent: 1, indent: 1,
view: file.data('view') view: file.data('view')
}; };
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
case 'projects:boards:show': case 'projects:boards:show':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
break; break;
case 'projects:merge_requests:index':
case 'projects:issues:index': case 'projects:issues:index':
Issuable.init(); Issuable.init();
new IssuableBulkActions(); new IssuableBulkActions();
...@@ -92,7 +93,8 @@ ...@@ -92,7 +93,8 @@
new MergedButtons(); new MergedButtons();
break; break;
case "projects:merge_requests:conflicts": case "projects:merge_requests:conflicts":
new MergeConflictResolver() window.mcui = new MergeConflictResolver()
break;
case 'projects:merge_requests:index': case 'projects:merge_requests:index':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
Issuable.init(); Issuable.init();
...@@ -167,6 +169,8 @@ ...@@ -167,6 +169,8 @@
} }
break; break;
case 'projects:network:show': case 'projects:network:show':
// Ensure we don't create a particular shortcut handler here. This is
// already created, where the network graph is created.
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:forks:new': case 'projects:forks:new':
...@@ -266,12 +270,14 @@ ...@@ -266,12 +270,14 @@
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
} }
} }
// If we haven't installed a custom shortcut handler, install the default one
if (!shortcut_handler) { if (!shortcut_handler) {
return new Shortcuts(); return new Shortcuts();
} }
}; };
Dispatcher.prototype.initSearch = function() { Dispatcher.prototype.initSearch = function() {
// Only when search form is present
if ($('.search').length) { if ($('.search').length) {
return new SearchAutocomplete(); return new SearchAutocomplete();
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
this.DueDateSelect = (function() { this.DueDateSelect = (function() {
function DueDateSelect() { function DueDateSelect() {
var $datePicker, $dueDate, $loading; var $datePicker, $dueDate, $loading;
// Milestone edit/new form
$datePicker = $('.datepicker'); $datePicker = $('.datepicker');
if ($datePicker.length) { if ($datePicker.length) {
$dueDate = $('#milestone_due_date'); $dueDate = $('#milestone_due_date');
...@@ -16,6 +17,7 @@ ...@@ -16,6 +17,7 @@
e.preventDefault(); e.preventDefault();
return $.datepicker._clearDate($datePicker); return $.datepicker._clearDate($datePicker);
}); });
// Issuable sidebar
$loading = $('.js-issuable-update .due_date').find('.block-loading').hide(); $loading = $('.js-issuable-update .due_date').find('.block-loading').hide();
$('.js-due-date-select').each(function(i, dropdown) { $('.js-due-date-select').each(function(i, dropdown) {
var $block, $dropdown, $dropdownParent, $selectbox, $sidebarValue, $value, $valueContent, abilityName, addDueDate, fieldName, issueUpdateURL; var $block, $dropdown, $dropdownParent, $selectbox, $sidebarValue, $value, $valueContent, abilityName, addDueDate, fieldName, issueUpdateURL;
...@@ -38,6 +40,7 @@ ...@@ -38,6 +40,7 @@
}); });
addDueDate = function(isDropdown) { addDueDate = function(isDropdown) {
var data, date, mediumDate, value; var data, date, mediumDate, value;
// Create the post date
value = $("input[name='" + fieldName + "']").val(); value = $("input[name='" + fieldName + "']").val();
if (value !== '') { if (value !== '') {
date = new Date(value.replace(new RegExp('-', 'g'), ',')); date = new Date(value.replace(new RegExp('-', 'g'), ','));
......
// Disable an element and add the 'disabled' Bootstrap class
(function() { (function() {
$.fn.extend({ $.fn.extend({
disable: function() { disable: function() {
...@@ -5,6 +6,7 @@ ...@@ -5,6 +6,7 @@
} }
}); });
// Enable an element and remove the 'disabled' Bootstrap class
$.fn.extend({ $.fn.extend({
enable: function() { enable: function() {
return $(this).removeAttr('disabled').removeClass('disabled'); return $(this).removeAttr('disabled').removeClass('disabled');
......
// Creates the variables for setting up GFM auto-completion
(function() { (function() {
if (window.GitLab == null) { if (window.GitLab == null) {
window.GitLab = {}; window.GitLab = {};
...@@ -8,18 +9,22 @@ ...@@ -8,18 +9,22 @@
dataLoaded: false, dataLoaded: false,
cachedData: {}, cachedData: {},
dataSource: '', dataSource: '',
// Emoji
Emoji: { Emoji: {
template: '<li>${name} <img alt="${name}" height="20" src="${path}" width="20" /></li>' template: '<li>${name} <img alt="${name}" height="20" src="${path}" width="20" /></li>'
}, },
// Team Members
Members: { Members: {
template: '<li>${username} <small>${title}</small></li>' template: '<li>${username} <small>${title}</small></li>'
}, },
Labels: { Labels: {
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>' template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
}, },
// Issues and MergeRequests
Issues: { Issues: {
template: '<li><small>${id}</small> ${title}</li>' template: '<li><small>${id}</small> ${title}</li>'
}, },
// Milestones
Milestones: { Milestones: {
template: '<li>${title}</li>' template: '<li>${title}</li>'
}, },
...@@ -48,8 +53,11 @@ ...@@ -48,8 +53,11 @@
} }
}, },
setup: function(input) { setup: function(input) {
// Add GFM auto-completion to all input fields, that accept GFM input.
this.input = input || $('.js-gfm-input'); this.input = input || $('.js-gfm-input');
// destroy previous instances
this.destroyAtWho(); this.destroyAtWho();
// set up instances
this.setupAtWho(); this.setupAtWho();
if (this.dataSource) { if (this.dataSource) {
if (!this.dataLoading && !this.cachedData) { if (!this.dataLoading && !this.cachedData) {
...@@ -63,6 +71,11 @@ ...@@ -63,6 +71,11 @@
return _this.loadData(data); return _this.loadData(data);
}); });
}; };
// We should wait until initializations are done
// and only trigger the last .setup since
// The previous .dataSource belongs to the previous issuable
// and the last one will have the **proper** .dataSource property
// TODO: Make this a singleton and turn off events when moving to another page
})(this), 1000); })(this), 1000);
} }
if (this.cachedData != null) { if (this.cachedData != null) {
...@@ -71,6 +84,7 @@ ...@@ -71,6 +84,7 @@
} }
}, },
setupAtWho: function() { setupAtWho: function() {
// Emoji
this.input.atwho({ this.input.atwho({
at: ':', at: ':',
displayTpl: (function(_this) { displayTpl: (function(_this) {
...@@ -90,6 +104,7 @@ ...@@ -90,6 +104,7 @@
beforeInsert: this.DefaultOptions.beforeInsert beforeInsert: this.DefaultOptions.beforeInsert
} }
}); });
// Team Members
this.input.atwho({ this.input.atwho({
at: '@', at: '@',
displayTpl: (function(_this) { displayTpl: (function(_this) {
...@@ -321,13 +336,22 @@ ...@@ -321,13 +336,22 @@
loadData: function(data) { loadData: function(data) {
this.cachedData = data; this.cachedData = data;
this.dataLoaded = true; this.dataLoaded = true;
// load members
this.input.atwho('load', '@', data.members); this.input.atwho('load', '@', data.members);
// load issues
this.input.atwho('load', 'issues', data.issues); this.input.atwho('load', 'issues', data.issues);
// load milestones
this.input.atwho('load', 'milestones', data.milestones); this.input.atwho('load', 'milestones', data.milestones);
// load merge requests
this.input.atwho('load', 'mergerequests', data.mergerequests); this.input.atwho('load', 'mergerequests', data.mergerequests);
// load emojis
this.input.atwho('load', ':', data.emojis); this.input.atwho('load', ':', data.emojis);
// load labels
this.input.atwho('load', '~', data.labels); this.input.atwho('load', '~', data.labels);
// load commands
this.input.atwho('load', '/', data.commands); this.input.atwho('load', '/', data.commands);
// This trigger at.js again
// otherwise we would be stuck with loading until the user types
return $(':focus').trigger('keyup'); return $(':focus').trigger('keyup');
} }
}; };
......
This diff is collapsed.
...@@ -3,12 +3,15 @@ ...@@ -3,12 +3,15 @@
function GLForm(form) { function GLForm(form) {
this.form = form; this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input'); this.textarea = this.form.find('textarea.js-gfm-input');
// Before we start, we should clean up any previous data for this form
this.destroy(); this.destroy();
// Setup the form
this.setupForm(); this.setupForm();
this.form.data('gl-form', this); this.form.data('gl-form', this);
} }
GLForm.prototype.destroy = function() { GLForm.prototype.destroy = function() {
// Clean form listeners
this.clearEventListeners(); this.clearEventListeners();
return this.form.data('gl-form', null); return this.form.data('gl-form', null);
}; };
...@@ -21,12 +24,15 @@ ...@@ -21,12 +24,15 @@
this.form.find('.div-dropzone').remove(); this.form.find('.div-dropzone').remove();
this.form.addClass('gfm-form'); this.form.addClass('gfm-form');
disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
// remove notify commit author checkbox for non-commit notes
GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
new DropzoneInput(this.form); new DropzoneInput(this.form);
autosize(this.textarea); autosize(this.textarea);
// form and textarea event listeners
this.addEventListeners(); this.addEventListeners();
gl.text.init(this.form); gl.text.init(this.form);
} }
// hide discard button
this.form.find('.js-note-discard').hide(); this.form.find('.js-note-discard').hide();
return this.form.show(); return this.form.show();
}; };
......
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
/*= require_tree . */ /*= require_tree . */
(function() { (function() {
}).call(this); }).call(this);
...@@ -204,6 +204,7 @@ ...@@ -204,6 +204,7 @@
function ContributorsAuthorGraph(data1) { function ContributorsAuthorGraph(data1) {
this.data = data1; this.data = data1;
// Don't split graph size in half for mobile devices.
if ($(window).width() < 768) { if ($(window).width() < 768) {
this.width = $('.content').width() - 80; this.width = $('.content').width() - 80;
} else { } else {
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
return _this.formatSelection.apply(_this, args); return _this.formatSelection.apply(_this, args);
}, },
dropdownCssClass: "ajax-groups-dropdown", dropdownCssClass: "ajax-groups-dropdown",
// we do not want to escape markup since we are displaying html in results
escapeMarkup: function(m) { escapeMarkup: function(m) {
return m; return m;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
Issuable.initTemplates(); Issuable.initTemplates();
Issuable.initSearch(); Issuable.initSearch();
Issuable.initChecks(); Issuable.initChecks();
Issuable.initResetFilters();
return Issuable.initLabelFilterRemove(); return Issuable.initLabelFilterRemove();
}, },
initTemplates: function() { initTemplates: function() {
...@@ -37,9 +38,11 @@ ...@@ -37,9 +38,11 @@
return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) { return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) {
var $button; var $button;
$button = $(this); $button = $(this);
// Remove the label input box
$('input[name="label_name[]"]').filter(function() { $('input[name="label_name[]"]').filter(function() {
return this.value === $button.data('label'); return this.value === $button.data('label');
}).remove(); }).remove();
// Submit the form to get new data
Issuable.filterResults($('.filter-form')); Issuable.filterResults($('.filter-form'));
return $('.js-label-select').trigger('update.label'); return $('.js-label-select').trigger('update.label');
}); });
...@@ -55,6 +58,17 @@ ...@@ -55,6 +58,17 @@
return Turbolinks.visit(issuesUrl); return Turbolinks.visit(issuesUrl);
}; };
})(this), })(this),
initResetFilters: function() {
$('.reset-filters').on('click', function(e) {
e.preventDefault();
const target = e.target;
const $form = $(target).parents('.js-filter-form');
const baseIssuesUrl = target.href;
$form.attr('action', baseIssuesUrl);
Turbolinks.visit(baseIssuesUrl);
});
},
initChecks: function() { initChecks: function() {
this.issuableBulkActions = $('.bulk-update').data('bulkActions'); this.issuableBulkActions = $('.bulk-update').data('bulkActions');
$('.check_all_issues').off('click').on('click', function() { $('.check_all_issues').off('click').on('click', function() {
...@@ -64,19 +78,22 @@ ...@@ -64,19 +78,22 @@
return $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(this)); return $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(this));
}, },
checkChanged: function() { checkChanged: function() {
var checked_issues, ids; const $checkedIssues = $('.selected_issue:checked');
checked_issues = $('.selected_issue:checked'); const $updateIssuesIds = $('#update_issuable_ids');
if (checked_issues.length > 0) { const $issuesOtherFilters = $('.issues-other-filters');
ids = $.map(checked_issues, function(value) { const $issuesBulkUpdate = $('.issues_bulk_update');
if ($checkedIssues.length > 0) {
let ids = $.map($checkedIssues, function(value) {
return $(value).data('id'); return $(value).data('id');
}); });
$('#update_issues_ids').val(ids); $updateIssuesIds.val(ids);
$('.issues-other-filters').hide(); $issuesOtherFilters.hide();
$('.issues_bulk_update').show(); $issuesBulkUpdate.show();
} else { } else {
$('#update_issues_ids').val([]); $updateIssuesIds.val([]);
$('.issues_bulk_update').hide(); $issuesBulkUpdate.hide();
$('.issues-other-filters').show(); $issuesOtherFilters.show();
this.issuableBulkActions.willUpdateLabels = false; this.issuableBulkActions.willUpdateLabels = false;
} }
return true; return true;
......
/*= require flash */ /*= require flash */
/*= require jquery.waitforimages */ /*= require jquery.waitforimages */
/*= require task_list */ /*= require task_list */
(function() { (function() {
...@@ -13,6 +9,7 @@ ...@@ -13,6 +9,7 @@
this.Issue = (function() { this.Issue = (function() {
function Issue() { function Issue() {
this.submitNoteForm = bind(this.submitNoteForm, this); this.submitNoteForm = bind(this.submitNoteForm, this);
// Prevent duplicate event bindings
this.disableTaskList(); this.disableTaskList();
if ($('a.btn-close').length) { if ($('a.btn-close').length) {
this.initTaskList(); this.initTaskList();
...@@ -99,6 +96,8 @@ ...@@ -99,6 +96,8 @@
url: $('form.js-issuable-update').attr('action'), url: $('form.js-issuable-update').attr('action'),
data: patchData data: patchData
}); });
// TODO (rspeicher): Make the issue description inline-editable like a note so
// that we can re-use its form here
}; };
Issue.prototype.initMergeRequests = function() { Issue.prototype.initMergeRequests = function() {
...@@ -128,6 +127,8 @@ ...@@ -128,6 +127,8 @@
Issue.prototype.initCanCreateBranch = function() { Issue.prototype.initCanCreateBranch = function() {
var $container; var $container;
$container = $('#new-branch'); $container = $('#new-branch');
// If the user doesn't have the required permissions the container isn't
// rendered at all.
if ($container.length === 0) { if ($container.length === 0) {
return; return;
} }
......
(function() { (function() {
this.IssuableBulkActions = (function() { this.IssuableBulkActions = (function() {
function IssuableBulkActions(opts) { function IssuableBulkActions(opts) {
// Set defaults
var ref, ref1, ref2; var ref, ref1, ref2;
if (opts == null) { if (opts == null) {
opts = {}; opts = {};
} }
this.container = (ref = opts.container) != null ? ref : $('.content'), this.form = (ref1 = opts.form) != null ? ref1 : this.getElement('.bulk-update'), this.issues = (ref2 = opts.issues) != null ? ref2 : this.getElement('.issues-list .issue'); this.container = (ref = opts.container) != null ? ref : $('.content'), this.form = (ref1 = opts.form) != null ? ref1 : this.getElement('.bulk-update'), this.issues = (ref2 = opts.issues) != null ? ref2 : this.getElement('.issuable-list > li');
// Save instance
this.form.data('bulkActions', this); this.form.data('bulkActions', this);
this.willUpdateLabels = false; this.willUpdateLabels = false;
this.bindEvents(); this.bindEvents();
// Fixes bulk-assign not working when navigating through pages
Issuable.initChecks(); Issuable.initChecks();
} }
...@@ -86,6 +89,7 @@ ...@@ -86,6 +89,7 @@
ref1 = this.getLabelsFromSelection(); ref1 = this.getLabelsFromSelection();
for (j = 0, len1 = ref1.length; j < len1; j++) { for (j = 0, len1 = ref1.length; j < len1; j++) {
id = ref1[j]; id = ref1[j];
// Only the ones that we are not going to keep
if (labelsToKeep.indexOf(id) === -1) { if (labelsToKeep.indexOf(id) === -1) {
result.push(id); result.push(id);
} }
...@@ -106,7 +110,7 @@ ...@@ -106,7 +110,7 @@
state_event: this.form.find('input[name="update[state_event]"]').val(), state_event: this.form.find('input[name="update[state_event]"]').val(),
assignee_id: this.form.find('input[name="update[assignee_id]"]').val(), assignee_id: this.form.find('input[name="update[assignee_id]"]').val(),
milestone_id: this.form.find('input[name="update[milestone_id]"]').val(), milestone_id: this.form.find('input[name="update[milestone_id]"]').val(),
issues_ids: this.form.find('input[name="update[issues_ids]"]').val(), issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(),
subscription_event: this.form.find('input[name="update[subscription_event]"]').val(), subscription_event: this.form.find('input[name="update[subscription_event]"]').val(),
add_label_ids: [], add_label_ids: [],
remove_label_ids: [] remove_label_ids: []
...@@ -147,6 +151,8 @@ ...@@ -147,6 +151,8 @@
indeterminatedLabels = this.getUnmarkedIndeterminedLabels(); indeterminatedLabels = this.getUnmarkedIndeterminedLabels();
labelsToApply = this.getLabelsToApply(); labelsToApply = this.getLabelsToApply();
indeterminatedLabels.map(function(id) { indeterminatedLabels.map(function(id) {
// We need to exclude label IDs that will be applied
// By not doing this will cause issues from selection to not add labels at all
if (labelsToApply.indexOf(id) === -1) { if (labelsToApply.indexOf(id) === -1) {
return result.push(id); return result.push(id);
} }
......
...@@ -26,13 +26,16 @@ ...@@ -26,13 +26,16 @@
var previewColor; var previewColor;
previewColor = $('input#label_color').val(); previewColor = $('input#label_color').val();
return $('div.label-color-preview').css('background-color', previewColor); return $('div.label-color-preview').css('background-color', previewColor);
// Updates the the preview color with the hex-color input
}; };
// Updates the preview color with a click on a suggested color
Labels.prototype.setSuggestedColor = function(e) { Labels.prototype.setSuggestedColor = function(e) {
var color; var color;
color = $(e.currentTarget).data('color'); color = $(e.currentTarget).data('color');
$('input#label_color').val(color); $('input#label_color').val(color);
this.updateColorPreview(); this.updateColorPreview();
// Notify the form, that color has changed
$('.label-form').trigger('keyup'); $('.label-form').trigger('keyup');
return e.preventDefault(); return e.preventDefault();
}; };
......
...@@ -156,15 +156,17 @@ ...@@ -156,15 +156,17 @@
selectedClass.push('is-indeterminate'); selectedClass.push('is-indeterminate');
} }
if (active.indexOf(label.id) !== -1) { if (active.indexOf(label.id) !== -1) {
// Remove is-indeterminate class if the item will be marked as active
i = selectedClass.indexOf('is-indeterminate'); i = selectedClass.indexOf('is-indeterminate');
if (i !== -1) { if (i !== -1) {
selectedClass.splice(i, 1); selectedClass.splice(i, 1);
} }
selectedClass.push('is-active'); selectedClass.push('is-active');
// Add input manually
instance.addInput(this.fieldName, label.id); instance.addInput(this.fieldName, label.id);
} }
} }
if ($form.find("input[type='hidden'][name='" + ($dropdown.data('fieldName')) + "'][value='" + escape(this.id(label)) + "']").length) { if (this.id(label) && $form.find("input[type='hidden'][name='" + ($dropdown.data('fieldName')) + "'][value='" + this.id(label).toString().replace(/'/g, '\\\'') + "']").length) {
selectedClass.push('is-active'); selectedClass.push('is-active');
} }
if ($dropdown.hasClass('js-multiselect') && removesAll) { if ($dropdown.hasClass('js-multiselect') && removesAll) {
...@@ -172,6 +174,7 @@ ...@@ -172,6 +174,7 @@
} }
if (label.duplicate) { if (label.duplicate) {
spacing = 100 / label.color.length; spacing = 100 / label.color.length;
// Reduce the colors to 4
label.color = label.color.filter(function(color, i) { label.color = label.color.filter(function(color, i) {
return i < 4; return i < 4;
}); });
...@@ -192,11 +195,13 @@ ...@@ -192,11 +195,13 @@
} else { } else {
colorEl = ''; colorEl = '';
} }
// We need to identify which items are actually labels
if (label.id) { if (label.id) {
selectedClass.push('label-item'); selectedClass.push('label-item');
$a.attr('data-label-id', label.id); $a.attr('data-label-id', label.id);
} }
$a.addClass(selectedClass.join(' ')).html(colorEl + " " + label.title); $a.addClass(selectedClass.join(' ')).html(colorEl + " " + label.title);
// Return generated html
return $li.html($a).prop('outerHTML'); return $li.html($a).prop('outerHTML');
}, },
persistWhenHide: $dropdown.data('persistWhenHide'), persistWhenHide: $dropdown.data('persistWhenHide'),
...@@ -238,6 +243,7 @@ ...@@ -238,6 +243,7 @@
isIssueIndex = page === 'projects:issues:index'; isIssueIndex = page === 'projects:issues:index';
isMRIndex = page === 'projects:merge_requests:index'; isMRIndex = page === 'projects:merge_requests:index';
$selectbox.hide(); $selectbox.hide();
// display:block overrides the hide-collapse rule
$value.removeAttr('style'); $value.removeAttr('style');
if (page === 'projects:boards:show') { if (page === 'projects:boards:show') {
return; return;
...@@ -255,6 +261,7 @@ ...@@ -255,6 +261,7 @@
} }
} }
if ($dropdown.hasClass('js-filter-bulk-update')) { if ($dropdown.hasClass('js-filter-bulk-update')) {
// If we are persisting state we need the classes
if (!this.options.persistWhenHide) { if (!this.options.persistWhenHide) {
return $dropdown.parent().find('.is-active, .is-indeterminate').removeClass(); return $dropdown.parent().find('.is-active, .is-indeterminate').removeClass();
} }
...@@ -324,7 +331,9 @@ ...@@ -324,7 +331,9 @@
if ($('.selected_issue:checked').length) { if ($('.selected_issue:checked').length) {
return; return;
} }
// Remove inputs
$('.issues_bulk_update .labels-filter input[type="hidden"]').remove(); $('.issues_bulk_update .labels-filter input[type="hidden"]').remove();
// Also restore button text
return $('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label'); return $('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label');
}; };
......
...@@ -10,11 +10,13 @@ ...@@ -10,11 +10,13 @@
}; };
$(function() { $(function() {
hideEndFade($('.scrolling-tabs')); var $scrollingTabs = $('.scrolling-tabs');
hideEndFade($scrollingTabs);
$(window).off('resize.nav').on('resize.nav', function() { $(window).off('resize.nav').on('resize.nav', function() {
return hideEndFade($('.scrolling-tabs')); return hideEndFade($scrollingTabs);
}); });
return $('.scrolling-tabs').on('scroll', function(event) { $scrollingTabs.off('scroll').on('scroll', function(event) {
var $this, currentPosition, maxPosition; var $this, currentPosition, maxPosition;
$this = $(this); $this = $(this);
currentPosition = $this.scrollLeft(); currentPosition = $this.scrollLeft();
...@@ -22,6 +24,23 @@ ...@@ -22,6 +24,23 @@
$this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0); $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0);
return $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1); return $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1);
}); });
$scrollingTabs.each(function () {
var $this = $(this),
scrollingTabWidth = $this.width(),
$active = $this.find('.active'),
activeWidth = $active.width();
if ($active.length) {
var offset = $active.offset().left + activeWidth;
if (offset > scrollingTabWidth - 30) {
var scrollLeft = scrollingTabWidth / 2;
scrollLeft = (offset - scrollLeft) - (activeWidth / 2);
$this.scrollLeft(scrollLeft);
}
}
});
}); });
}).call(this); }).call(this);
...@@ -3,5 +3,4 @@ ...@@ -3,5 +3,4 @@
(function() { (function() {
}).call(this); }).call(this);
...@@ -3,5 +3,4 @@ ...@@ -3,5 +3,4 @@
(function() { (function() {
}).call(this); }).call(this);
...@@ -3,5 +3,4 @@ ...@@ -3,5 +3,4 @@
(function() { (function() {
}).call(this); }).call(this);
/*= require raphael */ /*= require raphael */
/*= require g.raphael */ /*= require g.raphael */
/*= require g.bar */ /*= require g.bar */
(function() { (function() {
}).call(this); }).call(this);
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
if (setTimeago) { if (setTimeago) {
$timeagoEls.timeago(); $timeagoEls.timeago();
$timeagoEls.tooltip('destroy'); $timeagoEls.tooltip('destroy');
// Recreate with custom template
return $timeagoEls.tooltip({ return $timeagoEls.tooltip({
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
}); });
......
gl.emojiAliases = ->
JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>')
(function() {
gl.emojiAliases = function() {
return JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>');
};
}).call(this);
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
notification = new Notification(message, opts); notification = new Notification(message, opts);
setTimeout(function() { setTimeout(function() {
return notification.close(); return notification.close();
// Hide the notification after X amount of seconds
}, 8000); }, 8000);
if (onclick) { if (onclick) {
return notification.onclick = onclick; return notification.onclick = onclick;
...@@ -22,12 +23,16 @@ ...@@ -22,12 +23,16 @@
body: body, body: body,
icon: icon icon: icon
}; };
// Let's check if the browser supports notifications
if (!('Notification' in window)) { if (!('Notification' in window)) {
// do nothing
} else if (Notification.permission === 'granted') { } else if (Notification.permission === 'granted') {
// If it's okay let's create a notification
return notificationGranted(message, opts, onclick); return notificationGranted(message, opts, onclick);
} else if (Notification.permission !== 'denied') { } else if (Notification.permission !== 'denied') {
return Notification.requestPermission(function(permission) { return Notification.requestPermission(function(permission) {
// If the user accepts, let's create a notification
if (permission === 'granted') { if (permission === 'granted') {
return notificationGranted(message, opts, onclick); return notificationGranted(message, opts, onclick);
} }
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
lineBefore = this.lineBefore(text, textArea); lineBefore = this.lineBefore(text, textArea);
lineAfter = this.lineAfter(text, textArea); lineAfter = this.lineAfter(text, textArea);
if (lineBefore === blockTag && lineAfter === blockTag) { if (lineBefore === blockTag && lineAfter === blockTag) {
// To remove the block tag we have to select the line before & after
if (blockTag != null) { if (blockTag != null) {
textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1); textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1);
textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1); textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1);
...@@ -63,11 +64,11 @@ ...@@ -63,11 +64,11 @@
if (!inserted) { if (!inserted) {
try { try {
document.execCommand("ms-beginUndoUnit"); document.execCommand("ms-beginUndoUnit");
} catch (undefined) {} } catch (error) {}
textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText); textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText);
try { try {
document.execCommand("ms-endUndoUnit"); document.execCommand("ms-endUndoUnit");
} catch (undefined) {} } catch (error) {}
} }
return this.moveCursor(textArea, tag, wrap); return this.moveCursor(textArea, tag, wrap);
}; };
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
if ((base = w.gl).utils == null) { if ((base = w.gl).utils == null) {
base.utils = {}; base.utils = {};
} }
// Returns an array containing the value(s) of the
// of the key passed as an argument
w.gl.utils.getParameterValues = function(sParam) { w.gl.utils.getParameterValues = function(sParam) {
var i, sPageURL, sParameterName, sURLVariables, values; var i, sPageURL, sParameterName, sURLVariables, values;
sPageURL = decodeURIComponent(window.location.search.substring(1)); sPageURL = decodeURIComponent(window.location.search.substring(1));
...@@ -23,6 +25,8 @@ ...@@ -23,6 +25,8 @@
} }
return values; return values;
}; };
// @param {Object} params - url keys and value to merge
// @param {String} url
w.gl.utils.mergeUrlParams = function(params, url) { w.gl.utils.mergeUrlParams = function(params, url) {
var lastChar, newUrl, paramName, paramValue, pattern; var lastChar, newUrl, paramName, paramValue, pattern;
newUrl = decodeURIComponent(url); newUrl = decodeURIComponent(url);
...@@ -37,12 +41,14 @@ ...@@ -37,12 +41,14 @@
newUrl = "" + newUrl + (newUrl.indexOf('?') > 0 ? '&' : '?') + paramName + "=" + paramValue; newUrl = "" + newUrl + (newUrl.indexOf('?') > 0 ? '&' : '?') + paramName + "=" + paramValue;
} }
} }
// Remove a trailing ampersand
lastChar = newUrl[newUrl.length - 1]; lastChar = newUrl[newUrl.length - 1];
if (lastChar === '&') { if (lastChar === '&') {
newUrl = newUrl.slice(0, -1); newUrl = newUrl.slice(0, -1);
} }
return newUrl; return newUrl;
}; };
// removes parameter query string from url. returns the modified url
w.gl.utils.removeParamQueryString = function(url, param) { w.gl.utils.removeParamQueryString = function(url, param) {
var urlVariables, variables; var urlVariables, variables;
url = decodeURIComponent(url); url = decodeURIComponent(url);
......
// LineHighlighter
//
// Handles single- and multi-line selection and highlight for blob views.
//
/*= require jquery.scrollTo */ /*= require jquery.scrollTo */
//
// ### Example Markup
//
// <div id="blob-content-holder">
// <div class="file-content">
// <div class="line-numbers">
// <a href="#L1" id="L1" data-line-number="1">1</a>
// <a href="#L2" id="L2" data-line-number="2">2</a>
// <a href="#L3" id="L3" data-line-number="3">3</a>
// <a href="#L4" id="L4" data-line-number="4">4</a>
// <a href="#L5" id="L5" data-line-number="5">5</a>
// </div>
// <pre class="code highlight">
// <code>
// <span id="LC1" class="line">...</span>
// <span id="LC2" class="line">...</span>
// <span id="LC3" class="line">...</span>
// <span id="LC4" class="line">...</span>
// <span id="LC5" class="line">...</span>
// </code>
// </pre>
// </div>
// </div>
//
(function() { (function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.LineHighlighter = (function() { this.LineHighlighter = (function() {
// CSS class applied to highlighted lines
LineHighlighter.prototype.highlightClass = 'hll'; LineHighlighter.prototype.highlightClass = 'hll';
// Internal copy of location.hash so we're not dependent on `location` in tests
LineHighlighter.prototype._hash = ''; LineHighlighter.prototype._hash = '';
function LineHighlighter(hash) { function LineHighlighter(hash) {
var range; var range;
if (hash == null) { if (hash == null) {
// Initialize a LineHighlighter object
//
// hash - String URL hash for dependency injection in tests
hash = location.hash; hash = location.hash;
} }
this.setHash = bind(this.setHash, this); this.setHash = bind(this.setHash, this);
...@@ -24,6 +56,8 @@ ...@@ -24,6 +56,8 @@
if (range[0]) { if (range[0]) {
this.highlightRange(range); this.highlightRange(range);
$.scrollTo("#L" + range[0], { $.scrollTo("#L" + range[0], {
// Scroll to the first highlighted line on initial load
// Offset -50 for the sticky top bar, and another -100 for some context
offset: -150 offset: -150
}); });
} }
...@@ -32,6 +66,12 @@ ...@@ -32,6 +66,12 @@
LineHighlighter.prototype.bindEvents = function() { LineHighlighter.prototype.bindEvents = function() {
$('#blob-content-holder').on('mousedown', 'a[data-line-number]', this.clickHandler); $('#blob-content-holder').on('mousedown', 'a[data-line-number]', this.clickHandler);
// While it may seem odd to bind to the mousedown event and then throw away
// the click event, there is a method to our madness.
//
// If not done this way, the line number anchor will sometimes keep its
// active state even when the event is cancelled, resulting in an ugly border
// around the link and/or a persisted underline text decoration.
return $('#blob-content-holder').on('click', 'a[data-line-number]', function(event) { return $('#blob-content-holder').on('click', 'a[data-line-number]', function(event) {
return event.preventDefault(); return event.preventDefault();
}); });
...@@ -44,6 +84,8 @@ ...@@ -44,6 +84,8 @@
lineNumber = $(event.target).closest('a').data('line-number'); lineNumber = $(event.target).closest('a').data('line-number');
current = this.hashToRange(this._hash); current = this.hashToRange(this._hash);
if (!(current[0] && event.shiftKey)) { if (!(current[0] && event.shiftKey)) {
// If there's no current selection, or there is but Shift wasn't held,
// treat this like a single-line selection.
this.setHash(lineNumber); this.setHash(lineNumber);
return this.highlightLine(lineNumber); return this.highlightLine(lineNumber);
} else if (event.shiftKey) { } else if (event.shiftKey) {
...@@ -59,10 +101,23 @@ ...@@ -59,10 +101,23 @@
LineHighlighter.prototype.clearHighlight = function() { LineHighlighter.prototype.clearHighlight = function() {
return $("." + this.highlightClass).removeClass(this.highlightClass); return $("." + this.highlightClass).removeClass(this.highlightClass);
// Unhighlight previously highlighted lines
}; };
// Convert a URL hash String into line numbers
//
// hash - Hash String
//
// Examples:
//
// hashToRange('#L5') # => [5, null]
// hashToRange('#L5-15') # => [5, 15]
// hashToRange('#foo') # => [null, null]
//
// Returns an Array
LineHighlighter.prototype.hashToRange = function(hash) { LineHighlighter.prototype.hashToRange = function(hash) {
var first, last, matches; var first, last, matches;
//?L(\d+)(?:-(\d+))?$/)
matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/); matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/);
if (matches && matches.length) { if (matches && matches.length) {
first = parseInt(matches[1]); first = parseInt(matches[1]);
...@@ -73,10 +128,16 @@ ...@@ -73,10 +128,16 @@
} }
}; };
// Highlight a single line
//
// lineNumber - Line number to highlight
LineHighlighter.prototype.highlightLine = function(lineNumber) { LineHighlighter.prototype.highlightLine = function(lineNumber) {
return $("#LC" + lineNumber).addClass(this.highlightClass); return $("#LC" + lineNumber).addClass(this.highlightClass);
}; };
// Highlight all lines within a range
//
// range - Array containing the starting and ending line numbers
LineHighlighter.prototype.highlightRange = function(range) { LineHighlighter.prototype.highlightRange = function(range) {
var i, lineNumber, ref, ref1, results; var i, lineNumber, ref, ref1, results;
if (range[1]) { if (range[1]) {
...@@ -90,6 +151,7 @@ ...@@ -90,6 +151,7 @@
} }
}; };
// Set the URL hash string
LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) { LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
var hash; var hash;
if (lastLineNumber) { if (lastLineNumber) {
...@@ -101,10 +163,15 @@ ...@@ -101,10 +163,15 @@
return this.__setLocationHash__(hash); return this.__setLocationHash__(hash);
}; };
// Make the actual hash change in the browser
//
// This method is stubbed in tests.
LineHighlighter.prototype.__setLocationHash__ = function(value) { LineHighlighter.prototype.__setLocationHash__ = function(value) {
return history.pushState({ return history.pushState({
turbolinks: false, turbolinks: false,
url: value url: value
// We're using pushState instead of assigning location.hash directly to
// prevent the page from scrolling on the hashchange event
}, document.title, value); }, document.title, value);
}; };
......
/*= require jquery.waitforimages */ /*= require jquery.waitforimages */
/*= require task_list */ /*= require task_list */
/*= require merge_request_tabs */ /*= require merge_request_tabs */
(function() { (function() {
...@@ -12,6 +8,11 @@ ...@@ -12,6 +8,11 @@
this.MergeRequest = (function() { this.MergeRequest = (function() {
function MergeRequest(opts) { function MergeRequest(opts) {
// Initialize MergeRequest behavior
//
// Options:
// action - String, current controller action
//
this.opts = opts != null ? opts : {}; this.opts = opts != null ? opts : {};
this.submitNoteForm = bind(this.submitNoteForm, this); this.submitNoteForm = bind(this.submitNoteForm, this);
this.$el = $('.merge-request'); this.$el = $('.merge-request');
...@@ -21,6 +22,7 @@ ...@@ -21,6 +22,7 @@
}; };
})(this)); })(this));
this.initTabs(); this.initTabs();
// Prevent duplicate event bindings
this.disableTaskList(); this.disableTaskList();
this.initMRBtnListeners(); this.initMRBtnListeners();
if ($("a.btn-close").length) { if ($("a.btn-close").length) {
...@@ -28,14 +30,17 @@ ...@@ -28,14 +30,17 @@
} }
} }
// Local jQuery finder
MergeRequest.prototype.$ = function(selector) { MergeRequest.prototype.$ = function(selector) {
return this.$el.find(selector); return this.$el.find(selector);
}; };
MergeRequest.prototype.initTabs = function() { MergeRequest.prototype.initTabs = function() {
if (this.opts.action !== 'new') { if (this.opts.action !== 'new') {
// `MergeRequests#new` has no tab-persisting or lazy-loading behavior
window.mrTabs = new MergeRequestTabs(this.opts); window.mrTabs = new MergeRequestTabs(this.opts);
} else { } else {
// Show the first tab (Commits)
return $('.merge-request-tabs a[data-toggle="tab"]:first').tab('show'); return $('.merge-request-tabs a[data-toggle="tab"]:first').tab('show');
} }
}; };
...@@ -96,6 +101,8 @@ ...@@ -96,6 +101,8 @@
url: $('form.js-issuable-update').attr('action'), url: $('form.js-issuable-update').attr('action'),
data: patchData data: patchData
}); });
// TODO (rspeicher): Make the merge request description inline-editable like a
// note so that we can re-use its form here
}; };
return MergeRequest; return MergeRequest;
......
// MergeRequestTabs
//
// Handles persisting and restoring the current tab selection and lazily-loading
// content on the MergeRequests#show page.
//
/*= require jquery.cookie */ /*= require jquery.cookie */
//
// ### Example Markup
//
// <ul class="nav-links merge-request-tabs">
// <li class="notes-tab active">
// <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
// Discussion
// </a>
// </li>
// <li class="commits-tab">
// <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/merge_requests/1/commits">
// Commits
// </a>
// </li>
// <li class="diffs-tab">
// <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/merge_requests/1/diffs">
// Diffs
// </a>
// </li>
// </ul>
//
// <div class="tab-content">
// <div class="notes tab-pane active" id="notes">
// Notes Content
// </div>
// <div class="commits tab-pane" id="commits">
// Commits Content
// </div>
// <div class="diffs tab-pane" id="diffs">
// Diffs Content
// </div>
// </div>
//
// <div class="mr-loading-status">
// <div class="loading">
// Loading Animation
// </div>
// </div>
//
(function() { (function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
...@@ -19,6 +62,7 @@ ...@@ -19,6 +62,7 @@
this.setCurrentAction = bind(this.setCurrentAction, this); this.setCurrentAction = bind(this.setCurrentAction, this);
this.tabShown = bind(this.tabShown, this); this.tabShown = bind(this.tabShown, this);
this.showTab = bind(this.showTab, this); this.showTab = bind(this.showTab, this);
// Store the `location` object, allowing for easier stubbing in tests
this._location = location; this._location = location;
this.bindEvents(); this.bindEvents();
this.activateTab(this.opts.action); this.activateTab(this.opts.action);
...@@ -77,6 +121,7 @@ ...@@ -77,6 +121,7 @@
} }
}; };
// Activate a tab based on the current action
MergeRequestTabs.prototype.activateTab = function(action) { MergeRequestTabs.prototype.activateTab = function(action) {
if (action === 'show') { if (action === 'show') {
action = 'notes'; action = 'notes';
...@@ -84,20 +129,48 @@ ...@@ -84,20 +129,48 @@
return $(".merge-request-tabs a[data-action='" + action + "']").tab('show'); return $(".merge-request-tabs a[data-action='" + action + "']").tab('show');
}; };
// Replaces the current Merge Request-specific action in the URL with a new one
//
// If the action is "notes", the URL is reset to the standard
// `MergeRequests#show` route.
//
// Examples:
//
// location.pathname # => "/namespace/project/merge_requests/1"
// setCurrentAction('diffs')
// location.pathname # => "/namespace/project/merge_requests/1/diffs"
//
// location.pathname # => "/namespace/project/merge_requests/1/diffs"
// setCurrentAction('notes')
// location.pathname # => "/namespace/project/merge_requests/1"
//
// location.pathname # => "/namespace/project/merge_requests/1/diffs"
// setCurrentAction('commits')
// location.pathname # => "/namespace/project/merge_requests/1/commits"
//
// Returns the new URL String
MergeRequestTabs.prototype.setCurrentAction = function(action) { MergeRequestTabs.prototype.setCurrentAction = function(action) {
var new_state; var new_state;
// Normalize action, just to be safe
if (action === 'show') { if (action === 'show') {
action = 'notes'; action = 'notes';
} }
this.currentAction = action; this.currentAction = action;
// Remove a trailing '/commits' or '/diffs'
new_state = this._location.pathname.replace(/\/(commits|diffs|builds|pipelines)(\.html)?\/?$/, ''); new_state = this._location.pathname.replace(/\/(commits|diffs|builds|pipelines)(\.html)?\/?$/, '');
// Append the new action if we're on a tab other than 'notes'
if (action !== 'notes') { if (action !== 'notes') {
new_state += "/" + action; new_state += "/" + action;
} }
// Ensure parameters and hash come along for the ride
new_state += this._location.search + this._location.hash; new_state += this._location.search + this._location.hash;
history.replaceState({ history.replaceState({
turbolinks: true, turbolinks: true,
url: new_state url: new_state
// Replace the current history state with the new one without breaking
// Turbolinks' history.
//
// See https://github.com/rails/turbolinks/issues/363
}, document.title, new_state); }, document.title, new_state);
return new_state; return new_state;
}; };
...@@ -159,10 +232,10 @@ ...@@ -159,10 +232,10 @@
$('.hll').removeClass('hll'); $('.hll').removeClass('hll');
locationHash = window.location.hash; locationHash = window.location.hash;
if (locationHash !== '') { if (locationHash !== '') {
hashClassString = "." + (locationHash.replace('#', '')); dataLineString = '[data-line-code="' + locationHash.replace('#', '') + '"]';
$diffLine = $(locationHash + ":not(.match)", $('#diffs')); $diffLine = $(locationHash + ":not(.match)", $('#diffs'));
if (!$diffLine.is('tr')) { if (!$diffLine.is('tr')) {
$diffLine = $('#diffs').find("td" + locationHash + ", td" + hashClassString); $diffLine = $('#diffs').find("td" + locationHash + ", td" + dataLineString);
} else { } else {
$diffLine = $diffLine.find('td'); $diffLine = $diffLine.find('td');
} }
...@@ -206,6 +279,9 @@ ...@@ -206,6 +279,9 @@
}); });
}; };
// Show or hide the loading spinner
//
// status - Boolean, true to show, false to hide
MergeRequestTabs.prototype.toggleLoading = function(status) { MergeRequestTabs.prototype.toggleLoading = function(status) {
return $('.mr-loading-status .loading').toggle(status); return $('.mr-loading-status .loading').toggle(status);
}; };
...@@ -232,6 +308,7 @@ ...@@ -232,6 +308,7 @@
MergeRequestTabs.prototype.diffViewType = function() { MergeRequestTabs.prototype.diffViewType = function() {
return $('.inline-parallel-buttons a.active').data('view-type'); return $('.inline-parallel-buttons a.active').data('view-type');
// Returns diff view type
}; };
MergeRequestTabs.prototype.expandViewContainer = function() { MergeRequestTabs.prototype.expandViewContainer = function() {
...@@ -245,6 +322,8 @@ ...@@ -245,6 +322,8 @@
if ($gutterIcon.is('.fa-angle-double-right')) { if ($gutterIcon.is('.fa-angle-double-right')) {
return $gutterIcon.closest('a').trigger('click', [true]); return $gutterIcon.closest('a').trigger('click', [true]);
} }
// Wait until listeners are set
// Only when sidebar is expanded
}, 0); }, 0);
}; };
...@@ -259,6 +338,9 @@ ...@@ -259,6 +338,9 @@
return $gutterIcon.closest('a').trigger('click', [true]); return $gutterIcon.closest('a').trigger('click', [true]);
} }
}, 0); }, 0);
// Expand the issuable sidebar unless the user explicitly collapsed it
// Wait until listeners are set
// Only when sidebar is collapsed
}; };
return MergeRequestTabs; return MergeRequestTabs;
......
...@@ -3,6 +3,12 @@ ...@@ -3,6 +3,12 @@
this.MergeRequestWidget = (function() { this.MergeRequestWidget = (function() {
function MergeRequestWidget(opts) { function MergeRequestWidget(opts) {
// Initialize MergeRequestWidget behavior
//
// check_enable - Boolean, whether to check automerge status
// merge_check_url - String, URL to use to check automerge status
// ci_status_url - String, URL to use to check CI status
//
this.opts = opts; this.opts = opts;
$('#modal_merge_info').modal({ $('#modal_merge_info').modal({
show: false show: false
...@@ -118,6 +124,8 @@ ...@@ -118,6 +124,8 @@
if (data.coverage) { if (data.coverage) {
_this.showCICoverage(data.coverage); _this.showCICoverage(data.coverage);
} }
// The first check should only update the UI, a notification
// should only be displayed on status changes
if (showNotification && !_this.firstCICheck) { if (showNotification && !_this.firstCICheck) {
status = _this.ciLabelForStatus(data.status); status = _this.ciLabelForStatus(data.status);
if (status === "preparing") { if (status === "preparing") {
......
...@@ -110,6 +110,7 @@ ...@@ -110,6 +110,7 @@
}, },
update: function(event, ui) { update: function(event, ui) {
var data; var data;
// Prevents sorting from container which element has been removed.
if ($(this).find(ui.item).length > 0) { if ($(this).find(ui.item).length > 0) {
data = $(this).sortable("serialize"); data = $(this).sortable("serialize");
return Milestone.sortIssues(data); return Milestone.sortIssues(data);
......
...@@ -92,6 +92,7 @@ ...@@ -92,6 +92,7 @@
}, },
hidden: function() { hidden: function() {
$selectbox.hide(); $selectbox.hide();
// display:block overrides the hide-collapse rule
return $value.css('display', ''); return $value.css('display', '');
}, },
clicked: function(selected, $el, e) { clicked: function(selected, $el, e) {
......
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
results = []; results = [];
while (k < this.mspace) { while (k < this.mspace) {
this.colors.push(Raphael.getColor(.8)); this.colors.push(Raphael.getColor(.8));
// Skipping a few colors in the spectrum to get more contrast between colors
Raphael.getColor(); Raphael.getColor();
Raphael.getColor(); Raphael.getColor();
results.push(k++); results.push(k++);
...@@ -112,6 +113,7 @@ ...@@ -112,6 +113,7 @@
for (mm = j = 0, len = ref.length; j < len; mm = ++j) { for (mm = j = 0, len = ref.length; j < len; mm = ++j) {
day = ref[mm]; day = ref[mm];
if (cuday !== day[0] || cumonth !== day[1]) { if (cuday !== day[0] || cumonth !== day[1]) {
// Dates
r.text(55, this.offsetY + this.unitTime * mm, day[0]).attr({ r.text(55, this.offsetY + this.unitTime * mm, day[0]).attr({
font: "12px Monaco, monospace", font: "12px Monaco, monospace",
fill: "#BBB" fill: "#BBB"
...@@ -119,6 +121,7 @@ ...@@ -119,6 +121,7 @@
cuday = day[0]; cuday = day[0];
} }
if (cumonth !== day[1]) { if (cumonth !== day[1]) {
// Months
r.text(20, this.offsetY + this.unitTime * mm, day[1]).attr({ r.text(20, this.offsetY + this.unitTime * mm, day[1]).attr({
font: "12px Monaco, monospace", font: "12px Monaco, monospace",
fill: "#EEE" fill: "#EEE"
...@@ -207,6 +210,7 @@ ...@@ -207,6 +210,7 @@
} }
r = this.r; r = this.r;
shortrefs = commit.refs; shortrefs = commit.refs;
// Truncate if longer than 15 chars
if (shortrefs.length > 17) { if (shortrefs.length > 17) {
shortrefs = shortrefs.substr(0, 15) + ""; shortrefs = shortrefs.substr(0, 15) + "";
} }
...@@ -217,6 +221,7 @@ ...@@ -217,6 +221,7 @@
title: commit.refs title: commit.refs
}); });
textbox = text.getBBox(); textbox = text.getBBox();
// Create rectangle based on the size of the textbox
rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr({ rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr({
fill: "#000", fill: "#000",
"fill-opacity": .5, "fill-opacity": .5,
...@@ -229,6 +234,7 @@ ...@@ -229,6 +234,7 @@
}); });
label = r.set(rect, text); label = r.set(rect, text);
label.transform(["t", -rect.getBBox().width - 15, 0]); label.transform(["t", -rect.getBBox().width - 15, 0]);
// Set text to front
return text.toFront(); return text.toFront();
}; };
...@@ -283,11 +289,13 @@ ...@@ -283,11 +289,13 @@
parentY = this.offsetY + this.unitTime * parentCommit.time; parentY = this.offsetY + this.unitTime * parentCommit.time;
parentX1 = this.offsetX + this.unitSpace * (this.mspace - parentCommit.space); parentX1 = this.offsetX + this.unitSpace * (this.mspace - parentCommit.space);
parentX2 = this.offsetX + this.unitSpace * (this.mspace - parent[1]); parentX2 = this.offsetX + this.unitSpace * (this.mspace - parent[1]);
// Set line color
if (parentCommit.space <= commit.space) { if (parentCommit.space <= commit.space) {
color = this.colors[commit.space]; color = this.colors[commit.space];
} else { } else {
color = this.colors[parentCommit.space]; color = this.colors[parentCommit.space];
} }
// Build line shape
if (parent[1] === commit.space) { if (parent[1] === commit.space) {
offset = [0, 5]; offset = [0, 5];
arrow = "l-2,5,4,0,-2,-5,0,5"; arrow = "l-2,5,4,0,-2,-5,0,5";
...@@ -298,13 +306,17 @@ ...@@ -298,13 +306,17 @@
offset = [-3, 3]; offset = [-3, 3];
arrow = "l-5,0,2,4,3,-4,-4,2"; arrow = "l-5,0,2,4,3,-4,-4,2";
} }
// Start point
route = ["M", x + offset[0], y + offset[1]]; route = ["M", x + offset[0], y + offset[1]];
// Add arrow if not first parent
if (i > 0) { if (i > 0) {
route.push(arrow); route.push(arrow);
} }
// Circumvent if overlap
if (commit.space !== parentCommit.space || commit.space !== parent[1]) { if (commit.space !== parentCommit.space || commit.space !== parent[1]) {
route.push("L", parentX2, y + 10, "L", parentX2, parentY - 5); route.push("L", parentX2, y + 10, "L", parentX2, parentY - 5);
} }
// End point
route.push("L", parentX1, parentY); route.push("L", parentX1, parentY);
results.push(r.path(route).attr({ results.push(r.path(route).attr({
stroke: color, stroke: color,
...@@ -325,6 +337,7 @@ ...@@ -325,6 +337,7 @@
"fill-opacity": .5, "fill-opacity": .5,
stroke: "none" stroke: "none"
}); });
// Displayed in the center
return this.element.scrollTop(y - this.graphHeight / 2); return this.element.scrollTop(y - this.graphHeight / 2);
} }
}; };
......
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
/*= require_tree . */ /*= require_tree . */
(function() { (function() {
......
This diff is collapsed.
// MarkdownPreview
//
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
// and showing a warning when more than `x` users are referenced.
//
(function() { (function() {
var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector; var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector;
this.MarkdownPreview = (function() { this.MarkdownPreview = (function() {
function MarkdownPreview() {} function MarkdownPreview() {}
// Minimum number of users referenced before triggering a warning
MarkdownPreview.prototype.referenceThreshold = 10; MarkdownPreview.prototype.referenceThreshold = 10;
MarkdownPreview.prototype.ajaxCache = {}; MarkdownPreview.prototype.ajaxCache = {};
...@@ -101,8 +107,10 @@ ...@@ -101,8 +107,10 @@
return; return;
} }
lastTextareaPreviewed = $form.find('textarea.markdown-area'); lastTextareaPreviewed = $form.find('textarea.markdown-area');
// toggle tabs
$form.find(writeButtonSelector).parent().removeClass('active'); $form.find(writeButtonSelector).parent().removeClass('active');
$form.find(previewButtonSelector).parent().addClass('active'); $form.find(previewButtonSelector).parent().addClass('active');
// toggle content
$form.find('.md-write-holder').hide(); $form.find('.md-write-holder').hide();
$form.find('.md-preview-holder').show(); $form.find('.md-preview-holder').show();
return markdownPreview.showPreview($form); return markdownPreview.showPreview($form);
...@@ -113,8 +121,10 @@ ...@@ -113,8 +121,10 @@
return; return;
} }
lastTextareaPreviewed = null; lastTextareaPreviewed = null;
// toggle tabs
$form.find(writeButtonSelector).parent().addClass('active'); $form.find(writeButtonSelector).parent().addClass('active');
$form.find(previewButtonSelector).parent().removeClass('active'); $form.find(previewButtonSelector).parent().removeClass('active');
// toggle content
$form.find('.md-write-holder').show(); $form.find('.md-write-holder').show();
$form.find('textarea.markdown-area').focus(); $form.find('textarea.markdown-area').focus();
return $form.find('.md-preview-holder').hide(); return $form.find('.md-preview-holder').hide();
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
GitLabCrop = (function() { GitLabCrop = (function() {
var FILENAMEREGEX; var FILENAMEREGEX;
// Matches everything but the file name
FILENAMEREGEX = /^.*[\\\/]/; FILENAMEREGEX = /^.*[\\\/]/;
function GitLabCrop(input, opts) { function GitLabCrop(input, opts) {
...@@ -17,11 +18,18 @@ ...@@ -17,11 +18,18 @@
this.onModalShow = bind(this.onModalShow, this); this.onModalShow = bind(this.onModalShow, this);
this.onPickImageClick = bind(this.onPickImageClick, this); this.onPickImageClick = bind(this.onPickImageClick, this);
this.fileInput = $(input); this.fileInput = $(input);
// We should rename to avoid spec to fail
// Form will submit the proper input filed with a file using FormData
this.fileInput.attr('name', (this.fileInput.attr('name')) + "-trigger").attr('id', (this.fileInput.attr('id')) + "-trigger"); this.fileInput.attr('name', (this.fileInput.attr('name')) + "-trigger").attr('id', (this.fileInput.attr('id')) + "-trigger");
// Set defaults
this.exportWidth = (ref = opts.exportWidth) != null ? ref : 200, this.exportHeight = (ref1 = opts.exportHeight) != null ? ref1 : 200, this.cropBoxWidth = (ref2 = opts.cropBoxWidth) != null ? ref2 : 200, this.cropBoxHeight = (ref3 = opts.cropBoxHeight) != null ? ref3 : 200, this.form = (ref4 = opts.form) != null ? ref4 : this.fileInput.parents('form'), this.filename = opts.filename, this.previewImage = opts.previewImage, this.modalCrop = opts.modalCrop, this.pickImageEl = opts.pickImageEl, this.uploadImageBtn = opts.uploadImageBtn, this.modalCropImg = opts.modalCropImg; this.exportWidth = (ref = opts.exportWidth) != null ? ref : 200, this.exportHeight = (ref1 = opts.exportHeight) != null ? ref1 : 200, this.cropBoxWidth = (ref2 = opts.cropBoxWidth) != null ? ref2 : 200, this.cropBoxHeight = (ref3 = opts.cropBoxHeight) != null ? ref3 : 200, this.form = (ref4 = opts.form) != null ? ref4 : this.fileInput.parents('form'), this.filename = opts.filename, this.previewImage = opts.previewImage, this.modalCrop = opts.modalCrop, this.pickImageEl = opts.pickImageEl, this.uploadImageBtn = opts.uploadImageBtn, this.modalCropImg = opts.modalCropImg;
// Required params
// Ensure needed elements are jquery objects
// If selector is provided we will convert them to a jQuery Object
this.filename = this.getElement(this.filename); this.filename = this.getElement(this.filename);
this.previewImage = this.getElement(this.previewImage); this.previewImage = this.getElement(this.previewImage);
this.pickImageEl = this.getElement(this.pickImageEl); this.pickImageEl = this.getElement(this.pickImageEl);
// Modal elements usually are outside the @form element
this.modalCrop = _.isString(this.modalCrop) ? $(this.modalCrop) : this.modalCrop; this.modalCrop = _.isString(this.modalCrop) ? $(this.modalCrop) : this.modalCrop;
this.uploadImageBtn = _.isString(this.uploadImageBtn) ? $(this.uploadImageBtn) : this.uploadImageBtn; this.uploadImageBtn = _.isString(this.uploadImageBtn) ? $(this.uploadImageBtn) : this.uploadImageBtn;
this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg; this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
...@@ -93,8 +101,8 @@ ...@@ -93,8 +101,8 @@
return this.modalCropImg.attr('src', '').cropper('destroy'); return this.modalCropImg.attr('src', '').cropper('destroy');
}; };
GitLabCrop.prototype.onUploadImageBtnClick = function(e) { GitLabCrop.prototype.onUploadImageBtnClick = function(e) { // Remove attached image
e.preventDefault(); e.preventDefault(); // Destroy cropper instance
this.setBlob(); this.setBlob();
this.setPreview(); this.setPreview();
this.modalCrop.modal('hide'); this.modalCrop.modal('hide');
......
...@@ -11,9 +11,11 @@ ...@@ -11,9 +11,11 @@
this.form = (ref = opts.form) != null ? ref : $('.edit-user'); this.form = (ref = opts.form) != null ? ref : $('.edit-user');
$('.js-preferences-form').on('change.preference', 'input[type=radio]', function() { $('.js-preferences-form').on('change.preference', 'input[type=radio]', function() {
return $(this).parents('form').submit(); return $(this).parents('form').submit();
// Automatically submit the Preferences form when any of its radio buttons change
}); });
$('#user_notification_email').on('change', function() { $('#user_notification_email').on('change', function() {
return $(this).parents('form').submit(); return $(this).parents('form').submit();
// Automatically submit email form when it changes
}); });
$('.update-username').on('ajax:before', function() { $('.update-username').on('ajax:before', function() {
$('.loading-username').show(); $('.loading-username').show();
...@@ -76,6 +78,7 @@ ...@@ -76,6 +78,7 @@
}, },
complete: function() { complete: function() {
window.scrollTo(0, 0); window.scrollTo(0, 0);
// Enable submit button after requests ends
return self.form.find(':input[disabled]').enable(); return self.form.find(':input[disabled]').enable();
} }
}); });
...@@ -93,6 +96,7 @@ ...@@ -93,6 +96,7 @@
if (comment && comment.length > 1 && $title.val() === '') { if (comment && comment.length > 1 && $title.val() === '') {
return $title.val(comment[1]).change(); return $title.val(comment[1]).change();
} }
// Extract the SSH Key title from its comment
}); });
if (gl.utils.getPagePath() === 'profiles') { if (gl.utils.getPagePath() === 'profiles') {
return new Profile(); return new Profile();
......
...@@ -3,5 +3,4 @@ ...@@ -3,5 +3,4 @@
(function() { (function() {
}).call(this); }).call(this);
...@@ -11,7 +11,13 @@ ...@@ -11,7 +11,13 @@
url = $("#project_clone").val(); url = $("#project_clone").val();
$('#project_clone').val(url); $('#project_clone').val(url);
return $('.clone').text(url); return $('.clone').text(url);
// Git protocol switcher
// Remove the active class for all buttons (ssh, http, kerberos if shown)
// Add the active class for the clicked button
// Update the input field
// Update the command line instructions
}); });
// Ref switcher
this.initRefSwitcher(); this.initRefSwitcher();
$('.project-refs-select').on('change', function() { $('.project-refs-select').on('change', function() {
return $(this).parents('form').submit(); return $(this).parents('form').submit();
......
...@@ -13,8 +13,11 @@ ...@@ -13,8 +13,11 @@
this.selectRowUp = bind(this.selectRowUp, this); this.selectRowUp = bind(this.selectRowUp, this);
this.filePaths = {}; this.filePaths = {};
this.inputElement = this.element.find(".file-finder-input"); this.inputElement = this.element.find(".file-finder-input");
// init event
this.initEvent(); this.initEvent();
// focus text input box
this.inputElement.focus(); this.inputElement.focus();
// load file list
this.load(this.options.url); this.load(this.options.url);
} }
...@@ -42,6 +45,7 @@ ...@@ -42,6 +45,7 @@
} }
} }
}); });
// init event
}; };
ProjectFindFile.prototype.findFile = function() { ProjectFindFile.prototype.findFile = function() {
...@@ -49,8 +53,10 @@ ...@@ -49,8 +53,10 @@
searchText = this.inputElement.val(); searchText = this.inputElement.val();
result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths; result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
return this.renderList(result, searchText); return this.renderList(result, searchText);
// find file
}; };
// files pathes load
ProjectFindFile.prototype.load = function(url) { ProjectFindFile.prototype.load = function(url) {
return $.ajax({ return $.ajax({
url: url, url: url,
...@@ -67,6 +73,7 @@ ...@@ -67,6 +73,7 @@
}); });
}; };
// render result
ProjectFindFile.prototype.renderList = function(filePaths, searchText) { ProjectFindFile.prototype.renderList = function(filePaths, searchText) {
var blobItemUrl, filePath, html, i, j, len, matches, results; var blobItemUrl, filePath, html, i, j, len, matches, results;
this.element.find(".tree-table > tbody").empty(); this.element.find(".tree-table > tbody").empty();
...@@ -86,6 +93,7 @@ ...@@ -86,6 +93,7 @@
return results; return results;
}; };
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
highlighter = function(element, text, matches) { highlighter = function(element, text, matches) {
var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched; var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched;
lastIndex = 0; lastIndex = 0;
...@@ -110,6 +118,7 @@ ...@@ -110,6 +118,7 @@
return element.append(document.createTextNode(text.substring(lastIndex))); return element.append(document.createTextNode(text.substring(lastIndex)));
}; };
// make tbody row html
ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) { ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) {
var $tr; var $tr;
$tr = $("<tr class='tree-item'><td class='tree-item-file-name'><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'><a></a></span></td></tr>"); $tr = $("<tr class='tree-item'><td class='tree-item-file-name'><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'><a></a></span></td></tr>");
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment