Merge branch 'master' into zj-create-mattermost-team
This commit is contained in:
		
						commit
						ddfd09c09c
					
				| 
						 | 
				
			
			@ -23,6 +23,7 @@ AllCops:
 | 
			
		|||
    - 'tmp/**/*'
 | 
			
		||||
    - 'bin/**/*'
 | 
			
		||||
    - 'generator_templates/**/*'
 | 
			
		||||
    - 'builds/**/*'
 | 
			
		||||
 | 
			
		||||
# Gems in consecutive lines should be alphabetically sorted
 | 
			
		||||
Bundler/OrderedGems:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										25
									
								
								CHANGELOG.md
								
								
								
								
							
							
						
						
									
										25
									
								
								CHANGELOG.md
								
								
								
								
							| 
						 | 
				
			
			@ -2,6 +2,25 @@
 | 
			
		|||
documentation](doc/development/changelog.md) for instructions on adding your own
 | 
			
		||||
entry.
 | 
			
		||||
 | 
			
		||||
## 8.17.2 (2017-03-01)
 | 
			
		||||
 | 
			
		||||
- Expire all webpack assets after 8.17.1 included a badly compiled asset. !9602
 | 
			
		||||
 | 
			
		||||
## 8.17.1 (2017-02-28)
 | 
			
		||||
 | 
			
		||||
- Replace setInterval with setTimeout to prevent highly frequent requests. !9271 (Takuya Noguchi)
 | 
			
		||||
- Disable unused tags count cache for Projects, Builds and Runners.
 | 
			
		||||
- Spam check and reCAPTCHA improvements.
 | 
			
		||||
- Allow searching issues for strings containing colons.
 | 
			
		||||
- Disabled tooltip on add issues button in usse boards.
 | 
			
		||||
- Fixed commit search UI.
 | 
			
		||||
- Fix MR changes tab size count when there are over 100 files in the diff.
 | 
			
		||||
- Disable invalid service templates.
 | 
			
		||||
- Use default branch as target_branch when parameter is missing.
 | 
			
		||||
- Upgrade GitLab Pages to v0.3.2.
 | 
			
		||||
- Add performance query regression fix for !9088 affecting #27267.
 | 
			
		||||
- Chat slash commands show labels correctly.
 | 
			
		||||
 | 
			
		||||
## 8.17.0 (2017-02-22)
 | 
			
		||||
 | 
			
		||||
- API: Fix file downloading. !0 (8267)
 | 
			
		||||
| 
						 | 
				
			
			@ -182,6 +201,12 @@ entry.
 | 
			
		|||
- Remove deprecated GitlabCiService.
 | 
			
		||||
- Requeue pending deletion projects.
 | 
			
		||||
 | 
			
		||||
## 8.16.7 (2017-02-27)
 | 
			
		||||
 | 
			
		||||
- No changes.
 | 
			
		||||
- No changes.
 | 
			
		||||
- Fix MR changes tab size count when there are over 100 files in the diff.
 | 
			
		||||
 | 
			
		||||
## 8.16.6 (2017-02-17)
 | 
			
		||||
 | 
			
		||||
- API: Fix file downloading. !0 (8267)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,28 @@
 | 
			
		|||
## Contributor license agreement
 | 
			
		||||
 | 
			
		||||
By submitting code as an individual you agree to the
 | 
			
		||||
[individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md).
 | 
			
		||||
By submitting code as an entity you agree to the
 | 
			
		||||
