Commit 302d0453 authored by Alfredo Sumaran's avatar Alfredo Sumaran

Merge branch 'remove-iifes-1' into 'master'

Remove IIFEs for several JS files - Part 1

See merge request !9884
parents 18a688ff c8f986fe
/* eslint-disable no-param-reassign */ const MAX_MESSAGE_LENGTH = 500;
const MESSAGE_CELL_SELECTOR = '.abuse-reports .message';
((global) => { class AbuseReports {
const MAX_MESSAGE_LENGTH = 500; constructor() {
const MESSAGE_CELL_SELECTOR = '.abuse-reports .message'; $(MESSAGE_CELL_SELECTOR).each(this.truncateLongMessage);
$(document)
class AbuseReports { .off('click', MESSAGE_CELL_SELECTOR)
constructor() { .on('click', MESSAGE_CELL_SELECTOR, this.toggleMessageTruncation);
$(MESSAGE_CELL_SELECTOR).each(this.truncateLongMessage); }
$(document)
.off('click', MESSAGE_CELL_SELECTOR)
.on('click', MESSAGE_CELL_SELECTOR, this.toggleMessageTruncation);
}
truncateLongMessage() { truncateLongMessage() {
const $messageCellElement = $(this); const $messageCellElement = $(this);
const reportMessage = $messageCellElement.text(); const reportMessage = $messageCellElement.text();
if (reportMessage.length > MAX_MESSAGE_LENGTH) { if (reportMessage.length > MAX_MESSAGE_LENGTH) {
$messageCellElement.data('original-message', reportMessage); $messageCellElement.data('original-message', reportMessage);
$messageCellElement.data('message-truncated', 'true'); $messageCellElement.data('message-truncated', 'true');
$messageCellElement.text(global.text.truncate(reportMessage, MAX_MESSAGE_LENGTH)); $messageCellElement.text(window.gl.text.truncate(reportMessage, MAX_MESSAGE_LENGTH));
}
} }
}
toggleMessageTruncation() { toggleMessageTruncation() {
const $messageCellElement = $(this); const $messageCellElement = $(this);
const originalMessage = $messageCellElement.data('original-message'); const originalMessage = $messageCellElement.data('original-message');
if (!originalMessage) return; if (!originalMessage) return;
if ($messageCellElement.data('message-truncated') === 'true') { if ($messageCellElement.data('message-truncated') === 'true') {
$messageCellElement.data('message-truncated', 'false'); $messageCellElement.data('message-truncated', 'false');
$messageCellElement.text(originalMessage); $messageCellElement.text(originalMessage);
} else { } else {
$messageCellElement.data('message-truncated', 'true'); $messageCellElement.data('message-truncated', 'true');
$messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`); $messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`);
}
} }
} }
}
global.AbuseReports = AbuseReports; window.gl = window.gl || {};
})(window.gl || (window.gl = {})); window.gl.AbuseReports = AbuseReports;
...@@ -2,36 +2,35 @@ ...@@ -2,36 +2,35 @@
/* global Pager */ /* global Pager */
/* global Cookies */ /* global Cookies */
((global) => { class Activities {
class Activities { constructor() {
constructor() { Pager.init(20, true, false, this.updateTooltips);
Pager.init(20, true, false, this.updateTooltips); $('.event-filter-link').on('click', (e) => {
$('.event-filter-link').on('click', (e) => { e.preventDefault();
e.preventDefault(); this.toggleFilter(e.currentTarget);
this.toggleFilter(e.currentTarget); this.reloadActivities();
this.reloadActivities(); });
}); }
}
updateTooltips() { updateTooltips() {
gl.utils.localTimeAgo($('.js-timeago', '.content_list')); gl.utils.localTimeAgo($('.js-timeago', '.content_list'));
} }
reloadActivities() { reloadActivities() {
$('.content_list').html(''); $('.content_list').html('');
Pager.init(20, true, false, this.updateTooltips); Pager.init(20, true, false, this.updateTooltips);
} }
toggleFilter(sender) { toggleFilter(sender) {
const $sender = $(sender); const $sender = $(sender);
const filter = $sender.attr('id').split('_')[0]; const filter = $sender.attr('id').split('_')[0];
$('.event-filter .active').removeClass('active'); $('.event-filter .active').removeClass('active');
Cookies.set('event_filter', filter); 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;
/* 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 */ /* 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() { window.Admin = (function() {
this.Admin = (function() { function Admin() {
function Admin() { var modal, showBlacklistType;
var modal, showBlacklistType; $('input#user_force_random_password').on('change', function(elem) {
$('input#user_force_random_password').on('change', function(elem) { var elems;
var elems; elems = $('#user_password, #user_password_confirmation');
elems = $('#user_password, #user_password_confirmation'); if ($(this).attr('checked')) {
if ($(this).attr('checked')) { return elems.val('').attr('disabled', true);
return elems.val('').attr('disabled', true); } else {
} else { return elems.removeAttr('disabled');
return elems.removeAttr('disabled'); }
} });
}); $('body').on('click', '.js-toggle-colors-link', function(e) {
$('body').on('click', '.js-toggle-colors-link', function(e) { e.preventDefault();
e.preventDefault(); return $('.js-toggle-colors-container').toggle();
return $('.js-toggle-colors-container').toggle(); });
}); $('.log-tabs a').click(function(e) {
$('.log-tabs a').click(function(e) { e.preventDefault();
e.preventDefault(); return $(this).tab('show');
return $(this).tab('show'); });
}); $('.log-bottom').click(function(e) {
$('.log-bottom').click(function(e) { var visible_log;
var visible_log; e.preventDefault();
e.preventDefault(); visible_log = $(".file-content:visible");
visible_log = $(".file-content:visible"); return visible_log.animate({
return visible_log.animate({ scrollTop: visible_log.find('ol').height()
scrollTop: visible_log.find('ol').height() }, "fast");
}, "fast"); });
}); modal = $('.change-owner-holder');
modal = $('.change-owner-holder'); $('.change-owner-link').bind("click", function(e) {
$('.change-owner-link').bind("click", function(e) { e.preventDefault();
e.preventDefault(); $(this).hide();
$(this).hide(); return modal.show();
return modal.show(); });
}); $('.change-owner-cancel-link').bind("click", function(e) {
$('.change-owner-cancel-link').bind("click", function(e) { e.preventDefault();
e.preventDefault(); modal.hide();
modal.hide(); return $('.change-owner-link').show();
return $('.change-owner-link').show(); });
}); $('li.project_member').bind('ajax:success', function() {
$('li.project_member').bind('ajax:success', function() { return gl.utils.refreshCurrentPage();
return gl.utils.refreshCurrentPage(); });
}); $('li.group_member').bind('ajax:success', function() {
$('li.group_member').bind('ajax:success', function() { return gl.utils.refreshCurrentPage();
return gl.utils.refreshCurrentPage(); });
}); showBlacklistType = function() {
showBlacklistType = function() { if ($("input[name='blacklist_type']:checked").val() === 'file') {
if ($("input[name='blacklist_type']:checked").val() === 'file') { $('.blacklist-file').show();
$('.blacklist-file').show(); return $('.blacklist-raw').hide();
return $('.blacklist-raw').hide(); } else {
} else { $('.blacklist-file').hide();
$('.blacklist-file').hide(); return $('.blacklist-raw').show();
return $('.blacklist-raw').show(); }
} };
}; $("input[name='blacklist_type']").click(showBlacklistType);
$("input[name='blacklist_type']").click(showBlacklistType); showBlacklistType();
showBlacklistType(); }
}
return Admin; return Admin;
})(); })();
}).call(window);
/* 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 */ /* 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 = {
var Api = { groupsPath: "/api/:version/groups.json",
groupsPath: "/api/:version/groups.json", groupPath: "/api/:version/groups/:id.json",
groupPath: "/api/:version/groups/:id.json", namespacesPath: "/api/:version/namespaces.json",
namespacesPath: "/api/:version/namespaces.json", groupProjectsPath: "/api/:version/groups/:id/projects.json",
groupProjectsPath: "/api/:version/groups/:id/projects.json", projectsPath: "/api/:version/projects.json?simple=true",
projectsPath: "/api/:version/projects.json?simple=true", labelsPath: "/:namespace_path/:project_path/labels",
labelsPath: "/:namespace_path/:project_path/labels", licensePath: "/api/:version/templates/licenses/:key",
licensePath: "/api/:version/templates/licenses/:key", gitignorePath: "/api/:version/templates/gitignores/:key",
gitignorePath: "/api/:version/templates/gitignores/:key", gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key", dockerfilePath: "/api/:version/templates/dockerfiles/:key",
dockerfilePath: "/api/:version/templates/dockerfiles/:key", issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key", group: function(group_id, callback) {
group: function(group_id, callback) { var url = Api.buildUrl(Api.groupPath)
var url = Api.buildUrl(Api.groupPath) .replace(':id', group_id);
.replace(':id', group_id); return $.ajax({
return $.ajax({ url: url,
url: url, dataType: "json"
dataType: "json" }).done(function(group) {
}).done(function(group) { return callback(group);
return callback(group); });
}); },
}, // Return groups list. Filtered by query
// Return groups list. Filtered by query groups: function(query, options, callback) {
groups: function(query, options, callback) { var url = Api.buildUrl(Api.groupsPath);
var url = Api.buildUrl(Api.groupsPath); return $.ajax({
return $.ajax({ url: url,
url: url, data: $.extend({
data: $.extend({ search: query,
search: query, per_page: 20
per_page: 20 }, options),
}, options), dataType: "json"
dataType: "json" }).done(function(groups) {
}).done(function(groups) { return callback(groups);
return callback(groups); });
}); },
}, // Return namespaces list. Filtered by query
// Return namespaces list. Filtered by query namespaces: function(query, callback) {
namespaces: function(query, callback) { var url = Api.buildUrl(Api.namespacesPath);
var url = Api.buildUrl(Api.namespacesPath); return $.ajax({
return $.ajax({ url: url,
url: url, data: {
data: { search: query,
search: query, per_page: 20
per_page: 20 },
}, dataType: "json"
dataType: "json" }).done(function(namespaces) {
}).done(function(namespaces) { return callback(namespaces);
return callback(namespaces); });
}); },
}, // Return projects list. Filtered by query
// Return projects list. Filtered by query projects: function(query, order, callback) {
projects: function(query, order, callback) { var url = Api.buildUrl(Api.projectsPath);
var url = Api.buildUrl(Api.projectsPath); return $.ajax({
return $.ajax({ url: url,
url: url, data: {
data: { search: query,
search: query, order_by: order,
order_by: order, per_page: 20
per_page: 20 },
}, dataType: "json"
dataType: "json" }).done(function(projects) {
}).done(function(projects) { return callback(projects);
return callback(projects); });
}); },
}, newLabel: function(namespace_path, project_path, data, callback) {
newLabel: function(namespace_path, project_path, data, callback) { var url = Api.buildUrl(Api.labelsPath)
var url = Api.buildUrl(Api.labelsPath) .replace(':namespace_path', namespace_path)
.replace(':namespace_path', namespace_path) .replace(':project_path', project_path);
.replace(':project_path', project_path); return $.ajax({
return $.ajax({ url: url,
url: url, type: "POST",
type: "POST", data: { 'label': data },
data: { 'label': data }, dataType: "json"
dataType: "json" }).done(function(label) {
}).done(function(label) { return callback(label);
return callback(label); }).error(function(message) {
}).error(function(message) { return callback(message.responseJSON);
return callback(message.responseJSON); });
}); },
}, // Return group projects list. Filtered by query
// Return group projects list. Filtered by query groupProjects: function(group_id, query, callback) {
groupProjects: function(group_id, query, callback) { var url = Api.buildUrl(Api.groupProjectsPath)
var url = Api.buildUrl(Api.groupProjectsPath) .replace(':id', group_id);
.replace(':id', group_id); return $.ajax({
return $.ajax({ url: url,
url: url, data: {
data: { search: query,
search: query, per_page: 20
per_page: 20 },
}, dataType: "json"
dataType: "json" }).done(function(projects) {
}).done(function(projects) { return callback(projects);
return callback(projects); });
}); },
}, // Return text for a specific license
// Return text for a specific license licenseText: function(key, data, callback) {
licenseText: function(key, data, callback) { var url = Api.buildUrl(Api.licensePath)
var url = Api.buildUrl(Api.licensePath) .replace(':key', key);
.replace(':key', key); return $.ajax({
return $.ajax({ url: url,
url: url, data: data
data: data }).done(function(license) {
}).done(function(license) { return callback(license);
return callback(license); });
}); },
}, gitignoreText: function(key, callback) {
gitignoreText: function(key, callback) { var url = Api.buildUrl(Api.gitignorePath)
var url = Api.buildUrl(Api.gitignorePath) .replace(':key', key);
.replace(':key', key); return $.get(url, function(gitignore) {
return $.get(url, function(gitignore) { return callback(gitignore);
return callback(gitignore); });
}); },
}, gitlabCiYml: function(key, callback) {
gitlabCiYml: function(key, callback) { var url = Api.buildUrl(Api.gitlabCiYmlPath)
var url = Api.buildUrl(Api.gitlabCiYmlPath) .replace(':key', key);
.replace(':key', key); return $.get(url, function(file) {
return $.get(url, function(file) { return callback(file);
return callback(file); });
}); },
}, dockerfileYml: function(key, callback) {
dockerfileYml: function(key, callback) { var url = Api.buildUrl(Api.dockerfilePath).replace(':key', key);
var url = Api.buildUrl(Api.dockerfilePath).replace(':key', key); $.get(url, callback);
$.get(url, callback); },
}, issueTemplate: function(namespacePath, projectPath, key, type, callback) {
issueTemplate: function(namespacePath, projectPath, key, type, callback) { var url = Api.buildUrl(Api.issuableTemplatePath)
var url = Api.buildUrl(Api.issuableTemplatePath) .replace(':key', key)
.replace(':key', key) .replace(':type', type)
.replace(':type', type) .replace(':project_path', projectPath)
.replace(':project_path', projectPath) .replace(':namespace_path', namespacePath);
.replace(':namespace_path', namespacePath); $.ajax({
$.ajax({ url: url,
url: url, dataType: 'json'
dataType: 'json' }).done(function(file) {
}).done(function(file) { callback(null, file);
callback(null, file); }).error(callback);
}).error(callback); },
}, buildUrl: function(url) {
buildUrl: function(url) { if (gon.relative_url_root != null) {
if (gon.relative_url_root != null) { url = gon.relative_url_root + url;
url = gon.relative_url_root + url;
}
return url.replace(':version', gon.api_version);
} }
}; return url.replace(':version', gon.api_version);
}
};
window.Api = Api; window.Api = Api;
}).call(window);
/* 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 */ /* 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; window.Aside = (function() {
})(); function Aside() {
}).call(window); $(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;
})();
/* 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 */ /* 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() { window.Autosave = (function() {
var e, text; function Autosave(field, key) {
if (window.localStorage == null) { this.field = field;
return; if (key.join != null) {
} key = key.join("/");
try { }
text = window.localStorage.getItem(this.key); this.key = "autosave/" + key;
} catch (error) { this.field.data("autosave", this);
e = error; this.restore();
return; this.field.on("input", (function(_this) {
} return function() {
if ((text != null ? text.length : void 0) > 0) { return _this.save();
this.field.val(text); };
} })(this));
return this.field.trigger("input"); }
};
Autosave.prototype.save = function() { Autosave.prototype.restore = function() {
var text; var e, text;
if (window.localStorage == null) { if (window.localStorage == null) {
return; return;
} }
text = this.field.val(); try {
if ((text != null ? text.length : void 0) > 0) { text = window.localStorage.getItem(this.key);
try { } catch (error) {
return window.localStorage.setItem(this.key, text); e = error;
} catch (error) {} return;
} else { }
return this.reset(); if ((text != null ? text.length : void 0) > 0) {
} this.field.val(text);
}; }
return this.field.trigger("input");
};
Autosave.prototype.reset = function() { Autosave.prototype.save = function() {
if (window.localStorage == null) { var text;
return; if (window.localStorage == null) {
} return;
}
text = this.field.val();
if ((text != null ? text.length : void 0) > 0) {
try { try {
return window.localStorage.removeItem(this.key); return window.localStorage.setItem(this.key, text);
} catch (error) {} } 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; return Autosave;
})(); })();
}).call(window);
/* 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 */ /* 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 Breakpoints = (function() { var BreakpointInstance, instance;
var BreakpointInstance, instance;
function Breakpoints() {} function Breakpoints() {}
instance = null; instance = null;
BreakpointInstance = (function() { BreakpointInstance = (function() {
var BREAKPOINTS; var BREAKPOINTS;
BREAKPOINTS = ["xs", "sm", "md", "lg"]; BREAKPOINTS = ["xs", "sm", "md", "lg"];
function BreakpointInstance() { function BreakpointInstance() {
this.setup(); this.setup();
} }
BreakpointInstance.prototype.setup = function() {
var allDeviceSelector, els;
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
return ".device-" + breakpoint;
});
if ($(allDeviceSelector.join(",")).length) {
return;
}
// Create all the elements
els = $.map(BREAKPOINTS, function(breakpoint) {
return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>";
});
return $("body").append(els.join(''));
};
BreakpointInstance.prototype.visibleDevice = function() { BreakpointInstance.prototype.setup = function() {
var allDeviceSelector; var allDeviceSelector, els;
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) { allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
return ".device-" + breakpoint; return ".device-" + breakpoint;
}); });
return $(allDeviceSelector.join(",")).filter(":visible"); if ($(allDeviceSelector.join(",")).length) {
}; return;
}
BreakpointInstance.prototype.getBreakpointSize = function() { // Create all the elements
var $visibleDevice; els = $.map(BREAKPOINTS, function(breakpoint) {
$visibleDevice = this.visibleDevice; return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>";
// TODO: Consider refactoring in light of turbolinks removal. });
// the page refreshed via turbolinks return $("body").append(els.join(''));
if (!$visibleDevice().length) { };
this.setup();
}
$visibleDevice = this.visibleDevice();
return $visibleDevice.attr("class").split("visible-")[1];
};
return BreakpointInstance; BreakpointInstance.prototype.visibleDevice = function() {
})(); var allDeviceSelector;
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
return ".device-" + breakpoint;
});
return $(allDeviceSelector.join(",")).filter(":visible");
};
Breakpoints.get = function() { BreakpointInstance.prototype.getBreakpointSize = function() {
return instance != null ? instance : instance = new BreakpointInstance; 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) { Breakpoints.get = function() {
return function() { return instance != null ? instance : instance = new BreakpointInstance;
return _this.bp = Breakpoints.get(); };
};
})(this)); return Breakpoints;
})();
$(() => { window.bp = Breakpoints.get(); });
window.Breakpoints = Breakpoints; window.Breakpoints = Breakpoints;
}).call(window);
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, no-else-return, object-shorthand, comma-dangle, max-len */ /* 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() { $(function() {
var previewPath; var previewPath;
$('input#broadcast_message_color').on('input', function() { $('input#broadcast_message_color').on('input', function() {
var previewColor; var previewColor;
previewColor = $(this).val(); previewColor = $(this).val();
return $('div.broadcast-message-preview').css('background-color', previewColor); return $('div.broadcast-message-preview').css('background-color', previewColor);
}); });
$('input#broadcast_message_font').on('input', function() { $('input#broadcast_message_font').on('input', function() {
var previewColor; var previewColor;
previewColor = $(this).val(); previewColor = $(this).val();
return $('div.broadcast-message-preview').css('color', previewColor); return $('div.broadcast-message-preview').css('color', previewColor);
}); });
previewPath = $('textarea#broadcast_message_message').data('preview-path'); previewPath = $('textarea#broadcast_message_message').data('preview-path');
return $('textarea#broadcast_message_message').on('input', function() { return $('textarea#broadcast_message_message').on('input', function() {
var message; var message;
message = $(this).val(); message = $(this).val();
if (message === '') { if (message === '') {
return $('.js-broadcast-message-preview').text("Your message here"); return $('.js-broadcast-message-preview').text("Your message here");
} else { } else {
return $.ajax({ return $.ajax({
url: previewPath, url: previewPath,
type: "POST", type: "POST",
data: { data: {
broadcast_message: { broadcast_message: {
message: message message: message
}
} }
}); }
} });
}); }
}); });
}).call(window); });
/* 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 */ /* 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 */ /* global Breakpoints */
(function() { var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }; var AUTO_SCROLL_OFFSET = 75;
var AUTO_SCROLL_OFFSET = 75; var DOWN_BUILD_TRACE = '#down-build-trace';
var DOWN_BUILD_TRACE = '#down-build-trace';
window.Build = (function() {
this.Build = (function() { Build.timeout = null;
Build.timeout = null;
Build.state = null;
Build.state = null;
function Build(options) {
function Build(options) { options = options || $('.js-build-options').data();
options = options || $('.js-build-options').data(); this.pageUrl = options.pageUrl;
this.pageUrl = options.pageUrl; this.buildUrl = options.buildUrl;
this.buildUrl = options.buildUrl; this.buildStatus = options.buildStatus;
this.buildStatus = options.buildStatus; this.state = options.logState;
this.state = options.logState; this.buildStage = options.buildStage;
this.buildStage = options.buildStage; this.updateDropdown = bind(this.updateDropdown, this);
this.updateDropdown = bind(this.updateDropdown, this); this.$document = $(document);
this.$document = $(document); this.$body = $('body');
this.$body = $('body'); this.$buildTrace = $('#build-trace');
this.$buildTrace = $('#build-trace'); this.$autoScrollContainer = $('.autoscroll-container');
this.$autoScrollContainer = $('.autoscroll-container'); this.$autoScrollStatus = $('#autoscroll-status');
this.$autoScrollStatus = $('#autoscroll-status'); this.$autoScrollStatusText = this.$autoScrollStatus.find('.status-text');
this.$autoScrollStatusText = this.$autoScrollStatus.find('.status-text'); this.$upBuildTrace = $('#up-build-trace');
this.$upBuildTrace = $('#up-build-trace'); this.$downBuildTrace = $(DOWN_BUILD_TRACE);
this.$downBuildTrace = $(DOWN_BUILD_TRACE); this.$scrollTopBtn = $('#scroll-top');
this.$scrollTopBtn = $('#scroll-top'); this.$scrollBottomBtn = $('#scroll-bottom');
this.$scrollBottomBtn = $('#scroll-bottom'); this.$buildRefreshAnimation = $('.js-build-refresh');
this.$buildRefreshAnimation = $('.js-build-refresh');
clearTimeout(Build.timeout);
clearTimeout(Build.timeout); // Init breakpoint checker
// Init breakpoint checker this.bp = Breakpoints.get();
this.bp = Breakpoints.get();
this.initSidebar();
this.initSidebar(); this.$buildScroll = $('#js-build-scroll');
this.$buildScroll = $('#js-build-scroll');
this.populateJobs(this.buildStage);
this.populateJobs(this.buildStage); this.updateStageDropdownText(this.buildStage);
this.updateStageDropdownText(this.buildStage); this.sidebarOnResize();
this.sidebarOnResize();
this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this));
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.off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown); this.$document.on('scroll', this.initScrollMonitor.bind(this));
this.$document.on('scroll', this.initScrollMonitor.bind(this)); $(window).off('resize.build').on('resize.build', this.sidebarOnResize.bind(this));
$(window).off('resize.build').on('resize.build', this.sidebarOnResize.bind(this)); $('a', this.$buildScroll).off('click.stepTrace').on('click.stepTrace', this.stepTrace);
$('a', this.$buildScroll).off('click.stepTrace').on('click.stepTrace', this.stepTrace); this.updateArtifactRemoveDate();
this.updateArtifactRemoveDate(); if ($('#build-trace').length) {
if ($('#build-trace').length) { this.getInitialBuildTrace();
this.getInitialBuildTrace(); this.initScrollButtonAffix();
this.initScrollButtonAffix();
}
this.invokeBuildTrace();
} }
this.invokeBuildTrace();
Build.prototype.initSidebar = function() { }
this.$sidebar = $('.js-build-sidebar');
this.$sidebar.niceScroll(); Build.prototype.initSidebar = function() {
this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar); 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.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 Build.prototype.invokeBuildTrace = function() {
if (continueRefreshStatuses.indexOf(this.buildStatus) !== -1) { var continueRefreshStatuses = ['running', 'pending'];
// Check for new build output if user still watching build page // Continue to update build trace when build is running or pending
// Only valid for runnig build when output changes during time if (continueRefreshStatuses.indexOf(this.buildStatus) !== -1) {
Build.timeout = setTimeout((function(_this) { // Check for new build output if user still watching build page
return function() { // Only valid for runnig build when output changes during time
if (_this.location() === _this.pageUrl) { Build.timeout = setTimeout((function(_this) {
return _this.getBuildTrace(); 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());
} }
if (removeRefreshStatuses.indexOf(buildData.status) !== -1) { };
this.$buildRefreshAnimation.remove(); })(this), 4000);
return this.initScrollMonitor(); }
};
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) _this.invokeBuildTrace();
}); if (log.status === "running") {
}; if (log.append) {
$('.js-build-output').append(log.html);
Build.prototype.getBuildTrace = function() { } else {
return $.ajax({ $('.js-build-output').html(log.html);
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(); return _this.checkAutoscroll();
if (log.status === "running") { } else if (log.status !== _this.buildStatus) {
if (log.append) { pageUrl = _this.pageUrl;
$('.js-build-output').append(log.html); if (_this.$autoScrollStatus.data('state') === 'enabled') {
} else { pageUrl += DOWN_BUILD_TRACE;
$('.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);
} }
};
})(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() { return gl.utils.visitUrl(pageUrl);
// Hide everything initially }
this.$scrollTopBtn.hide(); };
this.$scrollBottomBtn.hide(); })(this)
this.$autoScrollContainer.hide(); });
}; };
// Page scroll listener to detect if user has scrolling page Build.prototype.checkAutoscroll = function() {
// and handle following cases if (this.$autoScrollStatus.data("state") === "enabled") {
// 1) User is at Top of Build Log; return $("html,body").scrollTop(this.$buildTrace.height());
// - Hide Top Arrow button }
// - Show Bottom Arrow button
// - Disable Autoscroll and hide indicator (when build is running)
// 2) User is at Bottom of Build Log;
// - Show Top Arrow button
// - Hide Bottom Arrow button
// - Enable Autoscroll and show indicator (when build is running)
// 3) User is somewhere in middle of Build Log;
// - Show Top Arrow button
// - Show Bottom Arrow button
// - Disable Autoscroll and hide indicator (when build is running)
Build.prototype.initScrollMonitor = function() {
if (!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
// User is somewhere in middle of Build Log
this.$scrollTopBtn.show();
if (this.buildStatus === 'success' || this.buildStatus === 'failed') { // Check if Build is completed
this.$scrollBottomBtn.show();
} else if (this.$buildRefreshAnimation.is(':visible') && !gl.utils.isInViewport(this.$buildRefreshAnimation.get(0))) {
this.$scrollBottomBtn.show();
} else {
this.$scrollBottomBtn.hide();
}
// Hide Autoscroll Status Indicator
if (this.$scrollBottomBtn.is(':visible')) {
this.$autoScrollContainer.hide();
this.$autoScrollStatusText.removeClass('animate');
} else {
this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show();
this.$autoScrollStatusText.addClass('animate');
}
} else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
// User is at Top of Build Log
this.$scrollTopBtn.hide(); // 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(); this.$scrollBottomBtn.show();
} else {
this.$scrollBottomBtn.hide();
}
// Hide Autoscroll Status Indicator
if (this.$scrollBottomBtn.is(':visible')) {
this.$autoScrollContainer.hide(); this.$autoScrollContainer.hide();
this.$autoScrollStatusText.removeClass('animate'); this.$autoScrollStatusText.removeClass('animate');
} else if ((!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) || } else {
(this.$buildRefreshAnimation.is(':visible') && gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)))) {
// User is at Bottom of Build Log
this.$scrollTopBtn.show();
this.$scrollBottomBtn.hide();
// Show and Reposition Autoscroll Status Indicator
this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show(); this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show();
this.$autoScrollStatusText.addClass('animate'); 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.$scrollTopBtn.hide();
this.$scrollBottomBtn.hide(); this.$scrollBottomBtn.show();
// Hide Autoscroll Status Indicator this.$autoScrollContainer.hide();
this.$autoScrollContainer.hide(); this.$autoScrollStatusText.removeClass('animate');
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") { this.$scrollTopBtn.show();
// Check if Refresh Animation is in Viewport and enable Autoscroll, disable otherwise. this.$scrollBottomBtn.hide();
this.$autoScrollStatus.data("state", gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)) ? 'enabled' : 'disabled');
} // Show and Reposition Autoscroll Status Indicator
}; this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show();
this.$autoScrollStatusText.addClass('animate');
Build.prototype.shouldHideSidebarForViewport = function() { } else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
var bootstrapBreakpoint; // Build Log height is small
bootstrapBreakpoint = this.bp.getBreakpointSize();
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm'; this.$scrollTopBtn.hide();
}; this.$scrollBottomBtn.hide();
Build.prototype.toggleSidebar = function(shouldHide) { // Hide Autoscroll Status Indicator
var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined; this.$autoScrollContainer.hide();
this.$buildScroll.toggleClass('sidebar-expanded', shouldShow) this.$autoScrollStatusText.removeClass('animate');
.toggleClass('sidebar-collapsed', shouldHide); }
this.$sidebar.toggleClass('right-sidebar-expanded', shouldShow)
.toggleClass('right-sidebar-collapsed', shouldHide); 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.sidebarOnResize = function() { }
this.toggleSidebar(this.shouldHideSidebarForViewport()); };
};
Build.prototype.shouldHideSidebarForViewport = function() {
Build.prototype.sidebarOnClick = function() { var bootstrapBreakpoint;
if (this.shouldHideSidebarForViewport()) this.toggleSidebar(); bootstrapBreakpoint = this.bp.getBreakpointSize();
}; return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
};
Build.prototype.updateArtifactRemoveDate = function() {
var $date, date; Build.prototype.toggleSidebar = function(shouldHide) {
$date = $('.js-artifacts-remove'); var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
if ($date.length) { this.$buildScroll.toggleClass('sidebar-expanded', shouldShow)
date = $date.text(); .toggleClass('sidebar-collapsed', shouldHide);
return $date.text(gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' ')); this.$sidebar.toggleClass('right-sidebar-expanded', shouldShow)
} .toggleClass('right-sidebar-collapsed', shouldHide);
}; };
Build.prototype.populateJobs = function(stage) { Build.prototype.sidebarOnResize = function() {
$('.build-job').hide(); this.toggleSidebar(this.shouldHideSidebarForViewport());
$('.build-job[data-stage="' + stage + '"]').show(); };
};
Build.prototype.sidebarOnClick = function() {
Build.prototype.updateStageDropdownText = function(stage) { if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
$('.stage-selection').text(stage); };
};
Build.prototype.updateArtifactRemoveDate = function() {
Build.prototype.updateDropdown = function(e) { var $date, date;
e.preventDefault(); $date = $('.js-artifacts-remove');
var stage = e.currentTarget.text; if ($date.length) {
this.updateStageDropdownText(stage); date = $date.text();
this.populateJobs(stage); return $date.text(gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '));
}; }
};
Build.prototype.stepTrace = function(e) {
var $currentTarget; Build.prototype.populateJobs = function(stage) {
e.preventDefault(); $('.build-job').hide();
$currentTarget = $(e.currentTarget); $('.build-job[data-stage="' + stage + '"]').show();
$.scrollTo($currentTarget.attr('href'), { };
offset: 0
}); Build.prototype.updateStageDropdownText = function(stage) {
}; $('.stage-selection').text(stage);
};
return Build;
})(); Build.prototype.updateDropdown = function(e) {
}).call(window); 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;
})();
/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, no-return-assign, max-len */ /* 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() { window.BuildArtifacts = (function() {
$('.top-block').on('click', '.download', function(e) { function BuildArtifacts() {
return e.stopPropagation(); this.disablePropagation();
}); this.setupEntryClick();
return $('.tree-holder').on('click', 'tr[data-link] a', function(e) { }
return e.stopImmediatePropagation();
});
};
BuildArtifacts.prototype.setupEntryClick = function() { BuildArtifacts.prototype.disablePropagation = function() {
return $('.tree-holder').on('click', 'tr[data-link]', function(e) { $('.top-block').on('click', '.download', function(e) {
return window.location = this.dataset.link; return e.stopPropagation();
}); });
}; return $('.tree-holder').on('click', 'tr[data-link] a', function(e) {
return e.stopImmediatePropagation();
});
};
return BuildArtifacts; BuildArtifacts.prototype.setupEntryClick = function() {
})(); return $('.tree-holder').on('click', 'tr[data-link]', function(e) {
}).call(window); return window.location = this.dataset.link;
});
};
return BuildArtifacts;
})();
(() => {
window.gl = window.gl || {};
class CILintEditor { window.gl = window.gl || {};
constructor() {
this.editor = window.ace.edit('ci-editor');
this.textarea = document.querySelector('#content');
this.editor.getSession().setMode('ace/mode/yaml'); class CILintEditor {
this.editor.on('input', () => { constructor() {
const content = this.editor.getSession().getValue(); this.editor = window.ace.edit('ci-editor');
this.textarea.value = content; 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;
})();
/* eslint-disable func-names, space-before-function-paren, wrap-iife */ /* eslint-disable func-names, space-before-function-paren, wrap-iife */
/* global CommitFile */ /* global CommitFile */
(function() { window.Commit = (function() {
this.Commit = (function() { function Commit() {
function Commit() { $('.files .diff-file').each(function() {
$('.files .diff-file').each(function() { return new CommitFile(this);
return new CommitFile(this); });
}); }
}
return Commit; return Commit;
})(); })();
}).call(window);
/* 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 */ /* 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 */ /* global Pager */
(function() { window.CommitsList = (function() {
this.CommitsList = (function() { var CommitsList = {};
var CommitsList = {};
CommitsList.timer = null; CommitsList.timer = null;
CommitsList.init = function(limit) { CommitsList.init = function(limit) {
$("body").on("click", ".day-commits-table li.commit", function(e) { $("body").on("click", ".day-commits-table li.commit", function(e) {
if (e.target.nodeName !== "A") { if (e.target.nodeName !== "A") {
location.href = $(this).attr("url"); location.href = $(this).attr("url");
e.stopPropagation(); e.stopPropagation();
return false; return false;
} }
}); });
Pager.init(limit, false, false, function() { Pager.init(limit, false, false, function() {
gl.utils.localTimeAgo($('.js-timeago')); gl.utils.localTimeAgo($('.js-timeago'));
}); });
this.content = $("#commits-list"); this.content = $("#commits-list");
this.searchField = $("#commits-search"); this.searchField = $("#commits-search");
this.lastSearch = this.searchField.val(); this.lastSearch = this.searchField.val();
return this.initSearch(); return this.initSearch();
}; };
CommitsList.initSearch = function() { CommitsList.initSearch = function() {
this.timer = null; this.timer = null;
return this.searchField.keyup((function(_this) { return this.searchField.keyup((function(_this) {
return function() { return function() {
clearTimeout(_this.timer); clearTimeout(_this.timer);
return _this.timer = setTimeout(_this.filterResults, 500); return _this.timer = setTimeout(_this.filterResults, 500);
}; };
})(this)); })(this));
}; };
CommitsList.filterResults = function() { CommitsList.filterResults = function() {
var commitsUrl, form, search; var commitsUrl, form, search;
form = $(".commits-search-form"); form = $(".commits-search-form");
search = CommitsList.searchField.val(); search = CommitsList.searchField.val();
if (search === CommitsList.lastSearch) return; if (search === CommitsList.lastSearch) return;
commitsUrl = form.attr("action") + '?' + form.serialize(); commitsUrl = form.attr("action") + '?' + form.serialize();
CommitsList.content.fadeTo('fast', 0.5); CommitsList.content.fadeTo('fast', 0.5);
return $.ajax({ return $.ajax({
type: "GET", type: "GET",
url: form.attr("action"), url: form.attr("action"),
data: form.serialize(), data: form.serialize(),
complete: function() { complete: function() {
return CommitsList.content.fadeTo('fast', 1.0); return CommitsList.content.fadeTo('fast', 1.0);
}, },
success: function(data) { success: function(data) {
CommitsList.lastSearch = search; CommitsList.lastSearch = search;
CommitsList.content.html(data.html); CommitsList.content.html(data.html);
return history.replaceState({ return history.replaceState({
page: commitsUrl page: commitsUrl
// Change url so if user reload a page - search results are saved // Change url so if user reload a page - search results are saved
}, document.title, commitsUrl); }, document.title, commitsUrl);
}, },
error: function() { error: function() {
CommitsList.lastSearch = null; CommitsList.lastSearch = null;
}, },
dataType: "json" dataType: "json"
}); });
}; };
return CommitsList; return CommitsList;
})(); })();
}).call(window);
/* 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 */ /* 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() { window.Compare = (function() {
function Compare(opts) { function Compare(opts) {
this.opts = opts; this.opts = opts;
this.source_loading = $(".js-source-loading"); this.source_loading = $(".js-source-loading");
this.target_loading = $(".js-target-loading"); this.target_loading = $(".js-target-loading");
$('.js-compare-dropdown').each((function(_this) { $('.js-compare-dropdown').each((function(_this) {
return function(i, dropdown) { return function(i, dropdown) {
var $dropdown; var $dropdown;
$dropdown = $(dropdown); $dropdown = $(dropdown);
return $dropdown.glDropdown({ return $dropdown.glDropdown({
selectable: true, selectable: true,
fieldName: $dropdown.data('field-name'), fieldName: $dropdown.data('field-name'),
filterable: true, filterable: true,
id: function(obj, $el) { id: function(obj, $el) {
return $el.data('id'); return $el.data('id');
}, },
toggleLabel: function(obj, $el) { toggleLabel: function(obj, $el) {
return $el.text().trim(); return $el.text().trim();
}, },
clicked: function(e, el) { clicked: function(e, el) {
if ($dropdown.is('.js-target-branch')) { if ($dropdown.is('.js-target-branch')) {
return _this.getTargetHtml(); return _this.getTargetHtml();
} else if ($dropdown.is('.js-source-branch')) { } else if ($dropdown.is('.js-source-branch')) {
return _this.getSourceHtml(); return _this.getSourceHtml();
} else if ($dropdown.is('.js-target-project')) { } else if ($dropdown.is('.js-target-project')) {
return _this.getTargetProject(); return _this.getTargetProject();
}
} }
}); }
}; });
})(this)); };
this.initialState(); })(this));
} this.initialState();
}
Compare.prototype.initialState = function() { Compare.prototype.initialState = function() {
this.getSourceHtml(); this.getSourceHtml();
return this.getTargetHtml(); return this.getTargetHtml();
}; };
Compare.prototype.getTargetProject = function() { Compare.prototype.getTargetProject = function() {
return $.ajax({ return $.ajax({
url: this.opts.targetProjectUrl, url: this.opts.targetProjectUrl,
data: { data: {
target_project_id: $("input[name='merge_request[target_project_id]']").val() target_project_id: $("input[name='merge_request[target_project_id]']").val()
}, },
beforeSend: function() { beforeSend: function() {
return $('.mr_target_commit').empty(); return $('.mr_target_commit').empty();
}, },
success: function(html) { success: function(html) {
return $('.js-target-branch-dropdown .dropdown-content').html(html); return $('.js-target-branch-dropdown .dropdown-content').html(html);
} }
}); });
}; };
Compare.prototype.getSourceHtml = function() { Compare.prototype.getSourceHtml = function() {
return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', { return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', {
ref: $("input[name='merge_request[source_branch]']").val() ref: $("input[name='merge_request[source_branch]']").val()
}); });
}; };
Compare.prototype.getTargetHtml = function() { Compare.prototype.getTargetHtml = function() {
return this.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', { return this.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', {
target_project_id: $("input[name='merge_request[target_project_id]']").val(), target_project_id: $("input[name='merge_request[target_project_id]']").val(),
ref: $("input[name='merge_request[target_branch]']").val() ref: $("input[name='merge_request[target_branch]']").val()
}); });
}; };
Compare.prototype.sendAjax = function(url, loading, target, data) { Compare.prototype.sendAjax = function(url, loading, target, data) {
var $target; var $target;
$target = $(target); $target = $(target);
return $.ajax({ return $.ajax({
url: url, url: url,
data: data, data: data,
beforeSend: function() { beforeSend: function() {
loading.show(); loading.show();
return $target.empty(); return $target.empty();
}, },
success: function(html) { success: function(html) {
loading.hide(); loading.hide();
$target.html(html); $target.html(html);
var className = '.' + $target[0].className.replace(' ', '.'); var className = '.' + $target[0].className.replace(' ', '.');
gl.utils.localTimeAgo($('.js-timeago', className)); gl.utils.localTimeAgo($('.js-timeago', className));
} }
}); });
}; };
return Compare; return Compare;
})(); })();
}).call(window);
/* 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 */ /* 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() { window.CompareAutocomplete = (function() {
this.CompareAutocomplete = (function() { function CompareAutocomplete() {
function CompareAutocomplete() { this.initDropdown();
this.initDropdown(); }
}
CompareAutocomplete.prototype.initDropdown = function() { CompareAutocomplete.prototype.initDropdown = function() {
return $('.js-compare-dropdown').each(function() { return $('.js-compare-dropdown').each(function() {
var $dropdown, selected; var $dropdown, selected;
$dropdown = $(this); $dropdown = $(this);
selected = $dropdown.data('selected'); selected = $dropdown.data('selected');
const $dropdownContainer = $dropdown.closest('.dropdown'); const $dropdownContainer = $dropdown.closest('.dropdown');
const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer); const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer);
const $filterInput = $('input[type="search"]', $dropdownContainer); const $filterInput = $('input[type="search"]', $dropdownContainer);
$dropdown.glDropdown({ $dropdown.glDropdown({
data: function(term, callback) { data: function(term, callback) {
return $.ajax({ return $.ajax({
url: $dropdown.data('refs-url'), url: $dropdown.data('refs-url'),
data: { data: {
ref: $dropdown.data('ref') 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);
} }
}, }).done(function(refs) {
id: function(obj, $el) { return callback(refs);
return $el.attr('data-ref'); });
}, },
toggleLabel: function(obj, $el) { selectable: true,
return $el.text().trim(); 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) => { id: function(obj, $el) {
const keyCode = e.keyCode || e.which; return $el.attr('data-ref');
if (keyCode !== 13) return; },
const text = $filterInput.val(); toggleLabel: function(obj, $el) {
$fieldInput.val(text); return $el.text().trim();
$('.dropdown-toggle-text', $dropdown).text(text); }
$dropdownContainer.removeClass('open'); });
}); $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) => { $dropdownContainer.on('click', '.dropdown-content a', (e) => {
$dropdown.prop('title', e.target.text.replace(/_+?/g, '-')); $dropdown.prop('title', e.target.text.replace(/_+?/g, '-'));
if ($dropdown.hasClass('has-tooltip')) { if ($dropdown.hasClass('has-tooltip')) {
$dropdown.tooltip('fixTitle'); $dropdown.tooltip('fixTitle');
} }
});
}); });
}; });
};
return CompareAutocomplete; return CompareAutocomplete;
})(); })();
}).call(window);
/* 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 */ /* 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; window.ConfirmDangerModal = (function() {
})(); function ConfirmDangerModal(form, text) {
}).call(window); 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;
})();
/* 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 */ /* 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'); require('./lib/utils/common_utils');
(() => { const gfmRules = {
const gfmRules = { // The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert
// The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert // GitLab Flavored Markdown (GFM) to HTML.
// GitLab Flavored Markdown (GFM) to HTML. // These handlers consequently convert that same HTML to GFM to be copied to the clipboard.
// 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
// Every filter in lib/banzai/pipeline/gfm_pipeline.rb that generates HTML // from GFM should have a handler here, in reverse order.
// 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.
// The GFM-to-HTML-to-GFM cycle is tested in spec/features/copy_as_gfm_spec.rb. InlineDiffFilter: {
InlineDiffFilter: { 'span.idiff.addition'(el, text) {
'span.idiff.addition'(el, text) { return `{+${text}+}`;
return `{+${text}+}`; },
}, 'span.idiff.deletion'(el, text) {
'span.idiff.deletion'(el, text) { return `{-${text}-}`;
return `{-${text}-}`; },
}, },
}, TaskListFilter: {
TaskListFilter: { 'input[type=checkbox].task-list-item-checkbox'(el, text) {
'input[type=checkbox].task-list-item-checkbox'(el, text) { return `[${el.checked ? 'x' : ' '}]`;
return `[${el.checked ? 'x' : ' '}]`; },
}, },
}, ReferenceFilter: {
ReferenceFilter: { '.tooltip'(el, text) {
'.tooltip'(el, text) { return '';
return ''; },
}, 'a.gfm:not([data-link=true])'(el, text) {
'a.gfm:not([data-link=true])'(el, text) { return el.dataset.original || text;
return el.dataset.original || text; },
}, },
}, AutolinkFilter: {
AutolinkFilter: { 'a'(el, text) {
'a'(el, text) { // Fallback on the regular MarkdownFilter's `a` handler.
// Fallback on the regular MarkdownFilter's `a` handler. if (text !== el.getAttribute('href')) return false;
if (text !== el.getAttribute('href')) return false;
return text;
return text; },
}, },
}, TableOfContentsFilter: {
TableOfContentsFilter: { 'ul.section-nav'(el, text) {
'ul.section-nav'(el, text) { return '[[_TOC_]]';
return '[[_TOC_]]'; },
}, },
}, EmojiFilter: {
EmojiFilter: { 'img.emoji'(el, text) {
'img.emoji'(el, text) { return el.getAttribute('alt');
return el.getAttribute('alt'); },
}, 'gl-emoji'(el, text) {
'gl-emoji'(el, text) { return `:${el.getAttribute('data-name')}:`;
return `:${el.getAttribute('data-name')}:`; },
}, },
}, ImageLinkFilter: {
ImageLinkFilter: { 'a.no-attachment-icon'(el, text) {
'a.no-attachment-icon'(el, text) { return text;
return text; },
}, },
}, VideoLinkFilter: {
VideoLinkFilter: { '.video-container'(el, text) {
'.video-container'(el, text) { const videoEl = el.querySelector('video');
const videoEl = el.querySelector('video'); if (!videoEl) return false;
if (!videoEl) return false;
return CopyAsGFM.nodeToGFM(videoEl);
return CopyAsGFM.nodeToGFM(videoEl); },
}, 'video'(el, text) {
'video'(el, text) { return `![${el.dataset.title}](${el.getAttribute('src')})`;
return `![${el.dataset.title}](${el.getAttribute('src')})`; },
}, },
}, MathFilter: {
MathFilter: { 'pre.code.math[data-math-style=display]'(el, text) {
'pre.code.math[data-math-style=display]'(el, text) { return `\`\`\`math\n${text.trim()}\n\`\`\``;
return `\`\`\`math\n${text.trim()}\n\`\`\``; },
}, 'code.code.math[data-math-style=inline]'(el, text) {
'code.code.math[data-math-style=inline]'(el, text) { return `$\`${text}\`$`;
return `$\`${text}\`$`; },
}, 'span.katex-display span.katex-mathml'(el, text) {
'span.katex-display span.katex-mathml'(el, text) { const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]'); if (!mathAnnotation) return false;
if (!mathAnnotation) return false;
return `\`\`\`math\n${CopyAsGFM.nodeToGFM(mathAnnotation)}\n\`\`\``;
return `\`\`\`math\n${CopyAsGFM.nodeToGFM(mathAnnotation)}\n\`\`\``; },
}, 'span.katex-mathml'(el, text) {
'span.katex-mathml'(el, text) { const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]'); if (!mathAnnotation) return false;
if (!mathAnnotation) return false;
return `$\`${CopyAsGFM.nodeToGFM(mathAnnotation)}\`$`;
return `$\`${CopyAsGFM.nodeToGFM(mathAnnotation)}\`$`; },
}, 'span.katex-html'(el, text) {
'span.katex-html'(el, text) { // We don't want to include the content of this element in the copied text.
// We don't want to include the content of this element in the copied text. return '';
return ''; },
}, 'annotation[encoding="application/x-tex"]'(el, text) {
'annotation[encoding="application/x-tex"]'(el, text) { return text.trim();
return text.trim(); },
}, },
}, SanitizationFilter: {
SanitizationFilter: { 'a[name]:not([href]):empty'(el, text) {
'a[name]:not([href]):empty'(el, text) { return el.outerHTML;
return el.outerHTML; },
}, 'dl'(el, text) {
'dl'(el, text) { let lines = text.trim().split('\n');
let lines = text.trim().split('\n'); // Add two spaces to the front of subsequent list items lines,
// Add two spaces to the front of subsequent list items lines, // or leave the line entirely blank.
// or leave the line entirely blank. lines = lines.map((l) => {
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(); const line = l.trim();
if (line.length === 0) return ''; if (line.length === 0) return '';
return ` ${line}`; return ` ${line}`;
}); }).join('\n');
}
return `<dl>\n${lines.join('\n')}\n</dl>`; return `\`\`\`${lang}\n${text}\n\`\`\``;
}, },
'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr, summary, details'(el, text) { 'pre > code'(el, text) {
const tag = el.nodeName.toLowerCase(); // Don't wrap code blocks in ``
return `<${tag}>${text}</${tag}>`; return text;
}, },
},
MarkdownFilter: {
'br'(el, text) {
// Two spaces at the end of a line are turned into a BR
return ' ';
}, },
SyntaxHighlightFilter: { 'code'(el, text) {
'pre.code.highlight'(el, t) { let backtickCount = 1;
const text = t.trim(); const backtickMatch = text.match(/`+/);
if (backtickMatch) {
backtickCount = backtickMatch[0].length + 1;
}
let lang = el.getAttribute('lang'); const backticks = Array(backtickCount + 1).join('`');
if (lang === 'plaintext') { const spaceOrNoSpace = backtickCount > 1 ? ' ' : '';
lang = '';
}
// Prefixes lines with 4 spaces if the code contains triple backticks return backticks + spaceOrNoSpace + text + spaceOrNoSpace + backticks;
if (lang === '' && text.match(/^```/gm)) { },
return text.split('\n').map((l) => { 'blockquote'(el, text) {
const line = l.trim(); return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n');
if (line.length === 0) return ''; },
'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}`; const theadText = CopyAsGFM.nodeToGFM(theadEl);
}).join('\n'); const tbodyText = CopyAsGFM.nodeToGFM(tbodyEl);
}
return `\`\`\`${lang}\n${text}\n\`\`\``; return theadText + tbodyText;
}, },
'pre > code'(el, text) { 'thead'(el, text) {
// Don't wrap code blocks in `` const cells = _.map(el.querySelectorAll('th'), (cell) => {
return text; let chars = CopyAsGFM.nodeToGFM(cell).trim().length + 2;
},
}, let before = '';
MarkdownFilter: { let after = '';
'br'(el, text) { switch (cell.style.textAlign) {
// Two spaces at the end of a line are turned into a BR case 'center':
return ' '; before = ':';
}, after = ':';
'code'(el, text) { chars -= 2;
let backtickCount = 1; break;
const backtickMatch = text.match(/`+/); case 'right':
if (backtickMatch) { after = ':';
backtickCount = backtickMatch[0].length + 1; chars -= 1;
break;
default:
break;
} }
const backticks = Array(backtickCount + 1).join('`'); chars = Math.max(chars, 3);
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;
// If the documentFragment contains more than just Markdown, don't copy as GFM. const middle = Array(chars + 1).join('-');
if (documentFragment.querySelector('.md, .wiki')) return;
e.preventDefault(); return before + middle + after;
clipboardData.setData('text/plain', documentFragment.textContent); });
const gfm = CopyAsGFM.nodeToGFM(documentFragment); return `${text}|${cells.join('|')}|`;
clipboardData.setData('text/x-gfm', gfm); },
} 'tr'(el, text) {
const cells = _.map(el.querySelectorAll('td, th'), cell => CopyAsGFM.nodeToGFM(cell).trim());
return `| ${cells.join(' | ')} |`;
},
},
};
handlePaste(e) { class CopyAsGFM {
const clipboardData = e.originalEvent.clipboardData; constructor() {
if (!clipboardData) return; $(document).on('copy', '.md, .wiki', this.handleCopy);
$(document).on('paste', '.js-gfm-input', this.handlePaste);
}
const gfm = clipboardData.getData('text/x-gfm'); handleCopy(e) {
if (!gfm) return; 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) { e.preventDefault();
if (node.nodeType === Node.TEXT_NODE) { clipboardData.setData('text/plain', documentFragment.textContent);
return node.textContent;
}
const text = this.innerGFM(node); const gfm = CopyAsGFM.nodeToGFM(documentFragment);
clipboardData.setData('text/x-gfm', gfm);
}
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { handlePaste(e) {
return text; const clipboardData = e.originalEvent.clipboardData;
} if (!clipboardData) return;
for (const filter in gfmRules) { const gfm = clipboardData.getData('text/x-gfm');
const rules = gfmRules[filter]; if (!gfm) return;
for (const selector in rules) { e.preventDefault();
const func = rules[selector];
if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue; window.gl.utils.insertText(e.target, gfm);
}
const result = func(node, text); static nodeToGFM(node) {
if (result === false) continue; 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; return text;
} }
static innerGFM(parentNode) { for (const filter in gfmRules) {
const nodes = parentNode.childNodes; const rules = gfmRules[filter];
const clonedParentNode = parentNode.cloneNode(true); for (const selector in rules) {
const clonedNodes = Array.prototype.slice.call(clonedParentNode.childNodes, 0); const func = rules[selector];
for (let i = 0; i < nodes.length; i += 1) { if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue;
const node = nodes[i];
const clonedNode = clonedNodes[i];
const text = this.nodeToGFM(node); const result = func(node, text);
if (result === false) continue;
// `clonedNode.replaceWith(text)` is not yet widely supported return result;
clonedNode.parentNode.replaceChild(document.createTextNode(text), clonedNode);
} }
}
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 = window.gl || {};
window.gl.CopyAsGFM = CopyAsGFM; window.gl.CopyAsGFM = CopyAsGFM;
new CopyAsGFM(); new CopyAsGFM();
})();
/* 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 */ /* 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 */
import Clipboard from 'vendor/clipboard';
window.Clipboard = require('vendor/clipboard');
var genericError, genericSuccess, showTooltip;
(function() {
var genericError, genericSuccess, showTooltip; genericSuccess = function(e) {
showTooltip(e.trigger, 'Copied');
genericSuccess = function(e) { // Clear the selection and blur the trigger so it loses its border
showTooltip(e.trigger, 'Copied'); e.clearSelection();
// Clear the selection and blur the trigger so it loses its border return $(e.trigger).blur();
e.clearSelection(); };
return $(e.trigger).blur();
}; // Safari doesn't support `execCommand`, so instead we inform the user to
// copy manually.
// Safari doesn't support `execCommand`, so instead we inform the user to //
// copy manually. // See http://clipboardjs.com/#browser-support
// genericError = function(e) {
// See http://clipboardjs.com/#browser-support var key;
genericError = function(e) { if (/Mac/i.test(navigator.userAgent)) {
var key; key = '&#8984;'; // Command
if (/Mac/i.test(navigator.userAgent)) { } else {
key = '&#8984;'; // Command key = 'Ctrl';
} else { }
key = 'Ctrl'; return showTooltip(e.trigger, "Press " + key + "-C to copy");
} };
return showTooltip(e.trigger, "Press " + key + "-C to copy");
}; showTooltip = function(target, title) {
var $target = $(target);
showTooltip = function(target, title) { var originalTitle = $target.data('original-title');
var $target = $(target);
var originalTitle = $target.data('original-title'); $target
.attr('title', 'Copied')
$target .tooltip('fixTitle')
.attr('title', 'Copied') .tooltip('show')
.tooltip('fixTitle') .attr('title', originalTitle)
.tooltip('show') .tooltip('fixTitle');
.attr('title', originalTitle) };
.tooltip('fixTitle');
}; $(function() {
var clipboard;
$(function() {
var clipboard; clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]');
clipboard.on('success', genericSuccess);
clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]'); return clipboard.on('error', genericError);
clipboard.on('success', genericSuccess); });
return clipboard.on('error', genericError);
});
}).call(window);
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-param-reassign, wrap-iife, max-len */ /* 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 */ /* global Api */
(function (w) { class CreateLabelDropdown {
class CreateLabelDropdown { constructor ($el, namespacePath, projectPath) {
constructor ($el, namespacePath, projectPath) { this.$el = $el;
this.$el = $el; this.namespacePath = namespacePath;
this.namespacePath = namespacePath; this.projectPath = projectPath;
this.projectPath = projectPath; this.$dropdownBack = $('.dropdown-menu-back', this.$el.closest('.dropdown'));
this.$dropdownBack = $('.dropdown-menu-back', this.$el.closest('.dropdown')); this.$cancelButton = $('.js-cancel-label-btn', this.$el);
this.$cancelButton = $('.js-cancel-label-btn', this.$el); this.$newLabelField = $('#new_label_name', this.$el);
this.$newLabelField = $('#new_label_name', this.$el); this.$newColorField = $('#new_label_color', this.$el);
this.$newColorField = $('#new_label_color', this.$el); this.$colorPreview = $('.js-dropdown-label-color-preview', this.$el);
this.$colorPreview = $('.js-dropdown-label-color-preview', this.$el); this.$newLabelError = $('.js-label-error', this.$el);
this.$newLabelError = $('.js-label-error', this.$el); this.$newLabelCreateButton = $('.js-new-label-btn', this.$el);
this.$newLabelCreateButton = $('.js-new-label-btn', this.$el); this.$colorSuggestions = $('.suggest-colors-dropdown a', this.$el);
this.$colorSuggestions = $('.suggest-colors-dropdown a', this.$el);
this.$newLabelError.hide();
this.$newLabelError.hide(); this.$newLabelCreateButton.disable();
this.$newLabelCreateButton.disable();
this.cleanBinding();
this.addBinding();
}
this.cleanBinding(); cleanBinding () {
this.addBinding(); 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 () { addBinding () {
this.$colorSuggestions.off('click'); const self = this;
this.$newLabelField.off('keyup change');
this.$newColorField.off('keyup change');
this.$dropdownBack.off('click');
this.$cancelButton.off('click');
this.$newLabelCreateButton.off('click');
}
addBinding () { this.$colorSuggestions.on('click', function (e) {
const self = this; const $this = $(this);
self.addColorValue(e, $this);
});
this.$colorSuggestions.on('click', function (e) { this.$newLabelField.on('keyup change', this.enableLabelCreateButton.bind(this));
const $this = $(this); this.$newColorField.on('keyup change', this.enableLabelCreateButton.bind(this));
self.addColorValue(e, $this);
});
this.$newLabelField.on('keyup change', this.enableLabelCreateButton.bind(this)); this.$dropdownBack.on('click', this.resetForm.bind(this));
this.$newColorField.on('keyup change', this.enableLabelCreateButton.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) { self.resetForm();
e.preventDefault(); self.$dropdownBack.trigger('click');
e.stopPropagation(); });
self.resetForm(); this.$newLabelCreateButton.on('click', this.saveLabel.bind(this));
self.$dropdownBack.trigger('click'); }
});
this.$newLabelCreateButton.on('click', this.saveLabel.bind(this)); addColorValue (e, $this) {
} e.preventDefault();
e.stopPropagation();
addColorValue (e, $this) { this.$newColorField.val($this.data('color')).trigger('change');
e.preventDefault(); this.$colorPreview
e.stopPropagation(); .css('background-color', $this.data('color'))
.parent()
.addClass('is-active');
}
this.$newColorField.val($this.data('color')).trigger('change'); enableLabelCreateButton () {
this.$colorPreview if (this.$newLabelField.val() !== '' && this.$newColorField.val() !== '') {
.css('background-color', $this.data('color')) this.$newLabelError.hide();
.parent() this.$newLabelCreateButton.enable();
.addClass('is-active'); } else {
this.$newLabelCreateButton.disable();
} }
}
enableLabelCreateButton () { resetForm () {
if (this.$newLabelField.val() !== '' && this.$newColorField.val() !== '') { this.$newLabelField
this.$newLabelError.hide(); .val('')
this.$newLabelCreateButton.enable(); .trigger('change');
} else {
this.$newLabelCreateButton.disable();
}
}
resetForm () { this.$newColorField
this.$newLabelField .val('')
.val('') .trigger('change');
.trigger('change');
this.$newColorField this.$colorPreview
.val('') .css('background-color', '')
.trigger('change'); .parent()
.removeClass('is-active');
}
this.$colorPreview saveLabel (e) {
.css('background-color', '') e.preventDefault();
.parent() e.stopPropagation();
.removeClass('is-active');
}
saveLabel (e) { Api.newLabel(this.namespacePath, this.projectPath, {
e.preventDefault(); title: this.$newLabelField.val(),
e.stopPropagation(); color: this.$newColorField.val()
}, (label) => {
this.$newLabelCreateButton.enable();
Api.newLabel(this.namespacePath, this.projectPath, { if (label.message) {
title: this.$newLabelField.val(), let errors;
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');
$(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) { this.$newLabelError
w.gl = {}; .html(errors)
.show();
} else {
this.$dropdownBack.trigger('click');
$(document).trigger('created.label', label);
}
});
} }
}
gl.CreateLabelDropdown = CreateLabelDropdown; window.gl = window.gl || {};
})(window); gl.CreateLabelDropdown = CreateLabelDropdown;
...@@ -2,129 +2,127 @@ ...@@ -2,129 +2,127 @@
require('./lib/utils/url_utility'); require('./lib/utils/url_utility');
(() => { const UNFOLD_COUNT = 20;
const UNFOLD_COUNT = 20; let isBound = false;
let isBound = false;
class Diff { class Diff {
constructor() { constructor() {
const $diffFile = $('.files .diff-file'); const $diffFile = $('.files .diff-file');
$diffFile.singleFileDiff(); $diffFile.singleFileDiff();
$diffFile.filesCommentButton(); $diffFile.filesCommentButton();
$diffFile.each((index, file) => new gl.ImageFile(file)); $diffFile.each((index, file) => new gl.ImageFile(file));
if (this.diffViewType() === 'parallel') { if (this.diffViewType() === 'parallel') {
$('.content-wrapper .container-fluid').removeClass('container-limited'); $('.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 (gl.utils.getLocationHash()) { if (!isBound) {
this.highlightSelectedLine(); $(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) { this.openAnchoredDiff();
const $target = $(e.target); }
// current babel config relies on iterators implementation, so we cannot simply do:
// const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent()); handleClickUnfold(e) {
const ref = this.lineNumbers($target.parent()); const $target = $(e.target);
const oldLineNumber = ref[0]; // current babel config relies on iterators implementation, so we cannot simply do:
const newLineNumber = ref[1]; // const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent());
const offset = newLineNumber - oldLineNumber; const ref = this.lineNumbers($target.parent());
const bottom = $target.hasClass('js-unfold-bottom'); const oldLineNumber = ref[0];
let since; const newLineNumber = ref[1];
let to; const offset = newLineNumber - oldLineNumber;
let unfold = true; const bottom = $target.hasClass('js-unfold-bottom');
let since;
if (bottom) { let to;
const lineNumber = newLineNumber + 1; let unfold = true;
since = lineNumber;
to = lineNumber + UNFOLD_COUNT; if (bottom) {
} else { const lineNumber = newLineNumber + 1;
const lineNumber = newLineNumber - 1; since = lineNumber;
since = lineNumber - UNFOLD_COUNT; to = lineNumber + UNFOLD_COUNT;
to = lineNumber; } else {
const lineNumber = newLineNumber - 1;
// make sure we aren't loading more than we need since = lineNumber - UNFOLD_COUNT;
const prevNewLine = this.lineNumbers($target.parent().prev())[1]; to = lineNumber;
if (since <= prevNewLine + 1) {
since = prevNewLine + 1; // make sure we aren't loading more than we need
unfold = false; const prevNewLine = this.lineNumbers($target.parent().prev())[1];
} if (since <= prevNewLine + 1) {
since = prevNewLine + 1;
unfold = false;
} }
}
const file = $target.parents('.diff-file'); const file = $target.parents('.diff-file');
const link = file.data('blob-diff-path'); const link = file.data('blob-diff-path');
const view = file.data('view'); const view = file.data('view');
const params = { since, to, bottom, offset, unfold, view }; const params = { since, to, bottom, offset, unfold, view };
$.get(link, params, response => $target.parent().replaceWith(response)); $.get(link, params, response => $target.parent().replaceWith(response));
} }
openAnchoredDiff(cb) { openAnchoredDiff(cb) {
const locationHash = gl.utils.getLocationHash(); const locationHash = gl.utils.getLocationHash();
const anchoredDiff = locationHash && locationHash.split('_')[0]; 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();
}
}
handleClickLineNum(e) { if (!anchoredDiff) return;
const hash = $(e.currentTarget).attr('href');
e.preventDefault(); const diffTitle = $(`#${anchoredDiff}`);
if (window.history.pushState) { const diffFile = diffTitle.closest('.diff-file');
window.history.pushState(null, null, hash); const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
} else { if (nothingHereBlock.length) {
window.location.hash = hash; const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
} diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
this.highlightSelectedLine(); this.highlightSelectedLine();
if (cb) cb();
});
} else if (cb) {
cb();
} }
}
diffViewType() { handleClickLineNum(e) {
return $('.inline-parallel-buttons a.active').data('view-type'); 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) { diffViewType() {
if (!line.children().length) { return $('.inline-parallel-buttons a.active').data('view-type');
return [0, 0]; }
}
return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10)); lineNumbers(line) {
if (!line.children().length) {
return [0, 0];
} }
return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10));
}
highlightSelectedLine() { highlightSelectedLine() {
const hash = gl.utils.getLocationHash(); const hash = gl.utils.getLocationHash();
const $diffFiles = $('.diff-file'); const $diffFiles = $('.diff-file');
$diffFiles.find('.hll').removeClass('hll'); $diffFiles.find('.hll').removeClass('hll');
if (hash) { if (hash) {
$diffFiles $diffFiles
.find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`) .find(`tr#${hash}:not(.match) td, td#${hash}, td[data-line-code="${hash}"]`)
.addClass('hll'); .addClass('hll');
}
} }
} }
}
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.Diff = Diff; window.gl.Diff = Diff;
})();
...@@ -3,218 +3,216 @@ ...@@ -3,218 +3,216 @@
require('./preview_markdown'); require('./preview_markdown');
(function() { window.DropzoneInput = (function() {
this.DropzoneInput = (function() { function DropzoneInput(form) {
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;
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;
Dropzone.autoDiscover = false; alertClass = "alert alert-danger alert-dismissable div-dropzone-alert";
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"; alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\"";
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""; divHover = "<div class=\"div-dropzone-hover\"></div>";
divHover = "<div class=\"div-dropzone-hover\"></div>"; divSpinner = "<div class=\"div-dropzone-spinner\"></div>";
divSpinner = "<div class=\"div-dropzone-spinner\"></div>"; divAlert = "<div class=\"" + alertClass + "\"></div>";
divAlert = "<div class=\"" + alertClass + "\"></div>"; iconPaperclip = "<i class=\"fa fa-paperclip div-dropzone-icon\"></i>";
iconPaperclip = "<i class=\"fa fa-paperclip div-dropzone-icon\"></i>"; iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>";
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"; uploadProgress = $("<div class=\"div-dropzone-progress\"></div>");
uploadProgress = $("<div class=\"div-dropzone-progress\"></div>"); btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>";
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"; project_uploads_path = window.project_uploads_path || null;
project_uploads_path = window.project_uploads_path || null; max_file_size = gon.max_file_size || 10;
max_file_size = gon.max_file_size || 10; form_textarea = $(form).find(".js-gfm-input");
form_textarea = $(form).find(".js-gfm-input"); form_textarea.wrap("<div class=\"div-dropzone\"></div>");
form_textarea.wrap("<div class=\"div-dropzone\"></div>"); form_textarea.on('paste', (function(_this) {
form_textarea.on('paste', (function(_this) { return function(event) {
return function(event) { return handlePaste(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);
}
}
}; };
isImage = function(data) { })(this));
var i, item; $mdArea = $(form_textarea).closest('.md-area');
i = 0; $(form).setupMarkdownPreview();
while (i < data.clipboardData.items.length) { form_dropzone = $(form).find('.div-dropzone');
item = data.clipboardData.items[i]; form_dropzone.parent().addClass("div-dropzone-wrapper");
if (item.type.indexOf("image") !== -1) { form_dropzone.append(divHover);
return item; form_dropzone.find(".div-dropzone-hover").append(iconPaperclip);
} form_dropzone.append(divSpinner);
i += 1; form_dropzone.find(".div-dropzone-spinner").append(iconSpinner);
} form_dropzone.find(".div-dropzone-spinner").append(uploadProgress);
return false; form_dropzone.find(".div-dropzone-spinner").css({
}; "opacity": 0,
pasteText = function(text) { "display": "none"
var afterSelection, beforeSelection, caretEnd, caretStart, textEnd; });
var formattedText = text + "\n\n"; dropzone = form_dropzone.dropzone({
caretStart = $(child)[0].selectionStart; url: project_uploads_path,
caretEnd = $(child)[0].selectionEnd; dictDefaultMessage: "",
textEnd = $(child).val().length; clickable: true,
beforeSelection = $(child).val().substring(0, caretStart); paramName: "file",
afterSelection = $(child).val().substring(caretEnd, textEnd); maxFilesize: max_file_size,
$(child).val(beforeSelection + formattedText + afterSelection); uploadMultiple: false,
child.get(0).setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length); headers: {
return form_textarea.trigger("input"); "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
}; },
getFilename = function(e) { previewContainer: false,
var value; processing: function() {
if (window.clipboardData && window.clipboardData.getData) { return $(".div-dropzone-alert").alert("close");
value = window.clipboardData.getData("Text"); },
} else if (e.clipboardData && e.clipboardData.getData) { dragover: function() {
value = e.clipboardData.getData("text/plain"); $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(); totaluploadprogress: function(totalUploadProgress) {
}; uploadProgress.text(Math.round(totalUploadProgress) + "%");
uploadFile = function(item, filename) { },
var formData; sending: function() {
formData = new FormData(); form_dropzone.find(".div-dropzone-spinner").css({
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, "opacity": 0.7,
"display": "inherit" "display": "inherit"
}); });
}; },
closeSpinner = function() { queuecomplete: function() {
return form.find(".div-dropzone-spinner").css({ uploadProgress.text("");
$(".dz-preview").remove();
$(".markdown-area").trigger("input");
$(".div-dropzone-spinner").css({
"opacity": 0, "opacity": 0,
"display": "none" "display": "none"
}); });
}; }
showError = function(message) { });
var checkIfMsgExists, errorAlert; child = $(dropzone[0]).children("textarea");
errorAlert = $(form).find('.error-alert'); handlePaste = function(event) {
checkIfMsgExists = errorAlert.children().length; var filename, image, pasteEvent, text;
if (checkIfMsgExists === 0) { pasteEvent = event.originalEvent;
errorAlert.append(divAlert); if (pasteEvent.clipboardData && pasteEvent.clipboardData.items) {
return $(".div-dropzone-alert").append(btnAlert + message); 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"); isImage = function(data) {
}; var i, item;
form.find(".markdown-selector").click(function(e) { i = 0;
e.preventDefault(); while (i < data.clipboardData.items.length) {
$(this).closest('.gfm-form').find('.div-dropzone').click(); 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; return DropzoneInput;
})(); })();
}).call(window);
...@@ -2,203 +2,202 @@ ...@@ -2,203 +2,202 @@
/* global dateFormat */ /* global dateFormat */
/* global Pikaday */ /* global Pikaday */
(function(global) { class DueDateSelect {
class DueDateSelect { constructor({ $dropdown, $loading } = {}) {
constructor({ $dropdown, $loading } = {}) { const $dropdownParent = $dropdown.closest('.dropdown');
const $dropdownParent = $dropdown.closest('.dropdown'); const $block = $dropdown.closest('.block');
const $block = $dropdown.closest('.block'); this.$loading = $loading;
this.$loading = $loading; this.$dropdown = $dropdown;
this.$dropdown = $dropdown; this.$dropdownParent = $dropdownParent;
this.$dropdownParent = $dropdownParent; this.$datePicker = $dropdownParent.find('.js-due-date-calendar');
this.$datePicker = $dropdownParent.find('.js-due-date-calendar'); this.$block = $block;
this.$block = $block; this.$selectbox = $dropdown.closest('.selectbox');
this.$selectbox = $dropdown.closest('.selectbox'); this.$value = $block.find('.value');
this.$value = $block.find('.value'); this.$valueContent = $block.find('.value-content');
this.$valueContent = $block.find('.value-content'); this.$sidebarValue = $('.js-due-date-sidebar-value', $block);
this.$sidebarValue = $('.js-due-date-sidebar-value', $block); this.fieldName = $dropdown.data('field-name'),
this.fieldName = $dropdown.data('field-name'), this.abilityName = $dropdown.data('ability-name'),
this.abilityName = $dropdown.data('ability-name'), this.issueUpdateURL = $dropdown.data('issue-update');
this.issueUpdateURL = $dropdown.data('issue-update');
this.rawSelectedDate = null;
this.rawSelectedDate = null; this.displayedDate = null;
this.displayedDate = null; this.datePayload = null;
this.datePayload = null;
this.initGlDropdown();
this.initGlDropdown(); this.initRemoveDueDate();
this.initRemoveDueDate(); this.initDatePicker();
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);
if (this.$dropdown.hasClass('js-issue-boards-due-date')) { initGlDropdown() {
gl.issueBoards.BoardsStore.detail.issue.dueDate = $dueDateInput.val(); this.$dropdown.glDropdown({
this.updateIssueBoardIssue(); opened: () => {
} else { const calendar = this.$datePicker.data('pikaday');
this.saveDueDate(true); calendar.show();
} },
} hidden: () => {
}); this.$selectbox.hide();
this.$value.css('display', '');
}
});
}
calendar.setDate(new Date($dueDateInput.val())); initDatePicker() {
this.$datePicker.append(calendar.el); const $dueDateInput = $(`input[name='${this.fieldName}']`);
this.$datePicker.data('pikaday', calendar);
}
initRemoveDueDate() { const calendar = new Pikaday({
this.$block.on('click', '.js-remove-due-date', (e) => { field: $dueDateInput.get(0),
const calendar = this.$datePicker.data('pikaday'); theme: 'gitlab-theme',
e.preventDefault(); 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')) { 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(); this.updateIssueBoardIssue();
} else { } else {
$("input[name='" + this.fieldName + "']").val(''); this.saveDueDate(true);
return this.saveDueDate(false);
} }
}); }
} });
saveDueDate(isDropdown) { calendar.setDate(new Date($dueDateInput.val()));
this.parseSelectedDate(); this.$datePicker.append(calendar.el);
this.prepSelectedDate(); this.$datePicker.data('pikaday', calendar);
this.submitSelectedDate(isDropdown); }
}
initRemoveDueDate() {
this.$block.on('click', '.js-remove-due-date', (e) => {
const calendar = this.$datePicker.data('pikaday');
e.preventDefault();
parseSelectedDate() { calendar.setDate(null);
this.rawSelectedDate = $(`input[name='${this.fieldName}']`).val();
if (this.rawSelectedDate.length) { if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
// Construct Date object manually to avoid buggy dateString support within Date constructor gl.issueBoards.BoardsStore.detail.issue.dueDate = '';
const dateArray = this.rawSelectedDate.split('-').map(v => parseInt(v, 10)); this.updateIssueBoardIssue();
const dateObj = new Date(dateArray[0], dateArray[1] - 1, dateArray[2]);
this.displayedDate = dateFormat(dateObj, 'mmm d, yyyy');
} else { } else {
this.displayedDate = 'No due date'; $("input[name='" + this.fieldName + "']").val('');
return this.saveDueDate(false);
} }
} });
}
prepSelectedDate() { saveDueDate(isDropdown) {
const datePayload = {}; this.parseSelectedDate();
datePayload[this.abilityName] = {}; this.prepSelectedDate();
datePayload[this.abilityName].due_date = this.rawSelectedDate; this.submitSelectedDate(isDropdown);
this.datePayload = datePayload; }
}
updateIssueBoardIssue () { parseSelectedDate() {
this.$loading.fadeIn(); this.rawSelectedDate = $(`input[name='${this.fieldName}']`).val();
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')) if (this.rawSelectedDate.length) {
.then(() => { // Construct Date object manually to avoid buggy dateString support within Date constructor
this.$loading.fadeOut(); 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) { if (isDropdown) {
this.$dropdown.trigger('loaded.gl.dropdown'); this.$dropdown.trigger('loading.gl.dropdown');
this.$dropdown.dropdown('toggle'); 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 { class DueDateSelectors {
constructor() { constructor() {
this.initMilestoneDatePicker(); this.initMilestoneDatePicker();
this.initIssuableSelect(); this.initIssuableSelect();
} }
initMilestoneDatePicker() { initMilestoneDatePicker() {
$('.datepicker').each(function() { $('.datepicker').each(function() {
const $datePicker = $(this); const $datePicker = $(this);
const calendar = new Pikaday({ const calendar = new Pikaday({
field: $datePicker.get(0), field: $datePicker.get(0),
theme: 'gitlab-theme', theme: 'gitlab-theme',
format: 'yyyy-mm-dd', format: 'yyyy-mm-dd',
onSelect(dateText) { onSelect(dateText) {
$datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); $datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
} }
});
calendar.setDate(new Date($datePicker.val()));
$datePicker.data('pikaday', calendar);
}); });
calendar.setDate(new Date($datePicker.val()));
$('.js-clear-due-date,.js-clear-start-date').on('click', (e) => { $datePicker.data('pikaday', calendar);
e.preventDefault(); });
const calendar = $(e.target).siblings('.datepicker').data('pikaday');
calendar.setDate(null);
});
}
initIssuableSelect() { $('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
const $loading = $('.js-issuable-update .due_date').find('.block-loading').hide(); 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) => { $('.js-due-date-select').each((i, dropdown) => {
const $dropdown = $(dropdown); const $dropdown = $(dropdown);
new DueDateSelect({ new DueDateSelect({
$dropdown, $dropdown,
$loading $loading
});
}); });
} });
} }
}
global.DueDateSelectors = DueDateSelectors; window.gl = window.gl || {};
})(window.gl || (window.gl = {})); window.gl.DueDateSelectors = DueDateSelectors;
...@@ -2,142 +2,140 @@ ...@@ -2,142 +2,140 @@
/* global FilesCommentButton */ /* global FilesCommentButton */
/* global notes */ /* global notes */
(function() { let $commentButtonTemplate;
let $commentButtonTemplate; var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.FilesCommentButton = (function() { 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; 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) { function FilesCommentButton(filesContainerElement) {
this.render = bind(this.render, this); this.render = bind(this.render, this);
this.hideButton = bind(this.hideButton, this); this.hideButton = bind(this.hideButton, this);
this.isParallelView = notes.isParallelView(); this.isParallelView = notes.isParallelView();
filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render) filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render)
.on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton); .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) { textFileElement = this.getTextFileElement($currentTarget);
var $currentTarget, buttonParentElement, lineContentElement, textFileElement, $button; buttonParentElement.append(this.buildButton({
$currentTarget = $(e.currentTarget); 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.removeClass('is-over')
buttonParentElement = this.getButtonParent($currentTarget); .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); FilesCommentButton.prototype.getTextFileElement = function(hoveredElement) {
buttonParentElement.addClass('is-over') return hoveredElement.closest(TEXT_FILE_SELECTOR);
.nextUntil(`.${LINE_CONTENT_CLASS}`).addClass('is-over'); };
if ($button.length) { FilesCommentButton.prototype.getLineContent = function(hoveredElement) {
return; 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); FilesCommentButton.prototype.getButtonParent = function(hoveredElement) {
buttonParentElement.append(this.buildButton({ if (!this.isParallelView) {
noteableType: textFileElement.attr('data-noteable-type'), if (hoveredElement.hasClass(OLD_LINE_CLASS)) {
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)) {
return hoveredElement; return hoveredElement;
} }
if (!this.isParallelView) { return hoveredElement.parent().find("." + OLD_LINE_CLASS);
return $(hoveredElement).closest(LINE_HOLDER_CLASS).find("." + LINE_CONTENT_CLASS); } else {
} else { if (hoveredElement.hasClass(LINE_NUMBER_CLASS)) {
return $(hoveredElement).next("." + LINE_CONTENT_CLASS); return hoveredElement;
}
};
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).prev("." + LINE_NUMBER_CLASS);
}
};
FilesCommentButton.prototype.validateButtonParent = function(buttonParentElement) { FilesCommentButton.prototype.validateButtonParent = function(buttonParentElement) {
return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS); return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS);
}; };
FilesCommentButton.prototype.validateLineContent = function(lineContentElement) { FilesCommentButton.prototype.validateLineContent = function(lineContentElement) {
return lineContentElement.attr('data-discussion-id') && lineContentElement.attr('data-discussion-id') !== ''; return lineContentElement.attr('data-discussion-id') && lineContentElement.attr('data-discussion-id') !== '';
}; };
return FilesCommentButton; return FilesCommentButton;
})(); })();
$.fn.filesCommentButton = function() { $.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>'); $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))) { if (!(this && (this.parent().data('can-create-note') != null))) {
return; 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);
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Makes search request for content when user types a value in the search input. * 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. * Updates the html content of the page with the received one.
*/ */
export default class FilterableList { export default class FilterableList {
constructor(form, filter, holder) { constructor(form, filter, holder) {
this.filterForm = form; this.filterForm = form;
......
/* 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 */ /* 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() { window.Flash = (function() {
return $(this).fadeOut(); var hideFlash;
};
function Flash(message, type, parent) { hideFlash = function() {
var flash, textDiv; return $(this).fadeOut();
if (type == null) { };
type = 'alert';
} function Flash(message, type, parent) {
if (parent == null) { var flash, textDiv;
parent = null; if (type == null) {
} type = 'alert';
if (parent) { }
this.flashContainer = parent.find('.flash-container'); if (parent == null) {
} else { parent = null;
this.flashContainer = $('.flash-container-page'); }
} if (parent) {
this.flashContainer.html(''); this.flashContainer = parent.find('.flash-container');
flash = $('<div/>', { } else {
"class": "flash-" + type this.flashContainer = $('.flash-container-page');
}); }
flash.on('click', hideFlash); this.flashContainer.html('');
textDiv = $('<div/>', { flash = $('<div/>', {
"class": 'flash-text', "class": "flash-" + type
text: message });
}); flash.on('click', hideFlash);
textDiv.appendTo(flash); textDiv = $('<div/>', {
if (this.flashContainer.parent().hasClass('content-wrapper')) { "class": 'flash-text',
textDiv.addClass('container-fluid container-limited'); text: message
} });
flash.appendTo(this.flashContainer); textDiv.appendTo(flash);
this.flashContainer.show(); if (this.flashContainer.parent().hasClass('content-wrapper')) {
textDiv.addClass('container-fluid container-limited');
} }
flash.appendTo(this.flashContainer);
this.flashContainer.show();
}
return Flash; return Flash;
})(); })();
}).call(window);
...@@ -5,390 +5,386 @@ import emojiAliases from 'emojis/aliases.json'; ...@@ -5,390 +5,386 @@ import emojiAliases from 'emojis/aliases.json';
import { glEmojiTag } from '~/behaviors/gl_emoji'; import { glEmojiTag } from '~/behaviors/gl_emoji';
// Creates the variables for setting up GFM auto-completion // Creates the variables for setting up GFM auto-completion
(function() { window.gl = window.gl || {};
if (window.gl == null) {
window.gl = {};
}
function sanitize(str) { function sanitize(str) {
return str.replace(/<(?:.|\n)*?>/gm, ''); return str.replace(/<(?:.|\n)*?>/gm, '');
} }
window.gl.GfmAutoComplete = { window.gl.GfmAutoComplete = {
dataSources: {}, dataSources: {},
defaultLoadingData: ['loading'], defaultLoadingData: ['loading'],
cachedData: {}, cachedData: {},
isLoadingData: {}, isLoadingData: {},
atTypeMap: { atTypeMap: {
':': 'emojis', ':': 'emojis',
'@': 'members', '@': 'members',
'#': 'issues', '#': 'issues',
'!': 'mergeRequests', '!': 'mergeRequests',
'~': 'labels', '~': 'labels',
'%': 'milestones', '%': 'milestones',
'/': 'commands' '/': 'commands'
}, },
// Emoji // Emoji
Emoji: { Emoji: {
templateFunction: function(name) { templateFunction: function(name) {
return `<li> return `<li>
${name} ${glEmojiTag(name)} ${name} ${glEmojiTag(name)}
</li> </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 filter: function(query, data, searchKey) {
Members: { if (gl.GfmAutoComplete.isLoading(data)) {
template: '<li>${avatarTag} ${username} <small>${title}</small></li>' gl.GfmAutoComplete.fetchData(this.$inputor, this.at);
}, return data;
Labels: { } else {
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>' return $.fn.atwho["default"].callbacks.filter(query, data, searchKey);
}, }
// Issues and MergeRequests
Issues: {
template: '<li><small>${id}</small> ${title}</li>'
},
// Milestones
Milestones: {
template: '<li>${title}</li>'
}, },
Loading: { beforeInsert: function(value) {
template: '<li style="pointer-events: none;"><i class="fa fa-refresh fa-spin"></i> Loading...</li>' if (value && !this.setting.skipSpecialCharacterTest) {
var withoutAt = value.substring(1);
if (withoutAt && /[^\w\d]/.test(withoutAt)) value = value.charAt() + '"' + withoutAt + '"';
}
return value;
}, },
DefaultOptions: { matcher: function (flag, subtext) {
sorter: function(query, items, searchKey) { // The below is taken from At.js source
this.setting.highlightFirst = this.setting.alwaysHighlightFirst || query.length > 0; // Tweaked to commands to start without a space only if char before is a non-word character
if (gl.GfmAutoComplete.isLoading(items)) { // https://github.com/ichord/At.js
this.setting.highlightFirst = false; var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar;
return items; atSymbolsWithBar = Object.keys(this.app.controllers).join('|');
} atSymbolsWithoutBar = Object.keys(this.app.controllers).join('');
return $.fn.atwho["default"].callbacks.sorter(query, items, searchKey); subtext = subtext.split(/\s+/g).pop();
}, flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
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, "\\$&");
_a = decodeURI("%C3%80"); _a = decodeURI("%C3%80");
_y = decodeURI("%C3%BF"); _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) { if (match) {
return match[1]; return match[1];
} else { } else {
return null; return null;
}
} }
}, }
setup: function(input) { },
// Add GFM auto-completion to all input fields, that accept GFM input. setup: function(input) {
this.input = input || $('.js-gfm-input'); // Add GFM auto-completion to all input fields, that accept GFM input.
this.setupLifecycle(); this.input = input || $('.js-gfm-input');
}, this.setupLifecycle();
setupLifecycle() { },
this.input.each((i, input) => { setupLifecycle() {
const $input = $(input); this.input.each((i, input) => {
$input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input)); const $input = $(input);
// This triggers at.js again $input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input));
// Needed for slash commands with suffixes (ex: /label ~) // This triggers at.js again
$input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup')); // Needed for slash commands with suffixes (ex: /label ~)
}); $input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup'));
}, });
setupAtWho: function($input) { },
// Emoji setupAtWho: function($input) {
$input.atwho({ // Emoji
at: ':', $input.atwho({
displayTpl: function(value) { at: ':',
return value && value.name ? this.Emoji.templateFunction(value.name) : this.Loading.template; displayTpl: function(value) {
}.bind(this), return value && value.name ? this.Emoji.templateFunction(value.name) : this.Loading.template;
insertTpl: ':${name}:', }.bind(this),
skipSpecialCharacterTest: true, insertTpl: ':${name}:',
data: this.defaultLoadingData, skipSpecialCharacterTest: true,
callbacks: { data: this.defaultLoadingData,
sorter: this.DefaultOptions.sorter, callbacks: {
beforeInsert: this.DefaultOptions.beforeInsert, sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter beforeInsert: this.DefaultOptions.beforeInsert,
} filter: this.DefaultOptions.filter
}); }
// Team Members });
$input.atwho({ // Team Members
at: '@', $input.atwho({
displayTpl: function(value) { at: '@',
return value.username != null ? this.Members.template : this.Loading.template; displayTpl: function(value) {
}.bind(this), return value.username != null ? this.Members.template : this.Loading.template;
insertTpl: '${atwho-at}${username}', }.bind(this),
searchKey: 'search', insertTpl: '${atwho-at}${username}',
alwaysHighlightFirst: true, searchKey: 'search',
skipSpecialCharacterTest: true, alwaysHighlightFirst: true,
data: this.defaultLoadingData, skipSpecialCharacterTest: true,
callbacks: { data: this.defaultLoadingData,
sorter: this.DefaultOptions.sorter, callbacks: {
filter: this.DefaultOptions.filter, sorter: this.DefaultOptions.sorter,
beforeInsert: this.DefaultOptions.beforeInsert, filter: this.DefaultOptions.filter,
matcher: this.DefaultOptions.matcher, beforeInsert: this.DefaultOptions.beforeInsert,
beforeSave: function(members) { matcher: this.DefaultOptions.matcher,
return $.map(members, function(m) { beforeSave: function(members) {
let title = ''; return $.map(members, function(m) {
if (m.username == null) { let title = '';
return m; if (m.username == null) {
} return m;
title = m.name; }
if (m.count) { title = m.name;
title += " (" + m.count + ")"; if (m.count) {
} title += " (" + m.count + ")";
}
const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase(); 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 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 txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`;
return { return {
username: m.username, username: m.username,
avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar, avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
title: sanitize(title), title: sanitize(title),
search: sanitize(m.username + " " + m.name) search: sanitize(m.username + " " + m.name)
}; };
}); });
}
} }
}); }
$input.atwho({ });
at: '#', $input.atwho({
alias: 'issues', at: '#',
searchKey: 'search', alias: 'issues',
displayTpl: function(value) { searchKey: 'search',
return value.title != null ? this.Issues.template : this.Loading.template; displayTpl: function(value) {
}.bind(this), return value.title != null ? this.Issues.template : this.Loading.template;
data: this.defaultLoadingData, }.bind(this),
insertTpl: '${atwho-at}${id}', data: this.defaultLoadingData,
callbacks: { insertTpl: '${atwho-at}${id}',
sorter: this.DefaultOptions.sorter, callbacks: {
filter: this.DefaultOptions.filter, sorter: this.DefaultOptions.sorter,
beforeInsert: this.DefaultOptions.beforeInsert, filter: this.DefaultOptions.filter,
matcher: this.DefaultOptions.matcher, beforeInsert: this.DefaultOptions.beforeInsert,
beforeSave: function(issues) { matcher: this.DefaultOptions.matcher,
return $.map(issues, function(i) { beforeSave: function(issues) {
if (i.title == null) { return $.map(issues, function(i) {
return i; if (i.title == null) {
} return i;
return { }
id: i.iid, return {
title: sanitize(i.title), id: i.iid,
search: i.iid + " " + i.title title: sanitize(i.title),
}; search: i.iid + " " + i.title
}); };
} });
} }
}); }
$input.atwho({ });
at: '%', $input.atwho({
alias: 'milestones', at: '%',
searchKey: 'search', alias: 'milestones',
insertTpl: '${atwho-at}${title}', searchKey: 'search',
displayTpl: function(value) { insertTpl: '${atwho-at}${title}',
return value.title != null ? this.Milestones.template : this.Loading.template; displayTpl: function(value) {
}.bind(this), return value.title != null ? this.Milestones.template : this.Loading.template;
data: this.defaultLoadingData, }.bind(this),
callbacks: { data: this.defaultLoadingData,
matcher: this.DefaultOptions.matcher, callbacks: {
sorter: this.DefaultOptions.sorter, matcher: this.DefaultOptions.matcher,
beforeInsert: this.DefaultOptions.beforeInsert, sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter, beforeInsert: this.DefaultOptions.beforeInsert,
beforeSave: function(milestones) { filter: this.DefaultOptions.filter,
return $.map(milestones, function(m) { beforeSave: function(milestones) {
if (m.title == null) { return $.map(milestones, function(m) {
return m; if (m.title == null) {
} return m;
return { }
id: m.iid, return {
title: sanitize(m.title), id: m.iid,
search: "" + m.title title: sanitize(m.title),
}; search: "" + m.title
}); };
} });
} }
}); }
$input.atwho({ });
at: '!', $input.atwho({
alias: 'mergerequests', at: '!',
searchKey: 'search', alias: 'mergerequests',
displayTpl: function(value) { searchKey: 'search',
return value.title != null ? this.Issues.template : this.Loading.template; displayTpl: function(value) {
}.bind(this), return value.title != null ? this.Issues.template : this.Loading.template;
data: this.defaultLoadingData, }.bind(this),
insertTpl: '${atwho-at}${id}', data: this.defaultLoadingData,
callbacks: { insertTpl: '${atwho-at}${id}',
sorter: this.DefaultOptions.sorter, callbacks: {
filter: this.DefaultOptions.filter, sorter: this.DefaultOptions.sorter,
beforeInsert: this.DefaultOptions.beforeInsert, filter: this.DefaultOptions.filter,
matcher: this.DefaultOptions.matcher, beforeInsert: this.DefaultOptions.beforeInsert,
beforeSave: function(merges) { matcher: this.DefaultOptions.matcher,
return $.map(merges, function(m) { beforeSave: function(merges) {
if (m.title == null) { return $.map(merges, function(m) {
return m; if (m.title == null) {
} return m;
return { }
id: m.iid, return {
title: sanitize(m.title), id: m.iid,
search: m.iid + " " + m.title title: sanitize(m.title),
}; search: m.iid + " " + m.title
}); };
} });
} }
}); }
$input.atwho({ });
at: '~', $input.atwho({
alias: 'labels', at: '~',
searchKey: 'search', alias: 'labels',
data: this.defaultLoadingData, searchKey: 'search',
displayTpl: function(value) { data: this.defaultLoadingData,
return this.isLoading(value) ? this.Loading.template : this.Labels.template; displayTpl: function(value) {
}.bind(this), return this.isLoading(value) ? this.Loading.template : this.Labels.template;
insertTpl: '${atwho-at}${title}', }.bind(this),
callbacks: { insertTpl: '${atwho-at}${title}',
matcher: this.DefaultOptions.matcher, callbacks: {
beforeInsert: this.DefaultOptions.beforeInsert, matcher: this.DefaultOptions.matcher,
filter: this.DefaultOptions.filter, beforeInsert: this.DefaultOptions.beforeInsert,
sorter: this.DefaultOptions.sorter, filter: this.DefaultOptions.filter,
beforeSave: function(merges) { sorter: this.DefaultOptions.sorter,
if (gl.GfmAutoComplete.isLoading(merges)) return merges; beforeSave: function(merges) {
var sanitizeLabelTitle; if (gl.GfmAutoComplete.isLoading(merges)) return merges;
sanitizeLabelTitle = function(title) { var sanitizeLabelTitle;
if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) { sanitizeLabelTitle = function(title) {
return "\"" + (sanitize(title)) + "\""; if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) {
} else { return "\"" + (sanitize(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({ // We don't instantiate the slash commands autocomplete for note and issue/MR edit forms
at: '/', $input.filter('[data-supports-slash-commands="true"]').atwho({
alias: 'commands', at: '/',
searchKey: 'search', alias: 'commands',
skipSpecialCharacterTest: true, searchKey: 'search',
data: this.defaultLoadingData, skipSpecialCharacterTest: true,
displayTpl: function(value) { data: this.defaultLoadingData,
if (this.isLoading(value)) return this.Loading.template; displayTpl: function(value) {
var tpl = '<li>/${name}'; if (this.isLoading(value)) return this.Loading.template;
if (value.aliases.length > 0) { var tpl = '<li>/${name}';
tpl += ' <small>(or /<%- aliases.join(", /") %>)</small>'; if (value.aliases.length > 0) {
} tpl += ' <small>(or /<%- aliases.join(", /") %>)</small>';
if (value.params.length > 0) { }
tpl += ' <small><%- params.join(" ") %></small>'; if (value.params.length > 0) {
} tpl += ' <small><%- params.join(" ") %></small>';
if (value.description !== '') { }
tpl += '<small class="description"><i><%- description %></i></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); return _.template(tpl)({ reference_prefix: reference_prefix });
}.bind(this), },
insertTpl: function(value) { suffix: '',
var tpl = "/${name} "; callbacks: {
var reference_prefix = null; sorter: this.DefaultOptions.sorter,
if (value.params.length > 0) { filter: this.DefaultOptions.filter,
reference_prefix = value.params[0][0]; beforeInsert: this.DefaultOptions.beforeInsert,
if (/^[@%~]/.test(reference_prefix)) { beforeSave: function(commands) {
tpl += '<%- reference_prefix %>'; 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 {
return _.template(tpl)({ reference_prefix: reference_prefix }); name: c.name,
aliases: c.aliases,
params: c.params,
description: c.description,
search: search
};
});
}, },
suffix: '', matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) {
callbacks: { var regexp = /(?:^|\n)\/([A-Za-z_]*)$/gi;
sorter: this.DefaultOptions.sorter, var match = regexp.exec(subtext);
filter: this.DefaultOptions.filter, if (match) {
beforeInsert: this.DefaultOptions.beforeInsert, return match[1];
beforeSave: function(commands) { } else {
if (gl.GfmAutoComplete.isLoading(commands)) return commands; return null;
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;
}
} }
} }
});
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;
return dataToInspect && },
(dataToInspect === loadingState || dataToInspect.name === loadingState); 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);
}
};
/* 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 */ /* 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 */ /* global fuzzaldrinPlus */
(function() { var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote,
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
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; };
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() {
GitLabDropdownFilter = (function() { var ARROW_KEY_CODES, BLUR_KEYCODES, HAS_VALUE_CLASS;
var ARROW_KEY_CODES, BLUR_KEYCODES, HAS_VALUE_CLASS;
BLUR_KEYCODES = [27, 40];
BLUR_KEYCODES = [27, 40];
ARROW_KEY_CODES = [38, 40];
ARROW_KEY_CODES = [38, 40];
HAS_VALUE_CLASS = "has-value";
HAS_VALUE_CLASS = "has-value";
function GitLabDropdownFilter(input, options) {
function GitLabDropdownFilter(input, options) { var $clearButton, $inputContainer, ref, timeout;
var $clearButton, $inputContainer, ref, timeout; this.input = input;
this.input = input; this.options = options;
this.options = options; this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true;
this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true; $inputContainer = this.input.parent();
$inputContainer = this.input.parent(); $clearButton = $inputContainer.find('.js-dropdown-input-clear');
$clearButton = $inputContainer.find('.js-dropdown-input-clear'); $clearButton.on('click', (function(_this) {
$clearButton.on('click', (function(_this) { // Clear click
// Clear click return function(e) {
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.preventDefault();
e.stopPropagation(); }
return _this.input.val('').trigger('input').focus(); })
}; .on('input', function() {
})(this)); if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
// Key events $inputContainer.addClass(HAS_VALUE_CLASS);
timeout = ""; } else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
this.input $inputContainer.removeClass(HAS_VALUE_CLASS);
.on('keydown', function (e) { }
var keyCode = e.which; // Only filter asynchronously only if option remote is set
if (keyCode === 13 && !options.elIsInput) { if (this.options.remote) {
e.preventDefault(); clearTimeout(timeout);
} return timeout = setTimeout(function() {
}) $inputContainer.parent().addClass('is-loading');
.on('input', function() {
if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) { return this.options.query(this.input.val(), function(data) {
$inputContainer.addClass(HAS_VALUE_CLASS); $inputContainer.parent().removeClass('is-loading');
} else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) { return this.options.callback(data);
$inputContainer.removeClass(HAS_VALUE_CLASS); }.bind(this));
} }.bind(this), 250);
// Only filter asynchronously only if option remote is set } else {
if (this.options.remote) { return this.filter(this.input.val());
clearTimeout(timeout); }
return timeout = setTimeout(function() { }.bind(this));
$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) { GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) {
return BLUR_KEYCODES.indexOf(keyCode) !== -1; return BLUR_KEYCODES.indexOf(keyCode) !== -1;
}; };
GitLabDropdownFilter.prototype.filter = function(search_text) { GitLabDropdownFilter.prototype.filter = function(search_text) {
var data, elements, group, key, results, tmp; var data, elements, group, key, results, tmp;
if (this.options.onFilter) { if (this.options.onFilter) {
this.options.onFilter(search_text); this.options.onFilter(search_text);
} }
data = this.options.data(); data = this.options.data();
if ((data != null) && !this.options.filterByText) { if ((data != null) && !this.options.filterByText) {
results = data; results = data;
if (search_text !== '') { if (search_text !== '') {
// When data is an array of objects therefore [object Array] e.g. // When data is an array of objects therefore [object Array] e.g.
// [ // [
// { prop: 'foo' }, // { prop: 'foo' },
// { prop: 'baz' } // { prop: 'baz' }
// ] // ]
if (_.isArray(data)) { if (_.isArray(data)) {
results = fuzzaldrinPlus.filter(data, search_text, { results = fuzzaldrinPlus.filter(data, search_text, {
key: this.options.keys key: this.options.keys
}); });
} else { } else {
// If data is grouped therefore an [object Object]. e.g. // If data is grouped therefore an [object Object]. e.g.
// { // {
// groupName1: [ // groupName1: [
// { prop: 'foo' }, // { prop: 'foo' },
// { prop: 'baz' } // { prop: 'baz' }
// ], // ],
// groupName2: [ // groupName2: [
// { prop: 'abc' }, // { prop: 'abc' },
// { prop: 'def' } // { prop: 'def' }
// ] // ]
// } // }
if (gl.utils.isObject(data)) { if (gl.utils.isObject(data)) {
results = {}; results = {};
for (key in data) { for (key in data) {
group = data[key]; group = data[key];
tmp = fuzzaldrinPlus.filter(group, search_text, { tmp = fuzzaldrinPlus.filter(group, search_text, {
key: this.options.keys 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 { return this.options.callback(results);
elements = this.options.elements(); } else {
if (search_text) { elements = this.options.elements();
return elements.each(function() { if (search_text) {
var $el, matches; return elements.each(function() {
$el = $(this); var $el, matches;
matches = fuzzaldrinPlus.match($el.text().trim(), search_text); $el = $(this);
if (!$el.is('.dropdown-header')) { matches = fuzzaldrinPlus.match($el.text().trim(), search_text);
if (matches.length) { if (!$el.is('.dropdown-header')) {
return $el.show().removeClass('option-hidden'); if (matches.length) {
} else { return $el.show().removeClass('option-hidden');
return $el.hide().addClass('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() { return GitLabDropdownFilter;
function GitLabDropdownRemote(dataEndpoint, options) { })();
this.dataEndpoint = dataEndpoint;
this.options = options; 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() { GitLabDropdownRemote.prototype.fetchData = function() {
if (typeof this.dataEndpoint === "string") { return $.ajax({
return this.fetchData(); url: this.dataEndpoint,
} else if (typeof this.dataEndpoint === "function") { dataType: this.options.dataType,
if (this.options.beforeSend) { beforeSend: (function(_this) {
this.options.beforeSend(); return function() {
} if (_this.options.beforeSend) {
return this.dataEndpoint("", (function(_this) { return _this.options.beforeSend();
// Fetch the data by calling the data funcfion }
return function(data) { };
if (_this.options.success) { })(this),
_this.options.success(data); success: (function(_this) {
} return function(data) {
if (_this.options.beforeSend) { if (_this.options.success) {
return _this.options.beforeSend(); return _this.options.success(data);
} }
}; };
})(this)); })(this)
} });
}; // Fetch the data through ajax if the data is a string
};
GitLabDropdownRemote.prototype.fetchData = function() { return GitLabDropdownRemote;
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; 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() { LOADING_CLASS = "is-loading";
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"; 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)";
SELECTABLE_CLASSES = ".dropdown-content li:not(" + NON_SELECTABLE_CLASSES + ", .option-hidden)"; CURSOR_SELECT_SCROLL_PADDING = 5;
CURSOR_SELECT_SCROLL_PADDING = 5; FILTER_INPUT = '.dropdown-input .dropdown-input-field';
FILTER_INPUT = '.dropdown-input .dropdown-input-field'; function GitLabDropdown(el1, options) {
var searchFields, selector, self;
function GitLabDropdown(el1, options) { this.el = el1;
var searchFields, selector, self; this.options = options;
this.el = el1; this.updateLabel = bind(this.updateLabel, this);
this.options = options; this.hidden = bind(this.hidden, this);
this.updateLabel = bind(this.updateLabel, this); this.opened = bind(this.opened, this);
this.hidden = bind(this.hidden, this); this.shouldPropagate = bind(this.shouldPropagate, this);
this.opened = bind(this.opened, this); self = this;
this.shouldPropagate = bind(this.shouldPropagate, this); selector = $(this.el).data("target");
self = this; this.dropdown = selector != null ? $(selector) : $(this.el).parent();
selector = $(this.el).data("target"); // Set Defaults
this.dropdown = selector != null ? $(selector) : $(this.el).parent(); this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT);
// Set Defaults this.highlight = !!this.options.highlight;
this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT); this.filterInputBlur = this.options.filterInputBlur != null
this.highlight = !!this.options.highlight; ? this.options.filterInputBlur
this.filterInputBlur = this.options.filterInputBlur != null : true;
? this.options.filterInputBlur // If no input is passed create a default one
: true; self = this;
// If no input is passed create a default one // If selector was passed
self = this; if (_.isString(this.filterInput)) {
// If selector was passed this.filterInput = this.getElement(this.filterInput);
if (_.isString(this.filterInput)) { }
this.filterInput = this.getElement(this.filterInput); searchFields = this.options.search ? this.options.search.fields : [];
} if (this.options.data) {
searchFields = this.options.search ? this.options.search.fields : []; // If we provided data
if (this.options.data) { // data could be an array of objects or a group of arrays
// If we provided data if (_.isObject(this.options.data) && !_.isFunction(this.options.data)) {
// data could be an array of objects or a group of arrays this.fullData = this.options.data;
if (_.isObject(this.options.data) && !_.isFunction(this.options.data)) { currentIndex = -1;
this.fullData = this.options.data; this.parseData(this.options.data);
currentIndex = -1; this.focusTextInput();
this.parseData(this.options.data); } else {
this.focusTextInput(); this.remote = new GitLabDropdownRemote(this.options.data, {
} else { dataType: this.options.dataType,
this.remote = new GitLabDropdownRemote(this.options.data, { beforeSend: this.toggleLoading.bind(this),
dataType: this.options.dataType, success: (function(_this) {
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) {
return function(data) { return function(data) {
_this.parseData(data); _this.fullData = data;
if (_this.filterInput.val() !== '') { _this.parseData(_this.fullData);
selector = SELECTABLE_CLASSES; _this.focusTextInput();
if (_this.dropdown.find('.dropdown-toggle-page').length) { if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val() && _this.filter.input.val().trim() !== '') {
selector = ".dropdown-page-one " + selector; return _this.filter.input.trigger('input');
}
if ($(_this.el).is('input')) {
currentIndex = -1;
} else {
$(selector, _this.dropdown).first().find('a').addClass('is-focused');
currentIndex = 0;
}
} }
}; };
// Remote data
})(this) })(this)
}); });
} }
// Event listeners }
this.dropdown.on("shown.bs.dropdown", this.opened); // Init filterable
this.dropdown.on("hidden.bs.dropdown", this.hidden); if (this.options.filterable) {
$(this.el).on("update.label", this.updateLabel); this.filter = new GitLabDropdownFilter(this.filterInput, {
this.dropdown.on("click", ".dropdown-menu, .dropdown-menu-close", this.shouldPropagate); elIsInput: $(this.el).is('input'),
this.dropdown.on('keyup', (function(_this) { filterInputBlur: this.filterInputBlur,
return function(e) { filterByText: this.options.filterByText,
// Escape key onFilter: this.options.onFilter,
if (e.which === 27) { remote: this.options.filterRemote,
return $('.dropdown-menu-close', _this.dropdown).trigger('click'); query: this.options.data,
} keys: searchFields,
}; elements: (function(_this) {
})(this)); return function() {
this.dropdown.on('blur', 'a', (function(_this) { selector = '.dropdown-content li:not(' + NON_SELECTABLE_CLASSES + ')';
return function(e) { if (_this.dropdown.find('.dropdown-toggle-page').length) {
var $dropdownMenu, $relatedTarget; selector = ".dropdown-page-one " + selector;
if (e.relatedTarget != null) { }
$relatedTarget = $(e.relatedTarget); return $(selector);
$dropdownMenu = $relatedTarget.closest('.dropdown-menu'); };
if ($dropdownMenu.length === 0) { })(this),
return _this.dropdown.removeClass('open'); 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)); })(this));
}
if (this.options.selectable) {
selector = ".dropdown-content a";
if (this.dropdown.find(".dropdown-toggle-page").length) { if (this.dropdown.find(".dropdown-toggle-page").length) {
this.dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on("click", (function(_this) { selector = ".dropdown-page-one .dropdown-content a";
return function(e) { }
e.preventDefault(); this.dropdown.on("click", selector, function(e) {
e.stopPropagation(); var $el, selected, selectedObj, isMarking;
return _this.togglePage(); $el = $(this);
}; selected = self.rowClicked($el);
})(this)); selectedObj = selected ? selected[0] : null;
} isMarking = selected ? selected[1] : null;
if (this.options.selectable) { if (self.options.clicked) {
selector = ".dropdown-content a"; self.options.clicked(selectedObj, $el, e, isMarking);
if (this.dropdown.find(".dropdown-toggle-page").length) {
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);
}
// Update label right after all modifications in dropdown has been done // Update label right after all modifications in dropdown has been done
if (self.options.toggleLabel) { if (self.options.toggleLabel) {
self.updateLabel(selectedObj, $el, self); self.updateLabel(selectedObj, $el, self);
} }
$el.trigger('blur'); $el.trigger('blur');
}); });
}
} }
}
// Finds an element inside wrapper element // Finds an element inside wrapper element
GitLabDropdown.prototype.getElement = function(selector) { GitLabDropdown.prototype.getElement = function(selector) {
return this.dropdown.find(selector); return this.dropdown.find(selector);
}; };
GitLabDropdown.prototype.toggleLoading = function() { GitLabDropdown.prototype.toggleLoading = function() {
return $('.dropdown-menu', this.dropdown).toggleClass(LOADING_CLASS); return $('.dropdown-menu', this.dropdown).toggleClass(LOADING_CLASS);
}; };
GitLabDropdown.prototype.togglePage = function() { GitLabDropdown.prototype.togglePage = function() {
var menu; var menu;
menu = $('.dropdown-menu', this.dropdown); menu = $('.dropdown-menu', this.dropdown);
if (menu.hasClass(PAGE_TWO_CLASS)) { if (menu.hasClass(PAGE_TWO_CLASS)) {
if (this.remote) { if (this.remote) {
this.remote.execute(); 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;
} }
return data.map((function(_this) { }
return function(obj, index) { menu.toggleClass(PAGE_TWO_CLASS);
return _this.renderItem(obj, group, index); // Focus first visible input on active page
}; return this.dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus();
})(this)); };
};
GitLabDropdown.prototype.parseData = function(data) {
GitLabDropdown.prototype.shouldPropagate = function(e) { var full_html, groupData, html, name;
var $target; this.renderedData = data;
if (this.options.multiSelect) { if (this.options.filterable && data.length === 0) {
$target = $(e.target); // render no matching results
if ($target && !$target.hasClass('dropdown-menu-close') && html = [this.noResults()];
!$target.hasClass('dropdown-menu-close-icon') && } else {
!$target.data('is-link')) { // Handle array groups
e.stopPropagation(); if (gl.utils.isObject(data)) {
return false; html = [];
} else { for (name in data) {
return true; 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) { GitLabDropdown.prototype.renderData = function(data, group) {
var contentHtml; if (group == null) {
this.resetRows(); group = false;
this.addArrowKeyEvent(); }
return data.map((function(_this) {
return function(obj, index) {
return _this.renderItem(obj, group, index);
};
})(this));
};
// Makes indeterminate items effective GitLabDropdown.prototype.shouldPropagate = function(e) {
if (this.fullData && this.dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')) { var $target;
this.parseData(this.fullData); if (this.options.multiSelect) {
} $target = $(e.target);
contentHtml = $('.dropdown-content', this.dropdown).html(); if ($target && !$target.hasClass('dropdown-menu-close') &&
if (this.remote && contentHtml === "") { !$target.hasClass('dropdown-menu-close-icon') &&
this.remote.execute(); !$target.data('is-link')) {
e.stopPropagation();
return false;
} else { } else {
this.focusTextInput(); return true;
} }
}
};
if (this.options.showMenuAbove) { GitLabDropdown.prototype.opened = function(e) {
this.positionMenuAbove(); var contentHtml;
} this.resetRows();
this.addArrowKeyEvent();
if (this.options.opened) { // Makes indeterminate items effective
this.options.opened.call(this, e); 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() { return this.dropdown.trigger('shown.gl.dropdown');
var $button = $(this.el); };
var $menu = this.dropdown.find('.dropdown-menu');
$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) { $menu.css('top', ($button.height() + $menu.height()) * -1);
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');
};
// Render the full menu GitLabDropdown.prototype.hidden = function(e) {
GitLabDropdown.prototype.renderMenu = function(html) { var $input;
if (this.options.renderMenu) { this.resetRows();
return this.options.renderMenu(html); this.removeArrayKeyEvent();
} else { $input = this.dropdown.find(".dropdown-input-field");
var ul = document.createElement('ul'); 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) { // Render the full menu
var el = html[i]; GitLabDropdown.prototype.renderMenu = function(html) {
if (this.options.renderMenu) {
return this.options.renderMenu(html);
} else {
var ul = document.createElement('ul');
if (el instanceof jQuery) { for (var i = 0; i < html.length; i += 1) {
el = el.get(0); var el = html[i];
}
if (typeof el === 'string') { if (el instanceof jQuery) {
ul.innerHTML += el; el = el.get(0);
} else {
ul.appendChild(el);
}
} }
return ul; if (typeof el === 'string') {
ul.innerHTML += el;
} else {
ul.appendChild(el);
}
} }
};
// Append the menu into the dropdown return ul;
GitLabDropdown.prototype.appendMenu = function(html) { }
return this.clearMenu().append(html); };
};
// Append the menu into the dropdown
GitLabDropdown.prototype.appendMenu = function(html) {
return this.clearMenu().append(html);
};
GitLabDropdown.prototype.clearMenu = function() { GitLabDropdown.prototype.clearMenu = function() {
var selector; var selector;
selector = '.dropdown-content'; selector = '.dropdown-content';
if (this.dropdown.find(".dropdown-toggle-page").length) { if (this.dropdown.find(".dropdown-toggle-page").length) {
selector = ".dropdown-page-one .dropdown-content"; selector = ".dropdown-page-one .dropdown-content";
} }
return $(selector, this.dropdown).empty(); return $(selector, this.dropdown).empty();
}; };
GitLabDropdown.prototype.renderItem = function(data, group, index) { GitLabDropdown.prototype.renderItem = function(data, group, index) {
var field, fieldName, html, selected, text, url, value; var field, fieldName, html, selected, text, url, value;
if (group == null) { if (group == null) {
group = false; 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) { // Set URL
// Render the row if (this.options.url != null) {
index = false; url = this.options.url(data);
} else {
url = data.url != null ? data.url : '#';
} }
html = document.createElement('li'); // Set Text
if (data === 'divider' || data === 'separator') { if (this.options.text != null) {
html.className = data; text = this.options.text(data);
return html; } else {
text = data.text != null ? data.text : '';
} }
// Header if (this.highlight) {
if (data.header != null) { text = this.highlightTextMatches(text, this.filterInput.val());
html.className = 'dropdown-header';
html.innerHTML = data.header;
return html;
} }
if (this.options.renderRow) { // Create the list item & the link
// Call the render function var link = document.createElement('a');
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;
if (selected) { link.href = url;
link.className = 'is-active'; link.innerHTML = text;
}
if (group) {
link.dataset.group = group;
link.dataset.index = index;
}
html.appendChild(link); if (selected) {
link.className = 'is-active';
} }
return html;
}; if (group) {
link.dataset.group = group;
GitLabDropdown.prototype.highlightTextMatches = function(text, term) { link.dataset.index = index;
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 (this.options.vue) { html.appendChild(link);
if (el.hasClass(ACTIVE_CLASS)) { }
el.removeClass(ACTIVE_CLASS); return html;
} else { };
el.addClass(ACTIVE_CLASS);
}
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 = []; GitLabDropdown.prototype.noResults = function() {
value = this.options.id var html;
? this.options.id(selectedObject, el) return html = "<li class='dropdown-menu-empty-link'> <a href='#' class='is-focused'> No matching results. </a> </li>";
: selectedObject.id; };
if (isInput) {
field = $(this.el); GitLabDropdown.prototype.rowClicked = function(el) {
} else if (value) { var field, fieldName, groupName, isInput, selectedIndex, selectedObject, value, isMarking;
field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']");
}
if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) { fieldName = this.options.fieldName;
return; 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)) { if (el.hasClass(ACTIVE_CLASS)) {
isMarking = false;
el.removeClass(ACTIVE_CLASS); 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 { } 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); 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() { field = [];
if (this.options.filterable) { this.filterInput.focus(); } 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) { if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) {
var $input; return;
// Create hidden input for form }
$input = $('<input>').attr('type', 'hidden').attr('name', fieldName).val(value);
if (this.options.inputId != null) { if (el.hasClass(ACTIVE_CLASS)) {
$input.attr('id', this.options.inputId); isMarking = false;
} el.removeClass(ACTIVE_CLASS);
return this.dropdown.before($input); if (field && field.length) {
}; this.clearField(field, isInput);
}
GitLabDropdown.prototype.selectRowAtIndex = function(index) { } else if (el.hasClass(INDETERMINATE_CLASS)) {
var $el, selector; isMarking = true;
// If we pass an option index el.addClass(ACTIVE_CLASS);
if (typeof index !== "undefined") { el.removeClass(INDETERMINATE_CLASS);
selector = SELECTABLE_CLASSES + ":eq(" + index + ") a"; if (field && field.length && value == null) {
} else { this.clearField(field, isInput);
selector = ".dropdown-content .is-focused"; }
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) { if (field && field.length && value == null) {
selector = ".dropdown-page-one " + selector; this.clearField(field, isInput);
} }
// simulate a click on the first link // Toggle active class for the tick mark
$el = $(selector, this.dropdown); el.addClass(ACTIVE_CLASS);
if ($el.length) { if (value != null) {
var href = $el.attr('href'); if ((!field || !field.length) && fieldName) {
if (href && href !== '#') { this.addInput(fieldName, value, selectedObject);
gl.utils.visitUrl(href); } else if (field && field.length) {
} else { field.val(value).trigger('change');
$el.first().trigger('click');
} }
} }
}; }
GitLabDropdown.prototype.addArrowKeyEvent = function() { return [selectedObject, isMarking];
var $input, ARROW_KEY_CODES, selector; };
ARROW_KEY_CODES = [38, 40];
$input = this.dropdown.find(".dropdown-input-field"); GitLabDropdown.prototype.focusTextInput = function() {
selector = SELECTABLE_CLASSES; if (this.options.filterable) { this.filterInput.focus(); }
if (this.dropdown.find(".dropdown-toggle-page").length) { };
selector = ".dropdown-page-one " + selector;
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; GitLabDropdown.prototype.addArrowKeyEvent = function() {
if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) { var $input, ARROW_KEY_CODES, selector;
e.preventDefault(); ARROW_KEY_CODES = [38, 40];
e.stopImmediatePropagation(); $input = this.dropdown.find(".dropdown-input-field");
PREV_INDEX = currentIndex; selector = SELECTABLE_CLASSES;
$listItems = $(selector, _this.dropdown); if (this.dropdown.find(".dropdown-toggle-page").length) {
// if @options.filterable selector = ".dropdown-page-one " + selector;
// $input.blur() }
if (currentKeyCode === 40) { return $('body').on('keydown', (function(_this) {
// Move down return function(e) {
if (currentIndex < ($listItems.length - 1)) { var $listItems, PREV_INDEX, currentKeyCode;
currentIndex += 1; currentKeyCode = e.which;
} if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) {
} else if (currentKeyCode === 38) { e.preventDefault();
// Move up e.stopImmediatePropagation();
if (currentIndex > 0) { PREV_INDEX = currentIndex;
currentIndex -= 1; $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) { } else if (currentKeyCode === 38) {
_this.highlightRowAtIndex($listItems, currentIndex); // Move up
if (currentIndex > 0) {
currentIndex -= 1;
} }
return false;
} }
if (currentKeyCode === 13 && currentIndex !== -1) { if (currentIndex !== PREV_INDEX) {
e.preventDefault(); _this.highlightRowAtIndex($listItems, currentIndex);
_this.selectRowAtIndex();
} }
}; return false;
})(this)); }
}; if (currentKeyCode === 13 && currentIndex !== -1) {
e.preventDefault();
GitLabDropdown.prototype.removeArrayKeyEvent = function() { _this.selectRowAtIndex();
return $('body').off('keydown'); }
}; };
})(this));
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);
}
};
GitLabDropdown.prototype.updateLabel = function(selected, el, instance) { GitLabDropdown.prototype.removeArrayKeyEvent = function() {
if (selected == null) { return $('body').off('keydown');
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) { GitLabDropdown.prototype.resetRows = function resetRows() {
return isInput ? field.val('') : field.remove(); 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) { GitLabDropdown.prototype.updateLabel = function(selected, el, instance) {
return this.each(function() { if (selected == null) {
if (!$.data(this, 'glDropdown')) { selected = null;
return $.data(this, 'glDropdown', new GitLabDropdown(this, opts)); }
} 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));
}
});
};
/* 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
* This class overrides the browser's validation error bubbles, displaying custom * class `gl-show-field-errors` to the form element, and ensure error messages are
* error messages for invalid fields instead. To begin validating any form, add the * declared in each inputs' `title` attribute. If no title is declared for an invalid
* class `gl-show-field-errors` to the form element, and ensure error messages are * field the user attempts to submit, "This field is required." will be shown by default.
* 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.
* *
* 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`
* Set a custom error anchor for error message to be injected after with the *
* class `gl-field-error-anchor` * Examples:
* *
* Examples: * Basic:
* *
* Basic: * <form class='gl-show-field-errors'>
* * <input type='text' name='username' title='Username is required.'/>
* <form class='gl-show-field-errors'> * </form>
* <input type='text' name='username' title='Username is required.'/> *
* </form> * Ignore specific inputs (e.g. UsernameValidator):
* *
* Ignore specific inputs (e.g. UsernameValidator): * <form class='gl-show-field-errors'>
* * <div class="form-group>
* <form class='gl-show-field-errors'> * <input type='text' class='gl-field-errors-ignore' pattern='[a-zA-Z0-9-_]+'/>
* <div class="form-group> * </div>
* <input type='text' class='gl-field-errors-ignore' pattern='[a-zA-Z0-9-_]+'/> * <div class="form-group">
* </div> * <input type='text' name='username' title='Username is required.'/>
* <div class="form-group"> * </div>
* <input type='text' name='username' title='Username is required.'/> * </form>
* </div> *
* </form> * Custom Error Anchor (allows error message to be injected after specified element):
* *
* 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">
* <form class='gl-show-field-errors'> * <input type='text' name='username' title='Username is required.'/>
* <div class="form-group gl-field-error-anchor"> * // Error message typically injected here
* <input type='text' name='username' title='Username is required.'/> * </div>
* // Error message typically injected here * // Error message now injected here
* </div> * </form>
* // Error message now injected here *
* </form> */
*
* */ /**
* Regex Patterns in use:
/* *
* Regex Patterns in use: * Only alphanumeric: : "[a-zA-Z0-9]+"
* * No special characters : "[a-zA-Z0-9-_]+",
* 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 errorMessageClass = 'gl-field-error'; const errorAnchorSelector = '.gl-field-error-anchor';
const inputErrorClass = 'gl-field-error-outline'; const ignoreInputSelector = '.gl-field-error-ignore';
const errorAnchorSelector = '.gl-field-error-anchor';
const ignoreInputSelector = '.gl-field-error-ignore'; class GlFieldError {
constructor({ input, formErrors }) {
class GlFieldError { this.inputElement = $(input);
constructor({ input, formErrors }) { this.inputDomElement = this.inputElement.get(0);
this.inputElement = $(input); this.form = formErrors;
this.inputDomElement = this.inputElement.get(0); this.errorMessage = this.inputElement.attr('title') || 'This field is required.';
this.form = formErrors; this.fieldErrorElement = $(`<p class='${errorMessageClass} hide'>${this.errorMessage}</p>`);
this.errorMessage = this.inputElement.attr('title') || 'This field is required.';
this.fieldErrorElement = $(`<p class='${errorMessageClass} hide'>${this.errorMessage}</p>`); this.state = {
valid: false,
this.state = { empty: true,
valid: false, };
empty: true,
}; this.initFieldValidation();
}
this.initFieldValidation();
}
initFieldValidation() { initFieldValidation() {
const customErrorAnchor = this.inputElement.parents(errorAnchorSelector); const customErrorAnchor = this.inputElement.parents(errorAnchorSelector);
const errorAnchor = customErrorAnchor.length ? customErrorAnchor : this.inputElement; const errorAnchor = customErrorAnchor.length ? customErrorAnchor : this.inputElement;
// hidden when injected into DOM // hidden when injected into DOM
errorAnchor.after(this.fieldErrorElement); errorAnchor.after(this.fieldErrorElement);
this.inputElement.off('invalid').on('invalid', this.handleInvalidSubmit.bind(this)); this.inputElement.off('invalid').on('invalid', this.handleInvalidSubmit.bind(this));
this.scopedSiblings = this.safelySelectSiblings(); this.scopedSiblings = this.safelySelectSiblings();
} }
safelySelectSiblings() { safelySelectSiblings() {
// Apply `ignoreSelector` in markup to siblings whose visibility should not be toggled // Apply `ignoreSelector` in markup to siblings whose visibility should not be toggled
const unignoredSiblings = this.inputElement.siblings(`p:not(${ignoreInputSelector})`); const unignoredSiblings = this.inputElement.siblings(`p:not(${ignoreInputSelector})`);
const parentContainer = this.inputElement.parent('.form-group'); const parentContainer = this.inputElement.parent('.form-group');
// Only select siblings when they're scoped within a form-group with one input // Only select siblings when they're scoped within a form-group with one input
const safelyScoped = parentContainer.length && parentContainer.find('input').length === 1; const safelyScoped = parentContainer.length && parentContainer.find('input').length === 1;
return safelyScoped ? unignoredSiblings : this.fieldErrorElement; return safelyScoped ? unignoredSiblings : this.fieldErrorElement;
} }
renderValidity() { renderValidity() {
this.renderClear(); this.renderClear();
if (this.state.valid) { if (this.state.valid) {
this.renderValid(); this.renderValid();
} else if (this.state.empty) { } else if (this.state.empty) {
this.renderEmpty(); this.renderEmpty();
} else if (!this.state.valid) { } else if (!this.state.valid) {
this.renderInvalid(); this.renderInvalid();
}
} }
}
handleInvalidSubmit(event) { handleInvalidSubmit(event) {
event.preventDefault(); event.preventDefault();
const currentValue = this.accessCurrentValue(); const currentValue = this.accessCurrentValue();
this.state.valid = false; this.state.valid = false;
this.state.empty = currentValue === ''; this.state.empty = currentValue === '';
this.renderValidity(); this.renderValidity();
this.form.focusOnFirstInvalid.apply(this.form); this.form.focusOnFirstInvalid.apply(this.form);
// For UX, wait til after first invalid submission to check each keyup // For UX, wait til after first invalid submission to check each keyup
this.inputElement.off('keyup.fieldValidator') this.inputElement.off('keyup.fieldValidator')
.on('keyup.fieldValidator', this.updateValidity.bind(this)); .on('keyup.fieldValidator', this.updateValidity.bind(this));
} }
/* Get or set current input value */ /* Get or set current input value */
accessCurrentValue(newVal) { accessCurrentValue(newVal) {
return newVal ? this.inputElement.val(newVal) : this.inputElement.val(); return newVal ? this.inputElement.val(newVal) : this.inputElement.val();
} }
getInputValidity() { getInputValidity() {
return this.inputDomElement.validity.valid; return this.inputDomElement.validity.valid;
} }
updateValidity() { updateValidity() {
const inputVal = this.accessCurrentValue(); const inputVal = this.accessCurrentValue();
this.state.empty = !inputVal.length; this.state.empty = !inputVal.length;
this.state.valid = this.getInputValidity(); this.state.valid = this.getInputValidity();
this.renderValidity(); this.renderValidity();
} }
renderValid() { renderValid() {
return this.renderClear(); return this.renderClear();
} }
renderEmpty() { renderEmpty() {
return this.renderInvalid(); return this.renderInvalid();
} }
renderInvalid() { renderInvalid() {
this.inputElement.addClass(inputErrorClass); this.inputElement.addClass(inputErrorClass);
this.scopedSiblings.hide(); this.scopedSiblings.hide();
return this.fieldErrorElement.show(); return this.fieldErrorElement.show();
} }
renderClear() { renderClear() {
const inputVal = this.accessCurrentValue(); const inputVal = this.accessCurrentValue();
if (!inputVal.split(' ').length) { if (!inputVal.split(' ').length) {
const trimmedInput = inputVal.trim(); const trimmedInput = inputVal.trim();
this.accessCurrentValue(trimmedInput); this.accessCurrentValue(trimmedInput);
}
this.inputElement.removeClass(inputErrorClass);
this.scopedSiblings.hide();
this.fieldErrorElement.hide();
} }
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;
...@@ -2,47 +2,46 @@ ...@@ -2,47 +2,46 @@
require('./gl_field_error'); require('./gl_field_error');
((global) => { const customValidationFlag = 'gl-field-error-ignore';
const customValidationFlag = 'gl-field-error-ignore';
class GlFieldErrors {
class GlFieldErrors { constructor(form) {
constructor(form) { this.form = $(form);
this.form = $(form); this.state = {
this.state = { inputs: [],
inputs: [], valid: false
valid: false };
}; this.initValidators();
this.initValidators(); }
}
initValidators () { initValidators () {
// register selectors here as needed // register selectors here as needed
const validateSelectors = [':text', ':password', '[type=email]'] const validateSelectors = [':text', ':password', '[type=email]']
.map((selector) => `input${selector}`).join(','); .map((selector) => `input${selector}`).join(',');
this.state.inputs = this.form.find(validateSelectors).toArray() this.state.inputs = this.form.find(validateSelectors).toArray()
.filter((input) => !input.classList.contains(customValidationFlag)) .filter((input) => !input.classList.contains(customValidationFlag))
.map((input) => new global.GlFieldError({ input, formErrors: this })); .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 /* Neccessary to prevent intercept and override invalid form submit
* because Safari & iOS quietly allow form submission when form is invalid * because Safari & iOS quietly allow form submission when form is invalid
* and prevents disabling of invalid submit button by application.js */ * and prevents disabling of invalid submit button by application.js */
catchInvalidFormSubmit (event) { catchInvalidFormSubmit (event) {
if (!event.currentTarget.checkValidity()) { if (!event.currentTarget.checkValidity()) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
}
} }
}
focusOnFirstInvalid () { focusOnFirstInvalid () {
const firstInvalid = this.state.inputs.filter((input) => !input.inputDomElement.validity.valid)[0]; const firstInvalid = this.state.inputs.filter((input) => !input.inputDomElement.validity.valid)[0];
firstInvalid.inputElement.focus(); firstInvalid.inputElement.focus();
}
} }
}
global.GlFieldErrors = GlFieldErrors; window.gl = window.gl || {};
})(window.gl || (window.gl = {})); window.gl.GlFieldErrors = GlFieldErrors;
...@@ -3,90 +3,88 @@ ...@@ -3,90 +3,88 @@
/* global DropzoneInput */ /* global DropzoneInput */
/* global autosize */ /* global autosize */
(() => { window.gl = window.gl || {};
const global = window.gl || (window.gl = {});
function GLForm(form) { function GLForm(form) {
this.form = form; this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input'); this.textarea = this.form.find('textarea.js-gfm-input');
// Before we start, we should clean up any previous data for this form // Before we start, we should clean up any previous data for this form
this.destroy(); this.destroy();
// Setup the form // Setup the form
this.setupForm(); this.setupForm();
this.form.data('gl-form', this); this.form.data('gl-form', this);
} }
GLForm.prototype.destroy = function() { GLForm.prototype.destroy = function() {
// Clean form listeners // Clean form listeners
this.clearEventListeners(); this.clearEventListeners();
return this.form.data('gl-form', null); return this.form.data('gl-form', null);
}; };
GLForm.prototype.setupForm = function() { GLForm.prototype.setupForm = function() {
var isNewForm; var isNewForm;
isNewForm = this.form.is(':not(.gfm-form)'); isNewForm = this.form.is(':not(.gfm-form)');
this.form.removeClass('js-new-note-form'); this.form.removeClass('js-new-note-form');
if (isNewForm) { if (isNewForm) {
this.form.find('.div-dropzone').remove(); this.form.find('.div-dropzone').remove();
this.form.addClass('gfm-form'); this.form.addClass('gfm-form');
// remove notify commit author checkbox for non-commit notes // 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.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input')); gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
new DropzoneInput(this.form); new DropzoneInput(this.form);
autosize(this.textarea); autosize(this.textarea);
// form and textarea event listeners // form and textarea event listeners
this.addEventListeners(); this.addEventListeners();
} }
gl.text.init(this.form); gl.text.init(this.form);
// hide discard button // hide discard button
this.form.find('.js-note-discard').hide(); this.form.find('.js-note-discard').hide();
this.form.show(); this.form.show();
if (this.isAutosizeable) this.setupAutosize(); if (this.isAutosizeable) this.setupAutosize();
}; };
GLForm.prototype.setupAutosize = function () { GLForm.prototype.setupAutosize = function () {
this.textarea.off('autosize:resized') this.textarea.off('autosize:resized')
.on('autosize:resized', this.setHeightData.bind(this)); .on('autosize:resized', this.setHeightData.bind(this));
this.textarea.off('mouseup.autosize') this.textarea.off('mouseup.autosize')
.on('mouseup.autosize', this.destroyAutosize.bind(this)); .on('mouseup.autosize', this.destroyAutosize.bind(this));
setTimeout(() => { setTimeout(() => {
autosize(this.textarea); autosize(this.textarea);
this.textarea.css('resize', 'vertical'); this.textarea.css('resize', 'vertical');
}, 0); }, 0);
}; };
GLForm.prototype.setHeightData = function () { GLForm.prototype.setHeightData = function () {
this.textarea.data('height', this.textarea.outerHeight()); this.textarea.data('height', this.textarea.outerHeight());
}; };
GLForm.prototype.destroyAutosize = function () { GLForm.prototype.destroyAutosize = function () {
const outerHeight = this.textarea.outerHeight(); 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.data('height', outerHeight);
this.textarea.outerHeight(outerHeight); this.textarea.outerHeight(outerHeight);
this.textarea.css('max-height', window.outerHeight); this.textarea.css('max-height', window.outerHeight);
}; };
GLForm.prototype.clearEventListeners = function() { GLForm.prototype.clearEventListeners = function() {
this.textarea.off('focus'); this.textarea.off('focus');
this.textarea.off('blur'); this.textarea.off('blur');
return gl.text.removeListeners(this.form); return gl.text.removeListeners(this.form);
}; };
GLForm.prototype.addEventListeners = function() { GLForm.prototype.addEventListeners = function() {
this.textarea.on('focus', function() { this.textarea.on('focus', function() {
return $(this).closest('.md-area').addClass('is-focused'); return $(this).closest('.md-area').addClass('is-focused');
}); });
return this.textarea.on('blur', function() { return this.textarea.on('blur', function() {
return $(this).closest('.md-area').removeClass('is-focused'); return $(this).closest('.md-area').removeClass('is-focused');
}); });
}; };
global.GLForm = GLForm; window.gl.GLForm = GLForm;
})();
/* 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 */ /* 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; window.GroupAvatar = (function() {
})(); function GroupAvatar() {
}).call(window); $('.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;
})();
/* eslint-disable func-names, object-shorthand, comma-dangle, wrap-iife, space-before-function-paren, no-param-reassign, max-len */ /* eslint-disable func-names, object-shorthand, comma-dangle, wrap-iife, space-before-function-paren, no-param-reassign, max-len */
(function(global) { class GroupLabelSubscription {
class GroupLabelSubscription { constructor(container) {
constructor(container) { const $container = $(container);
const $container = $(container); this.$dropdown = $container.find('.dropdown');
this.$dropdown = $container.find('.dropdown'); this.$subscribeButtons = $container.find('.js-subscribe-button');
this.$subscribeButtons = $container.find('.js-subscribe-button'); this.$unsubscribeButtons = $container.find('.js-unsubscribe-button');
this.$unsubscribeButtons = $container.find('.js-unsubscribe-button');
this.$subscribeButtons.on('click', this.subscribe.bind(this));
this.$subscribeButtons.on('click', this.subscribe.bind(this)); this.$unsubscribeButtons.on('click', this.unsubscribe.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');
}
} }
global.GroupLabelSubscription = GroupLabelSubscription; unsubscribe(event) {
})(window.gl || (window.gl = {})); 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;
/* 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 */ /* 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 */ /* global Api */
(function() { var slice = [].slice;
var slice = [].slice;
this.GroupsSelect = (function() { window.GroupsSelect = (function() {
function GroupsSelect() { function GroupsSelect() {
$('.ajax-groups-select').each((function(_this) { $('.ajax-groups-select').each((function(_this) {
return function(i, select) { return function(i, select) {
var all_available, skip_groups; var all_available, skip_groups;
all_available = $(select).data('all-available'); all_available = $(select).data('all-available');
skip_groups = $(select).data('skip-groups') || []; skip_groups = $(select).data('skip-groups') || [];
return $(select).select2({ return $(select).select2({
placeholder: "Search for a group", placeholder: "Search for a group",
multiple: $(select).hasClass('multiselect'), multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0, minimumInputLength: 0,
query: function(query) { query: function(query) {
var options = { all_available: all_available, skip_groups: skip_groups }; var options = { all_available: all_available, skip_groups: skip_groups };
return Api.groups(query.term, options, function(groups) { return Api.groups(query.term, options, function(groups) {
var data; var data;
data = { data = {
results: groups results: groups
}; };
return query.callback(data); return query.callback(data);
}); });
}, },
initSelection: function(element, callback) { initSelection: function(element, callback) {
var id; var id;
id = $(element).val(); id = $(element).val();
if (id !== "") { if (id !== "") {
return Api.group(id, callback); 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;
} }
}); },
}; formatResult: function() {
})(this)); 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) { GroupsSelect.prototype.formatResult = function(group) {
var avatar; var avatar;
if (group.avatar_url) { if (group.avatar_url) {
avatar = group.avatar_url; avatar = group.avatar_url;
} else { } else {
avatar = gon.default_avatar_url; 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>"; 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) { GroupsSelect.prototype.formatSelection = function(group) {
return group.full_name; return group.full_name;
}; };
return GroupsSelect; return GroupsSelect;
})(); })();
}).call(window);
/* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, no-var, max-len */ /* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var */
(function() {
$(document).on('todo:toggle', function(e, count) { $(document).on('todo:toggle', function(e, count) {
var $todoPendingCount = $('.todos-pending-count'); var $todoPendingCount = $('.todos-pending-count');
$todoPendingCount.text(gl.text.highCountTrim(count)); $todoPendingCount.text(gl.text.highCountTrim(count));
$todoPendingCount.toggleClass('hidden', count === 0); $todoPendingCount.toggleClass('hidden', count === 0);
}); });
})();
...@@ -198,189 +198,187 @@ import './visibility_select'; ...@@ -198,189 +198,187 @@ import './visibility_select';
import './wikis'; import './wikis';
import './zen_mode'; import './zen_mode';
(function () { document.addEventListener('beforeunload', function () {
document.addEventListener('beforeunload', function () { // Unbind scroll events
// Unbind scroll events $(document).off('scroll');
$(document).off('scroll'); // Close any open tooltips
// Close any open tooltips $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy');
$('.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);
$(function () { window.addEventListener('hashchange', gl.utils.handleLocationHash);
var $body = $('body'); window.addEventListener('load', function onLoad() {
var $document = $(document); window.removeEventListener('load', onLoad, false);
var $window = $(window); gl.utils.handleLocationHash();
var $sidebarGutterToggle = $('.js-sidebar-toggle'); }, false);
var $flash = $('.flash-container');
var bootstrapBreakpoint = bp.getBreakpointSize();
var fitSidebarForSize;
// Set the default path for all cookies to GitLab's root directory $(function () {
Cookies.defaults.path = gon.relative_url_root || '/'; 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 // Set the default path for all cookies to GitLab's root directory
$body.on('click', 'a[href^="#"]', function() { Cookies.defaults.path = gon.relative_url_root || '/';
var href = this.getAttribute('href');
if (href.substr(1) === gl.utils.getLocationHash()) {
setTimeout(gl.utils.handleLocationHash, 1);
}
});
// prevent default action for disabled buttons // `hashchange` is not triggered when link target is already in window.location
$('.btn').click(function(e) { $body.on('click', 'a[href^="#"]', function() {
if ($(this).hasClass('disabled')) { var href = this.getAttribute('href');
e.preventDefault(); if (href.substr(1) === gl.utils.getLocationHash()) {
e.stopImmediatePropagation(); setTimeout(gl.utils.handleLocationHash, 1);
return false; }
} });
});
$('.js-select-on-focus').on('focusin', function () { // prevent default action for disabled buttons
return $(this).select().one('mouseup', function (e) { $('.btn').click(function(e) {
return e.preventDefault(); if ($(this).hasClass('disabled')) {
}); e.preventDefault();
// Click a .js-select-on-focus field, select the contents e.stopImmediatePropagation();
// Prevent a mouseup event from deselecting the input return false;
});
$('.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();
} }
// Disable form buttons while a form is submitting });
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) {
var buttons; $('.js-select-on-focus').on('focusin', function () {
buttons = $('[type="submit"]', this); return $(this).select().one('mouseup', function (e) {
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(); return e.preventDefault();
}); });
$document.off('click', '.js-confirm-danger'); // Click a .js-select-on-focus field, select the contents
$document.on('click', '.js-confirm-danger', function (e) { // Prevent a mouseup event from deselecting the input
var btn = $(e.target); });
var form = btn.closest('form'); $('.remove-row').bind('ajax:success', function () {
var text = btn.data('confirm-danger-message'); $(this).tooltip('destroy')
e.preventDefault(); .closest('li')
return new ConfirmDangerModal(form, text); .fadeOut();
}); });
$('input[type="search"]').each(function () { $('.js-remove-tr').bind('ajax:before', function () {
var $this = $(this); return $(this).hide();
$this.attr('value', $this.val()); });
}); $('.js-remove-tr').bind('ajax:success', function () {
$document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function () { return $(this).closest('tr').fadeOut();
var $this; });
$this = $(this); $('select.select2').select2({
return $this.attr('value', $this.val()); width: 'resolve',
}); // Initialize select2 selects
$document.off('breakpoint:change').on('breakpoint:change', function (e, breakpoint) { dropdownAutoWidth: true
var $gutterIcon; });
if (breakpoint === 'sm' || breakpoint === 'xs') { $('.js-select2').bind('select2-close', function () {
$gutterIcon = $sidebarGutterToggle.find('i'); return setTimeout((function () {
if ($gutterIcon.hasClass('fa-angle-double-right')) { $('.select2-container-active').removeClass('select2-container-active');
return $sidebarGutterToggle.trigger('click'); 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 () { $flash.show();
var oldBootstrapBreakpoint; }
oldBootstrapBreakpoint = bootstrapBreakpoint; // Disable form buttons while a form is submitting
bootstrapBreakpoint = bp.getBreakpointSize(); $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) {
if (bootstrapBreakpoint !== oldBootstrapBreakpoint) { var buttons;
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]); 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();
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment