mirror of
https://github.com/wekan/wekan.git
synced 2026-03-09 07:02:34 +01:00
Enforce a consistent ES6 coding style
Replace the old (and broken) jshint + jscsrc by eslint and configure it to support some of the ES6 features. The command `eslint` currently has one error which is a bug that was discovered by its static analysis and should be fixed (usage of a dead object).
This commit is contained in:
parent
039cfe7edf
commit
b3851817ec
60 changed files with 1604 additions and 1692 deletions
|
|
@ -1,42 +1,40 @@
|
|||
// XXX Should we use something like Moderniz instead of our custom detector?
|
||||
|
||||
var whichTransitionEvent = function() {
|
||||
var t;
|
||||
var el = document.createElement('fakeelement');
|
||||
var transitions = {
|
||||
function whichTransitionEvent() {
|
||||
const el = document.createElement('fakeelement');
|
||||
const transitions = {
|
||||
transition:'transitionend',
|
||||
OTransition:'oTransitionEnd',
|
||||
MSTransition:'msTransitionEnd',
|
||||
MozTransition:'transitionend',
|
||||
WebkitTransition:'webkitTransitionEnd'
|
||||
WebkitTransition:'webkitTransitionEnd',
|
||||
};
|
||||
|
||||
for (t in transitions) {
|
||||
for (const t in transitions) {
|
||||
if (el.style[t] !== undefined) {
|
||||
return transitions[t];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var whichAnimationEvent = function() {
|
||||
var t;
|
||||
var el = document.createElement('fakeelement');
|
||||
var transitions = {
|
||||
function whichAnimationEvent() {
|
||||
const el = document.createElement('fakeelement');
|
||||
const transitions = {
|
||||
animation:'animationend',
|
||||
OAnimation:'oAnimationEnd',
|
||||
MSTransition:'msAnimationEnd',
|
||||
MozAnimation:'animationend',
|
||||
WebkitAnimation:'webkitAnimationEnd'
|
||||
WebkitAnimation:'webkitAnimationEnd',
|
||||
};
|
||||
|
||||
for (t in transitions) {
|
||||
for (const t in transitions) {
|
||||
if (el.style[t] !== undefined) {
|
||||
return transitions[t];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
CSSEvents = {
|
||||
transitionend: whichTransitionEvent(),
|
||||
animationend: whichAnimationEvent()
|
||||
animationend: whichAnimationEvent(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ EscapeActions = {
|
|||
enabledOnClick = true;
|
||||
}
|
||||
|
||||
let noClickEscapeOn = options.noClickEscapeOn;
|
||||
const noClickEscapeOn = options.noClickEscapeOn;
|
||||
|
||||
this._actions = _.sortBy([...this._actions, {
|
||||
priority,
|
||||
|
|
@ -44,20 +44,20 @@ EscapeActions = {
|
|||
|
||||
executeLowest() {
|
||||
return this._execute({
|
||||
multipleAction: false
|
||||
multipleAction: false,
|
||||
});
|
||||
},
|
||||
|
||||
executeAll() {
|
||||
return this._execute({
|
||||
multipleActions: true
|
||||
multipleActions: true,
|
||||
});
|
||||
},
|
||||
|
||||
executeUpTo(maxLabel) {
|
||||
return this._execute({
|
||||
maxLabel: maxLabel,
|
||||
multipleActions: true
|
||||
maxLabel,
|
||||
multipleActions: true,
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -66,10 +66,10 @@ EscapeActions = {
|
|||
this._nextclickPrevented = false;
|
||||
} else {
|
||||
return this._execute({
|
||||
maxLabel: maxLabel,
|
||||
maxLabel,
|
||||
multipleActions: false,
|
||||
isClick: true,
|
||||
clickTarget: target
|
||||
clickTarget: target,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
@ -79,7 +79,7 @@ EscapeActions = {
|
|||
},
|
||||
|
||||
_stopClick(action, clickTarget) {
|
||||
if (! _.isString(action.noClickEscapeOn))
|
||||
if (!_.isString(action.noClickEscapeOn))
|
||||
return false;
|
||||
else
|
||||
return $(clickTarget).closest(action.noClickEscapeOn).length > 0;
|
||||
|
|
@ -88,86 +88,46 @@ EscapeActions = {
|
|||
_execute(options) {
|
||||
const maxLabel = options.maxLabel;
|
||||
const multipleActions = options.multipleActions;
|
||||
const isClick = !! options.isClick;
|
||||
const isClick = Boolean(options.isClick);
|
||||
const clickTarget = options.clickTarget;
|
||||
|
||||
let executedAtLeastOne = false;
|
||||
let maxPriority;
|
||||
|
||||
if (! maxLabel)
|
||||
if (!maxLabel)
|
||||
maxPriority = Infinity;
|
||||
else
|
||||
maxPriority = this.hierarchy.indexOf(maxLabel);
|
||||
|
||||
for (let currentAction of this._actions) {
|
||||
for (const currentAction of this._actions) {
|
||||
if (currentAction.priority > maxPriority)
|
||||
return executedAtLeastOne;
|
||||
|
||||
if (isClick && this._stopClick(currentAction, clickTarget))
|
||||
return executedAtLeastOne;
|
||||
|
||||
let isEnabled = currentAction.enabledOnClick || ! isClick;
|
||||
const isEnabled = currentAction.enabledOnClick || !isClick;
|
||||
if (isEnabled && currentAction.condition()) {
|
||||
currentAction.action();
|
||||
executedAtLeastOne = true;
|
||||
if (! multipleActions)
|
||||
if (!multipleActions)
|
||||
return executedAtLeastOne;
|
||||
}
|
||||
}
|
||||
return executedAtLeastOne;
|
||||
}
|
||||
};
|
||||
|
||||
// MouseTrap plugin bindGlobal plugin. Adds a bindGlobal method to Mousetrap
|
||||
// that allows you to bind specific keyboard shortcuts that will still work
|
||||
// inside a text input field.
|
||||
//
|
||||
// usage:
|
||||
// Mousetrap.bindGlobal('ctrl+s', _saveChanges);
|
||||
//
|
||||
// source:
|
||||
// https://github.com/ccampbell/mousetrap/tree/master/plugins/global-bind
|
||||
var _globalCallbacks = {};
|
||||
var _originalStopCallback = Mousetrap.stopCallback;
|
||||
|
||||
Mousetrap.stopCallback = function(e, element, combo, sequence) {
|
||||
var self = this;
|
||||
|
||||
if (self.paused) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_globalCallbacks[combo] || _globalCallbacks[sequence]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _originalStopCallback.call(self, e, element, combo);
|
||||
};
|
||||
|
||||
Mousetrap.bindGlobal = function(keys, callback, action) {
|
||||
var self = this;
|
||||
self.bind(keys, callback, action);
|
||||
|
||||
if (keys instanceof Array) {
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
_globalCallbacks[keys[i]] = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_globalCallbacks[keys] = true;
|
||||
},
|
||||
};
|
||||
|
||||
// Pressing escape to execute one escape action. We use `bindGloabal` vecause
|
||||
// the shortcut sould work on textarea and inputs as well.
|
||||
Mousetrap.bindGlobal('esc', function() {
|
||||
Mousetrap.bindGlobal('esc', () => {
|
||||
EscapeActions.executeLowest();
|
||||
});
|
||||
|
||||
// On a left click on the document, we try to exectute one escape action (eg,
|
||||
// close the popup). We don't execute any action if the user has clicked on a
|
||||
// link or a button.
|
||||
$(document).on('click', function(evt) {
|
||||
$(document).on('click', (evt) => {
|
||||
if (evt.button === 0 &&
|
||||
$(evt.target).closest('a,button,.is-editable').length === 0) {
|
||||
EscapeActions.clickExecute(evt.target, 'multiselection');
|
||||
|
|
|
|||
|
|
@ -4,66 +4,66 @@
|
|||
// goal is to filter complete documents by using the local filters for each
|
||||
// fields.
|
||||
|
||||
var showFilterSidebar = function() {
|
||||
function showFilterSidebar() {
|
||||
Sidebar.setView('filter');
|
||||
};
|
||||
}
|
||||
|
||||
// Use a "set" filter for a field that is a set of documents uniquely
|
||||
// identified. For instance `{ labels: ['labelA', 'labelC', 'labelD'] }`.
|
||||
var SetFilter = function() {
|
||||
this._dep = new Tracker.Dependency();
|
||||
this._selectedElements = [];
|
||||
};
|
||||
class SetFilter {
|
||||
constructor() {
|
||||
this._dep = new Tracker.Dependency();
|
||||
this._selectedElements = [];
|
||||
}
|
||||
|
||||
_.extend(SetFilter.prototype, {
|
||||
isSelected: function(val) {
|
||||
isSelected(val) {
|
||||
this._dep.depend();
|
||||
return this._selectedElements.indexOf(val) > -1;
|
||||
},
|
||||
}
|
||||
|
||||
add: function(val) {
|
||||
add(val) {
|
||||
if (this._indexOfVal(val) === -1) {
|
||||
this._selectedElements.push(val);
|
||||
this._dep.changed();
|
||||
showFilterSidebar();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
remove: function(val) {
|
||||
var indexOfVal = this._indexOfVal(val);
|
||||
remove(val) {
|
||||
const indexOfVal = this._indexOfVal(val);
|
||||
if (this._indexOfVal(val) !== -1) {
|
||||
this._selectedElements.splice(indexOfVal, 1);
|
||||
this._dep.changed();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
toogle: function(val) {
|
||||
toogle(val) {
|
||||
if (this._indexOfVal(val) === -1) {
|
||||
this.add(val);
|
||||
} else {
|
||||
this.remove(val);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
reset: function() {
|
||||
reset() {
|
||||
this._selectedElements = [];
|
||||
this._dep.changed();
|
||||
},
|
||||
}
|
||||
|
||||
_indexOfVal: function(val) {
|
||||
_indexOfVal(val) {
|
||||
return this._selectedElements.indexOf(val);
|
||||
},
|
||||
}
|
||||
|
||||
_isActive: function() {
|
||||
_isActive() {
|
||||
this._dep.depend();
|
||||
return this._selectedElements.length !== 0;
|
||||
},
|
||||
}
|
||||
|
||||
_getMongoSelector: function() {
|
||||
_getMongoSelector() {
|
||||
this._dep.depend();
|
||||
return { $in: this._selectedElements };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// The global Filter object.
|
||||
// XXX It would be possible to re-write this object more elegantly, and removing
|
||||
|
|
@ -84,50 +84,46 @@ Filter = {
|
|||
_exceptions: [],
|
||||
_exceptionsDep: new Tracker.Dependency(),
|
||||
|
||||
isActive: function() {
|
||||
var self = this;
|
||||
return _.any(self._fields, function(fieldName) {
|
||||
return self[fieldName]._isActive();
|
||||
isActive() {
|
||||
return _.any(this._fields, (fieldName) => {
|
||||
return this[fieldName]._isActive();
|
||||
});
|
||||
},
|
||||
|
||||
_getMongoSelector: function() {
|
||||
var self = this;
|
||||
|
||||
if (! self.isActive())
|
||||
_getMongoSelector() {
|
||||
if (!this.isActive())
|
||||
return {};
|
||||
|
||||
var filterSelector = {};
|
||||
_.forEach(self._fields, function(fieldName) {
|
||||
var filter = self[fieldName];
|
||||
const filterSelector = {};
|
||||
_.forEach(this._fields, (fieldName) => {
|
||||
const filter = this[fieldName];
|
||||
if (filter._isActive())
|
||||
filterSelector[fieldName] = filter._getMongoSelector();
|
||||
});
|
||||
|
||||
var exceptionsSelector = {_id: {$in: this._exceptions}};
|
||||
const exceptionsSelector = {_id: {$in: this._exceptions}};
|
||||
this._exceptionsDep.depend();
|
||||
|
||||
return {$or: [filterSelector, exceptionsSelector]};
|
||||
},
|
||||
|
||||
mongoSelector: function(additionalSelector) {
|
||||
var filterSelector = this._getMongoSelector();
|
||||
mongoSelector(additionalSelector) {
|
||||
const filterSelector = this._getMongoSelector();
|
||||
if (_.isUndefined(additionalSelector))
|
||||
return filterSelector;
|
||||
else
|
||||
return {$and: [filterSelector, additionalSelector]};
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
var self = this;
|
||||
_.forEach(self._fields, function(fieldName) {
|
||||
var filter = self[fieldName];
|
||||
reset() {
|
||||
_.forEach(this._fields, (fieldName) => {
|
||||
const filter = this[fieldName];
|
||||
filter.reset();
|
||||
});
|
||||
self.resetExceptions();
|
||||
this.resetExceptions();
|
||||
},
|
||||
|
||||
addException: function(_id) {
|
||||
addException(_id) {
|
||||
if (this.isActive()) {
|
||||
this._exceptions.push(_id);
|
||||
this._exceptionsDep.changed();
|
||||
|
|
@ -135,10 +131,10 @@ Filter = {
|
|||
}
|
||||
},
|
||||
|
||||
resetExceptions: function() {
|
||||
resetExceptions() {
|
||||
this._exceptions = [];
|
||||
this._exceptionsDep.changed();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Blaze.registerHelper('Filter', Filter);
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
// the language reactively. If the user is not connected we use the language
|
||||
// information provided by the browser, and default to english.
|
||||
|
||||
Tracker.autorun(function() {
|
||||
var language;
|
||||
var currentUser = Meteor.user();
|
||||
Tracker.autorun(() => {
|
||||
const currentUser = Meteor.user();
|
||||
let language;
|
||||
if (currentUser) {
|
||||
language = currentUser.profile && currentUser.profile.language;
|
||||
} else {
|
||||
|
|
@ -12,11 +12,10 @@ Tracker.autorun(function() {
|
|||
}
|
||||
|
||||
if (language) {
|
||||
|
||||
TAPi18n.setLanguage(language);
|
||||
|
||||
// XXX
|
||||
var shortLanguage = language.split('-')[0];
|
||||
const shortLanguage = language.split('-')[0];
|
||||
T9n.setLanguage(shortLanguage);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,66 +13,66 @@
|
|||
// // the content when the form is close (optional)
|
||||
|
||||
// We can only have one inlined form element opened at a time
|
||||
currentlyOpenedForm = new ReactiveVar(null);
|
||||
const currentlyOpenedForm = new ReactiveVar(null);
|
||||
|
||||
InlinedForm = BlazeComponent.extendComponent({
|
||||
template: function() {
|
||||
template() {
|
||||
return 'inlinedForm';
|
||||
},
|
||||
|
||||
onCreated: function() {
|
||||
onCreated() {
|
||||
this.isOpen = new ReactiveVar(false);
|
||||
},
|
||||
|
||||
onDestroyed: function() {
|
||||
onDestroyed() {
|
||||
currentlyOpenedForm.set(null);
|
||||
},
|
||||
|
||||
open: function() {
|
||||
open() {
|
||||
// Close currently opened form, if any
|
||||
EscapeActions.executeUpTo('inlinedForm');
|
||||
this.isOpen.set(true);
|
||||
currentlyOpenedForm.set(this);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
close() {
|
||||
this.isOpen.set(false);
|
||||
currentlyOpenedForm.set(null);
|
||||
},
|
||||
|
||||
getValue: function() {
|
||||
var input = this.find('textarea,input[type=text]');
|
||||
getValue() {
|
||||
const input = this.find('textarea,input[type=text]');
|
||||
return this.isOpen.get() && input && input.value;
|
||||
},
|
||||
|
||||
events: function() {
|
||||
events() {
|
||||
return [{
|
||||
'click .js-close-inlined-form': this.close,
|
||||
'click .js-open-inlined-form': this.open,
|
||||
|
||||
// Pressing Ctrl+Enter should submit the form
|
||||
'keydown form textarea': function(evt) {
|
||||
'keydown form textarea'(evt) {
|
||||
if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
|
||||
this.find('button[type=submit]').click();
|
||||
}
|
||||
},
|
||||
|
||||
// Close the inlined form when after its submission
|
||||
submit: function() {
|
||||
submit() {
|
||||
if (this.currentData().autoclose !== false) {
|
||||
Tracker.afterFlush(() => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
}];
|
||||
}
|
||||
},
|
||||
}).register('inlinedForm');
|
||||
|
||||
// Press escape to close the currently opened inlinedForm
|
||||
EscapeActions.register('inlinedForm',
|
||||
function() { currentlyOpenedForm.get().close(); },
|
||||
function() { return currentlyOpenedForm.get() !== null; }, {
|
||||
noClickEscapeOn: '.js-inlined-form'
|
||||
() => { currentlyOpenedForm.get().close(); },
|
||||
() => { return currentlyOpenedForm.get() !== null; }, {
|
||||
noClickEscapeOn: '.js-inlined-form',
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Mousetrap.bind('x', () => {
|
|||
});
|
||||
|
||||
Mousetrap.bind(['down', 'up'], (evt, key) => {
|
||||
if (! Session.get('currentCard')) {
|
||||
if (!Session.get('currentCard')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -39,24 +39,24 @@ Mousetrap.bind(['down', 'up'], (evt, key) => {
|
|||
Template.keyboardShortcuts.helpers({
|
||||
mapping: [{
|
||||
keys: ['W'],
|
||||
action: 'shortcut-toogle-sidebar'
|
||||
action: 'shortcut-toogle-sidebar',
|
||||
}, {
|
||||
keys: ['Q'],
|
||||
action: 'shortcut-filter-my-cards'
|
||||
action: 'shortcut-filter-my-cards',
|
||||
}, {
|
||||
keys: ['X'],
|
||||
action: 'shortcut-clear-filters'
|
||||
action: 'shortcut-clear-filters',
|
||||
}, {
|
||||
keys: ['?'],
|
||||
action: 'shortcut-show-shortcuts'
|
||||
action: 'shortcut-show-shortcuts',
|
||||
}, {
|
||||
keys: ['ESC'],
|
||||
action: 'shortcut-close-dialog'
|
||||
action: 'shortcut-close-dialog',
|
||||
}, {
|
||||
keys: ['@'],
|
||||
action: 'shortcut-autocomplete-members'
|
||||
action: 'shortcut-autocomplete-members',
|
||||
}, {
|
||||
keys: [':'],
|
||||
action: 'shortcut-autocomplete-emojies'
|
||||
}]
|
||||
action: 'shortcut-autocomplete-emojies',
|
||||
}],
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const closedValue = null
|
||||
const closedValue = null;
|
||||
|
||||
window.Modal = new class {
|
||||
constructor() {
|
||||
|
|
|
|||
|
|
@ -1,53 +1,53 @@
|
|||
|
||||
var getCardsBetween = function(idA, idB) {
|
||||
function getCardsBetween(idA, idB) {
|
||||
|
||||
var pluckId = function(doc) {
|
||||
function pluckId(doc) {
|
||||
return doc._id;
|
||||
};
|
||||
}
|
||||
|
||||
var getListsStrictlyBetween = function(id1, id2) {
|
||||
function getListsStrictlyBetween(id1, id2) {
|
||||
return Lists.find({
|
||||
$and: [
|
||||
{ sort: { $gt: Lists.findOne(id1).sort } },
|
||||
{ sort: { $lt: Lists.findOne(id2).sort } }
|
||||
{ sort: { $lt: Lists.findOne(id2).sort } },
|
||||
],
|
||||
archived: false
|
||||
archived: false,
|
||||
}).map(pluckId);
|
||||
};
|
||||
}
|
||||
|
||||
var cards = _.sortBy([Cards.findOne(idA), Cards.findOne(idB)], function(c) {
|
||||
const cards = _.sortBy([Cards.findOne(idA), Cards.findOne(idB)], (c) => {
|
||||
return c.sort;
|
||||
});
|
||||
|
||||
var selector;
|
||||
let selector;
|
||||
if (cards[0].listId === cards[1].listId) {
|
||||
selector = {
|
||||
listId: cards[0].listId,
|
||||
sort: {
|
||||
$gte: cards[0].sort,
|
||||
$lte: cards[1].sort
|
||||
$lte: cards[1].sort,
|
||||
},
|
||||
archived: false
|
||||
archived: false,
|
||||
};
|
||||
} else {
|
||||
selector = {
|
||||
$or: [{
|
||||
listId: cards[0].listId,
|
||||
sort: { $lte: cards[0].sort }
|
||||
sort: { $lte: cards[0].sort },
|
||||
}, {
|
||||
listId: {
|
||||
$in: getListsStrictlyBetween(cards[0].listId, cards[1].listId)
|
||||
}
|
||||
$in: getListsStrictlyBetween(cards[0].listId, cards[1].listId),
|
||||
},
|
||||
}, {
|
||||
listId: cards[1].listId,
|
||||
sort: { $gte: cards[1].sort }
|
||||
sort: { $gte: cards[1].sort },
|
||||
}],
|
||||
archived: false
|
||||
archived: false,
|
||||
};
|
||||
}
|
||||
|
||||
return Cards.find(Filter.mongoSelector(selector)).map(pluckId);
|
||||
};
|
||||
}
|
||||
|
||||
MultiSelection = {
|
||||
sidebarView: 'multiselection',
|
||||
|
|
@ -58,30 +58,30 @@ MultiSelection = {
|
|||
|
||||
startRangeCardId: null,
|
||||
|
||||
reset: function() {
|
||||
reset() {
|
||||
this._selectedCards.set([]);
|
||||
},
|
||||
|
||||
getMongoSelector: function() {
|
||||
getMongoSelector() {
|
||||
return Filter.mongoSelector({
|
||||
_id: { $in: this._selectedCards.get() }
|
||||
_id: { $in: this._selectedCards.get() },
|
||||
});
|
||||
},
|
||||
|
||||
isActive: function() {
|
||||
isActive() {
|
||||
return this._isActive.get();
|
||||
},
|
||||
|
||||
count: function() {
|
||||
count() {
|
||||
return Cards.find(this.getMongoSelector()).count();
|
||||
},
|
||||
|
||||
isEmpty: function() {
|
||||
isEmpty() {
|
||||
return this.count() === 0;
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
if (! this.isActive()) {
|
||||
activate() {
|
||||
if (!this.isActive()) {
|
||||
EscapeActions.executeUpTo('detailsPane');
|
||||
this._isActive.set(true);
|
||||
Tracker.flush();
|
||||
|
|
@ -89,7 +89,7 @@ MultiSelection = {
|
|||
Sidebar.setView(this.sidebarView);
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
disable() {
|
||||
if (this.isActive()) {
|
||||
this._isActive.set(false);
|
||||
if (Sidebar && Sidebar.getView() === this.sidebarView) {
|
||||
|
|
@ -99,19 +99,19 @@ MultiSelection = {
|
|||
}
|
||||
},
|
||||
|
||||
add: function(cardIds) {
|
||||
add(cardIds) {
|
||||
return this.toogle(cardIds, { add: true, remove: false });
|
||||
},
|
||||
|
||||
remove: function(cardIds) {
|
||||
remove(cardIds) {
|
||||
return this.toogle(cardIds, { add: false, remove: true });
|
||||
},
|
||||
|
||||
toogleRange: function(cardId) {
|
||||
var selectedCards = this._selectedCards.get();
|
||||
var startRange;
|
||||
toogleRange(cardId) {
|
||||
const selectedCards = this._selectedCards.get();
|
||||
let startRange;
|
||||
this.reset();
|
||||
if (! this.isActive() || selectedCards.length === 0) {
|
||||
if (!this.isActive() || selectedCards.length === 0) {
|
||||
this.toogle(cardId);
|
||||
} else {
|
||||
startRange = selectedCards[selectedCards.length - 1];
|
||||
|
|
@ -119,23 +119,22 @@ MultiSelection = {
|
|||
}
|
||||
},
|
||||
|
||||
toogle: function(cardIds, options) {
|
||||
var self = this;
|
||||
toogle(cardIds, options) {
|
||||
cardIds = _.isString(cardIds) ? [cardIds] : cardIds;
|
||||
options = _.extend({
|
||||
add: true,
|
||||
remove: true
|
||||
remove: true,
|
||||
}, options || {});
|
||||
|
||||
if (! self.isActive()) {
|
||||
self.reset();
|
||||
self.activate();
|
||||
if (!this.isActive()) {
|
||||
this.reset();
|
||||
this.activate();
|
||||
}
|
||||
|
||||
var selectedCards = self._selectedCards.get();
|
||||
const selectedCards = this._selectedCards.get();
|
||||
|
||||
_.each(cardIds, function(cardId) {
|
||||
var indexOfCard = selectedCards.indexOf(cardId);
|
||||
_.each(cardIds, (cardId) => {
|
||||
const indexOfCard = selectedCards.indexOf(cardId);
|
||||
|
||||
if (options.remove && indexOfCard > -1)
|
||||
selectedCards.splice(indexOfCard, 1);
|
||||
|
|
@ -144,19 +143,19 @@ MultiSelection = {
|
|||
selectedCards.push(cardId);
|
||||
});
|
||||
|
||||
self._selectedCards.set(selectedCards);
|
||||
this._selectedCards.set(selectedCards);
|
||||
},
|
||||
|
||||
isSelected: function(cardId) {
|
||||
isSelected(cardId) {
|
||||
return this._selectedCards.get().indexOf(cardId) > -1;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Blaze.registerHelper('MultiSelection', MultiSelection);
|
||||
|
||||
EscapeActions.register('multiselection',
|
||||
function() { MultiSelection.disable(); },
|
||||
function() { return MultiSelection.isActive(); }, {
|
||||
noClickEscapeOn: '.js-minicard,.js-board-sidebar-content'
|
||||
() => { MultiSelection.disable(); },
|
||||
() => { return MultiSelection.isActive(); }, {
|
||||
noClickEscapeOn: '.js-minicard,.js-board-sidebar-content',
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,55 +1,53 @@
|
|||
// A simple tracker dependency that we invalidate every time the window is
|
||||
// resized. This is used to reactively re-calculate the popup position in case
|
||||
// of a window resize. This is the equivalent of a "Signal" in some other
|
||||
// programming environments.
|
||||
let windowResizeDep = new Tracker.Dependency()
|
||||
$(window).on('resize', () => windowResizeDep.changed())
|
||||
// programming environments (eg, elm).
|
||||
const windowResizeDep = new Tracker.Dependency();
|
||||
$(window).on('resize', () => windowResizeDep.changed());
|
||||
|
||||
window.Popup = new class {
|
||||
constructor() {
|
||||
// The template we use to render popups
|
||||
this.template = Template.popup
|
||||
this.template = Template.popup;
|
||||
|
||||
// We only want to display one popup at a time and we keep the view object
|
||||
// in this `Popup._current` variable. If there is no popup currently opened
|
||||
// the value is `null`.
|
||||
this._current = null
|
||||
this._current = null;
|
||||
|
||||
// It's possible to open a sub-popup B from a popup A. In that case we keep
|
||||
// the data of popup A so we can return back to it. Every time we open a new
|
||||
// popup the stack grows, every time we go back the stack decrease, and if
|
||||
// we close the popup the stack is reseted to the empty stack [].
|
||||
this._stack = []
|
||||
this._stack = [];
|
||||
|
||||
// We invalidate this internal dependency every time the top of the stack
|
||||
// has changed and we want to re-render a popup with the new top-stack data.
|
||||
this._dep = new Tracker.Dependency()
|
||||
this._dep = new Tracker.Dependency();
|
||||
}
|
||||
|
||||
/// This function returns a callback that can be used in an event map:
|
||||
///
|
||||
/// Template.tplName.events({
|
||||
/// 'click .elementClass': Popup.open("popupName")
|
||||
/// })
|
||||
///
|
||||
/// 'click .elementClass': Popup.open("popupName"),
|
||||
/// });
|
||||
/// The popup inherit the data context of its parent.
|
||||
open(name) {
|
||||
let self = this
|
||||
const popupName = `${name}Popup`
|
||||
const self = this;
|
||||
const popupName = `${name}Popup`;
|
||||
|
||||
function clickFromPopup(evt) {
|
||||
return $(evt.target).closest('.js-pop-over').length !== 0
|
||||
return $(evt.target).closest('.js-pop-over').length !== 0;
|
||||
}
|
||||
|
||||
return function(evt) {
|
||||
// If a popup is already opened, clicking again on the opener element
|
||||
// should close it -- and interrupt the current `open` function.
|
||||
if (self.isOpen()) {
|
||||
let previousOpenerElement = self._getTopStack().openerElement
|
||||
const previousOpenerElement = self._getTopStack().openerElement;
|
||||
if (previousOpenerElement === evt.currentTarget) {
|
||||
return self.close()
|
||||
return self.close();
|
||||
} else {
|
||||
$(previousOpenerElement).removeClass('is-active')
|
||||
$(previousOpenerElement).removeClass('is-active');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,16 +56,16 @@ window.Popup = new class {
|
|||
// if the popup has no parent, or from the parent `openerElement` if it
|
||||
// has one. This allows us to position a sub-popup exactly at the same
|
||||
// position than its parent.
|
||||
let openerElement
|
||||
let openerElement;
|
||||
if (clickFromPopup(evt)) {
|
||||
openerElement = self._getTopStack().openerElement
|
||||
openerElement = self._getTopStack().openerElement;
|
||||
} else {
|
||||
self._stack = []
|
||||
openerElement = evt.currentTarget
|
||||
self._stack = [];
|
||||
openerElement = evt.currentTarget;
|
||||
}
|
||||
|
||||
$(openerElement).addClass('is-active')
|
||||
evt.preventDefault()
|
||||
$(openerElement).addClass('is-active');
|
||||
evt.preventDefault();
|
||||
|
||||
// We push our popup data to the stack. The top of the stack is always
|
||||
// used as the data source for our current popup.
|
||||
|
|
@ -79,7 +77,7 @@ window.Popup = new class {
|
|||
depth: self._stack.length,
|
||||
offset: self._getOffset(openerElement),
|
||||
dataContext: this.currentData && this.currentData() || this,
|
||||
})
|
||||
});
|
||||
|
||||
// If there are no popup currently opened we use the Blaze API to render
|
||||
// one into the DOM. We use a reactive function as the data parameter that
|
||||
|
|
@ -90,39 +88,38 @@ window.Popup = new class {
|
|||
// Otherwise if there is already a popup open we just need to invalidate
|
||||
// our internal dependency, and since we just changed the top element of
|
||||
// our internal stack, the popup will be updated with the new data.
|
||||
if (! self.isOpen()) {
|
||||
if (!self.isOpen()) {
|
||||
self.current = Blaze.renderWithData(self.template, () => {
|
||||
self._dep.depend()
|
||||
return _.extend(self._getTopStack(), { stack: self._stack })
|
||||
}, document.body)
|
||||
self._dep.depend();
|
||||
return _.extend(self._getTopStack(), { stack: self._stack });
|
||||
}, document.body);
|
||||
|
||||
} else {
|
||||
self._dep.changed()
|
||||
self._dep.changed();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// This function returns a callback that can be used in an event map:
|
||||
///
|
||||
/// Template.tplName.events({
|
||||
/// 'click .elementClass': Popup.afterConfirm("popupName", function() {
|
||||
/// // What to do after the user has confirmed the action
|
||||
/// })
|
||||
/// })
|
||||
/// }),
|
||||
/// });
|
||||
afterConfirm(name, action) {
|
||||
let self = this
|
||||
const self = this;
|
||||
|
||||
return function(evt, tpl) {
|
||||
let context = this.currentData && this.currentData() || this
|
||||
context.__afterConfirmAction = action
|
||||
self.open(name).call(context, evt, tpl)
|
||||
}
|
||||
const context = this.currentData && this.currentData() || this;
|
||||
context.__afterConfirmAction = action;
|
||||
self.open(name).call(context, evt, tpl);
|
||||
};
|
||||
}
|
||||
|
||||
/// The public reactive state of the popup.
|
||||
isOpen() {
|
||||
this._dep.changed()
|
||||
return !! this.current
|
||||
this._dep.changed();
|
||||
return Boolean(this.current);
|
||||
}
|
||||
|
||||
/// In case the popup was opened from a parent popup we can get back to it
|
||||
|
|
@ -132,45 +129,45 @@ window.Popup = new class {
|
|||
/// steps back is greater than the popup stack size, the popup will be closed.
|
||||
back(n = 1) {
|
||||
if (this._stack.length > n) {
|
||||
_.times(n, () => this._stack.pop())
|
||||
this._dep.changed()
|
||||
_.times(n, () => this._stack.pop());
|
||||
this._dep.changed();
|
||||
} else {
|
||||
this.close()
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
/// Close the current opened popup.
|
||||
close() {
|
||||
if (this.isOpen()) {
|
||||
Blaze.remove(this.current)
|
||||
this.current = null
|
||||
Blaze.remove(this.current);
|
||||
this.current = null;
|
||||
|
||||
let openerElement = this._getTopStack().openerElement
|
||||
$(openerElement).removeClass('is-active')
|
||||
const openerElement = this._getTopStack().openerElement;
|
||||
$(openerElement).removeClass('is-active');
|
||||
|
||||
this._stack = []
|
||||
this._stack = [];
|
||||
}
|
||||
}
|
||||
|
||||
// An utility fonction that returns the top element of the internal stack
|
||||
_getTopStack() {
|
||||
return this._stack[this._stack.length - 1]
|
||||
return this._stack[this._stack.length - 1];
|
||||
}
|
||||
|
||||
// We automatically calculate the popup offset from the reference element
|
||||
// position and dimensions. We also reactively use the window dimensions to
|
||||
// ensure that the popup is always visible on the screen.
|
||||
_getOffset(element) {
|
||||
let $element = $(element)
|
||||
const $element = $(element);
|
||||
return () => {
|
||||
windowResizeDep.depend()
|
||||
const offset = $element.offset()
|
||||
const popupWidth = 300 + 15
|
||||
windowResizeDep.depend();
|
||||
const offset = $element.offset();
|
||||
const popupWidth = 300 + 15;
|
||||
return {
|
||||
left: Math.min(offset.left, $(window).width() - popupWidth),
|
||||
top: offset.top + $element.outerHeight(),
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// We get the title from the translation files. Instead of returning the
|
||||
|
|
@ -178,22 +175,22 @@ window.Popup = new class {
|
|||
// is a reactive data source, the title will be changed reactively.
|
||||
_getTitle(popupName) {
|
||||
return () => {
|
||||
const translationKey = `${popupName}-title`
|
||||
const translationKey = `${popupName}-title`;
|
||||
|
||||
// XXX There is no public API to check if there is an available
|
||||
// translation for a given key. So we try to translate the key and if the
|
||||
// translation output equals the key input we deduce that no translation
|
||||
// was available and returns `false`. There is a (small) risk a false
|
||||
// positives.
|
||||
const title = TAPi18n.__(translationKey)
|
||||
return title !== translationKey ? title : false
|
||||
}
|
||||
const title = TAPi18n.__(translationKey);
|
||||
return title !== translationKey ? title : false;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// We close a potential opened popup on any left click on the document, or go
|
||||
// one step back by pressing escape.
|
||||
const escapeActions = ['back', 'close']
|
||||
const escapeActions = ['back', 'close'];
|
||||
_.each(escapeActions, (actionName) => {
|
||||
EscapeActions.register(`popup-${actionName}`,
|
||||
() => Popup[actionName](),
|
||||
|
|
@ -202,6 +199,6 @@ _.each(escapeActions, (actionName) => {
|
|||
noClickEscapeOn: '.js-pop-over',
|
||||
enabledOnClick: actionName === 'close',
|
||||
}
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ UnsavedEdits = {
|
|||
// _collection: UnsavedEditCollection,
|
||||
|
||||
get({ fieldName, docId }, defaultTo = '') {
|
||||
let unsavedValue = this._getCollectionDocument(fieldName, docId);
|
||||
const unsavedValue = this._getCollectionDocument(fieldName, docId);
|
||||
if (unsavedValue) {
|
||||
return unsavedValue.value
|
||||
return unsavedValue.value;
|
||||
} else {
|
||||
return defaultTo;
|
||||
}
|
||||
|
|
@ -40,13 +40,9 @@ UnsavedEdits = {
|
|||
},
|
||||
|
||||
set({ fieldName, docId }, value) {
|
||||
let currentDoc = this._getCollectionDocument(fieldName, docId);
|
||||
const currentDoc = this._getCollectionDocument(fieldName, docId);
|
||||
if (currentDoc) {
|
||||
UnsavedEditCollection.update(currentDoc._id, {
|
||||
$set: {
|
||||
value: value
|
||||
}
|
||||
});
|
||||
UnsavedEditCollection.update(currentDoc._id, { $set: { value }});
|
||||
} else {
|
||||
UnsavedEditCollection.insert({
|
||||
fieldName,
|
||||
|
|
@ -57,7 +53,7 @@ UnsavedEdits = {
|
|||
},
|
||||
|
||||
reset({ fieldName, docId }) {
|
||||
let currentDoc = this._getCollectionDocument(fieldName, docId);
|
||||
const currentDoc = this._getCollectionDocument(fieldName, docId);
|
||||
if (currentDoc) {
|
||||
UnsavedEditCollection.remove(currentDoc._id);
|
||||
}
|
||||
|
|
@ -65,13 +61,13 @@ UnsavedEdits = {
|
|||
|
||||
_getCollectionDocument(fieldName, docId) {
|
||||
return UnsavedEditCollection.findOne({fieldName, docId});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Blaze.registerHelper('getUnsavedValue', (fieldName, docId, defaultTo) => {
|
||||
// Workaround some blaze feature that ass a list of keywords arguments as the
|
||||
// last parameter (even if the caller didn't specify any).
|
||||
if (! _.isString(defaultTo)) {
|
||||
if (!_.isString(defaultTo)) {
|
||||
defaultTo = '';
|
||||
}
|
||||
return UnsavedEdits.get({ fieldName, docId }, defaultTo);
|
||||
|
|
|
|||
|
|
@ -1,62 +1,70 @@
|
|||
Utils = {
|
||||
// XXX We should remove these two methods
|
||||
goBoardId: function(_id) {
|
||||
var board = Boards.findOne(_id);
|
||||
goBoardId(_id) {
|
||||
const board = Boards.findOne(_id);
|
||||
return board && FlowRouter.go('board', {
|
||||
id: board._id,
|
||||
slug: board.slug
|
||||
slug: board.slug,
|
||||
});
|
||||
},
|
||||
|
||||
goCardId: function(_id) {
|
||||
var card = Cards.findOne(_id);
|
||||
var board = Boards.findOne(card.boardId);
|
||||
goCardId(_id) {
|
||||
const card = Cards.findOne(_id);
|
||||
const board = Boards.findOne(card.boardId);
|
||||
return board && FlowRouter.go('card', {
|
||||
cardId: card._id,
|
||||
boardId: board._id,
|
||||
slug: board.slug
|
||||
slug: board.slug,
|
||||
});
|
||||
},
|
||||
|
||||
capitalize: function(string) {
|
||||
capitalize(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
},
|
||||
|
||||
getLabelIndex: function(boardId, labelId) {
|
||||
var board = Boards.findOne(boardId);
|
||||
var labels = {};
|
||||
_.each(board.labels, function(a, b) {
|
||||
getLabelIndex(boardId, labelId) {
|
||||
const board = Boards.findOne(boardId);
|
||||
const labels = {};
|
||||
_.each(board.labels, (a, b) => {
|
||||
labels[a._id] = b;
|
||||
});
|
||||
return {
|
||||
index: labels[labelId],
|
||||
key: function(key) {
|
||||
return 'labels.' + labels[labelId] + '.' + key;
|
||||
}
|
||||
key(key) {
|
||||
return `labels.${labels[labelId]}.${key}`;
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
// Determine the new sort index
|
||||
calculateIndex: function(prevCardDomElement, nextCardDomElement, nCards) {
|
||||
nCards = nCards || 1;
|
||||
|
||||
calculateIndex(prevCardDomElement, nextCardDomElement, nCards = 1) {
|
||||
let base, increment;
|
||||
// If we drop the card to an empty column
|
||||
if (! prevCardDomElement && ! nextCardDomElement) {
|
||||
return {base: 0, increment: 1};
|
||||
if (!prevCardDomElement && !nextCardDomElement) {
|
||||
base = 0;
|
||||
increment = 1;
|
||||
// If we drop the card in the first position
|
||||
} else if (! prevCardDomElement) {
|
||||
return {base: Blaze.getData(nextCardDomElement).sort - 1, increment: -1};
|
||||
} else if (!prevCardDomElement) {
|
||||
base = Blaze.getData(nextCardDomElement).sort - 1;
|
||||
increment = -1;
|
||||
// If we drop the card in the last position
|
||||
} else if (! nextCardDomElement) {
|
||||
return {base: Blaze.getData(prevCardDomElement).sort + 1, increment: 1};
|
||||
} else if (!nextCardDomElement) {
|
||||
base = Blaze.getData(prevCardDomElement).sort + 1;
|
||||
increment = 1;
|
||||
}
|
||||
// In the general case take the average of the previous and next element
|
||||
// sort indexes.
|
||||
else {
|
||||
var prevSortIndex = Blaze.getData(prevCardDomElement).sort;
|
||||
var nextSortIndex = Blaze.getData(nextCardDomElement).sort;
|
||||
var increment = (nextSortIndex - prevSortIndex) / (nCards + 1);
|
||||
return {base: prevSortIndex + increment, increment: increment};
|
||||
const prevSortIndex = Blaze.getData(prevCardDomElement).sort;
|
||||
const nextSortIndex = Blaze.getData(nextCardDomElement).sort;
|
||||
increment = (nextSortIndex - prevSortIndex) / (nCards + 1);
|
||||
base = prevSortIndex + increment;
|
||||
}
|
||||
}
|
||||
// XXX Return a generator that yield values instead of a base with a
|
||||
// increment number.
|
||||
return {
|
||||
base,
|
||||
increment,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue