diff options
Diffstat (limited to 'app/assets/javascripts')
32 files changed, 332 insertions, 171 deletions
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f0615481ed2..4849aab50f4 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -84,7 +84,6 @@ var $sidebarGutterToggle = $('.js-sidebar-toggle'); var $flash = $('.flash-container'); var bootstrapBreakpoint = bp.getBreakpointSize(); - var checkInitialSidebarSize; var fitSidebarForSize; // Set the default path for all cookies to GitLab's root directory @@ -246,19 +245,11 @@ return $document.trigger('breakpoint:change', [bootstrapBreakpoint]); } }; - checkInitialSidebarSize = function () { - bootstrapBreakpoint = bp.getBreakpointSize(); - if (bootstrapBreakpoint === 'xs' || 'sm') { - return $document.trigger('breakpoint:change', [bootstrapBreakpoint]); - } - }; $window.off('resize.app').on('resize.app', function () { return fitSidebarForSize(); }); gl.awardsHandler = new AwardsHandler(); - checkInitialSidebarSize(); new Aside(); - // bind sidebar events new gl.Sidebar(); }); diff --git a/app/assets/javascripts/behaviors/autosize.js b/app/assets/javascripts/behaviors/autosize.js index a6bc262b657..7e6c44fa1cd 100644 --- a/app/assets/javascripts/behaviors/autosize.js +++ b/app/assets/javascripts/behaviors/autosize.js @@ -1,7 +1,6 @@ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, consistent-return, max-len */ /* global autosize */ -/*= require jquery.ba-resize */ /*= require autosize */ (function() { diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js index 6a49715590c..a7181904ac9 100644 --- a/app/assets/javascripts/behaviors/toggler_behavior.js +++ b/app/assets/javascripts/behaviors/toggler_behavior.js @@ -1,6 +1,19 @@ /* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, vars-on-top, no-var, max-len */ (function(w) { $(function() { + var toggleContainer = function(container, /* optional */toggleState) { + var $container = $(container); + + $container + .find('.js-toggle-button .fa') + .toggleClass('fa-chevron-up', toggleState) + .toggleClass('fa-chevron-down', toggleState !== undefined ? !toggleState : undefined); + + $container + .find('.js-toggle-content') + .toggle(toggleState); + }; + // Toggle button. Show/hide content inside parent container. // Button does not change visibility. If button has icon - it changes chevron style. // @@ -10,14 +23,7 @@ // $('body').on('click', '.js-toggle-button', function(e) { e.preventDefault(); - $(this) - .find('.fa') - .toggleClass('fa-chevron-down fa-chevron-up') - .end() - .closest('.js-toggle-container') - .find('.js-toggle-content') - .toggle() - ; + toggleContainer($(this).closest('.js-toggle-container')); }); // If we're accessing a permalink, ensure it is not inside a @@ -26,8 +32,8 @@ var anchor = hash && document.getElementById(hash); var container = anchor && $(anchor).closest('.js-toggle-container'); - if (container && container.find('.js-toggle-content').is(':hidden')) { - container.find('.js-toggle-button').trigger('click'); + if (container) { + toggleContainer(container, true); anchor.scrollIntoView(); } }); diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6 index 02459722bbf..75dfcb66bb0 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js.es6 +++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6 @@ -29,6 +29,12 @@ watch: { detail: { handler () { + if (this.issue.id !== this.detail.issue.id) { + $('.js-issue-board-sidebar', this.$el).each((i, el) => { + $(el).data('glDropdown').clearMenu(); + }); + } + this.issue = this.detail.issue; }, deep: true diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index cabeae74ae3..c6fdfbcaa10 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -3,7 +3,7 @@ (function() { this.CommitsList = (function() { - function CommitsList() {} + var CommitsList = {}; CommitsList.timer = null; @@ -20,6 +20,7 @@ }); this.content = $("#commits-list"); this.searchField = $("#commits-search"); + this.lastSearch = this.searchField.val(); return this.initSearch(); }; @@ -37,6 +38,7 @@ var commitsUrl, form, search; form = $(".commits-search-form"); search = CommitsList.searchField.val(); + if (search === CommitsList.lastSearch) return; commitsUrl = form.attr("action") + '?' + form.serialize(); CommitsList.content.fadeTo('fast', 0.5); return $.ajax({ @@ -47,12 +49,16 @@ return CommitsList.content.fadeTo('fast', 1.0); }, success: function(data) { + CommitsList.lastSearch = search; CommitsList.content.html(data.html); return history.replaceState({ page: commitsUrl // Change url so if user reload a page - search results are saved }, document.title, commitsUrl); }, + error: function() { + CommitsList.lastSearch = null; + }, dataType: "json" }); }; diff --git a/app/assets/javascripts/diff.js.es6 b/app/assets/javascripts/diff.js.es6 index 5e1a4c948aa..35a029194d0 100644 --- a/app/assets/javascripts/diff.js.es6 +++ b/app/assets/javascripts/diff.js.es6 @@ -1,5 +1,7 @@ /* eslint-disable class-methods-use-this */ +//= require lib/utils/url_utility */ + (() => { const UNFOLD_COUNT = 20; @@ -104,11 +106,11 @@ } highlighSelectedLine() { + const hash = gl.utils.getLocationHash(); const $diffFiles = $('.diff-file'); $diffFiles.find('.hll').removeClass('hll'); - if (window.location.hash !== '') { - const hash = window.location.hash.replace('#', ''); + if (hash) { $diffFiles .find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`) .addClass('hll'); diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index 529d476ca4e..edec21e3b63 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -8,7 +8,6 @@ /* global ShortcutsIssuable */ /* global ZenMode */ /* global Milestone */ -/* global GLForm */ /* global IssuableForm */ /* global LabelsSelect */ /* global MilestoneSelect */ @@ -64,17 +63,6 @@ new UsernameValidator(); new ActiveTabMemoizer(); break; - case 'sessions:create': - if (!gon.u2f) break; - window.gl.u2fAuthenticate = new gl.U2FAuthenticate( - $("#js-authenticate-u2f"), - '#js-login-u2f-form', - gon.u2f, - document.querySelector('#js-login-2fa-device'), - document.querySelector('.js-2fa-form'), - ); - window.gl.u2fAuthenticate.start(); - break; case 'projects:boards:show': case 'projects:boards:index': shortcut_handler = new ShortcutsNavigation(); @@ -110,7 +98,7 @@ case 'projects:milestones:edit': new ZenMode(); new gl.DueDateSelectors(); - new GLForm($('.milestone-form')); + new gl.GLForm($('.milestone-form')); break; case 'groups:milestones:new': new ZenMode(); @@ -121,7 +109,7 @@ case 'projects:issues:new': case 'projects:issues:edit': shortcut_handler = new ShortcutsNavigation(); - new GLForm($('.issue-form')); + new gl.GLForm($('.issue-form')); new IssuableForm($('.issue-form')); new LabelsSelect(); new MilestoneSelect(); @@ -131,7 +119,7 @@ case 'projects:merge_requests:edit': new gl.Diff(); shortcut_handler = new ShortcutsNavigation(); - new GLForm($('.merge-request-form')); + new gl.GLForm($('.merge-request-form')); new IssuableForm($('.merge-request-form')); new LabelsSelect(); new MilestoneSelect(); @@ -139,11 +127,11 @@ break; case 'projects:tags:new': new ZenMode(); - new GLForm($('.tag-form')); + new gl.GLForm($('.tag-form')); break; case 'projects:releases:edit': new ZenMode(); - new GLForm($('.release-form')); + new gl.GLForm($('.release-form')); break; case 'projects:merge_requests:show': new gl.Diff(); @@ -280,6 +268,17 @@ break; } switch (path.first()) { + case 'sessions': + case 'omniauth_callbacks': + if (!gon.u2f) break; + gl.u2fAuthenticate = new gl.U2FAuthenticate( + $('#js-authenticate-u2f'), + '#js-login-u2f-form', + gon.u2f, + document.querySelector('#js-login-2fa-device'), + document.querySelector('.js-2fa-form'), + ); + gl.u2fAuthenticate.start(); case 'admin': new Admin(); switch (path[1]) { @@ -332,7 +331,7 @@ new gl.Wikis(); shortcut_handler = new ShortcutsNavigation(); new ZenMode(); - new GLForm($('.wiki-form')); + new gl.GLForm($('.wiki-form')); break; case 'snippets': shortcut_handler = new ShortcutsNavigation(); @@ -357,7 +356,7 @@ } // If we haven't installed a custom shortcut handler, install the default one if (!shortcut_handler) { - return new Shortcuts(); + new Shortcuts(); } }; diff --git a/app/assets/javascripts/droplab/droplab_ajax.js b/app/assets/javascripts/droplab/droplab_ajax.js index f7fed0987a2..c290e1a8355 100644 --- a/app/assets/javascripts/droplab/droplab_ajax.js +++ b/app/assets/javascripts/droplab/droplab_ajax.js @@ -9,6 +9,7 @@ require('../window')(function(w){ w.droplabAjax = { _loadUrlData: function _loadUrlData(url) { + var self = this; return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest; xhr.open('GET', url, true); @@ -16,6 +17,7 @@ require('../window')(function(w){ if(xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { var data = JSON.parse(xhr.responseText); + self.cache[url] = data; return resolve(data); } else { return reject([xhr.responseText, xhr.status]); @@ -26,8 +28,21 @@ require('../window')(function(w){ }); }, + _loadData: function _loadData(data, config, self) { + if (config.loadingTemplate) { + var dataLoadingTemplate = self.hook.list.list.querySelector('[data-loading-template]'); + + if (dataLoadingTemplate) { + dataLoadingTemplate.outerHTML = self.listTemplate; + } + } + + self.hook.list[config.method].call(self.hook.list, data); + }, + init: function init(hook) { var self = this; + self.cache = self.cache || {}; var config = hook.config.droplabAjax; this.hook = hook; @@ -50,22 +65,16 @@ require('../window')(function(w){ dynamicList.outerHTML = loadingTemplate.outerHTML; } - this._loadUrlData(config.endpoint) - .then(function(d) { - if (config.loadingTemplate) { - var dataLoadingTemplate = self.hook.list.list.querySelector('[data-loading-template]'); - - if (dataLoadingTemplate) { - dataLoadingTemplate.outerHTML = self.listTemplate; - } - } - - if (!self.hook.list.hidden) { - self.hook.list[config.method].call(self.hook.list, d); - } - }).catch(function(e) { - throw new droplabAjaxException(e.message || e); - }); + if (self.cache[config.endpoint]) { + self._loadData(self.cache[config.endpoint], config, self); + } else { + this._loadUrlData(config.endpoint) + .then(function(d) { + self._loadData(d, config, self); + }).catch(function(e) { + throw new droplabAjaxException(e.message || e); + }); + } }, destroy: function() { diff --git a/app/assets/javascripts/droplab/droplab_ajax_filter.js b/app/assets/javascripts/droplab/droplab_ajax_filter.js index 86a08d0d01d..b63d73066cb 100644 --- a/app/assets/javascripts/droplab/droplab_ajax_filter.js +++ b/app/assets/javascripts/droplab/droplab_ajax_filter.js @@ -72,32 +72,22 @@ require('../window')(function(w){ var params = config.params || {}; params[config.searchKey] = searchValue; var self = this; - this._loadUrlData(config.endpoint + this.buildParams(params)).then(function(data) { - if (config.loadingTemplate && self.hook.list.data === undefined || - self.hook.list.data.length === 0) { - const dataLoadingTemplate = self.hook.list.list.querySelector('[data-loading-template]'); - - if (dataLoadingTemplate) { - dataLoadingTemplate.outerHTML = self.listTemplate; - } - } - - if (!self.destroyed) { - var hookListChildren = self.hook.list.list.children; - var onlyDynamicList = hookListChildren.length === 1 && hookListChildren[0].hasAttribute('data-dynamic'); - - if (onlyDynamicList && data.length === 0) { - self.hook.list.hide(); - } - - self.hook.list.setData.call(self.hook.list, data); - } - self.notLoading(); - self.hook.list.currentIndex = 0; - }); + self.cache = self.cache || {}; + var url = config.endpoint + this.buildParams(params); + var urlCachedData = self.cache[url]; + + if (urlCachedData) { + self._loadData(urlCachedData, config, self); + } else { + this._loadUrlData(url) + .then(function(data) { + self._loadData(data, config, self); + }); + } }, _loadUrlData: function _loadUrlData(url) { + var self = this; return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest; xhr.open('GET', url, true); @@ -105,6 +95,7 @@ require('../window')(function(w){ if(xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { var data = JSON.parse(xhr.responseText); + self.cache[url] = data; return resolve(data); } else { return reject([xhr.responseText, xhr.status]); @@ -115,6 +106,30 @@ require('../window')(function(w){ }); }, + _loadData: function _loadData(data, config, self) { + if (config.loadingTemplate && self.hook.list.data === undefined || + self.hook.list.data.length === 0) { + const dataLoadingTemplate = self.hook.list.list.querySelector('[data-loading-template]'); + + if (dataLoadingTemplate) { + dataLoadingTemplate.outerHTML = self.listTemplate; + } + } + + if (!self.destroyed) { + var hookListChildren = self.hook.list.list.children; + var onlyDynamicList = hookListChildren.length === 1 && hookListChildren[0].hasAttribute('data-dynamic'); + + if (onlyDynamicList && data.length === 0) { + self.hook.list.hide(); + } + + self.hook.list.setData.call(self.hook.list, data); + } + self.notLoading(); + self.hook.list.currentIndex = 0; + }, + buildParams: function(params) { if (!params) return ''; var paramsArray = Object.keys(params).map(function(param) { diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index fea642467fa..971be04e2d2 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -182,7 +182,7 @@ <th class="environments-deploy">Last deployment</th> <th class="environments-build">Build</th> <th class="environments-commit">Commit</th> - <th class="environments-date">Created</th> + <th class="environments-date">Updated</th> <th class="hidden-xs environments-actions"></th> </tr> </thead> diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js.es6 b/app/assets/javascripts/filtered_search/dropdown_user.js.es6 index 7bf199d9274..162fd6044e5 100644 --- a/app/assets/javascripts/filtered_search/dropdown_user.js.es6 +++ b/app/assets/javascripts/filtered_search/dropdown_user.js.es6 @@ -39,8 +39,15 @@ getSearchInput() { const query = gl.DropdownUtils.getSearchInput(this.input); const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query); + let value = lastToken.value || ''; - return lastToken.value || ''; + // Removes the first character if it is a quotation so that we can search + // with multiple words + if (value[0] === '"' || value[0] === '\'') { + value = value.slice(1); + } + + return value; } init() { diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js.es6 b/app/assets/javascripts/filtered_search/dropdown_utils.js.es6 index eeab10fba17..de3fa116717 100644 --- a/app/assets/javascripts/filtered_search/dropdown_utils.js.es6 +++ b/app/assets/javascripts/filtered_search/dropdown_utils.js.es6 @@ -28,7 +28,12 @@ if (lastToken !== searchToken) { const title = updatedItem.title.toLowerCase(); let value = lastToken.value.toLowerCase(); - value = value.replace(/"(.*?)"/g, str => str.slice(1).slice(0, -1)); + + // Removes the first character if it is a quotation so that we can search + // with multiple words + if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) { + value = value.slice(1); + } // Eg. filterSymbol = ~ for labels const matchWithoutSymbol = lastToken.symbol === filterSymbol && title.indexOf(value) !== -1; @@ -83,8 +88,9 @@ const selectionStart = input.selectionStart; let inputValue = input.value; // Replace all spaces inside quote marks with underscores + // (will continue to match entire string until an end quote is found if any) // This helps with matching the beginning & end of a token:key - inputValue = inputValue.replace(/("(.*?)"|:\s+)/g, str => str.replace(/\s/g, '_')); + inputValue = inputValue.replace(/(('[^']*'{0,1})|("[^"]*"{0,1})|:\s+)/g, str => str.replace(/\s/g, '_')); // Get the right position for the word selected // Regex matches first space 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 8d62324b79f..029564ffc61 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 @@ -196,7 +196,8 @@ }); if (searchToken) { - paths.push(`search=${encodeURIComponent(searchToken)}`); + const sanitized = searchToken.split(' ').map(t => encodeURIComponent(t)).join('+'); + paths.push(`search=${sanitized}`); } Turbolinks.visit(`?scope=all&utf8=✓&${paths.join('&')}`); diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js.es6 index e46373024b6..e6b53cd4b55 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js.es6 +++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js.es6 @@ -21,6 +21,15 @@ symbol: '~', }]; + const alternativeTokenKeys = [{ + key: 'label', + type: 'string', + param: 'name', + symbol: '~', + }]; + + const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys); + const conditions = [{ url: 'assignee_id=0', tokenKey: 'assignee', @@ -44,6 +53,10 @@ return tokenKeys; } + static getAlternatives() { + return alternativeTokenKeys; + } + static getConditions() { return conditions; } @@ -57,7 +70,7 @@ } static searchByKeyParam(keyParam) { - return tokenKeys.find((tokenKey) => { + return tokenKeysWithAlternative.find((tokenKey) => { let tokenKeyParam = tokenKey.key; if (tokenKey.param) { diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 3f23095dad9..7f1f2a5d278 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -83,12 +83,12 @@ _a = decodeURI("%C3%80"); _y = decodeURI("%C3%BF"); - regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?![" + atSymbolsWithBar + "])([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)$", 'gi'); + regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?![" + atSymbolsWithBar + "])(([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]|[^\\x00-\\x7a])*)$", 'gi'); match = regexp.exec(subtext); if (match) { - return match[2] || match[1]; + return (match[1] || match[1] === "") ? match[1] : match[2]; } else { return null; } diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index cc1c0877cdf..d2f66cf5249 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -512,12 +512,17 @@ // Append the menu into the dropdown GitLabDropdown.prototype.appendMenu = function(html) { + return this.clearMenu().append(html); + }; + + GitLabDropdown.prototype.clearMenu = function() { var selector; selector = '.dropdown-content'; if (this.dropdown.find(".dropdown-toggle-page").length) { selector = ".dropdown-page-one .dropdown-content"; } - return $(selector, this.dropdown).empty().append(html); + + return $(selector, this.dropdown).empty(); }; GitLabDropdown.prototype.renderItem = function(data, group, index) { diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js deleted file mode 100644 index 08b2494f3df..00000000000 --- a/app/assets/javascripts/gl_form.js +++ /dev/null @@ -1,62 +0,0 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-new, max-len */ -/* global GitLab */ -/* global DropzoneInput */ -/* global autosize */ - -(function() { - this.GLForm = (function() { - function GLForm(form) { - this.form = form; - this.textarea = this.form.find('textarea.js-gfm-input'); - // Before we start, we should clean up any previous data for this form - this.destroy(); - // Setup the form - this.setupForm(); - this.form.data('gl-form', this); - } - - GLForm.prototype.destroy = function() { - // Clean form listeners - this.clearEventListeners(); - return this.form.data('gl-form', null); - }; - - GLForm.prototype.setupForm = function() { - var isNewForm; - isNewForm = this.form.is(':not(.gfm-form)'); - this.form.removeClass('js-new-note-form'); - if (isNewForm) { - this.form.find('.div-dropzone').remove(); - this.form.addClass('gfm-form'); - // remove notify commit author checkbox for non-commit notes - gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); - gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); - new DropzoneInput(this.form); - autosize(this.textarea); - // form and textarea event listeners - this.addEventListeners(); - } - gl.text.init(this.form); - // hide discard button - this.form.find('.js-note-discard').hide(); - return this.form.show(); - }; - - GLForm.prototype.clearEventListeners = function() { - this.textarea.off('focus'); - this.textarea.off('blur'); - return gl.text.removeListeners(this.form); - }; - - GLForm.prototype.addEventListeners = function() { - this.textarea.on('focus', function() { - return $(this).closest('.md-area').addClass('is-focused'); - }); - return this.textarea.on('blur', function() { - return $(this).closest('.md-area').removeClass('is-focused'); - }); - }; - - return GLForm; - })(); -}).call(this); diff --git a/app/assets/javascripts/gl_form.js.es6 b/app/assets/javascripts/gl_form.js.es6 new file mode 100644 index 00000000000..0b446ff364a --- /dev/null +++ b/app/assets/javascripts/gl_form.js.es6 @@ -0,0 +1,92 @@ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-new, max-len */ +/* global GitLab */ +/* global DropzoneInput */ +/* global autosize */ + +(() => { + const global = window.gl || (window.gl = {}); + + function GLForm(form) { + this.form = form; + this.textarea = this.form.find('textarea.js-gfm-input'); + // Before we start, we should clean up any previous data for this form + this.destroy(); + // Setup the form + this.setupForm(); + this.form.data('gl-form', this); + } + + GLForm.prototype.destroy = function() { + // Clean form listeners + this.clearEventListeners(); + return this.form.data('gl-form', null); + }; + + GLForm.prototype.setupForm = function() { + var isNewForm; + isNewForm = this.form.is(':not(.gfm-form)'); + this.form.removeClass('js-new-note-form'); + if (isNewForm) { + this.form.find('.div-dropzone').remove(); + this.form.addClass('gfm-form'); + // remove notify commit author checkbox for non-commit notes + gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); + gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); + new DropzoneInput(this.form); + autosize(this.textarea); + // form and textarea event listeners + this.addEventListeners(); + } + gl.text.init(this.form); + // hide discard button + this.form.find('.js-note-discard').hide(); + this.form.show(); + if (this.isAutosizeable) this.setupAutosize(); + }; + + GLForm.prototype.setupAutosize = function () { + this.textarea.off('autosize:resized') + .on('autosize:resized', this.setHeightData.bind(this)); + + this.textarea.off('mouseup.autosize') + .on('mouseup.autosize', this.destroyAutosize.bind(this)); + + setTimeout(() => { + autosize(this.textarea); + this.textarea.css('resize', 'vertical'); + }, 0); + }; + + GLForm.prototype.setHeightData = function () { + this.textarea.data('height', this.textarea.outerHeight()); + }; + + GLForm.prototype.destroyAutosize = function () { + const outerHeight = this.textarea.outerHeight(); + + if (this.textarea.data('height') === outerHeight) return; + + autosize.destroy(this.textarea); + + this.textarea.data('height', outerHeight); + this.textarea.outerHeight(outerHeight); + this.textarea.css('max-height', window.outerHeight); + }; + + GLForm.prototype.clearEventListeners = function() { + this.textarea.off('focus'); + this.textarea.off('blur'); + return gl.text.removeListeners(this.form); + }; + + GLForm.prototype.addEventListeners = function() { + this.textarea.on('focus', function() { + return $(this).closest('.md-area').addClass('is-focused'); + }); + return this.textarea.on('blur', function() { + return $(this).closest('.md-area').removeClass('is-focused'); + }); + }; + + global.GLForm = GLForm; +})(); diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index a50bc4a9057..bc88dc2d092 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -59,11 +59,11 @@ } else { avatar = gon.default_avatar_url; } - return "<div class='group-result'> <div class='group-name'>" + group.name + "</div> <div class='group-path'>" + group.path + "</div> </div>"; + return "<div class='group-result'> <div class='group-name'>" + group.full_name + "</div> <div class='group-path'>" + group.full_path + "</div> </div>"; }; GroupsSelect.prototype.formatSelection = function(group) { - return group.name; + return group.full_name; }; return GroupsSelect; diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js index 9c53cdee58e..c77fbb6a1c7 100644 --- a/app/assets/javascripts/issuable_context.js +++ b/app/assets/javascripts/issuable_context.js @@ -1,5 +1,7 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, comma-dangle, quotes, prefer-arrow-callback, consistent-return, one-var, no-var, one-var-declaration-per-line, no-underscore-dangle, max-len */ /* global UsersSelect */ +/* global Cookies */ +/* global bp */ (function() { this.IssuableContext = (function() { @@ -37,6 +39,13 @@ }, 0); } }); + window.addEventListener('beforeunload', function() { + // collapsed_gutter cookie hides the sidebar + var bpBreakpoint = bp.getBreakpointSize(); + if (bpBreakpoint === 'xs' || bpBreakpoint === 'sm') { + Cookies.set('collapsed_gutter', true); + } + }); $(".right-sidebar").niceScroll(); } diff --git a/app/assets/javascripts/label_manager.js.es6 b/app/assets/javascripts/label_manager.js.es6 index 8f48b1f57ce..2a50b72c8aa 100644 --- a/app/assets/javascripts/label_manager.js.es6 +++ b/app/assets/javascripts/label_manager.js.es6 @@ -8,6 +8,7 @@ this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels'); this.otherLabels = otherLabels || $('.js-other-labels'); this.errorMessage = 'Unable to update label prioritization at this time'; + this.emptyState = document.querySelector('#js-priority-labels-empty-state'); this.prioritizedLabels.sortable({ items: 'li', placeholder: 'list-placeholder', @@ -29,7 +30,12 @@ const action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add'; const $tooltip = $(`#${$btn.find('.has-tooltip:visible').attr('aria-describedby')}`); $tooltip.tooltip('destroy'); - return _this.toggleLabelPriority($label, action); + _this.toggleLabelPriority($label, action); + _this.toggleEmptyState($label, $btn, action); + } + + toggleEmptyState($label, $btn, action) { + this.emptyState.classList.toggle('hidden', !!this.prioritizedLabels[0].querySelector(':scope > li')); } toggleLabelPriority($label, action, persistState) { diff --git a/app/assets/javascripts/lib/ace.js b/app/assets/javascripts/lib/ace.js index 4cdf99cae72..9cdc0309503 100644 --- a/app/assets/javascripts/lib/ace.js +++ b/app/assets/javascripts/lib/ace.js @@ -1,2 +1,3 @@ -/*= require ace-rails-ap */ +/*= require ace/ace */ /*= require ace/ext-searchbox */ +/*= require ./ace/ace_config_paths */ diff --git a/app/assets/javascripts/lib/ace/ace_config_paths.js.erb b/app/assets/javascripts/lib/ace/ace_config_paths.js.erb new file mode 100644 index 00000000000..25e623f0fdc --- /dev/null +++ b/app/assets/javascripts/lib/ace/ace_config_paths.js.erb @@ -0,0 +1,25 @@ +<% +ace_gem_path = Bundler.rubygems.find_name('ace-rails-ap').first.full_gem_path +ace_workers = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/worker-*.js'].sort.map do |file| + File.basename(file, '.js').sub(/^worker-/, '') +end +ace_modes = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/mode-*.js'].sort.map do |file| + File.basename(file, '.js').sub(/^mode-/, '') +end +%> + +(function() { + window.gon = window.gon || {}; + var basePath = (window.gon.relative_url_root || '').replace(/\/$/, '') + '/assets/ace'; + ace.config.set('basePath', basePath); + + // configure paths for all worker modules +<% ace_workers.each do |worker| %> + ace.config.setModuleUrl('ace/mode/<%= worker %>_worker', basePath + '/worker-<%= worker %>.js'); +<% end %> + + // configure paths for all mode modules +<% ace_modes.each do |mode| %> + ace.config.setModuleUrl('ace/mode/<%= mode %>', basePath + '/mode-<%= mode %>.js'); +<% end %> +})(); diff --git a/app/assets/javascripts/lib/utils/common_utils.js.es6 b/app/assets/javascripts/lib/utils/common_utils.js.es6 index 51993bb3420..e3bff2559fd 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js.es6 +++ b/app/assets/javascripts/lib/utils/common_utils.js.es6 @@ -162,6 +162,7 @@ w.gl.utils.getSelectedFragment = () => { const selection = window.getSelection(); + if (selection.rangeCount === 0) return null; const documentFragment = selection.getRangeAt(0).cloneContents(); if (documentFragment.textContent.length === 0) return null; diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js index 4620715a521..2f147704c22 100644 --- a/app/assets/javascripts/line_highlighter.js +++ b/app/assets/javascripts/line_highlighter.js @@ -74,8 +74,9 @@ // If not done this way, the line number anchor will sometimes keep its // active state even when the event is cancelled, resulting in an ugly border // around the link and/or a persisted underline text decoration. - return $('#blob-content-holder').on('click', 'a[data-line-number]', function(event) { - return event.preventDefault(); + $('#blob-content-holder').on('click', 'a[data-line-number]', function(event) { + event.preventDefault(); + event.stopPropagation(); }); }; diff --git a/app/assets/javascripts/merge_request_widget.js.es6 b/app/assets/javascripts/merge_request_widget.js.es6 index 7cc319e2f4e..fa782ebbedf 100644 --- a/app/assets/javascripts/merge_request_widget.js.es6 +++ b/app/assets/javascripts/merge_request_widget.js.es6 @@ -154,12 +154,22 @@ return; } if (data.environments && data.environments.length) _this.renderEnvironments(data.environments); - if (data.status !== _this.opts.ci_status && (data.status != null)) { + if (data.status !== _this.opts.ci_status || + data.sha !== _this.opts.ci_sha || + data.pipeline !== _this.opts.ci_pipeline) { _this.opts.ci_status = data.status; _this.showCIStatus(data.status); if (data.coverage) { _this.showCICoverage(data.coverage); } + if (data.pipeline) { + _this.opts.ci_pipeline = data.pipeline; + _this.updatePipelineUrls(data.pipeline); + } + if (data.sha) { + _this.opts.ci_sha = data.sha; + _this.updateCommitUrls(data.sha); + } if (showNotification) { status = _this.ciLabelForStatus(data.status); if (status === "preparing") { @@ -248,6 +258,16 @@ return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-info btn-create').addClass(css_class); }; + MergeRequestWidget.prototype.updatePipelineUrls = function(id) { + const pipelineUrl = this.opts.pipeline_path; + $('.pipeline').text(`#${id}`).attr('href', [pipelineUrl, id].join('/')); + }; + + MergeRequestWidget.prototype.updateCommitUrls = function(id) { + const commitsUrl = this.opts.commits_path; + $('.js-commit-link').text(`#${id}`).attr('href', [commitsUrl, id].join('/')); + }; + return MergeRequestWidget; })(); })(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 9db830a7ada..c4722be3625 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -1,6 +1,5 @@ /* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape */ /* global Flash */ -/* global GLForm */ /* global Autosave */ /* global ResolveService */ /* global mrRefreshWidgetUrl */ @@ -420,7 +419,7 @@ Notes.prototype.setupNoteForm = function(form) { var textarea; - new GLForm(form); + new gl.GLForm(form); textarea = form.find(".js-note-text"); return new Autosave(textarea, ["Note", form.find("#note_noteable_type").val(), form.find("#note_noteable_id").val(), form.find("#note_commit_id").val(), form.find("#note_type").val(), form.find("#note_line_code").val(), form.find("#note_position").val()]); }; @@ -884,7 +883,7 @@ var targetId = $originalContentEl.data('target-id'); var targetType = $originalContentEl.data('target-type'); - new GLForm($editForm.find('form')); + new gl.GLForm($editForm.find('form')); $editForm.find('form') .attr('action', postUrl) diff --git a/app/assets/javascripts/profile/profile.js.es6 b/app/assets/javascripts/profile/profile.js.es6 index 6dbaae25f2a..5aec9c813fe 100644 --- a/app/assets/javascripts/profile/profile.js.es6 +++ b/app/assets/javascripts/profile/profile.js.es6 @@ -36,6 +36,7 @@ } onSubmitForm(e) { + e.preventDefault(); return this.saveForm(); } diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js index 489e567259c..b1c0dc37b4d 100644 --- a/app/assets/javascripts/search.js +++ b/app/assets/javascripts/search.js @@ -13,12 +13,12 @@ filterable: true, fieldName: 'group_id', search: { - fields: ['name'] + fields: ['full_name'] }, data: function(term, callback) { return Api.groups(term, {}, function(data) { data.unshift({ - name: 'Any' + full_name: 'Any' }); data.splice(1, 0, 'divider'); return callback(data); @@ -28,10 +28,10 @@ return obj.id; }, text: function(obj) { - return obj.name; + return obj.full_name; }, toggleLabel: function(obj) { - return ($groupDropdown.data('default-label')) + " " + obj.name; + return ($groupDropdown.data('default-label')) + " " + obj.full_name; }, clicked: (function(_this) { return function() { diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js index 4ef516af8c8..4dcc5ebe28f 100644 --- a/app/assets/javascripts/shortcuts_issuable.js +++ b/app/assets/javascripts/shortcuts_issuable.js @@ -39,17 +39,20 @@ } ShortcutsIssuable.prototype.replyWithSelectedText = function() { - var quote, replyField, documentFragment, selected, separator; + var quote, documentFragment, selected, separator; + var replyField = $('.js-main-target-form #note_note'); documentFragment = window.gl.utils.getSelectedFragment(); - if (!documentFragment) return; + if (!documentFragment) { + replyField.focus(); + return; + } // If the documentFragment contains more than just Markdown, don't copy as GFM. if (documentFragment.querySelector('.md, .wiki')) return; selected = window.gl.CopyAsGFM.nodeToGFM(documentFragment); - replyField = $('.js-main-target-form #note_note'); if (selected.trim() === "") { return; } diff --git a/app/assets/javascripts/todos.js.es6 b/app/assets/javascripts/todos.js.es6 index ef9c0a885fb..05622916ff8 100644 --- a/app/assets/javascripts/todos.js.es6 +++ b/app/assets/javascripts/todos.js.es6 @@ -85,7 +85,7 @@ }, success: (data) => { $target.remove(); - $('.prepend-top-default').html('<div class="nothing-here-block">You\'re all done!</div>'); + $('.js-todos-all').html('<div class="nothing-here-block">You\'re all done!</div>'); return this.updateBadges(data); } }); diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js index 7ffc546ffc1..e7280d643d3 100644 --- a/app/assets/javascripts/users/calendar.js +++ b/app/assets/javascripts/users/calendar.js @@ -158,7 +158,7 @@ }; Calendar.prototype.renderMonths = function() { - return this.svg.append('g').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) { + return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) { return date.x; }).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) { return function(date) { |