Commit 142bbd90 authored by Jarka Kadlecova's avatar Jarka Kadlecova

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into 18608-lock-issues

# Conflicts:
#	app/assets/javascripts/notes/components/issue_comment_form.vue
parents fea7b9e6 b83dcd3a
...@@ -7,4 +7,5 @@ ...@@ -7,4 +7,5 @@
/vendor/ /vendor/
karma.config.js karma.config.js
webpack.config.js webpack.config.js
svg.config.js
/app/assets/javascripts/locale/**/*.js /app/assets/javascripts/locale/**/*.js
...@@ -176,11 +176,20 @@ build-package: ...@@ -176,11 +176,20 @@ build-package:
image: ruby:2.4-alpine image: ruby:2.4-alpine
before_script: before_script:
- gem install gitlab --no-doc - gem install gitlab --no-doc
# We need to download the script rather than clone the repo since the
# review-docs-cleanup job will not be able to run when the branch gets
# deleted (when merging the MR).
- apk add --update openssl
- wget https://gitlab.com/gitlab-org/gitlab-ce/raw/master/scripts/trigger-build-docs
- chmod 755 trigger-build-docs
services: [] services: []
cache: {}
dependencies: []
artifacts: {}
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false" USE_BUNDLE_INSTALL: "false"
cache: {} GIT_STRATEGY: none
when: manual when: manual
only: only:
- branches - branches
...@@ -197,7 +206,7 @@ review-docs-deploy: ...@@ -197,7 +206,7 @@ review-docs-deploy:
url: http://preview-$CI_COMMIT_REF_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX url: http://preview-$CI_COMMIT_REF_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
on_stop: review-docs-cleanup on_stop: review-docs-cleanup
script: script:
- scripts/trigger-build-docs deploy - ./trigger-build-docs deploy
# Cleanup remote environment of gitlab-docs # Cleanup remote environment of gitlab-docs
review-docs-cleanup: review-docs-cleanup:
...@@ -207,7 +216,7 @@ review-docs-cleanup: ...@@ -207,7 +216,7 @@ review-docs-cleanup:
name: review-docs/$CI_COMMIT_REF_NAME name: review-docs/$CI_COMMIT_REF_NAME
action: stop action: stop
script: script:
- scripts/trigger-build-docs cleanup - ./trigger-build-docs cleanup
# Retrieve knapsack and rspec_flaky reports # Retrieve knapsack and rspec_flaky reports
retrieve-tests-metadata: retrieve-tests-metadata:
......
This diff is collapsed.
...@@ -116,7 +116,7 @@ gem 'seed-fu', '~> 2.3.5' ...@@ -116,7 +116,7 @@ gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'deckar01-task_list', '2.0.0' gem 'deckar01-task_list', '2.0.0'
gem 'gitlab-markup', '~> 1.5.1' gem 'gitlab-markup', '~> 1.6.2'
gem 'redcarpet', '~> 3.4' gem 'redcarpet', '~> 3.4'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 4.2' gem 'rdoc', '~> 4.2'
...@@ -128,7 +128,7 @@ gem 'asciidoctor-plantuml', '0.0.7' ...@@ -128,7 +128,7 @@ gem 'asciidoctor-plantuml', '0.0.7'
gem 'rouge', '~> 2.0' gem 'rouge', '~> 2.0'
gem 'truncato', '~> 0.7.9' gem 'truncato', '~> 0.7.9'
gem 'bootstrap_form', '~> 2.7.0' gem 'bootstrap_form', '~> 2.7.0'
gem 'nokogiri', '~> 1.8.0' gem 'nokogiri', '~> 1.8.1'
# Diffs # Diffs
gem 'diffy', '~> 3.1.0' gem 'diffy', '~> 3.1.0'
......
...@@ -293,7 +293,7 @@ GEM ...@@ -293,7 +293,7 @@ GEM
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab-markup (1.5.1) gitlab-markup (1.6.2)
gitlab_omniauth-ldap (2.0.4) gitlab_omniauth-ldap (2.0.4)
net-ldap (~> 0.16) net-ldap (~> 0.16)
omniauth (~> 1.3) omniauth (~> 1.3)
...@@ -482,7 +482,7 @@ GEM ...@@ -482,7 +482,7 @@ GEM
mime-types (2.99.3) mime-types (2.99.3)
mimemagic (0.3.0) mimemagic (0.3.0)
mini_mime (0.1.4) mini_mime (0.1.4)
mini_portile2 (2.2.0) mini_portile2 (2.3.0)
minitest (5.7.0) minitest (5.7.0)
mmap2 (2.2.7) mmap2 (2.2.7)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
...@@ -496,8 +496,8 @@ GEM ...@@ -496,8 +496,8 @@ GEM
net-ldap (0.16.0) net-ldap (0.16.0)
net-ssh (4.1.0) net-ssh (4.1.0)
netrc (0.11.0) netrc (0.11.0)
nokogiri (1.8.0) nokogiri (1.8.1)
mini_portile2 (~> 2.2.0) mini_portile2 (~> 2.3.0)
numerizer (0.1.1) numerizer (0.1.1)
oauth (0.5.1) oauth (0.5.1)
oauth2 (1.4.0) oauth2 (1.4.0)
...@@ -1028,7 +1028,7 @@ DEPENDENCIES ...@@ -1028,7 +1028,7 @@ DEPENDENCIES
gitaly-proto (~> 0.33.0) gitaly-proto (~> 0.33.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1) gitlab-markup (~> 1.6.2)
gitlab_omniauth-ldap (~> 2.0.4) gitlab_omniauth-ldap (~> 2.0.4)
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.4) gollum-rugged_adapter (~> 0.4.4)
...@@ -1068,7 +1068,7 @@ DEPENDENCIES ...@@ -1068,7 +1068,7 @@ DEPENDENCIES
mysql2 (~> 0.4.5) mysql2 (~> 0.4.5)
net-ldap net-ldap
net-ssh (~> 4.1.0) net-ssh (~> 4.1.0)
nokogiri (~> 1.8.0) nokogiri (~> 1.8.1)
oauth2 (~> 1.4) oauth2 (~> 1.4)
octokit (~> 4.6.2) octokit (~> 4.6.2)
oj (~> 2.17.4) oj (~> 2.17.4)
......
9.6.0-pre 10.1.0-pre
{"iconCount":134,"icons":["abuse","account","admin","angle-double-left","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-right","assignee","bold","book","branch","calendar","cancel","chevron-down","chevron-left","chevron-right","chevron-up","clock","code","comment-dots","comment-next","comment","comments","commit","credit-card","disk","doc_code","doc_image","doc_text","download","duplicate","earth","eye-slash","eye","file-additions","file-deletion","file-modified","filter","folder","fork","geo-nodes","git-merge","group","history","home","hook","issue-block","issue-child","issue-close","issue-duplicate","issue-new","issue-open-m","issue-open","issue-parent","issues","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","merge-request-close-m","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil","pipeline","play","plus-square-o","plus-square","plus","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","star-o","star","stop","talic","task-done","template","thump-down","thump-up","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 150"><g fill="none" fill-rule="evenodd"><g fill="#e5e5e5" transform="translate(0 102)"><rect width="74" height="4" x="34" y="21" opacity=".5" rx="2"/><path d="M152 23c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.994 1.994 0 0 1 152 23m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.994 1.994 0 0 1 166 23m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.994 1.994 0 0 1 180 23m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.994 1.994 0 0 1 194 23m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4A1.994 1.994 0 0 1 208 23"/></g><g fill="#31af64"><path fill-rule="nonzero" d="M19 144c-10.493 0-19-8.507-19-19s8.507-19 19-19 19 8.507 19 19-8.507 19-19 19m0-4c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15-8.284 0-15 6.716-15 15 0 8.284 6.716 15 15 15"/><path d="M17.07 127.02l-2.829-2.829a1.995 1.995 0 0 0-2.828 0 1.995 1.995 0 0 0 0 2.828l4.243 4.243a1.995 1.995 0 0 0 2.822.006l7.79-7.79a1.997 1.997 0 0 0-.006-2.823 1.992 1.992 0 0 0-2.823-.006l-6.37 6.37"/></g><g fill="#e52c5a"><path fill-rule="nonzero" d="M126 149.5c-12.979 0-23.5-10.521-23.5-23.5s10.521-23.5 23.5-23.5 23.5 10.521 23.5 23.5-10.521 23.5-23.5 23.5m0-5c10.217 0 18.5-8.283 18.5-18.5s-8.283-18.5-18.5-18.5-18.5 8.283-18.5 18.5 8.283 18.5 18.5 18.5"/><path d="M130.24 126l2.833-2.833a3 3 0 0 0-4.243-4.243l-2.833 2.833-2.833-2.833a3 3 0 0 0-4.243 4.243l2.833 2.833-2.833 2.833a3 3 0 0 0 4.243 4.243l2.833-2.833 2.833 2.833a3 3 0 0 0 4.243-4.243L130.24 126"/></g><path fill="#e5e5e5" fill-rule="nonzero" d="M236 139c-7.732 0-14-6.268-14-14s6.268-14 14-14 14 6.268 14 14-6.268 14-14 14m0-4c5.523 0 10-4.477 10-10s-4.477-10-10-10-10 4.477-10 10 4.477 10 10 10"/><g transform="translate(73 4)"><path stroke="#e5e5e5" stroke-width="4" d="M64.82 76H98c4.419 0 8-3.579 8-7.99V7.99C106 3.577 102.417 0 98 0H8.009c-4.419 0-8 3.579-8 7.99v60.02c0 4.413 3.583 7.99 8 7.99h31.935l9.263 9.855a4.357 4.357 0 0 0 6.354 0L64.824 76"/><rect width="18" height="6" x="11" y="19" fill="#fc8a51" rx="3"/><rect width="18" height="6" x="35" y="35" fill="#e52c5a" rx="3"/><rect width="18" height="6" x="29" y="51" fill="#e5e5e5" rx="3"/><rect width="12" height="6" x="35" y="19" fill="#fde5d8" rx="3"/><rect width="12" height="6" x="53" y="51" fill="#e52c5a" rx="3"/><rect width="12" height="6" x="11" y="51" fill="#b5a7dd" rx="3"/><rect width="18" height="6" x="77" y="19" fill="#fc8a51" rx="3"/><rect width="18" height="6" x="11" y="35" fill="#fde5d8" rx="3"/><rect width="6" height="6" x="53" y="19" fill="#e52c5a" rx="3"/><g fill="#fde5d8"><rect width="6" height="6" x="65" y="19" rx="3"/><rect width="6" height="6" x="71" y="35" rx="3"/></g><rect width="6" height="6" x="59" y="35" fill="#e52c5a" rx="3"/></g><path fill="#6b4fbb" fill-rule="nonzero" d="M151.869 77.403c-13.26 9.264-31.649 7.977-43.484-3.858-13.279-13.279-13.279-34.806 0-48.084 13.278-13.278 34.805-13.278 48.083 0 11.836 11.836 13.118 30.23 3.858 43.485.133.111.262.229.387.354l15.556 15.555a6.004 6.004 0 0 1 0 8.486 5.997 5.997 0 0 1-8.486 0l-15.555-15.556a6.051 6.051 0 0 1-.355-.387m-1.06-9.512c10.154-10.154 10.154-26.617 0-36.77-10.153-10.154-26.616-10.154-36.77 0-10.153 10.153-10.153 26.616 0 36.77 10.154 10.153 26.617 10.153 36.77 0"/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 446 249" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="a" d="M260.03 114h23.972v-.013c19.972-.53 36-16.887 36-36.987 0-20.435-16.565-37-37-37-.993 0-1.977.039-2.95.116-4.95-14.605-18.773-25.12-35.05-25.12a36.87 36.87 0 0 0-15.32 3.311c-6.649-9.841-17.909-16.311-30.68-16.311-20.435 0-37 16.565-37 37 0 .701.019 1.397.058 2.088C145.95 45.083 134 59.645 134 76.996c0 20.435 16.565 37 37 37 .324 0 .646-.004.968-.012"/><ellipse id="b" cx="41" cy="41" rx="41" ry="41"/><mask id="c" width="186" height="112" x="0" y="0" fill="#fff"><use xlink:href="#a"/></mask><mask id="d" width="82" height="82" x="0" y="0" fill="#fff"><use xlink:href="#b"/></mask></defs><g fill="none" fill-rule="evenodd"><path stroke="#b5a7dd" stroke-width="4" d="M228.415 137.792c8.443 17.156 21.89 32.082 39.688 42.358"/><path fill="#fb722e" d="M284.464 183.822a2.006 2.006 0 0 1 2.74-.727l6.914 3.992a2.001 2.001 0 0 1 .741 2.737 2.006 2.006 0 0 1-2.74.727l-6.914-3.992a2.001 2.001 0 0 1-.74-2.737m-5 8.66a2.006 2.006 0 0 1 2.74-.726l6.913 3.991a2.001 2.001 0 0 1 .741 2.737 2.006 2.006 0 0 1-2.74.727l-6.914-3.991a2.001 2.001 0 0 1-.74-2.737"/><path fill="#fde5d8" fill-rule="nonzero" d="M267.072 189.947l5.196 3a5.998 5.998 0 0 0 8.195-2.194l3.005-5.205a5.995 5.995 0 0 0-2.198-8.193l-5.196-3-9 15.588m6.032-18.447a3.005 3.005 0 0 1 4.098-1.11l6.07 3.505c4.784 2.761 6.426 8.871 3.662 13.658l-3.005 5.204c-2.76 4.782-8.875 6.42-13.659 3.658l-6.07-3.505a2.999 2.999 0 0 1-1.088-4.104l9.992-17.306"/><g fill-rule="nonzero"><path fill="#e5e5e5" d="M260.597 18.747C266.208 9.657 276.116 4 287 4c17.12 0 31 13.879 31 31 0 7.02-2.34 13.685-6.58 19.1l3.149 2.466A34.855 34.855 0 0 0 322 35.001c0-19.33-15.67-35-35-35-12.286 0-23.476 6.384-29.808 16.647l3.404 2.1"/><path fill="#b5a7dd" d="M281.982 23.991l-2.526 1.154-2.992-2.993a.4.4 0 0 0-.564.009l-1.738 1.738a.392.392 0 0 0-.009.564l2.987 2.987-1.147 2.524a12.26 12.26 0 0 0-1.04 3.883l-.269 2.76-4.08 1.093a.399.399 0 0 0-.275.492l.636 2.375c.06.223.273.346.485.29l4.087-1.096 1.611 2.262a12.017 12.017 0 0 0 2.827 2.828l2.26 1.612-1.094 4.08a.399.399 0 0 0 .29.485l2.374.636a.393.393 0 0 0 .493-.275l1.093-4.08 2.763-.267a12.14 12.14 0 0 0 3.862-1.035l2.526-1.154 2.992 2.992a.4.4 0 0 0 .564-.008l1.738-1.738a.392.392 0 0 0 .009-.564l-2.987-2.987 1.147-2.524a12.26 12.26 0 0 0 1.04-3.883l.27-2.76 4.08-1.093a.399.399 0 0 0 .274-.493l-.636-2.374a.393.393 0 0 0-.485-.29l-4.087 1.096-1.611-2.262a12.017 12.017 0 0 0-2.826-2.828l-2.26-1.612 1.093-4.08a.399.399 0 0 0-.29-.485l-2.373-.636a.393.393 0 0 0-.493.274l-1.094 4.081-2.763.266c-1.336.129-2.64.48-3.862 1.036m3.48-5.02l.375-1.4a4.393 4.393 0 0 1 5.392-3.103l2.375.636a4.399 4.399 0 0 1 3.117 5.383l-.375 1.401a16.077 16.077 0 0 1 3.761 3.767l1.405-.376a4.397 4.397 0 0 1 5.386 3.118l.636 2.375a4.398 4.398 0 0 1-3.103 5.39l-1.402.376a16.217 16.217 0 0 1-1.378 5.143l1.027 1.026a4.392 4.392 0 0 1-.008 6.22l-1.739 1.738a4.4 4.4 0 0 1-6.224.008l-1.028-1.028a16.09 16.09 0 0 1-5.14 1.381l-.376 1.4a4.393 4.393 0 0 1-5.392 3.104l-2.374-.636a4.399 4.399 0 0 1-3.118-5.383l.376-1.401a16.077 16.077 0 0 1-3.762-3.767l-1.404.376a4.397 4.397 0 0 1-5.386-3.118l-.637-2.374a4.398 4.398 0 0 1 3.103-5.391l1.402-.376a16.217 16.217 0 0 1 1.378-5.143l-1.026-1.026a4.392 4.392 0 0 1 .008-6.22l1.738-1.738a4.4 4.4 0 0 1 6.224-.008l1.028 1.028a16.09 16.09 0 0 1 5.141-1.381"/><path fill="#6b4fbb" d="M286.367 37.355a2.439 2.439 0 1 0 1.262-4.711 2.439 2.439 0 0 0-1.262 4.711m-1.035 3.864a6.44 6.44 0 1 1 3.333-12.44 6.44 6.44 0 0 1-3.333 12.44"/></g><use fill="#fff" stroke="#e5e5e5" stroke-width="8" mask="url(#c)" stroke-linejoin="round" xlink:href="#a"/><g transform="translate(175 58)"><use fill="#fff" stroke="#e5e5e5" stroke-width="8" mask="url(#d)" xlink:href="#b"/><g fill-rule="nonzero"><path fill="#e5e5e5" d="M41 78c20.435 0 37-16.565 37-37S61.435 4 41 4 4 20.565 4 41s16.565 37 37 37m0 4C18.356 82 0 63.644 0 41S18.356 0 41 0s41 18.356 41 41-18.356 41-41 41"/><path fill="#b5a7dd" d="M34.363 26.44l-2.527 1.154-3.211-3.211a1.495 1.495 0 0 0-2.117-.005l-2.131 2.13a1.504 1.504 0 0 0 .005 2.117l3.206 3.206-1.147 2.524a16.09 16.09 0 0 0-.897 2.503 16.08 16.08 0 0 0-.475 2.616l-.269 2.76-4.379 1.174a1.495 1.495 0 0 0-1.063 1.83l.78 2.911a1.504 1.504 0 0 0 1.836 1.054l4.387-1.176 1.612 2.263a15.954 15.954 0 0 0 3.737 3.742l2.26 1.612-1.173 4.38a1.495 1.495 0 0 0 1.053 1.835l2.908.78a1.504 1.504 0 0 0 1.83-1.063l1.174-4.38 2.763-.266a15.977 15.977 0 0 0 5.108-1.372l2.527-1.154 3.211 3.212a1.495 1.495 0 0 0 2.117.005l2.131-2.131a1.504 1.504 0 0 0-.005-2.117l-3.206-3.206 1.147-2.524a16.09 16.09 0 0 0 .897-2.503 16.1 16.1 0 0 0 .475-2.616l.269-2.76 4.379-1.173a1.495 1.495 0 0 0 1.063-1.83l-.78-2.912a1.504 1.504 0 0 0-1.836-1.054l-4.387 1.176-1.612-2.262a15.954 15.954 0 0 0-3.737-3.743l-2.26-1.612 1.173-4.38a1.495 1.495 0 0 0-1.053-1.835l-2.908-.779a1.504 1.504 0 0 0-1.83 1.063l-1.174 4.38-2.763.265c-1.767.17-3.493.636-5.108 1.373m4.726-5.355l.455-1.699a5.504 5.504 0 0 1 6.73-3.89l2.907.778a5.495 5.495 0 0 1 3.882 6.735l-.455 1.699a19.95 19.95 0 0 1 4.673 4.68l1.704-.457a5.503 5.503 0 0 1 6.734 3.886l.78 2.91a5.493 5.493 0 0 1-3.894 6.73l-1.701.455a20.134 20.134 0 0 1-.593 3.265 20.134 20.134 0 0 1-1.119 3.124l1.245 1.246a5.507 5.507 0 0 1 .008 7.774l-2.13 2.13a5.5 5.5 0 0 1-7.775-.001l-1.248-1.248c-2 .914-4.157 1.502-6.387 1.717l-.455 1.699a5.504 5.504 0 0 1-6.73 3.89l-2.907-.778a5.495 5.495 0 0 1-3.882-6.735l.455-1.699a19.95 19.95 0 0 1-4.673-4.68l-1.704.457a5.503 5.503 0 0 1-6.734-3.886l-.78-2.91a5.493 5.493 0 0 1 3.894-6.73l1.701-.455a20.258 20.258 0 0 1 1.712-6.389l-1.245-1.246a5.507 5.507 0 0 1-.008-7.774l2.13-2.13a5.5 5.5 0 0 1 7.775.001l1.248 1.248c2-.914 4.157-1.502 6.387-1.717"/><path fill="#6b4fbb" d="M39.965 44.863a4 4 0 1 0 2.07-7.727 4 4 0 0 0-2.07 7.727m-1.036 3.864a8 8 0 1 1 4.142-15.455 8 8 0 0 1-4.142 15.455"/></g></g><path fill="#e5e5e5" fill-rule="nonzero" d="M144 169.541v30.01a4.002 4.002 0 0 0 4 3.995h20c2.209 0 4-1.789 4-3.995v-30.01a4.002 4.002 0 0 0-4-3.995h-20c-2.209 0-4 1.789-4 3.995m-4 0c0-4.416 3.583-7.995 8-7.995h20c4.416 0 8 3.584 8 7.995v30.01c0 4.416-3.583 7.995-8 7.995h-20c-4.416 0-8-3.584-8-7.995v-30.01"/><g fill="#fb722e" transform="translate(140 161)"><rect width="4" height="11" x="10" y="18.545" rx="2"/><rect width="4" height="11" x="21" y="18.545" rx="2"/></g><path fill="#e5e5e5" fill-rule="nonzero" d="M445.16 245.34c-16.874-11.778-110.62-20.336-222.14-20.336-111.61 0-205.4 8.571-222.18 20.364a2 2 0 1 0 2.3 3.272c15.756-11.07 109.46-19.636 219.88-19.636 110.34 0 203.99 8.55 219.85 19.617a2.001 2.001 0 0 0 2.29-3.28"/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="68" viewBox="181 0 116 68"><g fill="none" fill-rule="evenodd" transform="translate(182)"><rect width="78" height="34" x="37" y="34" fill="#FAFAFA" rx="3"/><rect width="78" height="34" x="31" y="28" fill="#FFF" stroke="#EEE" stroke-width="4" stroke-linecap="round" rx="3"/><path fill="#FFF" stroke="#FC6D26" stroke-width="3" d="M34 35.8c-.6 0-1.4 0-1.8.4L29 38.8c-1 .7-1.7.4-2-.7l-.6-4c0-.5-.5-1.2-1-1.5L22 30.2c-1-.6-1-1.5 0-2l3.7-2c.5-.2 1-.8 1.2-1.3l1-4.2c.3-1 1-1.3 2-.5l3 3c.3.3 1 .6 1.6.6l4.2-.3c1 0 1.5.7 1 1.7L38 29c-.3.6-.3 1.4 0 2l1.3 3.8c.4 1 0 1.8-1.2 1.6l-4-.6z" stroke-linecap="round"/><path fill="#FDE5D8" d="M51.6 14.3c-.2-.2-.8-.4-1-.3l-2.8.5c-.7 0-1-.4-.8-1l1-2.8V9.5L46.6 7c-.3-.7 0-1.2.8-1h2.7c.3 0 .8-.2 1-.5l2-2c.6-.5 1-.4 1.3.3l.7 2.8c0 .3.4.8.7 1l2.3 1.2c.7.3.7 1 0 1.3l-2.2 1.7-.6 1-.4 3c-.2.6-.7.8-1.3.4l-2-1.7zM5.4 43.2c-.2-.2-.5-.2-.7-.2l-1.8.3c-.6 0-1-.2-.7-.7l.7-1.8V40l-1-1.7c0-.4 0-.7.6-.7h1.8c.3 0 .6 0 .8-.2L6.5 36c.3-.3.7-.2.8.2l.5 2 .5.5 1.6.8c.3.2.3.7 0 1l-1.6 1c-.2 0-.4.4-.4.7l-.4 2c0 .3-.4.5-.8.2l-1.4-1.2zM10.4 9.2C10.2 9 10 9 9.7 9L8 9.3c-.6 0-1-.2-.7-.7L8 6.8V6L7 4.3c0-.4 0-.7.6-.7h1.8c.3 0 .6 0 .8-.2L11.5 2c.3-.3.7-.2.8.2l.5 2 .5.5 1.6.8c.3.2.3.7 0 1l-1.6 1c-.2 0-.4.4-.4.7l-.4 2c0 .3-.4.5-.8.2l-1.4-1.2z"/><rect width="52" height="4" x="43" y="38" fill="#EEE" rx="2"/><rect width="36" height="4" x="43" y="48" fill="#EEE" rx="2"/></g></svg> <svg xmlns="http://www.w3.org/2000/svg" width="116" height="68" viewBox="181 0 116 68"><g fill="none" fill-rule="evenodd" transform="translate(182)"><rect width="78" height="34" x="37" y="34" fill="#FAFAFA" rx="3"/><rect width="78" height="34" x="31" y="28" fill="#FFF" stroke="#EEE" stroke-width="4" stroke-linecap="round" rx="3"/><path fill="#FFF" stroke="#FC6D26" stroke-width="3" d="M34 35.8c-.6 0-1.4 0-1.8.4L29 38.8c-1 .7-1.7.4-2-.7l-.6-4c0-.5-.5-1.2-1-1.5L22 30.2c-1-.6-1-1.5 0-2l3.7-2c.5-.2 1-.8 1.2-1.3l1-4.2c.3-1 1-1.3 2-.5l3 3c.3.3 1 .6 1.6.6l4.2-.3c1 0 1.5.7 1 1.7L38 29c-.3.6-.3 1.4 0 2l1.3 3.8c.4 1 0 1.8-1.2 1.6l-4-.6z" stroke-linecap="round"/><path fill="#FDE5D8" d="M51.6 14.3c-.2-.2-.8-.4-1-.3l-2.8.5c-.7 0-1-.4-.8-1l1-2.8V9.5L46.6 7c-.3-.7 0-1.2.8-1h2.7c.3 0 .8-.2 1-.5l2-2c.6-.5 1-.4 1.3.3l.7 2.8c0 .3.4.8.7 1l2.3 1.2c.7.3.7 1 0 1.3l-2.2 1.7-.6 1-.4 3c-.2.6-.7.8-1.3.4l-2-1.7zM5.4 43.2c-.2-.2-.5-.2-.7-.2l-1.8.3c-.6 0-1-.2-.7-.7l.7-1.8V40l-1-1.7c0-.4 0-.7.6-.7h1.8c.3 0 .6 0 .8-.2L6.5 36c.3-.3.7-.2.8.2l.5 2 .5.5 1.6.8c.3.2.3.7 0 1l-1.6 1c-.2 0-.4.4-.4.7l-.4 2c0 .3-.4.5-.8.2l-1.4-1.2zm5-34C10.2 9 10 9 9.7 9L8 9.3c-.6 0-1-.2-.7-.7L8 6.8V6L7 4.3c0-.4 0-.7.6-.7h1.8c.3 0 .6 0 .8-.2L11.5 2c.3-.3.7-.2.8.2l.5 2 .5.5 1.6.8c.3.2.3.7 0 1l-1.6 1c-.2 0-.4.4-.4.7l-.4 2c0 .3-.4.5-.8.2l-1.4-1.2z"/><rect width="52" height="4" x="43" y="38" fill="#EEE" rx="2"/><rect width="36" height="4" x="43" y="48" fill="#EEE" rx="2"/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 293 216"><g fill="none" fill-rule="evenodd"><g transform="rotate(-5 211.388 -693.89)"><rect width="163.6" height="200" x=".2" fill="#FFF" stroke="#EEE" stroke-width="3" stroke-linecap="round" stroke-dasharray="6 9" rx="6"/><g transform="translate(24 38)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="76" height="3" x="40" y="11" fill="#6B4FBB" opacity=".5" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#6B4FBB" opacity=".5" rx="1.5"/></g><g transform="translate(24 83)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="76" height="3" x="40" y="11" fill="#B5A7DD" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#B5A7DD" rx="1.5"/></g><g transform="translate(24 130)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="76" height="3" x="40" y="11" fill="#B5A7DD" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#B5A7DD" rx="1.5"/></g></g><path fill="#FFCE29" d="M30 11l-1.8 4-2-4-4-1.8 4-2 2-4 2 4 4 2M286 60l-2.7 6.3-3-6-6-3 6-3 3-6 2.8 6.2 6.6 2.8M263 97l-2 4-2-4-4-2 4-2 2-4 2 4 4 2M12 85l-2.7 6.3-3-6-6-3 6-3 3-6 2.8 6.2 6.6 2.8"/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 284 337" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><rect id="a" width="180" height="220" x="66.2" y="74.4" rx="6"/><mask id="l" width="180" height="220" x="0" y="0" fill="#fff"><use xlink:href="#a"/></mask><rect id="b" width="180" height="220" rx="6"/><mask id="m" width="180" height="220" x="0" y="0" fill="#fff"><use xlink:href="#b"/></mask><rect id="c" width="28" height="28" rx="4"/><mask id="n" width="28" height="28" x="0" y="0" fill="#fff"><use xlink:href="#c"/></mask><rect id="d" width="28" height="28" rx="4"/><mask id="o" width="28" height="28" x="0" y="0" fill="#fff"><use xlink:href="#d"/></mask><circle id="e" cx="21.5" cy="21.5" r="21.5"/><mask id="p" width="43" height="43" x="0" y="0" fill="#fff"><use xlink:href="#e"/></mask><circle id="f" cx="26.5" cy="26.5" r="26.5"/><mask id="q" width="53" height="53" x="0" y="0" fill="#fff"><use xlink:href="#f"/></mask><circle id="g" cx="9.5" cy="4.5" r="4.5"/><mask id="r" width="13" height="13" x="-2" y="-2"><path fill="#fff" d="M3-2h13v13H3z"/><use xlink:href="#g"/></mask><circle id="h" cx="26.5" cy="26.5" r="26.5"/><mask id="s" width="53" height="53" x="0" y="0" fill="#fff"><use xlink:href="#h"/></mask><circle id="i" cx="21.5" cy="21.5" r="21.5"/><mask id="t" width="43" height="43" x="0" y="0" fill="#fff"><use xlink:href="#i"/></mask><path id="j" d="M18 38h15c10.5 0 19-8.5 19-19S43.5 0 33 0H19C8.5 0 0 8.5 0 19c0 6.3 3 12 7.8 15.3l5.2 9c.6 1 1.4 1 2 0l3-5.3z"/><mask id="u" width="52" height="44" x="0" y="0" fill="#fff"><use xlink:href="#j"/></mask><circle id="k" cx="18.5" cy="18.5" r="18.5"/><mask id="v" width="37" height="37" x="0" y="0" fill="#fff"><use xlink:href="#k"/></mask></defs><g fill="none" fill-rule="evenodd" transform="translate(-6 -4)"><use stroke="#EEE" stroke-width="6" mask="url(#l)" transform="rotate(-5 156.245 184.425)" xlink:href="#a"/><g transform="rotate(5 -707.333 618.042)"><use fill="#FFF" stroke="#EEE" stroke-width="6" mask="url(#m)" xlink:href="#b"/><g transform="translate(29 24)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="86" height="3" x="40" y="11" fill="#6B4FBB" opacity=".5" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#6B4FBB" opacity=".5" rx="1.5"/></g><g transform="translate(29 69)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="86" height="3" x="40" y="11" fill="#B5A7DD" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#B5A7DD" rx="1.5"/></g><g transform="translate(28 160)"><use stroke="#E5E5E5" stroke-width="6" mask="url(#n)" opacity=".7" xlink:href="#c"/><rect width="26" height="3" x="41" y="7" fill="#ECECEC" rx="1.5"/><rect width="43" height="3" x="41" y="17" fill="#ECECEC" rx="1.5"/></g><g transform="translate(28 116)"><use stroke="#E5E5E5" stroke-width="6" mask="url(#o)" xlink:href="#d"/><rect width="86" height="3" x="41" y="7" fill="#E5E5E5" rx="1.5"/><rect width="43" height="3" x="41" y="17" fill="#E5E5E5" rx="1.5"/></g></g><g transform="rotate(-15 601.917 -782.362)"><use fill="#FFF" stroke="#B5A7DD" stroke-width="6" mask="url(#p)" xlink:href="#e"/><text fill="#6B4FBB" font-family="SourceSansPro-Black, Source Sans Pro" font-size="20" font-weight="700" letter-spacing="-.1"><tspan x="12" y="27">@</tspan></text></g><g transform="rotate(15 -686.59 1035.907)"><use fill="#FFF" stroke="#FDE5D8" stroke-width="6" mask="url(#q)" xlink:href="#f"/><path fill="#FC6D26" d="M26.5 38.2c3.3 0 9.5-2.5 9.5-9.6 0-7-2.4-6.6-9.5-6.6-7 0-9.5-.4-9.5 6.6s6.2 9.6 9.5 9.6z"/><g transform="translate(17 14)"><use fill="#FC6D26" xlink:href="#g"/><use stroke="#FFF" stroke-width="4" mask="url(#r)" xlink:href="#g"/></g></g><g transform="rotate(15 -85.125 65.185)"><use fill="#FFF" stroke="#B5A7DD" stroke-width="6" mask="url(#s)" xlink:href="#h"/><path fill="#6B4FBB" d="M24 18.5c0-1.4 1-2.5 2.5-2.5 1.4 0 2.5 1 2.5 2.5v9c0 1.4-1 2.5-2.5 2.5-1.4 0-2.5-1-2.5-2.5v-9zM26.5 37c1.4 0 2.5-1 2.5-2.5 0-1.4-1-2.5-2.5-2.5-1.4 0-2.5 1-2.5 2.5 0 1.4 1 2.5 2.5 2.5z"/></g><g transform="rotate(-15 716.492 78.873)"><use fill="#FFF" stroke="#FDE5D8" stroke-width="6" mask="url(#t)" xlink:href="#i"/><path fill="#FC6D26" d="M20 23v-3h3v3h-3zm0 3v1.5c0 .8-.7 1.5-1.5 1.5s-1.5-.7-1.5-1.5V26h-2.5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5H17v-3h-1.5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5H17v-2.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V17h3v-1.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V17h2.5c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5H26v3h1.5c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5H26v2.5c0 .8-.7 1.5-1.5 1.5s-1.5-.7-1.5-1.5V26h-3z"/></g><g transform="rotate(-15 129.114 -585.74)"><use stroke="#FDE5D8" stroke-width="6" mask="url(#u)" xlink:href="#j"/><circle cx="16" cy="20" r="2" fill="#FC6D26"/><circle cx="27" cy="20" r="2" fill="#FC6D26"/><circle cx="38" cy="20" r="2" fill="#FC6D26"/></g><g transform="rotate(-15 1254.8 -458.986)"><use stroke="#FDE5D8" stroke-width="6" mask="url(#v)" xlink:href="#k"/><path fill="#FC6D26" d="M10.6 19l2-2c.5-.5.5-1 0-1.5-.3-.4-1-.4-1.3 0l-2.8 2.8c-.2.2-.3.4-.3.7 0 .3 0 .5.3.7l2.8 2.8c.4.4 1 .4 1.4 0 .4-.4.4-1 0-1.4l-2-2zm14.8 0l-2-2c-.5-.5-.5-1 0-1.5.3-.4 1-.4 1.3 0l2.8 2.8c.2.2.3.4.3.7 0 .3 0 .5-.3.7l-2.8 2.8c-.4.4-1 .4-1.4 0-.4-.4-.4-1 0-1.4l2-2z"/><rect width="2" height="7" x="17" y="15.1" fill="#FC6D26" opacity=".5" transform="rotate(15 18.002 18.64)" rx="1"/></g></g></svg>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* global Flash */ /* global Flash */
import _ from 'underscore'; import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { isInIssuePage, updateTooltipTitle } from './lib/utils/common_utils';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd'; const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd'; const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
...@@ -23,6 +24,9 @@ const categoryLabelMap = { ...@@ -23,6 +24,9 @@ const categoryLabelMap = {
flags: 'Flags', flags: 'Flags',
}; };
const IS_VISIBLE = 'is-visible';
const IS_RENDERED = 'is-rendered';
class AwardsHandler { class AwardsHandler {
constructor(emoji) { constructor(emoji) {
this.emoji = emoji; this.emoji = emoji;
...@@ -50,7 +54,7 @@ class AwardsHandler { ...@@ -50,7 +54,7 @@ class AwardsHandler {
if (!$target.closest('.emoji-menu').length) { if (!$target.closest('.emoji-menu').length) {
if ($('.emoji-menu').is(':visible')) { if ($('.emoji-menu').is(':visible')) {
$('.js-add-award.is-active').removeClass('is-active'); $('.js-add-award.is-active').removeClass('is-active');
$('.emoji-menu').removeClass('is-visible'); this.hideMenuElement($('.emoji-menu'));
} }
} }
}); });
...@@ -87,12 +91,12 @@ class AwardsHandler { ...@@ -87,12 +91,12 @@ class AwardsHandler {
if ($menu.length) { if ($menu.length) {
if ($menu.is('.is-visible')) { if ($menu.is('.is-visible')) {
$addBtn.removeClass('is-active'); $addBtn.removeClass('is-active');
$menu.removeClass('is-visible'); this.hideMenuElement($menu);
$('.js-emoji-menu-search').blur(); $('.js-emoji-menu-search').blur();
} else { } else {
$addBtn.addClass('is-active'); $addBtn.addClass('is-active');
this.positionMenu($menu, $addBtn); this.positionMenu($menu, $addBtn);
$menu.addClass('is-visible'); this.showMenuElement($menu);
$('.js-emoji-menu-search').focus(); $('.js-emoji-menu-search').focus();
} }
} else { } else {
...@@ -102,7 +106,7 @@ class AwardsHandler { ...@@ -102,7 +106,7 @@ class AwardsHandler {
$addBtn.removeClass('is-loading'); $addBtn.removeClass('is-loading');
this.positionMenu($createdMenu, $addBtn); this.positionMenu($createdMenu, $addBtn);
return setTimeout(() => { return setTimeout(() => {
$createdMenu.addClass('is-visible'); this.showMenuElement($createdMenu);
$('.js-emoji-menu-search').focus(); $('.js-emoji-menu-search').focus();
}, 200); }, 200);
}); });
...@@ -237,10 +241,11 @@ class AwardsHandler { ...@@ -237,10 +241,11 @@ class AwardsHandler {
addAward(votesBlock, awardUrl, emoji, checkMutuality, callback) { addAward(votesBlock, awardUrl, emoji, checkMutuality, callback) {
const isMainAwardsBlock = votesBlock.closest('.js-issue-note-awards').length; const isMainAwardsBlock = votesBlock.closest('.js-issue-note-awards').length;
if (gl.utils.isInIssuePage() && !isMainAwardsBlock) { if (isInIssuePage() && !isMainAwardsBlock) {
const id = votesBlock.attr('id').replace('note_', ''); const id = votesBlock.attr('id').replace('note_', '');
$('.emoji-menu').removeClass('is-visible'); this.hideMenuElement($('.emoji-menu'));
$('.js-add-award.is-active').removeClass('is-active'); $('.js-add-award.is-active').removeClass('is-active');
const toggleAwardEvent = new CustomEvent('toggleAward', { const toggleAwardEvent = new CustomEvent('toggleAward', {
detail: { detail: {
...@@ -260,7 +265,8 @@ class AwardsHandler { ...@@ -260,7 +265,8 @@ class AwardsHandler {
return typeof callback === 'function' ? callback() : undefined; return typeof callback === 'function' ? callback() : undefined;
}); });
$('.emoji-menu').removeClass('is-visible'); this.hideMenuElement($('.emoji-menu'));
return $('.js-add-award.is-active').removeClass('is-active'); return $('.js-add-award.is-active').removeClass('is-active');
} }
...@@ -288,7 +294,7 @@ class AwardsHandler { ...@@ -288,7 +294,7 @@ class AwardsHandler {
} }
getVotesBlock() { getVotesBlock() {
if (gl.utils.isInIssuePage()) { if (isInIssuePage()) {
const $el = $('.js-add-award.is-active').closest('.note.timeline-entry'); const $el = $('.js-add-award.is-active').closest('.note.timeline-entry');
if ($el.length) { if ($el.length) {
...@@ -452,11 +458,11 @@ class AwardsHandler { ...@@ -452,11 +458,11 @@ class AwardsHandler {
userAuthored($emojiButton) { userAuthored($emojiButton) {
const oldTitle = this.getAwardTooltip($emojiButton); const oldTitle = this.getAwardTooltip($emojiButton);
const newTitle = 'You cannot vote on your own issue, MR and note'; const newTitle = 'You cannot vote on your own issue, MR and note';
gl.utils.updateTooltipTitle($emojiButton, newTitle).tooltip('show'); updateTooltipTitle($emojiButton, newTitle).tooltip('show');
// Restore tooltip back to award list // Restore tooltip back to award list
return setTimeout(() => { return setTimeout(() => {
$emojiButton.tooltip('hide'); $emojiButton.tooltip('hide');
gl.utils.updateTooltipTitle($emojiButton, oldTitle); updateTooltipTitle($emojiButton, oldTitle);
}, 2800); }, 2800);
} }
...@@ -528,6 +534,33 @@ class AwardsHandler { ...@@ -528,6 +534,33 @@ class AwardsHandler {
return $matchingElements.closest('li').clone(); return $matchingElements.closest('li').clone();
} }
/* showMenuElement and hideMenuElement are performance optimizations. We use
* opacity to show/hide the emoji menu, because we can animate it. But opacity
* leaves hidden elements in the render tree, which is unacceptable given the number
* of emoji elements in the emoji menu (5k+). To get the best of both worlds, we separately
* apply IS_RENDERED to add/remove the menu from the render tree and IS_VISIBLE to animate
* the menu being opened and closed. */
showMenuElement($emojiMenu) {
$emojiMenu.addClass(IS_RENDERED);
// enqueues animation as a microtask, so it begins ASAP once IS_RENDERED added
return Promise.resolve()
.then(() => $emojiMenu.addClass(IS_VISIBLE));
}
hideMenuElement($emojiMenu) {
$emojiMenu.on(transitionEndEventString, (e) => {
if (e.currentTarget === e.target) {
$emojiMenu
.removeClass(IS_RENDERED)
.off(transitionEndEventString);
}
});
$emojiMenu.removeClass(IS_VISIBLE);
}
destroy() { destroy() {
this.eventListeners.forEach((entry) => { this.eventListeners.forEach((entry) => {
entry.element.off.call(entry.element, ...entry.args); entry.element.off.call(entry.element, ...entry.args);
......
import '../commons/bootstrap'; import '../commons/bootstrap';
import { isInIssuePage } from '../lib/utils/common_utils';
// Quick Submit behavior // Quick Submit behavior
// //
...@@ -45,7 +46,7 @@ $(document).on('keydown.quick_submit', '.js-quick-submit', (e) => { ...@@ -45,7 +46,7 @@ $(document).on('keydown.quick_submit', '.js-quick-submit', (e) => {
if (!$submitButton.attr('disabled')) { if (!$submitButton.attr('disabled')) {
$submitButton.trigger('click', [e]); $submitButton.trigger('click', [e]);
if (!gl.utils.isInIssuePage()) { if (!isInIssuePage()) {
$submitButton.disable(); $submitButton.disable();
} }
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import '../lib/utils/url_utility'; import '../lib/utils/url_utility';
import { HIDDEN_CLASS } from '../lib/utils/constants'; import { HIDDEN_CLASS } from '../lib/utils/constants';
import csrf from '../lib/utils/csrf';
function toggleLoading($el, $icon, loading) { function toggleLoading($el, $icon, loading) {
if (loading) { if (loading) {
...@@ -36,9 +37,7 @@ export default class BlobFileDropzone { ...@@ -36,9 +37,7 @@ export default class BlobFileDropzone {
maxFiles: 1, maxFiles: 1,
addRemoveLinks: true, addRemoveLinks: true,
previewsContainer: '.dropzone-previews', previewsContainer: '.dropzone-previews',
headers: { headers: csrf.headers,
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
},
init: function () { init: function () {
this.on('addedfile', function () { this.on('addedfile', function () {
toggleLoading(submitButton, submitButtonLoadingIcon, false); toggleLoading(submitButton, submitButtonLoadingIcon, false);
......
/* global Flash */ /* global Flash */
import { handleLocationHash } from '../../lib/utils/common_utils';
export default class BlobViewer { export default class BlobViewer {
constructor() { constructor() {
BlobViewer.initAuxiliaryViewer(); BlobViewer.initAuxiliaryViewer();
...@@ -114,7 +116,7 @@ export default class BlobViewer { ...@@ -114,7 +116,7 @@ export default class BlobViewer {
$(viewer).renderGFM(); $(viewer).renderGFM();
this.$fileHolder.trigger('highlight:line'); this.$fileHolder.trigger('highlight:line');
gl.utils.handleLocationHash(); handleLocationHash();
this.toggleCopyButtonState(); this.toggleCopyButtonState();
}) })
......
...@@ -8,11 +8,11 @@ gl.issueBoards.ModalEmptyState = Vue.extend({ ...@@ -8,11 +8,11 @@ gl.issueBoards.ModalEmptyState = Vue.extend({
return ModalStore.store; return ModalStore.store;
}, },
props: { props: {
image: { newIssuePath: {
type: String, type: String,
required: true, required: true,
}, },
newIssuePath: { emptyStateSvg: {
type: String, type: String,
required: true, required: true,
}, },
...@@ -42,7 +42,7 @@ gl.issueBoards.ModalEmptyState = Vue.extend({ ...@@ -42,7 +42,7 @@ gl.issueBoards.ModalEmptyState = Vue.extend({
<section class="empty-state"> <section class="empty-state">
<div class="row"> <div class="row">
<div class="col-xs-12 col-sm-6 col-sm-push-6"> <div class="col-xs-12 col-sm-6 col-sm-push-6">
<aside class="svg-content" v-html="image"></aside> <aside class="svg-content"><img :src="emptyStateSvg"/></aside>
</div> </div>
<div class="col-xs-12 col-sm-6 col-sm-pull-6"> <div class="col-xs-12 col-sm-6 col-sm-pull-6">
<div class="text-content"> <div class="text-content">
......
...@@ -12,11 +12,11 @@ const ModalStore = gl.issueBoards.ModalStore; ...@@ -12,11 +12,11 @@ const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.IssuesModal = Vue.extend({ gl.issueBoards.IssuesModal = Vue.extend({
props: { props: {
blankStateImage: { newIssuePath: {
type: String, type: String,
required: true, required: true,
}, },
newIssuePath: { emptyStateSvg: {
type: String, type: String,
required: true, required: true,
}, },
...@@ -150,14 +150,14 @@ gl.issueBoards.IssuesModal = Vue.extend({ ...@@ -150,14 +150,14 @@ gl.issueBoards.IssuesModal = Vue.extend({
:label-path="labelPath"> :label-path="labelPath">
</modal-header> </modal-header>
<modal-list <modal-list
:image="blankStateImage"
:issue-link-base="issueLinkBase" :issue-link-base="issueLinkBase"
:root-path="rootPath" :root-path="rootPath"
:empty-state-svg="emptyStateSvg"
v-if="!loading && showList && !filterLoading"></modal-list> v-if="!loading && showList && !filterLoading"></modal-list>
<empty-state <empty-state
v-if="showEmptyState" v-if="showEmptyState"
:image="blankStateImage" :new-issue-path="newIssuePath"
:new-issue-path="newIssuePath"></empty-state> :empty-state-svg="emptyStateSvg"></empty-state>
<section <section
class="add-issues-list text-center" class="add-issues-list text-center"
v-if="loading || filterLoading"> v-if="loading || filterLoading">
......
...@@ -15,7 +15,7 @@ gl.issueBoards.ModalList = Vue.extend({ ...@@ -15,7 +15,7 @@ gl.issueBoards.ModalList = Vue.extend({
type: String, type: String,
required: true, required: true,
}, },
image: { emptyStateSvg: {
type: String, type: String,
required: true, required: true,
}, },
...@@ -119,8 +119,8 @@ gl.issueBoards.ModalList = Vue.extend({ ...@@ -119,8 +119,8 @@ gl.issueBoards.ModalList = Vue.extend({
class="empty-state add-issues-empty-state-filter text-center" class="empty-state add-issues-empty-state-filter text-center"
v-if="issuesCount > 0 && issues.length === 0"> v-if="issuesCount > 0 && issues.length === 0">
<div <div
class="svg-content" class="svg-content">
v-html="image"> <img :src="emptyStateSvg"/>
</div> </div>
<div class="text-content"> <div class="text-content">
<h4> <h4>
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* global List */ /* global List */
import _ from 'underscore'; import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { getUrlParamsArray } from '../../lib/utils/common_utils';
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
...@@ -21,7 +22,7 @@ gl.issueBoards.BoardsStore = { ...@@ -21,7 +22,7 @@ gl.issueBoards.BoardsStore = {
}, },
create () { create () {
this.state.lists = []; this.state.lists = [];
this.filter.path = gl.utils.getUrlParamsArray().join('&'); this.filter.path = getUrlParamsArray().join('&');
this.detail = { issue: {} }; this.detail = { issue: {} };
}, },
addList (listObj, defaultAvatar) { addList (listObj, defaultAvatar) {
......
...@@ -3,6 +3,7 @@ consistent-return, prefer-rest-params */ ...@@ -3,6 +3,7 @@ consistent-return, prefer-rest-params */
import _ from 'underscore'; import _ from 'underscore';
import bp from './breakpoints'; import bp from './breakpoints';
import { bytesToKiB } from './lib/utils/number_utils'; import { bytesToKiB } from './lib/utils/number_utils';
import { setCiStatusFavicon } from './lib/utils/common_utils';
window.Build = (function () { window.Build = (function () {
Build.timeout = null; Build.timeout = null;
...@@ -169,7 +170,7 @@ window.Build = (function () { ...@@ -169,7 +170,7 @@ window.Build = (function () {
data: this.state, data: this.state,
}) })
.done((log) => { .done((log) => {
gl.utils.setCiStatusFavicon(`${this.pageUrl}/status.json`); setCiStatusFavicon(`${this.pageUrl}/status.json`);
if (log.state) { if (log.state) {
this.state = log.state; this.state = log.state;
......
...@@ -11,14 +11,22 @@ ...@@ -11,14 +11,22 @@
function ImageFile(file) { function ImageFile(file) {
this.file = file; this.file = file;
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) { this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
// Determine if old and new file has same dimensions, if not show 'two-up' view
return function(deletedWidth, deletedHeight) { return function(deletedWidth, deletedHeight) {
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) { return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
if (width === deletedWidth && height === deletedHeight) { _this.initViewModes();
return _this.initViewModes();
} else { // Load two-up view after images are loaded
return _this.initView('two-up'); // so that we can display the correct width and height information
} const images = $('.two-up.view img', _this.file);
let loadedCount = 0;
images.on('load', () => {
loadedCount += 1;
if (loadedCount === images.length) {
_this.initView('two-up');
}
});
}); });
}; };
})(this)); })(this));
...@@ -134,8 +142,9 @@ ...@@ -134,8 +142,9 @@
width: maxWidth + 1, width: maxWidth + 1,
height: maxHeight + 2 height: maxHeight + 2
}); });
// Set swipeBar left position to match image frame
$swipeBar.css({ $swipeBar.css({
left: 0 left: 1
}); });
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
......
...@@ -35,6 +35,8 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -35,6 +35,8 @@ document.addEventListener('DOMContentLoaded', () => {
propsData: { propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint, endpoint: pipelineTableViewEl.dataset.endpoint,
helpPagePath: pipelineTableViewEl.dataset.helpPagePath, helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
emptyStateSvgPath: pipelineTableViewEl.dataset.emptyStateSvgPath,
errorStateSvgPath: pipelineTableViewEl.dataset.errorStateSvgPath,
autoDevopsHelpPath: pipelineTableViewEl.dataset.helpAutoDevopsPath, autoDevopsHelpPath: pipelineTableViewEl.dataset.helpAutoDevopsPath,
}, },
}).$mount(); }).$mount();
......
...@@ -17,6 +17,14 @@ ...@@ -17,6 +17,14 @@
type: String, type: String,
required: true, required: true,
}, },
emptyStateSvgPath: {
type: String,
required: true,
},
errorStateSvgPath: {
type: String,
required: true,
},
}, },
mixins: [ mixins: [
pipelinesMixin, pipelinesMixin,
...@@ -87,10 +95,12 @@ ...@@ -87,10 +95,12 @@
<empty-state <empty-state
v-if="shouldRenderEmptyState" v-if="shouldRenderEmptyState"
:help-page-path="helpPagePath" :help-page-path="helpPagePath"
:empty-state-svg-path="emptyStateSvgPath"
/> />
<error-state <error-state
v-if="shouldRenderErrorState" v-if="shouldRenderErrorState"
:error-state-svg-path="errorStateSvgPath"
/> />
<div <div
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, camelcase, one-var-declaration-per-line, no-else-return, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, camelcase, one-var-declaration-per-line, no-else-return, max-len */
import { rstrip } from './lib/utils/common_utils';
window.ConfirmDangerModal = (function() { window.ConfirmDangerModal = (function() {
function ConfirmDangerModal(form, text) { function ConfirmDangerModal(form, text) {
...@@ -12,7 +13,7 @@ window.ConfirmDangerModal = (function() { ...@@ -12,7 +13,7 @@ window.ConfirmDangerModal = (function() {
submit.disable(); submit.disable();
$('.js-confirm-danger-input').off('input'); $('.js-confirm-danger-input').off('input');
$('.js-confirm-danger-input').on('input', function() { $('.js-confirm-danger-input').on('input', function() {
if (gl.utils.rstrip($(this).val()) === project_path) { if (rstrip($(this).val()) === project_path) {
return submit.enable(); return submit.enable();
} else { } else {
return submit.disable(); return submit.disable();
......
/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */ /* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */
import _ from 'underscore'; import _ from 'underscore';
import './lib/utils/common_utils'; import { insertText, getSelectedFragment, nodeMatchesSelector } from './lib/utils/common_utils';
import { placeholderImage } from './lazy_loader'; import { placeholderImage } from './lazy_loader';
const gfmRules = { const gfmRules = {
...@@ -295,7 +295,7 @@ class CopyAsGFM { ...@@ -295,7 +295,7 @@ class CopyAsGFM {
const clipboardData = e.originalEvent.clipboardData; const clipboardData = e.originalEvent.clipboardData;
if (!clipboardData) return; if (!clipboardData) return;
const documentFragment = window.gl.utils.getSelectedFragment(); const documentFragment = getSelectedFragment();
if (!documentFragment) return; if (!documentFragment) return;
const el = transformer(documentFragment.cloneNode(true)); const el = transformer(documentFragment.cloneNode(true));
...@@ -412,7 +412,7 @@ class CopyAsGFM { ...@@ -412,7 +412,7 @@ class CopyAsGFM {
for (const selector in rules) { for (const selector in rules) {
const func = rules[selector]; const func = rules[selector];
if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue; if (!nodeMatchesSelector(node, selector)) continue;
let result; let result;
if (func.length === 2) { if (func.length === 2) {
......
...@@ -12,34 +12,38 @@ import './components/stage_review_component'; ...@@ -12,34 +12,38 @@ import './components/stage_review_component';
import './components/stage_staging_component'; import './components/stage_staging_component';
import './components/stage_test_component'; import './components/stage_test_component';
import './components/total_time_component'; import './components/total_time_component';
import './cycle_analytics_service'; import CycleAnalyticsService from './cycle_analytics_service';
import './cycle_analytics_store'; import CycleAnalyticsStore from './cycle_analytics_store';
Vue.use(Translate); Vue.use(Translate);
$(() => { $(() => {
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed'; const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
const cycleAnalyticsEl = document.querySelector('#cycle-analytics');
const cycleAnalyticsStore = gl.cycleAnalytics.CycleAnalyticsStore;
const cycleAnalyticsService = new gl.cycleAnalytics.CycleAnalyticsService({
requestPath: cycleAnalyticsEl.dataset.requestPath,
});
gl.cycleAnalyticsApp = new Vue({ gl.cycleAnalyticsApp = new Vue({
el: '#cycle-analytics', el: '#cycle-analytics',
name: 'CycleAnalytics', name: 'CycleAnalytics',
data: { data() {
state: cycleAnalyticsStore.state, const cycleAnalyticsEl = document.querySelector('#cycle-analytics');
isLoading: false, const cycleAnalyticsService = new CycleAnalyticsService({
isLoadingStage: false, requestPath: cycleAnalyticsEl.dataset.requestPath,
isEmptyStage: false, });
hasError: false,
startDate: 30, return {
isOverviewDialogDismissed: Cookies.get(OVERVIEW_DIALOG_COOKIE), store: CycleAnalyticsStore,
state: CycleAnalyticsStore.state,
isLoading: false,
isLoadingStage: false,
isEmptyStage: false,
hasError: false,
startDate: 30,
isOverviewDialogDismissed: Cookies.get(OVERVIEW_DIALOG_COOKIE),
service: cycleAnalyticsService,
};
}, },
computed: { computed: {
currentStage() { currentStage() {
return cycleAnalyticsStore.currentActiveStage(); return this.store.currentActiveStage();
}, },
}, },
components: { components: {
...@@ -56,7 +60,7 @@ $(() => { ...@@ -56,7 +60,7 @@ $(() => {
}, },
methods: { methods: {
handleError() { handleError() {
cycleAnalyticsStore.setErrorState(true); this.store.setErrorState(true);
return new Flash('There was an error while fetching cycle analytics data.'); return new Flash('There was an error while fetching cycle analytics data.');
}, },
initDropdown() { initDropdown() {
...@@ -77,17 +81,17 @@ $(() => { ...@@ -77,17 +81,17 @@ $(() => {
this.isLoading = true; this.isLoading = true;
cycleAnalyticsService this.service
.fetchCycleAnalyticsData(fetchOptions) .fetchCycleAnalyticsData(fetchOptions)
.done((response) => { .then(resp => resp.json())
cycleAnalyticsStore.setCycleAnalyticsData(response); .then((response) => {
this.store.setCycleAnalyticsData(response);
this.selectDefaultStage(); this.selectDefaultStage();
this.initDropdown(); this.initDropdown();
this.isLoading = false;
}) })
.error(() => { .catch(() => {
this.handleError(); this.handleError();
})
.always(() => {
this.isLoading = false; this.isLoading = false;
}); });
}, },
...@@ -100,27 +104,27 @@ $(() => { ...@@ -100,27 +104,27 @@ $(() => {
if (this.currentStage === stage) return; if (this.currentStage === stage) return;
if (!stage.isUserAllowed) { if (!stage.isUserAllowed) {
cycleAnalyticsStore.setActiveStage(stage); this.store.setActiveStage(stage);
return; return;
} }
this.isLoadingStage = true; this.isLoadingStage = true;
cycleAnalyticsStore.setStageEvents([], stage); this.store.setStageEvents([], stage);
cycleAnalyticsStore.setActiveStage(stage); this.store.setActiveStage(stage);
cycleAnalyticsService this.service
.fetchStageData({ .fetchStageData({
stage, stage,
startDate: this.startDate, startDate: this.startDate,
}) })
.done((response) => { .then(resp => resp.json())
.then((response) => {
this.isEmptyStage = !response.events.length; this.isEmptyStage = !response.events.length;
cycleAnalyticsStore.setStageEvents(response.events, stage); this.store.setStageEvents(response.events, stage);
this.isLoadingStage = false;
}) })
.error(() => { .catch(() => {
this.isEmptyStage = true; this.isEmptyStage = true;
})
.always(() => {
this.isLoadingStage = false; this.isLoadingStage = false;
}); });
}, },
......
/* eslint-disable no-param-reassign */ import Vue from 'vue';
import VueResource from 'vue-resource';
const global = window.gl || (window.gl = {}); Vue.use(VueResource);
global.cycleAnalytics = global.cycleAnalytics || {};
class CycleAnalyticsService { export default class CycleAnalyticsService {
constructor(options) { constructor(options) {
this.requestPath = options.requestPath; this.requestPath = options.requestPath;
this.cycleAnalytics = Vue.resource(this.requestPath);
} }
fetchCycleAnalyticsData(options) { fetchCycleAnalyticsData(options = { startDate: 30 }) {
options = options || { startDate: 30 }; return this.cycleAnalytics.get({ cycle_analytics: { start_date: options.startDate } });
return $.ajax({
url: this.requestPath,
method: 'GET',
dataType: 'json',
contentType: 'application/json',
data: {
cycle_analytics: {
start_date: options.startDate,
},
},
});
} }
fetchStageData(options) { fetchStageData(options) {
...@@ -30,12 +19,12 @@ class CycleAnalyticsService { ...@@ -30,12 +19,12 @@ class CycleAnalyticsService {
startDate, startDate,
} = options; } = options;
return $.get(`${this.requestPath}/events/${stage.name}.json`, { return Vue.http.get(`${this.requestPath}/events/${stage.name}.json`, {
cycle_analytics: { params: {
start_date: startDate, cycle_analytics: {
start_date: startDate,
},
}, },
}); });
} }
} }
global.cycleAnalytics.CycleAnalyticsService = CycleAnalyticsService;
...@@ -4,9 +4,6 @@ import { __ } from '../locale'; ...@@ -4,9 +4,6 @@ import { __ } from '../locale';
import '../lib/utils/text_utility'; import '../lib/utils/text_utility';
import DEFAULT_EVENT_OBJECTS from './default_event_objects'; import DEFAULT_EVENT_OBJECTS from './default_event_objects';
const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {};
const EMPTY_STAGE_TEXTS = { const EMPTY_STAGE_TEXTS = {
issue: __('The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.'), issue: __('The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.'),
plan: __('The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.'), plan: __('The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.'),
...@@ -17,7 +14,7 @@ const EMPTY_STAGE_TEXTS = { ...@@ -17,7 +14,7 @@ const EMPTY_STAGE_TEXTS = {
production: __('The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.'), production: __('The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.'),
}; };
global.cycleAnalytics.CycleAnalyticsStore = { export default {
state: { state: {
summary: '', summary: '',
stats: '', stats: '',
......
...@@ -77,6 +77,7 @@ import initProjectVisibilitySelector from './project_visibility'; ...@@ -77,6 +77,7 @@ import initProjectVisibilitySelector from './project_visibility';
import GpgBadges from './gpg_badges'; import GpgBadges from './gpg_badges';
import UserFeatureHelper from './helpers/user_feature_helper'; import UserFeatureHelper from './helpers/user_feature_helper';
import initChangesDropdown from './init_changes_dropdown'; import initChangesDropdown from './init_changes_dropdown';
import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils';
(function() { (function() {
var Dispatcher; var Dispatcher;
...@@ -100,7 +101,7 @@ import initChangesDropdown from './init_changes_dropdown'; ...@@ -100,7 +101,7 @@ import initChangesDropdown from './init_changes_dropdown';
$('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => { $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources); const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
const enableGFM = gl.utils.convertPermissionToBoolean(el.dataset.supportsAutocomplete); const enableGFM = convertPermissionToBoolean(el.dataset.supportsAutocomplete);
gfm.setup($(el), { gfm.setup($(el), {
emojis: true, emojis: true,
members: enableGFM, members: enableGFM,
...@@ -351,7 +352,7 @@ import initChangesDropdown from './init_changes_dropdown'; ...@@ -351,7 +352,7 @@ import initChangesDropdown from './init_changes_dropdown';
if ($('.blob-viewer').length) new BlobViewer(); if ($('.blob-viewer').length) new BlobViewer();
if ($('.project-show-activity').length) new gl.Activities(); if ($('.project-show-activity').length) new gl.Activities();
$('#tree-slider').waitForImages(function() { $('#tree-slider').waitForImages(function() {
gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
}); });
break; break;
case 'projects:edit': case 'projects:edit':
...@@ -427,7 +428,7 @@ import initChangesDropdown from './init_changes_dropdown'; ...@@ -427,7 +428,7 @@ import initChangesDropdown from './init_changes_dropdown';
new NewCommitForm($('.js-create-dir-form')); new NewCommitForm($('.js-create-dir-form'));
new UserCallout({ setCalloutPerProject: true }); new UserCallout({ setCalloutPerProject: true });
$('#tree-slider').waitForImages(function() { $('#tree-slider').waitForImages(function() {
gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
}); });
break; break;
case 'projects:find_file:show': case 'projects:find_file:show':
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* global Dropzone */ /* global Dropzone */
import _ from 'underscore'; import _ from 'underscore';
import './preview_markdown'; import './preview_markdown';
import csrf from './lib/utils/csrf';
window.DropzoneInput = (function() { window.DropzoneInput = (function() {
function DropzoneInput(form) { function DropzoneInput(form) {
...@@ -50,9 +51,7 @@ window.DropzoneInput = (function() { ...@@ -50,9 +51,7 @@ window.DropzoneInput = (function() {
paramName: 'file', paramName: 'file',
maxFilesize: maxFileSize, maxFilesize: maxFileSize,
uploadMultiple: false, uploadMultiple: false,
headers: { headers: csrf.headers,
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
previewContainer: false, previewContainer: false,
processing: function() { processing: function() {
return $('.div-dropzone-alert').alert('close'); return $('.div-dropzone-alert').alert('close');
...@@ -260,9 +259,7 @@ window.DropzoneInput = (function() { ...@@ -260,9 +259,7 @@ window.DropzoneInput = (function() {
dataType: 'json', dataType: 'json',
processData: false, processData: false,
contentType: false, contentType: false,
headers: { headers: csrf.headers,
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
beforeSend: function() { beforeSend: function() {
showSpinner(); showSpinner();
return closeAlertMessage(); return closeAlertMessage();
......
...@@ -6,7 +6,7 @@ import environmentTable from './environments_table.vue'; ...@@ -6,7 +6,7 @@ import environmentTable from './environments_table.vue';
import EnvironmentsStore from '../stores/environments_store'; import EnvironmentsStore from '../stores/environments_store';
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue'; import tablePagination from '../../vue_shared/components/table_pagination.vue';
import '../../lib/utils/common_utils'; import { convertPermissionToBoolean, getParameterByName, setParamInURL } from '../../lib/utils/common_utils';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import Poll from '../../lib/utils/poll'; import Poll from '../../lib/utils/poll';
import environmentsMixin from '../mixins/environments_mixin'; import environmentsMixin from '../mixins/environments_mixin';
...@@ -51,19 +51,19 @@ export default { ...@@ -51,19 +51,19 @@ export default {
computed: { computed: {
scope() { scope() {
return gl.utils.getParameterByName('scope'); return getParameterByName('scope');
}, },
canReadEnvironmentParsed() { canReadEnvironmentParsed() {
return gl.utils.convertPermissionToBoolean(this.canReadEnvironment); return convertPermissionToBoolean(this.canReadEnvironment);
}, },
canCreateDeploymentParsed() { canCreateDeploymentParsed() {
return gl.utils.convertPermissionToBoolean(this.canCreateDeployment); return convertPermissionToBoolean(this.canCreateDeployment);
}, },
canCreateEnvironmentParsed() { canCreateEnvironmentParsed() {
return gl.utils.convertPermissionToBoolean(this.canCreateEnvironment); return convertPermissionToBoolean(this.canCreateEnvironment);
}, },
}, },
...@@ -72,8 +72,8 @@ export default { ...@@ -72,8 +72,8 @@ export default {
* Toggles loading property. * Toggles loading property.
*/ */
created() { created() {
const scope = gl.utils.getParameterByName('scope') || this.visibility; const scope = getParameterByName('scope') || this.visibility;
const page = gl.utils.getParameterByName('page') || this.pageNumber; const page = getParameterByName('page') || this.pageNumber;
this.service = new EnvironmentsService(this.endpoint); this.service = new EnvironmentsService(this.endpoint);
...@@ -126,15 +126,15 @@ export default { ...@@ -126,15 +126,15 @@ export default {
* @return {String} * @return {String}
*/ */
changePage(pageNumber) { changePage(pageNumber) {
const param = gl.utils.setParamInURL('page', pageNumber); const param = setParamInURL('page', pageNumber);
gl.utils.visitUrl(param); gl.utils.visitUrl(param);
return param; return param;
}, },
fetchEnvironments() { fetchEnvironments() {
const scope = gl.utils.getParameterByName('scope') || this.visibility; const scope = getParameterByName('scope') || this.visibility;
const page = gl.utils.getParameterByName('page') || this.pageNumber; const page = getParameterByName('page') || this.pageNumber;
this.isLoading = true; this.isLoading = true;
......
...@@ -9,7 +9,7 @@ import tablePagination from '../../vue_shared/components/table_pagination.vue'; ...@@ -9,7 +9,7 @@ import tablePagination from '../../vue_shared/components/table_pagination.vue';
import Poll from '../../lib/utils/poll'; import Poll from '../../lib/utils/poll';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import environmentsMixin from '../mixins/environments_mixin'; import environmentsMixin from '../mixins/environments_mixin';
import '../../lib/utils/common_utils'; import { convertPermissionToBoolean, getParameterByName, setParamInURL } from '../../lib/utils/common_utils';
export default { export default {
components: { components: {
...@@ -47,15 +47,15 @@ export default { ...@@ -47,15 +47,15 @@ export default {
computed: { computed: {
scope() { scope() {
return gl.utils.getParameterByName('scope'); return getParameterByName('scope');
}, },
canReadEnvironmentParsed() { canReadEnvironmentParsed() {
return gl.utils.convertPermissionToBoolean(this.canReadEnvironment); return convertPermissionToBoolean(this.canReadEnvironment);
}, },
canCreateDeploymentParsed() { canCreateDeploymentParsed() {
return gl.utils.convertPermissionToBoolean(this.canCreateDeployment); return convertPermissionToBoolean(this.canCreateDeployment);
}, },
/** /**
...@@ -82,8 +82,8 @@ export default { ...@@ -82,8 +82,8 @@ export default {
* Toggles loading property. * Toggles loading property.
*/ */
created() { created() {
const scope = gl.utils.getParameterByName('scope') || this.visibility; const scope = getParameterByName('scope') || this.visibility;
const page = gl.utils.getParameterByName('page') || this.pageNumber; const page = getParameterByName('page') || this.pageNumber;
this.service = new EnvironmentsService(this.endpoint); this.service = new EnvironmentsService(this.endpoint);
...@@ -125,15 +125,15 @@ export default { ...@@ -125,15 +125,15 @@ export default {
* @param {Number} pageNumber desired page to go to. * @param {Number} pageNumber desired page to go to.
*/ */
changePage(pageNumber) { changePage(pageNumber) {
const param = gl.utils.setParamInURL('page', pageNumber); const param = setParamInURL('page', pageNumber);
gl.utils.visitUrl(param); gl.utils.visitUrl(param);
return param; return param;
}, },
fetchEnvironments() { fetchEnvironments() {
const scope = gl.utils.getParameterByName('scope') || this.visibility; const scope = getParameterByName('scope') || this.visibility;
const page = gl.utils.getParameterByName('page') || this.pageNumber; const page = getParameterByName('page') || this.pageNumber;
this.isLoading = true; this.isLoading = true;
......
import '~/lib/utils/common_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
/** /**
* Environments Store. * Environments Store.
* *
...@@ -66,8 +66,8 @@ export default class EnvironmentsStore { ...@@ -66,8 +66,8 @@ export default class EnvironmentsStore {
} }
setPagination(pagination = {}) { setPagination(pagination = {}) {
const normalizedHeaders = gl.utils.normalizeHeaders(pagination); const normalizedHeaders = normalizeHeaders(pagination);
const paginationInformation = gl.utils.parseIntPagination(normalizedHeaders); const paginationInformation = parseIntPagination(normalizedHeaders);
this.state.paginationInformation = paginationInformation; this.state.paginationInformation = paginationInformation;
return paginationInformation; return paginationInformation;
......
...@@ -34,7 +34,7 @@ export const canShowActiveSubItems = (el) => { ...@@ -34,7 +34,7 @@ export const canShowActiveSubItems = (el) => {
export const canShowSubItems = () => bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md' || bp.getBreakpointSize() === 'lg'; export const canShowSubItems = () => bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md' || bp.getBreakpointSize() === 'lg';
export const getHideSubItemsInterval = () => { export const getHideSubItemsInterval = () => {
if (!currentOpenMenu) return 0; if (!currentOpenMenu || !mousePos.length) return 0;
const currentMousePos = mousePos[mousePos.length - 1]; const currentMousePos = mousePos[mousePos.length - 1];
const prevMousePos = mousePos[0]; const prevMousePos = mousePos[0];
......
<script> <script>
import tablePagination from '~/vue_shared/components/table_pagination.vue'; import tablePagination from '~/vue_shared/components/table_pagination.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import { getParameterByName } from '../../lib/utils/common_utils';
export default { export default {
props: { props: {
...@@ -18,8 +19,8 @@ export default { ...@@ -18,8 +19,8 @@ export default {
}, },
methods: { methods: {
change(page) { change(page) {
const filterGroupsParam = gl.utils.getParameterByName('filter_groups'); const filterGroupsParam = getParameterByName('filter_groups');
const sortParam = gl.utils.getParameterByName('sort'); const sortParam = getParameterByName('sort');
eventHub.$emit('fetchPage', page, filterGroupsParam, sortParam); eventHub.$emit('fetchPage', page, filterGroupsParam, sortParam);
}, },
}, },
......
import FilterableList from '~/filterable_list'; import FilterableList from '~/filterable_list';
import eventHub from './event_hub'; import eventHub from './event_hub';
import { getParameterByName } from '../lib/utils/common_utils';
export default class GroupFilterableList extends FilterableList { export default class GroupFilterableList extends FilterableList {
constructor({ form, filter, holder, filterEndpoint, pagePath }) { constructor({ form, filter, holder, filterEndpoint, pagePath }) {
...@@ -54,7 +55,7 @@ export default class GroupFilterableList extends FilterableList { ...@@ -54,7 +55,7 @@ export default class GroupFilterableList extends FilterableList {
e.preventDefault(); e.preventDefault();
const queryData = {}; const queryData = {};
const sortParam = gl.utils.getParameterByName('sort', e.currentTarget.href); const sortParam = getParameterByName('sort', e.currentTarget.href);
if (sortParam) { if (sortParam) {
queryData.sort = sortParam; queryData.sort = sortParam;
......
...@@ -8,6 +8,7 @@ import GroupItem from './components/group_item.vue'; ...@@ -8,6 +8,7 @@ import GroupItem from './components/group_item.vue';
import GroupsStore from './stores/groups_store'; import GroupsStore from './stores/groups_store';
import GroupsService from './services/groups_service'; import GroupsService from './services/groups_service';
import eventHub from './event_hub'; import eventHub from './event_hub';
import { getParameterByName } from '../lib/utils/common_utils';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const el = document.getElementById('dashboard-group-app'); const el = document.getElementById('dashboard-group-app');
...@@ -58,17 +59,17 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -58,17 +59,17 @@ document.addEventListener('DOMContentLoaded', () => {
this.isLoading = true; this.isLoading = true;
} }
pageParam = gl.utils.getParameterByName('page'); pageParam = getParameterByName('page');
if (pageParam) { if (pageParam) {
page = pageParam; page = pageParam;
} }
filterGroupsParam = gl.utils.getParameterByName('filter_groups'); filterGroupsParam = getParameterByName('filter_groups');
if (filterGroupsParam) { if (filterGroupsParam) {
filterGroups = filterGroupsParam; filterGroups = filterGroupsParam;
} }
sortParam = gl.utils.getParameterByName('sort'); sortParam = getParameterByName('sort');
if (sortParam) { if (sortParam) {
sort = sortParam; sort = sortParam;
} }
......
import Vue from 'vue'; import Vue from 'vue';
import { parseIntPagination, normalizeHeaders } from '../../lib/utils/common_utils';
export default class GroupsStore { export default class GroupsStore {
constructor() { constructor() {
...@@ -30,8 +31,8 @@ export default class GroupsStore { ...@@ -30,8 +31,8 @@ export default class GroupsStore {
let paginationInfo; let paginationInfo;
if (Object.keys(pagination).length) { if (Object.keys(pagination).length) {
const normalizedHeaders = gl.utils.normalizeHeaders(pagination); const normalizedHeaders = normalizeHeaders(pagination);
paginationInfo = gl.utils.parseIntPagination(normalizedHeaders); paginationInfo = parseIntPagination(normalizedHeaders);
} else { } else {
paginationInfo = pagination; paginationInfo = pagination;
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
prefer-rest-params, prefer-spread, no-unused-vars, prefer-template, prefer-rest-params, prefer-spread, no-unused-vars, prefer-template,
promise/catch-or-return */ promise/catch-or-return */
import Api from './api'; import Api from './api';
import { normalizeCRLFHeaders } from './lib/utils/common_utils';
var slice = [].slice; var slice = [].slice;
...@@ -30,7 +31,7 @@ window.GroupsSelect = (function() { ...@@ -30,7 +31,7 @@ window.GroupsSelect = (function() {
$.ajax(params).then((data, status, xhr) => { $.ajax(params).then((data, status, xhr) => {
const results = data || []; const results = data || [];
const headers = gl.utils.normalizeCRLFHeaders(xhr.getAllResponseHeaders()); const headers = normalizeCRLFHeaders(xhr.getAllResponseHeaders());
const currentPage = parseInt(headers['X-PAGE'], 10) || 0; const currentPage = parseInt(headers['X-PAGE'], 10) || 0;
const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0; const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0;
const more = currentPage < totalPages; const more = currentPage < totalPages;
......
// We will render the icons list here
if ($('#user-content-gitlab-icons').length > 0) {
const $iconsHeader = $('#user-content-gitlab-icons');
const $iconsList = $('<div id="iconsList">ICONS</div>');
$($iconsList).insertAfter($iconsHeader.parent());
}
/*
This module provides easy access to the CSRF token and caches
it for re-use. It also exposes some values commonly used in relation
to the CSRF token (header key and headers object).
If you need to refresh the csrfToken for some reason, just call `init` and
then use the accessors as you would normally.
If you need to compose a headers object, use the spread operator:
```
headers: {
...csrf.headers,
someOtherHeader: '12345',
}
```
*/
const csrf = {
init() {
const tokenEl = document.querySelector('meta[name=csrf-token]');
if (tokenEl !== null) {
this.csrfToken = tokenEl.getAttribute('content');
} else {
this.csrfToken = null;
}
},
get token() {
return this.csrfToken;
},
get headerKey() {
return 'X-CSRF-Token';
},
get headers() {
if (this.csrfToken !== null) {
return {
[this.headerKey]: this.token,
};
}
return {};
},
};
csrf.init();
// use our cached token for any $.rails-generated AJAX requests
if ($.rails) {
$.rails.csrfToken = () => csrf.token;
}
export default csrf;
import httpStatusCodes from './http_status'; import httpStatusCodes from './http_status';
import { normalizeHeaders } from './common_utils';
/** /**
* Polling utility for handling realtime updates. * Polling utility for handling realtime updates.
...@@ -57,7 +58,7 @@ export default class Poll { ...@@ -57,7 +58,7 @@ export default class Poll {
} }
checkConditions(response) { checkConditions(response) {
const headers = gl.utils.normalizeHeaders(response.headers); const headers = normalizeHeaders(response.headers);
const pollInterval = parseInt(headers[this.intervalHeader], 10); const pollInterval = parseInt(headers[this.intervalHeader], 10);
if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) { if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
......
...@@ -8,6 +8,7 @@ import _ from 'underscore'; ...@@ -8,6 +8,7 @@ import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Dropzone from 'dropzone'; import Dropzone from 'dropzone';
import Sortable from 'vendor/Sortable'; import Sortable from 'vendor/Sortable';
import svg4everybody from 'svg4everybody';
// libraries with import side-effects // libraries with import side-effects
import 'mousetrap'; import 'mousetrap';
...@@ -40,7 +41,7 @@ import './commit/image_file'; ...@@ -40,7 +41,7 @@ import './commit/image_file';
// lib/utils // lib/utils
import './lib/utils/bootstrap_linked_tabs'; import './lib/utils/bootstrap_linked_tabs';
import './lib/utils/common_utils'; import { handleLocationHash } from './lib/utils/common_utils';
import './lib/utils/datetime_utility'; import './lib/utils/datetime_utility';
import './lib/utils/pretty_time'; import './lib/utils/pretty_time';
import './lib/utils/text_utility'; import './lib/utils/text_utility';
...@@ -151,6 +152,8 @@ if (process.env.NODE_ENV !== 'production') require('./test_utils/'); ...@@ -151,6 +152,8 @@ if (process.env.NODE_ENV !== 'production') require('./test_utils/');
Dropzone.autoDiscover = false; Dropzone.autoDiscover = false;
svg4everybody();
document.addEventListener('beforeunload', function () { document.addEventListener('beforeunload', function () {
// Unbind scroll events // Unbind scroll events
$(document).off('scroll'); $(document).off('scroll');
...@@ -160,10 +163,10 @@ document.addEventListener('beforeunload', function () { ...@@ -160,10 +163,10 @@ document.addEventListener('beforeunload', function () {
$('[data-toggle="popover"]').popover('destroy'); $('[data-toggle="popover"]').popover('destroy');
}); });
window.addEventListener('hashchange', gl.utils.handleLocationHash); window.addEventListener('hashchange', handleLocationHash);
window.addEventListener('load', function onLoad() { window.addEventListener('load', function onLoad() {
window.removeEventListener('load', onLoad, false); window.removeEventListener('load', onLoad, false);
gl.utils.handleLocationHash(); handleLocationHash();
}, false); }, false);
gl.lazyLoader = new LazyLoader({ gl.lazyLoader = new LazyLoader({
...@@ -189,7 +192,7 @@ $(function () { ...@@ -189,7 +192,7 @@ $(function () {
$body.on('click', 'a[href^="#"]', function() { $body.on('click', 'a[href^="#"]', function() {
var href = this.getAttribute('href'); var href = this.getAttribute('href');
if (href.substr(1) === gl.utils.getLocationHash()) { if (href.substr(1) === gl.utils.getLocationHash()) {
setTimeout(gl.utils.handleLocationHash, 1); setTimeout(handleLocationHash, 1);
} }
}); });
......
...@@ -7,6 +7,11 @@ import './flash'; ...@@ -7,6 +7,11 @@ import './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion'; import BlobForkSuggestion from './blob/blob_fork_suggestion';
import initChangesDropdown from './init_changes_dropdown'; import initChangesDropdown from './init_changes_dropdown';
import bp from './breakpoints'; import bp from './breakpoints';
import {
parseUrlPathname,
handleLocationHash,
isMetaClick,
} from './lib/utils/common_utils';
/* eslint-disable max-len */ /* eslint-disable max-len */
// MergeRequestTabs // MergeRequestTabs
...@@ -114,7 +119,7 @@ import bp from './breakpoints'; ...@@ -114,7 +119,7 @@ import bp from './breakpoints';
} }
clickTab(e) { clickTab(e) {
if (e.currentTarget && gl.utils.isMetaClick(e)) { if (e.currentTarget && isMetaClick(e)) {
const targetLink = e.currentTarget.getAttribute('href'); const targetLink = e.currentTarget.getAttribute('href');
e.stopImmediatePropagation(); e.stopImmediatePropagation();
e.preventDefault(); e.preventDefault();
...@@ -243,6 +248,8 @@ import bp from './breakpoints'; ...@@ -243,6 +248,8 @@ import bp from './breakpoints';
propsData: { propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint, endpoint: pipelineTableViewEl.dataset.endpoint,
helpPagePath: pipelineTableViewEl.dataset.helpPagePath, helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
emptyStateSvgPath: pipelineTableViewEl.dataset.emptyStateSvgPath,
errorStateSvgPath: pipelineTableViewEl.dataset.errorStateSvgPath,
autoDevopsHelpPath: pipelineTableViewEl.dataset.helpAutoDevopsPath, autoDevopsHelpPath: pipelineTableViewEl.dataset.helpAutoDevopsPath,
}, },
}).$mount(); }).$mount();
...@@ -260,7 +267,7 @@ import bp from './breakpoints'; ...@@ -260,7 +267,7 @@ import bp from './breakpoints';
// We extract pathname for the current Changes tab anchor href // We extract pathname for the current Changes tab anchor href
// some pages like MergeRequestsController#new has query parameters on that anchor // some pages like MergeRequestsController#new has query parameters on that anchor
const urlPathname = gl.utils.parseUrlPathname(source); const urlPathname = parseUrlPathname(source);
this.ajaxGet({ this.ajaxGet({
url: `${urlPathname}.json${location.search}`, url: `${urlPathname}.json${location.search}`,
...@@ -309,7 +316,7 @@ import bp from './breakpoints'; ...@@ -309,7 +316,7 @@ import bp from './breakpoints';
forceShow: true, forceShow: true,
}); });
anchor[0].scrollIntoView(); anchor[0].scrollIntoView();
window.gl.utils.handleLocationHash(); handleLocationHash();
// We have multiple elements on the page with `#note_xxx` // We have multiple elements on the page with `#note_xxx`
// (discussion and diff tabs) and `:target` only applies to the first // (discussion and diff tabs) and `:target` only applies to the first
anchor.addClass('target'); anchor.addClass('target');
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import MonitoringStore from '../stores/monitoring_store'; import MonitoringStore from '../stores/monitoring_store';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import { convertPermissionToBoolean } from '../../lib/utils/common_utils';
export default { export default {
...@@ -17,11 +18,14 @@ ...@@ -17,11 +18,14 @@
return { return {
store, store,
state: 'gettingStarted', state: 'gettingStarted',
hasMetrics: gl.utils.convertPermissionToBoolean(metricsData.hasMetrics), hasMetrics: convertPermissionToBoolean(metricsData.hasMetrics),
documentationPath: metricsData.documentationPath, documentationPath: metricsData.documentationPath,
settingsPath: metricsData.settingsPath, settingsPath: metricsData.settingsPath,
metricsEndpoint: metricsData.additionalMetrics, metricsEndpoint: metricsData.additionalMetrics,
deploymentEndpoint: metricsData.deploymentEndpoint, deploymentEndpoint: metricsData.deploymentEndpoint,
emptyGettingStartedSvgPath: metricsData.emptyGettingStartedSvgPath,
emptyLoadingSvgPath: metricsData.emptyLoadingSvgPath,
emptyUnableToConnectSvgPath: metricsData.emptyUnableToConnectSvgPath,
showEmptyState: true, showEmptyState: true,
updateAspectRatio: false, updateAspectRatio: false,
updatedAspectRatios: 0, updatedAspectRatios: 0,
...@@ -108,5 +112,8 @@ ...@@ -108,5 +112,8 @@
:selected-state="state" :selected-state="state"
:documentation-path="documentationPath" :documentation-path="documentationPath"
:settings-path="settingsPath" :settings-path="settingsPath"
:empty-getting-started-svg-path="emptyGettingStartedSvgPath"
:empty-loading-svg-path="emptyLoadingSvgPath"
:empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath"
/> />
</template> </template>
<script> <script>
import gettingStartedSvg from 'empty_states/monitoring/_getting_started.svg';
import loadingSvg from 'empty_states/monitoring/_loading.svg';
import unableToConnectSvg from 'empty_states/monitoring/_unable_to_connect.svg';
export default { export default {
props: { props: {
documentationPath: { documentationPath: {
...@@ -18,24 +14,36 @@ ...@@ -18,24 +14,36 @@
type: String, type: String,
required: true, required: true,
}, },
emptyGettingStartedSvgPath: {
type: String,
required: true,
},
emptyLoadingSvgPath: {
type: String,
required: true,
},
emptyUnableToConnectSvgPath: {
type: String,
required: true,
},
}, },
data() { data() {
return { return {
states: { states: {
gettingStarted: { gettingStarted: {
svg: gettingStartedSvg, svgUrl: this.emptyGettingStartedSvgPath,
title: 'Get started with performance monitoring', title: 'Get started with performance monitoring',
description: 'Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments.', description: 'Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments.',
buttonText: 'Configure Prometheus', buttonText: 'Configure Prometheus',
}, },
loading: { loading: {
svg: loadingSvg, svgUrl: this.emptyLoadingSvgPath,
title: 'Waiting for performance data', title: 'Waiting for performance data',
description: 'Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available.', description: 'Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available.',
buttonText: 'View documentation', buttonText: 'View documentation',
}, },
unableToConnect: { unableToConnect: {
svg: unableToConnectSvg, svgUrl: this.emptyUnableToConnectSvgPath,
title: 'Unable to connect to Prometheus server', title: 'Unable to connect to Prometheus server',
description: 'Ensure connectivity is available from the GitLab server to the ', description: 'Ensure connectivity is available from the GitLab server to the ',
buttonText: 'View documentation', buttonText: 'View documentation',
...@@ -66,7 +74,9 @@ ...@@ -66,7 +74,9 @@
<template> <template>
<div class="prometheus-state"> <div class="prometheus-state">
<div class="row"> <div class="row">
<div class="col-md-4 col-md-offset-4 state-svg" v-html="currentState.svg"></div> <div class="col-md-4 col-md-offset-4 state-svg svg-content">
<img :src="currentState.svgUrl"/>
</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6 col-md-offset-3"> <div class="col-md-6 col-md-offset-3">
......
import Vue from 'vue'; import Vue from 'vue';
import VueResource from 'vue-resource'; import VueResource from 'vue-resource';
import statusCodes from '../../lib/utils/http_status'; import statusCodes from '../../lib/utils/http_status';
import { backOff } from '../../lib/utils/common_utils';
Vue.use(VueResource); Vue.use(VueResource);
...@@ -8,7 +9,7 @@ const MAX_REQUESTS = 3; ...@@ -8,7 +9,7 @@ const MAX_REQUESTS = 3;
function backOffRequest(makeRequestCallback) { function backOffRequest(makeRequestCallback) {
let requestCounter = 0; let requestCounter = 0;
return gl.utils.backOff((next, stop) => { return backOff((next, stop) => {
makeRequestCallback().then((resp) => { makeRequestCallback().then((resp) => {
if (resp.status === statusCodes.NO_CONTENT) { if (resp.status === statusCodes.NO_CONTENT) {
requestCounter += 1; requestCounter += 1;
......
...@@ -23,6 +23,7 @@ import loadAwardsHandler from './awards_handler'; ...@@ -23,6 +23,7 @@ import loadAwardsHandler from './awards_handler';
import './autosave'; import './autosave';
import './dropzone_input'; import './dropzone_input';
import TaskList from './task_list'; import TaskList from './task_list';
import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils';
window.autosize = autosize; window.autosize = autosize;
window.Dropzone = Dropzone; window.Dropzone = Dropzone;
...@@ -81,7 +82,7 @@ export default class Notes { ...@@ -81,7 +82,7 @@ export default class Notes {
this.setViewType(view); this.setViewType(view);
// We are in the Merge Requests page so we need another edit form for Changes tab // We are in the Merge Requests page so we need another edit form for Changes tab
if (gl.utils.getPagePath(1) === 'merge_requests') { if (getPagePath(1) === 'merge_requests') {
$('.note-edit-form').clone() $('.note-edit-form').clone()
.addClass('mr-note-edit-form').insertAfter('.note-edit-form'); .addClass('mr-note-edit-form').insertAfter('.note-edit-form');
} }
...@@ -175,7 +176,7 @@ export default class Notes { ...@@ -175,7 +176,7 @@ export default class Notes {
keydownNoteText(e) { keydownNoteText(e) {
var $textarea, discussionNoteForm, editNote, myLastNote, myLastNoteEditBtn, newText, originalText; var $textarea, discussionNoteForm, editNote, myLastNote, myLastNoteEditBtn, newText, originalText;
if (gl.utils.isMetaKey(e)) { if (isMetaKey(e)) {
return; return;
} }
...@@ -644,10 +645,10 @@ export default class Notes { ...@@ -644,10 +645,10 @@ export default class Notes {
} }
else { else {
var $buttons = $el.find('.note-form-actions'); var $buttons = $el.find('.note-form-actions');
var isWidgetVisible = gl.utils.isInViewport($el.get(0)); var isWidgetVisible = isInViewport($el.get(0));
if (!isWidgetVisible) { if (!isWidgetVisible) {
gl.utils.scrollToElement($el); scrollToElement($el);
} }
$el.find('.js-finish-edit-warning').show(); $el.find('.js-finish-edit-warning').show();
...@@ -1188,7 +1189,7 @@ export default class Notes { ...@@ -1188,7 +1189,7 @@ export default class Notes {
} }
static checkMergeRequestStatus() { static checkMergeRequestStatus() {
if (gl.utils.getPagePath(1) === 'merge_requests') { if (getPagePath(1) === 'merge_requests') {
gl.mrWidget.checkStatus(); gl.mrWidget.checkStatus();
} }
} }
...@@ -1326,7 +1327,7 @@ export default class Notes { ...@@ -1326,7 +1327,7 @@ export default class Notes {
* 2) Identify comment type; a) Main thread b) Discussion thread c) Discussion resolve * 2) Identify comment type; a) Main thread b) Discussion thread c) Discussion resolve
* 3) Build temporary placeholder element (using `createPlaceholderNote`) * 3) Build temporary placeholder element (using `createPlaceholderNote`)
* 4) Show placeholder note on UI * 4) Show placeholder note on UI
* 5) Perform network request to submit the note using `gl.utils.ajaxPost` * 5) Perform network request to submit the note using `ajaxPost`
* a) If request is successfully completed * a) If request is successfully completed
* 1. Remove placeholder element * 1. Remove placeholder element
* 2. Show submitted Note element * 2. Show submitted Note element
...@@ -1408,7 +1409,7 @@ export default class Notes { ...@@ -1408,7 +1409,7 @@ export default class Notes {
/* eslint-disable promise/catch-or-return */ /* eslint-disable promise/catch-or-return */
// Make request to submit comment on server // Make request to submit comment on server
gl.utils.ajaxPost(formAction, formData) ajaxPost(formAction, formData)
.then((note) => { .then((note) => {
// Submission successful! remove placeholder // Submission successful! remove placeholder
$notesContainer.find(`#${noteUniqueId}`).remove(); $notesContainer.find(`#${noteUniqueId}`).remove();
...@@ -1481,7 +1482,7 @@ export default class Notes { ...@@ -1481,7 +1482,7 @@ export default class Notes {
* *
* 1) Get Form metadata * 1) Get Form metadata
* 2) Update note element with new content * 2) Update note element with new content
* 3) Perform network request to submit the updated note using `gl.utils.ajaxPost` * 3) Perform network request to submit the updated note using `ajaxPost`
* a) If request is successfully completed * a) If request is successfully completed
* 1. Show submitted Note element * 1. Show submitted Note element
* b) If request failed * b) If request failed
...@@ -1510,7 +1511,7 @@ export default class Notes { ...@@ -1510,7 +1511,7 @@ export default class Notes {
/* eslint-disable promise/catch-or-return */ /* eslint-disable promise/catch-or-return */
// Make request to update comment on server // Make request to update comment on server
gl.utils.ajaxPost(formAction, formData) ajaxPost(formAction, formData)
.then((note) => { .then((note) => {
// Submission successful! render final note element // Submission successful! render final note element
this.updateNote(note, $editingNote); this.updateNote(note, $editingNote);
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* global Flash, Autosave */ /* global Flash, Autosave */
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import _ from 'underscore'; import _ from 'underscore';
import autosize from 'vendor/autosize';
import '../../autosave'; import '../../autosave';
import TaskList from '../../task_list'; import TaskList from '../../task_list';
import * as constants from '../constants'; import * as constants from '../constants';
...@@ -97,6 +98,8 @@ ...@@ -97,6 +98,8 @@
methods: { methods: {
...mapActions([ ...mapActions([
'saveNote', 'saveNote',
'stopPolling',
'restartPolling',
'removePlaceholderNotes', 'removePlaceholderNotes',
]), ]),
setIsSubmitButtonDisabled(note, isSubmitting) { setIsSubmitButtonDisabled(note, isSubmitting) {
...@@ -125,10 +128,14 @@ ...@@ -125,10 +128,14 @@
} }
this.isSubmitting = true; this.isSubmitting = true;
this.note = ''; // Empty textarea while being requested. Repopulate in catch this.note = ''; // Empty textarea while being requested. Repopulate in catch
this.resizeTextarea();
this.stopPolling();
this.saveNote(noteData) this.saveNote(noteData)
.then((res) => { .then((res) => {
this.isSubmitting = false; this.isSubmitting = false;
this.restartPolling();
if (res.errors) { if (res.errors) {
if (res.errors.commands_only) { if (res.errors.commands_only) {
this.discard(); this.discard();
...@@ -175,6 +182,8 @@ ...@@ -175,6 +182,8 @@
if (shouldClear) { if (shouldClear) {
this.note = ''; this.note = '';
this.resizeTextarea();
this.$refs.markdownField.previewMarkdown = false;
} }
// reset autostave // reset autostave
...@@ -206,6 +215,11 @@ ...@@ -206,6 +215,11 @@
selector: '.notes', selector: '.notes',
}); });
}, },
resizeTextarea() {
this.$nextTick(() => {
autosize.update(this.$refs.textarea);
});
},
}, },
mixins: [ mixins: [
issuableStateMixin, issuableStateMixin,
...@@ -258,7 +272,8 @@ ...@@ -258,7 +272,8 @@
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath" :quick-actions-docs-path="quickActionsDocsPath"
:add-spacing-classes="false" :add-spacing-classes="false"
:is-confidential-issue="isIssueConfidential(getIssueData)"> :is-confidential-issue="isConfidentialIssue"
ref="markdownField">
<textarea <textarea
id="note-body" id="note-body"
name="note[note]" name="note[note]"
......
import iconArrowCircle from 'icons/_icon_arrow_circle_o_right.svg';
import iconCheck from 'icons/_icon_check_square_o.svg';
import iconClock from 'icons/_icon_clock_o.svg';
import iconCodeFork from 'icons/_icon_code_fork.svg';
import iconComment from 'icons/_icon_comment_o.svg';
import iconCommit from 'icons/_icon_commit.svg';
import iconEdit from 'icons/_icon_edit.svg';
import iconEye from 'icons/_icon_eye.svg';
import iconEyeSlash from 'icons/_icon_eye_slash.svg';
import iconMerge from 'icons/_icon_merge.svg';
import iconMerged from 'icons/_icon_merged.svg';
import iconRandom from 'icons/_icon_random.svg';
import iconClosed from 'icons/_icon_status_closed.svg';
import iconStatusOpen from 'icons/_icon_status_open.svg';
import iconStopwatch from 'icons/_icon_stopwatch.svg';
import iconTags from 'icons/_icon_tags.svg';
import iconUser from 'icons/_icon_user.svg';
export default {
icon_arrow_circle_o_right: iconArrowCircle,
icon_check_square_o: iconCheck,
icon_clock_o: iconClock,
icon_code_fork: iconCodeFork,
icon_comment_o: iconComment,
icon_commit: iconCommit,
icon_edit: iconEdit,
icon_eye: iconEye,
icon_eye_slash: iconEyeSlash,
icon_merge: iconMerge,
icon_merged: iconMerged,
icon_random: iconRandom,
icon_status_closed: iconClosed,
icon_status_open: iconStatusOpen,
icon_stopwatch: iconStopwatch,
icon_tags: iconTags,
icon_user: iconUser,
};
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import iconsMap from './issue_note_icons';
import issueNoteHeader from './issue_note_header.vue'; import issueNoteHeader from './issue_note_header.vue';
export default { export default {
...@@ -24,9 +23,9 @@ ...@@ -24,9 +23,9 @@
isTargetNote() { isTargetNote() {
return this.targetNoteHash === this.noteAnchorId; return this.targetNoteHash === this.noteAnchorId;
}, },
}, iconHtml() {
created() { return gl.utils.spriteIcon(this.note.system_note_icon_name);
this.svg = iconsMap[this.note.system_note_icon_name]; },
}, },
}; };
</script> </script>
...@@ -39,7 +38,7 @@ ...@@ -39,7 +38,7 @@
<div class="timeline-entry-inner"> <div class="timeline-entry-inner">
<div <div
class="timeline-icon" class="timeline-icon"
v-html="svg"> v-html="iconHtml">
</div> </div>
<div class="timeline-content"> <div class="timeline-content">
<div class="note-header"> <div class="note-header">
......
...@@ -7,6 +7,7 @@ import * as constants from '../constants'; ...@@ -7,6 +7,7 @@ import * as constants from '../constants';
import service from '../services/issue_notes_service'; import service from '../services/issue_notes_service';
import loadAwardsHandler from '../../awards_handler'; import loadAwardsHandler from '../../awards_handler';
import sidebarTimeTrackingEventHub from '../../sidebar/event_hub'; import sidebarTimeTrackingEventHub from '../../sidebar/event_hub';
import { isInViewport, scrollToElement } from '../../lib/utils/common_utils';
let eTagPoll; let eTagPoll;
...@@ -186,6 +187,14 @@ export const poll = ({ commit, state, getters }) => { ...@@ -186,6 +187,14 @@ export const poll = ({ commit, state, getters }) => {
}); });
}; };
export const stopPolling = () => {
eTagPoll.stop();
};
export const restartPolling = () => {
eTagPoll.restart();
};
export const fetchData = ({ commit, state, getters }) => { export const fetchData = ({ commit, state, getters }) => {
const requestData = { endpoint: state.notesData.notesPath, lastFetchedAt: state.lastFetchedAt }; const requestData = { endpoint: state.notesData.notesPath, lastFetchedAt: state.lastFetchedAt };
...@@ -211,7 +220,7 @@ export const toggleAwardRequest = ({ commit, getters, dispatch }, data) => { ...@@ -211,7 +220,7 @@ export const toggleAwardRequest = ({ commit, getters, dispatch }, data) => {
}; };
export const scrollToNoteIfNeeded = (context, el) => { export const scrollToNoteIfNeeded = (context, el) => {
if (!gl.utils.isInViewport(el[0])) { if (!isInViewport(el[0])) {
gl.utils.scrollToElement(el); scrollToElement(el);
} }
}; };
...@@ -5,15 +5,19 @@ import * as constants from '../constants'; ...@@ -5,15 +5,19 @@ import * as constants from '../constants';
export default { export default {
[types.ADD_NEW_NOTE](state, note) { [types.ADD_NEW_NOTE](state, note) {
const { discussion_id, type } = note; const { discussion_id, type } = note;
const noteData = { const [exists] = state.notes.filter(n => n.id === note.discussion_id);
expanded: true,
id: discussion_id, if (!exists) {
individual_note: !(type === constants.DISCUSSION_NOTE), const noteData = {
notes: [note], expanded: true,
reply_id: discussion_id, id: discussion_id,
}; individual_note: !(type === constants.DISCUSSION_NOTE),
notes: [note],
state.notes.push(noteData); reply_id: discussion_id,
};
state.notes.push(noteData);
}
}, },
[types.ADD_NEW_REPLY_TO_DISCUSSION](state, note) { [types.ADD_NEW_REPLY_TO_DISCUSSION](state, note) {
......
import '~/lib/utils/common_utils'; import { getParameterByName } from '~/lib/utils/common_utils';
import '~/lib/utils/url_utility'; import '~/lib/utils/url_utility';
(() => { (() => {
...@@ -9,7 +9,7 @@ import '~/lib/utils/url_utility'; ...@@ -9,7 +9,7 @@ import '~/lib/utils/url_utility';
init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) { init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) {
this.url = $('.content_list').data('href') || gl.utils.removeParams(['limit', 'offset']); this.url = $('.content_list').data('href') || gl.utils.removeParams(['limit', 'offset']);
this.limit = limit; this.limit = limit;
this.offset = parseInt(gl.utils.getParameterByName('offset'), 10) || this.limit; this.offset = parseInt(getParameterByName('offset'), 10) || this.limit;
this.disable = disable; this.disable = disable;
this.prepareData = prepareData; this.prepareData = prepareData;
this.callback = callback; this.callback = callback;
......
import { convertPermissionToBoolean } from '../lib/utils/common_utils';
function insertRow($row) { function insertRow($row) {
const $rowClone = $row.clone(); const $rowClone = $row.clone();
$rowClone.removeAttr('data-is-persisted'); $rowClone.removeAttr('data-is-persisted');
...@@ -6,7 +8,7 @@ function insertRow($row) { ...@@ -6,7 +8,7 @@ function insertRow($row) {
} }
function removeRow($row) { function removeRow($row) {
const isPersisted = gl.utils.convertPermissionToBoolean($row.attr('data-is-persisted')); const isPersisted = convertPermissionToBoolean($row.attr('data-is-persisted'));
if (isPersisted) { if (isPersisted) {
$row.hide(); $row.hide();
......
import LinkedTabs from './lib/utils/bootstrap_linked_tabs'; import LinkedTabs from './lib/utils/bootstrap_linked_tabs';
import { setCiStatusFavicon } from './lib/utils/common_utils';
export default class Pipelines { export default class Pipelines {
constructor(options = {}) { constructor(options = {}) {
...@@ -8,7 +9,7 @@ export default class Pipelines { ...@@ -8,7 +9,7 @@ export default class Pipelines {
} }
if (options.pipelineStatusUrl) { if (options.pipelineStatusUrl) {
gl.utils.setCiStatusFavicon(options.pipelineStatusUrl); setCiStatusFavicon(options.pipelineStatusUrl);
} }
} }
} }
<script> <script>
import pipelinesEmptyStateSVG from 'empty_states/icons/_pipelines_empty.svg';
export default { export default {
props: { props: {
helpPagePath: { helpPagePath: {
type: String, type: String,
required: true, required: true,
}, },
emptyStateSvgPath: {
type: String,
required: true,
},
}, },
data: () => ({ pipelinesEmptyStateSVG }),
}; };
</script> </script>
<template> <template>
<div class="row empty-state js-empty-state"> <div class="row empty-state js-empty-state">
<div class="col-xs-12"> <div class="col-xs-12">
<div class="svg-content" v-html="pipelinesEmptyStateSVG" /> <div class="svg-content">
<img :src="emptyStateSvgPath"/>
</div>
</div> </div>
<div class="col-xs-12 text-center"> <div class="col-xs-12 text-center">
......
<script> <script>
import pipelinesErrorStateSVG from 'empty_states/icons/_pipelines_failed.svg';
export default { export default {
data: () => ({ pipelinesErrorStateSVG }), props: {
errorStateSvgPath: {
type: String,
required: true,
},
},
}; };
</script> </script>
<template> <template>
<div class="row empty-state js-pipelines-error-state"> <div class="row empty-state js-pipelines-error-state">
<div class="col-xs-12"> <div class="col-xs-12">
<div class="svg-content" v-html="pipelinesErrorStateSVG" /> <div class="svg-content">
<img :src="errorStateSvgPath"/>
</div>
</div> </div>
<div class="col-xs-12 text-center"> <div class="col-xs-12 text-center">
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import tablePagination from '../../vue_shared/components/table_pagination.vue'; import tablePagination from '../../vue_shared/components/table_pagination.vue';
import navigationTabs from './navigation_tabs.vue'; import navigationTabs from './navigation_tabs.vue';
import navigationControls from './nav_controls.vue'; import navigationControls from './nav_controls.vue';
import { convertPermissionToBoolean, getParameterByName, setParamInURL } from '../../lib/utils/common_utils';
export default { export default {
props: { props: {
...@@ -26,6 +27,8 @@ ...@@ -26,6 +27,8 @@
return { return {
endpoint: pipelinesData.endpoint, endpoint: pipelinesData.endpoint,
helpPagePath: pipelinesData.helpPagePath, helpPagePath: pipelinesData.helpPagePath,
emptyStateSvgPath: pipelinesData.emptyStateSvgPath,
errorStateSvgPath: pipelinesData.errorStateSvgPath,
autoDevopsPath: pipelinesData.helpAutoDevopsPath, autoDevopsPath: pipelinesData.helpAutoDevopsPath,
newPipelinePath: pipelinesData.newPipelinePath, newPipelinePath: pipelinesData.newPipelinePath,
canCreatePipeline: pipelinesData.canCreatePipeline, canCreatePipeline: pipelinesData.canCreatePipeline,
...@@ -44,10 +47,10 @@ ...@@ -44,10 +47,10 @@
}, },
computed: { computed: {
canCreatePipelineParsed() { canCreatePipelineParsed() {
return gl.utils.convertPermissionToBoolean(this.canCreatePipeline); return convertPermissionToBoolean(this.canCreatePipeline);
}, },
scope() { scope() {
const scope = gl.utils.getParameterByName('scope'); const scope = getParameterByName('scope');
return scope === null ? 'all' : scope; return scope === null ? 'all' : scope;
}, },
...@@ -105,10 +108,10 @@ ...@@ -105,10 +108,10 @@
}; };
}, },
pageParameter() { pageParameter() {
return gl.utils.getParameterByName('page') || this.pagenum; return getParameterByName('page') || this.pagenum;
}, },
scopeParameter() { scopeParameter() {
return gl.utils.getParameterByName('scope') || this.apiScope; return getParameterByName('scope') || this.apiScope;
}, },
}, },
created() { created() {
...@@ -122,7 +125,7 @@ ...@@ -122,7 +125,7 @@
* @param {Number} pageNumber desired page to go to. * @param {Number} pageNumber desired page to go to.
*/ */
change(pageNumber) { change(pageNumber) {
const param = gl.utils.setParamInURL('page', pageNumber); const param = setParamInURL('page', pageNumber);
gl.utils.visitUrl(param); gl.utils.visitUrl(param);
return param; return param;
...@@ -181,9 +184,13 @@ ...@@ -181,9 +184,13 @@
<empty-state <empty-state
v-if="shouldRenderEmptyState" v-if="shouldRenderEmptyState"
:help-page-path="helpPagePath" :help-page-path="helpPagePath"
:empty-state-svg-path="emptyStateSvgPath"
/> />
<error-state v-if="shouldRenderErrorState" /> <error-state
v-if="shouldRenderErrorState"
:error-state-svg-path="errorStateSvgPath"
/>
<div <div
class="blank-state blank-state-no-icon" class="blank-state blank-state-no-icon"
......
...@@ -69,8 +69,7 @@ ...@@ -69,8 +69,7 @@
@click="onClickAction(action.path)" @click="onClickAction(action.path)"
:class="{ disabled: isActionDisabled(action) }" :class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"> :disabled="isActionDisabled(action)">
<span v-html="playIconSvg"></span> {{action.name}}
<span>{{action.name}}</span>
</button> </button>
</li> </li>
</ul> </ul>
......
...@@ -39,11 +39,7 @@ ...@@ -39,11 +39,7 @@
rel="nofollow" rel="nofollow"
download download
:href="artifact.path"> :href="artifact.path">
<i Download {{artifact.name}} artifacts
class="fa fa-download"
aria-hidden="true">
</i>
<span>Download {{artifact.name}} artifacts</span>
</a> </a>
</li> </li>
</ul> </ul>
......
import { parseIntPagination, normalizeHeaders } from '../../lib/utils/common_utils';
export default class PipelinesStore { export default class PipelinesStore {
constructor() { constructor() {
this.state = {}; this.state = {};
...@@ -19,8 +21,8 @@ export default class PipelinesStore { ...@@ -19,8 +21,8 @@ export default class PipelinesStore {
let paginationInfo; let paginationInfo;
if (Object.keys(pagination).length) { if (Object.keys(pagination).length) {
const normalizedHeaders = gl.utils.normalizeHeaders(pagination); const normalizedHeaders = normalizeHeaders(pagination);
paginationInfo = gl.utils.parseIntPagination(normalizedHeaders); paginationInfo = parseIntPagination(normalizedHeaders);
} else { } else {
paginationInfo = pagination; paginationInfo = pagination;
} }
......
/* eslint-disable comma-dangle, no-unused-vars, class-methods-use-this, quotes, consistent-return, func-names, prefer-arrow-callback, space-before-function-paren, max-len */ /* eslint-disable comma-dangle, no-unused-vars, class-methods-use-this, quotes, consistent-return, func-names, prefer-arrow-callback, space-before-function-paren, max-len */
/* global Flash */ /* global Flash */
import { getPagePath } from '../lib/utils/common_utils';
((global) => { ((global) => {
class Profile { class Profile {
...@@ -93,7 +94,7 @@ ...@@ -93,7 +94,7 @@
return $title.val(comment[1]).change(); return $title.val(comment[1]).change();
} }
}); });
if (global.utils.getPagePath() === 'profiles') { if (getPagePath() === 'profiles') {
return new Profile(); return new Profile();
} }
}); });
......
import PANEL_STATE from './constants'; import PANEL_STATE from './constants';
import { backOff } from '../lib/utils/common_utils';
export default class PrometheusMetrics { export default class PrometheusMetrics {
constructor(wrapperSelector) { constructor(wrapperSelector) {
...@@ -79,7 +80,7 @@ export default class PrometheusMetrics { ...@@ -79,7 +80,7 @@ export default class PrometheusMetrics {
loadActiveMetrics() { loadActiveMetrics() {
this.showMonitoringMetricsPanelState(PANEL_STATE.LOADING); this.showMonitoringMetricsPanelState(PANEL_STATE.LOADING);
gl.utils.backOff((next, stop) => { backOff((next, stop) => {
$.getJSON(this.activeMetricsEndpoint) $.getJSON(this.activeMetricsEndpoint)
.done((res) => { .done((res) => {
if (res && res.success) { if (res && res.success) {
......
/* eslint-disable comma-dangle, no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, no-cond-assign, consistent-return, object-shorthand, prefer-arrow-callback, func-names, space-before-function-paren, prefer-template, quotes, class-methods-use-this, no-unused-expressions, no-sequences, wrap-iife, no-lonely-if, no-else-return, no-param-reassign, vars-on-top, max-len */ /* eslint-disable comma-dangle, no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, no-cond-assign, consistent-return, object-shorthand, prefer-arrow-callback, func-names, space-before-function-paren, prefer-template, quotes, class-methods-use-this, no-unused-expressions, no-sequences, wrap-iife, no-lonely-if, no-else-return, no-param-reassign, vars-on-top, max-len */
import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from './lib/utils/common_utils';
((global) => { ((global) => {
const KEYCODE = { const KEYCODE = {
...@@ -146,14 +147,14 @@ ...@@ -146,14 +147,14 @@
} }
getCategoryContents() { getCategoryContents() {
var dashboardOptions, groupOptions, issuesPath, items, mrPath, name, options, projectOptions, userId, userName, utils; var dashboardOptions, groupOptions, issuesPath, items, mrPath, name, options, projectOptions, userId, userName;
userId = gon.current_user_id; userId = gon.current_user_id;
userName = gon.current_username; userName = gon.current_username;
utils = gl.utils, projectOptions = gl.projectOptions, groupOptions = gl.groupOptions, dashboardOptions = gl.dashboardOptions; projectOptions = gl.projectOptions, groupOptions = gl.groupOptions, dashboardOptions = gl.dashboardOptions;
if (utils.isInGroupsPage() && groupOptions) { if (isInGroupsPage() && groupOptions) {
options = groupOptions[utils.getGroupSlug()]; options = groupOptions[getGroupSlug()];
} else if (utils.isInProjectPage() && projectOptions) { } else if (isInProjectPage() && projectOptions) {
options = projectOptions[utils.getProjectSlug()]; options = projectOptions[getProjectSlug()];
} else if (dashboardOptions) { } else if (dashboardOptions) {
options = dashboardOptions; options = dashboardOptions;
} }
......
/* eslint-disable class-methods-use-this, no-unneeded-ternary, quote-props */ /* eslint-disable class-methods-use-this, no-unneeded-ternary, quote-props */
import UsersSelect from './users_select'; import UsersSelect from './users_select';
import { isMetaClick } from './lib/utils/common_utils';
export default class Todos { export default class Todos {
constructor() { constructor() {
...@@ -137,22 +138,17 @@ export default class Todos { ...@@ -137,22 +138,17 @@ export default class Todos {
goToTodoUrl(e) { goToTodoUrl(e) {
const todoLink = this.dataset.url; const todoLink = this.dataset.url;
if (!todoLink) { if (!todoLink || e.target.tagName === 'A' || e.target.tagName === 'IMG') {
return; return;
} }
if (gl.utils.isMetaClick(e)) { e.stopPropagation();
e.preventDefault();
if (isMetaClick(e)) {
const windowTarget = '_blank'; const windowTarget = '_blank';
const selected = e.target;
e.stopPropagation(); window.open(todoLink, windowTarget);
e.preventDefault();
if (selected.tagName === 'IMG') {
const avatarUrl = selected.parentElement.getAttribute('href');
window.open(avatarUrl, windowTarget);
} else {
window.open(todoLink, windowTarget);
}
} else { } else {
gl.utils.visitUrl(todoLink); gl.utils.visitUrl(todoLink);
} }
......
import statusCodes from '../../lib/utils/http_status'; import statusCodes from '../../lib/utils/http_status';
import { bytesToMiB } from '../../lib/utils/number_utils'; import { bytesToMiB } from '../../lib/utils/number_utils';
import { backOff } from '../../lib/utils/common_utils';
import MemoryGraph from '../../vue_shared/components/memory_graph'; import MemoryGraph from '../../vue_shared/components/memory_graph';
import MRWidgetService from '../services/mr_widget_service'; import MRWidgetService from '../services/mr_widget_service';
...@@ -84,7 +84,7 @@ export default { ...@@ -84,7 +84,7 @@ export default {
} }
}, },
loadMetrics() { loadMetrics() {
gl.utils.backOff((next, stop) => { backOff((next, stop) => {
MRWidgetService.fetchMetrics(this.metricsUrl) MRWidgetService.fetchMetrics(this.metricsUrl)
.then((res) => { .then((res) => {
if (res.status === statusCodes.NO_CONTENT) { if (res.status === statusCodes.NO_CONTENT) {
......
...@@ -31,6 +31,7 @@ import { ...@@ -31,6 +31,7 @@ import {
SquashBeforeMerge, SquashBeforeMerge,
notify, notify,
} from './dependencies'; } from './dependencies';
import { setFavicon } from '../lib/utils/common_utils';
export default { export default {
el: '#js-vue-mr-widget', el: '#js-vue-mr-widget',
...@@ -86,7 +87,7 @@ export default { ...@@ -86,7 +87,7 @@ export default {
.then((res) => { .then((res) => {
this.handleNotification(res); this.handleNotification(res);
this.mr.setData(res); this.mr.setData(res);
this.setFavicon(); this.setFaviconHelper();
if (cb) { if (cb) {
cb.call(null, res); cb.call(null, res);
...@@ -115,9 +116,9 @@ export default { ...@@ -115,9 +116,9 @@ export default {
immediateExecution: true, immediateExecution: true,
}); });
}, },
setFavicon() { setFaviconHelper() {
if (this.mr.ciStatusFaviconPath) { if (this.mr.ciStatusFaviconPath) {
gl.utils.setFavicon(this.mr.ciStatusFaviconPath); setFavicon(this.mr.ciStatusFaviconPath);
} }
}, },
fetchDeployments() { fetchDeployments() {
...@@ -193,7 +194,7 @@ export default { ...@@ -193,7 +194,7 @@ export default {
}); });
}, },
handleMounted() { handleMounted() {
this.setFavicon(); this.setFaviconHelper();
this.initDeploymentsPolling(); this.initDeploymentsPolling();
}, },
}, },
......
<script> <script>
import { s__ } from '../../locale';
const PAGINATION_UI_BUTTON_LIMIT = 4; const PAGINATION_UI_BUTTON_LIMIT = 4;
const UI_LIMIT = 6; const UI_LIMIT = 6;
const SPREAD = '...'; const SPREAD = '...';
const PREV = 'Prev'; const PREV = s__('Pagination|Prev');
const NEXT = 'Next'; const NEXT = s__('Pagination|Next');
const FIRST = '« First'; const FIRST = s__('Pagination|« First');
const LAST = 'Last »'; const LAST = s__('Pagination|Last »');
export default { export default {
props: { props: {
......
import Vue from 'vue'; import Vue from 'vue';
import VueResource from 'vue-resource'; import VueResource from 'vue-resource';
import csrf from '../lib/utils/csrf';
Vue.use(VueResource); Vue.use(VueResource);
...@@ -18,9 +19,7 @@ Vue.http.interceptors.push((request, next) => { ...@@ -18,9 +19,7 @@ Vue.http.interceptors.push((request, next) => {
// New Vue Resource version uses Headers, we are expecting a plain object to render pagination // New Vue Resource version uses Headers, we are expecting a plain object to render pagination
// and polling. // and polling.
Vue.http.interceptors.push((request, next) => { Vue.http.interceptors.push((request, next) => {
if ($.rails) { request.headers.set(csrf.headerKey, csrf.token);
request.headers.set('X-CSRF-Token', $.rails.csrfToken());
}
next((response) => { next((response) => {
// Headers object has a `forEach` property that iterates through all values. // Headers object has a `forEach` property that iterates through all values.
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
} }
.emoji-menu { .emoji-menu {
display: none;
position: absolute; position: absolute;
top: 0; top: 0;
margin-top: 3px; margin-top: 3px;
...@@ -27,6 +28,10 @@ ...@@ -27,6 +28,10 @@
transition: .3s cubic-bezier(.67, .06, .19, 1.44); transition: .3s cubic-bezier(.67, .06, .19, 1.44);
transition-property: transform, opacity; transition-property: transform, opacity;
&.is-rendered {
display: block;
}
&.is-aligned-right { &.is-aligned-right {
transform-origin: 100% -45px; transform-origin: 100% -45px;
} }
......
...@@ -319,16 +319,6 @@ ...@@ -319,16 +319,6 @@
padding: $gl-padding; padding: $gl-padding;
} }
.svg-content {
text-align: center;
svg {
max-width: 425px;
width: 100%;
padding: $gl-padding;
}
}
.emoji-icon { .emoji-icon {
display: inline-block; display: inline-block;
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
// Header // Header
header.navbar-gitlab-new { header.navbar-gitlab-new {
background: linear-gradient(to right, $color-900, $color-800); background-color: $color-900;
.navbar-collapse { .navbar-collapse {
color: $color-200; color: $color-200;
...@@ -201,7 +201,7 @@ body { ...@@ -201,7 +201,7 @@ body {
@include gitlab-theme($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-700, $theme-gray-700, $theme-gray-100, $theme-gray-700); @include gitlab-theme($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-700, $theme-gray-700, $theme-gray-100, $theme-gray-700);
header.navbar-gitlab-new { header.navbar-gitlab-new {
background: $theme-gray-100; background-color: $theme-gray-100;
box-shadow: 0 2px 0 0 $border-color; box-shadow: 0 2px 0 0 $border-color;
.logo-text svg { .logo-text svg {
......
...@@ -9,3 +9,27 @@ ...@@ -9,3 +9,27 @@
padding: 10px; padding: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.svg-content {
text-align: center;
padding: $gl-padding;
svg,
img {
max-width: 425px;
width: 100%;
}
}
@mixin svg-size($size) {
width: $size;
height: $size;
}
svg {
&.s16 { @include svg-size(16px); }
&.s24 { @include svg-size(24px); }
&.s32 { @include svg-size(32px); }
&.s48 { @include svg-size(48px); }
&.s72 { @include svg-size(72px); }
}
...@@ -375,8 +375,6 @@ header.navbar-gitlab-new { ...@@ -375,8 +375,6 @@ header.navbar-gitlab-new {
display: flex; display: flex;
width: 100%; width: 100%;
position: relative; position: relative;
padding-top: $gl-padding;
padding-bottom: $gl-padding;
align-items: center; align-items: center;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
} }
...@@ -388,6 +386,11 @@ header.navbar-gitlab-new { ...@@ -388,6 +386,11 @@ header.navbar-gitlab-new {
align-self: center; align-self: center;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
@media (max-width: $screen-xs-max) {
padding-left: 17px;
border-left: 1px solid $gl-text-color-quaternary;
}
.avatar-tile { .avatar-tile {
margin-right: 4px; margin-right: 4px;
border: 1px solid $border-color; border: 1px solid $border-color;
......
...@@ -445,9 +445,8 @@ $new-sidebar-collapsed-width: 50px; ...@@ -445,9 +445,8 @@ $new-sidebar-collapsed-width: 50px;
background-color: transparent; background-color: transparent;
border: 0; border: 0;
padding: 6px 16px; padding: 6px 16px;
margin: 0 16px 0 -15px; margin: 0 0 0 -15px;
height: 46px; height: 46px;
border-right: 1px solid $gl-text-color-quaternary;
i { i {
font-size: 20px; font-size: 20px;
...@@ -455,7 +454,12 @@ $new-sidebar-collapsed-width: 50px; ...@@ -455,7 +454,12 @@ $new-sidebar-collapsed-width: 50px;
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
display: inline-block; display: flex;
align-items: center;
i {
font-size: 18px;
}
} }
} }
......
...@@ -733,6 +733,12 @@ ul.notes { ...@@ -733,6 +733,12 @@ ul.notes {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
.btn {
svg path {
fill: $gray-darkest;
}
}
.btn.discussion-create-issue-btn { .btn.discussion-create-issue-btn {
margin-left: -4px; margin-left: -4px;
border-radius: 0; border-radius: 0;
...@@ -747,10 +753,6 @@ ul.notes { ...@@ -747,10 +753,6 @@ ul.notes {
border: 0; border: 0;
} }
} }
.new-issue-for-discussion path {
fill: $gray-darkest;
}
} }
} }
...@@ -823,16 +825,6 @@ ul.notes { ...@@ -823,16 +825,6 @@ ul.notes {
vertical-align: middle; vertical-align: middle;
} }
.discussion-next-btn {
svg {
margin: 0;
path {
fill: $gray-darkest;
}
}
}
// Merge request notes in diffs // Merge request notes in diffs
.diff-file { .diff-file {
// Diff is inline // Diff is inline
......
...@@ -117,19 +117,32 @@ module IssuableCollections ...@@ -117,19 +117,32 @@ module IssuableCollections
key = 'issuable_sort' key = 'issuable_sort'
cookies[key] = params[:sort] if params[:sort].present? cookies[key] = params[:sort] if params[:sort].present?
cookies[key] = update_cookie_value(cookies[key])
# id_desc and id_asc are old values for these two.
cookies[key] = sort_value_recently_created if cookies[key] == 'id_desc'
cookies[key] = sort_value_oldest_created if cookies[key] == 'id_asc'
params[:sort] = cookies[key] params[:sort] = cookies[key]
end end
def default_sort_order def default_sort_order
case params[:state] case params[:state]
when 'opened', 'all' then sort_value_recently_created when 'opened', 'all' then sort_value_created_date
when 'merged', 'closed' then sort_value_recently_updated when 'merged', 'closed' then sort_value_recently_updated
else sort_value_recently_created else sort_value_created_date
end
end
# Update old values to the actual ones.
def update_cookie_value(value)
case value
when 'id_asc' then sort_value_oldest_created
when 'id_desc' then sort_value_recently_created
when 'created_asc' then sort_value_created_date
when 'created_desc' then sort_value_created_date
when 'due_date_asc' then sort_value_due_date
when 'due_date_desc' then sort_value_due_date
when 'milestone_due_asc' then sort_value_milestone
when 'milestone_due_desc' then sort_value_milestone
when 'downvotes_asc' then sort_value_popularity
when 'downvotes_desc' then sort_value_popularity
else value
end end
end end
end end
...@@ -3,8 +3,13 @@ class HelpController < ApplicationController ...@@ -3,8 +3,13 @@ class HelpController < ApplicationController
layout 'help' layout 'help'
# Taken from Jekyll
# https://github.com/jekyll/jekyll/blob/3.5-stable/lib/jekyll/document.rb#L13
YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
def index def index
@help_index = File.read(Rails.root.join('doc', 'README.md')) # Remove YAML frontmatter so that it doesn't look weird
@help_index = File.read(Rails.root.join('doc', 'README.md')).sub(YAML_FRONT_MATTER_REGEXP, '')
# Prefix Markdown links with `help/` unless they are external links # Prefix Markdown links with `help/` unless they are external links
# See http://rubular.com/r/X3baHTbPO2 # See http://rubular.com/r/X3baHTbPO2
...@@ -22,7 +27,8 @@ class HelpController < ApplicationController ...@@ -22,7 +27,8 @@ class HelpController < ApplicationController
path = File.join(Rails.root, 'doc', "#{@path}.md") path = File.join(Rails.root, 'doc', "#{@path}.md")
if File.exist?(path) if File.exist?(path)
@markdown = File.read(path) # Remove YAML frontmatter so that it doesn't look weird
@markdown = File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '')
render 'show.html.haml' render 'show.html.haml'
else else
......
...@@ -35,7 +35,10 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -35,7 +35,10 @@ class Projects::TreeController < Projects::ApplicationController
end end
format.json do format.json do
render json: TreeSerializer.new(project: @project, repository: @repository, ref: @ref).represent(@tree) # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/38261
Gitlab::GitalyClient.allow_n_plus_1_calls do
render json: TreeSerializer.new(project: @project, repository: @repository, ref: @ref).represent(@tree)
end
end end
end end
end end
......
...@@ -30,10 +30,4 @@ module AppearancesHelper ...@@ -30,10 +30,4 @@ module AppearancesHelper
render 'shared/logo.svg' render 'shared/logo.svg'
end end
end end
def custom_icon(icon_name, size: 16)
# We can't simply do the below, because there are some .erb SVGs.
# File.read(Rails.root.join("app/views/shared/icons/_#{icon_name}.svg")).html_safe
render "shared/icons/#{icon_name}.svg", size: size
end
end end
...@@ -13,22 +13,29 @@ module AvatarsHelper ...@@ -13,22 +13,29 @@ module AvatarsHelper
user_name = options[:user].try(:name) || options[:user_name] user_name = options[:user].try(:name) || options[:user_name]
avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size) avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size)
has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip] has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip]
data_attributes = {} data_attributes = options[:data] || {}
css_class = %W[avatar s#{avatar_size}].push(*options[:css_class]) css_class = %W[avatar s#{avatar_size}].push(*options[:css_class])
if has_tooltip if has_tooltip
css_class.push('has-tooltip') css_class.push('has-tooltip')
data_attributes = { container: 'body' } data_attributes[:container] = 'body'
end end
image_tag( if options[:lazy]
avatar_url, css_class << 'lazy'
data_attributes[:src] = avatar_url
avatar_url = LazyImageTagHelper.placeholder_image
end
image_options = {
alt: "#{user_name}'s avatar",
src: avatar_url,
data: data_attributes,
class: css_class, class: css_class,
alt: "#{user_name}'s avatar", title: user_name
title: user_name, }
data: data_attributes,
lazy: true tag(:img, image_options)
)
end end
def user_avatar(options = {}) def user_avatar(options = {})
......
...@@ -176,13 +176,15 @@ module CommitsHelper ...@@ -176,13 +176,15 @@ module CommitsHelper
end end
end end
def view_file_button(commit_sha, diff_new_path, project) def view_file_button(commit_sha, diff_new_path, project, replaced: false)
title = replaced ? _('View replaced file @ ') : _('View file @ ')
link_to( link_to(
project_blob_path(project, project_blob_path(project,
tree_join(commit_sha, diff_new_path)), tree_join(commit_sha, diff_new_path)),
class: 'btn view-file js-view-file' class: 'btn view-file js-view-file'
) do ) do
raw('View file @ ') + content_tag(:span, Commit.truncate_sha(commit_sha), raw(title) + content_tag(:span, Commit.truncate_sha(commit_sha),
class: 'commit-sha') class: 'commit-sha')
end end
end end
......
...@@ -125,7 +125,7 @@ module GroupsHelper ...@@ -125,7 +125,7 @@ module GroupsHelper
end end
def default_help def default_help
s_("GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner.") s_("GroupSettings|This setting will be applied to all subgroups unless overridden by a group owner. Groups that already have access to the project will continue to have access unless removed manually.")
end end
def ancestor_locked_but_you_can_override(group) def ancestor_locked_but_you_can_override(group)
......
...@@ -17,6 +17,18 @@ module IconsHelper ...@@ -17,6 +17,18 @@ module IconsHelper
options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options) options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
end end
def custom_icon(icon_name, size: 16)
# We can't simply do the below, because there are some .erb SVGs.
# File.read(Rails.root.join("app/views/shared/icons/_#{icon_name}.svg")).html_safe
render "shared/icons/#{icon_name}.svg", size: size
end
def sprite_icon(icon_name, size: nil, css_class: nil)
css_classes = size ? "s#{size}" : nil
css_classes << " #{css_class}" unless css_class.blank?
content_tag(:svg, content_tag(:use, "", { "xlink:href" => "#{image_path('icons.svg')}##{icon_name}" } ), class: css_classes)
end
def audit_icon(names, options = {}) def audit_icon(names, options = {})
case names case names
when "standard" when "standard"
......
...@@ -239,8 +239,8 @@ module ProjectsHelper ...@@ -239,8 +239,8 @@ module ProjectsHelper
end end
end end
def has_projects_or_name?(projects, params) def show_projects?(projects, params)
!!(params[:name] || any_projects?(projects)) !!(params[:personal] || params[:name] || any_projects?(projects))
end end
private private
......
This diff is collapsed.
module SystemNoteHelper module SystemNoteHelper
ICON_NAMES_BY_ACTION = { ICON_NAMES_BY_ACTION = {
'commit' => 'icon_commit', 'commit' => 'commit',
'description' => 'icon_edit', 'description' => 'pencil',
'merge' => 'icon_merge', 'merge' => 'git-merge',
'merged' => 'icon_merged', 'merged' => 'git-merge',
'opened' => 'icon_status_open', 'opened' => 'issue-open',
'closed' => 'icon_status_closed', 'closed' => 'issue-close',
'time_tracking' => 'icon_stopwatch', 'time_tracking' => 'timer',
'assignee' => 'icon_user', 'assignee' => 'user',
'title' => 'icon_edit', 'title' => 'pencil',
'task' => 'icon_check_square_o', 'task' => 'task-done',
'label' => 'icon_tags', 'label' => 'label',
'cross_reference' => 'icon_random', 'cross_reference' => 'comment-dots',
'branch' => 'icon_code_fork', 'branch' => 'fork',
'confidential' => 'icon_eye_slash', 'confidential' => 'eye-slash',
'visible' => 'icon_eye', 'visible' => 'eye',
'milestone' => 'icon_clock_o', 'milestone' => 'clock',
'discussion' => 'icon_comment_o', 'discussion' => 'comment',
'moved' => 'icon_arrow_circle_o_right', 'moved' => 'arrow-right',
'outdated' => 'icon_edit', 'outdated' => 'pencil',
'duplicate' => 'icon_clone' 'duplicate' => 'issue-duplicate'
}.freeze }.freeze
def system_note_icon_name(note) def system_note_icon_name(note)
...@@ -28,7 +28,7 @@ module SystemNoteHelper ...@@ -28,7 +28,7 @@ module SystemNoteHelper
def icon_for_system_note(note) def icon_for_system_note(note)
icon_name = system_note_icon_name(note) icon_name = system_note_icon_name(note)
custom_icon(icon_name) if icon_name sprite_icon(icon_name) if icon_name
end end
extend self extend self
......
...@@ -143,16 +143,18 @@ module Issuable ...@@ -143,16 +143,18 @@ module Issuable
end end
def sort(method, excluded_labels: []) def sort(method, excluded_labels: [])
sorted = case method.to_s sorted =
when 'milestone_due_asc' then order_milestone_due_asc case method.to_s
when 'milestone_due_desc' then order_milestone_due_desc when 'downvotes_desc' then order_downvotes_desc
when 'downvotes_desc' then order_downvotes_desc when 'label_priority' then order_labels_priority(excluded_labels: excluded_labels)
when 'upvotes_desc' then order_upvotes_desc when 'milestone' then order_milestone_due_asc
when 'label_priority' then order_labels_priority(excluded_labels: excluded_labels) when 'milestone_due_asc' then order_milestone_due_asc
when 'priority' then order_due_date_and_labels_priority(excluded_labels: excluded_labels) when 'milestone_due_desc' then order_milestone_due_desc
else when 'popularity' then order_upvotes_desc
order_by(method) when 'priority' then order_due_date_and_labels_priority(excluded_labels: excluded_labels)
end when 'upvotes_desc' then order_upvotes_desc
else order_by(method)
end
# Break ties with the ID column for pagination # Break ties with the ID column for pagination
sorted.order(id: :desc) sorted.order(id: :desc)
...@@ -214,7 +216,7 @@ module Issuable ...@@ -214,7 +216,7 @@ module Issuable
def grouping_columns(sort) def grouping_columns(sort)
grouping_columns = [arel_table[:id]] grouping_columns = [arel_table[:id]]
if %w(milestone_due_desc milestone_due_asc).include?(sort) if %w(milestone_due_desc milestone_due_asc milestone).include?(sort)
milestone_table = Milestone.arel_table milestone_table = Milestone.arel_table
grouping_columns << milestone_table[:id] grouping_columns << milestone_table[:id]
grouping_columns << milestone_table[:due_date] grouping_columns << milestone_table[:due_date]
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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