UNPKG

fluro

Version:

Promise based HTTP Fluro client for the browser and node.js

1,853 lines (1,347 loc) 44.6 kB
import _ from 'lodash'; import moment from 'moment'; import stringSimilarity from 'string-similarity'; // const stringSimilarity = () => import('string-similarity'); ////////////////////////////////////////////////////////////////////// var FilterService = {}; ////////////////////////////////////////////////////////////////////// FilterService.activeFilters = function(config) { var memo = []; getActiveFilter(config, memo); return memo; //////////////////////////// function getActiveFilter(block, memo) { var isValid = FilterService.isValidFilter(block); if (isValid) { memo.push(block); } if (block.filters && block.filters.length) { _.each(block.filters, function(b) { getActiveFilter(b, memo); }) } } } ////////////////////////////////////////////////////////////////////// FilterService.activeFilterRows = function(config) { return _.filter(FilterService.activeFilters, function(row) { return row.comparator && row.comparator.length; }) } FilterService.activeFilterKeys = function(config) { var keys = _.chain(FilterService.activeFilters(config)) .map(function(entry) { if (!entry || !entry.key) { return; } return FilterService.getRootKey(entry.key); }) .compact() .uniq() .value(); return keys; } ////////////////////////////////////////////////////////////////////// FilterService.activeFilterCriteriaString = function(config) { var criteriaValue = _.chain(FilterService.activeFilters(config)) .map(function(block) { if (!block.criteria || !block.criteria.length) { return; } ////////////////////////// var activeCriteria = FilterService.activeFilters({ filters: block.criteria }); if (!activeCriteria || !activeCriteria.length) { return; } ////////////////////////// return FilterService.getFilterChangeString({ filters: activeCriteria }); }) .flatten() .compact() .map(function(value) { var nameTitle = value.title || value.name || value._id || value; return String(nameTitle).toLowerCase(); }) .compact() .uniq() .value(); //////////////////////// return criteriaValue; } ////////////////////////////////////////////////////////////////////// FilterService.activeFilterValues = function(config) { var values = _.chain(FilterService.activeFilters(config)) .map(function(block) { var all = []; var comparator = FilterService.getComparator(block.comparator); if (!comparator) { return; } switch (comparator.inputType) { case 'array': all = all.concat(block.values); break; case 'range': case 'daterange': all = all.concat([block.value, block.value2]); break; default: all.push(block.computedValue, block.value); break; } return all; }) .flatten() .compact() .map(function(value) { var nameTitle = value.title || value.name || value._id || value; return String(nameTitle).toLowerCase(); }) .compact() .uniq() .value(); return values; } FilterService.activeFilterComparators = function(config) { var memo = []; getActiveFilterComparator(config, memo); return memo; //////////////////////////// function getActiveFilterComparator(block, memo) { var isValid = FilterService.isValidFilter(block); if (isValid) { memo.push(block.comparator); } if (block.filters && block.filters.length) { _.each(block.filters, function(b) { getActiveFilterComparator(b, memo); }) } } } FilterService.activeFilterOperators = function(config) { var memo = []; var trail = []; ////////////////////////// getActiveFilterBlockOperator(config, memo, trail); ////////////////////////// var flat = _.chain(memo) .flatten() .reduce(function(set, operator) { if (!set[operator]) { set[operator] = 0; } set[operator]++; return set; }, {}) .map(function(i, key) { return `${i}${key}`; }) .compact() .value(); //////////////////////////// return flat; //////////////////////////// function getActiveFilterBlockOperator(block, memo, trail) { var operator = block.operator; //If it's a rule set if (operator) { // //Add the path to the block trail.push(operator); /////////////////////////////// //Check if any of it's filters are valid and active var isValid = _.some(block.filters, function(filter) { return FilterService.isValidFilter(filter); }) if (isValid) { memo.push(trail.slice()); } } else { trail.length = 0; } //////////////////////////////////////////////// //Go down the tree further if (block.filters && block.filters.length) { _.each(block.filters, function(b) { getActiveFilterBlockOperator(b, memo, trail); }) } else { trail.length = 0; } } } ////////////////////////////////////////////////////////////////////// FilterService.getFilterChangeString = function(config) { //Put all this together so we only refilter when we actually need to //each of these will only return if the filter is valid and actually changes //effects the results, without this step the table will update everytime you change the filters var string = [ FilterService.activeFilterKeys(config).join(', '), FilterService.activeFilterValues(config).join(', '), FilterService.activeFilterCriteriaString(config).join(', '), FilterService.activeFilterComparators(config).join(', '), FilterService.activeFilterOperators(config).join(', '), ].join(''); return string; } ////////////////////////////////////////////////////////////////////// //Helper function to map an input to a basic string function getString(input, includeIDs) { if (!input) { return ''; } if (includeIDs) { if (input._id) { return String(input._id); } if (input._external) { return String(input._external); } } if (input.title) { return String(input.title).toLowerCase(); } if (input.name) { return String(input.name).toLowerCase(); } return String(input).toLowerCase() } ////////////////////////////////////////////////////////////////////// function getAllStringMatches(input, includeIDs) { var matches = []; if (!input) { return matches; } if (!_.isObject(input)) { return [String(input).toLowerCase()]; } if (_.isArray(input)) { return _.flatten(getAllStringMatches(input, includeIDs)) } if (includeIDs) { if (input._id && input._id.length) { matches.push(input._id); } if (input._external && input._external.length) { matches.push(input._external); } } if (input.title && input.title.length) { matches.push(String(input.title).toLowerCase()); } if (input.name && input.name.length) { matches.push(String(input.name).toLowerCase()); } return matches; } ////////////////////////////////////////////////////////////////////// function isBetween(input, from, to) { var startFloat = parseFloat(from); var endFloat = parseFloat(to); var checkFloat = parseFloat(input); var start = Math.min(startFloat, endFloat); var end = Math.max(startFloat, endFloat); var match = (checkFloat >= start) && (checkFloat <= end); return match; } ////////////////////////////////////////////////////////////////////// function isIn(input, range) { var stringInput = getString(input, true); //Range is the array we are checking in return _.some(range, function(entry) { var entryString = getString(entry, true); return stringInput == getString(entry, true); }); } /////////////////////////////////// function isEmpty(input) { if(!input) { return true; } if(input == undefined) { return true; } if(input == null) { return true; } if(input == '') { return true; } if(_.isArray(input) && !input.length) { return true; } } /////////////////////////////////// FilterService.matchAnyString = function(keywords, row) { var values = _.values(row); var string = getString(keywords); return _.some(values, function(input) { return _.includes(getString(input), string) }) } /////////////////////////////////// FilterService.isSimilar = function(input, mustMatchValue, options) { if (!options) { options = {}; } // var stringSimilarity = () => import('string-similarity'); var score = stringSimilarity.compareTwoStrings(getString(input), getString(mustMatchValue)); var matches = (score >= 0.6); if (options.source) { if (!options._similar) { options._similar = 0; } //Increase the score options._similar += matches; } return matches; } /////////////////////////////////// /////////////////////////////////// function isEqual(input, range) { //Input is the field from the row, range is the filter input typed by the user var matchAny = getAllStringMatches(input, true); var rangeAsString = getString(range, true); if (matchAny == rangeAsString) { return true; } var isMatch = _.includes(matchAny, rangeAsString); return isMatch } function isContained(input, range) { //TODO Check if this is necessary //As i think this will always be a string if (_.isString(range)) { range = getString(range); } return _.includes(range, getString(input)); } /////////////////////////////// function dateCompare(input, range, type, format) { var date1 = new Date(input); date1.setHours(0, 0, 0, 0); var date2 = new Date(range); date2.setHours(0, 0, 0, 0); switch (type) { case 'date': return String(date1) == String(date2); break; case 'week': return moment(date1).format('W YYYY') == moment(date2).format('W YYYY'); break; case 'month': return moment(date1).format('M YYYY') == moment(date2).format('M YYYY'); break; case 'year': return moment(date1).format('YYYY') == moment(date2).format('YYYY'); break; case 'dateanniversary': return moment(date1).format('D MMM') == moment(date2).format('D MMM'); break; case 'dateanniversarymonth': return moment(date1).format('MMM') == moment(date2).format('MMM'); break; // case 'weekday': // return moment(date1).format('dddd') == moment(date2).format('dddd'); // break; case 'before': return date1 < date2 break; case 'after': return date1 > date2 break; } } /////////////////////////////// FilterService.comparators = []; /////////////////////////////// //Date Comparators FilterService.comparators.push({ title: 'Is on day ', operator: 'datesameday', match(input, mustMatchValue) { if (_.isArray(input)) { return _.some(input, function(i) { return dateCompare(i, mustMatchValue, 'date'); }); } else { return dateCompare(input, mustMatchValue, 'date'); } }, // dateDisplayFormat:'D MMM YYYY', restrict: [ 'date', ], }) FilterService.comparators.push({ title: 'Anniversary Date', operator: 'dateanniversary', match(input, mustMatchValue) { if (_.isArray(input)) { return _.some(input, function(i) { return dateCompare(i, mustMatchValue, 'dateanniversary'); }); } else { return dateCompare(input, mustMatchValue, 'dateanniversary'); } }, // dateDisplayFormat: 'YYYY', restrict: [ 'date', ], }) // FilterService.comparators.push({ // title: 'Is between', // operator: 'datebetween', // match(input, mustMatchValue1, mustMatchValue2) { // var checkDate = new Date(input); // checkDate.setHours(0, 0, 0, 0); // var date1 = new Date(mustMatchValue1) // date1.setHours(0, 0, 0, 0); // var date2 = new Date(mustMatchValue2) // date2.setHours(0, 0, 0, 0); // return isBetween(checkDate.getTime(), date1.getTime(), date2.getTime()); // }, // restrict: [ // 'date', // ], // inputType: 'daterange', // }) FilterService.comparators.push({ title: 'Anniversary is Between', operator: 'dateanniversarybetween', match(input, mustMatchValue1, mustMatchValue2) { if (!input) { return; } var checkDate = new Date(input); if (isNaN(checkDate)) { return; } checkDate.setHours(0, 0, 0, 0); var date1 = new Date(mustMatchValue1) date1.setHours(0, 0, 0, 0); var date2 = new Date(mustMatchValue2) date2.setHours(0, 0, 0, 0); //////////////////////////////////////// var startDate = new Date(Math.min(date1, date2)); var endDate = new Date(Math.max(date1, date2)); //////////////////////////////////////// function zeroPadded(str) { str = String(str); if (str.length == 1) { return '0' + str; } return str; } //////////////////////////////////////// var checkTimestamp = parseInt(`${zeroPadded(checkDate.getMonth())}${zeroPadded(checkDate.getDate())}`); var startTimestamp = parseInt(`${zeroPadded(startDate.getMonth())}${zeroPadded(startDate.getDate())}`); var endTimestamp = parseInt(`${zeroPadded(endDate.getMonth())}${zeroPadded(endDate.getDate())}`); return checkTimestamp >= startTimestamp && checkTimestamp <= endTimestamp; }, dateDisplayFormat: 'YYYY', restrict: [ 'date', ], inputType: 'daterange', }) // FilterService.comparators.push({ // title: 'Anniversary Month', // operator: 'dateanniversarymonth', // match(input, mustMatchValue) { // // console.log('TEST MONTH', mustMatchValue); // if (_.isArray(input)) { // //Check if any of the dates on the row // return _.some(input, function(i) { // //match any of the dates provided in the array // return _.some(mustMatchValue, function(d) { // return dateCompare(i, d, 'dateanniversarymonth'); // }); // }); // } else { // //check if the date on the row matches any of the dates // //in our array // return _.some(mustMatchValue, function(d) { // return dateCompare(input, d, 'dateanniversarymonth'); // }); // } // }, // dateDisplayFormat: 'MMMM', // restrict: [ // 'date', // ], // inputType: 'array', // }) /**/ FilterService.comparators.push({ title: 'Is same week as', operator: 'datesameweek', match(input, mustMatchValue) { if (_.isArray(input)) { return _.some(input, function(i) { return dateCompare(i, mustMatchValue, 'week'); }); } else { return dateCompare(input, mustMatchValue, 'week'); } }, dateDisplayFormat: '[Wk]W YYYY', restrict: [ 'date', ], inputType: 'array', }) FilterService.comparators.push({ title: 'Is same month as', operator: 'datesamemonth', match(input, mustMatchValue) { // match(input, mustMatchValue) { // var mustMatchString = String(mustMatchValue); // // console.log('Check date input', input); // if (_.isArray(input)) { // return _.some(input, function(i) { // var weekdayInteger = moment(i).weekday() // return _.includes(mustMatchString, weekdayInteger); // }); // } else { // var weekdayInteger = moment(input).weekday() // return _.includes(mustMatchString, weekdayInteger); // } // }, if (_.isArray(input)) { return _.some(input, function(i) { return dateCompare(i, mustMatchValue, 'month'); }); } else { return dateCompare(input, mustMatchValue, 'month'); } }, dateDisplayFormat: 'MMM YYYY', restrict: [ 'date', ], }) FilterService.comparators.push({ title: 'Is same year as', operator: 'datesameyear', match(input, mustMatchValue) { if (_.isArray(input)) { return _.some(input, function(i) { return dateCompare(i, mustMatchValue, 'year'); }); } else { return dateCompare(input, mustMatchValue, 'year'); } }, dateDisplayFormat: 'YYYY', restrict: [ 'date', ], }) /**/ FilterService.comparators.push({ title: 'Is weekday', operator: 'datesameweekday', match(input, mustMatchValue) { var mustMatchString = String(mustMatchValue); // console.log('Check date input', input); if (_.isArray(input)) { return _.some(input, function(i) { var weekdayInteger = moment(i).weekday() return _.includes(mustMatchString, weekdayInteger); }); } else { var weekdayInteger = moment(input).weekday() return _.includes(mustMatchString, weekdayInteger); } }, dateDisplayFormat: 'dddd', restrict: [ 'date', ], inputType: 'array', }) FilterService.comparators.push({ title: 'Is before', operator: 'datebefore', match(input, mustMatchValue) { return dateCompare(input, mustMatchValue, 'before'); }, restrict: [ 'date', ], }) FilterService.comparators.push({ title: 'Is after', operator: 'dateafter', match(input, mustMatchValue) { return dateCompare(input, mustMatchValue, 'after'); }, restrict: [ 'date', ], }) FilterService.comparators.push({ title: 'Is between', operator: 'datebetween', match(input, mustMatchValue1, mustMatchValue2) { var checkDate = new Date(input); checkDate.setHours(0, 0, 0, 0); var date1 = new Date(mustMatchValue1) date1.setHours(0, 0, 0, 0); var date2 = new Date(mustMatchValue2) date2.setHours(0, 0, 0, 0); return isBetween(checkDate.getTime(), date1.getTime(), date2.getTime()); }, restrict: [ 'date', ], inputType: 'daterange', }) FilterService.comparators.push({ title: 'Is not between', operator: 'datenotbetween', match(input, mustMatchValue1, mustMatchValue2) { var checkDate = new Date(input); checkDate.setHours(0, 0, 0, 0); var date1 = new Date(mustMatchValue1) date1.setHours(0, 0, 0, 0); var date2 = new Date(mustMatchValue2) date2.setHours(0, 0, 0, 0); return !isBetween(checkDate.getTime(), date1.getTime(), date2.getTime()); }, restrict: [ 'date', ], inputType: 'daterange', }) /////////////////////////////// /////////////////////////////// /////////////////////////////// FilterService.comparators.push({ title: 'Is one of', operator: 'in', match(input, mustMatchValue) { if (_.isArray(input)) { //Check if any match return !_.some(input, function(i) { return isIn(i, mustMatchValue); }); } else { var matches = isIn(input, mustMatchValue); return matches; } }, restrict: [ 'string', 'email', 'url', // 'number', // 'integer', // 'decimal', // 'float', 'reference', ], inputType: 'array', }) FilterService.comparators.push({ title: 'Is not one of', operator: 'notin', match(input, mustMatchValue) { if (_.isArray(input)) { return _.some(input, function(i) { return !isIn(i, mustMatchValue); }); } else { return !isIn(input, mustMatchValue); } }, restrict: [ 'string', 'email', 'url', //'number', //'integer', //'decimal', //'float', 'reference', ], inputType: 'array', }) FilterService.comparators.push({ title: 'Is ', operator: '==', match(input, mustMatchValue) { if (_.isArray(input)) { return _.some(input, function(i) { return isEqual(i, mustMatchValue); }); } else { return isEqual(input, mustMatchValue); } }, restrict: [ 'string', 'email', 'url', 'boolean', 'number', 'integer', 'decimal', 'float', 'reference', ], }) FilterService.comparators.push({ title: 'Is not', operator: '!=', positive: false, match(input, mustMatchValue) { if (_.isArray(input)) { return !_.some(input, function(i) { return isEqual(i, mustMatchValue); }); } else { return !isEqual(input, mustMatchValue); } }, restrict: [ 'string', 'email', 'url', 'boolean', 'number', 'integer', 'decimal', 'float', 'reference', ], }) FilterService.comparators.push({ title: 'Starts with', operator: 'startswith', match(input, mustMatchValue) { if (_.isArray(input)) { return _.some(input, function(i) { return _.startsWith(getString(i), getString(mustMatchValue)) }); } else { return _.startsWith(getString(input), getString(mustMatchValue)) } }, restrict: [ 'string', 'email', 'url', 'reference', ], }) FilterService.comparators.push({ title: 'Ends with', operator: 'endswith', match(input, mustMatchValue) { if (_.isArray(input)) { return _.some(input, function(i) { return _.endsWith(getString(i), getString(mustMatchValue)) }); } else { return _.endsWith(getString(input), getString(mustMatchValue)) } }, restrict: [ 'string', 'email', 'url', 'reference', ], }) FilterService.comparators.push({ title: 'Is similar to', operator: 'like', match(input, mustMatchValue, mustMatchValue2, options) { if (_.isArray(input)) { return _.some(input, function(i) { return FilterService.isSimilar(i, mustMatchValue); }); } else { return FilterService.isSimilar(input, mustMatchValue); } }, restrict: ['string'], }) FilterService.comparators.push({ title: 'Contains characters', operator: 'contains', match(input, mustMatchValue) { if (_.isArray(input)) { return _.some(input, function(i) { return isContained(mustMatchValue, i); }); } else { return isContained(mustMatchValue, input); } }, restrict: ['string'], }) FilterService.comparators.push({ title: 'Does not contain characters', operator: 'excludes', match(input, mustMatchValue) { if (_.isArray(input)) { return !_.some(input, function(i) { return isContained(mustMatchValue, i); }); } else { return !isContained(mustMatchValue, input); } }, restrict: ['string'], }) FilterService.comparators.push({ title: 'Is greater than', operator: '>', match(input, mustMatchValue) { return parseFloat(input) > parseFloat(mustMatchValue); }, restrict: [ 'number', 'integer', 'decimal', 'float', ], }) FilterService.comparators.push({ title: 'Is less than', operator: '<', match(input, mustMatchValue) { return parseFloat(input) < parseFloat(mustMatchValue); }, restrict: [ 'number', 'integer', 'decimal', 'float', ], }) FilterService.comparators.push({ title: 'Is greater than or equal to', operator: '>=', match(input, mustMatchValue) { return parseFloat(input) >= parseFloat(mustMatchValue); }, restrict: [ 'number', 'integer', 'decimal', 'float', ], }) FilterService.comparators.push({ title: 'Is less than or equal to', operator: '<=', match(input, mustMatchValue) { return parseFloat(input) <= parseFloat(mustMatchValue); }, restrict: [ 'number', 'integer', 'decimal', 'float', ], }) FilterService.comparators.push({ title: 'Is between', operator: 'between', match(input, mustMatchValue1, mustMatchValue2) { return isBetween(input, mustMatchValue1, mustMatchValue2); }, restrict: [ 'number', 'integer', 'decimal', 'float', ], inputType: 'range', }) FilterService.comparators.push({ title: 'Is not between', operator: 'notbetween', match(input, mustMatchValue1, mustMatchValue2) { return !isBetween(input, mustMatchValue1, mustMatchValue2); }, restrict: [ 'number', 'integer', 'decimal', 'float', ], inputType: 'range', }) FilterService.comparators.push({ title: 'Is empty', operator: 'empty', match(input, mustMatchValue) { return isEmpty(input); }, inputType: 'none', }) FilterService.comparators.push({ title: 'Is not empty', operator: 'notempty', match(input, mustMatchValue) { return !isEmpty(input); }, inputType: 'none', }) FilterService.comparators.push({ title: 'Does not start with', operator: 'doesnotstartwith', match(input, mustMatchValue) { if (_.isArray(input)) { return !_.some(input, function(i) { return _.startsWith(getString(i), getString(mustMatchValue)) }); } else { return !_.startsWith(getString(input), getString(mustMatchValue)) } }, restrict: [ 'string', 'email', 'url', 'reference', ], }) FilterService.comparators.push({ title: 'Does not end with', operator: 'doesnotendwith', match(input, mustMatchValue) { if (_.isArray(input)) { return !_.some(input, function(i) { return _.endsWith(getString(i), getString(mustMatchValue)) }); } else { return !_.endsWith(getString(input), getString(mustMatchValue)) } }, restrict: [ 'string', 'email', 'url', 'reference', ], }) var allTypes = [ 'string', 'date', 'boolean', 'number', 'integer', 'decimal', 'float', 'reference', ] /////////////////////////////// FilterService.comparatorLookup = {}; FilterService.comparatorTypeLookup = {}; /////////////////////////////// //Loop through each available comparator _.each(FilterService.comparators, function(comparator) { //map each comparator FilterService.comparatorLookup[comparator.operator] = comparator; //Find any restrictions for this comparator var restrictTypes = comparator.restrict || []; //If there are none, then it's available to all types if (!restrictTypes.length) { restrictTypes = allTypes; } //And map to each type it's restricted for _.each(restrictTypes, function(type) { var existing = FilterService.comparatorTypeLookup[type]; if (!existing) { existing = FilterService.comparatorTypeLookup[type] = []; } //Add the comparator to the list existing.push(comparator); }) }); /////////////////////////////// //Quick fast way to retrieve the comparator FilterService.getComparator = function(operator) { return FilterService.comparatorLookup[operator] } ////////////////////////////////////////////////////////////////// FilterService.getComparatorsForType = function(type) { //Return the matches or just all comparators return FilterService.comparatorTypeLookup[type] || FilterService.comparators; } //////////////////////////////////////////////////////// FilterService.isValidFilter = function(block) { if (block.operator) { return _.some(block.filters, FilterService.isValidFilter); } //////////////////////////////////////////////////////// var comparator = FilterService.getComparator(block.comparator); if (!comparator) { return; } //////////////////////////////////////////////////////// var key = FilterService.getRootKey(block.key); if (!key || !key.length) { return; } //////////////////////////////////////////////////////// var computedValue; if (block.computedValue && String(block.computedValue).length) { computedValue = block.computedValue; } //////////////////////////////////////////////////////// switch (comparator.inputType) { case 'none': return true; break; case 'range': if (!block.value || isNaN(block.value)) { return; } if (!block.value2 || isNaN(block.value2)) { return; } break; case 'daterange': if (!block.value || !_.isDate(new Date(block.value))) { return; } if (!block.value2 || !_.isDate(new Date(block.value))) { return; } break; case 'array': if (!block.values || !block.values.length) { return; } break; default: //It's a date by process of elimination if (block.computedValue) { return true; } if (!block.value || !_.isDate(new Date(block.value))) { return; } break; } return true; } ////////////////////////////////////////////////////////////////// FilterService.filterGroupMatch = function(filterGroup, item) { //If it's a group var operator = filterGroup.operator; var returnValue; var validFilters = _.filter(filterGroup.filters, FilterService.isValidFilter); if (validFilters && validFilters.length) { switch (operator) { case 'or': returnValue = _.some(validFilters, function(filterBlock) { var wasMatch = FilterService.filterMatch(filterBlock, item) return wasMatch; }) break; case 'and': default: returnValue = _.every(validFilters, function(filterBlock) { var wasMatch = FilterService.filterMatch(filterBlock, item) return wasMatch; }) break; } } return returnValue; } ////////////////////////////////////////////////////////////////// FilterService.getRootKey = function(key) { return String(key).split('|')[0]; } ////////////////////////////////////////////////////////////////// // FilterService.filter(cards, { // filter:filterConfig, // startDate:'', // endDate:'', // search:'Sophia', // }) //Easy function to filter according to all specified criteria when in the front end FilterService.filter = function(items, options) { if (!options) { options = {}; } ////////////////////////////////////// var searchKeywords = options.search ? String(options.search).toLowerCase().trim() : null; var searchPieces = (searchKeywords || '').split(' '); var startDate = options.startDate ? new Date(options.startDate) : null; var endDate = options.endDate ? new Date(options.endDate) : null; var filterConfig = options.filter; ////////////////////////////////////// var activeFilters = FilterService.activeFilters(filterConfig); var hasActiveFilters = (activeFilters && activeFilters.length); var hasSearchKeywords = (searchKeywords && searchKeywords.length); var hasDateBoundaries = (startDate && endDate); ////////////////////////////// //No filters are active if (!hasActiveFilters && !hasSearchKeywords && !hasDateBoundaries) { return items; } ////////////////////////////// return _.filter(items, function(item) { //There is filter criteria if (hasActiveFilters) { //Check if it matches the filters and if it doesn't var matchesFilters = FilterService.filterGroupMatch(filterConfig, item); if (!matchesFilters) { return; } } ///////////////////////////////// var searchIsCorrect; //Check if it matches the search keywords if (hasSearchKeywords) { //Get the title var itemTitle = String(item.title).trim().toLowerCase(); var idString = String(item._id).trim().toLowerCase(); //If the keyword string is an exact match for the id of the item var exactIDMatch = (searchKeywords == idString); if (exactIDMatch) { //Search is a match searchIsCorrect = true; } else { //If the title matches the keywords exactly var exactMatch = _.includes(itemTitle, searchKeywords); if (exactMatch) { //We are all done here searchIsCorrect = true; } else { //Check if the the keywords matches var keywordString = (item.keywords || []).join(' '); //If there are keywords if (keywordString.length) { //Check if there is an exact match for keywords var exactMatch = _.includes(keywordString, searchKeywords); if (exactMatch) { //We're done searchIsCorrect = true; } else { //If it's a Multiword Match var multiMatch = _.every(searchPieces, function(partial) { return _.includes(itemTitle, partial) || _.includes(keywordString, partial); }) if (multiMatch) { searchIsCorrect = true; } // return FilterService.matchAnyString(searchKeywords, item); } } } } ////////////////////////////////////// //If we have a search but the item doesn't match it //then finish and return false here if (!searchIsCorrect) { return; } } ////////////////////////////// if (hasDateBoundaries) { startDate.setHours(0, 0, 0, 0); endDate.setHours(0, 0, 0, 0); ////////////////////// var itemStartDate = new Date(row.startDate); itemStartDate.setHours(0, 0, 0, 0); var itemEndDate = new Date(row.endDate); itemEndDate.setHours(0, 0, 0, 0); if (itemEndDate < startDate) { return; } if (itemStartDate > endDate) { return; } } ////////////////////////////// //We made it here so it must be a correct match return true; }); } ////////////////////////////////////////////////////////////////// //Pass through FilterService.filterMatch = function(filter, item) { if (filter.filters) { return FilterService.filterGroupMatch(filter, item); } var key = FilterService.getRootKey(filter.key); var mustMatchValue = filter.value; var mustMatchValue2 = filter.value2; //////////////////////////////////////// if (filter.computedValue && filter.computedValue.length) { mustMatchValue = filter.computedValue; } //////////////////////////////////////// //Find the comparator var comparator = FilterService.getComparator(filter.comparator || '=='); var inputType = comparator.inputType; //////////////////////////////////////// if (!key || !key.length) { return true; } //////////////////////////////////////// //If we are a range type filter if (inputType == 'array') { //Get the second value var mustMatchValue = filter.values || []; //If we don't have a value 2 then return true if (!mustMatchValue || !mustMatchValue.length) { return true; } } //////////////////////////////////////// if (inputType != 'none') { //If we don't have a value yet then return true if (typeof mustMatchValue == 'undefined' || mustMatchValue === null) { return true; } } //////////////////////////////////////// //If we are a range type filter if (inputType == 'range' || inputType == 'daterange') { //If we don't have a value 2 then return true if (typeof mustMatchValue2 == 'undefined' || mustMatchValue2 === null) { return true; } } //////////////////////////////////////// //Get the actual value on the item var itemValue = _.get(item, key); //////////////////////////////////////// if (inputType != 'none') { //If we don't have a value yet then return true if (typeof mustMatchValue == 'undefined' || mustMatchValue === null || mustMatchValue == '') { return; } } //////////////////////////////////////// //Return if it matches var itMatches = comparator.match(itemValue, mustMatchValue, mustMatchValue2, { source: item, key: key, }); return itMatches; } /////////////////////////////// /////////////////////////////// FilterService.allKeys = function(initFields, config) { if (!config) { return []; } if (!initFields) { initFields = []; } var definitionFields = _.chain(config) .get('definition.fields') .map(function(field) { return Object.assign({}, field, { key: 'data.' + field.key, }) }) .value(); ////////////////////////////////////////////////////////////////////////////////// var typeFields = _.chain(config) .get('type.fields') .map(function(field) { return Object.assign({}, field) }) .value(); ////////////////////////////////////////////////////////////////////////////////// var indexIterator = 0; ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// function getFlattenedFields(array, trail, titles) { return _.chain(array) .map(function(field, key) { //Create a new object so we don't mutate var field = Object.assign({}, field); var returnValue = []; ///////////////////////////////////////// //If there are sub fields if (field.fields && field.fields.length) { if (field.asObject || field.directive == 'embedded') { //Push the field itself trail.push(field.key); titles.push(field.title) field.trail = trail.slice(); field.titles = titles.slice(); trail.pop(); titles.pop(); returnValue.push(field); /////////////////////////////// //Prepend the key to all lowed fields if (field.maximum != 1) { trail.push(field.key + '[' + indexIterator + ']'); titles.push(field.title); } else { trail.push(field.key); titles.push(field.title); } } var fields = getFlattenedFields(field.fields, trail, titles); if (field.asObject || field.directive == 'embedded') { trail.pop() titles.pop(); } returnValue.push(fields); } else { ///////////////////////////////////////// //Push the field key trail.push(field.key); titles.push(field.title); field.trail = trail.slice(); field.titles = titles.slice(); trail.pop(); titles.pop(); returnValue.push(field); } ///////////////////////////////////////// return returnValue; }) .flattenDeep() .value(); } ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// var detailSheetFields = _.reduce(config.details, function(set, detailSheet) { // //Get all the flattened fields var flattened = getFlattenedFields(detailSheet.fields, [], []); ////////////////////////////////// var mapped = _.chain(flattened) .map(function(field) { if (field.type == 'group') { return; } return { title: detailSheet.title + ' - ' + field.titles.join(' > '), key: `details.${detailSheet.definitionName}.items[0].data.${field.trail.join('.')}`, minimum: field.minimum, maximum: field.maximum, detail: detailSheet.definitionName, type: field.type, } }) .compact() .value(); ////////////////////////////////// return set.concat(mapped); }, []); ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// var fields = initFields.concat(typeFields, definitionFields, detailSheetFields); return _.orderBy(fields, 'title') } /////////////////////////////// export default FilterService;