summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/images/icon-merge-request-unmerged.svg1
-rw-r--r--app/assets/images/mailers/gitlab_footer_logo.gifbin0 -> 3654 bytes
-rw-r--r--app/assets/images/mailers/gitlab_header_logo.gifbin0 -> 3040 bytes
-rw-r--r--app/assets/javascripts/ajax_loading_spinner.js35
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js.es662
-rw-r--r--app/assets/javascripts/create_label.js.es66
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es618
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js.es618
-rw-r--r--app/assets/javascripts/dispatcher.js.es65
-rw-r--r--app/assets/javascripts/dropzone_input.js5
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.js.es638
-rw-r--r--app/assets/javascripts/environments/components/environment_item.js.es625
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js.es629
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_bundle.js12
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es627
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es66
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js.es615
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es65
-rw-r--r--app/assets/javascripts/gl_dropdown.js3
-rw-r--r--app/assets/javascripts/graphs/graphs_bundle.js7
-rw-r--r--app/assets/javascripts/header.js2
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js3
-rw-r--r--app/assets/javascripts/network/network_bundle.js5
-rw-r--r--app/assets/javascripts/search_autocomplete.js.es64
-rw-r--r--app/assets/javascripts/version_check_image.js.es616
-rw-r--r--app/assets/javascripts/vue_pipelines_index/pipelines.js.es62
-rw-r--r--app/assets/stylesheets/framework/header.scss26
-rw-r--r--app/assets/stylesheets/framework/lists.scss10
-rw-r--r--app/assets/stylesheets/framework/mixins.scss7
-rw-r--r--app/assets/stylesheets/framework/nav.scss10
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss16
-rw-r--r--app/assets/stylesheets/framework/typography.scss25
-rw-r--r--app/assets/stylesheets/pages/environments.scss18
-rw-r--r--app/assets/stylesheets/pages/issues.scss5
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss5
-rw-r--r--app/assets/stylesheets/pages/projects.scss37
-rw-r--r--app/assets/stylesheets/pages/todos.scss46
37 files changed, 380 insertions, 174 deletions
diff --git a/app/assets/images/icon-merge-request-unmerged.svg b/app/assets/images/icon-merge-request-unmerged.svg
new file mode 100644
index 00000000000..c4d8e65122d
--- /dev/null
+++ b/app/assets/images/icon-merge-request-unmerged.svg
@@ -0,0 +1 @@
+<svg width="12" height="15" viewBox="0 0 12 15" xmlns="http://www.w3.org/2000/svg"><path d="M10.267 11.028V5.167c-.028-.728-.318-1.372-.878-1.923-.56-.55-1.194-.85-1.922-.877h-.934V.5l-2.8 2.8 2.8 2.8V4.233h.934a.976.976 0 0 1 .644.29.88.88 0 0 1 .289.644v5.861a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472zM3.733 3.3a1.86 1.86 0 0 0-1.866-1.867 1.86 1.86 0 0 0-.934 3.472v6.123a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472V4.905c.55-.317.933-.914.933-1.605z" fill-rule="nonzero"/></svg>
diff --git a/app/assets/images/mailers/gitlab_footer_logo.gif b/app/assets/images/mailers/gitlab_footer_logo.gif
new file mode 100644
index 00000000000..3f4ef31947b
--- /dev/null
+++ b/app/assets/images/mailers/gitlab_footer_logo.gif
Binary files differ
diff --git a/app/assets/images/mailers/gitlab_header_logo.gif b/app/assets/images/mailers/gitlab_header_logo.gif
new file mode 100644
index 00000000000..387628f831c
--- /dev/null
+++ b/app/assets/images/mailers/gitlab_header_logo.gif
Binary files differ
diff --git a/app/assets/javascripts/ajax_loading_spinner.js b/app/assets/javascripts/ajax_loading_spinner.js
new file mode 100644
index 00000000000..38a8317dbd7
--- /dev/null
+++ b/app/assets/javascripts/ajax_loading_spinner.js
@@ -0,0 +1,35 @@
+class AjaxLoadingSpinner {
+ static init() {
+ const $elements = $('.js-ajax-loading-spinner');
+
+ $elements.on('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
+ $elements.on('ajax:complete', AjaxLoadingSpinner.ajaxComplete);
+ }
+
+ static ajaxBeforeSend(e) {
+ e.target.setAttribute('disabled', '');
+ const iconElement = e.target.querySelector('i');
+ // get first fa- icon
+ const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g).first();
+ iconElement.dataset.icon = originalIcon;
+ AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
+ $(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
+ }
+
+ static ajaxComplete(e) {
+ e.target.removeAttribute('disabled');
+ const iconElement = e.target.querySelector('i');
+ AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
+ $(e.target).off('ajax:complete', AjaxLoadingSpinner.ajaxComplete);
+ }
+
+ static toggleLoadingIcon(iconElement) {
+ const classList = iconElement.classList;
+ classList.toggle(iconElement.dataset.icon);
+ classList.toggle('fa-spinner');
+ classList.toggle('fa-spin');
+ }
+}
+
+window.gl = window.gl || {};
+gl.AjaxLoadingSpinner = AjaxLoadingSpinner;
diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6
index 878ad1b6031..55d13be6e5f 100644
--- a/app/assets/javascripts/boards/boards_bundle.js.es6
+++ b/app/assets/javascripts/boards/boards_bundle.js.es6
@@ -1,16 +1,20 @@
-/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren, import/newline-after-import, no-multi-spaces, max-len */
+/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */
/* global Vue */
/* global BoardService */
-function requireAll(context) { return context.keys().map(context); }
-
window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
-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('./models/issue');
+require('./models/label');
+require('./models/list');
+require('./models/milestone');
+require('./models/user');
+require('./stores/boards_store');
+require('./stores/modal_store');
+require('./services/board_service');
+require('./mixins/modal_mixins');
+require('./mixins/sortable_default_options');
+require('./filters/due_date_filters');
require('./components/board');
require('./components/board_sidebar');
require('./components/new_list_dropdown');
@@ -93,17 +97,53 @@ $(() => {
modal: ModalStore.store,
store: Store.state,
},
+ watch: {
+ disabled() {
+ this.updateTooltip();
+ },
+ },
computed: {
disabled() {
return !this.store.lists.filter(list => list.type !== 'blank' && list.type !== 'done').length;
},
+ tooltipTitle() {
+ if (this.disabled) {
+ return 'Please add a list to your board first';
+ }
+
+ return '';
+ },
+ },
+ methods: {
+ updateTooltip() {
+ const $tooltip = $(this.$el);
+
+ this.$nextTick(() => {
+ if (this.disabled) {
+ $tooltip.tooltip();
+ } else {
+ $tooltip.tooltip('destroy');
+ }
+ });
+ },
+ openModal() {
+ if (!this.disabled) {
+ this.toggleModal(true);
+ }
+ },
+ },
+ mounted() {
+ this.updateTooltip();
},
template: `
<button
- class="btn btn-create pull-right prepend-left-10 has-tooltip"
+ class="btn btn-create pull-right prepend-left-10"
type="button"
- :disabled="disabled"
- @click="toggleModal(true)">
+ data-placement="bottom"
+ :class="{ 'disabled': disabled }"
+ :title="tooltipTitle"
+ :aria-disabled="disabled"
+ @click="openModal">
Add issues
</button>
`,
diff --git a/app/assets/javascripts/create_label.js.es6 b/app/assets/javascripts/create_label.js.es6
index 947c129d5b5..85384d98126 100644
--- a/app/assets/javascripts/create_label.js.es6
+++ b/app/assets/javascripts/create_label.js.es6
@@ -107,9 +107,9 @@
if (typeof label.message === 'string') {
errors = label.message;
} else {
- errors = label.message.map(function (value, key) {
- return key + " " + value[0];
- }).join("<br/>");
+ errors = Object.keys(label.message).map(key =>
+ `${gl.text.humanize(key)} ${label.message[key].join(', ')}`
+ ).join("<br/>");
}
this.$newLabelError
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6 b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
index 1ac715aab77..411ac7b24b2 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js.es6
@@ -4,10 +4,20 @@
window.Vue = require('vue');
window.Cookies = require('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)$/));
+require('./svg/icon_branch');
+require('./svg/icon_build_status');
+require('./svg/icon_commit');
+require('./components/stage_code_component');
+require('./components/stage_issue_component');
+require('./components/stage_plan_component');
+require('./components/stage_production_component');
+require('./components/stage_review_component');
+require('./components/stage_staging_component');
+require('./components/stage_test_component');
+require('./components/total_time_component');
+require('./cycle_analytics_service');
+require('./cycle_analytics_store');
+require('./default_event_objects');
$(() => {
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6 b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
index 190461451d5..cadf8b96b87 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js.es6
@@ -1,14 +1,18 @@
-/* eslint-disable func-names, comma-dangle, new-cap, no-new, import/newline-after-import, no-multi-spaces, max-len */
+/* eslint-disable func-names, comma-dangle, new-cap, no-new, max-len */
/* global Vue */
/* global ResolveCount */
-function requireAll(context) { return context.keys().map(context); }
const Vue = require('vue');
-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)$/));
+require('./models/discussion');
+require('./models/note');
+require('./stores/comments');
+require('./services/resolve');
+require('./mixins/discussion');
+require('./components/comment_resolve_btn');
+require('./components/jump_to_discussion');
+require('./components/resolve_btn');
+require('./components/resolve_count');
+require('./components/resolve_discussion_btn');
$(() => {
const projectPath = document.querySelector('.merge-request').dataset.projectPath;
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index 45aa6050aed..089ecedeb78 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -74,7 +74,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:merge_requests:index':
case 'projects:issues:index':
if (gl.FilteredSearchManager) {
- new gl.FilteredSearchManager();
+ new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests');
}
Issuable.init();
new gl.IssuableBulkActions({
@@ -108,6 +108,9 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:compare:show':
new gl.Diff();
break;
+ case 'projects:branches:index':
+ gl.AjaxLoadingSpinner.init();
+ break;
case 'projects:issues:new':
case 'projects:issues:edit':
shortcut_handler = new ShortcutsNavigation();
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 64a7a9eaf37..646f836aff0 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -126,13 +126,14 @@ require('./preview_markdown');
};
pasteText = function(text) {
var afterSelection, beforeSelection, caretEnd, caretStart, textEnd;
+ var formattedText = text + "\n\n";
caretStart = $(child)[0].selectionStart;
caretEnd = $(child)[0].selectionEnd;
textEnd = $(child).val().length;
beforeSelection = $(child).val().substring(0, caretStart);
afterSelection = $(child).val().substring(caretEnd, textEnd);
- $(child).val(beforeSelection + text + afterSelection);
- child.get(0).setSelectionRange(caretStart + text.length, caretEnd + text.length);
+ $(child).val(beforeSelection + formattedText + afterSelection);
+ child.get(0).setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length);
return form_textarea.trigger("input");
};
getFilename = function(e) {
diff --git a/app/assets/javascripts/environments/components/environment_actions.js.es6 b/app/assets/javascripts/environments/components/environment_actions.js.es6
index c5a714d9673..978d4dd8b6b 100644
--- a/app/assets/javascripts/environments/components/environment_actions.js.es6
+++ b/app/assets/javascripts/environments/components/environment_actions.js.es6
@@ -15,29 +15,29 @@ module.exports = Vue.component('actions-component', {
},
template: `
- <div class="inline">
- <div class="dropdown">
- <a class="dropdown-new btn btn-default" data-toggle="dropdown">
+ <div class="btn-group" role="group">
+ <button class="dropdown btn btn-default dropdown-new" data-toggle="dropdown">
+ <span>
<span class="js-dropdown-play-icon-container" v-html="playIconSvg"></span>
<i class="fa fa-caret-down"></i>
- </a>
+ </span>
- <ul class="dropdown-menu dropdown-menu-align-right">
- <li v-for="action in actions">
- <a :href="action.play_path"
- data-method="post"
- rel="nofollow"
- class="js-manual-action-link">
+ <ul class="dropdown-menu dropdown-menu-align-right">
+ <li v-for="action in actions">
+ <a :href="action.play_path"
+ data-method="post"
+ rel="nofollow"
+ class="js-manual-action-link">
- <span class="js-action-play-icon-container" v-html="playIconSvg"></span>
+ <span class="js-action-play-icon-container" v-html="playIconSvg"></span>
- <span>
- {{action.name}}
- </span>
- </a>
- </li>
- </ul>
- </div>
- </div>
+ <span>
+ {{action.name}}
+ </span>
+ </a>
+ </li>
+ </ul>
+ </button>
+ </div>
`,
});
diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6
index 24fd58a301a..ad9d1d21a79 100644
--- a/app/assets/javascripts/environments/components/environment_item.js.es6
+++ b/app/assets/javascripts/environments/components/environment_item.js.es6
@@ -505,39 +505,26 @@ module.exports = Vue.component('environment-item', {
<td class="hidden-xs">
<div v-if="!model.isFolder">
- <div v-if="hasManualActions && canCreateDeployment"
- class="inline js-manual-actions-container">
- <actions-component
+ <div class="btn-group" role="group">
+ <actions-component v-if="hasManualActions && canCreateDeployment"
:play-icon-svg="playIconSvg"
:actions="manualActions">
</actions-component>
- </div>
- <div v-if="externalURL && canReadEnvironment"
- class="inline js-external-url-container">
- <external-url-component
+ <external-url-component v-if="externalURL && canReadEnvironment"
:external-url="externalURL">
</external-url-component>
- </div>
- <div v-if="hasStopAction && canCreateDeployment"
- class="inline js-stop-component-container">
- <stop-component
+ <stop-component v-if="hasStopAction && canCreateDeployment"
:stop-url="model.stop_path">
</stop-component>
- </div>
- <div v-if="model && model.terminal_path"
- class="inline js-terminal-button-container">
- <terminal-button-component
+ <terminal-button-component v-if="model && model.terminal_path"
:terminal-icon-svg="terminalIconSvg"
:terminal-path="model.terminal_path">
</terminal-button-component>
- </div>
- <div v-if="canRetry && canCreateDeployment"
- class="inline js-rollback-component-container">
- <rollback-component
+ <rollback-component v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment"
:retry-url="retryUrl">
</rollback-component>
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js.es6 b/app/assets/javascripts/filtered_search/dropdown_hint.js.es6
index 572c221929a..9e92d544bef 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js.es6
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js.es6
@@ -37,23 +37,18 @@ require('./filtered_search_dropdown');
}
renderContent() {
- const dropdownData = [{
- icon: 'fa-pencil',
- hint: 'author:',
- tag: '&lt;@author&gt;',
- }, {
- icon: 'fa-user',
- hint: 'assignee:',
- tag: '&lt;@assignee&gt;',
- }, {
- icon: 'fa-clock-o',
- hint: 'milestone:',
- tag: '&lt;%milestone&gt;',
- }, {
- icon: 'fa-tag',
- hint: 'label:',
- tag: '&lt;~label&gt;',
- }];
+ const dropdownData = [];
+
+ [].forEach.call(this.input.parentElement.querySelectorAll('.dropdown-menu'), (dropdownMenu) => {
+ const { icon, hint, tag } = dropdownMenu.dataset;
+ if (icon && hint && tag) {
+ dropdownData.push({
+ icon: `fa-${icon}`,
+ hint,
+ tag: `&lt;${tag}&gt;`,
+ });
+ }
+ });
this.droplab.changeHookList(this.hookId, this.dropdown, [droplabFilter], this.config);
this.droplab.setData(this.hookId, dropdownData);
diff --git a/app/assets/javascripts/filtered_search/filtered_search_bundle.js b/app/assets/javascripts/filtered_search/filtered_search_bundle.js
index 392f1835966..faaba994f46 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_bundle.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_bundle.js
@@ -1,3 +1,9 @@
-function requireAll(context) { return context.keys().map(context); }
-
-requireAll(require.context('./', true, /^\.\/(?!filtered_search_bundle).*\.(js|es6)$/));
+require('./dropdown_hint');
+require('./dropdown_non_user');
+require('./dropdown_user');
+require('./dropdown_utils');
+require('./filtered_search_dropdown_manager');
+require('./filtered_search_dropdown');
+require('./filtered_search_manager');
+require('./filtered_search_token_keys');
+require('./filtered_search_tokenizer');
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6
index e8c2df03a46..fbc72a3001a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6
@@ -52,8 +52,9 @@
}
renderContent(forceShowList = false) {
- if (forceShowList && this.getCurrentHook().list.hidden) {
- this.getCurrentHook().list.show();
+ const currentHook = this.getCurrentHook();
+ if (forceShowList && currentHook && currentHook.list.hidden) {
+ currentHook.list.show();
}
}
@@ -92,18 +93,24 @@
}
hideDropdown() {
- this.getCurrentHook().list.hide();
+ const currentHook = this.getCurrentHook();
+ if (currentHook) {
+ currentHook.list.hide();
+ }
}
resetFilters() {
const hook = this.getCurrentHook();
- const data = hook.list.data;
- const results = data.map((o) => {
- const updated = o;
- updated.droplab_hidden = false;
- return updated;
- });
- hook.list.render(results);
+
+ if (hook) {
+ const data = hook.list.data;
+ const results = data.map((o) => {
+ const updated = o;
+ updated.droplab_hidden = false;
+ return updated;
+ });
+ hook.list.render(results);
+ }
}
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
index 8ce4cf4fc36..cecd3518ce3 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
@@ -2,10 +2,12 @@
(() => {
class FilteredSearchDropdownManager {
- constructor(baseEndpoint = '') {
+ constructor(baseEndpoint = '', page) {
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = gl.FilteredSearchTokenizer;
+ this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
this.filteredSearchInput = document.querySelector('.filtered-search');
+ this.page = page;
this.setupMapping();
@@ -150,7 +152,7 @@
this.droplab = new DropLab();
}
- const match = gl.FilteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
+ const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
&& this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
index ffc7d29e4c5..bbafead0305 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
@@ -1,12 +1,13 @@
(() => {
class FilteredSearchManager {
- constructor() {
+ constructor(page) {
this.filteredSearchInput = document.querySelector('.filtered-search');
this.clearSearchButton = document.querySelector('.clear-search');
+ this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
if (this.filteredSearchInput) {
this.tokenizer = gl.FilteredSearchTokenizer;
- this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '');
+ this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', page);
this.bindEvents();
this.loadSearchParamsFromURL();
@@ -117,8 +118,8 @@
const keyParam = decodeURIComponent(split[0]);
const value = split[1];
- // Check if it matches edge conditions listed in gl.FilteredSearchTokenKeys
- const condition = gl.FilteredSearchTokenKeys.searchByConditionUrl(p);
+ // Check if it matches edge conditions listed in this.filteredSearchTokenKeys
+ const condition = this.filteredSearchTokenKeys.searchByConditionUrl(p);
if (condition) {
inputValues.push(`${condition.tokenKey}:${condition.value}`);
@@ -126,7 +127,7 @@
// Sanitize value since URL converts spaces into +
// Replace before decode so that we know what was originally + versus the encoded +
const sanitizedValue = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : value;
- const match = gl.FilteredSearchTokenKeys.searchByKeyParam(keyParam);
+ const match = this.filteredSearchTokenKeys.searchByKeyParam(keyParam);
if (match) {
const indexOf = keyParam.indexOf('_');
@@ -171,9 +172,9 @@
paths.push(`state=${currentState}`);
tokens.forEach((token) => {
- const condition = gl.FilteredSearchTokenKeys
+ const condition = this.filteredSearchTokenKeys
.searchByConditionKeyValue(token.key, token.value.toLowerCase());
- const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key);
+ const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
const keyParam = param ? `${token.key}_${param}` : token.key;
let tokenPath = '';
diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6
index cf53845a48b..9bf1b1ced88 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6
@@ -1,9 +1,12 @@
+require('./filtered_search_token_keys');
+
(() => {
class FilteredSearchTokenizer {
static processTokens(input) {
+ const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
// Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single)
- const tokenRegex = /(\w+):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\S+))/g;
+ const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
const tokens = [];
let lastToken = null;
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index bf3da8528f0..a01662e2f9e 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -47,9 +47,10 @@
}
// Only filter asynchronously only if option remote is set
if (this.options.remote) {
- $inputContainer.parent().addClass('is-loading');
clearTimeout(timeout);
return timeout = setTimeout(function() {
+ $inputContainer.parent().addClass('is-loading');
+
return this.options.query(this.input.val(), function(data) {
$inputContainer.parent().removeClass('is-loading');
return this.options.callback(data);
diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js
index 4f7777aa5bc..086dcb34571 100644
--- a/app/assets/javascripts/graphs/graphs_bundle.js
+++ b/app/assets/javascripts/graphs/graphs_bundle.js
@@ -1,3 +1,4 @@
-// require everything else in this directory
-function requireAll(context) { return context.keys().map(context); }
-requireAll(require.context('.', false, /^\.\/(?!graphs_bundle).*\.(js|es6)$/));
+require('./stat_graph_contributors_graph');
+require('./stat_graph_contributors_util');
+require('./stat_graph_contributors');
+require('./stat_graph');
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index fa85f9a6c86..a853c3aeb1f 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -2,7 +2,7 @@
(function() {
$(document).on('todo:toggle', function(e, count) {
var $todoPendingCount = $('.todos-pending-count');
- $todoPendingCount.text(gl.text.addDelimiter(count));
+ $todoPendingCount.text(gl.text.highCountTrim(count));
$todoPendingCount.toggleClass('hidden', count === 0);
});
})();
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index f755d212b3c..579d322e3fb 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -14,6 +14,9 @@ require('vendor/latinise');
gl.text.addDelimiter = function(text) {
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
};
+ gl.text.highCountTrim = function(count) {
+ return count > 99 ? '99+' : count;
+ };
gl.text.randomString = function() {
return Math.random().toString(36).substring(7);
};
diff --git a/app/assets/javascripts/network/network_bundle.js b/app/assets/javascripts/network/network_bundle.js
index aae509caa79..e5947586583 100644
--- a/app/assets/javascripts/network/network_bundle.js
+++ b/app/assets/javascripts/network/network_bundle.js
@@ -2,9 +2,8 @@
/* global Network */
/* global ShortcutsNetwork */
-// require everything else in this directory
-function requireAll(context) { return context.keys().map(context); }
-requireAll(require.context('.', false, /^\.\/(?!network_bundle).*\.(js|es6)$/));
+require('./branch_graph');
+require('./network');
(function() {
$(function() {
diff --git a/app/assets/javascripts/search_autocomplete.js.es6 b/app/assets/javascripts/search_autocomplete.js.es6
index 6250e75d407..6fd5345a0a6 100644
--- a/app/assets/javascripts/search_autocomplete.js.es6
+++ b/app/assets/javascripts/search_autocomplete.js.es6
@@ -169,10 +169,10 @@
url: issuesPath + "/?author_username=" + userName
}, 'separator', {
text: 'Merge requests assigned to me',
- url: mrPath + "/?assignee_id=" + userId
+ url: mrPath + "/?assignee_username=" + userName
}, {
text: "Merge requests I've created",
- url: mrPath + "/?author_id=" + userId
+ url: mrPath + "/?author_username=" + userName
}
];
if (!name) {
diff --git a/app/assets/javascripts/version_check_image.js.es6 b/app/assets/javascripts/version_check_image.js.es6
index 1fa2b5ac399..d4f716acb72 100644
--- a/app/assets/javascripts/version_check_image.js.es6
+++ b/app/assets/javascripts/version_check_image.js.es6
@@ -1,10 +1,10 @@
-(() => {
- class VersionCheckImage {
- static bindErrorEvent(imageElement) {
- imageElement.off('error').on('error', () => imageElement.hide());
- }
+class VersionCheckImage {
+ static bindErrorEvent(imageElement) {
+ imageElement.off('error').on('error', () => imageElement.hide());
}
+}
- window.gl = window.gl || {};
- gl.VersionCheckImage = VersionCheckImage;
-})();
+window.gl = window.gl || {};
+gl.VersionCheckImage = VersionCheckImage;
+
+module.exports = VersionCheckImage;
diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6
index 0265c00a414..9d66d28cc62 100644
--- a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6
+++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6
@@ -23,7 +23,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
apiScope: 'all',
pageInfo: {},
pagenum: 1,
- count: { all: 0, running_or_pending: 0 },
+ count: {},
pageRequest: false,
};
},
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 78434b99b62..3945a789c82 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -148,11 +148,16 @@ header {
}
.header-logo {
- display: inline-block;
- margin: 0 8px 0 3px;
- position: relative;
+ position: absolute;
+ left: 50%;
top: 7px;
transition-duration: .3s;
+ z-index: 999;
+
+ #logo {
+ position: relative;
+ left: -50%;
+ }
svg,
img {
@@ -162,6 +167,15 @@ header {
&:hover {
cursor: pointer;
}
+
+ @media (max-width: $screen-xs-max) {
+ right: 20px;
+ left: auto;
+
+ #logo {
+ left: auto;
+ }
+ }
}
.title {
@@ -169,7 +183,7 @@ header {
padding-right: 20px;
margin: 0;
font-size: 18px;
- max-width: 450px;
+ max-width: 385px;
display: inline-block;
line-height: $header-height;
font-weight: normal;
@@ -179,6 +193,10 @@ header {
vertical-align: top;
white-space: nowrap;
+ @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
+ max-width: 300px;
+ }
+
@media (max-width: $screen-xs-max) {
max-width: 190px;
}
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 2bfdb9f9601..55ed4b7b06c 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -96,16 +96,6 @@ ul.unstyled-list > li {
border-bottom: none;
}
-ul.task-list {
- li.task-list-item {
- list-style-type: none;
- }
-
- ul:not(.task-list) {
- padding-left: 1.3em;
- }
-}
-
// Generic content list
ul.content-list {
@include basic-list;
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 1acd06122a3..df78bbdea51 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -76,6 +76,13 @@
#{$property}: $value;
}
+/* http://phrappe.com/css/conditional-css-for-webkit-based-browsers/ */
+@mixin on-webkit-only {
+ @media screen and (-webkit-min-device-pixel-ratio:0) {
+ @content;
+ }
+}
+
@mixin keyframes($animation-name) {
@-webkit-keyframes #{$animation-name} {
@content;
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 7d4a814a36c..674d3bb45aa 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -1,7 +1,7 @@
@mixin fade($gradient-direction, $gradient-color) {
visibility: hidden;
opacity: 0;
- z-index: 1;
+ z-index: 2;
position: absolute;
bottom: 12px;
width: 43px;
@@ -18,7 +18,7 @@
.fa {
position: relative;
- top: 6px;
+ top: 5px;
font-size: 18px;
}
}
@@ -79,6 +79,7 @@
}
&.sub-nav {
+ text-align: center;
background-color: $gray-normal;
.container-fluid {
@@ -286,6 +287,7 @@
background: $gray-light;
border-bottom: 1px solid $border-color;
transition: padding $sidebar-transition-duration;
+ text-align: center;
.container-fluid {
position: relative;
@@ -351,7 +353,7 @@
right: -5px;
.fa {
- right: -28px;
+ right: -7px;
}
}
@@ -381,7 +383,7 @@
left: 0;
.fa {
- left: -4px;
+ left: 10px;
}
}
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index d09b1c9d7f5..040a7ce0c16 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -29,14 +29,16 @@
}
}
+@media (min-width: $screen-sm-min) {
+ .content-wrapper {
+ padding-right: $gutter_collapsed_width;
+ }
+}
+
.right-sidebar-collapsed {
padding-right: 0;
@media (min-width: $screen-sm-min) {
- .content-wrapper {
- padding-right: $gutter_collapsed_width;
- }
-
.merge-request-tabs-holder.affix {
right: $gutter_collapsed_width;
}
@@ -54,12 +56,6 @@
.right-sidebar-expanded {
padding-right: 0;
- @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
- &:not(.build-sidebar):not(.wiki-sidebar) {
- padding-right: $gutter_collapsed_width;
- }
- }
-
@media (min-width: $screen-md-min) {
.content-wrapper {
padding-right: $gutter_width;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 54958973f15..db5e2c51fe7 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -134,7 +134,7 @@
ul,
ol {
padding: 0;
- margin: 3px 0 3px 28px !important;
+ margin: 3px 0 !important;
}
ul:dir(rtl),
@@ -144,6 +144,29 @@
li {
line-height: 1.6em;
+ margin-left: 25px;
+ padding-left: 3px;
+
+ /* Normalize the bullet position on webkit. */
+ @include on-webkit-only {
+ margin-left: 28px;
+ padding-left: 0;
+ }
+ }
+
+ ul.task-list {
+ li.task-list-item {
+ list-style-type: none;
+ position: relative;
+ padding-left: 28px;
+ margin-left: 0 !important;
+
+ input.task-list-item-checkbox {
+ position: absolute;
+ left: 8px;
+ top: 5px;
+ }
+ }
}
a[href*="/uploads/"],
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 181dcb7721f..f789ae1ccd3 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -35,7 +35,6 @@
display: table-cell;
}
- .environments-name,
.environments-commit,
.environments-actions {
width: 20%;
@@ -45,6 +44,7 @@
width: 10%;
}
+ .environments-name,
.environments-deploy,
.environments-build {
width: 15%;
@@ -62,6 +62,22 @@
}
}
+ .btn-group {
+
+ > a {
+ color: $gl-text-color-secondary;
+ }
+
+ svg path {
+ fill: $gl-text-color-secondary;
+ }
+
+ .dropdown {
+ outline: none;
+ }
+ }
+
+
.commit-title {
margin: 0;
}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 80b0c9493d8..b595480561b 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -10,6 +10,11 @@
.issue-labels {
display: inline-block;
}
+
+ .icon-merge-request-unmerged {
+ height: 13px;
+ margin-bottom: 3px;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 00eb5b30fd5..3fe1eef307e 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -222,6 +222,11 @@
}
}
+ .dropdown-menu {
+ max-height: 250px;
+ overflow-y: auto;
+ }
+
.dropdown-toggle,
.dropdown-menu {
color: $gl-text-color-secondary;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index adbdc5214a4..67110813abb 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -268,6 +268,13 @@
}
}
+.project-repo-buttons {
+ .project-action-button .dropdown-menu {
+ max-height: 250px;
+ overflow-y: auto;
+ }
+}
+
.split-one {
display: inline-table;
margin-right: 12px;
@@ -645,23 +652,29 @@ pre.light-well {
}
}
-.container-fluid.project-stats-container {
- @media (max-width: $screen-xs-max) {
- padding: 12px 0;
- }
-}
-
.project-last-commit {
- background-color: $gray-light;
- padding: 12px $gl-padding;
- border: 1px solid $border-color;
-
@media (min-width: $screen-sm-min) {
margin-top: $gl-padding;
}
- @media (min-width: $screen-sm-min) {
- border-radius: $border-radius-base;
+ &.container-fluid {
+ padding-top: 12px;
+ padding-bottom: 12px;
+ background-color: $gray-light;
+ border: 1px solid $border-color;
+ border-right-width: 0;
+ border-left-width: 0;
+
+ @media (min-width: $screen-sm-min) {
+ border-right-width: 1px;
+ border-left-width: 1px;
+ }
+ }
+
+ &.container-limited {
+ @media (min-width: 1281px) {
+ border-radius: $border-radius-base;
+ }
}
.ci-status {
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 551a66fbf3a..af9ddb9ff80 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -6,6 +6,8 @@
.navbar-nav {
li {
.badge.todos-pending-count {
+ position: inherit;
+ top: -6px;
margin-top: -5px;
font-weight: normal;
background: $todo-alert-blue;
@@ -43,6 +45,12 @@
}
}
+ .todo-avatar,
+ .todo-actions {
+ -webkit-flex: 0 0 auto;
+ flex: 0 0 auto;
+ }
+
.todo-actions {
display: -webkit-flex;
display: flex;
@@ -55,8 +63,9 @@
}
.todo-item {
- -webkit-flex: auto;
- flex: auto;
+ -webkit-flex: 0 1 100%;
+ flex: 0 1 100%;
+ min-width: 0;
}
}
@@ -74,8 +83,29 @@
.todo-item {
.todo-title {
- @include str-truncated(calc(100% - 174px));
- overflow: visible;
+ display: flex;
+
+ & > .title-item {
+ -webkit-flex: 0 0 auto;
+ flex: 0 0 auto;
+ margin: 0 2px;
+
+ &:first-child {
+ margin-left: 0;
+ }
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+
+ .todo-label {
+ -webkit-flex: 0 1 auto;
+ flex: 0 1 auto;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
}
.status-box {
@@ -154,10 +184,12 @@
.todo-item {
.todo-title {
- white-space: normal;
- overflow: visible;
- max-width: 100%;
+ flex-flow: row wrap;
margin-bottom: 10px;
+
+ .todo-label {
+ white-space: normal;
+ }
}
.todo-body {