Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce

This commit is contained in:
Marin Jankovski 2017-02-06 10:54:53 +01:00
commit 3f01f05bde
545 changed files with 8618 additions and 4339 deletions

View File

@ -1,7 +1,9 @@
/builds/
/coverage/
/coverage-javascript/
/node_modules/
/public/
/tmp/
/vendor/
/builds/
karma.config.js
webpack.config.js

View File

@ -16,6 +16,8 @@
],
"rules": {
"filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"],
"no-multiple-empty-lines": ["error", { "max": 1 }]
"no-multiple-empty-lines": ["error", { "max": 1 }],
"import/no-extraneous-dependencies": "off",
"import/no-unresolved": "off"
}
}

View File

@ -107,11 +107,13 @@ setup-test-env:
<<: *dedicated-runner
stage: prepare
script:
- bundle exec rake gitlab:assets:compile 2>/dev/null
- npm install
- bundle exec rake gitlab:assets:compile
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts:
expire_in: 7d
paths:
- node_modules
- public/assets
- tmp/tests
@ -232,7 +234,7 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21
script:
- bundle exec $CI_BUILD_NAME
rubocop:
rubocop:
<<: *ruby-static-analysis
<<: *dedicated-runner
stage: test
@ -291,18 +293,17 @@ rake db:seed_fu:
paths:
- log/development.log
teaspoon:
karma:
cache:
paths:
- vendor/ruby
- node_modules/
- node_modules
stage: test
<<: *use-db
<<: *dedicated-runner
script:
- npm install
- npm link istanbul
- bundle exec rake teaspoon
- bundle exec rake karma
artifacts:
name: coverage-javascript
expire_in: 31d
@ -444,7 +445,7 @@ pages:
<<: *dedicated-runner
dependencies:
- coverage
- teaspoon
- karma
- lint:javascript:report
script:
- mv public/ .public/

View File

@ -17,6 +17,7 @@ AllCops:
# Exclude some GitLab files
Exclude:
- 'vendor/**/*'
- 'node_modules/**/*'
- 'db/*'
- 'db/fixtures/**/*'
- 'tmp/**/*'

View File

@ -88,6 +88,27 @@ contributing to GitLab.
Please see the [UX Guide for GitLab].
## Release retrospective and kickoff
### Retrospective
After each release (usually on the 22nd of each month), we have a retrospective
call where we discuss what went well, what went wrong, and what we can improve
for the next release. The [retrospective notes] are public and you are invited
to comment them.
If you're interested, you can even join the [retrospective call][retro-kickoff-call].
### Kickoff
Before working on the next release (usually on the 8th of each month), we have a
kickoff call to explain what we expect to ship in the next release. The
[kickoff notes] are public and you are invited to comment them.
If you're interested, you can even join the [kickoff call][retro-kickoff-call].
[retrospective notes]: https://docs.google.com/document/d/1nEkM_7Dj4bT21GJy0Ut3By76FZqCfLBmFQNVThmW2TY/edit?usp=sharing
[kickoff notes]: https://docs.google.com/document/d/1ElPkZ90A8ey_iOkTvUs_ByMlwKK6NAB2VOK5835wYK0/edit?usp=sharing
[retro-kickoff-call]: https://gitlab.zoom.us/j/918821206
## Issue tracker
To get support for your particular problem please use the

11
Gemfile
View File

@ -7,7 +7,6 @@ gem 'rails-deprecated_sanitizer', '~> 1.0.3'
gem 'responders', '~> 2.0'
gem 'sprockets', '~> 3.7.0'
gem 'sprockets-es6', '~> 0.9.2'
# Default values for AR models
gem 'default_value_for', '~> 3.0.0'
@ -109,7 +108,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2'
gem 'asciidoctor-plantuml', '0.0.6'
gem 'asciidoctor-plantuml', '0.0.7'
gem 'rouge', '~> 2.0'
gem 'truncato', '~> 0.7.8'
@ -219,10 +218,12 @@ gem 'oj', '~> 2.17.4'
gem 'chronic', '~> 0.10.2'
gem 'chronic_duration', '~> 0.10.6'
gem 'webpack-rails', '~> 0.9.9'
gem 'rack-proxy', '~> 0.6.0'
gem 'sass-rails', '~> 5.0.6'
gem 'coffee-rails', '~> 4.1.0'
gem 'uglifier', '~> 2.7.2'
gem 'gitlab-turbolinks-classic', '~> 2.5', '>= 2.5.6'
gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0'
@ -292,13 +293,9 @@ group :development, :test do
gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.9.0'
gem 'teaspoon', '~> 1.1.0'
gem 'teaspoon-jasmine', '~> 2.2.0'
gem 'spring', '~> 1.7.0'
gem 'spring-commands-rspec', '~> 1.0.4'
gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.46.0', require: false
gem 'rubocop-rspec', '~> 1.9.1', require: false

View File

@ -54,7 +54,7 @@ GEM
faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0)
asciidoctor (1.5.3)
asciidoctor-plantuml (0.0.6)
asciidoctor-plantuml (0.0.7)
asciidoctor (~> 1.5)
ast (2.3.0)
attr_encrypted (3.0.3)
@ -72,10 +72,6 @@ GEM
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
execjs (~> 2.0)
babosa (1.0.2)
base32 (0.3.2)
bcrypt (3.1.11)
@ -266,8 +262,6 @@ GEM
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
gitlab-markup (1.5.1)
gitlab-turbolinks-classic (2.5.6)
coffee-rails
gitlab_omniauth-ldap (1.2.1)
net-ldap (~> 0.9)
omniauth (~> 1.0)
@ -548,6 +542,8 @@ GEM
rack (>= 1.1)
rack-protection (1.5.3)
rack
rack-proxy (0.6.0)
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.7.1)
@ -735,15 +731,9 @@ GEM
spring (>= 0.9.1)
spring-commands-spinach (1.1.0)
spring (>= 0.9.1)
spring-commands-teaspoon (0.0.2)
spring (>= 0.9.1)
sprockets (3.7.0)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-es6 (0.9.2)
babel-source (>= 5.8.11)
babel-transpiler
sprockets (>= 3.0.0)
sprockets-rails (3.1.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
@ -761,10 +751,6 @@ GEM
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
teaspoon (1.1.5)
railties (>= 3.2.5, < 6)
teaspoon-jasmine (2.2.0)
teaspoon (>= 1.0.0)
temple (0.7.7)
test_after_commit (1.1.0)
activerecord (>= 3.2)
@ -816,6 +802,8 @@ GEM
webmock (1.21.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
webpack-rails (0.9.9)
rails (>= 3.2.0)
websocket-driver (0.6.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
@ -841,7 +829,7 @@ DEPENDENCIES
allocations (~> 1.0)
asana (~> 0.4.0)
asciidoctor (~> 1.5.2)
asciidoctor-plantuml (= 0.0.6)
asciidoctor-plantuml (= 0.0.7)
attr_encrypted (~> 3.0.0)
awesome_print (~> 1.2.0)
babosa (~> 1.0.2)
@ -891,7 +879,6 @@ DEPENDENCIES
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1)
gitlab-turbolinks-classic (~> 2.5, >= 2.5.6)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2)
@ -955,6 +942,7 @@ DEPENDENCIES
rack-attack (~> 4.4.1)
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rack-proxy (~> 0.6.0)
rails (= 4.2.7.1)
rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0)
@ -996,14 +984,10 @@ DEPENDENCIES
spring (~> 1.7.0)
spring-commands-rspec (~> 1.0.4)
spring-commands-spinach (~> 1.1.0)
spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 3.7.0)
sprockets-es6 (~> 0.9.2)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6)
teaspoon (~> 1.1.0)
teaspoon-jasmine (~> 2.2.0)
test_after_commit (~> 1.1)
thin (~> 1.7.0)
timecop (~> 0.8.0)
@ -1019,6 +1003,7 @@ DEPENDENCIES
vmstat (~> 2.3.0)
web-console (~> 2.0)
webmock (~> 1.21.0)
webpack-rails (~> 0.9.9)
wikicloth (= 0.8.1)
BUNDLED WITH

View File

