Коммит d2cb058a создал по автору Jacob Schatz's avatar Jacob Schatz
Просмотр файлов

Initial commit of coffee to js

владелец 95c4825a
// Generated by CoffeeScript 1.10.0
(function() {
this.LabelManager = (function() {
LabelManager.prototype.errorMessage = 'Unable to update label prioritization at this time';
function LabelManager(opts) {
var ref, ref1, ref2;
if (opts == null) {
opts = {};
}
this.togglePriorityButton = (ref = opts.togglePriorityButton) != null ? ref : $('.js-toggle-priority'), this.prioritizedLabels = (ref1 = opts.prioritizedLabels) != null ? ref1 : $('.js-prioritized-labels'), this.otherLabels = (ref2 = opts.otherLabels) != null ? ref2 : $('.js-other-labels');
this.prioritizedLabels.sortable({
items: 'li',
placeholder: 'list-placeholder',
axis: 'y',
update: this.onPrioritySortUpdate.bind(this)
});
this.bindEvents();
}
LabelManager.prototype.bindEvents = function() {
return this.togglePriorityButton.on('click', this, this.onTogglePriorityClick);
};
LabelManager.prototype.onTogglePriorityClick = function(e) {
var $btn, $label, $tooltip, _this, action;
e.preventDefault();
_this = e.data;
$btn = $(e.currentTarget);
$label = $("#" + ($btn.data('domId')));
action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
$tooltip = $("#" + ($btn.find('.has-tooltip:visible').attr('aria-describedby')));
$tooltip.tooltip('destroy');
return _this.toggleLabelPriority($label, action);
};
LabelManager.prototype.toggleLabelPriority = function($label, action, persistState) {
var $from, $target, _this, url, xhr;
if (persistState == null) {
persistState = true;
}
_this = this;
url = $label.find('.js-toggle-priority').data('url');
$target = this.prioritizedLabels;
$from = this.otherLabels;
if (action === 'remove') {
$target = this.otherLabels;
$from = this.prioritizedLabels;
}
if ($from.find('li').length === 1) {
$from.find('.empty-message').removeClass('hidden');
}
if (!$target.find('li').length) {
$target.find('.empty-message').addClass('hidden');
}
$label.detach().appendTo($target);
if (!persistState) {
return;
}
if (action === 'remove') {
xhr = $.ajax({
url: url,
type: 'DELETE'
});
if (!$from.find('li').length) {
$from.find('.empty-message').removeClass('hidden');
}
} else {
xhr = this.savePrioritySort($label, action);
}
return xhr.fail(this.rollbackLabelPosition.bind(this, $label, action));
};
LabelManager.prototype.onPrioritySortUpdate = function() {
var xhr;
xhr = this.savePrioritySort();
return xhr.fail(function() {
return new Flash(this.errorMessage, 'alert');
});
};
LabelManager.prototype.savePrioritySort = function() {
return $.post({
url: this.prioritizedLabels.data('url'),
data: {
label_ids: this.getSortedLabelsIds()
}
});
};
LabelManager.prototype.rollbackLabelPosition = function($label, originalAction) {
var action;
action = originalAction === 'remove' ? 'add' : 'remove';
this.toggleLabelPriority($label, action, false);
return new Flash(this.errorMessage, 'alert');
};
LabelManager.prototype.getSortedLabelsIds = function() {
var sortedIds;
sortedIds = [];
this.prioritizedLabels.find('li').each(function() {
return sortedIds.push($(this).data('id'));
});
return sortedIds;
};
return LabelManager;
})();
}).call(this);
class @LabelManager
errorMessage: 'Unable to update label prioritization at this time'
constructor: (opts = {}) ->
# Defaults
{
@togglePriorityButton = $('.js-toggle-priority')
@prioritizedLabels = $('.js-prioritized-labels')
@otherLabels = $('.js-other-labels')
} = opts
@prioritizedLabels.sortable(
items: 'li'
placeholder: 'list-placeholder'
axis: 'y'
update: @onPrioritySortUpdate.bind(@)
)
@bindEvents()
bindEvents: ->
@togglePriorityButton.on 'click', @, @onTogglePriorityClick
onTogglePriorityClick: (e) ->
e.preventDefault()
_this = e.data
$btn = $(e.currentTarget)
$label = $("##{$btn.data('domId')}")
action = if $btn.parents('.js-prioritized-labels').length then 'remove' else 'add'
# Make sure tooltip will hide
$tooltip = $ "##{$btn.find('.has-tooltip:visible').attr('aria-describedby')}"
$tooltip.tooltip 'destroy'
_this.toggleLabelPriority($label, action)
toggleLabelPriority: ($label, action, persistState = true) ->
_this = @
url = $label.find('.js-toggle-priority').data 'url'
$target = @prioritizedLabels
$from = @otherLabels
# Optimistic update
if action is 'remove'
$target = @otherLabels
$from = @prioritizedLabels
if $from.find('li').length is 1
$from.find('.empty-message').removeClass('hidden')
if not $target.find('li').length
$target.find('.empty-message').addClass('hidden')
$label.detach().appendTo($target)
# Return if we are not persisting state
return unless persistState
if action is 'remove'
xhr = $.ajax url: url, type: 'DELETE'
# Restore empty message
$from.find('.empty-message').removeClass('hidden') unless $from.find('li').length
else
xhr = @savePrioritySort($label, action)
xhr.fail @rollbackLabelPosition.bind(@, $label, action)
onPrioritySortUpdate: ->
xhr = @savePrioritySort()
xhr.fail ->
new Flash(@errorMessage, 'alert')
savePrioritySort: () ->
$.post
url: @prioritizedLabels.data('url')
data:
label_ids: @getSortedLabelsIds()
rollbackLabelPosition: ($label, originalAction)->
action = if originalAction is 'remove' then 'add' else 'remove'
@toggleLabelPriority($label, action, false)
new Flash(@errorMessage, 'alert')
getSortedLabelsIds: ->
sortedIds = []
@prioritizedLabels.find('li').each ->
sortedIds.push $(@).data 'id'
sortedIds
// Generated by CoffeeScript 1.10.0
(function() {
this.Activities = (function() {
function Activities() {
Pager.init(20, true, false, this.updateTooltips);
$(".event-filter-link").on("click", (function(_this) {
return function(event) {
event.preventDefault();
_this.toggleFilter($(event.currentTarget));
return _this.reloadActivities();
};
})(this));
}
Activities.prototype.updateTooltips = function() {
return gl.utils.localTimeAgo($('.js-timeago', '#activity'));
};
Activities.prototype.reloadActivities = function() {
$(".content_list").html('');
return Pager.init(20, true);
};
Activities.prototype.toggleFilter = function(sender) {
var event_filters, filter;
$('.event-filter .active').removeClass("active");
event_filters = $.cookie("event_filter");
filter = sender.attr("id").split("_")[0];
$.cookie("event_filter", (event_filters !== filter ? filter : ""), {
path: '/'
});
if (event_filters !== filter) {
return sender.closest('li').toggleClass("active");
}
};
return Activities;
})();
}).call(this);
class @Activities
constructor: ->
Pager.init 20, true, false, @updateTooltips
$(".event-filter-link").on "click", (event) =>
event.preventDefault()
@toggleFilter($(event.currentTarget))
@reloadActivities()
updateTooltips: ->
gl.utils.localTimeAgo($('.js-timeago', '#activity'))
reloadActivities: ->
$(".content_list").html ''
Pager.init 20, true
toggleFilter: (sender) ->
$('.event-filter .active').removeClass "active"
event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0]
$.cookie "event_filter", (if event_filters isnt filter then filter else ""), { path: '/' }
if event_filters isnt filter
sender.closest('li').toggleClass "active"
// Generated by CoffeeScript 1.10.0
(function() {
this.Admin = (function() {
function Admin() {
var modal, showBlacklistType;
$('input#user_force_random_password').on('change', function(elem) {
var elems;
elems = $('#user_password, #user_password_confirmation');
if ($(this).attr('checked')) {
return elems.val('').attr('disabled', true);
} else {
return elems.removeAttr('disabled');
}
});
$('body').on('click', '.js-toggle-colors-link', function(e) {
e.preventDefault();
return $('.js-toggle-colors-container').toggle();
});
$('.log-tabs a').click(function(e) {
e.preventDefault();
return $(this).tab('show');
});
$('.log-bottom').click(function(e) {
var visible_log;
e.preventDefault();
visible_log = $(".file-content:visible");
return visible_log.animate({
scrollTop: visible_log.find('ol').height()
}, "fast");
});
modal = $('.change-owner-holder');
$('.change-owner-link').bind("click", function(e) {
e.preventDefault();
$(this).hide();
return modal.show();
});
$('.change-owner-cancel-link').bind("click", function(e) {
e.preventDefault();
modal.hide();
return $('.change-owner-link').show();
});
$('li.project_member').bind('ajax:success', function() {
return Turbolinks.visit(location.href);
});
$('li.group_member').bind('ajax:success', function() {
return Turbolinks.visit(location.href);
});
showBlacklistType = function() {
if ($("input[name='blacklist_type']:checked").val() === 'file') {
$('.blacklist-file').show();
return $('.blacklist-raw').hide();
} else {
$('.blacklist-file').hide();
return $('.blacklist-raw').show();
}
};
$("input[name='blacklist_type']").click(showBlacklistType);
showBlacklistType();
}
return Admin;
})();
}).call(this);
class @Admin
constructor: ->
$('input#user_force_random_password').on 'change', (elem) ->
elems = $('#user_password, #user_password_confirmation')
if $(@).attr 'checked'
elems.val('').attr 'disabled', true
else
elems.removeAttr 'disabled'
$('body').on 'click', '.js-toggle-colors-link', (e) ->
e.preventDefault()
$('.js-toggle-colors-container').toggle()
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')
$('.log-bottom').click (e) ->
e.preventDefault()
visible_log = $(".file-content:visible")
visible_log.animate({ scrollTop: visible_log.find('ol').height() }, "fast")
modal = $('.change-owner-holder')
$('.change-owner-link').bind "click", (e) ->
e.preventDefault()
$(this).hide()
modal.show()
$('.change-owner-cancel-link').bind "click", (e) ->
e.preventDefault()
modal.hide()
$('.change-owner-link').show()
$('li.project_member').bind 'ajax:success', ->
Turbolinks.visit(location.href)
$('li.group_member').bind 'ajax:success', ->
Turbolinks.visit(location.href)
showBlacklistType = ->
if $("input[name='blacklist_type']:checked").val() == 'file'
$('.blacklist-file').show()
$('.blacklist-raw').hide()
else
$('.blacklist-file').hide()
$('.blacklist-raw').show()
$("input[name='blacklist_type']").click showBlacklistType
showBlacklistType()
// Generated by CoffeeScript 1.10.0
(function() {
this.Api = {
groupsPath: "/api/:version/groups.json",
groupPath: "/api/:version/groups/:id.json",
namespacesPath: "/api/:version/namespaces.json",
groupProjectsPath: "/api/:version/groups/:id/projects.json",
projectsPath: "/api/:version/projects.json?simple=true",
labelsPath: "/api/:version/projects/:id/labels",
licensePath: "/api/:version/licenses/:key",
gitignorePath: "/api/:version/gitignores/:key",
gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key",
group: function(group_id, callback) {
var url;
url = Api.buildUrl(Api.groupPath);
url = url.replace(':id', group_id);
return $.ajax({
url: url,
data: {
private_token: gon.api_token
},
dataType: "json"
}).done(function(group) {
return callback(group);
});
},
groups: function(query, skip_ldap, callback) {
var url;
url = Api.buildUrl(Api.groupsPath);
return $.ajax({
url: url,
data: {
private_token: gon.api_token,
search: query,
per_page: 20
},
dataType: "json"
}).done(function(groups) {
return callback(groups);
});
},
namespaces: function(query, callback) {
var url;
url = Api.buildUrl(Api.namespacesPath);
return $.ajax({
url: url,
data: {
private_token: gon.api_token,
search: query,
per_page: 20
},
dataType: "json"
}).done(function(namespaces) {
return callback(namespaces);
});
},
projects: function(query, order, callback) {
var url;
url = Api.buildUrl(Api.projectsPath);
return $.ajax({
url: url,
data: {
private_token: gon.api_token,
search: query,
order_by: order,
per_page: 20
},
dataType: "json"
}).done(function(projects) {
return callback(projects);
});
},
newLabel: function(project_id, data, callback) {
var url;
url = Api.buildUrl(Api.labelsPath);
url = url.replace(':id', project_id);
data.private_token = gon.api_token;
return $.ajax({
url: url,
type: "POST",
data: data,
dataType: "json"
}).done(function(label) {
return callback(label);
}).error(function(message) {
return callback(message.responseJSON);
});
},
groupProjects: function(group_id, query, callback) {
var url;
url = Api.buildUrl(Api.groupProjectsPath);
url = url.replace(':id', group_id);
return $.ajax({
url: url,
data: {
private_token: gon.api_token,
search: query,
per_page: 20
},
dataType: "json"
}).done(function(projects) {
return callback(projects);
});
},
licenseText: function(key, data, callback) {
var url;
url = Api.buildUrl(Api.licensePath).replace(':key', key);
return $.ajax({
url: url,
data: data
}).done(function(license) {
return callback(license);
});
},
gitignoreText: function(key, callback) {
var url;
url = Api.buildUrl(Api.gitignorePath).replace(':key', key);
return $.get(url, function(gitignore) {
return callback(gitignore);
});
},
gitlabCiYml: function(key, callback) {
var url;
url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key);
return $.get(url, function(file) {
return callback(file);
});
},
buildUrl: function(url) {
if (gon.relative_url_root != null) {
url = gon.relative_url_root + url;
}
return url.replace(':version', gon.api_version);
}
};
}).call(this);
@Api =
groupsPath: "/api/:version/groups.json"
groupPath: "/api/:version/groups/:id.json"
namespacesPath: "/api/:version/namespaces.json"
groupProjectsPath: "/api/:version/groups/:id/projects.json"
projectsPath: "/api/:version/projects.json?simple=true"
labelsPath: "/api/:version/projects/:id/labels"
licensePath: "/api/:version/licenses/:key"
gitignorePath: "/api/:version/gitignores/:key"
gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key"
group: (group_id, callback) ->
url = Api.buildUrl(Api.groupPath)
url = url.replace(':id', group_id)
$.ajax(
url: url
data:
private_token: gon.api_token
dataType: "json"
).done (group) ->
callback(group)
# Return groups list. Filtered by query
# Only active groups retrieved
groups: (query, skip_ldap, callback) ->
url = Api.buildUrl(Api.groupsPath)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (groups) ->
callback(groups)
# Return namespaces list. Filtered by query
namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespacesPath)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (namespaces) ->
callback(namespaces)
# Return projects list. Filtered by query
projects: (query, order, callback) ->
url = Api.buildUrl(Api.projectsPath)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
order_by: order
per_page: 20
dataType: "json"
).done (projects) ->
callback(projects)
newLabel: (project_id, data, callback) ->
url = Api.buildUrl(Api.labelsPath)
url = url.replace(':id', project_id)
data.private_token = gon.api_token
$.ajax(
url: url
type: "POST"
data: data
dataType: "json"
).done (label) ->
callback(label)
.error (message) ->
callback(message.responseJSON)
# Return group projects list. Filtered by query
groupProjects: (group_id, query, callback) ->
url = Api.buildUrl(Api.groupProjectsPath)
url = url.replace(':id', group_id)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (projects) ->
callback(projects)
# Return text for a specific license
licenseText: (key, data, callback) ->
url = Api.buildUrl(Api.licensePath).replace(':key', key)
$.ajax(
url: url
data: data
).done (license) ->
callback(license)
gitignoreText: (key, callback) ->
url = Api.buildUrl(Api.gitignorePath).replace(':key', key)
$.get url, (gitignore) ->
callback(gitignore)
gitlabCiYml: (key, callback) ->
url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key)
$.get url, (file) ->
callback(file)
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
// Generated by CoffeeScript 1.10.0
(function() {
window.slugify = function(text) {
return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase();
};
window.ajaxGet = function(url) {
return $.ajax({
type: "GET",
url: url,
dataType: "script"
});
};
window.split = function(val) {
return val.split(/,\s*/);
};
window.extractLast = function(term) {
return split(term).pop();
};
window.rstrip = function(val) {
if (val) {
return val.replace(/\s+$/, '');
} else {
return val;
}
};
window.disableButtonIfEmptyField = function(field_selector, button_selector) {
var closest_submit, field;
field = $(field_selector);
closest_submit = field.closest('form').find(button_selector);
if (rstrip(field.val()) === "") {
closest_submit.disable();
}
return field.on('input', function() {
if (rstrip($(this).val()) === "") {
return closest_submit.disable();
} else {
return closest_submit.enable();
}
});
};
window.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
var closest_submit, updateButtons;
closest_submit = form.find(button_selector);
updateButtons = function() {
var filled;
filled = true;
form.find('input').filter(form_selector).each(function() {
return filled = rstrip($(this).val()) !== "" || !$(this).attr('required');
});
if (filled) {
return closest_submit.enable();
} else {
return closest_submit.disable();
}
};
updateButtons();
return form.keyup(updateButtons);
};
window.sanitize = function(str) {
return str.replace(/<(?:.|\n)*?>/gm, '');
};
window.unbindEvents = function() {
return $(document).off('scroll');
};
window.shiftWindow = function() {
return scrollBy(0, -100);
};
document.addEventListener("page:fetch", unbindEvents);
window.addEventListener("hashchange", shiftWindow);
window.onload = function() {
if (location.hash) {
return setTimeout(shiftWindow, 100);
}
};
$(function() {
var $body, $document, $sidebarGutterToggle, $window, bootstrapBreakpoint, checkInitialSidebarSize, fitSidebarForSize, flash;
$document = $(document);
$window = $(window);
$body = $('body');
gl.utils.preventDisabledButtons();
bootstrapBreakpoint = bp.getBreakpointSize();
$(".nav-sidebar").niceScroll({
cursoropacitymax: '0.4',
cursorcolor: '#FFF',
cursorborder: "1px solid #FFF"
});
$(".js-select-on-focus").on("focusin", function() {
return $(this).select().one('mouseup', function(e) {
return e.preventDefault();
});
});
$('.remove-row').bind('ajax:success', function() {
return $(this).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',
dropdownAutoWidth: true
});
$('.js-select2').bind('select2-close', function() {
return setTimeout((function() {
$('.select2-container-active').removeClass('select2-container-active');
return $(':focus').blur();
}), 1);
});
$body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]',
placement: function(_, el) {
var $el;
$el = $(el);
return $el.data('placement') || 'bottom';
}
});
$('.trigger-submit').on('change', function() {
return $(this).parents('form').submit();
});
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
if ((flash = $(".flash-container")).length > 0) {
flash.click(function() {
return $(this).fadeOut();
});
flash.show();
}
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function(e) {
var buttons;
buttons = $('[type="submit"]', this);
switch (e.type) {
case 'ajax:beforeSend':
case 'submit':
return buttons.disable();
default:
return buttons.enable();
}
});
$(document).ajaxError(function(e, xhrObj, xhrSetting, xhrErrorText) {
var ref;
if (xhrObj.status === 401) {
return new Flash('You need to be logged in.', 'alert');
} else if ((ref = xhrObj.status) === 404 || ref === 500) {
return new Flash('Something went wrong on our end.', 'alert');
}
});
$('.account-box').hover(function() {
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();
});
$('.navbar-toggle').on('click', function() {
$('.header-content .title').toggle();
$('.header-content .header-logo').toggle();
$('.header-content .navbar-collapse').toggle();
return $('.navbar-toggle').toggleClass('active');
});
$body.on("click", ".js-toggle-diff-comments", function(e) {
$(this).toggleClass('active');
$(this).closest(".diff-file").find(".notes_holder").toggle();
return e.preventDefault();
});
$document.off("click", '.js-confirm-danger');
$document.on("click", '.js-confirm-danger', function(e) {
var btn, form, text;
e.preventDefault();
btn = $(e.target);
text = btn.data("confirm-danger-message");
form = btn.closest("form");
return new ConfirmDangerModal(form, text);
});
$document.on('click', 'button', function() {
return $(this).blur();
});
$('input[type="search"]').each(function() {
var $this;
$this = $(this);
$this.attr('value', $this.val());
});
$document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function(e) {
var $this;
$this = $(this);
return $this.attr('value', $this.val());
});
$sidebarGutterToggle = $('.js-sidebar-toggle');
$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');
}
}
});
fitSidebarForSize = function() {
var oldBootstrapBreakpoint;
oldBootstrapBreakpoint = bootstrapBreakpoint;
bootstrapBreakpoint = bp.getBreakpointSize();
if (bootstrapBreakpoint !== oldBootstrapBreakpoint) {
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
}
};
checkInitialSidebarSize = function() {
bootstrapBreakpoint = bp.getBreakpointSize();
if (bootstrapBreakpoint === "xs" || "sm") {
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
}
};
$window.off("resize.app").on("resize.app", function(e) {
return fitSidebarForSize();
});
gl.awardsHandler = new AwardsHandler();
checkInitialSidebarSize();
new Aside();
if ($window.width() < 1024 && $.cookie('pin_nav') === 'true') {
$.cookie('pin_nav', 'false', {
path: '/',
expires: 365 * 10
});
$('.page-with-sidebar').toggleClass('page-sidebar-collapsed page-sidebar-expanded').removeClass('page-sidebar-pinned');
$('.navbar-fixed-top').removeClass('header-pinned-nav');
}
return $document.off('click', '.js-nav-pin').on('click', '.js-nav-pin', function(e) {
var $page, $pinBtn, $tooltip, $topNav, doPinNav, tooltipText;
e.preventDefault();
$pinBtn = $(e.currentTarget);
$page = $('.page-with-sidebar');
$topNav = $('.navbar-fixed-top');
$tooltip = $("#" + ($pinBtn.attr('aria-describedby')));
doPinNav = !$page.is('.page-sidebar-pinned');
tooltipText = 'Pin navigation';
$(this).toggleClass('is-active');
if (doPinNav) {
$page.addClass('page-sidebar-pinned');
$topNav.addClass('header-pinned-nav');
} else {
$tooltip.remove();
$page.removeClass('page-sidebar-pinned').toggleClass('page-sidebar-collapsed page-sidebar-expanded');
$topNav.removeClass('header-pinned-nav').toggleClass('header-collapsed header-expanded');
}
$.cookie('pin_nav', doPinNav, {
path: '/',
expires: 365 * 10
});
if ($.cookie('pin_nav') === 'true' || doPinNav) {
tooltipText = 'Unpin navigation';
}
$tooltip.find('.tooltip-inner').text(tooltipText);
return $pinBtn.attr('title', tooltipText).tooltip('fixTitle');
});
});
}).call(this);
# This is a manifest file that'll be compiled into including all the files listed below.
# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
# be included in the compiled file accessible from http://example.com/assets/application.js
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require jquery2
#= require jquery-ui/autocomplete
#= require jquery-ui/datepicker
#= require jquery-ui/draggable
#= require jquery-ui/effect-highlight
#= require jquery-ui/sortable
#= require jquery_ujs
#= require jquery.cookie
#= require jquery.endless-scroll
#= require jquery.highlight
#= require jquery.waitforimages
#= require jquery.atwho
#= require jquery.scrollTo
#= require jquery.turbolinks
#= require turbolinks
#= require autosave
#= require bootstrap/affix
#= require bootstrap/alert
#= require bootstrap/button
#= require bootstrap/collapse
#= require bootstrap/dropdown
#= require bootstrap/modal
#= require bootstrap/scrollspy
#= require bootstrap/tab
#= require bootstrap/transition
#= require bootstrap/tooltip
#= require bootstrap/popover
#= require select2
#= require ace/ace
#= require ace/ext-searchbox
#= require underscore
#= require dropzone
#= require mousetrap
#= require mousetrap/pause
#= require shortcuts
#= require shortcuts_navigation
#= require shortcuts_dashboard_navigation
#= require shortcuts_issuable
#= require shortcuts_network
#= require jquery.nicescroll
#= require date.format
#= require_directory ./behaviors
#= require_directory ./blob
#= require_directory ./commit
#= require_directory ./extensions
#= require_directory ./lib/utils
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"})
window.split = (val) ->
return val.split( /,\s*/ )
window.extractLast = (term) ->
return split( term ).pop()
window.rstrip = (val) ->
return if val then val.replace(/\s+$/, '') else val
# Disable button if text field is empty
window.disableButtonIfEmptyField = (field_selector, button_selector) ->
field = $(field_selector)
closest_submit = field.closest('form').find(button_selector)
closest_submit.disable() if rstrip(field.val()) is ""
field.on 'input', ->
if rstrip($(@).val()) is ""
closest_submit.disable()
else
closest_submit.enable()
# Disable button if any input field with given selector is empty
window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
closest_submit = form.find(button_selector)
updateButtons = ->
filled = true
form.find('input').filter(form_selector).each ->
filled = rstrip($(this).val()) != "" || !$(this).attr('required')
if filled
closest_submit.enable()
else
closest_submit.disable()
updateButtons()
form.keyup(updateButtons)
window.sanitize = (str) ->
return str.replace(/<(?:.|\n)*?>/gm, '')
window.unbindEvents = ->
$(document).off('scroll')
window.shiftWindow = ->
scrollBy 0, -100
document.addEventListener("page:fetch", unbindEvents)
window.addEventListener "hashchange", shiftWindow
window.onload = ->
# Scroll the window to avoid the topnav bar
# https://github.com/twitter/bootstrap/issues/1768
if location.hash
setTimeout shiftWindow, 100
$ ->
$document = $(document)
$window = $(window)
$body = $('body')
gl.utils.preventDisabledButtons()
bootstrapBreakpoint = bp.getBreakpointSize()
$(".nav-sidebar").niceScroll(cursoropacitymax: '0.4', cursorcolor: '#FFF', cursorborder: "1px solid #FFF")
# Click a .js-select-on-focus field, select the contents
$(".js-select-on-focus").on "focusin", ->
# Prevent a mouseup event from deselecting the input
$(this).select().one 'mouseup', (e) ->
e.preventDefault()
$('.remove-row').bind 'ajax:success', ->
$(this).closest('li').fadeOut()
$('.js-remove-tr').bind 'ajax:before', ->
$(this).hide()
$('.js-remove-tr').bind 'ajax:success', ->
$(this).closest('tr').fadeOut()
# Initialize select2 selects
$('select.select2').select2(width: 'resolve', dropdownAutoWidth: true)
# Close select2 on escape
$('.js-select2').bind 'select2-close', ->
setTimeout ( ->
$('.select2-container-active').removeClass('select2-container-active')
$(':focus').blur()
), 1
# Initialize tooltips
$body.tooltip(
selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) ->
$el = $(el)
$el.data('placement') || 'bottom'
)
# Form submitter
$('.trigger-submit').on 'change', ->
$(@).parents('form').submit()
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true)
# Flash
if (flash = $(".flash-container")).length > 0
flash.click -> $(@).fadeOut()
flash.show()
# Disable form buttons while a form is submitting
$body.on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', @)
switch e.type
when 'ajax:beforeSend', 'submit'
buttons.disable()
else
buttons.enable()
$(document).ajaxError (e, xhrObj, xhrSetting, xhrErrorText) ->
if xhrObj.status is 401
new Flash 'You need to be logged in.', 'alert'
else if xhrObj.status in [ 404, 500 ]
new Flash 'Something went wrong on our end.', 'alert'
# Show/Hide the profile menu when hovering the account box
$('.account-box').hover -> $(@).toggleClass('hover')
# Commit show suppressed diff
$document.on 'click', '.diff-content .js-show-suppressed-diff', ->
$container = $(@).parent()
$container.next('table').show()
$container.remove()
$('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle()
$('.header-content .header-logo').toggle()
$('.header-content .navbar-collapse').toggle()
$('.navbar-toggle').toggleClass('active')
# Show/hide comments on diff
$body.on "click", ".js-toggle-diff-comments", (e) ->
$(@).toggleClass('active')
$(@).closest(".diff-file").find(".notes_holder").toggle()
e.preventDefault()
$document.off "click", '.js-confirm-danger'
$document.on "click", '.js-confirm-danger', (e) ->
e.preventDefault()
btn = $(e.target)
text = btn.data("confirm-danger-message")
form = btn.closest("form")
new ConfirmDangerModal(form, text)
$document.on 'click', 'button', ->
$(this).blur()
$('input[type="search"]').each ->
$this = $(this)
$this.attr 'value', $this.val()
return
$document
.off 'keyup', 'input[type="search"]'
.on 'keyup', 'input[type="search"]' , (e) ->
$this = $(this)
$this.attr 'value', $this.val()
$sidebarGutterToggle = $('.js-sidebar-toggle')
$document
.off 'breakpoint:change'
.on 'breakpoint:change', (e, breakpoint) ->
if breakpoint is 'sm' or breakpoint is 'xs'
$gutterIcon = $sidebarGutterToggle.find('i')
if $gutterIcon.hasClass('fa-angle-double-right')
$sidebarGutterToggle.trigger('click')
fitSidebarForSize = ->
oldBootstrapBreakpoint = bootstrapBreakpoint
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint != oldBootstrapBreakpoint
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
checkInitialSidebarSize = ->
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint is "xs" or "sm"
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
$window
.off "resize.app"
.on "resize.app", (e) ->
fitSidebarForSize()
gl.awardsHandler = new AwardsHandler()
checkInitialSidebarSize()
new Aside()
# Sidenav pinning
if $window.width() < 1024 and $.cookie('pin_nav') is 'true'
$.cookie('pin_nav', 'false', { path: '/', expires: 365 * 10 })
$('.page-with-sidebar')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
.removeClass('page-sidebar-pinned')
$('.navbar-fixed-top').removeClass('header-pinned-nav')
$document
.off 'click', '.js-nav-pin'
.on 'click', '.js-nav-pin', (e) ->
e.preventDefault()
$pinBtn = $(e.currentTarget)
$page = $ '.page-with-sidebar'
$topNav = $ '.navbar-fixed-top'
$tooltip = $ "##{$pinBtn.attr('aria-describedby')}"
doPinNav = not $page.is('.page-sidebar-pinned')
tooltipText = 'Pin navigation'
$(this).toggleClass 'is-active'
if doPinNav
$page.addClass('page-sidebar-pinned')
$topNav.addClass('header-pinned-nav')
else
$tooltip.remove() # Remove it immediately when collapsing the sidebar
$page.removeClass('page-sidebar-pinned')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
$topNav.removeClass('header-pinned-nav')
.toggleClass('header-collapsed header-expanded')
# Save settings
$.cookie 'pin_nav', doPinNav, { path: '/', expires: 365 * 10 }
if $.cookie('pin_nav') is 'true' or doPinNav
tooltipText = 'Unpin navigation'
# Update tooltip text immediately
$tooltip.find('.tooltip-inner').text(tooltipText)
# Persist tooltip title
$pinBtn.attr('title', tooltipText).tooltip('fixTitle')
// Generated by CoffeeScript 1.10.0
(function() {
this.Aside = (function() {
function Aside() {
$(document).off("click", "a.show-aside");
$(document).on("click", 'a.show-aside', function(e) {
var btn, icon;
e.preventDefault();
btn = $(e.currentTarget);
icon = btn.find('i');
if (icon.hasClass('fa-angle-left')) {
btn.parent().find('section').hide();
btn.parent().find('aside').fadeIn();
return icon.removeClass('fa-angle-left').addClass('fa-angle-right');
} else {
btn.parent().find('aside').hide();
btn.parent().find('section').fadeIn();
return icon.removeClass('fa-angle-right').addClass('fa-angle-left');
}
});
}
return Aside;
})();
}).call(this);
class @Aside
constructor: ->
$(document).off "click", "a.show-aside"
$(document).on "click", 'a.show-aside', (e) ->
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()
icon.removeClass('fa-angle-left').addClass('fa-angle-right')
else
btn.parent().find('aside').hide()
btn.parent().find('section').fadeIn()
icon.removeClass('fa-angle-right').addClass('fa-angle-left')
// Generated by CoffeeScript 1.10.0
(function() {
this.Autosave = (function() {
function Autosave(field, key) {
this.field = field;
if (key.join != null) {
key = key.join("/");
}
this.key = "autosave/" + key;
this.field.data("autosave", this);
this.restore();
this.field.on("input", (function(_this) {
return function() {
return _this.save();
};
})(this));
}
Autosave.prototype.restore = function() {
var e, error, text;
if (window.localStorage == null) {
return;
}
try {
text = window.localStorage.getItem(this.key);
} catch (error) {
e = error;
return;
}
if ((text != null ? text.length : void 0) > 0) {
this.field.val(text);
}
return this.field.trigger("input");
};
Autosave.prototype.save = function() {
var text;
if (window.localStorage == null) {
return;
}
text = this.field.val();
if ((text != null ? text.length : void 0) > 0) {
try {
return window.localStorage.setItem(this.key, text);
} catch (undefined) {}
} else {
return this.reset();
}
};
Autosave.prototype.reset = function() {
if (window.localStorage == null) {
return;
}
try {
return window.localStorage.removeItem(this.key);
} catch (undefined) {}
};
return Autosave;
})();
}).call(this);
class @Autosave
constructor: (field, key) ->
@field = field
key = key.join("/") if key.join?
@key = "autosave/#{key}"
@field.data "autosave", this
@restore()
@field.on "input", => @save()
restore: ->
return unless window.localStorage?
try
text = window.localStorage.getItem @key
catch e
return
@field.val text if text?.length > 0
@field.trigger "input"
save: ->
return unless window.localStorage?
text = @field.val()
if text?.length > 0
try
window.localStorage.setItem @key, text
else
@reset()
reset: ->
return unless window.localStorage?
try
window.localStorage.removeItem @key
class @AwardsHandler
constructor: ->
@aliases = gl.emojiAliases()
$(document)
.off 'click', '.js-add-award'
.on 'click', '.js-add-award', (e) =>
e.stopPropagation()
e.preventDefault()
@showEmojiMenu $(e.currentTarget)
$('html').on 'click', (e) ->
$target = $ e.target
unless $target.closest('.emoji-menu-content').length
$('.js-awards-block.current').removeClass 'current'
unless $target.closest('.emoji-menu').length
if $('.emoji-menu').is(':visible')
$('.js-add-award.is-active').removeClass 'is-active'
$('.emoji-menu').removeClass 'is-visible'
$(document)
.off 'click', '.js-emoji-btn'
.on 'click', '.js-emoji-btn', (e) =>
e.preventDefault()
$target = $ e.currentTarget
emoji = $target.find('.icon').data 'emoji'
$target.closest('.js-awards-block').addClass 'current'
@addAward @getVotesBlock(), @getAwardUrl(), emoji
showEmojiMenu: ($addBtn) ->
$menu = $ '.emoji-menu'
if $addBtn.hasClass 'js-note-emoji'
$addBtn.closest('.note').find('.js-awards-block').addClass 'current'
else
$addBtn.closest('.js-awards-block').addClass 'current'
if $menu.length
$holder = $addBtn.closest('.js-award-holder')
if $menu.is '.is-visible'
$addBtn.removeClass 'is-active'
$menu.removeClass 'is-visible'
$('#emoji_search').blur()
else
$addBtn.addClass 'is-active'
@positionMenu($menu, $addBtn)
$menu.addClass 'is-visible'
$('#emoji_search').focus()
else
$addBtn.addClass 'is-loading is-active'
url = @getAwardMenuUrl()
@createEmojiMenu url, =>
$addBtn.removeClass 'is-loading'
$menu = $('.emoji-menu')
@positionMenu($menu, $addBtn)
@renderFrequentlyUsedBlock() unless @frequentEmojiBlockRendered
setTimeout =>
$menu.addClass 'is-visible'
$('#emoji_search').focus()
@setupSearch()
, 200
createEmojiMenu: (awardMenuUrl, callback) ->
$.get awardMenuUrl, (response) ->
$('body').append response
callback()
positionMenu: ($menu, $addBtn) ->
position = $addBtn.data('position')
# The menu could potentially be off-screen or in a hidden overflow element
# So we position the element absolute in the body
css =
top: "#{$addBtn.offset().top + $addBtn.outerHeight()}px"
if position? and position is 'right'
css.left = "#{($addBtn.offset().left - $menu.outerWidth()) + 20}px"
$menu.addClass 'is-aligned-right'
else
css.left = "#{$addBtn.offset().left}px"
$menu.removeClass 'is-aligned-right'
$menu.css(css)
addAward: (votesBlock, awardUrl, emoji, checkMutuality = true, callback) ->
emoji = @normilizeEmojiName emoji
@postEmoji awardUrl, emoji, =>
@addAwardToEmojiBar votesBlock, emoji, checkMutuality
callback?()
$('.emoji-menu').removeClass 'is-visible'
addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = true) ->
@checkMutuality votesBlock, emoji if checkForMutuality
@addEmojiToFrequentlyUsedList emoji
emoji = @normilizeEmojiName emoji
$emojiButton = @findEmojiIcon(votesBlock, emoji).parent()
if $emojiButton.length > 0
if @isActive $emojiButton
@decrementCounter $emojiButton, emoji
else
counter = $emojiButton.find '.js-counter'
counter.text parseInt(counter.text()) + 1
$emojiButton.addClass 'active'
@addMeToUserList votesBlock, emoji
@animateEmoji $emojiButton
else
votesBlock.removeClass 'hidden'
@createEmoji votesBlock, emoji
getVotesBlock: ->
currentBlock = $ '.js-awards-block.current'
return if currentBlock.length then currentBlock else $('.js-awards-block').eq 0
getAwardUrl: -> return @getVotesBlock().data 'award-url'
checkMutuality: (votesBlock, emoji) ->
awardUrl = @getAwardUrl()
if emoji in [ 'thumbsup', 'thumbsdown' ]
mutualVote = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup'
$emojiButton = votesBlock.find("[data-emoji=#{mutualVote}]").parent()
isAlreadyVoted = $emojiButton.hasClass 'active'
if isAlreadyVoted
@showEmojiLoader $emojiButton
@addAward votesBlock, awardUrl, mutualVote, false, ->
$emojiButton.removeClass 'is-loading'
showEmojiLoader: ($emojiButton) ->
$loader = $emojiButton.find '.fa-spinner'
unless $loader.length
$emojiButton.append '<i class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>'
$emojiButton.addClass 'is-loading'
isActive: ($emojiButton) -> $emojiButton.hasClass 'active'
decrementCounter: ($emojiButton, emoji) ->
counter = $ '.js-counter', $emojiButton
counterNumber = parseInt counter.text(), 10
if counterNumber > 1
counter.text counterNumber - 1
@removeMeFromUserList $emojiButton, emoji
else if emoji is 'thumbsup' or emoji is 'thumbsdown'
$emojiButton.tooltip 'destroy'
counter.text '0'
@removeMeFromUserList $emojiButton, emoji
@removeEmoji $emojiButton if $emojiButton.parents('.note').length
else
@removeEmoji $emojiButton
$emojiButton.removeClass 'active'
removeEmoji: ($emojiButton) ->
$emojiButton.tooltip('destroy')
$emojiButton.remove()
$votesBlock = @getVotesBlock()
if $votesBlock.find('.js-emoji-btn').length is 0
$votesBlock.addClass 'hidden'
getAwardTooltip: ($awardBlock) ->
return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title') or ''
removeMeFromUserList: ($emojiButton, emoji) ->
awardBlock = $emojiButton
originalTitle = @getAwardTooltip awardBlock
authors = originalTitle.split ', '
authors.splice authors.indexOf('me'), 1
newAuthors = authors.join ', '
awardBlock
.closest '.js-emoji-btn'
.removeData 'original-title'
.attr 'data-original-title', newAuthors
@resetTooltip awardBlock
addMeToUserList: (votesBlock, emoji) ->
awardBlock = @findEmojiIcon(votesBlock, emoji).parent()
origTitle = @getAwardTooltip awardBlock
users = []
if origTitle
users = origTitle.trim().split ', '
users.push 'me'
awardBlock.attr 'title', users.join ', '
@resetTooltip awardBlock
resetTooltip: (award) ->
award.tooltip 'destroy'
# 'destroy' call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout.
cb = -> award.tooltip()
setTimeout cb, 200
createEmoji_: (votesBlock, emoji) ->
emojiCssClass = @resolveNameToCssClass emoji
buttonHtml = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'>
<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>
<span class='award-control-text js-counter'>1</span>
</button>"
$emojiButton = $ buttonHtml
$emojiButton
.insertBefore votesBlock.find '.js-award-holder'
.find '.emoji-icon'
.data 'emoji', emoji
@animateEmoji $emojiButton
$('.award-control').tooltip()
votesBlock.removeClass 'current'
animateEmoji: ($emoji) ->
className = 'pulse animated'
$emoji.addClass className
setTimeout (-> $emoji.removeClass className), 321
createEmoji: (votesBlock, emoji) ->
if $('.emoji-menu').length
return @createEmoji_ votesBlock, emoji
@createEmojiMenu @getAwardMenuUrl(), => @createEmoji_ votesBlock, emoji
getAwardMenuUrl: -> return gon.award_menu_url
resolveNameToCssClass: (emoji) ->
emojiIcon = $ ".emoji-menu-content [data-emoji='#{emoji}']"
if emojiIcon.length > 0
unicodeName = emojiIcon.data 'unicode-name'
else
# Find by alias
unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data 'unicode-name'
return "emoji-#{unicodeName}"
postEmoji: (awardUrl, emoji, callback) ->
$.post awardUrl, { name: emoji }, (data) ->
callback() if data.ok
findEmojiIcon: (votesBlock, emoji) ->
return votesBlock.find ".js-emoji-btn [data-emoji='#{emoji}']"
scrollToAwards: ->
options = scrollTop: $('.awards').offset().top - 110
$('body, html').animate options, 200
normilizeEmojiName: (emoji) -> return @aliases[emoji] or emoji
addEmojiToFrequentlyUsedList: (emoji) ->
frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
frequentlyUsedEmojis.push emoji
$.cookie 'frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }
getFrequentlyUsedEmojis: ->
frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') or '').split(',')
return _.compact _.uniq frequentlyUsedEmojis
renderFrequentlyUsedBlock: ->
if $.cookie 'frequently_used_emojis'
frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
ul = $("<ul class='clearfix emoji-menu-list frequent-emojis'>")
for emoji in frequentlyUsedEmojis
$(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
$('.emoji-menu-content')
.prepend(ul)
.prepend($('<h5>').text('Frequently used'))
@frequentEmojiBlockRendered = true
setupSearch: ->
$('input.emoji-search').on 'keyup', (ev) =>
term = $(ev.target).val()
# Clean previous search results
$('ul.emoji-menu-search, h5.emoji-search').remove()
if term
# Generate a search result block
h5 = $('<h5>').text('Search results')
found_emojis = @searchEmojis(term).show()
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis)
$('.emoji-menu-content ul, .emoji-menu-content h5').hide()
$('.emoji-menu-content').append(h5).append(ul)
else
$('.emoji-menu-content').children().show()
searchEmojis: (term) ->
$(".emoji-menu-list:not(.frequent-emojis) [data-emoji*='#{term}']").closest('li').clone()
// Generated by CoffeeScript 1.10.0
(function() {
this.AwardsHandler = (function() {
function AwardsHandler() {
this.aliases = gl.emojiAliases();
$(document).off('click', '.js-add-award').on('click', '.js-add-award', (function(_this) {
return function(e) {
e.stopPropagation();
e.preventDefault();
return _this.showEmojiMenu($(e.currentTarget));
};
})(this));
$('html').on('click', function(e) {
var $target;
$target = $(e.target);
if (!$target.closest('.emoji-menu-content').length) {
$('.js-awards-block.current').removeClass('current');
}
if (!$target.closest('.emoji-menu').length) {
if ($('.emoji-menu').is(':visible')) {
$('.js-add-award.is-active').removeClass('is-active');
return $('.emoji-menu').removeClass('is-visible');
}
}
});
$(document).off('click', '.js-emoji-btn').on('click', '.js-emoji-btn', (function(_this) {
return function(e) {
var $target, emoji;
e.preventDefault();
$target = $(e.currentTarget);
emoji = $target.find('.icon').data('emoji');
$target.closest('.js-awards-block').addClass('current');
return _this.addAward(_this.getVotesBlock(), _this.getAwardUrl(), emoji);
};
})(this));
}
AwardsHandler.prototype.showEmojiMenu = function($addBtn) {
var $holder, $menu, url;
$menu = $('.emoji-menu');
if ($addBtn.hasClass('js-note-emoji')) {
$addBtn.closest('.note').find('.js-awards-block').addClass('current');
} else {
$addBtn.closest('.js-awards-block').addClass('current');
}
if ($menu.length) {
$holder = $addBtn.closest('.js-award-holder');
if ($menu.is('.is-visible')) {
$addBtn.removeClass('is-active');
$menu.removeClass('is-visible');
return $('#emoji_search').blur();
} else {
$addBtn.addClass('is-active');
this.positionMenu($menu, $addBtn);
$menu.addClass('is-visible');
return $('#emoji_search').focus();
}
} else {
$addBtn.addClass('is-loading is-active');
url = this.getAwardMenuUrl();
return this.createEmojiMenu(url, (function(_this) {
return function() {
$addBtn.removeClass('is-loading');
$menu = $('.emoji-menu');
_this.positionMenu($menu, $addBtn);
if (!_this.frequentEmojiBlockRendered) {
_this.renderFrequentlyUsedBlock();
}
return setTimeout(function() {
$menu.addClass('is-visible');
$('#emoji_search').focus();
return _this.setupSearch();
}, 200);
};
})(this));
}
};
AwardsHandler.prototype.createEmojiMenu = function(awardMenuUrl, callback) {
return $.get(awardMenuUrl, function(response) {
$('body').append(response);
return callback();
});
};
AwardsHandler.prototype.positionMenu = function($menu, $addBtn) {
var css, position;
position = $addBtn.data('position');
css = {
top: ($addBtn.offset().top + $addBtn.outerHeight()) + "px"
};
if ((position != null) && position === 'right') {
css.left = (($addBtn.offset().left - $menu.outerWidth()) + 20) + "px";
$menu.addClass('is-aligned-right');
} else {
css.left = ($addBtn.offset().left) + "px";
$menu.removeClass('is-aligned-right');
}
return $menu.css(css);
};
AwardsHandler.prototype.addAward = function(votesBlock, awardUrl, emoji, checkMutuality, callback) {
if (checkMutuality == null) {
checkMutuality = true;
}
emoji = this.normilizeEmojiName(emoji);
this.postEmoji(awardUrl, emoji, (function(_this) {
return function() {
_this.addAwardToEmojiBar(votesBlock, emoji, checkMutuality);
return typeof callback === "function" ? callback() : void 0;
};
})(this));
return $('.emoji-menu').removeClass('is-visible');
};
AwardsHandler.prototype.addAwardToEmojiBar = function(votesBlock, emoji, checkForMutuality) {
var $emojiButton, counter;
if (checkForMutuality == null) {
checkForMutuality = true;
}
if (checkForMutuality) {
this.checkMutuality(votesBlock, emoji);
}
this.addEmojiToFrequentlyUsedList(emoji);
emoji = this.normilizeEmojiName(emoji);
$emojiButton = this.findEmojiIcon(votesBlock, emoji).parent();
if ($emojiButton.length > 0) {
if (this.isActive($emojiButton)) {
return this.decrementCounter($emojiButton, emoji);
} else {
counter = $emojiButton.find('.js-counter');
counter.text(parseInt(counter.text()) + 1);
$emojiButton.addClass('active');
this.addMeToUserList(votesBlock, emoji);
return this.animateEmoji($emojiButton);
}
} else {
votesBlock.removeClass('hidden');
return this.createEmoji(votesBlock, emoji);
}
};
AwardsHandler.prototype.getVotesBlock = function() {
var currentBlock;
currentBlock = $('.js-awards-block.current');
if (currentBlock.length) {
return currentBlock;
} else {
return $('.js-awards-block').eq(0);
}
};
AwardsHandler.prototype.getAwardUrl = function() {
return this.getVotesBlock().data('award-url');
};
AwardsHandler.prototype.checkMutuality = function(votesBlock, emoji) {
var $emojiButton, awardUrl, isAlreadyVoted, mutualVote;
awardUrl = this.getAwardUrl();
if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
mutualVote = emoji === 'thumbsup' ? 'thumbsdown' : 'thumbsup';
$emojiButton = votesBlock.find("[data-emoji=" + mutualVote + "]").parent();
isAlreadyVoted = $emojiButton.hasClass('active');
if (isAlreadyVoted) {
this.showEmojiLoader($emojiButton);
return this.addAward(votesBlock, awardUrl, mutualVote, false, function() {
return $emojiButton.removeClass('is-loading');
});
}
}
};
AwardsHandler.prototype.showEmojiLoader = function($emojiButton) {
var $loader;
$loader = $emojiButton.find('.fa-spinner');
if (!$loader.length) {
$emojiButton.append('<i class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>');
}
return $emojiButton.addClass('is-loading');
};
AwardsHandler.prototype.isActive = function($emojiButton) {
return $emojiButton.hasClass('active');
};
AwardsHandler.prototype.decrementCounter = function($emojiButton, emoji) {
var counter, counterNumber;
counter = $('.js-counter', $emojiButton);
counterNumber = parseInt(counter.text(), 10);
if (counterNumber > 1) {
counter.text(counterNumber - 1);
this.removeMeFromUserList($emojiButton, emoji);
} else if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
$emojiButton.tooltip('destroy');
counter.text('0');
this.removeMeFromUserList($emojiButton, emoji);
if ($emojiButton.parents('.note').length) {
this.removeEmoji($emojiButton);
}
} else {
this.removeEmoji($emojiButton);
}
return $emojiButton.removeClass('active');
};
AwardsHandler.prototype.removeEmoji = function($emojiButton) {
var $votesBlock;
$emojiButton.tooltip('destroy');
$emojiButton.remove();
$votesBlock = this.getVotesBlock();
if ($votesBlock.find('.js-emoji-btn').length === 0) {
return $votesBlock.addClass('hidden');
}
};
AwardsHandler.prototype.getAwardTooltip = function($awardBlock) {
return $awardBlock.attr('data-original-title') || $awardBlock.attr('data-title') || '';
};
AwardsHandler.prototype.removeMeFromUserList = function($emojiButton, emoji) {
var authors, awardBlock, newAuthors, originalTitle;
awardBlock = $emojiButton;
originalTitle = this.getAwardTooltip(awardBlock);
authors = originalTitle.split(', ');
authors.splice(authors.indexOf('me'), 1);
newAuthors = authors.join(', ');
awardBlock.closest('.js-emoji-btn').removeData('original-title').attr('data-original-title', newAuthors);
return this.resetTooltip(awardBlock);
};
AwardsHandler.prototype.addMeToUserList = function(votesBlock, emoji) {
var awardBlock, origTitle, users;
awardBlock = this.findEmojiIcon(votesBlock, emoji).parent();
origTitle = this.getAwardTooltip(awardBlock);
users = [];
if (origTitle) {
users = origTitle.trim().split(', ');
}
users.push('me');
awardBlock.attr('title', users.join(', '));
return this.resetTooltip(awardBlock);
};
AwardsHandler.prototype.resetTooltip = function(award) {
var cb;
award.tooltip('destroy');
cb = function() {
return award.tooltip();
};
return setTimeout(cb, 200);
};
AwardsHandler.prototype.createEmoji_ = function(votesBlock, emoji) {
var $emojiButton, buttonHtml, emojiCssClass;
emojiCssClass = this.resolveNameToCssClass(emoji);
buttonHtml = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'> <div class='icon emoji-icon " + emojiCssClass + "' data-emoji='" + emoji + "'></div> <span class='award-control-text js-counter'>1</span> </button>";
$emojiButton = $(buttonHtml);
$emojiButton.insertBefore(votesBlock.find('.js-award-holder')).find('.emoji-icon').data('emoji', emoji);
this.animateEmoji($emojiButton);
$('.award-control').tooltip();
return votesBlock.removeClass('current');
};
AwardsHandler.prototype.animateEmoji = function($emoji) {
var className;
className = 'pulse animated';
$emoji.addClass(className);
return setTimeout((function() {
return $emoji.removeClass(className);
}), 321);
};
AwardsHandler.prototype.createEmoji = function(votesBlock, emoji) {
if ($('.emoji-menu').length) {
return this.createEmoji_(votesBlock, emoji);
}
return this.createEmojiMenu(this.getAwardMenuUrl(), (function(_this) {
return function() {
return _this.createEmoji_(votesBlock, emoji);
};
})(this));
};
AwardsHandler.prototype.getAwardMenuUrl = function() {
return gon.award_menu_url;
};
AwardsHandler.prototype.resolveNameToCssClass = function(emoji) {
var emojiIcon, unicodeName;
emojiIcon = $(".emoji-menu-content [data-emoji='" + emoji + "']");
if (emojiIcon.length > 0) {
unicodeName = emojiIcon.data('unicode-name');
} else {
unicodeName = $(".emoji-menu-content [data-aliases*=':" + emoji + ":']").data('unicode-name');
}
return "emoji-" + unicodeName;
};
AwardsHandler.prototype.postEmoji = function(awardUrl, emoji, callback) {
return $.post(awardUrl, {
name: emoji
}, function(data) {
if (data.ok) {
return callback();
}
});
};
AwardsHandler.prototype.findEmojiIcon = function(votesBlock, emoji) {
return votesBlock.find(".js-emoji-btn [data-emoji='" + emoji + "']");
};
AwardsHandler.prototype.scrollToAwards = function() {
var options;
options = {
scrollTop: $('.awards').offset().top - 110
};
return $('body, html').animate(options, 200);
};
AwardsHandler.prototype.normilizeEmojiName = function(emoji) {
return this.aliases[emoji] || emoji;
};
AwardsHandler.prototype.addEmojiToFrequentlyUsedList = function(emoji) {
var frequentlyUsedEmojis;
frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
frequentlyUsedEmojis.push(emoji);
return $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), {
expires: 365
});
};
AwardsHandler.prototype.getFrequentlyUsedEmojis = function() {
var frequentlyUsedEmojis;
frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(',');
return _.compact(_.uniq(frequentlyUsedEmojis));
};
AwardsHandler.prototype.renderFrequentlyUsedBlock = function() {
var emoji, frequentlyUsedEmojis, i, len, ul;
if ($.cookie('frequently_used_emojis')) {
frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
ul = $("<ul class='clearfix emoji-menu-list frequent-emojis'>");
for (i = 0, len = frequentlyUsedEmojis.length; i < len; i++) {
emoji = frequentlyUsedEmojis[i];
$(".emoji-menu-content [data-emoji='" + emoji + "']").closest('li').clone().appendTo(ul);
}
$('.emoji-menu-content').prepend(ul).prepend($('<h5>').text('Frequently used'));
}
return this.frequentEmojiBlockRendered = true;
};
AwardsHandler.prototype.setupSearch = function() {
return $('input.emoji-search').on('keyup', (function(_this) {
return function(ev) {
var found_emojis, h5, term, ul;
term = $(ev.target).val();
$('ul.emoji-menu-search, h5.emoji-search').remove();
if (term) {
h5 = $('<h5>').text('Search results');
found_emojis = _this.searchEmojis(term).show();
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis);
$('.emoji-menu-content ul, .emoji-menu-content h5').hide();
return $('.emoji-menu-content').append(h5).append(ul);
} else {
return $('.emoji-menu-content').children().show();
}
};
})(this));
};
AwardsHandler.prototype.searchEmojis = function(term) {
return $(".emoji-menu-list:not(.frequent-emojis) [data-emoji*='" + term + "']").closest('li').clone();
};
return AwardsHandler;
})();
}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
$(function() {
var $fields;
$fields = $('.js-autosize');
$fields.on('autosize:resized', function() {
var $field;
$field = $(this);
return $field.data('height', $field.outerHeight());
});
$fields.on('resize.autosize', function() {
var $field;
$field = $(this);
if ($field.data('height') !== $field.outerHeight()) {
$field.data('height', $field.outerHeight());
autosize.destroy($field);
return $field.css('max-height', window.outerHeight);
}
});
autosize($fields);
autosize.update($fields);
return $fields.css('resize', 'vertical');
});
}).call(this);
#= require jquery.ba-resize
#= require autosize
$ ->
$fields = $('.js-autosize')
$fields.on 'autosize:resized', ->
$field = $(@)
$field.data('height', $field.outerHeight())
$fields.on 'resize.autosize', ->
$field = $(@)
if $field.data('height') != $field.outerHeight()
$field.data('height', $field.outerHeight())
autosize.destroy($field)
$field.css('max-height', window.outerHeight)
autosize($fields)
autosize.update($fields)
$fields.css('resize', 'vertical')
$ ->
$("body").on "click", ".js-details-target", ->
container = $(@).closest(".js-details-container")
container.toggleClass("open")
# Show details content. Hides link after click.
#
# %div
# %a.js-details-expand
# %div.js-details-content
#
$("body").on "click", ".js-details-expand", (e) ->
$(@).next('.js-details-content').removeClass("hide")
$(@).hide()
e.preventDefault()
// Generated by CoffeeScript 1.10.0
(function() {
$(function() {
$("body").on("click", ".js-details-target", function() {
var container;
container = $(this).closest(".js-details-container");
return container.toggleClass("open");
});
return $("body").on("click", ".js-details-expand", function(e) {
$(this).next('.js-details-content').removeClass("hide");
$(this).hide();
return e.preventDefault();
});
});
}).call(this);
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать