docs: function(initialURL) {
      // Allow the crawler to be initialized without the `new` operator. This is
      // handy for chaining API calls
      if (!(this instanceof Crawler)) {
          return new Crawler(initialURL);
      }
  
      if (arguments.length > 1) {
          throw new Error("Since 1.0.0, simplecrawler takes a single URL when initialized. Protocol, hostname, port and path are inferred from that argument.");
      }
  
      if (typeof initialURL !== "string") {
          throw new Error("The crawler needs a URL string to know where to start crawling");
      }
  
      EventEmitter.call(this);
  
      var crawler = this,
          parsedURL = uri(initialURL).normalize();
  
      /**
       * Controls which URL to request first
       * @type {String}
       */
      this.initialURL = initialURL;
  
      /**
       * Determines what hostname the crawler should limit requests to (so long as
       * {@link Crawler#filterByDomain} is true)
       * @type {String}
       */
      this.host = parsedURL.hostname();
  
      /**
       * Determines the interval at which new requests are spawned by the crawler,
       * as long as the number of open requests is under the
       * {@link Crawler#maxConcurrency} cap.
       * @type {Number}
       */
      this.interval = 250;
  
      /**
       * Maximum request concurrency. If necessary, simplecrawler will increase
       * node's http agent maxSockets value to match this setting.
       * @type {Number}
       */
      this.maxConcurrency = 5;
  
      /**
       * Maximum time we'll wait for headers
       * @type {Number}
       */
      this.timeout = 300000; // 5 minutes
  
      /**
       * Maximum time we'll wait for async listeners
       * @type {Number}
       */
      this.listenerTTL = 10000; // 10 seconds
  
      /**
       * Crawler's user agent string
       * @type {String}
       * @default "Node/simplecrawler <version> (https://github.com/simplecrawler/simplecrawler)"
       */
      this.userAgent =
          "Node/" + packageJson.name + " " + packageJson.version +
          " (" + packageJson.repository.url + ")";
  
      /**
       * Queue for requests. The crawler can use any implementation so long as it
       * uses the same interface. The default queue is simply backed by an array.
       * @type {FetchQueue}
       */
      this.queue = new FetchQueue();
  
      /**
       * Controls whether the crawler respects the robots.txt rules of any domain.
       * This is done both with regards to the robots.txt file, and `<meta>` tags
       * that specify a `nofollow` value for robots. The latter only applies if
       * the default {@link Crawler#discoverResources} method is used, though.
       * @type {Boolean}
       */
      this.respectRobotsTxt = true;
  
      /**
       * Controls whether the crawler is allowed to change the
       * {@link Crawler#host} setting if the first response is a redirect to
       * another domain.
       * @type {Boolean}
       */
      this.allowInitialDomainChange = false;
  
      /**
       * Controls whether HTTP responses are automatically decompressed based on
       * their Content-Encoding header. If true, it will also assign the
       * appropriate Accept-Encoding header to requests.
       * @type {Boolean}
       */
      this.decompressResponses = true;
  
      /**
       * Controls whether HTTP responses are automatically character converted to
       * standard JavaScript strings using the {@link https://www.npmjs.com/package/iconv-lite|iconv-lite}
       * module before emitted in the {@link Crawler#event:fetchcomplete} event.
       * The character encoding is interpreted from the Content-Type header
       * firstly, and secondly from any `<meta charset="xxx" />` tags.
       * @type {Boolean}
       */
      this.decodeResponses = false;
  
      /**
       * Controls whether the crawler fetches only URL's where the hostname
       * matches {@link Crawler#host}. Unless you want to be crawling the entire
       * internet, I would recommend leaving this on!
       * @type {Boolean}
       */
      this.filterByDomain = true;
  
      /**
       * Controls whether URL's that points to a subdomain of {@link Crawler#host}
       * should also be fetched.
       * @type {Boolean}
       */
      this.scanSubdomains = false;
  
      /**
       * Controls whether to treat the www subdomain as the same domain as
       * {@link Crawler#host}. So if {@link http://example.com/example} has
       * already been fetched, {@link http://www.example.com/example} won't be
       * fetched also.
       * @type {Boolean}
       */
      this.ignoreWWWDomain = true;
  
      /**
       * Controls whether to strip the www subdomain entirely from URL's at queue
       * item construction time.
       * @type {Boolean}
       */
      this.stripWWWDomain = false;
  
      /**
       * Internal cache store. Must implement `SimpleCache` interface. You can
       * save the site to disk using the built in file system cache like this:
       *
       * ```js
       * crawler.cache = new Crawler.cache('pathToCacheDirectory');
       * ```
       * @type {SimpleCache}
       */
      this.cache = null;
  
      /**
       * Controls whether an HTTP proxy should be used for requests
       * @type {Boolean}
       */
      this.useProxy = false;
  
      /**
       * If {@link Crawler#useProxy} is true, this setting controls what hostname
       * to use for the proxy
       * @type {String}
       */
      this.proxyHostname = "127.0.0.1";
  
      /**
       * If {@link Crawler#useProxy} is true, this setting controls what port to
       * use for the proxy
       * @type {Number}
       */
      this.proxyPort = 8123;
  
      /**
       * If {@link Crawler#useProxy} is true, this setting controls what username
       * to use for the proxy
       * @type {String}
       */
      this.proxyUser = null;
  
      /**
       * If {@link Crawler#useProxy} is true, this setting controls what password
       * to use for the proxy
       * @type {String}
       */
      this.proxyPass = null;
  
      /**
       * Controls whether to use HTTP Basic Auth
       * @type {Boolean}
       */
      this.needsAuth = false;
  
      /**
       * If {@link Crawler#needsAuth} is true, this setting controls what username
       * to send with HTTP Basic Auth
       * @type {String}
       */
      this.authUser = null;
  
      /**
       * If {@link Crawler#needsAuth} is true, this setting controls what password
       * to send with HTTP Basic Auth
       * @type {String}
       */
      this.authPass = null;
  
      /**
       * Controls whether to save and send cookies or not
       * @type {Boolean}
       */
      this.acceptCookies = true;
  
      /**
       * The module used to store cookies
       * @type {CookieJar}
       */
      this.cookies = new CookieJar();
  
      /**
       * Controls what headers (besides the default ones) to include with every
       * request.
       * @type {Object}
       */
      this.customHeaders = {};
  
      /**
       * Controls what domains the crawler is allowed to fetch from, regardless of
       * {@link Crawler#host} or {@link Crawler#filterByDomain} settings.
       * @type {Array}
       */
      this.domainWhitelist = [];
  
      /**
       * Controls what protocols the crawler is allowed to fetch from
       * @type {RegExp[]}
       */
      this.allowedProtocols = [
          /^http(s)?$/i,                  // HTTP & HTTPS
          /^(rss|atom|feed)(\+xml)?$/i    // RSS / XML
      ];
  
      /**
       * Controls the maximum allowed size in bytes of resources to be fetched
       * @default 16777216
       * @type {Number}
       */
      this.maxResourceSize = 1024 * 1024 * 16; // 16mb
  
      /**
       * Controls what mimetypes the crawler will scan for new resources. If
       * {@link Crawler#downloadUnsupported} is false, this setting will also
       * restrict what resources are downloaded.
       * @type {Array.<RegExp|string>}
       */
      this.supportedMimeTypes = [
          /^text\//i,
          /^application\/(rss|html|xhtml)?[+/-]?xml/i,
          /^application\/javascript/i,
          /^xml/i
      ];
  
      /**
       * Controls whether to download resources with unsupported mimetypes (as
       * specified by {@link Crawler#supportedMimeTypes})
       * @type {Boolean}
       */
      this.downloadUnsupported = true;
  
      /**
       * Controls what URL encoding to use. Can be either "unicode" or "iso8859"
       * @type {String}
       */
      this.urlEncoding = "unicode";
  
      /**
       * Controls whether to strip query string parameters from URL's at queue
       * item construction time.
       * @type {Boolean}
       */
      this.stripQuerystring = false;
  
      /**
       * Controls whether to sort query string parameters from URL's at queue
       * item construction time.
       * @type {Boolean}
       */
      this.sortQueryParameters = false;
  
      /**
       * Collection of regular expressions and functions that are applied in the
       * default {@link Crawler#discoverResources} method.
       * @type {Array.<RegExp|Function>}
       */
      this.discoverRegex = [
          /\s(?:href|src)\s*=\s*("|').*?\1/ig,
          /\s(?:href|src)\s*=\s*[^"'\s][^\s>]+/ig,
          /\s?url\((["']).*?\1\)/ig,
          /\s?url\([^"')]*?\)/ig,
  
          // This could easily duplicate matches above, e.g. in the case of
          // href="http://example.com"
          /https?:\/\/[^?\s><'",]+/ig,
  
          // This might be a bit of a gamble... but get hard-coded
          // strings out of javacript: URLs. They're often popup-image
          // or preview windows, which would otherwise be unavailable to us.
          // Worst case scenario is we make some junky requests.
          /^javascript:\s*[\w$.]+\(['"][^'"\s]+/ig,
  
          // Find srcset links
          function(string) {
              var result = /\ssrcset\s*=\s*("|')(.*?)\1/.exec(string);
              return Array.isArray(result) ? String(result[2]).split(",").map(function(string) {
                  return string.trim().split(/\s+/)[0];
              }) : "";
          },
  
          // Find resources in <meta> redirects. We need to wrap these RegExp's in
          // functions because we only want to return the first capture group, not
          // the entire match. And we need two RegExp's because the necessary
          // attributes on the <meta> tag can appear in any order
          function(string) {
              var match = string.match(/<meta[^>]*http-equiv\s*=\s*["']?refresh["']?[^>]*content\s*=\s*["'] ?[^"'>]*url=([^"'>]*)["']?[^>]*>/i);
              return Array.isArray(match) ? [match[1]] : undefined;
          },
          function(string) {
              var match = string.match(/<meta[^>]*content\s*=\s*["']?[^"'>]*url=([^"'>]*)["']?[^>]*http-equiv\s*=\s*["']?refresh["']?[^>]*>/i);
              return Array.isArray(match) ? [match[1]] : undefined;
          }
      ];
  
      /**
       * Controls whether the default {@link Crawler#discoverResources} should
       * scan for new resources inside of HTML comments.
       * @type {Boolean}
       */
      this.parseHTMLComments = true;
  
      /**
       * Controls whether the default {@link Crawler#discoverResources} should
       * scan for new resources inside of `<script>` tags.
       * @type {Boolean}
       */
      this.parseScriptTags = true;
  
      /**
       * Controls the max depth of resources that the crawler fetches. 0 means
       * that the crawler won't restrict requests based on depth. The initial
       * resource, as well as manually queued resources, are at depth 1. From
       * there, every discovered resource adds 1 to its referrer's depth.
       * @type {Number}
       */
      this.maxDepth = 0;
  
      /**
       * Controls whether to proceed anyway when the crawler encounters an invalid
       * SSL certificate.
       * @type {Boolean}
       */
      this.ignoreInvalidSSL = false;
  
      /**
       * Controls what HTTP agent to use. This is useful if you want to configure
       * eg. a SOCKS client.
       * @type {HTTPAgent}
       */
      this.httpAgent = http.globalAgent;
  
      /**
       * Controls what HTTPS agent to use. This is useful if you want to configure
       * eg. a SOCKS client.
       * @type {HTTPAgent}
       */
      this.httpsAgent = https.globalAgent;
  
      // STATE (AND OTHER) VARIABLES NOT TO STUFF WITH
      var hiddenProps = {
          _downloadConditions: [],
          _fetchConditions: [],
          _isFirstRequest: true,
          _openListeners: 0,
          _openRequests: [],
          _robotsTxts: [],
          _touchedHosts: []
      };
  
      // Apply all the hidden props
      Object.keys(hiddenProps).forEach(function(key) {
          Object.defineProperty(crawler, key, {
              writable: true,
              enumerable: false,
              value: hiddenProps[key]
          });
      });
  }