text: /*
   * Smart-Search
   * https://github.com/vdefilip/smart-search
   * Licensed under the MIT license.
   */
  
  'use strict';
  
  (function () {
  
    var _match = function (pattern, text, offset, options) {
      var insertions = 0;
      var matchIndexes = [];
      var iPattern = 0;
      for (var iText=offset; iText<text.length; iText++) {
        if (text[iText] === pattern[iPattern]) {
          matchIndexes.push(iText);
          if (++iPattern === pattern.length) {
            return ({
              insertions: insertions,
              matchIndexes: matchIndexes,
            });
          }
        } else if (matchIndexes.length) {
          insertions++;
          if (options.maxInsertions > -1
            && insertions > options.maxInsertions) {
            return null;
          }
        }
      }
      return null;
    };
  
    var _find = function (pattern, text, options) {
      var match = false;
      var insertions = null;
      var matchIndexes = null;
      var iPattern = 0;
      if (options.caseSensitive === false) {
        pattern = pattern.toLowerCase();
        text = text.toLowerCase();
      }
      for (var iText=0; iText<text.length; iText++) {
        if (text[iText] === pattern[iPattern]) {
          var res = _match(pattern, text, iText, options);
          if (res && (match === false || res.insertions <= insertions)) {
            if (match === false || res.insertions < insertions) {
              match = true;
              insertions = res.insertions;
              matchIndexes = res.matchIndexes;
            } else {
              matchIndexes = matchIndexes.concat(res.matchIndexes);
            }
          }
        }
      }
      if (match) {
        return ({
          value: pattern,
          insertions: insertions,
          matchIndexes: matchIndexes,
        });
      }
      return null;
    };
  
    var _score = function (entryResults) {
      var patternsMinInsertions = {};
      var patternsMinMatchIndex = {};
      entryResults.forEach(function (fieldResults) {
        fieldResults.patterns.forEach(function (pattern) {
          if (patternsMinInsertions[pattern.value] === undefined
            || pattern.insertions < patternsMinInsertions[pattern.value]) {
            patternsMinInsertions[pattern.value] = pattern.insertions;
            patternsMinMatchIndex[pattern.value] = pattern.matchIndexes;
          }
        });
      });
      var minInsertions = 0;
      var minMatchIndex = [];
      for (var pattern in patternsMinInsertions) {
        if (patternsMinInsertions.hasOwnProperty(pattern)) {
          minInsertions += patternsMinInsertions[pattern];
          minMatchIndex = minMatchIndex.concat(patternsMinMatchIndex[pattern]);
        }
      }
      return minInsertions + minMatchIndex.sort()[0] / 1000;
    };
  
    var _getFieldString = function (entry, field) {
      var path = field;
      var current = entry;
      for (var i=0; i<path.length; i++) {
        if (current[path[i]] === undefined) {
          return null;
        } else {
          current = current[path[i]];
        }
      }
      if (typeof current !== 'string') {
          return null;
      }
      return current;
    };
  
    var _forEachObject = function (object, fn) {
      var _locals = [];
  
      (function _private(object) {
        for (var key in object) {
          _locals.push(key);
          if (typeof object[key] === 'object') {
            _private(object[key]);
          } else {
            fn([].concat(_locals));
          }
          _locals.pop();
        }
      })(object);
    };
  
    var _search = function (entries, patterns, fields, options) {
      var results = [];
      entries.forEach(function (entry) {
        var match = false;
        var entryMatch = [];
        var entryResults = [];
        _forEachObject(fields, function (field) {
          var fieldString = _getFieldString(entry, field);
          if (fieldString === null) {
            return;
          }
          var fieldMatch = [];
          var fieldResults = {field:field.join('.'), patterns:[]};
          patterns.forEach(function (pattern) {
            var res = _find(pattern, fieldString, options);
            if (res) {
              fieldResults.patterns.push(res);
              fieldMatch.push(pattern);
              if (entryMatch.indexOf(pattern) === -1) {
                entryMatch.push(pattern);
              }
            }
          });
          if (fieldMatch.length === patterns.length) {
            entryResults.push(fieldResults);
            match = true;
          }
          else if (options.fieldMatching === false
            && fieldResults.patterns.length > 0) {
            entryResults.push(fieldResults);
          }
        });
        if ((options.fieldMatching === true && match === true) ||
          (options.fieldMatching === false
            && entryMatch.length === patterns.length)) {
          results.push({
            entry: entry,
            info: entryResults,
            score: _score(entryResults)
          });
        }
      });
      return results;
    };
  
    var _buildOptions = function(options) {
      var defaultOptions = {
        caseSensitive: false,
        fieldMatching: false,
        maxInsertions: -1,
      };
      if (options === undefined) {
        return defaultOptions;
      }
      for (var option in defaultOptions) {
        if (options[option] !== undefined) {
          defaultOptions[option] = options[option];
        }
      }
      return defaultOptions;
    };
  
    var sanitizeArray = function (array, caseSensitive) {
      if (array === undefined || array.length === undefined
        || array.length === 0) {
        return [];
      }
      var values = {};
      var newArray = [];
      array.forEach(function (elem) {
        if (typeof elem !== 'string') {
          return;
        }
        var element = !caseSensitive ? elem.toLowerCase() : elem;
        if (element && (element in values) === false) {
          values[element] = true;
          newArray.push(element);
        }
      });
      return newArray;
    };
  
    function smartSearch(entries, patterns, fields, options) {
      options = _buildOptions(options);
      patterns = sanitizeArray([].concat(patterns), options.caseSensitive);
      fields = typeof fields === 'string' ? { [fields]: true } : fields;
      if (entries.length === 0 || patterns.length === 0) {
        return;
      }
      var results = _search(entries, patterns, fields, options);
      results.sort(function (a, b) {
        return (a.score - b.score);
      });
      return results;
    }
  
    if (typeof exports !== 'undefined') {
      if (typeof module !== 'undefined' && module.exports) {
        exports = module.exports = smartSearch;
      }
      exports.smartSearch = smartSearch;
    }
    else if (angular) {
      angular
        .module('ngSmartSearch', [])
        .filter('smartSearch', function () {
          return smartSearch;
        });
    } else {
      window.smartSearch = smartSearch;
    }
  
  })();
  
  
  
  