Commit 4f891549 authored by barthc's avatar barthc

Merge branch 'master' into fix-double-query-string-url

parents 9b6c7e36 3764f118

Too many changes to show.

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

We’re closing our issue tracker on GitHub so we can focus on the GitLab.com project and respond to issues more quickly.
We encourage you to open an issue on the [GitLab.com issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues). You can log into GitLab.com using your GitHub account.
Thank you for taking the time to contribute back to GitLab!
Please open a merge request [on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests), we look forward to reviewing your contribution! You can log into GitLab.com using your GitHub account.
This diff is collapsed.
......@@ -13,7 +13,8 @@ AllCops:
# Exclude some GitLab files
Exclude:
- 'vendor/**/*'
- 'db/**/*'
- 'db/*'
- 'db/fixtures/**/*'
- 'tmp/**/*'
- 'bin/**/*'
- 'lib/backup/**/*'
......@@ -348,7 +349,7 @@ Style/MultilineArrayBraceLayout:
# Avoid multi-line chains of blocks.
Style/MultilineBlockChain:
Enabled: false
Enabled: true
# Ensures newlines after multiline block do statements.
Style/MultilineBlockLayout:
......@@ -1088,6 +1089,9 @@ Rails/TimeZone:
Rails/Validation:
Enabled: false
Rails/UniqBeforePluck:
Enabled: false
##################### RSpec ##################################
# Check that instances are not being stubbed globally.
......
This diff is collapsed.
......@@ -405,6 +405,7 @@ description area. Copy-paste it to retain the markdown format.
entire line to follow it. This prevents linting tools from generating warnings.
- Don't touch neighbouring lines. As an exception, automatic mass
refactoring modifications may leave style non-compliant.
1. If the merge request adds any new libraries (gems, JavaScript libraries, etc.), they should conform to our [Licensing guidelines][license-finder-doc]. See the instructions in that document for help if your MR fails the "license-finder" test with a "Dependencies that need approval" error.
## Changes for Stable Releases
......@@ -531,3 +532,4 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
[license-finder-doc]: doc/development/licensing.md
......@@ -38,20 +38,21 @@ gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt'
# Spam and anti-bot protection
gem 'recaptcha', require: 'recaptcha/rails'
gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails'
gem 'akismet', '~> 2.0'
# Two-factor authentication
gem 'devise-two-factor', '~> 3.0.0'
gem 'rqrcode-rails3', '~> 0.1.7'
gem 'attr_encrypted', '~> 3.0.0'
gem 'u2f', '~> 0.2.1'
# Browser detection
gem "browser", '~> 1.0.0'
gem "browser", '~> 2.0.3'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '~> 10.0'
gem "gitlab_git", '~> 10.2'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......@@ -85,6 +86,7 @@ gem 'dropzonejs-rails', '~> 0.7.1'
# for backups
gem 'fog-aws', '~> 0.9'
gem 'fog-azure', '~> 0.0'
gem 'fog-core', '~> 1.40'
gem 'fog-local', '~> 0.3'
gem 'fog-google', '~> 0.3'
......@@ -110,7 +112,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.10.1'
gem 'rouge', '~> 1.11'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
......@@ -208,6 +210,9 @@ gem 'mousetrap-rails', '~> 1.4.6'
# Detect and convert string character encoding
gem 'charlock_holmes', '~> 0.7.3'
# Parse duration
gem 'chronic_duration', '~> 0.10.6'
gem "sass-rails", '~> 5.0.0'
gem "coffee-rails", '~> 4.1.0'
gem "uglifier", '~> 2.7.2'
......@@ -216,13 +221,12 @@ gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.2'
gem 'font-awesome-rails', '~> 4.6.1'
gem 'gitlab_emoji', '~> 0.3.0'
gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0'
gem 'jquery-ui-rails', '~> 5.0.0'
gem 'raphael-rails', '~> 2.1.2'
gem 'request_store', '~> 1.3.0'
gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1'
......@@ -243,7 +247,7 @@ end
group :development do
gem "foreman"
gem 'brakeman', '~> 3.2.0', require: false
gem 'brakeman', '~> 3.3.0', require: false
gem 'letter_opener_web', '~> 1.3.0'
gem 'quiet_assets', '~> 1.0.2'
......@@ -305,6 +309,9 @@ group :development, :test do
gem 'bundler-audit', require: false
gem 'benchmark-ips', require: false
gem "license_finder", require: false
gem 'knapsack'
end
group :test do
......
......@@ -50,7 +50,7 @@ GEM
after_commit_queue (1.3.0)
activerecord (>= 3.0)
akismet (2.0.0)
allocations (1.0.4)
allocations (1.0.5)
arel (6.0.3)
asana (0.4.0)
faraday (~> 0.9)
......@@ -70,6 +70,21 @@ GEM
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
azure (0.7.5)
addressable (~> 2.3)
azure-core (~> 0.1)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
json (~> 1.8)
mime-types (>= 1, < 3.0)
nokogiri (~> 1.6)
systemu (~> 2.6)
thor (~> 0.19)
uuid (~> 2.0)
azure-core (0.1.2)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
nokogiri (~> 1.6)
babosa (1.0.2)
base32 (0.3.2)
bcrypt (3.1.11)
......@@ -82,17 +97,8 @@ GEM
bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4)
brakeman (3.2.1)
erubis (~> 2.6)
haml (>= 3.0, < 5.0)
highline (>= 1.6.20, < 2.0)
ruby2ruby (~> 2.3.0)
ruby_parser (~> 3.8.1)
safe_yaml (>= 1.0)
sass (~> 3.0)
slim (>= 1.3.6, < 4.0)
terminal-table (~> 1.4)
browser (1.0.1)
brakeman (3.3.2)
browser (2.0.3)
builder (3.2.2)
bullet (5.0.0)
activesupport (>= 3.0.0)
......@@ -118,6 +124,8 @@ GEM
mime-types (>= 1.16)
cause (0.1)
charlock_holmes (0.7.3)
chronic_duration (0.10.6)
numerizer (~> 0.1.1)
chunky_png (1.3.5)
cliver (0.3.2)
coderay (1.1.0)
......@@ -213,6 +221,11 @@ GEM
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
fog-azure (0.0.2)
azure (~> 0.6)
fog-core (~> 1.27)
fog-json (~> 1.0)
fog-xml (~> 0.1)
fog-core (1.40.0)
builder
excon (~> 0.49)
......@@ -233,7 +246,7 @@ GEM
fog-xml (0.1.2)
fog-core
nokogiri (~> 1.5, >= 1.5.11)
font-awesome-rails (4.5.0.1)
font-awesome-rails (4.6.1.0)
railties (>= 3.2, < 5.1)
foreman (0.78.0)
thor (~> 0.19.1)
......@@ -264,7 +277,7 @@ GEM
posix-spawn (~> 0.3)
gitlab_emoji (0.3.1)
gemojione (~> 2.2, >= 2.2.1)
gitlab_git (10.1.0)
gitlab_git (10.2.0)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
......@@ -318,7 +331,6 @@ GEM
hashie (3.4.3)
health_check (1.5.1)
rails (>= 2.3.0)
highline (1.7.8)
hipchat (1.5.2)
httparty
mimemagic
......@@ -358,6 +370,9 @@ GEM
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.10.0)
knapsack (1.11.0)
rake
timecop (>= 0.1.0)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.4.1)
......@@ -366,6 +381,12 @@ GEM
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
license_finder (2.1.0)
bundler
httparty
rubyzip
thor
xml-simple
licensee (8.0.0)
rugged (>= 0.24b)
listen (3.0.5)
......@@ -379,9 +400,9 @@ GEM
mime-types (>= 1.16, < 4)
mail_room (0.7.0)
method_source (0.8.2)
mime-types (2.99.1)
mime-types (2.99.2)
mimemagic (0.3.0)
mini_portile2 (2.0.0)
mini_portile2 (2.1.0)
minitest (5.7.0)
mousetrap-rails (1.4.6)
multi_json (1.11.2)
......@@ -392,8 +413,10 @@ GEM
net-ldap (0.12.1)
net-ssh (3.0.1)
newrelic_rpm (3.14.1.311)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
numerizer (0.1.1)
oauth (0.4.7)
oauth2 (1.0.0)
faraday (>= 0.8, < 0.10)
......@@ -465,6 +488,7 @@ GEM
parser (2.3.1.0)
ast (~> 2.2)
pg (0.18.4)
pkg-config (1.1.7)
poltergeist (1.9.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
......@@ -532,7 +556,6 @@ GEM
rainbow (2.1.0)
raindrops (0.15.0)
rake (10.5.0)
raphael-rails (2.1.2)
rb-fsevent (0.9.6)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
......@@ -540,7 +563,7 @@ GEM
debugger-ruby_core_source (~> 1.3)
rdoc (3.12.2)
json (~> 1.4)
recaptcha (1.0.2)
recaptcha (3.0.0)
json
redcarpet (3.3.3)
redis (3.3.0)
......@@ -569,7 +592,7 @@ GEM
railties (>= 4.2.0, < 5.1)
rinku (1.7.3)
rotp (2.1.2)
rouge (1.10.1)
rouge (1.11.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
......@@ -611,19 +634,17 @@ GEM
ruby-saml (1.1.2)
nokogiri (>= 1.5.10)
uuid (~> 2.3)
ruby2ruby (2.3.0)
ruby_parser (~> 3.1)
sexp_processor (~> 4.0)
ruby_parser (3.8.1)
ruby_parser (3.8.2)
sexp_processor (~> 4.1)
rubyntlm (0.5.2)
rubypants (0.2.0)
rubyzip (1.2.0)
rufus-scheduler (3.1.10)
rugged (0.24.0)
safe_yaml (1.0.4)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
sass (3.4.21)
sass (3.4.22)
sass-rails (5.0.4)
railties (>= 4.0.0, < 5.0)
sass (~> 3.1)
......@@ -672,9 +693,6 @@ GEM
tilt (>= 1.3, < 3)
six (0.2.0)
slack-notifier (1.2.1)
slim (3.0.6)
temple (~> 0.7.3)
tilt (>= 1.3.3, < 2.1)
slop (3.6.0)
spinach (0.8.10)
colorize
......@@ -715,10 +733,8 @@ GEM
railties (>= 3.2.5, < 6)
teaspoon-jasmine (2.2.0)
teaspoon (>= 1.0.0)
temple (0.7.6)
term-ansicolor (1.3.2)
tins (~> 1.0)
terminal-table (1.5.2)
test_after_commit (0.4.2)
activerecord (>= 3.2)
thin (1.6.4)
......@@ -727,7 +743,8 @@ GEM
rack (~> 1.0)
thor (0.19.1)
thread_safe (0.3.5)
tilt (2.0.2)
tilt (2.0.5)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
tinder (1.10.1)
eventmachine (~> 1.0)
......@@ -747,6 +764,7 @@ GEM
simple_oauth (~> 0.1.4)
tzinfo (1.2.2)
thread_safe (~> 0.1)
u2f (0.2.1)
uglifier (2.7.2)
execjs (>= 0.3.0)
json (>= 1.8.0)
......@@ -788,6 +806,7 @@ GEM
builder
expression_parser
rinku
xml-simple (1.1.5)
xpath (2.0.0)
nokogiri (~> 1.3)
......@@ -813,8 +832,8 @@ DEPENDENCIES
better_errors (~> 1.0.1)
binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0)
brakeman (~> 3.2.0)
browser (~> 1.0.0)
brakeman (~> 3.3.0)
browser (~> 2.0.3)
bullet
bundler-audit
byebug
......@@ -822,6 +841,7 @@ DEPENDENCIES
capybara-screenshot (~> 1.0.0)
carrierwave (~> 0.10.0)
charlock_holmes (~> 0.7.3)
chronic_duration (~> 0.10.6)
coffee-rails (~> 4.1.0)
connection_pool (~> 2.0)
coveralls (~> 0.8.2)
......@@ -841,11 +861,12 @@ DEPENDENCIES
flay
flog
fog-aws (~> 0.9)
fog-azure (~> 0.0)
fog-core (~> 1.40)
fog-google (~> 0.3)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
font-awesome-rails (~> 4.2)
font-awesome-rails (~> 4.6.1)
foreman
fuubar (~> 2.0.0)
gemnasium-gitlab-service (~> 0.2)
......@@ -853,7 +874,7 @@ DEPENDENCIES
github-markup (~> 1.3.1)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_emoji (~> 0.3.0)
gitlab_git (~> 10.0)
gitlab_git (~> 10.2)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.1.0)
......@@ -873,7 +894,9 @@ DEPENDENCIES
jquery-ui-rails (~> 5.0.0)
jwt
kaminari (~> 0.17.0)
knapsack
letter_opener_web (~> 1.3.0)
license_finder
licensee (~> 8.0.0)
loofah (~> 2.0.3)
mail_room (~> 0.7)
......@@ -914,10 +937,9 @@ DEPENDENCIES
rails (= 4.2.6)
rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0)
raphael-rails (~> 2.1.2)
rblineprof
rdoc (~> 3.6)
recaptcha
recaptcha (~> 3.0)
redcarpet (~> 3.3.3)
redis (~> 3.2)
redis-namespace
......@@ -925,7 +947,7 @@ DEPENDENCIES
request_store (~> 1.3.0)
rerun (~> 0.11.0)
responders (~> 2.0)
rouge (~> 1.10.1)
rouge (~> 1.11)
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.4.0)
rspec-retry
......@@ -963,6 +985,7 @@ DEPENDENCIES
thin (~> 1.6.1)
tinder (~> 1.10.0)
turbolinks (~> 2.5.0)
u2f (~> 0.2.1)
uglifier (~> 2.7.2)
underscore-rails (~> 1.8.0)
unf (~> 0.1.4)
......@@ -975,4 +998,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.12.4
1.12.5
......@@ -8,3 +8,5 @@ relative_url_conf = File.expand_path('../config/initializers/relative_url', __FI
require relative_url_conf if File.exist?("#{relative_url_conf}.rb")
Gitlab::Application.load_tasks
Knapsack.load_tasks if defined?(Knapsack)
class @LabelManager
errorMessage: 'Unable to update label prioritization at this time'
constructor: (opts = {}) ->
# Defaults
{
@togglePriorityButton = $('.js-toggle-priority')
@prioritizedLabels = $('.js-prioritized-labels')
@otherLabels = $('.js-other-labels')
} = opts
@prioritizedLabels.sortable(
items: 'li'
placeholder: 'list-placeholder'
axis: 'y'
update: @onPrioritySortUpdate.bind(@)
)
@bindEvents()
bindEvents: ->
@togglePriorityButton.on 'click', @, @onTogglePriorityClick
onTogglePriorityClick: (e) ->
e.preventDefault()
_this = e.data
$btn = $(e.currentTarget)
$label = $("##{$btn.data('domId')}")
action = if $btn.parents('.js-prioritized-labels').length then 'remove' else 'add'
_this.toggleLabelPriority($label, action)
toggleLabelPriority: ($label, action, persistState = true) ->
_this = @
url = $label.find('.js-toggle-priority').data 'url'
$target = @prioritizedLabels
$from = @otherLabels
# Optimistic update
if action is 'remove'
$target = @otherLabels
$from = @prioritizedLabels
if $from.find('li').length is 1
$from.find('.empty-message').removeClass('hidden')
if not $target.find('li').length
$target.find('.empty-message').addClass('hidden')
$label.detach().appendTo($target)
# Return if we are not persisting state
return unless persistState
if action is 'remove'
xhr = $.ajax url: url, type: 'DELETE'
# Restore empty message
$from.find('.empty-message').removeClass('hidden') unless $from.find('li').length
else
xhr = @savePrioritySort($label, action)
xhr.fail @rollbackLabelPosition.bind(@, $label, action)
onPrioritySortUpdate: ->
xhr = @savePrioritySort()
xhr.fail ->
new Flash(@errorMessage, 'alert')
savePrioritySort: () ->
$.post
url: @prioritizedLabels.data('url')
data:
label_ids: @getSortedLabelsIds()
rollbackLabelPosition: ($label, originalAction)->
action = if originalAction is 'remove' then 'add' else 'remove'
@toggleLabelPriority($label, action, false)
new Flash(@errorMessage, 'alert')
getSortedLabelsIds: ->
sortedIds = []
@prioritizedLabels.find('li').each ->
sortedIds.push $(@).data 'id'
sortedIds
class @Activities
constructor: ->
Pager.init 20, true
Pager.init 20, true, false, @updateTooltips
$(".event-filter-link").on "click", (event) =>
event.preventDefault()
@toggleFilter($(event.currentTarget))
@reloadActivities()
updateTooltips: ->
gl.utils.localTimeAgo($('.js-timeago', '#activity'))
reloadActivities: ->
$(".content_list").html ''
Pager.init 20, true
......
......@@ -4,7 +4,7 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require jquery
#= require jquery2
#= require jquery-ui/autocomplete
#= require jquery-ui/datepicker
#= require jquery-ui/draggable
......@@ -32,11 +32,6 @@
#= require bootstrap/tooltip
#= require bootstrap/popover
#= require select2
#= require raphael
#= require g.raphael
#= require g.bar
#= require Chart
#= require branch-graph
#= require ace/ace
#= require ace/ext-searchbox
#= require underscore
......@@ -56,9 +51,11 @@
#= require_directory ./commit
#= require_directory ./extensions
#= require_directory ./lib
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus
#= require cropper
#= require u2f
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......@@ -124,9 +121,10 @@ window.onload = ->
setTimeout shiftWindow, 100
$ ->
gl.utils.preventDisabledButtons()
bootstrapBreakpoint = bp.getBreakpointSize()
$(".nicescroll").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
$(".nav-sidebar").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
# Click a .js-select-on-focus field, select the contents
$(".js-select-on-focus").on "focusin", ->
......@@ -161,19 +159,6 @@ $ ->
$el.data('placement') || 'bottom'
)
$('.header-logo .home').tooltip(
placement: (_, el) ->
$el = $(el)
if $('.page-with-sidebar').hasClass('page-sidebar-collapsed') then 'right' else 'bottom'
container: 'body'
)
$('.page-with-sidebar').tooltip(
selector: '.sidebar-collapsed .nav-sidebar a, .sidebar-collapsed a.sidebar-user'
placement: 'right'
container: 'body'
)
# Form submitter
$('.trigger-submit').on 'change', ->
$(@).parents('form').submit()
......@@ -206,6 +191,7 @@ $ ->
$('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle()
$('.header-content .header-logo').toggle()
$('.header-content .navbar-collapse').toggle()
$('.navbar-toggle').toggleClass('active')
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
......@@ -224,6 +210,10 @@ $ ->
form = btn.closest("form")
new ConfirmDangerModal(form, text)
$(document).on 'click', 'button', ->
$(this).blur()
$('input[type="search"]').each ->
$this = $(this)
$this.attr 'value', $this.val()
......@@ -236,7 +226,6 @@ $ ->
$this.attr 'value', $this.val()
$sidebarGutterToggle = $('.js-sidebar-toggle')
$navIconToggle = $('.toggle-nav-collapse')
$(document)
.off 'breakpoint:change'
......@@ -246,10 +235,6 @@ $ ->
if $gutterIcon.hasClass('fa-angle-double-right')
$sidebarGutterToggle.trigger('click')
$navIcon = $navIconToggle.find('.fa')
if $navIcon.hasClass('fa-angle-left')
$navIconToggle.trigger('click')
fitSidebarForSize = ->
oldBootstrapBreakpoint = bootstrapBreakpoint
bootstrapBreakpoint = bp.getBreakpointSize()
......@@ -262,9 +247,38 @@ $ ->
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$(window)
.off "resize"
.on "resize", (e) ->
.off "resize.app"
.on "resize.app", (e) ->
fitSidebarForSize()
gl.awardsHandler = new AwardsHandler()
checkInitialSidebarSize()
new Aside()
# Sidenav pinning
if $(window).width() < 1440 and $.cookie('pin_nav') is 'true'
$.cookie('pin_nav', 'false')
$('.page-with-sidebar')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
.removeClass('page-sidebar-pinned')
$('.navbar-fixed-top').removeClass('header-pinned-nav')
$(document)
.off 'click', '.js-nav-pin'
.on 'click', '.js-nav-pin', (e) ->
e.preventDefault()
$(this).toggleClass 'is-active'
if $.cookie('pin_nav') is 'true'
$.cookie 'pin_nav', 'false'
$('.page-with-sidebar')
.removeClass('page-sidebar-pinned')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
$('.navbar-fixed-top')
.removeClass('header-pinned-nav')
.toggleClass('header-collapsed header-expanded')
else
$.cookie 'pin_nav', 'true'
$('.page-with-sidebar').addClass('page-sidebar-pinned')
$('.navbar-fixed-top').addClass('header-pinned-nav')
class @BlobGitignoreSelector
constructor: (opts) ->
{
@dropdown
@editor
@$wrapper = @dropdown.closest('.gitignore-selector')
@$filenameInput = $('#file_name')
@data = @dropdown.data('filenames')
} = opts
#= require blob/template_selector
@dropdown.glDropdown(
data: @data,
filterable: true,
selectable: true,
search:
fields: ['name']
clicked: @onClick
text: (gitignore) ->
gitignore.name
)
@toggleGitignoreSelector()
@bindEvents()
bindEvents: ->
@$filenameInput
.on 'keyup blur', (e) =>
@toggleGitignoreSelector()
toggleGitignoreSelector: ->
filename = @$filenameInput.val() or $('.editor-file-name').text().trim()
@$wrapper.toggleClass 'hidden', filename isnt '.gitignore'
onClick: (item, el, e) =>
e.preventDefault()
@requestIgnoreFile(item.name)
requestIgnoreFile: (name) ->
Api.gitignoreText name, @requestIgnoreFileSuccess.bind(@)
requestIgnoreFileSuccess: (gitignore) ->
@editor.setValue(gitignore.content, 1)
@editor.focus()
class @BlobGitignoreSelectors
constructor: (opts) ->
{
@$dropdowns = $('.js-gitignore-selector')
@editor
} = opts
@$dropdowns.each (i, dropdown) =>
$dropdown = $(dropdown)
new BlobGitignoreSelector(
dropdown: $dropdown,
editor: @editor
)
class @BlobGitignoreSelector extends TemplateSelector
requestFile: (query) ->
Api.gitignoreText query.name, @requestFileSuccess.bind(@)
class @BlobGitignoreSelectors
constructor: (opts) ->
{
@$dropdowns = $('.js-gitignore-selector')
@editor
} = opts
@$dropdowns.each (i, dropdown) =>
$dropdown = $(dropdown)
new BlobGitignoreSelector(
pattern: /(.gitignore)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-gitignore-selector-wrap'),
dropdown: $dropdown,
editor: @editor
)
class @BlobLicenseSelector
licenseRegex: /^(.+\/)?(licen[sc]e|copying)($|\.)/i
#= require blob/template_selector
constructor: (editor) ->
@$licenseSelector = $('.js-license-selector')
$fileNameInput = $('#file_name')
class @BlobLicenseSelector extends TemplateSelector
requestFile: (query) ->
data =
project: @dropdown.data('project')
fullname: @dropdown.data('fullname')
initialFileNameValue = if $fileNameInput.length
$fileNameInput.val()
else if $('.editor-file-name').length
$('.editor-file-name').text().trim()
@toggleLicenseSelector(initialFileNameValue)
if $fileNameInput
$fileNameInput.on 'keyup blur', (e) =>
@toggleLicenseSelector($(e.target).val())
$('select.license-select').on 'change', (e) ->
data =
project: $(this).data('project')
fullname: $(this).data('fullname')
Api.licenseText $(this).val(), data, (license) ->
editor.setValue(license.content, -1)
toggleLicenseSelector: (fileName) =>
if @licenseRegex.test(fileName)
@$licenseSelector.show()
else
@$licenseSelector.hide()
Api.licenseText query.id, data, @requestFileSuccess.bind(@)
class @BlobLicenseSelectors
constructor: (opts) ->
{
@$dropdowns = $('.js-license-selector')
@editor
} = opts
@$dropdowns.each (i, dropdown) =>
$dropdown = $(dropdown)
new BlobLicenseSelector(
pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-license-selector-wrap'),
dropdown: $dropdown,
editor: @editor
)
......@@ -12,8 +12,9 @@ class @EditBlob
$("#file-content").val(@editor.getValue())
@initModePanesAndLinks()
new BlobLicenseSelector(@editor)
new BlobGitignoreSelectors(editor: @editor)
new BlobLicenseSelectors { @editor }
new BlobGitignoreSelectors { @editor }
initModePanesAndLinks: ->
@$editModePanes = $(".js-edit-mode-pane")
......
class @TemplateSelector
constructor: (opts = {}) ->
{
@dropdown,
@data,
@pattern,
@wrapper,
@editor,
@fileEndpoint,
@$input = $('#file_name')
} = opts
@buildDropdown()
@bindEvents()
@onFilenameUpdate()
buildDropdown: ->
@dropdown.glDropdown(
data: @data,
filterable: true,
selectable: true,
search:
fields: ['name']
clicked: @onClick
text: (item) ->
item.name
)
bindEvents: ->
@$input.on('keyup blur', (e) =>
@onFilenameUpdate()
)
onFilenameUpdate: ->
return unless @$input.length
filenameMatches = @pattern.test(@$input.val().trim())
if not filenameMatches
@wrapper.addClass('hidden')
return
@wrapper.removeClass('hidden')
onClick: (item, el, e) =>
e.preventDefault()
@requestFile(item)
requestFile: (item) ->
# To be implemented on the extending class
# e.g.
# Api.gitignoreText item.name, @requestFileSuccess.bind(@)
requestFileSuccess: (file) ->
@editor.setValue(file.content, 1)
@editor.focus()
class CiBuild
class @CiBuild
@interval: null
@state: null
constructor: (build_url, build_status, build_state) ->
constructor: (@build_url, @build_status, @state) ->
clearInterval(CiBuild.interval)
@state = build_state
# Init breakpoint checker
@bp = Breakpoints.get()
@hideSidebar()
$('.js-build-sidebar').niceScroll()
$(document)
.off 'click', '.js-sidebar-build-toggle'
.on 'click', '.js-sidebar-build-toggle', @toggleSidebar
@initScrollButtonAffix()
$(window)
.off 'resize.build'
.on 'resize.build', @hideSidebar
if build_status == "running" || build_status == "pending"
@updateArtifactRemoveDate()
if $('#build-trace').length
@getInitialBuildTrace()
@initScrollButtonAffix()
if @build_status is "running" or @build_status is "pending"
#
# Bind autoscroll button to follow build output
#
$("#autoscroll-button").bind "click", ->
$('#autoscroll-button').on 'click', ->
state = $(this).data("state")
if "enabled" is state
$(this).data "state", "disabled"
......@@ -27,26 +41,37 @@ class CiBuild
# Only valid for runnig build when output changes during time
#
CiBuild.interval = setInterval =>
if window.location.href.split("#").first() is build_url
last_state = @state
$.ajax
url: build_url + "/trace.json?state=" + encodeURIComponent(@state)
dataType: "json"
success: (log) =>
return unless last_state is @state
if log.state and log.status is "running"
@state = log.state
if log.append
$('.fa-refresh').before log.html
else
$('#build-trace code').html log.html
$('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
@checkAutoscroll()
else if log.status isnt build_status
Turbolinks.visit build_url
if window.location.href.split("#").first() is @build_url
@getBuildTrace()
, 4000
getInitialBuildTrace: ->
$.ajax
url: @build_url
dataType: 'json'
success: (build_data) ->
$('.js-build-output').html build_data.trace_html
if build_data.status is 'success' or build_data.status is 'failed'
$('.js-build-refresh').remove()
getBuildTrace: ->
$.ajax
url: "#{@build_url}/trace.json?state=#{encodeURIComponent(@state)}"
dataType: "json"
success: (log) =>
if log.state
@state = log.state
if log.status is "running"
if log.append
$('.js-build-output').append log.html
else
$('.js-build-output').html log.html
@checkAutoscroll()
else if log.status isnt @build_status
Turbolinks.visit @build_url
checkAutoscroll: ->
$("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state")
......@@ -61,4 +86,29 @@ class CiBuild
$body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top)
)
@CiBuild = CiBuild
shouldHideSidebar: ->
bootstrapBreakpoint = @bp.getBreakpointSize()
bootstrapBreakpoint is 'xs' or bootstrapBreakpoint is 'sm'
toggleSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.toggleClass 'right-sidebar-expanded right-sidebar-collapsed'
hideSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.removeClass 'right-sidebar-expanded'
.addClass 'right-sidebar-collapsed'
else
$('.js-build-sidebar')
.removeClass 'right-sidebar-collapsed'
.addClass 'right-sidebar-expanded'
updateArtifactRemoveDate: ->
$date = $('.js-artifacts-remove')
if $date.length
date = $date.text()
$date.text $.timefor(new Date(date), ' ')
......@@ -23,13 +23,13 @@ class Dispatcher
new Issue()
shortcut_handler = new ShortcutsIssuable()
new ZenMode()
window.awardsHandler = new AwardsHandler()
when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show'
new Milestone()
when 'dashboard:todos:index'
new Todos()
when 'projects:milestones:new', 'projects:milestones:edit'
new ZenMode()
new DueDateSelect()
new GLForm($('.milestone-form'))
when 'groups:milestones:new'
new ZenMode()
......@@ -54,10 +54,13 @@ class Dispatcher
new Diff()
shortcut_handler = new ShortcutsIssuable(true)
new ZenMode()
window.awardsHandler = new AwardsHandler()
new MergedButtons()
when 'projects:merge_requests:commits', 'projects:merge_requests:builds'
new MergedButtons()
when "projects:merge_requests:diffs"
new Diff()
new ZenMode()
new MergedButtons()
when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation()
Issuable.init()
......@@ -70,9 +73,7 @@ class Dispatcher
new Diff()
new ZenMode()
shortcut_handler = new ShortcutsNavigation()
when 'projects:commits:show'
shortcut_handler = new ShortcutsNavigation()
when 'projects:activity'
when 'projects:commits:show', 'projects:activity'
shortcut_handler = new ShortcutsNavigation()
when 'projects:show'
shortcut_handler = new ShortcutsNavigation()
......@@ -98,8 +99,11 @@ class Dispatcher
when 'projects:blob:show', 'projects:blame:show'
new LineHighlighter()
shortcut_handler = new ShortcutsNavigation()
new ShortcutsBlob true
when 'projects:labels:new', 'projects:labels:edit'
new Labels()
when 'projects:labels:index'
new LabelManager() if $('.prioritized-labels').length
when 'projects:network:show'
# Ensure we don't create a particular shortcut handler here. This is
# already created, where the network graph is created.
......@@ -129,15 +133,11 @@ class Dispatcher
new Project()
new ProjectAvatar()
switch path[1]
when 'compare'
shortcut_handler = new ShortcutsNavigation()
when 'edit'
shortcut_handler = new ShortcutsNavigation()
new ProjectNew()
when 'new'
when 'new', 'show'
new ProjectNew()
when 'show'
new ProjectShow()
when 'wikis'
new Wikis()
shortcut_handler = new ShortcutsNavigation()
......@@ -146,9 +146,9 @@ class Dispatcher
when 'snippets'
shortcut_handler = new ShortcutsNavigation()
new ZenMode() if path[2] == 'show'
when 'labels', 'graphs'
shortcut_handler = new ShortcutsNavigation()
when 'project_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
when 'labels', 'graphs', 'compare', 'pipelines', 'forks', \
'milestones', 'project_members', 'deploy_keys', 'builds', \
'hooks', 'services', 'protected_branches'
shortcut_handler = new ShortcutsNavigation()
# If we haven't installed a custom shortcut handler, install the default one
......
class @DueDateSelect
constructor: ->
# Milestone edit/new form
$datePicker = $('.datepicker')
if $datePicker.length
$dueDate = $('#milestone_due_date')
$datePicker.datepicker
dateFormat: 'yy-mm-dd'
onSelect: (dateText, inst) ->
$dueDate.val(dateText)
.datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $dueDate.val()))
$('.js-clear-due-date').on 'click', (e) ->
e.preventDefault()
$.datepicker._clearDate($datePicker)
# Issuable sidebar
$loading = $('.js-issuable-update .due_date')
.find('.block-loading')
.hide()
......@@ -21,7 +37,7 @@ class @DueDateSelect
$dropdown.glDropdown(
hidden: ->
$selectbox.hide()
$value.removeAttr('style')
$value.css('display', '')
)
addDueDate = (isDropdown) ->
......@@ -32,7 +48,7 @@ class @DueDateSelect
date = new Date value.replace(new RegExp('-', 'g'), ',')
mediumDate = $.datepicker.formatDate 'M d, yy', date
else
mediumDate = 'None'
mediumDate = 'No due date'
data = {}
data[abilityName] = {}
......@@ -42,14 +58,16 @@ class @DueDateSelect
type: 'PUT'
url: issueUpdateURL
data: data
dataType: 'json'
beforeSend: ->
$loading.fadeIn()
if isDropdown
$dropdown.trigger('loading.gl.dropdown')
$selectbox.hide()
$value.removeAttr('style')
$value.css('display', '')
$valueContent.html(mediumDate)
cssClass = if Date.parse(mediumDate) then 'bold' else 'no-value'
$valueContent.html("<span class='#{cssClass}'>#{mediumDate}</span>")
$sidebarValue.html(mediumDate)
if value isnt ''
......
......@@ -3,6 +3,7 @@
window.GitLab ?= {}
GitLab.GfmAutoComplete =
dataLoading: false
dataLoaded: false
dataSource: ''
......@@ -14,6 +15,9 @@ GitLab.GfmAutoComplete =
Members:
template: '<li>${username} <small>${title}</small></li>'
Labels:
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
# Issues and MergeRequests
Issues:
template: '<li><small>${id}</small> ${title}</li>'
......@@ -22,6 +26,24 @@ GitLab.GfmAutoComplete =
Milestones:
template: '<li>${title}</li>'
Loading:
template: '<li><i class="fa fa-refresh fa-spin"></i> Loading...</li>'
DefaultOptions:
sorter: (query, items, searchKey) ->
return items if items[0].name? and items[0].name is 'loading'
$.fn.atwho.default.callbacks.sorter(query, items, searchKey)
filter: (query, data, searchKey) ->
return data if data[0] is 'loading'
$.fn.atwho.default.callbacks.filter(query, data, searchKey)
beforeInsert: (value) ->
if not GitLab.GfmAutoComplete.dataLoaded
@at
else
value
# Add GFM auto-completion to all input fields, that accept GFM input.
setup: (wrap) ->
@input = $('.js-gfm-input')
......@@ -53,18 +75,37 @@ GitLab.GfmAutoComplete =
# Emoji
@input.atwho
at: ':'
displayTpl: @Emoji.template
displayTpl: (value) =>
if value.path?
@Emoji.template
else
@Loading.template
insertTpl: ':${name}:'
data: ['loading']
callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
# Team Members
@input.atwho
at: '@'
displayTpl: @Members.template
displayTpl: (value) =>
if value.username?
@Members.template
else
@Loading.template
insertTpl: '${atwho-at}${username}'
searchKey: 'search'
data: ['loading']
callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
beforeSave: (members) ->
$.map members, (m) ->
return m if not m.username?
title = m.name
title += " (#{m.count})" if m.count
......@@ -76,11 +117,21 @@ GitLab.GfmAutoComplete =
at: '#'
alias: 'issues'
searchKey: 'search'
displayTpl: @Issues.template
displayTpl: (value) =>
if value.title?
@Issues.template
else
@Loading.template
data: ['loading']
insertTpl: '${atwho-at}${id}'
callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
beforeSave: (issues) ->
$.map issues, (i) ->
return i if not i.title?
id: i.iid
title: sanitize(i.title)
search: "#{i.iid} #{i.title}"
......@@ -89,11 +140,18 @@ GitLab.GfmAutoComplete =
at: '%'
alias: 'milestones'
searchKey: 'search'
displayTpl: @Milestones.template
displayTpl: (value) =>
if value.title?
@Milestones.template
else
@Loading.template
insertTpl: '${atwho-at}"${title}"'
data: ['loading']
callbacks:
beforeSave: (milestones) ->
$.map milestones, (m) ->
return m if not m.title?
id: m.iid
title: sanitize(m.title)
search: "#{m.title}"
......@@ -102,15 +160,44 @@ GitLab.GfmAutoComplete =
at: '!'
alias: 'mergerequests'
searchKey: 'search'
displayTpl: @Issues.template
displayTpl: (value) =>
if value.title?
@Issues.template
else
@Loading.template
data: ['loading']
insertTpl: '${atwho-at}${id}'
callbacks:
sorter: @DefaultOptions.sorter
filter: @DefaultOptions.filter
beforeInsert: @DefaultOptions.beforeInsert
beforeSave: (merges) ->
$.map merges, (m) ->
return m if not m.title?
id: m.iid
title: sanitize(m.title)
search: "#{m.iid} #{m.title}"
@input.atwho
at: '~'
alias: 'labels'
searchKey: 'search'
displayTpl: @Labels.template
insertTpl: '${atwho-at}${title}'
callbacks:
beforeSave: (merges) ->
sanitizeLabelTitle = (title)->
if /\w+\s+\w+/g.test(title)
"\"#{sanitize(title)}\""
else
sanitize(title)
$.map merges, (m) ->
title: sanitizeLabelTitle(m.title)
color: m.color
search: "#{m.title}"
destroyAtWho: ->
@input.atwho('destroy')
......@@ -118,6 +205,8 @@ GitLab.GfmAutoComplete =
$.getJSON(dataSource)
loadData: (data) ->
@dataLoaded = true
# load members
@input.atwho 'load', '@', data.members
# load issues
......@@ -128,3 +217,9 @@ GitLab.GfmAutoComplete =
@input.atwho 'load', 'mergerequests', data.mergerequests
# load emojis
@input.atwho 'load', ':', data.emojis
# load labels
@input.atwho 'load', '~', data.labels
# This trigger at.js again
# otherwise we would be stuck with loading until the user types
$(':focus').trigger('keyup')
......@@ -211,6 +211,7 @@ class GitLabDropdown
@dropdown.on "shown.bs.dropdown", @opened
@dropdown.on "hidden.bs.dropdown", @hidden
$(@el).on "update.label", @updateLabel
@dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate
@dropdown.on 'keyup', (e) =>
if e.which is 27 # Escape key
......@@ -453,7 +454,7 @@ class GitLabDropdown
# Toggle the dropdown label
if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel
@updateLabel()
else
selectedObject
else if el.hasClass(INDETERMINATE_CLASS)
......@@ -480,7 +481,7 @@ class GitLabDropdown
# Toggle the dropdown label
if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject, el)
@updateLabel(selectedObject, el)
if value?
if !field.length and fieldName
@addInput(fieldName, value)
......@@ -579,6 +580,9 @@ class GitLabDropdown
# Scroll the dropdown content up
$dropdownContent.scrollTop(listItemTop - dropdownContentTop)
updateLabel: (selected = null, el = null) =>
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el)
$.fn.glDropdown = (opts) ->
return @.each ->
if (!$.data @, 'glDropdown')
......
......@@ -4,4 +4,5 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require Chart
#= require_tree .
......@@ -6,12 +6,18 @@ issuable_created = false
Issuable.initTemplates()
Issuable.initSearch()
Issuable.initChecks()
Issuable.initLabelFilterRemove()
initTemplates: ->
Issuable.labelRow = _.template(
'<% _.each(labels, function(label){ %>
<span class="label-row">
<a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a>
<span class="label-row btn-group" role="group" aria-label="<%= _.escape(label.title) %>" style="color: <%= label.text_color %>;">
<a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%= label.color %>;" title="<%= _.escape(label.description) %>" data-container="body">
<%= _.escape(label.title) %>
</a>
<button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%= label.color %>;" data-label="<%= _.escape(label.title) %>">
<i class="fa fa-times"></i>
</button>
</span>
<% }); %>'
)
......@@ -35,12 +41,20 @@ issuable_created = false
Issuable.filterResults $form
, 500)
toggleLabelFilters: ->
$filteredLabels = $('.filtered-labels')
if $filteredLabels.find('.label-row').length > 0
$filteredLabels.removeClass('hidden')
else
$filteredLabels.addClass('hidden')
initLabelFilterRemove: ->
$(document)
.off 'click', '.js-label-filter-remove'
.on 'click', '.js-label-filter-remove', (e) ->
$button = $(@)
# Remove the label input box
$('input[name="label_name[]"]')
.filter -> @value is $button.data('label')
.remove()
# Submit the form to get new data
Issuable.filterResults $('.filter-form')
$('.js-label-select').trigger('update.label')
filterResults: (form) =>
formData = form.serialize()
......@@ -50,58 +64,16 @@ issuable_created = false
issuesUrl = formAction
issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}")
issuesUrl += formData
$.ajax
type: 'GET'
url: formAction
data: formData
complete: ->
$('.issues-holder, .merge-requests-holder').css('opacity', '1.0')
success: (data) ->
$('.issues-holder, .merge-requests-holder').html(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: issuesUrl}, document.title, issuesUrl
Issuable.reload()
Issuable.updateStateFilters()
$filteredLabels = $('.filtered-labels')
if typeof Issuable.labelRow is 'function'
$filteredLabels.html(Issuable.labelRow(data))
Issuable.toggleLabelFilters()
dataType: "json"
reload: ->
if Issuable.created
Issuable.initChecks()
$('#filter_issue_search').val($('#issue_search').val())
Turbolinks.visit(issuesUrl);
initChecks: ->
$('.check_all_issues').on 'click', ->
$('.check_all_issues').off('click').on('click', ->
$('.selected_issue').prop('checked', @checked)
Issuable.checkChanged()
)
$('.selected_issue').on 'change', Issuable.checkChanged
updateStateFilters: ->
stateFilters = $('.issues-state-filters, .dropdown-menu-sort')
newParams = {}
paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search', 'issue_search']
for paramKey in paramKeys
newParams[paramKey] = gl.utils.getParameterValues(paramKey)[0] or ''
if stateFilters.length
stateFilters.find('a').each ->
initialUrl = gl.utils.removeParamQueryString($(this).attr('href'), 'label_name[]')
labelNameValues = gl.utils.getParameterValues('label_name[]')
if labelNameValues
labelNameQueryString = ("label_name[]=#{value}" for value in labelNameValues).join('&')
newUrl = "#{gl.utils.mergeUrlParams(newParams, initialUrl)}&#{labelNameQueryString}"
else
newUrl = gl.utils.mergeUrlParams(newParams, initialUrl)
$(this).attr 'href', newUrl
$('.selected_issue').off('change').on('change', Issuable.checkChanged)
checkChanged: ->
checked_issues = $('.selected_issue:checked')
......
......@@ -102,6 +102,10 @@ class @IssuableForm
return {
results: data
}
data: (query) ->
{
search: query
}
formatResult: (project) ->
project.name_with_namespace
formatSelection: (project) ->
......
......@@ -9,6 +9,9 @@ class @IssuableBulkActions
@bindEvents()
# Fixes bulk-assign not working when navigating through pages
Issuable.initChecks();
getElement: (selector) ->
@container.find selector
......@@ -97,13 +100,22 @@ class @IssuableBulkActions
$labels = @form.find('.labels-filter input[name="update[label_ids][]"]')
$labels.each (k, label) ->
labelIds.push $(label).val() if label
labelIds.push parseInt($(label).val()) if label
labelIds
###*
* Just an alias of @getUnmarkedIndeterminedLabels
* @return {Array} Array of labels
* Returns Label IDs that will be removed from issue selection
* @return {Array} Array of labels IDs
###
getLabelsToRemove: ->
@getUnmarkedIndeterminedLabels()
result = []
indeterminatedLabels = @getUnmarkedIndeterminedLabels()
labelsToApply = @getLabelsToApply()
indeterminatedLabels.map (id) ->
# We need to exclude label IDs that will be applied
# By not doing this will cause issues from selection to not add labels at all
result.push(id) if labelsToApply.indexOf(id) is -1
result
......@@ -39,7 +39,7 @@ class @LabelsSelect
</a>
<% }); %>'
)
labelNoneHTMLTemplate = _.template('<div class="light">None</div>')
labelNoneHTMLTemplate = '<span class="no-value">None</span>'
if newLabelField.length
......@@ -95,8 +95,11 @@ class @LabelsSelect
$newLabelCreateButton.enable()
if label.message?
errors = _.map label.message, (value, key) ->
"#{key} #{value[0]}"
$newLabelError
.text label.message
.html errors.join("<br/>")
.show()
else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
......@@ -142,7 +145,7 @@ class @LabelsSelect
template = labelHTMLTemplate(data)
labelCount = data.labels.length
else
template = labelNoneHTMLTemplate()
template = labelNoneHTMLTemplate
$value
.removeAttr('style')
.html(template)
......@@ -254,7 +257,7 @@ class @LabelsSelect
search:
fields: ['title']
selectable: true
filterable: true
toggleLabel: (selected, el) ->
selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
......
class @LayoutNav
$ ->
$('.fade-left').addClass('end-scroll')
$('.scrolling-tabs').on 'scroll', (event) ->
$this = $(this)
$el = $(event.target)
currentPosition = $this.scrollLeft()
size = bp.getBreakpointSize()
controlBtnWidth = $('.controls').width()
maxPosition = $this.get(0).scrollWidth - $this.parent().width()
maxPosition += controlBtnWidth if size isnt 'xs' and $('.nav-control').length
$el.find('.fade-left').toggleClass('end-scroll', currentPosition is 0)
$el.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition)
hideEndFade = ($scrollingTabs) ->
$scrollingTabs.each ->
$this = $(@)
$this
.find('.fade-right')
.toggleClass('end-scroll', $this.width() is $this.prop('scrollWidth'))
$ ->
$('.fade-left').addClass('end-scroll')
hideEndFade($('.scrolling-tabs'))
$(window)
.off 'resize.nav'
.on 'resize.nav', ->
hideEndFade($('.scrolling-tabs'))
$('.scrolling-tabs').on 'scroll', (event) ->
$this = $(this)
currentPosition = $this.scrollLeft()
maxPosition = $this.prop('scrollWidth') - $this.outerWidth()
$this.find('.fade-left').toggleClass('end-scroll', currentPosition is 0)
$this.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition)
((w) ->
w.gl or= {}
w.gl.utils or= {}
w.gl.utils.isInGroupsPage = ->
return $('body').data('page').split(':')[0] is 'groups'
w.gl.utils.isInProjectPage = ->
return $('body').data('page').split(':')[0] is 'projects'
w.gl.utils.getProjectSlug = ->
return if @isInProjectPage() then $('body').data 'project' else null
w.gl.utils.getGroupSlug = ->
return if @isInGroupsPage() then $('body').data 'group' else null
gl.utils.updateTooltipTitle = ($tooltipEl, newTitle) ->
$tooltipEl
.tooltip 'destroy'
.attr 'title', newTitle
.tooltip 'fixTitle'
gl.utils.preventDisabledButtons = ->
$('.btn').click (e) ->
if $(this).hasClass 'disabled'
e.preventDefault()
e.stopImmediatePropagation()
return false
jQuery.timefor = (time, suffix, expiredLabel) ->
return '' unless time
suffix or= 'remaining'
expiredLabel or= 'Past due'
jQuery.timeago.settings.allowFuture = yes
{ suffixFromNow } = jQuery.timeago.settings.strings
jQuery.timeago.settings.strings.suffixFromNow = suffix
timefor = $.timeago time
if timefor.indexOf('ago') > -1
timefor = expiredLabel
jQuery.timeago.settings.strings.suffixFromNow = suffixFromNow
return timefor
) window
......@@ -12,6 +12,13 @@
$el.attr('title', gl.utils.formatDate($el.attr('datetime')))
)
$timeagoEls.timeago() if setTimeago
if setTimeago
$timeagoEls.timeago()
$timeagoEls.tooltip('destroy')
# Recreate with custom template
$timeagoEls.tooltip(
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
)
) window
window.emojiAliases = ->
gl.emojiAliases = ->
JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>')
......@@ -42,9 +42,3 @@ work = ->
$(document).on('page:fetch', start)
$(document).on('page:change', stop)
$ ->
# Make logo clickable as part of a workaround for Safari visited
# link behaviour (See !2690).
$('#logo').on 'click', ->
$('#js-shortcuts-home').get(0).click()
......@@ -9,7 +9,7 @@ class @MergeRequest
# Options:
# action - String, current controller action
#
constructor: (@opts) ->
constructor: (@opts = {}) ->
this.$el = $('.merge-request')
this.$('.show-all-commits').on 'click', =>
......
......@@ -88,7 +88,7 @@ class @MergeRequestTabs
scrollToElement: (container) ->
if window.location.hash
navBarHeight = $('.navbar-gitlab').outerHeight()
navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()
$el = $("#{container} #{window.location.hash}:not(.match)")
$.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length
......
class @MergedButtons
constructor: ->
@$removeBranchWidget = $('.remove_source_branch_widget')
@$removeBranchProgress = $('.remove_source_branch_in_progress')
@$removeBranchFailed = $('.remove_source_branch_widget.failed')
@cleanEventListeners()
@initEventListeners()
cleanEventListeners: ->
$(document).off 'click', '.remove_source_branch'
$(document).off 'ajax:success', '.remove_source_branch'
$(document).off 'ajax:error', '.remove_source_branch'
initEventListeners: ->
$(document).on 'click', '.remove_source_branch', @removeSourceBranch
$(document).on 'ajax:success', '.remove_source_branch', @removeBranchSuccess
$(document).on 'ajax:error', '.remove_source_branch', @removeBranchError
removeSourceBranch: =>
@$removeBranchWidget.hide()
@$removeBranchProgress.show()
removeBranchSuccess: ->
location.reload()
removeBranchError: ->
@$removeBranchWidget.hide()
@$removeBranchProgress.hide()
@$removeBranchFailed.show()
......@@ -24,10 +24,16 @@ class @MilestoneSelect
if issueUpdateURL
milestoneLinkTemplate = _.template(
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>"><%= _.escape(title) %></a>'
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>" class="bold has-tooltip" data-container="body" title="<%= remaining %>"><%= _.escape(title) %></a>'
)
milestoneLinkNoneTemplate = '<div class="light">None</div>'
milestoneLinkNoneTemplate = '<span class="no-value">None</span>'
collapsedSidebarLabelTemplate = _.template(
'<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left">
<%= _.escape(title) %>
</span>'
)
$dropdown.glDropdown(
data: (term, callback) ->
......@@ -83,7 +89,7 @@ class @MilestoneSelect
$selectbox.hide()
# display:block overrides the hide-collapse rule
$value.removeAttr('style')
$value.css('display', '')
clicked: (selected) ->
page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index'
......@@ -106,7 +112,7 @@ class @MilestoneSelect
.val()
data = {}
data[abilityName] = {}
data[abilityName].milestone_id = selected
data[abilityName].milestone_id = if selected? then selected else null
$loading
.fadeIn()
$dropdown.trigger('loading.gl.dropdown')
......@@ -118,12 +124,13 @@ class @MilestoneSelect
$dropdown.trigger('loaded.gl.dropdown')
$loading.fadeOut()
$selectbox.hide()
$value.removeAttr('style')
$value.css('display', '')
if data.milestone?
data.milestone.namespace = _this.currentProject.namespace
data.milestone.path = _this.currentProject.path
data.milestone.remaining = $.timefor data.milestone.due_date
$value.html(milestoneLinkTemplate(data.milestone))
$sidebarCollapsedValue.find('span').text(data.milestone.title)
$sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone))
else
$value.html(milestoneLinkNoneTemplate)
$sidebarCollapsedValue.find('span').text('No')
......
# This is a manifest file that'll be compiled into including all the files listed below.
# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
# be included in the compiled file accessible from http://example.com/assets/application.js
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require raphael
#= require g.raphael
#= require g.bar
#= require_tree .
$ ->
network_graph = new Network({
url: $(".network-graph").attr('data-url'),
commit_url: $(".network-graph").attr('data-commit-url'),
ref: $(".network-graph").attr('data-ref'),
commit_id: $(".network-graph").attr('data-commit-id')
})
new ShortcutsNetwork(network_graph.branch_graph)
......@@ -115,12 +115,14 @@ class @Notes
, @pollingInterval
refresh: =>
return if @refreshing is true
@refreshing = true
if not document.hidden and document.URL.indexOf(@noteable_url) is 0
@getContent()
getContent: ->
return if @refreshing
@refreshing = true
$.ajax
url: @notes_url
data: "last_fetched_at=" + @last_fetched_at
......@@ -162,13 +164,14 @@ class @Notes
renderNote: (note) ->
unless note.valid
if note.award
flash = new Flash('You have already used this award emoji!', 'alert')
flash = new Flash('You have already awarded this emoji!', 'alert')
flash.pinTo('.header-content')
return
if note.award
awardsHandler.addAwardToEmojiBar(note.name)
awardsHandler.scrollToAwards()
votesBlock = $('.js-awards-block').eq 0
gl.awardsHandler.addAwardToEmojiBar votesBlock, note.name
gl.awardsHandler.scrollToAwards()
# render note if it not present in loaded list
# or skip if rendered
......@@ -353,8 +356,7 @@ class @Notes
Called in response to clicking the edit note link
Replaces the note text with the note edit form
Adds a hidden div with the original content of the note to fill the edit note form with
if the user cancels
Adds a data attribute to the form with the original content of the note for cancellations
###
showEditForm: (e, scrollTo, myLastNote) ->
e.preventDefault()
......@@ -370,6 +372,8 @@ class @Notes
done = ($noteText) ->
# Neat little trick to put the cursor at the end
noteTextVal = $noteText.val()
# Store the original note text in a data attribute to retrieve if a user cancels edit.
form.find('form.edit-note').data 'original-note', noteTextVal
$noteText.val('').val(noteTextVal);
new GLForm form
......@@ -392,14 +396,16 @@ class @Notes
###
Called in response to clicking the edit note link
Hides edit form
Hides edit form and restores the original note text to the editor textarea.
###
cancelEdit: (e) ->
e.preventDefault()
note = $(this).closest(".note")
form = note.find(".current-note-edit-form")
note.removeClass "is-editting"
note.find(".current-note-edit-form")
.removeClass("current-note-edit-form")
form.removeClass("current-note-edit-form")
# Replace markdown textarea text with original note text.
form.find(".js-note-text").val(form.find('form.edit-note').data('original-note'))
###
Called in response to deleting a note of any kind.
......
@Pager =
init: (@limit = 0, preload, @disable = false) ->
init: (@limit = 0, preload, @disable = false, @callback = $.noop) ->
@loading = $('.loading').first()
if preload
......@@ -19,6 +19,7 @@
@loading.hide()
success: (data) ->
Pager.append(data.count, data.html)
Pager.callback()
dataType: "json"
append: (count, html) ->
......
......@@ -7,12 +7,17 @@ class @ProjectNew
@toggleSettingsOnclick()
toggleSettings: ->
checked = $("#project_builds_enabled").prop("checked")
if checked
$('.builds-feature').show()
else
$('.builds-feature').hide()
toggleSettings: =>
@_showOrHide('#project_builds_enabled', '.builds-feature')
@_showOrHide('#project_merge_requests_enabled', '.merge-requests-feature')
toggleSettingsOnclick: ->
$("#project_builds_enabled").on 'click', @toggleSettings
$('#project_builds_enabled, #project_merge_requests_enabled').on 'click', @toggleSettings
_showOrHide: (checkElement, container) ->
$container = $(container)
if $(checkElement).prop('checked')
$container.show()
else
$container.hide()
......@@ -43,6 +43,55 @@ class @Sidebar
$('.right-sidebar')
.hasClass('right-sidebar-collapsed'), { path: '/' })
$(document)
.off 'click', '.js-issuable-todo'
.on 'click', '.js-issuable-todo', @toggleTodo
toggleTodo: (e) =>
$this = $(e.currentTarget)
$todoLoading = $('.js-issuable-todo-loading')
$btnText = $('.js-issuable-todo-text', $this)
ajaxType = if $this.attr('data-id') then 'PATCH' else 'POST'
ajaxUrlExtra = if $this.attr('data-id') then "/#{$this.attr('data-id')}" else ''
$.ajax(
url: "#{$this.data('url')}#{ajaxUrlExtra}"
type: ajaxType
dataType: 'json'
data:
issuable_id: $this.data('issuable')
issuable_type: $this.data('issuable-type')
beforeSend: =>
@beforeTodoSend($this, $todoLoading)
).done (data) =>
@todoUpdateDone(data, $this, $btnText, $todoLoading)
beforeTodoSend: ($btn, $todoLoading) ->
$btn.disable()
$todoLoading.removeClass 'hidden'
todoUpdateDone: (data, $btn, $btnText, $todoLoading) ->
$todoPendingCount = $('.todos-pending-count')
$todoPendingCount.text data.count
$btn.enable()
$todoLoading.addClass 'hidden'
if data.count is 0
$todoPendingCount.addClass 'hidden'
else
$todoPendingCount.removeClass 'hidden'
if data.todo?
$btn
.attr 'aria-label', $btn.data('mark-text')
.attr 'data-id', data.todo.id
$btnText.text $btn.data('mark-text')
else
$btn
.attr 'aria-label', $btn.data('todo-text')
.removeAttr 'data-id'
$btnText.text $btn.data('todo-text')
sidebarDropdownLoading: (e) ->
$sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon')
......@@ -117,5 +166,3 @@ class @Sidebar
getBlock: (name) ->
@sidebar.find(".block.#{name}")
......@@ -67,8 +67,12 @@ class @SearchAutocomplete
getData: (term, callback) ->
_this = @
# Do not trigger request if input is empty
return if @searchInput.val() is ''
unless term
if contents = @getCategoryContents()
@searchInput.data('glDropdown').filter.options.callback contents
@enableAutocomplete()
return
# Prevent multiple ajax calls
return if @loadingSuggestions
......@@ -122,6 +126,37 @@ class @SearchAutocomplete
).always ->
_this.loadingSuggestions = false
getCategoryContents: ->
userId = gon.current_user_id
{ utils, projectOptions, groupOptions, dashboardOptions } = gl
if utils.isInGroupsPage() and groupOptions
options = groupOptions[utils.getGroupSlug()]
else if utils.isInProjectPage() and projectOptions
options = projectOptions[utils.getProjectSlug()]
else if dashboardOptions
options = dashboardOptions
{ issuesPath, mrPath, name } = options
items = [
{ header: "#{name}" }
{ text: 'Issues assigned to me', url: "#{issuesPath}/?assignee_id=#{userId}" }
{ text: "Issues I've created", url: "#{issuesPath}/?author_id=#{userId}" }
'separator'
{ text: 'Merge requests assigned to me', url: "#{mrPath}/?assignee_id=#{userId}" }
{ text: "Merge requests I've created", url: "#{mrPath}/?author_id=#{userId}" }
]
items.splice 0, 1 unless name
return items
serializeState: ->
{
# Search Criteria
......@@ -209,6 +244,12 @@ class @SearchAutocomplete
@isFocused = true
@wrap.addClass('search-active')
@getData() if @getValue() is ''
getValue: -> return @searchInput.val()
onClearInputClick: (e) =>
e.preventDefault()
@searchInput.val('').focus()
......@@ -229,6 +270,10 @@ class @SearchAutocomplete
@locationBadgeEl.text(badgeText).show()
@wrap.addClass('has-location-badge')
hasLocationBadge: -> return @wrap.is '.has-location-badge'
restoreOriginalState: ->
inputs = Object.keys @originalState
......@@ -257,13 +302,14 @@ class @SearchAutocomplete
@getElement("##{input}").val('')
removeLocationBadge: ->
@locationBadgeEl.hide()
# Reset state
@locationBadgeEl.hide()
@resetSearchState()
@wrap.removeClass('has-location-badge')
@disableAutocomplete()
disableAutocomplete: ->
@searchInput.addClass('disabled')
......
class @Shortcuts
constructor: ->
constructor: (skipResetBindings) ->
@enabledHelp = []
Mousetrap.reset()
Mousetrap.reset() if not skipResetBindings
Mousetrap.bind('?', @onToggleHelp)
Mousetrap.bind('s', Shortcuts.focusSearch)
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview)
......
#= require shortcuts
class @ShortcutsBlob extends Shortcuts
constructor: (skipResetBindings) ->
super skipResetBindings
Mousetrap.bind('y', ShortcutsBlob.copyToClipboard)
@copyToClipboard: ->
clipboardButton = $('.btn-clipboard')
clipboardButton.click() if clipboardButton
......@@ -10,14 +10,6 @@ class @ShortcutsIssuable extends ShortcutsNavigation
@replyWithSelectedText()
return false
)
Mousetrap.bind('j', =>
@prevIssue()
return false
)
Mousetrap.bind('k', =>
@nextIssue()
return false
)
Mousetrap.bind('e', =>
@editIssue()
return false
......@@ -29,16 +21,6 @@ class @ShortcutsIssuable extends ShortcutsNavigation
else
@enabledHelp.push('.hidden-shortcut.issues')
prevIssue: ->
$prevBtn = $('.prev-btn')
if not $prevBtn.hasClass('disabled')
Turbolinks.visit($prevBtn.attr('href'))
nextIssue: ->
$nextBtn = $('.next-btn')
if not $nextBtn.hasClass('disabled')
Turbolinks.visit($nextBtn.attr('href'))
replyWithSelectedText: ->
if window.getSelection
selected = window.getSelection().toString()
......
......@@ -3,24 +3,35 @@ expanded = 'page-sidebar-expanded'
toggleSidebar = ->
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
$('header').toggleClass("header-collapsed header-expanded")
$('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
$.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
$('.navbar-fixed-top').toggleClass("header-collapsed header-expanded")
if $.cookie('pin_nav') is 'true'
$('.navbar-fixed-top').toggleClass('header-pinned-nav')
$('.page-with-sidebar').toggleClass('page-sidebar-pinned')
setTimeout ( ->
niceScrollBars = $('.nicescroll').niceScroll();
niceScrollBars = $('.nav-sidebar').niceScroll();
niceScrollBars.updateScrollBar();
), 300
$(document)
.off 'click', 'body'
.on 'click', 'body', (e) ->
unless $.cookie('pin_nav') is 'true'
$target = $(e.target)
$nav = $target.closest('.sidebar-wrapper')
pageExpanded = $('.page-with-sidebar').hasClass('page-sidebar-expanded')
$toggle = $target.closest('.toggle-nav-collapse, .side-nav-toggle')
if $nav.length is 0 and pageExpanded and $toggle.length is 0
$('.page-with-sidebar')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
$('.navbar-fixed-top')
.toggleClass('header-collapsed header-expanded')
$(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) ->
e.preventDefault()
toggleSidebar()
)
$ ->
size = bp.getBreakpointSize()
if size is "xs" or size is "sm"
if $('.page-with-sidebar').hasClass(expanded)
toggleSidebar()
......@@ -9,9 +9,11 @@ class @Star
$this.parent().find('.star-count').text data.star_count
if isStarred
$starSpan.removeClass('starred').text 'Star'
gl.utils.updateTooltipTitle $this, 'Star project'
$starIcon.removeClass('fa-star').addClass 'fa-star-o'
else
$starSpan.addClass('starred').text 'Unstar'
gl.utils.updateTooltipTitle $this, 'Unstar project'
$starIcon.removeClass('fa-star-o').addClass 'fa-star'
return
......
......@@ -19,3 +19,8 @@ class @Subscription
action = if status == 'subscribed' then 'Unsubscribe' else 'Subscribe'
btn.find('span').text(action)
@subscription_status.find('>div').toggleClass('hidden')
if btn.attr('data-original-title')
btn.tooltip('hide')
.attr('data-original-title', action)
.tooltip('fixTitle')
# Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
#
# State Flow #1: setup -> in_progress -> authenticated -> POST to server
# State Flow #2: setup -> in_progress -> error -> setup
class @U2FAuthenticate
constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id
@challenges = u2fParams.challenges
@signRequests = u2fParams.sign_requests
start: () =>
if U2FUtil.isU2FSupported()
@renderSetup()
else
@renderNotSupported()
authenticate: () =>
u2f.sign(@appId, @challenges, @signRequests, (response) =>
if response.errorCode
error = new U2FError(response.errorCode)
@renderError(error);
else
@renderAuthenticated(JSON.stringify(response))
, 10)
#############
# Rendering #
#############
templates: {
"notSupported": "#js-authenticate-u2f-not-supported",
"setup": '#js-authenticate-u2f-setup',
"inProgress": '#js-authenticate-u2f-in-progress',
"error": '#js-authenticate-u2f-error',
"authenticated": '#js-authenticate-u2f-authenticated'
}
renderTemplate: (name, params) =>
templateString = $(@templates[name]).html()
template = _.template(templateString)
@container.html(template(params))
renderSetup: () =>
@renderTemplate('setup')
@container.find('#js-login-u2f-device').on('click', @renderInProgress)
renderInProgress: () =>
@renderTemplate('inProgress')
@authenticate()
renderError: (error) =>
@renderTemplate('error', {error_message: error.message()})
@container.find('#js-u2f-try-again').on('click', @renderSetup)
renderAuthenticated: (deviceResponse) =>
@renderTemplate('authenticated')
# Prefer to do this instead of interpolating using Underscore templates
# because of JSON escaping issues.
@container.find("#js-device-response").val(deviceResponse)
renderNotSupported: () =>
@renderTemplate('notSupported')
class @U2FError
constructor: (@errorCode) ->
@httpsDisabled = (window.location.protocol isnt 'https:')
console.error("U2F Error Code: #{@errorCode}")
message: () =>
switch
when (@errorCode is u2f.ErrorCodes.BAD_REQUEST and @httpsDisabled)
"U2F only works with HTTPS-enabled websites. Contact your administrator for more details."
when @errorCode is u2f.ErrorCodes.DEVICE_INELIGIBLE
"This device has already been registered with us."
else
"There was a problem communicating with your device."
# Register U2F (universal 2nd factor) devices for users to authenticate with.
#
# State Flow #1: setup -> in_progress -> registered -> POST to server
# State Flow #2: setup -> in_progress -> error -> setup
class @U2FRegister
constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id
@registerRequests = u2fParams.register_requests
@signRequests = u2fParams.sign_requests
start: () =>
if U2FUtil.isU2FSupported()
@renderSetup()
else
@renderNotSupported()
register: () =>
u2f.register(@appId, @registerRequests, @signRequests, (response) =>
if response.errorCode
error = new U2FError(response.errorCode)
@renderError(error);
else
@renderRegistered(JSON.stringify(response))
, 10)
#############
# Rendering #
#############
templates: {
"notSupported": "#js-register-u2f-not-supported",
"setup": '#js-register-u2f-setup',
"inProgress": '#js-register-u2f-in-progress',
"error": '#js-register-u2f-error',
"registered": '#js-register-u2f-registered'
}
renderTemplate: (name, params) =>
templateString = $(@templates[name]).html()
template = _.template(templateString)
@container.html(template(params))
renderSetup: () =>
@renderTemplate('setup')
@container.find('#js-setup-u2f-device').on('click', @renderInProgress)
renderInProgress: () =>
@renderTemplate('inProgress')
@register()
renderError: (error) =>
@renderTemplate('error', {error_message: error.message()})
@container.find('#js-u2f-try-again').on('click', @renderSetup)
renderRegistered: (deviceResponse) =>
@renderTemplate('registered')
# Prefer to do this instead of interpolating using Underscore templates
# because of JSON escaping issues.
@container.find("#js-device-response").val(deviceResponse)
renderNotSupported: () =>
@renderTemplate('notSupported')
# Helper class for U2F (universal 2nd factor) device registration and authentication.
class @U2FUtil
@isU2FSupported: ->
if @testMode
true
else
gon.u2f.browser_supports_u2f
@enableTestMode: ->
@testMode = true
<% if Rails.env.test? %>
U2FUtil.enableTestMode();
<% end %>
......@@ -122,6 +122,9 @@ class @UserTabs
@parentEl.find(tabSelector).html(data.html)
@loaded[action] = true
# Fix tooltips
gl.utils.localTimeAgo($('.js-timeago', tabSelector))
loadActivities: (source) ->
return if @loaded['activity'] is true
......
......@@ -6,12 +6,6 @@ class @Calendar
@daySizeWithSpace = @daySize + (@daySpace * 2)
@monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
@months = []
@highestValue = 0
# Get the highest value from the timestampes
_.each timestamps, (count) =>
if count > @highestValue
@highestValue = count
# Loop through the timestamps to create a group of objects
# The group of objects will be grouped based on the day of the week they are
......@@ -39,8 +33,8 @@ class @Calendar
i++
# Init color functions
@color = @initColor()
@colorKey = @initColorKey()
@color = @initColor()
# Init the svg element
@renderSvg(group)
......@@ -104,7 +98,7 @@ class @Calendar
.attr 'class', 'user-contrib-cell js-tooltip'
.attr 'fill', (stamp) =>
if stamp.count isnt 0
@color(stamp.count)
@color(Math.min(stamp.count, 40))
else
'#ededed'
.attr 'data-container', 'body'
......@@ -164,10 +158,11 @@ class @Calendar
color
initColor: ->
colorRange = ['#ededed', @colorKey(0), @colorKey(1), @colorKey(2), @colorKey(3)]
d3.scale
.linear()
.range(['#acd5f2', '#254e77'])
.domain([0, @highestValue])
.threshold()
.domain([0, 10, 20, 30])
.range(colorRange)
initColorKey: ->
d3.scale
......
......@@ -31,7 +31,7 @@ class @UsersSelect
assignTo = (selected) ->
data = {}
data[abilityName] = {}
data[abilityName].assignee_id = selected
data[abilityName].assignee_id = if selected? then selected else null
$loading
.fadeIn()
$dropdown.trigger('loading.gl.dropdown')
......@@ -72,7 +72,7 @@ class @UsersSelect
assigneeTemplate = _.template(
'<% if (username) { %>
<a class="author_link " href="/u/<%= username %>">
<a class="author_link bold" href="/u/<%= username %>">
<% if( avatar ) { %>
<img width="32" class="avatar avatar-inline s32" alt="" src="<%= avatar %>">
<% } %>
......@@ -82,7 +82,7 @@ class @UsersSelect
</span>
</a>
<% } else { %>
<span class="assign-yourself">
<span class="no-value assign-yourself">
No assignee -
<a href="#" class="js-assign-yourself">
assign yourself
......@@ -95,7 +95,7 @@ class @UsersSelect
data: (term, callback) =>
isAuthorFilter = $('.js-author-search')
@users term, term is '' and isAuthorFilter, (users) =>
@users term, (users) =>
if term.length is 0
showDivider = 0
......@@ -149,7 +149,7 @@ class @UsersSelect
hidden: (e) ->
$selectbox.hide()
# display:block overrides the hide-collapse rule
$value.removeAttr('style')
$value.css('display', '')
clicked: (user) ->
page = $('body').data 'page'
......@@ -221,7 +221,7 @@ class @UsersSelect
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) =>
@users query.term, @projectId?, (users) =>
@users query.term, (users) =>
data = { results: users }
if query.term.length == 0
......@@ -304,7 +304,7 @@ class @UsersSelect
# Return users list. Filtered by query
# Only active users retrieved
users: (query, fromProject, callback) =>
users: (query, callback) =>
url = @buildUrl(@usersPath)
$.ajax(
......@@ -313,7 +313,7 @@ class @UsersSelect
search: query
per_page: 20
active: true
project_id: @projectId if fromProject
project_id: @projectId
group_id: @groupId
current_user: @showCurrentUser
author_id: @authorId
......
......@@ -61,6 +61,11 @@
margin-bottom: -$gl-padding;
}
&.content-component-block {
padding: 11px 0;
background-color: $white-light;
}
.title {
color: $gl-text-color;
}
......@@ -86,6 +91,10 @@
background-color: $white-light;
border-top: none;
}
&.top-block .container-fluid {
background-color: inherit;
}
}
.cover-block {
......
......@@ -79,6 +79,23 @@
@include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
}
@mixin btn-with-margin {
margin-left: $btn-side-margin;
float: left;
&.inline {
float: none;
}
&.btn-sm {
margin-left: $btn-sm-side-margin;
}
&.btn-xs {
margin-left: $btn-xs-side-margin;
}
}
.btn {
@include btn-default;
@include btn-white;
......@@ -142,15 +159,9 @@
}
&.btn-grouped {
margin-right: 7px;
float: left;
&:last-child {
margin-right: 0;
}
&.btn-xs {
margin-right: 3px;
}
@include btn-with-margin;
}
&.disabled {
pointer-events: auto !important;
}
......@@ -192,11 +203,7 @@
.btn-group {
&.btn-grouped {
margin-right: 7px;
float: left;
&:last-child {
margin-right: 0;
}
@include btn-with-margin;
}
}
......
......@@ -122,10 +122,9 @@
a {
display: block;
position: relative;
padding-left: 10px;
padding-right: 10px;
padding: 5px 10px;
color: $dropdown-link-color;
line-height: 34px;
line-height: initial;
text-overflow: ellipsis;
border-radius: 2px;
white-space: nowrap;
......@@ -162,6 +161,16 @@
}
}
.dropdown-menu-large {
width: 340px;
}
.dropdown-menu-no-wrap {
a {
white-space: normal;
}
}
.dropdown-menu-full-width {
width: 100%;
}
......@@ -236,8 +245,7 @@
&::before {
position: absolute;
left: 5px;
top: 50%;
margin-top: -7px;
top: 8px;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
......@@ -532,3 +540,14 @@
background-color: $calendar-unselectable-bg;
}
}
.dropdown-menu-inner-title {
display: block;
color: $gl-title-color;
font-weight: 600;
}
.dropdown-menu-inner-content {
display: block;
color: $gl-placeholder-color;
}
......@@ -76,6 +76,7 @@ label {
.form-control {
@include box-shadow(none);
border-radius: 3px;
padding: $gl-vert-padding $gl-input-padding;
}
.select-wrapper {
......
......@@ -8,34 +8,16 @@
*/
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
.page-with-sidebar {
.header-logo {
background: $color-darker;
a {
color: $color-light;
h3 {
color: $color-light;
}
}
.toggle-nav-collapse,
.pin-nav-btn {
color: $color-light;
background: $color;
&:hover {
background-color: $color-dark;
a {
color: #fff;
h3 {
color: #fff;
}
}
color: $white-light;
}
}
.collapse-nav a {
color: #fff;
background: $color;
}
.sidebar-wrapper {
background: $color-darker;
......@@ -45,7 +27,7 @@
&:hover {
background-color: $color-dark;
color: #fff;
color: $white-light;
text-decoration: none;
}
}
......@@ -63,10 +45,20 @@
color: $color-light;
}
path,
polygon {
fill: $color-light;
}
.count {
color: $color-light;
background: $color-dark;
}
svg {
position: relative;
top: 3px;
}
}
&.separate-item {
......@@ -74,7 +66,7 @@
}
&.active a {
color: #fff;
color: $white-light;
background: $color-dark;
&.no-highlight {
......@@ -82,15 +74,23 @@
}
i {
color: #fff
color: $white-light
}
path,
polygon {
fill: $white-light;
}
}
}
}
}
$theme-blue: #2980b9;
$theme-charcoal: #3d454d;
$theme-charcoal-dark: #383f45;
$theme-charcoal-text: #b9bbbe;
$theme-blue: #2980b9;
$theme-graphite: #666;
$theme-gray: #373737;
$theme-green: #019875;
......@@ -102,7 +102,7 @@ body {
}
&.ui_charcoal {
@include gitlab-theme(#d6d7d9, #485157, $theme-charcoal, #353b41);
@include gitlab-theme($theme-charcoal-text, #485157, $theme-charcoal, $theme-charcoal-dark);
}
&.ui_graphite {
......
......@@ -2,8 +2,19 @@
* Application Header
*
*/
@mixin tanuki-logo-colors($path-color) {
fill: $path-color;
transition: all 0.8s;
&:hover,
&.highlight {
fill: lighten($path-color, 25%);
transition: all 0.1s;
}
}
header {
transition-duration: .3s;
transition: padding $sidebar-transition-duration;
&.navbar-empty {
height: $header-height;
......@@ -82,10 +93,10 @@ header {
}
.side-nav-toggle {
display: none;
position: absolute;
left: -10px;
margin: 6px 0;
font-size: 18px;
padding: 6px 10px;
border: none;
background-color: $background-color;
......@@ -97,21 +108,13 @@ header {
&:focus {
outline: none;
}
@media (max-width: $screen-xs-min) {
display: block;
}
}
}
.header-content {
position: relative;
height: $header-height;
padding-right: 40px;
@media (max-width: $screen-xs-min) {
padding-left: 40px;
}
padding-left: 30px;
@media (min-width: $screen-sm-min) {
padding-right: 0;
......@@ -121,9 +124,29 @@ header {
margin-top: -5px;
}
.header-logo {
position: absolute;
left: 50%;
margin-left: -18px;
top: 7px;
transition-duration: .3s;
z-index: 999;
&:hover {
cursor: pointer;
}
@media (max-width: $screen-xs-max) {
right: 25px;
left: auto;
}
}
.title {
margin: 0;
font-size: 19px;
max-width: 400px;
display: inline-block;
line-height: $header-height;
font-weight: normal;
color: $gl-text-color;
......@@ -132,6 +155,10 @@ header {
vertical-align: top;
white-space: nowrap;
@media (max-width: $screen-sm-max) {
max-width: 190px;
}
a {
color: $gl-text-color;
&:hover {
......@@ -159,6 +186,10 @@ header {
.navbar-collapse {
float: right;
border-top: none;
@media (max-width: $screen-xs-max) {
float: none;
}
}
}
......@@ -171,32 +202,24 @@ header {
}
}
@mixin collapsed-header {
margin-left: $sidebar_collapsed_width;
}
.header-collapsed {
margin-left: $sidebar_collapsed_width;
#tanuki-logo {
@media (min-width: $screen-md-min) {
@include collapsed-header;
#tanuki-left-ear,
#tanuki-right-ear,
#tanuki-nose {
@include tanuki-logo-colors($tanuki-red);
}
@media (max-width: $screen-xs-min) {
margin-left: 0;
#tanuki-left-eye,
#tanuki-right-eye {
@include tanuki-logo-colors($tanuki-orange);
}
}
.header-expanded {
margin-left: $sidebar_collapsed_width;
@media (min-width: $screen-md-min) {
margin-left: $sidebar_width;
#tanuki-left-cheek,
#tanuki-right-cheek {
@include tanuki-logo-colors($tanuki-yellow);
}
@media (max-width: $screen-xs-min) {
margin-left: 0;
}
}
@media (max-width: $screen-xs-max) {
......
......@@ -2,6 +2,7 @@
font-family: $regular_font;
font-size: $font-size-base;
&.ui-datepicker,
&.ui-datepicker-inline {
border: 1px solid #ddd;
padding: 10px;
......@@ -10,6 +11,25 @@
.ui-datepicker-header {
background: #fff;
border-color: #ddd;
.ui-datepicker-prev,
.ui-datepicker-next {
top: 4px;
}
.ui-datepicker-prev {
left: 2px;
}
.ui-datepicker-next {
right: 2px;
}
.ui-state-hover {
background: transparent;
border: 0;
cursor: pointer;
}
}
.ui-datepicker-calendar td a {
......@@ -36,21 +56,18 @@
}
.ui-state-highlight {
border: 1px solid #eee;
background: #eee;
border: 0;
background: transparent;
}
.ui-state-active {
border: 1px solid $gl-primary;
background: $gl-primary;
color: #fff;
}
.ui-state-hover,
.ui-state-focus {
border: 1px solid $row-hover;
background: $row-hover;
color: #333;
.ui-datepicker-calendar {
.ui-state-active,
.ui-state-hover,
.ui-state-focus {
border: 1px solid $gl-primary;
background: $gl-primary;
color: #fff;
}
}
}
......
......@@ -137,10 +137,30 @@ ul.content-list {
padding-top: 1px;
float: right;
.btn {
padding: 10px 14px;
> .btn,
> .btn-group {
margin-right: $gl-padding-top;
display: inline-block;
margin-top: 4px;
margin-bottom: 4px;
&:last-child {
margin-right: 0;
}
}
}
// When dragging a list item
&.ui-sortable-helper {
border-bottom: none;
}
&.list-placeholder {
background-color: $gray-light;
border: dotted 1px $gray-dark;
margin: 1px 0;
min-height: 52px;
}
}
}
......
......@@ -66,10 +66,6 @@
display: none;
}
%ul.notes .note-role, .note-actions {
display: none;
}
.nav-links, .nav-links {
li a {
font-size: 14px;
......
@mixin fade($gradient-direction, $rgba, $gradient-color) {
visibility: visible;
opacity: 1;
z-index: 2;
position: absolute;
bottom: 12px;
width: 43px;
......@@ -41,8 +42,7 @@
a {
display: inline-block;
padding: 14px;
padding-top: $gl-padding;
padding: $gl-btn-padding;
padding-bottom: 11px;
margin-bottom: -1px;
font-size: 15px;
......@@ -67,6 +67,29 @@
color: #78a;
}
}
&.sub-nav {
text-align: center;
background-color: $background-color;
.container-fluid {
background-color: $background-color;
margin-bottom: 0;
}
li {
a {
margin: 0;
padding: 11px 10px 9px;
}
&.active a {
border-bottom: none;
color: $link-underline-blue;
}
}
}
}
.top-area {
......@@ -81,6 +104,10 @@
width: 50%;
line-height: 28px;
&.wiki-page {
padding: 16px 10px 11px;
}
/* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-sm-min) {
width: 100%;
......@@ -104,6 +131,10 @@
margin-bottom: 0;
border-bottom: none;
li a {
padding: 16px 10px 11px;
}
/* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-sm-max) {
width: 100%;
......@@ -179,7 +210,7 @@
@media (max-width: $screen-xs-max) {
padding-bottom: 0;
width: 100%;
.btn, form, .dropdown, .dropdown-menu-toggle, .form-control {
margin: 0 0 10px;
display: block;
......@@ -210,15 +241,11 @@
margin: 0;
}
}
}
/* Small devices (tablets, 768px and lower) */
@media (max-width: $screen-sm-max) {
width: 100%;
text-align: left;
input {
width: 300px;
}
&.adjust {
.nav-text, .nav-controls {
width: auto;
}
}
}
......@@ -230,7 +257,8 @@
z-index: 11;
background: $background-color;
border-bottom: 1px solid $border-color;
transition-duration: .3s;
transition: padding $sidebar-transition-duration;
text-align: center;
.container-fluid {
position: relative;
......@@ -259,11 +287,10 @@
}
.dropdown {
margin-left: 7px;
@media (max-width: $screen-xs-min) {
margin-left: 0;
}
position: absolute;
top: 7px;
right: 15px;
z-index: 2;
li.active {
font-weight: bold;
......@@ -276,6 +303,19 @@
border-bottom: none;
height: 51px;
svg {
position: relative;
top: 2px;
margin-right: 2px;
height: 15px;
width: auto;
path,
polygon {
fill: $layout-link-gray;
}
}
.fade-right {
@include fade(left, rgba(250, 250, 250, 0.4), $background-color);
right: 0;
......@@ -297,22 +337,36 @@
}
&.active {
a, i {
color: $black;
}
svg {
path,
polygon {
fill: $black;
}
}
}
.badge {
color: $gl-icon-color;
}
&:hover {
a, i {
color: $black;
}
}
}
}
.nav-control {
.fade-right {
.fade-right {
@media (min-width: $screen-xs-max) {
right: 67px;
right: 68px;
}
@media (max-width: $screen-xs-min) {
right: 0;
......@@ -321,6 +375,24 @@
}
}
.scrolling-tabs-container {
position: relative;
.nav-links {
@include scrolling-links();
.fade-right {
@include fade(left, rgba(255, 255, 255, 0.4), $background-color);
right: 0;
}
.fade-left {
@include fade(right, rgba(255, 255, 255, 0.4), $background-color);
left: 0;
}
}
}
.nav-block {
position: relative;
......
......@@ -8,7 +8,7 @@
background: #fff;
border-color: $input-border;
height: 35px;
padding: $gl-vert-padding $gl-btn-padding;
padding: $gl-vert-padding $gl-input-padding;
font-size: $gl-font-size;
line-height: 1.42857143;
border-radius: $border-radius-base;
......
#logo {
z-index: 2;
position: absolute;
width: 58px;
cursor: pointer;
margin-top: 8px;
}
.page-with-sidebar {
padding-top: $header-height;
transition-duration: .3s;
transition: padding $sidebar-transition-duration;
.sidebar-wrapper {
position: fixed;
top: 0;
bottom: 0;
overflow-y: auto;
overflow-x: hidden;
left: 0;
height: 100%;
transition-duration: .3s;
}
.gitlab-text-container-link {
z-index: 1;
position: absolute;
left: 0;
overflow: hidden;
transition: width $sidebar-transition-duration;
}
}
.sidebar-wrapper {
z-index: 1000;
background: $background-color;
.nicescroll-rails-hr {
// TODO: Figure out why nicescroll doesn't hide horizontal bar
display: none!important;
}
}
.content-wrapper {
width: 100%;
transition: padding $sidebar-transition-duration;
.container-fluid {
background: #fff;
......@@ -48,291 +39,164 @@
}
}
.sidebar-wrapper {
.header-logo {
border-bottom: 1px solid transparent;
float: left;
height: $header-height;
width: $sidebar_width;
position: fixed;
z-index: 999;
overflow: hidden;
transition-duration: .3s;
a {
float: left;
height: $header-height;
width: 100%;
padding-left: 22px;
overflow: hidden;
outline: none;
transition-duration: .3s;
img {
width: 36px;
height: 36px;
}
#tanuki-logo, img {
float: left;
}
.gitlab-text-container {
width: 230px;
h3 {
width: 158px;
float: left;
margin: 0;
margin-left: 50px;
font-size: 19px;
line-height: 50px;
font-weight: normal;
}
}
}
&:hover {
background-color: #eee;
}
}
.sidebar-user {
padding: 7px 22px;
position: fixed;
bottom: 40px;
width: $sidebar_width;
overflow: hidden;
transition-duration: .3s;
.sidebar-user {
padding: 15px;
position: absolute;
left: 0;
bottom: 0;
width: $sidebar_width;
overflow: hidden;
font-size: 16px;
line-height: 36px;
transition: width $sidebar-transition-duration, padding $sidebar-transition-duration;
.username {
margin-left: 10px;
width: $sidebar_width - 2 * 10px;
font-size: 16px;
line-height: 34px;
}
@media (min-width: $sidebar-breakpoint) {
bottom: 50px;
}
}
.nav-sidebar {
position: absolute;
top: 50px;
bottom: 65px;
width: $sidebar_width;
overflow-y: auto;
overflow-x: hidden;
.tanuki-shape {
transition: all 0.8s;
&:hover, &.highlight {
fill: rgb(255, 255, 255);
transition: all 0.1s;
@media (min-width: $sidebar-breakpoint) {
bottom: 115px;
}
}
.nav-sidebar {
margin-top: 14 + $header-height;
margin-bottom: 100px;
transition-duration: .3s;
list-style: none;
overflow: hidden;
&.navbar-collapse {
padding: 0 !important;
}
li {
width: $sidebar_width;
&.separate-item {
padding-top: 10px;
margin-top: 10px;
}
.icon-container {
width: 34px;
display: inline-block;
text-align: center;
}
a {
padding: 7px 15px;
padding: 7px 15px 7px 12px;
font-size: $gl-font-size;
line-height: 24px;
color: $gray;
display: block;
text-decoration: none;
padding-left: 23px;
font-weight: normal;
outline: none;
white-space: nowrap;
&:hover {
text-decoration: none;
}
&:active, &:focus {
&:hover,
&:active,
&:focus {
text-decoration: none;
}
i {
width: 16px;
color: $gray-light;
margin-right: 13px;
font-size: 16px;
}
.count {
float: right;
background: #eee;
padding: 0 8px;
@include border-radius(6px);
}
&.back-link i {
transition-duration: .3s;
i,
svg {
margin-right: 13px;
}
}
}
}
.sidebar-subnav {
margin-left: 0;
padding-left: 0;
li {
list-style: none;
.count {
float: right;
padding: 0 8px;
@include border-radius(6px);
}
}
.collapse-nav a {
.toggle-nav-collapse {
width: $sidebar_width;
position: fixed;
bottom: 0;
position: absolute;
top: 0;
left: 0;
font-size: 13px;
background: transparent;
height: 40px;
text-align: center;
line-height: 40px;
min-height: 50px;
padding: 5px 0;
font-size: 18px;
line-height: 30px;
}
.nav-header-btn {
padding: 10px 5px;
color: inherit;
transition-duration: .3s;
outline: none;
&:hover {
&:hover,
&:focus {
color: $white-light;
text-decoration: none;
}
}
.sidebar-wrapper {
&.hidden-nav {
width: 0;
}
}
.page-sidebar-collapsed {
padding-left: $sidebar_collapsed_width;
.pin-nav-btn {
display: none;
position: absolute;
left: 0;
bottom: 0;
height: 50px;
width: $sidebar_width;
line-height: 30px;
@media (max-width: $screen-xs-min) {
padding-left: 0;
@media (min-width: $sidebar-breakpoint) {
display: block;
}
.sidebar-wrapper {
width: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
width: 0;
}
.header-logo {
width: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
width: 0;
}
a {
padding-left: ($sidebar_collapsed_width - 36) / 2;
.gitlab-text-container {
display: none;
}
}
}
.nav-sidebar {
width: $sidebar_collapsed_width;
li {
width: auto;
a {
span {
display: none;
}
}
}
}
.collapse-nav a {
width: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
width: 0;
}
}
.sidebar-user {
padding-left: ($sidebar_collapsed_width - 36) / 2;
width: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
width: 0;
padding-left: 0;
padding-right: 0;
}
.username {
display: none;
}
}
.fa {
transition: transform .15s;
}
.layout-nav {
padding-right: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
padding-right: 0;;
&.is-active {
.fa {
transform: rotate(90deg);
}
}
}
.page-sidebar-expanded {
padding-left: $sidebar_collapsed_width;
@media (min-width: $screen-md-min) {
padding-left: $sidebar_width;
}
.page-sidebar-collapsed {
padding-left: 0;
@media (max-width: $screen-xs-min) {
padding-left: 0;
.sidebar-wrapper {
width: 0;
}
}
.page-sidebar-expanded {
.sidebar-wrapper {
width: $sidebar_width;
.nav-sidebar {
width: $sidebar_width;
}
.nav-sidebar li a {
width: $sidebar_width;
&.back-link {
i {
opacity: 0;
}
}
}
}
}
.page-sidebar-pinned {
.content-wrapper,
.layout-nav {
@media (max-width: $screen-xs-min) {
padding-right: 0;
@media (min-width: $sidebar-breakpoint) {
padding-left: $sidebar_width;
}
}
}
@media (min-width: $screen-xs-min) and (max-width: $screen-md-min) {
padding-right: 62px;
header.header-pinned-nav {
@media (min-width: $sidebar-breakpoint) {
padding-left: ($sidebar_width + $gl-padding);
.side-nav-toggle {
display: none;
}
@media (min-width: $screen-md-min) {
padding-right: $sidebar_width;
.header-content {
padding-left: 0;
}
}
}
......@@ -353,7 +217,9 @@
padding-right: 0;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
padding-right: $sidebar_collapsed_width;
&:not(.build-sidebar) {
padding-right: $sidebar_collapsed_width;
}
}
@media (min-width: $screen-md-min) {
......
......@@ -5,7 +5,7 @@
padding: 0;
.timeline-entry {
padding: $gl-padding $gl-btn-padding;
padding: $gl-padding $gl-btn-padding 11px;
border-color: $table-border-color;
color: $gl-gray;
border-bottom: 1px solid $border-white-light;
......
......@@ -192,3 +192,8 @@
.text-info:hover {
color: $brand-info;
}
// Prevent datetimes on tooltips to break into two lines
.local-timeago {
white-space: nowrap;
}
......@@ -6,6 +6,8 @@ $sidebar_width: 220px;
$gutter_collapsed_width: 62px;
$gutter_width: 290px;
$gutter_inner_width: 258px;
$sidebar-transition-duration: .15s;
$sidebar-breakpoint: 1440px;
/*
* UI elements
......@@ -57,6 +59,7 @@ $code_line_height: 1.5;
*/
$gl-padding: 16px;
$gl-btn-padding: 10px;
$gl-input-padding: 10px;
$gl-vert-padding: 6px;
$gl-padding-top: 10px;
......@@ -79,6 +82,9 @@ $provider-btn-not-active-color: #4688f1;
$link-underline-blue: #4a8bee;
$layout-link-gray: #7e7c7c;
$todo-alert-blue: #428bca;
$btn-side-margin: 10px;
$btn-sm-side-margin: 7px;
$btn-xs-side-margin: 5px;
/*
* Color schema
......@@ -121,7 +127,7 @@ $border-white-normal: #d6dae2;
$border-white-dark: #c6cacf;
$border-gray-light: #dcdcdc;
$border-gray-normal: rgba(0, 0, 0, 0.10);
$border-gray-normal: #d7d7d7;
$border-gray-dark: #c6cacf;
$border-green-light: #2faa60;
......@@ -150,6 +156,11 @@ $warning-message-border: #f0e2bb;
/* header */
$light-grey-header: #faf9f9;
/* tanuki logo colors */
$tanuki-red: #e24329;
$tanuki-orange: #fc6d26;
$tanuki-yellow: #fca326;
/*
* State colors:
*/
......@@ -256,3 +267,11 @@ $calendar-header-color: #b8b8b8;
$calendar-hover-bg: #ecf3fe;
$calendar-border-color: rgba(#000, .1);
$calendar-unselectable-bg: #faf9f9;
/*
* Personal Access Tokens
*/
$personal-access-tokens-disabled-label-color: #bbb;
$ci-output-bg: #1d1f21;
$ci-text-color: #c5c8c6;
......@@ -38,6 +38,10 @@ table {
margin: 0 auto;
text-align: left;
width: 600px;
& > td {
text-align: center;
}
}
&#body {
......
@import "framework/variables";
// This file is largely copied from `highlight/white.scss`, but modified to
// avoid all descendant selectors (`table td`). This is because the CSS inlining
// we use performs dramatically worse on descendant selectors than the
// alternatives.
// <https://gitlab.com/gitlab-org/gitlab-ee/issues/490#note_12283632>
//
// DO NOT ADD ANY DESCENDANT SELECTORS TO THIS FILE. Instead, use (in order of
// preference): plain class selectors, type (element name) selectors, or
// explicit child selectors.
table.code {
width: 100%;
font-family: monospace;
......@@ -11,33 +21,162 @@ table.code {
-premailer-cellspacing: 0;
-premailer-width: 100%;
td {
> tr > td {
line-height: $code_line_height;
font-family: monospace;
font-size: $code_font_size;
&.diff-line-num {
margin: 0;
padding: 0;
border: none;
padding: 0 5px;
border-right: 1px solid;
text-align: right;
min-width: 35px;
max-width: 50px;
width: 35px;
}
&.line_content {
display: block;
margin: 0;
padding: 0 0.5em;
border: none;
white-space: pre;
}
}
}
.line-numbers, .diff-line-num {
background-color: $background-color;
}
.diff-line-num, .diff-line-num a {
color: $black-transparent;
}
td.diff-line-num {
margin: 0;
padding: 0;
border: none;
background: $background-color;
color: rgba(0, 0, 0, 0.3);
padding: 0 5px;
border-right: 1px solid $border-color;
text-align: right;
min-width: 35px;
max-width: 50px;
width: 35px;
pre.code, .diff-line-num {
border-color: $table-border-gray;
}
.code.white, pre.code, .line_content {
background-color: #fff;
color: #333;
}
.diff-line-num {
&.old {
background-color: $line-number-old;
border-color: $line-removed-dark;
}
td.line_content {
display: block;
margin: 0;
padding: 0 0.5em;
border: none;
white-space: pre;
&.new {
background-color: $line-number-new;
border-color: $line-added-dark;
}
&.hll:not(.empty-cell) {
background-color: $line-number-select;
border-color: $line-select-yellow-dark;
}
}
.line_content {
&.old {
background-color: $line-removed;
> .line > span.idiff, > .line > span > span.idiff {
background-color: $line-removed-dark;
}
}
&.new {
background-color: $line-added;
> .line > span.idiff, > .line > span > span.idiff {
background-color: $line-added-dark;
}
}
&.match {
color: $black-transparent;
background-color: $match-line;
}
&.hll:not(.empty-cell) {
background-color: $line-select-yellow;
}
}
pre > .hll {
background-color: #f8eec7 !important;
}
span.highlight_word {
background-color: #fafe3d !important;
}
@import "highlight/white";
.hll { background-color: #f8f8f8 }
.c { color: #998; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; }
.k { font-weight: bold; }
.o { font-weight: bold; }
.cm { color: #998; font-style: italic; }
.cp { color: #999; font-weight: bold; }
.c1 { color: #998; font-style: italic; }
.cs { color: #999; font-weight: bold; font-style: italic; }
.gd { color: #000; background-color: #fdd; }
.gd .x { color: #000; background-color: #faa; }
.ge { font-style: italic; }
.gr { color: #a00; }
.gh { color: #999; }
.gi { color: #000; background-color: #dfd; }
.gi .x { color: #000; background-color: #afa; }
.go { color: #888; }
.gp { color: #555; }
.gs { font-weight: bold; }
.gu { color: #800080; font-weight: bold; }
.gt { color: #a00; }
.kc { font-weight: bold; }
.kd { font-weight: bold; }
.kn { font-weight: bold; }
.kp { font-weight: bold; }
.kr { font-weight: bold; }
.kt { color: #458; font-weight: bold; }
.m { color: #099; }
.s { color: #d14; }
.n { color: #333; }
.na { color: teal; }
.nb { color: #0086b3; }
.nc { color: #458; font-weight: bold; }
.no { color: teal; }
.ni { color: purple; }
.ne { color: #900; font-weight: bold; }
.nf { color: #900; font-weight: bold; }
.nn { color: #555; }
.nt { color: navy; }
.nv { color: teal; }
.ow { font-weight: bold; }
.w { color: #bbb; }
.mf { color: #099; }
.mh { color: #099; }
.mi { color: #099; }
.mo { color: #099; }
.sb { color: #d14; }
.sc { color: #d14; }
.sd { color: #d14; }
.s2 { color: #d14; }
.se { color: #d14; }
.sh { color: #d14; }
.si { color: #d14; }
.sx { color: #d14; }
.sr { color: #009926; }
.s1 { color: #d14; }
.ss { color: #990073; }
.bp { color: #999; }
.vc { color: teal; }
.vg { color: teal; }
.vi { color: teal; }
.il { color: #099; }
.gc { color: #999; background-color: #eaf2f5; }
......@@ -6,19 +6,19 @@ p.details {
font-style: italic;
color: #777
}
.footer p {
.footer > p {
font-size: small;
color: #777
}
pre.commit-message {
white-space: pre-wrap;
}
.file-stats a {
.file-stats > a {
text-decoration: none;
}
.file-stats .new-file {
color: #090;
}
.file-stats .deleted-file {
color: #b00;
> .new-file {
color: #090;
}
> .deleted-file {
color: #b00;
}
}
......@@ -95,20 +95,30 @@
.award-control {
margin-right: 5px;
margin-bottom: 5px;
padding-left: 5px;
padding-right: 5px;
line-height: 20px;
outline: 0;
&:hover,
&.active,
&:active {
background-color: $white-dark;
background-color: $row-hover;
border-color: $row-hover-border;
box-shadow: none;
outline: 0;
}
&.btn {
&:focus {
outline: 0;
}
}
&.is-loading {
.award-control-icon-normal {
.award-control-icon-normal,
.emoji-icon {
display: none;
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -2,13 +2,21 @@
margin-bottom: 20px;
border-bottom: 1px solid #eee;
> h1 {
> h1, h2, h3, h4, h5, h6 {
font-weight: 400;
}
.lead {
margin-bottom: 20px;
}
ul, ol {
padding-left: 0;
}
li {
list-style-type: none;
}
}
.confirmation-content {
......
......@@ -66,8 +66,7 @@
font-family: $regular_font;
}
.gitignore-selector {
.gitignore-selector, .license-selector {
.dropdown {
line-height: 21px;
}
......
.environments {
.commit-title {
margin: 0;
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -74,6 +74,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:two_factor_grace_period,
:gravatar_enabled,
:sign_in_text,
:after_sign_up_text,
:help_page_text,
:home_page_url,
:after_sign_out_path,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment