diff options
author | Alfredo Sumaran <a.sumaran@gmail.com> | 2017-07-14 19:53:00 +0000 |
---|---|---|
committer | Mike Greiling <mike@pixelcog.com> | 2017-08-10 22:28:24 -0500 |
commit | d60a863d287f50dce058a748ddb34505b0088ec0 (patch) | |
tree | 96048cd3eb2a762508c4f7596faf0da21b4c9614 | |
parent | d9ed329b8c4282e8b722034df22dc1949e46c732 (diff) | |
download | gitlab-ce-as-remove-iifes.tar.gz |
Remove IIFE from various JS filesas-remove-iifes
92 files changed, 8543 insertions, 8822 deletions
diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 34669dd13d6..a564643199c 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -1,62 +1,60 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-arrow-callback, camelcase, quotes, comma-dangle, max-len */ -window.Admin = (function() { - function Admin() { - var modal, showBlacklistType; - $('input#user_force_random_password').on('change', function(elem) { - var elems; - elems = $('#user_password, #user_password_confirmation'); - if ($(this).attr('checked')) { - return elems.val('').attr('disabled', true); - } else { - return elems.removeAttr('disabled'); - } - }); - $('body').on('click', '.js-toggle-colors-link', function(e) { - e.preventDefault(); - return $('.js-toggle-colors-container').toggle(); - }); - $('.log-tabs a').click(function(e) { - e.preventDefault(); - return $(this).tab('show'); - }); - $('.log-bottom').click(function(e) { - var visible_log; - e.preventDefault(); - visible_log = $(".file-content:visible"); - return visible_log.animate({ - scrollTop: visible_log.find('ol').height() - }, "fast"); - }); - modal = $('.change-owner-holder'); - $('.change-owner-link').bind("click", function(e) { - e.preventDefault(); - $(this).hide(); - return modal.show(); - }); - $('.change-owner-cancel-link').bind("click", function(e) { - e.preventDefault(); - modal.hide(); - return $('.change-owner-link').show(); - }); - $('li.project_member').bind('ajax:success', function() { - return gl.utils.refreshCurrentPage(); - }); - $('li.group_member').bind('ajax:success', function() { - return gl.utils.refreshCurrentPage(); - }); - showBlacklistType = function() { - if ($("input[name='blacklist_type']:checked").val() === 'file') { - $('.blacklist-file').show(); - return $('.blacklist-raw').hide(); - } else { - $('.blacklist-file').hide(); - return $('.blacklist-raw').show(); - } - }; - $("input[name='blacklist_type']").click(showBlacklistType); - showBlacklistType(); - } +function Admin() { + var modal, showBlacklistType; + $('input#user_force_random_password').on('change', function(elem) { + var elems; + elems = $('#user_password, #user_password_confirmation'); + if ($(this).attr('checked')) { + return elems.val('').attr('disabled', true); + } else { + return elems.removeAttr('disabled'); + } + }); + $('body').on('click', '.js-toggle-colors-link', function(e) { + e.preventDefault(); + return $('.js-toggle-colors-container').toggle(); + }); + $('.log-tabs a').click(function(e) { + e.preventDefault(); + return $(this).tab('show'); + }); + $('.log-bottom').click(function(e) { + var visible_log; + e.preventDefault(); + visible_log = $(".file-content:visible"); + return visible_log.animate({ + scrollTop: visible_log.find('ol').height() + }, "fast"); + }); + modal = $('.change-owner-holder'); + $('.change-owner-link').bind("click", function(e) { + e.preventDefault(); + $(this).hide(); + return modal.show(); + }); + $('.change-owner-cancel-link').bind("click", function(e) { + e.preventDefault(); + modal.hide(); + return $('.change-owner-link').show(); + }); + $('li.project_member').bind('ajax:success', function() { + return gl.utils.refreshCurrentPage(); + }); + $('li.group_member').bind('ajax:success', function() { + return gl.utils.refreshCurrentPage(); + }); + showBlacklistType = function() { + if ($("input[name='blacklist_type']:checked").val() === 'file') { + $('.blacklist-file').show(); + return $('.blacklist-raw').hide(); + } else { + $('.blacklist-file').hide(); + return $('.blacklist-raw').show(); + } + }; + $("input[name='blacklist_type']").click(showBlacklistType); + showBlacklistType(); +} - return Admin; -})(); +window.Admin = Admin; diff --git a/app/assets/javascripts/aside.js b/app/assets/javascripts/aside.js index 88756884d16..bb45aa04ef8 100644 --- a/app/assets/javascripts/aside.js +++ b/app/assets/javascripts/aside.js @@ -1,24 +1,22 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, prefer-arrow-callback, no-var, one-var, one-var-declaration-per-line, no-else-return, max-len */ -window.Aside = (function() { - function Aside() { - $(document).off("click", "a.show-aside"); - $(document).on("click", 'a.show-aside', function(e) { - var btn, icon; - e.preventDefault(); - btn = $(e.currentTarget); - icon = btn.find('i'); - if (icon.hasClass('fa-angle-left')) { - btn.parent().find('section').hide(); - btn.parent().find('aside').fadeIn(); - return icon.removeClass('fa-angle-left').addClass('fa-angle-right'); - } else { - btn.parent().find('aside').hide(); - btn.parent().find('section').fadeIn(); - return icon.removeClass('fa-angle-right').addClass('fa-angle-left'); - } - }); - } +function Aside() { + $(document).off("click", "a.show-aside"); + $(document).on("click", 'a.show-aside', function(e) { + var btn, icon; + e.preventDefault(); + btn = $(e.currentTarget); + icon = btn.find('i'); + if (icon.hasClass('fa-angle-left')) { + btn.parent().find('section').hide(); + btn.parent().find('aside').fadeIn(); + return icon.removeClass('fa-angle-left').addClass('fa-angle-right'); + } else { + btn.parent().find('aside').hide(); + btn.parent().find('section').fadeIn(); + return icon.removeClass('fa-angle-right').addClass('fa-angle-left'); + } + }); +} - return Aside; -})(); +window.Aside = Aside; diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js index cfab6c40b34..a9fad9c64b0 100644 --- a/app/assets/javascripts/autosave.js +++ b/app/assets/javascripts/autosave.js @@ -1,55 +1,49 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, quotes, prefer-template, no-var, one-var, no-unused-vars, one-var-declaration-per-line, no-void, consistent-return, no-empty, max-len */ import AccessorUtilities from './lib/utils/accessor'; -window.Autosave = (function() { - function Autosave(field, key) { - this.field = field; - this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); - - if (key.join != null) { - key = key.join("/"); - } - this.key = "autosave/" + key; - this.field.data("autosave", this); - this.restore(); - this.field.on("input", (function(_this) { - return function() { - return _this.save(); - }; - })(this)); +function Autosave(field, key) { + this.field = field; + this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); + + if (key.join != null) { + key = key.join("/"); } + this.key = "autosave/" + key; + this.field.data("autosave", this); + this.restore(); + this.field.on("input", () => this.save()); +} - Autosave.prototype.restore = function() { - var text; +Autosave.prototype.restore = function() { + var text; - if (!this.isLocalStorageAvailable) return; + if (!this.isLocalStorageAvailable) return; - text = window.localStorage.getItem(this.key); + text = window.localStorage.getItem(this.key); - if ((text != null ? text.length : void 0) > 0) { - this.field.val(text); - } - return this.field.trigger("input"); - }; + if ((text != null ? text.length : void 0) > 0) { + this.field.val(text); + } + return this.field.trigger("input"); +}; - Autosave.prototype.save = function() { - var text; - text = this.field.val(); +Autosave.prototype.save = function() { + var text; + text = this.field.val(); - if (this.isLocalStorageAvailable && (text != null ? text.length : void 0) > 0) { - return window.localStorage.setItem(this.key, text); - } + if (this.isLocalStorageAvailable && (text != null ? text.length : void 0) > 0) { + return window.localStorage.setItem(this.key, text); + } - return this.reset(); - }; + return this.reset(); +}; - Autosave.prototype.reset = function() { - if (!this.isLocalStorageAvailable) return; +Autosave.prototype.reset = function() { + if (!this.isLocalStorageAvailable) return; - return window.localStorage.removeItem(this.key); - }; + return window.localStorage.removeItem(this.key); +}; - return Autosave; -})(); +window.Autosave = Autosave; export default window.Autosave; diff --git a/app/assets/javascripts/breakpoints.js b/app/assets/javascripts/breakpoints.js index 2c1f988d987..8b28462e851 100644 --- a/app/assets/javascripts/breakpoints.js +++ b/app/assets/javascripts/breakpoints.js @@ -1,65 +1,56 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, quotes, no-shadow, prefer-arrow-callback, prefer-template, consistent-return, no-return-assign, new-parens, no-param-reassign, max-len */ -var Breakpoints = (function() { - var BreakpointInstance, instance; - - function Breakpoints() {} - - instance = null; - - BreakpointInstance = (function() { - var BREAKPOINTS; - - BREAKPOINTS = ["xs", "sm", "md", "lg"]; - - function BreakpointInstance() { - this.setup(); - } - - BreakpointInstance.prototype.setup = function() { - var allDeviceSelector, els; - allDeviceSelector = BREAKPOINTS.map(function(breakpoint) { - return ".device-" + breakpoint; - }); - if ($(allDeviceSelector.join(",")).length) { - return; - } - // Create all the elements - els = $.map(BREAKPOINTS, function(breakpoint) { - return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>"; - }); - return $("body").append(els.join('')); - }; - - BreakpointInstance.prototype.visibleDevice = function() { - var allDeviceSelector; - allDeviceSelector = BREAKPOINTS.map(function(breakpoint) { - return ".device-" + breakpoint; - }); - return $(allDeviceSelector.join(",")).filter(":visible"); - }; - - BreakpointInstance.prototype.getBreakpointSize = function() { - var $visibleDevice; - $visibleDevice = this.visibleDevice; - // TODO: Consider refactoring in light of turbolinks removal. - // the page refreshed via turbolinks - if (!$visibleDevice().length) { - this.setup(); - } - $visibleDevice = this.visibleDevice(); - return $visibleDevice.attr("class").split("visible-")[1]; - }; - - return BreakpointInstance; - })(); - - Breakpoints.get = function() { - return instance != null ? instance : instance = new BreakpointInstance; - }; - - return Breakpoints; -})(); +var instance, BREAKPOINTS; + +instance = null; +BREAKPOINTS = ["xs", "sm", "md", "lg"]; + +// BreakpointInstance +function BreakpointInstance() { + this.setup(); +} + +BreakpointInstance.prototype.setup = function() { + var allDeviceSelector, els; + allDeviceSelector = BREAKPOINTS.map(function(breakpoint) { + return ".device-" + breakpoint; + }); + if ($(allDeviceSelector.join(",")).length) { + return; + } + // Create all the elements + els = $.map(BREAKPOINTS, function(breakpoint) { + return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>"; + }); + return $("body").append(els.join('')); +}; + +BreakpointInstance.prototype.visibleDevice = function() { + var allDeviceSelector; + allDeviceSelector = BREAKPOINTS.map(function(breakpoint) { + return ".device-" + breakpoint; + }); + return $(allDeviceSelector.join(",")).filter(":visible"); +}; + +BreakpointInstance.prototype.getBreakpointSize = function() { + var $visibleDevice; + $visibleDevice = this.visibleDevice; + // TODO: Consider refactoring in light of turbolinks removal. + // the page refreshed via turbolinks + if (!$visibleDevice().length) { + this.setup(); + } + $visibleDevice = this.visibleDevice(); + return $visibleDevice.attr("class").split("visible-")[1]; +}; + +// Breakpoints +function Breakpoints() {} + +Breakpoints.get = function() { + return instance != null ? instance : instance = new BreakpointInstance; +}; $(() => { window.bp = Breakpoints.get(); }); diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 1dfa064acfd..85a967180e1 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -5,287 +5,285 @@ consistent-return, prefer-rest-params */ import _ from 'underscore'; import { bytesToKiB } from './lib/utils/number_utils'; -window.Build = (function () { - Build.timeout = null; - Build.state = null; - - function Build(options) { - this.options = options || $('.js-build-options').data(); - - this.pageUrl = this.options.pageUrl; - this.buildStatus = this.options.buildStatus; - this.state = this.options.logState; - this.buildStage = this.options.buildStage; - this.$document = $(document); - this.logBytes = 0; - this.hasBeenScrolled = false; - - this.updateDropdown = this.updateDropdown.bind(this); - this.getBuildTrace = this.getBuildTrace.bind(this); - - this.$buildTrace = $('#build-trace'); - this.$buildRefreshAnimation = $('.js-build-refresh'); - this.$truncatedInfo = $('.js-truncated-info'); - this.$buildTraceOutput = $('.js-build-output'); - this.$topBar = $('.js-top-bar'); - - // Scroll controllers - this.$scrollTopBtn = $('.js-scroll-up'); - this.$scrollBottomBtn = $('.js-scroll-down'); - - clearTimeout(Build.timeout); - // Init breakpoint checker - this.bp = Breakpoints.get(); - - this.initSidebar(); - this.populateJobs(this.buildStage); - this.updateStageDropdownText(this.buildStage); - this.sidebarOnResize(); - - 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); - - // add event listeners to the scroll buttons - this.$scrollTopBtn - .off('click') - .on('click', this.scrollToTop.bind(this)); - - this.$scrollBottomBtn - .off('click') - .on('click', this.scrollToBottom.bind(this)); - - this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100); - - $(window) - .off('scroll') - .on('scroll', () => { - const contentHeight = this.$buildTraceOutput.prop('scrollHeight'); - if (contentHeight > this.windowSize) { - // means the user did not scroll, the content was updated. - this.windowSize = contentHeight; - } else { - // User scrolled - this.hasBeenScrolled = true; - this.toggleScrollAnimation(false); - } +function Build(options) { + this.options = options || $('.js-build-options').data(); + + this.pageUrl = this.options.pageUrl; + this.buildStatus = this.options.buildStatus; + this.state = this.options.logState; + this.buildStage = this.options.buildStage; + this.$document = $(document); + this.logBytes = 0; + this.hasBeenScrolled = false; + + this.updateDropdown = this.updateDropdown.bind(this); + this.getBuildTrace = this.getBuildTrace.bind(this); + + this.$buildTrace = $('#build-trace'); + this.$buildRefreshAnimation = $('.js-build-refresh'); + this.$truncatedInfo = $('.js-truncated-info'); + this.$buildTraceOutput = $('.js-build-output'); + this.$topBar = $('.js-top-bar'); + + // Scroll controllers + this.$scrollTopBtn = $('.js-scroll-up'); + this.$scrollBottomBtn = $('.js-scroll-down'); + + clearTimeout(Build.timeout); + // Init breakpoint checker + this.bp = Breakpoints.get(); + + this.initSidebar(); + this.populateJobs(this.buildStage); + this.updateStageDropdownText(this.buildStage); + this.sidebarOnResize(); + + 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); + + // add event listeners to the scroll buttons + this.$scrollTopBtn + .off('click') + .on('click', this.scrollToTop.bind(this)); + + this.$scrollBottomBtn + .off('click') + .on('click', this.scrollToBottom.bind(this)); + + this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100); + + $(window) + .off('scroll') + .on('scroll', () => { + const contentHeight = this.$buildTraceOutput.prop('scrollHeight'); + if (contentHeight > this.windowSize) { + // means the user did not scroll, the content was updated. + this.windowSize = contentHeight; + } else { + // User scrolled + this.hasBeenScrolled = true; + this.toggleScrollAnimation(false); + } - this.scrollThrottled(); - }); + this.scrollThrottled(); + }); - $(window) - .off('resize.build') - .on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100)); + $(window) + .off('resize.build') + .on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100)); - this.updateArtifactRemoveDate(); - this.initAffixTopArea(); + this.updateArtifactRemoveDate(); + this.initAffixTopArea(); - this.getBuildTrace(); - } + this.getBuildTrace(); +} - Build.prototype.initAffixTopArea = function () { - /** - If the browser does not support position sticky, it returns the position as static. - If the browser does support sticky, then we allow the browser to handle it, if not - then we default back to Bootstraps affix - **/ - if (this.$topBar.css('position') !== 'static') return; +Build.timeout = null; +Build.state = null; - const offsetTop = this.$buildTrace.offset().top; +Build.prototype.initAffixTopArea = function () { + /** + If the browser does not support position sticky, it returns the position as static. + If the browser does support sticky, then we allow the browser to handle it, if not + then we default back to Bootstraps affix + **/ + if (this.$topBar.css('position') !== 'static') return; - this.$topBar.affix({ - offset: { - top: offsetTop, - }, - }); - }; + const offsetTop = this.$buildTrace.offset().top; - Build.prototype.canScroll = function () { - return document.body.scrollHeight > window.innerHeight; - }; + this.$topBar.affix({ + offset: { + top: offsetTop, + }, + }); +}; - Build.prototype.toggleScroll = function () { - const currentPosition = document.body.scrollTop; - const windowHeight = window.innerHeight; +Build.prototype.canScroll = function () { + return document.body.scrollHeight > window.innerHeight; +}; - if (this.canScroll()) { - if (currentPosition > 0 && - (document.body.scrollHeight - currentPosition !== windowHeight)) { - // User is in the middle of the log +Build.prototype.toggleScroll = function () { + const currentPosition = document.body.scrollTop; + const windowHeight = window.innerHeight; - this.toggleDisableButton(this.$scrollTopBtn, false); - this.toggleDisableButton(this.$scrollBottomBtn, false); - } else if (currentPosition === 0) { - // User is at Top of Build Log + if (this.canScroll()) { + if (currentPosition > 0 && + (document.body.scrollHeight - currentPosition !== windowHeight)) { + // User is in the middle of the log - this.toggleDisableButton(this.$scrollTopBtn, true); - this.toggleDisableButton(this.$scrollBottomBtn, false); - } else if (document.body.scrollHeight - currentPosition === windowHeight) { - // User is at the bottom of the build log. + this.toggleDisableButton(this.$scrollTopBtn, false); + this.toggleDisableButton(this.$scrollBottomBtn, false); + } else if (currentPosition === 0) { + // User is at Top of Build Log - this.toggleDisableButton(this.$scrollTopBtn, false); - this.toggleDisableButton(this.$scrollBottomBtn, true); - } - } else { this.toggleDisableButton(this.$scrollTopBtn, true); + this.toggleDisableButton(this.$scrollBottomBtn, false); + } else if (document.body.scrollHeight - currentPosition === windowHeight) { + // User is at the bottom of the build log. + + this.toggleDisableButton(this.$scrollTopBtn, false); this.toggleDisableButton(this.$scrollBottomBtn, true); } - }; - - Build.prototype.scrollDown = function () { - document.body.scrollTop = document.body.scrollHeight; - }; - - Build.prototype.scrollToBottom = function () { - this.scrollDown(); - this.hasBeenScrolled = true; - this.toggleScroll(); - }; - - Build.prototype.scrollToTop = function () { - document.body.scrollTop = 0; - this.hasBeenScrolled = true; - this.toggleScroll(); - }; - - Build.prototype.toggleDisableButton = function ($button, disable) { - if (disable && $button.prop('disabled')) return; - $button.prop('disabled', disable); - }; - - Build.prototype.toggleScrollAnimation = function (toggle) { - this.$scrollBottomBtn.toggleClass('animate', toggle); - }; - - Build.prototype.initSidebar = function () { - this.$sidebar = $('.js-build-sidebar'); - this.$sidebar.niceScroll(); - }; - - Build.prototype.getBuildTrace = function () { - return $.ajax({ - url: `${this.pageUrl}/trace.json`, - data: this.state, - }) - .done((log) => { - gl.utils.setCiStatusFavicon(`${this.pageUrl}/status.json`); - - if (log.state) { - this.state = log.state; - } - - this.windowSize = this.$buildTraceOutput.prop('scrollHeight'); + } else { + this.toggleDisableButton(this.$scrollTopBtn, true); + this.toggleDisableButton(this.$scrollBottomBtn, true); + } +}; + +Build.prototype.scrollDown = function () { + document.body.scrollTop = document.body.scrollHeight; +}; + +Build.prototype.scrollToBottom = function () { + this.scrollDown(); + this.hasBeenScrolled = true; + this.toggleScroll(); +}; + +Build.prototype.scrollToTop = function () { + document.body.scrollTop = 0; + this.hasBeenScrolled = true; + this.toggleScroll(); +}; + +Build.prototype.toggleDisableButton = function ($button, disable) { + if (disable && $button.prop('disabled')) return; + $button.prop('disabled', disable); +}; + +Build.prototype.toggleScrollAnimation = function (toggle) { + this.$scrollBottomBtn.toggleClass('animate', toggle); +}; + +Build.prototype.initSidebar = function () { + this.$sidebar = $('.js-build-sidebar'); + this.$sidebar.niceScroll(); +}; + +Build.prototype.getBuildTrace = function () { + return $.ajax({ + url: `${this.pageUrl}/trace.json`, + data: this.state, + }) + .done((log) => { + gl.utils.setCiStatusFavicon(`${this.pageUrl}/status.json`); + + if (log.state) { + this.state = log.state; + } - if (log.append) { - this.$buildTraceOutput.append(log.html); - this.logBytes += log.size; - } else { - this.$buildTraceOutput.html(log.html); - this.logBytes = log.size; - } + this.windowSize = this.$buildTraceOutput.prop('scrollHeight'); - // if the incremental sum of logBytes we received is less than the total - // we need to show a message warning the user about that. - if (this.logBytes < log.total) { - // size is in bytes, we need to calculate KiB - const size = bytesToKiB(this.logBytes); - $('.js-truncated-info-size').html(`${size}`); - this.$truncatedInfo.removeClass('hidden'); - } else { - this.$truncatedInfo.addClass('hidden'); - } + if (log.append) { + this.$buildTraceOutput.append(log.html); + this.logBytes += log.size; + } else { + this.$buildTraceOutput.html(log.html); + this.logBytes = log.size; + } - if (!log.complete) { - if (!this.hasBeenScrolled) { - this.toggleScrollAnimation(true); - } else { - this.toggleScrollAnimation(false); - } + // if the incremental sum of logBytes we received is less than the total + // we need to show a message warning the user about that. + if (this.logBytes < log.total) { + // size is in bytes, we need to calculate KiB + const size = bytesToKiB(this.logBytes); + $('.js-truncated-info-size').html(`${size}`); + this.$truncatedInfo.removeClass('hidden'); + } else { + this.$truncatedInfo.addClass('hidden'); + } - Build.timeout = setTimeout(() => { - this.getBuildTrace(); - }, 4000); + if (!log.complete) { + if (!this.hasBeenScrolled) { + this.toggleScrollAnimation(true); } else { - this.$buildRefreshAnimation.remove(); this.toggleScrollAnimation(false); } - if (log.status !== this.buildStatus) { - gl.utils.visitUrl(this.pageUrl); - } - }) - .fail(() => { + Build.timeout = setTimeout(() => { + this.getBuildTrace(); + }, 4000); + } else { this.$buildRefreshAnimation.remove(); - }) - .then(() => { - if (!this.hasBeenScrolled) { - this.scrollDown(); - } - }) - .then(() => this.toggleScroll()); - }; - - Build.prototype.shouldHideSidebarForViewport = function () { - const bootstrapBreakpoint = this.bp.getBreakpointSize(); - return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm'; - }; - - Build.prototype.toggleSidebar = function (shouldHide) { - const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined; - const $toggleButton = $('.js-sidebar-build-toggle-header'); - - this.$sidebar - .toggleClass('right-sidebar-expanded', shouldShow) - .toggleClass('right-sidebar-collapsed', shouldHide); - - this.$topBar - .toggleClass('sidebar-expanded', shouldShow) - .toggleClass('sidebar-collapsed', shouldHide); - - if (this.$sidebar.hasClass('right-sidebar-expanded')) { - $toggleButton.addClass('hidden'); - } else { - $toggleButton.removeClass('hidden'); - } - }; - - Build.prototype.sidebarOnResize = function () { - this.toggleSidebar(this.shouldHideSidebarForViewport()); - }; - - Build.prototype.sidebarOnClick = function () { - if (this.shouldHideSidebarForViewport()) this.toggleSidebar(); - }; - - Build.prototype.updateArtifactRemoveDate = function () { - const $date = $('.js-artifacts-remove'); - if ($date.length) { - const date = $date.text(); - return $date.text( - gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '), - ); - } - }; - - Build.prototype.populateJobs = function (stage) { - $('.build-job').hide(); - $(`.build-job[data-stage="${stage}"]`).show(); - }; - - Build.prototype.updateStageDropdownText = function (stage) { - $('.stage-selection').text(stage); - }; - - Build.prototype.updateDropdown = function (e) { - e.preventDefault(); - const stage = e.currentTarget.text; - this.updateStageDropdownText(stage); - this.populateJobs(stage); - }; - - return Build; -})(); + this.toggleScrollAnimation(false); + } + + if (log.status !== this.buildStatus) { + gl.utils.visitUrl(this.pageUrl); + } + }) + .fail(() => { + this.$buildRefreshAnimation.remove(); + }) + .then(() => { + if (!this.hasBeenScrolled) { + this.scrollDown(); + } + }) + .then(() => this.toggleScroll()); +}; + +Build.prototype.shouldHideSidebarForViewport = function () { + const bootstrapBreakpoint = this.bp.getBreakpointSize(); + return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm'; +}; + +Build.prototype.toggleSidebar = function (shouldHide) { + const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined; + const $toggleButton = $('.js-sidebar-build-toggle-header'); + + this.$sidebar + .toggleClass('right-sidebar-expanded', shouldShow) + .toggleClass('right-sidebar-collapsed', shouldHide); + + this.$topBar + .toggleClass('sidebar-expanded', shouldShow) + .toggleClass('sidebar-collapsed', shouldHide); + + if (this.$sidebar.hasClass('right-sidebar-expanded')) { + $toggleButton.addClass('hidden'); + } else { + $toggleButton.removeClass('hidden'); + } +}; + +Build.prototype.sidebarOnResize = function () { + this.toggleSidebar(this.shouldHideSidebarForViewport()); +}; + +Build.prototype.sidebarOnClick = function () { + if (this.shouldHideSidebarForViewport()) this.toggleSidebar(); +}; + +Build.prototype.updateArtifactRemoveDate = function () { + const $date = $('.js-artifacts-remove'); + if ($date.length) { + const date = $date.text(); + return $date.text( + gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '), + ); + } +}; + +Build.prototype.populateJobs = function (stage) { + $('.build-job').hide(); + $(`.build-job[data-stage="${stage}"]`).show(); +}; + +Build.prototype.updateStageDropdownText = function (stage) { + $('.stage-selection').text(stage); +}; + +Build.prototype.updateDropdown = function (e) { + e.preventDefault(); + const stage = e.currentTarget.text; + this.updateStageDropdownText(stage); + this.populateJobs(stage); +}; + +window.Build = Build; diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js index bd479700fd3..ed9ad3c6c57 100644 --- a/app/assets/javascripts/build_artifacts.js +++ b/app/assets/javascripts/build_artifacts.js @@ -1,25 +1,23 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, max-len */ -window.BuildArtifacts = (function() { - function BuildArtifacts() { - this.disablePropagation(); - this.setupEntryClick(); - } +function BuildArtifacts() { + this.disablePropagation(); + this.setupEntryClick(); +} - BuildArtifacts.prototype.disablePropagation = function() { - $('.top-block').on('click', '.download', function(e) { - return e.stopPropagation(); - }); - return $('.tree-holder').on('click', 'tr[data-link] a', function(e) { - return e.stopImmediatePropagation(); - }); - }; +BuildArtifacts.prototype.disablePropagation = function() { + $('.top-block').on('click', '.download', function(e) { + return e.stopPropagation(); + }); + return $('.tree-holder').on('click', 'tr[data-link] a', function(e) { + return e.stopImmediatePropagation(); + }); +}; - BuildArtifacts.prototype.setupEntryClick = function() { - return $('.tree-holder').on('click', 'tr[data-link]', function(e) { - return window.location = this.dataset.link; - }); - }; +BuildArtifacts.prototype.setupEntryClick = function() { + return $('.tree-holder').on('click', 'tr[data-link]', function(e) { + return window.location = this.dataset.link; + }); +}; - return BuildArtifacts; -})(); +window.BuildArtifacts = BuildArtifacts; diff --git a/app/assets/javascripts/commit.js b/app/assets/javascripts/commit.js index 5f637524e30..e3f8355915a 100644 --- a/app/assets/javascripts/commit.js +++ b/app/assets/javascripts/commit.js @@ -1,12 +1,10 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife */ /* global CommitFile */ -window.Commit = (function() { - function Commit() { - $('.files .diff-file').each(function() { - return new CommitFile(this); - }); - } +function Commit() { + $('.files .diff-file').each(function() { + return new CommitFile(this); + }); +} - return Commit; -})(); +window.Commit = Commit; diff --git a/app/assets/javascripts/commit/file.js b/app/assets/javascripts/commit/file.js index ee087c978dd..28e5e06b155 100644 --- a/app/assets/javascripts/commit/file.js +++ b/app/assets/javascripts/commit/file.js @@ -1,14 +1,10 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new */ /* global ImageFile */ -(function() { - this.CommitFile = (function() { - function CommitFile(file) { - if ($('.image', file).length) { - new gl.ImageFile(file); - } - } +function CommitFile(file) { + if ($('.image', file).length) { + new gl.ImageFile(file); + } +} - return CommitFile; - })(); -}).call(window); +window.CommitFile = CommitFile; diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js index 17d14dc1e79..b768ba5b17b 100644 --- a/app/assets/javascripts/commit/image_file.js +++ b/app/assets/javascripts/commit/image_file.js @@ -1,211 +1,209 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */ -(function() { - gl.ImageFile = (function() { - var prepareFrames; - - // Width where images must fits in, for 2-up this gets divided by 2 - ImageFile.availWidth = 900; - - ImageFile.viewModes = ['two-up', 'swipe']; - - function ImageFile(file) { - this.file = file; - this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) { - // Determine if old and new file has same dimensions, if not show 'two-up' view - return function(deletedWidth, deletedHeight) { - return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) { - if (width === deletedWidth && height === deletedHeight) { - return _this.initViewModes(); - } else { - return _this.initView('two-up'); - } - }); - }; - })(this)); - } - - ImageFile.prototype.initViewModes = function() { - var viewMode; - viewMode = ImageFile.viewModes[0]; - $('.view-modes', this.file).removeClass('hide'); - $('.view-modes-menu', this.file).on('click', 'li', (function(_this) { - return function(event) { - if (!$(event.currentTarget).hasClass('active')) { - return _this.activateViewMode(event.currentTarget.className); - } - }; - })(this)); - return this.activateViewMode(viewMode); - }; - - ImageFile.prototype.activateViewMode = function(viewMode) { - $('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active'); - return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) { - return function() { - $(".view." + viewMode, _this.file).fadeIn(200); - return _this.initView(viewMode); - }; - })(this)); - }; - - ImageFile.prototype.initView = function(viewMode) { - return this.views[viewMode].call(this); - }; - - ImageFile.prototype.initDraggable = function($el, padding, callback) { - var dragging = false; - var $body = $('body'); - var $offsetEl = $el.parent(); - - $el.off('mousedown').on('mousedown', function() { - dragging = true; - $body.css('user-select', 'none'); - }); - - $body.off('mouseup').off('mousemove').on('mouseup', function() { - dragging = false; - $body.css('user-select', ''); - }) - .on('mousemove', function(e) { - var left; - if (!dragging) return; - - left = e.pageX - ($offsetEl.offset().left + padding); - - callback(e, left); - }); - }; - - prepareFrames = function(view) { - var maxHeight, maxWidth; - maxWidth = 0; - maxHeight = 0; - $('.frame', view).each((function(_this) { - return function(index, frame) { - var height, width; - width = $(frame).width(); - height = $(frame).height(); - maxWidth = width > maxWidth ? width : maxWidth; - return maxHeight = height > maxHeight ? height : maxHeight; - }; - })(this)).css({ - width: maxWidth, - height: maxHeight +var prepareFrames; + +const global = window.gl || (window.gl = {}); + +function ImageFile(file) { + this.file = file; + this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) { + // Determine if old and new file has same dimensions, if not show 'two-up' view + return function(deletedWidth, deletedHeight) { + return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) { + if (width === deletedWidth && height === deletedHeight) { + return _this.initViewModes(); + } else { + return _this.initView('two-up'); + } }); - return [maxWidth, maxHeight]; }; - - ImageFile.prototype.views = { - 'two-up': function() { - return $('.two-up.view .wrap', this.file).each((function(_this) { - return function(index, wrap) { - $('img', wrap).each(function() { - var currentWidth; - currentWidth = $(this).width(); - if (currentWidth > ImageFile.availWidth / 2) { - return $(this).width(ImageFile.availWidth / 2); - } - }); - return _this.requestImageInfo($('img', wrap), function(width, height) { - $('.image-info .meta-width', wrap).text(width + "px"); - $('.image-info .meta-height', wrap).text(height + "px"); - return $('.image-info', wrap).removeClass('hide'); - }); - }; - })(this)); - }, - 'swipe': function() { - var maxHeight, maxWidth; - maxWidth = 0; - maxHeight = 0; - return $('.swipe.view', this.file).each((function(_this) { - return function(index, view) { - var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref; - ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; - $swipeFrame = $('.swipe-frame', view); - $swipeWrap = $('.swipe-wrap', view); - $swipeBar = $('.swipe-bar', view); - - $swipeFrame.css({ - width: maxWidth + 16, - height: maxHeight + 28 - }); - $swipeWrap.css({ - width: maxWidth + 1, - height: maxHeight + 2 - }); - $swipeBar.css({ - left: 0 - }); - - wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); - - _this.initDraggable($swipeBar, wrapPadding, function(e, left) { - if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) { - $swipeWrap.width((maxWidth + 1) - left); - $swipeBar.css('left', left); - } - }); - }; - })(this)); - }, - 'onion-skin': function() { - var dragTrackWidth, maxHeight, maxWidth; - maxWidth = 0; - maxHeight = 0; - dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width(); - return $('.onion-skin.view', this.file).each((function(_this) { - return function(index, view) { - var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false; - ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; - $frame = $('.onion-skin-frame', view); - $frameAdded = $('.frame.added', view); - $track = $('.drag-track', view); - $dragger = $('.dragger', $track); - - $frame.css({ - width: maxWidth + 16, - height: maxHeight + 28 - }); - $('.swipe-wrap', view).css({ - width: maxWidth + 1, - height: maxHeight + 2 - }); - $dragger.css({ - left: dragTrackWidth - }); - - framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); - - _this.initDraggable($dragger, framePadding, function(e, left) { - var opacity = left / dragTrackWidth; - - if (opacity >= 0 && opacity <= 1) { - $dragger.css('left', left); - $frameAdded.css('opacity', opacity); - } - }); - }; - })(this)); + })(this)); +} + +// Width where images must fits in, for 2-up this gets divided by 2 +ImageFile.availWidth = 900; + +ImageFile.viewModes = ['two-up', 'swipe']; + +ImageFile.prototype.initViewModes = function() { + var viewMode; + viewMode = ImageFile.viewModes[0]; + $('.view-modes', this.file).removeClass('hide'); + $('.view-modes-menu', this.file).on('click', 'li', (function(_this) { + return function(event) { + if (!$(event.currentTarget).hasClass('active')) { + return _this.activateViewMode(event.currentTarget.className); } }; - - ImageFile.prototype.requestImageInfo = function(img, callback) { - var domImg; - domImg = img.get(0); - if (domImg) { - if (domImg.complete) { - return callback.call(this, domImg.naturalWidth, domImg.naturalHeight); - } else { - return img.on('load', (function(_this) { - return function() { - return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight); - }; - })(this)); - } - } + })(this)); + return this.activateViewMode(viewMode); +}; + +ImageFile.prototype.activateViewMode = function(viewMode) { + $('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active'); + return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) { + return function() { + $(".view." + viewMode, _this.file).fadeIn(200); + return _this.initView(viewMode); + }; + })(this)); +}; + +ImageFile.prototype.initView = function(viewMode) { + return this.views[viewMode].call(this); +}; + +ImageFile.prototype.initDraggable = function($el, padding, callback) { + var dragging = false; + var $body = $('body'); + var $offsetEl = $el.parent(); + + $el.off('mousedown').on('mousedown', function() { + dragging = true; + $body.css('user-select', 'none'); + }); + + $body.off('mouseup').off('mousemove').on('mouseup', function() { + dragging = false; + $body.css('user-select', ''); + }) + .on('mousemove', function(e) { + var left; + if (!dragging) return; + + left = e.pageX - ($offsetEl.offset().left + padding); + + callback(e, left); + }); +}; + +prepareFrames = function(view) { + var maxHeight, maxWidth; + maxWidth = 0; + maxHeight = 0; + $('.frame', view).each((function(_this) { + return function(index, frame) { + var height, width; + width = $(frame).width(); + height = $(frame).height(); + maxWidth = width > maxWidth ? width : maxWidth; + return maxHeight = height > maxHeight ? height : maxHeight; }; + })(this)).css({ + width: maxWidth, + height: maxHeight + }); + return [maxWidth, maxHeight]; +}; + +ImageFile.prototype.views = { + 'two-up': function() { + return $('.two-up.view .wrap', this.file).each((function(_this) { + return function(index, wrap) { + $('img', wrap).each(function() { + var currentWidth; + currentWidth = $(this).width(); + if (currentWidth > ImageFile.availWidth / 2) { + return $(this).width(ImageFile.availWidth / 2); + } + }); + return _this.requestImageInfo($('img', wrap), function(width, height) { + $('.image-info .meta-width', wrap).text(width + "px"); + $('.image-info .meta-height', wrap).text(height + "px"); + return $('.image-info', wrap).removeClass('hide'); + }); + }; + })(this)); + }, + 'swipe': function() { + var maxHeight, maxWidth; + maxWidth = 0; + maxHeight = 0; + return $('.swipe.view', this.file).each((function(_this) { + return function(index, view) { + var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref; + ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; + $swipeFrame = $('.swipe-frame', view); + $swipeWrap = $('.swipe-wrap', view); + $swipeBar = $('.swipe-bar', view); + + $swipeFrame.css({ + width: maxWidth + 16, + height: maxHeight + 28 + }); + $swipeWrap.css({ + width: maxWidth + 1, + height: maxHeight + 2 + }); + $swipeBar.css({ + left: 0 + }); + + wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); + + _this.initDraggable($swipeBar, wrapPadding, function(e, left) { + if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) { + $swipeWrap.width((maxWidth + 1) - left); + $swipeBar.css('left', left); + } + }); + }; + })(this)); + }, + 'onion-skin': function() { + var dragTrackWidth, maxHeight, maxWidth; + maxWidth = 0; + maxHeight = 0; + dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width(); + return $('.onion-skin.view', this.file).each((function(_this) { + return function(index, view) { + var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false; + ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; + $frame = $('.onion-skin-frame', view); + $frameAdded = $('.frame.added', view); + $track = $('.drag-track', view); + $dragger = $('.dragger', $track); + + $frame.css({ + width: maxWidth + 16, + height: maxHeight + 28 + }); + $('.swipe-wrap', view).css({ + width: maxWidth + 1, + height: maxHeight + 2 + }); + $dragger.css({ + left: dragTrackWidth + }); + + framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); + + _this.initDraggable($dragger, framePadding, function(e, left) { + var opacity = left / dragTrackWidth; + + if (opacity >= 0 && opacity <= 1) { + $dragger.css('left', left); + $frameAdded.css('opacity', opacity); + } + }); + }; + })(this)); + } +}; + +ImageFile.prototype.requestImageInfo = function(img, callback) { + var domImg; + domImg = img.get(0); + if (domImg) { + if (domImg.complete) { + return callback.call(this, domImg.naturalWidth, domImg.naturalHeight); + } else { + return img.on('load', (function(_this) { + return function() { + return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight); + }; + })(this)); + } + } +}; - return ImageFile; - })(); -}).call(window); +global.ImageFile = ImageFile; diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index 2b0bf49cf92..b7db5ded846 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -1,97 +1,93 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, consistent-return, no-return-assign, no-param-reassign, one-var, no-var, one-var-declaration-per-line, no-unused-vars, prefer-template, object-shorthand, comma-dangle, max-len, prefer-arrow-callback */ /* global Pager */ -window.CommitsList = (function() { - var CommitsList = {}; +var CommitsList = {}; - CommitsList.timer = null; +CommitsList.timer = null; - CommitsList.init = function(limit) { - this.$contentList = $('.content_list'); +CommitsList.init = function(limit) { + this.$contentList = $('.content_list'); - $("body").on("click", ".day-commits-table li.commit", function(e) { - if (e.target.nodeName !== "A") { - location.href = $(this).attr("url"); - e.stopPropagation(); - return false; - } - }); + $("body").on("click", ".day-commits-table li.commit", function(e) { + if (e.target.nodeName !== "A") { + location.href = $(this).attr("url"); + e.stopPropagation(); + return false; + } + }); - Pager.init(limit, false, false, this.processCommits); + Pager.init(limit, false, false, this.processCommits); - this.content = $("#commits-list"); - this.searchField = $("#commits-search"); - this.lastSearch = this.searchField.val(); - return this.initSearch(); - }; + this.content = $("#commits-list"); + this.searchField = $("#commits-search"); + this.lastSearch = this.searchField.val(); + return this.initSearch(); +}; - CommitsList.initSearch = function() { - this.timer = null; - return this.searchField.keyup((function(_this) { - return function() { - clearTimeout(_this.timer); - return _this.timer = setTimeout(_this.filterResults, 500); - }; - })(this)); - }; +CommitsList.initSearch = function() { + this.timer = null; + return this.searchField.keyup(() => { + clearTimeout(this.timer); + return this.timer = setTimeout(this.filterResults, 500); + }); +}; - CommitsList.filterResults = function() { - 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({ - type: "GET", - url: form.attr("action"), - data: form.serialize(), - complete: function() { - 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" - }); - }; +CommitsList.filterResults = function() { + 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({ + type: "GET", + url: form.attr("action"), + data: form.serialize(), + complete: function() { + 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" + }); +}; - // Prepare loaded data. - CommitsList.processCommits = (data) => { - let processedData = data; - const $processedData = $(processedData); - const $commitsHeadersLast = CommitsList.$contentList.find('li.js-commit-header').last(); - const lastShownDay = $commitsHeadersLast.data('day'); - const $loadedCommitsHeadersFirst = $processedData.filter('li.js-commit-header').first(); - const loadedShownDayFirst = $loadedCommitsHeadersFirst.data('day'); - let commitsCount; +// Prepare loaded data. +CommitsList.processCommits = (data) => { + let processedData = data; + const $processedData = $(processedData); + const $commitsHeadersLast = CommitsList.$contentList.find('li.js-commit-header').last(); + const lastShownDay = $commitsHeadersLast.data('day'); + const $loadedCommitsHeadersFirst = $processedData.filter('li.js-commit-header').first(); + const loadedShownDayFirst = $loadedCommitsHeadersFirst.data('day'); + let commitsCount; - // If commits headers show the same date, - // remove the last header and change the previous one. - if (lastShownDay === loadedShownDayFirst) { - // Last shown commits count under the last commits header. - commitsCount = $commitsHeadersLast.nextUntil('li.js-commit-header').find('li.commit').length; + // If commits headers show the same date, + // remove the last header and change the previous one. + if (lastShownDay === loadedShownDayFirst) { + // Last shown commits count under the last commits header. + commitsCount = $commitsHeadersLast.nextUntil('li.js-commit-header').find('li.commit').length; - // Remove duplicate of commits header. - processedData = $processedData.not(`li.js-commit-header[data-day="${loadedShownDayFirst}"]`); + // Remove duplicate of commits header. + processedData = $processedData.not(`li.js-commit-header[data-day="${loadedShownDayFirst}"]`); - // Update commits count in the previous commits header. - commitsCount += Number($(processedData).nextUntil('li.js-commit-header').first().find('li.commit').length); - $commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${gl.text.pluralize('commit', commitsCount)}`); - } + // Update commits count in the previous commits header. + commitsCount += Number($(processedData).nextUntil('li.js-commit-header').first().find('li.commit').length); + $commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${gl.text.pluralize('commit', commitsCount)}`); + } - gl.utils.localTimeAgo($processedData.find('.js-timeago')); + gl.utils.localTimeAgo($processedData.find('.js-timeago')); - return processedData; - }; + return processedData; +}; - return CommitsList; -})(); +window.CommitsList = CommitsList; diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js index 9e5dbd64a7e..e5321ef9cc6 100644 --- a/app/assets/javascripts/compare.js +++ b/app/assets/javascripts/compare.js @@ -1,90 +1,86 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */ -window.Compare = (function() { - function Compare(opts) { - this.opts = opts; - this.source_loading = $(".js-source-loading"); - this.target_loading = $(".js-target-loading"); - $('.js-compare-dropdown').each((function(_this) { - return function(i, dropdown) { - var $dropdown; - $dropdown = $(dropdown); - return $dropdown.glDropdown({ - selectable: true, - fieldName: $dropdown.data('field-name'), - filterable: true, - id: function(obj, $el) { - return $el.data('id'); - }, - toggleLabel: function(obj, $el) { - return $el.text().trim(); - }, - clicked: function(e, el) { - if ($dropdown.is('.js-target-branch')) { - return _this.getTargetHtml(); - } else if ($dropdown.is('.js-source-branch')) { - return _this.getSourceHtml(); - } else if ($dropdown.is('.js-target-project')) { - return _this.getTargetProject(); - } - } - }); - }; - })(this)); - this.initialState(); - } - - Compare.prototype.initialState = function() { - this.getSourceHtml(); - return this.getTargetHtml(); - }; - - Compare.prototype.getTargetProject = function() { - return $.ajax({ - url: this.opts.targetProjectUrl, - data: { - target_project_id: $("input[name='merge_request[target_project_id]']").val() +function Compare(opts) { + this.opts = opts; + this.source_loading = $(".js-source-loading"); + this.target_loading = $(".js-target-loading"); + $('.js-compare-dropdown').each((i, dropdown) => { + var $dropdown; + $dropdown = $(dropdown); + return $dropdown.glDropdown({ + selectable: true, + fieldName: $dropdown.data('field-name'), + filterable: true, + id: function(obj, $el) { + return $el.data('id'); }, - beforeSend: function() { - return $('.mr_target_commit').empty(); + toggleLabel: function(obj, $el) { + return $el.text().trim(); }, - success: function(html) { - return $('.js-target-branch-dropdown .dropdown-content').html(html); + clicked: function(e, el) { + if ($dropdown.is('.js-target-branch')) { + return this.getTargetHtml(); + } else if ($dropdown.is('.js-source-branch')) { + return this.getSourceHtml(); + } else if ($dropdown.is('.js-target-project')) { + return this.getTargetProject(); + } } }); - }; + }); + this.initialState(); +} - Compare.prototype.getSourceHtml = function() { - return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', { - ref: $("input[name='merge_request[source_branch]']").val() - }); - }; +Compare.prototype.initialState = function() { + this.getSourceHtml(); + return this.getTargetHtml(); +}; - Compare.prototype.getTargetHtml = function() { - return this.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', { - target_project_id: $("input[name='merge_request[target_project_id]']").val(), - ref: $("input[name='merge_request[target_branch]']").val() - }); - }; +Compare.prototype.getTargetProject = function() { + return $.ajax({ + url: this.opts.targetProjectUrl, + data: { + target_project_id: $("input[name='merge_request[target_project_id]']").val() + }, + beforeSend: function() { + return $('.mr_target_commit').empty(); + }, + success: function(html) { + return $('.js-target-branch-dropdown .dropdown-content').html(html); + } + }); +}; - Compare.prototype.sendAjax = function(url, loading, target, data) { - var $target; - $target = $(target); - return $.ajax({ - url: url, - data: data, - beforeSend: function() { - loading.show(); - return $target.empty(); - }, - success: function(html) { - loading.hide(); - $target.html(html); - var className = '.' + $target[0].className.replace(' ', '.'); - gl.utils.localTimeAgo($('.js-timeago', className)); - } - }); - }; +Compare.prototype.getSourceHtml = function() { + return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', { + ref: $("input[name='merge_request[source_branch]']").val() + }); +}; + +Compare.prototype.getTargetHtml = function() { + return this.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', { + target_project_id: $("input[name='merge_request[target_project_id]']").val(), + ref: $("input[name='merge_request[target_branch]']").val() + }); +}; + +Compare.prototype.sendAjax = function(url, loading, target, data) { + var $target; + $target = $(target); + return $.ajax({ + url: url, + data: data, + beforeSend: function() { + loading.show(); + return $target.empty(); + }, + success: function(html) { + loading.hide(); + $target.html(html); + var className = '.' + $target[0].className.replace(' ', '.'); + gl.utils.localTimeAgo($('.js-timeago', className)); + } + }); +}; - return Compare; -})(); +window.Compare = Compare; diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js index 72c0d98d47c..e70f78d16a8 100644 --- a/app/assets/javascripts/compare_autocomplete.js +++ b/app/assets/javascripts/compare_autocomplete.js @@ -1,68 +1,66 @@ /* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, no-else-return, newline-per-chained-call, wrap-iife, max-len */ -window.CompareAutocomplete = (function() { - function CompareAutocomplete() { - this.initDropdown(); - } +function CompareAutocomplete() { + this.initDropdown(); +} - CompareAutocomplete.prototype.initDropdown = function() { - return $('.js-compare-dropdown').each(function() { - var $dropdown, selected; - $dropdown = $(this); - selected = $dropdown.data('selected'); - const $dropdownContainer = $dropdown.closest('.dropdown'); - const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer); - const $filterInput = $('input[type="search"]', $dropdownContainer); - $dropdown.glDropdown({ - data: function(term, callback) { - return $.ajax({ - url: $dropdown.data('refs-url'), - data: { - ref: $dropdown.data('ref'), - search: term, - } - }).done(function(refs) { - return callback(refs); - }); - }, - selectable: true, - filterable: true, - filterRemote: true, - fieldName: $dropdown.data('field-name'), - filterInput: 'input[type="search"]', - renderRow: function(ref) { - var link; - if (ref.header != null) { - return $('<li />').addClass('dropdown-header').text(ref.header); - } else { - link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref)); - return $('<li />').append(link); +CompareAutocomplete.prototype.initDropdown = function() { + return $('.js-compare-dropdown').each(function() { + var $dropdown, selected; + $dropdown = $(this); + selected = $dropdown.data('selected'); + const $dropdownContainer = $dropdown.closest('.dropdown'); + const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer); + const $filterInput = $('input[type="search"]', $dropdownContainer); + $dropdown.glDropdown({ + data: function(term, callback) { + return $.ajax({ + url: $dropdown.data('refs-url'), + data: { + ref: $dropdown.data('ref'), + search: term, } - }, - id: function(obj, $el) { - return $el.attr('data-ref'); - }, - toggleLabel: function(obj, $el) { - return $el.text().trim(); + }).done(function(refs) { + return callback(refs); + }); + }, + selectable: true, + filterable: true, + filterRemote: true, + fieldName: $dropdown.data('field-name'), + filterInput: 'input[type="search"]', + renderRow: function(ref) { + var link; + if (ref.header != null) { + return $('<li />').addClass('dropdown-header').text(ref.header); + } else { + link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref)); + return $('<li />').append(link); } - }); - $filterInput.on('keyup', (e) => { - const keyCode = e.keyCode || e.which; - if (keyCode !== 13) return; - const text = $filterInput.val(); - $fieldInput.val(text); - $('.dropdown-toggle-text', $dropdown).text(text); - $dropdownContainer.removeClass('open'); - }); + }, + id: function(obj, $el) { + return $el.attr('data-ref'); + }, + toggleLabel: function(obj, $el) { + return $el.text().trim(); + } + }); + $filterInput.on('keyup', (e) => { + const keyCode = e.keyCode || e.which; + if (keyCode !== 13) return; + const text = $filterInput.val(); + $fieldInput.val(text); + $('.dropdown-toggle-text', $dropdown).text(text); + $dropdownContainer.removeClass('open'); + }); - $dropdownContainer.on('click', '.dropdown-content a', (e) => { - $dropdown.prop('title', e.target.text.replace(/_+?/g, '-')); - if ($dropdown.hasClass('has-tooltip')) { - $dropdown.tooltip('fixTitle'); - } - }); + $dropdownContainer.on('click', '.dropdown-content a', (e) => { + $dropdown.prop('title', e.target.text.replace(/_+?/g, '-')); + if ($dropdown.hasClass('has-tooltip')) { + $dropdown.tooltip('fixTitle'); + } }); - }; + }); +}; - return CompareAutocomplete; -})(); +window.CompareAutocomplete = CompareAutocomplete; diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index b375b61202e..14bbc170699 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -1,30 +1,24 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, camelcase, one-var-declaration-per-line, no-else-return, max-len */ -window.ConfirmDangerModal = (function() { - function ConfirmDangerModal(form, text) { - var project_path, submit; - this.form = form; - $('.js-confirm-text').text(text || ''); - $('.js-confirm-danger-input').val(''); - $('#modal-confirm-danger').modal('show'); - project_path = $('.js-confirm-danger-match').text(); - submit = $('.js-confirm-danger-submit'); - submit.disable(); - $('.js-confirm-danger-input').off('input'); - $('.js-confirm-danger-input').on('input', function() { - if (gl.utils.rstrip($(this).val()) === project_path) { - return submit.enable(); - } else { - return submit.disable(); - } - }); - $('.js-confirm-danger-submit').off('click'); - $('.js-confirm-danger-submit').on('click', (function(_this) { - return function() { - return _this.form.submit(); - }; - })(this)); - } +function ConfirmDangerModal(form, text) { + var project_path, submit; + this.form = form; + $('.js-confirm-text').text(text || ''); + $('.js-confirm-danger-input').val(''); + $('#modal-confirm-danger').modal('show'); + project_path = $('.js-confirm-danger-match').text(); + submit = $('.js-confirm-danger-submit'); + submit.disable(); + $('.js-confirm-danger-input').off('input'); + $('.js-confirm-danger-input').on('input', function() { + if (gl.utils.rstrip($(this).val()) === project_path) { + return submit.enable(); + } else { + return submit.disable(); + } + }); + $('.js-confirm-danger-submit').off('click'); + $('.js-confirm-danger-submit').on('click', () => this.form.submit()); +} - return ConfirmDangerModal; -})(); +window.ConfirmDangerModal = ConfirmDangerModal; diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index 73675d300be..6fd567c76bb 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -3,300 +3,298 @@ import './preview_markdown'; -window.DropzoneInput = (function() { - function DropzoneInput(form) { - var updateAttachingMessage, $attachingFileMessage, $mdArea, $attachButton, $cancelButton, $retryLink, $uploadingErrorContainer, $uploadingErrorMessage, $uploadProgress, $uploadingProgressContainer, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divHover, divSpinner, dropzone, $formDropzone, formTextarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, maxFileSize, pasteText, uploadsPath, showError, showSpinner, uploadFile, addFileToForm; - Dropzone.autoDiscover = false; - divHover = '<div class="div-dropzone-hover"></div>'; - iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>'; - $attachButton = form.find('.button-attach-file'); - $attachingFileMessage = form.find('.attaching-file-message'); - $cancelButton = form.find('.button-cancel-uploading-files'); - $retryLink = form.find('.retry-uploading-link'); - $uploadProgress = form.find('.uploading-progress'); - $uploadingErrorContainer = form.find('.uploading-error-container'); - $uploadingErrorMessage = form.find('.uploading-error-message'); - $uploadingProgressContainer = form.find('.uploading-progress-container'); - uploadsPath = window.uploads_path || null; - maxFileSize = gon.max_file_size || 10; - formTextarea = form.find('.js-gfm-input'); - formTextarea.wrap('<div class="div-dropzone"></div>'); - formTextarea.on('paste', (function(_this) { - return function(event) { - return handlePaste(event); - }; - })(this)); - - // Add dropzone area to the form. - $mdArea = formTextarea.closest('.md-area'); - form.setupMarkdownPreview(); - $formDropzone = form.find('.div-dropzone'); - $formDropzone.parent().addClass('div-dropzone-wrapper'); - $formDropzone.append(divHover); - $formDropzone.find('.div-dropzone-hover').append(iconPaperclip); - - if (!uploadsPath) return; - - dropzone = $formDropzone.dropzone({ +function DropzoneInput(form) { + var updateAttachingMessage, $attachingFileMessage, $mdArea, $attachButton, $cancelButton, $retryLink, $uploadingErrorContainer, $uploadingErrorMessage, $uploadProgress, $uploadingProgressContainer, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divHover, divSpinner, dropzone, $formDropzone, formTextarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, maxFileSize, pasteText, uploadsPath, showError, showSpinner, uploadFile, addFileToForm; + Dropzone.autoDiscover = false; + divHover = '<div class="div-dropzone-hover"></div>'; + iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>'; + $attachButton = form.find('.button-attach-file'); + $attachingFileMessage = form.find('.attaching-file-message'); + $cancelButton = form.find('.button-cancel-uploading-files'); + $retryLink = form.find('.retry-uploading-link'); + $uploadProgress = form.find('.uploading-progress'); + $uploadingErrorContainer = form.find('.uploading-error-container'); + $uploadingErrorMessage = form.find('.uploading-error-message'); + $uploadingProgressContainer = form.find('.uploading-progress-container'); + uploadsPath = window.uploads_path || null; + maxFileSize = gon.max_file_size || 10; + formTextarea = form.find('.js-gfm-input'); + formTextarea.wrap('<div class="div-dropzone"></div>'); + formTextarea.on('paste', (function(_this) { + return function(event) { + return handlePaste(event); + }; + })(this)); + + // Add dropzone area to the form. + $mdArea = formTextarea.closest('.md-area'); + form.setupMarkdownPreview(); + $formDropzone = form.find('.div-dropzone'); + $formDropzone.parent().addClass('div-dropzone-wrapper'); + $formDropzone.append(divHover); + $formDropzone.find('.div-dropzone-hover').append(iconPaperclip); + + if (!uploadsPath) return; + + dropzone = $formDropzone.dropzone({ + url: uploadsPath, + dictDefaultMessage: '', + clickable: true, + paramName: 'file', + maxFilesize: maxFileSize, + uploadMultiple: false, + headers: { + 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') + }, + previewContainer: false, + processing: function() { + return $('.div-dropzone-alert').alert('close'); + }, + dragover: function() { + $mdArea.addClass('is-dropzone-hover'); + form.find('.div-dropzone-hover').css('opacity', 0.7); + }, + dragleave: function() { + $mdArea.removeClass('is-dropzone-hover'); + form.find('.div-dropzone-hover').css('opacity', 0); + }, + drop: function() { + $mdArea.removeClass('is-dropzone-hover'); + form.find('.div-dropzone-hover').css('opacity', 0); + formTextarea.focus(); + }, + success: function(header, response) { + const processingFileCount = this.getQueuedFiles().length + this.getUploadingFiles().length; + const shouldPad = processingFileCount >= 1; + + pasteText(response.link.markdown, shouldPad); + // Show 'Attach a file' link only when all files have been uploaded. + if (!processingFileCount) $attachButton.removeClass('hide'); + addFileToForm(response.link.url); + }, + error: function(file, errorMessage = 'Attaching the file failed.', xhr) { + // If 'error' event is fired by dropzone, the second parameter is error message. + // If the 'errorMessage' parameter is empty, the default error message is set. + // If the 'error' event is fired by backend (xhr) error response, the third parameter is + // xhr object (xhr.responseText is error message). + // On error we hide the 'Attach' and 'Cancel' buttons + // and show an error. + + // If there's xhr error message, let's show it instead of dropzone's one. + const message = xhr ? xhr.responseText : errorMessage; + + $uploadingErrorContainer.removeClass('hide'); + $uploadingErrorMessage.html(message); + $attachButton.addClass('hide'); + $cancelButton.addClass('hide'); + }, + totaluploadprogress: function(totalUploadProgress) { + updateAttachingMessage(this.files, $attachingFileMessage); + $uploadProgress.text(Math.round(totalUploadProgress) + '%'); + }, + sending: function(file) { + // DOM elements already exist. + // Instead of dynamically generating them, + // we just either hide or show them. + $attachButton.addClass('hide'); + $uploadingErrorContainer.addClass('hide'); + $uploadingProgressContainer.removeClass('hide'); + $cancelButton.removeClass('hide'); + }, + removedfile: function() { + $attachButton.removeClass('hide'); + $cancelButton.addClass('hide'); + $uploadingProgressContainer.addClass('hide'); + $uploadingErrorContainer.addClass('hide'); + }, + queuecomplete: function() { + $('.dz-preview').remove(); + $('.markdown-area').trigger('input'); + + $uploadingProgressContainer.addClass('hide'); + $cancelButton.addClass('hide'); + } + }); + + child = $(dropzone[0]).children('textarea'); + + // removeAllFiles(true) stops uploading files (if any) + // and remove them from dropzone files queue. + $cancelButton.on('click', (e) => { + const target = e.target.closest('form').querySelector('.div-dropzone'); + + e.preventDefault(); + e.stopPropagation(); + Dropzone.forElement(target).removeAllFiles(true); + }); + + // If 'error' event is fired, we store a failed files, + // clear dropzone files queue, change status of failed files to undefined, + // and add that files to the dropzone files queue again. + // addFile() adds file to dropzone files queue and upload it. + $retryLink.on('click', (e) => { + const dropzoneInstance = Dropzone.forElement(e.target.closest('form').querySelector('.div-dropzone')); + const failedFiles = dropzoneInstance.files; + + e.preventDefault(); + + // 'true' parameter of removeAllFiles() cancels uploading of files that are being uploaded at the moment. + dropzoneInstance.removeAllFiles(true); + + failedFiles.map((failedFile, i) => { + const file = failedFile; + + if (file.status === Dropzone.ERROR) { + file.status = undefined; + file.accepted = undefined; + } + + return dropzoneInstance.addFile(file); + }); + }); + + handlePaste = function(event) { + var filename, image, pasteEvent, text; + pasteEvent = event.originalEvent; + if (pasteEvent.clipboardData && pasteEvent.clipboardData.items) { + image = isImage(pasteEvent); + if (image) { + event.preventDefault(); + filename = getFilename(pasteEvent) || 'image.png'; + text = `{{${filename}}}`; + pasteText(text); + return uploadFile(image.getAsFile(), filename); + } + } + }; + + isImage = function(data) { + var i, item; + i = 0; + while (i < data.clipboardData.items.length) { + item = data.clipboardData.items[i]; + if (item.type.indexOf('image') !== -1) { + return item; + } + i += 1; + } + return false; + }; + + pasteText = function(text, shouldPad) { + var afterSelection, beforeSelection, caretEnd, caretStart, textEnd; + var formattedText = text; + if (shouldPad) formattedText += "\n\n"; + const textarea = child.get(0); + caretStart = textarea.selectionStart; + caretEnd = textarea.selectionEnd; + textEnd = $(child).val().length; + beforeSelection = $(child).val().substring(0, caretStart); + afterSelection = $(child).val().substring(caretEnd, textEnd); + $(child).val(beforeSelection + formattedText + afterSelection); + textarea.setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length); + textarea.style.height = `${textarea.scrollHeight}px`; + formTextarea.get(0).dispatchEvent(new Event('input')); + return formTextarea.trigger('input'); + }; + + addFileToForm = function(path) { + $(form).append('<input type="hidden" name="files[]" value="' + _.escape(path) + '">'); + }; + + getFilename = function(e) { + var value; + if (window.clipboardData && window.clipboardData.getData) { + value = window.clipboardData.getData('Text'); + } else if (e.clipboardData && e.clipboardData.getData) { + value = e.clipboardData.getData('text/plain'); + } + value = value.split("\r"); + return value.first(); + }; + + uploadFile = function(item, filename) { + var formData; + formData = new FormData(); + formData.append('file', item, filename); + return $.ajax({ url: uploadsPath, - dictDefaultMessage: '', - clickable: true, - paramName: 'file', - maxFilesize: maxFileSize, - uploadMultiple: false, + type: 'POST', + data: formData, + dataType: 'json', + processData: false, + contentType: false, headers: { 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') }, - previewContainer: false, - processing: function() { - return $('.div-dropzone-alert').alert('close'); - }, - dragover: function() { - $mdArea.addClass('is-dropzone-hover'); - form.find('.div-dropzone-hover').css('opacity', 0.7); - }, - dragleave: function() { - $mdArea.removeClass('is-dropzone-hover'); - form.find('.div-dropzone-hover').css('opacity', 0); - }, - drop: function() { - $mdArea.removeClass('is-dropzone-hover'); - form.find('.div-dropzone-hover').css('opacity', 0); - formTextarea.focus(); - }, - success: function(header, response) { - const processingFileCount = this.getQueuedFiles().length + this.getUploadingFiles().length; - const shouldPad = processingFileCount >= 1; - - pasteText(response.link.markdown, shouldPad); - // Show 'Attach a file' link only when all files have been uploaded. - if (!processingFileCount) $attachButton.removeClass('hide'); - addFileToForm(response.link.url); + beforeSend: function() { + showSpinner(); + return closeAlertMessage(); }, - error: function(file, errorMessage = 'Attaching the file failed.', xhr) { - // If 'error' event is fired by dropzone, the second parameter is error message. - // If the 'errorMessage' parameter is empty, the default error message is set. - // If the 'error' event is fired by backend (xhr) error response, the third parameter is - // xhr object (xhr.responseText is error message). - // On error we hide the 'Attach' and 'Cancel' buttons - // and show an error. - - // If there's xhr error message, let's show it instead of dropzone's one. - const message = xhr ? xhr.responseText : errorMessage; - - $uploadingErrorContainer.removeClass('hide'); - $uploadingErrorMessage.html(message); - $attachButton.addClass('hide'); - $cancelButton.addClass('hide'); + success: function(e, textStatus, response) { + return insertToTextArea(filename, response.responseJSON.link.markdown); }, - totaluploadprogress: function(totalUploadProgress) { - updateAttachingMessage(this.files, $attachingFileMessage); - $uploadProgress.text(Math.round(totalUploadProgress) + '%'); + error: function(response) { + return showError(response.responseJSON.message); }, - sending: function(file) { - // DOM elements already exist. - // Instead of dynamically generating them, - // we just either hide or show them. - $attachButton.addClass('hide'); - $uploadingErrorContainer.addClass('hide'); - $uploadingProgressContainer.removeClass('hide'); - $cancelButton.removeClass('hide'); - }, - removedfile: function() { - $attachButton.removeClass('hide'); - $cancelButton.addClass('hide'); - $uploadingProgressContainer.addClass('hide'); - $uploadingErrorContainer.addClass('hide'); - }, - queuecomplete: function() { - $('.dz-preview').remove(); - $('.markdown-area').trigger('input'); - - $uploadingProgressContainer.addClass('hide'); - $cancelButton.addClass('hide'); + complete: function() { + return closeSpinner(); } }); - - child = $(dropzone[0]).children('textarea'); - - // removeAllFiles(true) stops uploading files (if any) - // and remove them from dropzone files queue. - $cancelButton.on('click', (e) => { - const target = e.target.closest('form').querySelector('.div-dropzone'); - - e.preventDefault(); - e.stopPropagation(); - Dropzone.forElement(target).removeAllFiles(true); + }; + + updateAttachingMessage = (files, messageContainer) => { + let attachingMessage; + const filesCount = files.filter(function(file) { + return file.status === 'uploading' || + file.status === 'queued'; + }).length; + + // Dinamycally change uploading files text depending on files number in + // dropzone files queue. + if (filesCount > 1) { + attachingMessage = 'Attaching ' + filesCount + ' files -'; + } else { + attachingMessage = 'Attaching a file -'; + } + + messageContainer.text(attachingMessage); + }; + + insertToTextArea = function(filename, url) { + return $(child).val(function(index, val) { + return val.replace(`{{${filename}}}`, url); }); + }; - // If 'error' event is fired, we store a failed files, - // clear dropzone files queue, change status of failed files to undefined, - // and add that files to the dropzone files queue again. - // addFile() adds file to dropzone files queue and upload it. - $retryLink.on('click', (e) => { - const dropzoneInstance = Dropzone.forElement(e.target.closest('form').querySelector('.div-dropzone')); - const failedFiles = dropzoneInstance.files; - - e.preventDefault(); - - // 'true' parameter of removeAllFiles() cancels uploading of files that are being uploaded at the moment. - dropzoneInstance.removeAllFiles(true); - - failedFiles.map((failedFile, i) => { - const file = failedFile; - - if (file.status === Dropzone.ERROR) { - file.status = undefined; - file.accepted = undefined; - } - - return dropzoneInstance.addFile(file); - }); + appendToTextArea = function(url) { + return $(child).val(function(index, val) { + return val + url + "\n"; }); + }; - handlePaste = function(event) { - var filename, image, pasteEvent, text; - pasteEvent = event.originalEvent; - if (pasteEvent.clipboardData && pasteEvent.clipboardData.items) { - image = isImage(pasteEvent); - if (image) { - event.preventDefault(); - filename = getFilename(pasteEvent) || 'image.png'; - text = `{{${filename}}}`; - pasteText(text); - return uploadFile(image.getAsFile(), filename); - } - } - }; - - isImage = function(data) { - var i, item; - i = 0; - while (i < data.clipboardData.items.length) { - item = data.clipboardData.items[i]; - if (item.type.indexOf('image') !== -1) { - return item; - } - i += 1; - } - return false; - }; - - pasteText = function(text, shouldPad) { - var afterSelection, beforeSelection, caretEnd, caretStart, textEnd; - var formattedText = text; - if (shouldPad) formattedText += "\n\n"; - const textarea = child.get(0); - caretStart = textarea.selectionStart; - caretEnd = textarea.selectionEnd; - textEnd = $(child).val().length; - beforeSelection = $(child).val().substring(0, caretStart); - afterSelection = $(child).val().substring(caretEnd, textEnd); - $(child).val(beforeSelection + formattedText + afterSelection); - textarea.setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length); - textarea.style.height = `${textarea.scrollHeight}px`; - formTextarea.get(0).dispatchEvent(new Event('input')); - return formTextarea.trigger('input'); - }; - - addFileToForm = function(path) { - $(form).append('<input type="hidden" name="files[]" value="' + _.escape(path) + '">'); - }; - - getFilename = function(e) { - var value; - if (window.clipboardData && window.clipboardData.getData) { - value = window.clipboardData.getData('Text'); - } else if (e.clipboardData && e.clipboardData.getData) { - value = e.clipboardData.getData('text/plain'); - } - value = value.split("\r"); - return value.first(); - }; - - uploadFile = function(item, filename) { - var formData; - formData = new FormData(); - formData.append('file', item, filename); - return $.ajax({ - url: uploadsPath, - type: 'POST', - data: formData, - dataType: 'json', - processData: false, - contentType: false, - headers: { - 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') - }, - beforeSend: function() { - showSpinner(); - return closeAlertMessage(); - }, - success: function(e, textStatus, response) { - return insertToTextArea(filename, response.responseJSON.link.markdown); - }, - error: function(response) { - return showError(response.responseJSON.message); - }, - complete: function() { - return closeSpinner(); - } - }); - }; - - updateAttachingMessage = (files, messageContainer) => { - let attachingMessage; - const filesCount = files.filter(function(file) { - return file.status === 'uploading' || - file.status === 'queued'; - }).length; - - // Dinamycally change uploading files text depending on files number in - // dropzone files queue. - if (filesCount > 1) { - attachingMessage = 'Attaching ' + filesCount + ' files -'; - } else { - attachingMessage = 'Attaching a file -'; - } - - messageContainer.text(attachingMessage); - }; - - insertToTextArea = function(filename, url) { - return $(child).val(function(index, val) { - return val.replace(`{{${filename}}}`, url); - }); - }; - - appendToTextArea = function(url) { - return $(child).val(function(index, val) { - return val + url + "\n"; - }); - }; - - showSpinner = function(e) { - return $uploadingProgressContainer.removeClass('hide'); - }; + showSpinner = function(e) { + return $uploadingProgressContainer.removeClass('hide'); + }; - closeSpinner = function() { - return $uploadingProgressContainer.addClass('hide'); - }; + closeSpinner = function() { + return $uploadingProgressContainer.addClass('hide'); + }; - showError = function(message) { - $uploadingErrorContainer.removeClass('hide'); - $uploadingErrorMessage.html(message); - }; + showError = function(message) { + $uploadingErrorContainer.removeClass('hide'); + $uploadingErrorMessage.html(message); + }; - closeAlertMessage = function() { - return form.find('.div-dropzone-alert').alert('close'); - }; + closeAlertMessage = function() { + return form.find('.div-dropzone-alert').alert('close'); + }; - form.find('.markdown-selector').click(function(e) { - e.preventDefault(); - $(this).closest('.gfm-form').find('.div-dropzone').click(); - formTextarea.focus(); - }); - } + form.find('.markdown-selector').click(function(e) { + e.preventDefault(); + $(this).closest('.gfm-form').find('.div-dropzone').click(); + formTextarea.focus(); + }); +} - return DropzoneInput; -})(); +window.DropzoneInput = DropzoneInput; diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js index ccff8f0ace7..5152b0f27a3 100644 --- a/app/assets/javascripts/flash.js +++ b/app/assets/javascripts/flash.js @@ -1,71 +1,69 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-param-reassign, quotes, quote-props, prefer-template, comma-dangle, max-len */ -window.Flash = (function() { - var hideFlash; +var hideFlash; - hideFlash = function() { - return $(this).fadeOut(); - }; +hideFlash = function() { + return $(this).fadeOut(); +}; - /** - * Flash banner supports different types of Flash configurations - * along with ability to provide actionConfig which can be used to show - * additional action or link on banner next to message - * - * @param {String} message Flash message - * @param {String} type Type of Flash, it can be `notice` or `alert` (default) - * @param {Object} parent Reference to Parent element under which Flash needs to appear - * @param {Object} actionConfig Map of config to show action on banner - * @param {String} href URL to which action link should point (default '#') - * @param {String} title Title of action - * @param {Function} clickHandler Method to call when action is clicked on - */ - function Flash(message, type, parent, actionConfig) { - var flash, textDiv, actionLink; - if (type == null) { - type = 'alert'; - } - if (parent == null) { - parent = null; - } - if (parent) { - this.flashContainer = parent.find('.flash-container'); - } else { - this.flashContainer = $('.flash-container-page'); - } - this.flashContainer.html(''); - flash = $('<div/>', { - "class": "flash-" + type - }); - flash.on('click', hideFlash); - textDiv = $('<div/>', { - "class": 'flash-text', - text: message - }); - textDiv.appendTo(flash); +/** + * Flash banner supports different types of Flash configurations + * along with ability to provide actionConfig which can be used to show + * additional action or link on banner next to message + * + * @param {String} message Flash message + * @param {String} type Type of Flash, it can be `notice` or `alert` (default) + * @param {Object} parent Reference to Parent element under which Flash needs to appear + * @param {Object} actionConfig Map of config to show action on banner + * @param {String} href URL to which action link should point (default '#') + * @param {String} title Title of action + * @param {Function} clickHandler Method to call when action is clicked on + */ +function Flash(message, type, parent, actionConfig) { + var flash, textDiv, actionLink; + if (type == null) { + type = 'alert'; + } + if (parent == null) { + parent = null; + } + if (parent) { + this.flashContainer = parent.find('.flash-container'); + } else { + this.flashContainer = $('.flash-container-page'); + } + this.flashContainer.html(''); + flash = $('<div/>', { + "class": "flash-" + type + }); + flash.on('click', hideFlash); + textDiv = $('<div/>', { + "class": 'flash-text', + text: message + }); + textDiv.appendTo(flash); - if (actionConfig) { - const actionLinkConfig = { - class: 'flash-action', - href: actionConfig.href || '#', - text: actionConfig.title - }; + if (actionConfig) { + const actionLinkConfig = { + class: 'flash-action', + href: actionConfig.href || '#', + text: actionConfig.title + }; - if (!actionConfig.href) { - actionLinkConfig.role = 'button'; - } + if (!actionConfig.href) { + actionLinkConfig.role = 'button'; + } - actionLink = $('<a/>', actionLinkConfig); + actionLink = $('<a/>', actionLinkConfig); - actionLink.appendTo(flash); - this.flashContainer.on('click', '.flash-action', actionConfig.clickHandler); - } - if (this.flashContainer.parent().hasClass('content-wrapper')) { - textDiv.addClass('container-fluid container-limited'); - } - flash.appendTo(this.flashContainer); - this.flashContainer.show(); + actionLink.appendTo(flash); + this.flashContainer.on('click', '.flash-action', actionConfig.clickHandler); + } + if (this.flashContainer.parent().hasClass('content-wrapper')) { + textDiv.addClass('container-fluid container-limited'); } + flash.appendTo(this.flashContainer); + this.flashContainer.show(); +} - return Flash; -})(); +window.Flash = Flash; diff --git a/app/assets/javascripts/group_avatar.js b/app/assets/javascripts/group_avatar.js index f03b47b1c1d..e60a33fbf4a 100644 --- a/app/assets/javascripts/group_avatar.js +++ b/app/assets/javascripts/group_avatar.js @@ -1,19 +1,17 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, one-var, one-var-declaration-per-line, no-useless-escape, max-len */ -window.GroupAvatar = (function() { - function GroupAvatar() { - $('.js-choose-group-avatar-button').on("click", function() { - var form; - form = $(this).closest("form"); - return form.find(".js-group-avatar-input").click(); - }); - $('.js-group-avatar-input').on("change", function() { - var filename, form; - form = $(this).closest("form"); - filename = $(this).val().replace(/^.*[\\\/]/, ''); - return form.find(".js-avatar-filename").text(filename); - }); - } +function GroupAvatar() { + $('.js-choose-group-avatar-button').on("click", function() { + var form; + form = $(this).closest("form"); + return form.find(".js-group-avatar-input").click(); + }); + $('.js-group-avatar-input').on("change", function() { + var filename, form; + form = $(this).closest("form"); + filename = $(this).val().replace(/^.*[\\\/]/, ''); + return form.find(".js-avatar-filename").text(filename); + }); +} - return GroupAvatar; -})(); +window.GroupAvatar = GroupAvatar; diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index b5975295329..d7d8e403542 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -7,115 +7,113 @@ import Api from './api'; var slice = [].slice; -window.GroupsSelect = (function() { - function GroupsSelect() { - $('.ajax-groups-select').each((function(_this) { - const self = _this; +function GroupsSelect() { + $('.ajax-groups-select').each((function(_this) { + const self = _this; - return function(i, select) { - var all_available, skip_groups; - const $select = $(select); - all_available = $select.data('all-available'); - skip_groups = $select.data('skip-groups') || []; + return function(i, select) { + var all_available, skip_groups; + const $select = $(select); + all_available = $select.data('all-available'); + skip_groups = $select.data('skip-groups') || []; - $select.select2({ - placeholder: "Search for a group", - multiple: $select.hasClass('multiselect'), - minimumInputLength: 0, - ajax: { - url: Api.buildUrl(Api.groupsPath), - dataType: 'json', - quietMillis: 250, - transport: function (params) { - $.ajax(params).then((data, status, xhr) => { - const results = data || []; + $select.select2({ + placeholder: "Search for a group", + multiple: $select.hasClass('multiselect'), + minimumInputLength: 0, + ajax: { + url: Api.buildUrl(Api.groupsPath), + dataType: 'json', + quietMillis: 250, + transport: function (params) { + $.ajax(params).then((data, status, xhr) => { + const results = data || []; - const headers = gl.utils.normalizeCRLFHeaders(xhr.getAllResponseHeaders()); - const currentPage = parseInt(headers['X-PAGE'], 10) || 0; - const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0; - const more = currentPage < totalPages; - - return { - results, - pagination: { - more, - }, - }; - }).then(params.success).fail(params.error); - }, - data: function (search, page) { - return { - search, - page, - per_page: GroupsSelect.PER_PAGE, - all_available, - }; - }, - results: function (data, page) { - if (data.length) return { results: [] }; - - const groups = data.length ? data : data.results || []; - const more = data.pagination ? data.pagination.more : false; - const results = groups.filter(group => skip_groups.indexOf(group.id) === -1); + const headers = gl.utils.normalizeCRLFHeaders(xhr.getAllResponseHeaders()); + const currentPage = parseInt(headers['X-PAGE'], 10) || 0; + const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0; + const more = currentPage < totalPages; return { results, - page, - more, + pagination: { + more, + }, }; - }, - }, - initSelection: function(element, callback) { - var id; - id = $(element).val(); - if (id !== "") { - return Api.group(id, callback); - } + }).then(params.success).fail(params.error); }, - formatResult: function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return self.formatResult.apply(self, args); + data: function (search, page) { + return { + search, + page, + per_page: GroupsSelect.PER_PAGE, + all_available, + }; }, - formatSelection: function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return self.formatSelection.apply(self, args); + results: function (data, page) { + if (data.length) return { results: [] }; + + const groups = data.length ? data : data.results || []; + const more = data.pagination ? data.pagination.more : false; + const results = groups.filter(group => skip_groups.indexOf(group.id) === -1); + + return { + results, + page, + more, + }; }, - dropdownCssClass: "ajax-groups-dropdown select2-infinite", - // we do not want to escape markup since we are displaying html in results - escapeMarkup: function(m) { - return m; + }, + initSelection: function(element, callback) { + var id; + id = $(element).val(); + if (id !== "") { + return Api.group(id, callback); } - }); + }, + formatResult: function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return self.formatResult.apply(self, args); + }, + formatSelection: function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return self.formatSelection.apply(self, args); + }, + dropdownCssClass: "ajax-groups-dropdown select2-infinite", + // we do not want to escape markup since we are displaying html in results + escapeMarkup: function(m) { + return m; + } + }); - self.dropdown = document.querySelector('.select2-infinite .select2-results'); + self.dropdown = document.querySelector('.select2-infinite .select2-results'); - $select.on('select2-loaded', self.forceOverflow.bind(self)); - }; - })(this)); - } + $select.on('select2-loaded', self.forceOverflow.bind(self)); + }; + })(this)); +} - GroupsSelect.prototype.formatResult = function(group) { - var avatar; - if (group.avatar_url) { - avatar = group.avatar_url; - } else { - avatar = gon.default_avatar_url; - } - return "<div class='group-result'> <div class='group-name'>" + group.full_name + "</div> <div class='group-path'>" + group.full_path + "</div> </div>"; - }; +GroupsSelect.prototype.formatResult = function(group) { + var avatar; + if (group.avatar_url) { + avatar = group.avatar_url; + } else { + avatar = gon.default_avatar_url; + } + 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.full_name; - }; +GroupsSelect.prototype.formatSelection = function(group) { + return group.full_name; +}; - GroupsSelect.prototype.forceOverflow = function (e) { - const itemHeight = this.dropdown.querySelector('.select2-result:first-child').clientHeight; - this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight - (itemHeight * 0.9))}px`; - }; +GroupsSelect.prototype.forceOverflow = function (e) { + const itemHeight = this.dropdown.querySelector('.select2-result:first-child').clientHeight; + this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight - (itemHeight * 0.9))}px`; +}; - GroupsSelect.PER_PAGE = 20; +GroupsSelect.PER_PAGE = 20; - return GroupsSelect; -})(); +window.GroupsSelect = GroupsSelect; diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js index 5b4ca94ed30..3d97938c0f3 100644 --- a/app/assets/javascripts/importer_status.js +++ b/app/assets/javascripts/importer_status.js @@ -1,83 +1,79 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, camelcase, no-var, one-var, one-var-declaration-per-line, prefer-template, quotes, object-shorthand, comma-dangle, no-unused-vars, prefer-arrow-callback, no-else-return, vars-on-top, no-new, max-len */ -(function() { - window.ImporterStatus = (function() { - function ImporterStatus(jobs_url, import_url) { - this.jobs_url = jobs_url; - this.import_url = import_url; - this.initStatusPage(); - this.setAutoUpdate(); - } +function ImporterStatus(jobs_url, import_url) { + this.jobs_url = jobs_url; + this.import_url = import_url; + this.initStatusPage(); + this.setAutoUpdate(); +} - ImporterStatus.prototype.initStatusPage = function() { - $('.js-add-to-import').off('click').on('click', (function(_this) { - return function(e) { - var $btn, $namespace_input, $target_field, $tr, id, target_namespace, newName; - $btn = $(e.currentTarget); - $tr = $btn.closest('tr'); - $target_field = $tr.find('.import-target'); - $namespace_input = $target_field.find('.js-select-namespace option:selected'); - id = $tr.attr('id').replace('repo_', ''); - target_namespace = null; - newName = null; - if ($namespace_input.length > 0) { - target_namespace = $namespace_input[0].innerHTML; - newName = $target_field.find('#path').prop('value'); - $target_field.empty().append(target_namespace + "/" + newName); - } - $btn.disable().addClass('is-loading'); - return $.post(_this.import_url, { - repo_id: id, - target_namespace: target_namespace, - new_name: newName - }, { - dataType: 'script' - }); - }; - })(this)); - return $('.js-import-all').off('click').on('click', function(e) { - var $btn; - $btn = $(this); - $btn.disable().addClass('is-loading'); - return $('.js-add-to-import').each(function() { - return $(this).trigger('click'); - }); +ImporterStatus.prototype.initStatusPage = function() { + $('.js-add-to-import').off('click').on('click', (function(_this) { + return function(e) { + var $btn, $namespace_input, $target_field, $tr, id, target_namespace, newName; + $btn = $(e.currentTarget); + $tr = $btn.closest('tr'); + $target_field = $tr.find('.import-target'); + $namespace_input = $target_field.find('.js-select-namespace option:selected'); + id = $tr.attr('id').replace('repo_', ''); + target_namespace = null; + newName = null; + if ($namespace_input.length > 0) { + target_namespace = $namespace_input[0].innerHTML; + newName = $target_field.find('#path').prop('value'); + $target_field.empty().append(target_namespace + "/" + newName); + } + $btn.disable().addClass('is-loading'); + return $.post(_this.import_url, { + repo_id: id, + target_namespace: target_namespace, + new_name: newName + }, { + dataType: 'script' }); }; + })(this)); + return $('.js-import-all').off('click').on('click', function(e) { + var $btn; + $btn = $(this); + $btn.disable().addClass('is-loading'); + return $('.js-add-to-import').each(function() { + return $(this).trigger('click'); + }); + }); +}; - ImporterStatus.prototype.setAutoUpdate = function() { - return setInterval(((function(_this) { - return function() { - return $.get(_this.jobs_url, function(data) { - return $.each(data, function(i, job) { - var job_item, status_field; - job_item = $("#project_" + job.id); - status_field = job_item.find(".job-status"); - if (job.import_status === 'finished') { - job_item.removeClass("active").addClass("success"); - return status_field.html('<span><i class="fa fa-check"></i> done</span>'); - } else if (job.import_status === 'scheduled') { - return status_field.html("<i class='fa fa-spinner fa-spin'></i> scheduled"); - } else if (job.import_status === 'started') { - return status_field.html("<i class='fa fa-spinner fa-spin'></i> started"); - } else { - return status_field.html(job.import_status); - } - }); - }); - }; - })(this)), 4000); +ImporterStatus.prototype.setAutoUpdate = function() { + return setInterval(((function(_this) { + return function() { + return $.get(_this.jobs_url, function(data) { + return $.each(data, function(i, job) { + var job_item, status_field; + job_item = $("#project_" + job.id); + status_field = job_item.find(".job-status"); + if (job.import_status === 'finished') { + job_item.removeClass("active").addClass("success"); + return status_field.html('<span><i class="fa fa-check"></i> done</span>'); + } else if (job.import_status === 'scheduled') { + return status_field.html("<i class='fa fa-spinner fa-spin'></i> scheduled"); + } else if (job.import_status === 'started') { + return status_field.html("<i class='fa fa-spinner fa-spin'></i> started"); + } else { + return status_field.html(job.import_status); + } + }); + }); }; + })(this)), 4000); +}; - return ImporterStatus; - })(); +window.ImporterStatus = ImporterStatus; - $(function() { - if ($('.js-importer-status').length) { - var jobsImportPath = $('.js-importer-status').data('jobs-import-path'); - var importPath = $('.js-importer-status').data('import-path'); +$(function() { + if ($('.js-importer-status').length) { + var jobsImportPath = $('.js-importer-status').data('jobs-import-path'); + var importPath = $('.js-importer-status').data('import-path'); - new window.ImporterStatus(jobsImportPath, importPath); - } - }); -}).call(window); + new window.ImporterStatus(jobsImportPath, importPath); + } +}); diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js index a4d7bf096ef..e8cfc0241fe 100644 --- a/app/assets/javascripts/issuable_context.js +++ b/app/assets/javascripts/issuable_context.js @@ -4,76 +4,72 @@ import Cookies from 'js-cookie'; import UsersSelect from './users_select'; -(function() { - this.IssuableContext = (function() { - function IssuableContext(currentUser) { - this.initParticipants(); - new UsersSelect(currentUser); - $('select.select2').select2({ - width: 'resolve', - dropdownAutoWidth: true - }); - $(".issuable-sidebar .inline-update").on("change", "select", function() { - return $(this).submit(); - }); - $(".issuable-sidebar .inline-update").on("change", ".js-assignee", function() { - return $(this).submit(); - }); - $(document).off('click', '.issuable-sidebar .dropdown-content a').on('click', '.issuable-sidebar .dropdown-content a', function(e) { - return e.preventDefault(); - }); - $(document).off('click', '.edit-link').on('click', '.edit-link', function(e) { - var $block, $selectbox; - e.preventDefault(); - $block = $(this).parents('.block'); - $selectbox = $block.find('.selectbox'); - if ($selectbox.is(':visible')) { - $selectbox.hide(); - $block.find('.value').show(); - } else { - $selectbox.show(); - $block.find('.value').hide(); - } - if ($selectbox.is(':visible')) { - return setTimeout(function() { - return $block.find('.dropdown-menu-toggle').trigger('click'); - }, 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); - } - }); +function IssuableContext(currentUser) { + this.initParticipants(); + new UsersSelect(currentUser); + $('select.select2').select2({ + width: 'resolve', + dropdownAutoWidth: true + }); + $(".issuable-sidebar .inline-update").on("change", "select", function() { + return $(this).submit(); + }); + $(".issuable-sidebar .inline-update").on("change", ".js-assignee", function() { + return $(this).submit(); + }); + $(document).off('click', '.issuable-sidebar .dropdown-content a').on('click', '.issuable-sidebar .dropdown-content a', function(e) { + return e.preventDefault(); + }); + $(document).off('click', '.edit-link').on('click', '.edit-link', function(e) { + var $block, $selectbox; + e.preventDefault(); + $block = $(this).parents('.block'); + $selectbox = $block.find('.selectbox'); + if ($selectbox.is(':visible')) { + $selectbox.hide(); + $block.find('.value').show(); + } else { + $selectbox.show(); + $block.find('.value').hide(); } + if ($selectbox.is(':visible')) { + return setTimeout(function() { + return $block.find('.dropdown-menu-toggle').trigger('click'); + }, 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); + } + }); +} - IssuableContext.prototype.initParticipants = function() { - var _this; - _this = this; - $(document).on("click", ".js-participants-more", this.toggleHiddenParticipants); - return $(".js-participants-author").each(function(i) { - if (i >= _this.PARTICIPANTS_ROW_COUNT) { - return $(this).addClass("js-participants-hidden").hide(); - } - }); - }; +IssuableContext.prototype.initParticipants = function() { + var _this; + _this = this; + $(document).on("click", ".js-participants-more", this.toggleHiddenParticipants); + return $(".js-participants-author").each(function(i) { + if (i >= _this.PARTICIPANTS_ROW_COUNT) { + return $(this).addClass("js-participants-hidden").hide(); + } + }); +}; - IssuableContext.prototype.toggleHiddenParticipants = function(e) { - var currentText, lessText, originalText; - e.preventDefault(); - currentText = $(this).text().trim(); - lessText = $(this).data("less-text"); - originalText = $(this).data("original-text"); - if (currentText === originalText) { - $(this).text(lessText); - } else { - $(this).text(originalText); - } - return $(".js-participants-hidden").toggle(); - }; +IssuableContext.prototype.toggleHiddenParticipants = function(e) { + var currentText, lessText, originalText; + e.preventDefault(); + currentText = $(this).text().trim(); + lessText = $(this).data("less-text"); + originalText = $(this).data("original-text"); + if (currentText === originalText) { + $(this).text(lessText); + } else { + $(this).text(originalText); + } + return $(".js-participants-hidden").toggle(); +}; - return IssuableContext; - })(); -}).call(window); +window.IssuableContext = IssuableContext; diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index 9ac1325fc95..b43e60e1de8 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -8,153 +8,149 @@ import UsersSelect from './users_select'; import GfmAutoComplete from './gfm_auto_complete'; import ZenMode from './zen_mode'; -(function() { - this.IssuableForm = (function() { - IssuableForm.prototype.issueMoveConfirmMsg = 'Are you sure you want to move this issue to another project?'; - - IssuableForm.prototype.wipRegex = /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i; - - function IssuableForm(form) { - var $issuableDueDate, calendar; - this.form = form; - this.toggleWip = this.toggleWip.bind(this); - this.renderWipExplanation = this.renderWipExplanation.bind(this); - this.resetAutosave = this.resetAutosave.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup(); - new UsersSelect(); - new ZenMode(); - this.titleField = this.form.find("input[name*='[title]']"); - this.descriptionField = this.form.find("textarea[name*='[description]']"); - this.issueMoveField = this.form.find("#move_to_project_id"); - if (!(this.titleField.length && this.descriptionField.length)) { - return; +function IssuableForm(form) { + var $issuableDueDate, calendar; + this.form = form; + this.toggleWip = this.toggleWip.bind(this); + this.renderWipExplanation = this.renderWipExplanation.bind(this); + this.resetAutosave = this.resetAutosave.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup(); + new UsersSelect(); + new ZenMode(); + this.titleField = this.form.find("input[name*='[title]']"); + this.descriptionField = this.form.find("textarea[name*='[description]']"); + this.issueMoveField = this.form.find("#move_to_project_id"); + if (!(this.titleField.length && this.descriptionField.length)) { + return; + } + this.initAutosave(); + this.form.on("submit", this.handleSubmit); + this.form.on("click", ".btn-cancel", this.resetAutosave); + this.initWip(); + this.initMoveDropdown(); + $issuableDueDate = $('#issuable-due-date'); + if ($issuableDueDate.length) { + calendar = new Pikaday({ + field: $issuableDueDate.get(0), + theme: 'gitlab-theme animate-picker', + format: 'yyyy-mm-dd', + container: $issuableDueDate.parent().get(0), + onSelect: function(dateText) { + $issuableDueDate.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); } - this.initAutosave(); - this.form.on("submit", this.handleSubmit); - this.form.on("click", ".btn-cancel", this.resetAutosave); - this.initWip(); - this.initMoveDropdown(); - $issuableDueDate = $('#issuable-due-date'); - if ($issuableDueDate.length) { - calendar = new Pikaday({ - field: $issuableDueDate.get(0), - theme: 'gitlab-theme animate-picker', - format: 'yyyy-mm-dd', - container: $issuableDueDate.parent().get(0), - onSelect: function(dateText) { - $issuableDueDate.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); - } - }); - calendar.setDate(new Date($issuableDueDate.val())); - } - } + }); + calendar.setDate(new Date($issuableDueDate.val())); + } +} + +IssuableForm.prototype.issueMoveConfirmMsg = 'Are you sure you want to move this issue to another project?'; - IssuableForm.prototype.initAutosave = function() { - new Autosave(this.titleField, [document.location.pathname, document.location.search, "title"]); - return new Autosave(this.descriptionField, [document.location.pathname, document.location.search, "description"]); - }; +IssuableForm.prototype.wipRegex = /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i; - IssuableForm.prototype.handleSubmit = function() { - var fieldId = (this.issueMoveField != null) ? this.issueMoveField.val() : null; - if ((parseInt(fieldId, 10) || 0) > 0) { - if (!confirm(this.issueMoveConfirmMsg)) { - return false; +IssuableForm.prototype.initAutosave = function() { + new Autosave(this.titleField, [document.location.pathname, document.location.search, "title"]); + return new Autosave(this.descriptionField, [document.location.pathname, document.location.search, "description"]); +}; + +IssuableForm.prototype.handleSubmit = function() { + var fieldId = (this.issueMoveField != null) ? this.issueMoveField.val() : null; + if ((parseInt(fieldId, 10) || 0) > 0) { + if (!confirm(this.issueMoveConfirmMsg)) { + return false; + } + } + return this.resetAutosave(); +}; + +IssuableForm.prototype.resetAutosave = function() { + this.titleField.data("autosave").reset(); + return this.descriptionField.data("autosave").reset(); +}; + +IssuableForm.prototype.initWip = function() { + this.$wipExplanation = this.form.find(".js-wip-explanation"); + this.$noWipExplanation = this.form.find(".js-no-wip-explanation"); + if (!(this.$wipExplanation.length && this.$noWipExplanation.length)) { + return; + } + this.form.on("click", ".js-toggle-wip", this.toggleWip); + this.titleField.on("keyup blur", this.renderWipExplanation); + return this.renderWipExplanation(); +}; + +IssuableForm.prototype.workInProgress = function() { + return this.wipRegex.test(this.titleField.val()); +}; + +IssuableForm.prototype.renderWipExplanation = function() { + if (this.workInProgress()) { + this.$wipExplanation.show(); + return this.$noWipExplanation.hide(); + } else { + this.$wipExplanation.hide(); + return this.$noWipExplanation.show(); + } +}; + +IssuableForm.prototype.toggleWip = function(event) { + event.preventDefault(); + if (this.workInProgress()) { + this.removeWip(); + } else { + this.addWip(); + } + return this.renderWipExplanation(); +}; + +IssuableForm.prototype.removeWip = function() { + return this.titleField.val(this.titleField.val().replace(this.wipRegex, "")); +}; + +IssuableForm.prototype.addWip = function() { + return this.titleField.val("WIP: " + (this.titleField.val())); +}; + +IssuableForm.prototype.initMoveDropdown = function() { + var $moveDropdown, pageSize; + $moveDropdown = $('.js-move-dropdown'); + if ($moveDropdown.length) { + pageSize = $moveDropdown.data('page-size'); + return $('.js-move-dropdown').select2({ + ajax: { + url: $moveDropdown.data('projects-url'), + quietMillis: 125, + data: function(term, page, context) { + return { + search: term, + offset_id: context + }; + }, + results: function(data) { + var context, + more; + + if (data.length >= pageSize) + more = true; + + if (data[data.length - 1]) + context = data[data.length - 1].id; + + return { + results: data, + more: more, + context: context + }; } + }, + formatResult: function(project) { + return project.name_with_namespace; + }, + formatSelection: function(project) { + return project.name_with_namespace; } - return this.resetAutosave(); - }; - - IssuableForm.prototype.resetAutosave = function() { - this.titleField.data("autosave").reset(); - return this.descriptionField.data("autosave").reset(); - }; - - IssuableForm.prototype.initWip = function() { - this.$wipExplanation = this.form.find(".js-wip-explanation"); - this.$noWipExplanation = this.form.find(".js-no-wip-explanation"); - if (!(this.$wipExplanation.length && this.$noWipExplanation.length)) { - return; - } - this.form.on("click", ".js-toggle-wip", this.toggleWip); - this.titleField.on("keyup blur", this.renderWipExplanation); - return this.renderWipExplanation(); - }; - - IssuableForm.prototype.workInProgress = function() { - return this.wipRegex.test(this.titleField.val()); - }; - - IssuableForm.prototype.renderWipExplanation = function() { - if (this.workInProgress()) { - this.$wipExplanation.show(); - return this.$noWipExplanation.hide(); - } else { - this.$wipExplanation.hide(); - return this.$noWipExplanation.show(); - } - }; - - IssuableForm.prototype.toggleWip = function(event) { - event.preventDefault(); - if (this.workInProgress()) { - this.removeWip(); - } else { - this.addWip(); - } - return this.renderWipExplanation(); - }; - - IssuableForm.prototype.removeWip = function() { - return this.titleField.val(this.titleField.val().replace(this.wipRegex, "")); - }; - - IssuableForm.prototype.addWip = function() { - return this.titleField.val("WIP: " + (this.titleField.val())); - }; - - IssuableForm.prototype.initMoveDropdown = function() { - var $moveDropdown, pageSize; - $moveDropdown = $('.js-move-dropdown'); - if ($moveDropdown.length) { - pageSize = $moveDropdown.data('page-size'); - return $('.js-move-dropdown').select2({ - ajax: { - url: $moveDropdown.data('projects-url'), - quietMillis: 125, - data: function(term, page, context) { - return { - search: term, - offset_id: context - }; - }, - results: function(data) { - var context, - more; - - if (data.length >= pageSize) - more = true; - - if (data[data.length - 1]) - context = data[data.length - 1].id; - - return { - results: data, - more: more, - context: context - }; - } - }, - formatResult: function(project) { - return project.name_with_namespace; - }, - formatSelection: function(project) { - return project.name_with_namespace; - } - }); - } - }; + }); + } +}; - return IssuableForm; - })(); -}).call(window); +window.IssuableForm = IssuableForm; diff --git a/app/assets/javascripts/issuable_index.js b/app/assets/javascripts/issuable_index.js index 5c96646def8..4f3f5e0527c 100644 --- a/app/assets/javascripts/issuable_index.js +++ b/app/assets/javascripts/issuable_index.js @@ -4,168 +4,166 @@ import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar'; import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; -((global) => { - var issuable_created; - - issuable_created = false; - - global.IssuableIndex = { - init: function(pagePrefix) { - IssuableIndex.initTemplates(); - IssuableIndex.initSearch(); - IssuableIndex.initBulkUpdate(pagePrefix); - IssuableIndex.initResetFilters(); - IssuableIndex.resetIncomingEmailToken(); - IssuableIndex.initLabelFilterRemove(); - }, - initTemplates: function() { - return IssuableIndex.labelRow = _.template('<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>'); - }, - initSearch: function() { - const $searchInput = $('#issuable_search'); - - IssuableIndex.initSearchState($searchInput); - - // `immediate` param set to false debounces on the `trailing` edge, lets user finish typing - const debouncedExecSearch = _.debounce(IssuableIndex.executeSearch, 1000, false); - - $searchInput.off('keyup').on('keyup', debouncedExecSearch); - - // ensures existing filters are preserved when manually submitted - $('#issuable_search_form').on('submit', (e) => { - e.preventDefault(); - debouncedExecSearch(e); - }); - }, - initSearchState: function($searchInput) { - const currentSearchVal = $searchInput.val(); - - IssuableIndex.searchState = { - elem: $searchInput, - current: currentSearchVal - }; - - IssuableIndex.maybeFocusOnSearch(); - }, - accessSearchPristine: function(set) { - // store reference to previous value to prevent search on non-mutating keyup - const state = IssuableIndex.searchState; - const currentSearchVal = state.elem.val(); - - if (set) { - state.current = currentSearchVal; +var issuable_created; + +issuable_created = false; + +window.IssuableIndex = { + init: function(pagePrefix) { + IssuableIndex.initTemplates(); + IssuableIndex.initSearch(); + IssuableIndex.initBulkUpdate(pagePrefix); + IssuableIndex.initResetFilters(); + IssuableIndex.resetIncomingEmailToken(); + IssuableIndex.initLabelFilterRemove(); + }, + initTemplates: function() { + return IssuableIndex.labelRow = _.template('<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>'); + }, + initSearch: function() { + const $searchInput = $('#issuable_search'); + + IssuableIndex.initSearchState($searchInput); + + // `immediate` param set to false debounces on the `trailing` edge, lets user finish typing + const debouncedExecSearch = _.debounce(IssuableIndex.executeSearch, 1000, false); + + $searchInput.off('keyup').on('keyup', debouncedExecSearch); + + // ensures existing filters are preserved when manually submitted + $('#issuable_search_form').on('submit', (e) => { + e.preventDefault(); + debouncedExecSearch(e); + }); + }, + initSearchState: function($searchInput) { + const currentSearchVal = $searchInput.val(); + + IssuableIndex.searchState = { + elem: $searchInput, + current: currentSearchVal + }; + + IssuableIndex.maybeFocusOnSearch(); + }, + accessSearchPristine: function(set) { + // store reference to previous value to prevent search on non-mutating keyup + const state = IssuableIndex.searchState; + const currentSearchVal = state.elem.val(); + + if (set) { + state.current = currentSearchVal; + } else { + return state.current === currentSearchVal; + } + }, + maybeFocusOnSearch: function() { + const currentSearchVal = IssuableIndex.searchState.current; + if (currentSearchVal && currentSearchVal !== '') { + const queryLength = currentSearchVal.length; + const $searchInput = IssuableIndex.searchState.elem; + + /* The following ensures that the cursor is initially placed at + * the end of search input when focus is applied. It accounts + * for differences in browser implementations of `setSelectionRange` + * and cursor placement for elements in focus. + */ + $searchInput.focus(); + if ($searchInput.setSelectionRange) { + $searchInput.setSelectionRange(queryLength, queryLength); } else { - return state.current === currentSearchVal; - } - }, - maybeFocusOnSearch: function() { - const currentSearchVal = IssuableIndex.searchState.current; - if (currentSearchVal && currentSearchVal !== '') { - const queryLength = currentSearchVal.length; - const $searchInput = IssuableIndex.searchState.elem; - - /* The following ensures that the cursor is initially placed at - * the end of search input when focus is applied. It accounts - * for differences in browser implementations of `setSelectionRange` - * and cursor placement for elements in focus. - */ - $searchInput.focus(); - if ($searchInput.setSelectionRange) { - $searchInput.setSelectionRange(queryLength, queryLength); - } else { - $searchInput.val(currentSearchVal); - } - } - }, - executeSearch: function(e) { - const $search = $('#issuable_search'); - const $searchName = $search.attr('name'); - const $searchValue = $search.val(); - const $filtersForm = $('.js-filter-form'); - const $input = $(`input[name='${$searchName}']`, $filtersForm); - const isPristine = IssuableIndex.accessSearchPristine(); - - if (isPristine) { - return; + $searchInput.val(currentSearchVal); } + } + }, + executeSearch: function(e) { + const $search = $('#issuable_search'); + const $searchName = $search.attr('name'); + const $searchValue = $search.val(); + const $filtersForm = $('.js-filter-form'); + const $input = $(`input[name='${$searchName}']`, $filtersForm); + const isPristine = IssuableIndex.accessSearchPristine(); + + if (isPristine) { + return; + } - if (!$input.length) { - $filtersForm.append(`<input type='hidden' name='${$searchName}' value='${_.escape($searchValue)}'/>`); - } else { - $input.val($searchValue); - } + if (!$input.length) { + $filtersForm.append(`<input type='hidden' name='${$searchName}' value='${_.escape($searchValue)}'/>`); + } else { + $input.val($searchValue); + } - IssuableIndex.filterResults($filtersForm); - }, - initLabelFilterRemove: function() { - return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) { - var $button; - $button = $(this); - // Remove the label input box - $('input[name="label_name[]"]').filter(function() { - return this.value === $button.data('label'); - }).remove(); - // Submit the form to get new data - IssuableIndex.filterResults($('.filter-form')); + IssuableIndex.filterResults($filtersForm); + }, + initLabelFilterRemove: function() { + return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) { + var $button; + $button = $(this); + // Remove the label input box + $('input[name="label_name[]"]').filter(function() { + return this.value === $button.data('label'); + }).remove(); + // Submit the form to get new data + IssuableIndex.filterResults($('.filter-form')); + }); + }, + filterResults: (function(_this) { + return function(form) { + var formAction, formData, issuesUrl; + formData = form.serializeArray(); + formData = formData.filter(function(data) { + return data.value !== ''; }); - }, - filterResults: (function(_this) { - return function(form) { - var formAction, formData, issuesUrl; - formData = form.serializeArray(); - formData = formData.filter(function(data) { - return data.value !== ''; - }); - formData = $.param(formData); - formAction = form.attr('action'); - issuesUrl = formAction; - issuesUrl += "" + (formAction.indexOf('?') === -1 ? '?' : '&'); - issuesUrl += formData; - return gl.utils.visitUrl(issuesUrl); - }; - })(this), - initResetFilters: function() { - $('.reset-filters').on('click', function(e) { - e.preventDefault(); - const target = e.target; - const $form = $(target).parents('.js-filter-form'); - const baseIssuesUrl = target.href; - - $form.attr('action', baseIssuesUrl); - gl.utils.visitUrl(baseIssuesUrl); + formData = $.param(formData); + formAction = form.attr('action'); + issuesUrl = formAction; + issuesUrl += "" + (formAction.indexOf('?') === -1 ? '?' : '&'); + issuesUrl += formData; + return gl.utils.visitUrl(issuesUrl); + }; + })(this), + initResetFilters: function() { + $('.reset-filters').on('click', function(e) { + e.preventDefault(); + const target = e.target; + const $form = $(target).parents('.js-filter-form'); + const baseIssuesUrl = target.href; + + $form.attr('action', baseIssuesUrl); + gl.utils.visitUrl(baseIssuesUrl); + }); + }, + initBulkUpdate: function(pagePrefix) { + const userCanBulkUpdate = $('.issues-bulk-update').length > 0; + const alreadyInitialized = !!this.bulkUpdateSidebar; + + if (userCanBulkUpdate && !alreadyInitialized) { + IssuableBulkUpdateActions.init({ + prefixId: pagePrefix, }); - }, - initBulkUpdate: function(pagePrefix) { - const userCanBulkUpdate = $('.issues-bulk-update').length > 0; - const alreadyInitialized = !!this.bulkUpdateSidebar; - if (userCanBulkUpdate && !alreadyInitialized) { - IssuableBulkUpdateActions.init({ - prefixId: pagePrefix, - }); - - this.bulkUpdateSidebar = new IssuableBulkUpdateSidebar(); - } - }, - resetIncomingEmailToken: function() { - $('.incoming-email-token-reset').on('click', function(e) { - e.preventDefault(); - - $.ajax({ - type: 'PUT', - url: $('.incoming-email-token-reset').attr('href'), - dataType: 'json', - success: function(response) { - $('#issue_email').val(response.new_issue_address).focus(); - }, - beforeSend: function() { - $('.incoming-email-token-reset').text('resetting...'); - }, - complete: function() { - $('.incoming-email-token-reset').text('reset it'); - } - }); - }); + this.bulkUpdateSidebar = new IssuableBulkUpdateSidebar(); } - }; -})(window); + }, + resetIncomingEmailToken: function() { + $('.incoming-email-token-reset').on('click', function(e) { + e.preventDefault(); + + $.ajax({ + type: 'PUT', + url: $('.incoming-email-token-reset').attr('href'), + dataType: 'json', + success: function(response) { + $('#issue_email').val(response.new_issue_address).focus(); + }, + beforeSend: function() { + $('.incoming-email-token-reset').text('resetting...'); + }, + complete: function() { + $('.incoming-email-token-reset').text('reset it'); + } + }); + }); + } +}; diff --git a/app/assets/javascripts/issue_status_select.js b/app/assets/javascripts/issue_status_select.js index 56cb536dcde..097939097cf 100644 --- a/app/assets/javascripts/issue_status_select.js +++ b/app/assets/javascripts/issue_status_select.js @@ -1,34 +1,31 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, max-len */ -(function() { - this.IssueStatusSelect = (function() { - function IssueStatusSelect() { - $('.js-issue-status').each(function(i, el) { - var fieldName; - fieldName = $(el).data("field-name"); - return $(el).glDropdown({ - selectable: true, - fieldName: fieldName, - toggleLabel: (function(_this) { - return function(selected, el, instance) { - var $item, label; - label = 'Author'; - $item = instance.dropdown.find('.is-active'); - if ($item.length) { - label = $item.text(); - } - return label; - }; - })(this), - clicked: function(options) { - return options.e.preventDefault(); - }, - id: function(obj, el) { - return $(el).data("id"); + +function IssueStatusSelect() { + $('.js-issue-status').each(function(i, el) { + var fieldName; + fieldName = $(el).data("field-name"); + return $(el).glDropdown({ + selectable: true, + fieldName: fieldName, + toggleLabel: (function(_this) { + return function(selected, el, instance) { + var $item, label; + label = 'Author'; + $item = instance.dropdown.find('.is-active'); + if ($item.length) { + label = $item.text(); } - }); - }); - } + return label; + }; + })(this), + clicked: function(options) { + return options.e.preventDefault(); + }, + id: function(obj, el) { + return $(el).data("id"); + } + }); + }); +} - return IssueStatusSelect; - })(); -}).call(window); +window.IssueStatusSelect = IssueStatusSelect; diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js index d8814802d9e..4927abac4c1 100644 --- a/app/assets/javascripts/label_manager.js +++ b/app/assets/javascripts/label_manager.js @@ -1,124 +1,123 @@ /* eslint-disable comma-dangle, class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, consistent-return, func-names, space-before-function-paren, max-len */ /* global Flash */ /* global Sortable */ +const gl = window.gl || (window.gl = {}); -((global) => { - class LabelManager { - constructor({ togglePriorityButton, prioritizedLabels, otherLabels } = {}) { - this.togglePriorityButton = togglePriorityButton || $('.js-toggle-priority'); - 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.sortable = Sortable.create(this.prioritizedLabels.get(0), { - filter: '.empty-message', - forceFallback: true, - fallbackClass: 'is-dragging', - dataIdAttr: 'data-id', - onUpdate: this.onPrioritySortUpdate.bind(this), - }); - this.bindEvents(); - } +class LabelManager { + constructor({ togglePriorityButton, prioritizedLabels, otherLabels } = {}) { + this.togglePriorityButton = togglePriorityButton || $('.js-toggle-priority'); + 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.sortable = Sortable.create(this.prioritizedLabels.get(0), { + filter: '.empty-message', + forceFallback: true, + fallbackClass: 'is-dragging', + dataIdAttr: 'data-id', + onUpdate: this.onPrioritySortUpdate.bind(this), + }); + this.bindEvents(); + } - bindEvents() { - this.prioritizedLabels.find('.btn-action').on('mousedown', this, this.onButtonActionClick); - return this.togglePriorityButton.on('click', this, this.onTogglePriorityClick); - } + bindEvents() { + this.prioritizedLabels.find('.btn-action').on('mousedown', this, this.onButtonActionClick); + return this.togglePriorityButton.on('click', this, this.onTogglePriorityClick); + } - onTogglePriorityClick(e) { - e.preventDefault(); - const _this = e.data; - const $btn = $(e.currentTarget); - const $label = $(`#${$btn.data('domId')}`); - const action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add'; - const $tooltip = $(`#${$btn.find('.has-tooltip:visible').attr('aria-describedby')}`); - $tooltip.tooltip('destroy'); - _this.toggleLabelPriority($label, action); - _this.toggleEmptyState($label, $btn, action); - } + onTogglePriorityClick(e) { + e.preventDefault(); + const _this = e.data; + const $btn = $(e.currentTarget); + const $label = $(`#${$btn.data('domId')}`); + const action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add'; + const $tooltip = $(`#${$btn.find('.has-tooltip:visible').attr('aria-describedby')}`); + $tooltip.tooltip('destroy'); + _this.toggleLabelPriority($label, action); + _this.toggleEmptyState($label, $btn, action); + } - onButtonActionClick(e) { - e.stopPropagation(); - $(e.currentTarget).tooltip('hide'); - } + onButtonActionClick(e) { + e.stopPropagation(); + $(e.currentTarget).tooltip('hide'); + } - toggleEmptyState($label, $btn, action) { - this.emptyState.classList.toggle('hidden', !!this.prioritizedLabels[0].querySelector(':scope > li')); - } + toggleEmptyState($label, $btn, action) { + this.emptyState.classList.toggle('hidden', !!this.prioritizedLabels[0].querySelector(':scope > li')); + } - toggleLabelPriority($label, action, persistState) { - if (persistState == null) { - persistState = true; - } - let xhr; - const _this = this; - const url = $label.find('.js-toggle-priority').data('url'); - let $target = this.prioritizedLabels; - let $from = this.otherLabels; - if (action === 'remove') { - $target = this.otherLabels; - $from = this.prioritizedLabels; - } - $label.detach().appendTo($target); - if ($from.find('li').length) { - $from.find('.empty-message').removeClass('hidden'); - } - if ($target.find('> li:not(.empty-message)').length) { - $target.find('.empty-message').addClass('hidden'); - } - // Return if we are not persisting state - if (!persistState) { - return; - } - if (action === 'remove') { - xhr = $.ajax({ - url, - type: 'DELETE' - }); - // Restore empty message - if (!$from.find('li').length) { - $from.find('.empty-message').removeClass('hidden'); - } - } else { - xhr = this.savePrioritySort($label, action); - } - return xhr.fail(this.rollbackLabelPosition.bind(this, $label, action)); + toggleLabelPriority($label, action, persistState) { + if (persistState == null) { + persistState = true; } - - onPrioritySortUpdate() { - const xhr = this.savePrioritySort(); - return xhr.fail(function() { - return new Flash(this.errorMessage, 'alert'); - }); + let xhr; + const _this = this; + const url = $label.find('.js-toggle-priority').data('url'); + let $target = this.prioritizedLabels; + let $from = this.otherLabels; + if (action === 'remove') { + $target = this.otherLabels; + $from = this.prioritizedLabels; } - - savePrioritySort() { - return $.post({ - url: this.prioritizedLabels.data('url'), - data: { - label_ids: this.getSortedLabelsIds() - } + $label.detach().appendTo($target); + if ($from.find('li').length) { + $from.find('.empty-message').removeClass('hidden'); + } + if ($target.find('> li:not(.empty-message)').length) { + $target.find('.empty-message').addClass('hidden'); + } + // Return if we are not persisting state + if (!persistState) { + return; + } + if (action === 'remove') { + xhr = $.ajax({ + url, + type: 'DELETE' }); + // Restore empty message + if (!$from.find('li').length) { + $from.find('.empty-message').removeClass('hidden'); + } + } else { + xhr = this.savePrioritySort($label, action); } + return xhr.fail(this.rollbackLabelPosition.bind(this, $label, action)); + } - rollbackLabelPosition($label, originalAction) { - const action = originalAction === 'remove' ? 'add' : 'remove'; - this.toggleLabelPriority($label, action, false); + onPrioritySortUpdate() { + const xhr = this.savePrioritySort(); + return xhr.fail(function() { return new Flash(this.errorMessage, 'alert'); - } + }); + } - getSortedLabelsIds() { - const sortedIds = []; - this.prioritizedLabels.find('> li').each(function() { - const id = $(this).data('id'); + savePrioritySort() { + return $.post({ + url: this.prioritizedLabels.data('url'), + data: { + label_ids: this.getSortedLabelsIds() + } + }); + } - if (id) { - sortedIds.push(id); - } - }); - return sortedIds; - } + rollbackLabelPosition($label, originalAction) { + const action = originalAction === 'remove' ? 'add' : 'remove'; + this.toggleLabelPriority($label, action, false); + return new Flash(this.errorMessage, 'alert'); + } + + getSortedLabelsIds() { + const sortedIds = []; + this.prioritizedLabels.find('> li').each(function() { + const id = $(this).data('id'); + + if (id) { + sortedIds.push(id); + } + }); + return sortedIds; } +} - gl.LabelManager = LabelManager; -})(window.gl || (window.gl = {})); +gl.LabelManager = LabelManager; diff --git a/app/assets/javascripts/labels.js b/app/assets/javascripts/labels.js index 03dd61b4263..5e87f26afba 100644 --- a/app/assets/javascripts/labels.js +++ b/app/assets/javascripts/labels.js @@ -1,44 +1,41 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, vars-on-top, no-unused-vars, max-len */ -(function() { - this.Labels = (function() { - function Labels() { - this.setSuggestedColor = this.setSuggestedColor.bind(this); - this.updateColorPreview = this.updateColorPreview.bind(this); - var form; - form = $('.label-form'); - this.cleanBinding(); - this.addBinding(); - this.updateColorPreview(); - } - Labels.prototype.addBinding = function() { - $(document).on('click', '.suggest-colors a', this.setSuggestedColor); - return $(document).on('input', 'input#label_color', this.updateColorPreview); - }; +function Labels() { + this.setSuggestedColor = this.setSuggestedColor.bind(this); + this.updateColorPreview = this.updateColorPreview.bind(this); + var form; + form = $('.label-form'); + this.cleanBinding(); + this.addBinding(); + this.updateColorPreview(); +} - Labels.prototype.cleanBinding = function() { - $(document).off('click', '.suggest-colors a'); - return $(document).off('input', 'input#label_color'); - }; +Labels.prototype.addBinding = function() { + $(document).on('click', '.suggest-colors a', this.setSuggestedColor); + return $(document).on('input', 'input#label_color', this.updateColorPreview); +}; - Labels.prototype.updateColorPreview = function() { - var previewColor; - previewColor = $('input#label_color').val(); - return $('div.label-color-preview').css('background-color', previewColor); - // Updates the the preview color with the hex-color input - }; +Labels.prototype.cleanBinding = function() { + $(document).off('click', '.suggest-colors a'); + return $(document).off('input', 'input#label_color'); +}; - // Updates the preview color with a click on a suggested color - Labels.prototype.setSuggestedColor = function(e) { - var color; - color = $(e.currentTarget).data('color'); - $('input#label_color').val(color); - this.updateColorPreview(); - // Notify the form, that color has changed - $('.label-form').trigger('keyup'); - return e.preventDefault(); - }; +Labels.prototype.updateColorPreview = function() { + var previewColor; + previewColor = $('input#label_color').val(); + return $('div.label-color-preview').css('background-color', previewColor); +// Updates the the preview color with the hex-color input +}; - return Labels; - })(); -}).call(window); +// Updates the preview color with a click on a suggested color +Labels.prototype.setSuggestedColor = function(e) { + var color; + color = $(e.currentTarget).data('color'); + $('input#label_color').val(color); + this.updateColorPreview(); + // Notify the form, that color has changed + $('.label-form').trigger('keyup'); + return e.preventDefault(); +}; + +window.Labels = Labels; diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 8d7d3d73571..1b0de126bff 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -4,492 +4,488 @@ import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; -(function() { - this.LabelsSelect = (function() { - function LabelsSelect(els) { - var _this, $els; - _this = this; +function LabelsSelect(els) { + var _this, $els; + _this = this; + + $els = $(els); + + if (!els) { + $els = $('.js-label-select'); + } + + $els.each(function(i, dropdown) { + var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer; + $dropdown = $(dropdown); + $dropdownContainer = $dropdown.closest('.labels-filter'); + $toggleText = $dropdown.find('.dropdown-toggle-text'); + namespacePath = $dropdown.data('namespace-path'); + projectPath = $dropdown.data('project-path'); + labelUrl = $dropdown.data('labels'); + issueUpdateURL = $dropdown.data('issueUpdate'); + selectedLabel = $dropdown.data('selected'); + if ((selectedLabel != null) && !$dropdown.hasClass('js-multiselect')) { + selectedLabel = selectedLabel.split(','); + } + showNo = $dropdown.data('show-no'); + showAny = $dropdown.data('show-any'); + showMenuAbove = $dropdown.data('showMenuAbove'); + defaultLabel = $dropdown.data('default-label'); + abilityName = $dropdown.data('ability-name'); + $selectbox = $dropdown.closest('.selectbox'); + $block = $selectbox.closest('.block'); + $form = $dropdown.closest('form, .js-issuable-update'); + $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span'); + $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip'); + $value = $block.find('.value'); + $loading = $block.find('.block-loading').fadeOut(); + fieldName = $dropdown.data('field-name'); + useId = $dropdown.is('.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown'); + propertyName = useId ? 'id' : 'title'; + initialSelected = $selectbox + .find('input[name="' + $dropdown.data('field-name') + '"]') + .map(function () { + return this.value; + }).get(); + if (issueUpdateURL != null) { + issueURLSplit = issueUpdateURL.split('/'); + } + if (issueUpdateURL) { + labelHTMLTemplate = _.template('<% _.each(labels, function(label){ %> <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>"> <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;"> <%- label.title %> </span> </a> <% }); %>'); + labelNoneHTMLTemplate = '<span class="no-value">None</span>'; + } - $els = $(els); + $sidebarLabelTooltip.tooltip(); - if (!els) { - $els = $('.js-label-select'); - } + if ($dropdown.closest('.dropdown').find('.dropdown-new-label').length) { + new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), namespacePath, projectPath); + } - $els.each(function(i, dropdown) { - var $block, $colorPreview, $dropdown, $form, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, defaultLabel, enableLabelCreateButton, issueURLSplit, issueUpdateURL, labelHTMLTemplate, labelNoneHTMLTemplate, labelUrl, namespacePath, projectPath, saveLabelData, selectedLabel, showAny, showNo, $sidebarLabelTooltip, initialSelected, $toggleText, fieldName, useId, propertyName, showMenuAbove, $container, $dropdownContainer; - $dropdown = $(dropdown); - $dropdownContainer = $dropdown.closest('.labels-filter'); - $toggleText = $dropdown.find('.dropdown-toggle-text'); - namespacePath = $dropdown.data('namespace-path'); - projectPath = $dropdown.data('project-path'); - labelUrl = $dropdown.data('labels'); - issueUpdateURL = $dropdown.data('issueUpdate'); - selectedLabel = $dropdown.data('selected'); - if ((selectedLabel != null) && !$dropdown.hasClass('js-multiselect')) { - selectedLabel = selectedLabel.split(','); - } - showNo = $dropdown.data('show-no'); - showAny = $dropdown.data('show-any'); - showMenuAbove = $dropdown.data('showMenuAbove'); - defaultLabel = $dropdown.data('default-label'); - abilityName = $dropdown.data('ability-name'); - $selectbox = $dropdown.closest('.selectbox'); - $block = $selectbox.closest('.block'); - $form = $dropdown.closest('form, .js-issuable-update'); - $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span'); - $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip'); - $value = $block.find('.value'); - $loading = $block.find('.block-loading').fadeOut(); - fieldName = $dropdown.data('field-name'); - useId = $dropdown.is('.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown'); - propertyName = useId ? 'id' : 'title'; - initialSelected = $selectbox - .find('input[name="' + $dropdown.data('field-name') + '"]') - .map(function () { - return this.value; - }).get(); - if (issueUpdateURL != null) { - issueURLSplit = issueUpdateURL.split('/'); - } - if (issueUpdateURL) { - labelHTMLTemplate = _.template('<% _.each(labels, function(label){ %> <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>"> <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;"> <%- label.title %> </span> </a> <% }); %>'); - labelNoneHTMLTemplate = '<span class="no-value">None</span>'; - } + saveLabelData = function() { + var data, selected; + selected = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "']").map(function() { + return this.value; + }).get(); - $sidebarLabelTooltip.tooltip(); + if (_.isEqual(initialSelected, selected)) return; + initialSelected = selected; - if ($dropdown.closest('.dropdown').find('.dropdown-new-label').length) { - new gl.CreateLabelDropdown($dropdown.closest('.dropdown').find('.dropdown-new-label'), namespacePath, projectPath); + data = {}; + data[abilityName] = {}; + data[abilityName].label_ids = selected; + if (!selected.length) { + data[abilityName].label_ids = ['']; + } + $loading.removeClass('hidden').fadeIn(); + $dropdown.trigger('loading.gl.dropdown'); + return $.ajax({ + type: 'PUT', + url: issueUpdateURL, + dataType: 'JSON', + data: data + }).done(function(data) { + var labelCount, template, labelTooltipTitle, labelTitles; + $loading.fadeOut(); + $dropdown.trigger('loaded.gl.dropdown'); + $selectbox.hide(); + data.issueURLSplit = issueURLSplit; + labelCount = 0; + if (data.labels.length) { + template = labelHTMLTemplate(data); + labelCount = data.labels.length; } + else { + template = labelNoneHTMLTemplate; + } + $value.removeAttr('style').html(template); + $sidebarCollapsedValue.text(labelCount); - saveLabelData = function() { - var data, selected; - selected = $dropdown.closest('.selectbox').find("input[name='" + fieldName + "']").map(function() { - return this.value; - }).get(); - - if (_.isEqual(initialSelected, selected)) return; - initialSelected = selected; + if (data.labels.length) { + labelTitles = data.labels.map(function(label) { + return label.title; + }); - data = {}; - data[abilityName] = {}; - data[abilityName].label_ids = selected; - if (!selected.length) { - data[abilityName].label_ids = ['']; + if (labelTitles.length > 5) { + labelTitles = labelTitles.slice(0, 5); + labelTitles.push('and ' + (data.labels.length - 5) + ' more'); } - $loading.removeClass('hidden').fadeIn(); - $dropdown.trigger('loading.gl.dropdown'); - return $.ajax({ - type: 'PUT', - url: issueUpdateURL, - dataType: 'JSON', - data: data - }).done(function(data) { - var labelCount, template, labelTooltipTitle, labelTitles; - $loading.fadeOut(); - $dropdown.trigger('loaded.gl.dropdown'); - $selectbox.hide(); - data.issueURLSplit = issueURLSplit; - labelCount = 0; - if (data.labels.length) { - template = labelHTMLTemplate(data); - labelCount = data.labels.length; - } - else { - template = labelNoneHTMLTemplate; - } - $value.removeAttr('style').html(template); - $sidebarCollapsedValue.text(labelCount); - - if (data.labels.length) { - labelTitles = data.labels.map(function(label) { - return label.title; - }); - - if (labelTitles.length > 5) { - labelTitles = labelTitles.slice(0, 5); - labelTitles.push('and ' + (data.labels.length - 5) + ' more'); - } - labelTooltipTitle = labelTitles.join(', '); - } - else { - labelTooltipTitle = ''; - $sidebarLabelTooltip.tooltip('destroy'); - } + labelTooltipTitle = labelTitles.join(', '); + } + else { + labelTooltipTitle = ''; + $sidebarLabelTooltip.tooltip('destroy'); + } - $sidebarLabelTooltip - .attr('title', labelTooltipTitle) - .tooltip('fixTitle'); + $sidebarLabelTooltip + .attr('title', labelTooltipTitle) + .tooltip('fixTitle'); - $('.has-tooltip', $value).tooltip({ - container: 'body' - }); - return $value.find('a').each(function(i) { - return setTimeout((function(_this) { - return function() { - return gl.animate.animate($(_this), 'pulse'); - }; - })(this), 200 * i); - }); - }); - }; - $dropdown.glDropdown({ - showMenuAbove: showMenuAbove, - data: function(term, callback) { - return $.ajax({ - url: labelUrl - }).done(function(data) { - data = _.chain(data).groupBy(function(label) { - return label.title; - }).map(function(label) { - var color; - color = _.map(label, function(dup) { - return dup.color; - }); - return { - id: label[0].id, - title: label[0].title, - color: color, - duplicate: color.length > 1 - }; - }).value(); - if ($dropdown.hasClass('js-extra-options')) { - var extraData = []; - if (showNo) { - extraData.unshift({ - id: 0, - title: 'No Label' - }); - } - if (showAny) { - extraData.unshift({ - isAny: true, - title: 'Any Label' - }); - } - if (extraData.length) { - extraData.push('divider'); - data = extraData.concat(data); - } - } - - callback(data); - if (showMenuAbove) { - $dropdown.data('glDropdown').positionMenuAbove(); - } + $('.has-tooltip', $value).tooltip({ + container: 'body' + }); + return $value.find('a').each(function(i) { + return setTimeout((function(_this) { + return function() { + return gl.animate.animate($(_this), 'pulse'); + }; + })(this), 200 * i); + }); + }); + }; + $dropdown.glDropdown({ + showMenuAbove: showMenuAbove, + data: function(term, callback) { + return $.ajax({ + url: labelUrl + }).done(function(data) { + data = _.chain(data).groupBy(function(label) { + return label.title; + }).map(function(label) { + var color; + color = _.map(label, function(dup) { + return dup.color; }); - }, - renderRow: function(label, instance) { - var $a, $li, color, colorEl, indeterminate, removesAll, selectedClass, spacing, i, marked, dropdownName, dropdownValue; - $li = $('<li>'); - $a = $('<a href="#">'); - selectedClass = []; - removesAll = label.id <= 0 || (label.id == null); - if ($dropdown.hasClass('js-filter-bulk-update')) { - indeterminate = $dropdown.data('indeterminate') || []; - marked = $dropdown.data('marked') || []; - - if (indeterminate.indexOf(label.id) !== -1) { - selectedClass.push('is-indeterminate'); - } - - if (marked.indexOf(label.id) !== -1) { - // Remove is-indeterminate class if the item will be marked as active - i = selectedClass.indexOf('is-indeterminate'); - if (i !== -1) { - selectedClass.splice(i, 1); - } - selectedClass.push('is-active'); - } - } else { - if (this.id(label)) { - dropdownName = $dropdown.data('fieldName'); - dropdownValue = this.id(label).toString().replace(/'/g, '\\\''); - - if ($form.find("input[type='hidden'][name='" + dropdownName + "'][value='" + dropdownValue + "']").length) { - selectedClass.push('is-active'); - } - } - - if ($dropdown.hasClass('js-multiselect') && removesAll) { - selectedClass.push('dropdown-clear-active'); - } - } - if (label.duplicate) { - spacing = 100 / label.color.length; - // Reduce the colors to 4 - label.color = label.color.filter(function(color, i) { - return i < 4; + return { + id: label[0].id, + title: label[0].title, + color: color, + duplicate: color.length > 1 + }; + }).value(); + if ($dropdown.hasClass('js-extra-options')) { + var extraData = []; + if (showNo) { + extraData.unshift({ + id: 0, + title: 'No Label' }); - color = _.map(label.color, function(color, i) { - var percentFirst, percentSecond; - percentFirst = Math.floor(spacing * i); - percentSecond = Math.floor(spacing * (i + 1)); - return color + " " + percentFirst + "%," + color + " " + percentSecond + "% "; - }).join(','); - color = "linear-gradient(" + color + ")"; - } - else { - if (label.color != null) { - color = label.color[0]; - } - } - if (color) { - colorEl = "<span class='dropdown-label-box' style='background: " + color + "'></span>"; - } - else { - colorEl = ''; - } - // We need to identify which items are actually labels - if (label.id) { - selectedClass.push('label-item'); - $a.attr('data-label-id', label.id); - } - $a.addClass(selectedClass.join(' ')).html(colorEl + " " + label.title); - // Return generated html - return $li.html($a).prop('outerHTML'); - }, - search: { - fields: ['title'] - }, - selectable: true, - filterable: true, - selected: $dropdown.data('selected') || [], - toggleLabel: function(selected, el) { - var isSelected = el !== null ? el.hasClass('is-active') : false; - var title = selected.title; - var selectedLabels = this.selected; - - if (selected.id === 0) { - this.selected = []; - return 'No Label'; - } - else if (isSelected) { - this.selected.push(title); - } - else { - var index = this.selected.indexOf(title); - this.selected.splice(index, 1); - } - - if (selectedLabels.length === 1) { - return selectedLabels; - } - else if (selectedLabels.length) { - return selectedLabels[0] + " +" + (selectedLabels.length - 1) + " more"; - } - else { - return defaultLabel; - } - }, - fieldName: $dropdown.data('field-name'), - id: function(label) { - if (label.id <= 0) return label.title; - - if ($dropdown.hasClass('js-issuable-form-dropdown')) { - return label.id; } - - if ($dropdown.hasClass("js-filter-submit") && (label.isAny == null)) { - return label.title; - } - else { - return label.id; - } - }, - hidden: function() { - var isIssueIndex, isMRIndex, page, selectedLabels; - page = $('body').data('page'); - isIssueIndex = page === 'projects:issues:index'; - isMRIndex = page === 'projects:merge_requests:index'; - $selectbox.hide(); - // display:block overrides the hide-collapse rule - $value.removeAttr('style'); - - if ($dropdown.hasClass('js-issuable-form-dropdown')) { - return; - } - - if ($('html').hasClass('issue-boards-page')) { - return; + if (showAny) { + extraData.unshift({ + isAny: true, + title: 'Any Label' + }); } - if ($dropdown.hasClass('js-multiselect')) { - if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { - selectedLabels = $dropdown.closest('form').find("input:hidden[name='" + ($dropdown.data('fieldName')) + "']"); - Issuable.filterResults($dropdown.closest('form')); - } - else if ($dropdown.hasClass('js-filter-submit')) { - $dropdown.closest('form').submit(); - } - else { - if (!$dropdown.hasClass('js-filter-bulk-update')) { - saveLabelData(); - } - } + if (extraData.length) { + extraData.push('divider'); + data = extraData.concat(data); } - }, - multiSelect: $dropdown.hasClass('js-multiselect'), - vue: $dropdown.hasClass('js-issue-board-sidebar'), - clicked: function(options) { - const { $el, e, isMarking } = options; - const label = options.selectedObj; - - var isIssueIndex, isMRIndex, page, boardsModel; - var fadeOutLoader = () => { - $loading.fadeOut(); - }; + } - page = $('body').data('page'); - isIssueIndex = page === 'projects:issues:index'; - isMRIndex = page === 'projects:merge_requests:index'; + callback(data); + if (showMenuAbove) { + $dropdown.data('glDropdown').positionMenuAbove(); + } + }); + }, + renderRow: function(label, instance) { + var $a, $li, color, colorEl, indeterminate, removesAll, selectedClass, spacing, i, marked, dropdownName, dropdownValue; + $li = $('<li>'); + $a = $('<a href="#">'); + selectedClass = []; + removesAll = label.id <= 0 || (label.id == null); + if ($dropdown.hasClass('js-filter-bulk-update')) { + indeterminate = $dropdown.data('indeterminate') || []; + marked = $dropdown.data('marked') || []; + + if (indeterminate.indexOf(label.id) !== -1) { + selectedClass.push('is-indeterminate'); + } - if ($dropdown.parent().find('.is-active:not(.dropdown-clear-active)').length) { - $dropdown.parent() - .find('.dropdown-clear-active') - .removeClass('is-active'); + if (marked.indexOf(label.id) !== -1) { + // Remove is-indeterminate class if the item will be marked as active + i = selectedClass.indexOf('is-indeterminate'); + if (i !== -1) { + selectedClass.splice(i, 1); } + selectedClass.push('is-active'); + } + } else { + if (this.id(label)) { + dropdownName = $dropdown.data('fieldName'); + dropdownValue = this.id(label).toString().replace(/'/g, '\\\''); - if ($dropdown.hasClass('js-issuable-form-dropdown')) { - return; + if ($form.find("input[type='hidden'][name='" + dropdownName + "'][value='" + dropdownValue + "']").length) { + selectedClass.push('is-active'); } + } - if ($dropdown.hasClass('js-filter-bulk-update')) { - _this.enableBulkLabelDropdown(); - _this.setDropdownData($dropdown, isMarking, label.id); - return; - } + if ($dropdown.hasClass('js-multiselect') && removesAll) { + selectedClass.push('dropdown-clear-active'); + } + } + if (label.duplicate) { + spacing = 100 / label.color.length; + // Reduce the colors to 4 + label.color = label.color.filter(function(color, i) { + return i < 4; + }); + color = _.map(label.color, function(color, i) { + var percentFirst, percentSecond; + percentFirst = Math.floor(spacing * i); + percentSecond = Math.floor(spacing * (i + 1)); + return color + " " + percentFirst + "%," + color + " " + percentSecond + "% "; + }).join(','); + color = "linear-gradient(" + color + ")"; + } + else { + if (label.color != null) { + color = label.color[0]; + } + } + if (color) { + colorEl = "<span class='dropdown-label-box' style='background: " + color + "'></span>"; + } + else { + colorEl = ''; + } + // We need to identify which items are actually labels + if (label.id) { + selectedClass.push('label-item'); + $a.attr('data-label-id', label.id); + } + $a.addClass(selectedClass.join(' ')).html(colorEl + " " + label.title); + // Return generated html + return $li.html($a).prop('outerHTML'); + }, + search: { + fields: ['title'] + }, + selectable: true, + filterable: true, + selected: $dropdown.data('selected') || [], + toggleLabel: function(selected, el) { + var isSelected = el !== null ? el.hasClass('is-active') : false; + var title = selected.title; + var selectedLabels = this.selected; + + if (selected.id === 0) { + this.selected = []; + return 'No Label'; + } + else if (isSelected) { + this.selected.push(title); + } + else { + var index = this.selected.indexOf(title); + this.selected.splice(index, 1); + } - if ($dropdown.closest('.add-issues-modal').length) { - boardsModel = gl.issueBoards.ModalStore.store.filter; - } + if (selectedLabels.length === 1) { + return selectedLabels; + } + else if (selectedLabels.length) { + return selectedLabels[0] + " +" + (selectedLabels.length - 1) + " more"; + } + else { + return defaultLabel; + } + }, + fieldName: $dropdown.data('field-name'), + id: function(label) { + if (label.id <= 0) return label.title; - if (boardsModel) { - if (label.isAny) { - boardsModel['label_name'] = []; - } else if ($el.hasClass('is-active')) { - boardsModel['label_name'].push(label.title); - } + if ($dropdown.hasClass('js-issuable-form-dropdown')) { + return label.id; + } - e.preventDefault(); - return; - } - else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { - if (!$dropdown.hasClass('js-multiselect')) { - selectedLabel = label.title; - return Issuable.filterResults($dropdown.closest('form')); - } - } - else if ($dropdown.hasClass('js-filter-submit')) { - return $dropdown.closest('form').submit(); - } - else if ($dropdown.hasClass('js-issue-board-sidebar')) { - if ($el.hasClass('is-active')) { - gl.issueBoards.BoardsStore.detail.issue.labels.push(new ListLabel({ - id: label.id, - title: label.title, - color: label.color[0], - textColor: '#fff' - })); - } - else { - var labels = gl.issueBoards.BoardsStore.detail.issue.labels; - labels = labels.filter(function (selectedLabel) { - return selectedLabel.id !== label.id; - }); - gl.issueBoards.BoardsStore.detail.issue.labels = labels; - } - - $loading.fadeIn(); - - gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) - .then(fadeOutLoader) - .catch(fadeOutLoader); - } - else { - if ($dropdown.hasClass('js-multiselect')) { + if ($dropdown.hasClass("js-filter-submit") && (label.isAny == null)) { + return label.title; + } + else { + return label.id; + } + }, + hidden: function() { + var isIssueIndex, isMRIndex, page, selectedLabels; + page = $('body').data('page'); + isIssueIndex = page === 'projects:issues:index'; + isMRIndex = page === 'projects:merge_requests:index'; + $selectbox.hide(); + // display:block overrides the hide-collapse rule + $value.removeAttr('style'); + + if ($dropdown.hasClass('js-issuable-form-dropdown')) { + return; + } - } - else { - return saveLabelData(); - } + if ($('html').hasClass('issue-boards-page')) { + return; + } + if ($dropdown.hasClass('js-multiselect')) { + if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { + selectedLabels = $dropdown.closest('form').find("input:hidden[name='" + ($dropdown.data('fieldName')) + "']"); + Issuable.filterResults($dropdown.closest('form')); + } + else if ($dropdown.hasClass('js-filter-submit')) { + $dropdown.closest('form').submit(); + } + else { + if (!$dropdown.hasClass('js-filter-bulk-update')) { + saveLabelData(); } - }, - }); - - // Set dropdown data - _this.setOriginalDropdownData($dropdownContainer, $dropdown); - }); - this.bindEvents(); - } + } + } + }, + multiSelect: $dropdown.hasClass('js-multiselect'), + vue: $dropdown.hasClass('js-issue-board-sidebar'), + clicked: function(options) { + const { $el, e, isMarking } = options; + const label = options.selectedObj; + + var isIssueIndex, isMRIndex, page, boardsModel; + var fadeOutLoader = () => { + $loading.fadeOut(); + }; - LabelsSelect.prototype.bindEvents = function() { - return $('body').on('change', '.selected_issue', this.onSelectCheckboxIssue); - }; + page = $('body').data('page'); + isIssueIndex = page === 'projects:issues:index'; + isMRIndex = page === 'projects:merge_requests:index'; - LabelsSelect.prototype.onSelectCheckboxIssue = function() { - if ($('.selected_issue:checked').length) { - return; - } - return $('.issues-bulk-update .labels-filter .dropdown-toggle-text').text('Label'); - }; + if ($dropdown.parent().find('.is-active:not(.dropdown-clear-active)').length) { + $dropdown.parent() + .find('.dropdown-clear-active') + .removeClass('is-active'); + } - LabelsSelect.prototype.enableBulkLabelDropdown = function() { - IssuableBulkUpdateActions.willUpdateLabels = true; - }; + if ($dropdown.hasClass('js-issuable-form-dropdown')) { + return; + } - LabelsSelect.prototype.setDropdownData = function($dropdown, isMarking, value) { - var i, markedIds, unmarkedIds, indeterminateIds; + if ($dropdown.hasClass('js-filter-bulk-update')) { + _this.enableBulkLabelDropdown(); + _this.setDropdownData($dropdown, isMarking, label.id); + return; + } - markedIds = $dropdown.data('marked') || []; - unmarkedIds = $dropdown.data('unmarked') || []; - indeterminateIds = $dropdown.data('indeterminate') || []; + if ($dropdown.closest('.add-issues-modal').length) { + boardsModel = gl.issueBoards.ModalStore.store.filter; + } - if (isMarking) { - markedIds.push(value); + if (boardsModel) { + if (label.isAny) { + boardsModel['label_name'] = []; + } else if ($el.hasClass('is-active')) { + boardsModel['label_name'].push(label.title); + } - i = indeterminateIds.indexOf(value); - if (i > -1) { - indeterminateIds.splice(i, 1); + e.preventDefault(); + return; } - - i = unmarkedIds.indexOf(value); - if (i > -1) { - unmarkedIds.splice(i, 1); + else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { + if (!$dropdown.hasClass('js-multiselect')) { + selectedLabel = label.title; + return Issuable.filterResults($dropdown.closest('form')); + } } - } else { - // If marked item (not common) is unmarked - i = markedIds.indexOf(value); - if (i > -1) { - markedIds.splice(i, 1); + else if ($dropdown.hasClass('js-filter-submit')) { + return $dropdown.closest('form').submit(); } + else if ($dropdown.hasClass('js-issue-board-sidebar')) { + if ($el.hasClass('is-active')) { + gl.issueBoards.BoardsStore.detail.issue.labels.push(new ListLabel({ + id: label.id, + title: label.title, + color: label.color[0], + textColor: '#fff' + })); + } + else { + var labels = gl.issueBoards.BoardsStore.detail.issue.labels; + labels = labels.filter(function (selectedLabel) { + return selectedLabel.id !== label.id; + }); + gl.issueBoards.BoardsStore.detail.issue.labels = labels; + } - // If an indeterminate item is being unmarked - if (IssuableBulkUpdateActions.getOriginalIndeterminateIds().indexOf(value) > -1) { - unmarkedIds.push(value); + $loading.fadeIn(); + + gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) + .then(fadeOutLoader) + .catch(fadeOutLoader); } + else { + if ($dropdown.hasClass('js-multiselect')) { - // If a marked item is being unmarked - // (a marked item could also be a label that is present in all selection) - if (IssuableBulkUpdateActions.getOriginalCommonIds().indexOf(value) > -1) { - unmarkedIds.push(value); + } + else { + return saveLabelData(); + } } - } + }, + }); + + // Set dropdown data + _this.setOriginalDropdownData($dropdownContainer, $dropdown); + }); + this.bindEvents(); +} + +LabelsSelect.prototype.bindEvents = function() { + return $('body').on('change', '.selected_issue', this.onSelectCheckboxIssue); +}; + +LabelsSelect.prototype.onSelectCheckboxIssue = function() { + if ($('.selected_issue:checked').length) { + return; + } + return $('.issues-bulk-update .labels-filter .dropdown-toggle-text').text('Label'); +}; + +LabelsSelect.prototype.enableBulkLabelDropdown = function() { + IssuableBulkUpdateActions.willUpdateLabels = true; +}; + +LabelsSelect.prototype.setDropdownData = function($dropdown, isMarking, value) { + var i, markedIds, unmarkedIds, indeterminateIds; + + markedIds = $dropdown.data('marked') || []; + unmarkedIds = $dropdown.data('unmarked') || []; + indeterminateIds = $dropdown.data('indeterminate') || []; + + if (isMarking) { + markedIds.push(value); + + i = indeterminateIds.indexOf(value); + if (i > -1) { + indeterminateIds.splice(i, 1); + } - $dropdown.data('marked', markedIds); - $dropdown.data('unmarked', unmarkedIds); - $dropdown.data('indeterminate', indeterminateIds); - }; + i = unmarkedIds.indexOf(value); + if (i > -1) { + unmarkedIds.splice(i, 1); + } + } else { + // If marked item (not common) is unmarked + i = markedIds.indexOf(value); + if (i > -1) { + markedIds.splice(i, 1); + } - LabelsSelect.prototype.setOriginalDropdownData = function($container, $dropdown) { - var labels = []; - $container.find('[name="label_name[]"]').map(function() { - return labels.push(this.value); - }); - $dropdown.data('marked', labels); - }; + // If an indeterminate item is being unmarked + if (IssuableBulkUpdateActions.getOriginalIndeterminateIds().indexOf(value) > -1) { + unmarkedIds.push(value); + } - return LabelsSelect; - })(); -}).call(window); + // If a marked item is being unmarked + // (a marked item could also be a label that is present in all selection) + if (IssuableBulkUpdateActions.getOriginalCommonIds().indexOf(value) > -1) { + unmarkedIds.push(value); + } + } + + $dropdown.data('marked', markedIds); + $dropdown.data('unmarked', unmarkedIds); + $dropdown.data('indeterminate', indeterminateIds); +}; + +LabelsSelect.prototype.setOriginalDropdownData = function($container, $dropdown) { + var labels = []; + $container.find('[name="label_name[]"]').map(function() { + return labels.push(this.value); + }); + $dropdown.data('marked', labels); +}; + +window.LabelsSelect = LabelsSelect; diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index 71064ccc539..db6ab6f83b6 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -1,58 +1,56 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, vars-on-top, max-len */ import _ from 'underscore'; -(function() { - var hideEndFade; - - hideEndFade = function($scrollingTabs) { - return $scrollingTabs.each(function() { - var $this; - $this = $(this); - return $this.siblings('.fade-right').toggleClass('scrolling', $this.width() < $this.prop('scrollWidth')); - }); - }; - - $(document).on('init.scrolling-tabs', () => { - const $scrollingTabs = $('.scrolling-tabs').not('.is-initialized'); - $scrollingTabs.addClass('is-initialized'); - - hideEndFade($scrollingTabs); - $(window).off('resize.nav').on('resize.nav', function() { - return hideEndFade($scrollingTabs); - }); - $scrollingTabs.off('scroll').on('scroll', function(event) { - var $this, currentPosition, maxPosition; - $this = $(this); - currentPosition = $this.scrollLeft(); - maxPosition = $this.prop('scrollWidth') - $this.outerWidth(); - $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0); - return $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1); - }); - - $scrollingTabs.each(function () { - var $this = $(this); - var scrollingTabWidth = $this.width(); - var $active = $this.find('.active'); - var activeWidth = $active.width(); - - if ($active.length) { - var offset = $active.offset().left + activeWidth; - - if (offset > scrollingTabWidth - 30) { - var scrollLeft = scrollingTabWidth / 2; - scrollLeft = (offset - scrollLeft) - (activeWidth / 2); - $this.scrollLeft(scrollLeft); - } - } - }); +var hideEndFade; + +hideEndFade = function($scrollingTabs) { + return $scrollingTabs.each(function() { + var $this; + $this = $(this); + return $this.siblings('.fade-right').toggleClass('scrolling', $this.width() < $this.prop('scrollWidth')); + }); +}; + +$(document).on('init.scrolling-tabs', () => { + const $scrollingTabs = $('.scrolling-tabs').not('.is-initialized'); + $scrollingTabs.addClass('is-initialized'); + + hideEndFade($scrollingTabs); + $(window).off('resize.nav').on('resize.nav', function() { + return hideEndFade($scrollingTabs); }); + $scrollingTabs.off('scroll').on('scroll', function(event) { + var $this, currentPosition, maxPosition; + $this = $(this); + currentPosition = $this.scrollLeft(); + maxPosition = $this.prop('scrollWidth') - $this.outerWidth(); + $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0); + return $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1); + }); + + $scrollingTabs.each(function () { + var $this = $(this); + var scrollingTabWidth = $this.width(); + var $active = $this.find('.active'); + var activeWidth = $active.width(); - function applyScrollNavClass() { - const scrollOpacityHeight = 40; - $('.navbar-border').css('opacity', Math.min($(window).scrollTop() / scrollOpacityHeight, 1)); - } + if ($active.length) { + var offset = $active.offset().left + activeWidth; - $(() => { - $(window).on('scroll', _.throttle(applyScrollNavClass, 100)); + if (offset > scrollingTabWidth - 30) { + var scrollLeft = scrollingTabWidth / 2; + scrollLeft = (offset - scrollLeft) - (activeWidth / 2); + $this.scrollLeft(scrollLeft); + } + } }); -}).call(window); +}); + +function applyScrollNavClass() { + const scrollOpacityHeight = 40; + $('.navbar-border').css('opacity', Math.min($(window).scrollTop() / scrollOpacityHeight, 1)); +} + +$(() => { + $(window).on('scroll', _.throttle(applyScrollNavClass, 100)); +}); diff --git a/app/assets/javascripts/lib/utils/animate.js b/app/assets/javascripts/lib/utils/animate.js index d93c1d0da59..a081e003891 100644 --- a/app/assets/javascripts/lib/utils/animate.js +++ b/app/assets/javascripts/lib/utils/animate.js @@ -1,49 +1,45 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, no-void, prefer-template, no-var, new-cap, prefer-arrow-callback, consistent-return, max-len */ -(function() { - (function(w) { - if (w.gl == null) { - w.gl = {}; +const w = window; + +if (w.gl == null) { + w.gl = {}; +} +if (gl.animate == null) { + gl.animate = {}; +} +gl.animate.animate = function($el, animation, options, done) { + if ((options != null ? options.cssStart : void 0) != null) { + $el.css(options.cssStart); + } + $el.removeClass(animation + ' animated').addClass(animation + ' animated').one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { + $(this).removeClass(animation + ' animated'); + if (done != null) { + done(); } - if (gl.animate == null) { - gl.animate = {}; + if ((options != null ? options.cssEnd : void 0) != null) { + $el.css(options.cssEnd); } - gl.animate.animate = function($el, animation, options, done) { - if ((options != null ? options.cssStart : void 0) != null) { - $el.css(options.cssStart); - } - $el.removeClass(animation + ' animated').addClass(animation + ' animated').one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { - $(this).removeClass(animation + ' animated'); - if (done != null) { - done(); + }); +}; +gl.animate.animateEach = function($els, animation, time, options, done) { + var dfd; + dfd = $.Deferred(); + if (!$els.length) { + dfd.resolve(); + } + $els.each(function(i) { + setTimeout(function() { + var $this; + $this = $(this); + return gl.animate.animate($this, animation, options, function() { + if (i === $els.length - 1) { + dfd.resolve(); + if (done != null) { + return done(); + } } - if ((options != null ? options.cssEnd : void 0) != null) { - $el.css(options.cssEnd); - } - }); - }; - gl.animate.animateEach = function($els, animation, time, options, done) { - var dfd; - dfd = $.Deferred(); - if (!$els.length) { - dfd.resolve(); - } - $els.each(function(i) { - setTimeout((function(_this) { - return function() { - var $this; - $this = $(_this); - return gl.animate.animate($this, animation, options, function() { - if (i === $els.length - 1) { - dfd.resolve(); - if (done != null) { - return done(); - } - } - }); - }; - })(this), time * i); }); - return dfd.promise(); - }; - })(window); -}).call(window); + }.bind(this), time * i); + }); + return dfd.promise(); +}; diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 122ec138c59..1c8efcb245c 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -1,423 +1,420 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-param-reassign, no-else-return, quotes, object-shorthand, comma-dangle, camelcase, one-var, vars-on-top, one-var-declaration-per-line, no-return-assign, consistent-return, max-len, prefer-template */ -(function() { - (function(w) { - var base; - const faviconEl = document.getElementById('favicon'); - const originalFavicon = faviconEl ? faviconEl.getAttribute('href') : null; - w.gl || (w.gl = {}); - (base = w.gl).utils || (base.utils = {}); - w.gl.utils.isInGroupsPage = function() { - return gl.utils.getPagePath() === 'groups'; - }; - w.gl.utils.isInProjectPage = function() { - return gl.utils.getPagePath() === 'projects'; - }; - w.gl.utils.getProjectSlug = function() { - if (this.isInProjectPage()) { - return $('body').data('project'); - } else { - return null; - } - }; - w.gl.utils.getGroupSlug = function() { - if (this.isInGroupsPage()) { - return $('body').data('group'); - } else { - return null; - } - }; - - w.gl.utils.ajaxGet = function(url) { - return $.ajax({ - type: "GET", - url: url, - dataType: "script" - }); - }; - - w.gl.utils.ajaxPost = function(url, data) { - return $.ajax({ - type: 'POST', - url: url, - data: data, - }); - }; - - w.gl.utils.extractLast = function(term) { - return this.split(term).pop(); - }; - - w.gl.utils.rstrip = function rstrip(val) { - if (val) { - return val.replace(/\s+$/, ''); +var base; +const w = window; +const faviconEl = document.getElementById('favicon'); +const originalFavicon = faviconEl ? faviconEl.getAttribute('href') : null; +w.gl || (w.gl = {}); +(base = w.gl).utils || (base.utils = {}); +w.gl.utils.isInGroupsPage = function() { + return gl.utils.getPagePath() === 'groups'; +}; +w.gl.utils.isInProjectPage = function() { + return gl.utils.getPagePath() === 'projects'; +}; +w.gl.utils.getProjectSlug = function() { + if (this.isInProjectPage()) { + return $('body').data('project'); + } else { + return null; + } +}; +w.gl.utils.getGroupSlug = function() { + if (this.isInGroupsPage()) { + return $('body').data('group'); + } else { + return null; + } +}; + +w.gl.utils.ajaxGet = function(url) { + return $.ajax({ + type: "GET", + url: url, + dataType: "script" + }); +}; + +w.gl.utils.ajaxPost = function(url, data) { + return $.ajax({ + type: 'POST', + url: url, + data: data, + }); +}; + +w.gl.utils.extractLast = function(term) { + return this.split(term).pop(); +}; + +w.gl.utils.rstrip = function rstrip(val) { + if (val) { + return val.replace(/\s+$/, ''); + } else { + return val; + } +}; + +gl.utils.updateTooltipTitle = function($tooltipEl, newTitle) { + return $tooltipEl.attr('title', newTitle).tooltip('fixTitle'); +}; + +w.gl.utils.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) { + event_name = event_name || 'input'; + var closest_submit, field, that; + that = this; + field = $(field_selector); + closest_submit = field.closest('form').find(button_selector); + if (this.rstrip(field.val()) === "") { + closest_submit.disable(); + } + return field.on(event_name, function() { + if (that.rstrip($(this).val()) === "") { + return closest_submit.disable(); + } else { + return closest_submit.enable(); + } + }); +}; + +// automatically adjust scroll position for hash urls taking the height of the navbar into account +// https://github.com/twitter/bootstrap/issues/1768 +w.gl.utils.handleLocationHash = function() { + var hash = w.gl.utils.getLocationHash(); + if (!hash) return; + + // This is required to handle non-unicode characters in hash + hash = decodeURIComponent(hash); + + var fixedTabs = document.querySelector('.js-tabs-affix'); + var fixedNav = document.querySelector('.navbar-gitlab'); + + var adjustment = 0; + if (fixedNav) adjustment -= fixedNav.offsetHeight; + + // scroll to user-generated markdown anchor if we cannot find a match + if (document.getElementById(hash) === null) { + var target = document.getElementById('user-content-' + hash); + if (target && target.scrollIntoView) { + target.scrollIntoView(true); + window.scrollBy(0, adjustment); + } + } else { + // only adjust for fixedTabs when not targeting user-generated content + if (fixedTabs) { + adjustment -= fixedTabs.offsetHeight; + } + window.scrollBy(0, adjustment); + } +}; + +// 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(index) { + index = index || 0; + return $('body').data('page').split(':')[index]; +}; + +gl.utils.parseUrl = function (url) { + var parser = document.createElement('a'); + parser.href = url; + return parser; +}; + +gl.utils.parseUrlPathname = function (url) { + var parsedUrl = gl.utils.parseUrl(url); + // parsedUrl.pathname will return an absolute path for Firefox and a relative path for IE11 + // We have to make sure we always have an absolute path. + return parsedUrl.pathname.charAt(0) === '/' ? parsedUrl.pathname : '/' + parsedUrl.pathname; +}; + +gl.utils.getUrlParamsArray = function () { + // We can trust that each param has one & since values containing & will be encoded + // Remove the first character of search as it is always ? + return window.location.search.slice(1).split('&').map((param) => { + const split = param.split('='); + return [decodeURI(split[0]), split[1]].join('='); + }); +}; + +gl.utils.isMetaKey = function(e) { + return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey; +}; + +gl.utils.isMetaClick = function(e) { + // Identify following special clicks + // 1) Cmd + Click on Mac (e.metaKey) + // 2) Ctrl + Click on PC (e.ctrlKey) + // 3) Middle-click or Mouse Wheel Click (e.which is 2) + return e.metaKey || e.ctrlKey || e.which === 2; +}; + +gl.utils.scrollToElement = function($el) { + var top = $el.offset().top; + gl.mrTabsHeight = gl.mrTabsHeight || $('.merge-request-tabs').height(); + + return $('body, html').animate({ + scrollTop: top - (gl.mrTabsHeight) + }, 200); +}; + +/** + this will take in the `name` of the param you want to parse in the url + if the name does not exist this function will return `null` + otherwise it will return the value of the param key provided +*/ +w.gl.utils.getParameterByName = (name, parseUrl) => { + const url = parseUrl || window.location.href; + name = name.replace(/[[\]]/g, '\\$&'); + const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`); + const results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, ' ')); +}; + +w.gl.utils.getSelectedFragment = () => { + const selection = window.getSelection(); + if (selection.rangeCount === 0) return null; + const documentFragment = document.createDocumentFragment(); + for (let i = 0; i < selection.rangeCount; i += 1) { + documentFragment.appendChild(selection.getRangeAt(i).cloneContents()); + } + if (documentFragment.textContent.length === 0) return null; + + return documentFragment; +}; + +w.gl.utils.insertText = (target, text) => { + // Firefox doesn't support `document.execCommand('insertText', false, text)` on textareas + + const selectionStart = target.selectionStart; + const selectionEnd = target.selectionEnd; + const value = target.value; + + const textBefore = value.substring(0, selectionStart); + const textAfter = value.substring(selectionEnd, value.length); + + const insertedText = text instanceof Function ? text(textBefore, textAfter) : text; + const newText = textBefore + insertedText + textAfter; + + target.value = newText; + target.selectionStart = target.selectionEnd = selectionStart + insertedText.length; + + // Trigger autosave + $(target).trigger('input'); + + // Trigger autosize + var event = document.createEvent('Event'); + event.initEvent('autosize:update', true, false); + target.dispatchEvent(event); +}; + +w.gl.utils.nodeMatchesSelector = (node, selector) => { + const matches = Element.prototype.matches || + Element.prototype.matchesSelector || + Element.prototype.mozMatchesSelector || + Element.prototype.msMatchesSelector || + Element.prototype.oMatchesSelector || + Element.prototype.webkitMatchesSelector; + + if (matches) { + return matches.call(node, selector); + } + + // IE11 doesn't support `node.matches(selector)` + + let parentNode = node.parentNode; + if (!parentNode) { + parentNode = document.createElement('div'); + node = node.cloneNode(true); + parentNode.appendChild(node); + } + + const matchingNodes = parentNode.querySelectorAll(selector); + return Array.prototype.indexOf.call(matchingNodes, node) !== -1; +}; + +/** + this will take in the headers from an API response and normalize them + this way we don't run into production issues when nginx gives us lowercased header keys +*/ +w.gl.utils.normalizeHeaders = (headers) => { + const upperCaseHeaders = {}; + + Object.keys(headers).forEach((e) => { + upperCaseHeaders[e.toUpperCase()] = headers[e]; + }); + + return upperCaseHeaders; +}; + +/** + this will take in the getAllResponseHeaders result and normalize them + this way we don't run into production issues when nginx gives us lowercased header keys +*/ +w.gl.utils.normalizeCRLFHeaders = (headers) => { + const headersObject = {}; + const headersArray = headers.split('\n'); + + headersArray.forEach((header) => { + const keyValue = header.split(': '); + headersObject[keyValue[0]] = keyValue[1]; + }); + + return w.gl.utils.normalizeHeaders(headersObject); +}; + +/** + * Parses pagination object string values into numbers. + * + * @param {Object} paginationInformation + * @returns {Object} + */ +w.gl.utils.parseIntPagination = paginationInformation => ({ + perPage: parseInt(paginationInformation['X-PER-PAGE'], 10), + page: parseInt(paginationInformation['X-PAGE'], 10), + total: parseInt(paginationInformation['X-TOTAL'], 10), + totalPages: parseInt(paginationInformation['X-TOTAL-PAGES'], 10), + nextPage: parseInt(paginationInformation['X-NEXT-PAGE'], 10), + previousPage: parseInt(paginationInformation['X-PREV-PAGE'], 10), +}); + +/** + * Updates the search parameter of a URL given the parameter and value provided. + * + * If no search params are present we'll add it. + * If param for page is already present, we'll update it + * If there are params but not for the given one, we'll add it at the end. + * Returns the new search parameters. + * + * @param {String} param + * @param {Number|String|Undefined|Null} value + * @return {String} + */ +w.gl.utils.setParamInURL = (param, value) => { + let search; + const locationSearch = window.location.search; + + if (locationSearch.length) { + const parameters = locationSearch.substring(1, locationSearch.length) + .split('&') + .reduce((acc, element) => { + const val = element.split('='); + acc[val[0]] = decodeURIComponent(val[1]); + return acc; + }, {}); + + parameters[param] = value; + + const toString = Object.keys(parameters) + .map(val => `${val}=${encodeURIComponent(parameters[val])}`) + .join('&'); + + search = `?${toString}`; + } else { + search = `?${param}=${value}`; + } + + return search; +}; + +/** + * Converts permission provided as strings to booleans. + * + * @param {String} string + * @returns {Boolean} + */ +w.gl.utils.convertPermissionToBoolean = permission => permission === 'true'; + +/** + * Back Off exponential algorithm + * backOff :: (Function<next, stop>, Number) -> Promise<Any, Error> + * + * @param {Function<next, stop>} fn function to be called + * @param {Number} timeout + * @return {Promise<Any, Error>} + * @example + * ``` + * backOff(function (next, stop) { + * // Let's perform this function repeatedly for 60s or for the timeout provided. + * + * ourFunction() + * .then(function (result) { + * // continue if result is not what we need + * next(); + * + * // when result is what we need let's stop with the repetions and jump out of the cycle + * stop(result); + * }) + * .catch(function (error) { + * // if there is an error, we need to stop this with an error. + * stop(error); + * }) + * }, 60000) + * .then(function (result) {}) + * .catch(function (error) { + * // deal with errors passed to stop() + * }) + * ``` + */ +w.gl.utils.backOff = (fn, timeout = 60000) => { + const maxInterval = 32000; + let nextInterval = 2000; + + const startTime = Date.now(); + + return new Promise((resolve, reject) => { + const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg)); + + const next = () => { + if (Date.now() - startTime < timeout) { + setTimeout(fn.bind(null, next, stop), nextInterval); + nextInterval = Math.min(nextInterval + nextInterval, maxInterval); } else { - return val; + reject(new Error('BACKOFF_TIMEOUT')); } }; - gl.utils.updateTooltipTitle = function($tooltipEl, newTitle) { - return $tooltipEl.attr('title', newTitle).tooltip('fixTitle'); - }; - - w.gl.utils.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) { - event_name = event_name || 'input'; - var closest_submit, field, that; - that = this; - field = $(field_selector); - closest_submit = field.closest('form').find(button_selector); - if (this.rstrip(field.val()) === "") { - closest_submit.disable(); - } - return field.on(event_name, function() { - if (that.rstrip($(this).val()) === "") { - return closest_submit.disable(); - } else { - return closest_submit.enable(); - } - }); - }; - - // automatically adjust scroll position for hash urls taking the height of the navbar into account - // https://github.com/twitter/bootstrap/issues/1768 - w.gl.utils.handleLocationHash = function() { - var hash = w.gl.utils.getLocationHash(); - if (!hash) return; - - // This is required to handle non-unicode characters in hash - hash = decodeURIComponent(hash); - - var fixedTabs = document.querySelector('.js-tabs-affix'); - var fixedNav = document.querySelector('.navbar-gitlab'); - - var adjustment = 0; - if (fixedNav) adjustment -= fixedNav.offsetHeight; - - // scroll to user-generated markdown anchor if we cannot find a match - if (document.getElementById(hash) === null) { - var target = document.getElementById('user-content-' + hash); - if (target && target.scrollIntoView) { - target.scrollIntoView(true); - window.scrollBy(0, adjustment); - } + fn(next, stop); + }); +}; + +w.gl.utils.setFavicon = (faviconPath) => { + if (faviconEl && faviconPath) { + faviconEl.setAttribute('href', faviconPath); + } +}; + +w.gl.utils.resetFavicon = () => { + if (faviconEl) { + faviconEl.setAttribute('href', originalFavicon); + } +}; + +w.gl.utils.setCiStatusFavicon = (pageUrl) => { + $.ajax({ + url: pageUrl, + dataType: 'json', + success: function(data) { + if (data && data.favicon) { + gl.utils.setFavicon(data.favicon); } else { - // only adjust for fixedTabs when not targeting user-generated content - if (fixedTabs) { - adjustment -= fixedTabs.offsetHeight; - } - window.scrollBy(0, adjustment); + gl.utils.resetFavicon(); } - }; - - // 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(index) { - index = index || 0; - return $('body').data('page').split(':')[index]; - }; - - gl.utils.parseUrl = function (url) { - var parser = document.createElement('a'); - parser.href = url; - return parser; - }; - - gl.utils.parseUrlPathname = function (url) { - var parsedUrl = gl.utils.parseUrl(url); - // parsedUrl.pathname will return an absolute path for Firefox and a relative path for IE11 - // We have to make sure we always have an absolute path. - return parsedUrl.pathname.charAt(0) === '/' ? parsedUrl.pathname : '/' + parsedUrl.pathname; - }; - - gl.utils.getUrlParamsArray = function () { - // We can trust that each param has one & since values containing & will be encoded - // Remove the first character of search as it is always ? - return window.location.search.slice(1).split('&').map((param) => { - const split = param.split('='); - return [decodeURI(split[0]), split[1]].join('='); - }); - }; - - gl.utils.isMetaKey = function(e) { - return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey; - }; - - gl.utils.isMetaClick = function(e) { - // Identify following special clicks - // 1) Cmd + Click on Mac (e.metaKey) - // 2) Ctrl + Click on PC (e.ctrlKey) - // 3) Middle-click or Mouse Wheel Click (e.which is 2) - return e.metaKey || e.ctrlKey || e.which === 2; - }; - - gl.utils.scrollToElement = function($el) { - var top = $el.offset().top; - gl.mrTabsHeight = gl.mrTabsHeight || $('.merge-request-tabs').height(); - - return $('body, html').animate({ - scrollTop: top - (gl.mrTabsHeight) - }, 200); - }; - - /** - this will take in the `name` of the param you want to parse in the url - if the name does not exist this function will return `null` - otherwise it will return the value of the param key provided - */ - w.gl.utils.getParameterByName = (name, parseUrl) => { - const url = parseUrl || window.location.href; - name = name.replace(/[[\]]/g, '\\$&'); - const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`); - const results = regex.exec(url); - if (!results) return null; - if (!results[2]) return ''; - return decodeURIComponent(results[2].replace(/\+/g, ' ')); - }; - - w.gl.utils.getSelectedFragment = () => { - const selection = window.getSelection(); - if (selection.rangeCount === 0) return null; - const documentFragment = document.createDocumentFragment(); - for (let i = 0; i < selection.rangeCount; i += 1) { - documentFragment.appendChild(selection.getRangeAt(i).cloneContents()); - } - if (documentFragment.textContent.length === 0) return null; - - return documentFragment; - }; - - w.gl.utils.insertText = (target, text) => { - // Firefox doesn't support `document.execCommand('insertText', false, text)` on textareas - - const selectionStart = target.selectionStart; - const selectionEnd = target.selectionEnd; - const value = target.value; - - const textBefore = value.substring(0, selectionStart); - const textAfter = value.substring(selectionEnd, value.length); - - const insertedText = text instanceof Function ? text(textBefore, textAfter) : text; - const newText = textBefore + insertedText + textAfter; - - target.value = newText; - target.selectionStart = target.selectionEnd = selectionStart + insertedText.length; - - // Trigger autosave - $(target).trigger('input'); - - // Trigger autosize - var event = document.createEvent('Event'); - event.initEvent('autosize:update', true, false); - target.dispatchEvent(event); - }; - - w.gl.utils.nodeMatchesSelector = (node, selector) => { - const matches = Element.prototype.matches || - Element.prototype.matchesSelector || - Element.prototype.mozMatchesSelector || - Element.prototype.msMatchesSelector || - Element.prototype.oMatchesSelector || - Element.prototype.webkitMatchesSelector; - - if (matches) { - return matches.call(node, selector); - } - - // IE11 doesn't support `node.matches(selector)` - - let parentNode = node.parentNode; - if (!parentNode) { - parentNode = document.createElement('div'); - node = node.cloneNode(true); - parentNode.appendChild(node); - } - - const matchingNodes = parentNode.querySelectorAll(selector); - return Array.prototype.indexOf.call(matchingNodes, node) !== -1; - }; - - /** - this will take in the headers from an API response and normalize them - this way we don't run into production issues when nginx gives us lowercased header keys - */ - w.gl.utils.normalizeHeaders = (headers) => { - const upperCaseHeaders = {}; - - Object.keys(headers).forEach((e) => { - upperCaseHeaders[e.toUpperCase()] = headers[e]; - }); - - return upperCaseHeaders; - }; - - /** - this will take in the getAllResponseHeaders result and normalize them - this way we don't run into production issues when nginx gives us lowercased header keys - */ - w.gl.utils.normalizeCRLFHeaders = (headers) => { - const headersObject = {}; - const headersArray = headers.split('\n'); - - headersArray.forEach((header) => { - const keyValue = header.split(': '); - headersObject[keyValue[0]] = keyValue[1]; - }); - - return w.gl.utils.normalizeHeaders(headersObject); - }; - - /** - * Parses pagination object string values into numbers. - * - * @param {Object} paginationInformation - * @returns {Object} - */ - w.gl.utils.parseIntPagination = paginationInformation => ({ - perPage: parseInt(paginationInformation['X-PER-PAGE'], 10), - page: parseInt(paginationInformation['X-PAGE'], 10), - total: parseInt(paginationInformation['X-TOTAL'], 10), - totalPages: parseInt(paginationInformation['X-TOTAL-PAGES'], 10), - nextPage: parseInt(paginationInformation['X-NEXT-PAGE'], 10), - previousPage: parseInt(paginationInformation['X-PREV-PAGE'], 10), - }); - - /** - * Updates the search parameter of a URL given the parameter and value provided. - * - * If no search params are present we'll add it. - * If param for page is already present, we'll update it - * If there are params but not for the given one, we'll add it at the end. - * Returns the new search parameters. - * - * @param {String} param - * @param {Number|String|Undefined|Null} value - * @return {String} - */ - w.gl.utils.setParamInURL = (param, value) => { - let search; - const locationSearch = window.location.search; - - if (locationSearch.length) { - const parameters = locationSearch.substring(1, locationSearch.length) - .split('&') - .reduce((acc, element) => { - const val = element.split('='); - acc[val[0]] = decodeURIComponent(val[1]); - return acc; - }, {}); - - parameters[param] = value; - - const toString = Object.keys(parameters) - .map(val => `${val}=${encodeURIComponent(parameters[val])}`) - .join('&'); - - search = `?${toString}`; - } else { - search = `?${param}=${value}`; - } - - return search; - }; - - /** - * Converts permission provided as strings to booleans. - * - * @param {String} string - * @returns {Boolean} - */ - w.gl.utils.convertPermissionToBoolean = permission => permission === 'true'; - - /** - * Back Off exponential algorithm - * backOff :: (Function<next, stop>, Number) -> Promise<Any, Error> - * - * @param {Function<next, stop>} fn function to be called - * @param {Number} timeout - * @return {Promise<Any, Error>} - * @example - * ``` - * backOff(function (next, stop) { - * // Let's perform this function repeatedly for 60s or for the timeout provided. - * - * ourFunction() - * .then(function (result) { - * // continue if result is not what we need - * next(); - * - * // when result is what we need let's stop with the repetions and jump out of the cycle - * stop(result); - * }) - * .catch(function (error) { - * // if there is an error, we need to stop this with an error. - * stop(error); - * }) - * }, 60000) - * .then(function (result) {}) - * .catch(function (error) { - * // deal with errors passed to stop() - * }) - * ``` - */ - w.gl.utils.backOff = (fn, timeout = 60000) => { - const maxInterval = 32000; - let nextInterval = 2000; - - const startTime = Date.now(); - - return new Promise((resolve, reject) => { - const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg)); - - const next = () => { - if (Date.now() - startTime < timeout) { - setTimeout(fn.bind(null, next, stop), nextInterval); - nextInterval = Math.min(nextInterval + nextInterval, maxInterval); - } else { - reject(new Error('BACKOFF_TIMEOUT')); - } - }; - - fn(next, stop); - }); - }; - - w.gl.utils.setFavicon = (faviconPath) => { - if (faviconEl && faviconPath) { - faviconEl.setAttribute('href', faviconPath); - } - }; - - w.gl.utils.resetFavicon = () => { - if (faviconEl) { - faviconEl.setAttribute('href', originalFavicon); - } - }; - - w.gl.utils.setCiStatusFavicon = (pageUrl) => { - $.ajax({ - url: pageUrl, - dataType: 'json', - success: function(data) { - if (data && data.favicon) { - gl.utils.setFavicon(data.favicon); - } else { - gl.utils.resetFavicon(); - } - }, - error: function() { - gl.utils.resetFavicon(); - } - }); - }; - })(window); -}).call(window); + }, + error: function() { + gl.utils.resetFavicon(); + } + }); +}; diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 1d1763c3963..ee7b6417c5b 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len */ +/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len, vars-on-top */ import timeago from 'timeago.js'; import dateFormat from 'vendor/date.format'; @@ -11,123 +11,120 @@ import { window.timeago = timeago; window.dateFormat = dateFormat; -(function() { - (function(w) { - var base; - var timeagoInstance; +var w = window; +var base; +var timeagoInstance; - if (w.gl == null) { - w.gl = {}; - } - if ((base = w.gl).utils == null) { - base.utils = {}; - } - w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; - - w.gl.utils.formatDate = function(datetime) { - return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z'); - }; +if (w.gl == null) { + w.gl = {}; +} +if ((base = w.gl).utils == null) { + base.utils = {}; +} +w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; - w.gl.utils.getDayName = function(date) { - return this.days[date.getDay()]; - }; +w.gl.utils.formatDate = function(datetime) { + return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z'); +}; - w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) { - $timeagoEls.each((i, el) => { - el.setAttribute('title', el.getAttribute('title')); +w.gl.utils.getDayName = function(date) { + return this.days[date.getDay()]; +}; - if (setTimeago) { - // Recreate with custom template - $(el).tooltip({ - template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' - }); - } +w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) { + $timeagoEls.each((i, el) => { + el.setAttribute('title', el.getAttribute('title')); - el.classList.add('js-timeago-render'); + if (setTimeago) { + // Recreate with custom template + $(el).tooltip({ + template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' }); + } - gl.utils.renderTimeago($timeagoEls); + el.classList.add('js-timeago-render'); + }); + + gl.utils.renderTimeago($timeagoEls); +}; + +w.gl.utils.getTimeago = function() { + var locale; + + if (!timeagoInstance) { + const localeRemaining = function(number, index) { + return [ + [s__('Timeago|less than a minute ago'), s__('Timeago|a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], + [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], + [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], + [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')], + [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')], + [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')], + [s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')], + [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')], + [s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')], + [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')], + [s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')], + [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')], + [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')] + ][index]; }; - - w.gl.utils.getTimeago = function() { - var locale; - - if (!timeagoInstance) { - const localeRemaining = function(number, index) { - return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|a while')], - [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')], - [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')], - [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], - [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')], - [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')], - [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')], - [s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')], - [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')], - [s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')], - [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')], - [s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')], - [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')], - [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')] - ][index]; - }; - locale = function(number, index) { - return [ - [s__('Timeago|less than a minute ago'), s__('Timeago|a while')], - [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], - [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], - [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], - [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')], - [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')], - [s__('Timeago|a day ago'), s__('Timeago|in 1 day')], - [s__('Timeago|%s days ago'), s__('Timeago|in %s days')], - [s__('Timeago|a week ago'), s__('Timeago|in 1 week')], - [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')], - [s__('Timeago|a month ago'), s__('Timeago|in 1 month')], - [s__('Timeago|%s months ago'), s__('Timeago|in %s months')], - [s__('Timeago|a year ago'), s__('Timeago|in 1 year')], - [s__('Timeago|%s years ago'), s__('Timeago|in %s years')] - ][index]; - }; - - timeago.register(lang, locale); - timeago.register(`${lang}-remaining`, localeRemaining); - timeagoInstance = timeago(); - } - - return timeagoInstance; + locale = function(number, index) { + return [ + [s__('Timeago|less than a minute ago'), s__('Timeago|a while')], + [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')], + [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')], + [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], + [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')], + [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')], + [s__('Timeago|a day ago'), s__('Timeago|in 1 day')], + [s__('Timeago|%s days ago'), s__('Timeago|in %s days')], + [s__('Timeago|a week ago'), s__('Timeago|in 1 week')], + [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')], + [s__('Timeago|a month ago'), s__('Timeago|in 1 month')], + [s__('Timeago|%s months ago'), s__('Timeago|in %s months')], + [s__('Timeago|a year ago'), s__('Timeago|in 1 year')], + [s__('Timeago|%s years ago'), s__('Timeago|in %s years')] + ][index]; }; - w.gl.utils.timeFor = function(time, suffix, expiredLabel) { - var timefor; - if (!time) { - return ''; - } - if (new Date(time) < new Date()) { - expiredLabel || (expiredLabel = s__('Timeago|Past due')); - timefor = expiredLabel; - } else { - timefor = gl.utils.getTimeago().format(time, `${lang}-remaining`).trim(); - } - return timefor; - }; + timeago.register(lang, locale); + timeago.register(`${lang}-remaining`, localeRemaining); + timeagoInstance = timeago(); + } - w.gl.utils.renderTimeago = function($els) { - const timeagoEls = $els || document.querySelectorAll('.js-timeago-render'); + return timeagoInstance; +}; - // timeago.js sets timeouts internally for each timeago value to be updated in real time - gl.utils.getTimeago().render(timeagoEls, lang); - }; +w.gl.utils.timeFor = function(time, suffix, expiredLabel) { + var timefor; + if (!time) { + return ''; + } + if (new Date(time) < new Date()) { + expiredLabel || (expiredLabel = s__('Timeago|Past due')); + timefor = expiredLabel; + } else { + timefor = gl.utils.getTimeago().format(time, `${lang}-remaining`).trim(); + } + return timefor; +}; - w.gl.utils.getDayDifference = function(a, b) { - var millisecondsPerDay = 1000 * 60 * 60 * 24; - var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()); - var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()); +w.gl.utils.renderTimeago = function($els) { + const timeagoEls = $els || document.querySelectorAll('.js-timeago-render'); - return Math.floor((date2 - date1) / millisecondsPerDay); - }; - })(window); -}).call(window); + // timeago.js sets timeouts internally for each timeago value to be updated in real time + gl.utils.getTimeago().render(timeagoEls, lang); +}; + +w.gl.utils.getDayDifference = function(a, b) { + var millisecondsPerDay = 1000 * 60 * 60 * 24; + var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()); + var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()); + + return Math.floor((date2 - date1) / millisecondsPerDay); +}; /** * Port of ruby helper time_interval_in_words. diff --git a/app/assets/javascripts/lib/utils/pretty_time.js b/app/assets/javascripts/lib/utils/pretty_time.js index ae397212e55..a3d0b55afd3 100644 --- a/app/assets/javascripts/lib/utils/pretty_time.js +++ b/app/assets/javascripts/lib/utils/pretty_time.js @@ -1,65 +1,63 @@ -(() => { +/* + * TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints, + * stringifyTime condensed or non-condensed, abbreviateTimelengths) + * */ +const gl = window.gl || (window.gl = {}); +const utils = window.gl.utils = gl.utils || {}; +const prettyTime = utils.prettyTime = { /* - * TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints, - * stringifyTime condensed or non-condensed, abbreviateTimelengths) - * */ + * Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # } + * Seconds can be negative or positive, zero or non-zero. + */ + parseSeconds(seconds) { + const DAYS_PER_WEEK = 5; + const HOURS_PER_DAY = 8; + const MINUTES_PER_HOUR = 60; + const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR; + const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR; + + const timePeriodConstraints = { + weeks: MINUTES_PER_WEEK, + days: MINUTES_PER_DAY, + hours: MINUTES_PER_HOUR, + minutes: 1, + }; + + let unorderedMinutes = prettyTime.secondsToMinutes(seconds); + + return _.mapObject(timePeriodConstraints, (minutesPerPeriod) => { + const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod); + + unorderedMinutes -= (periodCount * minutesPerPeriod); + + return periodCount; + }); + }, - const utils = window.gl.utils = gl.utils || {}; - const prettyTime = utils.prettyTime = { - /* - * Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # } - * Seconds can be negative or positive, zero or non-zero. - */ - parseSeconds(seconds) { - const DAYS_PER_WEEK = 5; - const HOURS_PER_DAY = 8; - const MINUTES_PER_HOUR = 60; - const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR; - const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR; - - const timePeriodConstraints = { - weeks: MINUTES_PER_WEEK, - days: MINUTES_PER_DAY, - hours: MINUTES_PER_HOUR, - minutes: 1, - }; - - let unorderedMinutes = prettyTime.secondsToMinutes(seconds); - - return _.mapObject(timePeriodConstraints, (minutesPerPeriod) => { - const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod); - - unorderedMinutes -= (periodCount * minutesPerPeriod); - - return periodCount; - }); - }, - - /* - * Accepts a timeObject and returns a condensed string representation of it - * (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included. - */ - - stringifyTime(timeObject) { - const reducedTime = _.reduce(timeObject, (memo, unitValue, unitName) => { - const isNonZero = !!unitValue; - return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo; - }, '').trim(); - return reducedTime.length ? reducedTime : '0m'; - }, - - /* - * Accepts a time string of any size (e.g. '1w 2d 3h 5m' or '1w 2d') and returns - * the first non-zero unit/value pair. - */ - - abbreviateTime(timeStr) { - return timeStr.split(' ') - .filter(unitStr => unitStr.charAt(0) !== '0')[0]; - }, + /* + * Accepts a timeObject and returns a condensed string representation of it + * (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included. + */ + + stringifyTime(timeObject) { + const reducedTime = _.reduce(timeObject, (memo, unitValue, unitName) => { + const isNonZero = !!unitValue; + return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo; + }, '').trim(); + return reducedTime.length ? reducedTime : '0m'; + }, - secondsToMinutes(seconds) { - return Math.abs(seconds / 60); - }, - }; -})(window.gl || (window.gl = {})); + /* + * Accepts a time string of any size (e.g. '1w 2d 3h 5m' or '1w 2d') and returns + * the first non-zero unit/value pair. + */ + + abbreviateTime(timeStr) { + return timeStr.split(' ') + .filter(unitStr => unitStr.charAt(0) !== '0')[0]; + }, + + secondsToMinutes(seconds) { + return Math.abs(seconds / 60); + }, +}; diff --git a/app/assets/javascripts/logo.js b/app/assets/javascripts/logo.js index 729baa2e1a7..5b96686a331 100644 --- a/app/assets/javascripts/logo.js +++ b/app/assets/javascripts/logo.js @@ -1,7 +1,5 @@ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback */ -(function() { - window.addEventListener('beforeunload', function() { - $('.tanuki-logo').addClass('animate'); - }); -}).call(window); +window.addEventListener('beforeunload', function() { + $('.tanuki-logo').addClass('animate'); +}); diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js index e034729bd39..9b633485ff2 100644 --- a/app/assets/javascripts/member_expiration_date.js +++ b/app/assets/javascripts/member_expiration_date.js @@ -1,53 +1,51 @@ /* global Pikaday */ /* global dateFormat */ -(() => { - // Add datepickers to all `js-access-expiration-date` elements. If those elements are - // children of an element with the `clearable-input` class, and have a sibling - // `js-clear-input` element, then show that element when there is a value in the - // datepicker, and make clicking on that element clear the field. - // - window.gl = window.gl || {}; - gl.MemberExpirationDate = (selector = '.js-access-expiration-date') => { - function toggleClearInput() { - $(this).closest('.clearable-input').toggleClass('has-value', $(this).val() !== ''); - } - const inputs = $(selector); - - inputs.each((i, el) => { - const $input = $(el); - - const calendar = new Pikaday({ - field: $input.get(0), - theme: 'gitlab-theme animate-picker', - format: 'yyyy-mm-dd', - minDate: new Date(), - container: $input.parent().get(0), - onSelect(dateText) { - $input.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); - - $input.trigger('change'); - - toggleClearInput.call($input); - }, - }); - - calendar.setDate(new Date($input.val())); - $input.data('pikaday', calendar); +// Add datepickers to all `js-access-expiration-date` elements. If those elements are +// children of an element with the `clearable-input` class, and have a sibling +// `js-clear-input` element, then show that element when there is a value in the +// datepicker, and make clicking on that element clear the field. +// +window.gl = window.gl || {}; +gl.MemberExpirationDate = (selector = '.js-access-expiration-date') => { + function toggleClearInput() { + $(this).closest('.clearable-input').toggleClass('has-value', $(this).val() !== ''); + } + const inputs = $(selector); + + inputs.each((i, el) => { + const $input = $(el); + + const calendar = new Pikaday({ + field: $input.get(0), + theme: 'gitlab-theme animate-picker', + format: 'yyyy-mm-dd', + minDate: new Date(), + container: $input.parent().get(0), + onSelect(dateText) { + $input.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); + + $input.trigger('change'); + + toggleClearInput.call($input); + }, }); - inputs.next('.js-clear-input').on('click', function clicked(event) { - event.preventDefault(); + calendar.setDate(new Date($input.val())); + $input.data('pikaday', calendar); + }); - const input = $(this).closest('.clearable-input').find(selector); - const calendar = input.data('pikaday'); + inputs.next('.js-clear-input').on('click', function clicked(event) { + event.preventDefault(); - calendar.setDate(null); - input.trigger('change'); - toggleClearInput.call(input); - }); + const input = $(this).closest('.clearable-input').find(selector); + const calendar = input.data('pikaday'); + + calendar.setDate(null); + input.trigger('change'); + toggleClearInput.call(input); + }); - inputs.on('blur', toggleClearInput); + inputs.on('blur', toggleClearInput); - inputs.each(toggleClearInput); - }; -}).call(window); + inputs.each(toggleClearInput); +}; diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js index 8291b8c4a70..12a9abe281f 100644 --- a/app/assets/javascripts/members.js +++ b/app/assets/javascripts/members.js @@ -1,81 +1,79 @@ /* eslint-disable class-methods-use-this */ -(() => { - window.gl = window.gl || {}; +window.gl = window.gl || {}; - class Members { - constructor() { - this.addListeners(); - this.initGLDropdown(); - } +class Members { + constructor() { + this.addListeners(); + this.initGLDropdown(); + } - addListeners() { - $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); - $('.js-member-update-control').off('change').on('change', this.formSubmit.bind(this)); - $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess.bind(this)); - gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); - } + addListeners() { + $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); + $('.js-member-update-control').off('change').on('change', this.formSubmit.bind(this)); + $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess.bind(this)); + gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); + } - initGLDropdown() { - $('.js-member-permissions-dropdown').each((i, btn) => { - const $btn = $(btn); + initGLDropdown() { + $('.js-member-permissions-dropdown').each((i, btn) => { + const $btn = $(btn); - $btn.glDropdown({ - selectable: true, - isSelectable(selected, $el) { - return !$el.hasClass('is-active'); - }, - fieldName: $btn.data('field-name'), - id(selected, $el) { - return $el.data('id'); - }, - toggleLabel(selected, $el) { - return $el.text(); - }, - clicked: (options) => { - this.formSubmit(null, options.$el); - }, - }); + $btn.glDropdown({ + selectable: true, + isSelectable(selected, $el) { + return !$el.hasClass('is-active'); + }, + fieldName: $btn.data('field-name'), + id(selected, $el) { + return $el.data('id'); + }, + toggleLabel(selected, $el) { + return $el.text(); + }, + clicked: (options) => { + this.formSubmit(null, options.$el); + }, }); - } + }); + } - removeRow(e) { - const $target = $(e.target); + removeRow(e) { + const $target = $(e.target); - if ($target.hasClass('btn-remove')) { - $target.closest('.member') - .fadeOut(function fadeOutMemberRow() { - $(this).remove(); - }); - } + if ($target.hasClass('btn-remove')) { + $target.closest('.member') + .fadeOut(function fadeOutMemberRow() { + $(this).remove(); + }); } + } - formSubmit(e, $el = null) { - const $this = e ? $(e.currentTarget) : $el; - const { $toggle, $dateInput } = this.getMemberListItems($this); + formSubmit(e, $el = null) { + const $this = e ? $(e.currentTarget) : $el; + const { $toggle, $dateInput } = this.getMemberListItems($this); - $this.closest('form').trigger('submit.rails'); + $this.closest('form').trigger('submit.rails'); - $toggle.disable(); - $dateInput.disable(); - } + $toggle.disable(); + $dateInput.disable(); + } - formSuccess(e) { - const { $toggle, $dateInput } = this.getMemberListItems($(e.currentTarget).closest('.member')); + formSuccess(e) { + const { $toggle, $dateInput } = this.getMemberListItems($(e.currentTarget).closest('.member')); - $toggle.enable(); - $dateInput.enable(); - } + $toggle.enable(); + $dateInput.enable(); + } - getMemberListItems($el) { - const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('el-id')}`); + getMemberListItems($el) { + const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('el-id')}`); - return { - $memberListItem, - $toggle: $memberListItem.find('.dropdown-menu-toggle'), - $dateInput: $memberListItem.find('.js-access-expiration-date'), - }; - } + return { + $memberListItem, + $toggle: $memberListItem.find('.dropdown-menu-toggle'), + $dateInput: $memberListItem.find('.js-access-expiration-date'), + }; } +} - gl.Members = Members; -})(); +gl.Members = Members; diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index 0db2abe507d..5444457f651 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -6,127 +6,123 @@ import TaskList from './task_list'; import './merge_request_tabs'; import IssuablesHelper from './helpers/issuables_helper'; -(function() { - this.MergeRequest = (function() { - function MergeRequest(opts) { - // Initialize MergeRequest behavior - // - // Options: - // action - String, current controller action - // - this.opts = opts != null ? opts : {}; - this.submitNoteForm = this.submitNoteForm.bind(this); - this.$el = $('.merge-request'); - this.$('.show-all-commits').on('click', (function(_this) { - return function() { - return _this.showAllCommits(); - }; - })(this)); - - this.initTabs(); - this.initMRBtnListeners(); - this.initCommitMessageListeners(); - this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport(); - - if ($("a.btn-close").length) { - this.taskList = new TaskList({ - dataType: 'merge_request', - fieldName: 'description', - selector: '.detail-page-description', - onSuccess: (result) => { - document.querySelector('#task_status').innerText = result.task_status; - document.querySelector('#task_status_short').innerText = result.task_status_short; - } - }); - } - } - - // Local jQuery finder - MergeRequest.prototype.$ = function(selector) { - return this.$el.find(selector); - }; - - MergeRequest.prototype.initTabs = function() { - if (window.mrTabs) { - window.mrTabs.unbindEvents(); - } - window.mrTabs = new gl.MergeRequestTabs(this.opts); +function MergeRequest(opts) { + // Initialize MergeRequest behavior + // + // Options: + // action - String, current controller action + // + this.opts = opts != null ? opts : {}; + this.submitNoteForm = this.submitNoteForm.bind(this); + this.$el = $('.merge-request'); + this.$('.show-all-commits').on('click', (function(_this) { + return function() { + return _this.showAllCommits(); }; - - MergeRequest.prototype.showAllCommits = function() { - this.$('.first-commits').remove(); - return this.$('.all-commits').removeClass('hide'); - }; - - MergeRequest.prototype.initMRBtnListeners = function() { - var _this; - _this = this; - return $('a.btn-close, a.btn-reopen').on('click', function(e) { - var $this, shouldSubmit; - $this = $(this); - shouldSubmit = $this.hasClass('btn-comment'); - if (shouldSubmit && $this.data('submitted')) { - return; - } - - if (this.closeReopenReportToggle) this.closeReopenReportToggle.setDisable(); - - if (shouldSubmit) { - if ($this.hasClass('btn-comment-and-close') || $this.hasClass('btn-comment-and-reopen')) { - e.preventDefault(); - e.stopImmediatePropagation(); - - _this.submitNoteForm($this.closest('form'), $this); - } - } - }); - }; - - MergeRequest.prototype.submitNoteForm = function(form, $button) { - var noteText; - noteText = form.find("textarea.js-note-text").val(); - if (noteText.trim().length > 0) { - form.submit(); - $button.data('submitted', true); - return $button.trigger('click'); + })(this)); + + this.initTabs(); + this.initMRBtnListeners(); + this.initCommitMessageListeners(); + this.closeReopenReportToggle = IssuablesHelper.initCloseReopenReport(); + + if ($("a.btn-close").length) { + this.taskList = new TaskList({ + dataType: 'merge_request', + fieldName: 'description', + selector: '.detail-page-description', + onSuccess: (result) => { + document.querySelector('#task_status').innerText = result.task_status; + document.querySelector('#task_status_short').innerText = result.task_status_short; } - }; - - MergeRequest.prototype.initCommitMessageListeners = function() { - $(document).on('click', 'a.js-with-description-link', function(e) { - var textarea = $('textarea.js-commit-message'); - e.preventDefault(); + }); + } +} + +// Local jQuery finder +MergeRequest.prototype.$ = function(selector) { + return this.$el.find(selector); +}; + +MergeRequest.prototype.initTabs = function() { + if (window.mrTabs) { + window.mrTabs.unbindEvents(); + } + window.mrTabs = new gl.MergeRequestTabs(this.opts); +}; + +MergeRequest.prototype.showAllCommits = function() { + this.$('.first-commits').remove(); + return this.$('.all-commits').removeClass('hide'); +}; + +MergeRequest.prototype.initMRBtnListeners = function() { + var _this; + _this = this; + return $('a.btn-close, a.btn-reopen').on('click', function(e) { + var $this, shouldSubmit; + $this = $(this); + shouldSubmit = $this.hasClass('btn-comment'); + if (shouldSubmit && $this.data('submitted')) { + return; + } - textarea.val(textarea.data('messageWithDescription')); - $('.js-with-description-hint').hide(); - $('.js-without-description-hint').show(); - }); + if (this.closeReopenReportToggle) this.closeReopenReportToggle.setDisable(); - $(document).on('click', 'a.js-without-description-link', function(e) { - var textarea = $('textarea.js-commit-message'); + if (shouldSubmit) { + if ($this.hasClass('btn-comment-and-close') || $this.hasClass('btn-comment-and-reopen')) { e.preventDefault(); + e.stopImmediatePropagation(); - textarea.val(textarea.data('messageWithoutDescription')); - $('.js-with-description-hint').show(); - $('.js-without-description-hint').hide(); - }); - }; - - MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, newStatusText) { - $('.detail-page-header .status-box') - .removeClass(classToRemove) - .addClass(classToAdd) - .find('span') - .text(newStatusText); - }; - - MergeRequest.prototype.decreaseCounter = function(by = 1) { - const $el = $('.nav-links .js-merge-counter'); - const count = Math.max((parseInt($el.text().replace(/[^\d]/, ''), 10) - by), 0); - - $el.text(gl.text.addDelimiter(count)); - }; - - return MergeRequest; - })(); -}).call(window); + _this.submitNoteForm($this.closest('form'), $this); + } + } + }); +}; + +MergeRequest.prototype.submitNoteForm = function(form, $button) { + var noteText; + noteText = form.find("textarea.js-note-text").val(); + if (noteText.trim().length > 0) { + form.submit(); + $button.data('submitted', true); + return $button.trigger('click'); + } +}; + +MergeRequest.prototype.initCommitMessageListeners = function() { + $(document).on('click', 'a.js-with-description-link', function(e) { + var textarea = $('textarea.js-commit-message'); + e.preventDefault(); + + textarea.val(textarea.data('messageWithDescription')); + $('.js-with-description-hint').hide(); + $('.js-without-description-hint').show(); + }); + + $(document).on('click', 'a.js-without-description-link', function(e) { + var textarea = $('textarea.js-commit-message'); + e.preventDefault(); + + textarea.val(textarea.data('messageWithoutDescription')); + $('.js-with-description-hint').show(); + $('.js-without-description-hint').hide(); + }); +}; + +MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, newStatusText) { + $('.detail-page-header .status-box') + .removeClass(classToRemove) + .addClass(classToAdd) + .find('span') + .text(newStatusText); +}; + +MergeRequest.prototype.decreaseCounter = function(by = 1) { + const $el = $('.nav-links .js-merge-counter'); + const count = Math.max((parseInt($el.text().replace(/[^\d]/, ''), 10) - by), 0); + + $el.text(gl.text.addDelimiter(count)); +}; + +window.MergeRequest = MergeRequest; diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 7840f05a8ae..d73ebdb1a22 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -54,370 +54,368 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion'; // /* eslint-enable max-len */ -(() => { - // Store the `location` object, allowing for easier stubbing in tests - let location = window.location; +// Store the `location` object, allowing for easier stubbing in tests +let location = window.location; - class MergeRequestTabs { +class MergeRequestTabs { - constructor({ action, setUrl, stubLocation } = {}) { - this.diffsLoaded = false; - this.pipelinesLoaded = false; - this.commitsLoaded = false; - this.fixedLayoutPref = null; + constructor({ action, setUrl, stubLocation } = {}) { + this.diffsLoaded = false; + this.pipelinesLoaded = false; + this.commitsLoaded = false; + this.fixedLayoutPref = null; - this.setUrl = setUrl !== undefined ? setUrl : true; - this.setCurrentAction = this.setCurrentAction.bind(this); - this.tabShown = this.tabShown.bind(this); - this.showTab = this.showTab.bind(this); + this.setUrl = setUrl !== undefined ? setUrl : true; + this.setCurrentAction = this.setCurrentAction.bind(this); + this.tabShown = this.tabShown.bind(this); + this.showTab = this.showTab.bind(this); - if (stubLocation) { - location = stubLocation; - } - - this.bindEvents(); - this.activateTab(action); - this.initAffix(); + if (stubLocation) { + location = stubLocation; } - bindEvents() { - $(document) - .on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) - .on('click', '.js-show-tab', this.showTab); + this.bindEvents(); + this.activateTab(action); + this.initAffix(); + } - $('.merge-request-tabs a[data-toggle="tab"]') - .on('click', this.clickTab); - } + bindEvents() { + $(document) + .on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) + .on('click', '.js-show-tab', this.showTab); + + $('.merge-request-tabs a[data-toggle="tab"]') + .on('click', this.clickTab); + } - // Used in tests - unbindEvents() { - $(document) - .off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) - .off('click', '.js-show-tab', this.showTab); + // Used in tests + unbindEvents() { + $(document) + .off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown) + .off('click', '.js-show-tab', this.showTab); - $('.merge-request-tabs a[data-toggle="tab"]') - .off('click', this.clickTab); - } + $('.merge-request-tabs a[data-toggle="tab"]') + .off('click', this.clickTab); + } - destroyPipelinesView() { - if (this.commitPipelinesTable) { - this.commitPipelinesTable.$destroy(); - this.commitPipelinesTable = null; + destroyPipelinesView() { + if (this.commitPipelinesTable) { + this.commitPipelinesTable.$destroy(); + this.commitPipelinesTable = null; - document.querySelector('#commit-pipeline-table-view').innerHTML = ''; - } + document.querySelector('#commit-pipeline-table-view').innerHTML = ''; } + } - showTab(e) { + showTab(e) { + e.preventDefault(); + this.activateTab($(e.target).data('action')); + } + + clickTab(e) { + if (e.currentTarget && gl.utils.isMetaClick(e)) { + const targetLink = e.currentTarget.getAttribute('href'); + e.stopImmediatePropagation(); e.preventDefault(); - this.activateTab($(e.target).data('action')); + window.open(targetLink, '_blank'); } + } - clickTab(e) { - if (e.currentTarget && gl.utils.isMetaClick(e)) { - const targetLink = e.currentTarget.getAttribute('href'); - e.stopImmediatePropagation(); - e.preventDefault(); - window.open(targetLink, '_blank'); + tabShown(e) { + const $target = $(e.target); + const action = $target.data('action'); + + if (action === 'commits') { + this.loadCommits($target.attr('href')); + this.expandView(); + this.resetViewContainer(); + this.destroyPipelinesView(); + } else if (this.isDiffAction(action)) { + this.loadDiff($target.attr('href')); + if (Breakpoints.get().getBreakpointSize() !== 'lg') { + this.shrinkView(); } - } - - tabShown(e) { - const $target = $(e.target); - const action = $target.data('action'); - - if (action === 'commits') { - this.loadCommits($target.attr('href')); - this.expandView(); - this.resetViewContainer(); - this.destroyPipelinesView(); - } else if (this.isDiffAction(action)) { - this.loadDiff($target.attr('href')); - if (Breakpoints.get().getBreakpointSize() !== 'lg') { - this.shrinkView(); - } - if (this.diffViewType() === 'parallel') { - this.expandViewContainer(); - } - this.destroyPipelinesView(); - } else if (action === 'pipelines') { - this.resetViewContainer(); - this.mountPipelinesView(); - } else { - if (Breakpoints.get().getBreakpointSize() !== 'xs') { - this.expandView(); - } - this.resetViewContainer(); - this.destroyPipelinesView(); + if (this.diffViewType() === 'parallel') { + this.expandViewContainer(); } - if (this.setUrl) { - this.setCurrentAction(action); + this.destroyPipelinesView(); + } else if (action === 'pipelines') { + this.resetViewContainer(); + this.mountPipelinesView(); + } else { + if (Breakpoints.get().getBreakpointSize() !== 'xs') { + this.expandView(); } + this.resetViewContainer(); + this.destroyPipelinesView(); } + if (this.setUrl) { + this.setCurrentAction(action); + } + } - scrollToElement(container) { - if (location.hash) { - const offset = 0 - ( - $('.navbar-gitlab').outerHeight() + - $('.js-tabs-affix').outerHeight() - ); - const $el = $(`${container} ${location.hash}:not(.match)`); - if ($el.length) { - $.scrollTo($el[0], { offset }); - } + scrollToElement(container) { + if (location.hash) { + const offset = 0 - ( + $('.navbar-gitlab').outerHeight() + + $('.js-tabs-affix').outerHeight() + ); + const $el = $(`${container} ${location.hash}:not(.match)`); + if ($el.length) { + $.scrollTo($el[0], { offset }); } } + } + + // Activate a tab based on the current action + activateTab(action) { + // important note: the .tab('show') method triggers 'shown.bs.tab' event itself + $(`.merge-request-tabs a[data-action='${action}']`).tab('show'); + } - // Activate a tab based on the current action - activateTab(action) { - // important note: the .tab('show') method triggers 'shown.bs.tab' event itself - $(`.merge-request-tabs a[data-action='${action}']`).tab('show'); + // Replaces the current Merge Request-specific action in the URL with a new one + // + // If the action is "notes", the URL is reset to the standard + // `MergeRequests#show` route. + // + // Examples: + // + // location.pathname # => "/namespace/project/merge_requests/1" + // setCurrentAction('diffs') + // location.pathname # => "/namespace/project/merge_requests/1/diffs" + // + // location.pathname # => "/namespace/project/merge_requests/1/diffs" + // setCurrentAction('show') + // location.pathname # => "/namespace/project/merge_requests/1" + // + // location.pathname # => "/namespace/project/merge_requests/1/diffs" + // setCurrentAction('commits') + // location.pathname # => "/namespace/project/merge_requests/1/commits" + // + // Returns the new URL String + setCurrentAction(action) { + this.currentAction = action; + + // Remove a trailing '/commits' '/diffs' '/pipelines' + let newState = location.pathname.replace(/\/(commits|diffs|pipelines)(\.html)?\/?$/, ''); + + // Append the new action if we're on a tab other than 'notes' + if (this.currentAction !== 'show' && this.currentAction !== 'new') { + newState += `/${this.currentAction}`; } - // Replaces the current Merge Request-specific action in the URL with a new one - // - // If the action is "notes", the URL is reset to the standard - // `MergeRequests#show` route. - // - // Examples: - // - // location.pathname # => "/namespace/project/merge_requests/1" - // setCurrentAction('diffs') - // location.pathname # => "/namespace/project/merge_requests/1/diffs" - // - // location.pathname # => "/namespace/project/merge_requests/1/diffs" - // setCurrentAction('show') - // location.pathname # => "/namespace/project/merge_requests/1" - // - // location.pathname # => "/namespace/project/merge_requests/1/diffs" - // setCurrentAction('commits') - // location.pathname # => "/namespace/project/merge_requests/1/commits" - // - // Returns the new URL String - setCurrentAction(action) { - this.currentAction = action; + // Ensure parameters and hash come along for the ride + newState += location.search + location.hash; - // Remove a trailing '/commits' '/diffs' '/pipelines' - let newState = location.pathname.replace(/\/(commits|diffs|pipelines)(\.html)?\/?$/, ''); + // TODO: Consider refactoring in light of turbolinks removal. - // Append the new action if we're on a tab other than 'notes' - if (this.currentAction !== 'show' && this.currentAction !== 'new') { - newState += `/${this.currentAction}`; - } + // Replace the current history state with the new one without breaking + // Turbolinks' history. + // + // See https://github.com/rails/turbolinks/issues/363 + window.history.replaceState({ + url: newState, + }, document.title, newState); - // Ensure parameters and hash come along for the ride - newState += location.search + location.hash; + return newState; + } - // TODO: Consider refactoring in light of turbolinks removal. + loadCommits(source) { + if (this.commitsLoaded) { + return; + } + this.ajaxGet({ + url: `${source}.json`, + success: (data) => { + document.querySelector('div#commits').innerHTML = data.html; + gl.utils.localTimeAgo($('.js-timeago', 'div#commits')); + this.commitsLoaded = true; + this.scrollToElement('#commits'); + }, + }); + } - // Replace the current history state with the new one without breaking - // Turbolinks' history. - // - // See https://github.com/rails/turbolinks/issues/363 - window.history.replaceState({ - url: newState, - }, document.title, newState); + mountPipelinesView() { + const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); + const CommitPipelinesTable = gl.CommitPipelinesTable; + this.commitPipelinesTable = new CommitPipelinesTable({ + propsData: { + endpoint: pipelineTableViewEl.dataset.endpoint, + helpPagePath: pipelineTableViewEl.dataset.helpPagePath, + }, + }).$mount(); + + // $mount(el) replaces the el with the new rendered component. We need it in order to mount + // it everytime this tab is clicked - https://vuejs.org/v2/api/#vm-mount + pipelineTableViewEl.appendChild(this.commitPipelinesTable.$el); + } - return newState; + loadDiff(source) { + if (this.diffsLoaded) { + return; } - loadCommits(source) { - if (this.commitsLoaded) { - return; - } - this.ajaxGet({ - url: `${source}.json`, - success: (data) => { - document.querySelector('div#commits').innerHTML = data.html; - gl.utils.localTimeAgo($('.js-timeago', 'div#commits')); - this.commitsLoaded = true; - this.scrollToElement('#commits'); - }, - }); - } + // We extract pathname for the current Changes tab anchor href + // some pages like MergeRequestsController#new has query parameters on that anchor + const urlPathname = gl.utils.parseUrlPathname(source); - mountPipelinesView() { - const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); - const CommitPipelinesTable = gl.CommitPipelinesTable; - this.commitPipelinesTable = new CommitPipelinesTable({ - propsData: { - endpoint: pipelineTableViewEl.dataset.endpoint, - helpPagePath: pipelineTableViewEl.dataset.helpPagePath, - }, - }).$mount(); + this.ajaxGet({ + url: `${urlPathname}.json${location.search}`, + success: (data) => { + const $container = $('#diffs'); + $container.html(data.html); - // $mount(el) replaces the el with the new rendered component. We need it in order to mount - // it everytime this tab is clicked - https://vuejs.org/v2/api/#vm-mount - pipelineTableViewEl.appendChild(this.commitPipelinesTable.$el); - } + if (typeof gl.diffNotesCompileComponents !== 'undefined') { + gl.diffNotesCompileComponents(); + } - loadDiff(source) { - if (this.diffsLoaded) { - return; - } + gl.utils.localTimeAgo($('.js-timeago', 'div#diffs')); + $('#diffs .js-syntax-highlight').syntaxHighlight(); - // We extract pathname for the current Changes tab anchor href - // some pages like MergeRequestsController#new has query parameters on that anchor - const urlPathname = gl.utils.parseUrlPathname(source); - - this.ajaxGet({ - url: `${urlPathname}.json${location.search}`, - success: (data) => { - const $container = $('#diffs'); - $container.html(data.html); - - if (typeof gl.diffNotesCompileComponents !== 'undefined') { - gl.diffNotesCompileComponents(); - } - - gl.utils.localTimeAgo($('.js-timeago', 'div#diffs')); - $('#diffs .js-syntax-highlight').syntaxHighlight(); - - if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) { - this.expandViewContainer(); - } - this.diffsLoaded = true; - - new gl.Diff(); - this.scrollToElement('#diffs'); - - $('.diff-file').each((i, el) => { - new BlobForkSuggestion({ - openButtons: $(el).find('.js-edit-blob-link-fork-toggler'), - forkButtons: $(el).find('.js-fork-suggestion-button'), - cancelButtons: $(el).find('.js-cancel-fork-suggestion-button'), - suggestionSections: $(el).find('.js-file-fork-suggestion-section'), - actionTextPieces: $(el).find('.js-file-fork-suggestion-section-action'), - }) - .init(); + if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) { + this.expandViewContainer(); + } + this.diffsLoaded = true; + + new gl.Diff(); + this.scrollToElement('#diffs'); + + $('.diff-file').each((i, el) => { + new BlobForkSuggestion({ + openButtons: $(el).find('.js-edit-blob-link-fork-toggler'), + forkButtons: $(el).find('.js-fork-suggestion-button'), + cancelButtons: $(el).find('.js-cancel-fork-suggestion-button'), + suggestionSections: $(el).find('.js-file-fork-suggestion-section'), + actionTextPieces: $(el).find('.js-file-fork-suggestion-section-action'), + }) + .init(); + }); + + // Scroll any linked note into view + // Similar to `toggler_behavior` in the discussion tab + const hash = window.gl.utils.getLocationHash(); + const anchor = hash && $container.find(`.note[id="${hash}"]`); + if (anchor && anchor.length > 0) { + const notesContent = anchor.closest('.notes_content'); + const lineType = notesContent.hasClass('new') ? 'new' : 'old'; + notes.toggleDiffNote({ + target: anchor, + lineType, + forceShow: true, }); + anchor[0].scrollIntoView(); + window.gl.utils.handleLocationHash(); + // We have multiple elements on the page with `#note_xxx` + // (discussion and diff tabs) and `:target` only applies to the first + anchor.addClass('target'); + } + }, + }); + } - // Scroll any linked note into view - // Similar to `toggler_behavior` in the discussion tab - const hash = window.gl.utils.getLocationHash(); - const anchor = hash && $container.find(`.note[id="${hash}"]`); - if (anchor && anchor.length > 0) { - const notesContent = anchor.closest('.notes_content'); - const lineType = notesContent.hasClass('new') ? 'new' : 'old'; - notes.toggleDiffNote({ - target: anchor, - lineType, - forceShow: true, - }); - anchor[0].scrollIntoView(); - window.gl.utils.handleLocationHash(); - // We have multiple elements on the page with `#note_xxx` - // (discussion and diff tabs) and `:target` only applies to the first - anchor.addClass('target'); - } - }, - }); - } + // Show or hide the loading spinner + // + // status - Boolean, true to show, false to hide + toggleLoading(status) { + $('.mr-loading-status .loading').toggle(status); + } - // Show or hide the loading spinner - // - // status - Boolean, true to show, false to hide - toggleLoading(status) { - $('.mr-loading-status .loading').toggle(status); - } + ajaxGet(options) { + const defaults = { + beforeSend: () => this.toggleLoading(true), + error: () => new Flash('An error occurred while fetching this tab.', 'alert'), + complete: () => this.toggleLoading(false), + dataType: 'json', + type: 'GET', + }; + $.ajax($.extend({}, defaults, options)); + } - ajaxGet(options) { - const defaults = { - beforeSend: () => this.toggleLoading(true), - error: () => new Flash('An error occurred while fetching this tab.', 'alert'), - complete: () => this.toggleLoading(false), - dataType: 'json', - type: 'GET', - }; - $.ajax($.extend({}, defaults, options)); - } + diffViewType() { + return $('.inline-parallel-buttons a.active').data('view-type'); + } - diffViewType() { - return $('.inline-parallel-buttons a.active').data('view-type'); - } + isDiffAction(action) { + return action === 'diffs' || action === 'new/diffs'; + } - isDiffAction(action) { - return action === 'diffs' || action === 'new/diffs'; + expandViewContainer() { + const $wrapper = $('.content-wrapper .container-fluid'); + if (this.fixedLayoutPref === null) { + this.fixedLayoutPref = $wrapper.hasClass('container-limited'); } + $wrapper.removeClass('container-limited'); + } - expandViewContainer() { - const $wrapper = $('.content-wrapper .container-fluid'); - if (this.fixedLayoutPref === null) { - this.fixedLayoutPref = $wrapper.hasClass('container-limited'); - } - $wrapper.removeClass('container-limited'); + resetViewContainer() { + if (this.fixedLayoutPref !== null) { + $('.content-wrapper .container-fluid') + .toggleClass('container-limited', this.fixedLayoutPref); } + } - resetViewContainer() { - if (this.fixedLayoutPref !== null) { - $('.content-wrapper .container-fluid') - .toggleClass('container-limited', this.fixedLayoutPref); - } - } + shrinkView() { + const $gutterIcon = $('.js-sidebar-toggle i:visible'); - shrinkView() { - const $gutterIcon = $('.js-sidebar-toggle i:visible'); + // Wait until listeners are set + setTimeout(() => { + // Only when sidebar is expanded + if ($gutterIcon.is('.fa-angle-double-right')) { + $gutterIcon.closest('a').trigger('click', [true]); + } + }, 0); + } - // Wait until listeners are set - setTimeout(() => { - // Only when sidebar is expanded - if ($gutterIcon.is('.fa-angle-double-right')) { - $gutterIcon.closest('a').trigger('click', [true]); - } - }, 0); + // Expand the issuable sidebar unless the user explicitly collapsed it + expandView() { + if (Cookies.get('collapsed_gutter') === 'true') { + return; } + const $gutterIcon = $('.js-sidebar-toggle i:visible'); - // Expand the issuable sidebar unless the user explicitly collapsed it - expandView() { - if (Cookies.get('collapsed_gutter') === 'true') { - return; + // Wait until listeners are set + setTimeout(() => { + // Only when sidebar is collapsed + if ($gutterIcon.is('.fa-angle-double-left')) { + $gutterIcon.closest('a').trigger('click', [true]); } - const $gutterIcon = $('.js-sidebar-toggle i:visible'); + }, 0); + } - // Wait until listeners are set - setTimeout(() => { - // Only when sidebar is collapsed - if ($gutterIcon.is('.fa-angle-double-left')) { - $gutterIcon.closest('a').trigger('click', [true]); - } - }, 0); - } + initAffix() { + const $tabs = $('.js-tabs-affix'); + const $fixedNav = $('.navbar-gitlab'); + + // Screen space on small screens is usually very sparse + // So we dont affix the tabs on these + if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return; + + /** + If the browser does not support position sticky, it returns the position as static. + If the browser does support sticky, then we allow the browser to handle it, if not + then we default back to Bootstraps affix + **/ + if ($tabs.css('position') !== 'static') return; + + const $diffTabs = $('#diff-notes-app'); + + $tabs.off('affix.bs.affix affix-top.bs.affix') + .affix({ + offset: { + top: () => ( + $diffTabs.offset().top - $tabs.height() - $fixedNav.height() + ), + }, + }) + .on('affix.bs.affix', () => $diffTabs.css({ marginTop: $tabs.height() })) + .on('affix-top.bs.affix', () => $diffTabs.css({ marginTop: '' })); - initAffix() { - const $tabs = $('.js-tabs-affix'); - const $fixedNav = $('.navbar-gitlab'); - - // Screen space on small screens is usually very sparse - // So we dont affix the tabs on these - if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return; - - /** - If the browser does not support position sticky, it returns the position as static. - If the browser does support sticky, then we allow the browser to handle it, if not - then we default back to Bootstraps affix - **/ - if ($tabs.css('position') !== 'static') return; - - const $diffTabs = $('#diff-notes-app'); - - $tabs.off('affix.bs.affix affix-top.bs.affix') - .affix({ - offset: { - top: () => ( - $diffTabs.offset().top - $tabs.height() - $fixedNav.height() - ), - }, - }) - .on('affix.bs.affix', () => $diffTabs.css({ marginTop: $tabs.height() })) - .on('affix-top.bs.affix', () => $diffTabs.css({ marginTop: '' })); - - // Fix bug when reloading the page already scrolling - if ($tabs.hasClass('affix')) { - $tabs.trigger('affix.bs.affix'); - } + // Fix bug when reloading the page already scrolling + if ($tabs.hasClass('affix')) { + $tabs.trigger('affix.bs.affix'); } } +} - window.gl = window.gl || {}; - window.gl.MergeRequestTabs = MergeRequestTabs; -})(); +window.gl = window.gl || {}; +window.gl.MergeRequestTabs = MergeRequestTabs; diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index 3e07ec4d0aa..6f503b7293a 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -2,52 +2,48 @@ /* global Flash */ /* global Sortable */ -(function() { - this.Milestone = (function() { - function Milestone() { - this.bindTabsSwitching(); - - // Load merge request tab if it is active - // merge request tab is active based on different conditions in the backend - this.loadTab($('.js-milestone-tabs .active a')); - - this.loadInitialTab(); - } - - Milestone.prototype.bindTabsSwitching = function() { - return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => { - const $target = $(e.target); - - location.hash = $target.attr('href'); - this.loadTab($target); - }); - }; - - Milestone.prototype.loadInitialTab = function() { - const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`); - - if ($target.length) { - $target.tab('show'); - } - }; - - Milestone.prototype.loadTab = function($target) { - const endpoint = $target.data('endpoint'); - const tabElId = $target.attr('href'); - - if (endpoint && !$target.hasClass('is-loaded')) { - $.ajax({ - url: endpoint, - dataType: 'JSON', - }) - .fail(() => new Flash('Error loading milestone tab')) - .done((data) => { - $(tabElId).html(data.html); - $target.addClass('is-loaded'); - }); - } - }; - - return Milestone; - })(); -}).call(window); +function Milestone() { + this.bindTabsSwitching(); + + // Load merge request tab if it is active + // merge request tab is active based on different conditions in the backend + this.loadTab($('.js-milestone-tabs .active a')); + + this.loadInitialTab(); +} + +Milestone.prototype.bindTabsSwitching = function() { + return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => { + const $target = $(e.target); + + location.hash = $target.attr('href'); + this.loadTab($target); + }); +}; + +Milestone.prototype.loadInitialTab = function() { + const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`); + + if ($target.length) { + $target.tab('show'); + } +}; + +Milestone.prototype.loadTab = function($target) { + const endpoint = $target.data('endpoint'); + const tabElId = $target.attr('href'); + + if (endpoint && !$target.hasClass('is-loaded')) { + $.ajax({ + url: endpoint, + dataType: 'JSON', + }) + .fail(() => new Flash('Error loading milestone tab')) + .done((data) => { + $(tabElId).html(data.html); + $target.addClass('is-loaded'); + }); + } +}; + +window.Milestone = Milestone; diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 9d481d7c003..bde4923fd2e 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -2,224 +2,220 @@ /* global Issuable */ /* global ListMilestone */ -(function() { - this.MilestoneSelect = (function() { - function MilestoneSelect(currentProject, els) { - var _this, $els; - if (currentProject != null) { - _this = this; - this.currentProject = JSON.parse(currentProject); - } +function MilestoneSelect(currentProject, els) { + var _this, $els; + if (currentProject != null) { + _this = this; + this.currentProject = JSON.parse(currentProject); + } - $els = $(els); + $els = $(els); - if (!els) { - $els = $('.js-milestone-select'); - } + if (!els) { + $els = $('.js-milestone-select'); + } - $els.each(function(i, dropdown) { - var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, defaultNo, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, selectedMilestoneDefault, showAny, showNo, showUpcoming, showStarted, useId, showMenuAbove; - $dropdown = $(dropdown); - projectId = $dropdown.data('project-id'); - milestonesUrl = $dropdown.data('milestones'); - issueUpdateURL = $dropdown.data('issueUpdate'); - showNo = $dropdown.data('show-no'); - showAny = $dropdown.data('show-any'); - showMenuAbove = $dropdown.data('showMenuAbove'); - showUpcoming = $dropdown.data('show-upcoming'); - showStarted = $dropdown.data('show-started'); - useId = $dropdown.data('use-id'); - defaultLabel = $dropdown.data('default-label'); - defaultNo = $dropdown.data('default-no'); - issuableId = $dropdown.data('issuable-id'); - abilityName = $dropdown.data('ability-name'); - $selectbox = $dropdown.closest('.selectbox'); - $block = $selectbox.closest('.block'); - $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); - $value = $block.find('.value'); - $loading = $block.find('.block-loading').fadeOut(); - selectedMilestoneDefault = (showAny ? '' : null); - selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault); - selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault; - if (issueUpdateURL) { - milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); - milestoneLinkNoneTemplate = '<span class="no-value">None</span>'; - collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left"> <%- title %> </span>'); - } - return $dropdown.glDropdown({ - showMenuAbove: showMenuAbove, - data: function(term, callback) { - return $.ajax({ - url: milestonesUrl - }).done(function(data) { - var extraOptions = []; - if (showAny) { - extraOptions.push({ - id: 0, - name: '', - title: 'Any Milestone' - }); - } - if (showNo) { - extraOptions.push({ - id: -1, - name: 'No Milestone', - title: 'No Milestone' - }); - } - if (showUpcoming) { - extraOptions.push({ - id: -2, - name: '#upcoming', - title: 'Upcoming' - }); - } - if (showStarted) { - extraOptions.push({ - id: -3, - name: '#started', - title: 'Started' - }); - } - if (extraOptions.length) { - extraOptions.push('divider'); - } - - callback(extraOptions.concat(data)); - if (showMenuAbove) { - $dropdown.data('glDropdown').positionMenuAbove(); - } - $(`[data-milestone-id="${selectedMilestone}"] > a`).addClass('is-active'); + $els.each(function(i, dropdown) { + var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, defaultNo, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, selectedMilestoneDefault, showAny, showNo, showUpcoming, showStarted, useId, showMenuAbove; + $dropdown = $(dropdown); + projectId = $dropdown.data('project-id'); + milestonesUrl = $dropdown.data('milestones'); + issueUpdateURL = $dropdown.data('issueUpdate'); + showNo = $dropdown.data('show-no'); + showAny = $dropdown.data('show-any'); + showMenuAbove = $dropdown.data('showMenuAbove'); + showUpcoming = $dropdown.data('show-upcoming'); + showStarted = $dropdown.data('show-started'); + useId = $dropdown.data('use-id'); + defaultLabel = $dropdown.data('default-label'); + defaultNo = $dropdown.data('default-no'); + issuableId = $dropdown.data('issuable-id'); + abilityName = $dropdown.data('ability-name'); + $selectbox = $dropdown.closest('.selectbox'); + $block = $selectbox.closest('.block'); + $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); + $value = $block.find('.value'); + $loading = $block.find('.block-loading').fadeOut(); + selectedMilestoneDefault = (showAny ? '' : null); + selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault); + selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault; + if (issueUpdateURL) { + milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); + milestoneLinkNoneTemplate = '<span class="no-value">None</span>'; + collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left"> <%- title %> </span>'); + } + return $dropdown.glDropdown({ + showMenuAbove: showMenuAbove, + data: function(term, callback) { + return $.ajax({ + url: milestonesUrl + }).done(function(data) { + var extraOptions = []; + if (showAny) { + extraOptions.push({ + id: 0, + name: '', + title: 'Any Milestone' }); - }, - renderRow: function(milestone) { - return ` - <li data-milestone-id="${milestone.name}"> - <a href='#' class='dropdown-menu-milestone-link'> - ${_.escape(milestone.title)} - </a> - </li> - `; - }, - filterable: true, - search: { - fields: ['title'] - }, - selectable: true, - toggleLabel: function(selected, el, e) { - if (selected && 'id' in selected && $(el).hasClass('is-active')) { - return selected.title; - } else { - return defaultLabel; - } - }, - defaultLabel: defaultLabel, - fieldName: $dropdown.data('field-name'), - text: function(milestone) { - return _.escape(milestone.title); - }, - id: function(milestone) { - if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) { - return milestone.name; - } else { - return milestone.id; - } - }, - isSelected: function(milestone) { - return milestone.name === selectedMilestone; - }, - hidden: function() { - $selectbox.hide(); - // display:block overrides the hide-collapse rule - return $value.css('display', ''); - }, - opened: function(e) { - const $el = $(e.currentTarget); - if ($dropdown.hasClass('js-issue-board-sidebar')) { - selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault; - } - $('a.is-active', $el).removeClass('is-active'); - $(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active'); - }, - vue: $dropdown.hasClass('js-issue-board-sidebar'), - clicked: function(options) { - const { $el, e } = options; - let selected = options.selectedObj; - var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore; - page = $('body').data('page'); - isIssueIndex = page === 'projects:issues:index'; - isMRIndex = (page === page && page === 'projects:merge_requests:index'); - isSelecting = (selected.name !== selectedMilestone); - selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault; - if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { - e.preventDefault(); - return; - } + } + if (showNo) { + extraOptions.push({ + id: -1, + name: 'No Milestone', + title: 'No Milestone' + }); + } + if (showUpcoming) { + extraOptions.push({ + id: -2, + name: '#upcoming', + title: 'Upcoming' + }); + } + if (showStarted) { + extraOptions.push({ + id: -3, + name: '#started', + title: 'Started' + }); + } + if (extraOptions.length) { + extraOptions.push('divider'); + } - if ($dropdown.closest('.add-issues-modal').length) { - boardsStore = gl.issueBoards.ModalStore.store.filter; - } + callback(extraOptions.concat(data)); + if (showMenuAbove) { + $dropdown.data('glDropdown').positionMenuAbove(); + } + $(`[data-milestone-id="${selectedMilestone}"] > a`).addClass('is-active'); + }); + }, + renderRow: function(milestone) { + return ` + <li data-milestone-id="${milestone.name}"> + <a href='#' class='dropdown-menu-milestone-link'> + ${_.escape(milestone.title)} + </a> + </li> + `; + }, + filterable: true, + search: { + fields: ['title'] + }, + selectable: true, + toggleLabel: function(selected, el, e) { + if (selected && 'id' in selected && $(el).hasClass('is-active')) { + return selected.title; + } else { + return defaultLabel; + } + }, + defaultLabel: defaultLabel, + fieldName: $dropdown.data('field-name'), + text: function(milestone) { + return _.escape(milestone.title); + }, + id: function(milestone) { + if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) { + return milestone.name; + } else { + return milestone.id; + } + }, + isSelected: function(milestone) { + return milestone.name === selectedMilestone; + }, + hidden: function() { + $selectbox.hide(); + // display:block overrides the hide-collapse rule + return $value.css('display', ''); + }, + opened: function(e) { + const $el = $(e.currentTarget); + if ($dropdown.hasClass('js-issue-board-sidebar')) { + selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault; + } + $('a.is-active', $el).removeClass('is-active'); + $(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active'); + }, + vue: $dropdown.hasClass('js-issue-board-sidebar'), + clicked: function(options) { + const { $el, e } = options; + let selected = options.selectedObj; + var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore; + page = $('body').data('page'); + isIssueIndex = page === 'projects:issues:index'; + isMRIndex = (page === page && page === 'projects:merge_requests:index'); + isSelecting = (selected.name !== selectedMilestone); + selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault; + if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { + e.preventDefault(); + return; + } + + if ($dropdown.closest('.add-issues-modal').length) { + boardsStore = gl.issueBoards.ModalStore.store.filter; + } - if (boardsStore) { - boardsStore[$dropdown.data('field-name')] = selected.name; - e.preventDefault(); - } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { - return Issuable.filterResults($dropdown.closest('form')); - } else if ($dropdown.hasClass('js-filter-submit')) { - return $dropdown.closest('form').submit(); - } else if ($dropdown.hasClass('js-issue-board-sidebar')) { - if (selected.id !== -1 && isSelecting) { - gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({ - id: selected.id, - title: selected.name - })); - } else { - gl.issueBoards.boardStoreIssueDelete('milestone'); - } + if (boardsStore) { + boardsStore[$dropdown.data('field-name')] = selected.name; + e.preventDefault(); + } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { + return Issuable.filterResults($dropdown.closest('form')); + } else if ($dropdown.hasClass('js-filter-submit')) { + return $dropdown.closest('form').submit(); + } else if ($dropdown.hasClass('js-issue-board-sidebar')) { + if (selected.id !== -1 && isSelecting) { + gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({ + id: selected.id, + title: selected.name + })); + } else { + gl.issueBoards.boardStoreIssueDelete('milestone'); + } - $dropdown.trigger('loading.gl.dropdown'); - $loading.removeClass('hidden').fadeIn(); + $dropdown.trigger('loading.gl.dropdown'); + $loading.removeClass('hidden').fadeIn(); - gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) - .then(function () { - $dropdown.trigger('loaded.gl.dropdown'); - $loading.fadeOut(); - }) - .catch(() => { - $loading.fadeOut(); - }); + gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) + .then(function () { + $dropdown.trigger('loaded.gl.dropdown'); + $loading.fadeOut(); + }) + .catch(() => { + $loading.fadeOut(); + }); + } else { + selected = $selectbox.find('input[type="hidden"]').val(); + data = {}; + data[abilityName] = {}; + data[abilityName].milestone_id = selected != null ? selected : null; + $loading.removeClass('hidden').fadeIn(); + $dropdown.trigger('loading.gl.dropdown'); + return $.ajax({ + type: 'PUT', + url: issueUpdateURL, + data: data + }).done(function(data) { + $dropdown.trigger('loaded.gl.dropdown'); + $loading.fadeOut(); + $selectbox.hide(); + $value.css('display', ''); + if (data.milestone != null) { + data.milestone.full_path = _this.currentProject.full_path; + data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date); + $value.html(milestoneLinkTemplate(data.milestone)); + return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); } else { - selected = $selectbox.find('input[type="hidden"]').val(); - data = {}; - data[abilityName] = {}; - data[abilityName].milestone_id = selected != null ? selected : null; - $loading.removeClass('hidden').fadeIn(); - $dropdown.trigger('loading.gl.dropdown'); - return $.ajax({ - type: 'PUT', - url: issueUpdateURL, - data: data - }).done(function(data) { - $dropdown.trigger('loaded.gl.dropdown'); - $loading.fadeOut(); - $selectbox.hide(); - $value.css('display', ''); - if (data.milestone != null) { - data.milestone.full_path = _this.currentProject.full_path; - data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date); - $value.html(milestoneLinkTemplate(data.milestone)); - return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); - } else { - $value.html(milestoneLinkNoneTemplate); - return $sidebarCollapsedValue.find('span').text('No'); - } - }); + $value.html(milestoneLinkNoneTemplate); + return $sidebarCollapsedValue.find('span').text('No'); } - } - }); - }); - } + }); + } + } + }); + }); +} - return MilestoneSelect; - })(); -}).call(window); +window.MilestoneSelect = MilestoneSelect; diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js index 5da2db063a4..6ecd54e67f6 100644 --- a/app/assets/javascripts/namespace_select.js +++ b/app/assets/javascripts/namespace_select.js @@ -1,85 +1,78 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, vars-on-top, one-var-declaration-per-line, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, prefer-arrow-callback, no-param-reassign, no-cond-assign, max-len */ import Api from './api'; -(function() { - window.NamespaceSelect = (function() { - function NamespaceSelect(opts) { - this.onSelectItem = this.onSelectItem.bind(this); - var fieldName, showAny; - this.dropdown = opts.dropdown; - showAny = true; - fieldName = 'namespace_id'; - if (this.dropdown.attr('data-field-name')) { - fieldName = this.dropdown.data('fieldName'); +function NamespaceSelect(opts) { + this.onSelectItem = this.onSelectItem.bind(this); + var fieldName, showAny; + this.dropdown = opts.dropdown; + showAny = true; + fieldName = 'namespace_id'; + if (this.dropdown.attr('data-field-name')) { + fieldName = this.dropdown.data('fieldName'); + } + if (this.dropdown.attr('data-show-any')) { + showAny = this.dropdown.data('showAny'); + } + this.dropdown.glDropdown({ + filterable: true, + selectable: true, + filterRemote: true, + search: { + fields: ['path'] + }, + fieldName: fieldName, + toggleLabel: function(selected) { + if (selected.id == null) { + return selected.text; + } else { + return selected.kind + ": " + selected.full_path; } - if (this.dropdown.attr('data-show-any')) { - showAny = this.dropdown.data('showAny'); - } - this.dropdown.glDropdown({ - filterable: true, - selectable: true, - filterRemote: true, - search: { - fields: ['path'] - }, - fieldName: fieldName, - toggleLabel: function(selected) { - if (selected.id == null) { - return selected.text; - } else { - return selected.kind + ": " + selected.full_path; - } - }, - data: function(term, dataCallback) { - return Api.namespaces(term, function(namespaces) { - var anyNamespace; - if (showAny) { - anyNamespace = { - text: 'Any namespace', - id: null - }; - namespaces.unshift(anyNamespace); - namespaces.splice(1, 0, 'divider'); - } - return dataCallback(namespaces); - }); - }, - text: function(namespace) { - if (namespace.id == null) { - return namespace.text; - } else { - return namespace.kind + ": " + namespace.full_path; - } - }, - renderRow: this.renderRow, - clicked: this.onSelectItem + }, + data: function(term, dataCallback) { + return Api.namespaces(term, function(namespaces) { + var anyNamespace; + if (showAny) { + anyNamespace = { + text: 'Any namespace', + id: null + }; + namespaces.unshift(anyNamespace); + namespaces.splice(1, 0, 'divider'); + } + return dataCallback(namespaces); }); - } - - NamespaceSelect.prototype.onSelectItem = function(options) { - const { e } = options; - return e.preventDefault(); - }; + }, + text: function(namespace) { + if (namespace.id == null) { + return namespace.text; + } else { + return namespace.kind + ": " + namespace.full_path; + } + }, + renderRow: this.renderRow, + clicked: this.onSelectItem + }); +} - return NamespaceSelect; - })(); +NamespaceSelect.prototype.onSelectItem = function(options) { + const { e } = options; + return e.preventDefault(); +}; - window.NamespaceSelects = (function() { - function NamespaceSelects(opts) { - var ref; - if (opts == null) { - opts = {}; - } - this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-namespace-select'); - this.$dropdowns.each(function(i, dropdown) { - var $dropdown; - $dropdown = $(dropdown); - return new window.NamespaceSelect({ - dropdown: $dropdown - }); - }); - } +function NamespaceSelects(opts) { + var ref; + if (opts == null) { + opts = {}; + } + this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-namespace-select'); + this.$dropdowns.each(function(i, dropdown) { + var $dropdown; + $dropdown = $(dropdown); + return new window.NamespaceSelect({ + dropdown: $dropdown + }); + }); +} - return NamespaceSelects; - })(); -}).call(window); +window.NamespaceSelect = NamespaceSelect; +window.NamespaceSelects = NamespaceSelects; diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js index 39fb302b644..ad11ec92e58 100644 --- a/app/assets/javascripts/new_branch_form.js +++ b/app/assets/javascripts/new_branch_form.js @@ -1,97 +1,93 @@ /* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len, object-shorthand */ import RefSelectDropdown from '~/ref_select_dropdown'; -(function() { - this.NewBranchForm = (function() { - function NewBranchForm(form, availableRefs) { - this.validate = this.validate.bind(this); - this.branchNameError = form.find('.js-branch-name-error'); - this.name = form.find('.js-branch-name'); - this.ref = form.find('#ref'); - new RefSelectDropdown($('.js-branch-select'), availableRefs); // eslint-disable-line no-new - this.setupRestrictions(); - this.addBinding(); - this.init(); - } +function NewBranchForm(form, availableRefs) { + this.validate = this.validate.bind(this); + this.branchNameError = form.find('.js-branch-name-error'); + this.name = form.find('.js-branch-name'); + this.ref = form.find('#ref'); + new RefSelectDropdown($('.js-branch-select'), availableRefs); // eslint-disable-line no-new + this.setupRestrictions(); + this.addBinding(); + this.init(); +} - NewBranchForm.prototype.addBinding = function() { - return this.name.on('blur', this.validate); - }; +NewBranchForm.prototype.addBinding = function() { + return this.name.on('blur', this.validate); +}; - NewBranchForm.prototype.init = function() { - if (this.name.length && this.name.val().length > 0) { - return this.name.trigger('blur'); - } - }; +NewBranchForm.prototype.init = function() { + if (this.name.length && this.name.val().length > 0) { + return this.name.trigger('blur'); + } +}; - NewBranchForm.prototype.setupRestrictions = function() { - var endsWith, invalid, single, startsWith; - startsWith = { - pattern: /^(\/|\.)/g, - prefix: "can't start with", - conjunction: "or" - }; - endsWith = { - pattern: /(\/|\.|\.lock)$/g, - prefix: "can't end in", - conjunction: "or" - }; - invalid = { - pattern: /(\s|~|\^|:|\?|\*|\[|\\|\.\.|@\{|\/{2,}){1}/g, - prefix: "can't contain", - conjunction: ", " - }; - single = { - pattern: /^@+$/g, - prefix: "can't be", - conjunction: "or" - }; - return this.restrictions = [startsWith, invalid, endsWith, single]; - }; +NewBranchForm.prototype.setupRestrictions = function() { + var endsWith, invalid, single, startsWith; + startsWith = { + pattern: /^(\/|\.)/g, + prefix: "can't start with", + conjunction: "or" + }; + endsWith = { + pattern: /(\/|\.|\.lock)$/g, + prefix: "can't end in", + conjunction: "or" + }; + invalid = { + pattern: /(\s|~|\^|:|\?|\*|\[|\\|\.\.|@\{|\/{2,}){1}/g, + prefix: "can't contain", + conjunction: ", " + }; + single = { + pattern: /^@+$/g, + prefix: "can't be", + conjunction: "or" + }; + return this.restrictions = [startsWith, invalid, endsWith, single]; +}; - NewBranchForm.prototype.validate = function() { - var errorMessage, errors, formatter, unique, validator; - const indexOf = [].indexOf; +NewBranchForm.prototype.validate = function() { + var errorMessage, errors, formatter, unique, validator; + const indexOf = [].indexOf; - this.branchNameError.empty(); - unique = function(values, value) { - if (indexOf.call(values, value) === -1) { - values.push(value); - } - return values; - }; - formatter = function(values, restriction) { - var formatted; - formatted = values.map(function(value) { - switch (false) { - case !/\s/.test(value): - return 'spaces'; - case !/\/{2,}/g.test(value): - return 'consecutive slashes'; - default: - return "'" + value + "'"; - } - }); - return restriction.prefix + " " + (formatted.join(restriction.conjunction)); - }; - validator = (function(_this) { - return function(errors, restriction) { - var matched; - matched = _this.name.val().match(restriction.pattern); - if (matched) { - return errors.concat(formatter(matched.reduce(unique, []), restriction)); - } else { - return errors; - } - }; - })(this); - errors = this.restrictions.reduce(validator, []); - if (errors.length > 0) { - errorMessage = $("<span/>").text(errors.join(', ')); - return this.branchNameError.append(errorMessage); + this.branchNameError.empty(); + unique = function(values, value) { + if (indexOf.call(values, value) === -1) { + values.push(value); + } + return values; + }; + formatter = function(values, restriction) { + var formatted; + formatted = values.map(function(value) { + switch (false) { + case !/\s/.test(value): + return 'spaces'; + case !/\/{2,}/g.test(value): + return 'consecutive slashes'; + default: + return "'" + value + "'"; + } + }); + return restriction.prefix + " " + (formatted.join(restriction.conjunction)); + }; + validator = (function(_this) { + return function(errors, restriction) { + var matched; + matched = _this.name.val().match(restriction.pattern); + if (matched) { + return errors.concat(formatter(matched.reduce(unique, []), restriction)); + } else { + return errors; } }; + })(this); + errors = this.restrictions.reduce(validator, []); + if (errors.length > 0) { + errorMessage = $("<span/>").text(errors.join(', ')); + return this.branchNameError.append(errorMessage); + } +}; - return NewBranchForm; - })(); -}).call(window); +window.NewBranchForm = NewBranchForm; diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js index 04073ef7270..9f7820b05f8 100644 --- a/app/assets/javascripts/new_commit_form.js +++ b/app/assets/javascripts/new_commit_form.js @@ -1,32 +1,29 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-return-assign, max-len */ -(function() { - this.NewCommitForm = (function() { - function NewCommitForm(form) { - this.form = form; - this.renderDestination = this.renderDestination.bind(this); - this.branchName = form.find('.js-branch-name'); - this.originalBranch = form.find('.js-original-branch'); - this.createMergeRequest = form.find('.js-create-merge-request'); - this.createMergeRequestContainer = form.find('.js-create-merge-request-container'); - this.branchName.keyup(this.renderDestination); - this.renderDestination(); - } - NewCommitForm.prototype.renderDestination = function() { - var different; - different = this.branchName.val() !== this.originalBranch.val(); - if (different) { - this.createMergeRequestContainer.show(); - if (!this.wasDifferent) { - this.createMergeRequest.prop('checked', true); - } - } else { - this.createMergeRequestContainer.hide(); - this.createMergeRequest.prop('checked', false); - } - return this.wasDifferent = different; - }; +function NewCommitForm(form) { + this.form = form; + this.renderDestination = this.renderDestination.bind(this); + this.branchName = form.find('.js-branch-name'); + this.originalBranch = form.find('.js-original-branch'); + this.createMergeRequest = form.find('.js-create-merge-request'); + this.createMergeRequestContainer = form.find('.js-create-merge-request-container'); + this.branchName.keyup(this.renderDestination); + this.renderDestination(); +} + +NewCommitForm.prototype.renderDestination = function() { + var different; + different = this.branchName.val() !== this.originalBranch.val(); + if (different) { + this.createMergeRequestContainer.show(); + if (!this.wasDifferent) { + this.createMergeRequest.prop('checked', true); + } + } else { + this.createMergeRequestContainer.hide(); + this.createMergeRequest.prop('checked', false); + } + return this.wasDifferent = different; +}; - return NewCommitForm; - })(); -}).call(window); +window.NewCommitForm = NewCommitForm; diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js index 838356133cd..f9f486fb4d8 100644 --- a/app/assets/javascripts/notifications_dropdown.js +++ b/app/assets/javascripts/notifications_dropdown.js @@ -1,31 +1,27 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, prefer-arrow-callback, no-else-return, max-len */ /* global Flash */ -(function() { - this.NotificationsDropdown = (function() { - function NotificationsDropdown() { - $(document).off('click', '.update-notification').on('click', '.update-notification', function(e) { - var form, label, notificationLevel; - e.preventDefault(); - if ($(this).is('.is-active') && $(this).data('notification-level') === 'custom') { - return; - } - notificationLevel = $(this).data('notification-level'); - label = $(this).data('notification-title'); - form = $(this).parents('.notification-form:first'); - form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner'); - form.find('#notification_setting_level').val(notificationLevel); - return form.submit(); - }); - $(document).off('ajax:success', '.notification-form').on('ajax:success', '.notification-form', function(e, data) { - if (data.saved) { - return $(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html); - } else { - return new Flash('Failed to save new settings', 'alert'); - } - }); +function NotificationsDropdown() { + $(document).off('click', '.update-notification').on('click', '.update-notification', function(e) { + var form, label, notificationLevel; + e.preventDefault(); + if ($(this).is('.is-active') && $(this).data('notification-level') === 'custom') { + return; } + notificationLevel = $(this).data('notification-level'); + label = $(this).data('notification-title'); + form = $(this).parents('.notification-form:first'); + form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner'); + form.find('#notification_setting_level').val(notificationLevel); + return form.submit(); + }); + $(document).off('ajax:success', '.notification-form').on('ajax:success', '.notification-form', function(e, data) { + if (data.saved) { + return $(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html); + } else { + return new Flash('Failed to save new settings', 'alert'); + } + }); +} - return NotificationsDropdown; - })(); -}).call(window); +window.NotificationsDropdown = NotificationsDropdown; diff --git a/app/assets/javascripts/notifications_form.js b/app/assets/javascripts/notifications_form.js index 2ab9c4fed2c..d8709a6fa5f 100644 --- a/app/assets/javascripts/notifications_form.js +++ b/app/assets/javascripts/notifications_form.js @@ -1,55 +1,51 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, newline-per-chained-call, comma-dangle, consistent-return, prefer-arrow-callback, max-len */ -(function() { - this.NotificationsForm = (function() { - function NotificationsForm() { - this.toggleCheckbox = this.toggleCheckbox.bind(this); - this.removeEventListeners(); - this.initEventListeners(); - } +function NotificationsForm() { + this.toggleCheckbox = this.toggleCheckbox.bind(this); + this.removeEventListeners(); + this.initEventListeners(); +} - NotificationsForm.prototype.removeEventListeners = function() { - return $(document).off('change', '.js-custom-notification-event'); - }; +NotificationsForm.prototype.removeEventListeners = function() { + return $(document).off('change', '.js-custom-notification-event'); +}; - NotificationsForm.prototype.initEventListeners = function() { - return $(document).on('change', '.js-custom-notification-event', this.toggleCheckbox); - }; +NotificationsForm.prototype.initEventListeners = function() { + return $(document).on('change', '.js-custom-notification-event', this.toggleCheckbox); +}; - NotificationsForm.prototype.toggleCheckbox = function(e) { - var $checkbox, $parent; - $checkbox = $(e.currentTarget); - $parent = $checkbox.closest('.checkbox'); - return this.saveEvent($checkbox, $parent); - }; +NotificationsForm.prototype.toggleCheckbox = function(e) { + var $checkbox, $parent; + $checkbox = $(e.currentTarget); + $parent = $checkbox.closest('.checkbox'); + return this.saveEvent($checkbox, $parent); +}; - NotificationsForm.prototype.showCheckboxLoadingSpinner = function($parent) { - return $parent.addClass('is-loading').find('.custom-notification-event-loading').removeClass('fa-check').addClass('fa-spin fa-spinner').removeClass('is-done'); - }; +NotificationsForm.prototype.showCheckboxLoadingSpinner = function($parent) { + return $parent.addClass('is-loading').find('.custom-notification-event-loading').removeClass('fa-check').addClass('fa-spin fa-spinner').removeClass('is-done'); +}; - NotificationsForm.prototype.saveEvent = function($checkbox, $parent) { - var form; - form = $parent.parents('form:first'); - return $.ajax({ - url: form.attr('action'), - method: form.attr('method'), - dataType: 'json', - data: form.serialize(), - beforeSend: (function(_this) { - return function() { - return _this.showCheckboxLoadingSpinner($parent); - }; - })(this) - }).done(function(data) { - $checkbox.enable(); - if (data.saved) { - $parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done'); - return setTimeout(function() { - return $parent.removeClass('is-loading').find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done'); - }, 2000); - } - }); - }; +NotificationsForm.prototype.saveEvent = function($checkbox, $parent) { + var form; + form = $parent.parents('form:first'); + return $.ajax({ + url: form.attr('action'), + method: form.attr('method'), + dataType: 'json', + data: form.serialize(), + beforeSend: (function(_this) { + return function() { + return _this.showCheckboxLoadingSpinner($parent); + }; + })(this) + }).done(function(data) { + $checkbox.enable(); + if (data.saved) { + $parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done'); + return setTimeout(function() { + return $parent.removeClass('is-loading').find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done'); + }, 2000); + } + }); +}; - return NotificationsForm; - })(); -}).call(window); +window.NotificationsForm = NotificationsForm; diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index 01110420cca..898bc40dacb 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -1,78 +1,76 @@ import '~/lib/utils/common_utils'; import '~/lib/utils/url_utility'; -(() => { - const ENDLESS_SCROLL_BOTTOM_PX = 400; - const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000; +const ENDLESS_SCROLL_BOTTOM_PX = 400; +const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000; - const Pager = { - init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) { - this.url = $('.content_list').data('href') || gl.utils.removeParams(['limit', 'offset']); - this.limit = limit; - this.offset = parseInt(gl.utils.getParameterByName('offset'), 10) || this.limit; - this.disable = disable; - this.prepareData = prepareData; - this.callback = callback; - this.loading = $('.loading').first(); - if (preload) { - this.offset = 0; - this.getOld(); - } - this.initLoadMore(); - }, +const Pager = { + init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) { + this.url = $('.content_list').data('href') || gl.utils.removeParams(['limit', 'offset']); + this.limit = limit; + this.offset = parseInt(gl.utils.getParameterByName('offset'), 10) || this.limit; + this.disable = disable; + this.prepareData = prepareData; + this.callback = callback; + this.loading = $('.loading').first(); + if (preload) { + this.offset = 0; + this.getOld(); + } + this.initLoadMore(); + }, - getOld() { - this.loading.show(); - $.ajax({ - type: 'GET', - url: this.url, - data: `limit=${this.limit}&offset=${this.offset}`, - dataType: 'json', - error: () => this.loading.hide(), - success: (data) => { - this.append(data.count, this.prepareData(data.html)); - this.callback(); + getOld() { + this.loading.show(); + $.ajax({ + type: 'GET', + url: this.url, + data: `limit=${this.limit}&offset=${this.offset}`, + dataType: 'json', + error: () => this.loading.hide(), + success: (data) => { + this.append(data.count, this.prepareData(data.html)); + this.callback(); - // keep loading until we've filled the viewport height - if (!this.disable && !this.isScrollable()) { - this.getOld(); - } else { - this.loading.hide(); - } - }, - }); - }, + // keep loading until we've filled the viewport height + if (!this.disable && !this.isScrollable()) { + this.getOld(); + } else { + this.loading.hide(); + } + }, + }); + }, - append(count, html) { - $('.content_list').append(html); - if (count > 0) { - this.offset += count; - } else { - this.disable = true; - } - }, + append(count, html) { + $('.content_list').append(html); + if (count > 0) { + this.offset += count; + } else { + this.disable = true; + } + }, - isScrollable() { - const $w = $(window); - return $(document).height() > $w.height() + $w.scrollTop() + ENDLESS_SCROLL_BOTTOM_PX; - }, + isScrollable() { + const $w = $(window); + return $(document).height() > $w.height() + $w.scrollTop() + ENDLESS_SCROLL_BOTTOM_PX; + }, - initLoadMore() { - $(document).unbind('scroll'); - $(document).endlessScroll({ - bottomPixels: ENDLESS_SCROLL_BOTTOM_PX, - fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS, - fireOnce: true, - ceaseFire: () => this.disable === true, - callback: () => { - if (!this.loading.is(':visible')) { - this.loading.show(); - this.getOld(); - } - }, - }); - }, - }; + initLoadMore() { + $(document).unbind('scroll'); + $(document).endlessScroll({ + bottomPixels: ENDLESS_SCROLL_BOTTOM_PX, + fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS, + fireOnce: true, + ceaseFire: () => this.disable === true, + callback: () => { + if (!this.loading.is(':visible')) { + this.loading.show(); + this.getOld(); + } + }, + }); + }, +}; - window.Pager = Pager; -})(); +window.Pager = Pager; diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js index 141333b2b4d..fd1cbda10dd 100644 --- a/app/assets/javascripts/preview_markdown.js +++ b/app/assets/javascripts/preview_markdown.js @@ -6,195 +6,191 @@ // (including the explanation of quick actions), and showing a warning when // more than `x` users are referenced. // -(function () { - var lastTextareaPreviewed; - var lastTextareaHeight = null; - var markdownPreview; - var previewButtonSelector; - var writeButtonSelector; - - window.MarkdownPreview = (function () { - function MarkdownPreview() {} - - // Minimum number of users referenced before triggering a warning - MarkdownPreview.prototype.referenceThreshold = 10; - MarkdownPreview.prototype.emptyMessage = 'Nothing to preview.'; - - MarkdownPreview.prototype.ajaxCache = {}; - - MarkdownPreview.prototype.showPreview = function ($form) { - var mdText; - var preview = $form.find('.js-md-preview'); - var url = preview.data('url'); - if (preview.hasClass('md-preview-loading')) { - return; - } - mdText = $form.find('textarea.markdown-area').val(); - - if (mdText.trim().length === 0) { - preview.text(this.emptyMessage); - this.hideReferencedUsers($form); +var lastTextareaPreviewed; +var lastTextareaHeight = null; +var markdownPreview; +var previewButtonSelector; +var writeButtonSelector; + +function MarkdownPreview() {} + +// Minimum number of users referenced before triggering a warning +MarkdownPreview.prototype.referenceThreshold = 10; +MarkdownPreview.prototype.emptyMessage = 'Nothing to preview.'; + +MarkdownPreview.prototype.ajaxCache = {}; + +MarkdownPreview.prototype.showPreview = function ($form) { + var mdText; + var preview = $form.find('.js-md-preview'); + var url = preview.data('url'); + if (preview.hasClass('md-preview-loading')) { + return; + } + mdText = $form.find('textarea.markdown-area').val(); + + if (mdText.trim().length === 0) { + preview.text(this.emptyMessage); + this.hideReferencedUsers($form); + } else { + preview.addClass('md-preview-loading').text('Loading...'); + this.fetchMarkdownPreview(mdText, url, (function (response) { + var body; + if (response.body.length > 0) { + body = response.body; } else { - preview.addClass('md-preview-loading').text('Loading...'); - this.fetchMarkdownPreview(mdText, url, (function (response) { - var body; - if (response.body.length > 0) { - body = response.body; - } else { - body = this.emptyMessage; - } - - preview.removeClass('md-preview-loading').html(body); - preview.renderGFM(); - this.renderReferencedUsers(response.references.users, $form); - - if (response.references.commands) { - this.renderReferencedCommands(response.references.commands, $form); - } - }).bind(this)); - } - }; - - MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) { - if (!url) { - return; + body = this.emptyMessage; } - if (text === this.ajaxCache.text) { - success(this.ajaxCache.response); - return; - } - $.ajax({ - type: 'POST', - url: url, - data: { - text: text - }, - dataType: 'json', - success: (function (response) { - this.ajaxCache = { - text: text, - response: response - }; - success(response); - }).bind(this) - }); - }; - - MarkdownPreview.prototype.hideReferencedUsers = function ($form) { - $form.find('.referenced-users').hide(); - }; - - MarkdownPreview.prototype.renderReferencedUsers = function (users, $form) { - var referencedUsers; - referencedUsers = $form.find('.referenced-users'); - if (referencedUsers.length) { - if (users.length >= this.referenceThreshold) { - referencedUsers.show(); - referencedUsers.find('.js-referenced-users-count').text(users.length); - } else { - referencedUsers.hide(); - } - } - }; - - MarkdownPreview.prototype.hideReferencedCommands = function ($form) { - $form.find('.referenced-commands').hide(); - }; - - MarkdownPreview.prototype.renderReferencedCommands = function (commands, $form) { - var referencedCommands; - referencedCommands = $form.find('.referenced-commands'); - if (commands.length > 0) { - referencedCommands.html(commands); - referencedCommands.show(); - } else { - referencedCommands.html(''); - referencedCommands.hide(); - } - }; - - return MarkdownPreview; - }()); - - markdownPreview = new window.MarkdownPreview(); - - previewButtonSelector = '.js-md-preview-button'; - - writeButtonSelector = '.js-md-write-button'; - - lastTextareaPreviewed = null; - $.fn.setupMarkdownPreview = function () { - var $form = $(this); - $form.find('textarea.markdown-area').on('input', function () { - markdownPreview.hideReferencedUsers($form); - }); - }; + preview.removeClass('md-preview-loading').html(body); + preview.renderGFM(); + this.renderReferencedUsers(response.references.users, $form); - $(document).on('markdown-preview:show', function (e, $form) { - if (!$form) { - return; + if (response.references.commands) { + this.renderReferencedCommands(response.references.commands, $form); + } + }).bind(this)); + } +}; + +MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) { + if (!url) { + return; + } + if (text === this.ajaxCache.text) { + success(this.ajaxCache.response); + return; + } + $.ajax({ + type: 'POST', + url: url, + data: { + text: text + }, + dataType: 'json', + success: (function (response) { + this.ajaxCache = { + text: text, + response: response + }; + success(response); + }).bind(this) + }); +}; + +MarkdownPreview.prototype.hideReferencedUsers = function ($form) { + $form.find('.referenced-users').hide(); +}; + +MarkdownPreview.prototype.renderReferencedUsers = function (users, $form) { + var referencedUsers; + referencedUsers = $form.find('.referenced-users'); + if (referencedUsers.length) { + if (users.length >= this.referenceThreshold) { + referencedUsers.show(); + referencedUsers.find('.js-referenced-users-count').text(users.length); + } else { + referencedUsers.hide(); } + } +}; - lastTextareaPreviewed = $form.find('textarea.markdown-area'); - lastTextareaHeight = lastTextareaPreviewed.height(); +MarkdownPreview.prototype.hideReferencedCommands = function ($form) { + $form.find('.referenced-commands').hide(); +}; - // toggle tabs - $form.find(writeButtonSelector).parent().removeClass('active'); - $form.find(previewButtonSelector).parent().addClass('active'); +MarkdownPreview.prototype.renderReferencedCommands = function (commands, $form) { + var referencedCommands; + referencedCommands = $form.find('.referenced-commands'); + if (commands.length > 0) { + referencedCommands.html(commands); + referencedCommands.show(); + } else { + referencedCommands.html(''); + referencedCommands.hide(); + } +}; - // toggle content - $form.find('.md-write-holder').hide(); - $form.find('.md-preview-holder').show(); - markdownPreview.showPreview($form); - }); +window.MarkdownPreview = MarkdownPreview; - $(document).on('markdown-preview:hide', function (e, $form) { - if (!$form) { - return; - } - lastTextareaPreviewed = null; +markdownPreview = new window.MarkdownPreview(); - if (lastTextareaHeight) { - $form.find('textarea.markdown-area').height(lastTextareaHeight); - } +previewButtonSelector = '.js-md-preview-button'; - // toggle tabs - $form.find(writeButtonSelector).parent().addClass('active'); - $form.find(previewButtonSelector).parent().removeClass('active'); +writeButtonSelector = '.js-md-write-button'; - // toggle content - $form.find('.md-write-holder').show(); - $form.find('textarea.markdown-area').focus(); - $form.find('.md-preview-holder').hide(); +lastTextareaPreviewed = null; - markdownPreview.hideReferencedCommands($form); - }); - - $(document).on('markdown-preview:toggle', function (e, keyboardEvent) { - var $target; - $target = $(keyboardEvent.target); - if ($target.is('textarea.markdown-area')) { - $(document).triggerHandler('markdown-preview:show', [$target.closest('form')]); - keyboardEvent.preventDefault(); - } else if (lastTextareaPreviewed) { - $target = lastTextareaPreviewed; - $(document).triggerHandler('markdown-preview:hide', [$target.closest('form')]); - keyboardEvent.preventDefault(); - } - }); - - $(document).on('click', previewButtonSelector, function (e) { - var $form; - e.preventDefault(); - $form = $(this).closest('form'); - $(document).triggerHandler('markdown-preview:show', [$form]); +$.fn.setupMarkdownPreview = function () { + var $form = $(this); + $form.find('textarea.markdown-area').on('input', function () { + markdownPreview.hideReferencedUsers($form); }); +}; + +$(document).on('markdown-preview:show', function (e, $form) { + if (!$form) { + return; + } + + lastTextareaPreviewed = $form.find('textarea.markdown-area'); + lastTextareaHeight = lastTextareaPreviewed.height(); + + // toggle tabs + $form.find(writeButtonSelector).parent().removeClass('active'); + $form.find(previewButtonSelector).parent().addClass('active'); + + // toggle content + $form.find('.md-write-holder').hide(); + $form.find('.md-preview-holder').show(); + markdownPreview.showPreview($form); +}); + +$(document).on('markdown-preview:hide', function (e, $form) { + if (!$form) { + return; + } + lastTextareaPreviewed = null; - $(document).on('click', writeButtonSelector, function (e) { - var $form; - e.preventDefault(); - $form = $(this).closest('form'); - $(document).triggerHandler('markdown-preview:hide', [$form]); - }); -}()); + if (lastTextareaHeight) { + $form.find('textarea.markdown-area').height(lastTextareaHeight); + } + + // toggle tabs + $form.find(writeButtonSelector).parent().addClass('active'); + $form.find(previewButtonSelector).parent().removeClass('active'); + + // toggle content + $form.find('.md-write-holder').show(); + $form.find('textarea.markdown-area').focus(); + $form.find('.md-preview-holder').hide(); + + markdownPreview.hideReferencedCommands($form); +}); + +$(document).on('markdown-preview:toggle', function (e, keyboardEvent) { + var $target; + $target = $(keyboardEvent.target); + if ($target.is('textarea.markdown-area')) { + $(document).triggerHandler('markdown-preview:show', [$target.closest('form')]); + keyboardEvent.preventDefault(); + } else if (lastTextareaPreviewed) { + $target = lastTextareaPreviewed; + $(document).triggerHandler('markdown-preview:hide', [$target.closest('form')]); + keyboardEvent.preventDefault(); + } +}); + +$(document).on('click', previewButtonSelector, function (e) { + var $form; + e.preventDefault(); + $form = $(this).closest('form'); + $(document).triggerHandler('markdown-preview:show', [$form]); +}); + +$(document).on('click', writeButtonSelector, function (e) { + var $form; + e.preventDefault(); + $form = $(this).closest('form'); + $(document).triggerHandler('markdown-preview:hide', [$form]); +}); diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js index 738e710deb9..e93ab0f06f2 100644 --- a/app/assets/javascripts/project.js +++ b/app/assets/javascripts/project.js @@ -3,129 +3,125 @@ import Cookies from 'js-cookie'; -(function() { - this.Project = (function() { - function Project() { - $('ul.clone-options-dropdown a').click(function() { - var url; - if ($(this).hasClass('active')) { - return; - } - $('.active').not($(this)).removeClass('active'); - $(this).toggleClass('active'); - url = $("#project_clone").val(); - $('#project_clone').val(url); - return $('.clone').text(url); - // Git protocol switcher - // Remove the active class for all buttons (ssh, http, kerberos if shown) - // Add the active class for the clicked button - // Update the input field - // Update the command line instructions - }); - // Ref switcher - this.initRefSwitcher(); - $('.project-refs-select').on('change', function() { - return $(this).parents('form').submit(); - }); - $('.hide-no-ssh-message').on('click', function(e) { - Cookies.set('hide_no_ssh_message', 'false'); - $(this).parents('.no-ssh-key-message').remove(); - return e.preventDefault(); - }); - $('.hide-no-password-message').on('click', function(e) { - Cookies.set('hide_no_password_message', 'false'); - $(this).parents('.no-password-message').remove(); - return e.preventDefault(); - }); - this.projectSelectDropdown(); +function Project() { + $('ul.clone-options-dropdown a').click(function() { + var url; + if ($(this).hasClass('active')) { + return; } + $('.active').not($(this)).removeClass('active'); + $(this).toggleClass('active'); + url = $("#project_clone").val(); + $('#project_clone').val(url); + return $('.clone').text(url); + // Git protocol switcher + // Remove the active class for all buttons (ssh, http, kerberos if shown) + // Add the active class for the clicked button + // Update the input field + // Update the command line instructions + }); + // Ref switcher + this.initRefSwitcher(); + $('.project-refs-select').on('change', function() { + return $(this).parents('form').submit(); + }); + $('.hide-no-ssh-message').on('click', function(e) { + Cookies.set('hide_no_ssh_message', 'false'); + $(this).parents('.no-ssh-key-message').remove(); + return e.preventDefault(); + }); + $('.hide-no-password-message').on('click', function(e) { + Cookies.set('hide_no_password_message', 'false'); + $(this).parents('.no-password-message').remove(); + return e.preventDefault(); + }); + this.projectSelectDropdown(); +} - Project.prototype.projectSelectDropdown = function() { - new ProjectSelect(); - $('.project-item-select').on('click', (function(_this) { - return function(e) { - return _this.changeProject($(e.currentTarget).val()); - }; - })(this)); - return $('.js-projects-dropdown-toggle').on('click', function(e) { - e.preventDefault(); - return $('.js-projects-dropdown').select2('open'); - }); +Project.prototype.projectSelectDropdown = function() { + new ProjectSelect(); + $('.project-item-select').on('click', (function(_this) { + return function(e) { + return _this.changeProject($(e.currentTarget).val()); }; + })(this)); + return $('.js-projects-dropdown-toggle').on('click', function(e) { + e.preventDefault(); + return $('.js-projects-dropdown').select2('open'); + }); +}; - Project.prototype.changeProject = function(url) { - return window.location = url; - }; +Project.prototype.changeProject = function(url) { + return window.location = url; +}; - Project.prototype.initRefSwitcher = function() { - var refListItem = document.createElement('li'); - var refLink = document.createElement('a'); +Project.prototype.initRefSwitcher = function() { + var refListItem = document.createElement('li'); + var refLink = document.createElement('a'); - refLink.href = '#'; + refLink.href = '#'; - return $('.js-project-refs-dropdown').each(function() { - var $dropdown, selected; - $dropdown = $(this); - selected = $dropdown.data('selected'); - return $dropdown.glDropdown({ - data: function(term, callback) { - return $.ajax({ - url: $dropdown.data('refs-url'), - data: { - ref: $dropdown.data('ref'), - search: term - }, - dataType: "json" - }).done(function(refs) { - return callback(refs); - }); + return $('.js-project-refs-dropdown').each(function() { + var $dropdown, selected; + $dropdown = $(this); + selected = $dropdown.data('selected'); + return $dropdown.glDropdown({ + data: function(term, callback) { + return $.ajax({ + url: $dropdown.data('refs-url'), + data: { + ref: $dropdown.data('ref'), + search: term }, - selectable: true, - filterable: true, - filterRemote: true, - filterByText: true, - fieldName: $dropdown.data('field-name'), - renderRow: function(ref) { - var li = refListItem.cloneNode(false); + dataType: "json" + }).done(function(refs) { + return callback(refs); + }); + }, + selectable: true, + filterable: true, + filterRemote: true, + filterByText: true, + fieldName: $dropdown.data('field-name'), + renderRow: function(ref) { + var li = refListItem.cloneNode(false); - if (ref.header != null) { - li.className = 'dropdown-header'; - li.textContent = ref.header; - } else { - var link = refLink.cloneNode(false); + if (ref.header != null) { + li.className = 'dropdown-header'; + li.textContent = ref.header; + } else { + var link = refLink.cloneNode(false); - if (ref === selected) { - link.className = 'is-active'; - } + if (ref === selected) { + link.className = 'is-active'; + } - link.textContent = ref; - link.dataset.ref = ref; + link.textContent = ref; + link.dataset.ref = ref; - li.appendChild(link); - } + li.appendChild(link); + } - return li; - }, - id: function(obj, $el) { - return $el.attr('data-ref'); - }, - toggleLabel: function(obj, $el) { - return $el.text().trim(); - }, - clicked: function(options) { - const { e } = options; - e.preventDefault(); - if ($('input[name="ref"]').length) { - var $form = $dropdown.closest('form'); - var action = $form.attr('action'); - var divider = action.indexOf('?') === -1 ? '?' : '&'; - gl.utils.visitUrl(action + '' + divider + '' + $form.serialize()); - } - } - }); - }); - }; + return li; + }, + id: function(obj, $el) { + return $el.attr('data-ref'); + }, + toggleLabel: function(obj, $el) { + return $el.text().trim(); + }, + clicked: function(options) { + const { e } = options; + e.preventDefault(); + if ($('input[name="ref"]').length) { + var $form = $dropdown.closest('form'); + var action = $form.attr('action'); + var divider = action.indexOf('?') === -1 ? '?' : '&'; + gl.utils.visitUrl(action + '' + divider + '' + $form.serialize()); + } + } + }); + }); +}; - return Project; - })(); -}).call(window); +window.Project = Project; diff --git a/app/assets/javascripts/project_avatar.js b/app/assets/javascripts/project_avatar.js index aabdfbf65e2..e412504de31 100644 --- a/app/assets/javascripts/project_avatar.js +++ b/app/assets/javascripts/project_avatar.js @@ -1,20 +1,16 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-useless-escape, max-len */ -(function() { - this.ProjectAvatar = (function() { - function ProjectAvatar() { - $('.js-choose-project-avatar-button').bind('click', function() { - var form; - form = $(this).closest('form'); - return form.find('.js-project-avatar-input').click(); - }); - $('.js-project-avatar-input').bind('change', function() { - var filename, form; - form = $(this).closest('form'); - filename = $(this).val().replace(/^.*[\\\/]/, ''); - return form.find('.js-avatar-filename').text(filename); - }); - } +function ProjectAvatar() { + $('.js-choose-project-avatar-button').bind('click', function() { + var form; + form = $(this).closest('form'); + return form.find('.js-project-avatar-input').click(); + }); + $('.js-project-avatar-input').bind('change', function() { + var filename, form; + form = $(this).closest('form'); + filename = $(this).val().replace(/^.*[\\\/]/, ''); + return form.find('.js-avatar-filename').text(filename); + }); +} - return ProjectAvatar; - })(); -}).call(window); +window.ProjectAvatar = ProjectAvatar; diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index 11f9754780d..e59901d5fc1 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -1,169 +1,161 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, object-shorthand, no-param-reassign, comma-dangle, prefer-template, no-unused-vars, no-return-assign */ /* global fuzzaldrinPlus */ -(function() { - this.ProjectFindFile = (function() { - var highlighter; +var highlighter; - function ProjectFindFile(element1, options) { - this.element = element1; - this.options = options; - this.goToBlob = this.goToBlob.bind(this); - this.goToTree = this.goToTree.bind(this); - this.selectRowDown = this.selectRowDown.bind(this); - this.selectRowUp = this.selectRowUp.bind(this); - this.filePaths = {}; - this.inputElement = this.element.find(".file-finder-input"); - // init event - this.initEvent(); - // focus text input box - this.inputElement.focus(); - // load file list - this.load(this.options.url); - } +function ProjectFindFile(element1, options) { + this.element = element1; + this.options = options; + this.goToBlob = this.goToBlob.bind(this); + this.goToTree = this.goToTree.bind(this); + this.selectRowDown = this.selectRowDown.bind(this); + this.selectRowUp = this.selectRowUp.bind(this); + this.filePaths = {}; + this.inputElement = this.element.find(".file-finder-input"); + // init event + this.initEvent(); + // focus text input box + this.inputElement.focus(); + // load file list + this.load(this.options.url); +} - ProjectFindFile.prototype.initEvent = function() { - this.inputElement.off("keyup"); - this.inputElement.on("keyup", (function(_this) { - return function(event) { - var oldValue, ref, target, value; - target = $(event.target); - value = target.val(); - oldValue = (ref = target.data("oldValue")) != null ? ref : ""; - if (value !== oldValue) { - target.data("oldValue", value); - _this.findFile(); - return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus(); - } - }; - })(this)); - }; +ProjectFindFile.prototype.initEvent = function() { + this.inputElement.off("keyup"); + this.inputElement.on("keyup", (event) => { + var oldValue, ref, target, value; + target = $(event.target); + value = target.val(); + oldValue = (ref = target.data("oldValue")) != null ? ref : ""; + if (value !== oldValue) { + target.data("oldValue", value); + this.findFile(); + return this.element.find("tr.tree-item").eq(0).addClass("selected").focus(); + } + }); +}; - ProjectFindFile.prototype.findFile = function() { - var result, searchText; - searchText = this.inputElement.val(); - result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths; - return this.renderList(result, searchText); - // find file - }; +ProjectFindFile.prototype.findFile = function() { + var result, searchText; + searchText = this.inputElement.val(); + result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths; + return this.renderList(result, searchText); +// find file +}; - // files pathes load - ProjectFindFile.prototype.load = function(url) { - return $.ajax({ - url: url, - method: "get", - dataType: "json", - success: (function(_this) { - return function(data) { - _this.element.find(".loading").hide(); - _this.filePaths = data; - _this.findFile(); - return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus(); - }; - })(this) - }); - }; +// files pathes load +ProjectFindFile.prototype.load = function(url) { + return $.ajax({ + url: url, + method: "get", + dataType: "json", + success: (data) => { + this.element.find(".loading").hide(); + this.filePaths = data; + this.findFile(); + return this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus(); + } + }); +}; - // render result - ProjectFindFile.prototype.renderList = function(filePaths, searchText) { - var blobItemUrl, filePath, html, i, j, len, matches, results; - this.element.find(".tree-table > tbody").empty(); - results = []; - for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) { - filePath = filePaths[i]; - if (i === 20) { - break; - } - if (searchText) { - matches = fuzzaldrinPlus.match(filePath, searchText); - } - blobItemUrl = this.options.blobUrlTemplate + "/" + filePath; - html = this.makeHtml(filePath, matches, blobItemUrl); - results.push(this.element.find(".tree-table > tbody").append(html)); - } - return results; - }; +// render result +ProjectFindFile.prototype.renderList = function(filePaths, searchText) { + var blobItemUrl, filePath, html, i, j, len, matches, results; + this.element.find(".tree-table > tbody").empty(); + results = []; + for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) { + filePath = filePaths[i]; + if (i === 20) { + break; + } + if (searchText) { + matches = fuzzaldrinPlus.match(filePath, searchText); + } + blobItemUrl = this.options.blobUrlTemplate + "/" + filePath; + html = this.makeHtml(filePath, matches, blobItemUrl); + results.push(this.element.find(".tree-table > tbody").append(html)); + } + return results; +}; - // highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> ) - highlighter = function(element, text, matches) { - var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched; - lastIndex = 0; - highlightText = ""; - matchedChars = []; - for (j = 0, len = matches.length; j < len; j += 1) { - matchIndex = matches[j]; - unmatched = text.substring(lastIndex, matchIndex); - if (unmatched) { - if (matchedChars.length) { - element.append(matchedChars.join("").bold()); - } - matchedChars = []; - element.append(document.createTextNode(unmatched)); - } - matchedChars.push(text[matchIndex]); - lastIndex = matchIndex + 1; - } +// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> ) +highlighter = function(element, text, matches) { + var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched; + lastIndex = 0; + highlightText = ""; + matchedChars = []; + for (j = 0, len = matches.length; j < len; j += 1) { + matchIndex = matches[j]; + unmatched = text.substring(lastIndex, matchIndex); + if (unmatched) { if (matchedChars.length) { element.append(matchedChars.join("").bold()); } - return element.append(document.createTextNode(text.substring(lastIndex))); - }; + matchedChars = []; + element.append(document.createTextNode(unmatched)); + } + matchedChars.push(text[matchIndex]); + lastIndex = matchIndex + 1; + } + if (matchedChars.length) { + element.append(matchedChars.join("").bold()); + } + return element.append(document.createTextNode(text.substring(lastIndex))); +}; - // make tbody row html - ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) { - var $tr; - $tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>"); - if (matches) { - $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl)); - } else { - $tr.find("a").attr("href", blobItemUrl); - $tr.find(".str-truncated").text(filePath); - } - return $tr; - }; +// make tbody row html +ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) { + var $tr; + $tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>"); + if (matches) { + $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl)); + } else { + $tr.find("a").attr("href", blobItemUrl); + $tr.find(".str-truncated").text(filePath); + } + return $tr; +}; - ProjectFindFile.prototype.selectRow = function(type) { - var next, rows, selectedRow; - rows = this.element.find(".files-slider tr.tree-item"); - selectedRow = this.element.find(".files-slider tr.tree-item.selected"); - if (rows && rows.length > 0) { - if (selectedRow && selectedRow.length > 0) { - if (type === "UP") { - next = selectedRow.prev(); - } else if (type === "DOWN") { - next = selectedRow.next(); - } - if (next.length > 0) { - selectedRow.removeClass("selected"); - selectedRow = next; - } - } else { - selectedRow = rows.eq(0); - } - return selectedRow.addClass("selected").focus(); +ProjectFindFile.prototype.selectRow = function(type) { + var next, rows, selectedRow; + rows = this.element.find(".files-slider tr.tree-item"); + selectedRow = this.element.find(".files-slider tr.tree-item.selected"); + if (rows && rows.length > 0) { + if (selectedRow && selectedRow.length > 0) { + if (type === "UP") { + next = selectedRow.prev(); + } else if (type === "DOWN") { + next = selectedRow.next(); } - }; + if (next.length > 0) { + selectedRow.removeClass("selected"); + selectedRow = next; + } + } else { + selectedRow = rows.eq(0); + } + return selectedRow.addClass("selected").focus(); + } +}; - ProjectFindFile.prototype.selectRowUp = function() { - return this.selectRow("UP"); - }; +ProjectFindFile.prototype.selectRowUp = function() { + return this.selectRow("UP"); +}; - ProjectFindFile.prototype.selectRowDown = function() { - return this.selectRow("DOWN"); - }; +ProjectFindFile.prototype.selectRowDown = function() { + return this.selectRow("DOWN"); +}; - ProjectFindFile.prototype.goToTree = function() { - return location.href = this.options.treeUrl; - }; +ProjectFindFile.prototype.goToTree = function() { + return location.href = this.options.treeUrl; +}; - ProjectFindFile.prototype.goToBlob = function() { - var $link = this.element.find(".tree-item.selected .tree-item-file-name a"); +ProjectFindFile.prototype.goToBlob = function() { + var $link = this.element.find(".tree-item.selected .tree-item-file-name a"); - if ($link.length) { - $link.get(0).click(); - } - }; + if ($link.length) { + $link.get(0).click(); + } +}; - return ProjectFindFile; - })(); -}).call(window); +window.ProjectFindFile = ProjectFindFile; diff --git a/app/assets/javascripts/project_fork.js b/app/assets/javascripts/project_fork.js index 47197db39d3..51b1a7db108 100644 --- a/app/assets/javascripts/project_fork.js +++ b/app/assets/javascripts/project_fork.js @@ -1,13 +1,9 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, max-len */ -(function() { - this.ProjectFork = (function() { - function ProjectFork() { - $('.fork-thumbnail a').on('click', function() { - $('.fork-namespaces').hide(); - return $('.save-project-loader').show(); - }); - } +function ProjectFork() { + $('.fork-thumbnail a').on('click', function() { + $('.fork-namespaces').hide(); + return $('.save-project-loader').show(); + }); +} - return ProjectFork; - })(); -}).call(window); +window.ProjectFork = ProjectFork; diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js index 08334bf1ec5..f6d4d51d1c7 100644 --- a/app/assets/javascripts/project_import.js +++ b/app/assets/javascripts/project_import.js @@ -1,13 +1,9 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, max-len */ -(function() { - this.ProjectImport = (function() { - function ProjectImport() { - setTimeout(function() { - return gl.utils.visitUrl(location.href); - }, 5000); - } +function ProjectImport() { + setTimeout(function() { + return gl.utils.visitUrl(location.href); + }, 5000); +} - return ProjectImport; - })(); -}).call(window); +window.ProjectImport = ProjectImport; diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js index 0a811627600..d17a9bac82b 100644 --- a/app/assets/javascripts/project_label_subscription.js +++ b/app/assets/javascripts/project_label_subscription.js @@ -1,55 +1,54 @@ /* eslint-disable wrap-iife, func-names, space-before-function-paren, object-shorthand, comma-dangle, one-var, one-var-declaration-per-line, no-restricted-syntax, max-len, no-param-reassign */ +const global = window.gl || (window.gl = {}); -(function(global) { - class ProjectLabelSubscription { - constructor(container) { - this.$container = $(container); - this.$buttons = this.$container.find('.js-subscribe-button'); +class ProjectLabelSubscription { + constructor(container) { + this.$container = $(container); + this.$buttons = this.$container.find('.js-subscribe-button'); - this.$buttons.on('click', this.toggleSubscription.bind(this)); - } + this.$buttons.on('click', this.toggleSubscription.bind(this)); + } - toggleSubscription(event) { - event.preventDefault(); + toggleSubscription(event) { + event.preventDefault(); - const $btn = $(event.currentTarget); - const $span = $btn.find('span'); - const url = $btn.attr('data-url'); - const oldStatus = $btn.attr('data-status'); + const $btn = $(event.currentTarget); + const $span = $btn.find('span'); + const url = $btn.attr('data-url'); + const oldStatus = $btn.attr('data-status'); - $btn.addClass('disabled'); - $span.toggleClass('hidden'); + $btn.addClass('disabled'); + $span.toggleClass('hidden'); - $.ajax({ - type: 'POST', - url: url - }).done(() => { - let newStatus, newAction; + $.ajax({ + type: 'POST', + url: url + }).done(() => { + let newStatus, newAction; - if (oldStatus === 'unsubscribed') { - [newStatus, newAction] = ['subscribed', 'Unsubscribe']; - } else { - [newStatus, newAction] = ['unsubscribed', 'Subscribe']; - } + if (oldStatus === 'unsubscribed') { + [newStatus, newAction] = ['subscribed', 'Unsubscribe']; + } else { + [newStatus, newAction] = ['unsubscribed', 'Subscribe']; + } - $span.toggleClass('hidden'); - $btn.removeClass('disabled'); + $span.toggleClass('hidden'); + $btn.removeClass('disabled'); - this.$buttons.attr('data-status', newStatus); - this.$buttons.find('> span').text(newAction); + this.$buttons.attr('data-status', newStatus); + this.$buttons.find('> span').text(newAction); - this.$buttons.map((button) => { - const $button = $(button); + this.$buttons.map((button) => { + const $button = $(button); - if ($button.attr('data-original-title')) { - $button.tooltip('hide').attr('data-original-title', newAction).tooltip('fixTitle'); - } + if ($button.attr('data-original-title')) { + $button.tooltip('hide').attr('data-original-title', newAction).tooltip('fixTitle'); + } - return button; - }); + return button; }); - } + }); } +} - global.ProjectLabelSubscription = ProjectLabelSubscription; -})(window.gl || (window.gl = {})); +global.ProjectLabelSubscription = ProjectLabelSubscription; diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js index fd89a1a85c3..a015ff58411 100644 --- a/app/assets/javascripts/project_new.js +++ b/app/assets/javascripts/project_new.js @@ -7,153 +7,149 @@ function highlightChanges($elm) { setTimeout(() => $elm.removeClass('highlight-changes'), 10); } -(function() { - this.ProjectNew = (function() { - function ProjectNew() { - this.toggleSettings = this.toggleSettings.bind(this); - this.$selects = $('.features select'); - this.$repoSelects = this.$selects.filter('.js-repo-select'); - this.$projectSelects = this.$selects.not('.js-repo-select'); - - $('.project-edit-container').on('ajax:before', (function(_this) { - return function() { - $('.project-edit-container').hide(); - return $('.save-project-loader').show(); - }; - })(this)); - - this.initVisibilitySelect(); - - this.toggleSettings(); - this.toggleSettingsOnclick(); - this.toggleRepoVisibility(); - } +function ProjectNew() { + this.toggleSettings = this.toggleSettings.bind(this); + this.$selects = $('.features select'); + this.$repoSelects = this.$selects.filter('.js-repo-select'); + this.$projectSelects = this.$selects.not('.js-repo-select'); + + $('.project-edit-container').on('ajax:before', (function(_this) { + return function() { + $('.project-edit-container').hide(); + return $('.save-project-loader').show(); + }; + })(this)); + + this.initVisibilitySelect(); + + this.toggleSettings(); + this.toggleSettingsOnclick(); + this.toggleRepoVisibility(); +} + +ProjectNew.prototype.initVisibilitySelect = function() { + const visibilityContainer = document.querySelector('.js-visibility-select'); + if (!visibilityContainer) return; + const visibilitySelect = new VisibilitySelect(visibilityContainer); + visibilitySelect.init(); + + const $visibilitySelect = $(visibilityContainer).find('select'); + let projectVisibility = $visibilitySelect.val(); + const PROJECT_VISIBILITY_PRIVATE = '0'; + + $visibilitySelect.on('change', () => { + const newProjectVisibility = $visibilitySelect.val(); + + if (projectVisibility !== newProjectVisibility) { + this.$projectSelects.each((idx, select) => { + const $select = $(select); + const $options = $select.find('option'); + const values = $.map($options, e => e.value); + + // if switched to "private", limit visibility options + if (newProjectVisibility === PROJECT_VISIBILITY_PRIVATE) { + if ($select.val() !== values[0] && $select.val() !== values[1]) { + $select.val(values[1]).trigger('change'); + highlightChanges($select); + } + $options.slice(2).disable(); + } - ProjectNew.prototype.initVisibilitySelect = function() { - const visibilityContainer = document.querySelector('.js-visibility-select'); - if (!visibilityContainer) return; - const visibilitySelect = new VisibilitySelect(visibilityContainer); - visibilitySelect.init(); - - const $visibilitySelect = $(visibilityContainer).find('select'); - let projectVisibility = $visibilitySelect.val(); - const PROJECT_VISIBILITY_PRIVATE = '0'; - - $visibilitySelect.on('change', () => { - const newProjectVisibility = $visibilitySelect.val(); - - if (projectVisibility !== newProjectVisibility) { - this.$projectSelects.each((idx, select) => { - const $select = $(select); - const $options = $select.find('option'); - const values = $.map($options, e => e.value); - - // if switched to "private", limit visibility options - if (newProjectVisibility === PROJECT_VISIBILITY_PRIVATE) { - if ($select.val() !== values[0] && $select.val() !== values[1]) { - $select.val(values[1]).trigger('change'); - highlightChanges($select); - } - $options.slice(2).disable(); - } - - // if switched from "private", increase visibility for non-disabled options - if (projectVisibility === PROJECT_VISIBILITY_PRIVATE) { - $options.enable(); - if ($select.val() !== values[0] && $select.val() !== values[values.length - 1]) { - $select.val(values[values.length - 1]).trigger('change'); - highlightChanges($select); - } - } - }); - - projectVisibility = newProjectVisibility; + // if switched from "private", increase visibility for non-disabled options + if (projectVisibility === PROJECT_VISIBILITY_PRIVATE) { + $options.enable(); + if ($select.val() !== values[0] && $select.val() !== values[values.length - 1]) { + $select.val(values[values.length - 1]).trigger('change'); + highlightChanges($select); + } } }); - }; - ProjectNew.prototype.toggleSettings = function() { - var self = this; + projectVisibility = newProjectVisibility; + } + }); +}; + +ProjectNew.prototype.toggleSettings = function() { + var self = this; + + this.$selects.each(function () { + var $select = $(this); + var className = $select.data('field') + .replace(/_/g, '-') + .replace('access-level', 'feature'); + self._showOrHide($select, '.' + className); + }); +}; + +ProjectNew.prototype.toggleSettingsOnclick = function() { + this.$selects.on('change', this.toggleSettings); +}; + +ProjectNew.prototype._showOrHide = function(checkElement, container) { + var $container = $(container); + + if ($(checkElement).val() !== '0') { + return $container.show(); + } else { + return $container.hide(); + } +}; + +ProjectNew.prototype.toggleRepoVisibility = function () { + var $repoAccessLevel = $('.js-repo-access-level select'); + var $lfsEnabledOption = $('.js-lfs-enabled select'); + var containerRegistry = document.querySelectorAll('.js-container-registry')[0]; + var containerRegistryCheckbox = document.getElementById('project_container_registry_enabled'); + var prevSelectedVal = parseInt($repoAccessLevel.val(), 10); + + this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']") + .nextAll() + .hide(); + + $repoAccessLevel.off('change') + .on('change', function () { + var selectedVal = parseInt($repoAccessLevel.val(), 10); + + this.$repoSelects.each(function () { + var $this = $(this); + var repoSelectVal = parseInt($this.val(), 10); + + $this.find('option').enable(); + + if (selectedVal < repoSelectVal || repoSelectVal === prevSelectedVal) { + $this.val(selectedVal).trigger('change'); + highlightChanges($this); + } - this.$selects.each(function () { - var $select = $(this); - var className = $select.data('field') - .replace(/_/g, '-') - .replace('access-level', 'feature'); - self._showOrHide($select, '.' + className); + $this.find("option[value='" + selectedVal + "']").nextAll().disable(); }); - }; - - ProjectNew.prototype.toggleSettingsOnclick = function() { - this.$selects.on('change', this.toggleSettings); - }; - ProjectNew.prototype._showOrHide = function(checkElement, container) { - var $container = $(container); + if (selectedVal) { + this.$repoSelects.removeClass('disabled'); - if ($(checkElement).val() !== '0') { - return $container.show(); + if ($lfsEnabledOption.length) { + $lfsEnabledOption.removeClass('disabled'); + highlightChanges($lfsEnabledOption); + } + if (containerRegistry) { + containerRegistry.style.display = ''; + } } else { - return $container.hide(); - } - }; + this.$repoSelects.addClass('disabled'); - ProjectNew.prototype.toggleRepoVisibility = function () { - var $repoAccessLevel = $('.js-repo-access-level select'); - var $lfsEnabledOption = $('.js-lfs-enabled select'); - var containerRegistry = document.querySelectorAll('.js-container-registry')[0]; - var containerRegistryCheckbox = document.getElementById('project_container_registry_enabled'); - var prevSelectedVal = parseInt($repoAccessLevel.val(), 10); - - this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']") - .nextAll() - .hide(); - - $repoAccessLevel.off('change') - .on('change', function () { - var selectedVal = parseInt($repoAccessLevel.val(), 10); - - this.$repoSelects.each(function () { - var $this = $(this); - var repoSelectVal = parseInt($this.val(), 10); - - $this.find('option').enable(); - - if (selectedVal < repoSelectVal || repoSelectVal === prevSelectedVal) { - $this.val(selectedVal).trigger('change'); - highlightChanges($this); - } - - $this.find("option[value='" + selectedVal + "']").nextAll().disable(); - }); - - if (selectedVal) { - this.$repoSelects.removeClass('disabled'); - - if ($lfsEnabledOption.length) { - $lfsEnabledOption.removeClass('disabled'); - highlightChanges($lfsEnabledOption); - } - if (containerRegistry) { - containerRegistry.style.display = ''; - } - } else { - this.$repoSelects.addClass('disabled'); - - if ($lfsEnabledOption.length) { - $lfsEnabledOption.val('false').addClass('disabled'); - highlightChanges($lfsEnabledOption); - } - if (containerRegistry) { - containerRegistry.style.display = 'none'; - containerRegistryCheckbox.checked = false; - } - } + if ($lfsEnabledOption.length) { + $lfsEnabledOption.val('false').addClass('disabled'); + highlightChanges($lfsEnabledOption); + } + if (containerRegistry) { + containerRegistry.style.display = 'none'; + containerRegistryCheckbox.checked = false; + } + } - prevSelectedVal = selectedVal; - }.bind(this)); - }; + prevSelectedVal = selectedVal; + }.bind(this)); +}; - return ProjectNew; - })(); -}).call(window); +window.ProjectNew = ProjectNew; diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js index 9896b88d487..c46182126f9 100644 --- a/app/assets/javascripts/project_select.js +++ b/app/assets/javascripts/project_select.js @@ -1,111 +1,105 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-var, comma-dangle, object-shorthand, one-var, one-var-declaration-per-line, no-else-return, quotes, max-len */ import Api from './api'; -(function() { - this.ProjectSelect = (function() { - function ProjectSelect() { - $('.js-projects-dropdown-toggle').each(function(i, dropdown) { - var $dropdown; - $dropdown = $(dropdown); - return $dropdown.glDropdown({ - filterable: true, - filterRemote: true, - search: { - fields: ['name_with_namespace'] - }, - data: function(term, callback) { - var finalCallback, projectsCallback; - var orderBy = $dropdown.data('order-by'); - finalCallback = function(projects) { - return callback(projects); +function ProjectSelect() { + $('.js-projects-dropdown-toggle').each(function(i, dropdown) { + var $dropdown; + $dropdown = $(dropdown); + return $dropdown.glDropdown({ + filterable: true, + filterRemote: true, + search: { + fields: ['name_with_namespace'] + }, + data: function(term, callback) { + var finalCallback, projectsCallback; + var orderBy = $dropdown.data('order-by'); + finalCallback = function(projects) { + return callback(projects); + }; + if (this.includeGroups) { + projectsCallback = function(projects) { + var groupsCallback; + groupsCallback = function(groups) { + var data; + data = groups.concat(projects); + return finalCallback(data); }; - if (this.includeGroups) { - projectsCallback = function(projects) { - var groupsCallback; - groupsCallback = function(groups) { - var data; - data = groups.concat(projects); - return finalCallback(data); - }; - return Api.groups(term, {}, groupsCallback); - }; - } else { - projectsCallback = finalCallback; - } - if (this.groupId) { - return Api.groupProjects(this.groupId, term, projectsCallback); - } else { - return Api.projects(term, { order_by: orderBy }, projectsCallback); - } - }, - url: function(project) { - return project.web_url; - }, - text: function(project) { - return project.name_with_namespace; - } - }); - }); - $('.ajax-project-select').each(function(i, select) { - var placeholder; - this.groupId = $(select).data('group-id'); - this.includeGroups = $(select).data('include-groups'); - this.orderBy = $(select).data('order-by') || 'id'; - this.withIssuesEnabled = $(select).data('with-issues-enabled'); - this.withMergeRequestsEnabled = $(select).data('with-merge-requests-enabled'); + return Api.groups(term, {}, groupsCallback); + }; + } else { + projectsCallback = finalCallback; + } + if (this.groupId) { + return Api.groupProjects(this.groupId, term, projectsCallback); + } else { + return Api.projects(term, { order_by: orderBy }, projectsCallback); + } + }, + url: function(project) { + return project.web_url; + }, + text: function(project) { + return project.name_with_namespace; + } + }); + }); + $('.ajax-project-select').each(function(i, select) { + var placeholder; + this.groupId = $(select).data('group-id'); + this.includeGroups = $(select).data('include-groups'); + this.orderBy = $(select).data('order-by') || 'id'; + this.withIssuesEnabled = $(select).data('with-issues-enabled'); + this.withMergeRequestsEnabled = $(select).data('with-merge-requests-enabled'); - placeholder = "Search for project"; + placeholder = "Search for project"; + if (this.includeGroups) { + placeholder += " or group"; + } + return $(select).select2({ + placeholder: placeholder, + minimumInputLength: 0, + query: (query) => { + var finalCallback, projectsCallback; + finalCallback = function(projects) { + var data; + data = { + results: projects + }; + return query.callback(data); + }; if (this.includeGroups) { - placeholder += " or group"; - } - return $(select).select2({ - placeholder: placeholder, - minimumInputLength: 0, - query: (function(_this) { - return function(query) { - var finalCallback, projectsCallback; - finalCallback = function(projects) { - var data; - data = { - results: projects - }; - return query.callback(data); - }; - if (_this.includeGroups) { - projectsCallback = function(projects) { - var groupsCallback; - groupsCallback = function(groups) { - var data; - data = groups.concat(projects); - return finalCallback(data); - }; - return Api.groups(query.term, {}, groupsCallback); - }; - } else { - projectsCallback = finalCallback; - } - if (_this.groupId) { - return Api.groupProjects(_this.groupId, query.term, projectsCallback); - } else { - return Api.projects(query.term, { - order_by: _this.orderBy, - with_issues_enabled: _this.withIssuesEnabled, - with_merge_requests_enabled: _this.withMergeRequestsEnabled - }, projectsCallback); - } + projectsCallback = function(projects) { + var groupsCallback; + groupsCallback = function(groups) { + var data; + data = groups.concat(projects); + return finalCallback(data); }; - })(this), - id: function(project) { - return project.web_url; - }, - text: function(project) { - return project.name_with_namespace || project.name; - }, - dropdownCssClass: "ajax-project-dropdown" - }); - }); - } + return Api.groups(query.term, {}, groupsCallback); + }; + } else { + projectsCallback = finalCallback; + } + if (this.groupId) { + return Api.groupProjects(this.groupId, query.term, projectsCallback); + } else { + return Api.projects(query.term, { + order_by: this.orderBy, + with_issues_enabled: this.withIssuesEnabled, + with_merge_requests_enabled: this.withMergeRequestsEnabled + }, projectsCallback); + } + }, + id: function(project) { + return project.web_url; + }, + text: function(project) { + return project.name_with_namespace || project.name; + }, + dropdownCssClass: "ajax-project-dropdown" + }); + }); +} - return ProjectSelect; - })(); -}).call(window); +window.ProjectSelect = ProjectSelect; diff --git a/app/assets/javascripts/project_show.js b/app/assets/javascripts/project_show.js index 3a51c1f26ac..ff1a923a982 100644 --- a/app/assets/javascripts/project_show.js +++ b/app/assets/javascripts/project_show.js @@ -1,11 +1,6 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife */ -(function() { - this.ProjectShow = (function() { - function ProjectShow() {} - - return ProjectShow; - })(); -}).call(window); - // I kept class for future +function ProjectShow() {} + +window.ProjectShow = ProjectShow; diff --git a/app/assets/javascripts/project_variables.js b/app/assets/javascripts/project_variables.js index 4ee2e49306d..5688ed7ebb3 100644 --- a/app/assets/javascripts/project_variables.js +++ b/app/assets/javascripts/project_variables.js @@ -1,43 +1,41 @@ -(() => { - const HIDDEN_VALUE_TEXT = '******'; +const HIDDEN_VALUE_TEXT = '******'; - class ProjectVariables { - constructor() { - this.$revealBtn = $('.js-btn-toggle-reveal-values'); - this.$revealBtn.on('click', this.toggleRevealState.bind(this)); - } +class ProjectVariables { + constructor() { + this.$revealBtn = $('.js-btn-toggle-reveal-values'); + this.$revealBtn.on('click', this.toggleRevealState.bind(this)); + } - toggleRevealState(e) { - e.preventDefault(); + toggleRevealState(e) { + e.preventDefault(); - const oldStatus = this.$revealBtn.attr('data-status'); - let newStatus = 'hidden'; - let newAction = 'Reveal Values'; + const oldStatus = this.$revealBtn.attr('data-status'); + let newStatus = 'hidden'; + let newAction = 'Reveal Values'; - if (oldStatus === 'hidden') { - newStatus = 'revealed'; - newAction = 'Hide Values'; - } + if (oldStatus === 'hidden') { + newStatus = 'revealed'; + newAction = 'Hide Values'; + } - this.$revealBtn.attr('data-status', newStatus); + this.$revealBtn.attr('data-status', newStatus); - const $variables = $('.variable-value'); + const $variables = $('.variable-value'); - $variables.each((_, variable) => { - const $variable = $(variable); - let newText = HIDDEN_VALUE_TEXT; + $variables.each((_, variable) => { + const $variable = $(variable); + let newText = HIDDEN_VALUE_TEXT; - if (newStatus === 'revealed') { - newText = $variable.attr('data-value'); - } + if (newStatus === 'revealed') { + newText = $variable.attr('data-value'); + } - $variable.text(newText); - }); + $variable.text(newText); + }); - this.$revealBtn.text(newAction); - } + this.$revealBtn.text(newAction); } +} - window.gl = window.gl || {}; - window.gl.ProjectVariables = ProjectVariables; -})(); +window.gl = window.gl || {}; +window.gl.ProjectVariables = ProjectVariables; diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js index 2c3a9cacd38..d57486e2434 100644 --- a/app/assets/javascripts/render_gfm.js +++ b/app/assets/javascripts/render_gfm.js @@ -4,14 +4,12 @@ // // Delegates to syntax highlight and render math // -(function() { - $.fn.renderGFM = function() { - this.find('.js-syntax-highlight').syntaxHighlight(); - this.find('.js-render-math').renderMath(); - return this; - }; +$.fn.renderGFM = function() { + this.find('.js-syntax-highlight').syntaxHighlight(); + this.find('.js-render-math').renderMath(); + return this; +}; - $(document).on('ready load', function() { - return $('body').renderGFM(); - }); -}).call(window); +$(document).on('ready load', function() { + return $('body').renderGFM(); +}); diff --git a/app/assets/javascripts/render_math.js b/app/assets/javascripts/render_math.js index 8b3fee49cb9..1596cbe266f 100644 --- a/app/assets/javascripts/render_math.js +++ b/app/assets/javascripts/render_math.js @@ -8,49 +8,47 @@ // // <code class="js-render-math"></div> // -(function() { - // Only load once - var katexLoaded = false; +// Only load once +var katexLoaded = false; - // Loop over all math elements and render math - var renderWithKaTeX = function (elements) { - elements.each(function () { - var mathNode = $('<span></span>'); - var $this = $(this); +// Loop over all math elements and render math +var renderWithKaTeX = function (elements) { + elements.each(function () { + var mathNode = $('<span></span>'); + var $this = $(this); - var display = $this.attr('data-math-style') === 'display'; - try { - katex.render($this.text(), mathNode.get(0), { displayMode: display }); - mathNode.insertAfter($this); - $this.remove(); - } catch (err) { - // What can we do?? - console.log(err.message); - } - }); - }; - - $.fn.renderMath = function() { - var $this = this; - if ($this.length === 0) return; + var display = $this.attr('data-math-style') === 'display'; + try { + katex.render($this.text(), mathNode.get(0), { displayMode: display }); + mathNode.insertAfter($this); + $this.remove(); + } catch (err) { + // What can we do?? + console.log(err.message); + } + }); +}; - if (katexLoaded) renderWithKaTeX($this); - else { - // Request CSS file so it is in the cache - $.get(gon.katex_css_url, function() { - var css = $('<link>', - { rel: 'stylesheet', - type: 'text/css', - href: gon.katex_css_url, - }); - css.appendTo('head'); +$.fn.renderMath = function() { + var $this = this; + if ($this.length === 0) return; - // Load KaTeX js - $.getScript(gon.katex_js_url, function() { - katexLoaded = true; - renderWithKaTeX($this); // Run KaTeX + if (katexLoaded) renderWithKaTeX($this); + else { + // Request CSS file so it is in the cache + $.get(gon.katex_css_url, function() { + var css = $('<link>', + { rel: 'stylesheet', + type: 'text/css', + href: gon.katex_css_url, }); + css.appendTo('head'); + + // Load KaTeX js + $.getScript(gon.katex_js_url, function() { + katexLoaded = true; + renderWithKaTeX($this); // Run KaTeX }); - } - }; -}).call(window); + }); + } +}; diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index d8f1fe10b26..9b16e4cd613 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -3,218 +3,214 @@ import Cookies from 'js-cookie'; import SidebarHeightManager from './sidebar_height_manager'; -(function() { - this.Sidebar = (function() { - function Sidebar(currentUser) { - this.toggleTodo = this.toggleTodo.bind(this); - this.sidebar = $('aside'); - - this.removeListeners(); - this.addEventListeners(); +function Sidebar(currentUser) { + this.toggleTodo = this.toggleTodo.bind(this); + this.sidebar = $('aside'); + + this.removeListeners(); + this.addEventListeners(); +} + +Sidebar.prototype.removeListeners = function () { + this.sidebar.off('click', '.sidebar-collapsed-icon'); + $('.dropdown').off('hidden.gl.dropdown'); + $('.dropdown').off('loading.gl.dropdown'); + $('.dropdown').off('loaded.gl.dropdown'); + $(document).off('click', '.js-sidebar-toggle'); +}; + +Sidebar.prototype.addEventListeners = function() { + SidebarHeightManager.init(); + const $document = $(document); + + this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked); + $('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden); + $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading); + $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); + + $document.on('click', '.js-sidebar-toggle', function(e, triggered) { + var $allGutterToggleIcons, $this, $thisIcon; + e.preventDefault(); + $this = $(this); + $thisIcon = $this.find('i'); + $allGutterToggleIcons = $('.js-sidebar-toggle i'); + if ($thisIcon.hasClass('fa-angle-double-right')) { + $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); + $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); + } else { + $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); + $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); + $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); } - - Sidebar.prototype.removeListeners = function () { - this.sidebar.off('click', '.sidebar-collapsed-icon'); - $('.dropdown').off('hidden.gl.dropdown'); - $('.dropdown').off('loading.gl.dropdown'); - $('.dropdown').off('loaded.gl.dropdown'); - $(document).off('click', '.js-sidebar-toggle'); - }; - - Sidebar.prototype.addEventListeners = function() { - SidebarHeightManager.init(); - const $document = $(document); - - this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked); - $('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden); - $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading); - $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); - - $document.on('click', '.js-sidebar-toggle', function(e, triggered) { - var $allGutterToggleIcons, $this, $thisIcon; - e.preventDefault(); - $this = $(this); - $thisIcon = $this.find('i'); - $allGutterToggleIcons = $('.js-sidebar-toggle i'); - if ($thisIcon.hasClass('fa-angle-double-right')) { - $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); - $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); - } else { - $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); - $('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); - } - if (!triggered) { - return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); - } - }); - return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); - }; - - Sidebar.prototype.toggleTodo = function(e) { - var $btnText, $this, $todoLoading, ajaxType, url; - $this = $(e.currentTarget); - ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST'; - if ($this.attr('data-delete-path')) { - url = "" + ($this.attr('data-delete-path')); - } else { - url = "" + ($this.data('url')); - } - - $this.tooltip('hide'); - - return $.ajax({ - url: url, - type: ajaxType, - dataType: 'json', - data: { - issuable_id: $this.data('issuable-id'), - issuable_type: $this.data('issuable-type') - }, - beforeSend: (function(_this) { - return function() { - $('.js-issuable-todo').disable() - .addClass('is-loading'); - }; - })(this) - }).done((function(_this) { - return function(data) { - return _this.todoUpdateDone(data); - }; - })(this)); - }; - - Sidebar.prototype.todoUpdateDone = function(data) { - const deletePath = data.delete_path ? data.delete_path : null; - const attrPrefix = deletePath ? 'mark' : 'todo'; - const $todoBtns = $('.js-issuable-todo'); - - $(document).trigger('todo:toggle', data.count); - - $todoBtns.each((i, el) => { - const $el = $(el); - const $elText = $el.find('.js-issuable-todo-inner'); - - $el.removeClass('is-loading') - .enable() - .attr('aria-label', $el.data(`${attrPrefix}-text`)) - .attr('data-delete-path', deletePath) - .attr('title', $el.data(`${attrPrefix}-text`)); - - if ($el.hasClass('has-tooltip')) { - $el.tooltip('fixTitle'); - } - - if ($el.data(`${attrPrefix}-icon`)) { - $elText.html($el.data(`${attrPrefix}-icon`)); - } else { - $elText.text($el.data(`${attrPrefix}-text`)); - } - }); - }; - - Sidebar.prototype.sidebarDropdownLoading = function(e) { - var $loading, $sidebarCollapsedIcon, i, img; - $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); - img = $sidebarCollapsedIcon.find('img'); - i = $sidebarCollapsedIcon.find('i'); - $loading = $('<i class="fa fa-spinner fa-spin"></i>'); - if (img.length) { - img.before($loading); - return img.hide(); - } else if (i.length) { - i.before($loading); - return i.hide(); - } - }; - - Sidebar.prototype.sidebarDropdownLoaded = function(e) { - var $sidebarCollapsedIcon, i, img; - $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); - img = $sidebarCollapsedIcon.find('img'); - $sidebarCollapsedIcon.find('i.fa-spin').remove(); - i = $sidebarCollapsedIcon.find('i'); - if (img.length) { - return img.show(); - } else { - return i.show(); - } - }; - - Sidebar.prototype.sidebarCollapseClicked = function(e) { - var $block, sidebar; - if ($(e.currentTarget).hasClass('dont-change-state')) { - return; - } - sidebar = e.data; - e.preventDefault(); - $block = $(this).closest('.block'); - return sidebar.openDropdown($block); + if (!triggered) { + return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); + } + }); + return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); +}; + +Sidebar.prototype.toggleTodo = function(e) { + var $btnText, $this, $todoLoading, ajaxType, url; + $this = $(e.currentTarget); + ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST'; + if ($this.attr('data-delete-path')) { + url = "" + ($this.attr('data-delete-path')); + } else { + url = "" + ($this.data('url')); + } + + $this.tooltip('hide'); + + return $.ajax({ + url: url, + type: ajaxType, + dataType: 'json', + data: { + issuable_id: $this.data('issuable-id'), + issuable_type: $this.data('issuable-type') + }, + beforeSend: (function(_this) { + return function() { + $('.js-issuable-todo').disable() + .addClass('is-loading'); + }; + })(this) + }).done((function(_this) { + return function(data) { + return _this.todoUpdateDone(data); }; + })(this)); +}; - Sidebar.prototype.openDropdown = function(blockOrName) { - var $block; - $block = _.isString(blockOrName) ? this.getBlock(blockOrName) : blockOrName; - $block.find('.edit-link').trigger('click'); - if (!this.isOpen()) { - this.setCollapseAfterUpdate($block); - return this.toggleSidebar('open'); - } - }; +Sidebar.prototype.todoUpdateDone = function(data) { + const deletePath = data.delete_path ? data.delete_path : null; + const attrPrefix = deletePath ? 'mark' : 'todo'; + const $todoBtns = $('.js-issuable-todo'); - Sidebar.prototype.setCollapseAfterUpdate = function($block) { - $block.addClass('collapse-after-update'); - return $('.page-with-sidebar').addClass('with-overlay'); - }; + $(document).trigger('todo:toggle', data.count); - Sidebar.prototype.onSidebarDropdownHidden = function(e) { - var $block, sidebar; - sidebar = e.data; - e.preventDefault(); - $block = $(this).closest('.block'); - return sidebar.sidebarDropdownHidden($block); - }; + $todoBtns.each((i, el) => { + const $el = $(el); + const $elText = $el.find('.js-issuable-todo-inner'); - Sidebar.prototype.sidebarDropdownHidden = function($block) { - if ($block.hasClass('collapse-after-update')) { - $block.removeClass('collapse-after-update'); - $('.page-with-sidebar').removeClass('with-overlay'); - return this.toggleSidebar('hide'); - } - }; + $el.removeClass('is-loading') + .enable() + .attr('aria-label', $el.data(`${attrPrefix}-text`)) + .attr('data-delete-path', deletePath) + .attr('title', $el.data(`${attrPrefix}-text`)); - Sidebar.prototype.triggerOpenSidebar = function() { - return this.sidebar.find('.js-sidebar-toggle').trigger('click'); - }; + if ($el.hasClass('has-tooltip')) { + $el.tooltip('fixTitle'); + } - Sidebar.prototype.toggleSidebar = function(action) { - if (action == null) { - action = 'toggle'; - } - if (action === 'toggle') { - this.triggerOpenSidebar(); - } - if (action === 'open') { - if (!this.isOpen()) { - this.triggerOpenSidebar(); - } - } - if (action === 'hide') { - if (this.isOpen()) { - return this.triggerOpenSidebar(); - } - } - }; + if ($el.data(`${attrPrefix}-icon`)) { + $elText.html($el.data(`${attrPrefix}-icon`)); + } else { + $elText.text($el.data(`${attrPrefix}-text`)); + } + }); +}; + +Sidebar.prototype.sidebarDropdownLoading = function(e) { + var $loading, $sidebarCollapsedIcon, i, img; + $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); + img = $sidebarCollapsedIcon.find('img'); + i = $sidebarCollapsedIcon.find('i'); + $loading = $('<i class="fa fa-spinner fa-spin"></i>'); + if (img.length) { + img.before($loading); + return img.hide(); + } else if (i.length) { + i.before($loading); + return i.hide(); + } +}; + +Sidebar.prototype.sidebarDropdownLoaded = function(e) { + var $sidebarCollapsedIcon, i, img; + $sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); + img = $sidebarCollapsedIcon.find('img'); + $sidebarCollapsedIcon.find('i.fa-spin').remove(); + i = $sidebarCollapsedIcon.find('i'); + if (img.length) { + return img.show(); + } else { + return i.show(); + } +}; + +Sidebar.prototype.sidebarCollapseClicked = function(e) { + var $block, sidebar; + if ($(e.currentTarget).hasClass('dont-change-state')) { + return; + } + sidebar = e.data; + e.preventDefault(); + $block = $(this).closest('.block'); + return sidebar.openDropdown($block); +}; + +Sidebar.prototype.openDropdown = function(blockOrName) { + var $block; + $block = _.isString(blockOrName) ? this.getBlock(blockOrName) : blockOrName; + $block.find('.edit-link').trigger('click'); + if (!this.isOpen()) { + this.setCollapseAfterUpdate($block); + return this.toggleSidebar('open'); + } +}; + +Sidebar.prototype.setCollapseAfterUpdate = function($block) { + $block.addClass('collapse-after-update'); + return $('.page-with-sidebar').addClass('with-overlay'); +}; + +Sidebar.prototype.onSidebarDropdownHidden = function(e) { + var $block, sidebar; + sidebar = e.data; + e.preventDefault(); + $block = $(this).closest('.block'); + return sidebar.sidebarDropdownHidden($block); +}; + +Sidebar.prototype.sidebarDropdownHidden = function($block) { + if ($block.hasClass('collapse-after-update')) { + $block.removeClass('collapse-after-update'); + $('.page-with-sidebar').removeClass('with-overlay'); + return this.toggleSidebar('hide'); + } +}; + +Sidebar.prototype.triggerOpenSidebar = function() { + return this.sidebar.find('.js-sidebar-toggle').trigger('click'); +}; + +Sidebar.prototype.toggleSidebar = function(action) { + if (action == null) { + action = 'toggle'; + } + if (action === 'toggle') { + this.triggerOpenSidebar(); + } + if (action === 'open') { + if (!this.isOpen()) { + this.triggerOpenSidebar(); + } + } + if (action === 'hide') { + if (this.isOpen()) { + return this.triggerOpenSidebar(); + } + } +}; - Sidebar.prototype.isOpen = function() { - return this.sidebar.is('.right-sidebar-expanded'); - }; +Sidebar.prototype.isOpen = function() { + return this.sidebar.is('.right-sidebar-expanded'); +}; - Sidebar.prototype.getBlock = function(name) { - return this.sidebar.find(".block." + name); - }; +Sidebar.prototype.getBlock = function(name) { + return this.sidebar.find(".block." + name); +}; - return Sidebar; - })(); -}).call(window); +window.Sidebar = Sidebar; diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js index 05caf177aec..15b7add82c8 100644 --- a/app/assets/javascripts/search.js +++ b/app/assets/javascripts/search.js @@ -2,117 +2,113 @@ /* global Flash */ import Api from './api'; -(function() { - this.Search = (function() { - function Search() { - var $groupDropdown, $projectDropdown; - $groupDropdown = $('.js-search-group-dropdown'); - $projectDropdown = $('.js-search-project-dropdown'); - this.groupId = $groupDropdown.data('group-id'); - this.eventListeners(); - $groupDropdown.glDropdown({ - selectable: true, - filterable: true, - fieldName: 'group_id', - search: { - fields: ['full_name'] - }, - data: function(term, callback) { - return Api.groups(term, {}, function(data) { - data.unshift({ - full_name: 'Any' - }); - data.splice(1, 0, 'divider'); - return callback(data); - }); - }, - id: function(obj) { - return obj.id; - }, - text: function(obj) { - return obj.full_name; - }, - toggleLabel: function(obj) { - return ($groupDropdown.data('default-label')) + " " + obj.full_name; - }, - clicked: (function(_this) { - return function() { - return _this.submitSearch(); - }; - })(this) +function Search() { + var $groupDropdown, $projectDropdown; + $groupDropdown = $('.js-search-group-dropdown'); + $projectDropdown = $('.js-search-project-dropdown'); + this.groupId = $groupDropdown.data('group-id'); + this.eventListeners(); + $groupDropdown.glDropdown({ + selectable: true, + filterable: true, + fieldName: 'group_id', + search: { + fields: ['full_name'] + }, + data: function(term, callback) { + return Api.groups(term, {}, function(data) { + data.unshift({ + full_name: 'Any' + }); + data.splice(1, 0, 'divider'); + return callback(data); }); - $projectDropdown.glDropdown({ - selectable: true, - filterable: true, - fieldName: 'project_id', - search: { - fields: ['name'] - }, - data: (term, callback) => { - this.getProjectsData(term) - .then((data) => { - data.unshift({ - name_with_namespace: 'Any' - }); - data.splice(1, 0, 'divider'); + }, + id: function(obj) { + return obj.id; + }, + text: function(obj) { + return obj.full_name; + }, + toggleLabel: function(obj) { + return ($groupDropdown.data('default-label')) + " " + obj.full_name; + }, + clicked: (function(_this) { + return function() { + return _this.submitSearch(); + }; + })(this) + }); + $projectDropdown.glDropdown({ + selectable: true, + filterable: true, + fieldName: 'project_id', + search: { + fields: ['name'] + }, + data: (term, callback) => { + this.getProjectsData(term) + .then((data) => { + data.unshift({ + name_with_namespace: 'Any' + }); + data.splice(1, 0, 'divider'); - return data; - }) - .then(data => callback(data)) - .catch(() => new Flash('Error fetching projects')); - }, - id: function(obj) { - return obj.id; - }, - text: function(obj) { - return obj.name_with_namespace; - }, - toggleLabel: function(obj) { - return ($projectDropdown.data('default-label')) + " " + obj.name_with_namespace; - }, - clicked: (function(_this) { - return function() { - return _this.submitSearch(); - }; - })(this) - }); - } + return data; + }) + .then(data => callback(data)) + .catch(() => new Flash('Error fetching projects')); + }, + id: function(obj) { + return obj.id; + }, + text: function(obj) { + return obj.name_with_namespace; + }, + toggleLabel: function(obj) { + return ($projectDropdown.data('default-label')) + " " + obj.name_with_namespace; + }, + clicked: (function(_this) { + return function() { + return _this.submitSearch(); + }; + })(this) + }); +} - Search.prototype.eventListeners = function() { - $(document).off('keyup', '.js-search-input').on('keyup', '.js-search-input', this.searchKeyUp); - return $(document).off('click', '.js-search-clear').on('click', '.js-search-clear', this.clearSearchField); - }; +Search.prototype.eventListeners = function() { + $(document).off('keyup', '.js-search-input').on('keyup', '.js-search-input', this.searchKeyUp); + return $(document).off('click', '.js-search-clear').on('click', '.js-search-clear', this.clearSearchField); +}; - Search.prototype.submitSearch = function() { - return $('.js-search-form').submit(); - }; +Search.prototype.submitSearch = function() { + return $('.js-search-form').submit(); +}; - Search.prototype.searchKeyUp = function() { - var $input; - $input = $(this); - if ($input.val() === '') { - return $('.js-search-clear').addClass('hidden'); - } else { - return $('.js-search-clear').removeClass('hidden'); - } - }; +Search.prototype.searchKeyUp = function() { + var $input; + $input = $(this); + if ($input.val() === '') { + return $('.js-search-clear').addClass('hidden'); + } else { + return $('.js-search-clear').removeClass('hidden'); + } +}; - Search.prototype.clearSearchField = function() { - return $('.js-search-input').val('').trigger('keyup').focus(); - }; +Search.prototype.clearSearchField = function() { + return $('.js-search-input').val('').trigger('keyup').focus(); +}; - Search.prototype.getProjectsData = function(term) { - return new Promise((resolve) => { - if (this.groupId) { - Api.groupProjects(this.groupId, term, resolve); - } else { - Api.projects(term, { - order_by: 'id', - }, resolve); - } - }); - }; +Search.prototype.getProjectsData = function(term) { + return new Promise((resolve) => { + if (this.groupId) { + Api.groupProjects(this.groupId, term, resolve); + } else { + Api.projects(term, { + order_by: 'id', + }, resolve); + } + }); +}; - return Search; - })(); -}).call(window); +window.Search = Search; diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 6fd5345a0a6..68966c400b3 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -1,432 +1,427 @@ /* eslint-disable comma-dangle, no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, no-cond-assign, consistent-return, object-shorthand, prefer-arrow-callback, func-names, space-before-function-paren, prefer-template, quotes, class-methods-use-this, no-unused-expressions, no-sequences, wrap-iife, no-lonely-if, no-else-return, no-param-reassign, vars-on-top, max-len */ - -((global) => { - const KEYCODE = { - ESCAPE: 27, - BACKSPACE: 8, - ENTER: 13, - UP: 38, - DOWN: 40 - }; - - class SearchAutocomplete { - constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) { - this.bindEventContext(); - this.wrap = wrap || $('.search'); - this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts'); - this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path'); - this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || ''); - this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || ''); - this.dropdown = this.wrap.find('.dropdown'); - this.dropdownContent = this.dropdown.find('.dropdown-content'); - this.locationBadgeEl = this.getElement('.location-badge'); - this.scopeInputEl = this.getElement('#scope'); - this.searchInput = this.getElement('.search-input'); - this.projectInputEl = this.getElement('#search_project_id'); - this.groupInputEl = this.getElement('#group_id'); - this.searchCodeInputEl = this.getElement('#search_code'); - this.repositoryInputEl = this.getElement('#repository_ref'); - this.clearInput = this.getElement('.js-clear-input'); - this.saveOriginalState(); - // Only when user is logged in - if (gon.current_user_id) { - this.createAutocomplete(); - } - this.searchInput.addClass('disabled'); - this.saveTextLength(); - this.bindEvents(); +const global = window.gl || (window.gl = {}); + +const KEYCODE = { + ESCAPE: 27, + BACKSPACE: 8, + ENTER: 13, + UP: 38, + DOWN: 40 +}; + +class SearchAutocomplete { + constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) { + this.bindEventContext(); + this.wrap = wrap || $('.search'); + this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts'); + this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path'); + this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || ''); + this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || ''); + this.dropdown = this.wrap.find('.dropdown'); + this.dropdownContent = this.dropdown.find('.dropdown-content'); + this.locationBadgeEl = this.getElement('.location-badge'); + this.scopeInputEl = this.getElement('#scope'); + this.searchInput = this.getElement('.search-input'); + this.projectInputEl = this.getElement('#search_project_id'); + this.groupInputEl = this.getElement('#group_id'); + this.searchCodeInputEl = this.getElement('#search_code'); + this.repositoryInputEl = this.getElement('#repository_ref'); + this.clearInput = this.getElement('.js-clear-input'); + this.saveOriginalState(); + // Only when user is logged in + if (gon.current_user_id) { + this.createAutocomplete(); } + this.searchInput.addClass('disabled'); + this.saveTextLength(); + this.bindEvents(); + } - // Finds an element inside wrapper element - bindEventContext() { - this.onSearchInputBlur = this.onSearchInputBlur.bind(this); - this.onClearInputClick = this.onClearInputClick.bind(this); - this.onSearchInputFocus = this.onSearchInputFocus.bind(this); - this.onSearchInputClick = this.onSearchInputClick.bind(this); - this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this); - this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this); - } - getElement(selector) { - return this.wrap.find(selector); - } + // Finds an element inside wrapper element + bindEventContext() { + this.onSearchInputBlur = this.onSearchInputBlur.bind(this); + this.onClearInputClick = this.onClearInputClick.bind(this); + this.onSearchInputFocus = this.onSearchInputFocus.bind(this); + this.onSearchInputClick = this.onSearchInputClick.bind(this); + this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this); + this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this); + } + getElement(selector) { + return this.wrap.find(selector); + } - saveOriginalState() { - return this.originalState = this.serializeState(); - } + saveOriginalState() { + return this.originalState = this.serializeState(); + } - saveTextLength() { - return this.lastTextLength = this.searchInput.val().length; - } + saveTextLength() { + return this.lastTextLength = this.searchInput.val().length; + } - createAutocomplete() { - return this.searchInput.glDropdown({ - filterInputBlur: false, - filterable: true, - filterRemote: true, - highlight: true, - enterCallback: false, - filterInput: 'input#search', - search: { - fields: ['text'] - }, - id: this.getSearchText, - data: this.getData.bind(this), - selectable: true, - clicked: this.onClick.bind(this) - }); - } + createAutocomplete() { + return this.searchInput.glDropdown({ + filterInputBlur: false, + filterable: true, + filterRemote: true, + highlight: true, + enterCallback: false, + filterInput: 'input#search', + search: { + fields: ['text'] + }, + id: this.getSearchText, + data: this.getData.bind(this), + selectable: true, + clicked: this.onClick.bind(this) + }); + } - getSearchText(selectedObject, el) { - return selectedObject.id ? selectedObject.text : ''; - } + getSearchText(selectedObject, el) { + return selectedObject.id ? selectedObject.text : ''; + } - getData(term, callback) { - var _this, contents, jqXHR; - _this = this; - if (!term) { - if (contents = this.getCategoryContents()) { - this.searchInput.data('glDropdown').filter.options.callback(contents); - this.enableAutocomplete(); - } - return; + getData(term, callback) { + var _this, contents, jqXHR; + _this = this; + if (!term) { + if (contents = this.getCategoryContents()) { + this.searchInput.data('glDropdown').filter.options.callback(contents); + this.enableAutocomplete(); } - // Prevent multiple ajax calls - if (this.loadingSuggestions) { + return; + } + // Prevent multiple ajax calls + if (this.loadingSuggestions) { + return; + } + this.loadingSuggestions = true; + return jqXHR = $.get(this.autocompletePath, { + project_id: this.projectId, + project_ref: this.projectRef, + term: term + }, function(response) { + var data, firstCategory, i, lastCategory, len, suggestion; + // Hide dropdown menu if no suggestions returns + if (!response.length) { + _this.disableAutocomplete(); return; } - this.loadingSuggestions = true; - return jqXHR = $.get(this.autocompletePath, { - project_id: this.projectId, - project_ref: this.projectRef, - term: term - }, function(response) { - var data, firstCategory, i, lastCategory, len, suggestion; - // Hide dropdown menu if no suggestions returns - if (!response.length) { - _this.disableAutocomplete(); - return; - } - data = []; - // List results - firstCategory = true; - for (i = 0, len = response.length; i < len; i += 1) { - suggestion = response[i]; - // Add group header before list each group - if (lastCategory !== suggestion.category) { - if (!firstCategory) { - data.push('separator'); - } - if (firstCategory) { - firstCategory = false; - } - data.push({ - header: suggestion.category - }); - lastCategory = suggestion.category; + data = []; + // List results + firstCategory = true; + for (i = 0, len = response.length; i < len; i += 1) { + suggestion = response[i]; + // Add group header before list each group + if (lastCategory !== suggestion.category) { + if (!firstCategory) { + data.push('separator'); + } + if (firstCategory) { + firstCategory = false; } data.push({ - id: (suggestion.category.toLowerCase()) + "-" + suggestion.id, - category: suggestion.category, - text: suggestion.label, - url: suggestion.url - }); - } - // Add option to proceed with the search - if (data.length) { - data.push('separator'); - data.push({ - text: "Result name contains \"" + term + "\"", - url: "/search?search=" + term + "&project_id=" + (_this.projectInputEl.val()) + "&group_id=" + (_this.groupInputEl.val()) + header: suggestion.category }); + lastCategory = suggestion.category; } - return callback(data); - }).always(function() { - return _this.loadingSuggestions = false; - }); - } - - getCategoryContents() { - var dashboardOptions, groupOptions, issuesPath, items, mrPath, name, options, projectOptions, userId, userName, utils; - userId = gon.current_user_id; - userName = gon.current_username; - utils = gl.utils, projectOptions = gl.projectOptions, groupOptions = gl.groupOptions, dashboardOptions = gl.dashboardOptions; - if (utils.isInGroupsPage() && groupOptions) { - options = groupOptions[utils.getGroupSlug()]; - } else if (utils.isInProjectPage() && projectOptions) { - options = projectOptions[utils.getProjectSlug()]; - } else if (dashboardOptions) { - options = dashboardOptions; + data.push({ + id: (suggestion.category.toLowerCase()) + "-" + suggestion.id, + category: suggestion.category, + text: suggestion.label, + url: suggestion.url + }); } - issuesPath = options.issuesPath, mrPath = options.mrPath, name = options.name; - items = [ - { - header: "" + name - }, { - text: 'Issues assigned to me', - url: issuesPath + "/?assignee_username=" + userName - }, { - text: "Issues I've created", - url: issuesPath + "/?author_username=" + userName - }, 'separator', { - text: 'Merge requests assigned to me', - url: mrPath + "/?assignee_username=" + userName - }, { - text: "Merge requests I've created", - url: mrPath + "/?author_username=" + userName - } - ]; - if (!name) { - items.splice(0, 1); + // Add option to proceed with the search + if (data.length) { + data.push('separator'); + data.push({ + text: "Result name contains \"" + term + "\"", + url: "/search?search=" + term + "&project_id=" + (_this.projectInputEl.val()) + "&group_id=" + (_this.groupInputEl.val()) + }); } - return items; - } - - serializeState() { - return { - // Search Criteria - search_project_id: this.projectInputEl.val(), - group_id: this.groupInputEl.val(), - search_code: this.searchCodeInputEl.val(), - repository_ref: this.repositoryInputEl.val(), - scope: this.scopeInputEl.val(), - // Location badge - _location: this.locationBadgeEl.text() - }; - } + return callback(data); + }).always(function() { + return _this.loadingSuggestions = false; + }); + } - bindEvents() { - this.searchInput.on('keydown', this.onSearchInputKeyDown); - this.searchInput.on('keyup', this.onSearchInputKeyUp); - this.searchInput.on('click', this.onSearchInputClick); - this.searchInput.on('focus', this.onSearchInputFocus); - this.searchInput.on('blur', this.onSearchInputBlur); - this.clearInput.on('click', this.onClearInputClick); - return this.locationBadgeEl.on('click', (function(_this) { - return function() { - return _this.searchInput.focus(); - }; - })(this)); + getCategoryContents() { + var dashboardOptions, groupOptions, issuesPath, items, mrPath, name, options, projectOptions, userId, userName, utils; + userId = gon.current_user_id; + userName = gon.current_username; + utils = gl.utils, projectOptions = gl.projectOptions, groupOptions = gl.groupOptions, dashboardOptions = gl.dashboardOptions; + if (utils.isInGroupsPage() && groupOptions) { + options = groupOptions[utils.getGroupSlug()]; + } else if (utils.isInProjectPage() && projectOptions) { + options = projectOptions[utils.getProjectSlug()]; + } else if (dashboardOptions) { + options = dashboardOptions; } - - enableAutocomplete() { - var _this; - // No need to enable anything if user is not logged in - if (!gon.current_user_id) { - return; - } - if (!this.dropdown.hasClass('open')) { - _this = this; - this.loadingSuggestions = false; - this.dropdown.addClass('open').trigger('shown.bs.dropdown'); - return this.searchInput.removeClass('disabled'); + issuesPath = options.issuesPath, mrPath = options.mrPath, name = options.name; + items = [ + { + header: "" + name + }, { + text: 'Issues assigned to me', + url: issuesPath + "/?assignee_username=" + userName + }, { + text: "Issues I've created", + url: issuesPath + "/?author_username=" + userName + }, 'separator', { + text: 'Merge requests assigned to me', + url: mrPath + "/?assignee_username=" + userName + }, { + text: "Merge requests I've created", + url: mrPath + "/?author_username=" + userName } + ]; + if (!name) { + items.splice(0, 1); } + return items; + } - // Saves last length of the entered text - onSearchInputKeyDown() { - return this.saveTextLength(); + serializeState() { + return { + // Search Criteria + search_project_id: this.projectInputEl.val(), + group_id: this.groupInputEl.val(), + search_code: this.searchCodeInputEl.val(), + repository_ref: this.repositoryInputEl.val(), + scope: this.scopeInputEl.val(), + // Location badge + _location: this.locationBadgeEl.text() + }; + } + + bindEvents() { + this.searchInput.on('keydown', this.onSearchInputKeyDown); + this.searchInput.on('keyup', this.onSearchInputKeyUp); + this.searchInput.on('click', this.onSearchInputClick); + this.searchInput.on('focus', this.onSearchInputFocus); + this.searchInput.on('blur', this.onSearchInputBlur); + this.clearInput.on('click', this.onClearInputClick); + return this.locationBadgeEl.on('click', () => this.searchInput.focus()); + } + + enableAutocomplete() { + var _this; + // No need to enable anything if user is not logged in + if (!gon.current_user_id) { + return; } + if (!this.dropdown.hasClass('open')) { + _this = this; + this.loadingSuggestions = false; + this.dropdown.addClass('open').trigger('shown.bs.dropdown'); + return this.searchInput.removeClass('disabled'); + } + } - onSearchInputKeyUp(e) { - switch (e.keyCode) { - case KEYCODE.BACKSPACE: - // when trying to remove the location badge - if (this.lastTextLength === 0 && this.badgePresent()) { - this.removeLocationBadge(); - } - // When removing the last character and no badge is present - if (this.lastTextLength === 1) { - this.disableAutocomplete(); - } - // When removing any character from existin value - if (this.lastTextLength > 1) { - this.enableAutocomplete(); - } - break; - case KEYCODE.ESCAPE: - this.restoreOriginalState(); - break; - case KEYCODE.ENTER: + // Saves last length of the entered text + onSearchInputKeyDown() { + return this.saveTextLength(); + } + + onSearchInputKeyUp(e) { + switch (e.keyCode) { + case KEYCODE.BACKSPACE: + // when trying to remove the location badge + if (this.lastTextLength === 0 && this.badgePresent()) { + this.removeLocationBadge(); + } + // When removing the last character and no badge is present + if (this.lastTextLength === 1) { this.disableAutocomplete(); - break; - case KEYCODE.UP: - case KEYCODE.DOWN: - return; - default: - // Handle the case when deleting the input value other than backspace - // e.g. Pressing ctrl + backspace or ctrl + x - if (this.searchInput.val() === '') { - this.disableAutocomplete(); - } else { - // We should display the menu only when input is not empty - if (e.keyCode !== KEYCODE.ENTER) { - this.enableAutocomplete(); - } + } + // When removing any character from existin value + if (this.lastTextLength > 1) { + this.enableAutocomplete(); + } + break; + case KEYCODE.ESCAPE: + this.restoreOriginalState(); + break; + case KEYCODE.ENTER: + this.disableAutocomplete(); + break; + case KEYCODE.UP: + case KEYCODE.DOWN: + return; + default: + // Handle the case when deleting the input value other than backspace + // e.g. Pressing ctrl + backspace or ctrl + x + if (this.searchInput.val() === '') { + this.disableAutocomplete(); + } else { + // We should display the menu only when input is not empty + if (e.keyCode !== KEYCODE.ENTER) { + this.enableAutocomplete(); } - } - this.wrap.toggleClass('has-value', !!e.target.value); + } } + this.wrap.toggleClass('has-value', !!e.target.value); + } - // Avoid falsy value to be returned - onSearchInputClick(e) { - return e.stopImmediatePropagation(); - } + // Avoid falsy value to be returned + onSearchInputClick(e) { + return e.stopImmediatePropagation(); + } - onSearchInputFocus() { - this.isFocused = true; - this.wrap.addClass('search-active'); - if (this.getValue() === '') { - return this.getData(); - } + onSearchInputFocus() { + this.isFocused = true; + this.wrap.addClass('search-active'); + if (this.getValue() === '') { + return this.getData(); } + } - getValue() { - return this.searchInput.val(); - } + getValue() { + return this.searchInput.val(); + } - onClearInputClick(e) { - e.preventDefault(); - return this.searchInput.val('').focus(); - } + onClearInputClick(e) { + e.preventDefault(); + return this.searchInput.val('').focus(); + } - onSearchInputBlur(e) { - this.isFocused = false; - this.wrap.removeClass('search-active'); - // If input is blank then restore state - if (this.searchInput.val() === '') { - return this.restoreOriginalState(); - } + onSearchInputBlur(e) { + this.isFocused = false; + this.wrap.removeClass('search-active'); + // If input is blank then restore state + if (this.searchInput.val() === '') { + return this.restoreOriginalState(); } + } - addLocationBadge(item) { - var badgeText, category, value; - category = item.category != null ? item.category + ": " : ''; - value = item.value != null ? item.value : ''; - badgeText = "" + category + value; - this.locationBadgeEl.text(badgeText).show(); - return this.wrap.addClass('has-location-badge'); - } + addLocationBadge(item) { + var badgeText, category, value; + category = item.category != null ? item.category + ": " : ''; + value = item.value != null ? item.value : ''; + badgeText = "" + category + value; + this.locationBadgeEl.text(badgeText).show(); + return this.wrap.addClass('has-location-badge'); + } - hasLocationBadge() { - return this.wrap.is('.has-location-badge'); - } + hasLocationBadge() { + return this.wrap.is('.has-location-badge'); + } - restoreOriginalState() { - var i, input, inputs, len; - inputs = Object.keys(this.originalState); - for (i = 0, len = inputs.length; i < len; i += 1) { - input = inputs[i]; - this.getElement("#" + input).val(this.originalState[input]); - } - if (this.originalState._location === '') { - return this.locationBadgeEl.hide(); - } else { - return this.addLocationBadge({ - value: this.originalState._location - }); - } + restoreOriginalState() { + var i, input, inputs, len; + inputs = Object.keys(this.originalState); + for (i = 0, len = inputs.length; i < len; i += 1) { + input = inputs[i]; + this.getElement("#" + input).val(this.originalState[input]); } - - badgePresent() { - return this.locationBadgeEl.length; + if (this.originalState._location === '') { + return this.locationBadgeEl.hide(); + } else { + return this.addLocationBadge({ + value: this.originalState._location + }); } + } - resetSearchState() { - var i, input, inputs, len, results; - inputs = Object.keys(this.originalState); - results = []; - for (i = 0, len = inputs.length; i < len; i += 1) { - input = inputs[i]; - // _location isnt a input - if (input === '_location') { - break; - } - results.push(this.getElement("#" + input).val('')); + badgePresent() { + return this.locationBadgeEl.length; + } + + resetSearchState() { + var i, input, inputs, len, results; + inputs = Object.keys(this.originalState); + results = []; + for (i = 0, len = inputs.length; i < len; i += 1) { + input = inputs[i]; + // _location isnt a input + if (input === '_location') { + break; } - return results; + results.push(this.getElement("#" + input).val('')); } + return results; + } - removeLocationBadge() { - this.locationBadgeEl.hide(); - this.resetSearchState(); - this.wrap.removeClass('has-location-badge'); - return this.disableAutocomplete(); - } + removeLocationBadge() { + this.locationBadgeEl.hide(); + this.resetSearchState(); + this.wrap.removeClass('has-location-badge'); + return this.disableAutocomplete(); + } - disableAutocomplete() { - if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) { - this.searchInput.addClass('disabled'); - this.dropdown.removeClass('open').trigger('hidden.bs.dropdown'); - this.restoreMenu(); - } + disableAutocomplete() { + if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) { + this.searchInput.addClass('disabled'); + this.dropdown.removeClass('open').trigger('hidden.bs.dropdown'); + this.restoreMenu(); } + } - restoreMenu() { - var html; - html = "<ul> <li><a class='dropdown-menu-empty-link is-focused'>Loading...</a></li> </ul>"; - return this.dropdownContent.html(html); - } + restoreMenu() { + var html; + html = "<ul> <li><a class='dropdown-menu-empty-link is-focused'>Loading...</a></li> </ul>"; + return this.dropdownContent.html(html); + } - onClick(item, $el, e) { - if (location.pathname.indexOf(item.url) !== -1) { - if (!e.metaKey) e.preventDefault(); - if (!this.badgePresent) { - if (item.category === 'Projects') { - this.projectInputEl.val(item.id); - this.addLocationBadge({ - value: 'This project' - }); - } - if (item.category === 'Groups') { - this.groupInputEl.val(item.id); - this.addLocationBadge({ - value: 'This group' - }); - } + onClick(item, $el, e) { + if (location.pathname.indexOf(item.url) !== -1) { + if (!e.metaKey) e.preventDefault(); + if (!this.badgePresent) { + if (item.category === 'Projects') { + this.projectInputEl.val(item.id); + this.addLocationBadge({ + value: 'This project' + }); + } + if (item.category === 'Groups') { + this.groupInputEl.val(item.id); + this.addLocationBadge({ + value: 'This group' + }); } - $el.removeClass('is-active'); - this.disableAutocomplete(); - return this.searchInput.val('').focus(); } + $el.removeClass('is-active'); + this.disableAutocomplete(); + return this.searchInput.val('').focus(); } } +} - global.SearchAutocomplete = SearchAutocomplete; +global.SearchAutocomplete = SearchAutocomplete; - $(function() { - var $projectOptionsDataEl = $('.js-search-project-options'); - var $groupOptionsDataEl = $('.js-search-group-options'); - var $dashboardOptionsDataEl = $('.js-search-dashboard-options'); +$(function() { + var $projectOptionsDataEl = $('.js-search-project-options'); + var $groupOptionsDataEl = $('.js-search-group-options'); + var $dashboardOptionsDataEl = $('.js-search-dashboard-options'); - if ($projectOptionsDataEl.length) { - gl.projectOptions = gl.projectOptions || {}; + if ($projectOptionsDataEl.length) { + gl.projectOptions = gl.projectOptions || {}; - var projectPath = $projectOptionsDataEl.data('project-path'); + var projectPath = $projectOptionsDataEl.data('project-path'); - gl.projectOptions[projectPath] = { - name: $projectOptionsDataEl.data('name'), - issuesPath: $projectOptionsDataEl.data('issues-path'), - mrPath: $projectOptionsDataEl.data('mr-path') - }; - } + gl.projectOptions[projectPath] = { + name: $projectOptionsDataEl.data('name'), + issuesPath: $projectOptionsDataEl.data('issues-path'), + mrPath: $projectOptionsDataEl.data('mr-path') + }; + } - if ($groupOptionsDataEl.length) { - gl.groupOptions = gl.groupOptions || {}; + if ($groupOptionsDataEl.length) { + gl.groupOptions = gl.groupOptions || {}; - var groupPath = $groupOptionsDataEl.data('group-path'); + var groupPath = $groupOptionsDataEl.data('group-path'); - gl.groupOptions[groupPath] = { - name: $groupOptionsDataEl.data('name'), - issuesPath: $groupOptionsDataEl.data('issues-path'), - mrPath: $groupOptionsDataEl.data('mr-path') - }; - } + gl.groupOptions[groupPath] = { + name: $groupOptionsDataEl.data('name'), + issuesPath: $groupOptionsDataEl.data('issues-path'), + mrPath: $groupOptionsDataEl.data('mr-path') + }; + } - if ($dashboardOptionsDataEl.length) { - gl.dashboardOptions = { - issuesPath: $dashboardOptionsDataEl.data('issues-path'), - mrPath: $dashboardOptionsDataEl.data('mr-path') - }; - } - }); -})(window.gl || (window.gl = {})); + if ($dashboardOptionsDataEl.length) { + gl.dashboardOptions = { + issuesPath: $dashboardOptionsDataEl.data('issues-path'), + mrPath: $dashboardOptionsDataEl.data('mr-path') + }; + } +}); diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js index e3daa8cf949..e9575fda369 100644 --- a/app/assets/javascripts/shortcuts.js +++ b/app/assets/javascripts/shortcuts.js @@ -4,139 +4,135 @@ import Cookies from 'js-cookie'; import findAndFollowLink from './shortcuts_dashboard_navigation'; -(function() { - this.Shortcuts = (function() { - function Shortcuts(skipResetBindings) { - this.onToggleHelp = this.onToggleHelp.bind(this); - this.enabledHelp = []; - if (!skipResetBindings) { - Mousetrap.reset(); - } - Mousetrap.bind('?', this.onToggleHelp); - Mousetrap.bind('s', Shortcuts.focusSearch); - Mousetrap.bind('f', (e => this.focusFilter(e))); - Mousetrap.bind('p b', this.onTogglePerfBar); - - const $globalDropdownMenu = $('.global-dropdown-menu'); - const $globalDropdownToggle = $('.global-dropdown-toggle'); - const findFileURL = document.body.dataset.findFile; - - $('.global-dropdown').on('hide.bs.dropdown', () => { - $globalDropdownMenu.removeClass('shortcuts'); - }); +function Shortcuts(skipResetBindings) { + this.onToggleHelp = this.onToggleHelp.bind(this); + this.enabledHelp = []; + if (!skipResetBindings) { + Mousetrap.reset(); + } + Mousetrap.bind('?', this.onToggleHelp); + Mousetrap.bind('s', Shortcuts.focusSearch); + Mousetrap.bind('f', (e => this.focusFilter(e))); + Mousetrap.bind('p b', this.onTogglePerfBar); + + const $globalDropdownMenu = $('.global-dropdown-menu'); + const $globalDropdownToggle = $('.global-dropdown-toggle'); + const findFileURL = document.body.dataset.findFile; + + $('.global-dropdown').on('hide.bs.dropdown', () => { + $globalDropdownMenu.removeClass('shortcuts'); + }); - Mousetrap.bind('n', () => { - $globalDropdownMenu.toggleClass('shortcuts'); - $globalDropdownToggle.trigger('click'); + Mousetrap.bind('n', () => { + $globalDropdownMenu.toggleClass('shortcuts'); + $globalDropdownToggle.trigger('click'); - if (!$globalDropdownMenu.is(':visible')) { - $globalDropdownToggle.blur(); - } - }); - - Mousetrap.bind('shift+t', () => findAndFollowLink('.shortcuts-todos')); - Mousetrap.bind('shift+a', () => findAndFollowLink('.dashboard-shortcuts-activity')); - Mousetrap.bind('shift+i', () => findAndFollowLink('.dashboard-shortcuts-issues')); - Mousetrap.bind('shift+m', () => findAndFollowLink('.dashboard-shortcuts-merge_requests')); - Mousetrap.bind('shift+p', () => findAndFollowLink('.dashboard-shortcuts-projects')); - Mousetrap.bind('shift+g', () => findAndFollowLink('.dashboard-shortcuts-groups')); - Mousetrap.bind('shift+l', () => findAndFollowLink('.dashboard-shortcuts-milestones')); - Mousetrap.bind('shift+s', () => findAndFollowLink('.dashboard-shortcuts-snippets')); - - Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], this.toggleMarkdownPreview); - if (typeof findFileURL !== "undefined" && findFileURL !== null) { - Mousetrap.bind('t', function() { - return gl.utils.visitUrl(findFileURL); - }); - } + if (!$globalDropdownMenu.is(':visible')) { + $globalDropdownToggle.blur(); } - - Shortcuts.prototype.onToggleHelp = function(e) { - e.preventDefault(); - return Shortcuts.toggleHelp(this.enabledHelp); - }; - - Shortcuts.prototype.onTogglePerfBar = function(e) { - e.preventDefault(); - const performanceBarCookieName = 'perf_bar_enabled'; - if (Cookies.get(performanceBarCookieName) === 'true') { - Cookies.remove(performanceBarCookieName, { path: '/' }); - } else { - Cookies.set(performanceBarCookieName, 'true', { path: '/' }); - } - gl.utils.refreshCurrentPage(); - }; - - Shortcuts.prototype.toggleMarkdownPreview = function(e) { - // Check if short-cut was triggered while in Write Mode - const $target = $(e.target); - const $form = $target.closest('form'); - - if ($target.hasClass('js-note-text')) { - $('.js-md-preview-button', $form).focus(); - } - return $(document).triggerHandler('markdown-preview:toggle', [e]); - }; - - Shortcuts.toggleHelp = function(location) { - var $modal; - $modal = $('#modal-shortcuts'); - if ($modal.length) { - $modal.modal('toggle'); - return; - } - return $.ajax({ - url: gon.shortcuts_path, - dataType: 'script', - success: function(e) { - var i, l, len, results; - if (location && location.length > 0) { - results = []; - for (i = 0, len = location.length; i < len; i += 1) { - l = location[i]; - results.push($(l).show()); - } - return results; - } else { - $('.hidden-shortcut').show(); - return $('.js-more-help-button').remove(); - } - } - }); - }; - - Shortcuts.prototype.focusFilter = function(e) { - if (this.filterInput == null) { - this.filterInput = $('input[type=search]', '.nav-controls'); - } - this.filterInput.focus(); - return e.preventDefault(); - }; - - Shortcuts.focusSearch = function(e) { - $('#search').focus(); - return e.preventDefault(); - }; - - return Shortcuts; - })(); - - $(document).on('click.more_help', '.js-more-help-button', function(e) { - $(this).remove(); - $('.hidden-shortcut').show(); - return e.preventDefault(); }); - Mousetrap.stopCallback = (function() { - var defaultStopCallback; - defaultStopCallback = Mousetrap.stopCallback; - return function(e, element, combo) { - // allowed shortcuts if textarea, input, contenteditable are focused - if (['ctrl+shift+p', 'command+shift+p'].indexOf(combo) !== -1) { - return false; + Mousetrap.bind('shift+t', () => findAndFollowLink('.shortcuts-todos')); + Mousetrap.bind('shift+a', () => findAndFollowLink('.dashboard-shortcuts-activity')); + Mousetrap.bind('shift+i', () => findAndFollowLink('.dashboard-shortcuts-issues')); + Mousetrap.bind('shift+m', () => findAndFollowLink('.dashboard-shortcuts-merge_requests')); + Mousetrap.bind('shift+p', () => findAndFollowLink('.dashboard-shortcuts-projects')); + Mousetrap.bind('shift+g', () => findAndFollowLink('.dashboard-shortcuts-groups')); + Mousetrap.bind('shift+l', () => findAndFollowLink('.dashboard-shortcuts-milestones')); + Mousetrap.bind('shift+s', () => findAndFollowLink('.dashboard-shortcuts-snippets')); + + Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], this.toggleMarkdownPreview); + if (typeof findFileURL !== "undefined" && findFileURL !== null) { + Mousetrap.bind('t', function() { + return gl.utils.visitUrl(findFileURL); + }); + } +} + +Shortcuts.prototype.onToggleHelp = function(e) { + e.preventDefault(); + return Shortcuts.toggleHelp(this.enabledHelp); +}; + +Shortcuts.prototype.onTogglePerfBar = function(e) { + e.preventDefault(); + const performanceBarCookieName = 'perf_bar_enabled'; + if (Cookies.get(performanceBarCookieName) === 'true') { + Cookies.remove(performanceBarCookieName, { path: '/' }); + } else { + Cookies.set(performanceBarCookieName, 'true', { path: '/' }); + } + gl.utils.refreshCurrentPage(); +}; + +Shortcuts.prototype.toggleMarkdownPreview = function(e) { + // Check if short-cut was triggered while in Write Mode + const $target = $(e.target); + const $form = $target.closest('form'); + + if ($target.hasClass('js-note-text')) { + $('.js-md-preview-button', $form).focus(); + } + return $(document).triggerHandler('markdown-preview:toggle', [e]); +}; + +Shortcuts.toggleHelp = function(location) { + var $modal; + $modal = $('#modal-shortcuts'); + if ($modal.length) { + $modal.modal('toggle'); + return; + } + return $.ajax({ + url: gon.shortcuts_path, + dataType: 'script', + success: function(e) { + var i, l, len, results; + if (location && location.length > 0) { + results = []; + for (i = 0, len = location.length; i < len; i += 1) { + l = location[i]; + results.push($(l).show()); + } + return results; } else { - return defaultStopCallback.apply(this, arguments); + $('.hidden-shortcut').show(); + return $('.js-more-help-button').remove(); } - }; - })(); -}).call(window); + } + }); +}; + +Shortcuts.prototype.focusFilter = function(e) { + if (this.filterInput == null) { + this.filterInput = $('input[type=search]', '.nav-controls'); + } + this.filterInput.focus(); + return e.preventDefault(); +}; + +Shortcuts.focusSearch = function(e) { + $('#search').focus(); + return e.preventDefault(); +}; + +window.Shortcuts = Shortcuts; + +$(document).on('click.more_help', '.js-more-help-button', function(e) { + $(this).remove(); + $('.hidden-shortcut').show(); + return e.preventDefault(); +}); + +Mousetrap.stopCallback = (function() { + var defaultStopCallback; + defaultStopCallback = Mousetrap.stopCallback; + return function(e, element, combo) { + // allowed shortcuts if textarea, input, contenteditable are focused + if (['ctrl+shift+p', 'command+shift+p'].indexOf(combo) !== -1) { + return false; + } else { + return defaultStopCallback.apply(this, arguments); + } + }; +})(); diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js index b18b6139b35..dd41c070c8d 100644 --- a/app/assets/javascripts/shortcuts_find_file.js +++ b/app/assets/javascripts/shortcuts_find_file.js @@ -4,35 +4,31 @@ import './shortcuts_navigation'; -(function() { - var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; - this.ShortcutsFindFile = (function(superClass) { - extend(ShortcutsFindFile, superClass); +function ShortcutsFindFile(projectFindFile) { + var _oldStopCallback; + this.projectFindFile = projectFindFile; + ShortcutsFindFile.__super__.constructor.call(this); + _oldStopCallback = Mousetrap.stopCallback; - function ShortcutsFindFile(projectFindFile) { - var _oldStopCallback; - this.projectFindFile = projectFindFile; - ShortcutsFindFile.__super__.constructor.call(this); - _oldStopCallback = Mousetrap.stopCallback; - Mousetrap.stopCallback = (function(_this) { - // override to fire shortcuts action when focus in textbox - return function(event, element, combo) { - if (element === _this.projectFindFile.inputElement[0] && (combo === 'up' || combo === 'down' || combo === 'esc' || combo === 'enter')) { - // when press up/down key in textbox, cusor prevent to move to home/end - event.preventDefault(); - return false; - } - return _oldStopCallback(event, element, combo); - }; - })(this); - Mousetrap.bind('up', this.projectFindFile.selectRowUp); - Mousetrap.bind('down', this.projectFindFile.selectRowDown); - Mousetrap.bind('esc', this.projectFindFile.goToTree); - Mousetrap.bind('enter', this.projectFindFile.goToBlob); + // override to fire shortcuts action when focus in textbox + Mousetrap.stopCallback = function(event, element, combo) { + if (element === this.projectFindFile.inputElement[0] && (combo === 'up' || combo === 'down' || combo === 'esc' || combo === 'enter')) { + // when press up/down key in textbox, cusor prevent to move to home/end + event.preventDefault(); + return false; } + return _oldStopCallback(event, element, combo); + }.bind(this); - return ShortcutsFindFile; - })(ShortcutsNavigation); -}).call(window); + Mousetrap.bind('up', this.projectFindFile.selectRowUp); + Mousetrap.bind('down', this.projectFindFile.selectRowDown); + Mousetrap.bind('esc', this.projectFindFile.goToTree); + Mousetrap.bind('enter', this.projectFindFile.goToBlob); +} + +extend(ShortcutsFindFile, ShortcutsNavigation); + +window.ShortcutsFindFile = ShortcutsFindFile; diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js index 51448252c0f..138b9768735 100644 --- a/app/assets/javascripts/shortcuts_issuable.js +++ b/app/assets/javascripts/shortcuts_issuable.js @@ -6,87 +6,81 @@ import 'mousetrap'; import './shortcuts_navigation'; -(function() { - var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; - this.ShortcutsIssuable = (function(superClass) { - extend(ShortcutsIssuable, superClass); +var ShortcutsIssuable = {}; - function ShortcutsIssuable(isMergeRequest) { - ShortcutsIssuable.__super__.constructor.call(this); - Mousetrap.bind('a', this.openSidebarDropdown.bind(this, 'assignee')); - Mousetrap.bind('m', this.openSidebarDropdown.bind(this, 'milestone')); - Mousetrap.bind('r', (function(_this) { - return function() { - _this.replyWithSelectedText(); - return false; - }; - })(this)); - Mousetrap.bind('e', (function(_this) { - return function() { - _this.editIssue(); - return false; - }; - })(this)); - Mousetrap.bind('l', this.openSidebarDropdown.bind(this, 'labels')); - if (isMergeRequest) { - this.enabledHelp.push('.hidden-shortcut.merge_requests'); - } else { - this.enabledHelp.push('.hidden-shortcut.issues'); - } - } +extend(ShortcutsIssuable, ShortcutsNavigation); - ShortcutsIssuable.prototype.replyWithSelectedText = function() { - var quote, documentFragment, el, selected, separator; - var replyField = $('.js-main-target-form #note_note'); +ShortcutsIssuable = function(isMergeRequest) { + ShortcutsIssuable.__super__.constructor.call(this); + Mousetrap.bind('a', this.openSidebarDropdown.bind(this, 'assignee')); + Mousetrap.bind('m', this.openSidebarDropdown.bind(this, 'milestone')); + Mousetrap.bind('r', function() { + this.replyWithSelectedText(); + return false; + }.bind(this)); + Mousetrap.bind('e', function() { + this.editIssue(); + return false; + }.bind(this)); + Mousetrap.bind('l', this.openSidebarDropdown.bind(this, 'labels')); + if (isMergeRequest) { + this.enabledHelp.push('.hidden-shortcut.merge_requests'); + } else { + this.enabledHelp.push('.hidden-shortcut.issues'); + } +}; - documentFragment = window.gl.utils.getSelectedFragment(); - if (!documentFragment) { - replyField.focus(); - return; - } +ShortcutsIssuable.prototype.replyWithSelectedText = function() { + var quote, documentFragment, el, selected, separator; + var replyField = $('.js-main-target-form #note_note'); - el = window.gl.CopyAsGFM.transformGFMSelection(documentFragment.cloneNode(true)); - selected = window.gl.CopyAsGFM.nodeToGFM(el); + documentFragment = window.gl.utils.getSelectedFragment(); + if (!documentFragment) { + replyField.focus(); + return; + } - if (selected.trim() === "") { - return; - } - quote = _.map(selected.split("\n"), function(val) { - return ("> " + val).trim() + "\n"; - }); - // If replyField already has some content, add a newline before our quote - separator = replyField.val().trim() !== "" && "\n\n" || ''; - replyField.val(function(_, current) { - return current + separator + quote.join('') + "\n"; - }); + el = window.gl.CopyAsGFM.transformGFMSelection(documentFragment.cloneNode(true)); + selected = window.gl.CopyAsGFM.nodeToGFM(el); - // Trigger autosave - replyField.trigger('input'); + if (selected.trim() === "") { + return; + } + quote = _.map(selected.split("\n"), function(val) { + return ("> " + val).trim() + "\n"; + }); + // If replyField already has some content, add a newline before our quote + separator = replyField.val().trim() !== "" && "\n\n" || ''; + replyField.val(function(_, current) { + return current + separator + quote.join('') + "\n"; + }); - // Trigger autosize - var event = document.createEvent('Event'); - event.initEvent('autosize:update', true, false); - replyField.get(0).dispatchEvent(event); + // Trigger autosave + replyField.trigger('input'); - // Focus the input field - return replyField.focus(); - }; + // Trigger autosize + var event = document.createEvent('Event'); + event.initEvent('autosize:update', true, false); + replyField.get(0).dispatchEvent(event); - ShortcutsIssuable.prototype.editIssue = function() { - var $editBtn; - $editBtn = $('.issuable-edit'); - // Need to click the element as on issues, editing is inline - // on merge request, editing is on a different page - $editBtn.get(0).click(); - }; + // Focus the input field + return replyField.focus(); +}; - ShortcutsIssuable.prototype.openSidebarDropdown = function(name) { - sidebar.openDropdown(name); - return false; - }; +ShortcutsIssuable.prototype.editIssue = function() { + var $editBtn; + $editBtn = $('.issuable-edit'); + // Need to click the element as on issues, editing is inline + // on merge request, editing is on a different page + $editBtn.get(0).click(); +}; - return ShortcutsIssuable; - })(ShortcutsNavigation); -}).call(window); +ShortcutsIssuable.prototype.openSidebarDropdown = function(name) { + sidebar.openDropdown(name); + return false; +}; + +window.ShortcutsIssuable = ShortcutsIssuable; diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js index 55bae0c08a1..bba292cac2b 100644 --- a/app/assets/javascripts/shortcuts_navigation.js +++ b/app/assets/javascripts/shortcuts_navigation.js @@ -5,32 +5,30 @@ import findAndFollowLink from './shortcuts_dashboard_navigation'; import './shortcuts'; -(function() { - var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; - this.ShortcutsNavigation = (function(superClass) { - extend(ShortcutsNavigation, superClass); +var ShortcutsNavigation = {}; - function ShortcutsNavigation() { - ShortcutsNavigation.__super__.constructor.call(this); - Mousetrap.bind('g p', () => findAndFollowLink('.shortcuts-project')); - Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-project-activity')); - Mousetrap.bind('g f', () => findAndFollowLink('.shortcuts-tree')); - Mousetrap.bind('g c', () => findAndFollowLink('.shortcuts-commits')); - Mousetrap.bind('g j', () => findAndFollowLink('.shortcuts-builds')); - Mousetrap.bind('g n', () => findAndFollowLink('.shortcuts-network')); - Mousetrap.bind('g d', () => findAndFollowLink('.shortcuts-repository-charts')); - Mousetrap.bind('g i', () => findAndFollowLink('.shortcuts-issues')); - Mousetrap.bind('g b', () => findAndFollowLink('.shortcuts-issue-boards')); - Mousetrap.bind('g m', () => findAndFollowLink('.shortcuts-merge_requests')); - Mousetrap.bind('g t', () => findAndFollowLink('.shortcuts-todos')); - Mousetrap.bind('g w', () => findAndFollowLink('.shortcuts-wiki')); - Mousetrap.bind('g s', () => findAndFollowLink('.shortcuts-snippets')); - Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue')); - this.enabledHelp.push('.hidden-shortcut.project'); - } +extend(ShortcutsNavigation, Shortcuts); - return ShortcutsNavigation; - })(Shortcuts); -}).call(window); +ShortcutsNavigation = function() { + ShortcutsNavigation.__super__.constructor.call(this); + Mousetrap.bind('g p', () => findAndFollowLink('.shortcuts-project')); + Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-project-activity')); + Mousetrap.bind('g f', () => findAndFollowLink('.shortcuts-tree')); + Mousetrap.bind('g c', () => findAndFollowLink('.shortcuts-commits')); + Mousetrap.bind('g j', () => findAndFollowLink('.shortcuts-builds')); + Mousetrap.bind('g n', () => findAndFollowLink('.shortcuts-network')); + Mousetrap.bind('g d', () => findAndFollowLink('.shortcuts-repository-charts')); + Mousetrap.bind('g i', () => findAndFollowLink('.shortcuts-issues')); + Mousetrap.bind('g b', () => findAndFollowLink('.shortcuts-issue-boards')); + Mousetrap.bind('g m', () => findAndFollowLink('.shortcuts-merge_requests')); + Mousetrap.bind('g t', () => findAndFollowLink('.shortcuts-todos')); + Mousetrap.bind('g w', () => findAndFollowLink('.shortcuts-wiki')); + Mousetrap.bind('g s', () => findAndFollowLink('.shortcuts-snippets')); + Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue')); + this.enabledHelp.push('.hidden-shortcut.project'); +}; + +window.ShortcutsNavigation = ShortcutsNavigation; diff --git a/app/assets/javascripts/shortcuts_network.js b/app/assets/javascripts/shortcuts_network.js index cc44082efa9..8ff5b86939a 100644 --- a/app/assets/javascripts/shortcuts_network.js +++ b/app/assets/javascripts/shortcuts_network.js @@ -4,25 +4,23 @@ import './shortcuts_navigation'; -(function() { - var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; - this.ShortcutsNetwork = (function(superClass) { - extend(ShortcutsNetwork, superClass); +var ShortcutsNetwork = {}; - function ShortcutsNetwork(graph) { - this.graph = graph; - ShortcutsNetwork.__super__.constructor.call(this); - Mousetrap.bind(['left', 'h'], this.graph.scrollLeft); - Mousetrap.bind(['right', 'l'], this.graph.scrollRight); - Mousetrap.bind(['up', 'k'], this.graph.scrollUp); - Mousetrap.bind(['down', 'j'], this.graph.scrollDown); - Mousetrap.bind(['shift+up', 'shift+k'], this.graph.scrollTop); - Mousetrap.bind(['shift+down', 'shift+j'], this.graph.scrollBottom); - this.enabledHelp.push('.hidden-shortcut.network'); - } +extend(ShortcutsNetwork, ShortcutsNavigation); - return ShortcutsNetwork; - })(ShortcutsNavigation); -}).call(window); +ShortcutsNetwork = function(graph) { + this.graph = graph; + ShortcutsNetwork.__super__.constructor.call(this); + Mousetrap.bind(['left', 'h'], this.graph.scrollLeft); + Mousetrap.bind(['right', 'l'], this.graph.scrollRight); + Mousetrap.bind(['up', 'k'], this.graph.scrollUp); + Mousetrap.bind(['down', 'j'], this.graph.scrollDown); + Mousetrap.bind(['shift+up', 'shift+k'], this.graph.scrollTop); + Mousetrap.bind(['shift+down', 'shift+j'], this.graph.scrollBottom); + this.enabledHelp.push('.hidden-shortcut.network'); +}; + +window.ShortcutsNetwork = ShortcutsNetwork; diff --git a/app/assets/javascripts/templates/issuable_template_selector.js b/app/assets/javascripts/templates/issuable_template_selector.js index 9dd14488f22..4d81402d12c 100644 --- a/app/assets/javascripts/templates/issuable_template_selector.js +++ b/app/assets/javascripts/templates/issuable_template_selector.js @@ -3,58 +3,58 @@ import Api from '../api'; import TemplateSelector from '../blob/template_selector'; -((global) => { - class IssuableTemplateSelector extends TemplateSelector { - constructor(...args) { - super(...args); - this.projectPath = this.dropdown.data('project-path'); - this.namespacePath = this.dropdown.data('namespace-path'); - this.issuableType = this.$dropdownContainer.data('issuable-type'); - this.titleInput = $(`#${this.issuableType}_title`); - - const initialQuery = { - name: this.dropdown.data('selected') - }; - - if (initialQuery.name) this.requestFile(initialQuery); - - $('.reset-template', this.dropdown.parent()).on('click', () => { - this.setInputValueToTemplateContent(); - }); - - $('.no-template', this.dropdown.parent()).on('click', () => { - this.currentTemplate.content = ''; - this.setInputValueToTemplateContent(); - $('.dropdown-toggle-text', this.dropdown).text('Choose a template'); - }); - } +const global = window.gl || (window.gl = {}); + +class IssuableTemplateSelector extends TemplateSelector { + constructor(...args) { + super(...args); + this.projectPath = this.dropdown.data('project-path'); + this.namespacePath = this.dropdown.data('namespace-path'); + this.issuableType = this.$dropdownContainer.data('issuable-type'); + this.titleInput = $(`#${this.issuableType}_title`); + + const initialQuery = { + name: this.dropdown.data('selected') + }; + + if (initialQuery.name) this.requestFile(initialQuery); + + $('.reset-template', this.dropdown.parent()).on('click', () => { + this.setInputValueToTemplateContent(); + }); + + $('.no-template', this.dropdown.parent()).on('click', () => { + this.currentTemplate.content = ''; + this.setInputValueToTemplateContent(); + $('.dropdown-toggle-text', this.dropdown).text('Choose a template'); + }); + } - requestFile(query) { - this.startLoadingSpinner(); - Api.issueTemplate(this.namespacePath, this.projectPath, query.name, this.issuableType, (err, currentTemplate) => { - this.currentTemplate = currentTemplate; - if (err) return; // Error handled by global AJAX error handler - this.stopLoadingSpinner(); - this.setInputValueToTemplateContent(); - }); - return; - } + requestFile(query) { + this.startLoadingSpinner(); + Api.issueTemplate(this.namespacePath, this.projectPath, query.name, this.issuableType, (err, currentTemplate) => { + this.currentTemplate = currentTemplate; + if (err) return; // Error handled by global AJAX error handler + this.stopLoadingSpinner(); + this.setInputValueToTemplateContent(); + }); + return; + } - setInputValueToTemplateContent() { - // `this.setEditorContent` sets the value of the description input field - // to the content of the template selected. - if (this.titleInput.val() === '') { - // If the title has not yet been set, focus the title input and - // skip focusing the description input by setting `true` as the - // `skipFocus` option to `setEditorContent`. - this.setEditorContent(this.currentTemplate, { skipFocus: true }); - this.titleInput.focus(); - } else { - this.setEditorContent(this.currentTemplate, { skipFocus: false }); - } - return; + setInputValueToTemplateContent() { + // `this.setEditorContent` sets the value of the description input field + // to the content of the template selected. + if (this.titleInput.val() === '') { + // If the title has not yet been set, focus the title input and + // skip focusing the description input by setting `true` as the + // `skipFocus` option to `setEditorContent`. + this.setEditorContent(this.currentTemplate, { skipFocus: true }); + this.titleInput.focus(); + } else { + this.setEditorContent(this.currentTemplate, { skipFocus: false }); } + return; } +} - global.IssuableTemplateSelector = IssuableTemplateSelector; -})(window.gl || (window.gl = {})); +global.IssuableTemplateSelector = IssuableTemplateSelector; diff --git a/app/assets/javascripts/templates/issuable_template_selectors.js b/app/assets/javascripts/templates/issuable_template_selectors.js index 97f6d37364d..5eb516c16f0 100644 --- a/app/assets/javascripts/templates/issuable_template_selectors.js +++ b/app/assets/javascripts/templates/issuable_template_selectors.js @@ -1,31 +1,31 @@ /* eslint-disable no-new, comma-dangle, class-methods-use-this, no-param-reassign */ -((global) => { - class IssuableTemplateSelectors { - constructor({ $dropdowns, editor } = {}) { - this.$dropdowns = $dropdowns || $('.js-issuable-selector'); - this.editor = editor || this.initEditor(); +const global = window.gl || (window.gl = {}); - this.$dropdowns.each((i, dropdown) => { - const $dropdown = $(dropdown); - new gl.IssuableTemplateSelector({ - pattern: /(\.md)/, - data: $dropdown.data('data'), - wrapper: $dropdown.closest('.js-issuable-selector-wrap'), - dropdown: $dropdown, - editor: this.editor - }); +class IssuableTemplateSelectors { + constructor({ $dropdowns, editor } = {}) { + this.$dropdowns = $dropdowns || $('.js-issuable-selector'); + this.editor = editor || this.initEditor(); + + this.$dropdowns.each((i, dropdown) => { + const $dropdown = $(dropdown); + new gl.IssuableTemplateSelector({ + pattern: /(\.md)/, + data: $dropdown.data('data'), + wrapper: $dropdown.closest('.js-issuable-selector-wrap'), + dropdown: $dropdown, + editor: this.editor }); - } + }); + } - initEditor() { - const editor = $('.markdown-area'); - // Proxy ace-editor's .setValue to jQuery's .val - editor.setValue = editor.val; - editor.getValue = editor.val; - return editor; - } + initEditor() { + const editor = $('.markdown-area'); + // Proxy ace-editor's .setValue to jQuery's .val + editor.setValue = editor.val; + editor.getValue = editor.val; + return editor; } +} - global.IssuableTemplateSelectors = IssuableTemplateSelectors; -})(window.gl || (window.gl = {})); +global.IssuableTemplateSelectors = IssuableTemplateSelectors; diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js index cd5280948fd..04e943ca581 100644 --- a/app/assets/javascripts/u2f/authenticate.js +++ b/app/assets/javascripts/u2f/authenticate.js @@ -7,110 +7,104 @@ // // State Flow #1: setup -> in_progress -> authenticated -> POST to server // State Flow #2: setup -> in_progress -> error -> setup -(function() { - const global = window.gl || (window.gl = {}); +const global = window.gl || (window.gl = {}); - global.U2FAuthenticate = (function() { - function U2FAuthenticate(container, form, u2fParams, fallbackButton, fallbackUI) { - this.container = container; - this.renderNotSupported = this.renderNotSupported.bind(this); - this.renderAuthenticated = this.renderAuthenticated.bind(this); - this.renderError = this.renderError.bind(this); - this.renderInProgress = this.renderInProgress.bind(this); - this.renderTemplate = this.renderTemplate.bind(this); - this.authenticate = this.authenticate.bind(this); - this.start = this.start.bind(this); - this.appId = u2fParams.app_id; - this.challenge = u2fParams.challenge; - this.form = form; - this.fallbackButton = fallbackButton; - this.fallbackUI = fallbackUI; - if (this.fallbackButton) this.fallbackButton.addEventListener('click', this.switchToFallbackUI.bind(this)); - this.signRequests = u2fParams.sign_requests.map(function(request) { - // The U2F Javascript API v1.1 requires a single challenge, with - // _no challenges per-request_. The U2F Javascript API v1.0 requires a - // challenge per-request, which is done by copying the single challenge - // into every request. - // - // In either case, we don't need the per-request challenges that the server - // has generated, so we can remove them. - // - // Note: The server library fixes this behaviour in (unreleased) version 1.0.0. - // This can be removed once we upgrade. - // https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4 - return _(request).omit('challenge'); - }); - } +function U2FAuthenticate(container, form, u2fParams, fallbackButton, fallbackUI) { + this.container = container; + this.renderNotSupported = this.renderNotSupported.bind(this); + this.renderAuthenticated = this.renderAuthenticated.bind(this); + this.renderError = this.renderError.bind(this); + this.renderInProgress = this.renderInProgress.bind(this); + this.renderTemplate = this.renderTemplate.bind(this); + this.authenticate = this.authenticate.bind(this); + this.start = this.start.bind(this); + this.appId = u2fParams.app_id; + this.challenge = u2fParams.challenge; + this.form = form; + this.fallbackButton = fallbackButton; + this.fallbackUI = fallbackUI; + if (this.fallbackButton) this.fallbackButton.addEventListener('click', this.switchToFallbackUI.bind(this)); + this.signRequests = u2fParams.sign_requests.map(function(request) { + // The U2F Javascript API v1.1 requires a single challenge, with + // _no challenges per-request_. The U2F Javascript API v1.0 requires a + // challenge per-request, which is done by copying the single challenge + // into every request. + // + // In either case, we don't need the per-request challenges that the server + // has generated, so we can remove them. + // + // Note: The server library fixes this behaviour in (unreleased) version 1.0.0. + // This can be removed once we upgrade. + // https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4 + return _(request).omit('challenge'); + }); +} - U2FAuthenticate.prototype.start = function() { - if (U2FUtil.isU2FSupported()) { - return this.renderInProgress(); - } else { - return this.renderNotSupported(); - } - }; +U2FAuthenticate.prototype.start = function() { + if (U2FUtil.isU2FSupported()) { + return this.renderInProgress(); + } else { + return this.renderNotSupported(); + } +}; - U2FAuthenticate.prototype.authenticate = function() { - return u2f.sign(this.appId, this.challenge, this.signRequests, (function(_this) { - return function(response) { - var error; - if (response.errorCode) { - error = new U2FError(response.errorCode, 'authenticate'); - return _this.renderError(error); - } else { - return _this.renderAuthenticated(JSON.stringify(response)); - } - }; - })(this), 10); - }; +U2FAuthenticate.prototype.authenticate = function() { + return u2f.sign(this.appId, this.challenge, this.signRequests, function(response) { + var error; + if (response.errorCode) { + error = new U2FError(response.errorCode, 'authenticate'); + return this.renderError(error); + } else { + return this.renderAuthenticated(JSON.stringify(response)); + } + }.bind(this), 10); +}; - // Rendering # - U2FAuthenticate.prototype.templates = { - "notSupported": "#js-authenticate-u2f-not-supported", - "setup": '#js-authenticate-u2f-setup', - "inProgress": '#js-authenticate-u2f-in-progress', - "error": '#js-authenticate-u2f-error', - "authenticated": '#js-authenticate-u2f-authenticated' - }; +// Rendering # +U2FAuthenticate.prototype.templates = { + "notSupported": "#js-authenticate-u2f-not-supported", + "setup": '#js-authenticate-u2f-setup', + "inProgress": '#js-authenticate-u2f-in-progress', + "error": '#js-authenticate-u2f-error', + "authenticated": '#js-authenticate-u2f-authenticated' +}; - U2FAuthenticate.prototype.renderTemplate = function(name, params) { - var template, templateString; - templateString = $(this.templates[name]).html(); - template = _.template(templateString); - return this.container.html(template(params)); - }; +U2FAuthenticate.prototype.renderTemplate = function(name, params) { + var template, templateString; + templateString = $(this.templates[name]).html(); + template = _.template(templateString); + return this.container.html(template(params)); +}; - U2FAuthenticate.prototype.renderInProgress = function() { - this.renderTemplate('inProgress'); - return this.authenticate(); - }; +U2FAuthenticate.prototype.renderInProgress = function() { + this.renderTemplate('inProgress'); + return this.authenticate(); +}; - U2FAuthenticate.prototype.renderError = function(error) { - this.renderTemplate('error', { - error_message: error.message(), - error_code: error.errorCode - }); - return this.container.find('#js-u2f-try-again').on('click', this.renderInProgress); - }; +U2FAuthenticate.prototype.renderError = function(error) { + this.renderTemplate('error', { + error_message: error.message(), + error_code: error.errorCode + }); + return this.container.find('#js-u2f-try-again').on('click', this.renderInProgress); +}; - U2FAuthenticate.prototype.renderAuthenticated = function(deviceResponse) { - this.renderTemplate('authenticated'); - const container = this.container[0]; - container.querySelector('#js-device-response').value = deviceResponse; - container.querySelector(this.form).submit(); - this.fallbackButton.classList.add('hidden'); - }; +U2FAuthenticate.prototype.renderAuthenticated = function(deviceResponse) { + this.renderTemplate('authenticated'); + const container = this.container[0]; + container.querySelector('#js-device-response').value = deviceResponse; + container.querySelector(this.form).submit(); + this.fallbackButton.classList.add('hidden'); +}; - U2FAuthenticate.prototype.renderNotSupported = function() { - return this.renderTemplate('notSupported'); - }; +U2FAuthenticate.prototype.renderNotSupported = function() { + return this.renderTemplate('notSupported'); +}; - U2FAuthenticate.prototype.switchToFallbackUI = function() { - this.fallbackButton.classList.add('hidden'); - this.container[0].classList.add('hidden'); - this.fallbackUI.classList.remove('hidden'); - }; +U2FAuthenticate.prototype.switchToFallbackUI = function() { + this.fallbackButton.classList.add('hidden'); + this.container[0].classList.add('hidden'); + this.fallbackUI.classList.remove('hidden'); +}; - return U2FAuthenticate; - })(); -})(); +global.U2FAuthenticate = U2FAuthenticate; diff --git a/app/assets/javascripts/u2f/error.js b/app/assets/javascripts/u2f/error.js index 3119b3480c3..f4a436c9f76 100644 --- a/app/assets/javascripts/u2f/error.js +++ b/app/assets/javascripts/u2f/error.js @@ -1,25 +1,21 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-console, quotes, prefer-template, max-len */ /* global u2f */ -(function() { - this.U2FError = (function() { - function U2FError(errorCode, u2fFlowType) { - this.errorCode = errorCode; - this.message = this.message.bind(this); - this.httpsDisabled = window.location.protocol !== 'https:'; - this.u2fFlowType = u2fFlowType; - } +function U2FError(errorCode, u2fFlowType) { + this.errorCode = errorCode; + this.message = this.message.bind(this); + this.httpsDisabled = window.location.protocol !== 'https:'; + this.u2fFlowType = u2fFlowType; +} - U2FError.prototype.message = function() { - if (this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled) { - return 'U2F only works with HTTPS-enabled websites. Contact your administrator for more details.'; - } else if (this.errorCode === u2f.ErrorCodes.DEVICE_INELIGIBLE) { - if (this.u2fFlowType === 'authenticate') return 'This device has not been registered with us.'; - if (this.u2fFlowType === 'register') return 'This device has already been registered with us.'; - } - return "There was a problem communicating with your device."; - }; +U2FError.prototype.message = function() { + if (this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled) { + return 'U2F only works with HTTPS-enabled websites. Contact your administrator for more details.'; + } else if (this.errorCode === u2f.ErrorCodes.DEVICE_INELIGIBLE) { + if (this.u2fFlowType === 'authenticate') return 'This device has not been registered with us.'; + if (this.u2fFlowType === 'register') return 'This device has already been registered with us.'; + } + return "There was a problem communicating with your device."; +}; - return U2FError; - })(); -}).call(window); +window.U2FError = U2FError; diff --git a/app/assets/javascripts/u2f/register.js b/app/assets/javascripts/u2f/register.js index 1234d17b8fd..38395710ad4 100644 --- a/app/assets/javascripts/u2f/register.js +++ b/app/assets/javascripts/u2f/register.js @@ -7,90 +7,84 @@ // // State Flow #1: setup -> in_progress -> registered -> POST to server // State Flow #2: setup -> in_progress -> error -> setup -(function() { - this.U2FRegister = (function() { - function U2FRegister(container, u2fParams) { - this.container = container; - this.renderNotSupported = this.renderNotSupported.bind(this); - this.renderRegistered = this.renderRegistered.bind(this); - this.renderError = this.renderError.bind(this); - this.renderInProgress = this.renderInProgress.bind(this); - this.renderSetup = this.renderSetup.bind(this); - this.renderTemplate = this.renderTemplate.bind(this); - this.register = this.register.bind(this); - this.start = this.start.bind(this); - this.appId = u2fParams.app_id; - this.registerRequests = u2fParams.register_requests; - this.signRequests = u2fParams.sign_requests; - } +function U2FRegister(container, u2fParams) { + this.container = container; + this.renderNotSupported = this.renderNotSupported.bind(this); + this.renderRegistered = this.renderRegistered.bind(this); + this.renderError = this.renderError.bind(this); + this.renderInProgress = this.renderInProgress.bind(this); + this.renderSetup = this.renderSetup.bind(this); + this.renderTemplate = this.renderTemplate.bind(this); + this.register = this.register.bind(this); + this.start = this.start.bind(this); + this.appId = u2fParams.app_id; + this.registerRequests = u2fParams.register_requests; + this.signRequests = u2fParams.sign_requests; +} - U2FRegister.prototype.start = function() { - if (U2FUtil.isU2FSupported()) { - return this.renderSetup(); - } else { - return this.renderNotSupported(); - } - }; +U2FRegister.prototype.start = function() { + if (U2FUtil.isU2FSupported()) { + return this.renderSetup(); + } else { + return this.renderNotSupported(); + } +}; - U2FRegister.prototype.register = function() { - return u2f.register(this.appId, this.registerRequests, this.signRequests, (function(_this) { - return function(response) { - var error; - if (response.errorCode) { - error = new U2FError(response.errorCode, 'register'); - return _this.renderError(error); - } else { - return _this.renderRegistered(JSON.stringify(response)); - } - }; - })(this), 10); - }; +U2FRegister.prototype.register = function() { + return u2f.register(this.appId, this.registerRequests, this.signRequests, (response) => { + var error; + if (response.errorCode) { + error = new U2FError(response.errorCode, 'register'); + return this.renderError(error); + } else { + return this.renderRegistered(JSON.stringify(response)); + } + }, 10); +}; - // Rendering # - U2FRegister.prototype.templates = { - "notSupported": "#js-register-u2f-not-supported", - "setup": '#js-register-u2f-setup', - "inProgress": '#js-register-u2f-in-progress', - "error": '#js-register-u2f-error', - "registered": '#js-register-u2f-registered' - }; +// Rendering # +U2FRegister.prototype.templates = { + "notSupported": "#js-register-u2f-not-supported", + "setup": '#js-register-u2f-setup', + "inProgress": '#js-register-u2f-in-progress', + "error": '#js-register-u2f-error', + "registered": '#js-register-u2f-registered' +}; - U2FRegister.prototype.renderTemplate = function(name, params) { - var template, templateString; - templateString = $(this.templates[name]).html(); - template = _.template(templateString); - return this.container.html(template(params)); - }; +U2FRegister.prototype.renderTemplate = function(name, params) { + var template, templateString; + templateString = $(this.templates[name]).html(); + template = _.template(templateString); + return this.container.html(template(params)); +}; - U2FRegister.prototype.renderSetup = function() { - this.renderTemplate('setup'); - return this.container.find('#js-setup-u2f-device').on('click', this.renderInProgress); - }; +U2FRegister.prototype.renderSetup = function() { + this.renderTemplate('setup'); + return this.container.find('#js-setup-u2f-device').on('click', this.renderInProgress); +}; - U2FRegister.prototype.renderInProgress = function() { - this.renderTemplate('inProgress'); - return this.register(); - }; +U2FRegister.prototype.renderInProgress = function() { + this.renderTemplate('inProgress'); + return this.register(); +}; - U2FRegister.prototype.renderError = function(error) { - this.renderTemplate('error', { - error_message: error.message(), - error_code: error.errorCode - }); - return this.container.find('#js-u2f-try-again').on('click', this.renderSetup); - }; +U2FRegister.prototype.renderError = function(error) { + this.renderTemplate('error', { + error_message: error.message(), + error_code: error.errorCode + }); + return this.container.find('#js-u2f-try-again').on('click', this.renderSetup); +}; - U2FRegister.prototype.renderRegistered = function(deviceResponse) { - this.renderTemplate('registered'); - // Prefer to do this instead of interpolating using Underscore templates - // because of JSON escaping issues. - return this.container.find("#js-device-response").val(deviceResponse); - }; +U2FRegister.prototype.renderRegistered = function(deviceResponse) { + this.renderTemplate('registered'); + // Prefer to do this instead of interpolating using Underscore templates + // because of JSON escaping issues. + return this.container.find("#js-device-response").val(deviceResponse); +}; - U2FRegister.prototype.renderNotSupported = function() { - return this.renderTemplate('notSupported'); - }; +U2FRegister.prototype.renderNotSupported = function() { + return this.renderTemplate('notSupported'); +}; - return U2FRegister; - })(); -}).call(window); +window.U2FRegister = U2FRegister; diff --git a/app/assets/javascripts/u2f/util.js b/app/assets/javascripts/u2f/util.js index 813d363db00..bcf62276ccf 100644 --- a/app/assets/javascripts/u2f/util.js +++ b/app/assets/javascripts/u2f/util.js @@ -1,12 +1,8 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife */ -(function() { - this.U2FUtil = (function() { - function U2FUtil() {} +function U2FUtil() {} - U2FUtil.isU2FSupported = function() { - return window.u2f; - }; +U2FUtil.isU2FSupported = function() { + return window.u2f; +}; - return U2FUtil; - })(); -}).call(window); +window.U2FUtil = U2FUtil; diff --git a/spec/javascripts/abuse_reports_spec.js b/spec/javascripts/abuse_reports_spec.js index 069d857eab6..99bae5858ae 100644 --- a/spec/javascripts/abuse_reports_spec.js +++ b/spec/javascripts/abuse_reports_spec.js @@ -1,43 +1,43 @@ import '~/lib/utils/text_utility'; import '~/abuse_reports'; -((global) => { - describe('Abuse Reports', () => { - const FIXTURE = 'abuse_reports/abuse_reports_list.html.raw'; - const MAX_MESSAGE_LENGTH = 500; - - let messages; - - const assertMaxLength = $message => expect($message.text().length).toEqual(MAX_MESSAGE_LENGTH); - const findMessage = searchText => messages.filter( - (index, element) => element.innerText.indexOf(searchText) > -1, - ).first(); - - preloadFixtures(FIXTURE); - - beforeEach(function () { - loadFixtures(FIXTURE); - this.abuseReports = new global.AbuseReports(); - messages = $('.abuse-reports .message'); - }); - - it('should truncate long messages', () => { - const $longMessage = findMessage('LONG MESSAGE'); - expect($longMessage.data('original-message')).toEqual(jasmine.anything()); - assertMaxLength($longMessage); - }); - - it('should not truncate short messages', () => { - const $shortMessage = findMessage('SHORT MESSAGE'); - expect($shortMessage.data('original-message')).not.toEqual(jasmine.anything()); - }); - - it('should allow clicking a truncated message to expand and collapse the full message', () => { - const $longMessage = findMessage('LONG MESSAGE'); - $longMessage.click(); - expect($longMessage.data('original-message').length).toEqual($longMessage.text().length); - $longMessage.click(); - assertMaxLength($longMessage); - }); +const global = window.gl || (window.gl = {}); + +describe('Abuse Reports', () => { + const FIXTURE = 'abuse_reports/abuse_reports_list.html.raw'; + const MAX_MESSAGE_LENGTH = 500; + + let messages; + + const assertMaxLength = $message => expect($message.text().length).toEqual(MAX_MESSAGE_LENGTH); + const findMessage = searchText => messages.filter( + (index, element) => element.innerText.indexOf(searchText) > -1, + ).first(); + + preloadFixtures(FIXTURE); + + beforeEach(function () { + loadFixtures(FIXTURE); + this.abuseReports = new global.AbuseReports(); + messages = $('.abuse-reports .message'); + }); + + it('should truncate long messages', () => { + const $longMessage = findMessage('LONG MESSAGE'); + expect($longMessage.data('original-message')).toEqual(jasmine.anything()); + assertMaxLength($longMessage); + }); + + it('should not truncate short messages', () => { + const $shortMessage = findMessage('SHORT MESSAGE'); + expect($shortMessage.data('original-message')).not.toEqual(jasmine.anything()); + }); + + it('should allow clicking a truncated message to expand and collapse the full message', () => { + const $longMessage = findMessage('LONG MESSAGE'); + $longMessage.click(); + expect($longMessage.data('original-message').length).toEqual($longMessage.text().length); + $longMessage.click(); + assertMaxLength($longMessage); }); -})(window.gl); +}); diff --git a/spec/javascripts/activities_spec.js b/spec/javascripts/activities_spec.js index e8c5f721423..a1ee4c9383a 100644 --- a/spec/javascripts/activities_spec.js +++ b/spec/javascripts/activities_spec.js @@ -4,59 +4,57 @@ import 'vendor/jquery.endless-scroll'; import '~/pager'; import '~/activities'; -(() => { - window.gon || (window.gon = {}); - const fixtureTemplate = 'static/event_filter.html.raw'; - const filters = [ - { - id: 'all', - }, { - id: 'push', - name: 'push events', - }, { - id: 'merged', - name: 'merge events', - }, { - id: 'comments', - }, { - id: 'team', - }]; - - function getEventName(index) { - const filter = filters[index]; - return filter.hasOwnProperty('name') ? filter.name : filter.id; - } - - function getSelector(index) { - const filter = filters[index]; - return `#${filter.id}_event_filter`; - } +window.gon || (window.gon = {}); +const fixtureTemplate = 'static/event_filter.html.raw'; +const filters = [ + { + id: 'all', + }, { + id: 'push', + name: 'push events', + }, { + id: 'merged', + name: 'merge events', + }, { + id: 'comments', + }, { + id: 'team', + }]; + +function getEventName(index) { + const filter = filters[index]; + return filter.hasOwnProperty('name') ? filter.name : filter.id; +} + +function getSelector(index) { + const filter = filters[index]; + return `#${filter.id}_event_filter`; +} + +describe('Activities', () => { + beforeEach(() => { + loadFixtures(fixtureTemplate); + new gl.Activities(); + }); - describe('Activities', () => { - beforeEach(() => { - loadFixtures(fixtureTemplate); - new gl.Activities(); - }); - - for (let i = 0; i < filters.length; i += 1) { - ((i) => { - describe(`when selecting ${getEventName(i)}`, () => { - beforeEach(() => { - $(getSelector(i)).click(); - }); - - for (let x = 0; x < filters.length; x += 1) { - ((x) => { - const shouldHighlight = i === x; - const testName = shouldHighlight ? 'should highlight' : 'should not highlight'; - - it(`${testName} ${getEventName(x)}`, () => { - expect($(getSelector(x)).parent().hasClass('active')).toEqual(shouldHighlight); - }); - })(x); - } + for (let i = 0; i < filters.length; i += 1) { + ((i) => { + describe(`when selecting ${getEventName(i)}`, () => { + beforeEach(() => { + $(getSelector(i)).click(); }); - })(i); - } - }); -})(); + + for (let x = 0; x < filters.length; x += 1) { + ((x) => { + const shouldHighlight = i === x; + const testName = shouldHighlight ? 'should highlight' : 'should not highlight'; + + it(`${testName} ${getEventName(x)}`, () => { + expect($(getSelector(x)).parent().hasClass('active')).toEqual(shouldHighlight); + }); + })(x); + } + }); + })(i); + } +}); diff --git a/spec/javascripts/behaviors/autosize_spec.js b/spec/javascripts/behaviors/autosize_spec.js index 67afba19190..3bc1e9d2c88 100644 --- a/spec/javascripts/behaviors/autosize_spec.js +++ b/spec/javascripts/behaviors/autosize_spec.js @@ -2,20 +2,18 @@ import '~/behaviors/autosize'; -(function() { - describe('Autosize behavior', function() { - var load; - beforeEach(function() { - return setFixtures('<textarea class="js-autosize" style="resize: vertical"></textarea>'); - }); - it('does not overwrite the resize property', function() { - load(); - return expect($('textarea')).toHaveCss({ - resize: 'vertical' - }); +describe('Autosize behavior', function() { + var load; + beforeEach(function() { + return setFixtures('<textarea class="js-autosize" style="resize: vertical"></textarea>'); + }); + it('does not overwrite the resize property', function() { + load(); + return expect($('textarea')).toHaveCss({ + resize: 'vertical' }); - return load = function() { - return $(document).trigger('load'); - }; }); -}).call(window); + return load = function() { + return $(document).trigger('load'); + }; +}); diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js index 6dc48f9a293..3546bd698eb 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js +++ b/spec/javascripts/behaviors/quick_submit_spec.js @@ -2,118 +2,116 @@ import '~/behaviors/quick_submit'; -(function() { - describe('Quick Submit behavior', function() { - var keydownEvent; - preloadFixtures('issues/open-issue.html.raw'); - beforeEach(function() { - loadFixtures('issues/open-issue.html.raw'); - $('form').submit(function(e) { - // Prevent a form submit from moving us off the testing page - return e.preventDefault(); - }); - this.spies = { - submit: spyOnEvent('form', 'submit') - }; +describe('Quick Submit behavior', function() { + var keydownEvent; + preloadFixtures('issues/open-issue.html.raw'); + beforeEach(function() { + loadFixtures('issues/open-issue.html.raw'); + $('form').submit(function(e) { + // Prevent a form submit from moving us off the testing page + return e.preventDefault(); + }); + this.spies = { + submit: spyOnEvent('form', 'submit') + }; + + this.textarea = $('.js-quick-submit textarea').first(); + }); + it('does not respond to other keyCodes', function() { + this.textarea.trigger(keydownEvent({ + keyCode: 32 + })); + return expect(this.spies.submit).not.toHaveBeenTriggered(); + }); + it('does not respond to Enter alone', function() { + this.textarea.trigger(keydownEvent({ + ctrlKey: false, + metaKey: false + })); + return expect(this.spies.submit).not.toHaveBeenTriggered(); + }); + it('does not respond to repeated events', function() { + this.textarea.trigger(keydownEvent({ + repeat: true + })); + return expect(this.spies.submit).not.toHaveBeenTriggered(); + }); + it('disables input of type submit', function() { + const submitButton = $('.js-quick-submit input[type=submit]'); + this.textarea.trigger(keydownEvent()); + + expect(submitButton).toBeDisabled(); + }); + it('disables button of type submit', function() { + const submitButton = $('.js-quick-submit input[type=submit]'); + this.textarea.trigger(keydownEvent()); - this.textarea = $('.js-quick-submit textarea').first(); + expect(submitButton).toBeDisabled(); + }); + it('only clicks one submit', function() { + const existingSubmit = $('.js-quick-submit input[type=submit]'); + // Add an extra submit button + const newSubmit = $('<button type="submit">Submit it</button>'); + newSubmit.insertAfter(this.textarea); + + const oldClick = spyOnEvent(existingSubmit, 'click'); + const newClick = spyOnEvent(newSubmit, 'click'); + + this.textarea.trigger(keydownEvent()); + + expect(oldClick).not.toHaveBeenTriggered(); + expect(newClick).toHaveBeenTriggered(); + }); + // We cannot stub `navigator.userAgent` for CI's `rake karma` task, so we'll + // only run the tests that apply to the current platform + if (navigator.userAgent.match(/Macintosh/)) { + it('responds to Meta+Enter', function() { + this.textarea.trigger(keydownEvent()); + return expect(this.spies.submit).toHaveBeenTriggered(); }); - it('does not respond to other keyCodes', function() { + it('excludes other modifier keys', function() { this.textarea.trigger(keydownEvent({ - keyCode: 32 + altKey: true })); - return expect(this.spies.submit).not.toHaveBeenTriggered(); - }); - it('does not respond to Enter alone', function() { this.textarea.trigger(keydownEvent({ - ctrlKey: false, - metaKey: false + ctrlKey: true })); - return expect(this.spies.submit).not.toHaveBeenTriggered(); - }); - it('does not respond to repeated events', function() { this.textarea.trigger(keydownEvent({ - repeat: true + shiftKey: true })); return expect(this.spies.submit).not.toHaveBeenTriggered(); }); - it('disables input of type submit', function() { - const submitButton = $('.js-quick-submit input[type=submit]'); + } else { + it('responds to Ctrl+Enter', function() { this.textarea.trigger(keydownEvent()); - - expect(submitButton).toBeDisabled(); + return expect(this.spies.submit).toHaveBeenTriggered(); }); - it('disables button of type submit', function() { - const submitButton = $('.js-quick-submit input[type=submit]'); - this.textarea.trigger(keydownEvent()); - - expect(submitButton).toBeDisabled(); - }); - it('only clicks one submit', function() { - const existingSubmit = $('.js-quick-submit input[type=submit]'); - // Add an extra submit button - const newSubmit = $('<button type="submit">Submit it</button>'); - newSubmit.insertAfter(this.textarea); - - const oldClick = spyOnEvent(existingSubmit, 'click'); - const newClick = spyOnEvent(newSubmit, 'click'); - - this.textarea.trigger(keydownEvent()); - - expect(oldClick).not.toHaveBeenTriggered(); - expect(newClick).toHaveBeenTriggered(); + it('excludes other modifier keys', function() { + this.textarea.trigger(keydownEvent({ + altKey: true + })); + this.textarea.trigger(keydownEvent({ + metaKey: true + })); + this.textarea.trigger(keydownEvent({ + shiftKey: true + })); + return expect(this.spies.submit).not.toHaveBeenTriggered(); }); - // We cannot stub `navigator.userAgent` for CI's `rake karma` task, so we'll - // only run the tests that apply to the current platform + } + return keydownEvent = function(options) { + var defaults; if (navigator.userAgent.match(/Macintosh/)) { - it('responds to Meta+Enter', function() { - this.textarea.trigger(keydownEvent()); - return expect(this.spies.submit).toHaveBeenTriggered(); - }); - it('excludes other modifier keys', function() { - this.textarea.trigger(keydownEvent({ - altKey: true - })); - this.textarea.trigger(keydownEvent({ - ctrlKey: true - })); - this.textarea.trigger(keydownEvent({ - shiftKey: true - })); - return expect(this.spies.submit).not.toHaveBeenTriggered(); - }); + defaults = { + keyCode: 13, + metaKey: true + }; } else { - it('responds to Ctrl+Enter', function() { - this.textarea.trigger(keydownEvent()); - return expect(this.spies.submit).toHaveBeenTriggered(); - }); - it('excludes other modifier keys', function() { - this.textarea.trigger(keydownEvent({ - altKey: true - })); - this.textarea.trigger(keydownEvent({ - metaKey: true - })); - this.textarea.trigger(keydownEvent({ - shiftKey: true - })); - return expect(this.spies.submit).not.toHaveBeenTriggered(); - }); + defaults = { + keyCode: 13, + ctrlKey: true + }; } - return keydownEvent = function(options) { - var defaults; - if (navigator.userAgent.match(/Macintosh/)) { - defaults = { - keyCode: 13, - metaKey: true - }; - } else { - defaults = { - keyCode: 13, - ctrlKey: true - }; - } - return $.Event('keydown', $.extend({}, defaults, options)); - }; - }); -}).call(window); + return $.Event('keydown', $.extend({}, defaults, options)); + }; +}); diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js index f9fa814b801..22d6504f917 100644 --- a/spec/javascripts/behaviors/requires_input_spec.js +++ b/spec/javascripts/behaviors/requires_input_spec.js @@ -2,38 +2,36 @@ import '~/behaviors/requires_input'; -(function() { - describe('requiresInput', function() { - preloadFixtures('branches/new_branch.html.raw'); - beforeEach(function() { - loadFixtures('branches/new_branch.html.raw'); - this.submitButton = $('button[type="submit"]'); - }); - it('disables submit when any field is required', function() { - $('.js-requires-input').requiresInput(); - return expect(this.submitButton).toBeDisabled(); - }); - it('enables submit when no field is required', function() { - $('*[required=required]').removeAttr('required'); - $('.js-requires-input').requiresInput(); - return expect(this.submitButton).not.toBeDisabled(); - }); - it('enables submit when all required fields are pre-filled', function() { - $('*[required=required]').remove(); - $('.js-requires-input').requiresInput(); - return expect($('.submit')).not.toBeDisabled(); - }); - it('enables submit when all required fields receive input', function() { - $('.js-requires-input').requiresInput(); - $('#required1').val('input1').change(); - expect(this.submitButton).toBeDisabled(); - $('#optional1').val('input1').change(); - expect(this.submitButton).toBeDisabled(); - $('#required2').val('input2').change(); - $('#required3').val('input3').change(); - $('#required4').val('input4').change(); - $('#required5').val('1').change(); - return expect($('.submit')).not.toBeDisabled(); - }); +describe('requiresInput', function() { + preloadFixtures('branches/new_branch.html.raw'); + beforeEach(function() { + loadFixtures('branches/new_branch.html.raw'); + this.submitButton = $('button[type="submit"]'); }); -}).call(window); + it('disables submit when any field is required', function() { + $('.js-requires-input').requiresInput(); + return expect(this.submitButton).toBeDisabled(); + }); + it('enables submit when no field is required', function() { + $('*[required=required]').removeAttr('required'); + $('.js-requires-input').requiresInput(); + return expect(this.submitButton).not.toBeDisabled(); + }); + it('enables submit when all required fields are pre-filled', function() { + $('*[required=required]').remove(); + $('.js-requires-input').requiresInput(); + return expect($('.submit')).not.toBeDisabled(); + }); + it('enables submit when all required fields receive input', function() { + $('.js-requires-input').requiresInput(); + $('#required1').val('input1').change(); + expect(this.submitButton).toBeDisabled(); + $('#optional1').val('input1').change(); + expect(this.submitButton).toBeDisabled(); + $('#required2').val('input2').change(); + $('#required3').val('input3').change(); + $('#required4').val('input4').change(); + $('#required5').val('1').change(); + return expect($('.submit')).not.toBeDisabled(); + }); +}); diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js index ace95000468..c0154d102d0 100644 --- a/spec/javascripts/commits_spec.js +++ b/spec/javascripts/commits_spec.js @@ -4,74 +4,72 @@ import 'vendor/jquery.endless-scroll'; import '~/pager'; import '~/commits'; -(() => { - describe('Commits List', () => { - beforeEach(() => { - setFixtures(` - <form class="commits-search-form" action="/h5bp/html5-boilerplate/commits/master"> - <input id="commits-search"> - </form> - <ol id="commits-list"></ol> - `); - }); - - it('should be defined', () => { - expect(CommitsList).toBeDefined(); - }); +describe('Commits List', () => { + beforeEach(() => { + setFixtures(` + <form class="commits-search-form" action="/h5bp/html5-boilerplate/commits/master"> + <input id="commits-search"> + </form> + <ol id="commits-list"></ol> + `); + }); - describe('processCommits', () => { - it('should join commit headers', () => { - CommitsList.$contentList = $(` - <div> - <li class="commit-header" data-day="2016-09-20"> - <span class="day">20 Sep, 2016</span> - <span class="commits-count">1 commit</span> - </li> - <li class="commit"></li> - </div> - `); + it('should be defined', () => { + expect(CommitsList).toBeDefined(); + }); - const data = ` + describe('processCommits', () => { + it('should join commit headers', () => { + CommitsList.$contentList = $(` + <div> <li class="commit-header" data-day="2016-09-20"> <span class="day">20 Sep, 2016</span> <span class="commits-count">1 commit</span> </li> <li class="commit"></li> - `; + </div> + `); - // The last commit header should be removed - // since the previous one has the same data-day value. - expect(CommitsList.processCommits(data).find('li.commit-header').length).toBe(0); - }); + const data = ` + <li class="commit-header" data-day="2016-09-20"> + <span class="day">20 Sep, 2016</span> + <span class="commits-count">1 commit</span> + </li> + <li class="commit"></li> + `; + + // The last commit header should be removed + // since the previous one has the same data-day value. + expect(CommitsList.processCommits(data).find('li.commit-header').length).toBe(0); }); + }); - describe('on entering input', () => { - let ajaxSpy; + describe('on entering input', () => { + let ajaxSpy; - beforeEach(() => { - CommitsList.init(25); - CommitsList.searchField.val(''); + beforeEach(() => { + CommitsList.init(25); + CommitsList.searchField.val(''); - spyOn(history, 'replaceState').and.stub(); - ajaxSpy = spyOn(jQuery, 'ajax').and.callFake((req) => { - req.success({ - data: '<li>Result</li>', - }); + spyOn(history, 'replaceState').and.stub(); + ajaxSpy = spyOn(jQuery, 'ajax').and.callFake((req) => { + req.success({ + data: '<li>Result</li>', }); }); + }); - it('should save the last search string', () => { - CommitsList.searchField.val('GitLab'); - CommitsList.filterResults(); - expect(ajaxSpy).toHaveBeenCalled(); - expect(CommitsList.lastSearch).toEqual('GitLab'); - }); + it('should save the last search string', () => { + CommitsList.searchField.val('GitLab'); + CommitsList.filterResults(); + expect(ajaxSpy).toHaveBeenCalled(); + expect(CommitsList.lastSearch).toEqual('GitLab'); + }); - it('should not make ajax call if the input does not change', () => { - CommitsList.filterResults(); - expect(ajaxSpy).not.toHaveBeenCalled(); - expect(CommitsList.lastSearch).toEqual(''); - }); + it('should not make ajax call if the input does not change', () => { + CommitsList.filterResults(); + expect(ajaxSpy).not.toHaveBeenCalled(); + expect(CommitsList.lastSearch).toEqual(''); }); }); -})(); +}); diff --git a/spec/javascripts/copy_as_gfm_spec.js b/spec/javascripts/copy_as_gfm_spec.js index ded450749d3..b8994d565ab 100644 --- a/spec/javascripts/copy_as_gfm_spec.js +++ b/spec/javascripts/copy_as_gfm_spec.js @@ -1,49 +1,47 @@ import '~/copy_as_gfm'; -(() => { - describe('gl.CopyAsGFM', () => { - describe('gl.CopyAsGFM.pasteGFM', () => { - function callPasteGFM() { - const e = { - originalEvent: { - clipboardData: { - getData(mimeType) { - // When GFM code is copied, we put the regular plain text - // on the clipboard as `text/plain`, and the GFM as `text/x-gfm`. - // This emulates the behavior of `getData` with that data. - if (mimeType === 'text/plain') { - return 'code'; - } - if (mimeType === 'text/x-gfm') { - return '`code`'; - } - return null; - }, +describe('gl.CopyAsGFM', () => { + describe('gl.CopyAsGFM.pasteGFM', () => { + function callPasteGFM() { + const e = { + originalEvent: { + clipboardData: { + getData(mimeType) { + // When GFM code is copied, we put the regular plain text + // on the clipboard as `text/plain`, and the GFM as `text/x-gfm`. + // This emulates the behavior of `getData` with that data. + if (mimeType === 'text/plain') { + return 'code'; + } + if (mimeType === 'text/x-gfm') { + return '`code`'; + } + return null; }, }, - preventDefault() {}, - }; + }, + preventDefault() {}, + }; - window.gl.CopyAsGFM.pasteGFM(e); - } + window.gl.CopyAsGFM.pasteGFM(e); + } - it('wraps pasted code when not already in code tags', () => { - spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => { - const insertedText = textFunc('This is code: ', ''); - expect(insertedText).toEqual('`code`'); - }); - - callPasteGFM(); + it('wraps pasted code when not already in code tags', () => { + spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => { + const insertedText = textFunc('This is code: ', ''); + expect(insertedText).toEqual('`code`'); }); - it('does not wrap pasted code when already in code tags', () => { - spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => { - const insertedText = textFunc('This is code: `', '`'); - expect(insertedText).toEqual('code'); - }); + callPasteGFM(); + }); - callPasteGFM(); + it('does not wrap pasted code when already in code tags', () => { + spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => { + const insertedText = textFunc('This is code: `', '`'); + expect(insertedText).toEqual('code'); }); + + callPasteGFM(); }); }); -})(); +}); diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js index 3391cade541..e2a9ab62b48 100644 --- a/spec/javascripts/datetime_utility_spec.js +++ b/spec/javascripts/datetime_utility_spec.js @@ -1,98 +1,96 @@ import { timeIntervalInWords } from '~/lib/utils/datetime_utility'; -(() => { - describe('Date time utils', () => { - describe('timeFor', () => { - it('returns `past due` when in past', () => { - const date = new Date(); - date.setFullYear(date.getFullYear() - 1); - - expect( - gl.utils.timeFor(date), - ).toBe('Past due'); - }); - - it('returns remaining time when in the future', () => { - const date = new Date(); - date.setFullYear(date.getFullYear() + 1); - - // Add a day to prevent a transient error. If date is even 1 second - // short of a full year, timeFor will return '11 months remaining' - date.setDate(date.getDate() + 1); - - expect( - gl.utils.timeFor(date), - ).toBe('1 year remaining'); - }); +describe('Date time utils', () => { + describe('timeFor', () => { + it('returns `past due` when in past', () => { + const date = new Date(); + date.setFullYear(date.getFullYear() - 1); + + expect( + gl.utils.timeFor(date), + ).toBe('Past due'); }); - describe('get day name', () => { - it('should return Sunday', () => { - const day = gl.utils.getDayName(new Date('07/17/2016')); - expect(day).toBe('Sunday'); - }); - - it('should return Monday', () => { - const day = gl.utils.getDayName(new Date('07/18/2016')); - expect(day).toBe('Monday'); - }); - - it('should return Tuesday', () => { - const day = gl.utils.getDayName(new Date('07/19/2016')); - expect(day).toBe('Tuesday'); - }); - - it('should return Wednesday', () => { - const day = gl.utils.getDayName(new Date('07/20/2016')); - expect(day).toBe('Wednesday'); - }); - - it('should return Thursday', () => { - const day = gl.utils.getDayName(new Date('07/21/2016')); - expect(day).toBe('Thursday'); - }); - - it('should return Friday', () => { - const day = gl.utils.getDayName(new Date('07/22/2016')); - expect(day).toBe('Friday'); - }); - - it('should return Saturday', () => { - const day = gl.utils.getDayName(new Date('07/23/2016')); - expect(day).toBe('Saturday'); - }); + it('returns remaining time when in the future', () => { + const date = new Date(); + date.setFullYear(date.getFullYear() + 1); + + // Add a day to prevent a transient error. If date is even 1 second + // short of a full year, timeFor will return '11 months remaining' + date.setDate(date.getDate() + 1); + + expect( + gl.utils.timeFor(date), + ).toBe('1 year remaining'); + }); + }); + + describe('get day name', () => { + it('should return Sunday', () => { + const day = gl.utils.getDayName(new Date('07/17/2016')); + expect(day).toBe('Sunday'); + }); + + it('should return Monday', () => { + const day = gl.utils.getDayName(new Date('07/18/2016')); + expect(day).toBe('Monday'); + }); + + it('should return Tuesday', () => { + const day = gl.utils.getDayName(new Date('07/19/2016')); + expect(day).toBe('Tuesday'); + }); + + it('should return Wednesday', () => { + const day = gl.utils.getDayName(new Date('07/20/2016')); + expect(day).toBe('Wednesday'); + }); + + it('should return Thursday', () => { + const day = gl.utils.getDayName(new Date('07/21/2016')); + expect(day).toBe('Thursday'); + }); + + it('should return Friday', () => { + const day = gl.utils.getDayName(new Date('07/22/2016')); + expect(day).toBe('Friday'); }); - describe('get day difference', () => { - it('should return 7', () => { - const firstDay = new Date('07/01/2016'); - const secondDay = new Date('07/08/2016'); - const difference = gl.utils.getDayDifference(firstDay, secondDay); - expect(difference).toBe(7); - }); - - it('should return 31', () => { - const firstDay = new Date('07/01/2016'); - const secondDay = new Date('08/01/2016'); - const difference = gl.utils.getDayDifference(firstDay, secondDay); - expect(difference).toBe(31); - }); - - it('should return 365', () => { - const firstDay = new Date('07/02/2015'); - const secondDay = new Date('07/01/2016'); - const difference = gl.utils.getDayDifference(firstDay, secondDay); - expect(difference).toBe(365); - }); + it('should return Saturday', () => { + const day = gl.utils.getDayName(new Date('07/23/2016')); + expect(day).toBe('Saturday'); }); }); - describe('timeIntervalInWords', () => { - it('should return string with number of minutes and seconds', () => { - expect(timeIntervalInWords(9.54)).toEqual('9 seconds'); - expect(timeIntervalInWords(1)).toEqual('1 second'); - expect(timeIntervalInWords(200)).toEqual('3 minutes 20 seconds'); - expect(timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds'); + describe('get day difference', () => { + it('should return 7', () => { + const firstDay = new Date('07/01/2016'); + const secondDay = new Date('07/08/2016'); + const difference = gl.utils.getDayDifference(firstDay, secondDay); + expect(difference).toBe(7); }); + + it('should return 31', () => { + const firstDay = new Date('07/01/2016'); + const secondDay = new Date('08/01/2016'); + const difference = gl.utils.getDayDifference(firstDay, secondDay); + expect(difference).toBe(31); + }); + + it('should return 365', () => { + const firstDay = new Date('07/02/2015'); + const secondDay = new Date('07/01/2016'); + const difference = gl.utils.getDayDifference(firstDay, secondDay); + expect(difference).toBe(365); + }); + }); +}); + +describe('timeIntervalInWords', () => { + it('should return string with number of minutes and seconds', () => { + expect(timeIntervalInWords(9.54)).toEqual('9 seconds'); + expect(timeIntervalInWords(1)).toEqual('1 second'); + expect(timeIntervalInWords(200)).toEqual('3 minutes 20 seconds'); + expect(timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds'); }); -})(); +}); diff --git a/spec/javascripts/extensions/array_spec.js b/spec/javascripts/extensions/array_spec.js index b1b81b4efc2..20dae79785c 100644 --- a/spec/javascripts/extensions/array_spec.js +++ b/spec/javascripts/extensions/array_spec.js @@ -2,21 +2,19 @@ import '~/extensions/array'; -(function() { - describe('Array extensions', function() { - describe('first', function() { - return it('returns the first item', function() { - var arr; - arr = [0, 1, 2, 3, 4, 5]; - return expect(arr.first()).toBe(0); - }); +describe('Array extensions', function() { + describe('first', function() { + return it('returns the first item', function() { + var arr; + arr = [0, 1, 2, 3, 4, 5]; + return expect(arr.first()).toBe(0); }); - describe('last', function() { - return it('returns the last item', function() { - var arr; - arr = [0, 1, 2, 3, 4, 5]; - return expect(arr.last()).toBe(5); - }); + }); + describe('last', function() { + return it('returns the last item', function() { + var arr; + arr = [0, 1, 2, 3, 4, 5]; + return expect(arr.last()).toBe(5); }); }); -}).call(window); +}); diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 55037bbbf73..3f18b1d57ec 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -2,395 +2,393 @@ import '~/lib/utils/common_utils'; -(() => { - describe('common_utils', () => { - describe('gl.utils.parseUrl', () => { - it('returns an anchor tag with url', () => { - expect(gl.utils.parseUrl('/some/absolute/url').pathname).toContain('some/absolute/url'); - }); - it('url is escaped', () => { - // IE11 will return a relative pathname while other browsers will return a full pathname. - // parseUrl uses an anchor element for parsing an url. With relative urls, the anchor - // element will create an absolute url relative to the current execution context. - // The JavaScript test suite is executed at '/' which will lead to an absolute url - // starting with '/'. - expect(gl.utils.parseUrl('" test="asf"').pathname).toContain('/%22%20test=%22asf%22'); - }); +describe('common_utils', () => { + describe('gl.utils.parseUrl', () => { + it('returns an anchor tag with url', () => { + expect(gl.utils.parseUrl('/some/absolute/url').pathname).toContain('some/absolute/url'); + }); + it('url is escaped', () => { + // IE11 will return a relative pathname while other browsers will return a full pathname. + // parseUrl uses an anchor element for parsing an url. With relative urls, the anchor + // element will create an absolute url relative to the current execution context. + // The JavaScript test suite is executed at '/' which will lead to an absolute url + // starting with '/'. + expect(gl.utils.parseUrl('" test="asf"').pathname).toContain('/%22%20test=%22asf%22'); }); + }); - describe('gl.utils.parseUrlPathname', () => { - beforeEach(() => { - spyOn(gl.utils, 'parseUrl').and.callFake(url => ({ - pathname: url, - })); - }); - it('returns an absolute url when given an absolute url', () => { - expect(gl.utils.parseUrlPathname('/some/absolute/url')).toEqual('/some/absolute/url'); - }); - it('returns an absolute url when given a relative url', () => { - expect(gl.utils.parseUrlPathname('some/relative/url')).toEqual('/some/relative/url'); - }); + describe('gl.utils.parseUrlPathname', () => { + beforeEach(() => { + spyOn(gl.utils, 'parseUrl').and.callFake(url => ({ + pathname: url, + })); + }); + it('returns an absolute url when given an absolute url', () => { + expect(gl.utils.parseUrlPathname('/some/absolute/url')).toEqual('/some/absolute/url'); }); + it('returns an absolute url when given a relative url', () => { + expect(gl.utils.parseUrlPathname('some/relative/url')).toEqual('/some/relative/url'); + }); + }); - describe('gl.utils.getUrlParamsArray', () => { - it('should return params array', () => { - expect(gl.utils.getUrlParamsArray() instanceof Array).toBe(true); - }); + describe('gl.utils.getUrlParamsArray', () => { + it('should return params array', () => { + expect(gl.utils.getUrlParamsArray() instanceof Array).toBe(true); + }); - it('should remove the question mark from the search params', () => { - const paramsArray = gl.utils.getUrlParamsArray(); - expect(paramsArray[0][0] !== '?').toBe(true); - }); + it('should remove the question mark from the search params', () => { + const paramsArray = gl.utils.getUrlParamsArray(); + expect(paramsArray[0][0] !== '?').toBe(true); + }); - it('should decode params', () => { - history.pushState('', '', '?label_name%5B%5D=test'); + it('should decode params', () => { + history.pushState('', '', '?label_name%5B%5D=test'); - expect( - gl.utils.getUrlParamsArray()[0], - ).toBe('label_name[]=test'); + expect( + gl.utils.getUrlParamsArray()[0], + ).toBe('label_name[]=test'); - history.pushState('', '', '?'); - }); + history.pushState('', '', '?'); }); + }); - describe('gl.utils.handleLocationHash', () => { - beforeEach(() => { - spyOn(window.document, 'getElementById').and.callThrough(); - }); + describe('gl.utils.handleLocationHash', () => { + beforeEach(() => { + spyOn(window.document, 'getElementById').and.callThrough(); + }); - afterEach(() => { - window.history.pushState({}, null, ''); - }); + afterEach(() => { + window.history.pushState({}, null, ''); + }); - function expectGetElementIdToHaveBeenCalledWith(elementId) { - expect(window.document.getElementById).toHaveBeenCalledWith(elementId); - } + function expectGetElementIdToHaveBeenCalledWith(elementId) { + expect(window.document.getElementById).toHaveBeenCalledWith(elementId); + } - it('decodes hash parameter', () => { - window.history.pushState({}, null, '#random-hash'); - gl.utils.handleLocationHash(); + it('decodes hash parameter', () => { + window.history.pushState({}, null, '#random-hash'); + gl.utils.handleLocationHash(); - expectGetElementIdToHaveBeenCalledWith('random-hash'); - expectGetElementIdToHaveBeenCalledWith('user-content-random-hash'); - }); + expectGetElementIdToHaveBeenCalledWith('random-hash'); + expectGetElementIdToHaveBeenCalledWith('user-content-random-hash'); + }); - it('decodes cyrillic hash parameter', () => { - window.history.pushState({}, null, '#definição'); - gl.utils.handleLocationHash(); + it('decodes cyrillic hash parameter', () => { + window.history.pushState({}, null, '#definição'); + gl.utils.handleLocationHash(); - expectGetElementIdToHaveBeenCalledWith('definição'); - expectGetElementIdToHaveBeenCalledWith('user-content-definição'); - }); + expectGetElementIdToHaveBeenCalledWith('definição'); + expectGetElementIdToHaveBeenCalledWith('user-content-definição'); + }); - it('decodes encoded cyrillic hash parameter', () => { - window.history.pushState({}, null, '#defini%C3%A7%C3%A3o'); - gl.utils.handleLocationHash(); + it('decodes encoded cyrillic hash parameter', () => { + window.history.pushState({}, null, '#defini%C3%A7%C3%A3o'); + gl.utils.handleLocationHash(); - expectGetElementIdToHaveBeenCalledWith('definição'); - expectGetElementIdToHaveBeenCalledWith('user-content-definição'); - }); + expectGetElementIdToHaveBeenCalledWith('definição'); + expectGetElementIdToHaveBeenCalledWith('user-content-definição'); }); + }); - describe('gl.utils.setParamInURL', () => { - afterEach(() => { - window.history.pushState({}, null, ''); - }); + describe('gl.utils.setParamInURL', () => { + afterEach(() => { + window.history.pushState({}, null, ''); + }); - it('should return the parameter', () => { - window.history.replaceState({}, null, ''); + it('should return the parameter', () => { + window.history.replaceState({}, null, ''); - expect(gl.utils.setParamInURL('page', 156)).toBe('?page=156'); - expect(gl.utils.setParamInURL('page', '156')).toBe('?page=156'); - }); + expect(gl.utils.setParamInURL('page', 156)).toBe('?page=156'); + expect(gl.utils.setParamInURL('page', '156')).toBe('?page=156'); + }); - it('should update the existing parameter when its a number', () => { - window.history.pushState({}, null, '?page=15'); + it('should update the existing parameter when its a number', () => { + window.history.pushState({}, null, '?page=15'); - expect(gl.utils.setParamInURL('page', 16)).toBe('?page=16'); - expect(gl.utils.setParamInURL('page', '16')).toBe('?page=16'); - expect(gl.utils.setParamInURL('page', true)).toBe('?page=true'); - }); + expect(gl.utils.setParamInURL('page', 16)).toBe('?page=16'); + expect(gl.utils.setParamInURL('page', '16')).toBe('?page=16'); + expect(gl.utils.setParamInURL('page', true)).toBe('?page=true'); + }); - it('should update the existing parameter when its a string', () => { - window.history.pushState({}, null, '?scope=all'); + it('should update the existing parameter when its a string', () => { + window.history.pushState({}, null, '?scope=all'); - expect(gl.utils.setParamInURL('scope', 'finished')).toBe('?scope=finished'); - }); + expect(gl.utils.setParamInURL('scope', 'finished')).toBe('?scope=finished'); + }); - it('should update the existing parameter when more than one parameter exists', () => { - window.history.pushState({}, null, '?scope=all&page=15'); + it('should update the existing parameter when more than one parameter exists', () => { + window.history.pushState({}, null, '?scope=all&page=15'); - expect(gl.utils.setParamInURL('scope', 'finished')).toBe('?scope=finished&page=15'); - }); + expect(gl.utils.setParamInURL('scope', 'finished')).toBe('?scope=finished&page=15'); + }); - it('should add a new parameter to the end of the existing ones', () => { - window.history.pushState({}, null, '?scope=all'); + it('should add a new parameter to the end of the existing ones', () => { + window.history.pushState({}, null, '?scope=all'); - expect(gl.utils.setParamInURL('page', 16)).toBe('?scope=all&page=16'); - expect(gl.utils.setParamInURL('page', '16')).toBe('?scope=all&page=16'); - expect(gl.utils.setParamInURL('page', true)).toBe('?scope=all&page=true'); - }); + expect(gl.utils.setParamInURL('page', 16)).toBe('?scope=all&page=16'); + expect(gl.utils.setParamInURL('page', '16')).toBe('?scope=all&page=16'); + expect(gl.utils.setParamInURL('page', true)).toBe('?scope=all&page=true'); }); + }); - describe('gl.utils.getParameterByName', () => { - beforeEach(() => { - window.history.pushState({}, null, '?scope=all&p=2'); - }); + describe('gl.utils.getParameterByName', () => { + beforeEach(() => { + window.history.pushState({}, null, '?scope=all&p=2'); + }); - afterEach(() => { - window.history.replaceState({}, null, null); - }); + afterEach(() => { + window.history.replaceState({}, null, null); + }); - it('should return valid parameter', () => { - const value = gl.utils.getParameterByName('scope'); - expect(gl.utils.getParameterByName('p')).toEqual('2'); - expect(value).toBe('all'); - }); + it('should return valid parameter', () => { + const value = gl.utils.getParameterByName('scope'); + expect(gl.utils.getParameterByName('p')).toEqual('2'); + expect(value).toBe('all'); + }); - it('should return invalid parameter', () => { - const value = gl.utils.getParameterByName('fakeParameter'); - expect(value).toBe(null); - }); + it('should return invalid parameter', () => { + const value = gl.utils.getParameterByName('fakeParameter'); + expect(value).toBe(null); + }); - it('should return valid paramentes if URL is provided', () => { - let value = gl.utils.getParameterByName('foo', 'http://cocteau.twins/?foo=bar'); - expect(value).toBe('bar'); + it('should return valid paramentes if URL is provided', () => { + let value = gl.utils.getParameterByName('foo', 'http://cocteau.twins/?foo=bar'); + expect(value).toBe('bar'); - value = gl.utils.getParameterByName('manan', 'http://cocteau.twins/?foo=bar&manan=canchu'); - expect(value).toBe('canchu'); - }); + value = gl.utils.getParameterByName('manan', 'http://cocteau.twins/?foo=bar&manan=canchu'); + expect(value).toBe('canchu'); }); + }); - describe('gl.utils.normalizedHeaders', () => { - it('should upperCase all the header keys to keep them consistent', () => { - const apiHeaders = { - 'X-Something-Workhorse': { workhorse: 'ok' }, - 'x-something-nginx': { nginx: 'ok' }, - }; + describe('gl.utils.normalizedHeaders', () => { + it('should upperCase all the header keys to keep them consistent', () => { + const apiHeaders = { + 'X-Something-Workhorse': { workhorse: 'ok' }, + 'x-something-nginx': { nginx: 'ok' }, + }; - const normalized = gl.utils.normalizeHeaders(apiHeaders); + const normalized = gl.utils.normalizeHeaders(apiHeaders); - const WORKHORSE = 'X-SOMETHING-WORKHORSE'; - const NGINX = 'X-SOMETHING-NGINX'; + const WORKHORSE = 'X-SOMETHING-WORKHORSE'; + const NGINX = 'X-SOMETHING-NGINX'; - expect(normalized[WORKHORSE].workhorse).toBe('ok'); - expect(normalized[NGINX].nginx).toBe('ok'); - }); + expect(normalized[WORKHORSE].workhorse).toBe('ok'); + expect(normalized[NGINX].nginx).toBe('ok'); }); + }); - describe('gl.utils.normalizeCRLFHeaders', () => { - beforeEach(function () { - this.CLRFHeaders = 'a-header: a-value\nAnother-Header: ANOTHER-VALUE\nLaSt-HeAdEr: last-VALUE'; + describe('gl.utils.normalizeCRLFHeaders', () => { + beforeEach(function () { + this.CLRFHeaders = 'a-header: a-value\nAnother-Header: ANOTHER-VALUE\nLaSt-HeAdEr: last-VALUE'; - spyOn(String.prototype, 'split').and.callThrough(); - spyOn(gl.utils, 'normalizeHeaders').and.callThrough(); + spyOn(String.prototype, 'split').and.callThrough(); + spyOn(gl.utils, 'normalizeHeaders').and.callThrough(); - this.normalizeCRLFHeaders = gl.utils.normalizeCRLFHeaders(this.CLRFHeaders); - }); + this.normalizeCRLFHeaders = gl.utils.normalizeCRLFHeaders(this.CLRFHeaders); + }); - it('should split by newline', function () { - expect(String.prototype.split).toHaveBeenCalledWith('\n'); - }); + it('should split by newline', function () { + expect(String.prototype.split).toHaveBeenCalledWith('\n'); + }); - it('should split by colon+space for each header', function () { - expect(String.prototype.split.calls.allArgs().filter(args => args[0] === ': ').length).toBe(3); - }); + it('should split by colon+space for each header', function () { + expect(String.prototype.split.calls.allArgs().filter(args => args[0] === ': ').length).toBe(3); + }); - it('should call gl.utils.normalizeHeaders with a parsed headers object', function () { - expect(gl.utils.normalizeHeaders).toHaveBeenCalledWith(jasmine.any(Object)); - }); + it('should call gl.utils.normalizeHeaders with a parsed headers object', function () { + expect(gl.utils.normalizeHeaders).toHaveBeenCalledWith(jasmine.any(Object)); + }); - it('should return a normalized headers object', function () { - expect(this.normalizeCRLFHeaders).toEqual({ - 'A-HEADER': 'a-value', - 'ANOTHER-HEADER': 'ANOTHER-VALUE', - 'LAST-HEADER': 'last-VALUE', - }); + it('should return a normalized headers object', function () { + expect(this.normalizeCRLFHeaders).toEqual({ + 'A-HEADER': 'a-value', + 'ANOTHER-HEADER': 'ANOTHER-VALUE', + 'LAST-HEADER': 'last-VALUE', }); }); + }); - describe('gl.utils.parseIntPagination', () => { - it('should parse to integers all string values and return pagination object', () => { - const pagination = { - 'X-PER-PAGE': 10, - 'X-PAGE': 2, - 'X-TOTAL': 30, - 'X-TOTAL-PAGES': 3, - 'X-NEXT-PAGE': 3, - 'X-PREV-PAGE': 1, - }; - - const expectedPagination = { - perPage: 10, - page: 2, - total: 30, - totalPages: 3, - nextPage: 3, - previousPage: 1, - }; - - expect(gl.utils.parseIntPagination(pagination)).toEqual(expectedPagination); - }); + describe('gl.utils.parseIntPagination', () => { + it('should parse to integers all string values and return pagination object', () => { + const pagination = { + 'X-PER-PAGE': 10, + 'X-PAGE': 2, + 'X-TOTAL': 30, + 'X-TOTAL-PAGES': 3, + 'X-NEXT-PAGE': 3, + 'X-PREV-PAGE': 1, + }; + + const expectedPagination = { + perPage: 10, + page: 2, + total: 30, + totalPages: 3, + nextPage: 3, + previousPage: 1, + }; + + expect(gl.utils.parseIntPagination(pagination)).toEqual(expectedPagination); }); + }); - describe('gl.utils.isMetaClick', () => { - it('should identify meta click on Windows/Linux', () => { - const e = { - metaKey: false, - ctrlKey: true, - which: 1, - }; + describe('gl.utils.isMetaClick', () => { + it('should identify meta click on Windows/Linux', () => { + const e = { + metaKey: false, + ctrlKey: true, + which: 1, + }; - expect(gl.utils.isMetaClick(e)).toBe(true); - }); + expect(gl.utils.isMetaClick(e)).toBe(true); + }); - it('should identify meta click on macOS', () => { - const e = { - metaKey: true, - ctrlKey: false, - which: 1, - }; + it('should identify meta click on macOS', () => { + const e = { + metaKey: true, + ctrlKey: false, + which: 1, + }; - expect(gl.utils.isMetaClick(e)).toBe(true); - }); + expect(gl.utils.isMetaClick(e)).toBe(true); + }); - it('should identify as meta click on middle-click or Mouse-wheel click', () => { - const e = { - metaKey: false, - ctrlKey: false, - which: 2, - }; + it('should identify as meta click on middle-click or Mouse-wheel click', () => { + const e = { + metaKey: false, + ctrlKey: false, + which: 2, + }; - expect(gl.utils.isMetaClick(e)).toBe(true); + expect(gl.utils.isMetaClick(e)).toBe(true); + }); + }); + + describe('gl.utils.backOff', () => { + it('solves the promise from the callback', (done) => { + const expectedResponseValue = 'Success!'; + gl.utils.backOff((next, stop) => ( + new Promise((resolve) => { + resolve(expectedResponseValue); + }).then((resp) => { + stop(resp); + }) + )).then((respBackoff) => { + expect(respBackoff).toBe(expectedResponseValue); + done(); }); }); - describe('gl.utils.backOff', () => { - it('solves the promise from the callback', (done) => { - const expectedResponseValue = 'Success!'; - gl.utils.backOff((next, stop) => ( - new Promise((resolve) => { - resolve(expectedResponseValue); - }).then((resp) => { - stop(resp); - }) - )).then((respBackoff) => { - expect(respBackoff).toBe(expectedResponseValue); - done(); - }); + it('catches the rejected promise from the callback ', (done) => { + const errorMessage = 'Mistakes were made!'; + gl.utils.backOff((next, stop) => { + new Promise((resolve, reject) => { + reject(new Error(errorMessage)); + }).then((resp) => { + stop(resp); + }).catch(err => stop(err)); + }).catch((errBackoffResp) => { + expect(errBackoffResp instanceof Error).toBe(true); + expect(errBackoffResp.message).toBe(errorMessage); + done(); }); + }); - it('catches the rejected promise from the callback ', (done) => { - const errorMessage = 'Mistakes were made!'; - gl.utils.backOff((next, stop) => { - new Promise((resolve, reject) => { - reject(new Error(errorMessage)); - }).then((resp) => { + it('solves the promise correctly after retrying a third time', (done) => { + let numberOfCalls = 1; + const expectedResponseValue = 'Success!'; + gl.utils.backOff((next, stop) => ( + new Promise((resolve) => { + resolve(expectedResponseValue); + }).then((resp) => { + if (numberOfCalls < 3) { + numberOfCalls += 1; + next(); + } else { stop(resp); - }).catch(err => stop(err)); - }).catch((errBackoffResp) => { - expect(errBackoffResp instanceof Error).toBe(true); - expect(errBackoffResp.message).toBe(errorMessage); - done(); - }); - }); + } + }) + )).then((respBackoff) => { + expect(respBackoff).toBe(expectedResponseValue); + expect(numberOfCalls).toBe(3); + done(); + }); + }, 10000); + + it('rejects the backOff promise after timing out', (done) => { + const expectedResponseValue = 'Success!'; + gl.utils.backOff(next => ( + new Promise((resolve) => { + resolve(expectedResponseValue); + }).then(() => { + setTimeout(next(), 5000); // it will time out + }) + ), 3000).catch((errBackoffResp) => { + expect(errBackoffResp instanceof Error).toBe(true); + expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT'); + done(); + }); + }, 10000); + }); - it('solves the promise correctly after retrying a third time', (done) => { - let numberOfCalls = 1; - const expectedResponseValue = 'Success!'; - gl.utils.backOff((next, stop) => ( - new Promise((resolve) => { - resolve(expectedResponseValue); - }).then((resp) => { - if (numberOfCalls < 3) { - numberOfCalls += 1; - next(); - } else { - stop(resp); - } - }) - )).then((respBackoff) => { - expect(respBackoff).toBe(expectedResponseValue); - expect(numberOfCalls).toBe(3); - done(); - }); - }, 10000); - - it('rejects the backOff promise after timing out', (done) => { - const expectedResponseValue = 'Success!'; - gl.utils.backOff(next => ( - new Promise((resolve) => { - resolve(expectedResponseValue); - }).then(() => { - setTimeout(next(), 5000); // it will time out - }) - ), 3000).catch((errBackoffResp) => { - expect(errBackoffResp instanceof Error).toBe(true); - expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT'); - done(); - }); - }, 10000); - }); - - describe('gl.utils.setFavicon', () => { - it('should set page favicon to provided favicon', () => { - const faviconPath = '//custom_favicon'; - const fakeLink = { - setAttribute() {}, - }; - - spyOn(window.document, 'getElementById').and.callFake(() => fakeLink); - spyOn(fakeLink, 'setAttribute').and.callFake((attr, val) => { - expect(attr).toEqual('href'); - expect(val.indexOf(faviconPath) > -1).toBe(true); - }); - gl.utils.setFavicon(faviconPath); + describe('gl.utils.setFavicon', () => { + it('should set page favicon to provided favicon', () => { + const faviconPath = '//custom_favicon'; + const fakeLink = { + setAttribute() {}, + }; + + spyOn(window.document, 'getElementById').and.callFake(() => fakeLink); + spyOn(fakeLink, 'setAttribute').and.callFake((attr, val) => { + expect(attr).toEqual('href'); + expect(val.indexOf(faviconPath) > -1).toBe(true); }); + gl.utils.setFavicon(faviconPath); }); + }); - describe('gl.utils.resetFavicon', () => { - it('should reset page favicon to tanuki', () => { - const fakeLink = { - setAttribute() {}, - }; + describe('gl.utils.resetFavicon', () => { + it('should reset page favicon to tanuki', () => { + const fakeLink = { + setAttribute() {}, + }; - spyOn(window.document, 'getElementById').and.callFake(() => fakeLink); - spyOn(fakeLink, 'setAttribute').and.callFake((attr, val) => { - expect(attr).toEqual('href'); - expect(val).toMatch(/favicon/); - }); - gl.utils.resetFavicon(); + spyOn(window.document, 'getElementById').and.callFake(() => fakeLink); + spyOn(fakeLink, 'setAttribute').and.callFake((attr, val) => { + expect(attr).toEqual('href'); + expect(val).toMatch(/favicon/); }); + gl.utils.resetFavicon(); }); + }); - describe('gl.utils.setCiStatusFavicon', () => { - it('should set page favicon to CI status favicon based on provided status', () => { - const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1/status.json`; - const FAVICON_PATH = '//icon_status_success'; - const spySetFavicon = spyOn(gl.utils, 'setFavicon').and.stub(); - const spyResetFavicon = spyOn(gl.utils, 'resetFavicon').and.stub(); - spyOn($, 'ajax').and.callFake(function (options) { - options.success({ favicon: FAVICON_PATH }); - expect(spySetFavicon).toHaveBeenCalledWith(FAVICON_PATH); - options.success(); - expect(spyResetFavicon).toHaveBeenCalled(); - options.error(); - expect(spyResetFavicon).toHaveBeenCalled(); - }); - - gl.utils.setCiStatusFavicon(BUILD_URL); - }); + describe('gl.utils.setCiStatusFavicon', () => { + it('should set page favicon to CI status favicon based on provided status', () => { + const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1/status.json`; + const FAVICON_PATH = '//icon_status_success'; + const spySetFavicon = spyOn(gl.utils, 'setFavicon').and.stub(); + const spyResetFavicon = spyOn(gl.utils, 'resetFavicon').and.stub(); + spyOn($, 'ajax').and.callFake(function (options) { + options.success({ favicon: FAVICON_PATH }); + expect(spySetFavicon).toHaveBeenCalledWith(FAVICON_PATH); + options.success(); + expect(spyResetFavicon).toHaveBeenCalled(); + options.error(); + expect(spyResetFavicon).toHaveBeenCalled(); + }); + + gl.utils.setCiStatusFavicon(BUILD_URL); }); + }); - describe('gl.utils.ajaxPost', () => { - it('should perform `$.ajax` call and do `POST` request', () => { - const requestURL = '/some/random/api'; - const data = { keyname: 'value' }; - const ajaxSpy = spyOn($, 'ajax').and.callFake(() => {}); + describe('gl.utils.ajaxPost', () => { + it('should perform `$.ajax` call and do `POST` request', () => { + const requestURL = '/some/random/api'; + const data = { keyname: 'value' }; + const ajaxSpy = spyOn($, 'ajax').and.callFake(() => {}); - gl.utils.ajaxPost(requestURL, data); - expect(ajaxSpy.calls.allArgs()[0][0].type).toEqual('POST'); - }); + gl.utils.ajaxPost(requestURL, data); + expect(ajaxSpy.calls.allArgs()[0][0].type).toEqual('POST'); }); }); -})(); +}); diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js index 6ff42e2378d..6897074512e 100644 --- a/spec/javascripts/merge_request_spec.js +++ b/spec/javascripts/merge_request_spec.js @@ -5,58 +5,56 @@ import '~/merge_request'; import CloseReopenReportToggle from '~/close_reopen_report_toggle'; import IssuablesHelper from '~/helpers/issuables_helper'; -(function() { - describe('MergeRequest', function() { - describe('task lists', function() { - preloadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - beforeEach(function() { - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - return this.merge = new MergeRequest(); - }); - it('modifies the Markdown field', function() { - spyOn(jQuery, 'ajax').and.stub(); - const changeEvent = document.createEvent('HTMLEvents'); - changeEvent.initEvent('change', true, true); - $('input[type=checkbox]').attr('checked', true)[0].dispatchEvent(changeEvent); - return expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); - }); - return it('submits an ajax request on tasklist:changed', function() { - spyOn(jQuery, 'ajax').and.callFake(function(req) { - expect(req.type).toBe('PATCH'); - expect(req.url).toBe(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`); - return expect(req.data.merge_request.description).not.toBe(null); - }); - return $('.js-task-list-field').trigger('tasklist:changed'); +describe('MergeRequest', function() { + describe('task lists', function() { + preloadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + beforeEach(function() { + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + return this.merge = new MergeRequest(); + }); + it('modifies the Markdown field', function() { + spyOn(jQuery, 'ajax').and.stub(); + const changeEvent = document.createEvent('HTMLEvents'); + changeEvent.initEvent('change', true, true); + $('input[type=checkbox]').attr('checked', true)[0].dispatchEvent(changeEvent); + return expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); + }); + return it('submits an ajax request on tasklist:changed', function() { + spyOn(jQuery, 'ajax').and.callFake(function(req) { + expect(req.type).toBe('PATCH'); + expect(req.url).toBe(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`); + return expect(req.data.merge_request.description).not.toBe(null); }); + return $('.js-task-list-field').trigger('tasklist:changed'); }); + }); - describe('class constructor', () => { - it('calls .initCloseReopenReport', () => { - spyOn(IssuablesHelper, 'initCloseReopenReport'); + describe('class constructor', () => { + it('calls .initCloseReopenReport', () => { + spyOn(IssuablesHelper, 'initCloseReopenReport'); - new MergeRequest(); // eslint-disable-line no-new + new MergeRequest(); // eslint-disable-line no-new - expect(IssuablesHelper.initCloseReopenReport).toHaveBeenCalled(); - }); + expect(IssuablesHelper.initCloseReopenReport).toHaveBeenCalled(); + }); - it('calls .initDroplab', () => { - const container = jasmine.createSpyObj('container', ['querySelector']); - const dropdownTrigger = {}; - const dropdownList = {}; - const button = {}; + it('calls .initDroplab', () => { + const container = jasmine.createSpyObj('container', ['querySelector']); + const dropdownTrigger = {}; + const dropdownList = {}; + const button = {}; - spyOn(CloseReopenReportToggle.prototype, 'initDroplab'); - spyOn(document, 'querySelector').and.returnValue(container); - container.querySelector.and.returnValues(dropdownTrigger, dropdownList, button); + spyOn(CloseReopenReportToggle.prototype, 'initDroplab'); + spyOn(document, 'querySelector').and.returnValue(container); + container.querySelector.and.returnValues(dropdownTrigger, dropdownList, button); - new MergeRequest(); // eslint-disable-line no-new + new MergeRequest(); // eslint-disable-line no-new - expect(document.querySelector).toHaveBeenCalledWith('.js-issuable-close-dropdown'); - expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-toggle'); - expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-menu'); - expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-button'); - expect(CloseReopenReportToggle.prototype.initDroplab).toHaveBeenCalled(); - }); + expect(document.querySelector).toHaveBeenCalledWith('.js-issuable-close-dropdown'); + expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-toggle'); + expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-menu'); + expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-button'); + expect(CloseReopenReportToggle.prototype.initDroplab).toHaveBeenCalled(); }); }); -}).call(window); +}); diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index dc40244c20e..accc10ce7ec 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -10,399 +10,397 @@ import '~/files_comment_button'; import '~/notes'; import 'vendor/jquery.scrollTo'; -(function () { - describe('MergeRequestTabs', function () { - var stubLocation = {}; - var setLocation = function (stubs) { - var defaults = { - pathname: '', - search: '', - hash: '' - }; - $.extend(stubLocation, defaults, stubs || {}); +describe('MergeRequestTabs', function () { + var stubLocation = {}; + var setLocation = function (stubs) { + var defaults = { + pathname: '', + search: '', + hash: '' }; + $.extend(stubLocation, defaults, stubs || {}); + }; + + const inlineChangesTabJsonFixture = 'merge_request_diffs/inline_changes_tab_with_comments.json'; + const parallelChangesTabJsonFixture = 'merge_request_diffs/parallel_changes_tab_with_comments.json'; + preloadFixtures( + 'merge_requests/merge_request_with_task_list.html.raw', + 'merge_requests/diff_comment.html.raw', + inlineChangesTabJsonFixture, + parallelChangesTabJsonFixture + ); + + beforeEach(function () { + this.class = new gl.MergeRequestTabs({ stubLocation: stubLocation }); + setLocation(); + + this.spies = { + history: spyOn(window.history, 'replaceState').and.callFake(function () {}) + }; + }); - const inlineChangesTabJsonFixture = 'merge_request_diffs/inline_changes_tab_with_comments.json'; - const parallelChangesTabJsonFixture = 'merge_request_diffs/parallel_changes_tab_with_comments.json'; - preloadFixtures( - 'merge_requests/merge_request_with_task_list.html.raw', - 'merge_requests/diff_comment.html.raw', - inlineChangesTabJsonFixture, - parallelChangesTabJsonFixture - ); + afterEach(function () { + this.class.unbindEvents(); + this.class.destroyPipelinesView(); + }); + describe('activateTab', function () { beforeEach(function () { - this.class = new gl.MergeRequestTabs({ stubLocation: stubLocation }); - setLocation(); - - this.spies = { - history: spyOn(window.history, 'replaceState').and.callFake(function () {}) - }; + spyOn($, 'ajax').and.callFake(function () {}); + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + this.subject = this.class.activateTab; }); - - afterEach(function () { - this.class.unbindEvents(); - this.class.destroyPipelinesView(); + it('shows the notes tab when action is show', function () { + this.subject('show'); + expect($('#notes')).toHaveClass('active'); }); - - describe('activateTab', function () { - beforeEach(function () { - spyOn($, 'ajax').and.callFake(function () {}); - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - this.subject = this.class.activateTab; - }); - it('shows the notes tab when action is show', function () { - this.subject('show'); - expect($('#notes')).toHaveClass('active'); - }); - it('shows the commits tab when action is commits', function () { - this.subject('commits'); - expect($('#commits')).toHaveClass('active'); - }); - it('shows the diffs tab when action is diffs', function () { - this.subject('diffs'); - expect($('#diffs')).toHaveClass('active'); - }); + it('shows the commits tab when action is commits', function () { + this.subject('commits'); + expect($('#commits')).toHaveClass('active'); }); + it('shows the diffs tab when action is diffs', function () { + this.subject('diffs'); + expect($('#diffs')).toHaveClass('active'); + }); + }); - describe('opensInNewTab', function () { - var tabUrl; - var windowTarget = '_blank'; - - beforeEach(function () { - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - - tabUrl = $('.commits-tab a').attr('href'); - - spyOn($.fn, 'attr').and.returnValue(tabUrl); - }); - - describe('meta click', () => { - beforeEach(function () { - spyOn(gl.utils, 'isMetaClick').and.returnValue(true); - }); + describe('opensInNewTab', function () { + var tabUrl; + var windowTarget = '_blank'; - it('opens page when commits link is clicked', function () { - spyOn(window, 'open').and.callFake(function (url, name) { - expect(url).toEqual(tabUrl); - expect(name).toEqual(windowTarget); - }); + beforeEach(function () { + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - this.class.bindEvents(); - document.querySelector('.merge-request-tabs .commits-tab a').click(); - }); + tabUrl = $('.commits-tab a').attr('href'); - it('opens page when commits badge is clicked', function () { - spyOn(window, 'open').and.callFake(function (url, name) { - expect(url).toEqual(tabUrl); - expect(name).toEqual(windowTarget); - }); + spyOn($.fn, 'attr').and.returnValue(tabUrl); + }); - this.class.bindEvents(); - document.querySelector('.merge-request-tabs .commits-tab a .badge').click(); - }); + describe('meta click', () => { + beforeEach(function () { + spyOn(gl.utils, 'isMetaClick').and.returnValue(true); }); - it('opens page tab in a new browser tab with Ctrl+Click - Windows/Linux', function () { + it('opens page when commits link is clicked', function () { spyOn(window, 'open').and.callFake(function (url, name) { expect(url).toEqual(tabUrl); expect(name).toEqual(windowTarget); }); - this.class.clickTab({ - metaKey: false, - ctrlKey: true, - which: 1, - stopImmediatePropagation: function () {} - }); + this.class.bindEvents(); + document.querySelector('.merge-request-tabs .commits-tab a').click(); }); - it('opens page tab in a new browser tab with Cmd+Click - Mac', function () { + it('opens page when commits badge is clicked', function () { spyOn(window, 'open').and.callFake(function (url, name) { expect(url).toEqual(tabUrl); expect(name).toEqual(windowTarget); }); - this.class.clickTab({ - metaKey: true, - ctrlKey: false, - which: 1, - stopImmediatePropagation: function () {} - }); + this.class.bindEvents(); + document.querySelector('.merge-request-tabs .commits-tab a .badge').click(); }); + }); - it('opens page tab in a new browser tab with Middle-click - Mac/PC', function () { - spyOn(window, 'open').and.callFake(function (url, name) { - expect(url).toEqual(tabUrl); - expect(name).toEqual(windowTarget); - }); + it('opens page tab in a new browser tab with Ctrl+Click - Windows/Linux', function () { + spyOn(window, 'open').and.callFake(function (url, name) { + expect(url).toEqual(tabUrl); + expect(name).toEqual(windowTarget); + }); - this.class.clickTab({ - metaKey: false, - ctrlKey: false, - which: 2, - stopImmediatePropagation: function () {} - }); + this.class.clickTab({ + metaKey: false, + ctrlKey: true, + which: 1, + stopImmediatePropagation: function () {} }); }); - describe('setCurrentAction', function () { - beforeEach(function () { - spyOn($, 'ajax').and.callFake(function () {}); - this.subject = this.class.setCurrentAction; + it('opens page tab in a new browser tab with Cmd+Click - Mac', function () { + spyOn(window, 'open').and.callFake(function (url, name) { + expect(url).toEqual(tabUrl); + expect(name).toEqual(windowTarget); }); - it('changes from commits', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/commits' - }); - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); - expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); + this.class.clickTab({ + metaKey: true, + ctrlKey: false, + which: 1, + stopImmediatePropagation: function () {} }); + }); - it('changes from diffs', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/diffs' - }); + it('opens page tab in a new browser tab with Middle-click - Mac/PC', function () { + spyOn(window, 'open').and.callFake(function (url, name) { + expect(url).toEqual(tabUrl); + expect(name).toEqual(windowTarget); + }); - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); - expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + this.class.clickTab({ + metaKey: false, + ctrlKey: false, + which: 2, + stopImmediatePropagation: function () {} }); + }); + }); - it('changes from diffs.html', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/diffs.html' - }); - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); - expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + describe('setCurrentAction', function () { + beforeEach(function () { + spyOn($, 'ajax').and.callFake(function () {}); + this.subject = this.class.setCurrentAction; + }); + + it('changes from commits', function () { + setLocation({ + pathname: '/foo/bar/merge_requests/1/commits' }); + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); + }); - it('changes from notes', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1' - }); - expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); - expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + it('changes from diffs', function () { + setLocation({ + pathname: '/foo/bar/merge_requests/1/diffs' }); - it('includes search parameters and hash string', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/diffs', - search: '?view=parallel', - hash: '#L15-35' - }); - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35'); + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + }); + + it('changes from diffs.html', function () { + setLocation({ + pathname: '/foo/bar/merge_requests/1/diffs.html' }); + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + }); - it('replaces the current history state', function () { - var newState; - setLocation({ - pathname: '/foo/bar/merge_requests/1' - }); - newState = this.subject('commits'); - expect(this.spies.history).toHaveBeenCalledWith({ - url: newState - }, document.title, newState); + it('changes from notes', function () { + setLocation({ + pathname: '/foo/bar/merge_requests/1' }); + expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); + expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + }); - it('treats "show" like "notes"', function () { - setLocation({ - pathname: '/foo/bar/merge_requests/1/commits' - }); - expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + it('includes search parameters and hash string', function () { + setLocation({ + pathname: '/foo/bar/merge_requests/1/diffs', + search: '?view=parallel', + hash: '#L15-35' }); + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35'); }); - describe('tabShown', () => { - beforeEach(function () { - spyOn($, 'ajax').and.callFake(function (options) { - options.success({ html: '' }); - }); - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + it('replaces the current history state', function () { + var newState; + setLocation({ + pathname: '/foo/bar/merge_requests/1' }); + newState = this.subject('commits'); + expect(this.spies.history).toHaveBeenCalledWith({ + url: newState + }, document.title, newState); + }); - describe('with "Side-by-side"/parallel diff view', () => { - beforeEach(function () { - this.class.diffViewType = () => 'parallel'; - gl.Diff.prototype.diffViewType = () => 'parallel'; - }); + it('treats "show" like "notes"', function () { + setLocation({ + pathname: '/foo/bar/merge_requests/1/commits' + }); + expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + }); + }); - it('maintains `container-limited` for pipelines tab', function (done) { - const asyncClick = function (selector) { - return new Promise((resolve) => { - setTimeout(() => { - document.querySelector(selector).click(); - resolve(); - }); - }); - }; - asyncClick('.merge-request-tabs .pipelines-tab a') - .then(() => asyncClick('.merge-request-tabs .diffs-tab a')) - .then(() => asyncClick('.merge-request-tabs .pipelines-tab a')) - .then(() => { - const hasContainerLimitedClass = document.querySelector('.content-wrapper .container-fluid').classList.contains('container-limited'); - expect(hasContainerLimitedClass).toBe(true); - }) - .then(done) - .catch((err) => { - done.fail(`Something went wrong clicking MR tabs: ${err.message}\n${err.stack}`); - }); - }); + describe('tabShown', () => { + beforeEach(function () { + spyOn($, 'ajax').and.callFake(function (options) { + options.success({ html: '' }); + }); + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + }); - it('maintains `container-limited` when switching from "Changes" tab before it loads', function (done) { - const asyncClick = function (selector) { - return new Promise((resolve) => { - setTimeout(() => { - document.querySelector(selector).click(); - resolve(); - }); + describe('with "Side-by-side"/parallel diff view', () => { + beforeEach(function () { + this.class.diffViewType = () => 'parallel'; + gl.Diff.prototype.diffViewType = () => 'parallel'; + }); + + it('maintains `container-limited` for pipelines tab', function (done) { + const asyncClick = function (selector) { + return new Promise((resolve) => { + setTimeout(() => { + document.querySelector(selector).click(); + resolve(); }); - }; - - asyncClick('.merge-request-tabs .diffs-tab a') - .then(() => asyncClick('.merge-request-tabs .notes-tab a')) - .then(() => { - const hasContainerLimitedClass = document.querySelector('.content-wrapper .container-fluid').classList.contains('container-limited'); - expect(hasContainerLimitedClass).toBe(true); - }) - .then(done) - .catch((err) => { - done.fail(`Something went wrong clicking MR tabs: ${err.message}\n${err.stack}`); + }); + }; + asyncClick('.merge-request-tabs .pipelines-tab a') + .then(() => asyncClick('.merge-request-tabs .diffs-tab a')) + .then(() => asyncClick('.merge-request-tabs .pipelines-tab a')) + .then(() => { + const hasContainerLimitedClass = document.querySelector('.content-wrapper .container-fluid').classList.contains('container-limited'); + expect(hasContainerLimitedClass).toBe(true); + }) + .then(done) + .catch((err) => { + done.fail(`Something went wrong clicking MR tabs: ${err.message}\n${err.stack}`); + }); + }); + + it('maintains `container-limited` when switching from "Changes" tab before it loads', function (done) { + const asyncClick = function (selector) { + return new Promise((resolve) => { + setTimeout(() => { + document.querySelector(selector).click(); + resolve(); }); - }); + }); + }; + + asyncClick('.merge-request-tabs .diffs-tab a') + .then(() => asyncClick('.merge-request-tabs .notes-tab a')) + .then(() => { + const hasContainerLimitedClass = document.querySelector('.content-wrapper .container-fluid').classList.contains('container-limited'); + expect(hasContainerLimitedClass).toBe(true); + }) + .then(done) + .catch((err) => { + done.fail(`Something went wrong clicking MR tabs: ${err.message}\n${err.stack}`); + }); }); }); + }); - describe('loadDiff', function () { - beforeEach(() => { - loadFixtures('merge_requests/diff_comment.html.raw'); - spyOn(window.gl.utils, 'getPagePath').and.returnValue('merge_requests'); - window.gl.ImageFile = () => {}; - window.notes = new Notes('', []); - spyOn(window.notes, 'toggleDiffNote').and.callThrough(); - }); + describe('loadDiff', function () { + beforeEach(() => { + loadFixtures('merge_requests/diff_comment.html.raw'); + spyOn(window.gl.utils, 'getPagePath').and.returnValue('merge_requests'); + window.gl.ImageFile = () => {}; + window.notes = new Notes('', []); + spyOn(window.notes, 'toggleDiffNote').and.callThrough(); + }); - afterEach(() => { - delete window.gl.ImageFile; - delete window.notes; + afterEach(() => { + delete window.gl.ImageFile; + delete window.notes; + }); + + it('requires an absolute pathname', function () { + spyOn($, 'ajax').and.callFake(function (options) { + expect(options.url).toEqual('/foo/bar/merge_requests/1/diffs.json'); }); - it('requires an absolute pathname', function () { - spyOn($, 'ajax').and.callFake(function (options) { - expect(options.url).toEqual('/foo/bar/merge_requests/1/diffs.json'); - }); + this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + }); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - }); + describe('with inline diff', () => { + let noteId; + let noteLineNumId; - describe('with inline diff', () => { - let noteId; - let noteLineNumId; - - beforeEach(() => { - const diffsResponse = getJSONFixture(inlineChangesTabJsonFixture); - - const $html = $(diffsResponse.html); - noteId = $html.find('.note').attr('id'); - noteLineNumId = $html - .find('.note') - .closest('.notes_holder') - .prev('.line_holder') - .find('a[data-linenumber]') - .attr('href') - .replace('#', ''); - - spyOn($, 'ajax').and.callFake(function (options) { - options.success(diffsResponse); - }); + beforeEach(() => { + const diffsResponse = getJSONFixture(inlineChangesTabJsonFixture); + + const $html = $(diffsResponse.html); + noteId = $html.find('.note').attr('id'); + noteLineNumId = $html + .find('.note') + .closest('.notes_holder') + .prev('.line_holder') + .find('a[data-linenumber]') + .attr('href') + .replace('#', ''); + + spyOn($, 'ajax').and.callFake(function (options) { + options.success(diffsResponse); }); + }); - describe('with note fragment hash', () => { - it('should expand and scroll to linked fragment hash #note_xxx', function () { - spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteId); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + describe('with note fragment hash', () => { + it('should expand and scroll to linked fragment hash #note_xxx', function () { + spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteId); + this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - expect(noteId.length).toBeGreaterThan(0); - expect(window.notes.toggleDiffNote).toHaveBeenCalledWith({ - target: jasmine.any(Object), - lineType: 'old', - forceShow: true, - }); + expect(noteId.length).toBeGreaterThan(0); + expect(window.notes.toggleDiffNote).toHaveBeenCalledWith({ + target: jasmine.any(Object), + lineType: 'old', + forceShow: true, }); + }); - it('should gracefully ignore non-existant fragment hash', function () { - spyOn(window.gl.utils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist'); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + it('should gracefully ignore non-existant fragment hash', function () { + spyOn(window.gl.utils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist'); + this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - expect(window.notes.toggleDiffNote).not.toHaveBeenCalled(); - }); + expect(window.notes.toggleDiffNote).not.toHaveBeenCalled(); }); + }); - describe('with line number fragment hash', () => { - it('should gracefully ignore line number fragment hash', function () { - spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteLineNumId); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + describe('with line number fragment hash', () => { + it('should gracefully ignore line number fragment hash', function () { + spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteLineNumId); + this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - expect(noteLineNumId.length).toBeGreaterThan(0); - expect(window.notes.toggleDiffNote).not.toHaveBeenCalled(); - }); + expect(noteLineNumId.length).toBeGreaterThan(0); + expect(window.notes.toggleDiffNote).not.toHaveBeenCalled(); }); }); + }); - describe('with parallel diff', () => { - let noteId; - let noteLineNumId; - - beforeEach(() => { - const diffsResponse = getJSONFixture(parallelChangesTabJsonFixture); - - const $html = $(diffsResponse.html); - noteId = $html.find('.note').attr('id'); - noteLineNumId = $html - .find('.note') - .closest('.notes_holder') - .prev('.line_holder') - .find('a[data-linenumber]') - .attr('href') - .replace('#', ''); - - spyOn($, 'ajax').and.callFake(function (options) { - options.success(diffsResponse); - }); + describe('with parallel diff', () => { + let noteId; + let noteLineNumId; + + beforeEach(() => { + const diffsResponse = getJSONFixture(parallelChangesTabJsonFixture); + + const $html = $(diffsResponse.html); + noteId = $html.find('.note').attr('id'); + noteLineNumId = $html + .find('.note') + .closest('.notes_holder') + .prev('.line_holder') + .find('a[data-linenumber]') + .attr('href') + .replace('#', ''); + + spyOn($, 'ajax').and.callFake(function (options) { + options.success(diffsResponse); }); + }); - describe('with note fragment hash', () => { - it('should expand and scroll to linked fragment hash #note_xxx', function () { - spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteId); + describe('with note fragment hash', () => { + it('should expand and scroll to linked fragment hash #note_xxx', function () { + spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteId); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - expect(noteId.length).toBeGreaterThan(0); - expect(window.notes.toggleDiffNote).toHaveBeenCalledWith({ - target: jasmine.any(Object), - lineType: 'new', - forceShow: true, - }); + expect(noteId.length).toBeGreaterThan(0); + expect(window.notes.toggleDiffNote).toHaveBeenCalledWith({ + target: jasmine.any(Object), + lineType: 'new', + forceShow: true, }); + }); - it('should gracefully ignore non-existant fragment hash', function () { - spyOn(window.gl.utils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist'); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + it('should gracefully ignore non-existant fragment hash', function () { + spyOn(window.gl.utils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist'); + this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - expect(window.notes.toggleDiffNote).not.toHaveBeenCalled(); - }); + expect(window.notes.toggleDiffNote).not.toHaveBeenCalled(); }); + }); - describe('with line number fragment hash', () => { - it('should gracefully ignore line number fragment hash', function () { - spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteLineNumId); - this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); + describe('with line number fragment hash', () => { + it('should gracefully ignore line number fragment hash', function () { + spyOn(window.gl.utils, 'getLocationHash').and.returnValue(noteLineNumId); + this.class.loadDiff('/foo/bar/merge_requests/1/diffs'); - expect(noteLineNumId.length).toBeGreaterThan(0); - expect(window.notes.toggleDiffNote).not.toHaveBeenCalled(); - }); + expect(noteLineNumId.length).toBeGreaterThan(0); + expect(window.notes.toggleDiffNote).not.toHaveBeenCalled(); }); }); }); }); -}).call(window); +}); diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index 2c096ed08a8..3394ea5f9d4 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -8,816 +8,814 @@ import '~/render_gfm'; import '~/render_math'; import '~/notes'; -(function() { - window.gon || (window.gon = {}); - window.gl = window.gl || {}; - gl.utils = gl.utils || {}; - - const htmlEscape = (comment) => { - const escapedString = comment.replace(/["&'<>]/g, (a) => { - const escapedToken = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '`': '`' - }[a]; - - return escapedToken; - }); - - return escapedString; - }; - - describe('Notes', function() { - const FLASH_TYPE_ALERT = 'alert'; - var commentsTemplate = 'issues/issue_with_comment.html.raw'; - preloadFixtures(commentsTemplate); - - beforeEach(function () { - loadFixtures(commentsTemplate); - gl.utils.disableButtonIfEmptyField = _.noop; - window.project_uploads_path = 'http://test.host/uploads'; - $('body').data('page', 'projects:issues:show'); - }); - - describe('task lists', function() { - beforeEach(function() { - $('.js-comment-button').on('click', function(e) { - e.preventDefault(); - }); - this.notes = new Notes('', []); - }); +window.gon || (window.gon = {}); +window.gl = window.gl || {}; +gl.utils = gl.utils || {}; + +const htmlEscape = (comment) => { + const escapedString = comment.replace(/["&'<>]/g, (a) => { + const escapedToken = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }[a]; + + return escapedToken; + }); + + return escapedString; +}; + +describe('Notes', function() { + const FLASH_TYPE_ALERT = 'alert'; + var commentsTemplate = 'issues/issue_with_comment.html.raw'; + preloadFixtures(commentsTemplate); + + beforeEach(function () { + loadFixtures(commentsTemplate); + gl.utils.disableButtonIfEmptyField = _.noop; + window.project_uploads_path = 'http://test.host/uploads'; + $('body').data('page', 'projects:issues:show'); + }); - it('modifies the Markdown field', function() { - const changeEvent = document.createEvent('HTMLEvents'); - changeEvent.initEvent('change', true, true); - $('input[type=checkbox]').attr('checked', true)[0].dispatchEvent(changeEvent); - expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); + describe('task lists', function() { + beforeEach(function() { + $('.js-comment-button').on('click', function(e) { + e.preventDefault(); }); + this.notes = new Notes('', []); + }); - it('submits an ajax request on tasklist:changed', function() { - spyOn(jQuery, 'ajax').and.callFake(function(req) { - expect(req.type).toBe('PATCH'); - expect(req.url).toBe('http://test.host/frontend-fixtures/issues-project/notes/1'); - return expect(req.data.note).not.toBe(null); - }); - $('.js-task-list-field').trigger('tasklist:changed'); + it('modifies the Markdown field', function() { + const changeEvent = document.createEvent('HTMLEvents'); + changeEvent.initEvent('change', true, true); + $('input[type=checkbox]').attr('checked', true)[0].dispatchEvent(changeEvent); + expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); + }); + + it('submits an ajax request on tasklist:changed', function() { + spyOn(jQuery, 'ajax').and.callFake(function(req) { + expect(req.type).toBe('PATCH'); + expect(req.url).toBe('http://test.host/frontend-fixtures/issues-project/notes/1'); + return expect(req.data.note).not.toBe(null); }); + $('.js-task-list-field').trigger('tasklist:changed'); }); + }); - describe('comments', function() { - var textarea = '.js-note-text'; + describe('comments', function() { + var textarea = '.js-note-text'; - beforeEach(function() { - this.notes = new Notes('', []); + beforeEach(function() { + this.notes = new Notes('', []); - this.autoSizeSpy = spyOnEvent($(textarea), 'autosize:update'); - spyOn(this.notes, 'renderNote').and.stub(); + this.autoSizeSpy = spyOnEvent($(textarea), 'autosize:update'); + spyOn(this.notes, 'renderNote').and.stub(); - $(textarea).data('autosave', { - reset: function() {} - }); + $(textarea).data('autosave', { + reset: function() {} + }); - $('.js-comment-button').on('click', (e) => { - const $form = $(this); - e.preventDefault(); - this.notes.addNote($form); - this.notes.reenableTargetFormSubmitButton(e); - this.notes.resetMainTargetForm(e); - }); + $('.js-comment-button').on('click', (e) => { + const $form = $(this); + e.preventDefault(); + this.notes.addNote($form); + this.notes.reenableTargetFormSubmitButton(e); + this.notes.resetMainTargetForm(e); }); + }); - it('autosizes after comment submission', function() { - $(textarea).text('This is an example comment note'); - expect(this.autoSizeSpy).not.toHaveBeenTriggered(); + it('autosizes after comment submission', function() { + $(textarea).text('This is an example comment note'); + expect(this.autoSizeSpy).not.toHaveBeenTriggered(); - $('.js-comment-button').click(); - expect(this.autoSizeSpy).toHaveBeenTriggered(); - }); + $('.js-comment-button').click(); + expect(this.autoSizeSpy).toHaveBeenTriggered(); + }); + }); + + describe('updateNote', () => { + let sampleComment; + let noteEntity; + let $form; + let $notesContainer; + + beforeEach(() => { + this.notes = new Notes('', []); + window.gon.current_username = 'root'; + window.gon.current_user_fullname = 'Administrator'; + sampleComment = 'foo'; + noteEntity = { + id: 1234, + html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> + <div class="note-text">${sampleComment}</div> + </li>`, + note: sampleComment, + valid: true + }; + $form = $('form.js-main-target-form'); + $notesContainer = $('ul.main-notes-list'); + $form.find('textarea.js-note-text').val(sampleComment); }); - describe('updateNote', () => { - let sampleComment; - let noteEntity; - let $form; - let $notesContainer; + it('updates note and resets edit form', () => { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + spyOn(this.notes, 'revertNoteEditForm'); + spyOn(this.notes, 'setupNewNote'); - beforeEach(() => { - this.notes = new Notes('', []); - window.gon.current_username = 'root'; - window.gon.current_user_fullname = 'Administrator'; - sampleComment = 'foo'; - noteEntity = { - id: 1234, - html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> - <div class="note-text">${sampleComment}</div> - </li>`, - note: sampleComment, - valid: true - }; - $form = $('form.js-main-target-form'); - $notesContainer = $('ul.main-notes-list'); - $form.find('textarea.js-note-text').val(sampleComment); - }); + $('.js-comment-button').click(); + deferred.resolve(noteEntity); - it('updates note and resets edit form', () => { - const deferred = $.Deferred(); - spyOn($, 'ajax').and.returnValue(deferred.promise()); - spyOn(this.notes, 'revertNoteEditForm'); - spyOn(this.notes, 'setupNewNote'); + const $targetNote = $notesContainer.find(`#note_${noteEntity.id}`); + const updatedNote = Object.assign({}, noteEntity); + updatedNote.note = 'bar'; + this.notes.updateNote(updatedNote, $targetNote); - $('.js-comment-button').click(); - deferred.resolve(noteEntity); + expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote); + expect(this.notes.setupNewNote).toHaveBeenCalled(); + }); + }); - const $targetNote = $notesContainer.find(`#note_${noteEntity.id}`); - const updatedNote = Object.assign({}, noteEntity); - updatedNote.note = 'bar'; - this.notes.updateNote(updatedNote, $targetNote); + describe('updateNoteTargetSelector', () => { + const hash = 'note_foo'; + let $note; - expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote); - expect(this.notes.setupNewNote).toHaveBeenCalled(); - }); + beforeEach(() => { + $note = $(`<div id="${hash}"></div>`); + spyOn($note, 'filter').and.callThrough(); + spyOn($note, 'toggleClass').and.callThrough(); }); - describe('updateNoteTargetSelector', () => { - const hash = 'note_foo'; - let $note; + it('sets target when hash matches', () => { + spyOn(gl.utils, 'getLocationHash'); + gl.utils.getLocationHash.and.returnValue(hash); - beforeEach(() => { - $note = $(`<div id="${hash}"></div>`); - spyOn($note, 'filter').and.callThrough(); - spyOn($note, 'toggleClass').and.callThrough(); - }); + Notes.updateNoteTargetSelector($note); - it('sets target when hash matches', () => { - spyOn(gl.utils, 'getLocationHash'); - gl.utils.getLocationHash.and.returnValue(hash); + expect($note.filter).toHaveBeenCalledWith(`#${hash}`); + expect($note.toggleClass).toHaveBeenCalledWith('target', true); + }); - Notes.updateNoteTargetSelector($note); + it('unsets target when hash does not match', () => { + spyOn(gl.utils, 'getLocationHash'); + gl.utils.getLocationHash.and.returnValue('note_doesnotexist'); - expect($note.filter).toHaveBeenCalledWith(`#${hash}`); - expect($note.toggleClass).toHaveBeenCalledWith('target', true); - }); + Notes.updateNoteTargetSelector($note); - it('unsets target when hash does not match', () => { - spyOn(gl.utils, 'getLocationHash'); - gl.utils.getLocationHash.and.returnValue('note_doesnotexist'); + expect($note.toggleClass).toHaveBeenCalledWith('target', false); + }); - Notes.updateNoteTargetSelector($note); + it('unsets target when there is not a hash fragment anymore', () => { + spyOn(gl.utils, 'getLocationHash'); + gl.utils.getLocationHash.and.returnValue(null); - expect($note.toggleClass).toHaveBeenCalledWith('target', false); - }); + Notes.updateNoteTargetSelector($note); - it('unsets target when there is not a hash fragment anymore', () => { - spyOn(gl.utils, 'getLocationHash'); - gl.utils.getLocationHash.and.returnValue(null); + expect($note.toggleClass).toHaveBeenCalledWith('target', false); + }); + }); + + describe('renderNote', () => { + let notes; + let note; + let $notesList; + + beforeEach(() => { + note = { + id: 1, + valid: true, + note: 'heya', + html: '<div>heya</div>', + }; + $notesList = jasmine.createSpyObj('$notesList', [ + 'find', + 'append', + ]); + + notes = jasmine.createSpyObj('notes', [ + 'setupNewNote', + 'refresh', + 'collapseLongCommitList', + 'updateNotesCount', + 'putConflictEditWarningInPlace' + ]); + notes.taskList = jasmine.createSpyObj('tasklist', ['init']); + notes.note_ids = []; + notes.updatedNotesTrackingMap = {}; + + spyOn(gl.utils, 'localTimeAgo'); + spyOn(Notes, 'isNewNote').and.callThrough(); + spyOn(Notes, 'isUpdatedNote').and.callThrough(); + spyOn(Notes, 'animateAppendNote').and.callThrough(); + spyOn(Notes, 'animateUpdateNote').and.callThrough(); + }); - Notes.updateNoteTargetSelector($note); + describe('when adding note', () => { + it('should call .animateAppendNote', () => { + Notes.isNewNote.and.returnValue(true); + Notes.prototype.renderNote.call(notes, note, null, $notesList); - expect($note.toggleClass).toHaveBeenCalledWith('target', false); + expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, $notesList); }); }); - describe('renderNote', () => { - let notes; - let note; - let $notesList; + describe('when note was edited', () => { + it('should call .animateUpdateNote', () => { + Notes.isNewNote.and.returnValue(false); + Notes.isUpdatedNote.and.returnValue(true); + const $note = $('<div>'); + $notesList.find.and.returnValue($note); + const $newNote = $(note.html); + Notes.animateUpdateNote.and.returnValue($newNote); - beforeEach(() => { - note = { - id: 1, - valid: true, - note: 'heya', - html: '<div>heya</div>', - }; - $notesList = jasmine.createSpyObj('$notesList', [ - 'find', - 'append', - ]); - - notes = jasmine.createSpyObj('notes', [ - 'setupNewNote', - 'refresh', - 'collapseLongCommitList', - 'updateNotesCount', - 'putConflictEditWarningInPlace' - ]); - notes.taskList = jasmine.createSpyObj('tasklist', ['init']); - notes.note_ids = []; - notes.updatedNotesTrackingMap = {}; - - spyOn(gl.utils, 'localTimeAgo'); - spyOn(Notes, 'isNewNote').and.callThrough(); - spyOn(Notes, 'isUpdatedNote').and.callThrough(); - spyOn(Notes, 'animateAppendNote').and.callThrough(); - spyOn(Notes, 'animateUpdateNote').and.callThrough(); + Notes.prototype.renderNote.call(notes, note, null, $notesList); + + expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note); + expect(notes.setupNewNote).toHaveBeenCalledWith($newNote); }); - describe('when adding note', () => { - it('should call .animateAppendNote', () => { - Notes.isNewNote.and.returnValue(true); + describe('while editing', () => { + it('should update textarea if nothing has been touched', () => { + Notes.isNewNote.and.returnValue(false); + Notes.isUpdatedNote.and.returnValue(true); + const $note = $(`<div class="is-editing"> + <div class="original-note-content">initial</div> + <textarea class="js-note-text">initial</textarea> + </div>`); + $notesList.find.and.returnValue($note); Notes.prototype.renderNote.call(notes, note, null, $notesList); - expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, $notesList); + expect($note.find('.js-note-text').val()).toEqual(note.note); }); - }); - describe('when note was edited', () => { - it('should call .animateUpdateNote', () => { + it('should call .putConflictEditWarningInPlace', () => { Notes.isNewNote.and.returnValue(false); Notes.isUpdatedNote.and.returnValue(true); - const $note = $('<div>'); + const $note = $(`<div class="is-editing"> + <div class="original-note-content">initial</div> + <textarea class="js-note-text">different</textarea> + </div>`); $notesList.find.and.returnValue($note); - const $newNote = $(note.html); - Notes.animateUpdateNote.and.returnValue($newNote); - Notes.prototype.renderNote.call(notes, note, null, $notesList); - expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note); - expect(notes.setupNewNote).toHaveBeenCalledWith($newNote); - }); - - describe('while editing', () => { - it('should update textarea if nothing has been touched', () => { - Notes.isNewNote.and.returnValue(false); - Notes.isUpdatedNote.and.returnValue(true); - const $note = $(`<div class="is-editing"> - <div class="original-note-content">initial</div> - <textarea class="js-note-text">initial</textarea> - </div>`); - $notesList.find.and.returnValue($note); - Notes.prototype.renderNote.call(notes, note, null, $notesList); - - expect($note.find('.js-note-text').val()).toEqual(note.note); - }); - - it('should call .putConflictEditWarningInPlace', () => { - Notes.isNewNote.and.returnValue(false); - Notes.isUpdatedNote.and.returnValue(true); - const $note = $(`<div class="is-editing"> - <div class="original-note-content">initial</div> - <textarea class="js-note-text">different</textarea> - </div>`); - $notesList.find.and.returnValue($note); - Notes.prototype.renderNote.call(notes, note, null, $notesList); - - expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith(note, $note); - }); + expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith(note, $note); }); }); }); + }); - describe('isUpdatedNote', () => { - it('should consider same note text as the same', () => { - const result = Notes.isUpdatedNote( - { - note: 'initial' - }, - $(`<div> - <div class="original-note-content">initial</div> - </div>`) - ); + describe('isUpdatedNote', () => { + it('should consider same note text as the same', () => { + const result = Notes.isUpdatedNote( + { + note: 'initial' + }, + $(`<div> + <div class="original-note-content">initial</div> + </div>`) + ); - expect(result).toEqual(false); - }); + expect(result).toEqual(false); + }); - it('should consider same note with trailing newline as the same', () => { - const result = Notes.isUpdatedNote( - { - note: 'initial\n' - }, - $(`<div> - <div class="original-note-content">initial\n</div> - </div>`) - ); - - expect(result).toEqual(false); - }); + it('should consider same note with trailing newline as the same', () => { + const result = Notes.isUpdatedNote( + { + note: 'initial\n' + }, + $(`<div> + <div class="original-note-content">initial\n</div> + </div>`) + ); - it('should consider different notes as different', () => { - const result = Notes.isUpdatedNote( - { - note: 'foo' - }, - $(`<div> - <div class="original-note-content">bar</div> - </div>`) - ); - - expect(result).toEqual(true); - }); + expect(result).toEqual(false); }); - describe('renderDiscussionNote', () => { - let discussionContainer; - let note; - let notes; - let $form; - let row; + it('should consider different notes as different', () => { + const result = Notes.isUpdatedNote( + { + note: 'foo' + }, + $(`<div> + <div class="original-note-content">bar</div> + </div>`) + ); - beforeEach(() => { - note = { - html: '<li></li>', - discussion_html: '<div></div>', - discussion_id: 1, - discussion_resolvable: false, - diff_discussion_html: false, - }; - $form = jasmine.createSpyObj('$form', ['closest', 'find']); - row = jasmine.createSpyObj('row', ['prevAll', 'first', 'find']); - - notes = jasmine.createSpyObj('notes', [ - 'isParallelView', - 'updateNotesCount', - ]); - notes.note_ids = []; - - spyOn(gl.utils, 'localTimeAgo'); - spyOn(Notes, 'isNewNote'); - spyOn(Notes, 'animateAppendNote'); - Notes.isNewNote.and.returnValue(true); - notes.isParallelView.and.returnValue(false); - row.prevAll.and.returnValue(row); - row.first.and.returnValue(row); - row.find.and.returnValue(row); - }); + expect(result).toEqual(true); + }); + }); - describe('Discussion root note', () => { - let body; + describe('renderDiscussionNote', () => { + let discussionContainer; + let note; + let notes; + let $form; + let row; + + beforeEach(() => { + note = { + html: '<li></li>', + discussion_html: '<div></div>', + discussion_id: 1, + discussion_resolvable: false, + diff_discussion_html: false, + }; + $form = jasmine.createSpyObj('$form', ['closest', 'find']); + row = jasmine.createSpyObj('row', ['prevAll', 'first', 'find']); + + notes = jasmine.createSpyObj('notes', [ + 'isParallelView', + 'updateNotesCount', + ]); + notes.note_ids = []; + + spyOn(gl.utils, 'localTimeAgo'); + spyOn(Notes, 'isNewNote'); + spyOn(Notes, 'animateAppendNote'); + Notes.isNewNote.and.returnValue(true); + notes.isParallelView.and.returnValue(false); + row.prevAll.and.returnValue(row); + row.first.and.returnValue(row); + row.find.and.returnValue(row); + }); - beforeEach(() => { - body = jasmine.createSpyObj('body', ['attr']); - discussionContainer = { length: 0 }; + describe('Discussion root note', () => { + let body; - $form.closest.and.returnValues(row, $form); - $form.find.and.returnValues(discussionContainer); - body.attr.and.returnValue(''); + beforeEach(() => { + body = jasmine.createSpyObj('body', ['attr']); + discussionContainer = { length: 0 }; - Notes.prototype.renderDiscussionNote.call(notes, note, $form); - }); + $form.closest.and.returnValues(row, $form); + $form.find.and.returnValues(discussionContainer); + body.attr.and.returnValue(''); - it('should call Notes.animateAppendNote', () => { - expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.discussion_html, $('.main-notes-list')); - }); + Notes.prototype.renderDiscussionNote.call(notes, note, $form); }); - describe('Discussion sub note', () => { - beforeEach(() => { - discussionContainer = { length: 1 }; - - $form.closest.and.returnValues(row, $form); - $form.find.and.returnValues(discussionContainer); - - Notes.prototype.renderDiscussionNote.call(notes, note, $form); - }); - - it('should call Notes.animateAppendNote', () => { - expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, discussionContainer); - }); + it('should call Notes.animateAppendNote', () => { + expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.discussion_html, $('.main-notes-list')); }); }); - describe('animateAppendNote', () => { - let noteHTML; - let $notesList; - let $resultantNote; - + describe('Discussion sub note', () => { beforeEach(() => { - noteHTML = '<div></div>'; - $notesList = jasmine.createSpyObj('$notesList', ['append']); + discussionContainer = { length: 1 }; - $resultantNote = Notes.animateAppendNote(noteHTML, $notesList); - }); + $form.closest.and.returnValues(row, $form); + $form.find.and.returnValues(discussionContainer); - it('should have `fade-in-full` class', () => { - expect($resultantNote.hasClass('fade-in-full')).toEqual(true); + Notes.prototype.renderDiscussionNote.call(notes, note, $form); }); - it('should append note to the notes list', () => { - expect($notesList.append).toHaveBeenCalledWith($resultantNote); + it('should call Notes.animateAppendNote', () => { + expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, discussionContainer); }); }); + }); - describe('animateUpdateNote', () => { - let noteHTML; - let $note; - let $updatedNote; - - beforeEach(() => { - noteHTML = '<div></div>'; - $note = jasmine.createSpyObj('$note', [ - 'replaceWith' - ]); + describe('animateAppendNote', () => { + let noteHTML; + let $notesList; + let $resultantNote; - $updatedNote = Notes.animateUpdateNote(noteHTML, $note); - }); + beforeEach(() => { + noteHTML = '<div></div>'; + $notesList = jasmine.createSpyObj('$notesList', ['append']); - it('should have `fade-in` class', () => { - expect($updatedNote.hasClass('fade-in')).toEqual(true); - }); + $resultantNote = Notes.animateAppendNote(noteHTML, $notesList); + }); - it('should call replaceWith on $note', () => { - expect($note.replaceWith).toHaveBeenCalledWith($updatedNote); - }); + it('should have `fade-in-full` class', () => { + expect($resultantNote.hasClass('fade-in-full')).toEqual(true); }); - describe('putEditFormInPlace', () => { - it('should call gl.GLForm with GFM parameter passed through', () => { - spyOn(gl, 'GLForm'); + it('should append note to the notes list', () => { + expect($notesList.append).toHaveBeenCalledWith($resultantNote); + }); + }); - const $el = jasmine.createSpyObj('$form', ['find', 'closest']); - $el.find.and.returnValue($('<div>')); - $el.closest.and.returnValue($('<div>')); + describe('animateUpdateNote', () => { + let noteHTML; + let $note; + let $updatedNote; - Notes.prototype.putEditFormInPlace.call({ - getEditFormSelector: () => '', - enableGFM: true - }, $el); + beforeEach(() => { + noteHTML = '<div></div>'; + $note = jasmine.createSpyObj('$note', [ + 'replaceWith' + ]); - expect(gl.GLForm).toHaveBeenCalledWith(jasmine.any(Object), true); - }); + $updatedNote = Notes.animateUpdateNote(noteHTML, $note); }); - describe('postComment & updateComment', () => { - const sampleComment = 'foo'; - const updatedComment = 'bar'; - const note = { - id: 1234, - html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> - <div class="note-text">${sampleComment}</div> - </li>`, - note: sampleComment, - valid: true - }; - let $form; - let $notesContainer; + it('should have `fade-in` class', () => { + expect($updatedNote.hasClass('fade-in')).toEqual(true); + }); - beforeEach(() => { - this.notes = new Notes('', []); - window.gon.current_username = 'root'; - window.gon.current_user_fullname = 'Administrator'; - $form = $('form.js-main-target-form'); - $notesContainer = $('ul.main-notes-list'); - $form.find('textarea.js-note-text').val(sampleComment); - }); + it('should call replaceWith on $note', () => { + expect($note.replaceWith).toHaveBeenCalledWith($updatedNote); + }); + }); - it('should show placeholder note while new comment is being posted', () => { - $('.js-comment-button').click(); - expect($notesContainer.find('.note.being-posted').length > 0).toEqual(true); - }); + describe('putEditFormInPlace', () => { + it('should call gl.GLForm with GFM parameter passed through', () => { + spyOn(gl, 'GLForm'); - it('should remove placeholder note when new comment is done posting', () => { - const deferred = $.Deferred(); - spyOn($, 'ajax').and.returnValue(deferred.promise()); - $('.js-comment-button').click(); + const $el = jasmine.createSpyObj('$form', ['find', 'closest']); + $el.find.and.returnValue($('<div>')); + $el.closest.and.returnValue($('<div>')); - deferred.resolve(note); - expect($notesContainer.find('.note.being-posted').length).toEqual(0); - }); + Notes.prototype.putEditFormInPlace.call({ + getEditFormSelector: () => '', + enableGFM: true + }, $el); - it('should show actual note element when new comment is done posting', () => { - const deferred = $.Deferred(); - spyOn($, 'ajax').and.returnValue(deferred.promise()); - $('.js-comment-button').click(); + expect(gl.GLForm).toHaveBeenCalledWith(jasmine.any(Object), true); + }); + }); - deferred.resolve(note); - expect($notesContainer.find(`#note_${note.id}`).length > 0).toEqual(true); - }); + describe('postComment & updateComment', () => { + const sampleComment = 'foo'; + const updatedComment = 'bar'; + const note = { + id: 1234, + html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> + <div class="note-text">${sampleComment}</div> + </li>`, + note: sampleComment, + valid: true + }; + let $form; + let $notesContainer; + + beforeEach(() => { + this.notes = new Notes('', []); + window.gon.current_username = 'root'; + window.gon.current_user_fullname = 'Administrator'; + $form = $('form.js-main-target-form'); + $notesContainer = $('ul.main-notes-list'); + $form.find('textarea.js-note-text').val(sampleComment); + }); - it('should reset Form when new comment is done posting', () => { - const deferred = $.Deferred(); - spyOn($, 'ajax').and.returnValue(deferred.promise()); - $('.js-comment-button').click(); + it('should show placeholder note while new comment is being posted', () => { + $('.js-comment-button').click(); + expect($notesContainer.find('.note.being-posted').length > 0).toEqual(true); + }); - deferred.resolve(note); - expect($form.find('textarea.js-note-text').val()).toEqual(''); - }); + it('should remove placeholder note when new comment is done posting', () => { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + $('.js-comment-button').click(); - it('should show flash error message when new comment failed to be posted', () => { - const deferred = $.Deferred(); - spyOn($, 'ajax').and.returnValue(deferred.promise()); - $('.js-comment-button').click(); + deferred.resolve(note); + expect($notesContainer.find('.note.being-posted').length).toEqual(0); + }); - deferred.reject(); - expect($notesContainer.parent().find('.flash-container .flash-text').is(':visible')).toEqual(true); - }); + it('should show actual note element when new comment is done posting', () => { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + $('.js-comment-button').click(); - it('should show flash error message when comment failed to be updated', () => { - const deferred = $.Deferred(); - spyOn($, 'ajax').and.returnValue(deferred.promise()); - $('.js-comment-button').click(); - - deferred.resolve(note); - const $noteEl = $notesContainer.find(`#note_${note.id}`); - $noteEl.find('.js-note-edit').click(); - $noteEl.find('textarea.js-note-text').val(updatedComment); - $noteEl.find('.js-comment-save-button').click(); - - deferred.reject(); - const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`); - expect($updatedNoteEl.hasClass('.being-posted')).toEqual(false); // Remove being-posted visuals - expect($updatedNoteEl.find('.note-text').text().trim()).toEqual(sampleComment); // See if comment reverted back to original - expect($('.flash-container').is(':visible')).toEqual(true); // Flash error message shown - }); + deferred.resolve(note); + expect($notesContainer.find(`#note_${note.id}`).length > 0).toEqual(true); }); - describe('postComment with Slash commands', () => { - const sampleComment = '/assign @root\n/award :100:'; - const note = { - commands_changes: { - assignee_id: 1, - emoji_award: '100' - }, - errors: { - commands_only: ['Commands applied'] - }, - valid: false - }; - let $form; - let $notesContainer; + it('should reset Form when new comment is done posting', () => { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + $('.js-comment-button').click(); - beforeEach(() => { - this.notes = new Notes('', []); - window.gon.current_username = 'root'; - window.gon.current_user_fullname = 'Administrator'; - gl.awardsHandler = { - addAwardToEmojiBar: () => {}, - scrollToAwards: () => {} - }; - gl.GfmAutoComplete = { - dataSources: { - commands: '/root/test-project/autocomplete_sources/commands' - } - }; - $form = $('form.js-main-target-form'); - $notesContainer = $('ul.main-notes-list'); - $form.find('textarea.js-note-text').val(sampleComment); - }); + deferred.resolve(note); + expect($form.find('textarea.js-note-text').val()).toEqual(''); + }); - it('should remove slash command placeholder when comment with slash commands is done posting', () => { - const deferred = $.Deferred(); - spyOn($, 'ajax').and.returnValue(deferred.promise()); - spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough(); - $('.js-comment-button').click(); + it('should show flash error message when new comment failed to be posted', () => { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + $('.js-comment-button').click(); - expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown - deferred.resolve(note); - expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed - }); + deferred.reject(); + expect($notesContainer.parent().find('.flash-container .flash-text').is(':visible')).toEqual(true); }); - describe('update comment with script tags', () => { - const sampleComment = '<script></script>'; - const updatedComment = '<script></script>'; - const note = { - id: 1234, - html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> - <div class="note-text">${sampleComment}</div> - </li>`, - note: sampleComment, - valid: true - }; - let $form; - let $notesContainer; - - beforeEach(() => { - this.notes = new Notes('', []); - window.gon.current_username = 'root'; - window.gon.current_user_fullname = 'Administrator'; - $form = $('form.js-main-target-form'); - $notesContainer = $('ul.main-notes-list'); - $form.find('textarea.js-note-text').html(sampleComment); - }); + it('should show flash error message when comment failed to be updated', () => { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + $('.js-comment-button').click(); + + deferred.resolve(note); + const $noteEl = $notesContainer.find(`#note_${note.id}`); + $noteEl.find('.js-note-edit').click(); + $noteEl.find('textarea.js-note-text').val(updatedComment); + $noteEl.find('.js-comment-save-button').click(); + + deferred.reject(); + const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`); + expect($updatedNoteEl.hasClass('.being-posted')).toEqual(false); // Remove being-posted visuals + expect($updatedNoteEl.find('.note-text').text().trim()).toEqual(sampleComment); // See if comment reverted back to original + expect($('.flash-container').is(':visible')).toEqual(true); // Flash error message shown + }); + }); - it('should not render a script tag', () => { - const deferred = $.Deferred(); - spyOn($, 'ajax').and.returnValue(deferred.promise()); - $('.js-comment-button').click(); + describe('postComment with Slash commands', () => { + const sampleComment = '/assign @root\n/award :100:'; + const note = { + commands_changes: { + assignee_id: 1, + emoji_award: '100' + }, + errors: { + commands_only: ['Commands applied'] + }, + valid: false + }; + let $form; + let $notesContainer; + + beforeEach(() => { + this.notes = new Notes('', []); + window.gon.current_username = 'root'; + window.gon.current_user_fullname = 'Administrator'; + gl.awardsHandler = { + addAwardToEmojiBar: () => {}, + scrollToAwards: () => {} + }; + gl.GfmAutoComplete = { + dataSources: { + commands: '/root/test-project/autocomplete_sources/commands' + } + }; + $form = $('form.js-main-target-form'); + $notesContainer = $('ul.main-notes-list'); + $form.find('textarea.js-note-text').val(sampleComment); + }); - deferred.resolve(note); - const $noteEl = $notesContainer.find(`#note_${note.id}`); - $noteEl.find('.js-note-edit').click(); - $noteEl.find('textarea.js-note-text').html(updatedComment); - $noteEl.find('.js-comment-save-button').click(); + it('should remove slash command placeholder when comment with slash commands is done posting', () => { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough(); + $('.js-comment-button').click(); - const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`).find('.js-task-list-container'); - expect($updatedNoteEl.find('.note-text').text().trim()).toEqual(''); - }); + expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown + deferred.resolve(note); + expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed }); + }); - describe('getFormData', () => { - let $form; - let sampleComment; + describe('update comment with script tags', () => { + const sampleComment = '<script></script>'; + const updatedComment = '<script></script>'; + const note = { + id: 1234, + html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> + <div class="note-text">${sampleComment}</div> + </li>`, + note: sampleComment, + valid: true + }; + let $form; + let $notesContainer; + + beforeEach(() => { + this.notes = new Notes('', []); + window.gon.current_username = 'root'; + window.gon.current_user_fullname = 'Administrator'; + $form = $('form.js-main-target-form'); + $notesContainer = $('ul.main-notes-list'); + $form.find('textarea.js-note-text').html(sampleComment); + }); - beforeEach(() => { - this.notes = new Notes('', []); + it('should not render a script tag', () => { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + $('.js-comment-button').click(); - $form = $('form'); - sampleComment = 'foobar'; - }); + deferred.resolve(note); + const $noteEl = $notesContainer.find(`#note_${note.id}`); + $noteEl.find('.js-note-edit').click(); + $noteEl.find('textarea.js-note-text').html(updatedComment); + $noteEl.find('.js-comment-save-button').click(); - it('should return form metadata object from form reference', () => { - $form.find('textarea.js-note-text').val(sampleComment); - const { formData, formContent, formAction } = this.notes.getFormData($form); + const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`).find('.js-task-list-container'); + expect($updatedNoteEl.find('.note-text').text().trim()).toEqual(''); + }); + }); - expect(formData.indexOf(sampleComment) > -1).toBe(true); - expect(formContent).toEqual(sampleComment); - expect(formAction).toEqual($form.attr('action')); - }); + describe('getFormData', () => { + let $form; + let sampleComment; - it('should return form metadata with sanitized formContent from form reference', () => { - spyOn(_, 'escape').and.callFake(htmlEscape); + beforeEach(() => { + this.notes = new Notes('', []); - sampleComment = '<script>alert("Boom!");</script>'; - $form.find('textarea.js-note-text').val(sampleComment); + $form = $('form'); + sampleComment = 'foobar'; + }); - const { formContent } = this.notes.getFormData($form); + it('should return form metadata object from form reference', () => { + $form.find('textarea.js-note-text').val(sampleComment); + const { formData, formContent, formAction } = this.notes.getFormData($form); - expect(_.escape).toHaveBeenCalledWith(sampleComment); - expect(formContent).toEqual('<script>alert("Boom!");</script>'); - }); + expect(formData.indexOf(sampleComment) > -1).toBe(true); + expect(formContent).toEqual(sampleComment); + expect(formAction).toEqual($form.attr('action')); }); - describe('hasQuickActions', () => { - beforeEach(() => { - this.notes = new Notes('', []); - }); + it('should return form metadata with sanitized formContent from form reference', () => { + spyOn(_, 'escape').and.callFake(htmlEscape); - it('should return true when comment begins with a quick action', () => { - const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; - const hasQuickActions = this.notes.hasQuickActions(sampleComment); + sampleComment = '<script>alert("Boom!");</script>'; + $form.find('textarea.js-note-text').val(sampleComment); - expect(hasQuickActions).toBeTruthy(); - }); + const { formContent } = this.notes.getFormData($form); - it('should return false when comment does NOT begin with a quick action', () => { - const sampleComment = 'Hey, /unassign Merging this'; - const hasQuickActions = this.notes.hasQuickActions(sampleComment); + expect(_.escape).toHaveBeenCalledWith(sampleComment); + expect(formContent).toEqual('<script>alert("Boom!");</script>'); + }); + }); - expect(hasQuickActions).toBeFalsy(); - }); + describe('hasQuickActions', () => { + beforeEach(() => { + this.notes = new Notes('', []); + }); - it('should return false when comment does NOT have any quick actions', () => { - const sampleComment = 'Looking good, Awesome!'; - const hasQuickActions = this.notes.hasQuickActions(sampleComment); + it('should return true when comment begins with a quick action', () => { + const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; + const hasQuickActions = this.notes.hasQuickActions(sampleComment); - expect(hasQuickActions).toBeFalsy(); - }); + expect(hasQuickActions).toBeTruthy(); }); - describe('stripQuickActions', () => { - it('should strip quick actions from the comment which begins with a quick action', () => { - this.notes = new Notes(); - const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; - const stripedComment = this.notes.stripQuickActions(sampleComment); + it('should return false when comment does NOT begin with a quick action', () => { + const sampleComment = 'Hey, /unassign Merging this'; + const hasQuickActions = this.notes.hasQuickActions(sampleComment); - expect(stripedComment).toBe(''); - }); + expect(hasQuickActions).toBeFalsy(); + }); - it('should strip quick actions from the comment but leaves plain comment if it is present', () => { - this.notes = new Notes(); - const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this'; - const stripedComment = this.notes.stripQuickActions(sampleComment); + it('should return false when comment does NOT have any quick actions', () => { + const sampleComment = 'Looking good, Awesome!'; + const hasQuickActions = this.notes.hasQuickActions(sampleComment); - expect(stripedComment).toBe('Merging this'); - }); + expect(hasQuickActions).toBeFalsy(); + }); + }); - it('should NOT strip string that has slashes within', () => { - this.notes = new Notes(); - const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1'; - const stripedComment = this.notes.stripQuickActions(sampleComment); + describe('stripQuickActions', () => { + it('should strip quick actions from the comment which begins with a quick action', () => { + this.notes = new Notes(); + const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; + const stripedComment = this.notes.stripQuickActions(sampleComment); - expect(stripedComment).toBe(sampleComment); - }); + expect(stripedComment).toBe(''); }); - describe('getQuickActionDescription', () => { - const availableQuickActions = [ - { name: 'close', description: 'Close this issue', params: [] }, - { name: 'title', description: 'Change title', params: [{}] }, - { name: 'estimate', description: 'Set time estimate', params: [{}] } - ]; + it('should strip quick actions from the comment but leaves plain comment if it is present', () => { + this.notes = new Notes(); + const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this'; + const stripedComment = this.notes.stripQuickActions(sampleComment); - beforeEach(() => { - this.notes = new Notes(); - }); + expect(stripedComment).toBe('Merging this'); + }); - it('should return executing quick action description when note has single quick action', () => { - const sampleComment = '/close'; - expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying command to close this issue'); - }); + it('should NOT strip string that has slashes within', () => { + this.notes = new Notes(); + const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1'; + const stripedComment = this.notes.stripQuickActions(sampleComment); - it('should return generic multiple quick action description when note has multiple quick actions', () => { - const sampleComment = '/close\n/title [Duplicate] Issue foobar'; - expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying multiple commands'); - }); + expect(stripedComment).toBe(sampleComment); + }); + }); - it('should return generic quick action description when available quick actions list is not populated', () => { - const sampleComment = '/close\n/title [Duplicate] Issue foobar'; - expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command'); - }); + describe('getQuickActionDescription', () => { + const availableQuickActions = [ + { name: 'close', description: 'Close this issue', params: [] }, + { name: 'title', description: 'Change title', params: [{}] }, + { name: 'estimate', description: 'Set time estimate', params: [{}] } + ]; + + beforeEach(() => { + this.notes = new Notes(); }); - describe('createPlaceholderNote', () => { - const sampleComment = 'foobar'; - const uniqueId = 'b1234-a4567'; - const currentUsername = 'root'; - const currentUserFullname = 'Administrator'; - const currentUserAvatar = 'avatar_url'; + it('should return executing quick action description when note has single quick action', () => { + const sampleComment = '/close'; + expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying command to close this issue'); + }); - beforeEach(() => { - this.notes = new Notes('', []); - }); + it('should return generic multiple quick action description when note has multiple quick actions', () => { + const sampleComment = '/close\n/title [Duplicate] Issue foobar'; + expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying multiple commands'); + }); - it('should return constructed placeholder element for regular note based on form contents', () => { - const $tempNote = this.notes.createPlaceholderNote({ - formContent: sampleComment, - uniqueId, - isDiscussionNote: false, - currentUsername, - currentUserFullname, - currentUserAvatar, - }); - const $tempNoteHeader = $tempNote.find('.note-header'); - - expect($tempNote.prop('nodeName')).toEqual('LI'); - expect($tempNote.attr('id')).toEqual(uniqueId); - expect($tempNote.hasClass('being-posted')).toBeTruthy(); - expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); - $tempNote.find('.timeline-icon > a, .note-header-info > a').each(function() { - expect($(this).attr('href')).toEqual(`/${currentUsername}`); - }); - expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual(currentUserAvatar); - expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeFalsy(); - expect($tempNoteHeader.find('.hidden-xs').text().trim()).toEqual(currentUserFullname); - expect($tempNoteHeader.find('.note-headline-light').text().trim()).toEqual(`@${currentUsername}`); - expect($tempNote.find('.note-body .note-text p').text().trim()).toEqual(sampleComment); - }); + it('should return generic quick action description when available quick actions list is not populated', () => { + const sampleComment = '/close\n/title [Duplicate] Issue foobar'; + expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command'); + }); + }); - it('should return constructed placeholder element for discussion note based on form contents', () => { - const $tempNote = this.notes.createPlaceholderNote({ - formContent: sampleComment, - uniqueId, - isDiscussionNote: true, - currentUsername, - currentUserFullname - }); + describe('createPlaceholderNote', () => { + const sampleComment = 'foobar'; + const uniqueId = 'b1234-a4567'; + const currentUsername = 'root'; + const currentUserFullname = 'Administrator'; + const currentUserAvatar = 'avatar_url'; - expect($tempNote.prop('nodeName')).toEqual('LI'); - expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy(); - }); + beforeEach(() => { + this.notes = new Notes('', []); }); - describe('createPlaceholderSystemNote', () => { - const sampleCommandDescription = 'Applying command to close this issue'; - const uniqueId = 'b1234-a4567'; + it('should return constructed placeholder element for regular note based on form contents', () => { + const $tempNote = this.notes.createPlaceholderNote({ + formContent: sampleComment, + uniqueId, + isDiscussionNote: false, + currentUsername, + currentUserFullname, + currentUserAvatar, + }); + const $tempNoteHeader = $tempNote.find('.note-header'); + + expect($tempNote.prop('nodeName')).toEqual('LI'); + expect($tempNote.attr('id')).toEqual(uniqueId); + expect($tempNote.hasClass('being-posted')).toBeTruthy(); + expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); + $tempNote.find('.timeline-icon > a, .note-header-info > a').each(function() { + expect($(this).attr('href')).toEqual(`/${currentUsername}`); + }); + expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual(currentUserAvatar); + expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeFalsy(); + expect($tempNoteHeader.find('.hidden-xs').text().trim()).toEqual(currentUserFullname); + expect($tempNoteHeader.find('.note-headline-light').text().trim()).toEqual(`@${currentUsername}`); + expect($tempNote.find('.note-body .note-text p').text().trim()).toEqual(sampleComment); + }); - beforeEach(() => { - this.notes = new Notes('', []); - spyOn(_, 'escape').and.callFake(htmlEscape); + it('should return constructed placeholder element for discussion note based on form contents', () => { + const $tempNote = this.notes.createPlaceholderNote({ + formContent: sampleComment, + uniqueId, + isDiscussionNote: true, + currentUsername, + currentUserFullname }); - it('should return constructed placeholder element for system note based on form contents', () => { - const $tempNote = this.notes.createPlaceholderSystemNote({ - formContent: sampleCommandDescription, - uniqueId, - }); + expect($tempNote.prop('nodeName')).toEqual('LI'); + expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy(); + }); + }); - expect($tempNote.prop('nodeName')).toEqual('LI'); - expect($tempNote.attr('id')).toEqual(uniqueId); - expect($tempNote.hasClass('being-posted')).toBeTruthy(); - expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); - expect($tempNote.find('.timeline-content i').text().trim()).toEqual(sampleCommandDescription); - }); + describe('createPlaceholderSystemNote', () => { + const sampleCommandDescription = 'Applying command to close this issue'; + const uniqueId = 'b1234-a4567'; + + beforeEach(() => { + this.notes = new Notes('', []); + spyOn(_, 'escape').and.callFake(htmlEscape); }); - describe('appendFlash', () => { - beforeEach(() => { - this.notes = new Notes(); + it('should return constructed placeholder element for system note based on form contents', () => { + const $tempNote = this.notes.createPlaceholderSystemNote({ + formContent: sampleCommandDescription, + uniqueId, }); - it('shows a flash message', () => { - this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline); + expect($tempNote.prop('nodeName')).toEqual('LI'); + expect($tempNote.attr('id')).toEqual(uniqueId); + expect($tempNote.hasClass('being-posted')).toBeTruthy(); + expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); + expect($tempNote.find('.timeline-content i').text().trim()).toEqual(sampleCommandDescription); + }); + }); - expect($('.flash-alert').is(':visible')).toBeTruthy(); - }); + describe('appendFlash', () => { + beforeEach(() => { + this.notes = new Notes(); }); - describe('clearFlash', () => { - beforeEach(() => { - $(document).off('ajax:success'); - this.notes = new Notes(); - }); + it('shows a flash message', () => { + this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline); - it('hides visible flash message', () => { - this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline); + expect($('.flash-alert').is(':visible')).toBeTruthy(); + }); + }); + + describe('clearFlash', () => { + beforeEach(() => { + $(document).off('ajax:success'); + this.notes = new Notes(); + }); - this.notes.clearFlash(); + it('hides visible flash message', () => { + this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline); - expect($('.flash-alert').is(':visible')).toBeFalsy(); - }); + this.notes.clearFlash(); + + expect($('.flash-alert').is(':visible')).toBeFalsy(); }); }); -}).call(window); +}); diff --git a/spec/javascripts/pretty_time_spec.js b/spec/javascripts/pretty_time_spec.js index de99e7e3894..eebab0ba166 100644 --- a/spec/javascripts/pretty_time_spec.js +++ b/spec/javascripts/pretty_time_spec.js @@ -1,134 +1,133 @@ import '~/lib/utils/pretty_time'; -(() => { - const prettyTime = gl.utils.prettyTime; +const gl = window.gl || (window.gl = {}); +const prettyTime = gl.utils.prettyTime; - describe('prettyTime methods', function () { - describe('parseSeconds', function () { - it('should correctly parse a negative value', function () { - const parser = prettyTime.parseSeconds; +describe('prettyTime methods', function () { + describe('parseSeconds', function () { + it('should correctly parse a negative value', function () { + const parser = prettyTime.parseSeconds; - const zeroSeconds = parser(-1000); + const zeroSeconds = parser(-1000); - expect(zeroSeconds.minutes).toBe(16); - expect(zeroSeconds.hours).toBe(0); - expect(zeroSeconds.days).toBe(0); - expect(zeroSeconds.weeks).toBe(0); - }); + expect(zeroSeconds.minutes).toBe(16); + expect(zeroSeconds.hours).toBe(0); + expect(zeroSeconds.days).toBe(0); + expect(zeroSeconds.weeks).toBe(0); + }); + + it('should correctly parse a zero value', function () { + const parser = prettyTime.parseSeconds; + + const zeroSeconds = parser(0); + + expect(zeroSeconds.minutes).toBe(0); + expect(zeroSeconds.hours).toBe(0); + expect(zeroSeconds.days).toBe(0); + expect(zeroSeconds.weeks).toBe(0); + }); + + it('should correctly parse a small non-zero second values', function () { + const parser = prettyTime.parseSeconds; + + const subOneMinute = parser(10); + + expect(subOneMinute.minutes).toBe(0); + expect(subOneMinute.hours).toBe(0); + expect(subOneMinute.days).toBe(0); + expect(subOneMinute.weeks).toBe(0); - it('should correctly parse a zero value', function () { - const parser = prettyTime.parseSeconds; + const aboveOneMinute = parser(100); - const zeroSeconds = parser(0); + expect(aboveOneMinute.minutes).toBe(1); + expect(aboveOneMinute.hours).toBe(0); + expect(aboveOneMinute.days).toBe(0); + expect(aboveOneMinute.weeks).toBe(0); - expect(zeroSeconds.minutes).toBe(0); - expect(zeroSeconds.hours).toBe(0); - expect(zeroSeconds.days).toBe(0); - expect(zeroSeconds.weeks).toBe(0); - }); + const manyMinutes = parser(1000); - it('should correctly parse a small non-zero second values', function () { - const parser = prettyTime.parseSeconds; + expect(manyMinutes.minutes).toBe(16); + expect(manyMinutes.hours).toBe(0); + expect(manyMinutes.days).toBe(0); + expect(manyMinutes.weeks).toBe(0); + }); - const subOneMinute = parser(10); + it('should correctly parse large second values', function () { + const parser = prettyTime.parseSeconds; - expect(subOneMinute.minutes).toBe(0); - expect(subOneMinute.hours).toBe(0); - expect(subOneMinute.days).toBe(0); - expect(subOneMinute.weeks).toBe(0); + const aboveOneHour = parser(4800); - const aboveOneMinute = parser(100); + expect(aboveOneHour.minutes).toBe(20); + expect(aboveOneHour.hours).toBe(1); + expect(aboveOneHour.days).toBe(0); + expect(aboveOneHour.weeks).toBe(0); - expect(aboveOneMinute.minutes).toBe(1); - expect(aboveOneMinute.hours).toBe(0); - expect(aboveOneMinute.days).toBe(0); - expect(aboveOneMinute.weeks).toBe(0); + const aboveOneDay = parser(110000); - const manyMinutes = parser(1000); + expect(aboveOneDay.minutes).toBe(33); + expect(aboveOneDay.hours).toBe(6); + expect(aboveOneDay.days).toBe(3); + expect(aboveOneDay.weeks).toBe(0); - expect(manyMinutes.minutes).toBe(16); - expect(manyMinutes.hours).toBe(0); - expect(manyMinutes.days).toBe(0); - expect(manyMinutes.weeks).toBe(0); - }); + const aboveOneWeek = parser(25000000); - it('should correctly parse large second values', function () { - const parser = prettyTime.parseSeconds; + expect(aboveOneWeek.minutes).toBe(26); + expect(aboveOneWeek.hours).toBe(0); + expect(aboveOneWeek.days).toBe(3); + expect(aboveOneWeek.weeks).toBe(173); + }); + }); - const aboveOneHour = parser(4800); + describe('stringifyTime', function () { + it('should stringify values with all non-zero units', function () { + const timeObject = { + weeks: 1, + days: 4, + hours: 7, + minutes: 20, + }; - expect(aboveOneHour.minutes).toBe(20); - expect(aboveOneHour.hours).toBe(1); - expect(aboveOneHour.days).toBe(0); - expect(aboveOneHour.weeks).toBe(0); + const timeString = prettyTime.stringifyTime(timeObject); - const aboveOneDay = parser(110000); + expect(timeString).toBe('1w 4d 7h 20m'); + }); - expect(aboveOneDay.minutes).toBe(33); - expect(aboveOneDay.hours).toBe(6); - expect(aboveOneDay.days).toBe(3); - expect(aboveOneDay.weeks).toBe(0); + it('should stringify values with some non-zero units', function () { + const timeObject = { + weeks: 0, + days: 4, + hours: 0, + minutes: 20, + }; - const aboveOneWeek = parser(25000000); + const timeString = prettyTime.stringifyTime(timeObject); - expect(aboveOneWeek.minutes).toBe(26); - expect(aboveOneWeek.hours).toBe(0); - expect(aboveOneWeek.days).toBe(3); - expect(aboveOneWeek.weeks).toBe(173); - }); + expect(timeString).toBe('4d 20m'); }); - describe('stringifyTime', function () { - it('should stringify values with all non-zero units', function () { - const timeObject = { - weeks: 1, - days: 4, - hours: 7, - minutes: 20, - }; - - const timeString = prettyTime.stringifyTime(timeObject); - - expect(timeString).toBe('1w 4d 7h 20m'); - }); - - it('should stringify values with some non-zero units', function () { - const timeObject = { - weeks: 0, - days: 4, - hours: 0, - minutes: 20, - }; - - const timeString = prettyTime.stringifyTime(timeObject); - - expect(timeString).toBe('4d 20m'); - }); - - it('should stringify values with no non-zero units', function () { - const timeObject = { - weeks: 0, - days: 0, - hours: 0, - minutes: 0, - }; - - const timeString = prettyTime.stringifyTime(timeObject); - - expect(timeString).toBe('0m'); - }); + it('should stringify values with no non-zero units', function () { + const timeObject = { + weeks: 0, + days: 0, + hours: 0, + minutes: 0, + }; + + const timeString = prettyTime.stringifyTime(timeObject); + + expect(timeString).toBe('0m'); }); + }); - describe('abbreviateTime', function () { - it('should abbreviate stringified times for weeks', function () { - const fullTimeString = '1w 3d 4h 5m'; - expect(prettyTime.abbreviateTime(fullTimeString)).toBe('1w'); - }); + describe('abbreviateTime', function () { + it('should abbreviate stringified times for weeks', function () { + const fullTimeString = '1w 3d 4h 5m'; + expect(prettyTime.abbreviateTime(fullTimeString)).toBe('1w'); + }); - it('should abbreviate stringified times for non-weeks', function () { - const fullTimeString = '0w 3d 4h 5m'; - expect(prettyTime.abbreviateTime(fullTimeString)).toBe('3d'); - }); + it('should abbreviate stringified times for non-weeks', function () { + const fullTimeString = '0w 3d 4h 5m'; + expect(prettyTime.abbreviateTime(fullTimeString)).toBe('3d'); }); }); -})(window.gl || (window.gl = {})); +}); diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index f2072a6f350..711b19bbf9a 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -4,85 +4,83 @@ import '~/commons/bootstrap'; import '~/right_sidebar'; -(function() { - var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState; - - this.sidebar = null; - - $aside = null; - - $toggle = null; - - $icon = null; - - $page = null; - - $labelsIcon = null; - - assertSidebarState = function(state) { - var shouldBeCollapsed, shouldBeExpanded; - shouldBeExpanded = state === 'expanded'; - shouldBeCollapsed = state === 'collapsed'; - expect($aside.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded); - expect($page.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded); - expect($icon.hasClass('fa-angle-double-right')).toBe(shouldBeExpanded); - expect($aside.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed); - expect($page.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed); - return expect($icon.hasClass('fa-angle-double-left')).toBe(shouldBeCollapsed); - }; - - describe('RightSidebar', function() { - var fixtureName = 'issues/open-issue.html.raw'; - preloadFixtures(fixtureName); - loadJSONFixtures('todos/todos.json'); - - beforeEach(function() { - loadFixtures(fixtureName); - this.sidebar = new Sidebar; - $aside = $('.right-sidebar'); - $page = $('.page-with-sidebar'); - $icon = $aside.find('i'); - $toggle = $aside.find('.js-sidebar-toggle'); - return $labelsIcon = $aside.find('.sidebar-collapsed-icon'); - }); - it('should expand/collapse the sidebar when arrow is clicked', function() { - assertSidebarState('expanded'); - $toggle.click(); - assertSidebarState('collapsed'); - $toggle.click(); - assertSidebarState('expanded'); - }); - it('should float over the page and when sidebar icons clicked', function() { - $labelsIcon.click(); - return assertSidebarState('expanded'); - }); - it('should collapse when the icon arrow clicked while it is floating on page', function() { - $labelsIcon.click(); - assertSidebarState('expanded'); - $toggle.click(); - return assertSidebarState('collapsed'); - }); +var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState; + +this.sidebar = null; + +$aside = null; + +$toggle = null; + +$icon = null; + +$page = null; - it('should broadcast todo:toggle event when add todo clicked', function() { - var todos = getJSONFixture('todos/todos.json'); - spyOn(jQuery, 'ajax').and.callFake(function() { - var d = $.Deferred(); - var response = todos; - d.resolve(response); - return d.promise(); - }); +$labelsIcon = null; - var todoToggleSpy = spyOnEvent(document, 'todo:toggle'); +assertSidebarState = function(state) { + var shouldBeCollapsed, shouldBeExpanded; + shouldBeExpanded = state === 'expanded'; + shouldBeCollapsed = state === 'collapsed'; + expect($aside.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded); + expect($page.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded); + expect($icon.hasClass('fa-angle-double-right')).toBe(shouldBeExpanded); + expect($aside.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed); + expect($page.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed); + return expect($icon.hasClass('fa-angle-double-left')).toBe(shouldBeCollapsed); +}; - $('.issuable-sidebar-header .js-issuable-todo').click(); +describe('RightSidebar', function() { + var fixtureName = 'issues/open-issue.html.raw'; + preloadFixtures(fixtureName); + loadJSONFixtures('todos/todos.json'); - expect(todoToggleSpy.calls.count()).toEqual(1); + beforeEach(function() { + loadFixtures(fixtureName); + this.sidebar = new Sidebar; + $aside = $('.right-sidebar'); + $page = $('.page-with-sidebar'); + $icon = $aside.find('i'); + $toggle = $aside.find('.js-sidebar-toggle'); + return $labelsIcon = $aside.find('.sidebar-collapsed-icon'); + }); + it('should expand/collapse the sidebar when arrow is clicked', function() { + assertSidebarState('expanded'); + $toggle.click(); + assertSidebarState('collapsed'); + $toggle.click(); + assertSidebarState('expanded'); + }); + it('should float over the page and when sidebar icons clicked', function() { + $labelsIcon.click(); + return assertSidebarState('expanded'); + }); + it('should collapse when the icon arrow clicked while it is floating on page', function() { + $labelsIcon.click(); + assertSidebarState('expanded'); + $toggle.click(); + return assertSidebarState('collapsed'); + }); + + it('should broadcast todo:toggle event when add todo clicked', function() { + var todos = getJSONFixture('todos/todos.json'); + spyOn(jQuery, 'ajax').and.callFake(function() { + var d = $.Deferred(); + var response = todos; + d.resolve(response); + return d.promise(); }); - it('should not hide collapsed icons', () => { - [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => { - expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); - }); + var todoToggleSpy = spyOnEvent(document, 'todo:toggle'); + + $('.issuable-sidebar-header .js-issuable-todo').click(); + + expect(todoToggleSpy.calls.count()).toEqual(1); + }); + + it('should not hide collapsed icons', () => { + [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => { + expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); }); }); -}).call(window); +}); diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index a53f58b5d0d..9594ffcdf39 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -5,173 +5,171 @@ import '~/search_autocomplete'; import '~/lib/utils/common_utils'; import 'vendor/fuzzaldrin-plus'; -(function() { - var addBodyAttributes, assertLinks, dashboardIssuesPath, dashboardMRsPath, groupIssuesPath, groupMRsPath, groupName, mockDashboardOptions, mockGroupOptions, mockProjectOptions, projectIssuesPath, projectMRsPath, projectName, userId, widget; - var userName = 'root'; - - widget = null; - - userId = 1; - - dashboardIssuesPath = '/dashboard/issues'; - - dashboardMRsPath = '/dashboard/merge_requests'; - - projectIssuesPath = '/gitlab-org/gitlab-ce/issues'; - - projectMRsPath = '/gitlab-org/gitlab-ce/merge_requests'; - - groupIssuesPath = '/groups/gitlab-org/issues'; - - groupMRsPath = '/groups/gitlab-org/merge_requests'; - - projectName = 'GitLab Community Edition'; - - groupName = 'Gitlab Org'; - - // Add required attributes to body before starting the test. - // section would be dashboard|group|project - addBodyAttributes = function(section) { - var $body; - if (section == null) { - section = 'dashboard'; - } - $body = $('body'); - $body.removeAttr('data-page'); - $body.removeAttr('data-project'); - $body.removeAttr('data-group'); - switch (section) { - case 'dashboard': - return $body.data('page', 'root:index'); - case 'group': - $body.data('page', 'groups:show'); - return $body.data('group', 'gitlab-org'); - case 'project': - $body.data('page', 'projects:show'); - return $body.data('project', 'gitlab-ce'); - } +var addBodyAttributes, assertLinks, dashboardIssuesPath, dashboardMRsPath, groupIssuesPath, groupMRsPath, groupName, mockDashboardOptions, mockGroupOptions, mockProjectOptions, projectIssuesPath, projectMRsPath, projectName, userId, widget; +var userName = 'root'; + +widget = null; + +userId = 1; + +dashboardIssuesPath = '/dashboard/issues'; + +dashboardMRsPath = '/dashboard/merge_requests'; + +projectIssuesPath = '/gitlab-org/gitlab-ce/issues'; + +projectMRsPath = '/gitlab-org/gitlab-ce/merge_requests'; + +groupIssuesPath = '/groups/gitlab-org/issues'; + +groupMRsPath = '/groups/gitlab-org/merge_requests'; + +projectName = 'GitLab Community Edition'; + +groupName = 'Gitlab Org'; + +// Add required attributes to body before starting the test. +// section would be dashboard|group|project +addBodyAttributes = function(section) { + var $body; + if (section == null) { + section = 'dashboard'; + } + $body = $('body'); + $body.removeAttr('data-page'); + $body.removeAttr('data-project'); + $body.removeAttr('data-group'); + switch (section) { + case 'dashboard': + return $body.data('page', 'root:index'); + case 'group': + $body.data('page', 'groups:show'); + return $body.data('group', 'gitlab-org'); + case 'project': + $body.data('page', 'projects:show'); + return $body.data('project', 'gitlab-ce'); + } +}; + +// Mock `gl` object in window for dashboard specific page. App code will need it. +mockDashboardOptions = function() { + window.gl || (window.gl = {}); + return window.gl.dashboardOptions = { + issuesPath: dashboardIssuesPath, + mrPath: dashboardMRsPath }; - - // Mock `gl` object in window for dashboard specific page. App code will need it. - mockDashboardOptions = function() { - window.gl || (window.gl = {}); - return window.gl.dashboardOptions = { - issuesPath: dashboardIssuesPath, - mrPath: dashboardMRsPath - }; - }; - - // Mock `gl` object in window for project specific page. App code will need it. - mockProjectOptions = function() { - window.gl || (window.gl = {}); - return window.gl.projectOptions = { - 'gitlab-ce': { - issuesPath: projectIssuesPath, - mrPath: projectMRsPath, - projectName: projectName - } - }; - }; - - mockGroupOptions = function() { - window.gl || (window.gl = {}); - return window.gl.groupOptions = { - 'gitlab-org': { - issuesPath: groupIssuesPath, - mrPath: groupMRsPath, - projectName: groupName - } - }; +}; + +// Mock `gl` object in window for project specific page. App code will need it. +mockProjectOptions = function() { + window.gl || (window.gl = {}); + return window.gl.projectOptions = { + 'gitlab-ce': { + issuesPath: projectIssuesPath, + mrPath: projectMRsPath, + projectName: projectName + } }; - - assertLinks = function(list, issuesPath, mrsPath) { - var a1, a2, a3, a4, issuesAssignedToMeLink, issuesIHaveCreatedLink, mrsAssignedToMeLink, mrsIHaveCreatedLink; - issuesAssignedToMeLink = issuesPath + "/?assignee_username=" + userName; - issuesIHaveCreatedLink = issuesPath + "/?author_username=" + userName; - mrsAssignedToMeLink = mrsPath + "/?assignee_username=" + userName; - mrsIHaveCreatedLink = mrsPath + "/?author_username=" + userName; - a1 = "a[href='" + issuesAssignedToMeLink + "']"; - a2 = "a[href='" + issuesIHaveCreatedLink + "']"; - a3 = "a[href='" + mrsAssignedToMeLink + "']"; - a4 = "a[href='" + mrsIHaveCreatedLink + "']"; - expect(list.find(a1).length).toBe(1); - expect(list.find(a1).text()).toBe('Issues assigned to me'); - expect(list.find(a2).length).toBe(1); - expect(list.find(a2).text()).toBe("Issues I've created"); - expect(list.find(a3).length).toBe(1); - expect(list.find(a3).text()).toBe('Merge requests assigned to me'); - expect(list.find(a4).length).toBe(1); - return expect(list.find(a4).text()).toBe("Merge requests I've created"); +}; + +mockGroupOptions = function() { + window.gl || (window.gl = {}); + return window.gl.groupOptions = { + 'gitlab-org': { + issuesPath: groupIssuesPath, + mrPath: groupMRsPath, + projectName: groupName + } }; +}; + +assertLinks = function(list, issuesPath, mrsPath) { + var a1, a2, a3, a4, issuesAssignedToMeLink, issuesIHaveCreatedLink, mrsAssignedToMeLink, mrsIHaveCreatedLink; + issuesAssignedToMeLink = issuesPath + "/?assignee_username=" + userName; + issuesIHaveCreatedLink = issuesPath + "/?author_username=" + userName; + mrsAssignedToMeLink = mrsPath + "/?assignee_username=" + userName; + mrsIHaveCreatedLink = mrsPath + "/?author_username=" + userName; + a1 = "a[href='" + issuesAssignedToMeLink + "']"; + a2 = "a[href='" + issuesIHaveCreatedLink + "']"; + a3 = "a[href='" + mrsAssignedToMeLink + "']"; + a4 = "a[href='" + mrsIHaveCreatedLink + "']"; + expect(list.find(a1).length).toBe(1); + expect(list.find(a1).text()).toBe('Issues assigned to me'); + expect(list.find(a2).length).toBe(1); + expect(list.find(a2).text()).toBe("Issues I've created"); + expect(list.find(a3).length).toBe(1); + expect(list.find(a3).text()).toBe('Merge requests assigned to me'); + expect(list.find(a4).length).toBe(1); + return expect(list.find(a4).text()).toBe("Merge requests I've created"); +}; + +describe('Search autocomplete dropdown', function() { + preloadFixtures('static/search_autocomplete.html.raw'); + beforeEach(function() { + loadFixtures('static/search_autocomplete.html.raw'); + widget = new gl.SearchAutocomplete; + // Prevent turbolinks from triggering within gl_dropdown + spyOn(window.gl.utils, 'visitUrl').and.returnValue(true); + + window.gon = {}; + window.gon.current_user_id = userId; + window.gon.current_username = userName; + + return widget = new gl.SearchAutocomplete; + }); - describe('Search autocomplete dropdown', function() { - preloadFixtures('static/search_autocomplete.html.raw'); - beforeEach(function() { - loadFixtures('static/search_autocomplete.html.raw'); - widget = new gl.SearchAutocomplete; - // Prevent turbolinks from triggering within gl_dropdown - spyOn(window.gl.utils, 'visitUrl').and.returnValue(true); - - window.gon = {}; - window.gon.current_user_id = userId; - window.gon.current_username = userName; - - return widget = new gl.SearchAutocomplete; - }); - - afterEach(function() { - window.gon = {}; - }); - it('should show Dashboard specific dropdown menu', function() { - var list; - addBodyAttributes(); - mockDashboardOptions(); - widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); - return assertLinks(list, dashboardIssuesPath, dashboardMRsPath); - }); - it('should show Group specific dropdown menu', function() { - var list; - addBodyAttributes('group'); - mockGroupOptions(); - widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); - return assertLinks(list, groupIssuesPath, groupMRsPath); - }); - it('should show Project specific dropdown menu', function() { - var list; - addBodyAttributes('project'); - mockProjectOptions(); - widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); - return assertLinks(list, projectIssuesPath, projectMRsPath); - }); - it('should not show category related menu if there is text in the input', function() { - var link, list; - addBodyAttributes('project'); - mockProjectOptions(); - widget.searchInput.val('help'); - widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); - link = "a[href='" + projectIssuesPath + "/?assignee_id=" + userId + "']"; - return expect(list.find(link).length).toBe(0); - }); - return it('should not submit the search form when selecting an autocomplete row with the keyboard', function() { - var ENTER = 13; - var DOWN = 40; - addBodyAttributes(); - mockDashboardOptions(true); - var submitSpy = spyOnEvent('form', 'submit'); - widget.searchInput.triggerHandler('focus'); - widget.wrap.trigger($.Event('keydown', { which: DOWN })); - var enterKeyEvent = $.Event('keydown', { which: ENTER }); - widget.searchInput.trigger(enterKeyEvent); - // This does not currently catch failing behavior. For security reasons, - // browsers will not trigger default behavior (form submit, in this - // example) on JavaScript-created keypresses. - expect(submitSpy).not.toHaveBeenTriggered(); - // Does a worse job at capturing the intent of the test, but works. - expect(enterKeyEvent.isDefaultPrevented()).toBe(true); - }); + afterEach(function() { + window.gon = {}; + }); + it('should show Dashboard specific dropdown menu', function() { + var list; + addBodyAttributes(); + mockDashboardOptions(); + widget.searchInput.triggerHandler('focus'); + list = widget.wrap.find('.dropdown-menu').find('ul'); + return assertLinks(list, dashboardIssuesPath, dashboardMRsPath); + }); + it('should show Group specific dropdown menu', function() { + var list; + addBodyAttributes('group'); + mockGroupOptions(); + widget.searchInput.triggerHandler('focus'); + list = widget.wrap.find('.dropdown-menu').find('ul'); + return assertLinks(list, groupIssuesPath, groupMRsPath); + }); + it('should show Project specific dropdown menu', function() { + var list; + addBodyAttributes('project'); + mockProjectOptions(); + widget.searchInput.triggerHandler('focus'); + list = widget.wrap.find('.dropdown-menu').find('ul'); + return assertLinks(list, projectIssuesPath, projectMRsPath); + }); + it('should not show category related menu if there is text in the input', function() { + var link, list; + addBodyAttributes('project'); + mockProjectOptions(); + widget.searchInput.val('help'); + widget.searchInput.triggerHandler('focus'); + list = widget.wrap.find('.dropdown-menu').find('ul'); + link = "a[href='" + projectIssuesPath + "/?assignee_id=" + userId + "']"; + return expect(list.find(link).length).toBe(0); + }); + return it('should not submit the search form when selecting an autocomplete row with the keyboard', function() { + var ENTER = 13; + var DOWN = 40; + addBodyAttributes(); + mockDashboardOptions(true); + var submitSpy = spyOnEvent('form', 'submit'); + widget.searchInput.triggerHandler('focus'); + widget.wrap.trigger($.Event('keydown', { which: DOWN })); + var enterKeyEvent = $.Event('keydown', { which: ENTER }); + widget.searchInput.trigger(enterKeyEvent); + // This does not currently catch failing behavior. For security reasons, + // browsers will not trigger default behavior (form submit, in this + // example) on JavaScript-created keypresses. + expect(submitSpy).not.toHaveBeenTriggered(); + // Does a worse job at capturing the intent of the test, but works. + expect(enterKeyEvent.isDefaultPrevented()).toBe(true); }); -}).call(window); +}); diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js index 3515dfbc60b..e621a13fe39 100644 --- a/spec/javascripts/shortcuts_issuable_spec.js +++ b/spec/javascripts/shortcuts_issuable_spec.js @@ -4,75 +4,73 @@ import '~/copy_as_gfm'; import '~/shortcuts_issuable'; -(function() { - describe('ShortcutsIssuable', function() { - var fixtureName = 'issues/open-issue.html.raw'; - preloadFixtures(fixtureName); +describe('ShortcutsIssuable', function() { + var fixtureName = 'issues/open-issue.html.raw'; + preloadFixtures(fixtureName); + beforeEach(function() { + loadFixtures(fixtureName); + document.querySelector('.js-new-note-form').classList.add('js-main-target-form'); + this.shortcut = new ShortcutsIssuable(); + }); + describe('replyWithSelectedText', function() { + var stubSelection; + // Stub window.gl.utils.getSelectedFragment to return a node with the provided HTML. + stubSelection = function(html) { + window.gl.utils.getSelectedFragment = function() { + var node = document.createElement('div'); + node.innerHTML = html; + return node; + }; + }; beforeEach(function() { - loadFixtures(fixtureName); - document.querySelector('.js-new-note-form').classList.add('js-main-target-form'); - this.shortcut = new ShortcutsIssuable(); + this.selector = 'form.js-main-target-form textarea#note_note'; }); - describe('replyWithSelectedText', function() { - var stubSelection; - // Stub window.gl.utils.getSelectedFragment to return a node with the provided HTML. - stubSelection = function(html) { - window.gl.utils.getSelectedFragment = function() { - var node = document.createElement('div'); - node.innerHTML = html; - return node; - }; - }; + describe('with empty selection', function() { + it('does not return an error', function() { + this.shortcut.replyWithSelectedText(); + expect($(this.selector).val()).toBe(''); + }); + it('triggers `focus`', function() { + this.shortcut.replyWithSelectedText(); + expect(document.activeElement).toBe(document.querySelector(this.selector)); + }); + }); + describe('with any selection', function() { beforeEach(function() { - this.selector = 'form.js-main-target-form textarea#note_note'; + stubSelection('<p>Selected text.</p>'); }); - describe('with empty selection', function() { - it('does not return an error', function() { - this.shortcut.replyWithSelectedText(); - expect($(this.selector).val()).toBe(''); - }); - it('triggers `focus`', function() { - this.shortcut.replyWithSelectedText(); - expect(document.activeElement).toBe(document.querySelector(this.selector)); - }); + it('leaves existing input intact', function() { + $(this.selector).val('This text was already here.'); + expect($(this.selector).val()).toBe('This text was already here.'); + this.shortcut.replyWithSelectedText(); + expect($(this.selector).val()).toBe("This text was already here.\n\n> Selected text.\n\n"); }); - describe('with any selection', function() { - beforeEach(function() { - stubSelection('<p>Selected text.</p>'); - }); - it('leaves existing input intact', function() { - $(this.selector).val('This text was already here.'); - expect($(this.selector).val()).toBe('This text was already here.'); - this.shortcut.replyWithSelectedText(); - expect($(this.selector).val()).toBe("This text was already here.\n\n> Selected text.\n\n"); - }); - it('triggers `input`', function() { - var triggered = false; - $(this.selector).on('input', function() { - triggered = true; - }); - this.shortcut.replyWithSelectedText(); - expect(triggered).toBe(true); - }); - it('triggers `focus`', function() { - this.shortcut.replyWithSelectedText(); - expect(document.activeElement).toBe(document.querySelector(this.selector)); + it('triggers `input`', function() { + var triggered = false; + $(this.selector).on('input', function() { + triggered = true; }); + this.shortcut.replyWithSelectedText(); + expect(triggered).toBe(true); }); - describe('with a one-line selection', function() { - it('quotes the selection', function() { - stubSelection('<p>This text has been selected.</p>'); - this.shortcut.replyWithSelectedText(); - expect($(this.selector).val()).toBe("> This text has been selected.\n\n"); - }); + it('triggers `focus`', function() { + this.shortcut.replyWithSelectedText(); + expect(document.activeElement).toBe(document.querySelector(this.selector)); }); - describe('with a multi-line selection', function() { - it('quotes the selected lines as a group', function() { - stubSelection("<p>Selected line one.</p>\n\n<p>Selected line two.</p>\n\n<p>Selected line three.</p>"); - this.shortcut.replyWithSelectedText(); - expect($(this.selector).val()).toBe("> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n"); - }); + }); + describe('with a one-line selection', function() { + it('quotes the selection', function() { + stubSelection('<p>This text has been selected.</p>'); + this.shortcut.replyWithSelectedText(); + expect($(this.selector).val()).toBe("> This text has been selected.\n\n"); + }); + }); + describe('with a multi-line selection', function() { + it('quotes the selected lines as a group', function() { + stubSelection("<p>Selected line one.</p>\n\n<p>Selected line two.</p>\n\n<p>Selected line three.</p>"); + this.shortcut.replyWithSelectedText(); + expect($(this.selector).val()).toBe("> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n"); }); }); }); -}).call(window); +}); diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js index 7833bf3fb04..71147eaea32 100644 --- a/spec/javascripts/smart_interval_spec.js +++ b/spec/javascripts/smart_interval_spec.js @@ -1,179 +1,178 @@ import '~/smart_interval'; -(() => { - const DEFAULT_MAX_INTERVAL = 100; - const DEFAULT_STARTING_INTERVAL = 5; - const DEFAULT_SHORT_TIMEOUT = 75; - const DEFAULT_LONG_TIMEOUT = 1000; - const DEFAULT_INCREMENT_FACTOR = 2; - - function createDefaultSmartInterval(config) { - const defaultParams = { - callback: () => {}, - startingInterval: DEFAULT_STARTING_INTERVAL, - maxInterval: DEFAULT_MAX_INTERVAL, - incrementByFactorOf: DEFAULT_INCREMENT_FACTOR, - lazyStart: false, - immediateExecution: false, - hiddenInterval: null, - }; - - if (config) { - _.extend(defaultParams, config); - } - - return new gl.SmartInterval(defaultParams); +const gl = window.gl || (window.gl = {}); +const DEFAULT_MAX_INTERVAL = 100; +const DEFAULT_STARTING_INTERVAL = 5; +const DEFAULT_SHORT_TIMEOUT = 75; +const DEFAULT_LONG_TIMEOUT = 1000; +const DEFAULT_INCREMENT_FACTOR = 2; + +function createDefaultSmartInterval(config) { + const defaultParams = { + callback: () => {}, + startingInterval: DEFAULT_STARTING_INTERVAL, + maxInterval: DEFAULT_MAX_INTERVAL, + incrementByFactorOf: DEFAULT_INCREMENT_FACTOR, + lazyStart: false, + immediateExecution: false, + hiddenInterval: null, + }; + + if (config) { + _.extend(defaultParams, config); } - describe('SmartInterval', function () { - describe('Increment Interval', function () { - beforeEach(function () { - this.smartInterval = createDefaultSmartInterval(); - }); - - it('should increment the interval delay', function (done) { - const interval = this.smartInterval; - setTimeout(() => { - const intervalConfig = this.smartInterval.cfg; - const iterationCount = 4; - const maxIntervalAfterIterations = intervalConfig.startingInterval * - (intervalConfig.incrementByFactorOf ** (iterationCount - 1)); // 40 - const currentInterval = interval.getCurrentInterval(); - - // Provide some flexibility for performance of testing environment - expect(currentInterval).toBeGreaterThan(intervalConfig.startingInterval); - expect(currentInterval <= maxIntervalAfterIterations).toBeTruthy(); - - done(); - }, DEFAULT_SHORT_TIMEOUT); // 4 iterations, increment by 2x = (5 + 10 + 20 + 40) - }); - - it('should not increment past maxInterval', function (done) { - const interval = this.smartInterval; - - setTimeout(() => { - const currentInterval = interval.getCurrentInterval(); - expect(currentInterval).toBe(interval.cfg.maxInterval); - - done(); - }, DEFAULT_LONG_TIMEOUT); - }); + return new gl.SmartInterval(defaultParams); +} + +describe('SmartInterval', function () { + describe('Increment Interval', function () { + beforeEach(function () { + this.smartInterval = createDefaultSmartInterval(); + }); + + it('should increment the interval delay', function (done) { + const interval = this.smartInterval; + setTimeout(() => { + const intervalConfig = this.smartInterval.cfg; + const iterationCount = 4; + const maxIntervalAfterIterations = intervalConfig.startingInterval * + (intervalConfig.incrementByFactorOf ** (iterationCount - 1)); // 40 + const currentInterval = interval.getCurrentInterval(); + + // Provide some flexibility for performance of testing environment + expect(currentInterval).toBeGreaterThan(intervalConfig.startingInterval); + expect(currentInterval <= maxIntervalAfterIterations).toBeTruthy(); + + done(); + }, DEFAULT_SHORT_TIMEOUT); // 4 iterations, increment by 2x = (5 + 10 + 20 + 40) + }); + + it('should not increment past maxInterval', function (done) { + const interval = this.smartInterval; + + setTimeout(() => { + const currentInterval = interval.getCurrentInterval(); + expect(currentInterval).toBe(interval.cfg.maxInterval); + + done(); + }, DEFAULT_LONG_TIMEOUT); }); + }); - describe('Public methods', function () { - beforeEach(function () { - this.smartInterval = createDefaultSmartInterval(); - }); + describe('Public methods', function () { + beforeEach(function () { + this.smartInterval = createDefaultSmartInterval(); + }); - it('should cancel an interval', function (done) { - const interval = this.smartInterval; + it('should cancel an interval', function (done) { + const interval = this.smartInterval; - setTimeout(() => { - interval.cancel(); + setTimeout(() => { + interval.cancel(); - const intervalId = interval.state.intervalId; - const currentInterval = interval.getCurrentInterval(); - const intervalLowerLimit = interval.cfg.startingInterval; + const intervalId = interval.state.intervalId; + const currentInterval = interval.getCurrentInterval(); + const intervalLowerLimit = interval.cfg.startingInterval; - expect(intervalId).toBeUndefined(); - expect(currentInterval).toBe(intervalLowerLimit); + expect(intervalId).toBeUndefined(); + expect(currentInterval).toBe(intervalLowerLimit); - done(); - }, DEFAULT_SHORT_TIMEOUT); - }); + done(); + }, DEFAULT_SHORT_TIMEOUT); + }); - it('should resume an interval', function (done) { - const interval = this.smartInterval; + it('should resume an interval', function (done) { + const interval = this.smartInterval; - setTimeout(() => { - interval.cancel(); + setTimeout(() => { + interval.cancel(); - interval.resume(); + interval.resume(); - const intervalId = interval.state.intervalId; + const intervalId = interval.state.intervalId; - expect(intervalId).toBeTruthy(); + expect(intervalId).toBeTruthy(); - done(); - }, DEFAULT_SHORT_TIMEOUT); - }); + done(); + }, DEFAULT_SHORT_TIMEOUT); }); + }); - describe('DOM Events', function () { - beforeEach(function () { - // This ensures DOM and DOM events are initialized for these specs. - setFixtures('<div></div>'); + describe('DOM Events', function () { + beforeEach(function () { + // This ensures DOM and DOM events are initialized for these specs. + setFixtures('<div></div>'); - this.smartInterval = createDefaultSmartInterval(); - }); + this.smartInterval = createDefaultSmartInterval(); + }); - it('should pause when page is not visible', function (done) { - const interval = this.smartInterval; + it('should pause when page is not visible', function (done) { + const interval = this.smartInterval; - setTimeout(() => { - expect(interval.state.intervalId).toBeTruthy(); + setTimeout(() => { + expect(interval.state.intervalId).toBeTruthy(); - // simulates triggering of visibilitychange event - interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } }); + // simulates triggering of visibilitychange event + interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } }); - expect(interval.state.intervalId).toBeUndefined(); - done(); - }, DEFAULT_SHORT_TIMEOUT); - }); + expect(interval.state.intervalId).toBeUndefined(); + done(); + }, DEFAULT_SHORT_TIMEOUT); + }); - it('should change to the hidden interval when page is not visible', function (done) { - const HIDDEN_INTERVAL = 1500; - const interval = createDefaultSmartInterval({ hiddenInterval: HIDDEN_INTERVAL }); + it('should change to the hidden interval when page is not visible', function (done) { + const HIDDEN_INTERVAL = 1500; + const interval = createDefaultSmartInterval({ hiddenInterval: HIDDEN_INTERVAL }); - setTimeout(() => { - expect(interval.state.intervalId).toBeTruthy(); - expect(interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL && - interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL).toBeTruthy(); + setTimeout(() => { + expect(interval.state.intervalId).toBeTruthy(); + expect(interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL && + interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL).toBeTruthy(); - // simulates triggering of visibilitychange event - interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } }); + // simulates triggering of visibilitychange event + interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } }); - expect(interval.state.intervalId).toBeTruthy(); - expect(interval.getCurrentInterval()).toBe(HIDDEN_INTERVAL); - done(); - }, DEFAULT_SHORT_TIMEOUT); - }); + expect(interval.state.intervalId).toBeTruthy(); + expect(interval.getCurrentInterval()).toBe(HIDDEN_INTERVAL); + done(); + }, DEFAULT_SHORT_TIMEOUT); + }); - it('should resume when page is becomes visible at the previous interval', function (done) { - const interval = this.smartInterval; + it('should resume when page is becomes visible at the previous interval', function (done) { + const interval = this.smartInterval; - setTimeout(() => { - expect(interval.state.intervalId).toBeTruthy(); + setTimeout(() => { + expect(interval.state.intervalId).toBeTruthy(); - // simulates triggering of visibilitychange event - interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } }); + // simulates triggering of visibilitychange event + interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } }); - expect(interval.state.intervalId).toBeUndefined(); + expect(interval.state.intervalId).toBeUndefined(); - // simulates triggering of visibilitychange event - interval.handleVisibilityChange({ target: { visibilityState: 'visible' } }); + // simulates triggering of visibilitychange event + interval.handleVisibilityChange({ target: { visibilityState: 'visible' } }); - expect(interval.state.intervalId).toBeTruthy(); + expect(interval.state.intervalId).toBeTruthy(); - done(); - }, DEFAULT_SHORT_TIMEOUT); - }); + done(); + }, DEFAULT_SHORT_TIMEOUT); + }); - it('should cancel on page unload', function (done) { - const interval = this.smartInterval; + it('should cancel on page unload', function (done) { + const interval = this.smartInterval; - setTimeout(() => { - $(document).triggerHandler('beforeunload'); - expect(interval.state.intervalId).toBeUndefined(); - expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval); - done(); - }, DEFAULT_SHORT_TIMEOUT); - }); + setTimeout(() => { + $(document).triggerHandler('beforeunload'); + expect(interval.state.intervalId).toBeUndefined(); + expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval); + done(); + }, DEFAULT_SHORT_TIMEOUT); + }); - it('should execute callback before first interval', function () { - const interval = createDefaultSmartInterval({ immediateExecution: true }); - expect(interval.cfg.immediateExecution).toBeFalsy(); - }); + it('should execute callback before first interval', function () { + const interval = createDefaultSmartInterval({ immediateExecution: true }); + expect(interval.cfg.immediateExecution).toBeFalsy(); }); }); -})(window.gl || (window.gl = {})); +}); diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js index 946f98379ce..4a7d623fb1c 100644 --- a/spec/javascripts/syntax_highlight_spec.js +++ b/spec/javascripts/syntax_highlight_spec.js @@ -2,43 +2,41 @@ import '~/syntax_highlight'; -(function() { - describe('Syntax Highlighter', function() { - var stubUserColorScheme; - stubUserColorScheme = function(value) { - if (window.gon == null) { - window.gon = {}; - } - return window.gon.user_color_scheme = value; - }; - describe('on a js-syntax-highlight element', function() { - beforeEach(function() { - return setFixtures('<div class="js-syntax-highlight"></div>'); - }); - return it('applies syntax highlighting', function() { - stubUserColorScheme('monokai'); - $('.js-syntax-highlight').syntaxHighlight(); - return expect($('.js-syntax-highlight')).toHaveClass('monokai'); - }); +describe('Syntax Highlighter', function() { + var stubUserColorScheme; + stubUserColorScheme = function(value) { + if (window.gon == null) { + window.gon = {}; + } + return window.gon.user_color_scheme = value; + }; + describe('on a js-syntax-highlight element', function() { + beforeEach(function() { + return setFixtures('<div class="js-syntax-highlight"></div>'); }); - return describe('on a parent element', function() { - beforeEach(function() { - return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>"); - }); - it('applies highlighting to all applicable children', function() { - stubUserColorScheme('monokai'); - $('.parent').syntaxHighlight(); - expect($('.parent, .foo')).not.toHaveClass('monokai'); - return expect($('.monokai').length).toBe(2); - }); - return it('prevents an infinite loop when no matches exist', function() { - var highlight; - setFixtures('<div></div>'); - highlight = function() { - return $('div').syntaxHighlight(); - }; - return expect(highlight).not.toThrow(); - }); + return it('applies syntax highlighting', function() { + stubUserColorScheme('monokai'); + $('.js-syntax-highlight').syntaxHighlight(); + return expect($('.js-syntax-highlight')).toHaveClass('monokai'); }); }); -}).call(window); + return describe('on a parent element', function() { + beforeEach(function() { + return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>"); + }); + it('applies highlighting to all applicable children', function() { + stubUserColorScheme('monokai'); + $('.parent').syntaxHighlight(); + expect($('.parent, .foo')).not.toHaveClass('monokai'); + return expect($('.monokai').length).toBe(2); + }); + return it('prevents an infinite loop when no matches exist', function() { + var highlight; + setFixtures('<div></div>'); + highlight = function() { + return $('div').syntaxHighlight(); + }; + return expect(highlight).not.toThrow(); + }); + }); +}); diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js index a160c86308d..c9a10f607e3 100644 --- a/spec/javascripts/u2f/authenticate_spec.js +++ b/spec/javascripts/u2f/authenticate_spec.js @@ -8,65 +8,63 @@ import '~/u2f/error'; import 'vendor/u2f'; import './mock_u2f_device'; -(function() { - describe('U2FAuthenticate', function() { - preloadFixtures('u2f/authenticate.html.raw'); +describe('U2FAuthenticate', function() { + preloadFixtures('u2f/authenticate.html.raw'); - beforeEach(function() { - loadFixtures('u2f/authenticate.html.raw'); - this.u2fDevice = new MockU2FDevice; - this.container = $("#js-authenticate-u2f"); - this.component = new window.gl.U2FAuthenticate( - this.container, - '#js-login-u2f-form', - { - sign_requests: [] - }, - document.querySelector('#js-login-2fa-device'), - document.querySelector('.js-2fa-form') - ); + beforeEach(function() { + loadFixtures('u2f/authenticate.html.raw'); + this.u2fDevice = new MockU2FDevice; + this.container = $("#js-authenticate-u2f"); + this.component = new window.gl.U2FAuthenticate( + this.container, + '#js-login-u2f-form', + { + sign_requests: [] + }, + document.querySelector('#js-login-2fa-device'), + document.querySelector('.js-2fa-form') + ); - // bypass automatic form submission within renderAuthenticated - spyOn(this.component, 'renderAuthenticated').and.returnValue(true); + // bypass automatic form submission within renderAuthenticated + spyOn(this.component, 'renderAuthenticated').and.returnValue(true); - return this.component.start(); + return this.component.start(); + }); + it('allows authenticating via a U2F device', function() { + var inProgressMessage; + inProgressMessage = this.container.find("p"); + expect(inProgressMessage.text()).toContain("Trying to communicate with your device"); + this.u2fDevice.respondToAuthenticateRequest({ + deviceData: "this is data from the device" }); - it('allows authenticating via a U2F device', function() { - var inProgressMessage; - inProgressMessage = this.container.find("p"); - expect(inProgressMessage.text()).toContain("Trying to communicate with your device"); + expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); + }); + return describe("errors", function() { + it("displays an error message", function() { + var errorMessage, setupButton; + setupButton = this.container.find("#js-login-u2f-device"); + setupButton.trigger('click'); this.u2fDevice.respondToAuthenticateRequest({ - deviceData: "this is data from the device" + errorCode: "error!" }); - expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); + errorMessage = this.container.find("p"); + return expect(errorMessage.text()).toContain("There was a problem communicating with your device"); }); - return describe("errors", function() { - it("displays an error message", function() { - var errorMessage, setupButton; - setupButton = this.container.find("#js-login-u2f-device"); - setupButton.trigger('click'); - this.u2fDevice.respondToAuthenticateRequest({ - errorCode: "error!" - }); - errorMessage = this.container.find("p"); - return expect(errorMessage.text()).toContain("There was a problem communicating with your device"); + return it("allows retrying authentication after an error", function() { + var retryButton, setupButton; + setupButton = this.container.find("#js-login-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + errorCode: "error!" }); - return it("allows retrying authentication after an error", function() { - var retryButton, setupButton; - setupButton = this.container.find("#js-login-u2f-device"); - setupButton.trigger('click'); - this.u2fDevice.respondToAuthenticateRequest({ - errorCode: "error!" - }); - retryButton = this.container.find("#js-u2f-try-again"); - retryButton.trigger('click'); - setupButton = this.container.find("#js-login-u2f-device"); - setupButton.trigger('click'); - this.u2fDevice.respondToAuthenticateRequest({ - deviceData: "this is data from the device" - }); - expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); + retryButton = this.container.find("#js-u2f-try-again"); + retryButton.trigger('click'); + setupButton = this.container.find("#js-login-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + deviceData: "this is data from the device" }); + expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); }); }); -}).call(window); +}); diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js index a445c80f2af..ac20e7e49af 100644 --- a/spec/javascripts/u2f/register_spec.js +++ b/spec/javascripts/u2f/register_spec.js @@ -8,70 +8,68 @@ import '~/u2f/error'; import 'vendor/u2f'; import './mock_u2f_device'; -(function() { - describe('U2FRegister', function() { - preloadFixtures('u2f/register.html.raw'); +describe('U2FRegister', function() { + preloadFixtures('u2f/register.html.raw'); - beforeEach(function() { - loadFixtures('u2f/register.html.raw'); - this.u2fDevice = new MockU2FDevice; - this.container = $("#js-register-u2f"); - this.component = new U2FRegister(this.container, $("#js-register-u2f-templates"), {}, "token"); - return this.component.start(); + beforeEach(function() { + loadFixtures('u2f/register.html.raw'); + this.u2fDevice = new MockU2FDevice; + this.container = $("#js-register-u2f"); + this.component = new U2FRegister(this.container, $("#js-register-u2f-templates"), {}, "token"); + return this.component.start(); + }); + it('allows registering a U2F device', function() { + var deviceResponse, inProgressMessage, registeredMessage, setupButton; + setupButton = this.container.find("#js-setup-u2f-device"); + expect(setupButton.text()).toBe('Setup new U2F device'); + setupButton.trigger('click'); + inProgressMessage = this.container.children("p"); + expect(inProgressMessage.text()).toContain("Trying to communicate with your device"); + this.u2fDevice.respondToRegisterRequest({ + deviceData: "this is data from the device" }); - it('allows registering a U2F device', function() { - var deviceResponse, inProgressMessage, registeredMessage, setupButton; + registeredMessage = this.container.find('p'); + deviceResponse = this.container.find('#js-device-response'); + expect(registeredMessage.text()).toContain("Your device was successfully set up!"); + return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}'); + }); + return describe("errors", function() { + it("doesn't allow the same device to be registered twice (for the same user", function() { + var errorMessage, setupButton; setupButton = this.container.find("#js-setup-u2f-device"); - expect(setupButton.text()).toBe('Setup new U2F device'); setupButton.trigger('click'); - inProgressMessage = this.container.children("p"); - expect(inProgressMessage.text()).toContain("Trying to communicate with your device"); this.u2fDevice.respondToRegisterRequest({ - deviceData: "this is data from the device" + errorCode: 4 }); - registeredMessage = this.container.find('p'); - deviceResponse = this.container.find('#js-device-response'); - expect(registeredMessage.text()).toContain("Your device was successfully set up!"); - return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}'); + errorMessage = this.container.find("p"); + return expect(errorMessage.text()).toContain("already been registered with us"); }); - return describe("errors", function() { - it("doesn't allow the same device to be registered twice (for the same user", function() { - var errorMessage, setupButton; - setupButton = this.container.find("#js-setup-u2f-device"); - setupButton.trigger('click'); - this.u2fDevice.respondToRegisterRequest({ - errorCode: 4 - }); - errorMessage = this.container.find("p"); - return expect(errorMessage.text()).toContain("already been registered with us"); + it("displays an error message for other errors", function() { + var errorMessage, setupButton; + setupButton = this.container.find("#js-setup-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + errorCode: "error!" }); - it("displays an error message for other errors", function() { - var errorMessage, setupButton; - setupButton = this.container.find("#js-setup-u2f-device"); - setupButton.trigger('click'); - this.u2fDevice.respondToRegisterRequest({ - errorCode: "error!" - }); - errorMessage = this.container.find("p"); - return expect(errorMessage.text()).toContain("There was a problem communicating with your device"); + errorMessage = this.container.find("p"); + return expect(errorMessage.text()).toContain("There was a problem communicating with your device"); + }); + return it("allows retrying registration after an error", function() { + var registeredMessage, retryButton, setupButton; + setupButton = this.container.find("#js-setup-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + errorCode: "error!" }); - return it("allows retrying registration after an error", function() { - var registeredMessage, retryButton, setupButton; - setupButton = this.container.find("#js-setup-u2f-device"); - setupButton.trigger('click'); - this.u2fDevice.respondToRegisterRequest({ - errorCode: "error!" - }); - retryButton = this.container.find("#U2FTryAgain"); - retryButton.trigger('click'); - setupButton = this.container.find("#js-setup-u2f-device"); - setupButton.trigger('click'); - this.u2fDevice.respondToRegisterRequest({ - deviceData: "this is data from the device" - }); - registeredMessage = this.container.find("p"); - return expect(registeredMessage.text()).toContain("Your device was successfully set up!"); + retryButton = this.container.find("#U2FTryAgain"); + retryButton.trigger('click'); + setupButton = this.container.find("#js-setup-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + deviceData: "this is data from the device" }); + registeredMessage = this.container.find("p"); + return expect(registeredMessage.text()).toContain("Your device was successfully set up!"); }); }); -}).call(window); +}); diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js index a225b04c47e..55beb6cc590 100644 --- a/spec/javascripts/zen_mode_spec.js +++ b/spec/javascripts/zen_mode_spec.js @@ -4,75 +4,73 @@ import ZenMode from '~/zen_mode'; -(function() { - var enterZen, escapeKeydown, exitZen; +var enterZen, escapeKeydown, exitZen; - describe('ZenMode', function() { - var fixtureName = 'issues/open-issue.html.raw'; - preloadFixtures(fixtureName); +describe('ZenMode', function() { + var fixtureName = 'issues/open-issue.html.raw'; + preloadFixtures(fixtureName); + beforeEach(function() { + loadFixtures(fixtureName); + spyOn(Dropzone, 'forElement').and.callFake(function() { + return { + enable: function() { + return true; + } + }; + // Stub Dropzone.forElement(...).enable() + }); + this.zen = new ZenMode(); + // Set this manually because we can't actually scroll the window + return this.zen.scroll_position = 456; + }); + describe('on enter', function() { + it('pauses Mousetrap', function() { + spyOn(Mousetrap, 'pause'); + enterZen(); + return expect(Mousetrap.pause).toHaveBeenCalled(); + }); + return it('removes textarea styling', function() { + $('.notes-form textarea').attr('style', 'height: 400px'); + enterZen(); + return expect($('.notes-form textarea')).not.toHaveAttr('style'); + }); + }); + describe('in use', function() { beforeEach(function() { - loadFixtures(fixtureName); - spyOn(Dropzone, 'forElement').and.callFake(function() { - return { - enable: function() { - return true; - } - }; - // Stub Dropzone.forElement(...).enable() - }); - this.zen = new ZenMode(); - // Set this manually because we can't actually scroll the window - return this.zen.scroll_position = 456; + return enterZen(); + }); + return it('exits on Escape', function() { + escapeKeydown(); + return expect($('.notes-form .zen-backdrop')).not.toHaveClass('fullscreen'); }); - describe('on enter', function() { - it('pauses Mousetrap', function() { - spyOn(Mousetrap, 'pause'); - enterZen(); - return expect(Mousetrap.pause).toHaveBeenCalled(); - }); - return it('removes textarea styling', function() { - $('.notes-form textarea').attr('style', 'height: 400px'); - enterZen(); - return expect($('.notes-form textarea')).not.toHaveAttr('style'); - }); + }); + return describe('on exit', function() { + beforeEach(function() { + return enterZen(); }); - describe('in use', function() { - beforeEach(function() { - return enterZen(); - }); - return it('exits on Escape', function() { - escapeKeydown(); - return expect($('.notes-form .zen-backdrop')).not.toHaveClass('fullscreen'); - }); + it('unpauses Mousetrap', function() { + spyOn(Mousetrap, 'unpause'); + exitZen(); + return expect(Mousetrap.unpause).toHaveBeenCalled(); }); - return describe('on exit', function() { - beforeEach(function() { - return enterZen(); - }); - it('unpauses Mousetrap', function() { - spyOn(Mousetrap, 'unpause'); - exitZen(); - return expect(Mousetrap.unpause).toHaveBeenCalled(); - }); - return it('restores the scroll position', function() { - spyOn(this.zen, 'scrollTo'); - exitZen(); - return expect(this.zen.scrollTo).toHaveBeenCalled(); - }); + return it('restores the scroll position', function() { + spyOn(this.zen, 'scrollTo'); + exitZen(); + return expect(this.zen.scrollTo).toHaveBeenCalled(); }); }); +}); - enterZen = function() { - return $('.notes-form .js-zen-enter').click(); - }; +enterZen = function() { + return $('.notes-form .js-zen-enter').click(); +}; - exitZen = function() { - return $('.notes-form .js-zen-leave').click(); - }; +exitZen = function() { + return $('.notes-form .js-zen-leave').click(); +}; - escapeKeydown = function() { - return $('.notes-form textarea').trigger($.Event('keydown', { - keyCode: 27 - })); - }; -}).call(window); +escapeKeydown = function() { + return $('.notes-form textarea').trigger($.Event('keydown', { + keyCode: 27 + })); +}; |