summaryrefslogtreecommitdiffstats
path: root/client/lib
diff options
context:
space:
mode:
Diffstat (limited to 'client/lib')
-rw-r--r--client/lib/filter.js11
-rw-r--r--client/lib/keyboard.js13
-rw-r--r--client/lib/multiSelection.js159
-rw-r--r--client/lib/popup.js4
4 files changed, 182 insertions, 5 deletions
diff --git a/client/lib/filter.js b/client/lib/filter.js
index d96fa89c..359b65d3 100644
--- a/client/lib/filter.js
+++ b/client/lib/filter.js
@@ -91,7 +91,7 @@ Filter = {
});
},
- getMongoSelector: function() {
+ _getMongoSelector: function() {
var self = this;
if (! self.isActive())
@@ -110,6 +110,14 @@ Filter = {
return {$or: [filterSelector, exceptionsSelector]};
},
+ mongoSelector: function(additionalSelector) {
+ var filterSelector = this._getMongoSelector();
+ if (_.isUndefined(additionalSelector))
+ return filterSelector;
+ else
+ return {$and: [filterSelector, additionalSelector]};
+ },
+
reset: function() {
var self = this;
_.forEach(self._fields, function(fieldName) {
@@ -123,6 +131,7 @@ Filter = {
if (this.isActive()) {
this._exceptions.push(_id);
this._exceptionsDep.changed();
+ Tracker.flush();
}
},
diff --git a/client/lib/keyboard.js b/client/lib/keyboard.js
index 0fbdbfd5..8b105c28 100644
--- a/client/lib/keyboard.js
+++ b/client/lib/keyboard.js
@@ -47,11 +47,16 @@ EscapeActions = {
'textcomplete',
'popup',
'inlinedForm',
+ 'multiselection-disable',
'sidebarView',
- 'detailedPane'
+ 'detailsPane',
+ 'multiselection-reset'
],
- register: function(label, condition, action) {
+ register: function(label, action, condition) {
+ if (_.isUndefined(condition))
+ condition = function() { return true; };
+
// XXX Rewrite this with ES6: .push({ priority, condition, action })
var priority = this.hierarchy.indexOf(label);
if (priority === -1) {
@@ -87,6 +92,10 @@ EscapeActions = {
if (!! currentAction.condition())
currentAction.action();
}
+ },
+
+ executeAll: function() {
+ return this.executeLowerThan();
}
};
diff --git a/client/lib/multiSelection.js b/client/lib/multiSelection.js
new file mode 100644
index 00000000..53c16da0
--- /dev/null
+++ b/client/lib/multiSelection.js
@@ -0,0 +1,159 @@
+
+var getCardsBetween = function(idA, idB) {
+
+ var pluckId = function(doc) {
+ return doc._id;
+ };
+
+ var getListsStrictlyBetween = function(id1, id2) {
+ return Lists.find({
+ $and: [
+ { sort: { $gt: Lists.findOne(id1).sort } },
+ { sort: { $lt: Lists.findOne(id2).sort } }
+ ],
+ archived: false
+ }).map(pluckId);
+ };
+
+ var cards = _.sortBy([Cards.findOne(idA), Cards.findOne(idB)], function(c) {
+ return c.sort;
+ });
+
+ var selector;
+ if (cards[0].listId === cards[1].listId) {
+ selector = {
+ listId: cards[0].listId,
+ sort: {
+ $gte: cards[0].sort,
+ $lte: cards[1].sort
+ },
+ archived: false
+ };
+ } else {
+ selector = {
+ $or: [{
+ listId: cards[0].listId,
+ sort: { $lte: cards[0].sort }
+ }, {
+ listId: {
+ $in: getListsStrictlyBetween(cards[0].listId, cards[1].listId)
+ }
+ }, {
+ listId: cards[1].listId,
+ sort: { $gte: cards[1].sort }
+ }],
+ archived: false
+ };
+ }
+
+ return Cards.find(Filter.mongoSelector(selector)).map(pluckId);
+};
+
+MultiSelection = {
+ sidebarView: 'multiselection',
+
+ _selectedCards: new ReactiveVar([]),
+
+ _isActive: new ReactiveVar(false),
+
+ startRangeCardId: null,
+
+ reset: function() {
+ this._selectedCards.set([]);
+ },
+
+ getMongoSelector: function() {
+ return Filter.mongoSelector({
+ _id: { $in: this._selectedCards.get() }
+ });
+ },
+
+ isActive: function() {
+ return this._isActive.get();
+ },
+
+ isEmpty: function() {
+ return this._selectedCards.get().length === 0;
+ },
+
+ activate: function() {
+ if (! this.isActive()) {
+ EscapeActions.executeLowerThan('detailsPane');
+ this._isActive.set(true);
+ Sidebar.setView(this.sidebarView);
+ Tracker.flush();
+ }
+ },
+
+ disable: function() {
+ if (this.isActive()) {
+ this._isActive.set(false);
+ if (Sidebar && Sidebar.getView() === this.sidebarView) {
+ Sidebar.setView();
+ }
+ }
+ },
+
+ add: function(cardIds) {
+ return this.toogle(cardIds, { add: true, remove: false });
+ },
+
+ remove: function(cardIds) {
+ return this.toogle(cardIds, { add: false, remove: true });
+ },
+
+ toogleRange: function(cardId) {
+ var selectedCards = this._selectedCards.get();
+ var startRange;
+ this.reset();
+ if (! this.isActive() || selectedCards.length === 0) {
+ this.toogle(cardId);
+ } else {
+ startRange = selectedCards[selectedCards.length - 1];
+ this.toogle(getCardsBetween(startRange, cardId));
+ }
+ },
+
+ toogle: function(cardIds, options) {
+ var self = this;
+ cardIds = _.isString(cardIds) ? [cardIds] : cardIds;
+ options = _.extend({
+ add: true,
+ remove: true
+ }, options || {});
+
+ if (! self.isActive()) {
+ self.reset();
+ self.activate();
+ }
+
+ var selectedCards = self._selectedCards.get();
+
+ _.each(cardIds, function(cardId) {
+ var indexOfCard = selectedCards.indexOf(cardId);
+
+ if (options.remove && indexOfCard > -1)
+ selectedCards.splice(indexOfCard, 1);
+
+ else if (options.add)
+ selectedCards.push(cardId);
+ });
+
+ self._selectedCards.set(selectedCards);
+ },
+
+ isSelected: function(cardId) {
+ return this._selectedCards.get().indexOf(cardId) > -1;
+ }
+};
+
+Blaze.registerHelper('MultiSelection', MultiSelection);
+
+EscapeActions.register('multiselection-disable',
+ function() { MultiSelection.disable(); },
+ function() { return MultiSelection.isActive(); }
+);
+
+EscapeActions.register('multiselection-reset',
+ function() { MultiSelection.reset(); }
+);
diff --git a/client/lib/popup.js b/client/lib/popup.js
index 6298ba81..46c137e8 100644
--- a/client/lib/popup.js
+++ b/client/lib/popup.js
@@ -205,6 +205,6 @@ $(document).on('click', function(evt) {
// Press escape to close the popup.
var bindPopup = function(f) { return _.bind(f, Popup); };
EscapeActions.register('popup',
- bindPopup(Popup.isOpen),
- bindPopup(Popup.close)
+ bindPopup(Popup.close),
+ bindPopup(Popup.isOpen)
);