@ -1,5 +1,4 @@
/* 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 */
/* global Turbolinks */
(function() {
this.Admin = (function() {
@ -42,10 +41,10 @@
return $('.change-owner-link').show();
});
$('li.project_member').bind('ajax:success', function() {
return Turbolinks.visit(location.href);
return gl.utils.refreshCurrentPage();
});
$('li.group_member').bind('ajax:success', function() {
return Turbolinks.visit(location.href);
return gl.utils.refreshCurrentPage();
});
showBlacklistType = function() {
if ($("input[name='blacklist_type']:checked").val() === 'file') {

View File

@ -1,4 +1,4 @@
/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len */
/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len, no-multi-spaces, import/newline-after-import */
/* global bp */
/* global Cookies */
/* global Flash */
@ -6,65 +6,60 @@
/* global AwardsHandler */
/* global Aside */
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
/*= require jquery2 */
/*= require jquery-ui/autocomplete */
/*= require jquery-ui/datepicker */
/*= require jquery-ui/draggable */
/*= require jquery-ui/effect-highlight */
/*= require jquery-ui/sortable */
/*= require jquery_ujs */
/*= require jquery.endless-scroll */
/*= require jquery.highlight */
/*= require jquery.waitforimages */
/*= require jquery.atwho */
/*= require jquery.scrollTo */
/*= require jquery.turbolinks */
/*= require js.cookie */
/*= require turbolinks */
/*= require autosave */
/*= require bootstrap/affix */
/*= require bootstrap/alert */
/*= require bootstrap/button */
/*= require bootstrap/collapse */
/*= require bootstrap/dropdown */
/*= require bootstrap/modal */
/*= require bootstrap/scrollspy */
/*= require bootstrap/tab */
/*= require bootstrap/transition */
/*= require bootstrap/tooltip */
/*= require bootstrap/popover */
/*= require select2 */
/*= require underscore */
/*= require dropzone */
/*= require mousetrap */
/*= require mousetrap/pause */
/*= require shortcuts */
/*= require shortcuts_navigation */
/*= require shortcuts_dashboard_navigation */
/*= require shortcuts_issuable */
/*= require shortcuts_network */
/*= require jquery.nicescroll */
/*= require date.format */
/*= require_directory ./behaviors */
/*= require_directory ./blob */
/*= require_directory ./templates */
/*= require_directory ./commit */
/*= require_directory ./extensions */
/*= require_directory ./lib/utils */
/*= require_directory ./u2f */
/*= require_directory ./droplab */
/*= require_directory . */
/*= require fuzzaldrin-plus */
/*= require es6-promise.auto */
function requireAll(context) { return context.keys().map(context); }
window.$ = window.jQuery = require('jquery');
require('jquery-ui/ui/autocomplete');
require('jquery-ui/ui/datepicker');
require('jquery-ui/ui/draggable');
require('jquery-ui/ui/effect-highlight');
require('jquery-ui/ui/sortable');
require('jquery-ujs');
require('vendor/jquery.endless-scroll');
require('vendor/jquery.highlight');
require('vendor/jquery.waitforimages');
require('vendor/jquery.caret');
require('vendor/jquery.atwho');
require('vendor/jquery.scrollTo');
window.Cookies = require('vendor/js.cookie');
require('./autosave');
require('bootstrap/js/affix');
require('bootstrap/js/alert');
require('bootstrap/js/button');
require('bootstrap/js/collapse');
require('bootstrap/js/dropdown');
require('bootstrap/js/modal');
require('bootstrap/js/scrollspy');
require('bootstrap/js/tab');
require('bootstrap/js/transition');
require('bootstrap/js/tooltip');
require('bootstrap/js/popover');
require('select2/select2.js');
window._ = require('underscore');
window.Dropzone = require('dropzone');
require('mousetrap');
require('mousetrap/plugins/pause/mousetrap-pause');
require('./shortcuts');
require('./shortcuts_navigation');
require('./shortcuts_dashboard_navigation');
require('./shortcuts_issuable');
require('./shortcuts_network');
require('vendor/jquery.nicescroll');
requireAll(require.context('./behaviors', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./blob', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./templates', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./commit', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./extensions', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./lib/utils', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/));
require('vendor/fuzzaldrin-plus');
window.ES6Promise = require('vendor/es6-promise.auto');
window.ES6Promise.polyfill();
(function () {
document.addEventListener('page:fetch', function () {
document.addEventListener('beforeunload', function () {
// Unbind scroll events
$(document).off('scroll');
// Close any open tooltips

View File

@ -1,11 +1,13 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, no-var, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-template, quotes, comma-dangle, no-param-reassign, no-void, brace-style, no-underscore-dangle, no-return-assign, camelcase */
/* global Cookies */
var emojiAliases = require('emoji-aliases');
(function() {
this.AwardsHandler = (function() {
var FROM_SENTENCE_REGEX = /(?:, and | and |, )/; // For separating lists produced by ruby's Array#toSentence
function AwardsHandler() {
this.aliases = gl.emojiAliases();
this.aliases = emojiAliases;
$(document).off('click', '.js-add-award').on('click', '.js-add-award', (function(_this) {
return function(e) {
e.stopPropagation();

View File

@ -1,7 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, consistent-return, max-len */
/* global autosize */
/*= require autosize */
var autosize = require('vendor/autosize');
(function() {
$(function() {

View File

@ -6,7 +6,7 @@
// "Meta+Enter" (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, the form
// is submitted.
//
/*= require extensions/jquery */
require('../extensions/jquery');
//
// ### Example Markup

View File

@ -4,7 +4,7 @@
// 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.
//
/*= require extensions/jquery */
require('../extensions/jquery');
//
// ### Example Markup

View File

@ -1,7 +1,8 @@
/* eslint-disable no-param-reassign, comma-dangle */
/* global Api */
/*= require blob/template_selector */
require('./template_selector');
((global) => {
class BlobCiYamlSelector extends gl.TemplateSelector {
requestFile(query) {

View File

@ -1,5 +1,6 @@
/* global Api */
/*= require blob/template_selector */
require('./template_selector');
(() => {
const global = window.gl || (window.gl = {});

View File

@ -1,7 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params */
/* global Api */
/*= require blob/template_selector */
require('./template_selector');
(function() {
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },

View File

@ -1,7 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params, comma-dangle */
/* global Api */
/*= require blob/template_selector */
require('./template_selector');
(function() {
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },

View File

@ -2,7 +2,7 @@
/* global EditBlob */
/* global NewCommitForm */
/*= require_tree . */
require('./edit_blob');
(function() {
$(function() {

View File

@ -1,23 +1,27 @@
/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */
/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren, import/newline-after-import, no-multi-spaces, max-len */
/* global Vue */
/* global BoardService */
//= require vue
//= require vue-resource
//= require Sortable
//= require_tree ./models
//= require_tree ./stores
//= require_tree ./services
//= require_tree ./mixins
//= require_tree ./filters
//= require ./components/board
//= require ./components/board_sidebar
//= require ./components/new_list_dropdown
//= require ./vue_resource_interceptor
function requireAll(context) { return context.keys().map(context); }
window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
window.Sortable = require('vendor/Sortable');
requireAll(require.context('./models', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./stores', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./services', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./mixins', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./filters', true, /^\.\/.*\.(js|es6)$/));
require('./components/board');
require('./components/board_sidebar');
require('./components/new_list_dropdown');
require('./components/modal/index');
require('./vue_resource_interceptor');
$(() => {
const $boardApp = document.getElementById('board-app');
const Store = gl.issueBoards.BoardsStore;
const ModalStore = gl.issueBoards.ModalStore;
window.gl = window.gl || {};
@ -31,7 +35,8 @@ $(() => {
el: $boardApp,
components: {
'board': gl.issueBoards.Board,
'board-sidebar': gl.issueBoards.BoardSidebar
'board-sidebar': gl.issueBoards.BoardSidebar,
'board-add-issues-modal': gl.issueBoards.IssuesModal,
},
data: {
state: Store.state,
@ -40,6 +45,8 @@ $(() => {
boardId: $boardApp.dataset.boardId,
disabled: $boardApp.dataset.disabled === 'true',
issueLinkBase: $boardApp.dataset.issueLinkBase,
rootPath: $boardApp.dataset.rootPath,
bulkUpdatePath: $boardApp.dataset.bulkUpdatePath,
detailIssue: Store.detail
},
computed: {
@ -48,7 +55,7 @@ $(() => {
},
},
created () {
gl.boardService = new BoardService(this.endpoint, this.boardId);
gl.boardService = new BoardService(this.endpoint, this.bulkUpdatePath, this.boardId);
},
mounted () {
Store.disabled = this.disabled;
@ -59,8 +66,6 @@ $(() => {
if (list.type === 'done') {
list.position = Infinity;
} else if (list.type === 'backlog') {
list.position = -1;
}
});
@ -73,7 +78,7 @@ $(() => {
});
gl.IssueBoardsSearch = new Vue({
el: '#js-boards-search',
el: document.getElementById('js-boards-search'),
data: {
filters: Store.state.filters
},
@ -81,4 +86,27 @@ $(() => {
gl.issueBoards.newListDropdownInit();
}
});
gl.IssueBoardsModalAddBtn = new Vue({
mixins: [gl.issueBoards.ModalMixins],
el: document.getElementById('js-add-issues-btn'),
data: {
modal: ModalStore.store,
store: Store.state,
},
computed: {
disabled() {
return Store.shouldAddBlankState();
},
},
template: `
<button
class="btn btn-create pull-right prepend-left-10 has-tooltip"
type="button"
:disabled="disabled"
@click="toggleModal(true)">
Add issues
</button>
`,
});
});

View File

@ -2,9 +2,9 @@
/* global Vue */
/* global Sortable */
//= require ./board_blank_state
//= require ./board_delete
//= require ./board_list
require('./board_blank_state');
require('./board_delete');
require('./board_list');
(() => {
const Store = gl.issueBoards.BoardsStore;
@ -22,7 +22,8 @@
props: {
list: Object,
disabled: Boolean,
issueLinkBase: String
issueLinkBase: String,
rootPath: String,
},
data () {
return {

View File

@ -1,6 +1,8 @@
/* eslint-disable comma-dangle, space-before-function-paren, dot-notation */
/* global Vue */
require('./issue_card_inner');
(() => {
const Store = gl.issueBoards.BoardsStore;
@ -9,12 +11,16 @@
gl.issueBoards.BoardCard = Vue.extend({
template: '#js-board-list-card',
components: {
'issue-card-inner': gl.issueBoards.IssueCardInner,
},
props: {
list: Object,
issue: Object,
issueLinkBase: String,
disabled: Boolean,
index: Number
index: Number,
rootPath: String,
},
data () {
return {
@ -28,31 +34,6 @@
}
},
methods: {
filterByLabel (label, e) {
let labelToggleText = label.title;
const labelIndex = Store.state.filters['label_name'].indexOf(label.title);
$(e.target).tooltip('hide');
if (labelIndex === -1) {
Store.state.filters['label_name'].push(label.title);
$('.labels-filter').prepend(`<input type="hidden" name="label_name[]" value="${label.title}" />`);
} else {
Store.state.filters['label_name'].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'];
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();
},
mouseDown () {
this.showDetail = true;
},
@ -71,6 +52,7 @@
Store.detail.issue = {};
} else {
Store.detail.issue = this.issue;
Store.detail.list = this.list;
}
}
}

View File

@ -2,8 +2,8 @@
/* global Vue */
/* global Sortable */
//= require ./board_card
//= require ./board_new_issue
require('./board_card');
require('./board_new_issue');
(() => {
const Store = gl.issueBoards.BoardsStore;
@ -23,6 +23,7 @@
issues: Array,
loading: Boolean,
issueLinkBase: String,
rootPath: String,
},
data () {
return {

View File

@ -37,6 +37,7 @@
$(this.$refs.submitButton).enable();
Store.detail.issue = issue;
Store.detail.list = this.list;
})
.catch(() => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions

View File

@ -5,6 +5,8 @@
/* global LabelsSelect */
/* global Sidebar */
require('./sidebar/remove_issue');
(() => {
const Store = gl.issueBoards.BoardsStore;
@ -18,7 +20,8 @@
data() {
return {
detail: Store.detail,
issue: {}
issue: {},
list: {},
};
},
computed: {
@ -36,6 +39,7 @@
}
this.issue = this.detail.issue;
this.list = this.detail.list;
},
deep: true
},
@ -60,6 +64,9 @@
new LabelsSelect();
new Sidebar();
gl.Subscription.bindAll('.subscription');
}
},
components: {
removeBtn: gl.issueBoards.RemoveIssueBtn,
},
});
})();

View File

@ -0,0 +1,111 @@
/* global Vue */
(() => {
const Store = gl.issueBoards.BoardsStore;
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.IssueCardInner = Vue.extend({
props: {
issue: {
type: Object,
required: true,
},
issueLinkBase: {
type: String,
required: true,
},
list: {
type: Object,
required: false,
},
rootPath: {
type: String,
required: true,
},
},
methods: {
showLabel(label) {
if (!this.list) return true;
return !this.list.label || label.id !== this.list.label.id;
},
filterByLabel(label, e) {
let labelToggleText = label.title;
const labelIndex = Store.state.filters.label_name.indexOf(label.title);
$(e.currentTarget).tooltip('hide');
if (labelIndex === -1) {
Store.state.filters.label_name.push(label.title);
$('.labels-filter').prepend(`<input type="hidden" name="label_name[]" value="${label.title}" />`);
} else {
Store.state.filters.label_name.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;
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();
},
labelStyle(label) {
return {
backgroundColor: label.color,
color: label.textColor,
};
},
},
template: `
<div>
<h4 class="card-title">
<i
class="fa fa-eye-slash confidential-icon"
v-if="issue.confidential"></i>
<a
:href="issueLinkBase + '/' + issue.id"
:title="issue.title">
{{ issue.title }}
</a>
</h4>
<div class="card-footer">
<span
class="card-number"
v-if="issue.id">
#{{ issue.id }}
</span>
<a
class="card-assignee has-tooltip"
:href="rootPath + issue.assignee.username"
:title="'Assigned to ' + issue.assignee.name"
v-if="issue.assignee"
data-container="body">
<img
class="avatar avatar-inline s20"
:src="issue.assignee.avatar"
width="20"
height="20"
:alt="'Avatar for ' + issue.assignee.name" />
</a>
<button
class="label color-label has-tooltip"
v-for="label in issue.labels"
type="button"
v-if="showLabel(label)"
@click="filterByLabel(label, $event)"
:style="labelStyle(label)"
:title="label.description"
data-container="body">
{{ label.title }}
</button>
</div>
</div>
`,
});
})();

View File

@ -0,0 +1,70 @@
/* global Vue */
(() => {
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalEmptyState = Vue.extend({
mixins: [gl.issueBoards.ModalMixins],
data() {
return ModalStore.store;
},
props: {
image: {
type: String,
required: true,
},
newIssuePath: {
type: String,
required: true,
},
},
computed: {
contents() {
const obj = {
title: 'You haven\'t added any issues to your project yet',
content: `
An issue can be a bug, a todo or a feature request that needs to be
discussed in a project. Besides, issues are searchable and filterable.
`,
};
if (this.activeTab === 'selected') {
obj.title = 'You haven\'t selected any issues yet';
obj.content = `
Go back to <strong>All issues</strong> and select some issues
to add to your board.
`;
}
return obj;
},
},
template: `
<section class="empty-state">
<div class="row">
<div class="col-xs-12 col-sm-6 col-sm-push-6">
<aside class="svg-content" v-html="image"></aside>
</div>
<div class="col-xs-12 col-sm-6 col-sm-pull-6">
<div class="text-content">
<h4>{{ contents.title }}</h4>
<p v-html="contents.content"></p>
<a
:href="newIssuePath"
class="btn btn-success btn-inverted"
v-if="activeTab === 'all'">
New issue
</a>
<button
type="button"
class="btn btn-default"
@click="changeTab('all')"
v-if="activeTab === 'selected'">
All issues
</button>
</div>
</div>
</div>
</section>
`,
});
})();

View File

@ -0,0 +1,83 @@
/* eslint-disable no-new */
/* global Vue */
/* global Flash */
require('./lists_dropdown');
(() => {
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalFooter = Vue.extend({
mixins: [gl.issueBoards.ModalMixins],
data() {
return {
modal: ModalStore.store,
state: gl.issueBoards.BoardsStore.state,
};
},
computed: {
submitDisabled() {
return !ModalStore.selectedCount();
},
submitText() {
const count = ModalStore.selectedCount();
return `Add ${count > 0 ? count : ''} ${gl.text.pluralize('issue', count)}`;
},
},
methods: {
addIssues() {
const list = this.modal.selectedList || this.state.lists[0];
const selectedIssues = ModalStore.getSelectedIssues();
const issueIds = selectedIssues.map(issue => issue.globalId);
// Post the data to the backend
gl.boardService.bulkUpdate(issueIds, {
add_label_ids: [list.label.id],
}).catch(() => {
new Flash('Failed to update issues, please try again.', 'alert');
selectedIssues.forEach((issue) => {
list.removeIssue(issue);
list.issuesSize -= 1;
});
});
// Add the issues on the frontend
selectedIssues.forEach((issue) => {
list.addIssue(issue);
list.issuesSize += 1;
});
this.toggleModal(false);
},
},
components: {
'lists-dropdown': gl.issueBoards.ModalFooterListsDropdown,
},
template: `
<footer
class="form-actions add-issues-footer">
<div class="pull-left">
<button
class="btn btn-success"
type="button"
:disabled="submitDisabled"
@click="addIssues">
{{ submitText }}
</button>
<span class="inline add-issues-footer-to-list">
to list
</span>
<lists-dropdown></lists-dropdown>
</div>
<button
class="btn btn-default pull-right"
type="button"
@click="toggleModal(false)">
Cancel
</button>
</footer>
`,
});
})();

View File

@ -0,0 +1,70 @@
/* global Vue */
require('./tabs');
(() => {
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalHeader = Vue.extend({
mixins: [gl.issueBoards.ModalMixins],
data() {
return ModalStore.store;
},
computed: {
selectAllText() {
if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
return 'Select all';
}
return 'Deselect all';
},
showSearch() {
return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
},
},
methods: {
toggleAll() {
this.$refs.selectAllBtn.blur();
ModalStore.toggleAll();
},
},
components: {
'modal-tabs': gl.issueBoards.ModalTabs,
},
template: `
<div>
<header class="add-issues-header form-actions">
<h2>
Add issues
<button
type="button"
class="close"
data-dismiss="modal"
aria-label="Close"
@click="toggleModal(false)">
<span aria-hidden="true">×</span>
</button>
</h2>
</header>
<modal-tabs v-if="!loading && issuesCount > 0"></modal-tabs>
<div
class="add-issues-search append-bottom-10"
v-if="showSearch">
<input
placeholder="Search issues..."
class="form-control"
type="search"
v-model="searchTerm" />
<button
type="button"
class="btn btn-success btn-inverted prepend-left-10"
ref="selectAllBtn"
@click="toggleAll">
{{ selectAllText }}
</button>
</div>
</div>
`,
});
})();

View File

@ -0,0 +1,136 @@
/* global Vue */
/* global ListIssue */
require('./header');
require('./list');
require('./footer');
require('./empty_state');
(() => {
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.IssuesModal = Vue.extend({
props: {
blankStateImage: {
type: String,
required: true,
},
newIssuePath: {
type: String,
required: true,
},
issueLinkBase: {
type: String,
required: true,
},
rootPath: {
type: String,
required: true,
},
},
data() {
return ModalStore.store;
},
watch: {
page() {
this.loadIssues();
},
searchTerm() {
this.searchOperation();
},
showAddIssuesModal() {
if (this.showAddIssuesModal && !this.issues.length) {
this.loading = true;
this.loadIssues()
.then(() => {
this.loading = false;
});
} else if (!this.showAddIssuesModal) {
this.issues = [];
this.selectedIssues = [];
this.issuesCount = false;
}
},
},
methods: {
searchOperation: _.debounce(function searchOperationDebounce() {
this.loadIssues(true);
}, 500),
loadIssues(clearIssues = false) {
return gl.boardService.getBacklog({
search: this.searchTerm,
page: this.page,
per: this.perPage,
}).then((res) => {
const data = res.json();
if (clearIssues) {
this.issues = [];
}
data.issues.forEach((issueObj) => {
const issue = new ListIssue(issueObj);
const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
issue.selected = !!foundSelectedIssue;
this.issues.push(issue);
});
this.loadingNewPage = false;
if (!this.issuesCount) {
this.issuesCount = data.size;
}
});
},
},
computed: {
showList() {
if (this.activeTab === 'selected') {
return this.selectedIssues.length > 0;
}
return this.issuesCount > 0;
},
showEmptyState() {
if (!this.loading && this.issuesCount === 0) {
return true;
}
return this.activeTab === 'selected' && this.selectedIssues.length === 0;
},
},
components: {
'modal-header': gl.issueBoards.ModalHeader,
'modal-list': gl.issueBoards.ModalList,
'modal-footer': gl.issueBoards.ModalFooter,
'empty-state': gl.issueBoards.ModalEmptyState,
},
template: `
<div
class="add-issues-modal"
v-if="showAddIssuesModal">
<div class="add-issues-container">
<modal-header></modal-header>
<modal-list
:issue-link-base="issueLinkBase"
:root-path="rootPath"
v-if="!loading && showList"></modal-list>
<empty-state
v-if="showEmptyState"
:image="blankStateImage"
:new-issue-path="newIssuePath"></empty-state>
<section
class="add-issues-list text-center"
v-if="loading">
<div class="add-issues-list-loading">
<i class="fa fa-spinner fa-spin"></i>
</div>
</section>
<modal-footer></modal-footer>
</div>
</div>
`,
});
})();

View File

@ -0,0 +1,142 @@
/* global Vue */
/* global ListIssue */
/* global bp */
(() => {
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalList = Vue.extend({
props: {
issueLinkBase: {
type: String,
required: true,
},
rootPath: {
type: String,
required: true,
},
},
data() {
return ModalStore.store;
},
watch: {
activeTab() {
if (this.activeTab === 'all') {
ModalStore.purgeUnselectedIssues();
}
},
},
computed: {
loopIssues() {
if (this.activeTab === 'all') {
return this.issues;
}
return this.selectedIssues;
},
groupedIssues() {
const groups = [];
this.loopIssues.forEach((issue, i) => {
const index = i % this.columns;
if (!groups[index]) {
groups.push([]);
}
groups[index].push(issue);
});
return groups;
},
},
methods: {
scrollHandler() {
const currentPage = Math.floor(this.issues.length / this.perPage);
if ((this.scrollTop() > this.scrollHeight() - 100) && !this.loadingNewPage
&& currentPage === this.page) {
this.loadingNewPage = true;
this.page += 1;
}
},
toggleIssue(e, issue) {
if (e.target.tagName !== 'A') {
ModalStore.toggleIssue(issue);
}
},
listHeight() {
return this.$refs.list.getBoundingClientRect().height;
},
scrollHeight() {
return this.$refs.list.scrollHeight;
},
scrollTop() {
return this.$refs.list.scrollTop + this.listHeight();
},
showIssue(issue) {
if (this.activeTab === 'all') return true;
const index = ModalStore.selectedIssueIndex(issue);
return index !== -1;
},
setColumnCount() {
const breakpoint = bp.getBreakpointSize();
if (breakpoint === 'lg' || breakpoint === 'md') {
this.columns = 3;
} else if (breakpoint === 'sm') {
this.columns = 2;
} else {
this.columns = 1;
}
},
},
mounted() {
this.scrollHandlerWrapper = this.scrollHandler.bind(this);
this.setColumnCountWrapper = this.setColumnCount.bind(this);
this.setColumnCount();
this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
window.addEventListener('resize', this.setColumnCountWrapper);
},
beforeDestroy() {
this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
window.removeEventListener('resize', this.setColumnCountWrapper);
},
components: {
'issue-card-inner': gl.issueBoards.IssueCardInner,
},
template: `
<section
class="add-issues-list add-issues-list-columns"
ref="list">
<div
v-for="group in groupedIssues"
class="add-issues-list-column">
<div
v-for="issue in group"
v-if="showIssue(issue)"
class="card-parent">
<div
class="card"
:class="{ 'is-active': issue.selected }"
@click="toggleIssue($event, issue)">
<issue-card-inner
:issue="issue"
:issue-link-base="issueLinkBase"
:root-path="rootPath">
</issue-card-inner>
<span
:aria-label="'Issue #' + issue.id + ' selected'"
aria-checked="true"
v-if="issue.selected"
class="issue-card-selected text-center">
<i class="fa fa-check"></i>
</span>
</div>
</div>
</div>
</section>
`,
});
})();

View File

@ -0,0 +1,56 @@
/* global Vue */
(() => {
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalFooterListsDropdown = Vue.extend({
data() {
return {
modal: ModalStore.store,
state: gl.issueBoards.BoardsStore.state,
};
},
computed: {
selected() {
return this.modal.selectedList || this.state.lists[0];
},
},
destroyed() {
this.modal.selectedList = null;
},
template: `
<div class="dropdown inline">
<button
class="dropdown-menu-toggle"
type="button"
data-toggle="dropdown"
aria-expanded="false">
<span
class="dropdown-label-box"
:style="{ backgroundColor: selected.label.color }">
</span>
{{ selected.title }}
<i class="fa fa-chevron-down"></i>
</button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
<ul>
<li
v-for="list in state.lists"
v-if="list.type == 'label'">
<a
href="#"
role="button"
:class="{ 'is-active': list.id == selected.id }"
@click.prevent="modal.selectedList = list">
<span
class="dropdown-label-box"
:style="{ backgroundColor: list.label.color }">
</span>
{{ list.title }}
</a>
</li>
</ul>
</div>
</div>
`,
});
})();

View File

@ -0,0 +1,47 @@
/* global Vue */
(() => {
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalTabs = Vue.extend({
mixins: [gl.issueBoards.ModalMixins],
data() {
return ModalStore.store;
},
computed: {
selectedCount() {
return ModalStore.selectedCount();
},
},
destroyed() {
this.activeTab = 'all';
},
template: `
<div class="top-area prepend-top-10 append-bottom-10">
<ul class="nav-links issues-state-filters">
<li :class="{ 'active': activeTab == 'all' }">
<a
href="#"
role="button"
@click.prevent="changeTab('all')">
All issues
<span class="badge">
{{ issuesCount }}
</span>
</a>
</li>
<li :class="{ 'active': activeTab == 'selected' }">
<a
href="#"
role="button"
@click.prevent="changeTab('selected')">
Selected issues
<span class="badge">
{{ selectedCount }}
</span>
</a>
</li>
</ul>
</div>
`,
});
})();

View File

@ -0,0 +1,59 @@
/* eslint-disable no-new */
/* global Vue */
/* global Flash */
(() => {
const Store = gl.issueBoards.BoardsStore;
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.RemoveIssueBtn = Vue.extend({
props: {
issue: {
type: Object,
required: true,
},
list: {
type: Object,
required: true,
},
},
methods: {
removeIssue() {
const issue = this.issue;
const lists = issue.getLists();
const labelIds = lists.map(list => list.label.id);
// Post the remove data
gl.boardService.bulkUpdate([issue.globalId], {
remove_label_ids: labelIds,
}).catch(() => {
new Flash('Failed to remove issue from board, please try again.', 'alert');
lists.forEach((list) => {
list.addIssue(issue);
});
});
// Remove from the frontend store
lists.forEach((list) => {
list.removeIssue(issue);
});
Store.detail.issue = {};
},
},
template: `
<div
class="block list"
v-if="list.type !== 'done'">
<button
class="btn btn-default btn-block"
type="button"
@click="removeIssue">
Remove from board
</button>
</div>
`,
});
})();

View File

@ -0,0 +1,14 @@
(() => {
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalMixins = {
methods: {
toggleModal(toggle) {
ModalStore.store.showAddIssuesModal = toggle;
},
changeTab(tab) {
ModalStore.store.activeTab = tab;
},
},
};
})();

View File

@ -6,12 +6,15 @@
class ListIssue {
constructor (obj) {
this.globalId = obj.id;
this.id = obj.iid;
this.title = obj.title;
this.confidential = obj.confidential;
this.dueDate = obj.due_date;
this.subscribed = obj.subscribed;
this.labels = [];
this.selected = false;
this.assignee = false;
if (obj.assignee) {
this.assignee = new ListUser(obj.assignee);

View File

@ -9,7 +9,7 @@ class List {
this.position = obj.position;
this.title = obj.title;
this.type = obj.list_type;
this.preset = ['backlog', '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.loading = true;

View File

@ -2,7 +2,13 @@
/* global Vue */
class BoardService {
constructor (root, boardId) {
constructor (root, bulkUpdatePath, boardId) {
this.boards = Vue.resource(`${root}{/id}.json`, {}, {
issues: {
method: 'GET',
url: `${root}/${boardId}/issues.json`
}
});
this.lists = Vue.resource(`${root}/${boardId}/lists{/id}`, {}, {
generate: {
method: 'POST',
@ -10,7 +16,12 @@ class BoardService {
}
});
this.issue = Vue.resource(`${root}/${boardId}/issues{/id}`, {});
this.issues = Vue.resource(`${root}/${boardId}/lists{/id}/issues`, {});
this.issues = Vue.resource(`${root}/${boardId}/lists{/id}/issues`, {}, {
bulkUpdate: {
method: 'POST',
url: bulkUpdatePath,
},
});
Vue.http.interceptors.push((request, next) => {
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
@ -65,6 +76,20 @@ class BoardService {
issue
});
}
getBacklog(data) {
return this.boards.issues(data);
}
bulkUpdate(issueIds, extraData = {}) {
const data = {
update: Object.assign(extraData, {
issuable_ids: issueIds.join(','),
}),
};
return this.issues.bulkUpdate(data);
}
}
window.BoardService = BoardService;

View File

@ -34,15 +34,10 @@
},
new (listObj) {
const list = this.addList(listObj);
const backlogList = this.findList('type', 'backlog', 'backlog');
list
.save()
.then(() => {
// Remove any new issues from the backlog
// as they will be visible in the new list
list.issues.forEach(backlogList.removeIssue.bind(backlogList));
this.state.lists = _.sortBy(this.state.lists, 'position');
});
this.removeBlankState();
@ -52,7 +47,7 @@
},
shouldAddBlankState () {
// Decide whether to add the blank state
return !(this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'done')[0]);
return !(this.state.lists.filter(list => list.type !== 'done')[0]);
},
addBlankState () {
if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
@ -102,7 +97,7 @@
listTo.addIssue(issue, listFrom, newIndex);
}
if (listTo.type === 'done' && listFrom.type !== 'backlog') {
if (listTo.type === 'done') {
issueLists.forEach((list) => {
list.removeIssue(issue);
});

View File

@ -0,0 +1,96 @@
(() => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
class ModalStore {
constructor() {
this.store = {
columns: 3,
issues: [],
issuesCount: false,
selectedIssues: [],
showAddIssuesModal: false,
activeTab: 'all',
selectedList: null,
searchTerm: '',
loading: false,
loadingNewPage: false,
page: 1,
perPage: 50,
};
}
selectedCount() {
return this.getSelectedIssues().length;
}
toggleIssue(issueObj) {
const issue = issueObj;
const selected = issue.selected;
issue.selected = !selected;
if (!selected) {
this.addSelectedIssue(issue);
} else {
this.removeSelectedIssue(issue);
}
}
toggleAll() {
const select = this.selectedCount() !== this.store.issues.length;
this.store.issues.forEach((issue) => {
const issueUpdate = issue;
if (issueUpdate.selected !== select) {
issueUpdate.selected = select;
if (select) {
this.addSelectedIssue(issue);
} else {
this.removeSelectedIssue(issue);
}
}
});
}
getSelectedIssues() {
return this.store.selectedIssues.filter(issue => issue.selected);
}
addSelectedIssue(issue) {
const index = this.selectedIssueIndex(issue);
if (index === -1) {
this.store.selectedIssues.push(issue);
}
}
removeSelectedIssue(issue, forcePurge = false) {
if (this.store.activeTab === 'all' || forcePurge) {
this.store.selectedIssues = this.store.selectedIssues
.filter(fIssue => fIssue.id !== issue.id);
}
}
purgeUnselectedIssues() {
this.store.selectedIssues.forEach((issue) => {
if (!issue.selected) {
this.removeSelectedIssue(issue, true);
}
});
}
selectedIssueIndex(issue) {
return this.store.selectedIssues.indexOf(issue);
}
findSelectedIssue(issue) {
return this.store.selectedIssues
.filter(filteredIssue => filteredIssue.id === issue.id)[0];
}
}
gl.issueBoards.ModalStore = new ModalStore();
})();

View File

@ -43,6 +43,7 @@
BreakpointInstance.prototype.getBreakpointSize = function() {
var $visibleDevice;
$visibleDevice = this.visibleDevice;
// TODO: Consider refactoring in light of turbolinks removal.
// the page refreshed via turbolinks
if (!$visibleDevice().length) {
this.setup();

View File

@ -1,6 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-param-reassign, quotes, yoda, no-else-return, consistent-return, comma-dangle, object-shorthand, prefer-template, one-var, one-var-declaration-per-line, no-unused-vars, max-len, vars-on-top */
/* global Breakpoints */
/* global Turbolinks */
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
@ -127,7 +126,7 @@
pageUrl += DOWN_BUILD_TRACE;
}
return Turbolinks.visit(pageUrl);
return gl.utils.visitUrl(pageUrl);
}
};
})(this)

View File

@ -1,7 +1,7 @@
/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */
/* jshint esversion: 6 */
/*= require lib/utils/common_utils */
require('./lib/utils/common_utils');
(() => {
const gfmRules = {

View File

@ -1,7 +1,7 @@
/* 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 */
/*= require clipboard */
window.Clipboard = require('vendor/clipboard');
(function() {
var genericError, genericSuccess, showTooltip;

View File

@ -2,9 +2,12 @@
/* global Cookies */
/* global Flash */
//= require vue
//= require_tree ./svg
//= require_tree .
window.Vue = require('vue');
window.Cookies = require('vendor/js.cookie');
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('./svg', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('.', true, /^\.\/(?!cycle_analytics_bundle).*\.(js|es6)$/));
$(() => {
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';

View File

@ -1,9 +1,10 @@
/* eslint-disable class-methods-use-this */
//= require lib/utils/url_utility */
require('./lib/utils/url_utility');
(() => {
const UNFOLD_COUNT = 20;
let isBound = false;
class Diff {
constructor() {
@ -17,10 +18,12 @@
$('.content-wrapper .container-fluid').removeClass('container-limited');
}
$(document)
.off('click', '.js-unfold, .diff-line-num a')
.on('click', '.js-unfold', this.handleClickUnfold.bind(this))
.on('click', '.diff-line-num a', this.handleClickLineNum.bind(this));
if (!isBound) {
$(document)
.on('click', '.js-unfold', this.handleClickUnfold.bind(this))
.on('click', '.diff-line-num a', this.handleClickLineNum.bind(this));
isBound = true;
}
this.openAnchoredDiff();
}

View File

@ -1,12 +1,13 @@
/* eslint-disable func-names, comma-dangle, new-cap, no-new */
/* eslint-disable func-names, comma-dangle, new-cap, no-new, import/newline-after-import, no-multi-spaces, max-len */
/* global Vue */
/* global ResolveCount */
//= require_directory ./models
//= require_directory ./stores
//= require_directory ./services
//= require_directory ./mixins
//= require_directory ./components
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./services', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./mixins', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./components', false, /^\.\/.*\.(js|es6)$/));
$(() => {
const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn';

View File

@ -1,7 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, one-var, no-var, one-var-declaration-per-line, no-unused-vars, camelcase, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, prefer-arrow-callback */
/* global Dropzone */
/*= require preview_markdown */
require('./preview_markdown');
(function() {
this.DropzoneInput = (function() {

View File

@ -3,10 +3,10 @@
/* global EnvironmentsService */
/* global Flash */
//= require vue
//= require vue-resource
//= require_tree ../services/
//= require ./environment_item
window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
require('../services/environments_service');
require('./environment_item');
(() => {
window.gl = window.gl || {};
@ -180,7 +180,7 @@
<tr>
<th class="environments-name">Environment</th>
<th class="environments-deploy">Last deployment</th>
<th class="environments-build">Build</th>
<th class="environments-build">Job</th>
<th class="environments-commit">Commit</th>
<th class="environments-date">Updated</th>
<th class="hidden-xs environments-actions"></th>

View File

@ -1,6 +1,7 @@
/*= require vue */
/* global Vue */
window.Vue = require('vue');
(() => {
window.gl = window.gl || {};
window.gl.environmentsList = window.gl.environmentsList || {};

View File

@ -1,6 +1,7 @@
/*= require vue */
/* global Vue */
window.Vue = require('vue');
(() => {
window.gl = window.gl || {};
window.gl.environmentsList = window.gl.environmentsList || {};

View File

@ -1,14 +1,15 @@
/* global Vue */
/* global timeago */
/*= require timeago */
/*= require lib/utils/text_utility */
/*= require vue_common_component/commit */
/*= require ./environment_actions */
/*= require ./environment_external_url */
/*= require ./environment_stop */
/*= require ./environment_rollback */
/*= require ./environment_terminal_button */
window.Vue = require('vue');
window.timeago = require('vendor/timeago');
require('../../lib/utils/text_utility');
require('../../vue_common_component/commit');
require('./environment_actions');
require('./environment_external_url');
require('./environment_stop');
require('./environment_rollback');
require('./environment_terminal_button');
(() => {
/**

View File

@ -1,6 +1,7 @@
/*= require vue */
/* global Vue */
window.Vue = require('vue');
(() => {
window.gl = window.gl || {};
window.gl.environmentsList = window.gl.environmentsList || {};

View File

@ -1,6 +1,7 @@
/*= require vue */
/* global Vue */
window.Vue = require('vue');
(() => {
window.gl = window.gl || {};
window.gl.environmentsList = window.gl.environmentsList || {};

View File

@ -1,6 +1,7 @@
/*= require vue */
/* global Vue */
window.Vue = require('vue');
(() => {
window.gl = window.gl || {};
window.gl.environmentsList = window.gl.environmentsList || {};

View File

@ -1,7 +1,8 @@
//= require vue
//= require_tree ./stores/
//= require ./components/environment
//= require ./vue_resource_interceptor
window.Vue = require('vue');
require('./stores/environments_store');
require('./components/environment');
require('./vue_resource_interceptor');
$(() => {
window.gl = window.gl || {};

View File

@ -1,5 +1,6 @@
/* globals Vue */
/* eslint-disable no-unused-vars, no-param-reassign */
class EnvironmentsService {
constructor(root) {

View File

@ -1,4 +1,7 @@
/* eslint-disable no-extend-native, func-names, space-before-function-paren, space-infix-ops, max-len */
/* eslint-disable no-extend-native, func-names, space-before-function-paren, space-infix-ops, strict, max-len */
'use strict';
Array.prototype.first = function() {
return this[0];
};

View File

@ -1,4 +1,4 @@
/*= require filtered_search/filtered_search_dropdown */
require('./filtered_search_dropdown');
/* global droplabFilter */

View File

@ -1,4 +1,4 @@
/*= require filtered_search/filtered_search_dropdown */
require('./filtered_search_dropdown');
/* global droplabAjax */
/* global droplabFilter */

View File

@ -1,4 +1,4 @@
/*= require filtered_search/filtered_search_dropdown */
require('./filtered_search_dropdown');
/* global droplabAjaxFilter */

View File

@ -1,7 +1,3 @@
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
/*= require_tree . */
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('./', true, /^\.\/(?!filtered_search_bundle).*\.(js|es6)$/));

View File

@ -9,7 +9,7 @@
this.setupMapping();
this.cleanupWrapper = this.cleanup.bind(this);
document.addEventListener('page:fetch', this.cleanupWrapper);
document.addEventListener('beforeunload', this.cleanupWrapper);
}
cleanup() {
@ -20,7 +20,7 @@
this.setupMapping();
document.removeEventListener('page:fetch', this.cleanupWrapper);
document.removeEventListener('beforeunload', this.cleanupWrapper);
}
setupMapping() {

View File

@ -1,5 +1,3 @@
/* global Turbolinks */
(() => {
class FilteredSearchManager {
constructor() {
@ -15,13 +13,13 @@
this.dropdownManager.setDropdown();
this.cleanupWrapper = this.cleanup.bind(this);
document.addEventListener('page:fetch', this.cleanupWrapper);
document.addEventListener('beforeunload', this.cleanupWrapper);
}
}
cleanup() {
this.unbindEvents();
document.removeEventListener('page:fetch', this.cleanupWrapper);
document.removeEventListener('beforeunload', this.cleanupWrapper);
}
bindEvents() {
@ -200,7 +198,9 @@
paths.push(`search=${sanitized}`);
}
Turbolinks.visit(`?scope=all&utf8=✓&${paths.join('&')}`);
const parameterizedUrl = `?scope=all&utf8=✓&${paths.join('&')}`;
gl.utils.visitUrl(parameterizedUrl);
}
getUsernameParams() {

View File

@ -1,6 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, prefer-rest-params, max-len, vars-on-top, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func, no-mixed-operators */
/* global fuzzaldrinPlus */
/* global Turbolinks */
(function() {
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote,
@ -249,7 +248,7 @@
_this.fullData = data;
_this.parseData(_this.fullData);
_this.focusTextInput();
if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val().trim() !== '') {
if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val() && _this.filter.input.val().trim() !== '') {
return _this.filter.input.trigger('input');
}
};
@ -723,7 +722,7 @@
if ($el.length) {
var href = $el.attr('href');
if (href && href !== '#') {
Turbolinks.visit(href);
gl.utils.visitUrl(href);
} else {
$el.first().trigger('click');
}

View File

@ -1,6 +1,6 @@
/* eslint-disable comma-dangle, class-methods-use-this, max-len, space-before-function-paren, arrow-parens, no-param-reassign */
//= require gl_field_error
require('./gl_field_error');
((global) => {
const customValidationFlag = 'gl-field-error-ignore';

View File

@ -1,12 +1,3 @@
/* eslint-disable func-names, space-before-function-paren */
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
/*= require_tree . */
(function() {
}).call(this);
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!graphs_bundle).*\.(js|es6)$/));

View File

@ -5,7 +5,7 @@
/* global ContributorsStatGraphUtil */
/* global d3 */
/*= require d3 */
window.d3 = require('d3');
(function() {
this.ContributorsStatGraph = (function() {

View File

@ -2,7 +2,7 @@
/* global d3 */
/* global ContributorsGraph */
/*= require d3 */
window.d3 = require('d3');
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },

View File

@ -1,6 +1,5 @@
/* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, wrap-iife, max-len */
/* global Issuable */
/* global Turbolinks */
((global) => {
var issuable_created;
@ -119,7 +118,7 @@
issuesUrl = formAction;
issuesUrl += "" + (formAction.indexOf('?') < 0 ? '?' : '&');
issuesUrl += formData;
return Turbolinks.visit(issuesUrl);
return gl.utils.visitUrl(issuesUrl);
};
})(this),
initResetFilters: function() {
@ -130,7 +129,7 @@
const baseIssuesUrl = target.href;
$form.attr('action', baseIssuesUrl);
Turbolinks.visit(baseIssuesUrl);
gl.utils.visitUrl(baseIssuesUrl);
});
},
initChecks: function() {

View File

@ -1 +1 @@
//= require ./time_tracking/time_tracking_bundle
require('./time_tracking/time_tracking_bundle');

View File

@ -1,5 +1,5 @@
/* global Vue */
//= require lib/utils/pretty_time
require('../../../lib/utils/pretty_time');
(() => {
Vue.component('time-tracking-collapsed-state', {
@ -39,4 +39,3 @@
`,
});
})();

View File

@ -1,5 +1,5 @@
/* global Vue */
//= require lib/utils/pretty_time
require('../../../lib/utils/pretty_time');
(() => {
const prettyTime = gl.utils.prettyTime;

View File

@ -1,10 +1,11 @@
/* global Vue */
//= require ./help_state
//= require ./collapsed_state
//= require ./spent_only_pane
//= require ./no_tracking_pane
//= require ./estimate_only_pane
//= require ./comparison_pane
require('./help_state');
require('./collapsed_state');
require('./spent_only_pane');
require('./no_tracking_pane');
require('./estimate_only_pane');
require('./comparison_pane');
(() => {
Vue.component('issuable-time-tracker', {

View File

@ -1,7 +1,8 @@
/* global Vue */
//= require ./components/time_tracker
//= require smart_interval
//= require subbable_resource
require('./components/time_tracker');
require('../../smart_interval');
require('../../subbable_resource');
(() => {
/* This Vue instance represents what will become the parent instance for the

View File

@ -1,9 +1,9 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, max-len */
/* global Flash */
/*= require flash */
/*= require jquery.waitforimages */
/*= require task_list */
require('./flash');
require('vendor/jquery.waitforimages');
require('vendor/task_list');
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };

View File

@ -1,7 +1,3 @@
/* eslint-disable func-names, space-before-function-paren */
/*= require Chart */
(function() {
}).call(this);
window.Chart = require('vendor/Chart');

View File

@ -1,7 +1,3 @@
/* eslint-disable func-names, space-before-function-paren */
/*= require d3 */
(function() {
}).call(this);
window.d3 = require('d3');

View File

@ -95,7 +95,6 @@
const newState = `${copySource}${this.currentLocation.search}${this.currentLocation.hash}`;
history.replaceState({
turbolinks: true,
url: newState,
}, document.title, newState);
return newState;

View File

@ -2,8 +2,8 @@
/* global timeago */
/* global dateFormat */
/*= require timeago */
/*= require date.format */
window.timeago = require('vendor/timeago');
window.dateFormat = require('vendor/date.format');
(function() {
(function(w) {

View File

@ -1,6 +0,0 @@
(function() {
gl.emojiAliases = function() {
return JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>');
};
}).call(this);

View File

@ -161,6 +161,9 @@
gl.text.humanize = function(string) {
return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1);
};
gl.text.pluralize = function(str, count) {
return str + (count > 1 || count === 0 ? 's' : '');
};
return gl.text.truncate = function(string, maxLength) {
return string.substr(0, (maxLength - 3)) + '...';
};

View File

@ -76,5 +76,11 @@
hashIndex = url.indexOf('#');
return hashIndex === -1 ? null : url.substring(hashIndex + 1);
};
w.gl.utils.refreshCurrentPage = () => gl.utils.visitUrl(document.location.href);
w.gl.utils.visitUrl = (url) => {
document.location.href = url;
};
})(window);
}).call(this);

View File

@ -1,2 +1,2 @@
//= require vue
//= require vue-resource
window.Vue = require('vue');
window.Vue.use(require('vue-resource'));

View File

@ -4,7 +4,7 @@
//
// Handles single- and multi-line selection and highlight for blob views.
//
/*= require jquery.scrollTo */
require('vendor/jquery.scrollTo');
//
// ### Example Markup
@ -171,7 +171,6 @@
// This method is stubbed in tests.
LineHighlighter.prototype.__setLocationHash__ = function(value) {
return history.pushState({
turbolinks: false,
url: value
// We're using pushState instead of assigning location.hash directly to
// prevent the page from scrolling on the hashchange event

View File

@ -1,14 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback */
/* global Turbolinks */
(function() {
Turbolinks.enableProgressBar();
$(document).on('page:fetch', function() {
window.addEventListener('beforeunload', function() {
$('.tanuki-logo').addClass('animate');
});
$(document).on('page:change', function() {
$('.tanuki-logo').removeClass('animate');
});
}).call(this);

View File

@ -2,14 +2,14 @@
/* global Vue */
/* global Flash */
//= require vue
//= require ./merge_conflict_store
//= require ./merge_conflict_service
//= require ./mixins/line_conflict_utils
//= require ./mixins/line_conflict_actions
//= require ./components/diff_file_editor
//= require ./components/inline_conflict_lines
//= require ./components/parallel_conflict_lines
window.Vue = require('vue');
require('./merge_conflict_store');
require('./merge_conflict_service');
require('./mixins/line_conflict_utils');
require('./mixins/line_conflict_actions');
require('./components/diff_file_editor');
require('./components/inline_conflict_lines');
require('./components/parallel_conflict_lines');
$(() => {
const INTERACTIVE_RESOLVE_MODE = 'interactive';

View File

@ -1,9 +1,9 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, max-len, prefer-arrow-callback */
/* global MergeRequestTabs */
/*= require jquery.waitforimages */
/*= require task_list */
/*= require merge_request_tabs */
require('vendor/jquery.waitforimages');
require('vendor/task_list');
require('./merge_request_tabs');
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
@ -110,9 +110,8 @@
};
MergeRequest.prototype.initCommitMessageListeners = function() {
var textarea = $('textarea.js-commit-message');
$('a.js-with-description-link').on('click', function(e) {
$(document).on('click', 'a.js-with-description-link', function(e) {
var textarea = $('textarea.js-commit-message');
e.preventDefault();
textarea.val(textarea.data('messageWithDescription'));
@ -120,7 +119,8 @@
$('p.js-without-description-hint').show();
});
$('a.js-without-description-link').on('click', function(e) {
$(document).on('click', 'a.js-without-description-link', function(e) {
var textarea = $('textarea.js-commit-message');
e.preventDefault();
textarea.val(textarea.data('messageWithoutDescription'));

View File

@ -1,11 +1,11 @@
/* eslint-disable no-new, class-methods-use-this */
/* global Breakpoints */
/* global Cookies */
/* global DiffNotesApp */
/* global Flash */
/*= require js.cookie */
/*= require breakpoints */
require('./breakpoints');
window.Cookies = require('vendor/js.cookie');
require('./flash');
/* eslint-disable max-len */
// MergeRequestTabs
@ -184,12 +184,13 @@
// Ensure parameters and hash come along for the ride
newState += location.search + location.hash;
// TODO: Consider refactoring in light of turbolinks removal.
// Replace the current history state with the new one without breaking
// Turbolinks' history.
//
// See https://github.com/rails/turbolinks/issues/363
window.history.replaceState({
turbolinks: true,
url: newState,
}, document.title, newState);

View File

@ -2,7 +2,8 @@
/* global notify */
/* global notifyPermissions */
/* global merge_request_widget */
/* global Turbolinks */
require('./smart_interval');
((global) => {
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; };
@ -69,13 +70,13 @@
}
MergeRequestWidget.prototype.clearEventListeners = function() {
return $(document).off('page:change.merge_request');
return $(document).off('DOMContentLoaded');
};
MergeRequestWidget.prototype.addEventListeners = function() {
var allowedPages;
allowedPages = ['show', 'commits', 'pipelines', 'changes'];
$(document).on('page:change.merge_request', (function(_this) {
$(document).on('DOMContentLoaded', (function(_this) {
return function() {
var page;
page = $('body').data('page').split(':').last();

View File

@ -47,7 +47,7 @@
$('.js-rebase-button').html("<i class='fa fa-spinner fa-spin'></i> Rebase in progress");
});
} else {
merge_request_widget.getMergeStatus();
setTimeout(() => merge_request_widget.getMergeStatus(), 200);
}
});
})();

View File

@ -2,13 +2,9 @@
/* global Network */
/* global ShortcutsNetwork */
// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
/*= require_tree . */
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!network_bundle).*\.(js|es6)$/));
(function() {
$(function() {

View File

@ -4,13 +4,14 @@
/* global ResolveService */
/* global mrRefreshWidgetUrl */
/*= require autosave */
/*= require autosize */
/*= require dropzone */
/*= require dropzone_input */
/*= require gfm_auto_complete */
/*= require jquery.atwho */
/*= require task_list */
require('./autosave');
window.autosize = require('vendor/autosize');
window.Dropzone = require('dropzone');
require('./dropzone_input');
require('./gfm_auto_complete');
require('vendor/jquery.caret'); // required by jquery.atwho
require('vendor/jquery.atwho');
require('vendor/task_list');
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };

View File

@ -1,6 +1,6 @@
/* eslint-disable no-new, guard-for-in, no-restricted-syntax, no-continue, no-param-reassign, max-len */
//= require lib/utils/bootstrap_linked_tabs
require('./lib/utils/bootstrap_linked_tabs');
((global) => {
class Pipelines {

View File

@ -1,7 +1,3 @@
/* eslint-disable func-names, space-before-function-paren */
/*= require_tree . */
(function() {
}).call(this);
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!profile_bundle).*\.(js|es6)$/));

View File

@ -1,6 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, consistent-return, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, no-else-return, newline-per-chained-call, no-shadow, vars-on-top, prefer-template, max-len */
/* global Cookies */
/* global Turbolinks */
/* global ProjectSelect */
(function() {
@ -58,6 +57,11 @@
};
Project.prototype.initRefSwitcher = function() {
var refListItem = document.createElement('li');
var refLink = document.createElement('a');
refLink.href = '#';
return $('.js-project-refs-dropdown').each(function() {
var $dropdown, selected;
$dropdown = $(this);
@ -67,7 +71,8 @@
return $.ajax({
url: $dropdown.data('refs-url'),
data: {
ref: $dropdown.data('ref')
ref: $dropdown.data('ref'),
search: term
},
dataType: "json"
}).done(function(refs) {
@ -76,16 +81,29 @@
},
selectable: true,
filterable: true,
filterRemote: true,
filterByText: true,
fieldName: $dropdown.data('field-name'),
renderRow: function(ref) {
var link;
var li = refListItem.cloneNode(false);
if (ref.header != null) {
return $('<li />').addClass('dropdown-header').text(ref.header);
li.className = 'dropdown-header';
li.textContent = ref.header;
} else {
link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', ref);
return $('<li />').append(link);
var link = refLink.cloneNode(false);
if (ref === selected) {
link.className = 'is-active';
}
link.textContent = ref;
link.dataset.ref = ref;
li.appendChild(link);
}
return li;
},
id: function(obj, $el) {
return $el.attr('data-ref');
@ -99,7 +117,7 @@
var $form = $dropdown.closest('form');
var action = $form.attr('action');
var divider = action.indexOf('?') < 0 ? '?' : '&';
Turbolinks.visit(action + '' + divider + '' + $form.serialize());
gl.utils.visitUrl(action + '' + divider + '' + $form.serialize());
}
}
});

View File

@ -1,11 +1,10 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, max-len */
/* global Turbolinks */
(function() {
this.ProjectImport = (function() {
function ProjectImport() {
setTimeout(function() {
return Turbolinks.visit(location.href);
return gl.utils.visitUrl(location.href);
}, 5000);
}

View File

@ -49,7 +49,7 @@ class ProtectedBranchDropdown {
onClickCreateWildcard() {
// Refresh the dropdown's data, which ends up calling `getProtectedBranches`
this.$dropdown.data('glDropdown').remote.execute();
this.$dropdown.data('glDropdown').selectRowAtIndex(0);
this.$dropdown.data('glDropdown').selectRowAtIndex();
}
getProtectedBranches(term, callback) {

View File

@ -1 +1,3 @@
/*= require_tree . */
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!protected_branches_bundle).*\.(js|es6)$/));

View File

@ -9,7 +9,7 @@
this.find('.js-render-math').renderMath();
};
$(document).on('ready page:load', function() {
$(document).on('ready load', function() {
return $('body').renderGFM();
});
}).call(this);

View File

@ -1,6 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, prefer-arrow-callback, consistent-return, object-shorthand, no-unused-vars, one-var, one-var-declaration-per-line, no-else-return, comma-dangle, max-len */
/* global Mousetrap */
/* global Turbolinks */
/* global findFileURL */
(function() {
@ -23,7 +22,7 @@
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], this.toggleMarkdownPreview);
if (typeof findFileURL !== "undefined" && findFileURL !== null) {
Mousetrap.bind('t', function() {
return Turbolinks.visit(findFileURL);
return gl.utils.visitUrl(findFileURL);
});
}
}

Some files were not shown because too many files have changed in this diff Show More