text: //
  // Dotty makes it easy to programmatically access arbitrarily nested objects and
  // their properties.
  //
  
  //
  // `object` is an object, `path` is the path to the property you want to check
  // for existence of.
  //
  // `path` can be provided as either a `"string.separated.with.dots"` or as
  // `["an", "array"]`.
  //
  // Returns `true` if the path can be completely resolved, `false` otherwise.
  //
  
  var exists = module.exports.exists = function exists(object, path) {
    if (typeof path === "string") {
      path = path.split(".");
    }
  
    if (!(path instanceof Array) || path.length === 0) {
      return false;
    }
  
    path = path.slice();
  
    var key = path.shift();
  
    if (typeof object !== "object" || object === null) {
      return false;
    }
  
    if (path.length === 0) {
      return Object.hasOwnProperty.apply(object, [key]);
    } else {
      return exists(object[key], path);
    }
  };
  
  //
  // These arguments are the same as those for `exists`.
  //
  // The return value, however, is the property you're trying to access, or
  // `undefined` if it can't be found. This means you won't be able to tell
  // the difference between an unresolved path and an undefined property, so you 
  // should not use `get` to check for the existence of a property. Use `exists`
  // instead.
  //
  
  var get = module.exports.get = function get(object, path) {
    if (typeof path === "string") {
      path = path.split(".");
    }
  
    if (!(path instanceof Array) || path.length === 0) {
      return;
    }
  
    path = path.slice();
  
    var key = path.shift();
  
    if (typeof object !== "object" || object === null) {
      return;
    }
  
    if (path.length === 0) {
      return object[key];
    }
  
    if (path.length) {
      return get(object[key], path);
    }
  };
  
  //
  // Arguments are similar to `exists` and `get`, with the exception that path
  // components are regexes with some special cases. If a path component is `"*"`
  // on its own, it'll be converted to `/.*/`.
  //
  // The return value is an array of values where the key path matches the
  // specified criterion. If none match, an empty array will be returned.
  //
  // If an action function is specified, that action will be applied to each
  // match. Action params are value, parent and key.
  //
  
  var search = module.exports.search = function search(object, path, action) {
    if (typeof path === "string") {
      path = path.split(".");
    }
  
    if (!(path instanceof Array) || path.length === 0) {
      return;
    }
  
    path = path.slice();
  
    var key = path.shift();
  
    if (typeof object !== "object" || object === null) {
      return;
    }
  
    if (key === "*") {
      key = ".*";
    }
  
    if (typeof key === "string") {
      key = new RegExp(key);
    }
  
    if (path.length === 0) {
      return Object.keys(object).filter(key.test.bind(key)).map(function(k) {
        var value = object[k];
        if(action){
          action(value, object, k);
        }
        return value;
      });
    } else {
      return Array.prototype.concat.apply([], Object.keys(object).filter(key.test.bind(key)).map(function(k) { return search(object[k], path, action); }));
    }
  };
  
  //
  // Perform a search and remove the matched keys.
  // The return value is the same object argument with modifications.
  //
  
  var removeSearch = module.exports.removeSearch = function removeSearch(object, path){
    search(object, path, function(value, object, key){
      delete object[key];
    });
    return object;
  };
  
  //
  // The first two arguments for `put` are the same as `exists` and `get`.
  //
  // The third argument is a value to `put` at the `path` of the `object`.
  // Objects in the middle will be created if they don't exist, or added to if
  // they do. If a value is encountered in the middle of the path that is *not*
  // an object, it will not be overwritten.
  //
  // The return value is `true` in the case that the value was `put`
  // successfully, or `false` otherwise.
  //
  
  var put = module.exports.put = function put(object, path, value) {
    if (typeof path === "string") {
      path = path.split(".");
    }
  
    if (!(path instanceof Array) || path.length === 0) {
      return false;
    }
    
    path = path.slice();
  
    var key = path.shift();
  
    if (typeof object !== "object" || object === null) {
      return false;
    }
  
    if (path.length === 0) {
      object[key] = value;
    } else {
      if (typeof object[key] === "undefined") {
        object[key] = {};
      }
  
      if (typeof object[key] !== "object" || object[key] === null) {
        return false;
      }
  
      return put(object[key], path, value);
    }
  };
  
  //
  // `remove` is like `put` in reverse!
  //
  // The return value is `true` in the case that the value existed and was removed
  // successfully, or `false` otherwise.
  //
  
  var remove = module.exports.remove = function remove(object, path, value) {
    if (typeof path === "string") {
      path = path.split(".");
    }
  
    if (!(path instanceof Array) || path.length === 0) {
      return false;
    }
    
    path = path.slice();
  
    var key = path.shift();
  
    if (typeof object !== "object" || object === null) {
      return false;
    }
  
    if (path.length === 0) {
      if (!Object.hasOwnProperty.call(object, key)) {
        return false;
      }
  
      delete object[key];
  
      return true;
    } else {
      return remove(object[key], path, value);
    }
  };
  
  //
  // `deepKeys` creates a list of all possible key paths for a given object.
  //
  // The return value is always an array, the members of which are paths in array
  // format. If you want them in dot-notation format, do something like this:
  //
  // ```js
  // dotty.deepKeys(obj).map(function(e) {
  //   return e.join(".");
  // });
  // ```
  //
  // *Note: this will probably explode on recursive objects. Be careful.*
  //
  
  var deepKeys = module.exports.deepKeys = function deepKeys(object, options, prefix) {
    options = options || {};
  
    if (typeof prefix === "undefined") {
      prefix = [];
    }
  
    var keys = [];
  
    for (var k in object) {
      if (!Object.hasOwnProperty.call(object, k)) {
        continue;
      }
  
      if (!options.leavesOnly || typeof object[k] !== "object") {
        keys.push(prefix.concat([k]));
      }
  
      if (typeof object[k] === "object" && object[k] !== null) {
        keys = keys.concat(deepKeys(object[k], {leavesOnly: options.leavesOnly}, prefix.concat([k])));
      }
    }
  
    if (options.asStrings) {
      keys = keys.map(function(e) { return e.join("."); });
    }
  
    return keys;
  };
  