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:
Maxime Quandalle 2015-09-03 23:12:46 +02:00
parent 039cfe7edf
commit b3851817ec
60 changed files with 1604 additions and 1692 deletions

View file

@ -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(),
};

View file

@ -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');

View file

@ -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);

View file

@ -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);
}
});

View file

@ -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',
}
);

View file

@ -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',
}],
});

View file

@ -1,4 +1,4 @@
const closedValue = null
const closedValue = null;
window.Modal = new class {
constructor() {

View file

@ -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',
}
);

View file

@ -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',
}
)
})
);
});

View file

@ -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);

View file

@ -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,
};
},
};