Commit 423943da authored by Jacob Schatz's avatar Jacob Schatz

Merge branch 'desplacturing' into 'master'

Refactor remnants of CoffeeScript destructured opts and super into ES6

## What does this MR do?

Gets rid of some CoffeeScript remnants that are difficult to work with, and updates them to use ES6 syntax/constructs. I updated nearby code to ES6, but these updates usually just included using the es6 class syntax and scoped variable keywords. 

**Important**: For the most part, I tried to avoid refactoring the design of the existing code. If I attempted that, this would've taken much much longer and it would've been out of this issue's scope. 

## Why was this MR needed?

The compiled coffeescript for destructured default params is very difficult to work in. 

These changes makes some of these rough patches more maintainable:

e.g.
![Screen_Shot_2016-09-19_at_4.43.08_PM](/uploads/40cd3f79b9c5f595f3738213b0dadbbf/Screen_Shot_2016-09-19_at_4.43.08_PM.png)

## Does this MR meet the acceptance criteria?

- [x] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added
- [x] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)
- [x] API support added
- Tests
  - [x] Added for this feature/bug
  - [x] All builds are passing
- [x] Conform by the [merge request performance guides](http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
- [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [x] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)

## What are the relevant issue numbers?

Meta Issue: 
https://gitlab.com/gitlab-org/gitlab-ce/issues/21887

Child Issues: 

https://gitlab.com/gitlab-org/gitlab-ce/issues/21942

https://gitlab.com/gitlab-org/gitlab-ce/issues/21941

https://gitlab.com/gitlab-org/gitlab-ce/issues/21943

## To Test Manually: 

#### app/assets/javascripts/LabelManager.js.es6 -> [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-1)
- Navigate to [Labels](http://localhost:3000/gitlab-org/gitlab-test/labels) and click around (toggle label priority, add new labels, subcribe, edit, delete)

#### app/assets/javascripts/blob/blob_ci_yaml.js.es6  -> [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-3)
- [Create new file](http://localhost:3000/gitlab-org/gitlab-test/new/master) and give it the name `.gitlab-ci.yml`, then attempt to select a template

#### app/assets/javascripts/blob/blob_license_selectors.js.es6 -> [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-7)
- [Create new file](http://localhost:3000/gitlab-org/gitlab-test/new/master) and give it the name `license`, then attempt to select a template

#### app/assets/javascripts/blob/template_selector.js.es6 -> [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-8)
- This class is extended by the two previously mentioned files. As long as they work, this should work.

#### app/assets/javascripts/issues-bulk-assignment.js.es6 -> [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-11)
- [Visit issues](http://localhost:3000/gitlab-org/gitlab-test/issues), assign multiple issues a new label or milestone and update.

#### app/assets/javascripts/profile/gl_crop.js.es6 -> [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-12)
- [Visit your profile settings](http://localhost:3000/profile), upload a photo and attempt to crop and set as new profile picture.

#### app/assets/javascripts/profile/profile.js.es6 > [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-14)
- [Visit your profile settings](http://localhost:3000/profile), change various fields (Name, Bio, Email, Notifs settings) and submit form

#### app/assets/javascripts/todos.js.es6 -> [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-18)
- [Visit an issue](http://localhost:3000/documentcloud/underscore/issues/6) and Add Todo, then visit [Todos](http://localhost:3000/dashboard/todos). Try out the sorting features, making sure everything looks right. Mark the Todo done. 

#### app/assets/javascripts/user.js.es6 -> [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-19)
- [Visit user profile](http://localhost:3000/u/root) and click between tabs. 

#### app/assets/javascripts/user_tabs.js.es6 -> [diff](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6261/diffs#diff-20)
- Same as previous.

cc: @jschatz1

See merge request !6261
parents 219413c5 2d41c3f5
......@@ -80,6 +80,7 @@ v 8.12.2
- Only update issuable labels if they have been changed
- Fix bug where 'Search results' repeated many times when a search in the emoji search form is cleared (Xavier Bick) (@zeiv)
- Fix resolve discussion buttons endpoint path
- Refactor remnants of CoffeeScript destructured opts and super !6261
v 8.12.1
- Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST
......
(function() {
this.LabelManager = (function() {
LabelManager.prototype.errorMessage = 'Unable to update label prioritization at this time';
((global) => {
function LabelManager(opts) {
// Defaults
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');
class LabelManager {
constructor({ togglePriorityButton, prioritizedLabels, otherLabels } = {}) {
this.togglePriorityButton = togglePriorityButton || $('.js-toggle-priority');
this.prioritizedLabels = prioritizedLabels || $('.js-prioritized-labels');
this.otherLabels = otherLabels || $('.js-other-labels');
this.errorMessage = 'Unable to update label prioritization at this time';
this.prioritizedLabels.sortable({
items: 'li',
placeholder: 'list-placeholder',
......@@ -18,33 +15,30 @@
this.bindEvents();
}
LabelManager.prototype.bindEvents = function() {
bindEvents() {
return this.togglePriorityButton.on('click', this, this.onTogglePriorityClick);
};
}
LabelManager.prototype.onTogglePriorityClick = function(e) {
var $btn, $label, $tooltip, _this, action;
onTogglePriorityClick(e) {
e.preventDefault();
_this = e.data;
$btn = $(e.currentTarget);
$label = $("#" + ($btn.data('domId')));
action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
// Make sure tooltip will hide
$tooltip = $("#" + ($btn.find('.has-tooltip:visible').attr('aria-describedby')));
const _this = e.data;
const $btn = $(e.currentTarget);
const $label = $(`#${$btn.data('domId')}`);
const action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
const $tooltip = $(`#${$btn.find('.has-tooltip:visible').attr('aria-describedby')}`);
$tooltip.tooltip('destroy');
return _this.toggleLabelPriority($label, action);
};
}
LabelManager.prototype.toggleLabelPriority = function($label, action, persistState) {
var $from, $target, _this, url, xhr;
toggleLabelPriority($label, action, persistState) {
if (persistState == null) {
persistState = true;
}
_this = this;
url = $label.find('.js-toggle-priority').data('url');
$target = this.prioritizedLabels;
$from = this.otherLabels;
// Optimistic update
let xhr;
const _this = this;
const url = $label.find('.js-toggle-priority').data('url');
let $target = this.prioritizedLabels;
let $from = this.otherLabels;
if (action === 'remove') {
$target = this.otherLabels;
$from = this.prioritizedLabels;
......@@ -62,7 +56,7 @@
}
if (action === 'remove') {
xhr = $.ajax({
url: url,
url,
type: 'DELETE'
});
// Restore empty message
......@@ -73,43 +67,40 @@
xhr = this.savePrioritySort($label, action);
}
return xhr.fail(this.rollbackLabelPosition.bind(this, $label, action));
};
}
LabelManager.prototype.onPrioritySortUpdate = function() {
var xhr;
xhr = this.savePrioritySort();
onPrioritySortUpdate() {
const xhr = this.savePrioritySort();
return xhr.fail(function() {
return new Flash(this.errorMessage, 'alert');
});
};
}
LabelManager.prototype.savePrioritySort = function() {
savePrioritySort() {
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';
rollbackLabelPosition($label, originalAction) {
const action = originalAction === 'remove' ? 'add' : 'remove';
this.toggleLabelPriority($label, action, false);
return new Flash(this.errorMessage, 'alert');
};
}
LabelManager.prototype.getSortedLabelsIds = function() {
var sortedIds;
sortedIds = [];
getSortedLabelsIds() {
const sortedIds = [];
this.prioritizedLabels.find('li').each(function() {
return sortedIds.push($(this).data('id'));
sortedIds.push($(this).data('id'));
});
return sortedIds;
};
}
}
return LabelManager;
gl.LabelManager = LabelManager;
})();
})(window.gl || (window.gl = {}));
}).call(this);
/*= require blob/template_selector */
(function() {
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
this.BlobCiYamlSelector = (function(superClass) {
extend(BlobCiYamlSelector, superClass);
function BlobCiYamlSelector() {
return BlobCiYamlSelector.__super__.constructor.apply(this, arguments);
}
BlobCiYamlSelector.prototype.requestFile = function(query) {
return Api.gitlabCiYml(query.name, this.requestFileSuccess.bind(this));
};
return BlobCiYamlSelector;
})(TemplateSelector);
this.BlobCiYamlSelectors = (function() {
function BlobCiYamlSelectors(opts) {
var ref;
this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-gitlab-ci-yml-selector'), this.editor = opts.editor;
this.$dropdowns.each((function(_this) {
return function(i, dropdown) {
var $dropdown;
$dropdown = $(dropdown);
return new BlobCiYamlSelector({
pattern: /(.gitlab-ci.yml)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
dropdown: $dropdown,
editor: _this.editor
});
};
})(this));
}
return BlobCiYamlSelectors;
})();
}).call(this);
/*= require blob/template_selector */
((global) => {
class BlobCiYamlSelector extends gl.TemplateSelector {
requestFile(query) {
return Api.gitlabCiYml(query.name, this.requestFileSuccess.bind(this));
}
requestFileSuccess(file) {
return super.requestFileSuccess(file);
}
}
global.BlobCiYamlSelector = BlobCiYamlSelector;
class BlobCiYamlSelectors {
constructor({ editor, $dropdowns } = {}) {
this.editor = editor;
this.$dropdowns = $dropdowns || $('.js-gitlab-ci-yml-selector');
this.initSelectors();
}
initSelectors() {
const editor = this.editor;
this.$dropdowns.each((i, dropdown) => {
const $dropdown = $(dropdown);
return new BlobCiYamlSelector({
editor,
pattern: /(.gitlab-ci.yml)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
dropdown: $dropdown
});
});
}
}
global.BlobCiYamlSelectors = BlobCiYamlSelectors;
})(window.gl || (window.gl = {}));
......@@ -18,6 +18,6 @@
return BlobGitignoreSelector;
})(TemplateSelector);
})(gl.TemplateSelector);
}).call(this);
......@@ -23,6 +23,6 @@
return BlobLicenseSelector;
})(TemplateSelector);
})(gl.TemplateSelector);
}).call(this);
(function() {
this.BlobLicenseSelectors = (function() {
function BlobLicenseSelectors(opts) {
var ref;
this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-license-selector'), this.editor = opts.editor;
this.$dropdowns.each((function(_this) {
return function(i, dropdown) {
var $dropdown;
$dropdown = $(dropdown);
return new BlobLicenseSelector({
pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-license-selector-wrap'),
dropdown: $dropdown,
editor: _this.editor
});
};
})(this));
}
return BlobLicenseSelectors;
})();
}).call(this);
((global) => {
class BlobLicenseSelectors {
constructor({ $dropdowns, editor }) {
this.$dropdowns = $('.js-license-selector');
this.editor = editor;
this.$dropdowns.each((i, dropdown) => {
const $dropdown = $(dropdown);
return new BlobLicenseSelector({
editor,
pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-license-selector-wrap'),
dropdown: $dropdown,
});
});
}
}
global.BlobLicenseSelectors = BlobLicenseSelectors;
})(window.gl || (window.gl = {}));
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.TemplateSelector = (function() {
function TemplateSelector(opts) {
var ref;
if (opts == null) {
opts = {};
}
this.onClick = bind(this.onClick, this);
this.dropdown = opts.dropdown, this.data = opts.data, this.pattern = opts.pattern, this.wrapper = opts.wrapper, this.editor = opts.editor, this.fileEndpoint = opts.fileEndpoint, this.$input = (ref = opts.$input) != null ? ref : $('#file_name');
this.dropdownIcon = $('.fa-chevron-down', this.dropdown);
this.buildDropdown();
this.bindEvents();
this.onFilenameUpdate();
this.autosizeUpdateEvent = document.createEvent('Event');
this.autosizeUpdateEvent.initEvent('autosize:update', true, false);
}
TemplateSelector.prototype.buildDropdown = function() {
return this.dropdown.glDropdown({
data: this.data,
filterable: true,
selectable: true,
toggleLabel: this.toggleLabel,
search: {
fields: ['name']
},
clicked: this.onClick,
text: function(item) {
return item.name;
}
});
};
TemplateSelector.prototype.bindEvents = function() {
return this.$input.on('keyup blur', (function(_this) {
return function(e) {
return _this.onFilenameUpdate();
};
})(this));
};
TemplateSelector.prototype.toggleLabel = function(item) {
return item.name;
};
TemplateSelector.prototype.onFilenameUpdate = function() {
var filenameMatches;
if (!this.$input.length) {
return;
}
filenameMatches = this.pattern.test(this.$input.val().trim());
if (!filenameMatches) {
this.wrapper.addClass('hidden');
return;
}
return this.wrapper.removeClass('hidden');
};
TemplateSelector.prototype.onClick = function(item, el, e) {
e.preventDefault();
return this.requestFile(item);
};
TemplateSelector.prototype.requestFile = function(item) {
// This `requestFile` method is an abstract method that should
// be added by all subclasses.
};
// To be implemented on the extending class
// e.g.
// Api.gitignoreText item.name, @requestFileSuccess.bind(@)
TemplateSelector.prototype.requestFileSuccess = function(file, opts) {
var oldValue = this.editor.getValue();
var newValue = file.content;
if (opts == null) {
opts = {};
}
if (opts.append && oldValue.length && oldValue !== newValue) {
newValue = oldValue + '\n\n' + newValue;
}
this.editor.setValue(newValue, 1);
if (!opts.skipFocus) this.editor.focus();
if (this.editor instanceof jQuery) {
this.editor.get(0).dispatchEvent(this.autosizeUpdateEvent);
}
};
TemplateSelector.prototype.startLoadingSpinner = function() {
this.dropdownIcon
.addClass('fa-spinner fa-spin')
.removeClass('fa-chevron-down');
};
TemplateSelector.prototype.stopLoadingSpinner = function() {
this.dropdownIcon
.addClass('fa-chevron-down')
.removeClass('fa-spinner fa-spin');
};
return TemplateSelector;
})();
}).call(this);
((global) => {
class TemplateSelector {
constructor({ dropdown, data, pattern, wrapper, editor, fileEndpoint, $input } = {}) {
this.onClick = this.onClick.bind(this);
this.dropdown = dropdown;
this.data = data;
this.pattern = pattern;
this.wrapper = wrapper;
this.editor = editor;
this.fileEndpoint = fileEndpoint;
this.$input = $input || $('#file_name');
this.dropdownIcon = $('.fa-chevron-down', this.dropdown);
this.buildDropdown();
this.bindEvents();
this.onFilenameUpdate();
this.autosizeUpdateEvent = document.createEvent('Event');
this.autosizeUpdateEvent.initEvent('autosize:update', true, false);
}
buildDropdown() {
return this.dropdown.glDropdown({
data: this.data,
filterable: true,
selectable: true,
toggleLabel: this.toggleLabel,
search: {
fields: ['name']
},
clicked: this.onClick,
text: function(item) {
return item.name;
}
});
}
bindEvents() {
return this.$input.on('keyup blur', (e) => this.onFilenameUpdate());
}
toggleLabel(item) {
return item.name;
}
onFilenameUpdate() {
var filenameMatches;
if (!this.$input.length) {
return;
}
filenameMatches = this.pattern.test(this.$input.val().trim());
if (!filenameMatches) {
this.wrapper.addClass('hidden');
return;
}
return this.wrapper.removeClass('hidden');
}
onClick(item, el, e) {
e.preventDefault();
return this.requestFile(item);
}
requestFile(item) {
// This `requestFile` method is an abstract method that should
// be added by all subclasses.
}
// To be implemented on the extending class
// e.g.
// Api.gitignoreText item.name, @requestFileSuccess.bind(@)
requestFileSuccess(file, { skipFocus, append } = {}) {
const oldValue = this.editor.getValue();
let newValue = file.content;
if (append && oldValue.length && oldValue !== newValue) {
newValue = oldValue + '\n\n' + newValue;
}
this.editor.setValue(newValue, 1);
if (!skipFocus) this.editor.focus();
if (this.editor instanceof jQuery) {
this.editor.get(0).dispatchEvent(this.autosizeUpdateEvent);
}
}
startLoadingSpinner() {
this.dropdownIcon
.addClass('fa-spinner fa-spin')
.removeClass('fa-chevron-down');
}
stopLoadingSpinner() {
this.dropdownIcon
.addClass('fa-chevron-down')
.removeClass('fa-spinner fa-spin');
}
}
global.TemplateSelector = TemplateSelector;
})(window.gl || ( window.gl = {}));
......@@ -23,13 +23,13 @@
})(this));
this.initModePanesAndLinks();
this.initSoftWrap();
new BlobLicenseSelectors({
new gl.BlobLicenseSelectors({
editor: this.editor
});
new BlobGitignoreSelectors({
editor: this.editor
});
new BlobCiYamlSelectors({
new gl.BlobCiYamlSelectors({
editor: this.editor
});
}
......
......@@ -26,7 +26,7 @@
case 'projects:merge_requests:index':
case 'projects:issues:index':
Issuable.init();
new IssuableBulkActions();
new gl.IssuableBulkActions();
shortcut_handler = new ShortcutsNavigation();
break;
case 'projects:issues:show':
......@@ -40,7 +40,7 @@
new Milestone();
break;
case 'dashboard:todos:index':
new Todos();
new gl.Todos();
break;
case 'projects:milestones:new':
case 'projects:milestones:edit':
......@@ -59,7 +59,7 @@
shortcut_handler = new ShortcutsNavigation();
new GLForm($('.issue-form'));
new IssuableForm($('.issue-form'));
new IssuableTemplateSelectors();
new gl.IssuableTemplateSelectors();
break;
case 'projects:merge_requests:new':
case 'projects:merge_requests:edit':
......@@ -67,7 +67,7 @@
shortcut_handler = new ShortcutsNavigation();
new GLForm($('.merge-request-form'));
new IssuableForm($('.merge-request-form'));
new IssuableTemplateSelectors();
new gl.IssuableTemplateSelectors();
break;
case 'projects:tags:new':
new ZenMode();
......@@ -165,7 +165,7 @@
break;
case 'projects:labels:index':
if ($('.prioritized-labels').length) {
new LabelManager();
new gl.LabelManager();
}
break;
case 'projects:network:show':
......@@ -279,7 +279,7 @@
Dispatcher.prototype.initSearch = function() {
// Only when search form is present
if ($('.search').length) {
return new SearchAutocomplete();
return new gl.SearchAutocomplete();
}
};
......
(function() {
this.IssuableBulkActions = (function() {
function IssuableBulkActions(opts) {
// Set defaults
var ref, ref1, ref2;
if (opts == null) {
opts = {};
}
this.container = (ref = opts.container) != null ? ref : $('.content'), this.form = (ref1 = opts.form) != null ? ref1 : this.getElement('.bulk-update'), this.issues = (ref2 = opts.issues) != null ? ref2 : this.getElement('.issuable-list > li');
// Save instance
((global) => {
class IssuableBulkActions {
constructor({ container, form, issues } = {}) {
this.container = container || $('.content'),
this.form = form || this.getElement('.bulk-update');
this.issues = issues || this.getElement('.issues-list .issue');
this.form.data('bulkActions', this);
this.willUpdateLabels = false;
this.bindEvents();
......@@ -15,53 +12,46 @@
Issuable.initChecks();
}
IssuableBulkActions.prototype.getElement = function(selector) {
getElement(selector) {
return this.container.find(selector);
};
}
IssuableBulkActions.prototype.bindEvents = function() {
bindEvents() {
return this.form.off('submit').on('submit', this.onFormSubmit.bind(this));
};
}
IssuableBulkActions.prototype.onFormSubmit = function(e) {
onFormSubmit(e) {
e.preventDefault();
return this.submit();
};
}
IssuableBulkActions.prototype.submit = function() {
var _this, xhr;
_this = this;
xhr = $.ajax({
submit() {
const _this = this;
const xhr = $.ajax({
url: this.form.attr('action'),
method: this.form.attr('method'),
dataType: 'JSON',
data: this.getFormDataAsObject()
});
xhr.done(function(response, status, xhr) {
return location.reload();
});
xhr.fail(function() {
return new Flash("Issue update failed");
});
xhr.done(() => window.location.reload());
xhr.fail(() => new Flash("Issue update failed"));
return xhr.always(this.onFormSubmitAlways.bind(this));
};
}
IssuableBulkActions.prototype.onFormSubmitAlways = function() {
onFormSubmitAlways() {
return this.form.find('[type="submit"]').enable();
};
}
IssuableBulkActions.prototype.getSelectedIssues = function() {
getSelectedIssues() {
return this.issues.has('.selected_issue:checked');
};
}
IssuableBulkActions.prototype.getLabelsFromSelection = function() {
var labels;
labels = [];
getLabelsFromSelection() {
const labels = [];
this.getSelectedIssues().map(function() {
var _labels;
_labels = $(this).data('labels');
if (_labels) {
return _labels.map(function(labelId) {
const labelsData = $(this).data('labels');
if (labelsData) {
return labelsData.map(function(labelId) {
if (labels.indexOf(labelId) === -1) {
return labels.push(labelId);
}
......@@ -69,7 +59,7 @@
}
});
return labels;
};
}
/**
......@@ -77,25 +67,21 @@
* @return {Array} Label IDs
*/
IssuableBulkActions.prototype.getUnmarkedIndeterminedLabels = function() {
var el, i, id, j, labelsToKeep, len, len1, ref, ref1, result;
result = [];
labelsToKeep = [];
ref = this.getElement('.labels-filter .is-indeterminate');
for (i = 0, len = ref.length; i < len; i++) {
el = ref[i];
labelsToKeep.push($(el).data('labelId'));
}
ref1 = this.getLabelsFromSelection();
for (j = 0, len1 = ref1.length; j < len1; j++) {
id = ref1[j];
// Only the ones that we are not going to keep
getUnmarkedIndeterminedLabels() {
const result = [];
const labelsToKeep = [];
this.getElement('.labels-filter .is-indeterminate')
.each((i, el) => labelsToKeep.push($(el).data('labelId')));
this.getLabelsFromSelection().forEach((id) => {
if (labelsToKeep.indexOf(id) === -1) {
result.push(id);
}
}
});
return result;
};
}
/**
......@@ -103,9 +89,8 @@
* Returns key/value pairs from form data
*/
IssuableBulkActions.prototype.getFormDataAsObject = function() {
var formData;
formData = {
getFormDataAsObject() {
const formData = {
update: {
state_event: this.form.find('input[name="update[state_event]"]').val(),
assignee_id: this.form.find('input[name="update[assignee_id]"]').val(),
......@@ -125,19 +110,18 @@
});
}
return formData;
};
}
IssuableBulkActions.prototype.getLabelsToApply = function() {
var $labels, labelIds;
labelIds = [];
$labels = this.form.find('.labels-filter input[name="update[label_ids][]"]');
getLabelsToApply() {
const labelIds = [];
const $labels = this.form.find('.labels-filter input[name="update[label_ids][]"]');
$labels.each(function(k, label) {
if (label) {
return labelIds.push(parseInt($(label).val()));
}
});
return labelIds;
};
}
/**
......@@ -145,11 +129,10 @@
* @return {Array} Array of labels IDs
*/
IssuableBulkActions.prototype.getLabelsToRemove = function() {
var indeterminatedLabels, labelsToApply, result;
result = [];
indeterminatedLabels = this.getUnmarkedIndeterminedLabels();
labelsToApply = this.getLabelsToApply();
getLabelsToRemove() {
const result = [];
const indeterminatedLabels = this.getUnmarkedIndeterminedLabels();
const labelsToApply = this.getLabelsToApply();
indeterminatedLabels.map(function(id) {
// We need to exclude label IDs that will be applied
// By not doing this will cause issues from selection to not add labels at all
......@@ -158,10 +141,9 @@
}
});
return result;
};
return IssuableBulkActions;
}
}
})();
global.IssuableBulkActions = IssuableBulkActions;
}).call(this);
})(window.gl || (window.gl = {}));
(function() {
var GitLabCrop,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
((global) => {
GitLabCrop = (function() {
var FILENAMEREGEX;
// Matches everything but the file name
const FILENAMEREGEX = /^.*[\\\/]/;
// Matches everything but the file name
FILENAMEREGEX = /^.*[\\\/]/;
class GitLabCrop {
constructor(input, { filename, previewImage, modalCrop, pickImageEl, uploadImageBtn, modalCropImg,
exportWidth = 200, exportHeight = 200, cropBoxWidth = 200, cropBoxHeight = 200 } = {}) {
function GitLabCrop(input, opts) {
var ref, ref1, ref2, ref3, ref4;
if (opts == null) {
opts = {};
}
this.onUploadImageBtnClick = bind(this.onUploadImageBtnClick, this);
this.onModalHide = bind(this.onModalHide, this);
this.onModalShow = bind(this.onModalShow, this);
this.onPickImageClick = bind(this.onPickImageClick, this);
this.onUploadImageBtnClick = this.onUploadImageBtnClick.bind(this);
this.onModalHide = this.onModalHide.bind(this);
this.onModalShow = this.onModalShow.bind(this);
this.onPickImageClick = this.onPickImageClick.bind(this);
this.fileInput = $(input);
// We should rename to avoid spec to fail
// Form will submit the proper input filed with a file using FormData
this.fileInput.attr('name', (this.fileInput.attr('name')) + "-trigger").attr('id', (this.fileInput.attr('id')) + "-trigger");
// Set defaults
this.exportWidth = (ref = opts.exportWidth) != null ? ref : 200, this.exportHeight = (ref1 = opts.exportHeight) != null ? ref1 : 200, this.cropBoxWidth = (ref2 = opts.cropBoxWidth) != null ? ref2 : 200, this.cropBoxHeight = (ref3 = opts.cropBoxHeight) != null ? ref3 : 200, this.form = (ref4 = opts.form) != null ? ref4 : this.fileInput.parents('form'), this.filename = opts.filename, this.previewImage = opts.previewImage, this.modalCrop = opts.modalCrop, this.pickImageEl = opts.pickImageEl, this.uploadImageBtn = opts.uploadImageBtn, this.modalCropImg = opts.modalCropImg;
// Required params
// Ensure needed elements are jquery objects
// If selector is provided we will convert them to a jQuery Object
this.filename = this.getElement(this.filename);
this.previewImage = this.getElement(this.previewImage);
this.pickImageEl = this.getElement(this.pickImageEl);
// Modal elements usually are outside the @form element
this.modalCrop = _.isString(this.modalCrop) ? $(this.modalCrop) : this.modalCrop;
this.uploadImageBtn = _.isString(this.uploadImageBtn) ? $(this.uploadImageBtn) : this.uploadImageBtn;
this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
this.fileInput.attr('name', `${this.fileInput.attr('name')}-trigger`).attr('id', `this.fileInput.attr('id')-trigger`);
this.exportWidth = exportWidth;
this.exportHeight = exportHeight;
this.cropBoxWidth = cropBoxWidth;
this.cropBoxHeight = cropBoxHeight;
this.form = this.fileInput.parents('form');
this.filename = filename;
this.previewImage = previewImage;
this.modalCrop = modalCrop;
this.pickImageEl = pickImageEl;
this.uploadImageBtn = uploadImageBtn;
this.modalCropImg = modalCropImg;
this.filename = this.getElement(filename);
this.previewImage = this.getElement(previewImage);
this.pickImageEl = this.getElement(pickImageEl);
this.modalCrop = _.isString(modalCrop) ? $(modalCrop) : modalCrop;
this.uploadImageBtn = _.isString(uploadImageBtn) ? $(uploadImageBtn) : uploadImageBtn;
this.modalCropImg = _.isString(modalCropImg) ? $(modalCropImg) : modalCropImg;
this.cropActionsBtn = this.modalCrop.find('[data-method]');
this.bindEvents();
}
GitLabCrop.prototype.getElement = function(selector) {
getElement(selector) {
return $(selector, this.form);
};
}
GitLabCrop.prototype.bindEvents = function() {
bindEvents() {
var _this;
_this = this;
this.fileInput.on('change', function(e) {
......@@ -57,13 +55,13 @@
return _this.onActionBtnClick(btn);
});
return this.croppedImageBlob = null;
};
}
GitLabCrop.prototype.onPickImageClick = function() {
onPickImageClick() {
return this.fileInput.trigger('click');
};
}
GitLabCrop.prototype.onModalShow = function() {
onModalShow() {
var _this;
_this = this;
return this.modalCropImg.cropper({
......@@ -95,44 +93,44 @@
});
}
});
};
}
GitLabCrop.prototype.onModalHide = function() {
onModalHide() {
return this.modalCropImg.attr('src', '').cropper('destroy');
};
}
GitLabCrop.prototype.onUploadImageBtnClick = function(e) { // Remove attached image
e.preventDefault(); // Destroy cropper instance
onUploadImageBtnClick(e) {
e.preventDefault();
this.setBlob();
this.setPreview();
this.modalCrop.modal('hide');
return this.fileInput.val('');
};
}
GitLabCrop.prototype.onActionBtnClick = function(btn) {
onActionBtnClick(btn) {
var data, result;
data = $(btn).data();
if (this.modalCropImg.data('cropper') && data.method) {
return result = this.modalCropImg.cropper(data.method, data.option);
}
};
}
GitLabCrop.prototype.onFileInputChange = function(e, input) {
onFileInputChange(e, input) {
return this.readFile(input);
};
}
GitLabCrop.prototype.readFile = function(input) {
readFile(input) {
var _this, reader;
_this = this;
reader = new FileReader;
reader.onload = function() {
reader.onload = () => {
_this.modalCropImg.attr('src', reader.result);
return _this.modalCrop.modal('show');
};
return reader.readAsDataURL(input.files[0]);
};
}
GitLabCrop.prototype.dataURLtoBlob = function(dataURL) {
dataURLtoBlob(dataURL) {
var array, binary, i, k, len, v;
binary = atob(dataURL.split(',')[1]);
array = [];
......@@ -143,35 +141,32 @@
return new Blob([new Uint8Array(array)], {
type: 'image/png'
});
};
}
GitLabCrop.prototype.setPreview = function() {
setPreview() {
var filename;
this.previewImage.attr('src', this.dataURL);
filename = this.fileInput.val().replace(FILENAMEREGEX, '');
return this.filename.text(filename);
};
}
GitLabCrop.prototype.setBlob = function() {
setBlob() {
this.dataURL = this.modalCropImg.cropper('getCroppedCanvas', {
width: 200,
height: 200
}).toDataURL('image/png');
return this.croppedImageBlob = this.dataURLtoBlob(this.dataURL);
};
}
GitLabCrop.prototype.getBlob = function() {
getBlob() {
return this.croppedImageBlob;
};
return GitLabCrop;
})();
}
}
$.fn.glCrop = function(opts) {
return this.each(function() {
return $(this).data('glcrop', new GitLabCrop(this, opts));
});
};
}
}).call(this);
})(window.gl || (window.gl = {}));
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
((global) => {
this.Profile = (function() {
function Profile(opts) {
var cropOpts, ref;
if (opts == null) {
opts = {};
}
this.onSubmitForm = bind(this.onSubmitForm, this);
this.form = (ref = opts.form) != null ? ref : $('.edit-user');
$('.js-preferences-form').on('change.preference', 'input[type=radio]', function() {
return $(this).parents('form').submit();
// Automatically submit the Preferences form when any of its radio buttons change
});
$('#user_notification_email').on('change', function() {
return $(this).parents('form').submit();
// Automatically submit email form when it changes
});
$('.update-username').on('ajax:before', function() {
$('.loading-username').show();
$(this).find('.update-success').hide();
return $(this).find('.update-failed').hide();
});
$('.update-username').on('ajax:complete', function() {
$('.loading-username').hide();
$(this).find('.btn-save').enable();
return $(this).find('.loading-gif').hide();
});
$('.update-notifications').on('ajax:success', function(e, data) {
if (data.saved) {
return new Flash("Notification settings saved", "notice");
} else {
return new Flash("Failed to save new settings", "alert");
}
});
class Profile {
constructor({ form } = {}) {
this.onSubmitForm = this.onSubmitForm.bind(this);
this.form = form || $('.edit-user');
this.bindEvents();
cropOpts = {
this.initAvatarGlCrop();
}
initAvatarGlCrop() {
const cropOpts = {
filename: '.js-avatar-filename',
previewImage: '.avatar-image .avatar',
modalCrop: '.modal-profile-crop',
......@@ -46,23 +20,51 @@
this.avatarGlCrop = $('.js-user-avatar-input').glCrop(cropOpts).data('glcrop');
}
Profile.prototype.bindEvents = function() {
return this.form.on('submit', this.onSubmitForm);
};
bindEvents() {
$('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
$('#user_notification_email').on('change', this.submitForm);
$('.update-username').on('ajax:before', this.beforeUpdateUsername);
$('.update-username').on('ajax:complete', this.afterUpdateUsername);
$('.update-notifications').on('ajax:success', this.onUpdateNotifs);
this.form.on('submit', this.onSubmitForm);
}
submitForm() {
return $(this).parents('form').submit();
}
Profile.prototype.onSubmitForm = function(e) {
onSubmitForm(e) {
e.preventDefault();
return this.saveForm();
};
}
beforeUpdateUsername() {
$('.loading-username').show();
$(this).find('.update-success').hide();
return $(this).find('.update-failed').hide();
}
afterUpdateUsername() {
$('.loading-username').hide();
$(this).find('.btn-save').enable();
return $(this).find('.loading-gif').hide();
}
onUpdateNotifs(e, data) {
return data.saved ?
new Flash("Notification settings saved", "notice") :
new Flash("Failed to save new settings", "alert");
}
saveForm() {
const self = this;
const formData = new FormData(this.form[0]);
const avatarBlob = this.avatarGlCrop.getBlob();
Profile.prototype.saveForm = function() {
var avatarBlob, formData, self;
self = this;
formData = new FormData(this.form[0]);
avatarBlob = this.avatarGlCrop.getBlob();
if (avatarBlob != null) {
formData.append('user[avatar]', avatarBlob, 'avatar.png');
}
return $.ajax({
url: this.form.attr('action'),
type: this.form.attr('method'),
......@@ -70,37 +72,29 @@
dataType: "json",
processData: false,
contentType: false,
success: function(response) {
return new Flash(response.message, 'notice');
},
error: function(jqXHR) {
return new Flash(jqXHR.responseJSON.message, 'alert');
},
complete: function() {
success: response => new Flash(response.message, 'notice'),
error: jqXHR => new Flash(jqXHR.responseJSON.message, 'alert'),
complete: () => {
window.scrollTo(0, 0);
// Enable submit button after requests ends
return self.form.find(':input[disabled]').enable();
}
});
};
return Profile;
})();
}
}
$(function() {
$(document).on('focusout.ssh_key', '#key_key', function() {
var $title, comment;
$title = $('#key_title');
comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/);
const $title = $('#key_title');
const comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/);
if (comment && comment.length > 1 && $title.val() === '') {
return $title.val(comment[1]).change();
}
// Extract the SSH Key title from its comment
});
if (gl.utils.getPagePath() === 'profiles') {
if (global.utils.getPagePath() === 'profiles') {
return new Profile();
}
});
}).call(this);
})(window.gl || (window.gl = {}));
/*= require ../blob/template_selector */
((global) => {
class IssuableTemplateSelector extends TemplateSelector {
class IssuableTemplateSelector extends gl.TemplateSelector {
constructor(...args) {
super(...args);
this.projectPath = this.dropdown.data('project-path');
......@@ -50,4 +50,4 @@
}
global.IssuableTemplateSelector = IssuableTemplateSelector;
})(window);
})(window.gl || (window.gl = {}));
((global) => {
class IssuableTemplateSelectors {
constructor(opts = {}) {
this.$dropdowns = opts.$dropdowns || $('.js-issuable-selector');
this.editor = opts.editor || this.initEditor();
constructor({ $dropdowns, editor } = {}) {
this.$dropdowns = $dropdowns || $('.js-issuable-selector');
this.editor = editor || this.initEditor();
this.$dropdowns.each((i, dropdown) => {
let $dropdown = $(dropdown);
new IssuableTemplateSelector({
const $dropdown = $(dropdown);
new gl.IssuableTemplateSelector({
pattern: /(\.md)/,
data: $dropdown.data('data'),
wrapper: $dropdown.closest('.js-issuable-selector-wrap'),
......@@ -26,4 +26,4 @@
}
global.IssuableTemplateSelectors = IssuableTemplateSelectors;
})(window);
})(window.gl || (window.gl = {}));
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.Todos = (function() {
function Todos(opts) {
var ref;
if (opts == null) {
opts = {};
}
this.allDoneClicked = bind(this.allDoneClicked, this);
this.doneClicked = bind(this.doneClicked, this);
this.el = (ref = opts.el) != null ? ref : $('.js-todos-options');
((global) => {
class Todos {
constructor({ el } = {}) {
this.allDoneClicked = this.allDoneClicked.bind(this);
this.doneClicked = this.doneClicked.bind(this);
this.el = el || $('.js-todos-options');
this.perPage = this.el.data('perPage');
this.clearListeners();
this.initBtnListeners();
this.initFilters();
}
Todos.prototype.clearListeners = function() {
clearListeners() {
$('.done-todo').off('click');
$('.js-todos-mark-all').off('click');
return $('.todo').off('click');
};
}
Todos.prototype.initBtnListeners = function() {
initBtnListeners() {
$('.done-todo').on('click', this.doneClicked);
$('.js-todos-mark-all').on('click', this.allDoneClicked);
return $('.todo').on('click', this.goToTodoUrl);
};
}
Todos.prototype.initFilters = function() {
initFilters() {
new UsersSelect();
this.initFilterDropdown($('.js-project-search'), 'project_id', ['text']);
this.initFilterDropdown($('.js-type-search'), 'type');
......@@ -38,125 +33,117 @@
event.preventDefault();
Turbolinks.visit(this.action + '&' + $(this).serialize());
});
};
}
Todos.prototype.initFilterDropdown = function($dropdown, fieldName, searchFields) {
initFilterDropdown($dropdown, fieldName, searchFields) {
$dropdown.glDropdown({
fieldName,
selectable: true,
filterable: searchFields ? true : false,
fieldName: fieldName,
search: { fields: searchFields },
data: $dropdown.data('data'),
clicked: function() {
return $dropdown.closest('form.filter-form').submit();
}
})
};
}
Todos.prototype.doneClicked = function(e) {
var $this;
doneClicked(e) {
e.preventDefault();
e.stopImmediatePropagation();
$this = $(e.currentTarget);
$this.disable();
const $target = $(e.currentTarget);
$target.disable();
return $.ajax({
type: 'POST',
url: $this.attr('href'),
url: $target.attr('href'),
dataType: 'json',
data: {
'_method': 'delete'
},
success: (function(_this) {
return function(data) {
_this.redirectIfNeeded(data.count);
_this.clearDone($this.closest('li'));
return _this.updateBadges(data);
};
})(this)
success: (data) => {
this.redirectIfNeeded(data.count);
this.clearDone($target.closest('li'));
return this.updateBadges(data);
}
});
};
}
Todos.prototype.allDoneClicked = function(e) {
var $this;
allDoneClicked(e) {
e.preventDefault();
e.stopImmediatePropagation();
$this = $(e.currentTarget);
$this.disable();
$target = $(e.currentTarget);
$target.disable();
return $.ajax({
type: 'POST',
url: $this.attr('href'),
url: $target.attr('href'),
dataType: 'json',
data: {
'_method': 'delete'
},
success: (function(_this) {
return function(data) {
$this.remove();
$('.prepend-top-default').html('<div class="nothing-here-block">You\'re all done!</div>');
return _this.updateBadges(data);
};
})(this)
success: (data) => {
$target.remove();
$('.prepend-top-default').html('<div class="nothing-here-block">You\'re all done!</div>');
return this.updateBadges(data);
}
});
};
}
Todos.prototype.clearDone = function($row) {
var $ul;
$ul = $row.closest('ul');
clearDone($row) {
const $ul = $row.closest('ul');
$row.remove();
if (!$ul.find('li').length) {
return $ul.parents('.panel').remove();
}
};
}
Todos.prototype.updateBadges = function(data) {
updateBadges(data) {
$('.todos-pending .badge, .todos-pending-count').text(data.count);
return $('.todos-done .badge').text(data.done_count);
};
}
Todos.prototype.getTotalPages = function() {
getTotalPages() {
return this.el.data('totalPages');
};
}
Todos.prototype.getCurrentPage = function() {
getCurrentPage() {
return this.el.data('currentPage');
};
}
Todos.prototype.getTodosPerPage = function() {
getTodosPerPage() {
return this.el.data('perPage');
};
}
redirectIfNeeded(total) {
const currPages = this.getTotalPages();
const currPage = this.getCurrentPage();
Todos.prototype.redirectIfNeeded = function(total) {
var currPage, currPages, newPages, pageParams, url;
currPages = this.getTotalPages();
currPage = this.getCurrentPage();
// Refresh if no remaining Todos
if (!total) {
location.reload();
window.location.reload();
return;
}
// Do nothing if no pagination
if (!currPages) {
return;
}
newPages = Math.ceil(total / this.getTodosPerPage());
// Includes query strings
url = location.href;
// If new total of pages is different than we have now
const newPages = Math.ceil(total / this.getTodosPerPage());
let url = location.href;
if (newPages !== currPages) {
// Redirect to previous page if there's one available
if (currPages > 1 && currPage === currPages) {
pageParams = {
const pageParams = {
page: currPages - 1
};
url = gl.utils.mergeUrlParams(pageParams, url);
}
return Turbolinks.visit(url);
}
};
}
Todos.prototype.goToTodoUrl = function(e) {
var todoLink;
todoLink = $(this).data('url');
goToTodoUrl(e) {
const todoLink = $(this).data('url');
if (!todoLink) {
return;
}
......@@ -167,10 +154,8 @@
} else {
return Turbolinks.visit(todoLink);
}
};
return Todos;
})();
}
}
}).call(this);
global.Todos = Todos;
})(window.gl || (window.gl = {}));
(global => {
((global) => {
global.User = class {
constructor(opts) {
this.opts = opts;
constructor({ action }) {
this.action = action;
this.placeProfileAvatarsToTop();
this.initTabs();
this.hideProjectLimitMessage();
......@@ -14,9 +14,9 @@
}
initTabs() {
return new UserTabs({
return new global.UserTabs({
parentEl: '.user-profile',
action: this.opts.action
action: this.action
});
}
......
// UserTabs
//
// Handles persisting and restoring the current tab selection and lazily-loading
// content on the Users#show page.
//
// ### Example Markup
//
// <ul class="nav-links">
// <li class="activity-tab active">
// <a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
// Activity
// </a>
// </li>
// <li class="groups-tab">
// <a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
// Groups
// </a>
// </li>
// <li class="contributed-tab">
// <a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed">
// Contributed projects
// </a>
// </li>
// <li class="projects-tab">
// <a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects">
// Personal projects
// </a>
// </li>
// <li class="snippets-tab">
// <a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets">
// </a>
// </li>
// </ul>
//
// <div class="tab-content">
// <div class="tab-pane" id="activity">
// Activity Content
// </div>
// <div class="tab-pane" id="groups">
// Groups Content
// </div>
// <div class="tab-pane" id="contributed">
// Contributed projects content
// </div>
// <div class="tab-pane" id="projects">
// Projects content
// </div>
// <div class="tab-pane" id="snippets">
// Snippets content
// </div>
// </div>
//
// <div class="loading-status">
// <div class="loading">
// Loading Animation
// </div>
// </div>
//
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.UserTabs = (function() {
function UserTabs(opts) {
this.tabShown = bind(this.tabShown, this);
var i, item, len, ref, ref1, ref2, ref3;
this.action = (ref = opts.action) != null ? ref : 'activity', this.defaultAction = (ref1 = opts.defaultAction) != null ? ref1 : 'activity', this.parentEl = (ref2 = opts.parentEl) != null ? ref2 : $(document);
// Make jQuery object if selector is provided
if (typeof this.parentEl === 'string') {
this.parentEl = $(this.parentEl);
}
// Store the `location` object, allowing for easier stubbing in tests
this._location = location;
// Set tab states
this.loaded = {};
ref3 = this.parentEl.find('.nav-links a');
for (i = 0, len = ref3.length; i < len; i++) {
item = ref3[i];
this.loaded[$(item).attr('data-action')] = false;
}
// Actions
this.actions = Object.keys(this.loaded);
this.bindEvents();
// Set active tab
if (this.action === 'show') {
this.action = this.defaultAction;
}
this.activateTab(this.action);
}
UserTabs.prototype.bindEvents = function() {
// Toggle event listeners
return this.parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]').on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', this.tabShown);
};
UserTabs.prototype.tabShown = function(event) {
var $target, action, source;
$target = $(event.target);
action = $target.data('action');
source = $target.attr('href');
this.setTab(source, action);
return this.setCurrentAction(action);
};
UserTabs.prototype.activateTab = function(action) {
return this.parentEl.find(".nav-links .js-" + action + "-tab a").tab('show');
};
UserTabs.prototype.setTab = function(source, action) {
if (this.loaded[action] === true) {
return;
}
if (action === 'activity') {
this.loadActivities(source);
}
if (action === 'groups' || action === 'contributed' || action === 'projects' || action === 'snippets') {
return this.loadTab(source, action);
}
};
UserTabs.prototype.loadTab = function(source, action) {
return $.ajax({
beforeSend: (function(_this) {
return function() {
return _this.toggleLoading(true);
};
})(this),
complete: (function(_this) {
return function() {
return _this.toggleLoading(false);
};
})(this),
dataType: 'json',
type: 'GET',
url: source + ".json",
success: (function(_this) {
return function(data) {
var tabSelector;
tabSelector = 'div#' + action;
_this.parentEl.find(tabSelector).html(data.html);
_this.loaded[action] = true;
// Fix tooltips
return gl.utils.localTimeAgo($('.js-timeago', tabSelector));
};
})(this)
});
};
UserTabs.prototype.loadActivities = function(source) {
var $calendarWrap;
if (this.loaded['activity'] === true) {
return;
}
$calendarWrap = this.parentEl.find('.user-calendar');
$calendarWrap.load($calendarWrap.data('href'));
new Activities();
return this.loaded['activity'] = true;
};
UserTabs.prototype.toggleLoading = function(status) {
return this.parentEl.find('.loading-status .loading').toggle(status);
};
UserTabs.prototype.setCurrentAction = function(action) {
var new_state, regExp;
// Remove possible actions from URL
regExp = new RegExp('\/(' + this.actions.join('|') + ')(\.html)?\/?$');
new_state = this._location.pathname;
// remove trailing slashes
new_state = new_state.replace(/\/+$/, "");
new_state = new_state.replace(regExp, '');
// Append the new action if we're on a tab other than 'activity'
if (action !== this.defaultAction) {
new_state += "/" + action;
}
// Ensure parameters and hash come along for the ride
new_state += this._location.search + this._location.hash;
history.replaceState({
turbolinks: true,
url: new_state
}, document.title, new_state);
return new_state;
};
return UserTabs;
})();
}).call(this);
/*
UserTabs
Handles persisting and restoring the current tab selection and lazily-loading
content on the Users#show page.
### Example Markup
<ul class="nav-links">
<li class="activity-tab active">
<a data-action="activity" data-target="#activity" data-toggle="tab" href="/u/username">
Activity
</a>
</li>
<li class="groups-tab">
<a data-action="groups" data-target="#groups" data-toggle="tab" href="/u/username/groups">
Groups
</a>
</li>
<li class="contributed-tab">
<a data-action="contributed" data-target="#contributed" data-toggle="tab" href="/u/username/contributed">
Contributed projects
</a>
</li>
<li class="projects-tab">
<a data-action="projects" data-target="#projects" data-toggle="tab" href="/u/username/projects">
Personal projects
</a>
</li>
<li class="snippets-tab">
<a data-action="snippets" data-target="#snippets" data-toggle="tab" href="/u/username/snippets">
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane" id="activity">
Activity Content
</div>
<div class="tab-pane" id="groups">
Groups Content
</div>
<div class="tab-pane" id="contributed">
Contributed projects content
</div>
<div class="tab-pane" id="projects">
Projects content
</div>
<div class="tab-pane" id="snippets">
Snippets content
</div>
</div>
<div class="loading-status">
<div class="loading">
Loading Animation
</div>
</div>
*/
((global) => {
class UserTabs {
constructor ({ defaultAction, action, parentEl }) {
this.loaded = {};
this.defaultAction = defaultAction || 'activity';
this.action = action || this.defaultAction;
this.$parentEl = $(parentEl) || $(document);
this._location = window.location;
this.$parentEl.find('.nav-links a')
.each((i, navLink) => {
this.loaded[$(navLink).attr('data-action')] = false;
});
this.actions = Object.keys(this.loaded);
this.bindEvents();
if (this.action === 'show') {
this.action = this.defaultAction;
}
this.activateTab(this.action);
}
bindEvents() {
return this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
.on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event));
}
tabShown(event) {
const $target = $(event.target);
const action = $target.data('action');
const source = $target.attr('href');
this.setTab(source, action);
return this.setCurrentAction(action);
}
activateTab(action) {
return this.$parentEl.find(`.nav-links .js-${action}-tab a`)
.tab('show');
}
setTab(source, action) {
if (this.loaded[action]) {
return;
}
if (action === 'activity') {
this.loadActivities(source);
}
const loadableActions = [ 'groups', 'contributed', 'projects', 'snippets' ];
if (loadableActions.indexOf(action) > -1) {
return this.loadTab(source, action);
}
}
loadTab(source, action) {
return $.ajax({
beforeSend: () => this.toggleLoading(true),
complete: () => this.toggleLoading(false),
dataType: 'json',
type: 'GET',
url: `${source}.json`,
success: (data) => {
const tabSelector = `div#${action}`;
this.$parentEl.find(tabSelector).html(data.html);
this.loaded[action] = true;
return gl.utils.localTimeAgo($('.js-timeago', tabSelector));
}
});
}
loadActivities(source) {
if (this.loaded['activity']) {
return;
}
const $calendarWrap = this.$parentEl.find('.user-calendar');
$calendarWrap.load($calendarWrap.data('href'));
new Activities();
return this.loaded['activity'] = true;
}
toggleLoading(status) {
return this.$parentEl.find('.loading-status .loading')
.toggle(status);
}
setCurrentAction(action) {
const regExp = new RegExp(`\/(${this.actions.join('|')})(\.html)?\/?$`);
let new_state = this._location.pathname;
new_state = new_state.replace(/\/+$/, '');
new_state = new_state.replace(regExp, '');
if (action !== this.defaultAction) {
new_state += `/${action}`;
}
new_state += this._location.search + this._location.hash;
history.replaceState({
turbolinks: true,
url: new_state
}, document.title, new_state);
return new_state;
}
}
global.UserTabs = UserTabs;
})(window.gl || (window.gl = {}));
......@@ -112,7 +112,7 @@
fixture.preload('search_autocomplete.html');
beforeEach(function() {
fixture.load('search_autocomplete.html');
return widget = new SearchAutocomplete;
return widget = new gl.SearchAutocomplete;
});
it('should show Dashboard specific dropdown menu', function() {
var list;
......
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