diff options
35 files changed, 3683 insertions, 3748 deletions
diff --git a/app/assets/javascripts/abuse_reports.js b/app/assets/javascripts/abuse_reports.js index 8a260aae1b1..2934565caec 100644 --- a/app/assets/javascripts/abuse_reports.js +++ b/app/assets/javascripts/abuse_reports.js @@ -1,40 +1,37 @@ -/* eslint-disable no-param-reassign */ +const MAX_MESSAGE_LENGTH = 500; +const MESSAGE_CELL_SELECTOR = '.abuse-reports .message'; -((global) => { - const MAX_MESSAGE_LENGTH = 500; - const MESSAGE_CELL_SELECTOR = '.abuse-reports .message'; - - class AbuseReports { - constructor() { - $(MESSAGE_CELL_SELECTOR).each(this.truncateLongMessage); - $(document) - .off('click', MESSAGE_CELL_SELECTOR) - .on('click', MESSAGE_CELL_SELECTOR, this.toggleMessageTruncation); - } +class AbuseReports { + constructor() { + $(MESSAGE_CELL_SELECTOR).each(this.truncateLongMessage); + $(document) + .off('click', MESSAGE_CELL_SELECTOR) + .on('click', MESSAGE_CELL_SELECTOR, this.toggleMessageTruncation); + } - truncateLongMessage() { - const $messageCellElement = $(this); - const reportMessage = $messageCellElement.text(); - if (reportMessage.length > MAX_MESSAGE_LENGTH) { - $messageCellElement.data('original-message', reportMessage); - $messageCellElement.data('message-truncated', 'true'); - $messageCellElement.text(global.text.truncate(reportMessage, MAX_MESSAGE_LENGTH)); - } + truncateLongMessage() { + const $messageCellElement = $(this); + const reportMessage = $messageCellElement.text(); + if (reportMessage.length > MAX_MESSAGE_LENGTH) { + $messageCellElement.data('original-message', reportMessage); + $messageCellElement.data('message-truncated', 'true'); + $messageCellElement.text(global.text.truncate(reportMessage, MAX_MESSAGE_LENGTH)); } + } - toggleMessageTruncation() { - const $messageCellElement = $(this); - const originalMessage = $messageCellElement.data('original-message'); - if (!originalMessage) return; - if ($messageCellElement.data('message-truncated') === 'true') { - $messageCellElement.data('message-truncated', 'false'); - $messageCellElement.text(originalMessage); - } else { - $messageCellElement.data('message-truncated', 'true'); - $messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`); - } + toggleMessageTruncation() { + const $messageCellElement = $(this); + const originalMessage = $messageCellElement.data('original-message'); + if (!originalMessage) return; + if ($messageCellElement.data('message-truncated') === 'true') { + $messageCellElement.data('message-truncated', 'false'); + $messageCellElement.text(originalMessage); + } else { + $messageCellElement.data('message-truncated', 'true'); + $messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`); } } +} - global.AbuseReports = AbuseReports; -})(window.gl || (window.gl = {})); +window.gl = window.gl || {}; +window.gl.AbuseReports = AbuseReports; diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index 648cb4d5d85..aebda7780e1 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -2,36 +2,35 @@ /* global Pager */ /* global Cookies */ -((global) => { - class Activities { - constructor() { - Pager.init(20, true, false, this.updateTooltips); - $('.event-filter-link').on('click', (e) => { - e.preventDefault(); - this.toggleFilter(e.currentTarget); - this.reloadActivities(); - }); - } +class Activities { + constructor() { + Pager.init(20, true, false, this.updateTooltips); + $('.event-filter-link').on('click', (e) => { + e.preventDefault(); + this.toggleFilter(e.currentTarget); + this.reloadActivities(); + }); + } - updateTooltips() { - gl.utils.localTimeAgo($('.js-timeago', '.content_list')); - } + updateTooltips() { + gl.utils.localTimeAgo($('.js-timeago', '.content_list')); + } - reloadActivities() { - $('.content_list').html(''); - Pager.init(20, true, false, this.updateTooltips); - } + reloadActivities() { + $('.content_list').html(''); + Pager.init(20, true, false, this.updateTooltips); + } - toggleFilter(sender) { - const $sender = $(sender); - const filter = $sender.attr('id').split('_')[0]; + toggleFilter(sender) { + const $sender = $(sender); + const filter = $sender.attr('id').split('_')[0]; - $('.event-filter .active').removeClass('active'); - Cookies.set('event_filter', filter); + $('.event-filter .active').removeClass('active'); + Cookies.set('event_filter', filter); - $sender.closest('li').toggleClass('active'); - } + $sender.closest('li').toggleClass('active'); } +} - global.Activities = Activities; -})(window.gl || (window.gl = {})); +window.gl = window.gl || {}; +window.gl.Activities = Activities; diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index aaed74d6073..34669dd13d6 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -1,64 +1,62 @@ /* 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 */ -(function() { - this.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(); - } +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(); + } - return Admin; - })(); -}).call(window); + return Admin; +})(); diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 86e0ad89431..a0946eb392a 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -1,150 +1,148 @@ /* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, comma-dangle, prefer-arrow-callback, quote-props, no-param-reassign, max-len */ -(function() { - var Api = { - groupsPath: "/api/:version/groups.json", - groupPath: "/api/:version/groups/:id.json", - namespacesPath: "/api/:version/namespaces.json", - groupProjectsPath: "/api/:version/groups/:id/projects.json", - projectsPath: "/api/:version/projects.json?simple=true", - labelsPath: "/:namespace_path/:project_path/labels", - licensePath: "/api/:version/templates/licenses/:key", - gitignorePath: "/api/:version/templates/gitignores/:key", - gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key", - dockerfilePath: "/api/:version/templates/dockerfiles/:key", - issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key", - group: function(group_id, callback) { - var url = Api.buildUrl(Api.groupPath) - .replace(':id', group_id); - return $.ajax({ - url: url, - dataType: "json" - }).done(function(group) { - return callback(group); - }); - }, - // Return groups list. Filtered by query - groups: function(query, options, callback) { - var url = Api.buildUrl(Api.groupsPath); - return $.ajax({ - url: url, - data: $.extend({ - search: query, - per_page: 20 - }, options), - dataType: "json" - }).done(function(groups) { - return callback(groups); - }); - }, - // Return namespaces list. Filtered by query - namespaces: function(query, callback) { - var url = Api.buildUrl(Api.namespacesPath); - return $.ajax({ - url: url, - data: { - search: query, - per_page: 20 - }, - dataType: "json" - }).done(function(namespaces) { - return callback(namespaces); - }); - }, - // Return projects list. Filtered by query - projects: function(query, order, callback) { - var url = Api.buildUrl(Api.projectsPath); - return $.ajax({ - url: url, - data: { - search: query, - order_by: order, - per_page: 20 - }, - dataType: "json" - }).done(function(projects) { - return callback(projects); - }); - }, - newLabel: function(namespace_path, project_path, data, callback) { - var url = Api.buildUrl(Api.labelsPath) - .replace(':namespace_path', namespace_path) - .replace(':project_path', project_path); - return $.ajax({ - url: url, - type: "POST", - data: { 'label': data }, - dataType: "json" - }).done(function(label) { - return callback(label); - }).error(function(message) { - return callback(message.responseJSON); - }); - }, - // Return group projects list. Filtered by query - groupProjects: function(group_id, query, callback) { - var url = Api.buildUrl(Api.groupProjectsPath) - .replace(':id', group_id); - return $.ajax({ - url: url, - data: { - search: query, - per_page: 20 - }, - dataType: "json" - }).done(function(projects) { - return callback(projects); - }); - }, - // Return text for a specific license - licenseText: function(key, data, callback) { - var url = Api.buildUrl(Api.licensePath) - .replace(':key', key); - return $.ajax({ - url: url, - data: data - }).done(function(license) { - return callback(license); - }); - }, - gitignoreText: function(key, callback) { - var url = Api.buildUrl(Api.gitignorePath) - .replace(':key', key); - return $.get(url, function(gitignore) { - return callback(gitignore); - }); - }, - gitlabCiYml: function(key, callback) { - var url = Api.buildUrl(Api.gitlabCiYmlPath) - .replace(':key', key); - return $.get(url, function(file) { - return callback(file); - }); - }, - dockerfileYml: function(key, callback) { - var url = Api.buildUrl(Api.dockerfilePath).replace(':key', key); - $.get(url, callback); - }, - issueTemplate: function(namespacePath, projectPath, key, type, callback) { - var url = Api.buildUrl(Api.issuableTemplatePath) - .replace(':key', key) - .replace(':type', type) - .replace(':project_path', projectPath) - .replace(':namespace_path', namespacePath); - $.ajax({ - url: url, - dataType: 'json' - }).done(function(file) { - callback(null, file); - }).error(callback); - }, - buildUrl: function(url) { - if (gon.relative_url_root != null) { - url = gon.relative_url_root + url; - } - return url.replace(':version', gon.api_version); +var Api = { + groupsPath: "/api/:version/groups.json", + groupPath: "/api/:version/groups/:id.json", + namespacesPath: "/api/:version/namespaces.json", + groupProjectsPath: "/api/:version/groups/:id/projects.json", + projectsPath: "/api/:version/projects.json?simple=true", + labelsPath: "/:namespace_path/:project_path/labels", + licensePath: "/api/:version/templates/licenses/:key", + gitignorePath: "/api/:version/templates/gitignores/:key", + gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key", + dockerfilePath: "/api/:version/templates/dockerfiles/:key", + issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key", + group: function(group_id, callback) { + var url = Api.buildUrl(Api.groupPath) + .replace(':id', group_id); + return $.ajax({ + url: url, + dataType: "json" + }).done(function(group) { + return callback(group); + }); + }, + // Return groups list. Filtered by query + groups: function(query, options, callback) { + var url = Api.buildUrl(Api.groupsPath); + return $.ajax({ + url: url, + data: $.extend({ + search: query, + per_page: 20 + }, options), + dataType: "json" + }).done(function(groups) { + return callback(groups); + }); + }, + // Return namespaces list. Filtered by query + namespaces: function(query, callback) { + var url = Api.buildUrl(Api.namespacesPath); + return $.ajax({ + url: url, + data: { + search: query, + per_page: 20 + }, + dataType: "json" + }).done(function(namespaces) { + return callback(namespaces); + }); + }, + // Return projects list. Filtered by query + projects: function(query, order, callback) { + var url = Api.buildUrl(Api.projectsPath); + return $.ajax({ + url: url, + data: { + search: query, + order_by: order, + per_page: 20 + }, + dataType: "json" + }).done(function(projects) { + return callback(projects); + }); + }, + newLabel: function(namespace_path, project_path, data, callback) { + var url = Api.buildUrl(Api.labelsPath) + .replace(':namespace_path', namespace_path) + .replace(':project_path', project_path); + return $.ajax({ + url: url, + type: "POST", + data: { 'label': data }, + dataType: "json" + }).done(function(label) { + return callback(label); + }).error(function(message) { + return callback(message.responseJSON); + }); + }, + // Return group projects list. Filtered by query + groupProjects: function(group_id, query, callback) { + var url = Api.buildUrl(Api.groupProjectsPath) + .replace(':id', group_id); + return $.ajax({ + url: url, + data: { + search: query, + per_page: 20 + }, + dataType: "json" + }).done(function(projects) { + return callback(projects); + }); + }, + // Return text for a specific license + licenseText: function(key, data, callback) { + var url = Api.buildUrl(Api.licensePath) + .replace(':key', key); + return $.ajax({ + url: url, + data: data + }).done(function(license) { + return callback(license); + }); + }, + gitignoreText: function(key, callback) { + var url = Api.buildUrl(Api.gitignorePath) + .replace(':key', key); + return $.get(url, function(gitignore) { + return callback(gitignore); + }); + }, + gitlabCiYml: function(key, callback) { + var url = Api.buildUrl(Api.gitlabCiYmlPath) + .replace(':key', key); + return $.get(url, function(file) { + return callback(file); + }); + }, + dockerfileYml: function(key, callback) { + var url = Api.buildUrl(Api.dockerfilePath).replace(':key', key); + $.get(url, callback); + }, + issueTemplate: function(namespacePath, projectPath, key, type, callback) { + var url = Api.buildUrl(Api.issuableTemplatePath) + .replace(':key', key) + .replace(':type', type) + .replace(':project_path', projectPath) + .replace(':namespace_path', namespacePath); + $.ajax({ + url: url, + dataType: 'json' + }).done(function(file) { + callback(null, file); + }).error(callback); + }, + buildUrl: function(url) { + if (gon.relative_url_root != null) { + url = gon.relative_url_root + url; } - }; + return url.replace(':version', gon.api_version); + } +}; - window.Api = Api; -}).call(window); +window.Api = Api; diff --git a/app/assets/javascripts/aside.js b/app/assets/javascripts/aside.js index 448e6e2cc78..88756884d16 100644 --- a/app/assets/javascripts/aside.js +++ b/app/assets/javascripts/aside.js @@ -1,25 +1,24 @@ /* 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 */ -(function() { - this.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'); - } - }); - } - return Aside; - })(); -}).call(window); +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'); + } + }); + } + + return Aside; +})(); diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js index e55405135fb..8630b18a73f 100644 --- a/app/assets/javascripts/autosave.js +++ b/app/assets/javascripts/autosave.js @@ -1,62 +1,61 @@ /* 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 */ -(function() { - this.Autosave = (function() { - function Autosave(field, key) { - this.field = field; - 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)); - } - Autosave.prototype.restore = function() { - var e, text; - if (window.localStorage == null) { - return; - } - try { - text = window.localStorage.getItem(this.key); - } catch (error) { - e = error; - return; - } - if ((text != null ? text.length : void 0) > 0) { - this.field.val(text); - } - return this.field.trigger("input"); - }; +window.Autosave = (function() { + function Autosave(field, key) { + this.field = field; + 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)); + } - Autosave.prototype.save = function() { - var text; - if (window.localStorage == null) { - return; - } - text = this.field.val(); - if ((text != null ? text.length : void 0) > 0) { - try { - return window.localStorage.setItem(this.key, text); - } catch (error) {} - } else { - return this.reset(); - } - }; + Autosave.prototype.restore = function() { + var e, text; + if (window.localStorage == null) { + return; + } + try { + text = window.localStorage.getItem(this.key); + } catch (error) { + e = error; + return; + } + if ((text != null ? text.length : void 0) > 0) { + this.field.val(text); + } + return this.field.trigger("input"); + }; - Autosave.prototype.reset = function() { - if (window.localStorage == null) { - return; - } + Autosave.prototype.save = function() { + var text; + if (window.localStorage == null) { + return; + } + text = this.field.val(); + if ((text != null ? text.length : void 0) > 0) { try { - return window.localStorage.removeItem(this.key); + return window.localStorage.setItem(this.key, text); } catch (error) {} - }; + } else { + return this.reset(); + } + }; + + Autosave.prototype.reset = function() { + if (window.localStorage == null) { + return; + } + try { + return window.localStorage.removeItem(this.key); + } catch (error) {} + }; - return Autosave; - })(); -}).call(window); + return Autosave; +})(); diff --git a/app/assets/javascripts/breakpoints.js b/app/assets/javascripts/breakpoints.js index 22e93328548..2c1f988d987 100644 --- a/app/assets/javascripts/breakpoints.js +++ b/app/assets/javascripts/breakpoints.js @@ -1,72 +1,66 @@ /* 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 */ -(function() { - var Breakpoints = (function() { - var BreakpointInstance, instance; +var Breakpoints = (function() { + var BreakpointInstance, instance; - function Breakpoints() {} + function Breakpoints() {} - instance = null; + instance = null; - BreakpointInstance = (function() { - var BREAKPOINTS; + BreakpointInstance = (function() { + var BREAKPOINTS; - BREAKPOINTS = ["xs", "sm", "md", "lg"]; + 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('')); - }; + function BreakpointInstance() { + this.setup(); + } - 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]; - }; + 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('')); + }; - return BreakpointInstance; - })(); + BreakpointInstance.prototype.visibleDevice = function() { + var allDeviceSelector; + allDeviceSelector = BREAKPOINTS.map(function(breakpoint) { + return ".device-" + breakpoint; + }); + return $(allDeviceSelector.join(",")).filter(":visible"); + }; - Breakpoints.get = function() { - return instance != null ? instance : instance = new BreakpointInstance; + 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 Breakpoints; + return BreakpointInstance; })(); - $((function(_this) { - return function() { - return _this.bp = Breakpoints.get(); - }; - })(this)); + Breakpoints.get = function() { + return instance != null ? instance : instance = new BreakpointInstance; + }; + + return Breakpoints; +})(); + +$(() => { window.bp = Breakpoints.get(); }); - window.Breakpoints = Breakpoints; -}).call(window); +window.Breakpoints = Breakpoints; diff --git a/app/assets/javascripts/broadcast_message.js b/app/assets/javascripts/broadcast_message.js index e8531c43b4b..f73e489e7b2 100644 --- a/app/assets/javascripts/broadcast_message.js +++ b/app/assets/javascripts/broadcast_message.js @@ -1,34 +1,33 @@ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, no-else-return, object-shorthand, comma-dangle, max-len */ -(function() { - $(function() { - var previewPath; - $('input#broadcast_message_color').on('input', function() { - var previewColor; - previewColor = $(this).val(); - return $('div.broadcast-message-preview').css('background-color', previewColor); - }); - $('input#broadcast_message_font').on('input', function() { - var previewColor; - previewColor = $(this).val(); - return $('div.broadcast-message-preview').css('color', previewColor); - }); - previewPath = $('textarea#broadcast_message_message').data('preview-path'); - return $('textarea#broadcast_message_message').on('input', function() { - var message; - message = $(this).val(); - if (message === '') { - return $('.js-broadcast-message-preview').text("Your message here"); - } else { - return $.ajax({ - url: previewPath, - type: "POST", - data: { - broadcast_message: { - message: message - } + +$(function() { + var previewPath; + $('input#broadcast_message_color').on('input', function() { + var previewColor; + previewColor = $(this).val(); + return $('div.broadcast-message-preview').css('background-color', previewColor); + }); + $('input#broadcast_message_font').on('input', function() { + var previewColor; + previewColor = $(this).val(); + return $('div.broadcast-message-preview').css('color', previewColor); + }); + previewPath = $('textarea#broadcast_message_message').data('preview-path'); + return $('textarea#broadcast_message_message').on('input', function() { + var message; + message = $(this).val(); + if (message === '') { + return $('.js-broadcast-message-preview').text("Your message here"); + } else { + return $.ajax({ + url: previewPath, + type: "POST", + data: { + broadcast_message: { + message: message } - }); - } - }); + } + }); + } }); -}).call(window); +}); diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 6e6e9b18686..6efd26ccc37 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -1,285 +1,283 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-param-reassign, quotes, yoda, no-else-return, consistent-return, comma-dangle, object-shorthand, prefer-template, one-var, one-var-declaration-per-line, no-unused-vars, max-len, vars-on-top */ /* global Breakpoints */ -(function() { - var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }; - var AUTO_SCROLL_OFFSET = 75; - var DOWN_BUILD_TRACE = '#down-build-trace'; - - this.Build = (function() { - Build.timeout = null; - - Build.state = null; - - function Build(options) { - options = options || $('.js-build-options').data(); - this.pageUrl = options.pageUrl; - this.buildUrl = options.buildUrl; - this.buildStatus = options.buildStatus; - this.state = options.logState; - this.buildStage = options.buildStage; - this.updateDropdown = bind(this.updateDropdown, this); - this.$document = $(document); - this.$body = $('body'); - this.$buildTrace = $('#build-trace'); - this.$autoScrollContainer = $('.autoscroll-container'); - this.$autoScrollStatus = $('#autoscroll-status'); - this.$autoScrollStatusText = this.$autoScrollStatus.find('.status-text'); - this.$upBuildTrace = $('#up-build-trace'); - this.$downBuildTrace = $(DOWN_BUILD_TRACE); - this.$scrollTopBtn = $('#scroll-top'); - this.$scrollBottomBtn = $('#scroll-bottom'); - this.$buildRefreshAnimation = $('.js-build-refresh'); - - clearTimeout(Build.timeout); - // Init breakpoint checker - this.bp = Breakpoints.get(); - - this.initSidebar(); - this.$buildScroll = $('#js-build-scroll'); - - 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); - this.$document.on('scroll', this.initScrollMonitor.bind(this)); - $(window).off('resize.build').on('resize.build', this.sidebarOnResize.bind(this)); - $('a', this.$buildScroll).off('click.stepTrace').on('click.stepTrace', this.stepTrace); - this.updateArtifactRemoveDate(); - if ($('#build-trace').length) { - this.getInitialBuildTrace(); - this.initScrollButtonAffix(); - } - this.invokeBuildTrace(); +var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }; +var AUTO_SCROLL_OFFSET = 75; +var DOWN_BUILD_TRACE = '#down-build-trace'; + +window.Build = (function() { + Build.timeout = null; + + Build.state = null; + + function Build(options) { + options = options || $('.js-build-options').data(); + this.pageUrl = options.pageUrl; + this.buildUrl = options.buildUrl; + this.buildStatus = options.buildStatus; + this.state = options.logState; + this.buildStage = options.buildStage; + this.updateDropdown = bind(this.updateDropdown, this); + this.$document = $(document); + this.$body = $('body'); + this.$buildTrace = $('#build-trace'); + this.$autoScrollContainer = $('.autoscroll-container'); + this.$autoScrollStatus = $('#autoscroll-status'); + this.$autoScrollStatusText = this.$autoScrollStatus.find('.status-text'); + this.$upBuildTrace = $('#up-build-trace'); + this.$downBuildTrace = $(DOWN_BUILD_TRACE); + this.$scrollTopBtn = $('#scroll-top'); + this.$scrollBottomBtn = $('#scroll-bottom'); + this.$buildRefreshAnimation = $('.js-build-refresh'); + + clearTimeout(Build.timeout); + // Init breakpoint checker + this.bp = Breakpoints.get(); + + this.initSidebar(); + this.$buildScroll = $('#js-build-scroll'); + + 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); + this.$document.on('scroll', this.initScrollMonitor.bind(this)); + $(window).off('resize.build').on('resize.build', this.sidebarOnResize.bind(this)); + $('a', this.$buildScroll).off('click.stepTrace').on('click.stepTrace', this.stepTrace); + this.updateArtifactRemoveDate(); + if ($('#build-trace').length) { + this.getInitialBuildTrace(); + this.initScrollButtonAffix(); } - - Build.prototype.initSidebar = function() { - this.$sidebar = $('.js-build-sidebar'); - this.$sidebar.niceScroll(); - this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar); - }; - - Build.prototype.location = function() { - return window.location.href.split("#")[0]; - }; - - Build.prototype.invokeBuildTrace = function() { - var continueRefreshStatuses = ['running', 'pending']; - // Continue to update build trace when build is running or pending - if (continueRefreshStatuses.indexOf(this.buildStatus) !== -1) { - // Check for new build output if user still watching build page - // Only valid for runnig build when output changes during time - Build.timeout = setTimeout((function(_this) { - return function() { - if (_this.location() === _this.pageUrl) { - return _this.getBuildTrace(); - } - }; - })(this), 4000); - } - }; - - Build.prototype.getInitialBuildTrace = function() { - var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped']; - - return $.ajax({ - url: this.buildUrl, - dataType: 'json', - success: function(buildData) { - $('.js-build-output').html(buildData.trace_html); - if (window.location.hash === DOWN_BUILD_TRACE) { - $("html,body").scrollTop(this.$buildTrace.height()); + this.invokeBuildTrace(); + } + + Build.prototype.initSidebar = function() { + this.$sidebar = $('.js-build-sidebar'); + this.$sidebar.niceScroll(); + this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar); + }; + + Build.prototype.location = function() { + return window.location.href.split("#")[0]; + }; + + Build.prototype.invokeBuildTrace = function() { + var continueRefreshStatuses = ['running', 'pending']; + // Continue to update build trace when build is running or pending + if (continueRefreshStatuses.indexOf(this.buildStatus) !== -1) { + // Check for new build output if user still watching build page + // Only valid for runnig build when output changes during time + Build.timeout = setTimeout((function(_this) { + return function() { + if (_this.location() === _this.pageUrl) { + return _this.getBuildTrace(); } - if (removeRefreshStatuses.indexOf(buildData.status) !== -1) { - this.$buildRefreshAnimation.remove(); - return this.initScrollMonitor(); + }; + })(this), 4000); + } + }; + + Build.prototype.getInitialBuildTrace = function() { + var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped']; + + return $.ajax({ + url: this.buildUrl, + dataType: 'json', + success: function(buildData) { + $('.js-build-output').html(buildData.trace_html); + if (window.location.hash === DOWN_BUILD_TRACE) { + $("html,body").scrollTop(this.$buildTrace.height()); + } + if (removeRefreshStatuses.indexOf(buildData.status) !== -1) { + this.$buildRefreshAnimation.remove(); + return this.initScrollMonitor(); + } + }.bind(this) + }); + }; + + Build.prototype.getBuildTrace = function() { + return $.ajax({ + url: this.pageUrl + "/trace.json?state=" + (encodeURIComponent(this.state)), + dataType: "json", + success: (function(_this) { + return function(log) { + var pageUrl; + + if (log.state) { + _this.state = log.state; } - }.bind(this) - }); - }; - - Build.prototype.getBuildTrace = function() { - return $.ajax({ - url: this.pageUrl + "/trace.json?state=" + (encodeURIComponent(this.state)), - dataType: "json", - success: (function(_this) { - return function(log) { - var pageUrl; - - if (log.state) { - _this.state = log.state; + _this.invokeBuildTrace(); + if (log.status === "running") { + if (log.append) { + $('.js-build-output').append(log.html); + } else { + $('.js-build-output').html(log.html); } - _this.invokeBuildTrace(); - if (log.status === "running") { - if (log.append) { - $('.js-build-output').append(log.html); - } else { - $('.js-build-output').html(log.html); - } - return _this.checkAutoscroll(); - } else if (log.status !== _this.buildStatus) { - pageUrl = _this.pageUrl; - if (_this.$autoScrollStatus.data('state') === 'enabled') { - pageUrl += DOWN_BUILD_TRACE; - } - - return gl.utils.visitUrl(pageUrl); + return _this.checkAutoscroll(); + } else if (log.status !== _this.buildStatus) { + pageUrl = _this.pageUrl; + if (_this.$autoScrollStatus.data('state') === 'enabled') { + pageUrl += DOWN_BUILD_TRACE; } - }; - })(this) - }); - }; - - Build.prototype.checkAutoscroll = function() { - if (this.$autoScrollStatus.data("state") === "enabled") { - return $("html,body").scrollTop(this.$buildTrace.height()); - } - - // Handle a situation where user started new build - // but never scrolled a page - if (!this.$scrollTopBtn.is(':visible') && - !this.$scrollBottomBtn.is(':visible') && - !gl.utils.isInViewport(this.$downBuildTrace.get(0))) { - this.$scrollBottomBtn.show(); - } - }; - Build.prototype.initScrollButtonAffix = function() { - // Hide everything initially - this.$scrollTopBtn.hide(); - this.$scrollBottomBtn.hide(); - this.$autoScrollContainer.hide(); - }; - - // Page scroll listener to detect if user has scrolling page - // and handle following cases - // 1) User is at Top of Build Log; - // - Hide Top Arrow button - // - Show Bottom Arrow button - // - Disable Autoscroll and hide indicator (when build is running) - // 2) User is at Bottom of Build Log; - // - Show Top Arrow button - // - Hide Bottom Arrow button - // - Enable Autoscroll and show indicator (when build is running) - // 3) User is somewhere in middle of Build Log; - // - Show Top Arrow button - // - Show Bottom Arrow button - // - Disable Autoscroll and hide indicator (when build is running) - Build.prototype.initScrollMonitor = function() { - if (!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) { - // User is somewhere in middle of Build Log - - this.$scrollTopBtn.show(); - - if (this.buildStatus === 'success' || this.buildStatus === 'failed') { // Check if Build is completed - this.$scrollBottomBtn.show(); - } else if (this.$buildRefreshAnimation.is(':visible') && !gl.utils.isInViewport(this.$buildRefreshAnimation.get(0))) { - this.$scrollBottomBtn.show(); - } else { - this.$scrollBottomBtn.hide(); - } - - // Hide Autoscroll Status Indicator - if (this.$scrollBottomBtn.is(':visible')) { - this.$autoScrollContainer.hide(); - this.$autoScrollStatusText.removeClass('animate'); - } else { - this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show(); - this.$autoScrollStatusText.addClass('animate'); - } - } else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) { - // User is at Top of Build Log + return gl.utils.visitUrl(pageUrl); + } + }; + })(this) + }); + }; + + Build.prototype.checkAutoscroll = function() { + if (this.$autoScrollStatus.data("state") === "enabled") { + return $("html,body").scrollTop(this.$buildTrace.height()); + } - this.$scrollTopBtn.hide(); + // Handle a situation where user started new build + // but never scrolled a page + if (!this.$scrollTopBtn.is(':visible') && + !this.$scrollBottomBtn.is(':visible') && + !gl.utils.isInViewport(this.$downBuildTrace.get(0))) { + this.$scrollBottomBtn.show(); + } + }; + + Build.prototype.initScrollButtonAffix = function() { + // Hide everything initially + this.$scrollTopBtn.hide(); + this.$scrollBottomBtn.hide(); + this.$autoScrollContainer.hide(); + }; + + // Page scroll listener to detect if user has scrolling page + // and handle following cases + // 1) User is at Top of Build Log; + // - Hide Top Arrow button + // - Show Bottom Arrow button + // - Disable Autoscroll and hide indicator (when build is running) + // 2) User is at Bottom of Build Log; + // - Show Top Arrow button + // - Hide Bottom Arrow button + // - Enable Autoscroll and show indicator (when build is running) + // 3) User is somewhere in middle of Build Log; + // - Show Top Arrow button + // - Show Bottom Arrow button + // - Disable Autoscroll and hide indicator (when build is running) + Build.prototype.initScrollMonitor = function() { + if (!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) { + // User is somewhere in middle of Build Log + + this.$scrollTopBtn.show(); + + if (this.buildStatus === 'success' || this.buildStatus === 'failed') { // Check if Build is completed + this.$scrollBottomBtn.show(); + } else if (this.$buildRefreshAnimation.is(':visible') && !gl.utils.isInViewport(this.$buildRefreshAnimation.get(0))) { this.$scrollBottomBtn.show(); + } else { + this.$scrollBottomBtn.hide(); + } + // Hide Autoscroll Status Indicator + if (this.$scrollBottomBtn.is(':visible')) { this.$autoScrollContainer.hide(); this.$autoScrollStatusText.removeClass('animate'); - } else if ((!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) || - (this.$buildRefreshAnimation.is(':visible') && gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)))) { - // User is at Bottom of Build Log - - this.$scrollTopBtn.show(); - this.$scrollBottomBtn.hide(); - - // Show and Reposition Autoscroll Status Indicator + } else { this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show(); this.$autoScrollStatusText.addClass('animate'); - } else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) { - // Build Log height is small + } + } else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) { + // User is at Top of Build Log - this.$scrollTopBtn.hide(); - this.$scrollBottomBtn.hide(); + this.$scrollTopBtn.hide(); + this.$scrollBottomBtn.show(); - // Hide Autoscroll Status Indicator - this.$autoScrollContainer.hide(); - this.$autoScrollStatusText.removeClass('animate'); - } + this.$autoScrollContainer.hide(); + this.$autoScrollStatusText.removeClass('animate'); + } else if ((!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) || + (this.$buildRefreshAnimation.is(':visible') && gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)))) { + // User is at Bottom of Build Log - if (this.buildStatus === "running" || this.buildStatus === "pending") { - // Check if Refresh Animation is in Viewport and enable Autoscroll, disable otherwise. - this.$autoScrollStatus.data("state", gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)) ? 'enabled' : 'disabled'); - } - }; - - Build.prototype.shouldHideSidebarForViewport = function() { - var bootstrapBreakpoint; - bootstrapBreakpoint = this.bp.getBreakpointSize(); - return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm'; - }; - - Build.prototype.toggleSidebar = function(shouldHide) { - var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined; - this.$buildScroll.toggleClass('sidebar-expanded', shouldShow) - .toggleClass('sidebar-collapsed', shouldHide); - this.$sidebar.toggleClass('right-sidebar-expanded', shouldShow) - .toggleClass('right-sidebar-collapsed', shouldHide); - }; - - Build.prototype.sidebarOnResize = function() { - this.toggleSidebar(this.shouldHideSidebarForViewport()); - }; - - Build.prototype.sidebarOnClick = function() { - if (this.shouldHideSidebarForViewport()) this.toggleSidebar(); - }; - - Build.prototype.updateArtifactRemoveDate = function() { - var $date, date; - $date = $('.js-artifacts-remove'); - if ($date.length) { - 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(); - var stage = e.currentTarget.text; - this.updateStageDropdownText(stage); - this.populateJobs(stage); - }; - - Build.prototype.stepTrace = function(e) { - var $currentTarget; - e.preventDefault(); - $currentTarget = $(e.currentTarget); - $.scrollTo($currentTarget.attr('href'), { - offset: 0 - }); - }; - - return Build; - })(); -}).call(window); + this.$scrollTopBtn.show(); + this.$scrollBottomBtn.hide(); + + // Show and Reposition Autoscroll Status Indicator + this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show(); + this.$autoScrollStatusText.addClass('animate'); + } else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) { + // Build Log height is small + + this.$scrollTopBtn.hide(); + this.$scrollBottomBtn.hide(); + + // Hide Autoscroll Status Indicator + this.$autoScrollContainer.hide(); + this.$autoScrollStatusText.removeClass('animate'); + } + + if (this.buildStatus === "running" || this.buildStatus === "pending") { + // Check if Refresh Animation is in Viewport and enable Autoscroll, disable otherwise. + this.$autoScrollStatus.data("state", gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)) ? 'enabled' : 'disabled'); + } + }; + + Build.prototype.shouldHideSidebarForViewport = function() { + var bootstrapBreakpoint; + bootstrapBreakpoint = this.bp.getBreakpointSize(); + return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm'; + }; + + Build.prototype.toggleSidebar = function(shouldHide) { + var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined; + this.$buildScroll.toggleClass('sidebar-expanded', shouldShow) + .toggleClass('sidebar-collapsed', shouldHide); + this.$sidebar.toggleClass('right-sidebar-expanded', shouldShow) + .toggleClass('right-sidebar-collapsed', shouldHide); + }; + + Build.prototype.sidebarOnResize = function() { + this.toggleSidebar(this.shouldHideSidebarForViewport()); + }; + + Build.prototype.sidebarOnClick = function() { + if (this.shouldHideSidebarForViewport()) this.toggleSidebar(); + }; + + Build.prototype.updateArtifactRemoveDate = function() { + var $date, date; + $date = $('.js-artifacts-remove'); + if ($date.length) { + 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(); + var stage = e.currentTarget.text; + this.updateStageDropdownText(stage); + this.populateJobs(stage); + }; + + Build.prototype.stepTrace = function(e) { + var $currentTarget; + e.preventDefault(); + $currentTarget = $(e.currentTarget); + $.scrollTo($currentTarget.attr('href'), { + offset: 0 + }); + }; + + return Build; +})(); diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js index cae9a0ffca4..bd479700fd3 100644 --- a/app/assets/javascripts/build_artifacts.js +++ b/app/assets/javascripts/build_artifacts.js @@ -1,26 +1,25 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, max-len */ -(function() { - this.BuildArtifacts = (function() { - 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(); - }); - }; +window.BuildArtifacts = (function() { + function BuildArtifacts() { + this.disablePropagation(); + this.setupEntryClick(); + } - BuildArtifacts.prototype.setupEntryClick = function() { - return $('.tree-holder').on('click', 'tr[data-link]', function(e) { - return window.location = this.dataset.link; - }); - }; + 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(); + }); + }; - return BuildArtifacts; - })(); -}).call(window); + BuildArtifacts.prototype.setupEntryClick = function() { + return $('.tree-holder').on('click', 'tr[data-link]', function(e) { + return window.location = this.dataset.link; + }); + }; + + return BuildArtifacts; +})(); diff --git a/app/assets/javascripts/ci_lint_editor.js b/app/assets/javascripts/ci_lint_editor.js index 56ffaa765a8..dd4a08a2f31 100644 --- a/app/assets/javascripts/ci_lint_editor.js +++ b/app/assets/javascripts/ci_lint_editor.js @@ -1,18 +1,17 @@ -(() => { - window.gl = window.gl || {}; - class CILintEditor { - constructor() { - this.editor = window.ace.edit('ci-editor'); - this.textarea = document.querySelector('#content'); +window.gl = window.gl || {}; - this.editor.getSession().setMode('ace/mode/yaml'); - this.editor.on('input', () => { - const content = this.editor.getSession().getValue(); - this.textarea.value = content; - }); - } +class CILintEditor { + constructor() { + this.editor = window.ace.edit('ci-editor'); + this.textarea = document.querySelector('#content'); + + this.editor.getSession().setMode('ace/mode/yaml'); + this.editor.on('input', () => { + const content = this.editor.getSession().getValue(); + this.textarea.value = content; + }); } +} - gl.CILintEditor = CILintEditor; -})(); +gl.CILintEditor = CILintEditor; diff --git a/app/assets/javascripts/commit.js b/app/assets/javascripts/commit.js index 566b322eb49..5f637524e30 100644 --- a/app/assets/javascripts/commit.js +++ b/app/assets/javascripts/commit.js @@ -1,14 +1,12 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife */ /* global CommitFile */ -(function() { - this.Commit = (function() { - function Commit() { - $('.files .diff-file').each(function() { - return new CommitFile(this); - }); - } +window.Commit = (function() { + function Commit() { + $('.files .diff-file').each(function() { + return new CommitFile(this); + }); + } - return Commit; - })(); -}).call(window); + return Commit; +})(); diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index ccd895f3bf4..e3f9eaaf39c 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -1,68 +1,66 @@ /* 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 */ -(function() { - this.CommitsList = (function() { - var CommitsList = {}; +window.CommitsList = (function() { + var CommitsList = {}; - CommitsList.timer = null; + CommitsList.timer = null; - CommitsList.init = function(limit) { - $("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, function() { - gl.utils.localTimeAgo($('.js-timeago')); - }); - this.content = $("#commits-list"); - this.searchField = $("#commits-search"); - this.lastSearch = this.searchField.val(); - return this.initSearch(); - }; + CommitsList.init = function(limit) { + $("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, function() { + gl.utils.localTimeAgo($('.js-timeago')); + }); + 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((function(_this) { + return function() { + clearTimeout(_this.timer); + return _this.timer = setTimeout(_this.filterResults, 500); + }; + })(this)); + }; - 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" + }); + }; - return CommitsList; - })(); -}).call(window); + return CommitsList; +})(); diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js index 15df105d4cc..9e5dbd64a7e 100644 --- a/app/assets/javascripts/compare.js +++ b/app/assets/javascripts/compare.js @@ -1,91 +1,90 @@ /* 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 */ -(function() { - this.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(); - } + +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(); - } + } + }); + }; + })(this)); + this.initialState(); + } - Compare.prototype.initialState = function() { - this.getSourceHtml(); - return this.getTargetHtml(); - }; + 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() - }, - beforeSend: function() { - return $('.mr_target_commit').empty(); - }, - success: function(html) { - return $('.js-target-branch-dropdown .dropdown-content').html(html); - } - }); - }; + 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.getSourceHtml = function() { - return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', { - ref: $("input[name='merge_request[source_branch]']").val() - }); - }; + 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.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)); - } - }); - }; + 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; - })(); -}).call(window); + return Compare; +})(); diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js index 1eca973e069..d91bfb1ccbd 100644 --- a/app/assets/javascripts/compare_autocomplete.js +++ b/app/assets/javascripts/compare_autocomplete.js @@ -1,69 +1,67 @@ /* 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 */ -(function() { - this.CompareAutocomplete = (function() { - function CompareAutocomplete() { - this.initDropdown(); - } +window.CompareAutocomplete = (function() { + 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') - } - }).done(function(refs) { - return callback(refs); - }); - }, - selectable: true, - filterable: true, - filterByText: 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') } - }, - 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, + filterByText: 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; - })(); -}).call(window); + return CompareAutocomplete; +})(); diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index a1c1b721228..b375b61202e 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -1,31 +1,30 @@ /* 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 */ -(function() { - this.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)); - } - return ConfirmDangerModal; - })(); -}).call(window); +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)); + } + + return ConfirmDangerModal; +})(); diff --git a/app/assets/javascripts/copy_as_gfm.js b/app/assets/javascripts/copy_as_gfm.js index 8883c339335..0fb7bde1fd6 100644 --- a/app/assets/javascripts/copy_as_gfm.js +++ b/app/assets/javascripts/copy_as_gfm.js @@ -1,364 +1,361 @@ /* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */ -/* jshint esversion: 6 */ require('./lib/utils/common_utils'); -(() => { - const gfmRules = { - // The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert - // GitLab Flavored Markdown (GFM) to HTML. - // These handlers consequently convert that same HTML to GFM to be copied to the clipboard. - // Every filter in lib/banzai/pipeline/gfm_pipeline.rb that generates HTML - // from GFM should have a handler here, in reverse order. - // The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb. - InlineDiffFilter: { - 'span.idiff.addition'(el, text) { - return `{+${text}+}`; - }, - 'span.idiff.deletion'(el, text) { - return `{-${text}-}`; - }, - }, - TaskListFilter: { - 'input[type=checkbox].task-list-item-checkbox'(el, text) { - return `[${el.checked ? 'x' : ' '}]`; - }, - }, - ReferenceFilter: { - '.tooltip'(el, text) { - return ''; - }, - 'a.gfm:not([data-link=true])'(el, text) { - return el.dataset.original || text; - }, - }, - AutolinkFilter: { - 'a'(el, text) { - // Fallback on the regular MarkdownFilter's `a` handler. - if (text !== el.getAttribute('href')) return false; - - return text; - }, - }, - TableOfContentsFilter: { - 'ul.section-nav'(el, text) { - return '[[_TOC_]]'; - }, - }, - EmojiFilter: { - 'img.emoji'(el, text) { - return el.getAttribute('alt'); - }, - 'gl-emoji'(el, text) { - return `:${el.getAttribute('data-name')}:`; - }, - }, - ImageLinkFilter: { - 'a.no-attachment-icon'(el, text) { - return text; - }, - }, - VideoLinkFilter: { - '.video-container'(el, text) { - const videoEl = el.querySelector('video'); - if (!videoEl) return false; - - return CopyAsGFM.nodeToGFM(videoEl); - }, - 'video'(el, text) { - return `![${el.dataset.title}](${el.getAttribute('src')})`; - }, - }, - MathFilter: { - 'pre.code.math[data-math-style=display]'(el, text) { - return `\`\`\`math\n${text.trim()}\n\`\`\``; - }, - 'code.code.math[data-math-style=inline]'(el, text) { - return `$\`${text}\`$`; - }, - 'span.katex-display span.katex-mathml'(el, text) { - const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]'); - if (!mathAnnotation) return false; - - return `\`\`\`math\n${CopyAsGFM.nodeToGFM(mathAnnotation)}\n\`\`\``; - }, - 'span.katex-mathml'(el, text) { - const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]'); - if (!mathAnnotation) return false; - - return `$\`${CopyAsGFM.nodeToGFM(mathAnnotation)}\`$`; - }, - 'span.katex-html'(el, text) { - // We don't want to include the content of this element in the copied text. - return ''; - }, - 'annotation[encoding="application/x-tex"]'(el, text) { - return text.trim(); - }, - }, - SanitizationFilter: { - 'a[name]:not([href]):empty'(el, text) { - return el.outerHTML; - }, - 'dl'(el, text) { - let lines = text.trim().split('\n'); - // Add two spaces to the front of subsequent list items lines, - // or leave the line entirely blank. - lines = lines.map((l) => { +const gfmRules = { + // The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert + // GitLab Flavored Markdown (GFM) to HTML. + // These handlers consequently convert that same HTML to GFM to be copied to the clipboard. + // Every filter in lib/banzai/pipeline/gfm_pipeline.rb that generates HTML + // from GFM should have a handler here, in reverse order. + // The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb. + InlineDiffFilter: { + 'span.idiff.addition'(el, text) { + return `{+${text}+}`; + }, + 'span.idiff.deletion'(el, text) { + return `{-${text}-}`; + }, + }, + TaskListFilter: { + 'input[type=checkbox].task-list-item-checkbox'(el, text) { + return `[${el.checked ? 'x' : ' '}]`; + }, + }, + ReferenceFilter: { + '.tooltip'(el, text) { + return ''; + }, + 'a.gfm:not([data-link=true])'(el, text) { + return el.dataset.original || text; + }, + }, + AutolinkFilter: { + 'a'(el, text) { + // Fallback on the regular MarkdownFilter's `a` handler. + if (text !== el.getAttribute('href')) return false; + + return text; + }, + }, + TableOfContentsFilter: { + 'ul.section-nav'(el, text) { + return '[[_TOC_]]'; + }, + }, + EmojiFilter: { + 'img.emoji'(el, text) { + return el.getAttribute('alt'); + }, + 'gl-emoji'(el, text) { + return `:${el.getAttribute('data-name')}:`; + }, + }, + ImageLinkFilter: { + 'a.no-attachment-icon'(el, text) { + return text; + }, + }, + VideoLinkFilter: { + '.video-container'(el, text) { + const videoEl = el.querySelector('video'); + if (!videoEl) return false; + + return CopyAsGFM.nodeToGFM(videoEl); + }, + 'video'(el, text) { + return `![${el.dataset.title}](${el.getAttribute('src')})`; + }, + }, + MathFilter: { + 'pre.code.math[data-math-style=display]'(el, text) { + return `\`\`\`math\n${text.trim()}\n\`\`\``; + }, + 'code.code.math[data-math-style=inline]'(el, text) { + return `$\`${text}\`$`; + }, + 'span.katex-display span.katex-mathml'(el, text) { + const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]'); + if (!mathAnnotation) return false; + + return `\`\`\`math\n${CopyAsGFM.nodeToGFM(mathAnnotation)}\n\`\`\``; + }, + 'span.katex-mathml'(el, text) { + const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]'); + if (!mathAnnotation) return false; + + return `$\`${CopyAsGFM.nodeToGFM(mathAnnotation)}\`$`; + }, + 'span.katex-html'(el, text) { + // We don't want to include the content of this element in the copied text. + return ''; + }, + 'annotation[encoding="application/x-tex"]'(el, text) { + return text.trim(); + }, + }, + SanitizationFilter: { + 'a[name]:not([href]):empty'(el, text) { + return el.outerHTML; + }, + 'dl'(el, text) { + let lines = text.trim().split('\n'); + // Add two spaces to the front of subsequent list items lines, + // or leave the line entirely blank. + lines = lines.map((l) => { + const line = l.trim(); + if (line.length === 0) return ''; + + return ` ${line}`; + }); + + return `<dl>\n${lines.join('\n')}\n</dl>`; + }, + 'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr, summary, details'(el, text) { + const tag = el.nodeName.toLowerCase(); + return `<${tag}>${text}</${tag}>`; + }, + }, + SyntaxHighlightFilter: { + 'pre.code.highlight'(el, t) { + const text = t.trim(); + + let lang = el.getAttribute('lang'); + if (lang === 'plaintext') { + lang = ''; + } + + // Prefixes lines with 4 spaces if the code contains triple backticks + if (lang === '' && text.match(/^```/gm)) { + return text.split('\n').map((l) => { const line = l.trim(); if (line.length === 0) return ''; - return ` ${line}`; - }); + return ` ${line}`; + }).join('\n'); + } - return `<dl>\n${lines.join('\n')}\n</dl>`; - }, - 'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr, summary, details'(el, text) { - const tag = el.nodeName.toLowerCase(); - return `<${tag}>${text}</${tag}>`; - }, + return `\`\`\`${lang}\n${text}\n\`\`\``; + }, + 'pre > code'(el, text) { + // Don't wrap code blocks in `` + return text; + }, + }, + MarkdownFilter: { + 'br'(el, text) { + // Two spaces at the end of a line are turned into a BR + return ' '; }, - SyntaxHighlightFilter: { - 'pre.code.highlight'(el, t) { - const text = t.trim(); + 'code'(el, text) { + let backtickCount = 1; + const backtickMatch = text.match(/`+/); + if (backtickMatch) { + backtickCount = backtickMatch[0].length + 1; + } - let lang = el.getAttribute('lang'); - if (lang === 'plaintext') { - lang = ''; - } + const backticks = Array(backtickCount + 1).join('`'); + const spaceOrNoSpace = backtickCount > 1 ? ' ' : ''; - // Prefixes lines with 4 spaces if the code contains triple backticks - if (lang === '' && text.match(/^```/gm)) { - return text.split('\n').map((l) => { - const line = l.trim(); - if (line.length === 0) return ''; + return backticks + spaceOrNoSpace + text + spaceOrNoSpace + backticks; + }, + 'blockquote'(el, text) { + return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n'); + }, + 'img'(el, text) { + return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`; + }, + 'a.anchor'(el, text) { + // Don't render a Markdown link for the anchor link inside a heading + return text; + }, + 'a'(el, text) { + return `[${text}](${el.getAttribute('href')})`; + }, + 'li'(el, text) { + const lines = text.trim().split('\n'); + const firstLine = `- ${lines.shift()}`; + // Add four spaces to the front of subsequent list items lines, + // or leave the line entirely blank. + const nextLines = lines.map((s) => { + if (s.trim().length === 0) return ''; + + return ` ${s}`; + }); + + return `${firstLine}\n${nextLines.join('\n')}`; + }, + 'ul'(el, text) { + return text; + }, + 'ol'(el, text) { + // LIs get a `- ` prefix by default, which we replace by `1. ` for ordered lists. + return text.replace(/^- /mg, '1. '); + }, + 'h1'(el, text) { + return `# ${text.trim()}`; + }, + 'h2'(el, text) { + return `## ${text.trim()}`; + }, + 'h3'(el, text) { + return `### ${text.trim()}`; + }, + 'h4'(el, text) { + return `#### ${text.trim()}`; + }, + 'h5'(el, text) { + return `##### ${text.trim()}`; + }, + 'h6'(el, text) { + return `###### ${text.trim()}`; + }, + 'strong'(el, text) { + return `**${text}**`; + }, + 'em'(el, text) { + return `_${text}_`; + }, + 'del'(el, text) { + return `~~${text}~~`; + }, + 'sup'(el, text) { + return `^${text}`; + }, + 'hr'(el, text) { + return '-----'; + }, + 'table'(el, text) { + const theadEl = el.querySelector('thead'); + const tbodyEl = el.querySelector('tbody'); + if (!theadEl || !tbodyEl) return false; - return ` ${line}`; - }).join('\n'); - } + const theadText = CopyAsGFM.nodeToGFM(theadEl); + const tbodyText = CopyAsGFM.nodeToGFM(tbodyEl); - return `\`\`\`${lang}\n${text}\n\`\`\``; - }, - 'pre > code'(el, text) { - // Don't wrap code blocks in `` - return text; - }, - }, - MarkdownFilter: { - 'br'(el, text) { - // Two spaces at the end of a line are turned into a BR - return ' '; - }, - 'code'(el, text) { - let backtickCount = 1; - const backtickMatch = text.match(/`+/); - if (backtickMatch) { - backtickCount = backtickMatch[0].length + 1; + return theadText + tbodyText; + }, + 'thead'(el, text) { + const cells = _.map(el.querySelectorAll('th'), (cell) => { + let chars = CopyAsGFM.nodeToGFM(cell).trim().length + 2; + + let before = ''; + let after = ''; + switch (cell.style.textAlign) { + case 'center': + before = ':'; + after = ':'; + chars -= 2; + break; + case 'right': + after = ':'; + chars -= 1; + break; + default: + break; } - const backticks = Array(backtickCount + 1).join('`'); - const spaceOrNoSpace = backtickCount > 1 ? ' ' : ''; - - return backticks + spaceOrNoSpace + text + spaceOrNoSpace + backticks; - }, - 'blockquote'(el, text) { - return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n'); - }, - 'img'(el, text) { - return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`; - }, - 'a.anchor'(el, text) { - // Don't render a Markdown link for the anchor link inside a heading - return text; - }, - 'a'(el, text) { - return `[${text}](${el.getAttribute('href')})`; - }, - 'li'(el, text) { - const lines = text.trim().split('\n'); - const firstLine = `- ${lines.shift()}`; - // Add four spaces to the front of subsequent list items lines, - // or leave the line entirely blank. - const nextLines = lines.map((s) => { - if (s.trim().length === 0) return ''; - - return ` ${s}`; - }); - - return `${firstLine}\n${nextLines.join('\n')}`; - }, - 'ul'(el, text) { - return text; - }, - 'ol'(el, text) { - // LIs get a `- ` prefix by default, which we replace by `1. ` for ordered lists. - return text.replace(/^- /mg, '1. '); - }, - 'h1'(el, text) { - return `# ${text.trim()}`; - }, - 'h2'(el, text) { - return `## ${text.trim()}`; - }, - 'h3'(el, text) { - return `### ${text.trim()}`; - }, - 'h4'(el, text) { - return `#### ${text.trim()}`; - }, - 'h5'(el, text) { - return `##### ${text.trim()}`; - }, - 'h6'(el, text) { - return `###### ${text.trim()}`; - }, - 'strong'(el, text) { - return `**${text}**`; - }, - 'em'(el, text) { - return `_${text}_`; - }, - 'del'(el, text) { - return `~~${text}~~`; - }, - 'sup'(el, text) { - return `^${text}`; - }, - 'hr'(el, text) { - return '-----'; - }, - 'table'(el, text) { - const theadEl = el.querySelector('thead'); - const tbodyEl = el.querySelector('tbody'); - if (!theadEl || !tbodyEl) return false; - - const theadText = CopyAsGFM.nodeToGFM(theadEl); - const tbodyText = CopyAsGFM.nodeToGFM(tbodyEl); - - return theadText + tbodyText; - }, - 'thead'(el, text) { - const cells = _.map(el.querySelectorAll('th'), (cell) => { - let chars = CopyAsGFM.nodeToGFM(cell).trim().length + 2; - - let before = ''; - let after = ''; - switch (cell.style.textAlign) { - case 'center': - before = ':'; - after = ':'; - chars -= 2; - break; - case 'right': - after = ':'; - chars -= 1; - break; - default: - break; - } - - chars = Math.max(chars, 3); - - const middle = Array(chars + 1).join('-'); - - return before + middle + after; - }); - - return `${text}|${cells.join('|')}|`; - }, - 'tr'(el, text) { - const cells = _.map(el.querySelectorAll('td, th'), cell => CopyAsGFM.nodeToGFM(cell).trim()); - return `| ${cells.join(' | ')} |`; - }, - }, - }; - - class CopyAsGFM { - constructor() { - $(document).on('copy', '.md, .wiki', this.handleCopy); - $(document).on('paste', '.js-gfm-input', this.handlePaste); - } - - handleCopy(e) { - const clipboardData = e.originalEvent.clipboardData; - if (!clipboardData) return; - - const documentFragment = window.gl.utils.getSelectedFragment(); - if (!documentFragment) return; + chars = Math.max(chars, 3); - // If the documentFragment contains more than just Markdown, don't copy as GFM. - if (documentFragment.querySelector('.md, .wiki')) return; + const middle = Array(chars + 1).join('-'); - e.preventDefault(); - clipboardData.setData('text/plain', documentFragment.textContent); + return before + middle + after; + }); - const gfm = CopyAsGFM.nodeToGFM(documentFragment); - clipboardData.setData('text/x-gfm', gfm); - } + return `${text}|${cells.join('|')}|`; + }, + 'tr'(el, text) { + const cells = _.map(el.querySelectorAll('td, th'), cell => CopyAsGFM.nodeToGFM(cell).trim()); + return `| ${cells.join(' | ')} |`; + }, + }, +}; - handlePaste(e) { - const clipboardData = e.originalEvent.clipboardData; - if (!clipboardData) return; +class CopyAsGFM { + constructor() { + $(document).on('copy', '.md, .wiki', this.handleCopy); + $(document).on('paste', '.js-gfm-input', this.handlePaste); + } - const gfm = clipboardData.getData('text/x-gfm'); - if (!gfm) return; + handleCopy(e) { + const clipboardData = e.originalEvent.clipboardData; + if (!clipboardData) return; - e.preventDefault(); + const documentFragment = window.gl.utils.getSelectedFragment(); + if (!documentFragment) return; - window.gl.utils.insertText(e.target, gfm); - } + // If the documentFragment contains more than just Markdown, don't copy as GFM. + if (documentFragment.querySelector('.md, .wiki')) return; - static nodeToGFM(node) { - if (node.nodeType === Node.TEXT_NODE) { - return node.textContent; - } + e.preventDefault(); + clipboardData.setData('text/plain', documentFragment.textContent); - const text = this.innerGFM(node); + const gfm = CopyAsGFM.nodeToGFM(documentFragment); + clipboardData.setData('text/x-gfm', gfm); + } - if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { - return text; - } + handlePaste(e) { + const clipboardData = e.originalEvent.clipboardData; + if (!clipboardData) return; - for (const filter in gfmRules) { - const rules = gfmRules[filter]; + const gfm = clipboardData.getData('text/x-gfm'); + if (!gfm) return; - for (const selector in rules) { - const func = rules[selector]; + e.preventDefault(); - if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue; + window.gl.utils.insertText(e.target, gfm); + } - const result = func(node, text); - if (result === false) continue; + static nodeToGFM(node) { + if (node.nodeType === Node.TEXT_NODE) { + return node.textContent; + } - return result; - } - } + const text = this.innerGFM(node); + if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { return text; } - static innerGFM(parentNode) { - const nodes = parentNode.childNodes; + for (const filter in gfmRules) { + const rules = gfmRules[filter]; - const clonedParentNode = parentNode.cloneNode(true); - const clonedNodes = Array.prototype.slice.call(clonedParentNode.childNodes, 0); + for (const selector in rules) { + const func = rules[selector]; - for (let i = 0; i < nodes.length; i += 1) { - const node = nodes[i]; - const clonedNode = clonedNodes[i]; + if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue; - const text = this.nodeToGFM(node); + const result = func(node, text); + if (result === false) continue; - // `clonedNode.replaceWith(text)` is not yet widely supported - clonedNode.parentNode.replaceChild(document.createTextNode(text), clonedNode); + return result; } + } + + return text; + } - return clonedParentNode.innerText || clonedParentNode.textContent; + static innerGFM(parentNode) { + const nodes = parentNode.childNodes; + + const clonedParentNode = parentNode.cloneNode(true); + const clonedNodes = Array.prototype.slice.call(clonedParentNode.childNodes, 0); + + for (let i = 0; i < nodes.length; i += 1) { + const node = nodes[i]; + const clonedNode = clonedNodes[i]; + + const text = this.nodeToGFM(node); + + // `clonedNode.replaceWith(text)` is not yet widely supported + clonedNode.parentNode.replaceChild(document.createTextNode(text), clonedNode); } + + return clonedParentNode.innerText || clonedParentNode.textContent; } +} - window.gl = window.gl || {}; - window.gl.CopyAsGFM = CopyAsGFM; +window.gl = window.gl || {}; +window.gl.CopyAsGFM = CopyAsGFM; - new CopyAsGFM(); -})(); +new CopyAsGFM(); diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js index 615f485e18a..6dbec50b890 100644 --- a/app/assets/javascripts/copy_to_clipboard.js +++ b/app/assets/javascripts/copy_to_clipboard.js @@ -1,49 +1,46 @@ /* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, prefer-arrow-callback, max-len */ -/* global Clipboard */ - -window.Clipboard = require('vendor/clipboard'); - -(function() { - var genericError, genericSuccess, showTooltip; - - genericSuccess = function(e) { - showTooltip(e.trigger, 'Copied'); - // Clear the selection and blur the trigger so it loses its border - e.clearSelection(); - return $(e.trigger).blur(); - }; - - // Safari doesn't support `execCommand`, so instead we inform the user to - // copy manually. - // - // See http://clipboardjs.com/#browser-support - genericError = function(e) { - var key; - if (/Mac/i.test(navigator.userAgent)) { - key = '⌘'; // Command - } else { - key = 'Ctrl'; - } - return showTooltip(e.trigger, "Press " + key + "-C to copy"); - }; - - showTooltip = function(target, title) { - var $target = $(target); - var originalTitle = $target.data('original-title'); - - $target - .attr('title', 'Copied') - .tooltip('fixTitle') - .tooltip('show') - .attr('title', originalTitle) - .tooltip('fixTitle'); - }; - - $(function() { - var clipboard; - - clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]'); - clipboard.on('success', genericSuccess); - return clipboard.on('error', genericError); - }); -}).call(window); + +import Clipboard from 'vendor/clipboard'; + +var genericError, genericSuccess, showTooltip; + +genericSuccess = function(e) { + showTooltip(e.trigger, 'Copied'); + // Clear the selection and blur the trigger so it loses its border + e.clearSelection(); + return $(e.trigger).blur(); +}; + +// Safari doesn't support `execCommand`, so instead we inform the user to +// copy manually. +// +// See http://clipboardjs.com/#browser-support +genericError = function(e) { + var key; + if (/Mac/i.test(navigator.userAgent)) { + key = '⌘'; // Command + } else { + key = 'Ctrl'; + } + return showTooltip(e.trigger, "Press " + key + "-C to copy"); +}; + +showTooltip = function(target, title) { + var $target = $(target); + var originalTitle = $target.data('original-title'); + + $target + .attr('title', 'Copied') + .tooltip('fixTitle') + .tooltip('show') + .attr('title', originalTitle) + .tooltip('fixTitle'); +}; + +$(function() { + var clipboard; + + clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]'); + clipboard.on('success', genericSuccess); + return clipboard.on('error', genericError); +}); diff --git a/app/assets/javascripts/create_label.js b/app/assets/javascripts/create_label.js index 85384d98126..121d64db789 100644 --- a/app/assets/javascripts/create_label.js +++ b/app/assets/javascripts/create_label.js @@ -1,132 +1,127 @@ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-param-reassign, wrap-iife, max-len */ /* global Api */ -(function (w) { - class CreateLabelDropdown { - constructor ($el, namespacePath, projectPath) { - this.$el = $el; - this.namespacePath = namespacePath; - this.projectPath = projectPath; - this.$dropdownBack = $('.dropdown-menu-back', this.$el.closest('.dropdown')); - this.$cancelButton = $('.js-cancel-label-btn', this.$el); - this.$newLabelField = $('#new_label_name', this.$el); - this.$newColorField = $('#new_label_color', this.$el); - this.$colorPreview = $('.js-dropdown-label-color-preview', this.$el); - this.$newLabelError = $('.js-label-error', this.$el); - this.$newLabelCreateButton = $('.js-new-label-btn', this.$el); - this.$colorSuggestions = $('.suggest-colors-dropdown a', this.$el); - - this.$newLabelError.hide(); - this.$newLabelCreateButton.disable(); +class CreateLabelDropdown { + constructor ($el, namespacePath, projectPath) { + this.$el = $el; + this.namespacePath = namespacePath; + this.projectPath = projectPath; + this.$dropdownBack = $('.dropdown-menu-back', this.$el.closest('.dropdown')); + this.$cancelButton = $('.js-cancel-label-btn', this.$el); + this.$newLabelField = $('#new_label_name', this.$el); + this.$newColorField = $('#new_label_color', this.$el); + this.$colorPreview = $('.js-dropdown-label-color-preview', this.$el); + this.$newLabelError = $('.js-label-error', this.$el); + this.$newLabelCreateButton = $('.js-new-label-btn', this.$el); + this.$colorSuggestions = $('.suggest-colors-dropdown a', this.$el); + + this.$newLabelError.hide(); + this.$newLabelCreateButton.disable(); + + this.cleanBinding(); + this.addBinding(); + } - this.cleanBinding(); - this.addBinding(); - } + cleanBinding () { + this.$colorSuggestions.off('click'); + this.$newLabelField.off('keyup change'); + this.$newColorField.off('keyup change'); + this.$dropdownBack.off('click'); + this.$cancelButton.off('click'); + this.$newLabelCreateButton.off('click'); + } - cleanBinding () { - this.$colorSuggestions.off('click'); - this.$newLabelField.off('keyup change'); - this.$newColorField.off('keyup change'); - this.$dropdownBack.off('click'); - this.$cancelButton.off('click'); - this.$newLabelCreateButton.off('click'); - } + addBinding () { + const self = this; - addBinding () { - const self = this; + this.$colorSuggestions.on('click', function (e) { + const $this = $(this); + self.addColorValue(e, $this); + }); - this.$colorSuggestions.on('click', function (e) { - const $this = $(this); - self.addColorValue(e, $this); - }); + this.$newLabelField.on('keyup change', this.enableLabelCreateButton.bind(this)); + this.$newColorField.on('keyup change', this.enableLabelCreateButton.bind(this)); - this.$newLabelField.on('keyup change', this.enableLabelCreateButton.bind(this)); - this.$newColorField.on('keyup change', this.enableLabelCreateButton.bind(this)); + this.$dropdownBack.on('click', this.resetForm.bind(this)); - this.$dropdownBack.on('click', this.resetForm.bind(this)); + this.$cancelButton.on('click', function(e) { + e.preventDefault(); + e.stopPropagation(); - this.$cancelButton.on('click', function(e) { - e.preventDefault(); - e.stopPropagation(); + self.resetForm(); + self.$dropdownBack.trigger('click'); + }); - self.resetForm(); - self.$dropdownBack.trigger('click'); - }); + this.$newLabelCreateButton.on('click', this.saveLabel.bind(this)); + } - this.$newLabelCreateButton.on('click', this.saveLabel.bind(this)); - } + addColorValue (e, $this) { + e.preventDefault(); + e.stopPropagation(); - addColorValue (e, $this) { - e.preventDefault(); - e.stopPropagation(); + this.$newColorField.val($this.data('color')).trigger('change'); + this.$colorPreview + .css('background-color', $this.data('color')) + .parent() + .addClass('is-active'); + } - this.$newColorField.val($this.data('color')).trigger('change'); - this.$colorPreview - .css('background-color', $this.data('color')) - .parent() - .addClass('is-active'); + enableLabelCreateButton () { + if (this.$newLabelField.val() !== '' && this.$newColorField.val() !== '') { + this.$newLabelError.hide(); + this.$newLabelCreateButton.enable(); + } else { + this.$newLabelCreateButton.disable(); } + } - enableLabelCreateButton () { - if (this.$newLabelField.val() !== '' && this.$newColorField.val() !== '') { - this.$newLabelError.hide(); - this.$newLabelCreateButton.enable(); - } else { - this.$newLabelCreateButton.disable(); - } - } + resetForm () { + this.$newLabelField + .val('') + .trigger('change'); - resetForm () { - this.$newLabelField - .val('') - .trigger('change'); + this.$newColorField + .val('') + .trigger('change'); - this.$newColorField - .val('') - .trigger('change'); + this.$colorPreview + .css('background-color', '') + .parent() + .removeClass('is-active'); + } - this.$colorPreview - .css('background-color', '') - .parent() - .removeClass('is-active'); - } + saveLabel (e) { + e.preventDefault(); + e.stopPropagation(); - saveLabel (e) { - e.preventDefault(); - e.stopPropagation(); + Api.newLabel(this.namespacePath, this.projectPath, { + title: this.$newLabelField.val(), + color: this.$newColorField.val() + }, (label) => { + this.$newLabelCreateButton.enable(); - Api.newLabel(this.namespacePath, this.projectPath, { - title: this.$newLabelField.val(), - color: this.$newColorField.val() - }, (label) => { - this.$newLabelCreateButton.enable(); - - if (label.message) { - let errors; - - if (typeof label.message === 'string') { - errors = label.message; - } else { - errors = Object.keys(label.message).map(key => - `${gl.text.humanize(key)} ${label.message[key].join(', ')}` - ).join("<br/>"); - } - - this.$newLabelError - .html(errors) - .show(); - } else { - this.$dropdownBack.trigger('click'); + if (label.message) { + let errors; - $(document).trigger('created.label', label); + if (typeof label.message === 'string') { + errors = label.message; + } else { + errors = Object.keys(label.message).map(key => + `${gl.text.humanize(key)} ${label.message[key].join(', ')}` + ).join("<br/>"); } - }); - } - } - if (!w.gl) { - w.gl = {}; + this.$newLabelError + .html(errors) + .show(); + } else { + this.$dropdownBack.trigger('click'); + + $(document).trigger('created.label', label); + } + }); } +} - gl.CreateLabelDropdown = CreateLabelDropdown; -})(window); +window.gl = window.gl || {}; +gl.CreateLabelDropdown = CreateLabelDropdown; diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index 6829e8aeaea..cfa60325fcc 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -2,129 +2,127 @@ require('./lib/utils/url_utility'); -(() => { - const UNFOLD_COUNT = 20; - let isBound = false; +const UNFOLD_COUNT = 20; +let isBound = false; - class Diff { - constructor() { - const $diffFile = $('.files .diff-file'); - $diffFile.singleFileDiff(); - $diffFile.filesCommentButton(); +class Diff { + constructor() { + const $diffFile = $('.files .diff-file'); + $diffFile.singleFileDiff(); + $diffFile.filesCommentButton(); - $diffFile.each((index, file) => new gl.ImageFile(file)); + $diffFile.each((index, file) => new gl.ImageFile(file)); - if (this.diffViewType() === 'parallel') { - $('.content-wrapper .container-fluid').removeClass('container-limited'); - } - - if (!isBound) { - $(document) - .on('click', '.js-unfold', this.handleClickUnfold.bind(this)) - .on('click', '.diff-line-num a', this.handleClickLineNum.bind(this)); - isBound = true; - } + if (this.diffViewType() === 'parallel') { + $('.content-wrapper .container-fluid').removeClass('container-limited'); + } - if (gl.utils.getLocationHash()) { - this.highlightSelectedLine(); - } + if (!isBound) { + $(document) + .on('click', '.js-unfold', this.handleClickUnfold.bind(this)) + .on('click', '.diff-line-num a', this.handleClickLineNum.bind(this)); + isBound = true; + } - this.openAnchoredDiff(); + if (gl.utils.getLocationHash()) { + this.highlightSelectedLine(); } - handleClickUnfold(e) { - const $target = $(e.target); - // current babel config relies on iterators implementation, so we cannot simply do: - // const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent()); - const ref = this.lineNumbers($target.parent()); - const oldLineNumber = ref[0]; - const newLineNumber = ref[1]; - const offset = newLineNumber - oldLineNumber; - const bottom = $target.hasClass('js-unfold-bottom'); - let since; - let to; - let unfold = true; - - if (bottom) { - const lineNumber = newLineNumber + 1; - since = lineNumber; - to = lineNumber + UNFOLD_COUNT; - } else { - const lineNumber = newLineNumber - 1; - since = lineNumber - UNFOLD_COUNT; - to = lineNumber; - - // make sure we aren't loading more than we need - const prevNewLine = this.lineNumbers($target.parent().prev())[1]; - if (since <= prevNewLine + 1) { - since = prevNewLine + 1; - unfold = false; - } + this.openAnchoredDiff(); + } + + handleClickUnfold(e) { + const $target = $(e.target); + // current babel config relies on iterators implementation, so we cannot simply do: + // const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent()); + const ref = this.lineNumbers($target.parent()); + const oldLineNumber = ref[0]; + const newLineNumber = ref[1]; + const offset = newLineNumber - oldLineNumber; + const bottom = $target.hasClass('js-unfold-bottom'); + let since; + let to; + let unfold = true; + + if (bottom) { + const lineNumber = newLineNumber + 1; + since = lineNumber; + to = lineNumber + UNFOLD_COUNT; + } else { + const lineNumber = newLineNumber - 1; + since = lineNumber - UNFOLD_COUNT; + to = lineNumber; + + // make sure we aren't loading more than we need + const prevNewLine = this.lineNumbers($target.parent().prev())[1]; + if (since <= prevNewLine + 1) { + since = prevNewLine + 1; + unfold = false; } + } - const file = $target.parents('.diff-file'); - const link = file.data('blob-diff-path'); - const view = file.data('view'); + const file = $target.parents('.diff-file'); + const link = file.data('blob-diff-path'); + const view = file.data('view'); - const params = { since, to, bottom, offset, unfold, view }; - $.get(link, params, response => $target.parent().replaceWith(response)); - } + const params = { since, to, bottom, offset, unfold, view }; + $.get(link, params, response => $target.parent().replaceWith(response)); + } - openAnchoredDiff(cb) { - const locationHash = gl.utils.getLocationHash(); - const anchoredDiff = locationHash && locationHash.split('_')[0]; - - if (!anchoredDiff) return; - - const diffTitle = $(`#${anchoredDiff}`); - const diffFile = diffTitle.closest('.diff-file'); - const nothingHereBlock = $('.nothing-here-block:visible', diffFile); - if (nothingHereBlock.length) { - const clickTarget = $('.js-file-title, .click-to-expand', diffFile); - diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => { - this.highlightSelectedLine(); - if (cb) cb(); - }); - } else if (cb) { - cb(); - } - } + openAnchoredDiff(cb) { + const locationHash = gl.utils.getLocationHash(); + const anchoredDiff = locationHash && locationHash.split('_')[0]; - handleClickLineNum(e) { - const hash = $(e.currentTarget).attr('href'); - e.preventDefault(); - if (window.history.pushState) { - window.history.pushState(null, null, hash); - } else { - window.location.hash = hash; - } - this.highlightSelectedLine(); + if (!anchoredDiff) return; + + const diffTitle = $(`#${anchoredDiff}`); + const diffFile = diffTitle.closest('.diff-file'); + const nothingHereBlock = $('.nothing-here-block:visible', diffFile); + if (nothingHereBlock.length) { + const clickTarget = $('.js-file-title, .click-to-expand', diffFile); + diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => { + this.highlightSelectedLine(); + if (cb) cb(); + }); + } else if (cb) { + cb(); } + } - diffViewType() { - return $('.inline-parallel-buttons a.active').data('view-type'); + handleClickLineNum(e) { + const hash = $(e.currentTarget).attr('href'); + e.preventDefault(); + if (window.history.pushState) { + window.history.pushState(null, null, hash); + } else { + window.location.hash = hash; } + this.highlightSelectedLine(); + } - lineNumbers(line) { - if (!line.children().length) { - return [0, 0]; - } - return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10)); + diffViewType() { + return $('.inline-parallel-buttons a.active').data('view-type'); + } + + lineNumbers(line) { + if (!line.children().length) { + return [0, 0]; } + return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10)); + } - highlightSelectedLine() { - const hash = gl.utils.getLocationHash(); - const $diffFiles = $('.diff-file'); - $diffFiles.find('.hll').removeClass('hll'); + highlightSelectedLine() { + const hash = gl.utils.getLocationHash(); + const $diffFiles = $('.diff-file'); + $diffFiles.find('.hll').removeClass('hll'); - if (hash) { - $diffFiles - .find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`) - .addClass('hll'); - } + if (hash) { + $diffFiles + .find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`) + .addClass('hll'); } } +} - window.gl = window.gl || {}; - window.gl.Diff = Diff; -})(); +window.gl = window.gl || {}; +window.gl.Diff = Diff; diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js index 646f836aff0..f2963a5eb19 100644 --- a/app/assets/javascripts/dropzone_input.js +++ b/app/assets/javascripts/dropzone_input.js @@ -3,218 +3,216 @@ require('./preview_markdown'); -(function() { - this.DropzoneInput = (function() { - function DropzoneInput(form) { - var $mdArea, alertAttr, alertClass, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divAlert, divHover, divSpinner, dropzone, form_dropzone, form_textarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, max_file_size, pasteText, project_uploads_path, showError, showSpinner, uploadFile, uploadProgress; - Dropzone.autoDiscover = false; - alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"; - alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""; - divHover = "<div class=\"div-dropzone-hover\"></div>"; - divSpinner = "<div class=\"div-dropzone-spinner\"></div>"; - divAlert = "<div class=\"" + alertClass + "\"></div>"; - iconPaperclip = "<i class=\"fa fa-paperclip div-dropzone-icon\"></i>"; - iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"; - uploadProgress = $("<div class=\"div-dropzone-progress\"></div>"); - btnAlert = "<button type=\"button\"" + alertAttr + ">×</button>"; - project_uploads_path = window.project_uploads_path || null; - max_file_size = gon.max_file_size || 10; - form_textarea = $(form).find(".js-gfm-input"); - form_textarea.wrap("<div class=\"div-dropzone\"></div>"); - form_textarea.on('paste', (function(_this) { - return function(event) { - return handlePaste(event); - }; - })(this)); - $mdArea = $(form_textarea).closest('.md-area'); - $(form).setupMarkdownPreview(); - form_dropzone = $(form).find('.div-dropzone'); - form_dropzone.parent().addClass("div-dropzone-wrapper"); - form_dropzone.append(divHover); - form_dropzone.find(".div-dropzone-hover").append(iconPaperclip); - form_dropzone.append(divSpinner); - form_dropzone.find(".div-dropzone-spinner").append(iconSpinner); - form_dropzone.find(".div-dropzone-spinner").append(uploadProgress); - form_dropzone.find(".div-dropzone-spinner").css({ - "opacity": 0, - "display": "none" - }); - dropzone = form_dropzone.dropzone({ - url: project_uploads_path, - dictDefaultMessage: "", - clickable: true, - paramName: "file", - maxFilesize: max_file_size, - 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); - form_textarea.focus(); - }, - success: function(header, response) { - pasteText(response.link.markdown); - }, - error: function(temp) { - var checkIfMsgExists, errorAlert; - errorAlert = $(form).find('.error-alert'); - checkIfMsgExists = errorAlert.children().length; - if (checkIfMsgExists === 0) { - errorAlert.append(divAlert); - $(".div-dropzone-alert").append(btnAlert + "Attaching the file failed."); - } - }, - totaluploadprogress: function(totalUploadProgress) { - uploadProgress.text(Math.round(totalUploadProgress) + "%"); - }, - sending: function() { - form_dropzone.find(".div-dropzone-spinner").css({ - "opacity": 0.7, - "display": "inherit" - }); - }, - queuecomplete: function() { - uploadProgress.text(""); - $(".dz-preview").remove(); - $(".markdown-area").trigger("input"); - $(".div-dropzone-spinner").css({ - "opacity": 0, - "display": "none" - }); - } - }); - child = $(dropzone[0]).children("textarea"); - 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); - } - } +window.DropzoneInput = (function() { + function DropzoneInput(form) { + var $mdArea, alertAttr, alertClass, appendToTextArea, btnAlert, child, closeAlertMessage, closeSpinner, divAlert, divHover, divSpinner, dropzone, form_dropzone, form_textarea, getFilename, handlePaste, iconPaperclip, iconSpinner, insertToTextArea, isImage, max_file_size, pasteText, project_uploads_path, showError, showSpinner, uploadFile, uploadProgress; + Dropzone.autoDiscover = false; + alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"; + alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""; + divHover = "<div class=\"div-dropzone-hover\"></div>"; + divSpinner = "<div class=\"div-dropzone-spinner\"></div>"; + divAlert = "<div class=\"" + alertClass + "\"></div>"; + iconPaperclip = "<i class=\"fa fa-paperclip div-dropzone-icon\"></i>"; + iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"; + uploadProgress = $("<div class=\"div-dropzone-progress\"></div>"); + btnAlert = "<button type=\"button\"" + alertAttr + ">×</button>"; + project_uploads_path = window.project_uploads_path || null; + max_file_size = gon.max_file_size || 10; + form_textarea = $(form).find(".js-gfm-input"); + form_textarea.wrap("<div class=\"div-dropzone\"></div>"); + form_textarea.on('paste', (function(_this) { + return function(event) { + return handlePaste(event); }; - 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) { - var afterSelection, beforeSelection, caretEnd, caretStart, textEnd; - var formattedText = text + "\n\n"; - caretStart = $(child)[0].selectionStart; - caretEnd = $(child)[0].selectionEnd; - textEnd = $(child).val().length; - beforeSelection = $(child).val().substring(0, caretStart); - afterSelection = $(child).val().substring(caretEnd, textEnd); - $(child).val(beforeSelection + formattedText + afterSelection); - child.get(0).setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length); - return form_textarea.trigger("input"); - }; - 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"); + })(this)); + $mdArea = $(form_textarea).closest('.md-area'); + $(form).setupMarkdownPreview(); + form_dropzone = $(form).find('.div-dropzone'); + form_dropzone.parent().addClass("div-dropzone-wrapper"); + form_dropzone.append(divHover); + form_dropzone.find(".div-dropzone-hover").append(iconPaperclip); + form_dropzone.append(divSpinner); + form_dropzone.find(".div-dropzone-spinner").append(iconSpinner); + form_dropzone.find(".div-dropzone-spinner").append(uploadProgress); + form_dropzone.find(".div-dropzone-spinner").css({ + "opacity": 0, + "display": "none" + }); + dropzone = form_dropzone.dropzone({ + url: project_uploads_path, + dictDefaultMessage: "", + clickable: true, + paramName: "file", + maxFilesize: max_file_size, + 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); + form_textarea.focus(); + }, + success: function(header, response) { + pasteText(response.link.markdown); + }, + error: function(temp) { + var checkIfMsgExists, errorAlert; + errorAlert = $(form).find('.error-alert'); + checkIfMsgExists = errorAlert.children().length; + if (checkIfMsgExists === 0) { + errorAlert.append(divAlert); + $(".div-dropzone-alert").append(btnAlert + "Attaching the file failed."); } - value = value.split("\r"); - return value.first(); - }; - uploadFile = function(item, filename) { - var formData; - formData = new FormData(); - formData.append("file", item, filename); - return $.ajax({ - url: project_uploads_path, - 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(); - } - }); - }; - insertToTextArea = function(filename, url) { - return $(child).val(function(index, val) { - return val.replace("{{" + filename + "}}", url + "\n"); - }); - }; - appendToTextArea = function(url) { - return $(child).val(function(index, val) { - return val + url + "\n"; - }); - }; - showSpinner = function(e) { - return form.find(".div-dropzone-spinner").css({ + }, + totaluploadprogress: function(totalUploadProgress) { + uploadProgress.text(Math.round(totalUploadProgress) + "%"); + }, + sending: function() { + form_dropzone.find(".div-dropzone-spinner").css({ "opacity": 0.7, "display": "inherit" }); - }; - closeSpinner = function() { - return form.find(".div-dropzone-spinner").css({ + }, + queuecomplete: function() { + uploadProgress.text(""); + $(".dz-preview").remove(); + $(".markdown-area").trigger("input"); + $(".div-dropzone-spinner").css({ "opacity": 0, "display": "none" }); - }; - showError = function(message) { - var checkIfMsgExists, errorAlert; - errorAlert = $(form).find('.error-alert'); - checkIfMsgExists = errorAlert.children().length; - if (checkIfMsgExists === 0) { - errorAlert.append(divAlert); - return $(".div-dropzone-alert").append(btnAlert + message); + } + }); + child = $(dropzone[0]).children("textarea"); + 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); } - }; - 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(); + } + }; + 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) { + var afterSelection, beforeSelection, caretEnd, caretStart, textEnd; + var formattedText = text + "\n\n"; + caretStart = $(child)[0].selectionStart; + caretEnd = $(child)[0].selectionEnd; + textEnd = $(child).val().length; + beforeSelection = $(child).val().substring(0, caretStart); + afterSelection = $(child).val().substring(caretEnd, textEnd); + $(child).val(beforeSelection + formattedText + afterSelection); + child.get(0).setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length); + return form_textarea.trigger("input"); + }; + 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: project_uploads_path, + 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(); + } + }); + }; + insertToTextArea = function(filename, url) { + return $(child).val(function(index, val) { + return val.replace("{{" + filename + "}}", url + "\n"); + }); + }; + appendToTextArea = function(url) { + return $(child).val(function(index, val) { + return val + url + "\n"; + }); + }; + showSpinner = function(e) { + return form.find(".div-dropzone-spinner").css({ + "opacity": 0.7, + "display": "inherit" + }); + }; + closeSpinner = function() { + return form.find(".div-dropzone-spinner").css({ + "opacity": 0, + "display": "none" }); - } + }; + showError = function(message) { + var checkIfMsgExists, errorAlert; + errorAlert = $(form).find('.error-alert'); + checkIfMsgExists = errorAlert.children().length; + if (checkIfMsgExists === 0) { + errorAlert.append(divAlert); + return $(".div-dropzone-alert").append(btnAlert + message); + } + }; + 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(); + }); + } - return DropzoneInput; - })(); -}).call(window); + return DropzoneInput; +})(); diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js index 9169fcd7328..fdbb4644971 100644 --- a/app/assets/javascripts/due_date_select.js +++ b/app/assets/javascripts/due_date_select.js @@ -2,203 +2,202 @@ /* global dateFormat */ /* global Pikaday */ -(function(global) { - class DueDateSelect { - constructor({ $dropdown, $loading } = {}) { - const $dropdownParent = $dropdown.closest('.dropdown'); - const $block = $dropdown.closest('.block'); - this.$loading = $loading; - this.$dropdown = $dropdown; - this.$dropdownParent = $dropdownParent; - this.$datePicker = $dropdownParent.find('.js-due-date-calendar'); - this.$block = $block; - this.$selectbox = $dropdown.closest('.selectbox'); - this.$value = $block.find('.value'); - this.$valueContent = $block.find('.value-content'); - this.$sidebarValue = $('.js-due-date-sidebar-value', $block); - this.fieldName = $dropdown.data('field-name'), - this.abilityName = $dropdown.data('ability-name'), - this.issueUpdateURL = $dropdown.data('issue-update'); - - this.rawSelectedDate = null; - this.displayedDate = null; - this.datePayload = null; - - this.initGlDropdown(); - this.initRemoveDueDate(); - this.initDatePicker(); - } - - initGlDropdown() { - this.$dropdown.glDropdown({ - opened: () => { - const calendar = this.$datePicker.data('pikaday'); - calendar.show(); - }, - hidden: () => { - this.$selectbox.hide(); - this.$value.css('display', ''); - } - }); - } - - initDatePicker() { - const $dueDateInput = $(`input[name='${this.fieldName}']`); - - const calendar = new Pikaday({ - field: $dueDateInput.get(0), - theme: 'gitlab-theme', - format: 'yyyy-mm-dd', - onSelect: (dateText) => { - const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd'); - - $dueDateInput.val(formattedDate); +class DueDateSelect { + constructor({ $dropdown, $loading } = {}) { + const $dropdownParent = $dropdown.closest('.dropdown'); + const $block = $dropdown.closest('.block'); + this.$loading = $loading; + this.$dropdown = $dropdown; + this.$dropdownParent = $dropdownParent; + this.$datePicker = $dropdownParent.find('.js-due-date-calendar'); + this.$block = $block; + this.$selectbox = $dropdown.closest('.selectbox'); + this.$value = $block.find('.value'); + this.$valueContent = $block.find('.value-content'); + this.$sidebarValue = $('.js-due-date-sidebar-value', $block); + this.fieldName = $dropdown.data('field-name'), + this.abilityName = $dropdown.data('ability-name'), + this.issueUpdateURL = $dropdown.data('issue-update'); + + this.rawSelectedDate = null; + this.displayedDate = null; + this.datePayload = null; + + this.initGlDropdown(); + this.initRemoveDueDate(); + this.initDatePicker(); + } - if (this.$dropdown.hasClass('js-issue-boards-due-date')) { - gl.issueBoards.BoardsStore.detail.issue.dueDate = $dueDateInput.val(); - this.updateIssueBoardIssue(); - } else { - this.saveDueDate(true); - } - } - }); + initGlDropdown() { + this.$dropdown.glDropdown({ + opened: () => { + const calendar = this.$datePicker.data('pikaday'); + calendar.show(); + }, + hidden: () => { + this.$selectbox.hide(); + this.$value.css('display', ''); + } + }); + } - calendar.setDate(new Date($dueDateInput.val())); - this.$datePicker.append(calendar.el); - this.$datePicker.data('pikaday', calendar); - } + initDatePicker() { + const $dueDateInput = $(`input[name='${this.fieldName}']`); - initRemoveDueDate() { - this.$block.on('click', '.js-remove-due-date', (e) => { - const calendar = this.$datePicker.data('pikaday'); - e.preventDefault(); + const calendar = new Pikaday({ + field: $dueDateInput.get(0), + theme: 'gitlab-theme', + format: 'yyyy-mm-dd', + onSelect: (dateText) => { + const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd'); - calendar.setDate(null); + $dueDateInput.val(formattedDate); if (this.$dropdown.hasClass('js-issue-boards-due-date')) { - gl.issueBoards.BoardsStore.detail.issue.dueDate = ''; + gl.issueBoards.BoardsStore.detail.issue.dueDate = $dueDateInput.val(); this.updateIssueBoardIssue(); } else { - $("input[name='" + this.fieldName + "']").val(''); - return this.saveDueDate(false); + this.saveDueDate(true); } - }); - } + } + }); - saveDueDate(isDropdown) { - this.parseSelectedDate(); - this.prepSelectedDate(); - this.submitSelectedDate(isDropdown); - } + calendar.setDate(new Date($dueDateInput.val())); + this.$datePicker.append(calendar.el); + this.$datePicker.data('pikaday', calendar); + } + + initRemoveDueDate() { + this.$block.on('click', '.js-remove-due-date', (e) => { + const calendar = this.$datePicker.data('pikaday'); + e.preventDefault(); - parseSelectedDate() { - this.rawSelectedDate = $(`input[name='${this.fieldName}']`).val(); + calendar.setDate(null); - if (this.rawSelectedDate.length) { - // Construct Date object manually to avoid buggy dateString support within Date constructor - const dateArray = this.rawSelectedDate.split('-').map(v => parseInt(v, 10)); - const dateObj = new Date(dateArray[0], dateArray[1] - 1, dateArray[2]); - this.displayedDate = dateFormat(dateObj, 'mmm d, yyyy'); + if (this.$dropdown.hasClass('js-issue-boards-due-date')) { + gl.issueBoards.BoardsStore.detail.issue.dueDate = ''; + this.updateIssueBoardIssue(); } else { - this.displayedDate = 'No due date'; + $("input[name='" + this.fieldName + "']").val(''); + return this.saveDueDate(false); } - } + }); + } - prepSelectedDate() { - const datePayload = {}; - datePayload[this.abilityName] = {}; - datePayload[this.abilityName].due_date = this.rawSelectedDate; - this.datePayload = datePayload; - } + saveDueDate(isDropdown) { + this.parseSelectedDate(); + this.prepSelectedDate(); + this.submitSelectedDate(isDropdown); + } - updateIssueBoardIssue () { - this.$loading.fadeIn(); - this.$dropdown.trigger('loading.gl.dropdown'); - this.$selectbox.hide(); - this.$value.css('display', ''); + parseSelectedDate() { + this.rawSelectedDate = $(`input[name='${this.fieldName}']`).val(); - gl.issueBoards.BoardsStore.detail.issue.update(this.$dropdown.attr('data-issue-update')) - .then(() => { - this.$loading.fadeOut(); - }); + if (this.rawSelectedDate.length) { + // Construct Date object manually to avoid buggy dateString support within Date constructor + const dateArray = this.rawSelectedDate.split('-').map(v => parseInt(v, 10)); + const dateObj = new Date(dateArray[0], dateArray[1] - 1, dateArray[2]); + this.displayedDate = dateFormat(dateObj, 'mmm d, yyyy'); + } else { + this.displayedDate = 'No due date'; } + } + + prepSelectedDate() { + const datePayload = {}; + datePayload[this.abilityName] = {}; + datePayload[this.abilityName].due_date = this.rawSelectedDate; + this.datePayload = datePayload; + } + + updateIssueBoardIssue () { + this.$loading.fadeIn(); + this.$dropdown.trigger('loading.gl.dropdown'); + this.$selectbox.hide(); + this.$value.css('display', ''); + + gl.issueBoards.BoardsStore.detail.issue.update(this.$dropdown.attr('data-issue-update')) + .then(() => { + this.$loading.fadeOut(); + }); + } + + submitSelectedDate(isDropdown) { + return $.ajax({ + type: 'PUT', + url: this.issueUpdateURL, + data: this.datePayload, + dataType: 'json', + beforeSend: () => { + const selectedDateValue = this.datePayload[this.abilityName].due_date; + const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value'; + + this.$loading.fadeIn(); - submitSelectedDate(isDropdown) { - return $.ajax({ - type: 'PUT', - url: this.issueUpdateURL, - data: this.datePayload, - dataType: 'json', - beforeSend: () => { - const selectedDateValue = this.datePayload[this.abilityName].due_date; - const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value'; - - this.$loading.fadeIn(); - - if (isDropdown) { - this.$dropdown.trigger('loading.gl.dropdown'); - this.$selectbox.hide(); - } - - this.$value.css('display', ''); - this.$valueContent.html(`<span class='${displayedDateStyle}'>${this.displayedDate}</span>`); - this.$sidebarValue.html(this.displayedDate); - - return selectedDateValue.length ? - $('.js-remove-due-date-holder').removeClass('hidden') : - $('.js-remove-due-date-holder').addClass('hidden'); - } - }).done((data) => { if (isDropdown) { - this.$dropdown.trigger('loaded.gl.dropdown'); - this.$dropdown.dropdown('toggle'); + this.$dropdown.trigger('loading.gl.dropdown'); + this.$selectbox.hide(); } - return this.$loading.fadeOut(); - }); - } + + this.$value.css('display', ''); + this.$valueContent.html(`<span class='${displayedDateStyle}'>${this.displayedDate}</span>`); + this.$sidebarValue.html(this.displayedDate); + + return selectedDateValue.length ? + $('.js-remove-due-date-holder').removeClass('hidden') : + $('.js-remove-due-date-holder').addClass('hidden'); + } + }).done((data) => { + if (isDropdown) { + this.$dropdown.trigger('loaded.gl.dropdown'); + this.$dropdown.dropdown('toggle'); + } + return this.$loading.fadeOut(); + }); } +} - class DueDateSelectors { - constructor() { - this.initMilestoneDatePicker(); - this.initIssuableSelect(); - } +class DueDateSelectors { + constructor() { + this.initMilestoneDatePicker(); + this.initIssuableSelect(); + } - initMilestoneDatePicker() { - $('.datepicker').each(function() { - const $datePicker = $(this); - const calendar = new Pikaday({ - field: $datePicker.get(0), - theme: 'gitlab-theme', - format: 'yyyy-mm-dd', - onSelect(dateText) { - $datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); - } - }); - calendar.setDate(new Date($datePicker.val())); - - $datePicker.data('pikaday', calendar); + initMilestoneDatePicker() { + $('.datepicker').each(function() { + const $datePicker = $(this); + const calendar = new Pikaday({ + field: $datePicker.get(0), + theme: 'gitlab-theme', + format: 'yyyy-mm-dd', + onSelect(dateText) { + $datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); + } }); + calendar.setDate(new Date($datePicker.val())); - $('.js-clear-due-date,.js-clear-start-date').on('click', (e) => { - e.preventDefault(); - const calendar = $(e.target).siblings('.datepicker').data('pikaday'); - calendar.setDate(null); - }); - } + $datePicker.data('pikaday', calendar); + }); - initIssuableSelect() { - const $loading = $('.js-issuable-update .due_date').find('.block-loading').hide(); + $('.js-clear-due-date,.js-clear-start-date').on('click', (e) => { + e.preventDefault(); + const calendar = $(e.target).siblings('.datepicker').data('pikaday'); + calendar.setDate(null); + }); + } + + initIssuableSelect() { + const $loading = $('.js-issuable-update .due_date').find('.block-loading').hide(); - $('.js-due-date-select').each((i, dropdown) => { - const $dropdown = $(dropdown); - new DueDateSelect({ - $dropdown, - $loading - }); + $('.js-due-date-select').each((i, dropdown) => { + const $dropdown = $(dropdown); + new DueDateSelect({ + $dropdown, + $loading }); - } + }); } +} - global.DueDateSelectors = DueDateSelectors; -})(window.gl || (window.gl = {})); +window.gl = window.gl || {}; +window.gl.DueDateSelectors = DueDateSelectors; diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js index bf84f2a0a8f..3f041172ff3 100644 --- a/app/assets/javascripts/files_comment_button.js +++ b/app/assets/javascripts/files_comment_button.js @@ -2,142 +2,140 @@ /* global FilesCommentButton */ /* global notes */ -(function() { - let $commentButtonTemplate; - var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }; +let $commentButtonTemplate; +var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }; - this.FilesCommentButton = (function() { - var COMMENT_BUTTON_CLASS, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS; +window.FilesCommentButton = (function() { + var COMMENT_BUTTON_CLASS, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS; - COMMENT_BUTTON_CLASS = '.add-diff-note'; + COMMENT_BUTTON_CLASS = '.add-diff-note'; - LINE_HOLDER_CLASS = '.line_holder'; + LINE_HOLDER_CLASS = '.line_holder'; - LINE_NUMBER_CLASS = 'diff-line-num'; + LINE_NUMBER_CLASS = 'diff-line-num'; - LINE_CONTENT_CLASS = 'line_content'; + LINE_CONTENT_CLASS = 'line_content'; - UNFOLDABLE_LINE_CLASS = 'js-unfold'; + UNFOLDABLE_LINE_CLASS = 'js-unfold'; - EMPTY_CELL_CLASS = 'empty-cell'; + EMPTY_CELL_CLASS = 'empty-cell'; - OLD_LINE_CLASS = 'old_line'; + OLD_LINE_CLASS = 'old_line'; - LINE_COLUMN_CLASSES = "." + LINE_NUMBER_CLASS + ", .line_content"; + LINE_COLUMN_CLASSES = "." + LINE_NUMBER_CLASS + ", .line_content"; - TEXT_FILE_SELECTOR = '.text-file'; + TEXT_FILE_SELECTOR = '.text-file'; - function FilesCommentButton(filesContainerElement) { - this.render = bind(this.render, this); - this.hideButton = bind(this.hideButton, this); - this.isParallelView = notes.isParallelView(); - filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render) - .on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton); + function FilesCommentButton(filesContainerElement) { + this.render = bind(this.render, this); + this.hideButton = bind(this.hideButton, this); + this.isParallelView = notes.isParallelView(); + filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render) + .on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton); + } + + FilesCommentButton.prototype.render = function(e) { + var $currentTarget, buttonParentElement, lineContentElement, textFileElement, $button; + $currentTarget = $(e.currentTarget); + + if ($currentTarget.hasClass('js-no-comment-btn')) return; + + lineContentElement = this.getLineContent($currentTarget); + buttonParentElement = this.getButtonParent($currentTarget); + + if (!this.validateButtonParent(buttonParentElement) || !this.validateLineContent(lineContentElement)) return; + + $button = $(COMMENT_BUTTON_CLASS, buttonParentElement); + buttonParentElement.addClass('is-over') + .nextUntil(`.${LINE_CONTENT_CLASS}`).addClass('is-over'); + + if ($button.length) { + return; } - FilesCommentButton.prototype.render = function(e) { - var $currentTarget, buttonParentElement, lineContentElement, textFileElement, $button; - $currentTarget = $(e.currentTarget); + textFileElement = this.getTextFileElement($currentTarget); + buttonParentElement.append(this.buildButton({ + noteableType: textFileElement.attr('data-noteable-type'), + noteableID: textFileElement.attr('data-noteable-id'), + commitID: textFileElement.attr('data-commit-id'), + noteType: lineContentElement.attr('data-note-type'), + position: lineContentElement.attr('data-position'), + lineType: lineContentElement.attr('data-line-type'), + discussionID: lineContentElement.attr('data-discussion-id'), + lineCode: lineContentElement.attr('data-line-code') + })); + }; - if ($currentTarget.hasClass('js-no-comment-btn')) return; + FilesCommentButton.prototype.hideButton = function(e) { + var $currentTarget = $(e.currentTarget); + var buttonParentElement = this.getButtonParent($currentTarget); - lineContentElement = this.getLineContent($currentTarget); - buttonParentElement = this.getButtonParent($currentTarget); + buttonParentElement.removeClass('is-over') + .nextUntil(`.${LINE_CONTENT_CLASS}`).removeClass('is-over'); + }; - if (!this.validateButtonParent(buttonParentElement) || !this.validateLineContent(lineContentElement)) return; + FilesCommentButton.prototype.buildButton = function(buttonAttributes) { + return $commentButtonTemplate.clone().attr({ + 'data-noteable-type': buttonAttributes.noteableType, + 'data-noteable-id': buttonAttributes.noteableID, + 'data-commit-id': buttonAttributes.commitID, + 'data-note-type': buttonAttributes.noteType, + 'data-line-code': buttonAttributes.lineCode, + 'data-position': buttonAttributes.position, + 'data-discussion-id': buttonAttributes.discussionID, + 'data-line-type': buttonAttributes.lineType + }); + }; - $button = $(COMMENT_BUTTON_CLASS, buttonParentElement); - buttonParentElement.addClass('is-over') - .nextUntil(`.${LINE_CONTENT_CLASS}`).addClass('is-over'); + FilesCommentButton.prototype.getTextFileElement = function(hoveredElement) { + return hoveredElement.closest(TEXT_FILE_SELECTOR); + }; - if ($button.length) { - return; - } + FilesCommentButton.prototype.getLineContent = function(hoveredElement) { + if (hoveredElement.hasClass(LINE_CONTENT_CLASS)) { + return hoveredElement; + } + if (!this.isParallelView) { + return $(hoveredElement).closest(LINE_HOLDER_CLASS).find("." + LINE_CONTENT_CLASS); + } else { + return $(hoveredElement).next("." + LINE_CONTENT_CLASS); + } + }; - textFileElement = this.getTextFileElement($currentTarget); - buttonParentElement.append(this.buildButton({ - noteableType: textFileElement.attr('data-noteable-type'), - noteableID: textFileElement.attr('data-noteable-id'), - commitID: textFileElement.attr('data-commit-id'), - noteType: lineContentElement.attr('data-note-type'), - position: lineContentElement.attr('data-position'), - lineType: lineContentElement.attr('data-line-type'), - discussionID: lineContentElement.attr('data-discussion-id'), - lineCode: lineContentElement.attr('data-line-code') - })); - }; - - FilesCommentButton.prototype.hideButton = function(e) { - var $currentTarget = $(e.currentTarget); - var buttonParentElement = this.getButtonParent($currentTarget); - - buttonParentElement.removeClass('is-over') - .nextUntil(`.${LINE_CONTENT_CLASS}`).removeClass('is-over'); - }; - - FilesCommentButton.prototype.buildButton = function(buttonAttributes) { - return $commentButtonTemplate.clone().attr({ - 'data-noteable-type': buttonAttributes.noteableType, - 'data-noteable-id': buttonAttributes.noteableID, - 'data-commit-id': buttonAttributes.commitID, - 'data-note-type': buttonAttributes.noteType, - 'data-line-code': buttonAttributes.lineCode, - 'data-position': buttonAttributes.position, - 'data-discussion-id': buttonAttributes.discussionID, - 'data-line-type': buttonAttributes.lineType - }); - }; - - FilesCommentButton.prototype.getTextFileElement = function(hoveredElement) { - return hoveredElement.closest(TEXT_FILE_SELECTOR); - }; - - FilesCommentButton.prototype.getLineContent = function(hoveredElement) { - if (hoveredElement.hasClass(LINE_CONTENT_CLASS)) { + FilesCommentButton.prototype.getButtonParent = function(hoveredElement) { + if (!this.isParallelView) { + if (hoveredElement.hasClass(OLD_LINE_CLASS)) { return hoveredElement; } - if (!this.isParallelView) { - return $(hoveredElement).closest(LINE_HOLDER_CLASS).find("." + LINE_CONTENT_CLASS); - } else { - return $(hoveredElement).next("." + LINE_CONTENT_CLASS); - } - }; - - FilesCommentButton.prototype.getButtonParent = function(hoveredElement) { - if (!this.isParallelView) { - if (hoveredElement.hasClass(OLD_LINE_CLASS)) { - return hoveredElement; - } - return hoveredElement.parent().find("." + OLD_LINE_CLASS); - } else { - if (hoveredElement.hasClass(LINE_NUMBER_CLASS)) { - return hoveredElement; - } - return $(hoveredElement).prev("." + LINE_NUMBER_CLASS); + return hoveredElement.parent().find("." + OLD_LINE_CLASS); + } else { + if (hoveredElement.hasClass(LINE_NUMBER_CLASS)) { + return hoveredElement; } - }; + return $(hoveredElement).prev("." + LINE_NUMBER_CLASS); + } + }; - FilesCommentButton.prototype.validateButtonParent = function(buttonParentElement) { - return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS); - }; + FilesCommentButton.prototype.validateButtonParent = function(buttonParentElement) { + return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS); + }; - FilesCommentButton.prototype.validateLineContent = function(lineContentElement) { - return lineContentElement.attr('data-discussion-id') && lineContentElement.attr('data-discussion-id') !== ''; - }; + FilesCommentButton.prototype.validateLineContent = function(lineContentElement) { + return lineContentElement.attr('data-discussion-id') && lineContentElement.attr('data-discussion-id') !== ''; + }; - return FilesCommentButton; - })(); + return FilesCommentButton; +})(); - $.fn.filesCommentButton = function() { - $commentButtonTemplate = $('<button name="button" type="submit" class="add-diff-note js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>'); +$.fn.filesCommentButton = function() { + $commentButtonTemplate = $('<button name="button" type="submit" class="add-diff-note js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>'); - if (!(this && (this.parent().data('can-create-note') != null))) { - return; + if (!(this && (this.parent().data('can-create-note') != null))) { + return; + } + return this.each(function() { + if (!$.data(this, 'filesCommentButton')) { + return $.data(this, 'filesCommentButton', new FilesCommentButton($(this))); } - return this.each(function() { - if (!$.data(this, 'filesCommentButton')) { - return $.data(this, 'filesCommentButton', new FilesCommentButton($(this))); - } - }); - }; -}).call(window); + }); +}; diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js index 47a40e28461..aaaeb9bddb1 100644 --- a/app/assets/javascripts/filterable_list.js +++ b/app/assets/javascripts/filterable_list.js @@ -2,6 +2,7 @@ * Makes search request for content when user types a value in the search input. * Updates the html content of the page with the received one. */ + export default class FilterableList { constructor(form, filter, holder) { this.filterForm = form; diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js index 730104b89f9..eec30624ff2 100644 --- a/app/assets/javascripts/flash.js +++ b/app/assets/javascripts/flash.js @@ -1,42 +1,41 @@ /* 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 */ -(function() { - this.Flash = (function() { - var hideFlash; - hideFlash = function() { - return $(this).fadeOut(); - }; +window.Flash = (function() { + var hideFlash; - function Flash(message, type, parent) { - var flash, textDiv; - 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 (this.flashContainer.parent().hasClass('content-wrapper')) { - textDiv.addClass('container-fluid container-limited'); - } - flash.appendTo(this.flashContainer); - this.flashContainer.show(); + hideFlash = function() { + return $(this).fadeOut(); + }; + + function Flash(message, type, parent) { + var flash, textDiv; + 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 (this.flashContainer.parent().hasClass('content-wrapper')) { + textDiv.addClass('container-fluid container-limited'); } + flash.appendTo(this.flashContainer); + this.flashContainer.show(); + } - return Flash; - })(); -}).call(window); + return Flash; +})(); diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 4f7ce1fa197..9ac4c49d697 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -5,390 +5,386 @@ import emojiAliases from 'emojis/aliases.json'; import { glEmojiTag } from '~/behaviors/gl_emoji'; // Creates the variables for setting up GFM auto-completion -(function() { - if (window.gl == null) { - window.gl = {}; - } +window.gl = window.gl || {}; - function sanitize(str) { - return str.replace(/<(?:.|\n)*?>/gm, ''); - } +function sanitize(str) { + return str.replace(/<(?:.|\n)*?>/gm, ''); +} - window.gl.GfmAutoComplete = { - dataSources: {}, - defaultLoadingData: ['loading'], - cachedData: {}, - isLoadingData: {}, - atTypeMap: { - ':': 'emojis', - '@': 'members', - '#': 'issues', - '!': 'mergeRequests', - '~': 'labels', - '%': 'milestones', - '/': 'commands' - }, - // Emoji - Emoji: { - templateFunction: function(name) { - return `<li> - ${name} ${glEmojiTag(name)} - </li> - `; +window.gl.GfmAutoComplete = { + dataSources: {}, + defaultLoadingData: ['loading'], + cachedData: {}, + isLoadingData: {}, + atTypeMap: { + ':': 'emojis', + '@': 'members', + '#': 'issues', + '!': 'mergeRequests', + '~': 'labels', + '%': 'milestones', + '/': 'commands' + }, + // Emoji + Emoji: { + templateFunction: function(name) { + return `<li> + ${name} ${glEmojiTag(name)} + </li> + `; + } + }, + // Team Members + Members: { + template: '<li>${avatarTag} ${username} <small>${title}</small></li>' + }, + Labels: { + template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>' + }, + // Issues and MergeRequests + Issues: { + template: '<li><small>${id}</small> ${title}</li>' + }, + // Milestones + Milestones: { + template: '<li>${title}</li>' + }, + Loading: { + template: '<li style="pointer-events: none;"><i class="fa fa-refresh fa-spin"></i> Loading...</li>' + }, + DefaultOptions: { + sorter: function(query, items, searchKey) { + this.setting.highlightFirst = this.setting.alwaysHighlightFirst || query.length > 0; + if (gl.GfmAutoComplete.isLoading(items)) { + this.setting.highlightFirst = false; + return items; } + return $.fn.atwho["default"].callbacks.sorter(query, items, searchKey); }, - // Team Members - Members: { - template: '<li>${avatarTag} ${username} <small>${title}</small></li>' - }, - Labels: { - template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>' - }, - // Issues and MergeRequests - Issues: { - template: '<li><small>${id}</small> ${title}</li>' - }, - // Milestones - Milestones: { - template: '<li>${title}</li>' + filter: function(query, data, searchKey) { + if (gl.GfmAutoComplete.isLoading(data)) { + gl.GfmAutoComplete.fetchData(this.$inputor, this.at); + return data; + } else { + return $.fn.atwho["default"].callbacks.filter(query, data, searchKey); + } }, - Loading: { - template: '<li style="pointer-events: none;"><i class="fa fa-refresh fa-spin"></i> Loading...</li>' + beforeInsert: function(value) { + if (value && !this.setting.skipSpecialCharacterTest) { + var withoutAt = value.substring(1); + if (withoutAt && /[^\w\d]/.test(withoutAt)) value = value.charAt() + '"' + withoutAt + '"'; + } + return value; }, - DefaultOptions: { - sorter: function(query, items, searchKey) { - this.setting.highlightFirst = this.setting.alwaysHighlightFirst || query.length > 0; - if (gl.GfmAutoComplete.isLoading(items)) { - this.setting.highlightFirst = false; - return items; - } - return $.fn.atwho["default"].callbacks.sorter(query, items, searchKey); - }, - filter: function(query, data, searchKey) { - if (gl.GfmAutoComplete.isLoading(data)) { - gl.GfmAutoComplete.fetchData(this.$inputor, this.at); - return data; - } else { - return $.fn.atwho["default"].callbacks.filter(query, data, searchKey); - } - }, - beforeInsert: function(value) { - if (value && !this.setting.skipSpecialCharacterTest) { - var withoutAt = value.substring(1); - if (withoutAt && /[^\w\d]/.test(withoutAt)) value = value.charAt() + '"' + withoutAt + '"'; - } - return value; - }, - matcher: function (flag, subtext) { - // The below is taken from At.js source - // Tweaked to commands to start without a space only if char before is a non-word character - // https://github.com/ichord/At.js - var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar; - atSymbolsWithBar = Object.keys(this.app.controllers).join('|'); - atSymbolsWithoutBar = Object.keys(this.app.controllers).join(''); - subtext = subtext.split(/\s+/g).pop(); - flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + matcher: function (flag, subtext) { + // The below is taken from At.js source + // Tweaked to commands to start without a space only if char before is a non-word character + // https://github.com/ichord/At.js + var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar; + atSymbolsWithBar = Object.keys(this.app.controllers).join('|'); + atSymbolsWithoutBar = Object.keys(this.app.controllers).join(''); + subtext = subtext.split(/\s+/g).pop(); + flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - _a = decodeURI("%C3%80"); - _y = decodeURI("%C3%BF"); + _a = decodeURI("%C3%80"); + _y = decodeURI("%C3%BF"); - regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?!" + atSymbolsWithBar + ")((?:[A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]|[^\\x00-\\x7a])*)$", 'gi'); + regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?!" + atSymbolsWithBar + ")((?:[A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]|[^\\x00-\\x7a])*)$", 'gi'); - match = regexp.exec(subtext); + match = regexp.exec(subtext); - if (match) { - return match[1]; - } else { - return null; - } + if (match) { + return match[1]; + } else { + return null; } - }, - setup: function(input) { - // Add GFM auto-completion to all input fields, that accept GFM input. - this.input = input || $('.js-gfm-input'); - this.setupLifecycle(); - }, - setupLifecycle() { - this.input.each((i, input) => { - const $input = $(input); - $input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input)); - // This triggers at.js again - // Needed for slash commands with suffixes (ex: /label ~) - $input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup')); - }); - }, - setupAtWho: function($input) { - // Emoji - $input.atwho({ - at: ':', - displayTpl: function(value) { - return value && value.name ? this.Emoji.templateFunction(value.name) : this.Loading.template; - }.bind(this), - insertTpl: ':${name}:', - skipSpecialCharacterTest: true, - data: this.defaultLoadingData, - callbacks: { - sorter: this.DefaultOptions.sorter, - beforeInsert: this.DefaultOptions.beforeInsert, - filter: this.DefaultOptions.filter - } - }); - // Team Members - $input.atwho({ - at: '@', - displayTpl: function(value) { - return value.username != null ? this.Members.template : this.Loading.template; - }.bind(this), - insertTpl: '${atwho-at}${username}', - searchKey: 'search', - alwaysHighlightFirst: true, - skipSpecialCharacterTest: true, - data: this.defaultLoadingData, - callbacks: { - sorter: this.DefaultOptions.sorter, - filter: this.DefaultOptions.filter, - beforeInsert: this.DefaultOptions.beforeInsert, - matcher: this.DefaultOptions.matcher, - beforeSave: function(members) { - return $.map(members, function(m) { - let title = ''; - if (m.username == null) { - return m; - } - title = m.name; - if (m.count) { - title += " (" + m.count + ")"; - } + } + }, + setup: function(input) { + // Add GFM auto-completion to all input fields, that accept GFM input. + this.input = input || $('.js-gfm-input'); + this.setupLifecycle(); + }, + setupLifecycle() { + this.input.each((i, input) => { + const $input = $(input); + $input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input)); + // This triggers at.js again + // Needed for slash commands with suffixes (ex: /label ~) + $input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup')); + }); + }, + setupAtWho: function($input) { + // Emoji + $input.atwho({ + at: ':', + displayTpl: function(value) { + return value && value.name ? this.Emoji.templateFunction(value.name) : this.Loading.template; + }.bind(this), + insertTpl: ':${name}:', + skipSpecialCharacterTest: true, + data: this.defaultLoadingData, + callbacks: { + sorter: this.DefaultOptions.sorter, + beforeInsert: this.DefaultOptions.beforeInsert, + filter: this.DefaultOptions.filter + } + }); + // Team Members + $input.atwho({ + at: '@', + displayTpl: function(value) { + return value.username != null ? this.Members.template : this.Loading.template; + }.bind(this), + insertTpl: '${atwho-at}${username}', + searchKey: 'search', + alwaysHighlightFirst: true, + skipSpecialCharacterTest: true, + data: this.defaultLoadingData, + callbacks: { + sorter: this.DefaultOptions.sorter, + filter: this.DefaultOptions.filter, + beforeInsert: this.DefaultOptions.beforeInsert, + matcher: this.DefaultOptions.matcher, + beforeSave: function(members) { + return $.map(members, function(m) { + let title = ''; + if (m.username == null) { + return m; + } + title = m.name; + if (m.count) { + title += " (" + m.count + ")"; + } - const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase(); - const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar avatar-inline center s26"/>`; - const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`; + const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase(); + const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar avatar-inline center s26"/>`; + const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`; - return { - username: m.username, - avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar, - title: sanitize(title), - search: sanitize(m.username + " " + m.name) - }; - }); - } + return { + username: m.username, + avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar, + title: sanitize(title), + search: sanitize(m.username + " " + m.name) + }; + }); } - }); - $input.atwho({ - at: '#', - alias: 'issues', - searchKey: 'search', - displayTpl: function(value) { - return value.title != null ? this.Issues.template : this.Loading.template; - }.bind(this), - data: this.defaultLoadingData, - insertTpl: '${atwho-at}${id}', - callbacks: { - sorter: this.DefaultOptions.sorter, - filter: this.DefaultOptions.filter, - beforeInsert: this.DefaultOptions.beforeInsert, - matcher: this.DefaultOptions.matcher, - beforeSave: function(issues) { - return $.map(issues, function(i) { - if (i.title == null) { - return i; - } - return { - id: i.iid, - title: sanitize(i.title), - search: i.iid + " " + i.title - }; - }); - } + } + }); + $input.atwho({ + at: '#', + alias: 'issues', + searchKey: 'search', + displayTpl: function(value) { + return value.title != null ? this.Issues.template : this.Loading.template; + }.bind(this), + data: this.defaultLoadingData, + insertTpl: '${atwho-at}${id}', + callbacks: { + sorter: this.DefaultOptions.sorter, + filter: this.DefaultOptions.filter, + beforeInsert: this.DefaultOptions.beforeInsert, + matcher: this.DefaultOptions.matcher, + beforeSave: function(issues) { + return $.map(issues, function(i) { + if (i.title == null) { + return i; + } + return { + id: i.iid, + title: sanitize(i.title), + search: i.iid + " " + i.title + }; + }); } - }); - $input.atwho({ - at: '%', - alias: 'milestones', - searchKey: 'search', - insertTpl: '${atwho-at}${title}', - displayTpl: function(value) { - return value.title != null ? this.Milestones.template : this.Loading.template; - }.bind(this), - data: this.defaultLoadingData, - callbacks: { - matcher: this.DefaultOptions.matcher, - sorter: this.DefaultOptions.sorter, - beforeInsert: this.DefaultOptions.beforeInsert, - filter: this.DefaultOptions.filter, - beforeSave: function(milestones) { - return $.map(milestones, function(m) { - if (m.title == null) { - return m; - } - return { - id: m.iid, - title: sanitize(m.title), - search: "" + m.title - }; - }); - } + } + }); + $input.atwho({ + at: '%', + alias: 'milestones', + searchKey: 'search', + insertTpl: '${atwho-at}${title}', + displayTpl: function(value) { + return value.title != null ? this.Milestones.template : this.Loading.template; + }.bind(this), + data: this.defaultLoadingData, + callbacks: { + matcher: this.DefaultOptions.matcher, + sorter: this.DefaultOptions.sorter, + beforeInsert: this.DefaultOptions.beforeInsert, + filter: this.DefaultOptions.filter, + beforeSave: function(milestones) { + return $.map(milestones, function(m) { + if (m.title == null) { + return m; + } + return { + id: m.iid, + title: sanitize(m.title), + search: "" + m.title + }; + }); } - }); - $input.atwho({ - at: '!', - alias: 'mergerequests', - searchKey: 'search', - displayTpl: function(value) { - return value.title != null ? this.Issues.template : this.Loading.template; - }.bind(this), - data: this.defaultLoadingData, - insertTpl: '${atwho-at}${id}', - callbacks: { - sorter: this.DefaultOptions.sorter, - filter: this.DefaultOptions.filter, - beforeInsert: this.DefaultOptions.beforeInsert, - matcher: this.DefaultOptions.matcher, - beforeSave: function(merges) { - return $.map(merges, function(m) { - if (m.title == null) { - return m; - } - return { - id: m.iid, - title: sanitize(m.title), - search: m.iid + " " + m.title - }; - }); - } + } + }); + $input.atwho({ + at: '!', + alias: 'mergerequests', + searchKey: 'search', + displayTpl: function(value) { + return value.title != null ? this.Issues.template : this.Loading.template; + }.bind(this), + data: this.defaultLoadingData, + insertTpl: '${atwho-at}${id}', + callbacks: { + sorter: this.DefaultOptions.sorter, + filter: this.DefaultOptions.filter, + beforeInsert: this.DefaultOptions.beforeInsert, + matcher: this.DefaultOptions.matcher, + beforeSave: function(merges) { + return $.map(merges, function(m) { + if (m.title == null) { + return m; + } + return { + id: m.iid, + title: sanitize(m.title), + search: m.iid + " " + m.title + }; + }); } - }); - $input.atwho({ - at: '~', - alias: 'labels', - searchKey: 'search', - data: this.defaultLoadingData, - displayTpl: function(value) { - return this.isLoading(value) ? this.Loading.template : this.Labels.template; - }.bind(this), - insertTpl: '${atwho-at}${title}', - callbacks: { - matcher: this.DefaultOptions.matcher, - beforeInsert: this.DefaultOptions.beforeInsert, - filter: this.DefaultOptions.filter, - sorter: this.DefaultOptions.sorter, - beforeSave: function(merges) { - if (gl.GfmAutoComplete.isLoading(merges)) return merges; - var sanitizeLabelTitle; - sanitizeLabelTitle = function(title) { - if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) { - return "\"" + (sanitize(title)) + "\""; - } else { - return sanitize(title); - } + } + }); + $input.atwho({ + at: '~', + alias: 'labels', + searchKey: 'search', + data: this.defaultLoadingData, + displayTpl: function(value) { + return this.isLoading(value) ? this.Loading.template : this.Labels.template; + }.bind(this), + insertTpl: '${atwho-at}${title}', + callbacks: { + matcher: this.DefaultOptions.matcher, + beforeInsert: this.DefaultOptions.beforeInsert, + filter: this.DefaultOptions.filter, + sorter: this.DefaultOptions.sorter, + beforeSave: function(merges) { + if (gl.GfmAutoComplete.isLoading(merges)) return merges; + var sanitizeLabelTitle; + sanitizeLabelTitle = function(title) { + if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) { + return "\"" + (sanitize(title)) + "\""; + } else { + return sanitize(title); + } + }; + return $.map(merges, function(m) { + return { + title: sanitize(m.title), + color: m.color, + search: "" + m.title }; - return $.map(merges, function(m) { - return { - title: sanitize(m.title), - color: m.color, - search: "" + m.title - }; - }); - } + }); } - }); - // We don't instantiate the slash commands autocomplete for note and issue/MR edit forms - $input.filter('[data-supports-slash-commands="true"]').atwho({ - at: '/', - alias: 'commands', - searchKey: 'search', - skipSpecialCharacterTest: true, - data: this.defaultLoadingData, - displayTpl: function(value) { - if (this.isLoading(value)) return this.Loading.template; - var tpl = '<li>/${name}'; - if (value.aliases.length > 0) { - tpl += ' <small>(or /<%- aliases.join(", /") %>)</small>'; - } - if (value.params.length > 0) { - tpl += ' <small><%- params.join(" ") %></small>'; - } - if (value.description !== '') { - tpl += '<small class="description"><i><%- description %></i></small>'; + } + }); + // We don't instantiate the slash commands autocomplete for note and issue/MR edit forms + $input.filter('[data-supports-slash-commands="true"]').atwho({ + at: '/', + alias: 'commands', + searchKey: 'search', + skipSpecialCharacterTest: true, + data: this.defaultLoadingData, + displayTpl: function(value) { + if (this.isLoading(value)) return this.Loading.template; + var tpl = '<li>/${name}'; + if (value.aliases.length > 0) { + tpl += ' <small>(or /<%- aliases.join(", /") %>)</small>'; + } + if (value.params.length > 0) { + tpl += ' <small><%- params.join(" ") %></small>'; + } + if (value.description !== '') { + tpl += '<small class="description"><i><%- description %></i></small>'; + } + tpl += '</li>'; + return _.template(tpl)(value); + }.bind(this), + insertTpl: function(value) { + var tpl = "/${name} "; + var reference_prefix = null; + if (value.params.length > 0) { + reference_prefix = value.params[0][0]; + if (/^[@%~]/.test(reference_prefix)) { + tpl += '<%- reference_prefix %>'; } - tpl += '</li>'; - return _.template(tpl)(value); - }.bind(this), - insertTpl: function(value) { - var tpl = "/${name} "; - var reference_prefix = null; - if (value.params.length > 0) { - reference_prefix = value.params[0][0]; - if (/^[@%~]/.test(reference_prefix)) { - tpl += '<%- reference_prefix %>'; + } + return _.template(tpl)({ reference_prefix: reference_prefix }); + }, + suffix: '', + callbacks: { + sorter: this.DefaultOptions.sorter, + filter: this.DefaultOptions.filter, + beforeInsert: this.DefaultOptions.beforeInsert, + beforeSave: function(commands) { + if (gl.GfmAutoComplete.isLoading(commands)) return commands; + return $.map(commands, function(c) { + var search = c.name; + if (c.aliases.length > 0) { + search = search + " " + c.aliases.join(" "); } - } - return _.template(tpl)({ reference_prefix: reference_prefix }); + return { + name: c.name, + aliases: c.aliases, + params: c.params, + description: c.description, + search: search + }; + }); }, - suffix: '', - callbacks: { - sorter: this.DefaultOptions.sorter, - filter: this.DefaultOptions.filter, - beforeInsert: this.DefaultOptions.beforeInsert, - beforeSave: function(commands) { - if (gl.GfmAutoComplete.isLoading(commands)) return commands; - return $.map(commands, function(c) { - var search = c.name; - if (c.aliases.length > 0) { - search = search + " " + c.aliases.join(" "); - } - return { - name: c.name, - aliases: c.aliases, - params: c.params, - description: c.description, - search: search - }; - }); - }, - matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) { - var regexp = /(?:^|\n)\/([A-Za-z_]*)$/gi; - var match = regexp.exec(subtext); - if (match) { - return match[1]; - } else { - return null; - } + matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) { + var regexp = /(?:^|\n)\/([A-Za-z_]*)$/gi; + var match = regexp.exec(subtext); + if (match) { + return match[1]; + } else { + return null; } } - }); - return; - }, - fetchData: function($input, at) { - if (this.isLoadingData[at]) return; - this.isLoadingData[at] = true; - if (this.cachedData[at]) { - this.loadData($input, at, this.cachedData[at]); - } else if (this.atTypeMap[at] === 'emojis') { - this.loadData($input, at, Object.keys(emojiMap).concat(Object.keys(emojiAliases))); - } else { - $.getJSON(this.dataSources[this.atTypeMap[at]], (data) => { - this.loadData($input, at, data); - }).fail(() => { this.isLoadingData[at] = false; }); - } - }, - loadData: function($input, at, data) { - this.isLoadingData[at] = false; - this.cachedData[at] = data; - $input.atwho('load', at, data); - // This trigger at.js again - // otherwise we would be stuck with loading until the user types - return $input.trigger('keyup'); - }, - isLoading(data) { - var dataToInspect = data; - if (data && data.length > 0) { - dataToInspect = data[0]; } - - var loadingState = this.defaultLoadingData[0]; - return dataToInspect && - (dataToInspect === loadingState || dataToInspect.name === loadingState); + }); + return; + }, + fetchData: function($input, at) { + if (this.isLoadingData[at]) return; + this.isLoadingData[at] = true; + if (this.cachedData[at]) { + this.loadData($input, at, this.cachedData[at]); + } else if (this.atTypeMap[at] === 'emojis') { + this.loadData($input, at, Object.keys(emojiMap).concat(Object.keys(emojiAliases))); + } else { + $.getJSON(this.dataSources[this.atTypeMap[at]], (data) => { + this.loadData($input, at, data); + }).fail(() => { this.isLoadingData[at] = false; }); + } + }, + loadData: function($input, at, data) { + this.isLoadingData[at] = false; + this.cachedData[at] = data; + $input.atwho('load', at, data); + // This trigger at.js again + // otherwise we would be stuck with loading until the user types + return $input.trigger('keyup'); + }, + isLoading(data) { + var dataToInspect = data; + if (data && data.length > 0) { + dataToInspect = data[0]; } - }; -}).call(window); + + var loadingState = this.defaultLoadingData[0]; + return dataToInspect && + (dataToInspect === loadingState || dataToInspect.name === loadingState); + } +}; diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 9e6ed06054b..a03f1202a6d 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -1,850 +1,848 @@ /* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, prefer-rest-params, max-len, vars-on-top, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func, no-mixed-operators */ /* global fuzzaldrinPlus */ -(function() { - var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, - bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }, - indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; }; - - GitLabDropdownFilter = (function() { - var ARROW_KEY_CODES, BLUR_KEYCODES, HAS_VALUE_CLASS; - - BLUR_KEYCODES = [27, 40]; - - ARROW_KEY_CODES = [38, 40]; - - HAS_VALUE_CLASS = "has-value"; - - function GitLabDropdownFilter(input, options) { - var $clearButton, $inputContainer, ref, timeout; - this.input = input; - this.options = options; - this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true; - $inputContainer = this.input.parent(); - $clearButton = $inputContainer.find('.js-dropdown-input-clear'); - $clearButton.on('click', (function(_this) { - // Clear click - return function(e) { +var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, + bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }, + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; }; + +GitLabDropdownFilter = (function() { + var ARROW_KEY_CODES, BLUR_KEYCODES, HAS_VALUE_CLASS; + + BLUR_KEYCODES = [27, 40]; + + ARROW_KEY_CODES = [38, 40]; + + HAS_VALUE_CLASS = "has-value"; + + function GitLabDropdownFilter(input, options) { + var $clearButton, $inputContainer, ref, timeout; + this.input = input; + this.options = options; + this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true; + $inputContainer = this.input.parent(); + $clearButton = $inputContainer.find('.js-dropdown-input-clear'); + $clearButton.on('click', (function(_this) { + // Clear click + return function(e) { + e.preventDefault(); + e.stopPropagation(); + return _this.input.val('').trigger('input').focus(); + }; + })(this)); + // Key events + timeout = ""; + this.input + .on('keydown', function (e) { + var keyCode = e.which; + if (keyCode === 13 && !options.elIsInput) { e.preventDefault(); - e.stopPropagation(); - return _this.input.val('').trigger('input').focus(); - }; - })(this)); - // Key events - timeout = ""; - this.input - .on('keydown', function (e) { - var keyCode = e.which; - if (keyCode === 13 && !options.elIsInput) { - e.preventDefault(); - } - }) - .on('input', function() { - if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) { - $inputContainer.addClass(HAS_VALUE_CLASS); - } else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) { - $inputContainer.removeClass(HAS_VALUE_CLASS); - } - // Only filter asynchronously only if option remote is set - if (this.options.remote) { - clearTimeout(timeout); - return timeout = setTimeout(function() { - $inputContainer.parent().addClass('is-loading'); - - return this.options.query(this.input.val(), function(data) { - $inputContainer.parent().removeClass('is-loading'); - return this.options.callback(data); - }.bind(this)); - }.bind(this), 250); - } else { - return this.filter(this.input.val()); - } - }.bind(this)); - } + } + }) + .on('input', function() { + if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) { + $inputContainer.addClass(HAS_VALUE_CLASS); + } else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) { + $inputContainer.removeClass(HAS_VALUE_CLASS); + } + // Only filter asynchronously only if option remote is set + if (this.options.remote) { + clearTimeout(timeout); + return timeout = setTimeout(function() { + $inputContainer.parent().addClass('is-loading'); + + return this.options.query(this.input.val(), function(data) { + $inputContainer.parent().removeClass('is-loading'); + return this.options.callback(data); + }.bind(this)); + }.bind(this), 250); + } else { + return this.filter(this.input.val()); + } + }.bind(this)); + } - GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) { - return BLUR_KEYCODES.indexOf(keyCode) !== -1; - }; + GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) { + return BLUR_KEYCODES.indexOf(keyCode) !== -1; + }; - GitLabDropdownFilter.prototype.filter = function(search_text) { - var data, elements, group, key, results, tmp; - if (this.options.onFilter) { - this.options.onFilter(search_text); - } - data = this.options.data(); - if ((data != null) && !this.options.filterByText) { - results = data; - if (search_text !== '') { - // When data is an array of objects therefore [object Array] e.g. - // [ - // { prop: 'foo' }, - // { prop: 'baz' } - // ] - if (_.isArray(data)) { - results = fuzzaldrinPlus.filter(data, search_text, { - key: this.options.keys - }); - } else { - // If data is grouped therefore an [object Object]. e.g. - // { - // groupName1: [ - // { prop: 'foo' }, - // { prop: 'baz' } - // ], - // groupName2: [ - // { prop: 'abc' }, - // { prop: 'def' } - // ] - // } - if (gl.utils.isObject(data)) { - results = {}; - for (key in data) { - group = data[key]; - tmp = fuzzaldrinPlus.filter(group, search_text, { - key: this.options.keys + GitLabDropdownFilter.prototype.filter = function(search_text) { + var data, elements, group, key, results, tmp; + if (this.options.onFilter) { + this.options.onFilter(search_text); + } + data = this.options.data(); + if ((data != null) && !this.options.filterByText) { + results = data; + if (search_text !== '') { + // When data is an array of objects therefore [object Array] e.g. + // [ + // { prop: 'foo' }, + // { prop: 'baz' } + // ] + if (_.isArray(data)) { + results = fuzzaldrinPlus.filter(data, search_text, { + key: this.options.keys + }); + } else { + // If data is grouped therefore an [object Object]. e.g. + // { + // groupName1: [ + // { prop: 'foo' }, + // { prop: 'baz' } + // ], + // groupName2: [ + // { prop: 'abc' }, + // { prop: 'def' } + // ] + // } + if (gl.utils.isObject(data)) { + results = {}; + for (key in data) { + group = data[key]; + tmp = fuzzaldrinPlus.filter(group, search_text, { + key: this.options.keys + }); + if (tmp.length) { + results[key] = tmp.map(function(item) { + return item; }); - if (tmp.length) { - results[key] = tmp.map(function(item) { - return item; - }); - } } } } } - return this.options.callback(results); - } else { - elements = this.options.elements(); - if (search_text) { - return elements.each(function() { - var $el, matches; - $el = $(this); - matches = fuzzaldrinPlus.match($el.text().trim(), search_text); - if (!$el.is('.dropdown-header')) { - if (matches.length) { - return $el.show().removeClass('option-hidden'); - } else { - return $el.hide().addClass('option-hidden'); - } + } + return this.options.callback(results); + } else { + elements = this.options.elements(); + if (search_text) { + return elements.each(function() { + var $el, matches; + $el = $(this); + matches = fuzzaldrinPlus.match($el.text().trim(), search_text); + if (!$el.is('.dropdown-header')) { + if (matches.length) { + return $el.show().removeClass('option-hidden'); + } else { + return $el.hide().addClass('option-hidden'); } - }); - } else { - return elements.show().removeClass('option-hidden'); - } + } + }); + } else { + return elements.show().removeClass('option-hidden'); } - }; - - return GitLabDropdownFilter; - })(); + } + }; - GitLabDropdownRemote = (function() { - function GitLabDropdownRemote(dataEndpoint, options) { - this.dataEndpoint = dataEndpoint; - this.options = options; + return GitLabDropdownFilter; +})(); + +GitLabDropdownRemote = (function() { + function GitLabDropdownRemote(dataEndpoint, options) { + this.dataEndpoint = dataEndpoint; + this.options = options; + } + + GitLabDropdownRemote.prototype.execute = function() { + if (typeof this.dataEndpoint === "string") { + return this.fetchData(); + } else if (typeof this.dataEndpoint === "function") { + if (this.options.beforeSend) { + this.options.beforeSend(); + } + return this.dataEndpoint("", (function(_this) { + // Fetch the data by calling the data funcfion + return function(data) { + if (_this.options.success) { + _this.options.success(data); + } + if (_this.options.beforeSend) { + return _this.options.beforeSend(); + } + }; + })(this)); } + }; - GitLabDropdownRemote.prototype.execute = function() { - if (typeof this.dataEndpoint === "string") { - return this.fetchData(); - } else if (typeof this.dataEndpoint === "function") { - if (this.options.beforeSend) { - this.options.beforeSend(); - } - return this.dataEndpoint("", (function(_this) { - // Fetch the data by calling the data funcfion - return function(data) { - if (_this.options.success) { - _this.options.success(data); - } - if (_this.options.beforeSend) { - return _this.options.beforeSend(); - } - }; - })(this)); - } - }; + GitLabDropdownRemote.prototype.fetchData = function() { + return $.ajax({ + url: this.dataEndpoint, + dataType: this.options.dataType, + beforeSend: (function(_this) { + return function() { + if (_this.options.beforeSend) { + return _this.options.beforeSend(); + } + }; + })(this), + success: (function(_this) { + return function(data) { + if (_this.options.success) { + return _this.options.success(data); + } + }; + })(this) + }); + // Fetch the data through ajax if the data is a string + }; - GitLabDropdownRemote.prototype.fetchData = function() { - return $.ajax({ - url: this.dataEndpoint, - dataType: this.options.dataType, - beforeSend: (function(_this) { - return function() { - if (_this.options.beforeSend) { - return _this.options.beforeSend(); - } - }; - })(this), - success: (function(_this) { - return function(data) { - if (_this.options.success) { - return _this.options.success(data); - } - }; - })(this) - }); - // Fetch the data through ajax if the data is a string - }; + return GitLabDropdownRemote; +})(); - return GitLabDropdownRemote; - })(); +GitLabDropdown = (function() { + var ACTIVE_CLASS, FILTER_INPUT, INDETERMINATE_CLASS, LOADING_CLASS, PAGE_TWO_CLASS, NON_SELECTABLE_CLASSES, SELECTABLE_CLASSES, CURSOR_SELECT_SCROLL_PADDING, currentIndex; - GitLabDropdown = (function() { - var ACTIVE_CLASS, FILTER_INPUT, INDETERMINATE_CLASS, LOADING_CLASS, PAGE_TWO_CLASS, NON_SELECTABLE_CLASSES, SELECTABLE_CLASSES, CURSOR_SELECT_SCROLL_PADDING, currentIndex; + LOADING_CLASS = "is-loading"; - LOADING_CLASS = "is-loading"; + PAGE_TWO_CLASS = "is-page-two"; - PAGE_TWO_CLASS = "is-page-two"; + ACTIVE_CLASS = "is-active"; - ACTIVE_CLASS = "is-active"; + INDETERMINATE_CLASS = "is-indeterminate"; - INDETERMINATE_CLASS = "is-indeterminate"; + currentIndex = -1; - currentIndex = -1; + NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-link'; - NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-link'; - - SELECTABLE_CLASSES = ".dropdown-content li:not(" + NON_SELECTABLE_CLASSES + ", .option-hidden)"; - - CURSOR_SELECT_SCROLL_PADDING = 5; - - FILTER_INPUT = '.dropdown-input .dropdown-input-field'; - - function GitLabDropdown(el1, options) { - var searchFields, selector, self; - this.el = el1; - this.options = options; - this.updateLabel = bind(this.updateLabel, this); - this.hidden = bind(this.hidden, this); - this.opened = bind(this.opened, this); - this.shouldPropagate = bind(this.shouldPropagate, this); - self = this; - selector = $(this.el).data("target"); - this.dropdown = selector != null ? $(selector) : $(this.el).parent(); - // Set Defaults - this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT); - this.highlight = !!this.options.highlight; - this.filterInputBlur = this.options.filterInputBlur != null - ? this.options.filterInputBlur - : true; - // If no input is passed create a default one - self = this; - // If selector was passed - if (_.isString(this.filterInput)) { - this.filterInput = this.getElement(this.filterInput); - } - searchFields = this.options.search ? this.options.search.fields : []; - if (this.options.data) { - // If we provided data - // data could be an array of objects or a group of arrays - if (_.isObject(this.options.data) && !_.isFunction(this.options.data)) { - this.fullData = this.options.data; - currentIndex = -1; - this.parseData(this.options.data); - this.focusTextInput(); - } else { - this.remote = new GitLabDropdownRemote(this.options.data, { - dataType: this.options.dataType, - beforeSend: this.toggleLoading.bind(this), - success: (function(_this) { - return function(data) { - _this.fullData = data; - _this.parseData(_this.fullData); - _this.focusTextInput(); - if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val() && _this.filter.input.val().trim() !== '') { - return _this.filter.input.trigger('input'); - } - }; - // Remote data - })(this) - }); - } - } - // Init filterable - if (this.options.filterable) { - this.filter = new GitLabDropdownFilter(this.filterInput, { - elIsInput: $(this.el).is('input'), - filterInputBlur: this.filterInputBlur, - filterByText: this.options.filterByText, - onFilter: this.options.onFilter, - remote: this.options.filterRemote, - query: this.options.data, - keys: searchFields, - elements: (function(_this) { - return function() { - selector = '.dropdown-content li:not(' + NON_SELECTABLE_CLASSES + ')'; - if (_this.dropdown.find('.dropdown-toggle-page').length) { - selector = ".dropdown-page-one " + selector; - } - return $(selector); - }; - })(this), - data: (function(_this) { - return function() { - return _this.fullData; - }; - })(this), - callback: (function(_this) { + SELECTABLE_CLASSES = ".dropdown-content li:not(" + NON_SELECTABLE_CLASSES + ", .option-hidden)"; + + CURSOR_SELECT_SCROLL_PADDING = 5; + + FILTER_INPUT = '.dropdown-input .dropdown-input-field'; + + function GitLabDropdown(el1, options) { + var searchFields, selector, self; + this.el = el1; + this.options = options; + this.updateLabel = bind(this.updateLabel, this); + this.hidden = bind(this.hidden, this); + this.opened = bind(this.opened, this); + this.shouldPropagate = bind(this.shouldPropagate, this); + self = this; + selector = $(this.el).data("target"); + this.dropdown = selector != null ? $(selector) : $(this.el).parent(); + // Set Defaults + this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT); + this.highlight = !!this.options.highlight; + this.filterInputBlur = this.options.filterInputBlur != null + ? this.options.filterInputBlur + : true; + // If no input is passed create a default one + self = this; + // If selector was passed + if (_.isString(this.filterInput)) { + this.filterInput = this.getElement(this.filterInput); + } + searchFields = this.options.search ? this.options.search.fields : []; + if (this.options.data) { + // If we provided data + // data could be an array of objects or a group of arrays + if (_.isObject(this.options.data) && !_.isFunction(this.options.data)) { + this.fullData = this.options.data; + currentIndex = -1; + this.parseData(this.options.data); + this.focusTextInput(); + } else { + this.remote = new GitLabDropdownRemote(this.options.data, { + dataType: this.options.dataType, + beforeSend: this.toggleLoading.bind(this), + success: (function(_this) { return function(data) { - _this.parseData(data); - if (_this.filterInput.val() !== '') { - selector = SELECTABLE_CLASSES; - if (_this.dropdown.find('.dropdown-toggle-page').length) { - selector = ".dropdown-page-one " + selector; - } - if ($(_this.el).is('input')) { - currentIndex = -1; - } else { - $(selector, _this.dropdown).first().find('a').addClass('is-focused'); - currentIndex = 0; - } + _this.fullData = data; + _this.parseData(_this.fullData); + _this.focusTextInput(); + if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val() && _this.filter.input.val().trim() !== '') { + return _this.filter.input.trigger('input'); } }; + // Remote data })(this) }); } - // Event listeners - this.dropdown.on("shown.bs.dropdown", this.opened); - this.dropdown.on("hidden.bs.dropdown", this.hidden); - $(this.el).on("update.label", this.updateLabel); - this.dropdown.on("click", ".dropdown-menu, .dropdown-menu-close", this.shouldPropagate); - this.dropdown.on('keyup', (function(_this) { - return function(e) { - // Escape key - if (e.which === 27) { - return $('.dropdown-menu-close', _this.dropdown).trigger('click'); - } - }; - })(this)); - this.dropdown.on('blur', 'a', (function(_this) { - return function(e) { - var $dropdownMenu, $relatedTarget; - if (e.relatedTarget != null) { - $relatedTarget = $(e.relatedTarget); - $dropdownMenu = $relatedTarget.closest('.dropdown-menu'); - if ($dropdownMenu.length === 0) { - return _this.dropdown.removeClass('open'); + } + // Init filterable + if (this.options.filterable) { + this.filter = new GitLabDropdownFilter(this.filterInput, { + elIsInput: $(this.el).is('input'), + filterInputBlur: this.filterInputBlur, + filterByText: this.options.filterByText, + onFilter: this.options.onFilter, + remote: this.options.filterRemote, + query: this.options.data, + keys: searchFields, + elements: (function(_this) { + return function() { + selector = '.dropdown-content li:not(' + NON_SELECTABLE_CLASSES + ')'; + if (_this.dropdown.find('.dropdown-toggle-page').length) { + selector = ".dropdown-page-one " + selector; + } + return $(selector); + }; + })(this), + data: (function(_this) { + return function() { + return _this.fullData; + }; + })(this), + callback: (function(_this) { + return function(data) { + _this.parseData(data); + if (_this.filterInput.val() !== '') { + selector = SELECTABLE_CLASSES; + if (_this.dropdown.find('.dropdown-toggle-page').length) { + selector = ".dropdown-page-one " + selector; + } + if ($(_this.el).is('input')) { + currentIndex = -1; + } else { + $(selector, _this.dropdown).first().find('a').addClass('is-focused'); + currentIndex = 0; + } } + }; + })(this) + }); + } + // Event listeners + this.dropdown.on("shown.bs.dropdown", this.opened); + this.dropdown.on("hidden.bs.dropdown", this.hidden); + $(this.el).on("update.label", this.updateLabel); + this.dropdown.on("click", ".dropdown-menu, .dropdown-menu-close", this.shouldPropagate); + this.dropdown.on('keyup', (function(_this) { + return function(e) { + // Escape key + if (e.which === 27) { + return $('.dropdown-menu-close', _this.dropdown).trigger('click'); + } + }; + })(this)); + this.dropdown.on('blur', 'a', (function(_this) { + return function(e) { + var $dropdownMenu, $relatedTarget; + if (e.relatedTarget != null) { + $relatedTarget = $(e.relatedTarget); + $dropdownMenu = $relatedTarget.closest('.dropdown-menu'); + if ($dropdownMenu.length === 0) { + return _this.dropdown.removeClass('open'); } + } + }; + })(this)); + if (this.dropdown.find(".dropdown-toggle-page").length) { + this.dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on("click", (function(_this) { + return function(e) { + e.preventDefault(); + e.stopPropagation(); + return _this.togglePage(); }; })(this)); + } + if (this.options.selectable) { + selector = ".dropdown-content a"; if (this.dropdown.find(".dropdown-toggle-page").length) { - this.dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on("click", (function(_this) { - return function(e) { - e.preventDefault(); - e.stopPropagation(); - return _this.togglePage(); - }; - })(this)); - } - if (this.options.selectable) { - selector = ".dropdown-content a"; - if (this.dropdown.find(".dropdown-toggle-page").length) { - selector = ".dropdown-page-one .dropdown-content a"; + selector = ".dropdown-page-one .dropdown-content a"; + } + this.dropdown.on("click", selector, function(e) { + var $el, selected, selectedObj, isMarking; + $el = $(this); + selected = self.rowClicked($el); + selectedObj = selected ? selected[0] : null; + isMarking = selected ? selected[1] : null; + if (self.options.clicked) { + self.options.clicked(selectedObj, $el, e, isMarking); } - this.dropdown.on("click", selector, function(e) { - var $el, selected, selectedObj, isMarking; - $el = $(this); - selected = self.rowClicked($el); - selectedObj = selected ? selected[0] : null; - isMarking = selected ? selected[1] : null; - if (self.options.clicked) { - self.options.clicked(selectedObj, $el, e, isMarking); - } - // Update label right after all modifications in dropdown has been done - if (self.options.toggleLabel) { - self.updateLabel(selectedObj, $el, self); - } + // Update label right after all modifications in dropdown has been done + if (self.options.toggleLabel) { + self.updateLabel(selectedObj, $el, self); + } - $el.trigger('blur'); - }); - } + $el.trigger('blur'); + }); } + } - // Finds an element inside wrapper element - GitLabDropdown.prototype.getElement = function(selector) { - return this.dropdown.find(selector); - }; + // Finds an element inside wrapper element + GitLabDropdown.prototype.getElement = function(selector) { + return this.dropdown.find(selector); + }; - GitLabDropdown.prototype.toggleLoading = function() { - return $('.dropdown-menu', this.dropdown).toggleClass(LOADING_CLASS); - }; + GitLabDropdown.prototype.toggleLoading = function() { + return $('.dropdown-menu', this.dropdown).toggleClass(LOADING_CLASS); + }; - GitLabDropdown.prototype.togglePage = function() { - var menu; - menu = $('.dropdown-menu', this.dropdown); - if (menu.hasClass(PAGE_TWO_CLASS)) { - if (this.remote) { - this.remote.execute(); - } - } - menu.toggleClass(PAGE_TWO_CLASS); - // Focus first visible input on active page - return this.dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus(); - }; - - GitLabDropdown.prototype.parseData = function(data) { - var full_html, groupData, html, name; - this.renderedData = data; - if (this.options.filterable && data.length === 0) { - // render no matching results - html = [this.noResults()]; - } else { - // Handle array groups - if (gl.utils.isObject(data)) { - html = []; - for (name in data) { - groupData = data[name]; - html.push(this.renderItem({ - header: name - // Add header for each group - }, name)); - this.renderData(groupData, name).map(function(item) { - return html.push(item); - }); - } - } else { - // Render each row - html = this.renderData(data); - } - } - // Render the full menu - full_html = this.renderMenu(html); - return this.appendMenu(full_html); - }; - - GitLabDropdown.prototype.renderData = function(data, group) { - if (group == null) { - group = false; + GitLabDropdown.prototype.togglePage = function() { + var menu; + menu = $('.dropdown-menu', this.dropdown); + if (menu.hasClass(PAGE_TWO_CLASS)) { + if (this.remote) { + this.remote.execute(); } - return data.map((function(_this) { - return function(obj, index) { - return _this.renderItem(obj, group, index); - }; - })(this)); - }; - - GitLabDropdown.prototype.shouldPropagate = function(e) { - var $target; - if (this.options.multiSelect) { - $target = $(e.target); - if ($target && !$target.hasClass('dropdown-menu-close') && - !$target.hasClass('dropdown-menu-close-icon') && - !$target.data('is-link')) { - e.stopPropagation(); - return false; - } else { - return true; + } + menu.toggleClass(PAGE_TWO_CLASS); + // Focus first visible input on active page + return this.dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus(); + }; + + GitLabDropdown.prototype.parseData = function(data) { + var full_html, groupData, html, name; + this.renderedData = data; + if (this.options.filterable && data.length === 0) { + // render no matching results + html = [this.noResults()]; + } else { + // Handle array groups + if (gl.utils.isObject(data)) { + html = []; + for (name in data) { + groupData = data[name]; + html.push(this.renderItem({ + header: name + // Add header for each group + }, name)); + this.renderData(groupData, name).map(function(item) { + return html.push(item); + }); } + } else { + // Render each row + html = this.renderData(data); } - }; + } + // Render the full menu + full_html = this.renderMenu(html); + return this.appendMenu(full_html); + }; - GitLabDropdown.prototype.opened = function(e) { - var contentHtml; - this.resetRows(); - this.addArrowKeyEvent(); + GitLabDropdown.prototype.renderData = function(data, group) { + if (group == null) { + group = false; + } + return data.map((function(_this) { + return function(obj, index) { + return _this.renderItem(obj, group, index); + }; + })(this)); + }; - // Makes indeterminate items effective - if (this.fullData && this.dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')) { - this.parseData(this.fullData); - } - contentHtml = $('.dropdown-content', this.dropdown).html(); - if (this.remote && contentHtml === "") { - this.remote.execute(); + GitLabDropdown.prototype.shouldPropagate = function(e) { + var $target; + if (this.options.multiSelect) { + $target = $(e.target); + if ($target && !$target.hasClass('dropdown-menu-close') && + !$target.hasClass('dropdown-menu-close-icon') && + !$target.data('is-link')) { + e.stopPropagation(); + return false; } else { - this.focusTextInput(); + return true; } + } + }; - if (this.options.showMenuAbove) { - this.positionMenuAbove(); - } + GitLabDropdown.prototype.opened = function(e) { + var contentHtml; + this.resetRows(); + this.addArrowKeyEvent(); - if (this.options.opened) { - this.options.opened.call(this, e); - } + // Makes indeterminate items effective + if (this.fullData && this.dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')) { + this.parseData(this.fullData); + } + contentHtml = $('.dropdown-content', this.dropdown).html(); + if (this.remote && contentHtml === "") { + this.remote.execute(); + } else { + this.focusTextInput(); + } + + if (this.options.showMenuAbove) { + this.positionMenuAbove(); + } - return this.dropdown.trigger('shown.gl.dropdown'); - }; + if (this.options.opened) { + this.options.opened.call(this, e); + } - GitLabDropdown.prototype.positionMenuAbove = function() { - var $button = $(this.el); - var $menu = this.dropdown.find('.dropdown-menu'); + return this.dropdown.trigger('shown.gl.dropdown'); + }; - $menu.css('top', ($button.height() + $menu.height()) * -1); - }; + GitLabDropdown.prototype.positionMenuAbove = function() { + var $button = $(this.el); + var $menu = this.dropdown.find('.dropdown-menu'); - GitLabDropdown.prototype.hidden = function(e) { - var $input; - this.resetRows(); - this.removeArrayKeyEvent(); - $input = this.dropdown.find(".dropdown-input-field"); - if (this.options.filterable) { - $input.blur(); - } - if (this.dropdown.find(".dropdown-toggle-page").length) { - $('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS); - } - if (this.options.hidden) { - this.options.hidden.call(this, e); - } - return this.dropdown.trigger('hidden.gl.dropdown'); - }; + $menu.css('top', ($button.height() + $menu.height()) * -1); + }; - // Render the full menu - GitLabDropdown.prototype.renderMenu = function(html) { - if (this.options.renderMenu) { - return this.options.renderMenu(html); - } else { - var ul = document.createElement('ul'); + GitLabDropdown.prototype.hidden = function(e) { + var $input; + this.resetRows(); + this.removeArrayKeyEvent(); + $input = this.dropdown.find(".dropdown-input-field"); + if (this.options.filterable) { + $input.blur(); + } + if (this.dropdown.find(".dropdown-toggle-page").length) { + $('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS); + } + if (this.options.hidden) { + this.options.hidden.call(this, e); + } + return this.dropdown.trigger('hidden.gl.dropdown'); + }; - for (var i = 0; i < html.length; i += 1) { - var el = html[i]; + // Render the full menu + GitLabDropdown.prototype.renderMenu = function(html) { + if (this.options.renderMenu) { + return this.options.renderMenu(html); + } else { + var ul = document.createElement('ul'); - if (el instanceof jQuery) { - el = el.get(0); - } + for (var i = 0; i < html.length; i += 1) { + var el = html[i]; - if (typeof el === 'string') { - ul.innerHTML += el; - } else { - ul.appendChild(el); - } + if (el instanceof jQuery) { + el = el.get(0); } - return ul; + if (typeof el === 'string') { + ul.innerHTML += el; + } else { + ul.appendChild(el); + } } - }; - // Append the menu into the dropdown - GitLabDropdown.prototype.appendMenu = function(html) { - return this.clearMenu().append(html); - }; + return ul; + } + }; + + // Append the menu into the dropdown + GitLabDropdown.prototype.appendMenu = function(html) { + return this.clearMenu().append(html); + }; - GitLabDropdown.prototype.clearMenu = function() { - var selector; - selector = '.dropdown-content'; - if (this.dropdown.find(".dropdown-toggle-page").length) { - selector = ".dropdown-page-one .dropdown-content"; - } + GitLabDropdown.prototype.clearMenu = function() { + var selector; + selector = '.dropdown-content'; + if (this.dropdown.find(".dropdown-toggle-page").length) { + selector = ".dropdown-page-one .dropdown-content"; + } - return $(selector, this.dropdown).empty(); - }; + return $(selector, this.dropdown).empty(); + }; - GitLabDropdown.prototype.renderItem = function(data, group, index) { - var field, fieldName, html, selected, text, url, value; - if (group == null) { - group = false; + GitLabDropdown.prototype.renderItem = function(data, group, index) { + var field, fieldName, html, selected, text, url, value; + if (group == null) { + group = false; + } + if (index == null) { + // Render the row + index = false; + } + html = document.createElement('li'); + if (data === 'divider' || data === 'separator') { + html.className = data; + return html; + } + // Header + if (data.header != null) { + html.className = 'dropdown-header'; + html.innerHTML = data.header; + return html; + } + if (this.options.renderRow) { + // Call the render function + html = this.options.renderRow.call(this.options, data, this); + } else { + if (!selected) { + value = this.options.id ? this.options.id(data) : data.id; + fieldName = this.options.fieldName; + + if (value) { value = value.toString().replace(/'/g, '\\\''); } + + field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value + "']"); + if (field.length) { + selected = true; + } } - if (index == null) { - // Render the row - index = false; + // Set URL + if (this.options.url != null) { + url = this.options.url(data); + } else { + url = data.url != null ? data.url : '#'; } - html = document.createElement('li'); - if (data === 'divider' || data === 'separator') { - html.className = data; - return html; + // Set Text + if (this.options.text != null) { + text = this.options.text(data); + } else { + text = data.text != null ? data.text : ''; } - // Header - if (data.header != null) { - html.className = 'dropdown-header'; - html.innerHTML = data.header; - return html; + if (this.highlight) { + text = this.highlightTextMatches(text, this.filterInput.val()); } - if (this.options.renderRow) { - // Call the render function - html = this.options.renderRow.call(this.options, data, this); - } else { - if (!selected) { - value = this.options.id ? this.options.id(data) : data.id; - fieldName = this.options.fieldName; - - if (value) { value = value.toString().replace(/'/g, '\\\''); } - - field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value + "']"); - if (field.length) { - selected = true; - } - } - // Set URL - if (this.options.url != null) { - url = this.options.url(data); - } else { - url = data.url != null ? data.url : '#'; - } - // Set Text - if (this.options.text != null) { - text = this.options.text(data); - } else { - text = data.text != null ? data.text : ''; - } - if (this.highlight) { - text = this.highlightTextMatches(text, this.filterInput.val()); - } - // Create the list item & the link - var link = document.createElement('a'); - - link.href = url; - link.innerHTML = text; + // Create the list item & the link + var link = document.createElement('a'); - if (selected) { - link.className = 'is-active'; - } - - if (group) { - link.dataset.group = group; - link.dataset.index = index; - } + link.href = url; + link.innerHTML = text; - html.appendChild(link); + if (selected) { + link.className = 'is-active'; } - return html; - }; - - GitLabDropdown.prototype.highlightTextMatches = function(text, term) { - var occurrences; - occurrences = fuzzaldrinPlus.match(text, term); - return text.split('').map(function(character, i) { - if (indexOf.call(occurrences, i) !== -1) { - return "<b>" + character + "</b>"; - } else { - return character; - } - }).join(''); - }; - - GitLabDropdown.prototype.noResults = function() { - var html; - return html = "<li class='dropdown-menu-empty-link'> <a href='#' class='is-focused'> No matching results. </a> </li>"; - }; - - GitLabDropdown.prototype.rowClicked = function(el) { - var field, fieldName, groupName, isInput, selectedIndex, selectedObject, value, isMarking; - - fieldName = this.options.fieldName; - isInput = $(this.el).is('input'); - if (this.renderedData) { - groupName = el.data('group'); - if (groupName) { - selectedIndex = el.data('index'); - selectedObject = this.renderedData[groupName][selectedIndex]; - } else { - selectedIndex = el.closest('li').index(); - selectedObject = this.renderedData[selectedIndex]; - } + + if (group) { + link.dataset.group = group; + link.dataset.index = index; } - if (this.options.vue) { - if (el.hasClass(ACTIVE_CLASS)) { - el.removeClass(ACTIVE_CLASS); - } else { - el.addClass(ACTIVE_CLASS); - } + html.appendChild(link); + } + return html; + }; - return [selectedObject]; + GitLabDropdown.prototype.highlightTextMatches = function(text, term) { + var occurrences; + occurrences = fuzzaldrinPlus.match(text, term); + return text.split('').map(function(character, i) { + if (indexOf.call(occurrences, i) !== -1) { + return "<b>" + character + "</b>"; + } else { + return character; } + }).join(''); + }; - field = []; - value = this.options.id - ? this.options.id(selectedObject, el) - : selectedObject.id; - if (isInput) { - field = $(this.el); - } else if (value) { - field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']"); - } + GitLabDropdown.prototype.noResults = function() { + var html; + return html = "<li class='dropdown-menu-empty-link'> <a href='#' class='is-focused'> No matching results. </a> </li>"; + }; + + GitLabDropdown.prototype.rowClicked = function(el) { + var field, fieldName, groupName, isInput, selectedIndex, selectedObject, value, isMarking; - if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) { - return; + fieldName = this.options.fieldName; + isInput = $(this.el).is('input'); + if (this.renderedData) { + groupName = el.data('group'); + if (groupName) { + selectedIndex = el.data('index'); + selectedObject = this.renderedData[groupName][selectedIndex]; + } else { + selectedIndex = el.closest('li').index(); + selectedObject = this.renderedData[selectedIndex]; } + } + if (this.options.vue) { if (el.hasClass(ACTIVE_CLASS)) { - isMarking = false; el.removeClass(ACTIVE_CLASS); - if (field && field.length) { - this.clearField(field, isInput); - } - } else if (el.hasClass(INDETERMINATE_CLASS)) { - isMarking = true; - el.addClass(ACTIVE_CLASS); - el.removeClass(INDETERMINATE_CLASS); - if (field && field.length && value == null) { - this.clearField(field, isInput); - } - if ((!field || !field.length) && fieldName) { - this.addInput(fieldName, value, selectedObject); - } } else { - isMarking = true; - if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) { - this.dropdown.find("." + ACTIVE_CLASS).removeClass(ACTIVE_CLASS); - if (!isInput) { - this.dropdown.parent().find("input[name='" + fieldName + "']").remove(); - } - } - if (field && field.length && value == null) { - this.clearField(field, isInput); - } - // Toggle active class for the tick mark el.addClass(ACTIVE_CLASS); - if (value != null) { - if ((!field || !field.length) && fieldName) { - this.addInput(fieldName, value, selectedObject); - } else if (field && field.length) { - field.val(value).trigger('change'); - } - } } - return [selectedObject, isMarking]; - }; + return [selectedObject]; + } - GitLabDropdown.prototype.focusTextInput = function() { - if (this.options.filterable) { this.filterInput.focus(); } - }; + field = []; + value = this.options.id + ? this.options.id(selectedObject, el) + : selectedObject.id; + if (isInput) { + field = $(this.el); + } else if (value) { + field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']"); + } - GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) { - var $input; - // Create hidden input for form - $input = $('<input>').attr('type', 'hidden').attr('name', fieldName).val(value); - if (this.options.inputId != null) { - $input.attr('id', this.options.inputId); - } - return this.dropdown.before($input); - }; - - GitLabDropdown.prototype.selectRowAtIndex = function(index) { - var $el, selector; - // If we pass an option index - if (typeof index !== "undefined") { - selector = SELECTABLE_CLASSES + ":eq(" + index + ") a"; - } else { - selector = ".dropdown-content .is-focused"; + if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) { + return; + } + + if (el.hasClass(ACTIVE_CLASS)) { + isMarking = false; + el.removeClass(ACTIVE_CLASS); + if (field && field.length) { + this.clearField(field, isInput); + } + } else if (el.hasClass(INDETERMINATE_CLASS)) { + isMarking = true; + el.addClass(ACTIVE_CLASS); + el.removeClass(INDETERMINATE_CLASS); + if (field && field.length && value == null) { + this.clearField(field, isInput); + } + if ((!field || !field.length) && fieldName) { + this.addInput(fieldName, value, selectedObject); + } + } else { + isMarking = true; + if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) { + this.dropdown.find("." + ACTIVE_CLASS).removeClass(ACTIVE_CLASS); + if (!isInput) { + this.dropdown.parent().find("input[name='" + fieldName + "']").remove(); + } } - if (this.dropdown.find(".dropdown-toggle-page").length) { - selector = ".dropdown-page-one " + selector; + if (field && field.length && value == null) { + this.clearField(field, isInput); } - // simulate a click on the first link - $el = $(selector, this.dropdown); - if ($el.length) { - var href = $el.attr('href'); - if (href && href !== '#') { - gl.utils.visitUrl(href); - } else { - $el.first().trigger('click'); + // Toggle active class for the tick mark + el.addClass(ACTIVE_CLASS); + if (value != null) { + if ((!field || !field.length) && fieldName) { + this.addInput(fieldName, value, selectedObject); + } else if (field && field.length) { + field.val(value).trigger('change'); } } - }; + } - GitLabDropdown.prototype.addArrowKeyEvent = function() { - var $input, ARROW_KEY_CODES, selector; - ARROW_KEY_CODES = [38, 40]; - $input = this.dropdown.find(".dropdown-input-field"); - selector = SELECTABLE_CLASSES; - if (this.dropdown.find(".dropdown-toggle-page").length) { - selector = ".dropdown-page-one " + selector; + return [selectedObject, isMarking]; + }; + + GitLabDropdown.prototype.focusTextInput = function() { + if (this.options.filterable) { this.filterInput.focus(); } + }; + + GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) { + var $input; + // Create hidden input for form + $input = $('<input>').attr('type', 'hidden').attr('name', fieldName).val(value); + if (this.options.inputId != null) { + $input.attr('id', this.options.inputId); + } + return this.dropdown.before($input); + }; + + GitLabDropdown.prototype.selectRowAtIndex = function(index) { + var $el, selector; + // If we pass an option index + if (typeof index !== "undefined") { + selector = SELECTABLE_CLASSES + ":eq(" + index + ") a"; + } else { + selector = ".dropdown-content .is-focused"; + } + if (this.dropdown.find(".dropdown-toggle-page").length) { + selector = ".dropdown-page-one " + selector; + } + // simulate a click on the first link + $el = $(selector, this.dropdown); + if ($el.length) { + var href = $el.attr('href'); + if (href && href !== '#') { + gl.utils.visitUrl(href); + } else { + $el.first().trigger('click'); } - return $('body').on('keydown', (function(_this) { - return function(e) { - var $listItems, PREV_INDEX, currentKeyCode; - currentKeyCode = e.which; - if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) { - e.preventDefault(); - e.stopImmediatePropagation(); - PREV_INDEX = currentIndex; - $listItems = $(selector, _this.dropdown); - // if @options.filterable - // $input.blur() - if (currentKeyCode === 40) { - // Move down - if (currentIndex < ($listItems.length - 1)) { - currentIndex += 1; - } - } else if (currentKeyCode === 38) { - // Move up - if (currentIndex > 0) { - currentIndex -= 1; - } + } + }; + + GitLabDropdown.prototype.addArrowKeyEvent = function() { + var $input, ARROW_KEY_CODES, selector; + ARROW_KEY_CODES = [38, 40]; + $input = this.dropdown.find(".dropdown-input-field"); + selector = SELECTABLE_CLASSES; + if (this.dropdown.find(".dropdown-toggle-page").length) { + selector = ".dropdown-page-one " + selector; + } + return $('body').on('keydown', (function(_this) { + return function(e) { + var $listItems, PREV_INDEX, currentKeyCode; + currentKeyCode = e.which; + if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) { + e.preventDefault(); + e.stopImmediatePropagation(); + PREV_INDEX = currentIndex; + $listItems = $(selector, _this.dropdown); + // if @options.filterable + // $input.blur() + if (currentKeyCode === 40) { + // Move down + if (currentIndex < ($listItems.length - 1)) { + currentIndex += 1; } - if (currentIndex !== PREV_INDEX) { - _this.highlightRowAtIndex($listItems, currentIndex); + } else if (currentKeyCode === 38) { + // Move up + if (currentIndex > 0) { + currentIndex -= 1; } - return false; } - if (currentKeyCode === 13 && currentIndex !== -1) { - e.preventDefault(); - _this.selectRowAtIndex(); + if (currentIndex !== PREV_INDEX) { + _this.highlightRowAtIndex($listItems, currentIndex); } - }; - })(this)); - }; - - GitLabDropdown.prototype.removeArrayKeyEvent = function() { - return $('body').off('keydown'); - }; - - GitLabDropdown.prototype.resetRows = function resetRows() { - currentIndex = -1; - $('.is-focused', this.dropdown).removeClass('is-focused'); - }; - - GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) { - var $dropdownContent, $listItem, dropdownContentBottom, dropdownContentHeight, dropdownContentTop, dropdownScrollTop, listItemBottom, listItemHeight, listItemTop; - // Remove the class for the previously focused row - $('.is-focused', this.dropdown).removeClass('is-focused'); - // Update the class for the row at the specific index - $listItem = $listItems.eq(index); - $listItem.find('a:first-child').addClass("is-focused"); - // Dropdown content scroll area - $dropdownContent = $listItem.closest('.dropdown-content'); - dropdownScrollTop = $dropdownContent.scrollTop(); - dropdownContentHeight = $dropdownContent.outerHeight(); - dropdownContentTop = $dropdownContent.prop('offsetTop'); - dropdownContentBottom = dropdownContentTop + dropdownContentHeight; - // Get the offset bottom of the list item - listItemHeight = $listItem.outerHeight(); - listItemTop = $listItem.prop('offsetTop'); - listItemBottom = listItemTop + listItemHeight; - if (!index) { - // Scroll the dropdown content to the top - $dropdownContent.scrollTop(0); - } else if (index === ($listItems.length - 1)) { - // Scroll the dropdown content to the bottom - $dropdownContent.scrollTop($dropdownContent.prop('scrollHeight')); - } else if (listItemBottom > (dropdownContentBottom + dropdownScrollTop)) { - // Scroll the dropdown content down - $dropdownContent.scrollTop(listItemBottom - dropdownContentBottom + CURSOR_SELECT_SCROLL_PADDING); - } else if (listItemTop < (dropdownContentTop + dropdownScrollTop)) { - // Scroll the dropdown content up - return $dropdownContent.scrollTop(listItemTop - dropdownContentTop - CURSOR_SELECT_SCROLL_PADDING); - } - }; + return false; + } + if (currentKeyCode === 13 && currentIndex !== -1) { + e.preventDefault(); + _this.selectRowAtIndex(); + } + }; + })(this)); + }; - GitLabDropdown.prototype.updateLabel = function(selected, el, instance) { - if (selected == null) { - selected = null; - } - if (el == null) { - el = null; - } - if (instance == null) { - instance = null; - } - return $(this.el).find(".dropdown-toggle-text").text(this.options.toggleLabel(selected, el, instance)); - }; + GitLabDropdown.prototype.removeArrayKeyEvent = function() { + return $('body').off('keydown'); + }; - GitLabDropdown.prototype.clearField = function(field, isInput) { - return isInput ? field.val('') : field.remove(); - }; + GitLabDropdown.prototype.resetRows = function resetRows() { + currentIndex = -1; + $('.is-focused', this.dropdown).removeClass('is-focused'); + }; - return GitLabDropdown; - })(); + GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) { + var $dropdownContent, $listItem, dropdownContentBottom, dropdownContentHeight, dropdownContentTop, dropdownScrollTop, listItemBottom, listItemHeight, listItemTop; + // Remove the class for the previously focused row + $('.is-focused', this.dropdown).removeClass('is-focused'); + // Update the class for the row at the specific index + $listItem = $listItems.eq(index); + $listItem.find('a:first-child').addClass("is-focused"); + // Dropdown content scroll area + $dropdownContent = $listItem.closest('.dropdown-content'); + dropdownScrollTop = $dropdownContent.scrollTop(); + dropdownContentHeight = $dropdownContent.outerHeight(); + dropdownContentTop = $dropdownContent.prop('offsetTop'); + dropdownContentBottom = dropdownContentTop + dropdownContentHeight; + // Get the offset bottom of the list item + listItemHeight = $listItem.outerHeight(); + listItemTop = $listItem.prop('offsetTop'); + listItemBottom = listItemTop + listItemHeight; + if (!index) { + // Scroll the dropdown content to the top + $dropdownContent.scrollTop(0); + } else if (index === ($listItems.length - 1)) { + // Scroll the dropdown content to the bottom + $dropdownContent.scrollTop($dropdownContent.prop('scrollHeight')); + } else if (listItemBottom > (dropdownContentBottom + dropdownScrollTop)) { + // Scroll the dropdown content down + $dropdownContent.scrollTop(listItemBottom - dropdownContentBottom + CURSOR_SELECT_SCROLL_PADDING); + } else if (listItemTop < (dropdownContentTop + dropdownScrollTop)) { + // Scroll the dropdown content up + return $dropdownContent.scrollTop(listItemTop - dropdownContentTop - CURSOR_SELECT_SCROLL_PADDING); + } + }; - $.fn.glDropdown = function(opts) { - return this.each(function() { - if (!$.data(this, 'glDropdown')) { - return $.data(this, 'glDropdown', new GitLabDropdown(this, opts)); - } - }); + GitLabDropdown.prototype.updateLabel = function(selected, el, instance) { + if (selected == null) { + selected = null; + } + if (el == null) { + el = null; + } + if (instance == null) { + instance = null; + } + return $(this.el).find(".dropdown-toggle-text").text(this.options.toggleLabel(selected, el, instance)); + }; + + GitLabDropdown.prototype.clearField = function(field, isInput) { + return isInput ? field.val('') : field.remove(); }; -}).call(window); + + return GitLabDropdown; +})(); + +$.fn.glDropdown = function(opts) { + return this.each(function() { + if (!$.data(this, 'glDropdown')) { + return $.data(this, 'glDropdown', new GitLabDropdown(this, opts)); + } + }); +}; diff --git a/app/assets/javascripts/gl_field_error.js b/app/assets/javascripts/gl_field_error.js index f7cbecc0385..76de249ac3b 100644 --- a/app/assets/javascripts/gl_field_error.js +++ b/app/assets/javascripts/gl_field_error.js @@ -1,164 +1,162 @@ -/* eslint-disable no-param-reassign */ -((global) => { - /* - * This class overrides the browser's validation error bubbles, displaying custom - * error messages for invalid fields instead. To begin validating any form, add the - * class `gl-show-field-errors` to the form element, and ensure error messages are - * declared in each inputs' `title` attribute. If no title is declared for an invalid - * field the user attempts to submit, "This field is required." will be shown by default. - * - * Opt not to validate certain fields by adding the class `gl-field-error-ignore` to the input. - * - * Set a custom error anchor for error message to be injected after with the - * class `gl-field-error-anchor` - * - * Examples: - * - * Basic: - * - * <form class='gl-show-field-errors'> - * <input type='text' name='username' title='Username is required.'/> - * </form> - * - * Ignore specific inputs (e.g. UsernameValidator): - * - * <form class='gl-show-field-errors'> - * <div class="form-group> - * <input type='text' class='gl-field-errors-ignore' pattern='[a-zA-Z0-9-_]+'/> - * </div> - * <div class="form-group"> - * <input type='text' name='username' title='Username is required.'/> - * </div> - * </form> - * - * Custom Error Anchor (allows error message to be injected after specified element): - * - * <form class='gl-show-field-errors'> - * <div class="form-group gl-field-error-anchor"> - * <input type='text' name='username' title='Username is required.'/> - * // Error message typically injected here - * </div> - * // Error message now injected here - * </form> - * - * */ - - /* - * Regex Patterns in use: - * - * Only alphanumeric: : "[a-zA-Z0-9]+" - * No special characters : "[a-zA-Z0-9-_]+", - * - * */ - - const errorMessageClass = 'gl-field-error'; - const inputErrorClass = 'gl-field-error-outline'; - const errorAnchorSelector = '.gl-field-error-anchor'; - const ignoreInputSelector = '.gl-field-error-ignore'; - - class GlFieldError { - constructor({ input, formErrors }) { - this.inputElement = $(input); - this.inputDomElement = this.inputElement.get(0); - this.form = formErrors; - this.errorMessage = this.inputElement.attr('title') || 'This field is required.'; - this.fieldErrorElement = $(`<p class='${errorMessageClass} hide'>${this.errorMessage}</p>`); - - this.state = { - valid: false, - empty: true, - }; - - this.initFieldValidation(); - } +/** + * This class overrides the browser's validation error bubbles, displaying custom + * error messages for invalid fields instead. To begin validating any form, add the + * class `gl-show-field-errors` to the form element, and ensure error messages are + * declared in each inputs' `title` attribute. If no title is declared for an invalid + * field the user attempts to submit, "This field is required." will be shown by default. + * + * Opt not to validate certain fields by adding the class `gl-field-error-ignore` to the input. + * + * Set a custom error anchor for error message to be injected after with the + * class `gl-field-error-anchor` + * + * Examples: + * + * Basic: + * + * <form class='gl-show-field-errors'> + * <input type='text' name='username' title='Username is required.'/> + * </form> + * + * Ignore specific inputs (e.g. UsernameValidator): + * + * <form class='gl-show-field-errors'> + * <div class="form-group> + * <input type='text' class='gl-field-errors-ignore' pattern='[a-zA-Z0-9-_]+'/> + * </div> + * <div class="form-group"> + * <input type='text' name='username' title='Username is required.'/> + * </div> + * </form> + * + * Custom Error Anchor (allows error message to be injected after specified element): + * + * <form class='gl-show-field-errors'> + * <div class="form-group gl-field-error-anchor"> + * <input type='text' name='username' title='Username is required.'/> + * // Error message typically injected here + * </div> + * // Error message now injected here + * </form> + * + */ + +/** + * Regex Patterns in use: + * + * Only alphanumeric: : "[a-zA-Z0-9]+" + * No special characters : "[a-zA-Z0-9-_]+", + * + */ + +const errorMessageClass = 'gl-field-error'; +const inputErrorClass = 'gl-field-error-outline'; +const errorAnchorSelector = '.gl-field-error-anchor'; +const ignoreInputSelector = '.gl-field-error-ignore'; + +class GlFieldError { + constructor({ input, formErrors }) { + this.inputElement = $(input); + this.inputDomElement = this.inputElement.get(0); + this.form = formErrors; + this.errorMessage = this.inputElement.attr('title') || 'This field is required.'; + this.fieldErrorElement = $(`<p class='${errorMessageClass} hide'>${this.errorMessage}</p>`); + + this.state = { + valid: false, + empty: true, + }; + + this.initFieldValidation(); + } - initFieldValidation() { - const customErrorAnchor = this.inputElement.parents(errorAnchorSelector); - const errorAnchor = customErrorAnchor.length ? customErrorAnchor : this.inputElement; + initFieldValidation() { + const customErrorAnchor = this.inputElement.parents(errorAnchorSelector); + const errorAnchor = customErrorAnchor.length ? customErrorAnchor : this.inputElement; - // hidden when injected into DOM - errorAnchor.after(this.fieldErrorElement); - this.inputElement.off('invalid').on('invalid', this.handleInvalidSubmit.bind(this)); - this.scopedSiblings = this.safelySelectSiblings(); - } + // hidden when injected into DOM + errorAnchor.after(this.fieldErrorElement); + this.inputElement.off('invalid').on('invalid', this.handleInvalidSubmit.bind(this)); + this.scopedSiblings = this.safelySelectSiblings(); + } - safelySelectSiblings() { - // Apply `ignoreSelector` in markup to siblings whose visibility should not be toggled - const unignoredSiblings = this.inputElement.siblings(`p:not(${ignoreInputSelector})`); - const parentContainer = this.inputElement.parent('.form-group'); + safelySelectSiblings() { + // Apply `ignoreSelector` in markup to siblings whose visibility should not be toggled + const unignoredSiblings = this.inputElement.siblings(`p:not(${ignoreInputSelector})`); + const parentContainer = this.inputElement.parent('.form-group'); - // Only select siblings when they're scoped within a form-group with one input - const safelyScoped = parentContainer.length && parentContainer.find('input').length === 1; + // Only select siblings when they're scoped within a form-group with one input + const safelyScoped = parentContainer.length && parentContainer.find('input').length === 1; - return safelyScoped ? unignoredSiblings : this.fieldErrorElement; - } + return safelyScoped ? unignoredSiblings : this.fieldErrorElement; + } - renderValidity() { - this.renderClear(); + renderValidity() { + this.renderClear(); - if (this.state.valid) { - this.renderValid(); - } else if (this.state.empty) { - this.renderEmpty(); - } else if (!this.state.valid) { - this.renderInvalid(); - } + if (this.state.valid) { + this.renderValid(); + } else if (this.state.empty) { + this.renderEmpty(); + } else if (!this.state.valid) { + this.renderInvalid(); } + } - handleInvalidSubmit(event) { - event.preventDefault(); - const currentValue = this.accessCurrentValue(); - this.state.valid = false; - this.state.empty = currentValue === ''; - - this.renderValidity(); - this.form.focusOnFirstInvalid.apply(this.form); - // For UX, wait til after first invalid submission to check each keyup - this.inputElement.off('keyup.fieldValidator') - .on('keyup.fieldValidator', this.updateValidity.bind(this)); - } + handleInvalidSubmit(event) { + event.preventDefault(); + const currentValue = this.accessCurrentValue(); + this.state.valid = false; + this.state.empty = currentValue === ''; + + this.renderValidity(); + this.form.focusOnFirstInvalid.apply(this.form); + // For UX, wait til after first invalid submission to check each keyup + this.inputElement.off('keyup.fieldValidator') + .on('keyup.fieldValidator', this.updateValidity.bind(this)); + } - /* Get or set current input value */ - accessCurrentValue(newVal) { - return newVal ? this.inputElement.val(newVal) : this.inputElement.val(); - } + /* Get or set current input value */ + accessCurrentValue(newVal) { + return newVal ? this.inputElement.val(newVal) : this.inputElement.val(); + } - getInputValidity() { - return this.inputDomElement.validity.valid; - } + getInputValidity() { + return this.inputDomElement.validity.valid; + } - updateValidity() { - const inputVal = this.accessCurrentValue(); - this.state.empty = !inputVal.length; - this.state.valid = this.getInputValidity(); - this.renderValidity(); - } + updateValidity() { + const inputVal = this.accessCurrentValue(); + this.state.empty = !inputVal.length; + this.state.valid = this.getInputValidity(); + this.renderValidity(); + } - renderValid() { - return this.renderClear(); - } + renderValid() { + return this.renderClear(); + } - renderEmpty() { - return this.renderInvalid(); - } + renderEmpty() { + return this.renderInvalid(); + } - renderInvalid() { - this.inputElement.addClass(inputErrorClass); - this.scopedSiblings.hide(); - return this.fieldErrorElement.show(); - } + renderInvalid() { + this.inputElement.addClass(inputErrorClass); + this.scopedSiblings.hide(); + return this.fieldErrorElement.show(); + } - renderClear() { - const inputVal = this.accessCurrentValue(); - if (!inputVal.split(' ').length) { - const trimmedInput = inputVal.trim(); - this.accessCurrentValue(trimmedInput); - } - this.inputElement.removeClass(inputErrorClass); - this.scopedSiblings.hide(); - this.fieldErrorElement.hide(); + renderClear() { + const inputVal = this.accessCurrentValue(); + if (!inputVal.split(' ').length) { + const trimmedInput = inputVal.trim(); + this.accessCurrentValue(trimmedInput); } + this.inputElement.removeClass(inputErrorClass); + this.scopedSiblings.hide(); + this.fieldErrorElement.hide(); } +} - global.GlFieldError = GlFieldError; -})(window.gl || (window.gl = {})); +window.gl = window.gl || {}; +window.gl.GlFieldError = GlFieldError; diff --git a/app/assets/javascripts/gl_field_errors.js b/app/assets/javascripts/gl_field_errors.js index e9add115429..636258ec555 100644 --- a/app/assets/javascripts/gl_field_errors.js +++ b/app/assets/javascripts/gl_field_errors.js @@ -2,47 +2,46 @@ require('./gl_field_error'); -((global) => { - const customValidationFlag = 'gl-field-error-ignore'; - - class GlFieldErrors { - constructor(form) { - this.form = $(form); - this.state = { - inputs: [], - valid: false - }; - this.initValidators(); - } +const customValidationFlag = 'gl-field-error-ignore'; + +class GlFieldErrors { + constructor(form) { + this.form = $(form); + this.state = { + inputs: [], + valid: false + }; + this.initValidators(); + } - initValidators () { - // register selectors here as needed - const validateSelectors = [':text', ':password', '[type=email]'] - .map((selector) => `input${selector}`).join(','); + initValidators () { + // register selectors here as needed + const validateSelectors = [':text', ':password', '[type=email]'] + .map((selector) => `input${selector}`).join(','); - this.state.inputs = this.form.find(validateSelectors).toArray() - .filter((input) => !input.classList.contains(customValidationFlag)) - .map((input) => new global.GlFieldError({ input, formErrors: this })); + this.state.inputs = this.form.find(validateSelectors).toArray() + .filter((input) => !input.classList.contains(customValidationFlag)) + .map((input) => new window.gl.GlFieldError({ input, formErrors: this })); - this.form.on('submit', this.catchInvalidFormSubmit); - } + this.form.on('submit', this.catchInvalidFormSubmit); + } - /* Neccessary to prevent intercept and override invalid form submit - * because Safari & iOS quietly allow form submission when form is invalid - * and prevents disabling of invalid submit button by application.js */ + /* Neccessary to prevent intercept and override invalid form submit + * because Safari & iOS quietly allow form submission when form is invalid + * and prevents disabling of invalid submit button by application.js */ - catchInvalidFormSubmit (event) { - if (!event.currentTarget.checkValidity()) { - event.preventDefault(); - event.stopPropagation(); - } + catchInvalidFormSubmit (event) { + if (!event.currentTarget.checkValidity()) { + event.preventDefault(); + event.stopPropagation(); } + } - focusOnFirstInvalid () { - const firstInvalid = this.state.inputs.filter((input) => !input.inputDomElement.validity.valid)[0]; - firstInvalid.inputElement.focus(); - } + focusOnFirstInvalid () { + const firstInvalid = this.state.inputs.filter((input) => !input.inputDomElement.validity.valid)[0]; + firstInvalid.inputElement.focus(); } +} - global.GlFieldErrors = GlFieldErrors; -})(window.gl || (window.gl = {})); +window.gl = window.gl || {}; +window.gl.GlFieldErrors = GlFieldErrors; diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index 0b446ff364a..e7c98e16581 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -3,90 +3,88 @@ /* global DropzoneInput */ /* global autosize */ -(() => { - const global = window.gl || (window.gl = {}); +window.gl = window.gl || {}; - function GLForm(form) { - this.form = form; - this.textarea = this.form.find('textarea.js-gfm-input'); - // Before we start, we should clean up any previous data for this form - this.destroy(); - // Setup the form - this.setupForm(); - this.form.data('gl-form', this); - } +function GLForm(form) { + this.form = form; + this.textarea = this.form.find('textarea.js-gfm-input'); + // Before we start, we should clean up any previous data for this form + this.destroy(); + // Setup the form + this.setupForm(); + this.form.data('gl-form', this); +} - GLForm.prototype.destroy = function() { - // Clean form listeners - this.clearEventListeners(); - return this.form.data('gl-form', null); - }; +GLForm.prototype.destroy = function() { + // Clean form listeners + this.clearEventListeners(); + return this.form.data('gl-form', null); +}; - GLForm.prototype.setupForm = function() { - var isNewForm; - isNewForm = this.form.is(':not(.gfm-form)'); - this.form.removeClass('js-new-note-form'); - if (isNewForm) { - this.form.find('.div-dropzone').remove(); - this.form.addClass('gfm-form'); - // remove notify commit author checkbox for non-commit notes - gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); - gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); - new DropzoneInput(this.form); - autosize(this.textarea); - // form and textarea event listeners - this.addEventListeners(); - } - gl.text.init(this.form); - // hide discard button - this.form.find('.js-note-discard').hide(); - this.form.show(); - if (this.isAutosizeable) this.setupAutosize(); - }; +GLForm.prototype.setupForm = function() { + var isNewForm; + isNewForm = this.form.is(':not(.gfm-form)'); + this.form.removeClass('js-new-note-form'); + if (isNewForm) { + this.form.find('.div-dropzone').remove(); + this.form.addClass('gfm-form'); + // remove notify commit author checkbox for non-commit notes + gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button')); + gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); + new DropzoneInput(this.form); + autosize(this.textarea); + // form and textarea event listeners + this.addEventListeners(); + } + gl.text.init(this.form); + // hide discard button + this.form.find('.js-note-discard').hide(); + this.form.show(); + if (this.isAutosizeable) this.setupAutosize(); +}; - GLForm.prototype.setupAutosize = function () { - this.textarea.off('autosize:resized') - .on('autosize:resized', this.setHeightData.bind(this)); +GLForm.prototype.setupAutosize = function () { + this.textarea.off('autosize:resized') + .on('autosize:resized', this.setHeightData.bind(this)); - this.textarea.off('mouseup.autosize') - .on('mouseup.autosize', this.destroyAutosize.bind(this)); + this.textarea.off('mouseup.autosize') + .on('mouseup.autosize', this.destroyAutosize.bind(this)); - setTimeout(() => { - autosize(this.textarea); - this.textarea.css('resize', 'vertical'); - }, 0); - }; + setTimeout(() => { + autosize(this.textarea); + this.textarea.css('resize', 'vertical'); + }, 0); +}; - GLForm.prototype.setHeightData = function () { - this.textarea.data('height', this.textarea.outerHeight()); - }; +GLForm.prototype.setHeightData = function () { + this.textarea.data('height', this.textarea.outerHeight()); +}; - GLForm.prototype.destroyAutosize = function () { - const outerHeight = this.textarea.outerHeight(); +GLForm.prototype.destroyAutosize = function () { + const outerHeight = this.textarea.outerHeight(); - if (this.textarea.data('height') === outerHeight) return; + if (this.textarea.data('height') === outerHeight) return; - autosize.destroy(this.textarea); + autosize.destroy(this.textarea); - this.textarea.data('height', outerHeight); - this.textarea.outerHeight(outerHeight); - this.textarea.css('max-height', window.outerHeight); - }; + this.textarea.data('height', outerHeight); + this.textarea.outerHeight(outerHeight); + this.textarea.css('max-height', window.outerHeight); +}; - GLForm.prototype.clearEventListeners = function() { - this.textarea.off('focus'); - this.textarea.off('blur'); - return gl.text.removeListeners(this.form); - }; +GLForm.prototype.clearEventListeners = function() { + this.textarea.off('focus'); + this.textarea.off('blur'); + return gl.text.removeListeners(this.form); +}; - GLForm.prototype.addEventListeners = function() { - this.textarea.on('focus', function() { - return $(this).closest('.md-area').addClass('is-focused'); - }); - return this.textarea.on('blur', function() { - return $(this).closest('.md-area').removeClass('is-focused'); - }); - }; +GLForm.prototype.addEventListeners = function() { + this.textarea.on('focus', function() { + return $(this).closest('.md-area').addClass('is-focused'); + }); + return this.textarea.on('blur', function() { + return $(this).closest('.md-area').removeClass('is-focused'); + }); +}; - global.GLForm = GLForm; -})(); +window.gl.GLForm = GLForm; diff --git a/app/assets/javascripts/group_avatar.js b/app/assets/javascripts/group_avatar.js index c5cb273c5b2..f03b47b1c1d 100644 --- a/app/assets/javascripts/group_avatar.js +++ b/app/assets/javascripts/group_avatar.js @@ -1,20 +1,19 @@ /* 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 */ -(function() { - this.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); - }); - } - return GroupAvatar; - })(); -}).call(window); +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); + }); + } + + return GroupAvatar; +})(); diff --git a/app/assets/javascripts/group_label_subscription.js b/app/assets/javascripts/group_label_subscription.js index 15e695e81cf..7dc9ce898e8 100644 --- a/app/assets/javascripts/group_label_subscription.js +++ b/app/assets/javascripts/group_label_subscription.js @@ -1,53 +1,52 @@ /* eslint-disable func-names, object-shorthand, comma-dangle, wrap-iife, space-before-function-paren, no-param-reassign, max-len */ -(function(global) { - class GroupLabelSubscription { - constructor(container) { - const $container = $(container); - this.$dropdown = $container.find('.dropdown'); - this.$subscribeButtons = $container.find('.js-subscribe-button'); - this.$unsubscribeButtons = $container.find('.js-unsubscribe-button'); - - this.$subscribeButtons.on('click', this.subscribe.bind(this)); - this.$unsubscribeButtons.on('click', this.unsubscribe.bind(this)); - } - - unsubscribe(event) { - event.preventDefault(); - - const url = this.$unsubscribeButtons.attr('data-url'); - - $.ajax({ - type: 'POST', - url: url - }).done(() => { - this.toggleSubscriptionButtons(); - this.$unsubscribeButtons.removeAttr('data-url'); - }); - } - - subscribe(event) { - event.preventDefault(); - - const $btn = $(event.currentTarget); - const url = $btn.attr('data-url'); - - this.$unsubscribeButtons.attr('data-url', url); - - $.ajax({ - type: 'POST', - url: url - }).done(() => { - this.toggleSubscriptionButtons(); - }); - } - - toggleSubscriptionButtons() { - this.$dropdown.toggleClass('hidden'); - this.$subscribeButtons.toggleClass('hidden'); - this.$unsubscribeButtons.toggleClass('hidden'); - } +class GroupLabelSubscription { + constructor(container) { + const $container = $(container); + this.$dropdown = $container.find('.dropdown'); + this.$subscribeButtons = $container.find('.js-subscribe-button'); + this.$unsubscribeButtons = $container.find('.js-unsubscribe-button'); + + this.$subscribeButtons.on('click', this.subscribe.bind(this)); + this.$unsubscribeButtons.on('click', this.unsubscribe.bind(this)); } - global.GroupLabelSubscription = GroupLabelSubscription; -})(window.gl || (window.gl = {})); + unsubscribe(event) { + event.preventDefault(); + + const url = this.$unsubscribeButtons.attr('data-url'); + + $.ajax({ + type: 'POST', + url: url + }).done(() => { + this.toggleSubscriptionButtons(); + this.$unsubscribeButtons.removeAttr('data-url'); + }); + } + + subscribe(event) { + event.preventDefault(); + + const $btn = $(event.currentTarget); + const url = $btn.attr('data-url'); + + this.$unsubscribeButtons.attr('data-url', url); + + $.ajax({ + type: 'POST', + url: url + }).done(() => { + this.toggleSubscriptionButtons(); + }); + } + + toggleSubscriptionButtons() { + this.$dropdown.toggleClass('hidden'); + this.$subscribeButtons.toggleClass('hidden'); + this.$unsubscribeButtons.toggleClass('hidden'); + } +} + +window.gl = window.gl || {}; +window.gl.GroupLabelSubscription = GroupLabelSubscription; diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index 6b937e7fa0f..e5dfa30edab 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -1,71 +1,69 @@ /* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, one-var, camelcase, one-var-declaration-per-line, quotes, object-shorthand, prefer-arrow-callback, comma-dangle, consistent-return, yoda, prefer-rest-params, prefer-spread, no-unused-vars, prefer-template, max-len */ /* global Api */ -(function() { - var slice = [].slice; +var slice = [].slice; - this.GroupsSelect = (function() { - function GroupsSelect() { - $('.ajax-groups-select').each((function(_this) { - return function(i, select) { - var all_available, skip_groups; - all_available = $(select).data('all-available'); - skip_groups = $(select).data('skip-groups') || []; - return $(select).select2({ - placeholder: "Search for a group", - multiple: $(select).hasClass('multiselect'), - minimumInputLength: 0, - query: function(query) { - var options = { all_available: all_available, skip_groups: skip_groups }; - return Api.groups(query.term, options, function(groups) { - var data; - data = { - results: groups - }; - return query.callback(data); - }); - }, - 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 _this.formatResult.apply(_this, args); - }, - formatSelection: function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return _this.formatSelection.apply(_this, args); - }, - dropdownCssClass: "ajax-groups-dropdown", - // we do not want to escape markup since we are displaying html in results - escapeMarkup: function(m) { - return m; +window.GroupsSelect = (function() { + function GroupsSelect() { + $('.ajax-groups-select').each((function(_this) { + return function(i, select) { + var all_available, skip_groups; + all_available = $(select).data('all-available'); + skip_groups = $(select).data('skip-groups') || []; + return $(select).select2({ + placeholder: "Search for a group", + multiple: $(select).hasClass('multiselect'), + minimumInputLength: 0, + query: function(query) { + var options = { all_available: all_available, skip_groups: skip_groups }; + return Api.groups(query.term, options, function(groups) { + var data; + data = { + results: groups + }; + return query.callback(data); + }); + }, + initSelection: function(element, callback) { + var id; + id = $(element).val(); + if (id !== "") { + return Api.group(id, callback); } - }); - }; - })(this)); - } + }, + formatResult: function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return _this.formatResult.apply(_this, args); + }, + formatSelection: function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return _this.formatSelection.apply(_this, args); + }, + dropdownCssClass: "ajax-groups-dropdown", + // we do not want to escape markup since we are displaying html in results + escapeMarkup: function(m) { + return m; + } + }); + }; + })(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; + }; - return GroupsSelect; - })(); -}).call(window); + return GroupsSelect; +})(); diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js index a853c3aeb1f..34f44dad7a5 100644 --- a/app/assets/javascripts/header.js +++ b/app/assets/javascripts/header.js @@ -1,8 +1,7 @@ -/* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, no-var, max-len */ -(function() { - $(document).on('todo:toggle', function(e, count) { - var $todoPendingCount = $('.todos-pending-count'); - $todoPendingCount.text(gl.text.highCountTrim(count)); - $todoPendingCount.toggleClass('hidden', count === 0); - }); -})(); +/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var */ + +$(document).on('todo:toggle', function(e, count) { + var $todoPendingCount = $('.todos-pending-count'); + $todoPendingCount.text(gl.text.highCountTrim(count)); + $todoPendingCount.toggleClass('hidden', count === 0); +}); diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 604ed91627a..c078af23ef9 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -206,189 +206,187 @@ import './visibility_select'; import './wikis'; import './zen_mode'; -(function () { - document.addEventListener('beforeunload', function () { - // Unbind scroll events - $(document).off('scroll'); - // Close any open tooltips - $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy'); - }); - - window.addEventListener('hashchange', gl.utils.handleLocationHash); - window.addEventListener('load', function onLoad() { - window.removeEventListener('load', onLoad, false); - gl.utils.handleLocationHash(); - }, false); +document.addEventListener('beforeunload', function () { + // Unbind scroll events + $(document).off('scroll'); + // Close any open tooltips + $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy'); +}); - $(function () { - var $body = $('body'); - var $document = $(document); - var $window = $(window); - var $sidebarGutterToggle = $('.js-sidebar-toggle'); - var $flash = $('.flash-container'); - var bootstrapBreakpoint = bp.getBreakpointSize(); - var fitSidebarForSize; +window.addEventListener('hashchange', gl.utils.handleLocationHash); +window.addEventListener('load', function onLoad() { + window.removeEventListener('load', onLoad, false); + gl.utils.handleLocationHash(); +}, false); - // Set the default path for all cookies to GitLab's root directory - Cookies.defaults.path = gon.relative_url_root || '/'; +$(function () { + var $body = $('body'); + var $document = $(document); + var $window = $(window); + var $sidebarGutterToggle = $('.js-sidebar-toggle'); + var $flash = $('.flash-container'); + var bootstrapBreakpoint = bp.getBreakpointSize(); + var fitSidebarForSize; - // `hashchange` is not triggered when link target is already in window.location - $body.on('click', 'a[href^="#"]', function() { - var href = this.getAttribute('href'); - if (href.substr(1) === gl.utils.getLocationHash()) { - setTimeout(gl.utils.handleLocationHash, 1); - } - }); + // Set the default path for all cookies to GitLab's root directory + Cookies.defaults.path = gon.relative_url_root || '/'; - // prevent default action for disabled buttons - $('.btn').click(function(e) { - if ($(this).hasClass('disabled')) { - e.preventDefault(); - e.stopImmediatePropagation(); - return false; - } - }); + // `hashchange` is not triggered when link target is already in window.location + $body.on('click', 'a[href^="#"]', function() { + var href = this.getAttribute('href'); + if (href.substr(1) === gl.utils.getLocationHash()) { + setTimeout(gl.utils.handleLocationHash, 1); + } + }); - $('.js-select-on-focus').on('focusin', function () { - return $(this).select().one('mouseup', function (e) { - return e.preventDefault(); - }); - // Click a .js-select-on-focus field, select the contents - // Prevent a mouseup event from deselecting the input - }); - $('.remove-row').bind('ajax:success', function () { - $(this).tooltip('destroy') - .closest('li') - .fadeOut(); - }); - $('.js-remove-tr').bind('ajax:before', function () { - return $(this).hide(); - }); - $('.js-remove-tr').bind('ajax:success', function () { - return $(this).closest('tr').fadeOut(); - }); - $('select.select2').select2({ - width: 'resolve', - // Initialize select2 selects - dropdownAutoWidth: true - }); - $('.js-select2').bind('select2-close', function () { - return setTimeout((function () { - $('.select2-container-active').removeClass('select2-container-active'); - return $(':focus').blur(); - }), 1); - // Close select2 on escape - }); - // Initialize tooltips - $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover'; - $body.tooltip({ - selector: '.has-tooltip, [data-toggle="tooltip"]', - placement: function (tip, el) { - return $(el).data('placement') || 'bottom'; - } - }); - $('.trigger-submit').on('change', function () { - return $(this).parents('form').submit(); - // Form submitter - }); - gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true); - // Flash - if ($flash.length > 0) { - $flash.click(function () { - return $(this).fadeOut(); - }); - $flash.show(); + // prevent default action for disabled buttons + $('.btn').click(function(e) { + if ($(this).hasClass('disabled')) { + e.preventDefault(); + e.stopImmediatePropagation(); + return false; } - // Disable form buttons while a form is submitting - $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) { - var buttons; - buttons = $('[type="submit"]', this); - switch (e.type) { - case 'ajax:beforeSend': - case 'submit': - return buttons.disable(); - default: - return buttons.enable(); - } - }); - $(document).ajaxError(function (e, xhrObj) { - var ref = xhrObj.status; - if (xhrObj.status === 401) { - return new Flash('You need to be logged in.', 'alert'); - } else if (ref === 404 || ref === 500) { - return new Flash('Something went wrong on our end.', 'alert'); - } - }); - $('.account-box').hover(function () { - // Show/Hide the profile menu when hovering the account box - return $(this).toggleClass('hover'); - }); - $document.on('click', '.diff-content .js-show-suppressed-diff', function () { - var $container; - $container = $(this).parent(); - $container.next('table').show(); - return $container.remove(); - // Commit show suppressed diff - }); - $('.navbar-toggle').on('click', function () { - $('.header-content .title').toggle(); - $('.header-content .header-logo').toggle(); - $('.header-content .navbar-collapse').toggle(); - return $('.navbar-toggle').toggleClass('active'); - }); - // Show/hide comments on diff - $body.on('click', '.js-toggle-diff-comments', function (e) { - var $this = $(this); - var notesHolders = $this.closest('.diff-file').find('.notes_holder'); - $this.toggleClass('active'); - if ($this.hasClass('active')) { - notesHolders.show().find('.hide, .content').show(); - } else { - notesHolders.hide().find('.content').hide(); - } - $(document).trigger('toggle.comments'); + }); + + $('.js-select-on-focus').on('focusin', function () { + return $(this).select().one('mouseup', function (e) { return e.preventDefault(); }); - $document.off('click', '.js-confirm-danger'); - $document.on('click', '.js-confirm-danger', function (e) { - var btn = $(e.target); - var form = btn.closest('form'); - var text = btn.data('confirm-danger-message'); - e.preventDefault(); - return new ConfirmDangerModal(form, text); - }); - $('input[type="search"]').each(function () { - var $this = $(this); - $this.attr('value', $this.val()); - }); - $document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function () { - var $this; - $this = $(this); - return $this.attr('value', $this.val()); - }); - $document.off('breakpoint:change').on('breakpoint:change', function (e, breakpoint) { - var $gutterIcon; - if (breakpoint === 'sm' || breakpoint === 'xs') { - $gutterIcon = $sidebarGutterToggle.find('i'); - if ($gutterIcon.hasClass('fa-angle-double-right')) { - return $sidebarGutterToggle.trigger('click'); - } - } + // Click a .js-select-on-focus field, select the contents + // Prevent a mouseup event from deselecting the input + }); + $('.remove-row').bind('ajax:success', function () { + $(this).tooltip('destroy') + .closest('li') + .fadeOut(); + }); + $('.js-remove-tr').bind('ajax:before', function () { + return $(this).hide(); + }); + $('.js-remove-tr').bind('ajax:success', function () { + return $(this).closest('tr').fadeOut(); + }); + $('select.select2').select2({ + width: 'resolve', + // Initialize select2 selects + dropdownAutoWidth: true + }); + $('.js-select2').bind('select2-close', function () { + return setTimeout((function () { + $('.select2-container-active').removeClass('select2-container-active'); + return $(':focus').blur(); + }), 1); + // Close select2 on escape + }); + // Initialize tooltips + $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover'; + $body.tooltip({ + selector: '.has-tooltip, [data-toggle="tooltip"]', + placement: function (tip, el) { + return $(el).data('placement') || 'bottom'; + } + }); + $('.trigger-submit').on('change', function () { + return $(this).parents('form').submit(); + // Form submitter + }); + gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true); + // Flash + if ($flash.length > 0) { + $flash.click(function () { + return $(this).fadeOut(); }); - fitSidebarForSize = function () { - var oldBootstrapBreakpoint; - oldBootstrapBreakpoint = bootstrapBreakpoint; - bootstrapBreakpoint = bp.getBreakpointSize(); - if (bootstrapBreakpoint !== oldBootstrapBreakpoint) { - return $document.trigger('breakpoint:change', [bootstrapBreakpoint]); + $flash.show(); + } + // Disable form buttons while a form is submitting + $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) { + var buttons; + buttons = $('[type="submit"]', this); + switch (e.type) { + case 'ajax:beforeSend': + case 'submit': + return buttons.disable(); + default: + return buttons.enable(); + } + }); + $(document).ajaxError(function (e, xhrObj) { + var ref = xhrObj.status; + if (xhrObj.status === 401) { + return new Flash('You need to be logged in.', 'alert'); + } else if (ref === 404 || ref === 500) { + return new Flash('Something went wrong on our end.', 'alert'); + } + }); + $('.account-box').hover(function () { + // Show/Hide the profile menu when hovering the account box + return $(this).toggleClass('hover'); + }); + $document.on('click', '.diff-content .js-show-suppressed-diff', function () { + var $container; + $container = $(this).parent(); + $container.next('table').show(); + return $container.remove(); + // Commit show suppressed diff + }); + $('.navbar-toggle').on('click', function () { + $('.header-content .title').toggle(); + $('.header-content .header-logo').toggle(); + $('.header-content .navbar-collapse').toggle(); + return $('.navbar-toggle').toggleClass('active'); + }); + // Show/hide comments on diff + $body.on('click', '.js-toggle-diff-comments', function (e) { + var $this = $(this); + var notesHolders = $this.closest('.diff-file').find('.notes_holder'); + $this.toggleClass('active'); + if ($this.hasClass('active')) { + notesHolders.show().find('.hide, .content').show(); + } else { + notesHolders.hide().find('.content').hide(); + } + $(document).trigger('toggle.comments'); + return e.preventDefault(); + }); + $document.off('click', '.js-confirm-danger'); + $document.on('click', '.js-confirm-danger', function (e) { + var btn = $(e.target); + var form = btn.closest('form'); + var text = btn.data('confirm-danger-message'); + e.preventDefault(); + return new ConfirmDangerModal(form, text); + }); + $('input[type="search"]').each(function () { + var $this = $(this); + $this.attr('value', $this.val()); + }); + $document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function () { + var $this; + $this = $(this); + return $this.attr('value', $this.val()); + }); + $document.off('breakpoint:change').on('breakpoint:change', function (e, breakpoint) { + var $gutterIcon; + if (breakpoint === 'sm' || breakpoint === 'xs') { + $gutterIcon = $sidebarGutterToggle.find('i'); + if ($gutterIcon.hasClass('fa-angle-double-right')) { + return $sidebarGutterToggle.trigger('click'); } - }; - $window.off('resize.app').on('resize.app', function () { - return fitSidebarForSize(); - }); - gl.awardsHandler = new AwardsHandler(); - new Aside(); - - gl.utils.initTimeagoTimeout(); + } }); -}).call(window); + fitSidebarForSize = function () { + var oldBootstrapBreakpoint; + oldBootstrapBreakpoint = bootstrapBreakpoint; + bootstrapBreakpoint = bp.getBreakpointSize(); + if (bootstrapBreakpoint !== oldBootstrapBreakpoint) { + return $document.trigger('breakpoint:change', [bootstrapBreakpoint]); + } + }; + $window.off('resize.app').on('resize.app', function () { + return fitSidebarForSize(); + }); + gl.awardsHandler = new AwardsHandler(); + new Aside(); + + gl.utils.initTimeagoTimeout(); +}); |