[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
 | 
			
		||||
 | 
			
		||||
_This notice should stay as the first item in the CONTRIBUTING.MD file._
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
 | 
			
		||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 | 
			
		||||
**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
 | 
			
		||||
 | 
			
		||||
- [Contribute to GitLab](#contribute-to-gitlab)
 | 
			
		||||
- [Contributor license agreement](#contributor-license-agreement)
 | 
			
		||||
- [Contribute to GitLab](#contribute-to-gitlab)
 | 
			
		||||
- [Security vulnerability disclosure](#security-vulnerability-disclosure)
 | 
			
		||||
- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
 | 
			
		||||
- [Helping others](#helping-others)
 | 
			
		||||
- [I want to contribute!](#i-want-to-contribute)
 | 
			
		||||
- [Implement design & UI elements](#implement-design-ui-elements)
 | 
			
		||||
- [Release retrospective and kickoff](#release-retrospective-and-kickoff)
 | 
			
		||||
    - [Retrospective](#retrospective)
 | 
			
		||||
    - [Kickoff](#kickoff)
 | 
			
		||||
- [Issue tracker](#issue-tracker)
 | 
			
		||||
    - [Feature proposals](#feature-proposals)
 | 
			
		||||
    - [Issue tracker guidelines](#issue-tracker-guidelines)
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +40,9 @@
 | 
			
		|||
 | 
			
		||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
 | 
			
		||||
 | 
			
		||||
# Contribute to GitLab
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## Contribute to GitLab
 | 
			
		||||
 | 
			
		||||
Thank you for your interest in contributing to GitLab. This guide details how
 | 
			
		||||
to contribute to GitLab in a way that is efficient for everyone.
 | 
			
		||||
| 
						 | 
				
			
			@ -41,13 +57,6 @@ operates please see [the GitLab contributing process](PROCESS.md).
 | 
			
		|||
 | 
			
		||||
- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
 | 
			
		||||
 | 
			
		||||
## Contributor license agreement
 | 
			
		||||
 | 
			
		||||
By submitting code as an individual you agree to the
 | 
			
		||||
[individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md).
 | 
			
		||||
By submitting code as an entity you agree to the
 | 
			
		||||
[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
 | 
			
		||||
 | 
			
		||||
## Security vulnerability disclosure
 | 
			
		||||
 | 
			
		||||
Please report suspected security vulnerabilities in private to
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										9
									
								
								Gemfile
								
								
								
								
							
							
						
						
									
										9
									
								
								Gemfile
								
								
								
								
							| 
						 | 
				
			
			@ -20,7 +20,7 @@ gem 'rugged', '~> 0.24.0'
 | 
			
		|||
# Authentication libraries
 | 
			
		||||
gem 'devise',                 '~> 4.2'
 | 
			
		||||
gem 'doorkeeper',             '~> 4.2.0'
 | 
			
		||||
gem 'omniauth',               '~> 1.3.2'
 | 
			
		||||
gem 'omniauth',               '~> 1.4.2'
 | 
			
		||||
gem 'omniauth-auth0',         '~> 1.4.1'
 | 
			
		||||
gem 'omniauth-azure-oauth2',  '~> 0.0.6'
 | 
			
		||||
gem 'omniauth-cas3',          '~> 1.1.2'
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +68,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
 | 
			
		|||
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
 | 
			
		||||
 | 
			
		||||
# API
 | 
			
		||||
gem 'grape',        '~> 0.18.0'
 | 
			
		||||
gem 'grape',        '~> 0.19.0'
 | 
			
		||||
gem 'grape-entity', '~> 0.6.0'
 | 
			
		||||
gem 'rack-cors',    '~> 0.4.0', require: 'rack/cors'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -329,8 +329,6 @@ group :test do
 | 
			
		|||
  gem 'timecop', '~> 0.8.0'
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
gem 'newrelic_rpm', '~> 3.16'
 | 
			
		||||
 | 
			
		||||
gem 'octokit', '~> 4.6.2'
 | 
			
		||||
 | 
			
		||||
gem 'mail_room', '~> 0.9.1'
 | 
			
		||||
| 
						 | 
				
			
			@ -352,3 +350,6 @@ gem 'health_check', '~> 2.2.0'
 | 
			
		|||
# System information
 | 
			
		||||
gem 'vmstat', '~> 2.3.0'
 | 
			
		||||
gem 'sys-filesystem', '~> 1.1.6'
 | 
			
		||||
 | 
			
		||||
# Gitaly GRPC client
 | 
			
		||||
gem 'gitaly', '~> 0.2.1'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								Gemfile.lock
								
								
								
								
							
							
						
						
									
										30
									
								
								Gemfile.lock
								
								
								
								
							| 
						 | 
				
			
			@ -245,6 +245,9 @@ GEM
 | 
			
		|||
      json
 | 
			
		||||
    get_process_mem (0.2.0)
 | 
			
		||||
    gherkin-ruby (0.3.2)
 | 
			
		||||
    gitaly (0.2.1)
 | 
			
		||||
      google-protobuf (~> 3.1)
 | 
			
		||||
      grpc (~> 1.0)
 | 
			
		||||
    github-linguist (4.7.6)
 | 
			
		||||
      charlock_holmes (~> 0.7.3)
 | 
			
		||||
      escape_utils (~> 1.1.0)
 | 
			
		||||
| 
						 | 
				
			
			@ -296,6 +299,7 @@ GEM
 | 
			
		|||
      multi_json (~> 1.10)
 | 
			
		||||
      retriable (~> 1.4)
 | 
			
		||||
      signet (~> 0.6)
 | 
			
		||||
    google-protobuf (3.2.0)
 | 
			
		||||
    googleauth (0.5.1)
 | 
			
		||||
      faraday (~> 0.9)
 | 
			
		||||
      jwt (~> 1.4)
 | 
			
		||||
| 
						 | 
				
			
			@ -304,7 +308,7 @@ GEM
 | 
			
		|||
      multi_json (~> 1.11)
 | 
			
		||||
      os (~> 0.9)
 | 
			
		||||
      signet (~> 0.7)
 | 
			
		||||
    grape (0.18.0)
 | 
			
		||||
    grape (0.19.1)
 | 
			
		||||
      activesupport
 | 
			
		||||
      builder
 | 
			
		||||
      hashie (>= 2.1.0)
 | 
			
		||||
| 
						 | 
				
			
			@ -317,6 +321,9 @@ GEM
 | 
			
		|||
    grape-entity (0.6.0)
 | 
			
		||||
      activesupport
 | 
			
		||||
      multi_json (>= 1.3.2)
 | 
			
		||||
    grpc (1.1.2)
 | 
			
		||||
      google-protobuf (~> 3.1)
 | 
			
		||||
      googleauth (~> 0.5.1)
 | 
			
		||||
    haml (4.0.7)
 | 
			
		||||
      tilt
 | 
			
		||||
    haml_lint (0.21.0)
 | 
			
		||||
| 
						 | 
				
			
			@ -328,7 +335,7 @@ GEM
 | 
			
		|||
      temple (~> 0.7.6)
 | 
			
		||||
      thor
 | 
			
		||||
      tilt
 | 
			
		||||
    hashie (3.4.4)
 | 
			
		||||
    hashie (3.5.5)
 | 
			
		||||
    health_check (2.2.1)
 | 
			
		||||
      rails (>= 4.0)
 | 
			
		||||
    hipchat (1.5.2)
 | 
			
		||||
| 
						 | 
				
			
			@ -353,8 +360,8 @@ GEM
 | 
			
		|||
      json (~> 1.8)
 | 
			
		||||
      multi_xml (>= 0.5.2)
 | 
			
		||||
    httpclient (2.8.2)
 | 
			
		||||
    i18n (0.8.0)
 | 
			
		||||
    ice_nine (0.11.1)
 | 
			
		||||
    i18n (0.8.1)
 | 
			
		||||
    ice_nine (0.11.2)
 | 
			
		||||
    influxdb (0.2.3)
 | 
			
		||||
      cause
 | 
			
		||||
      json
 | 
			
		||||
| 
						 | 
				
			
			@ -417,7 +424,7 @@ GEM
 | 
			
		|||
    minitest (5.7.0)
 | 
			
		||||
    mousetrap-rails (1.4.6)
 | 
			
		||||
    multi_json (1.12.1)
 | 
			
		||||
    multi_xml (0.5.5)
 | 
			
		||||
    multi_xml (0.6.0)
 | 
			
		||||
    multipart-post (2.0.0)
 | 
			
		||||
    mustermann (0.4.0)
 | 
			
		||||
      tool (~> 0.2)
 | 
			
		||||
| 
						 | 
				
			
			@ -427,7 +434,6 @@ GEM
 | 
			
		|||
    net-ldap (0.12.1)
 | 
			
		||||
    net-ssh (3.0.1)
 | 
			
		||||
    netrc (0.11.0)
 | 
			
		||||
    newrelic_rpm (3.16.0.318)
 | 
			
		||||
    nokogiri (1.6.8.1)
 | 
			
		||||
      mini_portile2 (~> 2.1.0)
 | 
			
		||||
    numerizer (0.1.1)
 | 
			
		||||
| 
						 | 
				
			
			@ -441,7 +447,7 @@ GEM
 | 
			
		|||
    octokit (4.6.2)
 | 
			
		||||
      sawyer (~> 0.8.0, >= 0.5.3)
 | 
			
		||||
    oj (2.17.4)
 | 
			
		||||
    omniauth (1.3.2)
 | 
			
		||||
    omniauth (1.4.2)
 | 
			
		||||
      hashie (>= 1.2, < 4)
 | 
			
		||||
      rack (>= 1.0, < 3)
 | 
			
		||||
    omniauth-auth0 (1.4.1)
 | 
			
		||||
| 
						 | 
				
			
			@ -758,7 +764,7 @@ GEM
 | 
			
		|||
      eventmachine (~> 1.0, >= 1.0.4)
 | 
			
		||||
      rack (>= 1, < 3)
 | 
			
		||||
    thor (0.19.4)
 | 
			
		||||
    thread_safe (0.3.5)
 | 
			
		||||
    thread_safe (0.3.6)
 | 
			
		||||
    tilt (2.0.6)
 | 
			
		||||
    timecop (0.8.1)
 | 
			
		||||
    timfel-krb5-auth (0.8.3)
 | 
			
		||||
| 
						 | 
				
			
			@ -878,6 +884,7 @@ DEPENDENCIES
 | 
			
		|||
  fuubar (~> 2.0.0)
 | 
			
		||||
  gemnasium-gitlab-service (~> 0.2)
 | 
			
		||||
  gemojione (~> 3.0)
 | 
			
		||||
  gitaly (~> 0.2.1)
 | 
			
		||||
  github-linguist (~> 4.7.0)
 | 
			
		||||
  gitlab-flowdock-git-hook (~> 1.0.1)
 | 
			
		||||
  gitlab-markup (~> 1.5.1)
 | 
			
		||||
| 
						 | 
				
			
			@ -886,7 +893,7 @@ DEPENDENCIES
 | 
			
		|||
  gollum-rugged_adapter (~> 0.4.2)
 | 
			
		||||
  gon (~> 6.1.0)
 | 
			
		||||
  google-api-client (~> 0.8.6)
 | 
			
		||||
  grape (~> 0.18.0)
 | 
			
		||||
  grape (~> 0.19.0)
 | 
			
		||||
  grape-entity (~> 0.6.0)
 | 
			
		||||
  haml_lint (~> 0.21.0)
 | 
			
		||||
  hamlit (~> 2.6.1)
 | 
			
		||||
| 
						 | 
				
			
			@ -915,12 +922,11 @@ DEPENDENCIES
 | 
			
		|||
  mousetrap-rails (~> 1.4.6)
 | 
			
		||||
  mysql2 (~> 0.3.16)
 | 
			
		||||
  net-ssh (~> 3.0.1)
 | 
			
		||||
  newrelic_rpm (~> 3.16)
 | 
			
		||||
  nokogiri (~> 1.6.7, >= 1.6.7.2)
 | 
			
		||||
  oauth2 (~> 1.2.0)
 | 
			
		||||
  octokit (~> 4.6.2)
 | 
			
		||||
  oj (~> 2.17.4)
 | 
			
		||||
  omniauth (~> 1.3.2)
 | 
			
		||||
  omniauth (~> 1.4.2)
 | 
			
		||||
  omniauth-auth0 (~> 1.4.1)
 | 
			
		||||
  omniauth-authentiq (~> 0.3.0)
 | 
			
		||||
  omniauth-azure-oauth2 (~> 0.0.6)
 | 
			
		||||
| 
						 | 
				
			
			@ -1011,4 +1017,4 @@ DEPENDENCIES
 | 
			
		|||
  wikicloth (= 0.8.1)
 | 
			
		||||
 | 
			
		||||
BUNDLED WITH
 | 
			
		||||
   1.14.3
 | 
			
		||||
   1.14.4
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,13 +6,7 @@
 | 
			
		|||
/* global AwardsHandler */
 | 
			
		||||
/* global Aside */
 | 
			
		||||
 | 
			
		||||
function requireAll(context) { return context.keys().map(context); }
 | 
			
		||||
 | 
			
		||||
window.$ = window.jQuery = require('jquery');
 | 
			
		||||
require('jquery-ui/ui/autocomplete');
 | 
			
		||||
require('jquery-ui/ui/draggable');
 | 
			
		||||
require('jquery-ui/ui/effect-highlight');
 | 
			
		||||
require('jquery-ui/ui/sortable');
 | 
			
		||||
require('jquery-ujs');
 | 
			
		||||
require('vendor/jquery.endless-scroll');
 | 
			
		||||
require('vendor/jquery.highlight');
 | 
			
		||||
| 
						 | 
				
			
			@ -46,15 +40,176 @@ require('./shortcuts_dashboard_navigation');
 | 
			
		|||
require('./shortcuts_issuable');
 | 
			
		||||
require('./shortcuts_network');
 | 
			
		||||
require('vendor/jquery.nicescroll');
 | 
			
		||||
requireAll(require.context('./behaviors',  false, /^\.\/.*\.(js|es6)$/));
 | 
			
		||||
requireAll(require.context('./blob',       false, /^\.\/.*\.(js|es6)$/));
 | 
			
		||||
requireAll(require.context('./templates',  false, /^\.\/.*\.(js|es6)$/));
 | 
			
		||||
requireAll(require.context('./commit',     false, /^\.\/.*\.(js|es6)$/));
 | 
			
		||||
requireAll(require.context('./extensions', false, /^\.\/.*\.(js|es6)$/));
 | 
			
		||||
requireAll(require.context('./lib/utils',  false, /^\.\/.*\.(js|es6)$/));
 | 
			
		||||
requireAll(require.context('./u2f',        false, /^\.\/.*\.(js|es6)$/));
 | 
			
		||||
requireAll(require.context('./droplab',    false, /^\.\/.*\.(js|es6)$/));
 | 
			
		||||
requireAll(require.context('.',            false, /^\.\/(?!application\.js).*\.(js|es6)$/));
 | 
			
		||||
 | 
			
		||||
// behaviors
 | 
			
		||||
require('./behaviors/autosize');
 | 
			
		||||
require('./behaviors/details_behavior');
 | 
			
		||||
require('./behaviors/quick_submit');
 | 
			
		||||
require('./behaviors/requires_input');
 | 
			
		||||
require('./behaviors/toggler_behavior');
 | 
			
		||||
 | 
			
		||||
// blob
 | 
			
		||||
require('./blob/blob_ci_yaml');
 | 
			
		||||
require('./blob/blob_dockerfile_selector');
 | 
			
		||||
require('./blob/blob_dockerfile_selectors');
 | 
			
		||||
require('./blob/blob_file_dropzone');
 | 
			
		||||
require('./blob/blob_gitignore_selector');
 | 
			
		||||
require('./blob/blob_gitignore_selectors');
 | 
			
		||||
require('./blob/blob_license_selector');
 | 
			
		||||
require('./blob/blob_license_selectors');
 | 
			
		||||
require('./blob/template_selector');
 | 
			
		||||
 | 
			
		||||
// templates
 | 
			
		||||
require('./templates/issuable_template_selector');
 | 
			
		||||
require('./templates/issuable_template_selectors');
 | 
			
		||||
 | 
			
		||||
// commit
 | 
			
		||||
require('./commit/file.js');
 | 
			
		||||
require('./commit/image_file.js');
 | 
			
		||||
 | 
			
		||||
// extensions
 | 
			
		||||
require('./extensions/array');
 | 
			
		||||
require('./extensions/custom_event');
 | 
			
		||||
require('./extensions/element');
 | 
			
		||||
require('./extensions/jquery');
 | 
			
		||||
require('./extensions/object');
 | 
			
		||||
 | 
			
		||||
// lib/utils
 | 
			
		||||
require('./lib/utils/animate');
 | 
			
		||||
require('./lib/utils/bootstrap_linked_tabs');
 | 
			
		||||
require('./lib/utils/common_utils');
 | 
			
		||||
require('./lib/utils/datetime_utility');
 | 
			
		||||
require('./lib/utils/notify');
 | 
			
		||||
require('./lib/utils/pretty_time');
 | 
			
		||||
require('./lib/utils/text_utility');
 | 
			
		||||
require('./lib/utils/type_utility');
 | 
			
		||||
require('./lib/utils/url_utility');
 | 
			
		||||
 | 
			
		||||
// u2f
 | 
			
		||||
require('./u2f/authenticate');
 | 
			
		||||
require('./u2f/error');
 | 
			
		||||
require('./u2f/register');
 | 
			
		||||
require('./u2f/util');
 | 
			
		||||
 | 
			
		||||
// droplab
 | 
			
		||||
require('./droplab/droplab');
 | 
			
		||||
require('./droplab/droplab_ajax');
 | 
			
		||||
require('./droplab/droplab_ajax_filter');
 | 
			
		||||
require('./droplab/droplab_filter');
 | 
			
		||||
 | 
			
		||||
// everything else
 | 
			
		||||
require('./abuse_reports');
 | 
			
		||||
require('./activities');
 | 
			
		||||
require('./admin');
 | 
			
		||||
require('./ajax_loading_spinner');
 | 
			
		||||
require('./api');
 | 
			
		||||
require('./aside');
 | 
			
		||||
require('./autosave');
 | 
			
		||||
require('./awards_handler');
 | 
			
		||||
require('./breakpoints');
 | 
			
		||||
require('./broadcast_message');
 | 
			
		||||
require('./build');
 | 
			
		||||
require('./build_artifacts');
 | 
			
		||||
require('./build_variables');
 | 
			
		||||
require('./ci_lint_editor');
 | 
			
		||||
require('./commit');
 | 
			
		||||
require('./commits');
 | 
			
		||||
require('./compare');
 | 
			
		||||
require('./compare_autocomplete');
 | 
			
		||||
require('./confirm_danger_modal');
 | 
			
		||||
require('./copy_as_gfm');
 | 
			
		||||
require('./copy_to_clipboard');
 | 
			
		||||
require('./create_label');
 | 
			
		||||
require('./diff');
 | 
			
		||||
require('./dispatcher');
 | 
			
		||||
require('./dropzone_input');
 | 
			
		||||
require('./due_date_select');
 | 
			
		||||
require('./files_comment_button');
 | 
			
		||||
require('./flash');
 | 
			
		||||
require('./gfm_auto_complete');
 | 
			
		||||
require('./gl_dropdown');
 | 
			
		||||
require('./gl_field_error');
 | 
			
		||||
require('./gl_field_errors');
 | 
			
		||||
require('./gl_form');
 | 
			
		||||
require('./group_avatar');
 | 
			
		||||
require('./group_label_subscription');
 | 
			
		||||
require('./groups_select');
 | 
			
		||||
require('./header');
 | 
			
		||||
require('./importer_status');
 | 
			
		||||
require('./issuable');
 | 
			
		||||
require('./issuable_context');
 | 
			
		||||
require('./issuable_form');
 | 
			
		||||
require('./issue');
 | 
			
		||||
require('./issue_status_select');
 | 
			
		||||
require('./issues_bulk_assignment');
 | 
			
		||||
require('./label_manager');
 | 
			
		||||
require('./labels');
 | 
			
		||||
require('./labels_select');
 | 
			
		||||
require('./layout_nav');
 | 
			
		||||
require('./line_highlighter');
 | 
			
		||||
require('./logo');
 | 
			
		||||
require('./member_expiration_date');
 | 
			
		||||
require('./members');
 | 
			
		||||
require('./merge_request');
 | 
			
		||||
require('./merge_request_tabs');
 | 
			
		||||
require('./merge_request_widget');
 | 
			
		||||
require('./merged_buttons');
 | 
			
		||||
require('./milestone');
 | 
			
		||||
require('./milestone_select');
 | 
			
		||||
require('./mini_pipeline_graph_dropdown');
 | 
			
		||||
require('./namespace_select');
 | 
			
		||||
require('./new_branch_form');
 | 
			
		||||
require('./new_commit_form');
 | 
			
		||||
require('./notes');
 | 
			
		||||
require('./notifications_dropdown');
 | 
			
		||||
require('./notifications_form');
 | 
			
		||||
require('./pager');
 | 
			
		||||
require('./pipelines');
 | 
			
		||||
require('./preview_markdown');
 | 
			
		||||
require('./project');
 | 
			
		||||
require('./project_avatar');
 | 
			
		||||
require('./project_find_file');
 | 
			
		||||
require('./project_fork');
 | 
			
		||||
require('./project_import');
 | 
			
		||||
require('./project_label_subscription');
 | 
			
		||||
require('./project_new');
 | 
			
		||||
require('./project_select');
 | 
			
		||||
require('./project_show');
 | 
			
		||||
require('./project_variables');
 | 
			
		||||
require('./projects_list');
 | 
			
		||||
require('./render_gfm');
 | 
			
		||||
require('./render_math');
 | 
			
		||||
require('./right_sidebar');
 | 
			
		||||
require('./search');
 | 
			
		||||
require('./search_autocomplete');
 | 
			
		||||
require('./shortcuts');
 | 
			
		||||
require('./shortcuts_blob');
 | 
			
		||||
require('./shortcuts_dashboard_navigation');
 | 
			
		||||
require('./shortcuts_find_file');
 | 
			
		||||
require('./shortcuts_issuable');
 | 
			
		||||
require('./shortcuts_navigation');
 | 
			
		||||
require('./shortcuts_network');
 | 
			
		||||
require('./signin_tabs_memoizer');
 | 
			
		||||
require('./single_file_diff');
 | 
			
		||||
require('./smart_interval');
 | 
			
		||||
require('./snippets_list');
 | 
			
		||||
require('./star');
 | 
			
		||||
require('./subbable_resource');
 | 
			
		||||
require('./subscription');
 | 
			
		||||
require('./subscription_select');
 | 
			
		||||
require('./syntax_highlight');
 | 
			
		||||
require('./task_list');
 | 
			
		||||
require('./todos');
 | 
			
		||||
require('./tree');
 | 
			
		||||
require('./user');
 | 
			
		||||
require('./user_tabs');
 | 
			
		||||
require('./username_validator');
 | 
			
		||||
require('./users_select');
 | 
			
		||||
require('./version_check_image');
 | 
			
		||||
require('./visibility_select');
 | 
			
		||||
require('./wikis');
 | 
			
		||||
require('./zen_mode');
 | 
			
		||||
 | 
			
		||||
require('vendor/fuzzaldrin-plus');
 | 
			
		||||
require('es6-promise').polyfill();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
/* global Vue */
 | 
			
		||||
require('./issue_card_inner');
 | 
			
		||||
 | 
			
		||||
const Store = gl.issueBoards.BoardsStore;
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'BoardsIssueCard',
 | 
			
		||||
  template: `
 | 
			
		||||
    <li class="card"
 | 
			
		||||
      :class="{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id, 'is-active': issueDetailVisible }"
 | 
			
		||||
      :index="index"
 | 
			
		||||
      :data-issue-id="issue.id"
 | 
			
		||||
      @mousedown="mouseDown"
 | 
			
		||||
      @mousemove="mouseMove"
 | 
			
		||||
      @mouseup="showIssue($event)">
 | 
			
		||||
      <issue-card-inner
 | 
			
		||||
        :list="list"
 | 
			
		||||
        :issue="issue"
 | 
			
		||||
        :issue-link-base="issueLinkBase"
 | 
			
		||||
        :root-path="rootPath" />
 | 
			
		||||
    </li>
 | 
			
		||||
  `,
 | 
			
		||||
  components: {
 | 
			
		||||
    'issue-card-inner': gl.issueBoards.IssueCardInner,
 | 
			
		||||
  },
 | 
			
		||||
  props: {
 | 
			
		||||
    list: Object,
 | 
			
		||||
    issue: Object,
 | 
			
		||||
    issueLinkBase: String,
 | 
			
		||||
    disabled: Boolean,
 | 
			
		||||
    index: Number,
 | 
			
		||||
    rootPath: String,
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      showDetail: false,
 | 
			
		||||
      detailIssue: Store.detail,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  computed: {
 | 
			
		||||
    issueDetailVisible() {
 | 
			
		||||
      return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    mouseDown() {
 | 
			
		||||
      this.showDetail = true;
 | 
			
		||||
    },
 | 
			
		||||
    mouseMove() {
 | 
			
		||||
      this.showDetail = false;
 | 
			
		||||
    },
 | 
			
		||||
    showIssue(e) {
 | 
			
		||||
      const targetTagName = e.target.tagName.toLowerCase();
 | 
			
		||||
 | 
			
		||||
      if (targetTagName === 'a' || targetTagName === 'button') return;
 | 
			
		||||
 | 
			
		||||
      if (this.showDetail) {
 | 
			
		||||
        this.showDetail = false;
 | 
			
		||||
 | 
			
		||||
        if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
 | 
			
		||||
          Store.detail.issue = {};
 | 
			
		||||
        } else {
 | 
			
		||||
          Store.detail.issue = this.issue;
 | 
			
		||||
          Store.detail.list = this.list;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,61 +0,0 @@
 | 
			
		|||
/* eslint-disable comma-dangle, space-before-function-paren, dot-notation */
 | 
			
		||||
/* global Vue */
 | 
			
		||||
 | 
			
		||||
require('./issue_card_inner');
 | 
			
		||||
 | 
			
		||||
(() => {
 | 
			
		||||
  const Store = gl.issueBoards.BoardsStore;
 | 
			
		||||
 | 
			
		||||
  window.gl = window.gl || {};
 | 
			
		||||
  window.gl.issueBoards = window.gl.issueBoards || {};
 | 
			
		||||
 | 
			
		||||
  gl.issueBoards.BoardCard = Vue.extend({
 | 
			
		||||
    template: '#js-board-list-card',
 | 
			
		||||
    components: {
 | 
			
		||||
      'issue-card-inner': gl.issueBoards.IssueCardInner,
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
      list: Object,
 | 
			
		||||
      issue: Object,
 | 
			
		||||
      issueLinkBase: String,
 | 
			
		||||
      disabled: Boolean,
 | 
			
		||||
      index: Number,
 | 
			
		||||
      rootPath: String,
 | 
			
		||||
    },
 | 
			
		||||
    data () {
 | 
			
		||||
      return {
 | 
			
		||||
        showDetail: false,
 | 
			
		||||
        detailIssue: Store.detail
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
      issueDetailVisible () {
 | 
			
		||||
        return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
      mouseDown () {
 | 
			
		||||
        this.showDetail = true;
 | 
			
		||||
      },
 | 
			
		||||
      mouseMove() {
 | 
			
		||||
        this.showDetail = false;
 | 
			
		||||
      },
 | 
			
		||||
      showIssue (e) {
 | 
			
		||||
        const targetTagName = e.target.tagName.toLowerCase();
 | 
			
		||||
 | 
			
		||||
        if (targetTagName === 'a' || targetTagName === 'button') return;
 | 
			
		||||
 | 
			
		||||
        if (this.showDetail) {
 | 
			
		||||
          this.showDetail = false;
 | 
			
		||||
 | 
			
		||||
          if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
 | 
			
		||||
            Store.detail.issue = {};
 | 
			
		||||
          } else {
 | 
			
		||||
            Store.detail.issue = this.issue;
 | 
			
		||||
            Store.detail.list = this.list;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
})();
 | 
			
		||||
| 
						 | 
				
			
			@ -2,8 +2,8 @@
 | 
			
		|||
/* global Vue */
 | 
			
		||||
/* global Sortable */
 | 
			
		||||
 | 
			
		||||
require('./board_card');
 | 
			
		||||
require('./board_new_issue');
 | 
			
		||||
import boardNewIssue from './board_new_issue';
 | 
			
		||||
import boardCard from './board_card';
 | 
			
		||||
 | 
			
		||||
(() => {
 | 
			
		||||
  const Store = gl.issueBoards.BoardsStore;
 | 
			
		||||
| 
						 | 
				
			
			@ -14,8 +14,8 @@ require('./board_new_issue');
 | 
			
		|||
  gl.issueBoards.BoardList = Vue.extend({
 | 
			
		||||
    template: '#js-board-list-template',
 | 
			
		||||
    components: {
 | 
			
		||||
      'board-card': gl.issueBoards.BoardCard,
 | 
			
		||||
      'board-new-issue': gl.issueBoards.BoardNewIssue
 | 
			
		||||
      boardCard,
 | 
			
		||||
      boardNewIssue,
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
      disabled: Boolean,
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +81,12 @@ require('./board_new_issue');
 | 
			
		|||
          });
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      toggleForm() {
 | 
			
		||||
        this.showIssueForm = !this.showIssueForm;
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    created() {
 | 
			
		||||
      gl.IssueBoardsApp.$on(`hide-issue-form-${this.list.id}`, this.toggleForm);
 | 
			
		||||
    },
 | 
			
		||||
    mounted () {
 | 
			
		||||
      const options = gl.issueBoards.getBoardSortableDefaultOptions({
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +121,9 @@ require('./board_new_issue');
 | 
			
		|||
          this.loadNextPage();
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
      gl.IssueBoardsApp.$off(`hide-issue-form-${this.list.id}`, this.toggleForm);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
})();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,92 @@
 | 
			
		|||
/* global ListIssue */
 | 
			
		||||
const Store = gl.issueBoards.BoardsStore;
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: 'BoardNewIssue',
 | 
			
		||||
  props: {
 | 
			
		||||
    list: Object,
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      title: '',
 | 
			
		||||
      error: false,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    submit(e) {
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      if (this.title.trim() === '') return;
 | 
			
		||||
 | 
			
		||||
      this.error = false;
 | 
			
		||||
 | 
			
		||||
      const labels = this.list.label ? [this.list.label] : [];
 | 
			
		||||
      const issue = new ListIssue({
 | 
			
		||||
        title: this.title,
 | 
			
		||||
        labels,
 | 
			
		||||
        subscribed: true,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      this.list.newIssue(issue)
 | 
			
		||||
        .then(() => {
 | 
			
		||||
          // Need this because our jQuery very kindly disables buttons on ALL form submissions
 | 
			
		||||
          $(this.$refs.submitButton).enable();
 | 
			
		||||
 | 
			
		||||
          Store.detail.issue = issue;
 | 
			
		||||
          Store.detail.list = this.list;
 | 
			
		||||
        })
 | 
			
		||||
        .catch(() => {
 | 
			
		||||
          // Need this because our jQuery very kindly disables buttons on ALL form submissions
 | 
			
		||||
          $(this.$refs.submitButton).enable();
 | 
			
		||||
 | 
			
		||||
          // Remove the issue
 | 
			
		||||
          this.list.removeIssue(issue);
 | 
			
		||||
 | 
			
		||||
          // Show error message
 | 
			
		||||
          this.error = true;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
      this.cancel();
 | 
			
		||||
    },
 | 
			
		||||
    cancel() {
 | 
			
		||||
      this.title = '';
 | 
			
		||||
      gl.IssueBoardsApp.$emit(`hide-issue-form-${this.list.id}`);
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.$refs.input.focus();
 | 
			
		||||
  },
 | 
			
		||||
  template: `
 | 
			
		||||
    <div class="card board-new-issue-form">
 | 
			
		||||
      <form @submit="submit($event)">
 | 
			
		||||
        <div class="flash-container"
 | 
			
		||||
          v-if="error">
 | 
			
		||||
          <div class="flash-alert">
 | 
			
		||||
            An error occured. Please try again.
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <label class="label-light"
 | 
			
		||||
          :for="list.id + '-title'">
 | 
			
		||||
          Title
 | 
			
		||||
        </label>
 | 
			
		||||
        <input class="form-control"
 | 
			
		||||
          type="text"
 | 
			
		||||
          v-model="title"
 | 
			
		||||
          ref="input"
 | 
			
		||||
          :id="list.id + '-title'" />
 | 
			
		||||
        <div class="clearfix prepend-top-10">
 | 
			
		||||
          <button class="btn btn-success pull-left"
 | 
			
		||||
            type="submit"
 | 
			
		||||
            :disabled="title === ''"
 | 
			
		||||
            ref="submit-button">
 | 
			
		||||
            Submit issue
 | 
			
		||||
          </button>
 | 
			
		||||
          <button class="btn btn-default pull-right"
 | 
			
		||||
            type="button"
 | 
			
		||||
            @click="cancel">
 | 
			
		||||
            Cancel
 | 
			
		||||
          </button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </form>
 | 
			
		||||
    </div>
 | 
			
		||||
  `,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,64 +0,0 @@
 | 
			
		|||
/* eslint-disable comma-dangle, no-unused-vars */
 | 
			
		||||
/* global Vue */
 | 
			
		||||
/* global ListIssue */
 | 
			
		||||
 | 
			
		||||
(() => {
 | 
			
		||||
  const Store = gl.issueBoards.BoardsStore;
 | 
			
		||||
 | 
			
		||||
  window.gl = window.gl || {};
 | 
			
		||||
 | 
			
		||||
  gl.issueBoards.BoardNewIssue = Vue.extend({
 | 
			
		||||
    props: {
 | 
			
		||||
      list: Object,
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
      return {
 | 
			
		||||
        title: '',
 | 
			
		||||
        error: false
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
      submit(e) {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        if (this.title.trim() === '') return;
 | 
			
		||||
 | 
			
		||||
        this.error = false;
 | 
			
		||||
 | 
			
		||||
        const labels = this.list.label ? [this.list.label] : [];
 | 
			
		||||
        const issue = new ListIssue({
 | 
			
		||||
          title: this.title,
 | 
			
		||||
          labels,
 | 
			
		||||
          subscribed: true
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.list.newIssue(issue)
 | 
			
		||||
          .then((data) => {
 | 
			
		||||
            // Need this because our jQuery very kindly disables buttons on ALL form submissions
 | 
			
		||||
            $(this.$refs.submitButton).enable();
 | 
			
		||||
 | 
			
		||||
            Store.detail.issue = issue;
 | 
			
		||||
            Store.detail.list = this.list;
 | 
			
		||||
          })
 | 
			
		||||
          .catch(() => {
 | 
			
		||||
            // Need this because our jQuery very kindly disables buttons on ALL form submissions
 | 
			
		||||
            $(this.$refs.submitButton).enable();
 | 
			
		||||
 | 
			
		||||
            // Remove the issue
 | 
			
		||||
            this.list.removeIssue(issue);
 | 
			
		||||
 | 
			
		||||
            // Show error message
 | 
			
		||||
            this.error = true;
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
        this.cancel();
 | 
			
		||||
      },
 | 
			
		||||
      cancel() {
 | 
			
		||||
        this.title = '';
 | 
			
		||||
        this.$parent.showIssueForm = false;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
      this.$refs.input.focus();
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
})();
 | 
			
		||||
| 
						 | 
				
			
			@ -123,13 +123,17 @@ class List {
 | 
			
		|||
 | 
			
		||||
      if (listFrom) {
 | 
			
		||||
        this.issuesSize += 1;
 | 
			
		||||
        this.updateIssueLabel(issue, listFrom);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateIssueLabel(issue, listFrom) {
 | 
			
		||||
    gl.boardService.moveIssue(issue.id, listFrom.id, this.id)
 | 
			
		||||
      .then(() => {
 | 
			
		||||
        listFrom.getIssues(false);
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  findIssue (id) {
 | 
			
		||||
    return this.issues.filter(issue => issue.id === id)[0];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -92,9 +92,12 @@
 | 
			
		|||
      const issueLists = issue.getLists();
 | 
			
		||||
      const listLabels = issueLists.map(listIssue => listIssue.label);
 | 
			
		||||
 | 
			
		||||
      // Add to new lists issues if it doesn't already exist
 | 
			
		||||
      if (!issueTo) {
 | 
			
		||||
        // Add to new lists issues if it doesn't already exist
 | 
			
		||||
        listTo.addIssue(issue, listFrom, newIndex);
 | 
			
		||||
      } else {
 | 
			
		||||
        listTo.updateIssueLabel(issue, listFrom);
 | 
			
		||||
        issueTo.removeLabel(listFrom.label);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (listTo.type === 'done') {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@
 | 
			
		|||
  var DOWN_BUILD_TRACE = '#down-build-trace';
 | 
			
		||||
 | 
			
		||||
  this.Build = (function() {
 | 
			
		||||
    Build.interval = null;
 | 
			
		||||
    Build.timeout = null;
 | 
			
		||||
 | 
			
		||||
    Build.state = null;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +31,7 @@
 | 
			
		|||
      this.$scrollBottomBtn = $('#scroll-bottom');
 | 
			
		||||
      this.$buildRefreshAnimation = $('.js-build-refresh');
 | 
			
		||||
 | 
			
		||||
      clearInterval(Build.interval);
 | 
			
		||||
      clearTimeout(Build.timeout);
 | 
			
		||||
      // Init breakpoint checker
 | 
			
		||||
      this.bp = Breakpoints.get();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,17 +52,7 @@
 | 
			
		|||
        this.getInitialBuildTrace();
 | 
			
		||||
        this.initScrollButtonAffix();
 | 
			
		||||
      }
 | 
			
		||||
      if (this.buildStatus === "running" || this.buildStatus === "pending") {
 | 
			
		||||
        Build.interval = setInterval((function(_this) {
 | 
			
		||||
          // Check for new build output if user still watching build page
 | 
			
		||||
          // Only valid for runnig build when output changes during time
 | 
			
		||||
          return function() {
 | 
			
		||||
            if (_this.location() === _this.pageUrl) {
 | 
			
		||||
              return _this.getBuildTrace();
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
        })(this), 4000);
 | 
			
		||||
      }
 | 
			
		||||
      this.invokeBuildTrace();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Build.prototype.initSidebar = function() {
 | 
			
		||||
| 
						 | 
				
			
			@ -75,6 +65,22 @@
 | 
			
		|||
      return window.location.href.split("#")[0];
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Build.prototype.invokeBuildTrace = function() {
 | 
			
		||||
      var continueRefreshStatuses = ['running', 'pending'];
 | 
			
		||||
      // Continue to update build trace when build is running or pending
 | 
			
		||||
      if (continueRefreshStatuses.indexOf(this.buildStatus) !== -1) {
 | 
			
		||||
        // Check for new build output if user still watching build page
 | 
			
		||||
        // Only valid for runnig build when output changes during time
 | 
			
		||||
        Build.timeout = setTimeout((function(_this) {
 | 
			
		||||
          return function() {
 | 
			
		||||
            if (_this.location() === _this.pageUrl) {
 | 
			
		||||
              return _this.getBuildTrace();
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
        })(this), 4000);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Build.prototype.getInitialBuildTrace = function() {
 | 
			
		||||
      var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped'];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +92,7 @@
 | 
			
		|||
          if (window.location.hash === DOWN_BUILD_TRACE) {
 | 
			
		||||
            $("html,body").scrollTop(this.$buildTrace.height());
 | 
			
		||||
          }
 | 
			
		||||
          if (removeRefreshStatuses.indexOf(buildData.status) >= 0) {
 | 
			
		||||
          if (removeRefreshStatuses.indexOf(buildData.status) !== -1) {
 | 
			
		||||
            this.$buildRefreshAnimation.remove();
 | 
			
		||||
            return this.initScrollMonitor();
 | 
			
		||||
          }
 | 
			
		||||
| 
						 | 
				
			
			@ -105,6 +111,7 @@
 | 
			
		|||
            if (log.state) {
 | 
			
		||||
              _this.state = log.state;
 | 
			
		||||
            }
 | 
			
		||||
            _this.invokeBuildTrace();
 | 
			
		||||
            if (log.status === "running") {
 | 
			
		||||
              if (log.append) {
 | 
			
		||||
                $('.js-build-output').append(log.html);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,6 +52,30 @@
 | 
			
		|||
      return this.views[viewMode].call(this);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ImageFile.prototype.initDraggable = function($el, padding, callback) {
 | 
			
		||||
      var dragging = false;
 | 
			
		||||
      var $body = $('body');
 | 
			
		||||
      var $offsetEl = $el.parent();
 | 
			
		||||
 | 
			
		||||
      $el.off('mousedown').on('mousedown', function() {
 | 
			
		||||
        dragging = true;
 | 
			
		||||
        $body.css('user-select', 'none');
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      $body.off('mouseup').off('mousemove').on('mouseup', function() {
 | 
			
		||||
        dragging = false;
 | 
			
		||||
        $body.css('user-select', '');
 | 
			
		||||
      })
 | 
			
		||||
      .on('mousemove', function(e) {
 | 
			
		||||
        var left;
 | 
			
		||||
        if (!dragging) return;
 | 
			
		||||
 | 
			
		||||
        left = e.pageX - ($offsetEl.offset().left + padding);
 | 
			
		||||
 | 
			
		||||
        callback(e, left);
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    prepareFrames = function(view) {
 | 
			
		||||
      var maxHeight, maxWidth;
 | 
			
		||||
      maxWidth = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -96,26 +120,30 @@
 | 
			
		|||
        maxHeight = 0;
 | 
			
		||||
        return $('.swipe.view', this.file).each((function(_this) {
 | 
			
		||||
          return function(index, view) {
 | 
			
		||||
            var ref;
 | 
			
		||||
            var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
 | 
			
		||||
            ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
 | 
			
		||||
            $('.swipe-frame', view).css({
 | 
			
		||||
            $swipeFrame = $('.swipe-frame', view);
 | 
			
		||||
            $swipeWrap = $('.swipe-wrap', view);
 | 
			
		||||
            $swipeBar = $('.swipe-bar', view);
 | 
			
		||||
 | 
			
		||||
            $swipeFrame.css({
 | 
			
		||||
              width: maxWidth + 16,
 | 
			
		||||
              height: maxHeight + 28
 | 
			
		||||
            });
 | 
			
		||||
            $('.swipe-wrap', view).css({
 | 
			
		||||
            $swipeWrap.css({
 | 
			
		||||
              width: maxWidth + 1,
 | 
			
		||||
              height: maxHeight + 2
 | 
			
		||||
            });
 | 
			
		||||
            return $('.swipe-bar', view).css({
 | 
			
		||||
            $swipeBar.css({
 | 
			
		||||
              left: 0
 | 
			
		||||
            }).draggable({
 | 
			
		||||
              axis: 'x',
 | 
			
		||||
              containment: 'parent',
 | 
			
		||||
              drag: function(event) {
 | 
			
		||||
                return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left);
 | 
			
		||||
              },
 | 
			
		||||
              stop: function(event) {
 | 
			
		||||
                return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
 | 
			
		||||
 | 
			
		||||
            _this.initDraggable($swipeBar, wrapPadding, function(e, left) {
 | 
			
		||||
              if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
 | 
			
		||||
                $swipeWrap.width((maxWidth + 1) - left);
 | 
			
		||||
                $swipeBar.css('left', left);
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
          };
 | 
			
		||||
| 
						 | 
				
			
			@ -128,9 +156,14 @@
 | 
			
		|||
        dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
 | 
			
		||||
        return $('.onion-skin.view', this.file).each((function(_this) {
 | 
			
		||||
          return function(index, view) {
 | 
			
		||||
            var ref;
 | 
			
		||||
            var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
 | 
			
		||||
            ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
 | 
			
		||||
            $('.onion-skin-frame', view).css({
 | 
			
		||||
            $frame = $('.onion-skin-frame', view);
 | 
			
		||||
            $frameAdded = $('.frame.added', view);
 | 
			
		||||
            $track = $('.drag-track', view);
 | 
			
		||||
            $dragger = $('.dragger', $track);
 | 
			
		||||
 | 
			
		||||
            $frame.css({
 | 
			
		||||
              width: maxWidth + 16,
 | 
			
		||||
              height: maxHeight + 28
 | 
			
		||||
            });
 | 
			
		||||
| 
						 | 
				
			
			@ -138,16 +171,18 @@
 | 
			
		|||
              width: maxWidth + 1,
 | 
			
		||||
              height: maxHeight + 2
 | 
			
		||||
            });
 | 
			
		||||
            return $('.dragger', view).css({
 | 
			
		||||
            $dragger.css({
 | 
			
		||||
              left: dragTrackWidth
 | 
			
		||||
            }).draggable({
 | 
			
		||||
              axis: 'x',
 | 
			
		||||
              containment: 'parent',
 | 
			
		||||
              drag: function(event) {
 | 
			
		||||
                return $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth);
 | 
			
		||||
              },
 | 
			
		||||
              stop: function(event) {
 | 
			
		||||
                return $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
 | 
			
		||||
 | 
			
		||||
            _this.initDraggable($dragger, framePadding, function(e, left) {
 | 
			
		||||
              var opacity = left / dragTrackWidth;
 | 
			
		||||
 | 
			
		||||
              if (opacity >= 0 && opacity <= 1) {
 | 
			
		||||
                $dragger.css('left', left);
 | 
			
		||||
                $frameAdded.css('opacity', opacity);
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
          };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,7 +69,9 @@ const PipelineStore = require('./pipelines_store');
 | 
			
		|||
      return pipelinesService.all()
 | 
			
		||||
        .then(response => response.json())
 | 
			
		||||
        .then((json) => {
 | 
			
		||||
          this.store.storePipelines(json);
 | 
			
		||||
          // depending of the endpoint the response can either bring a `pipelines` key or not.
 | 
			
		||||
          const pipelines = json.pipelines || json;
 | 
			
		||||
          this.store.storePipelines(pipelines);
 | 
			
		||||
          this.isLoading = false;
 | 
			
		||||
        })
 | 
			
		||||
        .catch(() => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,8 +75,11 @@ const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
 | 
			
		|||
        const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item);
 | 
			
		||||
 | 
			
		||||
        eventItem.totalTime = eventItem.total_time;
 | 
			
		||||
 | 
			
		||||
        if (eventItem.author) {
 | 
			
		||||
          eventItem.author.webUrl = eventItem.author.web_url;
 | 
			
		||||
          eventItem.author.avatarUrl = eventItem.author.avatar_url;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (eventItem.created_at) eventItem.createdAt = eventItem.created_at;
 | 
			
		||||
        if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,7 @@
 | 
			
		|||
/* global Shortcuts */
 | 
			
		||||
 | 
			
		||||
const ShortcutsBlob = require('./shortcuts_blob');
 | 
			
		||||
const UserCallout = require('./user_callout');
 | 
			
		||||
 | 
			
		||||
(function() {
 | 
			
		||||
  var Dispatcher;
 | 
			
		||||
| 
						 | 
				
			
			@ -280,6 +281,9 @@ const ShortcutsBlob = require('./shortcuts_blob');
 | 
			
		|||
        case 'ci:lints:show':
 | 
			
		||||
          new gl.CILintEditor();
 | 
			
		||||
          break;
 | 
			
		||||
        case 'users:show':
 | 
			
		||||
          new UserCallout();
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
      switch (path.first()) {
 | 
			
		||||
        case 'sessions':
 | 
			
		||||
| 
						 | 
				
			
			@ -316,6 +320,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
 | 
			
		|||
        case 'dashboard':
 | 
			
		||||
        case 'root':
 | 
			
		||||
          shortcut_handler = new ShortcutsDashboardNavigation();
 | 
			
		||||
          new UserCallout();
 | 
			
		||||
          break;
 | 
			
		||||
        case 'profiles':
 | 
			
		||||
          new NotificationsForm();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -145,7 +145,7 @@ module.exports = Vue.component('environment-component', {
 | 
			
		|||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="environments-container">
 | 
			
		||||
      <div class="content-list environments-container">
 | 
			
		||||
        <div class="environments-list-loading text-center" v-if="isLoading">
 | 
			
		||||
          <i class="fa fa-spinner fa-spin"></i>
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +181,7 @@ module.exports = Vue.component('environment-component', {
 | 
			
		|||
            :terminal-icon-svg="terminalIconSvg"
 | 
			
		||||
            :commit-icon-svg="commitIconSvg">
 | 
			
		||||
          </environment-table>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
 | 
			
		||||
          :change="changePage"
 | 
			
		||||
| 
						 | 
				
			
			@ -188,6 +189,5 @@ module.exports = Vue.component('environment-component', {
 | 
			
		|||
        </table-pagination>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  `,
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -503,9 +503,8 @@ module.exports = Vue.component('environment-item', {
 | 
			
		|||
        </span>
 | 
			
		||||
      </td>
 | 
			
		||||
 | 
			
		||||
      <td class="hidden-xs">
 | 
			
		||||
        <div v-if="!model.isFolder">
 | 
			
		||||
          <div class="btn-group" role="group">
 | 
			
		||||
      <td class="environments-actions">
 | 
			
		||||
        <div v-if="!model.isFolder" class="btn-group pull-right" role="group">
 | 
			
		||||
          <actions-component v-if="hasManualActions && canCreateDeployment"
 | 
			
		||||
            :play-icon-svg="playIconSvg"
 | 
			
		||||
            :actions="manualActions">
 | 
			
		||||
| 
						 | 
				
			
			@ -529,7 +528,6 @@ module.exports = Vue.component('environment-item', {
 | 
			
		|||
            :retry-url="retryUrl">
 | 
			
		||||
            </rollback-component>
 | 
			
		||||
        </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </td>
 | 
			
		||||
    </tr>
 | 
			
		||||
  `,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ module.exports = Vue.component('environment-table-component', {
 | 
			
		|||
  },
 | 
			
		||||
 | 
			
		||||
  template: `
 | 
			
		||||
    <table class="table ci-table environments">
 | 
			
		||||
    <table class="table ci-table">
 | 
			
		||||
      <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
          <th class="environments-name">Environment</th>
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +54,7 @@ module.exports = Vue.component('environment-table-component', {
 | 
			
		|||
          <th class="environments-build">Job</th>
 | 
			
		||||
          <th class="environments-commit">Commit</th>
 | 
			
		||||
          <th class="environments-date">Updated</th>
 | 
			
		||||
          <th class="hidden-xs environments-actions"></th>
 | 
			
		||||
          <th class="environments-actions"></th>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </thead>
 | 
			
		||||
      <tbody>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,16 @@
 | 
			
		|||
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, max-len, one-var, one-var-declaration-per-line, quotes, prefer-template, newline-per-chained-call, comma-dangle, new-cap, no-else-return, consistent-return */
 | 
			
		||||
/* global FilesCommentButton */
 | 
			
		||||
/* global notes */
 | 
			
		||||
 | 
			
		||||
(function() {
 | 
			
		||||
  let $commentButtonTemplate;
 | 
			
		||||
  var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
 | 
			
		||||
 | 
			
		||||
  this.FilesCommentButton = (function() {
 | 
			
		||||
    var COMMENT_BUTTON_CLASS, COMMENT_BUTTON_TEMPLATE, DEBOUNCE_TIMEOUT_DURATION, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS;
 | 
			
		||||
    var COMMENT_BUTTON_CLASS, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS;
 | 
			
		||||
 | 
			
		||||
    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';
 | 
			
		||||
| 
						 | 
				
			
			@ -27,26 +27,29 @@
 | 
			
		|||
 | 
			
		||||
    TEXT_FILE_SELECTOR = '.text-file';
 | 
			
		||||
 | 
			
		||||
    DEBOUNCE_TIMEOUT_DURATION = 100;
 | 
			
		||||
 | 
			
		||||
    function FilesCommentButton(filesContainerElement) {
 | 
			
		||||
      var debounce;
 | 
			
		||||
      this.filesContainerElement = filesContainerElement;
 | 
			
		||||
      this.destroy = bind(this.destroy, this);
 | 
			
		||||
      this.render = bind(this.render, this);
 | 
			
		||||
      this.VIEW_TYPE = $('input#view[type=hidden]').val();
 | 
			
		||||
      debounce = _.debounce(this.render, DEBOUNCE_TIMEOUT_DURATION);
 | 
			
		||||
      $(this.filesContainerElement).off('mouseover', LINE_COLUMN_CLASSES).off('mouseleave', LINE_COLUMN_CLASSES).on('mouseover', LINE_COLUMN_CLASSES, debounce).on('mouseleave', LINE_COLUMN_CLASSES, this.destroy);
 | 
			
		||||
      this.hideButton = bind(this.hideButton, this);
 | 
			
		||||
      this.isParallelView = notes.isParallelView();
 | 
			
		||||
      filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render)
 | 
			
		||||
        .on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FilesCommentButton.prototype.render = function(e) {
 | 
			
		||||
      var $currentTarget, buttonParentElement, lineContentElement, textFileElement;
 | 
			
		||||
      var $currentTarget, buttonParentElement, lineContentElement, textFileElement, $button;
 | 
			
		||||
      $currentTarget = $(e.currentTarget);
 | 
			
		||||
 | 
			
		||||
      buttonParentElement = this.getButtonParent($currentTarget);
 | 
			
		||||
      if (!this.validateButtonParent(buttonParentElement)) return;
 | 
			
		||||
      lineContentElement = this.getLineContent($currentTarget);
 | 
			
		||||
      if (!this.validateLineContent(lineContentElement)) return;
 | 
			
		||||
      buttonParentElement = this.getButtonParent($currentTarget);
 | 
			
		||||
 | 
			
		||||
      if (!this.validateButtonParent(buttonParentElement) || !this.validateLineContent(lineContentElement)) return;
 | 
			
		||||
 | 
			
		||||
      $button = $(COMMENT_BUTTON_CLASS, buttonParentElement);
 | 
			
		||||
      buttonParentElement.addClass('is-over')
 | 
			
		||||
        .nextUntil(`.${LINE_CONTENT_CLASS}`).addClass('is-over');
 | 
			
		||||
 | 
			
		||||
      if ($button.length) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      textFileElement = this.getTextFileElement($currentTarget);
 | 
			
		||||
      buttonParentElement.append(this.buildButton({
 | 
			
		||||
| 
						 | 
				
			
			@ -61,19 +64,16 @@
 | 
			
		|||
      }));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    FilesCommentButton.prototype.destroy = function(e) {
 | 
			
		||||
      if (this.isMovingToSameType(e)) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      $(COMMENT_BUTTON_CLASS, this.getButtonParent($(e.currentTarget))).remove();
 | 
			
		||||
    FilesCommentButton.prototype.hideButton = function(e) {
 | 
			
		||||
      var $currentTarget = $(e.currentTarget);
 | 
			
		||||
      var buttonParentElement = this.getButtonParent($currentTarget);
 | 
			
		||||
 | 
			
		||||
      buttonParentElement.removeClass('is-over')
 | 
			
		||||
        .nextUntil(`.${LINE_CONTENT_CLASS}`).removeClass('is-over');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    FilesCommentButton.prototype.buildButton = function(buttonAttributes) {
 | 
			
		||||
      var initializedButtonTemplate;
 | 
			
		||||
      initializedButtonTemplate = COMMENT_BUTTON_TEMPLATE({
 | 
			
		||||
        COMMENT_BUTTON_CLASS: COMMENT_BUTTON_CLASS.substr(1)
 | 
			
		||||
      });
 | 
			
		||||
      return $(initializedButtonTemplate).attr({
 | 
			
		||||
      return $commentButtonTemplate.clone().attr({
 | 
			
		||||
        'data-noteable-type': buttonAttributes.noteableType,
 | 
			
		||||
        'data-noteable-id': buttonAttributes.noteableID,
 | 
			
		||||
        'data-commit-id': buttonAttributes.commitID,
 | 
			
		||||
| 
						 | 
				
			
			@ -86,14 +86,14 @@
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    FilesCommentButton.prototype.getTextFileElement = function(hoveredElement) {
 | 
			
		||||
      return $(hoveredElement.closest(TEXT_FILE_SELECTOR));
 | 
			
		||||
      return hoveredElement.closest(TEXT_FILE_SELECTOR);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    FilesCommentButton.prototype.getLineContent = function(hoveredElement) {
 | 
			
		||||
      if (hoveredElement.hasClass(LINE_CONTENT_CLASS)) {
 | 
			
		||||
        return hoveredElement;
 | 
			
		||||
      }
 | 
			
		||||
      if (this.VIEW_TYPE === 'inline') {
 | 
			
		||||
      if (!this.isParallelView) {
 | 
			
		||||
        return $(hoveredElement).closest(LINE_HOLDER_CLASS).find("." + LINE_CONTENT_CLASS);
 | 
			
		||||
      } else {
 | 
			
		||||
        return $(hoveredElement).next("." + LINE_CONTENT_CLASS);
 | 
			
		||||
| 
						 | 
				
			
			@ -101,7 +101,7 @@
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    FilesCommentButton.prototype.getButtonParent = function(hoveredElement) {
 | 
			
		||||
      if (this.VIEW_TYPE === 'inline') {
 | 
			
		||||
      if (!this.isParallelView) {
 | 
			
		||||
        if (hoveredElement.hasClass(OLD_LINE_CLASS)) {
 | 
			
		||||
          return hoveredElement;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -114,17 +114,8 @@
 | 
			
		|||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    FilesCommentButton.prototype.isMovingToSameType = function(e) {
 | 
			
		||||
      var newButtonParent;
 | 
			
		||||
      newButtonParent = this.getButtonParent($(e.toElement));
 | 
			
		||||
      if (!newButtonParent) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      return newButtonParent.is(this.getButtonParent($(e.currentTarget)));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    FilesCommentButton.prototype.validateButtonParent = function(buttonParentElement) {
 | 
			
		||||
      return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS) && $(COMMENT_BUTTON_CLASS, buttonParentElement).length === 0;
 | 
			
		||||
      return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    FilesCommentButton.prototype.validateLineContent = function(lineContentElement) {
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +126,8 @@
 | 
			
		|||
  })();
 | 
			
		||||
 | 
			
		||||
  $.fn.filesCommentButton = function() {
 | 
			
		||||
    $commentButtonTemplate = $('<button name="button" type="submit" class="add-diff-note js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>');
 | 
			
		||||
 | 
			
		||||
    if (!(this && (this.parent().data('can-create-note') != null))) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,11 @@
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    setOffset(offset = 0) {
 | 
			
		||||
      if (window.innerWidth > 480) {
 | 
			
		||||
        this.dropdown.style.left = `${offset}px`;
 | 
			
		||||
      } else {
 | 
			
		||||
        this.dropdown.style.left = '0px';
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    renderContent(forceShowList = false) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,7 +63,7 @@
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) {
 | 
			
		||||
      return BLUR_KEYCODES.indexOf(keyCode) >= 0;
 | 
			
		||||
      return BLUR_KEYCODES.indexOf(keyCode) !== -1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    GitLabDropdownFilter.prototype.filter = function(search_text) {
 | 
			
		||||
| 
						 | 
				
			
			@ -605,7 +605,7 @@
 | 
			
		|||
      var occurrences;
 | 
			
		||||
      occurrences = fuzzaldrinPlus.match(text, term);
 | 
			
		||||
      return text.split('').map(function(character, i) {
 | 
			
		||||
        if (indexOf.call(occurrences, i) >= 0) {
 | 
			
		||||
        if (indexOf.call(occurrences, i) !== -1) {
 | 
			
		||||
          return "<b>" + character + "</b>";
 | 
			
		||||
        } else {
 | 
			
		||||
          return character;
 | 
			
		||||
| 
						 | 
				
			
			@ -748,7 +748,7 @@
 | 
			
		|||
        return function(e) {
 | 
			
		||||
          var $listItems, PREV_INDEX, currentKeyCode;
 | 
			
		||||
          currentKeyCode = e.which;
 | 
			
		||||
          if (ARROW_KEY_CODES.indexOf(currentKeyCode) >= 0) {
 | 
			
		||||
          if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            e.stopImmediatePropagation();
 | 
			
		||||
            PREV_INDEX = currentIndex;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
require('./stat_graph_contributors_graph');
 | 
			
		||||
require('./stat_graph_contributors_util');
 | 
			
		||||
require('./stat_graph_contributors');
 | 
			
		||||
require('./stat_graph');
 | 
			
		||||
import ContributorsStatGraph from './stat_graph_contributors';
 | 
			
		||||
 | 
			
		||||
// export to global scope
 | 
			
		||||
window.ContributorsStatGraph = ContributorsStatGraph;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +0,0 @@
 | 
			
		|||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-return-assign, max-len */
 | 
			
		||||
(function() {
 | 
			
		||||
  this.StatGraph = (function() {
 | 
			
		||||
    function StatGraph() {}
 | 
			
		||||
 | 
			
		||||
    StatGraph.log = {};
 | 
			
		||||
 | 
			
		||||
    StatGraph.get_log = function() {
 | 
			
		||||
      return this.log;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    StatGraph.set_log = function(data) {
 | 
			
		||||
      return this.log = data;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return StatGraph;
 | 
			
		||||
  })();
 | 
			
		||||
}).call(window);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,14 +1,10 @@
 | 
			
		|||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign */
 | 
			
		||||
/* global ContributorsGraph */
 | 
			
		||||
/* global ContributorsAuthorGraph */
 | 
			
		||||
/* global ContributorsMasterGraph */
 | 
			
		||||
/* global ContributorsStatGraphUtil */
 | 
			
		||||
/* global d3 */
 | 
			
		||||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */
 | 
			
		||||
 | 
			
		||||
window.d3 = require('d3');
 | 
			
		||||
import d3 from 'd3';
 | 
			
		||||
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
 | 
			
		||||
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
 | 
			
		||||
 | 
			
		||||
(function() {
 | 
			
		||||
  this.ContributorsStatGraph = (function() {
 | 
			
		||||
export default (function() {
 | 
			
		||||
  function ContributorsStatGraph() {}
 | 
			
		||||
 | 
			
		||||
  ContributorsStatGraph.prototype.init = function(log) {
 | 
			
		||||
| 
						 | 
				
			
			@ -113,4 +109,3 @@ window.d3 = require('d3');
 | 
			
		|||
 | 
			
		||||
  return ContributorsStatGraph;
 | 
			
		||||
})();
 | 
			
		||||
}).call(window);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,12 @@
 | 
			
		|||
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return */
 | 
			
		||||
/* global d3 */
 | 
			
		||||
/* global ContributorsGraph */
 | 
			
		||||
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */
 | 
			
		||||
 | 
			
		||||
window.d3 = require('d3');
 | 
			
		||||
import d3 from 'd3';
 | 
			
		||||
 | 
			
		||||
(function() {
 | 
			
		||||
  var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
 | 
			
		||||
    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
 | 
			
		||||
    hasProp = {}.hasOwnProperty;
 | 
			
		||||
const bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
 | 
			
		||||
const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
 | 
			
		||||
const hasProp = {}.hasOwnProperty;
 | 
			
		||||
 | 
			
		||||
  this.ContributorsGraph = (function() {
 | 
			
		||||
export const ContributorsGraph = (function() {
 | 
			
		||||
  function ContributorsGraph() {}
 | 
			
		||||
 | 
			
		||||
  ContributorsGraph.prototype.MARGIN = {
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +90,7 @@ window.d3 = require('d3');
 | 
			
		|||
  return ContributorsGraph;
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
  this.ContributorsMasterGraph = (function(superClass) {
 | 
			
		||||
export const ContributorsMasterGraph = (function(superClass) {
 | 
			
		||||
  extend(ContributorsMasterGraph, superClass);
 | 
			
		||||
 | 
			
		||||
  function ContributorsMasterGraph(data1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +194,7 @@ window.d3 = require('d3');
 | 
			
		|||
  return ContributorsMasterGraph;
 | 
			
		||||
})(ContributorsGraph);
 | 
			
		||||
 | 
			
		||||
  this.ContributorsAuthorGraph = (function(superClass) {
 | 
			
		||||
export const ContributorsAuthorGraph = (function(superClass) {
 | 
			
		||||
  extend(ContributorsAuthorGraph, superClass);
 | 
			
		||||
 | 
			
		||||
  function ContributorsAuthorGraph(data1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -273,4 +270,3 @@ window.d3 = require('d3');
 | 
			
		|||
 | 
			
		||||
  return ContributorsAuthorGraph;
 | 
			
		||||
})(ContributorsGraph);
 | 
			
		||||
}).call(window);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
/* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, max-len */
 | 
			
		||||
(function() {
 | 
			
		||||
  window.ContributorsStatGraphUtil = {
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  parse_log: function(log) {
 | 
			
		||||
    var by_author, by_email, data, entry, i, len, total, normalized_email;
 | 
			
		||||
    total = {};
 | 
			
		||||
| 
						 | 
				
			
			@ -135,4 +135,3 @@
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
}).call(window);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -116,7 +116,7 @@
 | 
			
		|||
        formData = $.param(formData);
 | 
			
		||||
        formAction = form.attr('action');
 | 
			
		||||
        issuesUrl = formAction;
 | 
			
		||||
        issuesUrl += "" + (formAction.indexOf('?') < 0 ? '?' : '&');
 | 
			
		||||
        issuesUrl += "" + (formAction.indexOf('?') === -1 ? '?' : '&');
 | 
			
		||||
        issuesUrl += formData;
 | 
			
		||||
        return gl.utils.visitUrl(issuesUrl);
 | 
			
		||||
      };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,12 +79,12 @@ require('./comparison_pane');
 | 
			
		|||
          <div class='help-button pull-right'
 | 
			
		||||
            v-if='!showHelpState'
 | 
			
		||||
            @click='toggleHelpState(true)'>
 | 
			
		||||
            <i class='fa fa-question-circle'></i>
 | 
			
		||||
            <i class='fa fa-question-circle' aria-hidden='true'></i>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class='close-help-button pull-right'
 | 
			
		||||
            v-if='showHelpState'
 | 
			
		||||
            @click='toggleHelpState(false)'>
 | 
			
		||||
            <i class='fa fa-close'></i>
 | 
			
		||||
            <i class='fa fa-close' aria-hidden='true'></i>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class='time-tracking-content hide-collapsed'>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,8 +39,9 @@ require('../../subbable_resource');
 | 
			
		|||
          listenForSlashCommands() {
 | 
			
		||||
            $(document).on('ajax:success', '.gfm-form', (e, data) => {
 | 
			
		||||
              const subscribedCommands = ['spend_time', 'time_estimate'];
 | 
			
		||||
              const changedCommands = data.commands_changes;
 | 
			
		||||
 | 
			
		||||
              const changedCommands = data.commands_changes
 | 
			
		||||
                ? Object.keys(data.commands_changes)
 | 
			
		||||
                : [];
 | 
			
		||||
              if (changedCommands && _.intersection(subscribedCommands, changedCommands).length) {
 | 
			
		||||
                this.fetchIssuable();
 | 
			
		||||
              }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -296,5 +296,58 @@
 | 
			
		|||
     * @returns {Boolean}
 | 
			
		||||
     */
 | 
			
		||||
    w.gl.utils.convertPermissionToBoolean = permission => permission === 'true';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Back Off exponential algorithm
 | 
			
		||||
     * backOff :: (Function<next, stop>, Number) -> Promise<Any, Error>
 | 
			
		||||
     *
 | 
			
		||||
     * @param {Function<next, stop>} fn function to be called
 | 
			
		||||
     * @param {Number} timeout
 | 
			
		||||
     * @return {Promise<Any, Error>}
 | 
			
		||||
     * @example
 | 
			
		||||
     * ```
 | 
			
		||||
     *  backOff(function (next, stop) {
 | 
			
		||||
     *    // Let's perform this function repeatedly for 60s or for the timeout provided.
 | 
			
		||||
     *
 | 
			
		||||
     *    ourFunction()
 | 
			
		||||
     *      .then(function (result) {
 | 
			
		||||
     *        // continue if result is not what we need
 | 
			
		||||
     *        next();
 | 
			
		||||
     *
 | 
			
		||||
     *        // when result is what we need let's stop with the repetions and jump out of the cycle
 | 
			
		||||
     *        stop(result);
 | 
			
		||||
     *      })
 | 
			
		||||
     *      .catch(function (error) {
 | 
			
		||||
     *        // if there is an error, we need to stop this with an error.
 | 
			
		||||
     *        stop(error);
 | 
			
		||||
     *      })
 | 
			
		||||
     *  }, 60000)
 | 
			
		||||
     *  .then(function (result) {})
 | 
			
		||||
     *  .catch(function (error) {
 | 
			
		||||
     *    // deal with errors passed to stop()
 | 
			
		||||
     *  })
 | 
			
		||||
     * ```
 | 
			
		||||
     */
 | 
			
		||||
    w.gl.utils.backOff = (fn, timeout = 60000) => {
 | 
			
		||||
      const maxInterval = 32000;
 | 
			
		||||
      let nextInterval = 2000;
 | 
			
		||||
 | 
			
		||||
      const startTime = Date.now();
 | 
			
		||||
 | 
			
		||||
      return new Promise((resolve, reject) => {
 | 
			
		||||
        const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg));
 | 
			
		||||
 | 
			
		||||
        const next = () => {
 | 
			
		||||
          if (Date.now() - startTime < timeout) {
 | 
			
		||||
            setTimeout(fn.bind(null, next, stop), nextInterval);
 | 
			
		||||
            nextInterval = Math.min(nextInterval + nextInterval, maxInterval);
 | 
			
		||||
          } else {
 | 
			
		||||
            reject(new Error('BACKOFF_TIMEOUT'));
 | 
			
		||||
          }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        fn(next, stop);
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
  })(window);
 | 
			
		||||
}).call(window);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
/**
 | 
			
		||||
 * exports HTTP status codes
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const statusCodes = {
 | 
			
		||||
  NO_CONTENT: 204,
 | 
			
		||||
  OK: 200,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module.exports = statusCodes;
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ require('./smart_interval');
 | 
			
		|||
        return function() {
 | 
			
		||||
          var page;
 | 
			
		||||
          page = $('body').data('page').split(':').last();
 | 
			
		||||
          if (allowedPages.indexOf(page) < 0) {
 | 
			
		||||
          if (allowedPages.indexOf(page) === -1) {
 | 
			
		||||
            return _this.clearEventListeners();
 | 
			
		||||
          }
 | 
			
		||||
        };
 | 
			
		||||
| 
						 | 
				
			
			@ -233,7 +233,7 @@ require('./smart_interval');
 | 
			
		|||
      }
 | 
			
		||||
      $('.ci_widget').hide();
 | 
			
		||||
      allowed_states = ["failed", "canceled", "running", "pending", "success", "success_with_warnings", "skipped", "not_found"];
 | 
			
		||||
      if (indexOf.call(allowed_states, state) >= 0) {
 | 
			
		||||
      if (indexOf.call(allowed_states, state) !== -1) {
 | 
			
		||||
        $('.ci_widget.ci-' + state).show();
 | 
			
		||||
        switch (state) {
 | 
			
		||||
          case "failed":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,9 +21,9 @@
 | 
			
		|||
    });
 | 
			
		||||
 | 
			
		||||
    $(document)
 | 
			
		||||
    .off('click', '.merge_when_build_succeeds')
 | 
			
		||||
    .on('click', '.merge_when_build_succeeds', () => {
 | 
			
		||||
      $('#merge_when_build_succeeds').val('1');
 | 
			
		||||
    .off('click', '.merge_when_pipeline_succeeds')
 | 
			
		||||
    .on('click', '.merge_when_pipeline_succeeds', () => {
 | 
			
		||||
      $('#merge_when_pipeline_succeeds').val('1');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $(document)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,7 +78,6 @@
 | 
			
		|||
      } else {
 | 
			
		||||
        $(element).find('.assignee-icon').empty();
 | 
			
		||||
      }
 | 
			
		||||
      return $(element).effect('highlight');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function Milestone() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@
 | 
			
		|||
        $value = $block.find('.value');
 | 
			
		||||
        $loading = $block.find('.block-loading').fadeOut();
 | 
			
		||||
        if (issueUpdateURL) {
 | 
			
		||||
          milestoneLinkTemplate = _.template('<a href="/<%- namespace %>/<%- path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>');
 | 
			
		||||
          milestoneLinkTemplate = _.template('<a href="/<%- full_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"> <%- title %> </span>');
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -181,8 +181,7 @@
 | 
			
		|||
                $selectbox.hide();
 | 
			
		||||
                $value.css('display', '');
 | 
			
		||||
                if (data.milestone != null) {
 | 
			
		||||
                  data.milestone.namespace = _this.currentProject.namespace;
 | 
			
		||||
                  data.milestone.path = _this.currentProject.path;
 | 
			
		||||
                  data.milestone.full_path = _this.currentProject.full_path;
 | 
			
		||||
                  data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date);
 | 
			
		||||
                  $value.html(milestoneLinkTemplate(data.milestone));
 | 
			
		||||
                  return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len */
 | 
			
		||||
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len, object-shorthand */
 | 
			
		||||
(function() {
 | 
			
		||||
  var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
 | 
			
		||||
    indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; };
 | 
			
		||||
| 
						 | 
				
			
			@ -20,15 +20,35 @@
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    NewBranchForm.prototype.init = function() {
 | 
			
		||||
      if (this.name.val().length > 0) {
 | 
			
		||||
      if (this.name.length && this.name.val().length > 0) {
 | 
			
		||||
        return this.name.trigger('blur');
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    NewBranchForm.prototype.setupAvailableRefs = function(availableRefs) {
 | 
			
		||||
      return this.ref.autocomplete({
 | 
			
		||||
        source: availableRefs,
 | 
			
		||||
        minLength: 1
 | 
			
		||||
      var $branchSelect = $('.js-branch-select');
 | 
			
		||||
 | 
			
		||||
      $branchSelect.glDropdown({
 | 
			
		||||
        data: availableRefs,
 | 
			
		||||
        filterable: true,
 | 
			
		||||
        filterByText: true,
 | 
			
		||||
        remote: false,
 | 
			
		||||
        fieldName: $branchSelect.data('field-name'),
 | 
			
		||||
        selectable: true,
 | 
			
		||||
        isSelectable: function(branch, $el) {
 | 
			
		||||
          return !$el.hasClass('is-active');
 | 
			
		||||
        },
 | 
			
		||||
        text: function(branch) {
 | 
			
		||||
          return branch;
 | 
			
		||||
        },
 | 
			
		||||
        id: function(branch) {
 | 
			
		||||
          return branch;
 | 
			
		||||
        },
 | 
			
		||||
        toggleLabel: function(branch) {
 | 
			
		||||
          if (branch) {
 | 
			
		||||
            return branch;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +81,7 @@
 | 
			
		|||
      var errorMessage, errors, formatter, unique, validator;
 | 
			
		||||
      this.branchNameError.empty();
 | 
			
		||||
      unique = function(values, value) {
 | 
			
		||||
        if (indexOf.call(values, value) < 0) {
 | 
			
		||||
        if (indexOf.call(values, value) === -1) {
 | 
			
		||||
          values.push(value);
 | 
			
		||||
        }
 | 
			
		||||
        return values;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -246,13 +246,22 @@ require('./task_list');
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    Notes.prototype.handleCreateChanges = function(note) {
 | 
			
		||||
      var votesBlock;
 | 
			
		||||
      if (typeof note === 'undefined') {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (note.commands_changes && note.commands_changes.indexOf('merge') !== -1) {
 | 
			
		||||
      if (note.commands_changes) {
 | 
			
		||||
        if ('merge' in note.commands_changes) {
 | 
			
		||||
          $.get(mrRefreshWidgetUrl);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ('emoji_award' in note.commands_changes) {
 | 
			
		||||
          votesBlock = $('.js-awards-block').eq(0);
 | 
			
		||||
          gl.awardsHandler.addAwardToEmojiBar(votesBlock, note.commands_changes.emoji_award);
 | 
			
		||||
          return gl.awardsHandler.scrollToAwards();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
| 
						 | 
				
			
			@ -262,26 +271,16 @@ require('./task_list');
 | 
			
		|||
     */
 | 
			
		||||
 | 
			
		||||
    Notes.prototype.renderNote = function(note) {
 | 
			
		||||
      var $notesList, votesBlock;
 | 
			
		||||
      var $notesList;
 | 
			
		||||
      if (!note.valid) {
 | 
			
		||||
        if (note.award) {
 | 
			
		||||
          new Flash('You have already awarded this emoji!', 'alert', this.parentTimeline);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
        if (note.errors.commands_only) {
 | 
			
		||||
          new Flash(note.errors.commands_only, 'notice', this.parentTimeline);
 | 
			
		||||
          this.refresh();
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (note.award) {
 | 
			
		||||
        votesBlock = $('.js-awards-block').eq(0);
 | 
			
		||||
        gl.awardsHandler.addAwardToEmojiBar(votesBlock, note.name);
 | 
			
		||||
        return gl.awardsHandler.scrollToAwards();
 | 
			
		||||
      // render note if it not present in loaded list
 | 
			
		||||
      // or skip if rendered
 | 
			
		||||
      } else if (this.isNewNote(note)) {
 | 
			
		||||
 | 
			
		||||
      if (this.isNewNote(note)) {
 | 
			
		||||
        this.note_ids.push(note.id);
 | 
			
		||||
        $notesList = $('ul.main-notes-list');
 | 
			
		||||
        $notesList.append(note.html).syntaxHighlight();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@
 | 
			
		|||
      this.onPickImageClick = this.onPickImageClick.bind(this);
 | 
			
		||||
      this.fileInput = $(input);
 | 
			
		||||
      this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
 | 
			
		||||
      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`);
 | 
			
		||||
      this.exportWidth = exportWidth;
 | 
			
		||||
      this.exportHeight = exportHeight;
 | 
			
		||||
      this.cropBoxWidth = cropBoxWidth;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,13 +84,14 @@
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  $(function() {
 | 
			
		||||
    $(document).on('focusout.ssh_key', '#key_key', function() {
 | 
			
		||||
    $(document).on('input.ssh_key', '#key_key', function() {
 | 
			
		||||
      const $title = $('#key_title');
 | 
			
		||||
      const comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/);
 | 
			
		||||
      if (comment && comment.length > 1 && $title.val() === '') {
 | 
			
		||||
 | 
			
		||||
      // Extract the SSH Key title from its comment
 | 
			
		||||
      if (comment && comment.length > 1) {
 | 
			
		||||
        return $title.val(comment[1]).change();
 | 
			
		||||
      }
 | 
			
		||||
    // Extract the SSH Key title from its comment
 | 
			
		||||
    });
 | 
			
		||||
    if (global.utils.getPagePath() === 'profiles') {
 | 
			
		||||
      return new Profile();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,2 @@
 | 
			
		|||
// require everything else in this directory
 | 
			
		||||
function requireAll(context) { return context.keys().map(context); }
 | 
			
		||||
requireAll(require.context('.', false, /^\.\/(?!profile_bundle).*\.(js|es6)$/));
 | 
			
		||||
require('./gl_crop');
 | 
			
		||||
require('./profile');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -116,7 +116,7 @@
 | 
			
		|||
            if ($('input[name="ref"]').length) {
 | 
			
		||||
              var $form = $dropdown.closest('form');
 | 
			
		||||
              var action = $form.attr('action');
 | 
			
		||||
              var divider = action.indexOf('?') < 0 ? '?' : '&';
 | 
			
		||||
              var divider = action.indexOf('?') === -1 ? '?' : '&';
 | 
			
		||||
              gl.utils.visitUrl(action + '' + divider + '' + $form.serialize());
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,9 @@
 | 
			
		|||
      // Do not update if one dropdown has not selected any option
 | 
			
		||||
      if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
 | 
			
		||||
 | 
			
		||||
      this.$allowedToMergeDropdown.disable();
 | 
			
		||||
      this.$allowedToPushDropdown.disable();
 | 
			
		||||
 | 
			
		||||
      $.ajax({
 | 
			
		||||
        type: 'POST',
 | 
			
		||||
        url: this.$wrap.data('url'),
 | 
			
		||||
| 
						 | 
				
			
			@ -53,13 +56,13 @@
 | 
			
		|||
            }]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        success: () => {
 | 
			
		||||
          this.$wrap.effect('highlight');
 | 
			
		||||
        },
 | 
			
		||||
        error() {
 | 
			
		||||
          $.scrollTo(0);
 | 
			
		||||
          new Flash('Failed to update branch!');
 | 
			
		||||
        }
 | 
			
		||||
      }).always(() => {
 | 
			
		||||
        this.$allowedToMergeDropdown.enable();
 | 
			
		||||
        this.$allowedToPushDropdown.enable();
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
// require everything else in this directory
 | 
			
		||||
function requireAll(context) { return context.keys().map(context); }
 | 
			
		||||
requireAll(require.context('.', false, /^\.\/(?!protected_branches_bundle).*\.(js|es6)$/));
 | 
			
		||||
require('./protected_branch_access_dropdown');
 | 
			
		||||
require('./protected_branch_create');
 | 
			
		||||
require('./protected_branch_dropdown');
 | 
			
		||||
require('./protected_branch_edit');
 | 
			
		||||
require('./protected_branch_edit_list');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,6 @@
 | 
			
		|||
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, max-len */
 | 
			
		||||
/* global ace */
 | 
			
		||||
 | 
			
		||||
// require everything else in this directory
 | 
			
		||||
function requireAll(context) { return context.keys().map(context); }
 | 
			
		||||
requireAll(require.context('.', false, /^\.\/(?!snippet_bundle).*\.(js|es6)$/));
 | 
			
		||||
 | 
			
		||||
(function() {
 | 
			
		||||
  $(function() {
 | 
			
		||||
    var editor = ace.edit("editor");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
/* global Flash */
 | 
			
		||||
require('vendor/task_list');
 | 
			
		||||
 | 
			
		||||
class TaskList {
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +7,16 @@ class TaskList {
 | 
			
		|||
    this.dataType = options.dataType;
 | 
			
		||||
    this.fieldName = options.fieldName;
 | 
			
		||||
    this.onSuccess = options.onSuccess || (() => {});
 | 
			
		||||
    this.onError = function showFlash(response) {
 | 
			
		||||
      let errorMessages = '';
 | 
			
		||||
 | 
			
		||||
      if (response.responseJSON) {
 | 
			
		||||
        errorMessages = response.responseJSON.errors.join(' ');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return new Flash(errorMessages || 'Update failed', 'alert');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.init();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +43,7 @@ class TaskList {
 | 
			
		|||
      url: $target.data('update-url') || $('form.js-issuable-update').attr('action'),
 | 
			
		||||
      data: patchData,
 | 
			
		||||
      success: this.onSuccess,
 | 
			
		||||
      error: this.onError,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
/* global Cookies */
 | 
			
		||||
 | 
			
		||||
const userCalloutElementName = '.user-callout';
 | 
			
		||||
const closeButton = '.close-user-callout';
 | 
			
		||||
const userCalloutBtn = '.user-callout-btn';
 | 
			
		||||
const userCalloutSvgAttrName = 'callout-svg';
 | 
			
		||||
 | 
			
		||||
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
 | 
			
		||||
 | 
			
		||||
const USER_CALLOUT_TEMPLATE = `
 | 
			
		||||
  <div class="bordered-box landing content-block">
 | 
			
		||||
    <button class="btn btn-default close close-user-callout" type="button">
 | 
			
		||||
      <i class="fa fa-times dismiss-icon"></i>
 | 
			
		||||
    </button>
 | 
			
		||||
    <div class="row">
 | 
			
		||||
      <div class="col-sm-3 col-xs-12 svg-container">
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col-sm-8 col-xs-12 inner-content">
 | 
			
		||||
        <h4>
 | 
			
		||||
          Customize your experience
 | 
			
		||||
        </h4>
 | 
			
		||||
        <p>
 | 
			
		||||
          Change syntax themes, default project pages, and more in preferences.
 | 
			
		||||
        </p>
 | 
			
		||||
        <a class="btn user-callout-btn" href="/profile/preferences">Check it out</a>
 | 
			
		||||
      </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>`;
 | 
			
		||||
 | 
			
		||||
class UserCallout {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.isCalloutDismissed = Cookies.get(USER_CALLOUT_COOKIE);
 | 
			
		||||
    this.userCalloutBody = $(userCalloutElementName);
 | 
			
		||||
    this.userCalloutSvg = $(userCalloutElementName).attr(userCalloutSvgAttrName);
 | 
			
		||||
    $(userCalloutElementName).removeAttr(userCalloutSvgAttrName);
 | 
			
		||||
    this.init();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init() {
 | 
			
		||||
    const $template = $(USER_CALLOUT_TEMPLATE);
 | 
			
		||||
    if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') {
 | 
			
		||||
      $template.find('.svg-container').append(this.userCalloutSvg);
 | 
			
		||||
      this.userCalloutBody.append($template);
 | 
			
		||||
      $template.find(closeButton).on('click', e => this.dismissCallout(e));
 | 
			
		||||
      $template.find(userCalloutBtn).on('click', e => this.dismissCallout(e));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dismissCallout(e) {
 | 
			
		||||
    Cookies.set(USER_CALLOUT_COOKIE, 'true');
 | 
			
		||||
    const $currentTarget = $(e.currentTarget);
 | 
			
		||||
    if ($currentTarget.hasClass('close-user-callout')) {
 | 
			
		||||
      this.userCalloutBody.empty();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = UserCallout;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1 @@
 | 
			
		|||
// require everything else in this directory
 | 
			
		||||
function requireAll(context) { return context.keys().map(context); }
 | 
			
		||||
requireAll(require.context('.', false, /^\.\/(?!users_bundle).*\.(js|es6)$/));
 | 
			
		||||
require('./calendar');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/* global Vue, Flash, gl */
 | 
			
		||||
/* eslint-disable no-param-reassign */
 | 
			
		||||
/* eslint-disable no-param-reassign, no-alert */
 | 
			
		||||
 | 
			
		||||
((gl) => {
 | 
			
		||||
  gl.VuePipelineActions = Vue.extend({
 | 
			
		||||
| 
						 | 
				
			
			@ -16,21 +16,33 @@
 | 
			
		|||
      download(name) {
 | 
			
		||||
        return `Download ${name} artifacts`;
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
       * Shows a dialog when the user clicks in the cancel button.
 | 
			
		||||
       * We need to prevent the default behavior and stop propagation because the
 | 
			
		||||
       * link relies on UJS.
 | 
			
		||||
       *
 | 
			
		||||
       * @param  {Event} event
 | 
			
		||||
       */
 | 
			
		||||
      confirmAction(event) {
 | 
			
		||||
        if (!confirm('Are you sure you want to cancel this pipeline?')) {
 | 
			
		||||
          event.preventDefault();
 | 
			
		||||
          event.stopPropagation();
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    template: `
 | 
			
		||||
      <td class="pipeline-actions hidden-xs">
 | 
			
		||||
        <div class="controls pull-right">
 | 
			
		||||
          <div class="btn-group inline">
 | 
			
		||||
      <td class="pipeline-actions">
 | 
			
		||||
        <div class="pull-right">
 | 
			
		||||
          <div class="btn-group">
 | 
			
		||||
            <div class="btn-group" v-if="actions">
 | 
			
		||||
              <button
 | 
			
		||||
                v-if='actions'
 | 
			
		||||
                class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
 | 
			
		||||
                data-toggle="dropdown"
 | 
			
		||||
                title="Manual job"
 | 
			
		||||
                data-placement="top"
 | 
			
		||||
                aria-label="Manual job"
 | 
			
		||||
              >
 | 
			
		||||
                <span v-html='svgs.iconPlay' aria-hidden="true"></span>
 | 
			
		||||
                aria-label="Manual job">
 | 
			
		||||
                <span v-html="svgs.iconPlay" aria-hidden="true"></span>
 | 
			
		||||
                <i class="fa fa-caret-down" aria-hidden="true"></i>
 | 
			
		||||
              </button>
 | 
			
		||||
              <ul class="dropdown-menu dropdown-menu-align-right">
 | 
			
		||||
| 
						 | 
				
			
			@ -38,23 +50,21 @@
 | 
			
		|||
                  <a
 | 
			
		||||
                    rel="nofollow"
 | 
			
		||||
                    data-method="post"
 | 
			
		||||
                    :href='action.path'
 | 
			
		||||
                  >
 | 
			
		||||
                    <span v-html='svgs.iconPlay' aria-hidden="true"></span>
 | 
			
		||||
                    :href="action.path">
 | 
			
		||||
                    <span v-html="svgs.iconPlay" aria-hidden="true"></span>
 | 
			
		||||
                    <span>{{action.name}}</span>
 | 
			
		||||
                  </a>
 | 
			
		||||
                </li>
 | 
			
		||||
              </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="btn-group">
 | 
			
		||||
 | 
			
		||||
            <div class="btn-group" v-if="artifacts">
 | 
			
		||||
              <button
 | 
			
		||||
                v-if='artifacts'
 | 
			
		||||
                class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
 | 
			
		||||
                title="Artifacts"
 | 
			
		||||
                data-placement="top"
 | 
			
		||||
                data-toggle="dropdown"
 | 
			
		||||
                aria-label="Artifacts"
 | 
			
		||||
              >
 | 
			
		||||
                aria-label="Artifacts">
 | 
			
		||||
                <i class="fa fa-download" aria-hidden="true"></i>
 | 
			
		||||
                <i class="fa fa-caret-down" aria-hidden="true"></i>
 | 
			
		||||
              </button>
 | 
			
		||||
| 
						 | 
				
			
			@ -62,20 +72,16 @@
 | 
			
		|||
                <li v-for='artifact in pipeline.details.artifacts'>
 | 
			
		||||
                  <a
 | 
			
		||||
                    rel="nofollow"
 | 
			
		||||
                    download
 | 
			
		||||
                    :href='artifact.path'
 | 
			
		||||
                  >
 | 
			
		||||
                    :href="artifact.path">
 | 
			
		||||
                    <i class="fa fa-download" aria-hidden="true"></i>
 | 
			
		||||
                    <span>{{download(artifact.name)}}</span>
 | 
			
		||||
                  </a>
 | 
			
		||||
                </li>
 | 
			
		||||
              </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="cancel-retry-btns inline">
 | 
			
		||||
            <div class="btn-group" v-if="pipeline.flags.retryable">
 | 
			
		||||
              <a
 | 
			
		||||
              v-if='pipeline.flags.retryable'
 | 
			
		||||
              class="btn has-tooltip"
 | 
			
		||||
                class="btn btn-default btn-retry has-tooltip"
 | 
			
		||||
                title="Retry"
 | 
			
		||||
                rel="nofollow"
 | 
			
		||||
                data-method="post"
 | 
			
		||||
| 
						 | 
				
			
			@ -85,8 +91,9 @@
 | 
			
		|||
                aria-label="Retry">
 | 
			
		||||
                <i class="fa fa-repeat" aria-hidden="true"></i>
 | 
			
		||||
              </a>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="btn-group" v-if="pipeline.flags.cancelable">
 | 
			
		||||
              <a
 | 
			
		||||
              v-if='pipeline.flags.cancelable'
 | 
			
		||||
                class="btn btn-remove has-tooltip"
 | 
			
		||||
                title="Cancel"
 | 
			
		||||
                rel="nofollow"
 | 
			
		||||
| 
						 | 
				
			
			@ -99,6 +106,7 @@
 | 
			
		|||
              </a>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </td>
 | 
			
		||||
    `,
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,18 +45,15 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
 | 
			
		|||
 | 
			
		||||
    methods: {
 | 
			
		||||
      /**
 | 
			
		||||
       * Changes the URL according to the pagination component.
 | 
			
		||||
       * Will change the page number and update the URL.
 | 
			
		||||
       *
 | 
			
		||||
       * If no scope is provided, 'all' is assumed.
 | 
			
		||||
       *
 | 
			
		||||
       * Pagination component sends "null" when no scope is provided.
 | 
			
		||||
       *
 | 
			
		||||
       * @param  {Number} pagenum
 | 
			
		||||
       * @param  {String} apiScope = 'all'
 | 
			
		||||
       * @param  {Number} pageNumber desired page to go to.
 | 
			
		||||
       */
 | 
			
		||||
      change(pagenum, apiScope) {
 | 
			
		||||
        if (!apiScope) apiScope = 'all';
 | 
			
		||||
        gl.utils.visitUrl(`?scope=${apiScope}&page=${pagenum}`);
 | 
			
		||||
      change(pageNumber) {
 | 
			
		||||
        const param = gl.utils.setParamInURL('page', pageNumber);
 | 
			
		||||
 | 
			
		||||
        gl.utils.visitUrl(param);
 | 
			
		||||
        return param;
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    template: `
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,13 @@
 | 
			
		|||
        required: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updated() {
 | 
			
		||||
      if (this.builds) {
 | 
			
		||||
        this.stopDropdownClickPropagation();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    methods: {
 | 
			
		||||
      fetchBuilds(e) {
 | 
			
		||||
        const areaExpanded = e.currentTarget.attributes['aria-expanded'];
 | 
			
		||||
| 
						 | 
				
			
			@ -37,17 +44,19 @@
 | 
			
		|||
            return flash;
 | 
			
		||||
          });
 | 
			
		||||
      },
 | 
			
		||||
      keepGraph(e) {
 | 
			
		||||
        const { target } = e;
 | 
			
		||||
 | 
			
		||||
        if (target.className.indexOf('js-ci-action-icon') >= 0) return null;
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
          target.parentElement &&
 | 
			
		||||
          (target.parentElement.className.indexOf('js-ci-action-icon') >= 0)
 | 
			
		||||
        ) return null;
 | 
			
		||||
 | 
			
		||||
        return e.stopPropagation();
 | 
			
		||||
      /**
 | 
			
		||||
       * When the user right clicks or cmd/ctrl + click in the job name
 | 
			
		||||
       * the dropdown should not be closed and the link should open in another tab,
 | 
			
		||||
       * so we stop propagation of the click event inside the dropdown.
 | 
			
		||||
       *
 | 
			
		||||
       * Since this component is rendered multiple times per page we need to guarantee we only
 | 
			
		||||
       * target the click event of this component.
 | 
			
		||||
       */
 | 
			
		||||
      stopDropdownClickPropagation() {
 | 
			
		||||
        $(this.$el.querySelectorAll('.js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item')).on('click', (e) => {
 | 
			
		||||
          e.stopPropagation();
 | 
			
		||||
        });
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
| 
						 | 
				
			
			@ -76,13 +85,13 @@
 | 
			
		|||
    template: `
 | 
			
		||||
      <div>
 | 
			
		||||
        <button
 | 
			
		||||
          @click='fetchBuilds($event)'
 | 
			
		||||
          @click="fetchBuilds($event)"
 | 
			
		||||
          :class="triggerButtonClass"
 | 
			
		||||
          :title='stage.title'
 | 
			
		||||
          :title="stage.title"
 | 
			
		||||
          data-placement="top"
 | 
			
		||||
          data-toggle="dropdown"
 | 
			
		||||
          type="button"
 | 
			
		||||
          :aria-label='stage.title'
 | 
			
		||||
          :aria-label="stage.title"
 | 
			
		||||
        >
 | 
			
		||||
          <span v-html="svg" aria-hidden="true"></span>
 | 
			
		||||
          <i class="fa fa-caret-down" aria-hidden="true"></i>
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +99,6 @@
 | 
			
		|||
        <ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container">
 | 
			
		||||
          <div class="arrow-up" aria-hidden="true"></div>
 | 
			
		||||
          <div
 | 
			
		||||
            @click='keepGraph($event)'
 | 
			
		||||
            :class="dropdownClass"
 | 
			
		||||
            class="js-builds-dropdown-list scrollable-menu"
 | 
			
		||||
            v-html="buildsOrSpinner"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,7 +54,7 @@ require('../lib/utils/datetime_utility');
 | 
			
		|||
      },
 | 
			
		||||
    },
 | 
			
		||||
    template: `
 | 
			
		||||
      <td>
 | 
			
		||||
      <td class="pipelines-time-ago">
 | 
			
		||||
        <p class="duration" v-if='duration'>
 | 
			
		||||
          <span v-html='svgs.iconTimer'></span>
 | 
			
		||||
          {{duration}}
 | 
			
		||||
| 
						 | 
				
			
			@ -65,8 +65,7 @@ require('../lib/utils/datetime_utility');
 | 
			
		|||
            data-toggle="tooltip"
 | 
			
		||||
            data-placement="top"
 | 
			
		||||
            data-container="body"
 | 
			
		||||
            :data-original-title='localTimeFinished'
 | 
			
		||||
          >
 | 
			
		||||
            :data-original-title='localTimeFinished'>
 | 
			
		||||
            {{timeStopped.words}}
 | 
			
		||||
          </time>
 | 
			
		||||
        </p>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ require('./pipelines_table_row');
 | 
			
		|||
            <th class="js-pipeline-commit pipeline-commit">Commit</th>
 | 
			
		||||
            <th class="js-pipeline-stages pipeline-stages">Stages</th>
 | 
			
		||||
            <th class="js-pipeline-date pipeline-date"></th>
 | 
			
		||||
            <th class="js-pipeline-actions pipeline-actions hidden-xs"></th>
 | 
			
		||||
            <th class="js-pipeline-actions pipeline-actions"></th>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,8 +23,8 @@ window.Vue = require('vue');
 | 
			
		|||
 | 
			
		||||
        Here is an example `change` method:
 | 
			
		||||
 | 
			
		||||
        change(pagenum, apiScope) {
 | 
			
		||||
          gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`);
 | 
			
		||||
        change(pagenum) {
 | 
			
		||||
          gl.utils.visitUrl(`?page=${pagenum}`);
 | 
			
		||||
        },
 | 
			
		||||
      */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -57,8 +57,6 @@ window.Vue = require('vue');
 | 
			
		|||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
      changePage(e) {
 | 
			
		||||
        const apiScope = gl.utils.getParameterByName('scope');
 | 
			
		||||
 | 
			
		||||
        const text = e.target.innerText;
 | 
			
		||||
        const { totalPages, nextPage, previousPage } = this.pageInfo;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -66,19 +64,19 @@ window.Vue = require('vue');
 | 
			
		|||
          case SPREAD:
 | 
			
		||||
            break;
 | 
			
		||||
          case LAST:
 | 
			
		||||
            this.change(totalPages, apiScope);
 | 
			
		||||
            this.change(totalPages);
 | 
			
		||||
            break;
 | 
			
		||||
          case NEXT:
 | 
			
		||||
            this.change(nextPage, apiScope);
 | 
			
		||||
            this.change(nextPage);
 | 
			
		||||
            break;
 | 
			
		||||
          case PREV:
 | 
			
		||||
            this.change(previousPage, apiScope);
 | 
			
		||||
            this.change(previousPage);
 | 
			
		||||
            break;
 | 
			
		||||
          case FIRST:
 | 
			
		||||
            this.change(1, apiScope);
 | 
			
		||||
            this.change(1);
 | 
			
		||||
            break;
 | 
			
		||||
          default:
 | 
			
		||||
            this.change(+text, apiScope);
 | 
			
		||||
            this.change(+text);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,6 @@
 | 
			
		|||
 * This is a manifest file that'll automatically include all the stylesheets available in this directory
 | 
			
		||||
 * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
 | 
			
		||||
 * the top of the compiled file, but it's generally better to create a new file per style scope.
 | 
			
		||||
 *= require jquery-ui/autocomplete
 | 
			
		||||
 *= require jquery.atwho
 | 
			
		||||
 *= require select2
 | 
			
		||||
 *= require_self
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -229,7 +229,7 @@
 | 
			
		|||
  .controls {
 | 
			
		||||
    float: right;
 | 
			
		||||
    margin-top: 8px;
 | 
			
		||||
    padding-bottom: 7px;
 | 
			
		||||
    padding-bottom: 8px;
 | 
			
		||||
    border-bottom: 1px solid $border-color;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
.calender-block {
 | 
			
		||||
  padding-left: 0;
 | 
			
		||||
  padding-right: 0;
 | 
			
		||||
  border-top: 0;
 | 
			
		||||
  direction: rtl;
 | 
			
		||||
 | 
			
		||||
  @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -271,6 +271,7 @@ span.idiff {
 | 
			
		|||
      font-size: 13px;
 | 
			
		||||
      line-height: 28px;
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      float: none;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,11 @@
 | 
			
		|||
.filtered-search-container {
 | 
			
		||||
  display: -webkit-flex;
 | 
			
		||||
  display: flex;
 | 
			
		||||
 | 
			
		||||
  @media (max-width: $screen-xs-min) {
 | 
			
		||||
    -webkit-flex-direction: column;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.filtered-search-input-container {
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +39,20 @@
 | 
			
		|||
  position: relative;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
 | 
			
		||||
  @media (max-width: $screen-xs-min) {
 | 
			
		||||
    -webkit-flex: 1 1 100%;
 | 
			
		||||
    flex: 1 1 100%;
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
 | 
			
		||||
    .dropdown-menu {
 | 
			
		||||
      width: auto;
 | 
			
		||||
      left: 0;
 | 
			
		||||
      right: 0;
 | 
			
		||||
      max-width: none;
 | 
			
		||||
      min-width: 100%;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .form-control {
 | 
			
		||||
    padding-left: 25px;
 | 
			
		||||
    padding-right: 25px;
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +98,31 @@
 | 
			
		|||
  overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: $screen-xs-min) {
 | 
			
		||||
  .issues-details-filters {
 | 
			
		||||
    padding: 0 0 10px;
 | 
			
		||||
    background-color: $white-light;
 | 
			
		||||
    border-top: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .filter-dropdown-container {
 | 
			
		||||
    .dropdown-toggle,
 | 
			
		||||
    .dropdown {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .dropdown {
 | 
			
		||||
      margin-left: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .fa-chevron-down {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      right: 10px;
 | 
			
		||||
      top: 10px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
%filter-dropdown-item-btn-hover {
 | 
			
		||||
  background-color: $dropdown-hover-color;
 | 
			
		||||
  color: $white-light;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -148,16 +148,11 @@ header {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    .header-logo {
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      left: 50%;
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      margin: 0 8px 0 3px;
 | 
			
		||||
      position: relative;
 | 
			
		||||
      top: 7px;
 | 
			
		||||
      transition-duration: .3s;
 | 
			
		||||
      z-index: 999;
 | 
			
		||||
 | 
			
		||||
      #logo {
 | 
			
		||||
        position: relative;
 | 
			
		||||
        left: -50%;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      svg,
 | 
			
		||||
      img {
 | 
			
		||||
| 
						 | 
				
			
			@ -167,15 +162,6 @@ header {
 | 
			
		|||
      &:hover {
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @media (max-width: $screen-xs-max) {
 | 
			
		||||
        right: 20px;
 | 
			
		||||
        left: auto;
 | 
			
		||||
 | 
			
		||||
        #logo {
 | 
			
		||||
          left: auto;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .title {
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +169,6 @@ header {
 | 
			
		|||
      padding-right: 20px;
 | 
			
		||||
      margin: 0;
 | 
			
		||||
      font-size: 18px;
 | 
			
		||||
      max-width: 385px;
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      line-height: $header-height;
 | 
			
		||||
      font-weight: normal;
 | 
			
		||||
| 
						 | 
				
			
			@ -193,14 +178,18 @@ header {
 | 
			
		|||
      vertical-align: top;
 | 
			
		||||
      white-space: nowrap;
 | 
			
		||||
 | 
			
		||||
      @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
 | 
			
		||||
        max-width: 300px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @media (max-width: $screen-xs-max) {
 | 
			
		||||
        max-width: 190px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
 | 
			
		||||
        max-width: 428px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      @media (min-width: $screen-lg-min) {
 | 
			
		||||
        max-width: 685px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      a {
 | 
			
		||||
        color: $gl-text-color;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,17 +2,6 @@
 | 
			
		|||
  font-family: $regular_font;
 | 
			
		||||
  font-size: $font-size-base;
 | 
			
		||||
 | 
			
		||||
  &.ui-autocomplete {
 | 
			
		||||
    border-color: $jq-ui-border;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    margin-top: 2px;
 | 
			
		||||
    z-index: 1001;
 | 
			
		||||
 | 
			
		||||
    .ui-menu-item a {
 | 
			
		||||
      padding: 4px 10px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .ui-state-default {
 | 
			
		||||
    border: 1px solid $white-light;
 | 
			
		||||
    background: $white-light;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,8 @@ $dark-highlight-bg: #ffe792;
 | 
			
		|||
$dark-highlight-color: $black;
 | 
			
		||||
$dark-pre-hll-bg: #373b41;
 | 
			
		||||
$dark-hll-bg: #373b41;
 | 
			
		||||
$dark-over-bg: #9f9ab5;
 | 
			
		||||
$dark-expanded-bg: #3e3e3e;
 | 
			
		||||
$dark-c: #969896;
 | 
			
		||||
$dark-err: #c66;
 | 
			
		||||
$dark-k: #b294bb;
 | 
			
		||||
| 
						 | 
				
			
			@ -139,9 +141,37 @@ $dark-il: #de935f;
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .diff-line-num {
 | 
			
		||||
      &.is-over,
 | 
			
		||||
      &.hll:not(.empty-cell).is-over {
 | 
			
		||||
        background-color: $dark-over-bg;
 | 
			
		||||
        border-color: darken($dark-over-bg, 5%);
 | 
			
		||||
 | 
			
		||||
        a {
 | 
			
		||||
          color: darken($dark-over-bg, 15%);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .line_content.match {
 | 
			
		||||
      @include dark-diff-match-line;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:not(.diff-expanded) + .diff-expanded,
 | 
			
		||||
    &.diff-expanded + .line_holder:not(.diff-expanded) {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        border-top: 1px solid $black;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.diff-expanded {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        background: $dark-expanded-bg;
 | 
			
		||||
        border-color: $dark-expanded-bg;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // highlight line via anchor
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,8 @@ $monokai-line-empty-bg: #49483e;
 | 
			
		|||
$monokai-line-empty-border: darken($monokai-line-empty-bg, 15%);
 | 
			
		||||
$monokai-diff-border: #808080;
 | 
			
		||||
$monokai-highlight-bg: #ffe792;
 | 
			
		||||
$monokai-over-bg: #9f9ab5;
 | 
			
		||||
$monokai-expanded-bg: #3e3e3e;
 | 
			
		||||
 | 
			
		||||
$monokai-new-bg: rgba(166, 226, 46, 0.1);
 | 
			
		||||
$monokai-new-idiff: rgba(166, 226, 46, 0.15);
 | 
			
		||||
| 
						 | 
				
			
			@ -139,9 +141,37 @@ $monokai-gi: #a6e22e;
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .diff-line-num {
 | 
			
		||||
      &.is-over,
 | 
			
		||||
      &.hll:not(.empty-cell).is-over {
 | 
			
		||||
        background-color: $monokai-over-bg;
 | 
			
		||||
        border-color: darken($monokai-over-bg, 5%);
 | 
			
		||||
 | 
			
		||||
        a {
 | 
			
		||||
          color: darken($monokai-over-bg, 15%);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .line_content.match {
 | 
			
		||||
      @include dark-diff-match-line;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:not(.diff-expanded) + .diff-expanded,
 | 
			
		||||
    &.diff-expanded + .line_holder:not(.diff-expanded) {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        border-top: 1px solid $black;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.diff-expanded {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        background: $monokai-expanded-bg;
 | 
			
		||||
        border-color: $monokai-expanded-bg;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // highlight line via anchor
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,8 @@ $solarized-dark-line-color-new: #5a766c;
 | 
			
		|||
$solarized-dark-line-color-old: #7a6c71;
 | 
			
		||||
$solarized-dark-highlight: #094554;
 | 
			
		||||
$solarized-dark-hll-bg: #174652;
 | 
			
		||||
$solarized-dark-over-bg: #9f9ab5;
 | 
			
		||||
$solarized-dark-expanded-bg: #010d10;
 | 
			
		||||
$solarized-dark-c: #586e75;
 | 
			
		||||
$solarized-dark-err: #93a1a1;
 | 
			
		||||
$solarized-dark-g: #93a1a1;
 | 
			
		||||
| 
						 | 
				
			
			@ -143,9 +145,37 @@ $solarized-dark-il: #2aa198;
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .diff-line-num {
 | 
			
		||||
      &.is-over,
 | 
			
		||||
      &.hll:not(.empty-cell).is-over {
 | 
			
		||||
        background-color: $solarized-dark-over-bg;
 | 
			
		||||
        border-color: darken($solarized-dark-over-bg, 5%);
 | 
			
		||||
 | 
			
		||||
        a {
 | 
			
		||||
          color: darken($solarized-dark-over-bg, 15%);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .line_content.match {
 | 
			
		||||
      @include dark-diff-match-line;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:not(.diff-expanded) + .diff-expanded,
 | 
			
		||||
    &.diff-expanded + .line_holder:not(.diff-expanded) {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        border-top: 1px solid $black;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.diff-expanded {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        background: $solarized-dark-expanded-bg;
 | 
			
		||||
        border-color: $solarized-dark-expanded-bg;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // highlight line via anchor
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,9 @@ $solarized-light-line-color-new: #a1a080;
 | 
			
		|||
$solarized-light-line-color-old: #ad9186;
 | 
			
		||||
$solarized-light-highlight: #eee8d5;
 | 
			
		||||
$solarized-light-hll-bg: #ddd8c5;
 | 
			
		||||
$solarized-light-over-bg: #ded7fc;
 | 
			
		||||
$solarized-light-expanded-border: #d2cdbd;
 | 
			
		||||
$solarized-light-expanded-bg: #ece6d4;
 | 
			
		||||
$solarized-light-c: #93a1a1;
 | 
			
		||||
$solarized-light-err: #586e75;
 | 
			
		||||
$solarized-light-g: #586e75;
 | 
			
		||||
| 
						 | 
				
			
			@ -150,9 +153,37 @@ $solarized-light-il: #2aa198;
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .diff-line-num {
 | 
			
		||||
      &.is-over,
 | 
			
		||||
      &.hll:not(.empty-cell).is-over {
 | 
			
		||||
        background-color: $solarized-light-over-bg;
 | 
			
		||||
        border-color: darken($solarized-light-over-bg, 5%);
 | 
			
		||||
 | 
			
		||||
        a {
 | 
			
		||||
          color: darken($solarized-light-over-bg, 15%);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .line_content.match {
 | 
			
		||||
      @include matchLine;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:not(.diff-expanded) + .diff-expanded,
 | 
			
		||||
    &.diff-expanded + .line_holder:not(.diff-expanded) {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        border-top: 1px solid $solarized-light-expanded-border;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.diff-expanded {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        background: $solarized-light-expanded-bg;
 | 
			
		||||
        border-color: $solarized-light-expanded-bg;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // highlight line via anchor
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,9 @@ $white-code-color: $gl-text-color;
 | 
			
		|||
$white-highlight: #fafe3d;
 | 
			
		||||
$white-pre-hll-bg: #f8eec7;
 | 
			
		||||
$white-hll-bg: #f8f8f8;
 | 
			
		||||
$white-over-bg: #ded7fc;
 | 
			
		||||
$white-expanded-border: #e0e0e0;
 | 
			
		||||
$white-expanded-bg: #f7f7f7;
 | 
			
		||||
$white-c: #998;
 | 
			
		||||
$white-err: #a61717;
 | 
			
		||||
$white-err-bg: #e3d2d2;
 | 
			
		||||
| 
						 | 
				
			
			@ -123,12 +126,38 @@ $white-gc-bg: #eaf2f5;
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &.is-over,
 | 
			
		||||
      &.hll:not(.empty-cell).is-over {
 | 
			
		||||
        background-color: $white-over-bg;
 | 
			
		||||
        border-color: darken($white-over-bg, 5%);
 | 
			
		||||
 | 
			
		||||
        a {
 | 
			
		||||
          color: darken($white-over-bg, 15%);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &.hll:not(.empty-cell) {
 | 
			
		||||
        background-color: $line-number-select;
 | 
			
		||||
        border-color: $line-select-yellow-dark;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:not(.diff-expanded) + .diff-expanded,
 | 
			
		||||
    &.diff-expanded + .line_holder:not(.diff-expanded) {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        border-top: 1px solid $white-expanded-border;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.diff-expanded {
 | 
			
		||||
      > .diff-line-num,
 | 
			
		||||
      > .line_content {
 | 
			
		||||
        background: $white-expanded-bg;
 | 
			
		||||
        border-color: $white-expanded-bg;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .line_content {
 | 
			
		||||
      &.old {
 | 
			
		||||
        background-color: $line-removed;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,6 +89,10 @@
 | 
			
		|||
 | 
			
		||||
      .diff-line-num {
 | 
			
		||||
        width: 50px;
 | 
			
		||||
 | 
			
		||||
        a {
 | 
			
		||||
          transition: none;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .line_holder td {
 | 
			
		||||
| 
						 | 
				
			
			@ -109,10 +113,6 @@
 | 
			
		|||
      td.line_content.parallel {
 | 
			
		||||
        width: 46%;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .add-diff-note {
 | 
			
		||||
        margin-left: -65px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .old_line,
 | 
			
		||||
| 
						 | 
				
			
			@ -133,8 +133,13 @@
 | 
			
		|||
        width: 35px;
 | 
			
		||||
        font-weight: normal;
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
          text-decoration: underline;
 | 
			
		||||
        &[disabled] {
 | 
			
		||||
          cursor: default;
 | 
			
		||||
 | 
			
		||||
          &:hover,
 | 
			
		||||
          &:active {
 | 
			
		||||
            text-decoration: none;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,24 +15,15 @@
 | 
			
		|||
  padding-top: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: $screen-xs-max) {
 | 
			
		||||
.environments-container {
 | 
			
		||||
  .table-holder {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.environments {
 | 
			
		||||
  table-layout: fixed;
 | 
			
		||||
 | 
			
		||||
  .environments-commit,
 | 
			
		||||
  .environments-actions,
 | 
			
		||||
  .environments-deploy,
 | 
			
		||||
  .environments-build,
 | 
			
		||||
  .environments-date {
 | 
			
		||||
    position: static;
 | 
			
		||||
    float: none;
 | 
			
		||||
    display: table-cell;
 | 
			
		||||
  .table.ci-table {
 | 
			
		||||
    .environments-actions {
 | 
			
		||||
      min-width: 200px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .environments-commit,
 | 
			
		||||
| 
						 | 
				
			
			@ -50,13 +41,11 @@
 | 
			
		|||
      width: 15%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  .environment-name,
 | 
			
		||||
  .environments-build-cell,
 | 
			
		||||
    .deployment-column {
 | 
			
		||||
      > span {
 | 
			
		||||
        word-break: break-all;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
  .deployment-column {
 | 
			
		||||
      .avatar {
 | 
			
		||||
        float: none;
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +66,6 @@
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    .commit-title {
 | 
			
		||||
      margin: 0;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +85,6 @@
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    .dropdown-menu {
 | 
			
		||||
 | 
			
		||||
      .fa {
 | 
			
		||||
        margin-right: 6px;
 | 
			
		||||
        color: $gl-text-color-secondary;
 | 
			
		||||
| 
						 | 
				
			
			@ -118,9 +105,7 @@
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  .deployment {
 | 
			
		||||
    .build-column {
 | 
			
		||||
 | 
			
		||||
    .deployment .build-column {
 | 
			
		||||
      .build-link {
 | 
			
		||||
        color: $gl-text-color;
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +114,6 @@
 | 
			
		|||
        float: none;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    .folder-icon {
 | 
			
		||||
      margin-right: 3px;
 | 
			
		||||
| 
						 | 
				
			
			@ -146,9 +130,7 @@
 | 
			
		|||
      color: $gl-text-color-secondary;
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table.ci-table.environments {
 | 
			
		||||
    .icon-container {
 | 
			
		||||
      width: 20px;
 | 
			
		||||
      text-align: center;
 | 
			
		||||
| 
						 | 
				
			
			@ -160,3 +142,4 @@
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -452,36 +452,37 @@ ul.notes {
 | 
			
		|||
 * Line note button on the side of diffs
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
.diff-file tr.line_holder {
 | 
			
		||||
  @mixin show-add-diff-note {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
.add-diff-note {
 | 
			
		||||
    margin-top: -8px;
 | 
			
		||||
    border-radius: 40px;
 | 
			
		||||
  display: none;
 | 
			
		||||
  margin-top: -2px;
 | 
			
		||||
  border-radius: 50%;
 | 
			
		||||
  background: $white-light;
 | 
			
		||||
    padding: 4px;
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
  padding: 1px 5px;
 | 
			
		||||
  font-size: 12px;
 | 
			
		||||
  color: $gl-link-color;
 | 
			
		||||
    margin-left: -56px;
 | 
			
		||||
  margin-left: -55px;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  z-index: 10;
 | 
			
		||||
    width: 32px;
 | 
			
		||||
    // "hide" it by default
 | 
			
		||||
    display: none;
 | 
			
		||||
  width: 23px;
 | 
			
		||||
  height: 23px;
 | 
			
		||||
  border: 1px solid $border-color;
 | 
			
		||||
  transition: transform .1s ease-in-out;
 | 
			
		||||
 | 
			
		||||
  &:hover {
 | 
			
		||||
    background: $gl-info;
 | 
			
		||||
    color: $white-light;
 | 
			
		||||
      @include show-add-diff-note;
 | 
			
		||||
    transform: scale(1.15);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &:active {
 | 
			
		||||
    outline: 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  // "show" the icon also if we just hover somewhere over the line
 | 
			
		||||
  &:hover > td {
 | 
			
		||||
.diff-file {
 | 
			
		||||
  .is-over {
 | 
			
		||||
    .add-diff-note {
 | 
			
		||||
      @include show-add-diff-note;
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,21 +13,16 @@
 | 
			
		|||
    white-space: nowrap;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .table-holder {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .commit-title {
 | 
			
		||||
    margin: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .controls {
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .btn {
 | 
			
		||||
    margin: 4px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .table.ci-table {
 | 
			
		||||
    min-width: 1200px;
 | 
			
		||||
    table-layout: fixed;
 | 
			
		||||
 | 
			
		||||
    .label {
 | 
			
		||||
      margin-bottom: 3px;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,16 +32,72 @@
 | 
			
		|||
      color: $black;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .pipeline-date,
 | 
			
		||||
    .pipeline-status {
 | 
			
		||||
      width: 10%;
 | 
			
		||||
    .stage-cell {
 | 
			
		||||
      min-width: 130px; // Guarantees we show at least 4 stages in line
 | 
			
		||||
      width: 20%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .pipelines-time-ago {
 | 
			
		||||
      text-align: right;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .pipeline-info,
 | 
			
		||||
    .pipeline-commit,
 | 
			
		||||
    .pipeline-stages,
 | 
			
		||||
    .pipeline-actions {
 | 
			
		||||
      width: 20%;
 | 
			
		||||
      padding-right: 0;
 | 
			
		||||
      min-width: 170px; //Guarantees buttons don't break in several lines.
 | 
			
		||||
 | 
			
		||||
      .btn-default {
 | 
			
		||||
        color: $gl-text-color-secondary;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .btn.btn-retry:hover,
 | 
			
		||||
      .btn.btn-retry:focus {
 | 
			
		||||
        border-color: $gray-darkest;
 | 
			
		||||
        background-color: $white-normal;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      svg path {
 | 
			
		||||
        fill: $gl-text-color-secondary;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .dropdown-menu {
 | 
			
		||||
        max-height: 250px;
 | 
			
		||||
        overflow-y: auto;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .dropdown-toggle,
 | 
			
		||||
      .dropdown-menu {
 | 
			
		||||
        color: $gl-text-color-secondary;
 | 
			
		||||
 | 
			
		||||
        .fa {
 | 
			
		||||
          color: $gl-text-color-secondary;
 | 
			
		||||
          font-size: 14px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        svg,
 | 
			
		||||
        .fa {
 | 
			
		||||
          margin-right: 0;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .btn-group {
 | 
			
		||||
        &.open {
 | 
			
		||||
          .btn-default {
 | 
			
		||||
            background-color: $white-normal;
 | 
			
		||||
            border-color: $border-white-normal;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .btn {
 | 
			
		||||
          .icon-play {
 | 
			
		||||
            height: 13px;
 | 
			
		||||
            width: 12px;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .tooltip {
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +105,7 @@
 | 
			
		|||
@media (max-width: $screen-md-max) {
 | 
			
		||||
  .content-list {
 | 
			
		||||
    &.pipelines,
 | 
			
		||||
    &.environments-container,
 | 
			
		||||
    &.builds-content-list {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      overflow: auto;
 | 
			
		||||
| 
						 | 
				
			
			@ -61,28 +113,11 @@
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content-list.pipelines .table-holder {
 | 
			
		||||
  min-height: 300px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.pipeline-holder {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table.ci-table {
 | 
			
		||||
  min-width: 900px;
 | 
			
		||||
 | 
			
		||||
  &.pipeline {
 | 
			
		||||
    min-width: 650px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.builds-page {
 | 
			
		||||
 | 
			
		||||
    tr {
 | 
			
		||||
  &.builds-page tr {
 | 
			
		||||
    height: 71px;
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  tr {
 | 
			
		||||
    th {
 | 
			
		||||
| 
						 | 
				
			
			@ -94,12 +129,16 @@
 | 
			
		|||
      padding: 10px 8px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    td.environments-actions {
 | 
			
		||||
      padding-right: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    td.stage-cell {
 | 
			
		||||
      padding: 10px 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .commit-link {
 | 
			
		||||
      padding: 9px 8px 10px;
 | 
			
		||||
      padding: 9px 8px 10px 2px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -206,73 +245,9 @@
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .pipeline-actions {
 | 
			
		||||
    min-width: 140px;
 | 
			
		||||
 | 
			
		||||
    .btn {
 | 
			
		||||
      margin: 0;
 | 
			
		||||
      color: $gl-text-color-secondary;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .cancel-retry-btns {
 | 
			
		||||
      vertical-align: middle;
 | 
			
		||||
 | 
			
		||||
      .btn:not(:first-child) {
 | 
			
		||||
        margin-left: 8px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .dropdown-menu {
 | 
			
		||||
      max-height: 250px;
 | 
			
		||||
      overflow-y: auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .dropdown-toggle,
 | 
			
		||||
    .dropdown-menu {
 | 
			
		||||
      color: $gl-text-color-secondary;
 | 
			
		||||
 | 
			
		||||
      .fa {
 | 
			
		||||
        color: $gl-text-color-secondary;
 | 
			
		||||
        font-size: 14px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      svg,
 | 
			
		||||
      .fa {
 | 
			
		||||
        margin-right: 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .btn-remove {
 | 
			
		||||
      color: $white-light;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .btn-group {
 | 
			
		||||
      &.open {
 | 
			
		||||
        .btn-default {
 | 
			
		||||
          background-color: $white-normal;
 | 
			
		||||
          border-color: $border-white-normal;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .btn {
 | 
			
		||||
        .icon-play {
 | 
			
		||||
          height: 13px;
 | 
			
		||||
          width: 12px;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .tooltip {
 | 
			
		||||
      white-space: nowrap;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .build-link {
 | 
			
		||||
 | 
			
		||||
    a {
 | 
			
		||||
  .build-link a {
 | 
			
		||||
    color: $gl-text-color;
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .btn-group.open .dropdown-toggle {
 | 
			
		||||
    box-shadow: none;
 | 
			
		||||
| 
						 | 
				
			
			@ -335,33 +310,10 @@
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
.tab-pane {
 | 
			
		||||
  &.pipelines {
 | 
			
		||||
    .ci-table {
 | 
			
		||||
      min-width: 900px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .content-list.pipelines {
 | 
			
		||||
      overflow: auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .stage {
 | 
			
		||||
      max-width: 100px;
 | 
			
		||||
      width: 100px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .pipeline-actions {
 | 
			
		||||
      min-width: initial;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &.builds {
 | 
			
		||||
    .ci-table {
 | 
			
		||||
      tr {
 | 
			
		||||
  &.builds .ci-table tr {
 | 
			
		||||
    height: 71px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pipeline graph
 | 
			
		||||
.pipeline-graph {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -277,3 +277,41 @@ table.u2f-registrations {
 | 
			
		|||
    padding-left: 18px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.user-callout {
 | 
			
		||||
  margin: 24px auto 0;
 | 
			
		||||
 | 
			
		||||
  .bordered-box {
 | 
			
		||||
    border: 1px solid $border-color;
 | 
			
		||||
    border-radius: $border-radius-default;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .landing {
 | 
			
		||||
    margin-bottom: $gl-padding;
 | 
			
		||||
 | 
			
		||||
    .close {
 | 
			
		||||
      margin-right: 20px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .dismiss-icon {
 | 
			
		||||
      float: right;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      color: $cycle-analytics-dismiss-icon-color;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .svg-container {
 | 
			
		||||
      text-align: center;
 | 
			
		||||
 | 
			
		||||
      svg {
 | 
			
		||||
        width: 136px;
 | 
			
		||||
        height: 136px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media(max-width: $screen-xs-max) {
 | 
			
		||||
    .inner-content {
 | 
			
		||||
      padding-left: 30px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -638,14 +638,6 @@ pre.light-well {
 | 
			
		|||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.activity-filter-block {
 | 
			
		||||
  .controls {
 | 
			
		||||
    padding-bottom: 7px;
 | 
			
		||||
    margin-top: 8px;
 | 
			
		||||
    border-bottom: 1px solid $border-color;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.commits-search-form {
 | 
			
		||||
  .input-short {
 | 
			
		||||
    min-width: 200px;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,6 +83,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
 | 
			
		|||
      :akismet_api_key,
 | 
			
		||||
      :akismet_enabled,
 | 
			
		||||
      :container_registry_token_expire_delay,
 | 
			
		||||
      :default_artifacts_expire_in,
 | 
			
		||||
      :default_branch_protection,
 | 
			
		||||
      :default_group_visibility,
 | 
			
		||||
      :default_project_visibility,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -72,14 +72,6 @@ class ApplicationController < ActionController::Base
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def authenticate_user!(*args)
 | 
			
		||||
    if redirect_to_home_page_url?
 | 
			
		||||
      return redirect_to current_application_settings.home_page_url
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    super(*args)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def log_exception(exception)
 | 
			
		||||
    application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
 | 
			
		||||
    application_trace.map!{ |t| "  #{t}\n" }
 | 
			
		||||
| 
						 | 
				
			
			@ -287,19 +279,6 @@ class ApplicationController < ActionController::Base
 | 
			
		|||
    session[:skip_tfa] && session[:skip_tfa] > Time.current
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def redirect_to_home_page_url?
 | 
			
		||||
    # If user is not signed-in and tries to access root_path - redirect him to landing page
 | 
			
		||||
    # Don't redirect to the default URL to prevent endless redirections
 | 
			
		||||
    return false unless current_application_settings.home_page_url.present?
 | 
			
		||||
 | 
			
		||||
    home_page_url = current_application_settings.home_page_url.chomp('/')
 | 
			
		||||
    root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]
 | 
			
		||||
 | 
			
		||||
    return false if root_urls.include?(home_page_url)
 | 
			
		||||
 | 
			
		||||
    current_user.nil? && root_path == request.path
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # U2F (universal 2nd factor) devices need a unique identifier for the application
 | 
			
		||||
  # to perform authentication.
 | 
			
		||||
  # https://developers.yubico.com/U2F/App_ID.html
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,23 @@ module IssuableActions
 | 
			
		|||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def render_conflict_response
 | 
			
		||||
    respond_to do |format|
 | 
			
		||||
      format.html do
 | 
			
		||||
        @conflict = true
 | 
			
		||||
        render :edit
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      format.json do
 | 
			
		||||
        render json: {
 | 
			
		||||
          errors: [
 | 
			
		||||
            "Someone edited this #{issuable.human_class_name} at the same time you did. Please refresh your browser and make sure your changes will not unintentionally remove theirs."
 | 
			
		||||
          ]
 | 
			
		||||
        }, status: 409
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def labels
 | 
			
		||||
    @labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ module ServiceParams
 | 
			
		|||
    :issues_url,
 | 
			
		||||
    :jira_issue_transition_id,
 | 
			
		||||
    :merge_requests_events,
 | 
			
		||||
    :mock_service_url,
 | 
			
		||||
    :namespace,
 | 
			
		||||
    :new_issue_url,
 | 
			
		||||
    :notify,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ class Projects::BlobController < Projects::ApplicationController
 | 
			
		|||
  include ActionView::Helpers::SanitizeHelper
 | 
			
		||||
 | 
			
		||||
  # Raised when given an invalid file path
 | 
			
		||||
  class InvalidPathError < StandardError; end
 | 
			
		||||
  InvalidPathError = Class.new(StandardError)
 | 
			
		||||
 | 
			
		||||
  before_action :require_non_empty_project, except: [:new, :create]
 | 
			
		||||
  before_action :authorize_download_code!
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -134,8 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController
 | 
			
		|||
    end
 | 
			
		||||
 | 
			
		||||
  rescue ActiveRecord::StaleObjectError
 | 
			
		||||
    @conflict = true
 | 
			
		||||
    render :edit
 | 
			
		||||
    render_conflict_response
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def referenced_merge_requests
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,11 +10,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 | 
			
		|||
  before_action :module_enabled
 | 
			
		||||
  before_action :merge_request, only: [
 | 
			
		||||
    :edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge, :merge_check,
 | 
			
		||||
    :ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
 | 
			
		||||
    :ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_pipeline_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
 | 
			
		||||
  ]
 | 
			
		||||
  before_action :validates_merge_request, only: [:show, :diffs, :commits, :pipelines]
 | 
			
		||||
  before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
 | 
			
		||||
  before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
 | 
			
		||||
  before_action :define_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check]
 | 
			
		||||
  before_action :define_commit_vars, only: [:diffs]
 | 
			
		||||
  before_action :define_diff_comment_vars, only: [:diffs]
 | 
			
		||||
  before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :conflict_for_path, :pipelines]
 | 
			
		||||
| 
						 | 
				
			
			@ -245,9 +245,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 | 
			
		|||
      format.json do
 | 
			
		||||
        define_pipelines_vars
 | 
			
		||||
 | 
			
		||||
        render json: PipelineSerializer
 | 
			
		||||
        render json: {
 | 
			
		||||
          pipelines: PipelineSerializer
 | 
			
		||||
          .new(project: @project, user: @current_user)
 | 
			
		||||
          .represent(@pipelines)
 | 
			
		||||
        }
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			@ -296,22 +298,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 | 
			
		|||
  def update
 | 
			
		||||
    @merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request)
 | 
			
		||||
 | 
			
		||||
    if @merge_request.valid?
 | 
			
		||||
    respond_to do |format|
 | 
			
		||||
      format.html do
 | 
			
		||||
          redirect_to([@merge_request.target_project.namespace.becomes(Namespace),
 | 
			
		||||
                       @merge_request.target_project, @merge_request])
 | 
			
		||||
        if @merge_request.valid?
 | 
			
		||||
          redirect_to([@merge_request.target_project.namespace.becomes(Namespace), @merge_request.target_project, @merge_request])
 | 
			
		||||
        else
 | 
			
		||||
          render :edit
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      format.json do
 | 
			
		||||
        render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    else
 | 
			
		||||
      render "edit"
 | 
			
		||||
    end
 | 
			
		||||
  rescue ActiveRecord::StaleObjectError
 | 
			
		||||
    @conflict = true
 | 
			
		||||
    render :edit
 | 
			
		||||
    render_conflict_response
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def remove_wip
 | 
			
		||||
| 
						 | 
				
			
			@ -327,8 +328,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 | 
			
		|||
    render partial: "projects/merge_requests/widget/show.html.haml", layout: false
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def cancel_merge_when_build_succeeds
 | 
			
		||||
    unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
 | 
			
		||||
  def cancel_merge_when_pipeline_succeeds
 | 
			
		||||
    unless @merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
 | 
			
		||||
      return access_denied!
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -340,9 +341,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 | 
			
		|||
  def merge
 | 
			
		||||
    return access_denied! unless @merge_request.can_be_merged_by?(current_user)
 | 
			
		||||
 | 
			
		||||
    # Disable the CI check if merge_when_build_succeeds is enabled since we have
 | 
			
		||||
    # Disable the CI check if merge_when_pipeline_succeeds is enabled since we have
 | 
			
		||||
    # to wait until CI completes to know
 | 
			
		||||
    unless @merge_request.mergeable?(skip_ci_check: merge_when_build_succeeds_active?)
 | 
			
		||||
    unless @merge_request.mergeable?(skip_ci_check: merge_when_pipeline_succeeds_active?)
 | 
			
		||||
      @status = :failed
 | 
			
		||||
      return
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			@ -354,7 +355,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 | 
			
		|||
 | 
			
		||||
    @merge_request.update(merge_error: nil)
 | 
			
		||||
 | 
			
		||||
    if params[:merge_when_build_succeeds].present?
 | 
			
		||||
    if params[:merge_when_pipeline_succeeds].present?
 | 
			
		||||
      unless @merge_request.head_pipeline
 | 
			
		||||
        @status = :failed
 | 
			
		||||
        return
 | 
			
		||||
| 
						 | 
				
			
			@ -365,7 +366,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 | 
			
		|||
          .new(@project, current_user, merge_params)
 | 
			
		||||
          .execute(@merge_request)
 | 
			
		||||
 | 
			
		||||
        @status = :merge_when_build_succeeds
 | 
			
		||||
        @status = :merge_when_pipeline_succeeds
 | 
			
		||||
      elsif @merge_request.head_pipeline.success?
 | 
			
		||||
        # This can be triggered when a user clicks the auto merge button while
 | 
			
		||||
        # the tests finish at about the same time
 | 
			
		||||
| 
						 | 
				
			
			@ -382,8 +383,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 | 
			
		|||
 | 
			
		||||
  def merge_widget_refresh
 | 
			
		||||
    @status =
 | 
			
		||||
      if merge_request.merge_when_build_succeeds
 | 
			
		||||
        :merge_when_build_succeeds
 | 
			
		||||
      if merge_request.merge_when_pipeline_succeeds
 | 
			
		||||
        :merge_when_pipeline_succeeds
 | 
			
		||||
      else
 | 
			
		||||
        # Only MRs that can be merged end in this action
 | 
			
		||||
        # MR can be already picked up for merge / merged already or can be waiting for worker to be picked up
 | 
			
		||||
| 
						 | 
				
			
			@ -673,8 +674,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 | 
			
		|||
    @merge_request.ensure_ref_fetched
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def merge_when_build_succeeds_active?
 | 
			
		||||
    params[:merge_when_build_succeeds].present? &&
 | 
			
		||||
  def merge_when_pipeline_succeeds_active?
 | 
			
		||||
    params[:merge_when_pipeline_succeeds].present? &&
 | 
			
		||||
      @merge_request.head_pipeline && @merge_request.head_pipeline.active?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -148,17 +148,10 @@ class Projects::NotesController < Projects::ApplicationController
 | 
			
		|||
 | 
			
		||||
  def note_json(note)
 | 
			
		||||
    attrs = {
 | 
			
		||||
      award: false,
 | 
			
		||||
      id: note.id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if note.is_a?(AwardEmoji)
 | 
			
		||||
      attrs.merge!(
 | 
			
		||||
        valid:  note.valid?,
 | 
			
		||||
        award:  true,
 | 
			
		||||
        name:   note.name
 | 
			
		||||
      )
 | 
			
		||||
    elsif note.persisted?
 | 
			
		||||
    if note.persisted?
 | 
			
		||||
      Banzai::NoteRenderer.render([note], @project, current_user)
 | 
			
		||||
 | 
			
		||||
      attrs.merge!(
 | 
			
		||||
| 
						 | 
				
			
			@ -198,7 +191,7 @@ class Projects::NotesController < Projects::ApplicationController
 | 
			
		|||
      )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    attrs[:commands_changes] = note.commands_changes unless attrs[:award]
 | 
			
		||||
    attrs[:commands_changes] = note.commands_changes
 | 
			
		||||
    attrs
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -314,7 +314,7 @@ class ProjectsController < Projects::ApplicationController
 | 
			
		|||
      :name,
 | 
			
		||||
      :namespace_id,
 | 
			
		||||
      :only_allow_merge_if_all_discussions_are_resolved,
 | 
			
		||||
      :only_allow_merge_if_build_succeeds,
 | 
			
		||||
      :only_allow_merge_if_pipeline_succeeds,
 | 
			
		||||
      :path,
 | 
			
		||||
      :public_builds,
 | 
			
		||||
      :request_access_enabled,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,9 @@
 | 
			
		|||
# `DashboardController#show`, which is the default.
 | 
			
		||||
class RootController < Dashboard::ProjectsController
 | 
			
		||||
  skip_before_action :authenticate_user!, only: [:index]
 | 
			
		||||
  before_action :redirect_to_custom_dashboard, only: [:index]
 | 
			
		||||
 | 
			
		||||
  before_action :redirect_unlogged_user, if: -> { current_user.nil? }
 | 
			
		||||
  before_action :redirect_logged_user, if: -> { current_user.present? }
 | 
			
		||||
 | 
			
		||||
  def index
 | 
			
		||||
    super
 | 
			
		||||
| 
						 | 
				
			
			@ -16,23 +18,38 @@ class RootController < Dashboard::ProjectsController
 | 
			
		|||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def redirect_to_custom_dashboard
 | 
			
		||||
    return redirect_to new_user_session_path unless current_user
 | 
			
		||||
  def redirect_unlogged_user
 | 
			
		||||
    if redirect_to_home_page_url?
 | 
			
		||||
      redirect_to(current_application_settings.home_page_url)
 | 
			
		||||
    else
 | 
			
		||||
      redirect_to(new_user_session_path)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def redirect_logged_user
 | 
			
		||||
    case current_user.dashboard
 | 
			
		||||
    when 'stars'
 | 
			
		||||
      flash.keep
 | 
			
		||||
      redirect_to starred_dashboard_projects_path
 | 
			
		||||
      redirect_to(starred_dashboard_projects_path)
 | 
			
		||||
    when 'project_activity'
 | 
			
		||||
      redirect_to activity_dashboard_path
 | 
			
		||||
      redirect_to(activity_dashboard_path)
 | 
			
		||||
    when 'starred_project_activity'
 | 
			
		||||
      redirect_to activity_dashboard_path(filter: 'starred')
 | 
			
		||||
      redirect_to(activity_dashboard_path(filter: 'starred'))
 | 
			
		||||
    when 'groups'
 | 
			
		||||
      redirect_to dashboard_groups_path
 | 
			
		||||
      redirect_to(dashboard_groups_path)
 | 
			
		||||
    when 'todos'
 | 
			
		||||
      redirect_to dashboard_todos_path
 | 
			
		||||
    else
 | 
			
		||||
      return
 | 
			
		||||
      redirect_to(dashboard_todos_path)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def redirect_to_home_page_url?
 | 
			
		||||
    # If user is not signed-in and tries to access root_path - redirect him to landing page
 | 
			
		||||
    # Don't redirect to the default URL to prevent endless redirections
 | 
			
		||||
    return false unless current_application_settings.home_page_url.present?
 | 
			
		||||
 | 
			
		||||
    home_page_url = current_application_settings.home_page_url.chomp('/')
 | 
			
		||||
    root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]
 | 
			
		||||
 | 
			
		||||
    root_urls.exclude?(home_page_url)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,4 +15,11 @@ module BuildsHelper
 | 
			
		|||
      log_state: @build.trace_with_state[:state].to_s
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def build_failed_issue_options
 | 
			
		||||
    {
 | 
			
		||||
      title: "Build Failed ##{@build.id}",
 | 
			
		||||
      description: namespace_project_build_url(@project.namespace, @project, @build)
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ module ButtonHelper
 | 
			
		|||
    title = data[:title] || 'Copy to clipboard'
 | 
			
		||||
    data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
 | 
			
		||||
    content_tag :button,
 | 
			
		||||
      icon('clipboard'),
 | 
			
		||||
      icon('clipboard', 'aria-hidden': 'true'),
 | 
			
		||||
      class: "btn #{css_class}",
 | 
			
		||||
      data: data,
 | 
			
		||||
      type: :button,
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ module ButtonHelper
 | 
			
		|||
 | 
			
		||||
    content_tag (append_link ? :a : :span), protocol,
 | 
			
		||||
      class: klass,
 | 
			
		||||
      href: (project.http_url_to_repo if append_link),
 | 
			
		||||
      href: (project.http_url_to_repo(current_user) if append_link),
 | 
			
		||||
      data: {
 | 
			
		||||
        html: true,
 | 
			
		||||
        placement: placement,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
module IssuablesHelper
 | 
			
		||||
  def sidebar_gutter_toggle_icon
 | 
			
		||||
    sidebar_gutter_collapsed? ? icon('angle-double-left') : icon('angle-double-right')
 | 
			
		||||
    sidebar_gutter_collapsed? ? icon('angle-double-left', { 'aria-hidden': 'true' }) : icon('angle-double-right', { 'aria-hidden': 'true' })
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def sidebar_gutter_collapsed_class
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ module IssuablesHelper
 | 
			
		|||
        field_name: 'issuable_template',
 | 
			
		||||
        selected: selected_template(issuable),
 | 
			
		||||
        project_path: ref_project.path,
 | 
			
		||||
        namespace_path: ref_project.namespace.path
 | 
			
		||||
        namespace_path: ref_project.namespace.full_path
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,7 +146,7 @@ module MergeRequestsHelper
 | 
			
		|||
 | 
			
		||||
  def merge_params(merge_request)
 | 
			
		||||
    {
 | 
			
		||||
      merge_when_build_succeeds: true,
 | 
			
		||||
      merge_when_pipeline_succeeds: true,
 | 
			
		||||
      should_remove_source_branch: true,
 | 
			
		||||
      sha: merge_request.diff_head_sha
 | 
			
		||||
    }.merge(merge_params_ee(merge_request))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -97,7 +97,7 @@ module MilestonesHelper
 | 
			
		|||
 | 
			
		||||
  def milestone_date_range(milestone)
 | 
			
		||||
    if milestone.start_date && milestone.due_date
 | 
			
		||||
      "#{milestone.start_date.to_s(:medium)} - #{milestone.due_date.to_s(:medium)}"
 | 
			
		||||
      "#{milestone.start_date.to_s(:medium)}–#{milestone.due_date.to_s(:medium)}"
 | 
			
		||||
    elsif milestone.due_date
 | 
			
		||||
      if milestone.due_date.past?
 | 
			
		||||
        "expired on #{milestone.due_date.to_s(:medium)}"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -241,7 +241,7 @@ module ProjectsHelper
 | 
			
		|||
    when 'ssh'
 | 
			
		||||
      project.ssh_url_to_repo
 | 
			
		||||
    else
 | 
			
		||||
      project.http_url_to_repo
 | 
			
		||||
      project.http_url_to_repo(current_user)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -76,6 +76,12 @@ class ApplicationSetting < ActiveRecord::Base
 | 
			
		|||
            presence: true,
 | 
			
		||||
            numericality: { only_integer: true, greater_than: 0 }
 | 
			
		||||
 | 
			
		||||
  validates :max_artifacts_size,
 | 
			
		||||
            presence: true,
 | 
			
		||||
            numericality: { only_integer: true, greater_than: 0 }
 | 
			
		||||
 | 
			
		||||
  validates :default_artifacts_expire_in, presence: true, duration: true
 | 
			
		||||
 | 
			
		||||
  validates :container_registry_token_expire_delay,
 | 
			
		||||
            presence: true,
 | 
			
		||||
            numericality: { only_integer: true, greater_than: 0 }
 | 
			
		||||
| 
						 | 
				
			
			@ -168,6 +174,7 @@ class ApplicationSetting < ActiveRecord::Base
 | 
			
		|||
      after_sign_up_text: nil,
 | 
			
		||||
      akismet_enabled: false,
 | 
			
		||||
      container_registry_token_expire_delay: 5,
 | 
			
		||||
      default_artifacts_expire_in: '30 days',
 | 
			
		||||
      default_branch_protection: Settings.gitlab['default_branch_protection'],
 | 
			
		||||
      default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
 | 
			
		||||
      default_projects_limit: Settings.gitlab['default_projects_limit'],
 | 
			
		||||
| 
						 | 
				
			
			@ -201,9 +208,9 @@ class ApplicationSetting < ActiveRecord::Base
 | 
			
		|||
      sign_in_text: nil,
 | 
			
		||||
      signin_enabled: Settings.gitlab['signin_enabled'],
 | 
			
		||||
      signup_enabled: Settings.gitlab['signup_enabled'],
 | 
			
		||||
      terminal_max_session_time: 0,
 | 
			
		||||
      two_factor_grace_period: 48,
 | 
			
		||||
      user_default_external: false,
 | 
			
		||||
      terminal_max_session_time: 0
 | 
			
		||||
      user_default_external: false
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -215,6 +222,14 @@ class ApplicationSetting < ActiveRecord::Base
 | 
			
		|||
    create(defaults)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.human_attribute_name(attr, _options = {})
 | 
			
		||||
    if attr == :default_artifacts_expire_in
 | 
			
		||||
      'Default artifacts expiration'
 | 
			
		||||
    else
 | 
			
		||||
      super
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def home_page_url_column_exist
 | 
			
		||||
    ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,15 +55,6 @@ module Ci
 | 
			
		|||
        pending.unstarted.order('created_at ASC').first
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def create_from(build)
 | 
			
		||||
        new_build = build.dup
 | 
			
		||||
        new_build.status = 'pending'
 | 
			
		||||
        new_build.runner_id = nil
 | 
			
		||||
        new_build.trigger_request_id = nil
 | 
			
		||||
        new_build.token = nil
 | 
			
		||||
        new_build.save
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def retry(build, current_user)
 | 
			
		||||
        Ci::RetryBuildService
 | 
			
		||||
          .new(build.project, current_user)
 | 
			
		||||
| 
						 | 
				
			
			@ -484,7 +475,7 @@ module Ci
 | 
			
		|||
    def artifacts_expire_in=(value)
 | 
			
		||||
      self.artifacts_expire_at =
 | 
			
		||||
        if value
 | 
			
		||||
          Time.now + ChronicDuration.parse(value)
 | 
			
		||||
          ChronicDuration.parse(value)&.seconds&.from_now
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
class Uniquify
 | 
			
		||||
  # Return a version of the given 'base' string that is unique
 | 
			
		||||
  # by appending a counter to it. Uniqueness is determined by
 | 
			
		||||
  # repeated calls to the passed block.
 | 
			
		||||
  #
 | 
			
		||||
  # If `base` is a function/proc, we expect that calling it with a
 | 
			
		||||
  # candidate counter returns a string to test/return.
 | 
			
		||||
  def string(base)
 | 
			
		||||
    @base = base
 | 
			
		||||
    @counter = nil
 | 
			
		||||
 | 
			
		||||
    increment_counter! while yield(base_string)
 | 
			
		||||
    base_string
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def base_string
 | 
			
		||||
    if @base.respond_to?(:call)
 | 
			
		||||
      @base.call(@counter)
 | 
			
		||||
    else
 | 
			
		||||
      "#{@base}#{@counter}"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def increment_counter!
 | 
			
		||||
    @counter ||= 0
 | 
			
		||||
    @counter += 1
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ class Event < ActiveRecord::Base
 | 
			
		|||
  scope :code_push, -> { where(action: PUSHED) }
 | 
			
		||||
 | 
			
		||||
  scope :in_projects, ->(projects) do
 | 
			
		||||
    where(project_id: projects).recent
 | 
			
		||||
    where(project_id: projects.pluck(:id)).recent
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  scope :with_associations, -> { includes(:author, :project, project: :namespace).preload(:target) }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,17 +91,13 @@ class MergeRequest < ActiveRecord::Base
 | 
			
		|||
    around_transition do |merge_request, transition, block|
 | 
			
		||||
      Gitlab::Timeless.timeless(merge_request, &block)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    after_transition unchecked: :cannot_be_merged do |merge_request, transition|
 | 
			
		||||
      TodoService.new.merge_request_became_unmergeable(merge_request)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  validates :source_project, presence: true, unless: [:allow_broken, :importing?, :closed_without_fork?]
 | 
			
		||||
  validates :source_branch, presence: true
 | 
			
		||||
  validates :target_project, presence: true
 | 
			
		||||
  validates :target_branch, presence: true
 | 
			
		||||
  validates :merge_user, presence: true, if: :merge_when_build_succeeds?, unless: :importing?
 | 
			
		||||
  validates :merge_user, presence: true, if: :merge_when_pipeline_succeeds?, unless: :importing?
 | 
			
		||||
  validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?]
 | 
			
		||||
  validate :validate_fork, unless: :closed_without_fork?
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -440,7 +436,7 @@ class MergeRequest < ActiveRecord::Base
 | 
			
		|||
    true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def can_cancel_merge_when_build_succeeds?(current_user)
 | 
			
		||||
  def can_cancel_merge_when_pipeline_succeeds?(current_user)
 | 
			
		||||
    can_be_merged_by?(current_user) || self.author == current_user
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -648,10 +644,10 @@ class MergeRequest < ActiveRecord::Base
 | 
			
		|||
    message.join("\n\n")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def reset_merge_when_build_succeeds
 | 
			
		||||
    return unless merge_when_build_succeeds?
 | 
			
		||||
  def reset_merge_when_pipeline_succeeds
 | 
			
		||||
    return unless merge_when_pipeline_succeeds?
 | 
			
		||||
 | 
			
		||||
    self.merge_when_build_succeeds = false
 | 
			
		||||
    self.merge_when_pipeline_succeeds = false
 | 
			
		||||
    self.merge_user = nil
 | 
			
		||||
    if merge_params
 | 
			
		||||
      merge_params.delete('should_remove_source_branch')
 | 
			
		||||
| 
						 | 
				
			
			@ -710,7 +706,7 @@ class MergeRequest < ActiveRecord::Base
 | 
			
		|||
  end
 | 
			
		||||
 | 
			
		||||
  def mergeable_ci_state?
 | 
			
		||||
    return true unless project.only_allow_merge_if_build_succeeds?
 | 
			
		||||
    return true unless project.only_allow_merge_if_pipeline_succeeds?
 | 
			
		||||
 | 
			
		||||
    !head_pipeline || head_pipeline.success? || head_pipeline.skipped?
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,14 +99,8 @@ class Namespace < ActiveRecord::Base
 | 
			
		|||
      # Work around that by setting their username to "blank", followed by a counter.
 | 
			
		||||
      path = "blank" if path.blank?
 | 
			
		||||
 | 
			
		||||
      counter = 0
 | 
			
		||||
      base = path
 | 
			
		||||
      while Namespace.find_by_path_or_name(path)
 | 
			
		||||
        counter += 1
 | 
			
		||||
        path = "#{base}#{counter}"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      path
 | 
			
		||||
      uniquify = Uniquify.new
 | 
			
		||||
      uniquify.string(path) { |s| Namespace.find_by_path_or_name(s) }
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -231,10 +231,6 @@ class Note < ActiveRecord::Base
 | 
			
		|||
    note =~ /\A#{Banzai::Filter::EmojiFilter.emoji_pattern}\s?\Z/
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def award_emoji_name
 | 
			
		||||
    note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def to_ability_name
 | 
			
		||||
    for_personal_snippet? ? 'personal_snippet' : noteable_type.underscore
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ class Project < ActiveRecord::Base
 | 
			
		|||
 | 
			
		||||
  extend Gitlab::ConfigHelper
 | 
			
		||||
 | 
			
		||||
  class BoardLimitExceeded < StandardError; end
 | 
			
		||||
  BoardLimitExceeded = Class.new(StandardError)
 | 
			
		||||
 | 
			
		||||
  NUMBER_OF_PERMITTED_BOARDS = 1
 | 
			
		||||
  UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze
 | 
			
		||||
| 
						 | 
				
			
			@ -359,7 +359,7 @@ class Project < ActiveRecord::Base
 | 
			
		|||
    end
 | 
			
		||||
 | 
			
		||||
    def reference_pattern
 | 
			
		||||
      name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR
 | 
			
		||||
      name_pattern = Gitlab::Regex::FULL_NAMESPACE_REGEX_STR
 | 
			
		||||
 | 
			
		||||
      %r{
 | 
			
		||||
        ((?<namespace>#{name_pattern})\/)?
 | 
			
		||||
| 
						 | 
				
			
			@ -847,10 +847,6 @@ class Project < ActiveRecord::Base
 | 
			
		|||
    gitlab_shell.url_to_repo(path_with_namespace)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def namespace_dir
 | 
			
		||||
    namespace.try(:path) || ''
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def repo_exists?
 | 
			
		||||
    @repo_exists ||= repository.exists?
 | 
			
		||||
  rescue
 | 
			
		||||
| 
						 | 
				
			
			@ -873,8 +869,14 @@ class Project < ActiveRecord::Base
 | 
			
		|||
    url_to_repo
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def http_url_to_repo
 | 
			
		||||
    "#{web_url}.git"
 | 
			
		||||
  def http_url_to_repo(user = nil)
 | 
			
		||||
    url = web_url
 | 
			
		||||
 | 
			
		||||
    if user
 | 
			
		||||
      url.sub!(%r{\Ahttps?://}) { |protocol| "#{protocol}#{user.username}@" }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    "#{url}.git"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Check if current branch name is marked as protected in the system
 | 
			
		||||
| 
						 | 
				
			
			@ -899,8 +901,8 @@ class Project < ActiveRecord::Base
 | 
			
		|||
 | 
			
		||||
  def rename_repo
 | 
			
		||||
    path_was = previous_changes['path'].first
 | 
			
		||||
    old_path_with_namespace = File.join(namespace_dir, path_was)
 | 
			
		||||
    new_path_with_namespace = File.join(namespace_dir, path)
 | 
			
		||||
    old_path_with_namespace = File.join(namespace.full_path, path_was)
 | 
			
		||||
    new_path_with_namespace = File.join(namespace.full_path, path)
 | 
			
		||||
 | 
			
		||||
    Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,8 +33,15 @@ class ProjectGroupLink < ActiveRecord::Base
 | 
			
		|||
  private
 | 
			
		||||
 | 
			
		||||
  def different_group
 | 
			
		||||
    if self.group && self.project && self.project.group == self.group
 | 
			
		||||
      errors.add(:base, "Project cannot be shared with the project it is in.")
 | 
			
		||||
    return unless self.group && self.project
 | 
			
		||||
 | 
			
		||||
    project_group = self.project.group
 | 
			
		||||
    return unless project_group
 | 
			
		||||
 | 
			
		||||
    group_ids = project_group.ancestors.map(&:id).push(project_group.id)
 | 
			
		||||
 | 
			
		||||
    if group_ids.include?(self.group.id)
 | 
			
		||||
      errors.add(:base, "Project cannot be shared with the group it is in or one of its ancestors.")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,8 +17,8 @@ class MattermostService < ChatNotificationService
 | 
			
		|||
    <ol>
 | 
			
		||||
      <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks">Enable incoming webhooks</a> in your Mattermost installation.</li>
 | 
			
		||||
      <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#creating-integrations-using-incoming-webhooks">Add an incoming webhook</a> in your Mattermost team. The default channel can be overridden for each event.</li>
 | 
			
		||||
      <li>Paste the webhook <strong>URL</strong> into the field bellow. </li>
 | 
			
		||||
      <li>Select events below to enable notifications. The channel and username are optional. </li>
 | 
			
		||||
      <li>Paste the webhook <strong>URL</strong> into the field below.</li>
 | 
			
		||||
      <li>Select events below to enable notifications. The <strong>Channel handle</strong> and <strong>Username</strong> fields are optional.</li>
 | 
			
		||||
    </ol>'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -28,14 +28,14 @@ class MattermostService < ChatNotificationService
 | 
			
		|||
 | 
			
		||||
  def default_fields
 | 
			
		||||
    [
 | 
			
		||||
      { type: 'text', name: 'webhook', placeholder: 'http://mattermost_host/hooks/...' },
 | 
			
		||||
      { type: 'text', name: 'username', placeholder: 'username' },
 | 
			
		||||
      { type: 'text', name: 'webhook', placeholder: 'e.g. http://mattermost_host/hooks/…' },
 | 
			
		||||
      { type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
 | 
			
		||||
      { type: 'checkbox', name: 'notify_only_broken_builds' },
 | 
			
		||||
      { type: 'checkbox', name: 'notify_only_broken_pipelines' },
 | 
			
		||||
    ]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def default_channel_placeholder
 | 
			
		||||
    "town-square"
 | 
			
		||||
    "Channel handle (e.g. town-square)"
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue