diff options
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r-- | app/assets/javascripts/build.js | 125 | ||||
-rw-r--r-- | app/assets/javascripts/dispatcher.js.es6 | 9 | ||||
-rw-r--r-- | app/assets/javascripts/gfm_auto_complete.js.es6 | 20 | ||||
-rw-r--r-- | app/assets/javascripts/issuable.js.es6 | 14 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/common_utils.js | 13 | ||||
-rw-r--r-- | app/assets/javascripts/merge_request_tabs.js.es6 | 33 | ||||
-rw-r--r-- | app/assets/javascripts/merge_request_widget.js.es6 | 3 | ||||
-rw-r--r-- | app/assets/javascripts/merge_request_widget/ci_bundle.js.es6 | 42 |
8 files changed, 176 insertions, 83 deletions
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 824febe3fd3..5e449170cd3 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -4,6 +4,7 @@ (function() { var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + var AUTO_SCROLL_OFFSET = 75; this.Build = (function() { Build.interval = null; @@ -19,6 +20,17 @@ this.buildStage = options.buildStage; this.updateDropdown = bind(this.updateDropdown, this); this.$document = $(document); + this.$body = $('body'); + this.$buildTrace = $('#build-trace'); + this.$autoScrollContainer = $('.autoscroll-container'); + this.$autoScrollStatus = $('#autoscroll-status'); + this.$autoScrollStatusText = this.$autoScrollStatus.find('.status-text'); + this.$upBuildTrace = $('#up-build-trace'); + this.$downBuildTrace = $('#down-build-trace'); + this.$scrollTopBtn = $('#scroll-top'); + this.$scrollBottomBtn = $('#scroll-bottom'); + this.$buildRefreshAnimation = $('.js-build-refresh'); + clearInterval(Build.interval); // Init breakpoint checker this.bp = Breakpoints.get(); @@ -32,6 +44,7 @@ this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this)); this.$document.off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown); + this.$document.on('scroll', this.initScrollMonitor.bind(this)); $(window).off('resize.build').on('resize.build', this.sidebarOnResize.bind(this)); $('a', this.$buildScroll).off('click.stepTrace').on('click.stepTrace', this.stepTrace); this.updateArtifactRemoveDate(); @@ -40,18 +53,6 @@ this.initScrollButtonAffix(); } if (this.buildStatus === "running" || this.buildStatus === "pending") { - // Bind autoscroll button to follow build output - $('#autoscroll-button').on('click', function() { - var state; - state = $(this).data("state"); - if ("enabled" === state) { - $(this).data("state", "disabled"); - return $(this).text("Enable autoscroll"); - } else { - $(this).data("state", "enabled"); - return $(this).text("Disable autoscroll"); - } - }); Build.interval = setInterval((function(_this) { // Check for new build output if user still watching build page // Only valid for runnig build when output changes during time @@ -91,9 +92,10 @@ success: function(buildData) { $('.js-build-output').html(buildData.trace_html); if (removeRefreshStatuses.indexOf(buildData.status) >= 0) { - return $('.js-build-refresh').remove(); + this.initScrollMonitor(); + return this.$buildRefreshAnimation.remove(); } - } + }.bind(this) }); }; @@ -122,22 +124,95 @@ }; Build.prototype.checkAutoscroll = function() { - if ("enabled" === $("#autoscroll-button").data("state")) { - return $("html,body").scrollTop($("#build-trace").height()); + if (this.$autoScrollStatus.data("state") === "enabled") { + return $("html,body").scrollTop(this.$buildTrace.height()); + } + + // Handle a situation where user started new build + // but never scrolled a page + if (!this.$scrollTopBtn.is(':visible') && + !this.$scrollBottomBtn.is(':visible') && + !gl.utils.isInViewport(this.$downBuildTrace.get(0))) { + this.$scrollBottomBtn.show(); } }; Build.prototype.initScrollButtonAffix = function() { - var $body, $buildTrace; - $body = $('body'); - $buildTrace = $('#build-trace'); - return this.$buildScroll.affix({ - offset: { - bottom: function() { - return $body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top); - } + // Hide everything initially + this.$scrollTopBtn.hide(); + this.$scrollBottomBtn.hide(); + this.$autoScrollContainer.hide(); + } + + // Page scroll listener to detect if user has scrolling page + // and handle following cases + // 1) User is at Top of Build Log; + // - Hide Top Arrow button + // - Show Bottom Arrow button + // - Disable Autoscroll and hide indicator (when build is running) + // 2) User is at Bottom of Build Log; + // - Show Top Arrow button + // - Hide Bottom Arrow button + // - Enable Autoscroll and show indicator (when build is running) + // 3) User is somewhere in middle of Build Log; + // - Show Top Arrow button + // - Show Bottom Arrow button + // - Disable Autoscroll and hide indicator (when build is running) + Build.prototype.initScrollMonitor = function() { + if (!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) { + // User is somewhere in middle of Build Log + + this.$scrollTopBtn.show(); + + if (this.buildStatus === 'success' || this.buildStatus === 'failed') { // Check if Build is completed + this.$scrollBottomBtn.show(); + } else if (this.$buildRefreshAnimation.is(':visible') && !gl.utils.isInViewport(this.$buildRefreshAnimation.get(0))) { + this.$scrollBottomBtn.show(); + } else { + this.$scrollBottomBtn.hide(); } - }); + + // Hide Autoscroll Status Indicator + if (this.$scrollBottomBtn.is(':visible')) { + this.$autoScrollContainer.hide(); + this.$autoScrollStatusText.removeClass('animate'); + } else { + this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show(); + this.$autoScrollStatusText.addClass('animate'); + } + } else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) { + // User is at Top of Build Log + + this.$scrollTopBtn.hide(); + this.$scrollBottomBtn.show(); + + this.$autoScrollContainer.hide(); + this.$autoScrollStatusText.removeClass('animate'); + } else if ((!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) || + (this.$buildRefreshAnimation.is(':visible') && gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)))) { + // User is at Bottom of Build Log + + this.$scrollTopBtn.show(); + this.$scrollBottomBtn.hide(); + + // Show and Reposition Autoscroll Status Indicator + this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show(); + this.$autoScrollStatusText.addClass('animate'); + } else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) { + // Build Log height is small + + this.$scrollTopBtn.hide(); + this.$scrollBottomBtn.hide(); + + // Hide Autoscroll Status Indicator + this.$autoScrollContainer.hide(); + this.$autoScrollStatusText.removeClass('animate'); + } + + if (this.buildStatus === "running" || this.buildStatus === "pending") { + // Check if Refresh Animation is in Viewport and enable Autoscroll, disable otherwise. + this.$autoScrollStatus.data("state", gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)) ? 'enabled' : 'disabled'); + } }; Build.prototype.shouldHideSidebarForViewport = function() { diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index 752f35e6356..78f68a247a2 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -138,14 +138,8 @@ new MergedButtons(); break; case 'projects:merge_requests:commits': - case 'projects:merge_requests:builds': new MergedButtons(); break; - case 'projects:merge_requests:pipelines': - new gl.MiniPipelineGraph({ - container: '.js-pipeline-table', - }); - break; case "projects:merge_requests:diffs": new gl.Diff(); new ZenMode(); @@ -168,9 +162,6 @@ container: '.js-pipeline-table', }); break; - case 'projects:commit:builds': - new gl.Pipelines(); - break; case 'projects:commits:show': case 'projects:activity': shortcut_handler = new ShortcutsNavigation(); diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 17d03c87bf5..12875eaa1c3 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -74,15 +74,16 @@ // The below is taken from At.js source // Tweaked to commands to start without a space only if char before is a non-word character // https://github.com/ichord/At.js - var _a, _y, regexp, match, atSymbols; - atSymbols = Object.keys(this.app.controllers).join('|'); + var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar; + atSymbolsWithBar = Object.keys(this.app.controllers).join('|'); + atSymbolsWithoutBar = Object.keys(this.app.controllers).join(''); subtext = subtext.split(' ').pop(); flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); _a = decodeURI("%C3%80"); _y = decodeURI("%C3%BF"); - regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "(?![" + atSymbols + "])([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_\'\.\+\-]*)$", 'gi'); match = regexp.exec(subtext); @@ -112,7 +113,6 @@ return value.path != null ? this.Emoji.template : this.Loading.template; }.bind(this), insertTpl: ':${name}:', - startWithSpace: false, skipSpecialCharacterTest: true, data: this.defaultLoadingData, callbacks: { @@ -129,7 +129,6 @@ }.bind(this), insertTpl: '${atwho-at}${username}', searchKey: 'search', - startWithSpace: false, alwaysHighlightFirst: true, skipSpecialCharacterTest: true, data: this.defaultLoadingData, @@ -172,7 +171,6 @@ }.bind(this), data: this.defaultLoadingData, insertTpl: '${atwho-at}${id}', - startWithSpace: false, callbacks: { sorter: this.DefaultOptions.sorter, filter: this.DefaultOptions.filter, @@ -200,7 +198,6 @@ displayTpl: function(value) { return value.title != null ? this.Milestones.template : this.Loading.template; }.bind(this), - startWithSpace: false, data: this.defaultLoadingData, callbacks: { matcher: this.DefaultOptions.matcher, @@ -225,7 +222,6 @@ at: '!', alias: 'mergerequests', searchKey: 'search', - startWithSpace: false, displayTpl: function(value) { return value.title != null ? this.Issues.template : this.Loading.template; }.bind(this), @@ -259,7 +255,6 @@ return this.isLoading(value) ? this.Loading.template : this.Labels.template; }.bind(this), insertTpl: '${atwho-at}${title}', - startWithSpace: false, callbacks: { matcher: this.DefaultOptions.matcher, sorter: this.DefaultOptions.sorter, @@ -379,14 +374,7 @@ togglePreventSelection(isPrevented = !!this.setting.tabSelectsMatch) { this.setting.tabSelectsMatch = !isPrevented; this.setting.spaceSelectsMatch = !isPrevented; - const eventListenerAction = `${isPrevented ? 'add' : 'remove'}EventListener`; - this.$inputor[0][eventListenerAction]('keydown', gl.GfmAutoComplete.preventSpaceTabEnter); }, - preventSpaceTabEnter(e) { - const key = e.which || e.keyCode; - const preventables = [9, 13, 32]; - if (preventables.indexOf(key) > -1) e.preventDefault(); - } }; }).call(this); diff --git a/app/assets/javascripts/issuable.js.es6 b/app/assets/javascripts/issuable.js.es6 index 1c10a7445bb..9c3c96c20ed 100644 --- a/app/assets/javascripts/issuable.js.es6 +++ b/app/assets/javascripts/issuable.js.es6 @@ -1,13 +1,13 @@ -/* eslint-disable 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, prefer-const, padded-blocks, wrap-iife, max-len */ +/* 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, prefer-const, padded-blocks, wrap-iife, max-len */ /* global Issuable */ /* global Turbolinks */ -(function() { +((global) => { var issuable_created; issuable_created = false; - this.Issuable = { + global.Issuable = { init: function() { Issuable.initTemplates(); Issuable.initSearch(); @@ -111,7 +111,11 @@ filterResults: (function(_this) { return function(form) { var formAction, formData, issuesUrl; - formData = form.serialize(); + formData = form.serializeArray(); + formData = formData.filter(function(data) { + return data.value !== ''; + }); + formData = $.param(formData); formAction = form.attr('action'); issuesUrl = formAction; issuesUrl += "" + (formAction.indexOf('?') < 0 ? '?' : '&'); @@ -184,4 +188,4 @@ } }; -}).call(this); +})(window); diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 8fa80502d92..0a0e73e0ccc 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -93,6 +93,19 @@ } }; + // Check if element scrolled into viewport from above or below + // Courtesy http://stackoverflow.com/a/7557433/414749 + w.gl.utils.isInViewport = function(el) { + var rect = el.getBoundingClientRect(); + + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= window.innerHeight && + rect.right <= window.innerWidth + ); + }; + gl.utils.getPagePath = function() { return $('body').data('page').split(':')[0]; }; diff --git a/app/assets/javascripts/merge_request_tabs.js.es6 b/app/assets/javascripts/merge_request_tabs.js.es6 index 3ec0f1fd613..860e7e066a0 100644 --- a/app/assets/javascripts/merge_request_tabs.js.es6 +++ b/app/assets/javascripts/merge_request_tabs.js.es6 @@ -59,16 +59,13 @@ class MergeRequestTabs { - constructor({ action, setUrl, buildsLoaded, stubLocation } = {}) { + constructor({ action, setUrl, stubLocation } = {}) { this.diffsLoaded = false; - this.buildsLoaded = false; this.pipelinesLoaded = false; this.commitsLoaded = false; this.fixedLayoutPref = null; this.setUrl = setUrl !== undefined ? setUrl : true; - this.buildsLoaded = buildsLoaded || false; - this.setCurrentAction = this.setCurrentAction.bind(this); this.tabShown = this.tabShown.bind(this); this.showTab = this.showTab.bind(this); @@ -119,10 +116,6 @@ $.scrollTo('.merge-request-details .merge-request-tabs', { offset: -navBarHeight, }); - } else if (action === 'builds') { - this.loadBuilds($target.attr('href')); - this.expandView(); - this.resetViewContainer(); } else if (action === 'pipelines') { this.loadPipelines($target.attr('href')); this.expandView(); @@ -180,8 +173,8 @@ setCurrentAction(action) { this.currentAction = action === 'show' ? 'notes' : action; - // Remove a trailing '/commits' '/diffs' '/builds' '/pipelines' '/new' '/new/diffs' - let newState = location.pathname.replace(/\/(commits|diffs|builds|pipelines|new|new\/diffs)(\.html)?\/?$/, ''); + // Remove a trailing '/commits' '/diffs' '/pipelines' '/new' '/new/diffs' + let newState = location.pathname.replace(/\/(commits|diffs|pipelines|new|new\/diffs)(\.html)?\/?$/, ''); // Append the new action if we're on a tab other than 'notes' if (this.currentAction !== 'notes') { @@ -255,22 +248,6 @@ }); } - loadBuilds(source) { - if (this.buildsLoaded) { - return; - } - this.ajaxGet({ - url: `${source}.json`, - success: (data) => { - document.querySelector('div#builds').innerHTML = data.html; - gl.utils.localTimeAgo($('.js-timeago', 'div#builds')); - this.buildsLoaded = true; - new gl.Pipelines(); - this.scrollToElement('#builds'); - }, - }); - } - loadPipelines(source) { if (this.pipelinesLoaded) { return; @@ -282,6 +259,10 @@ gl.utils.localTimeAgo($('.js-timeago', '#pipelines')); this.pipelinesLoaded = true; this.scrollToElement('#pipelines'); + + new gl.MiniPipelineGraph({ + container: '.js-pipeline-table', + }); }, }); } diff --git a/app/assets/javascripts/merge_request_widget.js.es6 b/app/assets/javascripts/merge_request_widget.js.es6 index e47047c4cca..0305aeb07d9 100644 --- a/app/assets/javascripts/merge_request_widget.js.es6 +++ b/app/assets/javascripts/merge_request_widget.js.es6 @@ -74,7 +74,7 @@ MergeRequestWidget.prototype.addEventListeners = function() { var allowedPages; - allowedPages = ['show', 'commits', 'builds', 'pipelines', 'changes']; + allowedPages = ['show', 'commits', 'pipelines', 'changes']; $(document).on('page:change.merge_request', (function(_this) { return function() { var page; @@ -173,7 +173,6 @@ message = message.replace('{{title}}', data.title); notify(title, message, _this.opts.gitlab_icon, function() { this.close(); - return Turbolinks.visit(_this.opts.builds_path); }); } } diff --git a/app/assets/javascripts/merge_request_widget/ci_bundle.js.es6 b/app/assets/javascripts/merge_request_widget/ci_bundle.js.es6 new file mode 100644 index 00000000000..2b074994b4a --- /dev/null +++ b/app/assets/javascripts/merge_request_widget/ci_bundle.js.es6 @@ -0,0 +1,42 @@ +/* global merge_request_widget */ + +(() => { + $(() => { + /* TODO: This needs a better home, or should be refactored. It was previously contained + * in a script tag in app/views/projects/merge_requests/widget/open/_accept.html.haml, + * but Vue chokes on script tags and prevents their execution. So it was moved here + * temporarily. + * */ + + if ($('.accept-mr-form').length) { + $('.accept-mr-form').on('ajax:send', () => { + $('.accept-mr-form :input').disable(); + }); + + $('.accept_merge_request').on('click', () => { + $('.js-merge-button').html('<i class="fa fa-spinner fa-spin"></i> Merge in progress'); + }); + + $('.merge_when_build_succeeds').on('click', () => { + $('#merge_when_build_succeeds').val('1'); + }); + + $('.js-merge-dropdown a').on('click', (e) => { + e.preventDefault(); + $(this).closest('form').submit(); + }); + } else if ($('.rebase-in-progress').length) { + merge_request_widget.rebaseInProgress(); + } else if ($('.rebase-mr-form').length) { + $('.rebase-mr-form').on('ajax:send', () => { + $('.rebase-mr-form :input').disable(); + }); + + $('.js-rebase-button').on('click', () => { + $('.js-rebase-button').html("<i class='fa fa-spinner fa-spin'></i> Rebase in progress"); + }); + } else { + merge_request_widget.getMergeStatus(); + } + }); +})(); |