Commit bf9db45a authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge remote-tracking branch 'origin/master' into ci-predefined-variables

parents 0aedeb56 b9ed9d65

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

......@@ -18,6 +18,7 @@ variables:
SIMPLECOV: "true"
USE_DB: "true"
USE_BUNDLE_INSTALL: "true"
GIT_DEPTH: "20"
before_script:
- source ./scripts/prepare_build.sh
......@@ -134,6 +135,11 @@ spinach 9 10: *spinach-knapsack
image: "ruby:2.3"
only:
- master
cache:
key: "ruby-23"
paths:
- vendor/apt
- vendor/ruby
.rspec-knapsack-ruby23: &rspec-knapsack-ruby23
<<: *rspec-knapsack
......
# Prefer single quotes
StringLiterals:
EnforcedStyle: single_quotes
Enabled: true
This diff is collapsed.
This diff is collapsed.
stage:
before:
- cp config/gitlab.teatro.yml config/gitlab.yml
- mkdir /apps/gitlab-satellites
- mkdir /apps/repositories
database:
- RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup
\ No newline at end of file
This diff is collapsed.
......@@ -145,7 +145,8 @@ might be edited to make them small and simple.
You are encouraged to use the template below for feature proposals.
```
## Description including problem, use cases, benefits, and/or goals
## Description
Include problem, use cases, benefits, and/or goals
## Proposal
......
source "https://rubygems.org"
source 'https://rubygems.org'
gem 'rails', '4.2.6'
gem 'rails', '4.2.7'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
......@@ -11,15 +11,15 @@ gem 'responders', '~> 2.0'
gem 'sprockets', '~> 3.6.0'
# Default values for AR models
gem "default_value_for", "~> 3.0.0"
gem 'default_value_for', '~> 3.0.0'
# Supported DBs
gem "mysql2", '~> 0.3.16', group: :mysql
gem "pg", '~> 0.18.2', group: :postgres
gem 'mysql2', '~> 0.3.16', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres
# Authentication libraries
gem 'devise', '~> 4.0'
gem 'doorkeeper', '~> 3.1'
gem 'doorkeeper', '~> 4.0'
gem 'omniauth', '~> 1.3.1'
gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
......@@ -28,9 +28,9 @@ gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-facebook', '~> 3.0.0'
gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-google-oauth2', '~> 0.2.0'
gem 'omniauth-google-oauth2', '~> 0.4.1'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-saml', '~> 1.5.0'
gem 'omniauth-saml', '~> 1.6.0'
gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0'
......@@ -48,24 +48,24 @@ gem 'attr_encrypted', '~> 3.0.0'
gem 'u2f', '~> 0.2.1'
# Browser detection
gem "browser", '~> 2.0.3'
gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '~> 10.2'
gem 'gitlab_git', '~> 10.3.2'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap"
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
# Git Wiki
# Required manually in config/initializers/gollum.rb to control load order
gem 'gollum-lib', '~> 4.1.0', require: false
gem 'gollum-lib', '~> 4.2', require: false
gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
# Language detection
gem "github-linguist", "~> 4.7.0", require: "linguist"
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API
gem 'grape', '~> 0.13.0'
......@@ -73,13 +73,13 @@ gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Pagination
gem "kaminari", "~> 0.17.0"
gem 'kaminari', '~> 0.17.0'
# HAML
gem "haml-rails", '~> 0.9.0'
gem 'hamlit', '~> 2.5'
# Files attachments
gem "carrierwave", '~> 0.10.0'
gem 'carrierwave', '~> 0.10.0'
# Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1'
......@@ -91,28 +91,29 @@ gem 'fog-core', '~> 1.40'
gem 'fog-local', '~> 0.3'
gem 'fog-google', '~> 0.3'
gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1'
# for aws storage
gem "unf", '~> 0.1.4'
gem 'unf', '~> 0.1.4'
# Authorization
gem "six", '~> 0.2.0'
gem 'six', '~> 0.2.0'
# Seed data
gem "seed-fu", '~> 2.3.5'
gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1'
gem 'github-markup', '~> 1.4'
gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.2.9'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.11'
gem 'rouge', '~> 2.0'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
......@@ -123,29 +124,29 @@ gem 'diffy', '~> 3.0.3'
# Application server
group :unicorn do
gem "unicorn", '~> 4.9.0'
gem 'unicorn', '~> 4.9.0'
gem 'unicorn-worker-killer', '~> 0.4.2'
end
# State machine
gem "state_machines-activerecord", '~> 0.4.0'
gem 'state_machines-activerecord', '~> 0.4.0'
# Run events after state machine commits
gem 'after_commit_queue'
gem 'after_commit_queue', '~> 1.3.0'
# Issue tags
gem 'acts-as-taggable-on', '~> 3.4'
# Background jobs
gem 'sinatra', '~> 1.4.4', require: nil
gem 'sinatra', '~> 1.4.4', require: false
gem 'sidekiq', '~> 4.0'
gem 'sidekiq-cron', '~> 0.4.0'
gem 'redis-namespace'
gem 'redis-namespace', '~> 1.5.2'
# HTTP requests
gem "httparty", '~> 0.13.3'
gem 'httparty', '~> 0.13.3'
# Colored output to console
gem "rainbow", '~> 2.1.0'
gem 'rainbow', '~> 2.1.0'
# GitLab settings
gem 'settingslogic', '~> 2.0.9'
......@@ -155,7 +156,7 @@ gem 'settingslogic', '~> 2.0.9'
gem 'version_sorter', '~> 2.0.0'
# Cache
gem "redis-rails", '~> 4.0.0'
gem 'redis-rails', '~> 4.0.0'
# Redis
gem 'redis', '~> 3.2'
......@@ -168,13 +169,13 @@ gem 'tinder', '~> 1.10.0'
gem 'hipchat', '~> 1.5.0'
# Flowdock integration
gem "gitlab-flowdock-git-hook", "~> 1.0.1"
gem 'gitlab-flowdock-git-hook', '~> 1.0.1'
# Gemnasium integration
gem "gemnasium-gitlab-service", "~> 0.2"
gem 'gemnasium-gitlab-service', '~> 0.2'
# Slack integration
gem "slack-notifier", "~> 1.2.0"
gem 'slack-notifier', '~> 1.2.0'
# Asana integration
gem 'asana', '~> 0.4.0'
......@@ -186,20 +187,20 @@ gem 'ruby-fogbugz', '~> 0.2.1'
gem 'd3_rails', '~> 3.5.0'
# underscore-rails
gem "underscore-rails", "~> 1.8.0"
gem 'underscore-rails', '~> 1.8.0'
# Sanitize user input
gem "sanitize", '~> 2.0'
gem 'sanitize', '~> 2.0'
gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input
gem "loofah", "~> 2.0.3"
gem 'loofah', '~> 2.0.3'
# Working with license
gem 'licensee', '~> 8.0.0'
# Protect against bruteforcing
gem "rack-attack", '~> 4.3.1'
gem 'rack-attack', '~> 4.3.1'
# Ace editor
gem 'ace-rails-ap', '~> 4.0.2'
......@@ -213,16 +214,16 @@ gem 'charlock_holmes', '~> 0.7.3'
# Parse duration
gem 'chronic_duration', '~> 0.10.6'
gem "sass-rails", '~> 5.0.0'
gem "coffee-rails", '~> 4.1.0'
gem "uglifier", '~> 2.7.2'
gem 'sass-rails', '~> 5.0.0'
gem 'coffee-rails', '~> 4.1.0'
gem 'uglifier', '~> 2.7.2'
gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.6.1'
gem 'gitlab_emoji', '~> 0.3.0'
gem 'gemojione', '~> 3.0'
gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0'
......@@ -234,7 +235,7 @@ gem 'net-ssh', '~> 3.0.1'
gem 'base32', '~> 0.3.0'
# Sentry integration
gem 'sentry-raven', '~> 0.15'
gem 'sentry-raven', '~> 1.1.0'
gem 'premailer-rails', '~> 1.9.0'
......@@ -246,14 +247,13 @@ group :metrics do
end
group :development do
gem "foreman"
gem 'foreman', '~> 0.78.0'
gem 'brakeman', '~> 3.3.0', require: false
gem 'letter_opener_web', '~> 1.3.0'
gem 'quiet_assets', '~> 1.0.2'
gem 'rerun', '~> 0.11.0'
gem 'bullet', require: false
gem 'rblineprof', platform: :mri, require: false
gem 'bullet', '~> 5.0.0', require: false
gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
gem 'web-console', '~> 2.0'
# Better errors handler
......@@ -261,23 +261,23 @@ group :development do
gem 'binding_of_caller', '~> 0.7.2'
# Docs generator
gem "sdoc", '~> 0.3.20'
gem 'sdoc', '~> 0.3.20'
# thin instead webrick
gem 'thin', '~> 1.6.1'
gem 'thin', '~> 1.7.0'
end
group :development, :test do
gem 'byebug', platform: :mri
gem 'pry-rails'
gem 'byebug', '~> 8.2.1', platform: :mri
gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.2.0', require: false
gem 'fuubar', '~> 2.0.0'
gem 'database_cleaner', '~> 1.4.0'
gem 'factory_girl_rails', '~> 4.6.0'
gem 'rspec-rails', '~> 3.4.0'
gem 'rspec-retry'
gem 'rspec-rails', '~> 3.5.0'
gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2'
......@@ -299,19 +299,18 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.40.0', require: false
gem 'rubocop', '~> 0.41.2', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.11.0', require: false
gem 'flog', require: false
gem 'flay', require: false
gem 'bundler-audit', require: false
gem 'flog', '~> 4.3.2', require: false
gem 'flay', '~> 2.6.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
gem 'benchmark-ips', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false
gem "license_finder", require: false
gem 'knapsack'
gem 'license_finder', '~> 2.1.0', require: false
gem 'knapsack', '~> 1.11.0'
end
group :test do
......@@ -319,30 +318,37 @@ group :test do
gem 'email_spec', '~> 1.6.0'
gem 'webmock', '~> 1.21.0'
gem 'test_after_commit', '~> 0.4.2'
gem 'sham_rack'
gem 'sham_rack', '~> 1.3.6'
end
group :production do
gem "gitlab_meta", '7.0'
gem 'gitlab_meta', '7.0'
end
gem "newrelic_rpm", '~> 3.14'
gem 'newrelic_rpm', '~> 3.14'
gem 'octokit', '~> 4.3.0'
gem "mail_room", "~> 0.7"
gem 'mail_room', '~> 0.8'
gem 'email_reply_parser', '~> 0.5.8'
## CI
gem 'activerecord-session_store', '~> 1.0.0'
gem "nested_form", '~> 0.3.2'
gem 'nested_form', '~> 0.3.2'
# OAuth
gem 'oauth2', '~> 1.0.0'
gem 'oauth2', '~> 1.2.0'
# Soft deletion
gem "paranoia", "~> 2.0"
gem 'paranoia', '~> 2.0'
# Health check
gem 'health_check', '~> 1.5.1'
gem 'health_check', '~> 2.1.0'
# System information
gem 'vmstat', '~> 2.1.0'
gem 'sys-filesystem', '~> 1.1.6'
# Secure headers for Content Security Policy
gem 'secure_headers', '~> 3.3'
This diff is collapsed.
# GitLab Maintenance Policy
GitLab is a fast moving and evolving project. We currently don't have the resources to support many releases concurrently. We support exactly one stable release at any given time.
GitLab follows the [Semantic Versioning](http://semver.org/) for its releases:
`(Major).(Minor).(Patch)` in a [pragmatic way].
GitLab follows the [Semantic Versioning](http://semver.org/) for its releases: `(Major).(Minor).(Patch)` in a [pragmatic way](https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e).
- **Major version**: Whenever there is something significant or any backwards
incompatible changes are introduced to the public API.
- **Minor version**: When new, backwards compatible functionality is introduced
to the public API or a minor feature is introduced, or when a set of smaller
features is rolled out.
- **Patch number**: When backwards compatible bug fixes are introduced that fix
incorrect behavior.
- **Major version**: Whenever there is something significant or any backwards incompatible changes are introduced to the public API.
- **Minor version**: When new, backwards compatible functionality is introduced to the public API or a minor feature is introduced, or when a set of smaller features is rolled out.
- **Patch number**: When backwards compatible bug fixes are introduced that fix incorrect behavior.
The current stable release will receive security patches and bug fixes
(eg. `8.9.0` -> `8.9.1`). Feature releases will mark the next supported stable
release where the minor version is increased numerically by increments of one
(eg. `8.9 -> 8.10`).
The current stable release will receive security patches and bug fixes (eg. `5.0` -> `5.0.1`). Feature releases will mark the next supported stable release where the minor version is increased numerically by increments of one (eg. `5.0 -> 5.1`).
Our current policy is to support one stable release at any given time, but for
medium-level security issues, we may consider [backporting to the previous two
monthly releases][rel-sec].
We encourage everyone to run the latest stable release to ensure that you can easily upgrade to the most secure and feature rich GitLab experience. In order to make sure you can easily run the most recent stable release, we are working hard to keep the update process simple and reliable.
We encourage everyone to run the latest stable release to ensure that you can
easily upgrade to the most secure and feature-rich GitLab experience. In order
to make sure you can easily run the most recent stable release, we are working
hard to keep the update process simple and reliable.
More information about the release procedures can be found in the doc/release directory.
More information about the release procedures can be found in our
[release-tools documentation][rel]. You may also want to read our
[Responsible Disclosure Policy][disclosure].
[rel-sec]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/security.md#backporting
[rel]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/
[disclosure]: https://about.gitlab.com/disclosure/
[pragmatic way]: https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e
8.9.0-pre
8.10.0-pre
app/assets/images/auth_buttons/azure_64.png

986 Bytes | W: | H:

app/assets/images/auth_buttons/azure_64.png

695 Bytes | W: | H:

app/assets/images/auth_buttons/azure_64.png
app/assets/images/auth_buttons/azure_64.png
app/assets/images/auth_buttons/azure_64.png
app/assets/images/auth_buttons/azure_64.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/bg_fallback.png

167 Bytes | W: | H:

app/assets/images/bg_fallback.png

167 Bytes | W: | H:

app/assets/images/bg_fallback.png
app/assets/images/bg_fallback.png
app/assets/images/bg_fallback.png
app/assets/images/bg_fallback.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/emoji.png

257 KB | W: | H:

app/assets/images/emoji.png

1.04 MB | W: | H:

app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/emoji@2x.png

674 KB | W: | H:

app/assets/images/emoji@2x.png

2.53 MB | W: | H:

app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/gitlab_logo.png

5.07 KB | W: | H:

app/assets/images/gitlab_logo.png

3.53 KB | W: | H:

app/assets/images/gitlab_logo.png
app/assets/images/gitlab_logo.png
app/assets/images/gitlab_logo.png
app/assets/images/gitlab_logo.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/gitorious-logo-black.png

809 Bytes | W: | H:

app/assets/images/gitorious-logo-black.png

631 Bytes | W: | H:

app/assets/images/gitorious-logo-black.png
app/assets/images/gitorious-logo-black.png
app/assets/images/gitorious-logo-black.png
app/assets/images/gitorious-logo-black.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/gitorious-logo-blue.png

495 Bytes | W: | H:

app/assets/images/gitorious-logo-blue.png

201 Bytes | W: | H:

app/assets/images/gitorious-logo-blue.png
app/assets/images/gitorious-logo-blue.png
app/assets/images/gitorious-logo-blue.png
app/assets/images/gitorious-logo-blue.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/icon-link.png

1.1 KB | W: | H:

app/assets/images/icon-link.png

729 Bytes | W: | H:

app/assets/images/icon-link.png
app/assets/images/icon-link.png
app/assets/images/icon-link.png
app/assets/images/icon-link.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/images.png

5.71 KB | W: | H:

app/assets/images/images.png

5.67 KB | W: | H:

app/assets/images/images.png
app/assets/images/images.png
app/assets/images/images.png
app/assets/images/images.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/no_avatar.png

621 Bytes | W: | H:

app/assets/images/no_avatar.png

621 Bytes | W: | H:

app/assets/images/no_avatar.png
app/assets/images/no_avatar.png
app/assets/images/no_avatar.png
app/assets/images/no_avatar.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/no_group_avatar.png

942 Bytes | W: | H:

app/assets/images/no_group_avatar.png

939 Bytes | W: | H:

app/assets/images/no_group_avatar.png
app/assets/images/no_group_avatar.png
app/assets/images/no_group_avatar.png
app/assets/images/no_group_avatar.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/slider_handles.png

1.34 KB | W: | H:

app/assets/images/slider_handles.png

1.31 KB | W: | H:

app/assets/images/slider_handles.png
app/assets/images/slider_handles.png
app/assets/images/slider_handles.png
app/assets/images/slider_handles.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/touch-icon-ipad.png

3.41 KB | W: | H:

app/assets/images/touch-icon-ipad.png

2.41 KB | W: | H:

app/assets/images/touch-icon-ipad.png
app/assets/images/touch-icon-ipad.png
app/assets/images/touch-icon-ipad.png
app/assets/images/touch-icon-ipad.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -3,10 +3,11 @@
groupPath: "/api/:version/groups/:id.json"
namespacesPath: "/api/:version/namespaces.json"
groupProjectsPath: "/api/:version/groups/:id/projects.json"
projectsPath: "/api/:version/projects.json"
projectsPath: "/api/:version/projects.json?simple=true"
labelsPath: "/api/:version/projects/:id/labels"
licensePath: "/api/:version/licenses/:key"
gitignorePath: "/api/:version/gitignores/:key"
gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key"
group: (group_id, callback) ->
url = Api.buildUrl(Api.groupPath)
......@@ -110,6 +111,12 @@
$.get url, (gitignore) ->
callback(gitignore)
gitlabCiYml: (key, callback) ->
url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key)
$.get url, (file) ->
callback(file)
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
......@@ -47,15 +47,12 @@
#= require date.format
#= require_directory ./behaviors
#= require_directory ./blob
#= require_directory ./ci
#= require_directory ./commit
#= require_directory ./extensions
#= require_directory ./lib
#= require_directory ./lib/utils
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus
#= require cropper
#= require u2f
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......@@ -121,6 +118,11 @@ window.onload = ->
setTimeout shiftWindow, 100
$ ->
$document = $(document)
$window = $(window)
$body = $('body')
gl.utils.preventDisabledButtons()
bootstrapBreakpoint = bp.getBreakpointSize()
......@@ -152,7 +154,7 @@ $ ->
), 1
# Initialize tooltips
$('body').tooltip(
$body.tooltip(
selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) ->
$el = $(el)
......@@ -171,7 +173,7 @@ $ ->
flash.show()
# Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
$body.on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', @)
switch e.type
......@@ -180,11 +182,20 @@ $ ->
else
buttons.enable()
$(document).ajaxError (e, xhrObj, xhrSetting, xhrErrorText) ->
if xhrObj.status is 401
new Flash 'You need to be logged in.', 'alert'
else if xhrObj.status in [ 404, 500 ]
new Flash 'Something went wrong on our end.', 'alert'
# Show/Hide the profile menu when hovering the account box
$('.account-box').hover -> $(@).toggleClass('hover')
# Commit show suppressed diff
$(document).on 'click', '.diff-content .js-show-suppressed-diff', ->
$document.on 'click', '.diff-content .js-show-suppressed-diff', ->
$container = $(@).parent()
$container.next('table').show()
$container.remove()
......@@ -194,16 +205,15 @@ $ ->
$('.header-content .header-logo').toggle()
$('.header-content .navbar-collapse').toggle()
$('.navbar-toggle').toggleClass('active')
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
# Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) ->
$body.on "click", ".js-toggle-diff-comments", (e) ->
$(@).toggleClass('active')
$(@).closest(".diff-file").find(".notes_holder").toggle()
e.preventDefault()
$(document).off "click", '.js-confirm-danger'
$(document).on "click", '.js-confirm-danger', (e) ->
$document.off "click", '.js-confirm-danger'
$document.on "click", '.js-confirm-danger', (e) ->
e.preventDefault()
btn = $(e.target)
text = btn.data("confirm-danger-message")
......@@ -211,7 +221,7 @@ $ ->
new ConfirmDangerModal(form, text)
$(document).on 'click', 'button', ->
$document.on 'click', 'button', ->
$(this).blur()
$('input[type="search"]').each ->
......@@ -219,7 +229,7 @@ $ ->
$this.attr 'value', $this.val()
return
$(document)
$document
.off 'keyup', 'input[type="search"]'
.on 'keyup', 'input[type="search"]' , (e) ->
$this = $(this)
......@@ -227,7 +237,7 @@ $ ->
$sidebarGutterToggle = $('.js-sidebar-toggle')
$(document)
$document
.off 'breakpoint:change'
.on 'breakpoint:change', (e, breakpoint) ->
if breakpoint is 'sm' or breakpoint is 'xs'
......@@ -239,14 +249,14 @@ $ ->
oldBootstrapBreakpoint = bootstrapBreakpoint
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint != oldBootstrapBreakpoint
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
checkInitialSidebarSize = ->
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint is "xs" or "sm"
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
$(window)
$window
.off "resize.app"
.on "resize.app", (e) ->
fitSidebarForSize()
......@@ -256,29 +266,45 @@ $ ->
new Aside()
# Sidenav pinning
if $(window).width() < 1440 and $.cookie('pin_nav') is 'true'
$.cookie('pin_nav', 'false')
if $window.width() < 1024 and $.cookie('pin_nav') is 'true'
$.cookie('pin_nav', 'false', { path: '/', expires: 365 * 10 })
$('.page-with-sidebar')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
.removeClass('page-sidebar-pinned')
$('.navbar-fixed-top').removeClass('header-pinned-nav')
$(document)
$document
.off 'click', '.js-nav-pin'
.on 'click', '.js-nav-pin', (e) ->
e.preventDefault()
$pinBtn = $(e.currentTarget)
$page = $ '.page-with-sidebar'
$topNav = $ '.navbar-fixed-top'
$tooltip = $ "##{$pinBtn.attr('aria-describedby')}"
doPinNav = not $page.is('.page-sidebar-pinned')
tooltipText = 'Pin navigation'
$(this).toggleClass 'is-active'
if $.cookie('pin_nav') is 'true'
$.cookie 'pin_nav', 'false'
$('.page-with-sidebar')
.removeClass('page-sidebar-pinned')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
$('.navbar-fixed-top')
.removeClass('header-pinned-nav')
.toggleClass('header-collapsed header-expanded')
if doPinNav
$page.addClass('page-sidebar-pinned')
$topNav.addClass('header-pinned-nav')
else
$.cookie 'pin_nav', 'true'
$('.page-with-sidebar').addClass('page-sidebar-pinned')
$('.navbar-fixed-top').addClass('header-pinned-nav')
$tooltip.remove() # Remove it immediately when collapsing the sidebar
$page.removeClass('page-sidebar-pinned')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
$topNav.removeClass('header-pinned-nav')
.toggleClass('header-collapsed header-expanded')
# Save settings
$.cookie 'pin_nav', doPinNav, { path: '/', expires: 365 * 10 }
if $.cookie('pin_nav') is 'true' or doPinNav
tooltipText = 'Unpin navigation'
# Update tooltip text immediately
$tooltip.find('.tooltip-inner').text(tooltipText)
# Persist tooltip title
$pinBtn.attr('title', tooltipText).tooltip('fixTitle')
......@@ -341,7 +341,9 @@ class @AwardsHandler
for emoji in frequentlyUsedEmojis
$(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
$('input.emoji-search').after(ul).after($('<h5>').text('Frequently used'))
$('.emoji-menu-content')
.prepend(ul)
.prepend($('<h5>').text('Frequently used'))
@frequentEmojiBlockRendered = true
......@@ -356,7 +358,7 @@ class @AwardsHandler
if term
# Generate a search result block
h5 = $('<h5>').text('Search results').addClass('emoji-search')
h5 = $('<h5>').text('Search results')
found_emojis = @searchEmojis(term).show()
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis)
$('.emoji-menu-content ul, .emoji-menu-content h5').hide()
......
#= require blob/template_selector
class @BlobCiYamlSelector extends TemplateSelector
requestFile: (query) ->
Api.gitlabCiYml query.name, @requestFileSuccess.bind(@)
class @BlobCiYamlSelectors
constructor: (opts) ->
{
@$dropdowns = $('.js-gitlab-ci-yml-selector')
@editor
} = opts
@$dropdowns.each (i, dropdown) =>
$dropdown = $(dropdown)
new BlobCiYamlSelector(
pattern: /(.gitlab-ci.yml)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
dropdown: $dropdown,
editor: @editor
)
......@@ -15,6 +15,7 @@ class @EditBlob
new BlobLicenseSelectors { @editor }
new BlobGitignoreSelectors { @editor }
new BlobCiYamlSelectors { @editor }
initModePanesAndLinks: ->
@$editModePanes = $(".js-edit-mode-pane")
......
......@@ -19,6 +19,7 @@ class @TemplateSelector
data: @data,
filterable: true,
selectable: true,
toggleLabel: @toggleLabel,
search:
fields: ['name']
clicked: @onClick
......@@ -31,6 +32,9 @@ class @TemplateSelector
@onFilenameUpdate()
)
toggleLabel: (item) ->
item.name
onFilenameUpdate: ->
return unless @$input.length
......
class @CiBuild
class @Build
@interval: null
@state: null
constructor: (@build_url, @build_status, @state) ->
clearInterval(CiBuild.interval)
constructor: (@page_url, @build_url, @build_status, @state) ->
clearInterval(Build.interval)
# Init breakpoint checker
@bp = Breakpoints.get()
......@@ -40,8 +40,8 @@ class @CiBuild
# Check for new build output if user still watching build page
# Only valid for runnig build when output changes during time
#
CiBuild.interval = setInterval =>
if window.location.href.split("#").first() is @build_url
Build.interval = setInterval =>
if window.location.href.split("#").first() is @page_url
@getBuildTrace()
, 4000
......@@ -57,7 +57,7 @@ class @CiBuild
getBuildTrace: ->
$.ajax
url: "#{@build_url}/trace.json?state=#{encodeURIComponent(@state)}"
url: "#{@page_url}/trace.json?state=#{encodeURIComponent(@state)}"
dataType: "json"
success: (log) =>
if log.state
......@@ -70,7 +70,7 @@ class @CiBuild
$('.js-build-output').html log.html
@checkAutoscroll()
else if log.status isnt @build_status
Turbolinks.visit @build_url
Turbolinks.visit @page_url
checkAutoscroll: ->
$("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state")
......
#= require pager
#= require jquery_nested_form
#= require_tree .
$(document).on 'click', '.assign-all-runner', ->
$(this).replaceWith('<i class="fa fa-refresh fa-spin"></i> Assign in progress..')
window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
document.addEventListener("page:fetch", unbindEvents)
$(document).on 'click', '.badge-codes-toggle', ->
$('.badge-codes-block').toggleClass("hide")
return false
class @CompareAutocomplete
constructor: ->
@initDropdown()
initDropdown: ->
$('.js-compare-dropdown').each ->
$dropdown = $(@)
selected = $dropdown.data('selected')
$dropdown.glDropdown(
data: (term, callback) ->
$.ajax(
url: $dropdown.data('refs-url')
data:
ref: $dropdown.data('ref')
).done (refs) ->
callback(refs)
selectable: true
filterable: true
filterByText: true
fieldName: $dropdown.attr('name')
filterInput: 'input[type="text"]'
renderRow: (ref) ->
if ref.header?
$('<li />')
.addClass('dropdown-header')
.text(ref.header)
else
link = $('<a />')
.attr('href', '#')
.addClass(if ref is selected then 'is-active' else '')
.text(ref)
.attr('data-ref', escape(ref))
$('<li />')
.append(link)
id: (obj, $el) ->
$el.attr('data-ref')
toggleLabel: (obj, $el) ->
$el.text().trim()
)
class @Diff
UNFOLD_COUNT = 20
constructor: ->
$('.files .diff-file').singleFileDiff()
@filesCommentButton = $('.files .diff-file').filesCommentButton()
$(document).off('click', '.js-unfold')
$(document).on('click', '.js-unfold', (event) =>
target = $(event.target)
......@@ -36,7 +39,7 @@ class @Diff
# see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent: 1
$.get(link, params, (response) =>
$.get(link, params, (response) ->
target.parent().replaceWith(response)
)
)
......
......@@ -84,6 +84,8 @@ class Dispatcher
new Activities()
when 'groups:show'
shortcut_handler = new ShortcutsNavigation()
new NotificationsForm()
new NotificationsDropdown()
when 'groups:group_members:index'
new GroupMembers()
new UsersSelect()
......@@ -125,17 +127,18 @@ class Dispatcher
when 'groups'
new UsersSelect()
when 'projects'
new NamespaceSelect()
new NamespaceSelects()
when 'dashboard', 'root'
shortcut_handler = new ShortcutsDashboardNavigation()
when 'profiles'
new Profile()
new NotificationsForm()
new NotificationsDropdown()
when 'projects'
new Project()
new ProjectAvatar()
switch path[1]
when 'compare'
new CompareAutocomplete()
when 'edit'
shortcut_handler = new ShortcutsNavigation()
new ProjectNew()
......
......@@ -70,12 +70,12 @@ class @DropzoneInput
pasteText response.link.markdown
return
error: (temp, errorMessage) ->
error: (temp) ->
errorAlert = $(form).find('.error-alert')
checkIfMsgExists = errorAlert.children().length
if checkIfMsgExists is 0
errorAlert.append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
$(".div-dropzone-alert").append "#{btnAlert}Attaching the file failed."
return
totaluploadprogress: (totalUploadProgress) ->
......
class @FilesCommentButton
COMMENT_BUTTON_CLASS = '.add-diff-note'
COMMENT_BUTTON_TEMPLATE = _.template '<button name="button" type="submit" class="btn <%- COMMENT_BUTTON_CLASS %> js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>'
LINE_HOLDER_CLASS = '.line_holder'
LINE_NUMBER_CLASS = 'diff-line-num'
LINE_CONTENT_CLASS = 'line_content'
UNFOLDABLE_LINE_CLASS = 'js-unfold'
EMPTY_CELL_CLASS = 'empty-cell'
OLD_LINE_CLASS = 'old_line'
NEW_CLASS = 'new'
LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content"
TEXT_FILE_SELECTOR = '.text-file'
DEBOUNCE_TIMEOUT_DURATION = 100
constructor: (@filesContainerElement) ->
@VIEW_TYPE = $('input#view[type=hidden]').val()
debounce = _.debounce @render, DEBOUNCE_TIMEOUT_DURATION
$(document)
.on 'mouseover', LINE_COLUMN_CLASSES, debounce
.on 'mouseleave', LINE_COLUMN_CLASSES, @destroy
render: (e) =>
$currentTarget = $(e.currentTarget)
buttonParentElement = @getButtonParent $currentTarget
return unless @shouldRender e, buttonParentElement
textFileElement = @getTextFileElement $currentTarget
lineContentElement = @getLineContent $currentTarget
buttonParentElement.append @buildButton
noteableType: textFileElement.attr 'data-noteable-type'
noteableID: textFileElement.attr 'data-noteable-id'
commitID: textFileElement.attr 'data-commit-id'
noteType: lineContentElement.attr 'data-note-type'
position: lineContentElement.attr 'data-position'
lineType: lineContentElement.attr 'data-line-type'
discussionID: lineContentElement.attr 'data-discussion-id'
lineCode: lineContentElement.attr 'data-line-code'
return
destroy: (e) =>
return if @isMovingToSameType e
$(COMMENT_BUTTON_CLASS, @getButtonParent $(e.currentTarget)).remove()
return
buildButton: (buttonAttributes) ->
initializedButtonTemplate = COMMENT_BUTTON_TEMPLATE
COMMENT_BUTTON_CLASS: COMMENT_BUTTON_CLASS.substr 1
$(initializedButtonTemplate).attr
'data-noteable-type': buttonAttributes.noteableType
'data-noteable-id': buttonAttributes.noteableID
'data-commit-id': buttonAttributes.commitID
'data-note-type': buttonAttributes.noteType
'data-line-code': buttonAttributes.lineCode
'data-position': buttonAttributes.position
'data-discussion-id': buttonAttributes.discussionID
'data-line-type': buttonAttributes.lineType
getTextFileElement: (hoveredElement) ->
$(hoveredElement.closest TEXT_FILE_SELECTOR)
getLineContent: (hoveredElement) ->
return hoveredElement if hoveredElement.hasClass LINE_CONTENT_CLASS
$(".#{LINE_CONTENT_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
getButtonParent: (hoveredElement) ->
if @VIEW_TYPE is 'inline'
return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS
$(".#{OLD_LINE_CLASS}", hoveredElement.parent())
else
return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS
$(".#{LINE_NUMBER_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
diffTypeClass: (hoveredElement) ->
if hoveredElement.hasClass(NEW_CLASS) then '.new' else '.old'
isMovingToSameType: (e) ->
newButtonParent = @getButtonParent $(e.toElement)
return false unless newButtonParent
newButtonParent.is @getButtonParent $(e.currentTarget)
shouldRender: (e, buttonParentElement) ->
(not buttonParentElement.hasClass(EMPTY_CELL_CLASS) and \
not buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS) and \
$(COMMENT_BUTTON_CLASS, buttonParentElement).length is 0)
$.fn.filesCommentButton = ->
return unless this and @parent().data('can-create-note')?
@each ->
unless $.data this, 'filesCommentButton'
$.data this, 'filesCommentButton', new FilesCommentButton $(this)
class @Flash
constructor: (message, type = 'alert')->
@flash = $(".flash-container")
@flash.html("")
hideFlash = -> $(@).fadeOut()
innerDiv = $('<div/>',
class: "flash-#{type}",
constructor: (message, type = 'alert', parent = null)->
if parent
@flashContainer = parent.find('.flash-container')
else
@flashContainer = $('.flash-container-page')
@flashContainer.html('')
flash = $('<div/>',
class: "flash-#{type}"
)
flash.on 'click', hideFlash
textDiv = $('<div/>',
class: 'flash-text',
text: message
)
innerDiv.appendTo(".flash-container")
textDiv.appendTo(flash)
if @flashContainer.parent().hasClass('content-wrapper')
textDiv.addClass('container-fluid container-limited')
@flash.click -> $(@).fadeOut()
@flash.show()
flash.appendTo(@flashContainer)
@flashContainer.show()
pinTo: (selector) ->
@flash.detach().appendTo(selector)
......@@ -4,7 +4,7 @@ window.GitLab ?= {}
GitLab.GfmAutoComplete =
dataLoading: false
dataLoaded: false
cachedData: {}
dataSource: ''
# Emoji
......@@ -55,7 +55,7 @@ GitLab.GfmAutoComplete =
@setupAtWho()
if @dataSource
if !@dataLoading
if not @dataLoading and not @cachedData
@dataLoading = true
# We should wait until initializations are done
......@@ -70,6 +70,8 @@ GitLab.GfmAutoComplete =
@loadData(data)
, 1000)
if @cachedData?
@loadData(@cachedData)
setupAtWho: ->
# Emoji
......@@ -188,7 +190,7 @@ GitLab.GfmAutoComplete =
callbacks:
beforeSave: (merges) ->
sanitizeLabelTitle = (title)->
if /\w+\s+\w+/g.test(title)
if /[\w\?&]+\s+[\w\?&]+/g.test(title)
"\"#{sanitize(title)}\""
else
sanitize(title)
......@@ -205,6 +207,7 @@ GitLab.GfmAutoComplete =
$.getJSON(dataSource)
loadData: (data) ->
@cachedData = data
@dataLoaded = true
# load members
......
......@@ -56,9 +56,10 @@ class GitLabDropdownFilter
return BLUR_KEYCODES.indexOf(keyCode) >= 0
filter: (search_text) ->
@options.onFilter(search_text) if @options.onFilter
data = @options.data()
if data?
if data? and not @options.filterByText
results = data
if search_text isnt ''
......@@ -102,10 +103,11 @@ class GitLabDropdownFilter
$el = $(@)
matches = fuzzaldrinPlus.match($el.text().trim(), search_text)
if matches.length
$el.show()
else
$el.hide()
unless $el.is('.dropdown-header')
if matches.length
$el.show()
else
$el.hide()
else
elements.show()
......@@ -185,12 +187,16 @@ class GitLabDropdown
@fullData = data
@parseData @fullData
@filter.input.trigger('keyup') if @options.filterable and @filter and @filter.input
}
# Init filterable
if @options.filterable
@filter = new GitLabDropdownFilter @filterInput,
filterInputBlur: @filterInputBlur
filterByText: @options.filterByText
onFilter: @options.onFilter
remote: @options.filterRemote
query: @options.data
keys: searchFields
......@@ -216,6 +222,13 @@ class GitLabDropdown
@dropdown.on 'keyup', (e) =>
if e.which is 27 # Escape key
$('.dropdown-menu-close', @dropdown).trigger 'click'
@dropdown.on 'blur', 'a', (e) =>
if e.relatedTarget?
$relatedTarget = $(e.relatedTarget)
$dropdownMenu = $relatedTarget.closest('.dropdown-menu')
if $dropdownMenu.length is 0
@dropdown.removeClass('open')
if @dropdown.find(".dropdown-toggle-page").length
@dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) =>
......@@ -237,6 +250,8 @@ class GitLabDropdown
if self.options.clicked
self.options.clicked(selected, $el, e)
$el.trigger('blur')
# Finds an element inside wrapper element
getElement: (selector) ->
@dropdown.find selector
......@@ -278,7 +293,7 @@ class GitLabDropdown
html = @renderData(data)
# Render the full menu
full_html = @renderMenu(html.join(""))
full_html = @renderMenu(html)
@appendMenu(full_html)
......@@ -349,7 +364,8 @@ class GitLabDropdown
if @options.renderMenu
menu_html = @options.renderMenu(html)
else
menu_html = "<ul>#{html}</ul>"
menu_html = $('<ul />')
.append(html)
return menu_html
......@@ -358,7 +374,9 @@ class GitLabDropdown
selector = '.dropdown-content'
if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one .dropdown-content"
$(selector, @dropdown).html html
$(selector, @dropdown)
.empty()
.append(html)
# Render the row
renderItem: (data, group = false, index = false) ->
......@@ -440,6 +458,8 @@ class GitLabDropdown
rowClicked: (el) ->
fieldName = @options.fieldName
isInput = $(@el).is('input')
if @renderedData
groupName = el.data('group')
if groupName
......@@ -450,14 +470,23 @@ class GitLabDropdown
selectedObject = @renderedData[selectedIndex]
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if isInput
field = $(@el)
else
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if el.hasClass(ACTIVE_CLASS)
el.removeClass(ACTIVE_CLASS)
field.remove()
if isInput
field.val('')
else
field.remove()
# Toggle the dropdown label
if @options.toggleLabel
@updateLabel()
@updateLabel(selectedObject, el, @)
else
selectedObject
else if el.hasClass(INDETERMINATE_CLASS)
......@@ -474,7 +503,9 @@ class GitLabDropdown
else
if not @options.multiSelect or el.hasClass('dropdown-clear-active')
@dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
@dropdown.parent().find("input[name='#{fieldName}']").remove()
unless isInput
@dropdown.parent().find("input[name='#{fieldName}']").remove()
if !value?
field.remove()
......@@ -484,12 +515,14 @@ class GitLabDropdown
# Toggle the dropdown label
if @options.toggleLabel
@updateLabel(selectedObject, el)
@updateLabel(selectedObject, el, @)
if value?
if !field.length and fieldName
@addInput(fieldName, value)
else
field.val value
field
.val value
.trigger 'change'
return selectedObject
......@@ -516,7 +549,7 @@ class GitLabDropdown
if $el.length
e.preventDefault()
e.stopImmediatePropagation()
$(selector, @dropdown)[0].click()
$el.first().trigger('click')
addArrowKeyEvent: ->
ARROW_KEY_CODES = [38, 40]
......@@ -583,8 +616,8 @@ class GitLabDropdown
# Scroll the dropdown content up
$dropdownContent.scrollTop(listItemTop - dropdownContentTop)
updateLabel: (selected = null, el = null) =>
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el)
updateLabel: (selected = null, el = null, instance = null) =>
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el, instance)
$.fn.glDropdown = (opts) ->
return @.each ->
......
......@@ -34,6 +34,8 @@ class @GLForm
# form and textarea event listeners
@addEventListeners()
gl.text.init(@form)
# hide discard button
@form.find('.js-note-discard').hide()
......@@ -42,6 +44,7 @@ class @GLForm
clearEventListeners: ->
@textarea.off 'focus'
@textarea.off 'blur'
gl.text.removeListeners(@form)
addEventListeners: ->
@textarea.on 'focus', ->
......
......@@ -4,5 +4,4 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require Chart
#= require_tree .
......@@ -121,7 +121,11 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.content').width()/2 - 100
# Don't split graph size in half for mobile devices.
if $(window).width() < 768
@width = $('.content').width() - 80
else
@width = ($('.content').width() / 2) - 100
@height = 200
@x = null
@y = null
......
......@@ -7,13 +7,16 @@ class @ImporterStatus
$('.js-add-to-import')
.off 'click'
.on 'click', (e) =>
new_namespace = null
$btn = $(e.currentTarget)
$tr = $btn.closest('tr')
$target_field = $tr.find('.import-target')
$namespace_input = $target_field.find('input')
id = $tr.attr('id').replace('repo_', '')
if $tr.find('.import-target input').length > 0
new_namespace = $tr.find('.import-target input').prop('value')
$tr.find('.import-target').empty().append("#{new_namespace} / #{$tr.find('.import-target').data('project_name')}")
new_namespace = null
if $namespace_input.length > 0
new_namespace = $namespace_input.prop('value')
$target_field.empty().append("#{new_namespace}/#{$target_field.data('project_name')}")
$btn
.disable()
......
......@@ -11,11 +11,11 @@ issuable_created = false
initTemplates: ->
Issuable.labelRow = _.template(
'<% _.each(labels, function(label){ %>
<span class="label-row btn-group" role="group" aria-label="<%= _.escape(label.title) %>" style="color: <%= label.text_color %>;">
<a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%= label.color %>;" title="<%= _.escape(label.description) %>" data-container="body">
<%= _.escape(label.title) %>
<span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;">
<a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body">
<%- label.title %>
</a>
<button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%= label.color %>;" data-label="<%= _.escape(label.title) %>">
<button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>">
<i class="fa fa-times"></i>
</button>
</span>
......@@ -32,13 +32,11 @@ issuable_created = false
$search = $('#issue_search')
$form = $('.js-filter-form')
$input = $("input[name='#{$search.attr('name')}']", $form)
if $input.length is 0
$form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>"
else
$input.val $search.val()
Issuable.filterResults $form
Issuable.filterResults $form if $search.val() isnt ''
, 500)
initLabelFilterRemove: ->
......@@ -59,21 +57,23 @@ issuable_created = false
filterResults: (form) =>
formData = form.serialize()
$('.issues-holder, .merge-requests-holder').css('opacity', '0.5')
formAction = form.attr('action')
issuesUrl = formAction
issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}")
issuesUrl += formData
Turbolinks.visit(issuesUrl);
Turbolinks.visit(issuesUrl)
initChecks: ->
@issuableBulkActions = $('.bulk-update').data('bulkActions')
$('.check_all_issues').off('click').on('click', ->
$('.selected_issue').prop('checked', @checked)
Issuable.checkChanged()
)
$('.selected_issue').off('change').on('change', Issuable.checkChanged)
$('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(@))
checkChanged: ->
checked_issues = $('.selected_issue:checked')
......@@ -88,3 +88,6 @@ issuable_created = false
$('#update_issues_ids').val []
$('.issues_bulk_update').hide()
$('.issues-other-filters').show()
@issuableBulkActions.willUpdateLabels = false
return true
......@@ -99,7 +99,7 @@ class @Issue
# If the user doesn't have the required permissions the container isn't
# rendered at all.
return unless $container
return if $container.length is 0
$.getJSON($container.data('path'))
.error ->
......
......@@ -6,6 +6,13 @@ class @IssueStatusSelect
$(el).glDropdown(
selectable: true
fieldName: fieldName
toggleLabel: (selected, el, instance) =>
label = 'Author'
$item = instance.dropdown.find('.is-active')
label = $item.text() if $item.length
label
clicked: (item, $el, e)->
e.preventDefault()
id: (obj, el) ->
$(el).data("id")
)
......@@ -7,6 +7,11 @@ class @IssuableBulkActions
@issues = @getElement('.issues-list .issue')
} = opts
# Save instance
@form.data 'bulkActions', @
@willUpdateLabels = false
@bindEvents()
# Fixes bulk-assign not working when navigating through pages
......@@ -80,18 +85,20 @@ class @IssuableBulkActions
getFormDataAsObject: ->
formData =
update:
state_event : @form.find('input[name="update[state_event]"]').val()
assignee_id : @form.find('input[name="update[assignee_id]"]').val()
milestone_id : @form.find('input[name="update[milestone_id]"]').val()
issues_ids : @form.find('input[name="update[issues_ids]"]').val()
add_label_ids : []
remove_label_ids : []
@getLabelsToApply().map (id) ->
formData.update.add_label_ids.push id
@getLabelsToRemove().map (id) ->
formData.update.remove_label_ids.push id
state_event : @form.find('input[name="update[state_event]"]').val()
assignee_id : @form.find('input[name="update[assignee_id]"]').val()
milestone_id : @form.find('input[name="update[milestone_id]"]').val()
issues_ids : @form.find('input[name="update[issues_ids]"]').val()
subscription_event : @form.find('input[name="update[subscription_event]"]').val()
add_label_ids : []
remove_label_ids : []
if @willUpdateLabels
@getLabelsToApply().map (id) ->
formData.update.add_label_ids.push id
@getLabelsToRemove().map (id) ->
formData.update.remove_label_ids.push id
formData
......
......@@ -32,9 +32,9 @@ class @LabelsSelect
if issueUpdateURL
labelHTMLTemplate = _.template(
'<% _.each(labels, function(label){ %>
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%= _.escape(label.title) %>">
<span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;">
<%= _.escape(label.title) %>
<a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>">
<span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">
<%- label.title %>
</span>
</a>
<% }); %>'
......@@ -261,7 +261,7 @@ class @LabelsSelect
$a.attr('data-label-id', label.id)
$a.addClass(selectedClass.join(' '))
.html("#{colorEl} #{_.escape(label.title)}")
.html("#{colorEl} #{label.title}")
# Return generated html
$li.html($a).prop('outerHTML')
......@@ -288,7 +288,7 @@ class @LabelsSelect
fieldName: $dropdown.data('field-name')
id: (label) ->
if $dropdown.hasClass("js-filter-submit") and not label.isAny?
_.escape label.title
label.title
else
label.id
......@@ -319,6 +319,8 @@ class @LabelsSelect
multiSelect: $dropdown.hasClass 'js-multiselect'
clicked: (label) ->
_this.enableBulkLabelDropdown()
if $dropdown.hasClass('js-filter-bulk-update')
return
......@@ -377,3 +379,8 @@ class @LabelsSelect
label_ids.push $("#issue_#{issue_id}").data('labels')
_.intersection.apply _, label_ids
enableBulkLabelDropdown: ->
if $('.selected_issue:checked').length
issuableBulkActions = $('.bulk-update').data('bulkActions')
issuableBulkActions.willUpdateLabels = true
......@@ -3,11 +3,10 @@ hideEndFade = ($scrollingTabs) ->
$this = $(@)
$this
.find('.fade-right')
.toggleClass('end-scroll', $this.width() is $this.prop('scrollWidth'))
.siblings('.fade-right')
.toggleClass('scrolling', $this.width() < $this.prop('scrollWidth'))
$ ->
$('.fade-left').addClass('end-scroll')
hideEndFade($('.scrolling-tabs'))
......@@ -21,5 +20,5 @@ $ ->
currentPosition = $this.scrollLeft()
maxPosition = $this.prop('scrollWidth') - $this.outerWidth()
$this.find('.fade-left').toggleClass('end-scroll', currentPosition is 0)
$this.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition)
$this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0)
$this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1)
#= require raphael
#= require g.raphael
#= require g.bar
......@@ -5,12 +5,12 @@
w.gl.utils.isInGroupsPage = ->
return $('body').data('page').split(':')[0] is 'groups'
return gl.utils.getPagePath() is 'groups'
w.gl.utils.isInProjectPage = ->
return $('body').data('page').split(':')[0] is 'projects'
return gl.utils.getPagePath() is 'projects'
w.gl.utils.getProjectSlug = ->
......@@ -40,6 +40,9 @@
e.stopImmediatePropagation()
return false
gl.utils.getPagePath = ->
return $('body').data('page').split(':')[0]
jQuery.timefor = (time, suffix, expiredLabel) ->
......
......@@ -2,10 +2,14 @@
w.gl ?= {}
w.gl.utils ?= {}
w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
w.gl.utils.formatDate = (datetime) ->
dateFormat(datetime, 'mmm d, yyyy h:MMtt Z')
w.gl.utils.getDayName = (date) ->
this.days[date.getDay()]
w.gl.utils.localTimeAgo = ($timeagoEls, setTimeago = true) ->
$timeagoEls.each( ->
$el = $(@)
......
((w) ->
w.gl ?= {}
w.gl.text ?= {}
gl.text.randomString = -> Math.random().toString(36).substring(7)
gl.text.replaceRange = (s, start, end, substitute) ->
s.substring(0, start) + substitute + s.substring(end);
gl.text.selectedText = (text, textarea) ->
text.substring(textarea.selectionStart, textarea.selectionEnd)
gl.text.lineBefore = (text, textarea) ->
split = text.substring(0, textarea.selectionStart).trim().split('\n')
split[split.length - 1]
gl.text.lineAfter = (text, textarea) ->
text.substring(textarea.selectionEnd).trim().split('\n')[0]
gl.text.blockTagText = (text, textArea, blockTag, selected) ->
lineBefore = @lineBefore(text, textArea)
lineAfter = @lineAfter(text, textArea)
if lineBefore is blockTag and lineAfter is blockTag
# To remove the block tag we have to select the line before & after
if blockTag?
textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1)
textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1)
selected
else
"#{blockTag}\n#{selected}\n#{blockTag}"
gl.text.insertText = (textArea, text, tag, blockTag, selected, wrap) ->
selectedSplit = selected.split('\n')
startChar = if not wrap and textArea.selectionStart > 0 then '\n' else ''
if selectedSplit.length > 1 and (not wrap or blockTag?)
if blockTag?
insertText = @blockTagText(text, textArea, blockTag, selected)
else
insertText = selectedSplit.map((val) ->
if val.indexOf(tag) is 0
"#{val.replace(tag, '')}"
else
"#{tag}#{val}"
).join('\n')
else
insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}"
if document.queryCommandSupported('insertText')
inserted = document.execCommand 'insertText', false, insertText
unless inserted
try
document.execCommand("ms-beginUndoUnit")
textArea.value = @replaceRange(
text,
textArea.selectionStart,
textArea.selectionEnd,
insertText)
try
document.execCommand("ms-endUndoUnit")
@moveCursor(textArea, tag, wrap)
gl.text.moveCursor = (textArea, tag, wrapped) ->
return unless textArea.setSelectionRange
if textArea.selectionStart is textArea.selectionEnd
if wrapped
pos = textArea.selectionStart - tag.length
else
pos = textArea.selectionStart
textArea.setSelectionRange pos, pos
gl.text.updateText = (textArea, tag, blockTag, wrap) ->
$textArea = $(textArea)
oldVal = $textArea.val()
textArea = $textArea.get(0)
text = $textArea.val()
selected = @selectedText(text, textArea)
$textArea.focus()
@insertText(textArea, text, tag, blockTag, selected, wrap)
gl.text.init = (form) ->
self = @
$('.js-md', form)
.off 'click'
.on 'click', ->
$this = $(@)
self.updateText(
$this.closest('.md-area').find('textarea'),
$this.data('md-tag'),
$this.data('md-block'),
not $this.data('md-prepend')
)
gl.text.removeListeners = (form) ->
$('.js-md', form).off()
) window
......@@ -153,17 +153,18 @@ class @MergeRequestTabs
loadDiff: (source) ->
return if @diffsLoaded
@_get
url: "#{source}.json" + @_location.search
success: (data) =>
$('#diffs').html data.html
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'))
$('#diffs .js-syntax-highlight').syntaxHighlight()
$('#diffs .diff-file').singleFileDiff()
@expandViewContainer() if @diffViewType() is 'parallel'
@diffsLoaded = true
@scrollToElement("#diffs")
@highlighSelectedLine()
@filesCommentButton = $('.files .diff-file').filesCommentButton()
$(document)
.off 'click', '.diff-line-num a'
......
......@@ -4,18 +4,10 @@ class @Milestone
type: "PUT"
url: issue_url
data: data
success: (data) ->
if data.saved == true
if data.assignee_avatar_url
img_tag = $('<img/>')
img_tag.attr('src', data.assignee_avatar_url)
img_tag.addClass('avatar s16')
$(li).find('.assignee-icon').html(img_tag)
else
$(li).find('.assignee-icon').html('')
$(li).effect 'highlight'
else
new Flash("Issue update failed", 'alert')
success: (_data) =>
@successCallback(_data, li)
error: (data) ->
new Flash("Issue update failed", 'alert')
dataType: "json"
@sortIssues: (data) ->
......@@ -25,9 +17,10 @@ class @Milestone
type: "PUT"
url: sort_issues_url
data: data
success: (data) ->
if data.saved != true
new Flash("Issues update failed", 'alert')
success: (_data) =>
@successCallback(_data)
error: ->
new Flash("Issues update failed", 'alert')
dataType: "json"
@sortMergeRequests: (data) ->
......@@ -37,9 +30,10 @@ class @Milestone
type: "PUT"
url: sort_mr_url
data: data
success: (data) ->
if data.saved != true
new Flash("MR update failed", 'alert')
success: (_data) =>
@successCallback(_data)
error: (data) ->
new Flash("Issue update failed", 'alert')
dataType: "json"
@updateMergeRequest: (li, merge_request_url, data) ->
......@@ -47,20 +41,23 @@ class @Milestone
type: "PUT"
url: merge_request_url
data: data
success: (data) ->
if data.saved == true
if data.assignee_avatar_url
img_tag = $('<img/>')
img_tag.attr('src', data.assignee_avatar_url)
img_tag.addClass('avatar s16')
$(li).find('.assignee-icon').html(img_tag)
else
$(li).find('.assignee-icon').html('')
$(li).effect 'highlight'
else
new Flash("Issue update failed", 'alert')
success: (_data) =>
@successCallback(_data, li)
error: (data) ->
new Flash("Issue update failed", 'alert')
dataType: "json"
@successCallback: (data, element) =>
if data.assignee
img_tag = $('<img/>')
img_tag.attr('src', data.assignee.avatar_url)
img_tag.addClass('avatar s16')
$(element).find('.assignee-icon').html(img_tag)
else
$(element).find('.assignee-icon').html('')
$(element).effect 'highlight'
constructor: ->
oldMouseStart = $.ui.sortable.prototype._mouseStart
$.ui.sortable.prototype._mouseStart = (event, overrideHandle, noActivation) ->
......@@ -81,8 +78,10 @@ class @Milestone
stop: (event, ui) ->
$(".issues-sortable-list").css "min-height", "0px"
update: (event, ui) ->
data = $(this).sortable("serialize")
Milestone.sortIssues(data)
# Prevents sorting from container which element has been removed.
if $(this).find(ui.item).length > 0
data = $(this).sortable("serialize")
Milestone.sortIssues(data)
receive: (event, ui) ->
new_state = $(this).data('state')
......
......@@ -24,14 +24,14 @@ class @MilestoneSelect
if issueUpdateURL
milestoneLinkTemplate = _.template(
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>" class="bold has-tooltip" data-container="body" title="<%= remaining %>"><%= _.escape(title) %></a>'
'<a href="/<%- namespace %>/<%- path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'
)
milestoneLinkNoneTemplate = '<span class="no-value">None</span>'
collapsedSidebarLabelTemplate = _.template(
'<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left">
<%= _.escape(title) %>
'<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left">
<%- title %>
</span>'
)
......
class @NamespaceSelect
constructor: ->
namespaceFormatResult = (namespace) ->
markup = "<div class='namespace-result'>"
markup += "<span class='namespace-kind'>" + namespace.kind + "</span>"
markup += "<span class='namespace-path'>" + namespace.path + "</span>"
markup += "</div>"
markup
formatSelection = (namespace) ->
namespace.kind + ": " + namespace.path
$('.ajax-namespace-select').each (i, select) ->
$(select).select2
placeholder: "Search for namespace"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.namespaces query.term, (namespaces) ->
data = { results: namespaces }
query.callback(data)
dropdownCssClass: "ajax-namespace-dropdown"
formatResult: namespaceFormatResult
formatSelection: formatSelection
constructor: (opts) ->
{
@dropdown
} = opts
showAny = true
fieldName = 'namespace_id'
if @dropdown.attr 'data-field-name'
fieldName = @dropdown.data 'fieldName'
if @dropdown.attr 'data-show-any'
showAny = @dropdown.data 'showAny'
@dropdown.glDropdown(
filterable: true
selectable: true
filterRemote: true
search:
fields: ['path']
fieldName: fieldName
toggleLabel: (selected) ->
return if not selected.id? then selected.text else "#{selected.kind}: #{selected.path}"
data: (term, dataCallback) ->
Api.namespaces term, (namespaces) ->
if showAny
anyNamespace =
text: 'Any namespace'
id: null
namespaces.unshift(anyNamespace)
namespaces.splice 1, 0, 'divider'
dataCallback(namespaces)
text: (namespace) ->
return if not namespace.id? then namespace.text else "#{namespace.kind}: #{namespace.path}"
renderRow: @renderRow
clicked: @onSelectItem
)
onSelectItem: (item, el, e) =>
e.preventDefault()
class @NamespaceSelects
constructor: (opts = {}) ->
{
@$dropdowns = $('.js-namespace-select')
} = opts
@$dropdowns.each (i, dropdown) ->
$dropdown = $(dropdown)
new NamespaceSelect(
dropdown: $dropdown
)
......@@ -4,9 +4,6 @@
# 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 raphael
#= require g.raphael
#= require g.bar
#= require_tree .
$ ->
......
......@@ -100,13 +100,43 @@ class @Notes
$('.note .js-task-list-container').taskList('disable')
$(document).off 'tasklist:changed', '.note .js-task-list-container'
keydownNoteText: (e) ->
$this = $(this)
if $this.val() is '' and e.which is 38 #aka the up key
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
if myLastNote.length
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
myLastNoteEditBtn.trigger('click', [true, myLastNote])
keydownNoteText: (e) =>
return if isMetaKey e
$textarea = $(e.target)
# Edit previous note when UP arrow is hit
switch e.which
when 38
return unless $textarea.val() is ''
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
if myLastNote.length
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
myLastNoteEditBtn.trigger('click', [true, myLastNote])
# Cancel creating diff note or editing any note when ESCAPE is hit
when 27
discussionNoteForm = $textarea.closest('.js-discussion-note-form')
if discussionNoteForm.length
if $textarea.val() isnt ''
return unless confirm('Are you sure you want to cancel creating this comment?')
@removeDiscussionNoteForm(discussionNoteForm)
return
editNote = $textarea.closest('.note')
if editNote.length
originalText = $textarea.closest('form').data('original-note')
newText = $textarea.val()
if originalText isnt newText
return unless confirm('Are you sure you want to cancel editing this comment?')
@removeNoteEditForm(editNote)
isMetaKey = (e) ->
(e.metaKey or e.ctrlKey or e.altKey or e.shiftKey)
initRefresh: ->
clearInterval(Notes.interval)
......@@ -164,8 +194,7 @@ class @Notes
renderNote: (note) ->
unless note.valid
if note.award
flash = new Flash('You have already awarded this emoji!', 'alert')
flash.pinTo('.header-content')
new Flash('You have already awarded this emoji!', 'alert')
return
if note.award
......@@ -210,12 +239,16 @@ class @Notes
@note_ids.push(note.id)
form = $("#new-discussion-note-form-#{note.discussion_id}")
if note.original_discussion_id? and form.length is 0
form = $("#new-discussion-note-form-#{note.original_discussion_id}")
row = form.closest("tr")
note_html = $(note.html)
note_html.syntaxHighlight()
# is this the first note of discussion?
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']")
if note.original_discussion_id? and discussionContainer.length is 0
discussionContainer = $(".notes[data-discussion-id='" + note.original_discussion_id + "']")
if discussionContainer.length is 0
# insert the note and the reply button after the temp row
row.after note.discussion_html
......@@ -288,8 +321,11 @@ class @Notes
form.addClass "js-main-target-form"
form.find("#note_line_code").remove()
form.find("#note_position").remove()
form.find("#note_type").remove()
@parentTimeline = form.parents('.timeline')
###
General note form setup.
......@@ -305,10 +341,12 @@ class @Notes
new Autosave textarea, [
"Note"
form.find("#note_commit_id").val()
form.find("#note_line_code").val()
form.find("#note_noteable_type").val()
form.find("#note_noteable_id").val()
form.find("#note_commit_id").val()
form.find("#note_type").val()
form.find("#note_line_code").val()
form.find("#note_position").val()
]
###
......@@ -320,8 +358,7 @@ class @Notes
@renderNote(note)
addNoteError: (xhr, note, status) =>
flash = new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert')
flash.pinTo('.md-area')
new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', @parentTimeline)
###
Called in response to the new note form being submitted
......@@ -398,9 +435,12 @@ class @Notes
Hides edit form and restores the original note text to the editor textarea.
###
cancelEdit: (e) ->
cancelEdit: (e) =>
e.preventDefault()
note = $(this).closest(".note")
note = $(e.target).closest('.note')
@removeNoteEditForm(note)
removeNoteEditForm: (note) ->
form = note.find(".current-note-edit-form")
note.removeClass "is-editting"
form.removeClass("current-note-edit-form")
......@@ -479,10 +519,12 @@ class @Notes
setupDiscussionNoteForm: (dataHolder, form) =>
# setup note target
form.attr 'id', "new-discussion-note-form-#{dataHolder.data("discussionId")}"
form.attr "data-line-code", dataHolder.data("lineCode")
form.find("#note_type").val dataHolder.data("noteType")
form.find("#line_type").val dataHolder.data("lineType")
form.find("#note_commit_id").val dataHolder.data("commitId")
form.find("#note_line_code").val dataHolder.data("lineCode")
form.find("#note_position").val dataHolder.attr("data-position")
form.find("#note_noteable_type").val dataHolder.data("noteableType")
form.find("#note_noteable_id").val dataHolder.data("noteableId")
form.find('.js-note-discard')
......
class @NotificationsDropdown
$ ->
constructor: ->
$(document)
.off 'click', '.update-notification'
.on 'click', '.update-notification', (e) ->
......@@ -18,7 +18,8 @@ class @NotificationsDropdown
.off 'ajax:success', '.notification-form'
.on 'ajax:success', '.notification-form', (e, data) ->
if data.saved
new Flash('Notification settings saved', 'notice')
$(e.currentTarget).closest('.notification-dropdown').replaceWith(data.html)
$(e.currentTarget)
.closest('.notification-dropdown')
.replaceWith(data.html)
else
new Flash('Failed to save new settings', 'alert')
......@@ -78,3 +78,6 @@ $ ->
if comment && comment.length > 1 && $title.val() == ''
$title.val(comment[1]).change()
if gl.utils.getPagePath() == 'profiles'
new Profile()
......@@ -19,6 +19,7 @@ class @Project
$('.clone').text(url)
# Ref switcher
@initRefSwitcher()
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
......@@ -34,7 +35,6 @@ class @Project
$(@).parents('.no-password-message').remove()
e.preventDefault()
@projectSelectDropdown()
projectSelectDropdown: ->
......@@ -50,3 +50,42 @@ class @Project
changeProject: (url) ->
window.location = url
initRefSwitcher: ->
$('.js-project-refs-dropdown').each ->
$dropdown = $(@)
selected = $dropdown.data('selected')
$dropdown.glDropdown(
data: (term, callback) ->
$.ajax(
url: $dropdown.data('refs-url')
data:
ref: $dropdown.data('ref')
).done (refs) ->
callback(refs)
selectable: true
filterable: true
filterByText: true
fieldName: 'ref'
renderRow: (ref) ->
if ref.header?
$('<li />')
.addClass('dropdown-header')
.text(ref.header)
else
link = $('<a />')
.attr('href', '#')
.addClass(if ref is selected then 'is-active' else '')
.text(ref)
.attr('data-ref', escape(ref))
$('<li />')
.append(link)
id: (obj, $el) ->
$el.attr('data-ref')
toggleLabel: (obj, $el) ->
$el.text().trim()
clicked: (e) ->
$dropdown.closest('form').submit()
)
......@@ -5,13 +5,12 @@
this.initPagination()
initSearch: ->
@timer = null
$(".projects-list-filter").on('keyup', ->
clearTimeout(@timer)
@timer = setTimeout(ProjectsList.filterResults, 500)
)
projectsListFilter = $('.projects-list-filter')
debounceFilter = _.debounce ProjectsList.filterResults, 500
projectsListFilter.on 'keyup', (e) ->
debounceFilter() if projectsListFilter.val() isnt ''
filterResults: =>
filterResults: ->
$('.projects-list-holder').fadeTo(250, 0.5)
form = null
......
class @ProtectedBranchSelect
constructor: (currentProject) ->
$('.dropdown-footer').hide();
@dropdown = $('.js-protected-branch-select').glDropdown(
data: @getProtectedBranches
filterable: true
remote: false
search:
fields: ['title']
selectable: true
toggleLabel: (selected) -> if (selected and 'id' of selected) then selected.title else 'Protected Branch'
fieldName: 'protected_branch[name]'
text: (protected_branch) -> _.escape(protected_branch.title)
id: (protected_branch) -> _.escape(protected_branch.id)
onFilter: @toggleCreateNewButton
clicked: () -> $('.protect-branch-btn').attr('disabled', false)
)
$('.create-new-protected-branch').on 'click', (event) =>
# Refresh the dropdown's data, which ends up calling `getProtectedBranches`
@dropdown.data('glDropdown').remote.execute()
@dropdown.data('glDropdown').selectRowAtIndex(event, 0)
getProtectedBranches: (term, callback) =>
if @selectedBranch
callback(gon.open_branches.concat(@selectedBranch))
else
callback(gon.open_branches)
toggleCreateNewButton: (branchName) =>
@selectedBranch = { title: branchName, id: branchName, text: branchName }
if branchName is ''
$('.protected-branch-select-footer-list').addClass('hidden')
$('.dropdown-footer').hide();
else
$('.create-new-protected-branch').text("Create Protected Branch: #{branchName}")
$('.protected-branch-select-footer-list').removeClass('hidden')
$('.dropdown-footer').show();
$ ->
$(".protected-branches-list :checkbox").change (e) ->
name = $(this).attr("name")
if name == "developers_can_push"
if name == "developers_can_push" || name == "developers_can_merge"
id = $(this).val()
checked = $(this).is(":checked")
can_push = $(this).is(":checked")
url = $(this).data("url")
$.ajax
type: "PUT"
type: "PATCH"
url: url
dataType: "json"
data:
id: id
developers_can_push: checked
protected_branch:
"#{name}": can_push
success: ->
row = $(e.target)
......
......@@ -171,22 +171,15 @@ class @SearchAutocomplete
}
bindEvents: ->
$(document).on 'click', @onDocumentClick
@searchInput.on 'keydown', @onSearchInputKeyDown
@searchInput.on 'keyup', @onSearchInputKeyUp
@searchInput.on 'click', @onSearchInputClick
@searchInput.on 'focus', @onSearchInputFocus
@searchInput.on 'blur', @onSearchInputBlur
@clearInput.on 'click', @onClearInputClick
@locationBadgeEl.on 'click', =>
@searchInput.focus()
onDocumentClick: (e) =>
# If clicking outside the search box
# And search input is not focused
# And we are not clicking inside a suggestion
if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).closest('.search-form').length
@onSearchInputBlur()
enableAutocomplete: ->
# No need to enable anything if user is not logged in
return if !gon.current_user_id
......@@ -287,8 +280,6 @@ class @SearchAutocomplete
value: @originalState._location
)
@dropdown.removeClass 'open'
badgePresent: ->
@locationBadgeEl.length
......
......@@ -2,19 +2,20 @@ class @Shortcuts
constructor: (skipResetBindings) ->
@enabledHelp = []
Mousetrap.reset() if not skipResetBindings
Mousetrap.bind('?', @onToggleHelp)
Mousetrap.bind('s', Shortcuts.focusSearch)
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview)
Mousetrap.bind '?', @onToggleHelp
Mousetrap.bind 's', Shortcuts.focusSearch
Mousetrap.bind 'f', (e) => @focusFilter e
Mousetrap.bind ['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview
Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL?
onToggleHelp: (e) =>
e.preventDefault()
@toggleHelp(@enabledHelp)
Shortcuts.toggleHelp(@enabledHelp)
toggleMarkdownPreview: (e) =>
toggleMarkdownPreview: (e) ->
$(document).triggerHandler('markdown-preview:toggle', [e])
toggleHelp: (location) ->
@toggleHelp: (location) ->
$modal = $('#modal-shortcuts')
if $modal.length
......@@ -32,10 +33,16 @@ class @Shortcuts
$('.js-more-help-button').remove()
)
focusFilter: (e) ->
@filterInput ?= $('input[type=search]', '.nav-controls')
@filterInput.focus()
e.preventDefault()
@focusSearch: (e) ->
$('#search').focus()
e.preventDefault()
$(document).on 'click.more_help', '.js-more-help-button', (e) ->
$(@).remove()
$('.hidden-shortcut').show()
......
class @SingleFileDiff
WRAPPER = '<div class="diff-content diff-wrap-lines"></div>'
LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'
ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'
COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. Click to expand it.</div>'
constructor: (@file) ->
@content = $('.diff-content', @file)
@diffForPath = @content.find('[data-diff-for-path]').data 'diff-for-path'
@isOpen = !@diffForPath
if @diffForPath
@collapsedContent = @content
@loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide()
@content = null
@collapsedContent.after(@loadingContent)
else
@collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide()
@content.after(@collapsedContent)
@collapsedContent.on 'click', @toggleDiff
$('.file-title > a', @file).on 'click', @toggleDiff
toggleDiff: (e) =>
@isOpen = !@isOpen
if not @isOpen and not @hasError
@content.hide()
@collapsedContent.show()
else if @content
@collapsedContent.hide()
@content.show()
else
@getContentHTML()
getContentHTML: ->
@collapsedContent.hide()
@loadingContent.show()
$.get @diffForPath, (data) =>
@loadingContent.hide()
if data.html
@content = $(data.html)
@content.syntaxHighlight()
else
@hasError = true
@content = $(ERROR_HTML)
@collapsedContent.after(@content)
return
$.fn.singleFileDiff = ->
return @each ->
if not $.data this, 'singleFileDiff'
$.data this, 'singleFileDiff', new SingleFileDiff this
class @SubscriptionSelect
constructor: ->
$('.js-subscription-event').each (i, el) ->
fieldName = $(el).data("field-name")
$(el).glDropdown(
selectable: true
fieldName: fieldName
toggleLabel: (selected, el, instance) =>
label = 'Subscription'
$item = instance.dropdown.find('.is-active')
label = $item.text() if $item.length
label
clicked: (item, $el, e)->
e.preventDefault()
id: (obj, el) ->
$(el).data("id")
)
......@@ -5,9 +5,15 @@ class @TreeView
# Code browser tree slider
# Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
$(".tree-content-holder .tree-item").on 'click', (e) ->
if (e.target.nodeName != "A")
path = $('.tree-item-file-name a', this).attr('href')
Turbolinks.visit(path)
$clickedEl = $(e.target)
path = $('.tree-item-file-name a', this).attr('href')
if not $clickedEl.is('a') and not $clickedEl.is('.str-truncated')
if e.metaKey or e.which is 2
e.preventDefault()
window.open path, '_blank'
else
Turbolinks.visit path
# Show the "Loading commit data" for only the first element
$('span.log_loading:first').removeClass('hide')
......
......@@ -6,8 +6,20 @@
class @U2FAuthenticate
constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id
@challenges = u2fParams.challenges
@signRequests = u2fParams.sign_requests
@challenge = u2fParams.challenge
# The U2F Javascript API v1.1 requires a single challenge, with
# _no challenges per-request_. The U2F Javascript API v1.0 requires a
# challenge per-request, which is done by copying the single challenge
# into every request.
#
# In either case, we don't need the per-request challenges that the server
# has generated, so we can remove them.
#
# Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
# This can be removed once we upgrade.
# https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
@signRequests = u2fParams.sign_requests.map (request) -> _(request).omit('challenge')
start: () =>
if U2FUtil.isU2FSupported()
......@@ -16,7 +28,7 @@ class @U2FAuthenticate
@renderNotSupported()
authenticate: () =>
u2f.sign(@appId, @challenges, @signRequests, (response) =>
u2f.sign(@appId, @challenge, @signRequests, (response) =>
if response.errorCode
error = new U2FError(response.errorCode)
@renderError(error);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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