filter.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Filtered view manager
  2. // We define local filter objects for each different type of field (SetFilter,
  3. // RangeFilter, dateFilter, etc.). We then define a global `Filter` object whose
  4. // goal is to filter complete documents by using the local filters for each
  5. // fields.
  6. var showFilterSidebar = function() {
  7. Sidebar.setView('filter');
  8. };
  9. // Use a "set" filter for a field that is a set of documents uniquely
  10. // identified. For instance `{ labels: ['labelA', 'labelC', 'labelD'] }`.
  11. var SetFilter = function() {
  12. this._dep = new Tracker.Dependency();
  13. this._selectedElements = [];
  14. };
  15. _.extend(SetFilter.prototype, {
  16. isSelected: function(val) {
  17. this._dep.depend();
  18. return this._selectedElements.indexOf(val) > -1;
  19. },
  20. add: function(val) {
  21. if (this._indexOfVal(val) === -1) {
  22. this._selectedElements.push(val);
  23. this._dep.changed();
  24. showFilterSidebar();
  25. }
  26. },
  27. remove: function(val) {
  28. var indexOfVal = this._indexOfVal(val);
  29. if (this._indexOfVal(val) !== -1) {
  30. this._selectedElements.splice(indexOfVal, 1);
  31. this._dep.changed();
  32. }
  33. },
  34. toogle: function(val) {
  35. if (this._indexOfVal(val) === -1) {
  36. this.add(val);
  37. } else {
  38. this.remove(val);
  39. }
  40. },
  41. reset: function() {
  42. this._selectedElements = [];
  43. this._dep.changed();
  44. },
  45. _indexOfVal: function(val) {
  46. return this._selectedElements.indexOf(val);
  47. },
  48. _isActive: function() {
  49. this._dep.depend();
  50. return this._selectedElements.length !== 0;
  51. },
  52. _getMongoSelector: function() {
  53. this._dep.depend();
  54. return { $in: this._selectedElements };
  55. }
  56. });
  57. // The global Filter object.
  58. // XXX It would be possible to re-write this object more elegantly, and removing
  59. // the need to provide a list of `_fields`. We also should move methods into the
  60. // object prototype.
  61. Filter = {
  62. // XXX I would like to rename this field into `labels` to be consistent with
  63. // the rest of the schema, but we need to set some migrations architecture
  64. // before changing the schema.
  65. labelIds: new SetFilter(),
  66. members: new SetFilter(),
  67. _fields: ['labelIds', 'members'],
  68. // We don't filter cards that have been added after the last filter change. To
  69. // implement this we keep the id of these cards in this `_exceptions` fields
  70. // and use a `$or` condition in the mongo selector we return.
  71. _exceptions: [],
  72. _exceptionsDep: new Tracker.Dependency(),
  73. isActive: function() {
  74. var self = this;
  75. return _.any(self._fields, function(fieldName) {
  76. return self[fieldName]._isActive();
  77. });
  78. },
  79. _getMongoSelector: function() {
  80. var self = this;
  81. if (! self.isActive())
  82. return {};
  83. var filterSelector = {};
  84. _.forEach(self._fields, function(fieldName) {
  85. var filter = self[fieldName];
  86. if (filter._isActive())
  87. filterSelector[fieldName] = filter._getMongoSelector();
  88. });
  89. var exceptionsSelector = {_id: {$in: this._exceptions}};
  90. this._exceptionsDep.depend();
  91. return {$or: [filterSelector, exceptionsSelector]};
  92. },
  93. mongoSelector: function(additionalSelector) {
  94. var filterSelector = this._getMongoSelector();
  95. if (_.isUndefined(additionalSelector))
  96. return filterSelector;
  97. else
  98. return {$and: [filterSelector, additionalSelector]};
  99. },
  100. reset: function() {
  101. var self = this;
  102. _.forEach(self._fields, function(fieldName) {
  103. var filter = self[fieldName];
  104. filter.reset();
  105. });
  106. self.resetExceptions();
  107. },
  108. addException: function(_id) {
  109. if (this.isActive()) {
  110. this._exceptions.push(_id);
  111. this._exceptionsDep.changed();
  112. Tracker.flush();
  113. }
  114. },
  115. resetExceptions: function() {
  116. this._exceptions = [];
  117. this._exceptionsDep.changed();
  118. }
  119. };
  120. Blaze.registerHelper('Filter', Filter);