diff options
-rw-r--r-- | client/lib/filter.js | 1014 |
1 files changed, 507 insertions, 507 deletions
diff --git a/client/lib/filter.js b/client/lib/filter.js index 4652c397..c3c1b070 100644 --- a/client/lib/filter.js +++ b/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);
\ No newline at end of file +Blaze.registerHelper('Filter', Filter); |