Browse Source

Fix lint errors.

Lauri Ojansivu 6 years ago
parent
commit
3f937764cb
1 changed files with 507 additions and 507 deletions
  1. 507 507
      client/lib/filter.js

+ 507 - 507
client/lib/filter.js

@@ -4,7 +4,7 @@
 // goal is to filter complete documents by using the local filters for each
 // fields.
 function showFilterSidebar() {
-    Sidebar.setView('filter');
+  Sidebar.setView('filter');
 }
 
 // Use a "set" filter for a field that is a set of documents uniquely
@@ -12,446 +12,446 @@ function showFilterSidebar() {
 // use "subField" for searching inside object Fields.
 // For instance '{ 'customFields._id': ['field1','field2']} (subField would be: _id)
 class SetFilter {
-    constructor(subField = '') {
-        this._dep = new Tracker.Dependency();
-        this._selectedElements = [];
-        this.subField = subField;
+  constructor(subField = '') {
+    this._dep = new Tracker.Dependency();
+    this._selectedElements = [];
+    this.subField = subField;
+  }
+
+  isSelected(val) {
+    this._dep.depend();
+    return this._selectedElements.indexOf(val) > -1;
+  }
+
+  add(val) {
+    if (this._indexOfVal(val) === -1) {
+      this._selectedElements.push(val);
+      this._dep.changed();
+      showFilterSidebar();
     }
+  }
 
-    isSelected(val) {
-        this._dep.depend();
-        return this._selectedElements.indexOf(val) > -1;
+  remove(val) {
+    const indexOfVal = this._indexOfVal(val);
+    if (this._indexOfVal(val) !== -1) {
+      this._selectedElements.splice(indexOfVal, 1);
+      this._dep.changed();
     }
+  }
 
-    add(val) {
-        if (this._indexOfVal(val) === -1) {
-            this._selectedElements.push(val);
-            this._dep.changed();
-            showFilterSidebar();
-        }
-    }
-
-    remove(val) {
-        const indexOfVal = this._indexOfVal(val);
-        if (this._indexOfVal(val) !== -1) {
-            this._selectedElements.splice(indexOfVal, 1);
-            this._dep.changed();
-        }
-    }
-
-    toggle(val) {
-        if (this._indexOfVal(val) === -1) {
-            this.add(val);
-        } else {
-            this.remove(val);
-        }
-    }
-
-    reset() {
-        this._selectedElements = [];
-        this._dep.changed();
-    }
-
-    _indexOfVal(val) {
-        return this._selectedElements.indexOf(val);
-    }
-
-    _isActive() {
-        this._dep.depend();
-        return this._selectedElements.length !== 0;
-    }
-
-    _getMongoSelector() {
-        this._dep.depend();
-        return {
-            $in: this._selectedElements
-        };
-    }
-
-    _getEmptySelector() {
-        this._dep.depend();
-        let includeEmpty = false;
-        this._selectedElements.forEach((el) => {
-            if (el === undefined) {
-                includeEmpty = true;
-            }
-        });
-        return includeEmpty ? {
-            $eq: []
-        } : null;
+  toggle(val) {
+    if (this._indexOfVal(val) === -1) {
+      this.add(val);
+    } else {
+      this.remove(val);
     }
+  }
+
+  reset() {
+    this._selectedElements = [];
+    this._dep.changed();
+  }
+
+  _indexOfVal(val) {
+    return this._selectedElements.indexOf(val);
+  }
+
+  _isActive() {
+    this._dep.depend();
+    return this._selectedElements.length !== 0;
+  }
+
+  _getMongoSelector() {
+    this._dep.depend();
+    return {
+      $in: this._selectedElements,
+    };
+  }
+
+  _getEmptySelector() {
+    this._dep.depend();
+    let includeEmpty = false;
+    this._selectedElements.forEach((el) => {
+      if (el === undefined) {
+        includeEmpty = true;
+      }
+    });
+    return includeEmpty ? {
+      $eq: [],
+    } : null;
+  }
 }
 
 
 // Advanced filter forms a MongoSelector from a users String.
 // Build by: Ignatz 19.05.2018 (github feuerball11)
 class AdvancedFilter {
-    constructor() {
-        this._dep = new Tracker.Dependency();
-        this._filter = '';
-        this._lastValide = {};
+  constructor() {
+    this._dep = new Tracker.Dependency();
+    this._filter = '';
+    this._lastValide = {};
+  }
+
+  set(str) {
+    this._filter = str;
+    this._dep.changed();
+  }
+
+  reset() {
+    this._filter = '';
+    this._lastValide = {};
+    this._dep.changed();
+  }
+
+  _isActive() {
+    this._dep.depend();
+    return this._filter !== '';
+  }
+
+  _filterToCommands() {
+    const commands = [];
+    let current = '';
+    let string = false;
+    let regex = false;
+    let wasString = false;
+    let ignore = false;
+    for (let i = 0; i < this._filter.length; i++) {
+      const char = this._filter.charAt(i);
+      if (ignore) {
+        ignore = false;
+        current += char;
+        continue;
+      }
+      if (char === '/') {
+        string = !string;
+        if (string) regex = true;
+        current += char;
+        continue;
+      }
+      if (char === '\'') {
+        string = !string;
+        if (string) wasString = true;
+        continue;
+      }
+      if (char === '\\' && !string) {
+        ignore = true;
+        continue;
+      }
+      if (char === ' ' && !string) {
+        commands.push({
+          'cmd': current,
+          'string': wasString,
+          regex,
+        });
+        wasString = false;
+        current = '';
+        continue;
+      }
+      current += char;
     }
-
-    set(str) {
-        this._filter = str;
-        this._dep.changed();
+    if (current !== '') {
+      commands.push({
+        'cmd': current,
+        'string': wasString,
+        regex,
+      });
     }
-
-    reset() {
-        this._filter = '';
-        this._lastValide = {};
-        this._dep.changed();
+    return commands;
+  }
+
+  _fieldNameToId(field) {
+    const found = CustomFields.findOne({
+      'name': field,
+    });
+    return found._id;
+  }
+
+  _fieldValueToId(field, value) {
+    const found = CustomFields.findOne({
+      'name': field,
+    });
+    if (found.settings.dropdownItems && found.settings.dropdownItems.length > 0) {
+      for (let i = 0; i < found.settings.dropdownItems.length; i++) {
+        if (found.settings.dropdownItems[i].name === value) {
+          return found.settings.dropdownItems[i]._id;
+        }
+      }
     }
-
-    _isActive() {
-        this._dep.depend();
-        return this._filter !== '';
+    return value;
+  }
+
+  _arrayToSelector(commands) {
+    try {
+      //let changed = false;
+      this._processSubCommands(commands);
+    } catch (e) {
+      return this._lastValide;
     }
-
-    _filterToCommands() {
-        const commands = [];
-        let current = '';
-        let string = false;
-        let regex = false;
-        let wasString = false;
-        let ignore = false;
-        for (let i = 0; i < this._filter.length; i++) {
-            const char = this._filter.charAt(i);
-            if (ignore) {
-                ignore = false;
-                current += char;
-                continue;
-            }
-            if (char === '/') {
-                string = !string;
-                if (string) regex = true;
-                current += char;
-                continue;
-            }
-            if (char === '\'') {
-                string = !string;
-                if (string) wasString = true;
-                continue;
-            }
-            if (char === '\\' && !string) {
-                ignore = true;
-                continue;
-            }
-            if (char === ' ' && !string) {
-                commands.push({
-                    'cmd': current,
-                    'string': wasString,
-                    regex
-                });
-                wasString = false;
-                current = '';
-                continue;
-            }
-            current += char;
+    this._lastValide = {
+      $or: commands,
+    };
+    return {
+      $or: commands,
+    };
+  }
+
+  _processSubCommands(commands) {
+    const subcommands = [];
+    let level = 0;
+    let start = -1;
+    for (let i = 0; i < commands.length; i++) {
+      if (commands[i].cmd) {
+        switch (commands[i].cmd) {
+        case '(':
+        {
+          level++;
+          if (start === -1) start = i;
+          continue;
         }
-        if (current !== '') {
-            commands.push({
-                'cmd': current,
-                'string': wasString,
-                regex
-            });
+        case ')':
+        {
+          level--;
+          commands.splice(i, 1);
+          i--;
+          continue;
         }
-        return commands;
-    }
-
-    _fieldNameToId(field) {
-        const found = CustomFields.findOne({
-            'name': field
-        });
-        return found._id;
-    }
-
-    _fieldValueToId(field, value) {
-        const found = CustomFields.findOne({
-            'name': field
-        });
-        if (found.settings.dropdownItems && found.settings.dropdownItems.length > 0) {
-            for (let i = 0; i < found.settings.dropdownItems.length; i++) {
-                if (found.settings.dropdownItems[i].name === value) {
-                    return found.settings.dropdownItems[i]._id;
-                }
-            }
+        default:
+        {
+          if (level > 0) {
+            subcommands.push(commands[i]);
+            commands.splice(i, 1);
+            i--;
+            continue;
+          }
         }
-        return value;
-    }
-
-    _arrayToSelector(commands) {
-        try {
-            //let changed = false;
-            this._processSubCommands(commands);
-        } catch (e) {
-            return this._lastValide;
         }
-        this._lastValide = {
-            $or: commands
-        };
-        return {
-            $or: commands
-        };
+      }
     }
-
-    _processSubCommands(commands) {
-        const subcommands = [];
-        let level = 0;
-        let start = -1;
-        for (let i = 0; i < commands.length; i++) {
-            if (commands[i].cmd) {
-                switch (commands[i].cmd) {
-                case '(':
-                    {
-                        level++;
-                        if (start === -1) start = i;
-                        continue;
-                    }
-                case ')':
-                    {
-                        level--;
-                        commands.splice(i, 1);
-                        i--;
-                        continue;
-                    }
-                default:
-                    {
-                        if (level > 0) {
-                            subcommands.push(commands[i]);
-                            commands.splice(i, 1);
-                            i--;
-                            continue;
-                        }
-                    }
-                }
-            }
+    if (start !== -1) {
+      this._processSubCommands(subcommands);
+      if (subcommands.length === 1)
+        commands.splice(start, 0, subcommands[0]);
+      else
+        commands.splice(start, 0, subcommands);
+    }
+    this._processConditions(commands);
+    this._processLogicalOperators(commands);
+  }
+
+  _processConditions(commands) {
+    for (let i = 0; i < commands.length; i++) {
+      if (!commands[i].string && commands[i].cmd) {
+        switch (commands[i].cmd) {
+        case '=':
+        case '==':
+        case '===':
+        {
+          const field = commands[i - 1].cmd;
+          const str = commands[i + 1].cmd;
+          if (commands[i + 1].regex) {
+            const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
+            let regex = null;
+            if (match.length > 2)
+              regex = new RegExp(match[1], match[2]);
+            else
+              regex = new RegExp(match[1]);
+            commands[i] = {
+              'customFields._id': this._fieldNameToId(field),
+              'customFields.value': regex,
+            };
+          } else {
+            commands[i] = {
+              'customFields._id': this._fieldNameToId(field),
+              'customFields.value': {
+                $in: [this._fieldValueToId(field, str), parseInt(str, 10)],
+              },
+            };
+          }
+          commands.splice(i - 1, 1);
+          commands.splice(i, 1);
+          //changed = true;
+          i--;
+          break;
         }
-        if (start !== -1) {
-            this._processSubCommands(subcommands);
-            if (subcommands.length === 1)
-                commands.splice(start, 0, subcommands[0]);
+        case '!=':
+        case '!==':
+        {
+          const field = commands[i - 1].cmd;
+          const str = commands[i + 1].cmd;
+          if (commands[i + 1].regex) {
+            const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
+            let regex = null;
+            if (match.length > 2)
+              regex = new RegExp(match[1], match[2]);
             else
-                commands.splice(start, 0, subcommands);
+              regex = new RegExp(match[1]);
+            commands[i] = {
+              'customFields._id': this._fieldNameToId(field),
+              'customFields.value': {
+                $not: regex,
+              },
+            };
+          } else {
+            commands[i] = {
+              'customFields._id': this._fieldNameToId(field),
+              'customFields.value': {
+                $not: {
+                  $in: [this._fieldValueToId(field, str), parseInt(str, 10)],
+                },
+              },
+            };
+          }
+          commands.splice(i - 1, 1);
+          commands.splice(i, 1);
+          //changed = true;
+          i--;
+          break;
         }
-        this._processConditions(commands);
-        this._processLogicalOperators(commands);
-    }
-
-    _processConditions(commands) {
-        for (let i = 0; i < commands.length; i++) {
-            if (!commands[i].string && commands[i].cmd) {
-                switch (commands[i].cmd) {
-                case '=':
-                case '==':
-                case '===':
-                    {
-                        const field = commands[i - 1].cmd;
-                        const str = commands[i + 1].cmd;
-                        if (commands[i + 1].regex) {
-                            const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
-                            let regex = null;
-                            if (match.length > 2)
-                                regex = new RegExp(match[1], match[2]);
-                            else
-                                regex = new RegExp(match[1]);
-                            commands[i] = {
-                                'customFields._id': this._fieldNameToId(field),
-                                'customFields.value': regex
-                            };
-                        } else {
-                            commands[i] = {
-                                'customFields._id': this._fieldNameToId(field),
-                                'customFields.value': {
-                                    $in: [this._fieldValueToId(field, str), parseInt(str, 10)]
-                                }
-                            };
-                        }
-                        commands.splice(i - 1, 1);
-                        commands.splice(i, 1);
-                        //changed = true;
-                        i--;
-                        break;
-                    }
-                case '!=':
-                case '!==':
-                    {
-                        const field = commands[i - 1].cmd;
-                        const str = commands[i + 1].cmd;
-                        if (commands[i + 1].regex) {
-                            const match = str.match(new RegExp('^/(.*?)/([gimy]*)$'));
-                            let regex = null;
-                            if (match.length > 2)
-                                regex = new RegExp(match[1], match[2]);
-                            else
-                                regex = new RegExp(match[1]);
-                            commands[i] = {
-                                'customFields._id': this._fieldNameToId(field),
-                                'customFields.value': {
-                                    $not: regex
-                                }
-                            };
-                        } else {
-                            commands[i] = {
-                                'customFields._id': this._fieldNameToId(field),
-                                'customFields.value': {
-                                    $not: {
-                                        $in: [this._fieldValueToId(field, str), parseInt(str, 10)]
-                                    }
-                                }
-                            };
-                        }
-                        commands.splice(i - 1, 1);
-                        commands.splice(i, 1);
-                        //changed = true;
-                        i--;
-                        break;
-                    }
-                case '>':
-                case 'gt':
-                case 'Gt':
-                case 'GT':
-                    {
-                        const field = commands[i - 1].cmd;
-                        const str = commands[i + 1].cmd;
-                        commands[i] = {
-                            'customFields._id': this._fieldNameToId(field),
-                            'customFields.value': {
-                                $gt: parseInt(str, 10)
-                            }
-                        };
-                        commands.splice(i - 1, 1);
-                        commands.splice(i, 1);
-                        //changed = true;
-                        i--;
-                        break;
-                    }
-                case '>=':
-                case '>==':
-                case 'gte':
-                case 'Gte':
-                case 'GTE':
-                    {
-                        const field = commands[i - 1].cmd;
-                        const str = commands[i + 1].cmd;
-                        commands[i] = {
-                            'customFields._id': this._fieldNameToId(field),
-                            'customFields.value': {
-                                $gte: parseInt(str, 10)
-                            }
-                        };
-                        commands.splice(i - 1, 1);
-                        commands.splice(i, 1);
-                        //changed = true;
-                        i--;
-                        break;
-                    }
-                case '<':
-                case 'lt':
-                case 'Lt':
-                case 'LT':
-                    {
-                        const field = commands[i - 1].cmd;
-                        const str = commands[i + 1].cmd;
-                        commands[i] = {
-                            'customFields._id': this._fieldNameToId(field),
-                            'customFields.value': {
-                                $lt: parseInt(str, 10)
-                            }
-                        };
-                        commands.splice(i - 1, 1);
-                        commands.splice(i, 1);
-                        //changed = true;
-                        i--;
-                        break;
-                    }
-                case '<=':
-                case '<==':
-                case 'lte':
-                case 'Lte':
-                case 'LTE':
-                    {
-                        const field = commands[i - 1].cmd;
-                        const str = commands[i + 1].cmd;
-                        commands[i] = {
-                            'customFields._id': this._fieldNameToId(field),
-                            'customFields.value': {
-                                $lte: parseInt(str, 10)
-                            }
-                        };
-                        commands.splice(i - 1, 1);
-                        commands.splice(i, 1);
-                        //changed = true;
-                        i--;
-                        break;
-                    }
-                }
-            }
+        case '>':
+        case 'gt':
+        case 'Gt':
+        case 'GT':
+        {
+          const field = commands[i - 1].cmd;
+          const str = commands[i + 1].cmd;
+          commands[i] = {
+            'customFields._id': this._fieldNameToId(field),
+            'customFields.value': {
+              $gt: parseInt(str, 10),
+            },
+          };
+          commands.splice(i - 1, 1);
+          commands.splice(i, 1);
+          //changed = true;
+          i--;
+          break;
+        }
+        case '>=':
+        case '>==':
+        case 'gte':
+        case 'Gte':
+        case 'GTE':
+        {
+          const field = commands[i - 1].cmd;
+          const str = commands[i + 1].cmd;
+          commands[i] = {
+            'customFields._id': this._fieldNameToId(field),
+            'customFields.value': {
+              $gte: parseInt(str, 10),
+            },
+          };
+          commands.splice(i - 1, 1);
+          commands.splice(i, 1);
+          //changed = true;
+          i--;
+          break;
+        }
+        case '<':
+        case 'lt':
+        case 'Lt':
+        case 'LT':
+        {
+          const field = commands[i - 1].cmd;
+          const str = commands[i + 1].cmd;
+          commands[i] = {
+            'customFields._id': this._fieldNameToId(field),
+            'customFields.value': {
+              $lt: parseInt(str, 10),
+            },
+          };
+          commands.splice(i - 1, 1);
+          commands.splice(i, 1);
+          //changed = true;
+          i--;
+          break;
+        }
+        case '<=':
+        case '<==':
+        case 'lte':
+        case 'Lte':
+        case 'LTE':
+        {
+          const field = commands[i - 1].cmd;
+          const str = commands[i + 1].cmd;
+          commands[i] = {
+            'customFields._id': this._fieldNameToId(field),
+            'customFields.value': {
+              $lte: parseInt(str, 10),
+            },
+          };
+          commands.splice(i - 1, 1);
+          commands.splice(i, 1);
+          //changed = true;
+          i--;
+          break;
         }
+        }
+      }
     }
-
-    _processLogicalOperators(commands) {
-        for (let i = 0; i < commands.length; i++) {
-            if (!commands[i].string && commands[i].cmd) {
-                switch (commands[i].cmd) {
-                case 'or':
-                case 'Or':
-                case 'OR':
-                case '|':
-                case '||':
-                    {
-                        const op1 = commands[i - 1];
-                        const op2 = commands[i + 1];
-                        commands[i] = {
-                            $or: [op1, op2]
-                        };
-                        commands.splice(i - 1, 1);
-                        commands.splice(i, 1);
-                        //changed = true;
-                        i--;
-                        break;
-                    }
-                case 'and':
-                case 'And':
-                case 'AND':
-                case '&':
-                case '&&':
-                    {
-                        const op1 = commands[i - 1];
-                        const op2 = commands[i + 1];
-                        commands[i] = {
-                            $and: [op1, op2]
-                        };
-                        commands.splice(i - 1, 1);
-                        commands.splice(i, 1);
-                        //changed = true;
-                        i--;
-                        break;
-                    }
-                case 'not':
-                case 'Not':
-                case 'NOT':
-                case '!':
-                    {
-                        const op1 = commands[i + 1];
-                        commands[i] = {
-                            $not: op1
-                        };
-                        commands.splice(i + 1, 1);
-                        //changed = true;
-                        i--;
-                        break;
-                    }
-                }
-            }
+  }
+
+  _processLogicalOperators(commands) {
+    for (let i = 0; i < commands.length; i++) {
+      if (!commands[i].string && commands[i].cmd) {
+        switch (commands[i].cmd) {
+        case 'or':
+        case 'Or':
+        case 'OR':
+        case '|':
+        case '||':
+        {
+          const op1 = commands[i - 1];
+          const op2 = commands[i + 1];
+          commands[i] = {
+            $or: [op1, op2],
+          };
+          commands.splice(i - 1, 1);
+          commands.splice(i, 1);
+          //changed = true;
+          i--;
+          break;
+        }
+        case 'and':
+        case 'And':
+        case 'AND':
+        case '&':
+        case '&&':
+        {
+          const op1 = commands[i - 1];
+          const op2 = commands[i + 1];
+          commands[i] = {
+            $and: [op1, op2],
+          };
+          commands.splice(i - 1, 1);
+          commands.splice(i, 1);
+          //changed = true;
+          i--;
+          break;
+        }
+        case 'not':
+        case 'Not':
+        case 'NOT':
+        case '!':
+        {
+          const op1 = commands[i + 1];
+          commands[i] = {
+            $not: op1,
+          };
+          commands.splice(i + 1, 1);
+          //changed = true;
+          i--;
+          break;
         }
+        }
+      }
     }
+  }
 
-    _getMongoSelector() {
-        this._dep.depend();
-        const commands = this._filterToCommands();
-        return this._arrayToSelector(commands);
-    }
+  _getMongoSelector() {
+    this._dep.depend();
+    const commands = this._filterToCommands();
+    return this._arrayToSelector(commands);
+  }
 
 }
 
@@ -460,101 +460,101 @@ class AdvancedFilter {
 // the need to provide a list of `_fields`. We also should move methods into the
 // object prototype.
 Filter = {
-    // XXX I would like to rename this field into `labels` to be consistent with
-    // the rest of the schema, but we need to set some migrations architecture
-    // before changing the schema.
-    labelIds: new SetFilter(),
-    members: new SetFilter(),
-    customFields: new SetFilter('_id'),
-    advanced: new AdvancedFilter(),
-
-    _fields: ['labelIds', 'members', 'customFields'],
-
-    // We don't filter cards that have been added after the last filter change. To
-    // implement this we keep the id of these cards in this `_exceptions` fields
-    // and use a `$or` condition in the mongo selector we return.
-    _exceptions: [],
-    _exceptionsDep: new Tracker.Dependency(),
-
-    isActive() {
-        return _.any(this._fields, (fieldName) => {
-            return this[fieldName]._isActive();
-        }) || this.advanced._isActive();
-    },
-
-    _getMongoSelector() {
-        if (!this.isActive())
-            return {};
-
-        const filterSelector = {};
-        const emptySelector = {};
-        let includeEmptySelectors = false;
-        this._fields.forEach((fieldName) => {
-            const filter = this[fieldName];
-            if (filter._isActive()) {
-                if (filter.subField !== '') {
-                    filterSelector[`${fieldName}.${filter.subField}`] = filter._getMongoSelector();
-                } else {
-                    filterSelector[fieldName] = filter._getMongoSelector();
-                }
-                emptySelector[fieldName] = filter._getEmptySelector();
-                if (emptySelector[fieldName] !== null) {
-                    includeEmptySelectors = true;
-                }
-            }
-        });
-
-        const exceptionsSelector = {
-            _id: {
-                $in: this._exceptions
-            }
-        };
-        this._exceptionsDep.depend();
-
-        const selectors = [exceptionsSelector];
-
-        if (_.any(this._fields, (fieldName) => {
-                return this[fieldName]._isActive();
-            })) selectors.push(filterSelector);
-        if (includeEmptySelectors) selectors.push(emptySelector);
-        if (this.advanced._isActive()) selectors.push(this.advanced._getMongoSelector());
-
-        return {
-            $or: selectors
-        };
-    },
-
-    mongoSelector(additionalSelector) {
-        const filterSelector = this._getMongoSelector();
-        if (_.isUndefined(additionalSelector))
-            return filterSelector;
-        else
-            return {
-                $and: [filterSelector, additionalSelector]
-            };
-    },
-
-    reset() {
-        this._fields.forEach((fieldName) => {
-            const filter = this[fieldName];
-            filter.reset();
-        });
-        this.advanced.reset();
-        this.resetExceptions();
-    },
-
-    addException(_id) {
-        if (this.isActive()) {
-            this._exceptions.push(_id);
-            this._exceptionsDep.changed();
-            Tracker.flush();
+  // XXX I would like to rename this field into `labels` to be consistent with
+  // the rest of the schema, but we need to set some migrations architecture
+  // before changing the schema.
+  labelIds: new SetFilter(),
+  members: new SetFilter(),
+  customFields: new SetFilter('_id'),
+  advanced: new AdvancedFilter(),
+
+  _fields: ['labelIds', 'members', 'customFields'],
+
+  // We don't filter cards that have been added after the last filter change. To
+  // implement this we keep the id of these cards in this `_exceptions` fields
+  // and use a `$or` condition in the mongo selector we return.
+  _exceptions: [],
+  _exceptionsDep: new Tracker.Dependency(),
+
+  isActive() {
+    return _.any(this._fields, (fieldName) => {
+      return this[fieldName]._isActive();
+    }) || this.advanced._isActive();
+  },
+
+  _getMongoSelector() {
+    if (!this.isActive())
+      return {};
+
+    const filterSelector = {};
+    const emptySelector = {};
+    let includeEmptySelectors = false;
+    this._fields.forEach((fieldName) => {
+      const filter = this[fieldName];
+      if (filter._isActive()) {
+        if (filter.subField !== '') {
+          filterSelector[`${fieldName}.${filter.subField}`] = filter._getMongoSelector();
+        } else {
+          filterSelector[fieldName] = filter._getMongoSelector();
+        }
+        emptySelector[fieldName] = filter._getEmptySelector();
+        if (emptySelector[fieldName] !== null) {
+          includeEmptySelectors = true;
         }
-    },
+      }
+    });
+
+    const exceptionsSelector = {
+      _id: {
+        $in: this._exceptions,
+      },
+    };
+    this._exceptionsDep.depend();
+
+    const selectors = [exceptionsSelector];
+
+    if (_.any(this._fields, (fieldName) => {
+      return this[fieldName]._isActive();
+    })) selectors.push(filterSelector);
+    if (includeEmptySelectors) selectors.push(emptySelector);
+    if (this.advanced._isActive()) selectors.push(this.advanced._getMongoSelector());
+
+    return {
+      $or: selectors,
+    };
+  },
+
+  mongoSelector(additionalSelector) {
+    const filterSelector = this._getMongoSelector();
+    if (_.isUndefined(additionalSelector))
+      return filterSelector;
+    else
+      return {
+        $and: [filterSelector, additionalSelector],
+      };
+  },
+
+  reset() {
+    this._fields.forEach((fieldName) => {
+      const filter = this[fieldName];
+      filter.reset();
+    });
+    this.advanced.reset();
+    this.resetExceptions();
+  },
+
+  addException(_id) {
+    if (this.isActive()) {
+      this._exceptions.push(_id);
+      this._exceptionsDep.changed();
+      Tracker.flush();
+    }
+  },
 
-    resetExceptions() {
-        this._exceptions = [];
-        this._exceptionsDep.changed();
-    },
+  resetExceptions() {
+    this._exceptions = [];
+    this._exceptionsDep.changed();
+  },
 };
 
-Blaze.registerHelper('Filter', Filter);
+Blaze.registerHelper('Filter', Filter);