filter.js 3.5 KB

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