Commit b5c80f99 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into 27574-pipelines-empty-state

* master: (209 commits)
  Fix Slack link when on notify
  Use Enumerable#index_by where possible
  Protect against unknown emojis in frequently used list
  Fix config option to disable Prometheus
  Fix double click token name
  fix describe block to make the karma reporter happy
  removes n+1 query from tags and branches indexes
  Fix broken links limit lines to 80 chars
  Add prometheus memory requirements, include additional detail on disabling prometheus in docs.
  Add `requirements: { id: %r{[^/]+} }` for all projects and groups namespaced API routes
  Expand on the good changelog/bad changelog documentation examples
  Add policy for closing stale merge requests
  Fix spec
  Use code icon for Raw
  Fix spec
  Add copy button to blob header and use icon for Raw button
  Fix archive prefix bug for refs containing dots
  Include routes when loading user projects
  Fixed search not working in issue boards modal
  Document a New Branch feature for Bare Projects
  ...
parents 6fb66321 4bf4612c
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
lib/gitlab/sanitizers/svg/whitelist.rb lib/gitlab/sanitizers/svg/whitelist.rb
lib/gitlab/diff/position_tracer.rb lib/gitlab/diff/position_tracer.rb
app/policies/project_policy.rb app/policies/project_policy.rb
app/models/concerns/relative_positioning.rb
*.js.es6 gitlab-language=javascript
...@@ -7,8 +7,6 @@ cache: ...@@ -7,8 +7,6 @@ cache:
variables: variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1" MYSQL_ALLOW_EMPTY_PASSWORD: "1"
# retry tests only in CI environment
RSPEC_RETRY_RETRY_COUNT: "3"
RAILS_ENV: "test" RAILS_ENV: "test"
SIMPLECOV: "true" SIMPLECOV: "true"
SETUP_DB: "true" SETUP_DB: "true"
...@@ -60,7 +58,7 @@ stages: ...@@ -60,7 +58,7 @@ stages:
<<: *dedicated-runner <<: *dedicated-runner
<<: *use-db <<: *use-db
script: script:
- JOB_NAME=( $CI_BUILD_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]} - export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]} - export CI_NODE_TOTAL=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/rspec_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export KNAPSACK_REPORT_PATH=knapsack/rspec_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
...@@ -69,16 +67,18 @@ stages: ...@@ -69,16 +67,18 @@ stages:
- knapsack rspec "--color --format documentation" - knapsack rspec "--color --format documentation"
artifacts: artifacts:
expire_in: 31d expire_in: 31d
when: always
paths: paths:
- knapsack/
- coverage/ - coverage/
- knapsack/
- tmp/capybara/
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
stage: test stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *use-db <<: *use-db
script: script:
- JOB_NAME=( $CI_BUILD_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]} - export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]} - export CI_NODE_TOTAL=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
...@@ -87,9 +87,11 @@ stages: ...@@ -87,9 +87,11 @@ stages:
- knapsack spinach "-r rerun" || retry '[[ -e tmp/spinach-rerun.txt ]] && bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)' - knapsack spinach "-r rerun" || retry '[[ -e tmp/spinach-rerun.txt ]] && bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
artifacts: artifacts:
expire_in: 31d expire_in: 31d
when: always
paths: paths:
- knapsack/
- coverage/ - coverage/
- knapsack/
- tmp/capybara/
# Prepare and merge knapsack tests # Prepare and merge knapsack tests
...@@ -178,7 +180,7 @@ spinach 9 10: *spinach-knapsack ...@@ -178,7 +180,7 @@ spinach 9 10: *spinach-knapsack
<<: *dedicated-runner <<: *dedicated-runner
stage: test stage: test
script: script:
- bundle exec $CI_BUILD_NAME - bundle exec $CI_JOB_NAME
rubocop: rubocop:
<<: *ruby-static-analysis <<: *ruby-static-analysis
...@@ -209,7 +211,7 @@ rake ee_compat_check: ...@@ -209,7 +211,7 @@ rake ee_compat_check:
- ee_compat_check/repo/ - ee_compat_check/repo/
- vendor/ruby - vendor/ruby
artifacts: artifacts:
name: "${CI_BUILD_NAME}_${CI_BUILD_REF_NAME}_${CI_BUILD_REF}" name: "${CI_JOB_NAME}_${CI_COMIT_REF_NAME}_${CI_COMMIT_SHA}"
when: on_failure when: on_failure
expire_in: 10d expire_in: 10d
paths: paths:
...@@ -222,6 +224,14 @@ rake db:migrate:reset: ...@@ -222,6 +224,14 @@ rake db:migrate:reset:
script: script:
- bundle exec rake db:migrate:reset - bundle exec rake db:migrate:reset
rake db:rollback:
stage: test
<<: *use-db
<<: *dedicated-runner
script:
- bundle exec rake db:rollback STEP=120
- bundle exec rake db:migrate
rake db:seed_fu: rake db:seed_fu:
stage: test stage: test
<<: *use-db <<: *use-db
...@@ -320,7 +330,7 @@ migration paths: ...@@ -320,7 +330,7 @@ migration paths:
- sed -i 's/localhost/redis/g' config/resque.yml - sed -i 's/localhost/redis/g' config/resque.yml
- bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3 - bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3
- bundle exec rake db:drop db:create db:schema:load db:seed_fu - bundle exec rake db:drop db:create db:schema:load db:seed_fu
- git checkout $CI_BUILD_REF - git checkout $CI_COMMIT_SHA
- source scripts/prepare_build.sh - source scripts/prepare_build.sh
- bundle exec rake db:migrate - bundle exec rake db:migrate
...@@ -358,7 +368,7 @@ lint:javascript:report: ...@@ -358,7 +368,7 @@ lint:javascript:report:
stage: post-test stage: post-test
before_script: [] before_script: []
script: script:
- find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files - find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
- yarn run eslint-report || true # ignore exit code - yarn run eslint-report || true # ignore exit code
artifacts: artifacts:
name: eslint-report name: eslint-report
...@@ -384,7 +394,6 @@ trigger_docs: ...@@ -384,7 +394,6 @@ trigger_docs:
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
# Notify slack in the end # Notify slack in the end
notify:slack: notify:slack:
stage: post-test stage: post-test
<<: *dedicated-runner <<: *dedicated-runner
...@@ -392,7 +401,7 @@ notify:slack: ...@@ -392,7 +401,7 @@ notify:slack:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false" USE_BUNDLE_INSTALL: "false"
script: script:
- ./scripts/notify_slack.sh "#development" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/pipelines>" - ./scripts/notify_slack.sh "#development" "Build on \`$CI_COMMIT_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_COMMIT_SHA"/pipelines>"
when: on_failure when: on_failure
only: only:
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
......
...@@ -78,6 +78,13 @@ towards getting your issue resolved. ...@@ -78,6 +78,13 @@ towards getting your issue resolved.
Issues and merge requests should be in English and contain appropriate language Issues and merge requests should be in English and contain appropriate language
for audiences of all ages. for audiences of all ages.
If a contributor is no longer actively working on a submitted merge request
we can decide that the merge request will be finished by one of our
[Merge request coaches][team] or close the merge request. We make this decision
based on how important the change is for our product vision. If a Merge request
coach is going to finish the merge request we assign the
~"coach will finish" label.
## Helping others ## Helping others
Please help other GitLab users when you can. The channels people will reach out Please help other GitLab users when you can. The channels people will reach out
...@@ -399,6 +406,12 @@ There are a few rules to get your merge request accepted: ...@@ -399,6 +406,12 @@ There are a few rules to get your merge request accepted:
1. Contains functionality we think other users will benefit from too 1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options or settings options since they complicate 1. Doesn't add configuration options or settings options since they complicate
making and testing future changes making and testing future changes
1. Changes do not adversely degrade performance.
- Avoid repeated polling of endpoints that require a significant amount of overhead
- Check for N+1 queries via the SQL log or [`QueryRecorder`](https://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
- Avoid repeated access of filesystem
1. If you need polling to support real-time features, please use
[polling with ETag caching][polling-etag].
1. Changes after submitting the merge request should be in separate commits 1. Changes after submitting the merge request should be in separate commits
(no squashing). If necessary, you will be asked to squash when the review is (no squashing). If necessary, you will be asked to squash when the review is
over, before merging. over, before merging.
...@@ -434,6 +447,7 @@ the feature you contribute through all of these steps. ...@@ -434,6 +447,7 @@ the feature you contribute through all of these steps.
1. Description explaining the relevancy (see following item) 1. Description explaining the relevancy (see following item)
1. Working and clean code that is commented where needed 1. Working and clean code that is commented where needed
1. Unit and integration tests that pass on the CI server 1. Unit and integration tests that pass on the CI server
1. Performance/scalability implications have been considered, addressed, and tested
1. [Documented][doc-styleguide] in the /doc directory 1. [Documented][doc-styleguide] in the /doc directory
1. Changelog entry added 1. Changelog entry added
1. Reviewed and any concerns are addressed 1. Reviewed and any concerns are addressed
...@@ -540,6 +554,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor ...@@ -540,6 +554,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/ [UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
[license-finder-doc]: doc/development/licensing.md [license-finder-doc]: doc/development/licensing.md
[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues [GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
[^1]: Specs other than JavaScript specs are considered backend code. Haml [^1]: Specs other than JavaScript specs are considered backend code. Haml
changes are considered backend code if they include Ruby code other than just changes are considered backend code if they include Ruby code other than just
......
/* eslint-disable no-param-reassign */ const MAX_MESSAGE_LENGTH = 500;
const MESSAGE_CELL_SELECTOR = '.abuse-reports .message';
((global) => { class AbuseReports {
const MAX_MESSAGE_LENGTH = 500; constructor() {
const MESSAGE_CELL_SELECTOR = '.abuse-reports .message'; $(MESSAGE_CELL_SELECTOR).each(this.truncateLongMessage);
$(document)
class AbuseReports { .off('click', MESSAGE_CELL_SELECTOR)
constructor() { .on('click', MESSAGE_CELL_SELECTOR, this.toggleMessageTruncation);
$(MESSAGE_CELL_SELECTOR).each(this.truncateLongMessage); }
$(document)
.off('click', MESSAGE_CELL_SELECTOR)
.on('click', MESSAGE_CELL_SELECTOR, this.toggleMessageTruncation);
}
truncateLongMessage() { truncateLongMessage() {
const $messageCellElement = $(this); const $messageCellElement = $(this);
const reportMessage = $messageCellElement.text(); const reportMessage = $messageCellElement.text();
if (reportMessage.length > MAX_MESSAGE_LENGTH) { if (reportMessage.length > MAX_MESSAGE_LENGTH) {
$messageCellElement.data('original-message', reportMessage); $messageCellElement.data('original-message', reportMessage);
$messageCellElement.data('message-truncated', 'true'); $messageCellElement.data('message-truncated', 'true');
$messageCellElement.text(global.text.truncate(reportMessage, MAX_MESSAGE_LENGTH)); $messageCellElement.text(window.gl.text.truncate(reportMessage, MAX_MESSAGE_LENGTH));
}
} }
}
toggleMessageTruncation() { toggleMessageTruncation() {
const $messageCellElement = $(this); const $messageCellElement = $(this);
const originalMessage = $messageCellElement.data('original-message'); const originalMessage = $messageCellElement.data('original-message');
if (!originalMessage) return; if (!originalMessage) return;
if ($messageCellElement.data('message-truncated') === 'true') { if ($messageCellElement.data('message-truncated') === 'true') {
$messageCellElement.data('message-truncated', 'false'); $messageCellElement.data('message-truncated', 'false');
$messageCellElement.text(originalMessage); $messageCellElement.text(originalMessage);
} else { } else {
$messageCellElement.data('message-truncated', 'true'); $messageCellElement.data('message-truncated', 'true');
$messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`); $messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`);
}
} }
} }
}
global.AbuseReports = AbuseReports; window.gl = window.gl || {};
})(window.gl || (window.gl = {})); window.gl.AbuseReports = AbuseReports;
...@@ -2,36 +2,35 @@ ...@@ -2,36 +2,35 @@
/* global Pager */ /* global Pager */
/* global Cookies */ /* global Cookies */
((global) => { class Activities {
class Activities { constructor() {
constructor() { Pager.init(20, true, false, this.updateTooltips);
Pager.init(20, true, false, this.updateTooltips); $('.event-filter-link').on('click', (e) => {
$('.event-filter-link').on('click', (e) => { e.preventDefault();
e.preventDefault(); this.toggleFilter(e.currentTarget);
this.toggleFilter(e.currentTarget); this.reloadActivities();
this.reloadActivities(); });
}); }
}
updateTooltips() { updateTooltips() {
gl.utils.localTimeAgo($('.js-timeago', '.content_list')); gl.utils.localTimeAgo($('.js-timeago', '.content_list'));
} }
reloadActivities() { reloadActivities() {
$('.content_list').html(''); $('.content_list').html('');
Pager.init(20, true, false, this.updateTooltips); Pager.init(20, true, false, this.updateTooltips);
} }
toggleFilter(sender) { toggleFilter(sender) {
const $sender = $(sender); const $sender = $(sender);
const filter = $sender.attr('id').split('_')[0]; const filter = $sender.attr('id').split('_')[0];
$('.event-filter .active').removeClass('active'); $('.event-filter .active').removeClass('active');
Cookies.set('event_filter', filter); Cookies.set('event_filter', filter);
$sender.closest('li').toggleClass('active'); $sender.closest('li').toggleClass('active');
}
} }
}
global.Activities = Activities; window.gl = window.gl || {};
})(window.gl || (window.gl = {})); window.gl.Activities = Activities;
/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-arrow-callback, camelcase, quotes, comma-dangle, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-arrow-callback, camelcase, quotes, comma-dangle, max-len */
(function() { window.Admin = (function() {
this.Admin = (function() { function Admin() {
function Admin() { var modal, showBlacklistType;
var modal, showBlacklistType; $('input#user_force_random_password').on('change', function(elem) {
$('input#user_force_random_password').on('change', function(elem) { var elems;
var elems; elems = $('#user_password, #user_password_confirmation');
elems = $('#user_password, #user_password_confirmation'); if ($(this).attr('checked')) {
if ($(this).attr('checked')) { return elems.val('').attr('disabled', true);
return elems.val('').attr('disabled', true); } else {
} else { return elems.removeAttr('disabled');
return elems.removeAttr('disabled'); }
} });
}); $('body').on('click', '.js-toggle-colors-link', function(e) {
$('body').on('click', '.js-toggle-colors-link', function(e) { e.preventDefault();
e.preventDefault(); return $('.js-toggle-colors-container').toggle();
return $('.js-toggle-colors-container').toggle(); });
}); $('.log-tabs a').click(function(e) {
$('.log-tabs a').click(function(e) { e.preventDefault();
e.preventDefault(); return $(this).tab('show');
return $(this).tab('show'); });
}); $('.log-bottom').click(function(e) {
$('.log-bottom').click(function(e) { var visible_log;
var visible_log; e.preventDefault();
e.preventDefault(); visible_log = $(".file-content:visible");
visible_log = $(".file-content:visible"); return visible_log.animate({
return visible_log.animate({ scrollTop: visible_log.find('ol').height()
scrollTop: visible_log.find('ol').height() }, "fast");
}, "fast"); });
}); modal = $('.change-owner-holder');
modal = $('.change-owner-holder'); $('.change-owner-link').bind("click", function(e) {
$('.change-owner-link').bind("click", function(e) { e.preventDefault();
e.preventDefault(); $(this).hide();
$(this).hide(); return modal.show();
return modal.show(); });
}); $('.change-owner-cancel-link').bind("click", function(e) {
$('.change-owner-cancel-link').bind("click", function(e) { e.preventDefault();
e.preventDefault(); modal.hide();
modal.hide(); return $('.change-owner-link').show();
return $('.change-owner-link').show(); });
}); $('li.project_member').bind('ajax:success', function() {
$('li.project_member').bind('ajax:success', function() { return gl.utils.refreshCurrentPage();
return gl.utils.refreshCurrentPage(); });
}); $('li.group_member').bind('ajax:success', function() {
$('li.group_member').bind('ajax:success', function() { return gl.utils.refreshCurrentPage();
return gl.utils.refreshCurrentPage(); });
}); showBlacklistType = function() {
showBlacklistType = function() { if ($("input[name='blacklist_type']:checked").val() === 'file') {
if ($("input[name='blacklist_type']:checked").val() === 'file') { $('.blacklist-file').show();
$('.blacklist-file').show(); return $('.blacklist-raw').hide();
return $('.blacklist-raw').hide(); } else {
} else { $('.blacklist-file').hide();
$('.blacklist-file').hide(); return $('.blacklist-raw').show();
return $('.blacklist-raw').show(); }
} };
}; $("input[name='blacklist_type']").click(showBlacklistType);
$("input[name='blacklist_type']").click(showBlacklistType); showBlacklistType();
showBlacklistType(); }
}
return Admin; return Admin;
})(); })();
}).call(window);
/* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, comma-dangle, prefer-arrow-callback, quote-props, no-param-reassign, max-len */ /* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, comma-dangle, prefer-arrow-callback, quote-props, no-param-reassign, max-len */
(function() { var Api = {
var Api = { groupsPath: "/api/:version/groups.json",
groupsPath: "/api/:version/groups.json", groupPath: "/api/:version/groups/:id.json",
groupPath: "/api/:version/groups/:id.json", namespacesPath: "/api/:version/namespaces.json",
namespacesPath: "/api/:version/namespaces.json", groupProjectsPath: "/api/:version/groups/:id/projects.json",
groupProjectsPath: "/api/:version/groups/:id/projects.json", projectsPath: "/api/:version/projects.json?simple=true",
projectsPath: "/api/:version/projects.json?simple=true", labelsPath: "/:namespace_path/:project_path/labels",
labelsPath: "/:namespace_path/:project_path/labels", licensePath: "/api/:version/templates/licenses/:key",
licensePath: "/api/:version/templates/licenses/:key", gitignorePath: "/api/:version/templates/gitignores/:key",
gitignorePath: "/api/:version/templates/gitignores/:key", gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key", dockerfilePath: "/api/:version/templates/dockerfiles/:key",
dockerfilePath: "/api/:version/templates/dockerfiles/:key", issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key", group: function(group_id, callback) {
group: function(group_id, callback) { var url = Api.buildUrl(Api.groupPath)
var url = Api.buildUrl(Api.groupPath) .replace(':id', group_id);
.replace(':id', group_id); return $.ajax({
return $.ajax({ url: url,
url: url, dataType: "json"
dataType: "json" }).done(function(group) {
}).done(function(group) { return callback(group);
return callback(group); });
}); },
}, // Return groups list. Filtered by query
// Return groups list. Filtered by query groups: function(query, options, callback) {
groups: function(query, options, callback) { var url = Api.buildUrl(Api.groupsPath);
var url = Api.buildUrl(Api.groupsPath); return $.ajax({
return $.ajax({ url: url,
url: url, data: $.extend({
data: $.extend({ search: query,
search: query, per_page: 20
per_page: 20 }, options),
}, options), dataType: "json"
dataType: "json" }).done(function(groups) {
}).done(function(groups) { return callback(groups);
return callback(groups); });
}); },
}, // Return namespaces list. Filtered by query
// Return namespaces list. Filtered by query namespaces: function(query, callback) {
namespaces: function(query, callback) { var url = Api.buildUrl(Api.namespacesPath);
var url = Api.buildUrl(Api.namespacesPath); return $.ajax({
return $.ajax({ url: url,
url: url, data: {
data: { search: query,
search: query, per_page: 20
per_page: 20 },
}, dataType: "json"
dataType: "json" }).done(function(namespaces) {
}).done(function(namespaces) { return callback(namespaces);
return callback(namespaces); });
}); },
}, // Return projects list. Filtered by query
// Return projects list. Filtered by query projects: function(query, options, callback) {
projects: function(query, order, callback) { var url = Api.buildUrl(Api.projectsPath);
var url = Api.buildUrl(Api.projectsPath); return $.ajax({
return $.ajax({ url: url,
url: url, data: $.extend({
data: { search: query,
search: query, per_page: 20,
order_by: order, membership: true
per_page: 20 }, options),
}, dataType: "json"
dataType: "json" }).done(function(projects) {
}).done(function(projects) { return callback(projects);
return callback(projects); });
}); },
}, newLabel: function(namespace_path, project_path, data, callback) {
newLabel: function(namespace_path, project_path, data, callback) { var url = Api.buildUrl(Api.labelsPath)
var url = Api.buildUrl(Api.labelsPath) .replace(':namespace_path', namespace_path)
.replace(':namespace_path', namespace_path) .replace(':project_path', project_path);
.replace(':project_path', project_path); return $.ajax({
return $.ajax({ url: url,
url: url, type: "POST",
type: "POST", data: { 'label': data },
data: { 'label': data }, dataType: "json"
dataType: "json" }).done(function(label) {
}).done(function(label) { return callback(label);
return callback(label); }).error(function(message) {
}).error(function(message) { return callback(message.responseJSON);
return callback(message.responseJSON); });
}); },
}, // Return group projects list. Filtered by query
// Return group projects list. Filtered by query groupProjects: function(group_id, query, callback) {
groupProjects: function(group_id, query, callback) { var url = Api.buildUrl(Api.groupProjectsPath)
var url = Api.buildUrl(Api.groupProjectsPath) .replace(':id', group_id);
.replace(':id', group_id); return $.ajax({
return $.ajax({ url: url,
url: url, data: {
data: { search: query,
search: query, per_page: 20
per_page: 20 },
}, dataType: "json"
dataType: "json" }).done(function(projects) {
}).done(function(projects) { return callback(projects);
return callback(projects); });
}); },
}, // Return text for a specific license
// Return text for a specific license licenseText: function(key, data, callback) {
licenseText: function(key, data, callback) { var url = Api.buildUrl(Api.licensePath)
var url = Api.buildUrl(Api.licensePath) .replace(':key', key);
.replace(':key', key); return $.ajax({
return $.ajax({ url: url,
url: url, data: data
data: data }).done(function(license) {
}).done(function(license) { return callback(license);
return callback(license); });
}); },
}, gitignoreText: function(key, callback) {
gitignoreText: function(key, callback) { var url = Api.buildUrl(Api.gitignorePath)
var url = Api.buildUrl(Api.gitignorePath) .replace(':key', key);
.replace(':key', key); return $.get(url, function(gitignore) {
return $.get(url, function(gitignore) { return callback(gitignore);
return callback(gitignore); });
}); },
}, gitlabCiYml: function(key, callback) {
gitlabCiYml: function(key, callback) { var url = Api.buildUrl(Api.gitlabCiYmlPath)
var url = Api.buildUrl(Api.gitlabCiYmlPath) .replace(':key', key);
.replace(':key', key); return $.get(url, function(file) {
return $.get(url, function(file) { return callback(file);
return callback(file); });
}); },
}, dockerfileYml: function(key, callback) {
dockerfileYml: function(key, callback) { var url = Api.buildUrl(Api.dockerfilePath).replace(':key', key);
var url = Api.buildUrl(Api.dockerfilePath).replace(':key', key); $.get(url, callback);
$.get(url, callback); },
}, issueTemplate: function(namespacePath, projectPath, key, type, callback) {
issueTemplate: function(namespacePath, projectPath, key, type, callback) { var url = Api.buildUrl(Api.issuableTemplatePath)
var url = Api.buildUrl(Api.issuableTemplatePath) .replace(':key', key)
.replace(':key', key) .replace(':type', type)
.replace(':type', type) .replace(':project_path', projectPath)
.replace(':project_path', projectPath) .replace(':namespace_path', namespacePath);
.replace(':namespace_path', namespacePath); $.ajax({
$.ajax({ url: url,
url: url, dataType: 'json'
dataType: 'json' }).done(function(file) {
}).done(function(file) { callback(null, file);
callback(null, file); }).error(callback);
}).error(callback); },
}, buildUrl: function(url) {
buildUrl: function(url) { if (gon.relative_url_root != null) {
if (gon.relative_url_root != null) { url = gon.relative_url_root + url;
url = gon.relative_url_root + url;
}
return url.replace(':version', gon.api_version);
} }
}; return url.replace(':version', gon.api_version);
}
};
window.Api = Api; window.Api = Api;
}).call(window);
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, prefer-arrow-callback, no-var, one-var, one-var-declaration-per-line, no-else-return, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, prefer-arrow-callback, no-var, one-var, one-var-declaration-per-line, no-else-return, max-len */
(function() {
this.Aside = (function() {
function Aside() {
$(document).off("click", "a.show-aside");
$(document).on("click", 'a.show-aside', function(e) {
var btn, icon;
e.preventDefault();
btn = $(e.currentTarget);
icon = btn.find('i');
if (icon.hasClass('fa-angle-left')) {
btn.parent().find('section').hide();
btn.parent().find('aside').fadeIn();
return icon.removeClass('fa-angle-left').addClass('fa-angle-right');
} else {
btn.parent().find('aside').hide();
btn.parent().find('section').fadeIn();
return icon.removeClass('fa-angle-right').addClass('fa-angle-left');
}
});
}
return Aside; window.Aside = (function() {
})(); function Aside() {
}).call(window); $(document).off("click", "a.show-aside");
$(document).on("click", 'a.show-aside', function(e) {
var btn, icon;
e.preventDefault();
btn = $(e.currentTarget);
icon = btn.find('i');
if (icon.hasClass('fa-angle-left')) {
btn.parent().find('section').hide();
btn.parent().find('aside').fadeIn();
return icon.removeClass('fa-angle-left').addClass('fa-angle-right');
} else {
btn.parent().find('aside').hide();
btn.parent().find('section').fadeIn();
return icon.removeClass('fa-angle-right').addClass('fa-angle-left');
}
});
}
return Aside;
})();
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, quotes, prefer-template, no-var, one-var, no-unused-vars, one-var-declaration-per-line, no-void, consistent-return, no-empty, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, quotes, prefer-template, no-var, one-var, no-unused-vars, one-var-declaration-per-line, no-void, consistent-return, no-empty, max-len */
(function() {
this.Autosave = (function() {
function Autosave(field, key) {
this.field = field;
if (key.join != null) {
key = key.join("/");
}
this.key = "autosave/" + key;
this.field.data("autosave", this);
this.restore();
this.field.on("input", (function(_this) {
return function() {
return _this.save();
};
})(this));
}
Autosave.prototype.restore = function() { window.Autosave = (function() {
var e, text; function Autosave(field, key) {
if (window.localStorage == null) { this.field = field;
return; if (key.join != null) {
} key = key.join("/");
try { }
text = window.localStorage.getItem(this.key); this.key = "autosave/" + key;
} catch (error) { this.field.data("autosave", this);
e = error; this.restore();
return; this.field.on("input", (function(_this) {
} return function() {
if ((text != null ? text.length : void 0) > 0) { return _this.save();
this.field.val(text); };
} })(this));
return this.field.trigger("input"); }
};
Autosave.prototype.save = function() { Autosave.prototype.restore = function() {
var text; var e, text;
if (window.localStorage == null) { if (window.localStorage == null) {
return; return;
} }
text = this.field.val(); try {
if ((text != null ? text.length : void 0) > 0) { text = window.localStorage.getItem(this.key);
try { } catch (error) {
return window.localStorage.setItem(this.key, text); e = error;
} catch (error) {} return;
} else { }
return this.reset(); if ((text != null ? text.length : void 0) > 0) {
} this.field.val(text);
}; }
return this.field.trigger("input");
};
Autosave.prototype.reset = function() { Autosave.prototype.save = function() {
if (window.localStorage == null) { var text;
return; if (window.localStorage == null) {
} return;
}
text = this.field.val();
if ((text != null ? text.length : void 0) > 0) {
try { try {
return window.localStorage.removeItem(this.key); return window.localStorage.setItem(this.key, text);
} catch (error) {} } catch (error) {}
}; } else {
return this.reset();
}
};
Autosave.prototype.reset = function() {
if (window.localStorage == null) {
return;
}
try {
return window.localStorage.removeItem(this.key);
} catch (error) {}
};
return Autosave; return Autosave;
})(); })();
}).call(window);
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import emojiMap from 'emojis/digests.json'; import emojiMap from 'emojis/digests.json';
import emojiAliases from 'emojis/aliases.json'; import emojiAliases from 'emojis/aliases.json';
import { glEmojiTag } from './behaviors/gl_emoji'; import { glEmojiTag } from './behaviors/gl_emoji';
import isEmojiNameValid from './behaviors/gl_emoji/is_emoji_name_valid';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd'; const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const requestAnimationFrame = window.requestAnimationFrame || const requestAnimationFrame = window.requestAnimationFrame ||
...@@ -454,14 +455,21 @@ AwardsHandler.prototype.normalizeEmojiName = function normalizeEmojiName(emoji) ...@@ -454,14 +455,21 @@ AwardsHandler.prototype.normalizeEmojiName = function normalizeEmojiName(emoji)
AwardsHandler AwardsHandler
.prototype .prototype
.addEmojiToFrequentlyUsedList = function addEmojiToFrequentlyUsedList(emoji) { .addEmojiToFrequentlyUsedList = function addEmojiToFrequentlyUsedList(emoji) {
const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis(); if (isEmojiNameValid(emoji)) {
frequentlyUsedEmojis.push(emoji); this.frequentlyUsedEmojis = _.uniq(this.getFrequentlyUsedEmojis().concat(emoji));
Cookies.set('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }); Cookies.set('frequently_used_emojis', this.frequentlyUsedEmojis.join(','), { expires: 365 });
}
}; };
AwardsHandler.prototype.getFrequentlyUsedEmojis = function getFrequentlyUsedEmojis() { AwardsHandler.prototype.getFrequentlyUsedEmojis = function getFrequentlyUsedEmojis() {
const frequentlyUsedEmojis = (Cookies.get('frequently_used_emojis') || '').split(','); return this.frequentlyUsedEmojis || (() => {
return _.compact(_.uniq(frequentlyUsedEmojis)); const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(','));
this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter(
inputName => isEmojiNameValid(inputName),
);
return this.frequentlyUsedEmojis;
})();
}; };
AwardsHandler.prototype.setupSearch = function setupSearch() { AwardsHandler.prototype.setupSearch = function setupSearch() {
......
...@@ -13,9 +13,14 @@ function emojiImageTag(name, src) { ...@@ -13,9 +13,14 @@ function emojiImageTag(name, src) {
} }
function assembleFallbackImageSrc(inputName) { function assembleFallbackImageSrc(inputName) {
const name = Object.prototype.hasOwnProperty.call(emojiAliases, inputName) ? let name = Object.prototype.hasOwnProperty.call(emojiAliases, inputName) ?
emojiAliases[inputName] : inputName; emojiAliases[inputName] : inputName;
const emojiInfo = emojiMap[name]; let emojiInfo = emojiMap[name];
// Fallback to question mark for unknown emojis
if (!emojiInfo) {
name = 'grey_question';
emojiInfo = emojiMap[name];
}
const fallbackImageSrc = `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/emoji/${name}-${emojiInfo.digest}.png`; const fallbackImageSrc = `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/emoji/${name}-${emojiInfo.digest}.png`;
return fallbackImageSrc; return fallbackImageSrc;
...@@ -26,9 +31,15 @@ const glEmojiTagDefaults = { ...@@ -26,9 +31,15 @@ const glEmojiTagDefaults = {
}; };
function glEmojiTag(inputName, options) { function glEmojiTag(inputName, options) {
const opts = Object.assign({}, glEmojiTagDefaults, options); const opts = Object.assign({}, glEmojiTagDefaults, options);
const name = Object.prototype.hasOwnProperty.call(emojiAliases, inputName) ? let name = Object.prototype.hasOwnProperty.call(emojiAliases, inputName) ?
emojiAliases[inputName] : inputName; emojiAliases[inputName] : inputName;
const emojiInfo = emojiMap[name]; let emojiInfo = emojiMap[name];
// Fallback to question mark for unknown emojis
if (!emojiInfo) {
name = 'grey_question';
emojiInfo = emojiMap[name];
}
const fallbackImageSrc = assembleFallbackImageSrc(name); const fallbackImageSrc = assembleFallbackImageSrc(name);
const fallbackSpriteClass = `emoji-${name}`; const fallbackSpriteClass = `emoji-${name}`;
......
import emojiMap from 'emojis/digests.json';
import emojiAliases from 'emojis/aliases.json';
function isEmojiNameValid(inputName) {
const name = Object.prototype.hasOwnProperty.call(emojiAliases, inputName) ?
emojiAliases[inputName] : inputName;
return name && emojiMap[name];
}
export default isEmojiNameValid;
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
// "Meta+Enter" (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, the form // "Meta+Enter" (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, the form
// is submitted. // is submitted.
// //
require('../extensions/jquery'); import '../commons/bootstrap';
// //
// ### Example Markup // ### Example Markup
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// When called on a form with input fields with the `required` attribute, the // When called on a form with input fields with the `required` attribute, the
// form's submit button will be disabled until all required fields have values. // form's submit button will be disabled until all required fields have values.
// //
require('../extensions/jquery'); import '../commons/bootstrap';
// //
// ### Example Markup // ### Example Markup
......
...@@ -21,8 +21,13 @@ ...@@ -21,8 +21,13 @@
// %a.js-toggle-button // %a.js-toggle-button
// %div.js-toggle-content // %div.js-toggle-content
// //
$('body').on('click', '.js-toggle-button', function() { $('body').on('click', '.js-toggle-button', function(e) {
toggleContainer($(this).closest('.js-toggle-container')); toggleContainer($(this).closest('.js-toggle-container'));
const targetTag = e.target.tagName.toLowerCase();
if (targetTag === 'a' || targetTag === 'button') {
e.preventDefault();
}
}); });
// If we're accessing a permalink, ensure it is not inside a // If we're accessing a permalink, ensure it is not inside a
......
const lineNumberRe = /^L[0-9]+/;
const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => {
const hash = gl.utils.getLocationHash();
if (hash && lineNumberRe.test(hash)) {
const hashUrlString = `#${hash}`;
[].concat(Array.prototype.slice.call(linksToUpdate)).forEach((permalinkButton) => {
const baseHref = permalinkButton.getAttribute('data-original-href') || (() => {
const href = permalinkButton.getAttribute('href');
permalinkButton.setAttribute('data-original-href', href);
return href;
})();
permalinkButton.setAttribute('href', `${baseHref}${hashUrlString}`);
});
}
};
function BlobLinePermalinkUpdater(blobContentHolder, lineNumberSelector, elementsToUpdate) {
const updateBlameAndBlobPermalinkCb = () => {
// Wait for the hash to update from the LineHighlighter callback
setTimeout(() => {
updateLineNumbersOnBlobPermalinks(elementsToUpdate);
}, 0);
};
blobContentHolder.addEventListener('click', (e) => {
if (e.target.matches(lineNumberSelector)) {
updateBlameAndBlobPermalinkCb();
}
});
updateBlameAndBlobPermalinkCb();
}
export default BlobLinePermalinkUpdater;
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
/* global Vue */ /* global Vue */
/* global BoardService */ /* global BoardService */
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
window.Vue = require('vue'); window.Vue = require('vue');
window.Vue.use(require('vue-resource')); window.Vue.use(require('vue-resource'));
require('./models/issue'); require('./models/issue');
...@@ -59,6 +62,14 @@ $(() => { ...@@ -59,6 +62,14 @@ $(() => {
}, },
created () { created () {
gl.boardService = new BoardService(this.endpoint, this.bulkUpdatePath, this.boardId); gl.boardService = new BoardService(this.endpoint, this.bulkUpdatePath, this.boardId);
this.filterManager = new FilteredSearchBoards(Store.filter, true);
// Listen for updateTokens event
eventHub.$on('updateTokens', this.updateTokens);
},
beforeDestroy() {
eventHub.$off('updateTokens', this.updateTokens);
}, },
mounted () { mounted () {
Store.disabled = this.disabled; Store.disabled = this.disabled;
...@@ -77,11 +88,16 @@ $(() => { ...@@ -77,11 +88,16 @@ $(() => {
Store.addBlankState(); Store.addBlankState();
this.loading = false; this.loading = false;
}); });
} },
methods: {
updateTokens() {
this.filterManager.updateTokens();
}
},
}); });
gl.IssueBoardsSearch = new Vue({ gl.IssueBoardsSearch = new Vue({
el: document.getElementById('js-boards-search'), el: document.getElementById('js-add-list'),
data: { data: {
filters: Store.state.filters filters: Store.state.filters
}, },
......
...@@ -28,16 +28,16 @@ require('./board_list'); ...@@ -28,16 +28,16 @@ require('./board_list');
data () { data () {
return { return {
detailIssue: Store.detail, detailIssue: Store.detail,
filters: Store.state.filters, filter: Store.filter,
}; };
}, },
watch: { watch: {
filters: { filter: {
handler () { handler() {
this.list.page = 1; this.list.page = 1;
this.list.getIssues(true); this.list.getIssues(true);
}, },
deep: true deep: true,
}, },
detailIssue: { detailIssue: {
handler () { handler () {
......
...@@ -17,7 +17,8 @@ export default { ...@@ -17,7 +17,8 @@ export default {
:list="list" :list="list"
:issue="issue" :issue="issue"
:issue-link-base="issueLinkBase" :issue-link-base="issueLinkBase"
:root-path="rootPath" /> :root-path="rootPath"
:update-filters="true" />
</li> </li>
`, `,
components: { components: {
......
/* global Vue */ /* global Vue */
import eventHub from '../eventhub';
(() => { (() => {
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
...@@ -23,6 +25,11 @@ ...@@ -23,6 +25,11 @@
type: String, type: String,
required: true, required: true,
}, },
updateFilters: {
type: Boolean,
required: false,
default: false,
},
}, },
methods: { methods: {
showLabel(label) { showLabel(label) {
...@@ -31,29 +38,25 @@ ...@@ -31,29 +38,25 @@
return !this.list.label || label.id !== this.list.label.id; return !this.list.label || label.id !== this.list.label.id;
}, },
filterByLabel(label, e) { filterByLabel(label, e) {
let labelToggleText = label.title; if (!this.updateFilters) return;
const labelIndex = Store.state.filters.label_name.indexOf(label.title);
const filterPath = gl.issueBoards.BoardsStore.filter.path.split('&');
const labelTitle = encodeURIComponent(label.title);
const param = `label_name[]=${labelTitle}`;
const labelIndex = filterPath.indexOf(param);
$(e.currentTarget).tooltip('hide'); $(e.currentTarget).tooltip('hide');
if (labelIndex === -1) { if (labelIndex === -1) {
Store.state.filters.label_name.push(label.title); filterPath.push(param);
$('.labels-filter').prepend(`<input type="hidden" name="label_name[]" value="${label.title}" />`);
} else { } else {
Store.state.filters.label_name.splice(labelIndex, 1); filterPath.splice(labelIndex, 1);
labelToggleText = Store.state.filters.label_name[0];
$(`.labels-filter input[name="label_name[]"][value="${label.title}"]`).remove();
} }
const selectedLabels = Store.state.filters.label_name; gl.issueBoards.BoardsStore.filter.path = filterPath.join('&');
if (selectedLabels.length === 0) {
labelToggleText = 'Label';
} else if (selectedLabels.length > 1) {
labelToggleText = `${selectedLabels[0]} + ${selectedLabels.length - 1} more`;
}
$('.labels-filter .dropdown-toggle-text').text(labelToggleText);
Store.updateFiltersUrl(); Store.updateFiltersUrl();
eventHub.$emit('updateTokens');
}, },
labelStyle(label) { labelStyle(label) {
return { return {
......
/* global Vue */ import FilteredSearchBoards from '../../filtered_search_boards';
const userFilter = require('./filters/user'); import FilteredSearchContainer from '../../../filtered_search/container';
const milestoneFilter = require('./filters/milestone');
const labelFilter = require('./filters/label');
module.exports = Vue.extend({ export default {
name: 'modal-filters', name: 'modal-filters',
props: { props: {
projectId: { store: {
type: Number, type: Object,
required: true,
},
milestonePath: {
type: String,
required: true,
},
labelPath: {
type: String,
required: true, required: true,
}, },
}, },
destroyed() { mounted() {
gl.issueBoards.ModalStore.setDefaultFilter(); FilteredSearchContainer.container = this.$el;
this.filteredSearch = new FilteredSearchBoards(this.store);
this.filteredSearch.removeTokens();
}, },
components: { beforeDestroy() {
userFilter, this.filteredSearch.cleanup();
milestoneFilter, FilteredSearchContainer.container = document;
labelFilter, this.store.path = '';
}, },
template: ` template: '#js-board-modal-filter',
<div class="modal-filters"> };
<user-filter
dropdown-class-name="dropdown-menu-author"
toggle-class-name="js-user-search js-author-search"
toggle-label="Author"
field-name="author_id"
:project-id="projectId"></user-filter>
<user-filter
dropdown-class-name="dropdown-menu-author"
toggle-class-name="js-assignee-search"
toggle-label="Assignee"
field-name="assignee_id"
:null-user="true"
:project-id="projectId"></user-filter>
<milestone-filter :milestone-path="milestonePath"></milestone-filter>
<label-filter :label-path="labelPath"></label-filter>
</div>
`,
});
/* eslint-disable no-new */
/* global Vue */
/* global LabelsSelect */
module.exports = Vue.extend({
name: 'filter-label',
props: {
labelPath: {
type: String,
required: true,
},
},
mounted() {
new LabelsSelect(this.$refs.dropdown);
},
template: `
<div class="dropdown">
<button
class="dropdown-menu-toggle js-label-select js-multiselect js-extra-options"
type="button"
data-toggle="dropdown"
data-show-any="true"
data-show-no="true"
:data-labels="labelPath"
ref="dropdown">
<span class="dropdown-toggle-text">
Label
</span>
<i class="fa fa-chevron-down"></i>
</button>
<div class="dropdown-menu dropdown-select dropdown-menu-paging dropdown-menu-labels dropdown-menu-selectable">
<div class="dropdown-title">
Filter by label
<button
class="dropdown-title-button dropdown-menu-close"
aria-label="Close"
type="button">
<i class="fa fa-times dropdown-menu-close-icon"></i>
</button>
</div>
<div class="dropdown-input">
<input
type="search"
class="dropdown-input-field"
placeholder="Search"
autocomplete="off" />
<i class="fa fa-search dropdown-input-search"></i>
<i role="button" class="fa fa-times dropdown-input-clear js-dropdown-input-clear"></i>
</div>
<div class="dropdown-content"></div>
<div class="dropdown-loading"><i class="fa fa-spinner fa-spin"></i></div>
</div>
</div>
`,
});
/* eslint-disable no-new */
/* global Vue */
/* global MilestoneSelect */
module.exports = Vue.extend({
name: 'filter-milestone',
props: {
milestonePath: {
type: String,
required: true,
},
},
mounted() {
new MilestoneSelect(null, this.$refs.dropdown);
},
template: `
<div class="dropdown">
<button
class="dropdown-menu-toggle js-milestone-select"
type="button"
data-toggle="dropdown"
data-show-any="true"
data-show-upcoming="true"
data-field-name="milestone_title"
:data-milestones="milestonePath"
ref="dropdown">
<span class="dropdown-toggle-text">
Milestone
</span>
<i class="fa fa-chevron-down"></i>
</button>
<div class="dropdown-menu dropdown-select dropdown-menu-selectable dropdown-menu-milestone">
<div class="dropdown-title">
<span>Filter by milestone</span>
<button
class="dropdown-title-button dropdown-menu-close"
aria-label="Close"
type="button">
<i class="fa fa-times dropdown-menu-close-icon"></i>
</button>
</div>
<div class="dropdown-input">
<input
type="search"
class="dropdown-input-field"
placeholder="Search milestones"
autocomplete="off" />
<i class="fa fa-search dropdown-input-search"></i>
<i role="button" class="fa fa-times dropdown-input-clear js-dropdown-input-clear"></i>
</div>
<div class="dropdown-content"></div>
<div class="dropdown-loading"><i class="fa fa-spinner fa-spin"></i></div>
</div>
</div>
`,
});
/* eslint-disable no-new */
/* global Vue */
/* global UsersSelect */
module.exports = Vue.extend({
name: 'filter-user',
props: {
toggleClassName: {
type: String,
required: true,
},
dropdownClassName: {
type: String,
required: false,
default: '',
},
toggleLabel: {
type: String,
required: true,
},
fieldName: {
type: String,
required: true,
},
nullUser: {
type: Boolean,
required: false,
default: false,
},
projectId: {
type: Number,
required: true,
},
},
mounted() {
new UsersSelect(null, this.$refs.dropdown);
},
computed: {
currentUsername() {
return gon.current_username;
},
dropdownTitle() {
return `Filter by ${this.toggleLabel.toLowerCase()}`;
},
inputPlaceholder() {
return `Search ${this.toggleLabel.toLowerCase()}`;
},
},
template: `
<div class="dropdown">
<button
class="dropdown-menu-toggle js-user-search"
:class="toggleClassName"
type="button"
data-toggle="dropdown"
data-current-user="true"
:data-any-user="'Any ' + toggleLabel"
:data-null-user="nullUser"
:data-field-name="fieldName"
:data-project-id="projectId"
:data-first-user="currentUsername"
ref="dropdown">
<span class="dropdown-toggle-text">
{{ toggleLabel }}
</span>
<i class="fa fa-chevron-down"></i>
</button>
<div
class="dropdown-menu dropdown-select dropdown-menu-user dropdown-menu-selectable"
:class="dropdownClassName">
<div class="dropdown-title">
{{ dropdownTitle }}
<button
class="dropdown-title-button dropdown-menu-close"
aria-label="Close"
type="button">
<i class="fa fa-times dropdown-menu-close-icon"></i>
</button>
</div>
<div class="dropdown-input">
<input
type="search"
class="dropdown-input-field"
autocomplete="off"
:placeholder="inputPlaceholder" />
<i class="fa fa-search dropdown-input-search"></i>
<i
role="button"
class="fa fa-times dropdown-input-clear js-dropdown-input-clear">
</i>
</div>
<div class="dropdown-content"></div>
<div class="dropdown-loading"><i class="fa fa-spinner fa-spin"></i></div>
</div>
</div>
`,
});
/* global Vue */ import Vue from 'vue';
import modalFilters from './filters';
require('./tabs'); require('./tabs');
const modalFilters = require('./filters');
(() => { (() => {
const ModalStore = gl.issueBoards.ModalStore; const ModalStore = gl.issueBoards.ModalStore;
...@@ -66,16 +67,7 @@ const modalFilters = require('./filters'); ...@@ -66,16 +67,7 @@ const modalFilters = require('./filters');
<div <div
class="add-issues-search append-bottom-10" class="add-issues-search append-bottom-10"
v-if="showSearch"> v-if="showSearch">
<modal-filters <modal-filters :store="filter" />
:project-id="projectId"
:milestone-path="milestonePath"
:label-path="labelPath">
</modal-filters>
<input
placeholder="Search issues..."
class="form-control"
type="search"
v-model="searchTerm" />
<button <button
type="button" type="button"
class="btn btn-success btn-inverted prepend-left-10" class="btn btn-success btn-inverted prepend-left-10"
......
/* global Vue */ /* global Vue */
/* global ListIssue */ /* global ListIssue */
import queryData from '../../utils/query_data';
require('./header'); require('./header');
require('./list'); require('./list');
...@@ -47,9 +48,6 @@ require('./empty_state'); ...@@ -47,9 +48,6 @@ require('./empty_state');
page() { page() {
this.loadIssues(); this.loadIssues();
}, },
searchTerm() {
this.searchOperation();
},
showAddIssuesModal() { showAddIssuesModal() {
if (this.showAddIssuesModal && !this.issues.length) { if (this.showAddIssuesModal && !this.issues.length) {
this.loading = true; this.loading = true;
...@@ -72,19 +70,13 @@ require('./empty_state'); ...@@ -72,19 +70,13 @@ require('./empty_state');
}, },
}, },
methods: { methods: {
searchOperation: _.debounce(function searchOperationDebounce() {
this.loadIssues(true);
}, 500),
loadIssues(clearIssues = false) { loadIssues(clearIssues = false) {
if (!this.showAddIssuesModal) return false; if (!this.showAddIssuesModal) return false;
const queryData = Object.assign({}, this.filter, { return gl.boardService.getBacklog(queryData(this.filter.path, {
search: this.searchTerm,
page: this.page, page: this.page,
per: this.perPage, per: this.perPage,
}); })).then((res) => {
return gl.boardService.getBacklog(queryData).then((res) => {
const data = res.json(); const data = res.json();
if (clearIssues) { if (clearIssues) {
......
import Vue from 'vue';
export default new Vue();
/* eslint-disable class-methods-use-this */
import FilteredSearchContainer from '../filtered_search/container';
export default class FilteredSearchBoards extends gl.FilteredSearchManager {
constructor(store, updateUrl = false) {
super('boards');
this.store = store;
this.updateUrl = updateUrl;
// Issue boards is slightly different, we handle all the requests async
// instead or reloading the page, we just re-fire the list ajax requests
this.isHandledAsync = true;
}
updateObject(path) {
this.store.path = path.substr(1);
if (this.updateUrl) {
gl.issueBoards.BoardsStore.updateFiltersUrl();
}
}
removeTokens() {
const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token');
// Remove all the tokens as they will be replaced by the search manager
[].forEach.call(tokens, (el) => {
el.parentNode.removeChild(el);
});
}
updateTokens() {
this.removeTokens();
this.loadSearchParamsFromURL();
// Get the placeholder back if search is empty
this.filteredSearchInput.dispatchEvent(new Event('input'));
}
}
/* eslint-disable space-before-function-paren, no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign, max-len, no-unused-vars */ /* eslint-disable space-before-function-paren, no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign, max-len, no-unused-vars */
/* global ListIssue */ /* global ListIssue */
/* global ListLabel */ /* global ListLabel */
import queryData from '../utils/query_data';
class List { class List {
constructor (obj) { constructor (obj) {
...@@ -10,7 +11,6 @@ class List { ...@@ -10,7 +11,6 @@ class List {
this.title = obj.title; this.title = obj.title;
this.type = obj.list_type; this.type = obj.list_type;
this.preset = ['done', 'blank'].indexOf(this.type) > -1; this.preset = ['done', 'blank'].indexOf(this.type) > -1;
this.filters = gl.issueBoards.BoardsStore.state.filters;
this.page = 1; this.page = 1;
this.loading = true; this.loading = true;
this.loadingMore = false; this.loadingMore = false;
...@@ -65,12 +65,9 @@ class List { ...@@ -65,12 +65,9 @@ class List {
} }
getIssues (emptyIssues = true) { getIssues (emptyIssues = true) {
const filters = this.filters; const data = queryData(gl.issueBoards.BoardsStore.filter.path, { page: this.page });
const data = { page: this.page };
Object.keys(filters).forEach((key) => { data[key] = filters[key]; }); if (this.label && data.label_name) {
if (this.label) {
data.label_name = data.label_name.filter(label => label !== this.label.title); data.label_name = data.label_name.filter(label => label !== this.label.title);
} }
......
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
gl.issueBoards.BoardsStore = { gl.issueBoards.BoardsStore = {
disabled: false, disabled: false,
filter: {
path: '',
},
state: {}, state: {},
detail: { detail: {
issue: {} issue: {}
...@@ -18,13 +21,7 @@ ...@@ -18,13 +21,7 @@
}, },
create () { create () {
this.state.lists = []; this.state.lists = [];
this.state.filters = { this.filter.path = gl.utils.getUrlParamsArray().join('&');
author_id: gl.utils.getParameterValues('author_id')[0],
assignee_id: gl.utils.getParameterValues('assignee_id')[0],
milestone_title: gl.utils.getParameterValues('milestone_title')[0],
label_name: gl.utils.getParameterValues('label_name[]'),
search: ''
};
}, },
addList (listObj) { addList (listObj) {
const list = new List(listObj); const list = new List(listObj);
...@@ -123,7 +120,7 @@ ...@@ -123,7 +120,7 @@
})[0]; })[0];
}, },
updateFiltersUrl () { updateFiltersUrl () {
history.pushState(null, null, `?${$.param(this.state.filters)}`); history.pushState(null, null, `?${this.filter.path}`);
} }
}; };
})(); })();
...@@ -17,17 +17,9 @@ ...@@ -17,17 +17,9 @@
loadingNewPage: false, loadingNewPage: false,
page: 1, page: 1,
perPage: 50, perPage: 50,
}; filter: {
path: '',
this.setDefaultFilter(); },
}
setDefaultFilter() {
this.store.filter = {
author_id: '',
assignee_id: '',
milestone_title: '',
label_name: [],
}; };
} }
......
export default (path, extraData) => path.split('&').reduce((dataParam, filterParam) => {
if (filterParam === '') return dataParam;
const data = dataParam;
const paramSplit = filterParam.split('=');
const paramKeyNormalized = paramSplit[0].replace('[]', '');
const isArray = paramSplit[0].indexOf('[]');
const value = decodeURIComponent(paramSplit[1]).replace(/\+/g, ' ');
if (isArray !== -1) {
if (!data[paramKeyNormalized]) {
data[paramKeyNormalized] = [];
}
data[paramKeyNormalized].push(value);
} else {
data[paramKeyNormalized] = value;
}
return data;
}, extraData);
/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, quotes, no-shadow, prefer-arrow-callback, prefer-template, consistent-return, no-return-assign, new-parens, no-param-reassign, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, quotes, no-shadow, prefer-arrow-callback, prefer-template, consistent-return, no-return-assign, new-parens, no-param-reassign, max-len */
(function() { var Breakpoints = (function() {
var Breakpoints = (function() { var BreakpointInstance, instance;
var BreakpointInstance, instance;
function Breakpoints() {} function Breakpoints() {}
instance = null; instance = null;
BreakpointInstance = (function() { BreakpointInstance = (function() {
var BREAKPOINTS; var BREAKPOINTS;
BREAKPOINTS = ["xs", "sm", "md", "lg"]; BREAKPOINTS = ["xs", "sm", "md", "lg"];
function BreakpointInstance() { function BreakpointInstance() {
this.setup(); this.setup();
} }
BreakpointInstance.prototype.setup = function() {
var allDeviceSelector, els;
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
return ".device-" + breakpoint;
});
if ($(allDeviceSelector.join(",")).length) {
return;
}
// Create all the elements
els = $.map(BREAKPOINTS, function(breakpoint) {
return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>";
});
return $("body").append(els.join(''));
};
BreakpointInstance.prototype.visibleDevice = function() { BreakpointInstance.prototype.setup = function() {
var allDeviceSelector; var allDeviceSelector, els;
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) { allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
return ".device-" + breakpoint; return ".device-" + breakpoint;
}); });
return $(allDeviceSelector.join(",")).filter(":visible"); if ($(allDeviceSelector.join(",")).length) {
}; return;
}
BreakpointInstance.prototype.getBreakpointSize = function() { // Create all the elements
var $visibleDevice; els = $.map(BREAKPOINTS, function(breakpoint) {
$visibleDevice = this.visibleDevice; return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>";
// TODO: Consider refactoring in light of turbolinks removal. });
// the page refreshed via turbolinks return $("body").append(els.join(''));
if (!$visibleDevice().length) { };
this.setup();
}
$visibleDevice = this.visibleDevice();
return $visibleDevice.attr("class").split("visible-")[1];
};
return BreakpointInstance; BreakpointInstance.prototype.visibleDevice = function() {
})(); var allDeviceSelector;
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
return ".device-" + breakpoint;
});
return $(allDeviceSelector.join(",")).filter(":visible");
};
Breakpoints.get = function() { BreakpointInstance.prototype.getBreakpointSize = function() {
return instance != null ? instance : instance = new BreakpointInstance; var $visibleDevice;
$visibleDevice = this.visibleDevice;
// TODO: Consider refactoring in light of turbolinks removal.
// the page refreshed via turbolinks
if (!$visibleDevice().length) {
this.setup();
}
$visibleDevice = this.visibleDevice();
return $visibleDevice.attr("class").split("visible-")[1];
}; };
return Breakpoints; return BreakpointInstance;
})(); })();
$((function(_this) { Breakpoints.get = function() {
return function() { return instance != null ? instance : instance = new BreakpointInstance;
return _this.bp = Breakpoints.get(); };
};
})(this)); return Breakpoints;
})();
$(() => { window.bp = Breakpoints.get(); });
window.Breakpoints = Breakpoints; window.Breakpoints = Breakpoints;
}).call(window);
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, no-else-return, object-shorthand, comma-dangle, max-len */ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, no-else-return, object-shorthand, comma-dangle, max-len */
(function() {
$(function() { $(function() {
var previewPath; var previewPath;
$('input#broadcast_message_color').on('input', function() { $('input#broadcast_message_color').on('input', function() {
var previewColor; var previewColor;
previewColor = $(this).val(); previewColor = $(this).val();
return $('div.broadcast-message-preview').css('background-color', previewColor); return $('div.broadcast-message-preview').css('background-color', previewColor);
}); });
$('input#broadcast_message_font').on('input', function() { $('input#broadcast_message_font').on('input', function() {
var previewColor; var previewColor;
previewColor = $(this).val(); previewColor = $(this).val();
return $('div.broadcast-message-preview').css('color', previewColor); return $('div.broadcast-message-preview').css('color', previewColor);
}); });
previewPath = $('textarea#broadcast_message_message').data('preview-path'); previewPath = $('textarea#broadcast_message_message').data('preview-path');
return $('textarea#broadcast_message_message').on('input', function() { return $('textarea#broadcast_message_message').on('input', function() {
var message; var message;
message = $(this).val(); message = $(this).val();
if (message === '') { if (message === '') {
return $('.js-broadcast-message-preview').text("Your message here"); return $('.js-broadcast-message-preview').text("Your message here");
} else { } else {
return $.ajax({ return $.ajax({
url: previewPath, url: previewPath,
type: "POST", type: "POST",
data: { data: {
broadcast_message: { broadcast_message: {
message: message message: message
}
} }
}); }
} });
}); }
}); });
}).call(window); });
This diff is collapsed.
/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, max-len */
(function() {
this.BuildArtifacts = (function() {
function BuildArtifacts() {
this.disablePropagation();
this.setupEntryClick();
}
BuildArtifacts.prototype.disablePropagation = function() { window.BuildArtifacts = (function() {
$('.top-block').on('click', '.download', function(e) { function BuildArtifacts() {
return e.stopPropagation(); this.disablePropagation();
}); this.setupEntryClick();
return $('.tree-holder').on('click', 'tr[data-link] a', function(e) { }
return e.stopImmediatePropagation();
});
};
BuildArtifacts.prototype.setupEntryClick = function() { BuildArtifacts.prototype.disablePropagation = function() {
return $('.tree-holder').on('click', 'tr[data-link]', function(e) { $('.top-block').on('click', '.download', function(e) {
return window.location = this.dataset.link; return e.stopPropagation();
}); });
}; return $('.tree-holder').on('click', 'tr[data-link] a', function(e) {
return e.stopImmediatePropagation();
});
};
return BuildArtifacts; BuildArtifacts.prototype.setupEntryClick = function() {
})(); return $('.tree-holder').on('click', 'tr[data-link]', function(e) {
}).call(window); return window.location = this.dataset.link;
});
};
return BuildArtifacts;
})();
(() => {
window.gl = window.gl || {};
class CILintEditor { window.gl = window.gl || {};
constructor() {
this.editor = window.ace.edit('ci-editor');
this.textarea = document.querySelector('#content');
this.editor.getSession().setMode('ace/mode/yaml'); class CILintEditor {
this.editor.on('input', () => { constructor() {
const content = this.editor.getSession().getValue(); this.editor = window.ace.edit('ci-editor');
this.textarea.value = content; this.textarea = document.querySelector('#content');
});
} this.editor.getSession().setMode('ace/mode/yaml');
this.editor.on('input', () => {
const content = this.editor.getSession().getValue();
this.textarea.value = content;
});
} }
}
gl.CILintEditor = CILintEditor; gl.CILintEditor = CILintEditor;
})();
/* eslint-disable func-names, space-before-function-paren, wrap-iife */ /* eslint-disable func-names, space-before-function-paren, wrap-iife */
/* global CommitFile */ /* global CommitFile */
(function() { window.Commit = (function() {
this.Commit = (function() { function Commit() {
function Commit() { $('.files .diff-file').each(function() {
$('.files .diff-file').each(function() { return new CommitFile(this);
return new CommitFile(this); });
}); }
}
return Commit; return Commit;
})(); })();
}).call(window);
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, max-len, prefer-arrow-callback */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, max-len, prefer-arrow-callback */
/* global Pager */ /* global Pager */
(function() { window.CommitsList = (function() {
this.CommitsList = (function() { var CommitsList = {};
var CommitsList = {};
CommitsList.timer = null; CommitsList.timer = null;
CommitsList.init = function(limit) { CommitsList.init = function(limit) {
$("body").on("click", ".day-commits-table li.commit", function(e) { $("body").on("click", ".day-commits-table li.commit", function(e) {
if (e.target.nodeName !== "A") { if (e.target.nodeName !== "A") {
location.href = $(this).attr("url"); location.href = $(this).attr("url");
e.stopPropagation(); e.stopPropagation();
return false; return false;
} }
}); });
Pager.init(limit, false, false, function() { Pager.init(limit, false, false, function() {
gl.utils.localTimeAgo($('.js-timeago')); gl.utils.localTimeAgo($('.js-timeago'));
}); });
this.content = $("#commits-list"); this.content = $("#commits-list");
this.searchField = $("#commits-search"); this.searchField = $("#commits-search");
this.lastSearch = this.searchField.val(); this.lastSearch = this.searchField.val();
return this.initSearch(); return this.initSearch();
}; };
CommitsList.initSearch = function() { CommitsList.initSearch = function() {
this.timer = null; this.timer = null;
return this.searchField.keyup((function(_this) { return this.searchField.keyup((function(_this) {
return function() { return function() {
clearTimeout(_this.timer); clearTimeout(_this.timer);
return _this.timer = setTimeout(_this.filterResults, 500); return _this.timer = setTimeout(_this.filterResults, 500);
}; };
})(this)); })(this));
}; };
CommitsList.filterResults = function() { CommitsList.filterResults = function() {
var commitsUrl, form, search; var commitsUrl, form, search;
form = $(".commits-search-form"); form = $(".commits-search-form");
search = CommitsList.searchField.val(); search = CommitsList.searchField.val();
if (search === CommitsList.lastSearch) return; if (search === CommitsList.lastSearch) return;
commitsUrl = form.attr("action") + '?' + form.serialize(); commitsUrl = form.attr("action") + '?' + form.serialize();
CommitsList.content.fadeTo('fast', 0.5); CommitsList.content.fadeTo('fast', 0.5);
return $.ajax({ return $.ajax({
type: "GET", type: "GET",
url: form.attr("action"), url: form.attr("action"),
data: form.serialize(), data: form.serialize(),
complete: function() { complete: function() {
return CommitsList.content.fadeTo('fast', 1.0); return CommitsList.content.fadeTo('fast', 1.0);
}, },
success: function(data) { success: function(data) {
CommitsList.lastSearch = search; CommitsList.lastSearch = search;
CommitsList.content.html(data.html); CommitsList.content.html(data.html);
return history.replaceState({ return history.replaceState({
page: commitsUrl page: commitsUrl
// Change url so if user reload a page - search results are saved // Change url so if user reload a page - search results are saved
}, document.title, commitsUrl); }, document.title, commitsUrl);
}, },
error: function() { error: function() {
CommitsList.lastSearch = null; CommitsList.lastSearch = null;
}, },
dataType: "json" dataType: "json"
}); });
}; };
return CommitsList; return CommitsList;
})(); })();
}).call(window);
import 'jquery'; import $ from 'jquery';
// bootstrap jQuery plugins // bootstrap jQuery plugins
import 'bootstrap-sass/assets/javascripts/bootstrap/affix'; import 'bootstrap-sass/assets/javascripts/bootstrap/affix';
...@@ -8,3 +8,9 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/modal'; ...@@ -8,3 +8,9 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/modal';
import 'bootstrap-sass/assets/javascripts/bootstrap/tab'; import 'bootstrap-sass/assets/javascripts/bootstrap/tab';
import 'bootstrap-sass/assets/javascripts/bootstrap/transition'; import 'bootstrap-sass/assets/javascripts/bootstrap/transition';
import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip'; import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip';
// custom jQuery functions
$.fn.extend({
disable() { return $(this).attr('disabled', 'disabled').addClass('disabled'); },
enable() { return $(this).removeAttr('disabled').removeClass('disabled'); },
});
import './polyfills';
import './jquery'; import './jquery';
import './bootstrap'; import './bootstrap';
// ECMAScript polyfills
import 'core-js/fn/array/find';
import 'core-js/fn/object/assign';
import 'core-js/fn/promise';
import 'core-js/fn/string/code-point-at';
import 'core-js/fn/string/from-code-point';
// Browser polyfills
import './polyfills/custom_event';
import './polyfills/element';
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = function CustomEvent(event, params) {
const evt = document.createEvent('CustomEvent');
const evtParams = params || { bubbles: false, cancelable: false, detail: undefined };
evt.initCustomEvent(event, evtParams.bubbles, evtParams.cancelable, evtParams.detail);
return evt;
};
window.CustomEvent.prototype = Event;
}
/* global Element */ Element.prototype.closest = Element.prototype.closest ||
/* eslint-disable consistent-return, max-len, no-empty, func-names */ function closest(selector, selectedElement = this) {
if (!selectedElement) return null;
Element.prototype.closest = Element.prototype.closest || function closest(selector, selectedElement = this) { return selectedElement.matches(selector) ?
if (!selectedElement) return; selectedElement :
return selectedElement.matches(selector) ? selectedElement : Element.prototype.closest(selector, selectedElement.parentElement); Element.prototype.closest(selector, selectedElement.parentElement);
}; };
Element.prototype.matches = Element.prototype.matches || Element.prototype.matches = Element.prototype.matches ||
Element.prototype.matchesSelector || Element.prototype.matchesSelector ||
...@@ -12,9 +12,9 @@ Element.prototype.matches = Element.prototype.matches || ...@@ -12,9 +12,9 @@ Element.prototype.matches = Element.prototype.matches ||
Element.prototype.msMatchesSelector || Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector || Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector || Element.prototype.webkitMatchesSelector ||
function (s) { function matches(selector) {
const matches = (this.document || this.ownerDocument).querySelectorAll(s); const elms = (this.document || this.ownerDocument).querySelectorAll(selector);
let i = matches.length - 1; let i = elms.length - 1;
while (i >= 0 && matches.item(i) !== this) { i -= 1; } while (i >= 0 && elms.item(i) !== this) { i -= 1; }
return i > -1; return i > -1;
}; };
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */
(function() {
this.Compare = (function() { window.Compare = (function() {
function Compare(opts) { function Compare(opts) {
this.opts = opts; this.opts = opts;
this.source_loading = $(".js-source-loading"); this.source_loading = $(".js-source-loading");
this.target_loading = $(".js-target-loading"); this.target_loading = $(".js-target-loading");
$('.js-compare-dropdown').each((function(_this) { $('.js-compare-dropdown').each((function(_this) {
return function(i, dropdown) { return function(i, dropdown) {
var $dropdown; var $dropdown;
$dropdown = $(dropdown); $dropdown = $(dropdown);
return $dropdown.glDropdown({ return $dropdown.glDropdown({
selectable: true, selectable: true,
fieldName: $dropdown.data('field-name'), fieldName: $dropdown.data('field-name'),
filterable: true, filterable: true,
id: function(obj, $el) { id: function(obj, $el) {
return $el.data('id'); return $el.data('id');
}, },
toggleLabel: function(obj, $el) { toggleLabel: function(obj, $el) {
return $el.text().trim(); return $el.text().trim();
}, },
clicked: function(e, el) { clicked: function(e, el) {
if ($dropdown.is('.js-target-branch')) { if ($dropdown.is('.js-target-branch')) {
return _this.getTargetHtml(); return _this.getTargetHtml();
} else if ($dropdown.is('.js-source-branch')) { } else if ($dropdown.is('.js-source-branch')) {
return _this.getSourceHtml(); return _this.getSourceHtml();
} else if ($dropdown.is('.js-target-project')) { } else if ($dropdown.is('.js-target-project')) {
return _this.getTargetProject(); return _this.getTargetProject();
}
} }
}); }
}; });
})(this)); };
this.initialState(); })(this));
} this.initialState();
}
Compare.prototype.initialState = function() { Compare.prototype.initialState = function() {
this.getSourceHtml(); this.getSourceHtml();
return this.getTargetHtml(); return this.getTargetHtml();
}; };
Compare.prototype.getTargetProject = function() { Compare.prototype.getTargetProject = function() {
return $.ajax({ return $.ajax({
url: this.opts.targetProjectUrl, url: this.opts.targetProjectUrl,
data: { data: {
target_project_id: $("input[name='merge_request[target_project_id]']").val() target_project_id: $("input[name='merge_request[target_project_id]']").val()
}, },
beforeSend: function() { beforeSend: function() {
return $('.mr_target_commit').empty(); return $('.mr_target_commit').empty();
}, },
success: function(html) { success: function(html) {
return $('.js-target-branch-dropdown .dropdown-content').html(html); return $('.js-target-branch-dropdown .dropdown-content').html(html);
} }
}); });
}; };
Compare.prototype.getSourceHtml = function() { Compare.prototype.getSourceHtml = function() {
return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', { return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', {
ref: $("input[name='merge_request[source_branch]']").val() ref: $("input[name='merge_request[source_branch]']").val()
}); });
}; };
Compare.prototype.getTargetHtml = function() { Compare.prototype.getTargetHtml = function() {
return this.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', { return this.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', {
target_project_id: $("input[name='merge_request[target_project_id]']").val(), target_project_id: $("input[name='merge_request[target_project_id]']").val(),
ref: $("input[name='merge_request[target_branch]']").val() ref: $("input[name='merge_request[target_branch]']").val()
}); });
}; };
Compare.prototype.sendAjax = function(url, loading, target, data) { Compare.prototype.sendAjax = function(url, loading, target, data) {
var $target; var $target;
$target = $(target); $target = $(target);
return $.ajax({ return $.ajax({
url: url, url: url,
data: data, data: data,
beforeSend: function() { beforeSend: function() {
loading.show(); loading.show();
return $target.empty(); return $target.empty();
}, },
success: function(html) { success: function(html) {
loading.hide(); loading.hide();
$target.html(html); $target.html(html);
var className = '.' + $target[0].className.replace(' ', '.'); var className = '.' + $target[0].className.replace(' ', '.');
gl.utils.localTimeAgo($('.js-timeago', className)); gl.utils.localTimeAgo($('.js-timeago', className));
} }
}); });
}; };
return Compare; return Compare;
})(); })();
}).call(window);
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, no-else-return, newline-per-chained-call, wrap-iife, max-len */ /* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, no-else-return, newline-per-chained-call, wrap-iife, max-len */
(function() { window.CompareAutocomplete = (function() {
this.CompareAutocomplete = (function() { function CompareAutocomplete() {
function CompareAutocomplete() { this.initDropdown();
this.initDropdown(); }
}
CompareAutocomplete.prototype.initDropdown = function() { CompareAutocomplete.prototype.initDropdown = function() {
return $('.js-compare-dropdown').each(function() { return $('.js-compare-dropdown').each(function() {
var $dropdown, selected; var $dropdown, selected;
$dropdown = $(this); $dropdown = $(this);
selected = $dropdown.data('selected'); selected = $dropdown.data('selected');
const $dropdownContainer = $dropdown.closest('.dropdown'); const $dropdownContainer = $dropdown.closest('.dropdown');
const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer); const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer);
const $filterInput = $('input[type="search"]', $dropdownContainer); const $filterInput = $('input[type="search"]', $dropdownContainer);
$dropdown.glDropdown({ $dropdown.glDropdown({
data: function(term, callback) { data: function(term, callback) {
return $.ajax({ return $.ajax({
url: $dropdown.data('refs-url'), url: $dropdown.data('refs-url'),
data: { data: {
ref: $dropdown.data('ref') ref: $dropdown.data('ref'),
} search: term,
}).done(function(refs) {
return callback(refs);
});
},
selectable: true,
filterable: true,
filterByText: true,
fieldName: $dropdown.data('field-name'),
filterInput: 'input[type="search"]',
renderRow: function(ref) {
var link;
if (ref.header != null) {
return $('<li />').addClass('dropdown-header').text(ref.header);
} else {
link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref));
return $('<li />').append(link);
} }
}, }).done(function(refs) {
id: function(obj, $el) { return callback(refs);
return $el.attr('data-ref'); });
}, },
toggleLabel: function(obj, $el) { selectable: true,
return $el.text().trim(); filterable: true,
filterRemote: true,
fieldName: $dropdown.data('field-name'),
filterInput: 'input[type="search"]',
renderRow: function(ref) {
var link;
if (ref.header != null) {
return $('<li />').addClass('dropdown-header').text(ref.header);
} else {
link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref));
return $('<li />').append(link);
} }
}); },
$filterInput.on('keyup', (e) => { id: function(obj, $el) {
const keyCode = e.keyCode || e.which; return $el.attr('data-ref');
if (keyCode !== 13) return; },
const text = $filterInput.val(); toggleLabel: function(obj, $el) {
$fieldInput.val(text); return $el.text().trim();
$('.dropdown-toggle-text', $dropdown).text(text); }
$dropdownContainer.removeClass('open'); });
}); $filterInput.on('keyup', (e) => {
const keyCode = e.keyCode || e.which;
if (keyCode !== 13) return;
const text = $filterInput.val();
$fieldInput.val(text);
$('.dropdown-toggle-text', $dropdown).text(text);
$dropdownContainer.removeClass('open');
});
$dropdownContainer.on('click', '.dropdown-content a', (e) => { $dropdownContainer.on('click', '.dropdown-content a', (e) => {
$dropdown.prop('title', e.target.text.replace(/_+?/g, '-')); $dropdown.prop('title', e.target.text.replace(/_+?/g, '-'));
if ($dropdown.hasClass('has-tooltip')) { if ($dropdown.hasClass('has-tooltip')) {
$dropdown.tooltip('fixTitle'); $dropdown.tooltip('fixTitle');
} }
});
}); });
}; });
};
return CompareAutocomplete; return CompareAutocomplete;
})(); })();
}).call(window);
/* 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 */
(function() {
this.ConfirmDangerModal = (function() {
function ConfirmDangerModal(form, text) {
var project_path, submit;
this.form = form;
$('.js-confirm-text').text(text || '');
$('.js-confirm-danger-input').val('');
$('#modal-confirm-danger').modal('show');
project_path = $('.js-confirm-danger-match').text();
submit = $('.js-confirm-danger-submit');
submit.disable();
$('.js-confirm-danger-input').off('input');
$('.js-confirm-danger-input').on('input', function() {
if (gl.utils.rstrip($(this).val()) === project_path) {
return submit.enable();
} else {
return submit.disable();
}
});
$('.js-confirm-danger-submit').off('click');
$('.js-confirm-danger-submit').on('click', (function(_this) {
return function() {
return _this.form.submit();
};
})(this));
}
return ConfirmDangerModal; window.ConfirmDangerModal = (function() {
})(); function ConfirmDangerModal(form, text) {
}).call(window); var project_path, submit;
this.form = form;
$('.js-confirm-text').text(text || '');
$('.js-confirm-danger-input').val('');
$('#modal-confirm-danger').modal('show');
project_path = $('.js-confirm-danger-match').text();
submit = $('.js-confirm-danger-submit');
submit.disable();
$('.js-confirm-danger-input').off('input');
$('.js-confirm-danger-input').on('input', function() {
if (gl.utils.rstrip($(this).val()) === project_path) {
return submit.enable();
} else {
return submit.disable();
}
});
$('.js-confirm-danger-submit').off('click');
$('.js-confirm-danger-submit').on('click', (function(_this) {
return function() {
return _this.form.submit();
};
})(this));
}
return ConfirmDangerModal;
})();
This diff is collapsed.
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, prefer-arrow-callback, max-len */ /* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, prefer-arrow-callback, max-len */
/* global Clipboard */
import Clipboard from 'vendor/clipboard';
window.Clipboard = require('vendor/clipboard');
var genericError, genericSuccess, showTooltip;
(function() {
var genericError, genericSuccess, showTooltip; genericSuccess = function(e) {
showTooltip(e.trigger, 'Copied');
genericSuccess = function(e) { // Clear the selection and blur the trigger so it loses its border
showTooltip(e.trigger, 'Copied'); e.clearSelection();
// Clear the selection and blur the trigger so it loses its border return $(e.trigger).blur();
e.clearSelection(); };
return $(e.trigger).blur();
}; // Safari doesn't support `execCommand`, so instead we inform the user to
// copy manually.
// Safari doesn't support `execCommand`, so instead we inform the user to //
// copy manually. // See http://clipboardjs.com/#browser-support
// genericError = function(e) {
// See http://clipboardjs.com/#browser-support var key;
genericError = function(e) { if (/Mac/i.test(navigator.userAgent)) {
var key; key = '&#8984;'; // Command
if (/Mac/i.test(navigator.userAgent)) { } else {
key = '&#8984;'; // Command key = 'Ctrl';
} else { }
key = 'Ctrl'; return showTooltip(e.trigger, "Press " + key + "-C to copy");
} };
return showTooltip(e.trigger, "Press " + key + "-C to copy");
}; showTooltip = function(target, title) {
var $target = $(target);
showTooltip = function(target, title) { var originalTitle = $target.data('original-title');
var $target = $(target);
var originalTitle = $target.data('original-title'); $target
.attr('title', 'Copied')
$target .tooltip('fixTitle')
.attr('title', 'Copied') .tooltip('show')
.tooltip('fixTitle') .attr('title', originalTitle)
.tooltip('show') .tooltip('fixTitle');
.attr('title', originalTitle) };
.tooltip('fixTitle');
}; $(function() {
var clipboard;
$(function() {
var clipboard; clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]');
clipboard.on('success', genericSuccess);
clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]'); return clipboard.on('error', genericError);
clipboard.on('success', genericSuccess); });
return clipboard.on('error', genericError);
});
}).call(window);
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-param-reassign, wrap-iife, max-len */ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-param-reassign, wrap-iife, max-len */
/* global Api */ /* global Api */
(function (w) { class CreateLabelDropdown {
class CreateLabelDropdown { constructor ($el, namespacePath, projectPath) {
constructor ($el, namespacePath, projectPath) { this.$el = $el;
this.$el = $el; this.namespacePath = namespacePath;
this.namespacePath = namespacePath; this.projectPath = projectPath;
this.projectPath = projectPath; this.$dropdownBack = $('.dropdown-menu-back', this.$el.closest('.dropdown'));
this.$dropdownBack = $('.dropdown-menu-back', this.$el.closest('.dropdown')); this.$cancelButton = $('.js-cancel-label-btn', this.$el);
this.$cancelButton = $('.js-cancel-label-btn', this.$el); this.$newLabelField = $('#new_label_name', this.$el);
this.$newLabelField = $('#new_label_name', this.$el); this.$newColorField = $('#new_label_color', this.$el);
this.$newColorField = $('#new_label_color', this.$el); this.$colorPreview = $('.js-dropdown-label-color-preview', this.$el);
this.$colorPreview = $('.js-dropdown-label-color-preview', this.$el); this.$newLabelError = $('.js-label-error', this.$el);
this.$newLabelError = $('.js-label-error', this.$el); this.$newLabelCreateButton = $('.js-new-label-btn', this.$el);
this.$newLabelCreateButton = $('.js-new-label-btn', this.$el); this.$colorSuggestions = $('.suggest-colors-dropdown a', this.$el);
this.$colorSuggestions = $('.suggest-colors-dropdown a', this.$el);
this.$newLabelError.hide();
this.$newLabelError.hide(); this.$newLabelCreateButton.disable();
this.$newLabelCreateButton.disable();
this.cleanBinding();
this.addBinding();
}
this.cleanBinding(); cleanBinding () {
this.addBinding(); this.$colorSuggestions.off('click');
} this.$newLabelField.off('keyup change');
this.$newColorField.off('keyup change');
this.$dropdownBack.off('click');
this.$cancelButton.off('click');
this.$newLabelCreateButton.off('click');
}
cleanBinding () { addBinding () {
this.$colorSuggestions.off('click'); const self = this;
this.$newLabelField.off('keyup change');
this.$newColorField.off('keyup change');
this.$dropdownBack.off('click');
this.$cancelButton.off('click');
this.$newLabelCreateButton.off('click');
}
addBinding () { this.$colorSuggestions.on('click', function (e) {
const self = this; const $this = $(this);
self.addColorValue(e, $this);
});
this.$colorSuggestions.on('click', function (e) { this.$newLabelField.on('keyup change', this.enableLabelCreateButton.bind(this));
const $this = $(this); this.$newColorField.on('keyup change', this.enableLabelCreateButton.bind(this));
self.addColorValue(e, $this);
});
this.$newLabelField.on('keyup change', this.enableLabelCreateButton.bind(this)); this.$dropdownBack.on('click', this.resetForm.bind(this));
this.$newColorField.on('keyup change', this.enableLabelCreateButton.bind(this));
this.$dropdownBack.on('click', this.resetForm.bind(this)); this.$cancelButton.on('click', function(e) {
e.preventDefault();
e.stopPropagation();
this.$cancelButton.on('click', function(e) { self.resetForm();
e.preventDefault(); self.$dropdownBack.trigger('click');
e.stopPropagation(); });
self.resetForm(); this.$newLabelCreateButton.on('click', this.saveLabel.bind(this));
self.$dropdownBack.trigger('click'); }
});
this.$newLabelCreateButton.on('click', this.saveLabel.bind(this)); addColorValue (e, $this) {
} e.preventDefault();
e.stopPropagation();
addColorValue (e, $this) { this.$newColorField.val($this.data('color')).trigger('change');
e.preventDefault(); this.$colorPreview
e.stopPropagation(); .css('background-color', $this.data('color'))
.parent()
.addClass('is-active');
}
this.$newColorField.val($this.data('color')).trigger('change'); enableLabelCreateButton () {
this.$colorPreview if (this.$newLabelField.val() !== '' && this.$newColorField.val() !== '') {
.css('background-color', $this.data('color')) this.$newLabelError.hide();
.parent() this.$newLabelCreateButton.enable();
.addClass('is-active'); } else {
this.$newLabelCreateButton.disable();
} }
}
enableLabelCreateButton () { resetForm () {
if (this.$newLabelField.val() !== '' && this.$newColorField.val() !== '') { this.$newLabelField
this.$newLabelError.hide(); .val('')
this.$newLabelCreateButton.enable(); .trigger('change');
} else {
this.$newLabelCreateButton.disable();
}
}
resetForm () { this.$newColorField
this.$newLabelField .val('')
.val('') .trigger('change');
.trigger('change');
this.$newColorField this.$colorPreview
.val('') .css('background-color', '')
.trigger('change'); .parent()
.removeClass('is-active');
}
this.$colorPreview saveLabel (e) {
.css('background-color', '') e.preventDefault();
.parent() e.stopPropagation();
.removeClass('is-active');
}
saveLabel (e) { Api.newLabel(this.namespacePath, this.projectPath, {
e.preventDefault(); title: this.$newLabelField.val(),
e.stopPropagation(); color: this.$newColorField.val()
}, (label) => {
this.$newLabelCreateButton.enable();
Api.newLabel(this.namespacePath, this.projectPath, { if (label.message) {
title: this.$newLabelField.val(), let errors;
color: this.$newColorField.val()
}, (label) => {
this.$newLabelCreateButton.enable();
if (label.message) {
let errors;
if (typeof label.message === 'string') {
errors = label.message;
} else {
errors = Object.keys(label.message).map(key =>
`${gl.text.humanize(key)} ${label.message[key].join(', ')}`
).join("<br/>");
}
this.$newLabelError
.html(errors)
.show();
} else {
this.$dropdownBack.trigger('click');
$(document).trigger('created.label', label); if (typeof label.message === 'string') {
errors = label.message;
} else {
errors = Object.keys(label.message).map(key =>
`${gl.text.humanize(key)} ${label.message[key].join(', ')}`
).join("<br/>");
} }
});
}
}
if (!w.gl) { this.$newLabelError
w.gl = {}; .html(errors)
.show();
} else {
this.$dropdownBack.trigger('click');
$(document).trigger('created.label', label);
}
});
} }
}
gl.CreateLabelDropdown = CreateLabelDropdown; window.gl = window.gl || {};
})(window); gl.CreateLabelDropdown = CreateLabelDropdown;
...@@ -2,129 +2,127 @@ ...@@ -2,129 +2,127 @@
require('./lib/utils/url_utility'); require('./lib/utils/url_utility');
(() => { const UNFOLD_COUNT = 20;
const UNFOLD_COUNT = 20; let isBound = false;
let isBound = false;
class Diff { class Diff {
constructor() { constructor() {
const $diffFile = $('.files .diff-file'); const $diffFile = $('.files .diff-file');
$diffFile.singleFileDiff(); $diffFile.singleFileDiff();
$diffFile.filesCommentButton(); $diffFile.filesCommentButton();
$diffFile.each((index, file) => new gl.ImageFile(file)); $diffFile.each((index, file) => new gl.ImageFile(file));
if (this.diffViewType() === 'parallel') { if (this.diffViewType() === 'parallel') {
$('.content-wrapper .container-fluid').removeClass('container-limited'); $('.content-wrapper .container-fluid').removeClass('container-limited');
} }
if (!isBound) {
$(document)
.on('click', '.js-unfold', this.handleClickUnfold.bind(this))
.on('click', '.diff-line-num a', this.handleClickLineNum.bind(this));
isBound = true;
}
if (gl.utils.getLocationHash()) { if (!isBound) {
this.highlightSelectedLine(); $(document)
} .on('click', '.js-unfold', this.handleClickUnfold.bind(this))
.on('click', '.diff-line-num a', this.handleClickLineNum.bind(this));
isBound = true;
}
this.openAnchoredDiff(); if (gl.utils.getLocationHash()) {
this.highlightSelectedLine();
} }
handleClickUnfold(e) { this.openAnchoredDiff();
const $target = $(e.target); }
// current babel config relies on iterators implementation, so we cannot simply do:
// const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent()); handleClickUnfold(e) {
const ref = this.lineNumbers($target.parent()); const $target = $(e.target);
const oldLineNumber = ref[0]; // current babel config relies on iterators implementation, so we cannot simply do:
const newLineNumber = ref[1]; // const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent());
const offset = newLineNumber - oldLineNumber; const ref = this.lineNumbers($target.parent());
const bottom = $target.hasClass('js-unfold-bottom'); const oldLineNumber = ref[0];
let since; const newLineNumber = ref[1];
let to; const offset = newLineNumber - oldLineNumber;
let unfold = true; const bottom = $target.hasClass('js-unfold-bottom');
let since;
if (bottom) { let to;
const lineNumber = newLineNumber + 1; let unfold = true;
since = lineNumber;
to = lineNumber + UNFOLD_COUNT; if (bottom) {
} else { const lineNumber = newLineNumber + 1;
const lineNumber = newLineNumber - 1; since = lineNumber;
since = lineNumber - UNFOLD_COUNT; to = lineNumber + UNFOLD_COUNT;
to = lineNumber; } else {
const lineNumber = newLineNumber - 1;
// make sure we aren't loading more than we need since = lineNumber - UNFOLD_COUNT;
const prevNewLine = this.lineNumbers($target.parent().prev())[1]; to = lineNumber;
if (since <= prevNewLine + 1) {
since = prevNewLine + 1; // make sure we aren't loading more than we need
unfold = false; const prevNewLine = this.lineNumbers($target.parent().prev())[1];
} if (since <= prevNewLine + 1) {
since = prevNewLine + 1;
unfold = false;
} }
}
const file = $target.parents('.diff-file'); const file = $target.parents('.diff-file');
const link = file.data('blob-diff-path'); const link = file.data('blob-diff-path');
const view = file.data('view'); const view = file.data('view');
const params = { since, to, bottom, offset, unfold, view }; const params = { since, to, bottom, offset, unfold, view };
$.get(link, params, response => $target.parent().replaceWith(response)); $.get(link, params, response => $target.parent().replaceWith(response));
} }
openAnchoredDiff(cb) { openAnchoredDiff(cb) {
const locationHash = gl.utils.getLocationHash(); const locationHash = gl.utils.getLocationHash();
const anchoredDiff = locationHash && locationHash.split('_')[0]; const anchoredDiff = locationHash && locationHash.split('_')[0];
if (!anchoredDiff) return;
const diffTitle = $(`#${anchoredDiff}`);
const diffFile = diffTitle.closest('.diff-file');
const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
if (nothingHereBlock.length) {
const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
this.highlightSelectedLine();
if (cb) cb();
});
} else if (cb) {
cb();
}
}
handleClickLineNum(e) { if (!anchoredDiff) return;
const hash = $(e.currentTarget).attr('href');
e.preventDefault(); const diffTitle = $(`#${anchoredDiff}`);
if (window.history.pushState) { const diffFile = diffTitle.closest('.diff-file');
window.history.pushState(null, null, hash); const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
} else { if (nothingHereBlock.length) {
window.location.hash = hash; const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
} diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
this.highlightSelectedLine(); this.highlightSelectedLine();
if (cb) cb();
});
} else if (cb) {
cb();
} }
}
diffViewType() { handleClickLineNum(e) {
return $('.inline-parallel-buttons a.active').data('view-type'); const hash = $(e.currentTarget).attr('href');
e.preventDefault();
if (window.history.pushState) {
window.history.pushState(null, null, hash);
} else {
window.location.hash = hash;
} }
this.highlightSelectedLine();
}
lineNumbers(line) { diffViewType() {
if (!line.children().length) { return $('.inline-parallel-buttons a.active').data('view-type');
return [0, 0]; }
}
return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10)); lineNumbers(line) {
if (!line.children().length) {
return [0, 0];
} }
return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10));
}
highlightSelectedLine() { highlightSelectedLine() {
const hash = gl.utils.getLocationHash(); const hash = gl.utils.getLocationHash();
const $diffFiles = $('.diff-file'); const $diffFiles = $('.diff-file');
$diffFiles.find('.hll').removeClass('hll'); $diffFiles.find('.hll').removeClass('hll');
if (hash) { if (hash) {
$diffFiles $diffFiles
.find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`) .find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`)
.addClass('hll'); .addClass('hll');
}
} }
} }
}
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.Diff = Diff; window.gl.Diff = Diff;
})();
/* global Vue */
/* global CommentsStore */
(() => {
const NewIssueForDiscussion = Vue.extend({
props: {
discussionId: {
type: String,
required: true,
},
},
data() {
return {
discussions: CommentsStore.state,
};
},
computed: {
discussion() {
return this.discussions[this.discussionId];
},
showButton() {
if (this.discussion) return !this.discussion.isResolved();
return false;
},
},
});
Vue.component('new-issue-for-discussion-btn', NewIssueForDiscussion);
})();
...@@ -14,10 +14,11 @@ require('./components/resolve_btn'); ...@@ -14,10 +14,11 @@ require('./components/resolve_btn');
require('./components/resolve_count'); require('./components/resolve_count');
require('./components/resolve_discussion_btn'); require('./components/resolve_discussion_btn');
require('./components/diff_note_avatars'); require('./components/diff_note_avatars');
require('./components/new_issue_for_discussion');
$(() => { $(() => {
const projectPath = document.querySelector('.merge-request').dataset.projectPath; const projectPath = document.querySelector('.merge-request').dataset.projectPath;
const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn'; const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn';
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.diffNoteApps = {}; window.gl.diffNoteApps = {};
......
...@@ -37,9 +37,11 @@ import PrometheusGraph from './monitoring/prometheus_graph'; // TODO: Maybe Make ...@@ -37,9 +37,11 @@ import PrometheusGraph from './monitoring/prometheus_graph'; // TODO: Maybe Make
import Issue from './issue'; import Issue from './issue';
import BindInOut from './behaviors/bind_in_out'; import BindInOut from './behaviors/bind_in_out';
import GroupName from './group_name';
import GroupsList from './groups_list'; import GroupsList from './groups_list';
import ProjectsList from './projects_list'; import ProjectsList from './projects_list';
import MiniPipelineGraph from './mini_pipeline_graph_dropdown'; import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater';
const ShortcutsBlob = require('./shortcuts_blob'); const ShortcutsBlob = require('./shortcuts_blob');
const UserCallout = require('./user_callout'); const UserCallout = require('./user_callout');
...@@ -66,6 +68,25 @@ const UserCallout = require('./user_callout'); ...@@ -66,6 +68,25 @@ const UserCallout = require('./user_callout');
} }
path = page.split(':'); path = page.split(':');
shortcut_handler = null; shortcut_handler = null;
function initBlob() {
new LineHighlighter();
new BlobLinePermalinkUpdater(
document.querySelector('#blob-content-holder'),
'.diff-line-num[data-line-number]',
document.querySelectorAll('.js-data-file-blob-permalink-url, .js-blob-blame-link'),
);
shortcut_handler = new ShortcutsNavigation();
fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
new ShortcutsBlob({
skipResetBindings: true,
fileBlobPermalinkUrl,
});
}
switch (page) { switch (page) {
case 'sessions:new': case 'sessions:new':
new UsernameValidator(); new UsernameValidator();
...@@ -180,10 +201,13 @@ const UserCallout = require('./user_callout'); ...@@ -180,10 +201,13 @@ const UserCallout = require('./user_callout');
new gl.Diff(); new gl.Diff();
new ZenMode(); new ZenMode();
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
break; break;
case 'projects:commit:pipelines': case 'projects:commit:pipelines':
new MiniPipelineGraph({ new MiniPipelineGraph({
container: '.js-pipeline-table', container: '.js-commit-pipeline-graph',
}).bindEvents(); }).bindEvents();
break; break;
case 'projects:commits:show': case 'projects:commits:show':
...@@ -258,27 +282,13 @@ const UserCallout = require('./user_callout'); ...@@ -258,27 +282,13 @@ const UserCallout = require('./user_callout');
break; break;
case 'projects:blob:show': case 'projects:blob:show':
gl.TargetBranchDropDown.bootstrap(); gl.TargetBranchDropDown.bootstrap();
new LineHighlighter(); initBlob();
shortcut_handler = new ShortcutsNavigation();
fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
new ShortcutsBlob({
skipResetBindings: true,
fileBlobPermalinkUrl,
});
break; break;
case 'projects:blob:edit': case 'projects:blob:edit':
gl.TargetBranchDropDown.bootstrap(); gl.TargetBranchDropDown.bootstrap();
break; break;
case 'projects:blame:show': case 'projects:blame:show':
new LineHighlighter(); initBlob();
shortcut_handler = new ShortcutsNavigation();
fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
new ShortcutsBlob({
skipResetBindings: true,
fileBlobPermalinkUrl,
});
break; break;
case 'groups:labels:new': case 'groups:labels:new':
case 'groups:labels:edit': case 'groups:labels:edit':
...@@ -362,6 +372,9 @@ const UserCallout = require('./user_callout'); ...@@ -362,6 +372,9 @@ const UserCallout = require('./user_callout');
shortcut_handler = new ShortcutsDashboardNavigation(); shortcut_handler = new ShortcutsDashboardNavigation();
new UserCallout(); new UserCallout();
break; break;
case 'groups':
new GroupName();
break;
case 'profiles': case 'profiles':
new NotificationsForm(); new NotificationsForm();
new NotificationsDropdown(); new NotificationsDropdown();
...@@ -369,6 +382,7 @@ const UserCallout = require('./user_callout'); ...@@ -369,6 +382,7 @@ const UserCallout = require('./user_callout');
case 'projects': case 'projects':
new Project(); new Project();
new ProjectAvatar(); new ProjectAvatar();
new GroupName();
switch (path[1]) { switch (path[1]) {
case 'compare': case 'compare':
new CompareAutocomplete(); new CompareAutocomplete();
......
...@@ -74,6 +74,9 @@ require('../window')(function(w){ ...@@ -74,6 +74,9 @@ require('../window')(function(w){
this._loadUrlData(config.endpoint) this._loadUrlData(config.endpoint)
.then(function(d) { .then(function(d) {
self._loadData(d, config, self); self._loadData(d, config, self);
}, function(xhrError) {
// TODO: properly handle errors due to XHR cancellation
return;
}).catch(function(e) { }).catch(function(e) {
throw new droplabAjaxException(e.message || e); throw new droplabAjaxException(e.message || e);
}); });
......
...@@ -82,6 +82,9 @@ require('../window')(function(w){ ...@@ -82,6 +82,9 @@ require('../window')(function(w){
this._loadUrlData(url) this._loadUrlData(url)
.then(function(data) { .then(function(data) {
self._loadData(data, config, self); self._loadData(data, config, self);
}, function(xhrError) {
// TODO: properly handle errors due to XHR cancellation
return;
}); });
} }
}, },
......
This diff is collapsed.
This diff is collapsed.
/* eslint-disable no-param-reassign, no-new */ /* eslint-disable no-param-reassign, no-new */
/* global Flash */ /* global Flash */
import EnvironmentsService from '../services/environments_service';
import EnvironmentTable from './environments_table';
import EnvironmentsStore from '../stores/environments_store';
import eventHub from '../event_hub';
const Vue = window.Vue = require('vue'); const Vue = window.Vue = require('vue');
window.Vue.use(require('vue-resource')); window.Vue.use(require('vue-resource'));
const EnvironmentsService = require('../services/environments_service');
const EnvironmentTable = require('./environments_table');
const EnvironmentsStore = require('../stores/environments_store');
require('../../vue_shared/components/table_pagination'); require('../../vue_shared/components/table_pagination');
require('../../lib/utils/common_utils'); require('../../lib/utils/common_utils');
require('../../vue_shared/vue_resource_interceptor'); require('../../vue_shared/vue_resource_interceptor');
module.exports = Vue.component('environment-component', { export default Vue.component('environment-component', {
components: { components: {
'environment-table': EnvironmentTable, 'environment-table': EnvironmentTable,
...@@ -66,33 +67,15 @@ module.exports = Vue.component('environment-component', { ...@@ -66,33 +67,15 @@ module.exports = Vue.component('environment-component', {
* Toggles loading property. * Toggles loading property.
*/ */
created() { created() {
const scope = gl.utils.getParameterByName('scope') || this.visibility; this.service = new EnvironmentsService(this.endpoint);
const pageNumber = gl.utils.getParameterByName('page') || this.pageNumber;
this.fetchEnvironments();
const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`;
eventHub.$on('refreshEnvironments', this.fetchEnvironments);
const service = new EnvironmentsService(endpoint); },
this.isLoading = true; beforeDestroyed() {
eventHub.$off('refreshEnvironments');
return service.get()
.then(resp => ({
headers: resp.headers,
body: resp.json(),
}))
.then((response) => {
this.store.storeAvailableCount(response.body.available_count);
this.store.storeStoppedCount(response.body.stopped_count);
this.store.storeEnvironments(response.body.environments);
this.store.setPagination(response.headers);
})
.then(() => {
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
new Flash('An error occurred while fetching the environments.', 'alert');
});
}, },
methods: { methods: {
...@@ -112,6 +95,32 @@ module.exports = Vue.component('environment-component', { ...@@ -112,6 +95,32 @@ module.exports = Vue.component('environment-component', {
gl.utils.visitUrl(param); gl.utils.visitUrl(param);
return param; return param;
}, },
fetchEnvironments() {
const scope = gl.utils.getParameterByName('scope') || this.visibility;
const pageNumber = gl.utils.getParameterByName('page') || this.pageNumber;
this.isLoading = true;
return this.service.get(scope, pageNumber)
.then(resp => ({
headers: resp.headers,
body: resp.json(),
}))
.then((response) => {
this.store.storeAvailableCount(response.body.available_count);
this.store.storeStoppedCount(response.body.stopped_count);
this.store.storeEnvironments(response.body.environments);
this.store.setPagination(response.headers);
})
.then(() => {
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
new Flash('An error occurred while fetching the environments.');
});
},
}, },
template: ` template: `
...@@ -144,7 +153,7 @@ module.exports = Vue.component('environment-component', { ...@@ -144,7 +153,7 @@ module.exports = Vue.component('environment-component', {
<div class="content-list environments-container"> <div class="content-list environments-container">
<div class="environments-list-loading text-center" v-if="isLoading"> <div class="environments-list-loading text-center" v-if="isLoading">
<i class="fa fa-spinner fa-spin"></i> <i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div> </div>
<div class="blank-state blank-state-no-icon" <div class="blank-state blank-state-no-icon"
...@@ -173,7 +182,8 @@ module.exports = Vue.component('environment-component', { ...@@ -173,7 +182,8 @@ module.exports = Vue.component('environment-component', {
<environment-table <environment-table
:environments="state.environments" :environments="state.environments"
:can-create-deployment="canCreateDeploymentParsed" :can-create-deployment="canCreateDeploymentParsed"
:can-read-environment="canReadEnvironmentParsed"/> :can-read-environment="canReadEnvironmentParsed"
:service="service"/>
</div> </div>
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1" <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
......
/** /**
* Renders the external url link in environments table. * Renders the external url link in environments table.
*/ */
const Vue = require('vue'); export default {
module.exports = Vue.component('external-url-component', {
props: { props: {
externalUrl: { externalUrl: {
type: String, type: String,
...@@ -12,8 +10,12 @@ module.exports = Vue.component('external-url-component', { ...@@ -12,8 +10,12 @@ module.exports = Vue.component('external-url-component', {
}, },
template: ` template: `
<a class="btn external_url" :href="externalUrl" target="_blank"> <a
<i class="fa fa-external-link"></i> class="btn external_url"
:href="externalUrl"
target="_blank"
title="Environment external URL">
<i class="fa fa-external-link" aria-hidden="true"></i>
</a> </a>
`, `,
}); };
const EnvironmentsComponent = require('./components/environment'); import EnvironmentsComponent from './components/environment';
$(() => { $(() => {
window.gl = window.gl || {}; window.gl = window.gl || {};
......
import Vue from 'vue';
export default new Vue();
const EnvironmentsFolderComponent = require('./environments_folder_view'); import EnvironmentsFolderComponent from './environments_folder_view';
$(() => { $(() => {
window.gl = window.gl || {}; window.gl = window.gl || {};
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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