|  |  | 'use strict';
 | 
						
						
						
							|  |  | var EventEmitter = require('events').EventEmitter;
 | 
						
						
						
							|  |  | var fs = require('fs');
 | 
						
						
						
							|  |  | var sysPath = require('path');
 | 
						
						
						
							|  |  | var asyncEach = require('async-each');
 | 
						
						
						
							|  |  | var anymatch = require('anymatch');
 | 
						
						
						
							|  |  | var globParent = require('glob-parent');
 | 
						
						
						
							|  |  | var isGlob = require('is-glob');
 | 
						
						
						
							|  |  | var isAbsolute = require('path-is-absolute');
 | 
						
						
						
							|  |  | var inherits = require('inherits');
 | 
						
						
						
							|  |  | var braces = require('braces');
 | 
						
						
						
							|  |  | var normalizePath = require('normalize-path');
 | 
						
						
						
							|  |  | var upath = require('upath');
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | var NodeFsHandler = require('./lib/nodefs-handler');
 | 
						
						
						
							|  |  | var FsEventsHandler = require('./lib/fsevents-handler');
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | var arrify = function(value) {
 | 
						
						
						
							|  |  |   if (value == null) return [];
 | 
						
						
						
							|  |  |   return Array.isArray(value) ? value : [value];
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | var flatten = function(list, result) {
 | 
						
						
						
							|  |  |   if (result == null) result = [];
 | 
						
						
						
							|  |  |   list.forEach(function(item) {
 | 
						
						
						
							|  |  |     if (Array.isArray(item)) {
 | 
						
						
						
							|  |  |       flatten(item, result);
 | 
						
						
						
							|  |  |     } else {
 | 
						
						
						
							|  |  |       result.push(item);
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  |   return result;
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Little isString util for use in Array#every.
 | 
						
						
						
							|  |  | var isString = function(thing) {
 | 
						
						
						
							|  |  |   return typeof thing === 'string';
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Public: Main class.
 | 
						
						
						
							|  |  | // Watches files & directories for changes.
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * _opts - object, chokidar options hash
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Emitted events:
 | 
						
						
						
							|  |  | // `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error`
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Examples
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | //  var watcher = new FSWatcher()
 | 
						
						
						
							|  |  | //    .add(directories)
 | 
						
						
						
							|  |  | //    .on('add', path => console.log('File', path, 'was added'))
 | 
						
						
						
							|  |  | //    .on('change', path => console.log('File', path, 'was changed'))
 | 
						
						
						
							|  |  | //    .on('unlink', path => console.log('File', path, 'was removed'))
 | 
						
						
						
							|  |  | //    .on('all', (event, path) => console.log(path, ' emitted ', event))
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | function FSWatcher(_opts) {
 | 
						
						
						
							|  |  |   EventEmitter.call(this);
 | 
						
						
						
							|  |  |   var opts = {};
 | 
						
						
						
							|  |  |   // in case _opts that is passed in is a frozen object
 | 
						
						
						
							|  |  |   if (_opts) for (var opt in _opts) opts[opt] = _opts[opt];
 | 
						
						
						
							|  |  |   this._watched = Object.create(null);
 | 
						
						
						
							|  |  |   this._closers = Object.create(null);
 | 
						
						
						
							|  |  |   this._ignoredPaths = Object.create(null);
 | 
						
						
						
							|  |  |   Object.defineProperty(this, '_globIgnored', {
 | 
						
						
						
							|  |  |     get: function() { return Object.keys(this._ignoredPaths); }
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  |   this.closed = false;
 | 
						
						
						
							|  |  |   this._throttled = Object.create(null);
 | 
						
						
						
							|  |  |   this._symlinkPaths = Object.create(null);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   function undef(key) {
 | 
						
						
						
							|  |  |     return opts[key] === undefined;
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Set up default options.
 | 
						
						
						
							|  |  |   if (undef('persistent')) opts.persistent = true;
 | 
						
						
						
							|  |  |   if (undef('ignoreInitial')) opts.ignoreInitial = false;
 | 
						
						
						
							|  |  |   if (undef('ignorePermissionErrors')) opts.ignorePermissionErrors = false;
 | 
						
						
						
							|  |  |   if (undef('interval')) opts.interval = 100;
 | 
						
						
						
							|  |  |   if (undef('binaryInterval')) opts.binaryInterval = 300;
 | 
						
						
						
							|  |  |   if (undef('disableGlobbing')) opts.disableGlobbing = false;
 | 
						
						
						
							|  |  |   this.enableBinaryInterval = opts.binaryInterval !== opts.interval;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Enable fsevents on OS X when polling isn't explicitly enabled.
 | 
						
						
						
							|  |  |   if (undef('useFsEvents')) opts.useFsEvents = !opts.usePolling;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // If we can't use fsevents, ensure the options reflect it's disabled.
 | 
						
						
						
							|  |  |   if (!FsEventsHandler.canUse()) opts.useFsEvents = false;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Use polling on Mac if not using fsevents.
 | 
						
						
						
							|  |  |   // Other platforms use non-polling fs.watch.
 | 
						
						
						
							|  |  |   if (undef('usePolling') && !opts.useFsEvents) {
 | 
						
						
						
							|  |  |     opts.usePolling = process.platform === 'darwin';
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Global override (useful for end-developers that need to force polling for all
 | 
						
						
						
							|  |  |   // instances of chokidar, regardless of usage/dependency depth)
 | 
						
						
						
							|  |  |   var envPoll = process.env.CHOKIDAR_USEPOLLING;
 | 
						
						
						
							|  |  |   if (envPoll !== undefined) {
 | 
						
						
						
							|  |  |     var envLower = envPoll.toLowerCase();
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     if (envLower === 'false' || envLower === '0') {
 | 
						
						
						
							|  |  |       opts.usePolling = false;
 | 
						
						
						
							|  |  |     } else if (envLower === 'true' || envLower === '1') {
 | 
						
						
						
							|  |  |       opts.usePolling = true;
 | 
						
						
						
							|  |  |     } else {
 | 
						
						
						
							|  |  |       opts.usePolling = !!envLower
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  |   var envInterval = process.env.CHOKIDAR_INTERVAL;
 | 
						
						
						
							|  |  |   if (envInterval) {
 | 
						
						
						
							|  |  |     opts.interval = parseInt(envInterval);
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Editor atomic write normalization enabled by default with fs.watch
 | 
						
						
						
							|  |  |   if (undef('atomic')) opts.atomic = !opts.usePolling && !opts.useFsEvents;
 | 
						
						
						
							|  |  |   if (opts.atomic) this._pendingUnlinks = Object.create(null);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (undef('followSymlinks')) opts.followSymlinks = true;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (undef('awaitWriteFinish')) opts.awaitWriteFinish = false;
 | 
						
						
						
							|  |  |   if (opts.awaitWriteFinish === true) opts.awaitWriteFinish = {};
 | 
						
						
						
							|  |  |   var awf = opts.awaitWriteFinish;
 | 
						
						
						
							|  |  |   if (awf) {
 | 
						
						
						
							|  |  |     if (!awf.stabilityThreshold) awf.stabilityThreshold = 2000;
 | 
						
						
						
							|  |  |     if (!awf.pollInterval) awf.pollInterval = 100;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     this._pendingWrites = Object.create(null);
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  |   if (opts.ignored) opts.ignored = arrify(opts.ignored);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   this._isntIgnored = function(path, stat) {
 | 
						
						
						
							|  |  |     return !this._isIgnored(path, stat);
 | 
						
						
						
							|  |  |   }.bind(this);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var readyCalls = 0;
 | 
						
						
						
							|  |  |   this._emitReady = function() {
 | 
						
						
						
							|  |  |     if (++readyCalls >= this._readyCount) {
 | 
						
						
						
							|  |  |       this._emitReady = Function.prototype;
 | 
						
						
						
							|  |  |       this._readyEmitted = true;
 | 
						
						
						
							|  |  |       // use process.nextTick to allow time for listener to be bound
 | 
						
						
						
							|  |  |       process.nextTick(this.emit.bind(this, 'ready'));
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |   }.bind(this);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   this.options = opts;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // You’re frozen when your heart’s not open.
 | 
						
						
						
							|  |  |   Object.freeze(opts);
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | inherits(FSWatcher, EventEmitter);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Common helpers
 | 
						
						
						
							|  |  | // --------------
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Private method: Normalize and emit events
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * event     - string, type of event
 | 
						
						
						
							|  |  | // * path      - string, file or directory path
 | 
						
						
						
							|  |  | // * val[1..3] - arguments to be passed with event
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Returns the error if defined, otherwise the value of the
 | 
						
						
						
							|  |  | // FSWatcher instance's `closed` flag
 | 
						
						
						
							|  |  | FSWatcher.prototype._emit = function(event, path, val1, val2, val3) {
 | 
						
						
						
							|  |  |   if (this.options.cwd) path = sysPath.relative(this.options.cwd, path);
 | 
						
						
						
							|  |  |   var args = [event, path];
 | 
						
						
						
							|  |  |   if (val3 !== undefined) args.push(val1, val2, val3);
 | 
						
						
						
							|  |  |   else if (val2 !== undefined) args.push(val1, val2);
 | 
						
						
						
							|  |  |   else if (val1 !== undefined) args.push(val1);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var awf = this.options.awaitWriteFinish;
 | 
						
						
						
							|  |  |   if (awf && this._pendingWrites[path]) {
 | 
						
						
						
							|  |  |     this._pendingWrites[path].lastChange = new Date();
 | 
						
						
						
							|  |  |     return this;
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (this.options.atomic) {
 | 
						
						
						
							|  |  |     if (event === 'unlink') {
 | 
						
						
						
							|  |  |       this._pendingUnlinks[path] = args;
 | 
						
						
						
							|  |  |       setTimeout(function() {
 | 
						
						
						
							|  |  |         Object.keys(this._pendingUnlinks).forEach(function(path) {
 | 
						
						
						
							|  |  |           this.emit.apply(this, this._pendingUnlinks[path]);
 | 
						
						
						
							|  |  |           this.emit.apply(this, ['all'].concat(this._pendingUnlinks[path]));
 | 
						
						
						
							|  |  |           delete this._pendingUnlinks[path];
 | 
						
						
						
							|  |  |         }.bind(this));
 | 
						
						
						
							|  |  |       }.bind(this), typeof this.options.atomic === "number"
 | 
						
						
						
							|  |  |         ? this.options.atomic
 | 
						
						
						
							|  |  |         : 100);
 | 
						
						
						
							|  |  |       return this;
 | 
						
						
						
							|  |  |     } else if (event === 'add' && this._pendingUnlinks[path]) {
 | 
						
						
						
							|  |  |       event = args[0] = 'change';
 | 
						
						
						
							|  |  |       delete this._pendingUnlinks[path];
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var emitEvent = function() {
 | 
						
						
						
							|  |  |     this.emit.apply(this, args);
 | 
						
						
						
							|  |  |     if (event !== 'error') this.emit.apply(this, ['all'].concat(args));
 | 
						
						
						
							|  |  |   }.bind(this);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (awf && (event === 'add' || event === 'change') && this._readyEmitted) {
 | 
						
						
						
							|  |  |     var awfEmit = function(err, stats) {
 | 
						
						
						
							|  |  |       if (err) {
 | 
						
						
						
							|  |  |         event = args[0] = 'error';
 | 
						
						
						
							|  |  |         args[1] = err;
 | 
						
						
						
							|  |  |         emitEvent();
 | 
						
						
						
							|  |  |       } else if (stats) {
 | 
						
						
						
							|  |  |         // if stats doesn't exist the file must have been deleted
 | 
						
						
						
							|  |  |         if (args.length > 2) {
 | 
						
						
						
							|  |  |           args[2] = stats;
 | 
						
						
						
							|  |  |         } else {
 | 
						
						
						
							|  |  |           args.push(stats);
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |         emitEvent();
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);
 | 
						
						
						
							|  |  |     return this;
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (event === 'change') {
 | 
						
						
						
							|  |  |     if (!this._throttle('change', path, 50)) return this;
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (
 | 
						
						
						
							|  |  |     this.options.alwaysStat && val1 === undefined &&
 | 
						
						
						
							|  |  |     (event === 'add' || event === 'addDir' || event === 'change')
 | 
						
						
						
							|  |  |   ) {
 | 
						
						
						
							|  |  |     var fullPath = this.options.cwd ? sysPath.join(this.options.cwd, path) : path;
 | 
						
						
						
							|  |  |     fs.stat(fullPath, function(error, stats) {
 | 
						
						
						
							|  |  |       // Suppress event when fs.stat fails, to avoid sending undefined 'stat'
 | 
						
						
						
							|  |  |       if (error || !stats) return;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       args.push(stats);
 | 
						
						
						
							|  |  |       emitEvent();
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  |   } else {
 | 
						
						
						
							|  |  |     emitEvent();
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   return this;
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Private method: Common handler for errors
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * error  - object, Error instance
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Returns the error if defined, otherwise the value of the
 | 
						
						
						
							|  |  | // FSWatcher instance's `closed` flag
 | 
						
						
						
							|  |  | FSWatcher.prototype._handleError = function(error) {
 | 
						
						
						
							|  |  |   var code = error && error.code;
 | 
						
						
						
							|  |  |   var ipe = this.options.ignorePermissionErrors;
 | 
						
						
						
							|  |  |   if (error &&
 | 
						
						
						
							|  |  |     code !== 'ENOENT' &&
 | 
						
						
						
							|  |  |     code !== 'ENOTDIR' &&
 | 
						
						
						
							|  |  |     (!ipe || (code !== 'EPERM' && code !== 'EACCES'))
 | 
						
						
						
							|  |  |   ) this.emit('error', error);
 | 
						
						
						
							|  |  |   return error || this.closed;
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Private method: Helper utility for throttling
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * action  - string, type of action being throttled
 | 
						
						
						
							|  |  | // * path    - string, path being acted upon
 | 
						
						
						
							|  |  | // * timeout - int, duration of time to suppress duplicate actions
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Returns throttle tracking object or false if action should be suppressed
 | 
						
						
						
							|  |  | FSWatcher.prototype._throttle = function(action, path, timeout) {
 | 
						
						
						
							|  |  |   if (!(action in this._throttled)) {
 | 
						
						
						
							|  |  |     this._throttled[action] = Object.create(null);
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  |   var throttled = this._throttled[action];
 | 
						
						
						
							|  |  |   if (path in throttled) {
 | 
						
						
						
							|  |  |     throttled[path].count++;
 | 
						
						
						
							|  |  |     return false;
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  |   function clear() {
 | 
						
						
						
							|  |  |     var count = throttled[path] ? throttled[path].count : 0;
 | 
						
						
						
							|  |  |     delete throttled[path];
 | 
						
						
						
							|  |  |     clearTimeout(timeoutObject);
 | 
						
						
						
							|  |  |     return count;
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  |   var timeoutObject = setTimeout(clear, timeout);
 | 
						
						
						
							|  |  |   throttled[path] = {timeoutObject: timeoutObject, clear: clear, count: 0};
 | 
						
						
						
							|  |  |   return throttled[path];
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Private method: Awaits write operation to finish
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * path    - string, path being acted upon
 | 
						
						
						
							|  |  | // * threshold - int, time in milliseconds a file size must be fixed before
 | 
						
						
						
							|  |  | //                    acknowledging write operation is finished
 | 
						
						
						
							|  |  | // * awfEmit - function, to be called when ready for event to be emitted
 | 
						
						
						
							|  |  | // Polls a newly created file for size variations. When files size does not
 | 
						
						
						
							|  |  | // change for 'threshold' milliseconds calls callback.
 | 
						
						
						
							|  |  | FSWatcher.prototype._awaitWriteFinish = function(path, threshold, event, awfEmit) {
 | 
						
						
						
							|  |  |   var timeoutHandler;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var fullPath = path;
 | 
						
						
						
							|  |  |   if (this.options.cwd && !isAbsolute(path)) {
 | 
						
						
						
							|  |  |     fullPath = sysPath.join(this.options.cwd, path);
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var now = new Date();
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var awaitWriteFinish = (function (prevStat) {
 | 
						
						
						
							|  |  |     fs.stat(fullPath, function(err, curStat) {
 | 
						
						
						
							|  |  |       if (err || !(path in this._pendingWrites)) {
 | 
						
						
						
							|  |  |         if (err && err.code !== 'ENOENT') awfEmit(err);
 | 
						
						
						
							|  |  |         return;
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       var now = new Date();
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       if (prevStat && curStat.size != prevStat.size) {
 | 
						
						
						
							|  |  |         this._pendingWrites[path].lastChange = now;
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       if (now - this._pendingWrites[path].lastChange >= threshold) {
 | 
						
						
						
							|  |  |         delete this._pendingWrites[path];
 | 
						
						
						
							|  |  |         awfEmit(null, curStat);
 | 
						
						
						
							|  |  |       } else {
 | 
						
						
						
							|  |  |         timeoutHandler = setTimeout(
 | 
						
						
						
							|  |  |           awaitWriteFinish.bind(this, curStat),
 | 
						
						
						
							|  |  |           this.options.awaitWriteFinish.pollInterval
 | 
						
						
						
							|  |  |         );
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     }.bind(this));
 | 
						
						
						
							|  |  |   }.bind(this));
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (!(path in this._pendingWrites)) {
 | 
						
						
						
							|  |  |     this._pendingWrites[path] = {
 | 
						
						
						
							|  |  |       lastChange: now,
 | 
						
						
						
							|  |  |       cancelWait: function() {
 | 
						
						
						
							|  |  |         delete this._pendingWrites[path];
 | 
						
						
						
							|  |  |         clearTimeout(timeoutHandler);
 | 
						
						
						
							|  |  |         return event;
 | 
						
						
						
							|  |  |       }.bind(this)
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     timeoutHandler = setTimeout(
 | 
						
						
						
							|  |  |       awaitWriteFinish.bind(this),
 | 
						
						
						
							|  |  |       this.options.awaitWriteFinish.pollInterval
 | 
						
						
						
							|  |  |     );
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Private method: Determines whether user has asked to ignore this path
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * path  - string, path to file or directory
 | 
						
						
						
							|  |  | // * stats - object, result of fs.stat
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Returns boolean
 | 
						
						
						
							|  |  | var dotRe = /\..*\.(sw[px])$|\~$|\.subl.*\.tmp/;
 | 
						
						
						
							|  |  | FSWatcher.prototype._isIgnored = function(path, stats) {
 | 
						
						
						
							|  |  |   if (this.options.atomic && dotRe.test(path)) return true;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (!this._userIgnored) {
 | 
						
						
						
							|  |  |     var cwd = this.options.cwd;
 | 
						
						
						
							|  |  |     var ignored = this.options.ignored;
 | 
						
						
						
							|  |  |     if (cwd && ignored) {
 | 
						
						
						
							|  |  |       ignored = ignored.map(function (path) {
 | 
						
						
						
							|  |  |         if (typeof path !== 'string') return path;
 | 
						
						
						
							|  |  |         return upath.normalize(isAbsolute(path) ? path : sysPath.join(cwd, path));
 | 
						
						
						
							|  |  |       });
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |     var paths = arrify(ignored)
 | 
						
						
						
							|  |  |       .filter(function(path) {
 | 
						
						
						
							|  |  |         return typeof path === 'string' && !isGlob(path);
 | 
						
						
						
							|  |  |       }).map(function(path) {
 | 
						
						
						
							|  |  |         return path + '/**';
 | 
						
						
						
							|  |  |       });
 | 
						
						
						
							|  |  |     this._userIgnored = anymatch(
 | 
						
						
						
							|  |  |       this._globIgnored.concat(ignored).concat(paths)
 | 
						
						
						
							|  |  |     );
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   return this._userIgnored([path, stats]);
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Private method: Provides a set of common helpers and properties relating to
 | 
						
						
						
							|  |  | // symlink and glob handling
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * path - string, file, directory, or glob pattern being watched
 | 
						
						
						
							|  |  | // * depth - int, at any depth > 0, this isn't a glob
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Returns object containing helpers for this path
 | 
						
						
						
							|  |  | var replacerRe = /^\.[\/\\]/;
 | 
						
						
						
							|  |  | FSWatcher.prototype._getWatchHelpers = function(path, depth) {
 | 
						
						
						
							|  |  |   path = path.replace(replacerRe, '');
 | 
						
						
						
							|  |  |   var watchPath = depth || this.options.disableGlobbing || !isGlob(path) ? path : globParent(path);
 | 
						
						
						
							|  |  |   var fullWatchPath = sysPath.resolve(watchPath);
 | 
						
						
						
							|  |  |   var hasGlob = watchPath !== path;
 | 
						
						
						
							|  |  |   var globFilter = hasGlob ? anymatch(path) : false;
 | 
						
						
						
							|  |  |   var follow = this.options.followSymlinks;
 | 
						
						
						
							|  |  |   var globSymlink = hasGlob && follow ? null : false;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var checkGlobSymlink = function(entry) {
 | 
						
						
						
							|  |  |     // only need to resolve once
 | 
						
						
						
							|  |  |     // first entry should always have entry.parentDir === ''
 | 
						
						
						
							|  |  |     if (globSymlink == null) {
 | 
						
						
						
							|  |  |       globSymlink = entry.fullParentDir === fullWatchPath ? false : {
 | 
						
						
						
							|  |  |         realPath: entry.fullParentDir,
 | 
						
						
						
							|  |  |         linkPath: fullWatchPath
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     if (globSymlink) {
 | 
						
						
						
							|  |  |       return entry.fullPath.replace(globSymlink.realPath, globSymlink.linkPath);
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     return entry.fullPath;
 | 
						
						
						
							|  |  |   };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var entryPath = function(entry) {
 | 
						
						
						
							|  |  |     return sysPath.join(watchPath,
 | 
						
						
						
							|  |  |       sysPath.relative(watchPath, checkGlobSymlink(entry))
 | 
						
						
						
							|  |  |     );
 | 
						
						
						
							|  |  |   };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var filterPath = function(entry) {
 | 
						
						
						
							|  |  |     if (entry.stat && entry.stat.isSymbolicLink()) return filterDir(entry);
 | 
						
						
						
							|  |  |     var resolvedPath = entryPath(entry);
 | 
						
						
						
							|  |  |     return (!hasGlob || globFilter(resolvedPath)) &&
 | 
						
						
						
							|  |  |       this._isntIgnored(resolvedPath, entry.stat) &&
 | 
						
						
						
							|  |  |       (this.options.ignorePermissionErrors ||
 | 
						
						
						
							|  |  |         this._hasReadPermissions(entry.stat));
 | 
						
						
						
							|  |  |   }.bind(this);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var getDirParts = function(path) {
 | 
						
						
						
							|  |  |     if (!hasGlob) return false;
 | 
						
						
						
							|  |  |     var parts = [];
 | 
						
						
						
							|  |  |     var expandedPath = braces.expand(path);
 | 
						
						
						
							|  |  |     expandedPath.forEach(function(path) {
 | 
						
						
						
							|  |  |       parts.push(sysPath.relative(watchPath, path).split(/[\/\\]/));
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  |     return parts;
 | 
						
						
						
							|  |  |   };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var dirParts = getDirParts(path);
 | 
						
						
						
							|  |  |   if (dirParts) {
 | 
						
						
						
							|  |  |     dirParts.forEach(function(parts) {
 | 
						
						
						
							|  |  |       if (parts.length > 1) parts.pop();
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  |   var unmatchedGlob;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   var filterDir = function(entry) {
 | 
						
						
						
							|  |  |     if (hasGlob) {
 | 
						
						
						
							|  |  |       var entryParts = getDirParts(checkGlobSymlink(entry));
 | 
						
						
						
							|  |  |       var globstar = false;
 | 
						
						
						
							|  |  |       unmatchedGlob = !dirParts.some(function(parts) {
 | 
						
						
						
							|  |  |         return parts.every(function(part, i) {
 | 
						
						
						
							|  |  |           if (part === '**') globstar = true;
 | 
						
						
						
							|  |  |           return globstar || !entryParts[0][i] || anymatch(part, entryParts[0][i]);
 | 
						
						
						
							|  |  |         });
 | 
						
						
						
							|  |  |       });
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |     return !unmatchedGlob && this._isntIgnored(entryPath(entry), entry.stat);
 | 
						
						
						
							|  |  |   }.bind(this);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   return {
 | 
						
						
						
							|  |  |     followSymlinks: follow,
 | 
						
						
						
							|  |  |     statMethod: follow ? 'stat' : 'lstat',
 | 
						
						
						
							|  |  |     path: path,
 | 
						
						
						
							|  |  |     watchPath: watchPath,
 | 
						
						
						
							|  |  |     entryPath: entryPath,
 | 
						
						
						
							|  |  |     hasGlob: hasGlob,
 | 
						
						
						
							|  |  |     globFilter: globFilter,
 | 
						
						
						
							|  |  |     filterPath: filterPath,
 | 
						
						
						
							|  |  |     filterDir: filterDir
 | 
						
						
						
							|  |  |   };
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Directory helpers
 | 
						
						
						
							|  |  | // -----------------
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Private method: Provides directory tracking objects
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * directory - string, path of the directory
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Returns the directory's tracking object
 | 
						
						
						
							|  |  | FSWatcher.prototype._getWatchedDir = function(directory) {
 | 
						
						
						
							|  |  |   var dir = sysPath.resolve(directory);
 | 
						
						
						
							|  |  |   var watcherRemove = this._remove.bind(this);
 | 
						
						
						
							|  |  |   if (!(dir in this._watched)) this._watched[dir] = {
 | 
						
						
						
							|  |  |     _items: Object.create(null),
 | 
						
						
						
							|  |  |     add: function(item) {
 | 
						
						
						
							|  |  |       if (item !== '.' && item !== '..') this._items[item] = true;
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     remove: function(item) {
 | 
						
						
						
							|  |  |       delete this._items[item];
 | 
						
						
						
							|  |  |       if (!this.children().length) {
 | 
						
						
						
							|  |  |         fs.readdir(dir, function(err) {
 | 
						
						
						
							|  |  |           if (err) watcherRemove(sysPath.dirname(dir), sysPath.basename(dir));
 | 
						
						
						
							|  |  |         });
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     },
 | 
						
						
						
							|  |  |     has: function(item) {return item in this._items;},
 | 
						
						
						
							|  |  |     children: function() {return Object.keys(this._items);}
 | 
						
						
						
							|  |  |   };
 | 
						
						
						
							|  |  |   return this._watched[dir];
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // File helpers
 | 
						
						
						
							|  |  | // ------------
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Private method: Check for read permissions
 | 
						
						
						
							|  |  | // Based on this answer on SO: http://stackoverflow.com/a/11781404/1358405
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * stats - object, result of fs.stat
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Returns boolean
 | 
						
						
						
							|  |  | FSWatcher.prototype._hasReadPermissions = function(stats) {
 | 
						
						
						
							|  |  |   return Boolean(4 & parseInt(((stats && stats.mode) & 0x1ff).toString(8)[0], 10));
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Private method: Handles emitting unlink events for
 | 
						
						
						
							|  |  | // files and directories, and via recursion, for
 | 
						
						
						
							|  |  | // files and directories within directories that are unlinked
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // * directory - string, directory within which the following item is located
 | 
						
						
						
							|  |  | // * item      - string, base path of item/directory
 | 
						
						
						
							|  |  | //
 | 
						
						
						
							|  |  | // Returns nothing
 | 
						
						
						
							|  |  | FSWatcher.prototype._remove = function(directory, item) {
 | 
						
						
						
							|  |  |   // if what is being deleted is a directory, get that directory's paths
 | 
						
						
						
							|  |  |   // for recursive deleting and cleaning of watched object
 | 
						
						
						
							|  |  |   // if it is not a directory, nestedDirectoryChildren will be empty array
 | 
						
						
						
							|  |  |   var path = sysPath.join(directory, item);
 | 
						
						
						
							|  |  |   var fullPath = sysPath.resolve(path);
 | 
						
						
						
							|  |  |   var isDirectory = this._watched[path] || this._watched[fullPath];
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // prevent duplicate handling in case of arriving here nearly simultaneously
 | 
						
						
						
							|  |  |   // via multiple paths (such as _handleFile and _handleDir)
 | 
						
						
						
							|  |  |   if (!this._throttle('remove', path, 100)) return;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // if the only watched file is removed, watch for its return
 | 
						
						
						
							|  |  |   var watchedDirs = Object.keys(this._watched);
 | 
						
						
						
							|  |  |   if (!isDirectory && !this.options.useFsEvents && watchedDirs.length === 1) {
 | 
						
						
						
							|  |  |     this.add(directory, item, true);
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // This will create a new entry in the watched object in either case
 | 
						
						
						
							|  |  |   // so we got to do the directory check beforehand
 | 
						
						
						
							|  |  |   var nestedDirectoryChildren = this._getWatchedDir(path).children();
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Recursively remove children directories / files.
 | 
						
						
						
							|  |  |   nestedDirectoryChildren.forEach(function(nestedItem) {
 | 
						
						
						
							|  |  |     this._remove(path, nestedItem);
 | 
						
						
						
							|  |  |   }, this);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Check if item was on the watched list and remove it
 | 
						
						
						
							|  |  |   var parent = this._getWatchedDir(directory);
 | 
						
						
						
							|  |  |   var wasTracked = parent.has(item);
 | 
						
						
						
							|  |  |   parent.remove(item);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // If we wait for this file to be fully written, cancel the wait.
 | 
						
						
						
							|  |  |   var relPath = path;
 | 
						
						
						
							|  |  |   if (this.options.cwd) relPath = sysPath.relative(this.options.cwd, path);
 | 
						
						
						
							|  |  |   if (this.options.awaitWriteFinish && this._pendingWrites[relPath]) {
 | 
						
						
						
							|  |  |     var event = this._pendingWrites[relPath].cancelWait();
 | 
						
						
						
							|  |  |     if (event === 'add') return;
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // The Entry will either be a directory that just got removed
 | 
						
						
						
							|  |  |   // or a bogus entry to a file, in either case we have to remove it
 | 
						
						
						
							|  |  |   delete this._watched[path];
 | 
						
						
						
							|  |  |   delete this._watched[fullPath];
 | 
						
						
						
							|  |  |   var eventName = isDirectory ? 'unlinkDir' : 'unlink';
 | 
						
						
						
							|  |  |   if (wasTracked && !this._isIgnored(path)) this._emit(eventName, path);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Avoid conflicts if we later create another file with the same name
 | 
						
						
						
							|  |  |   if (!this.options.useFsEvents) {
 | 
						
						
						
							|  |  |     this._closePath(path);
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | FSWatcher.prototype._closePath = function(path) {
 | 
						
						
						
							|  |  |   if (!this._closers[path]) return;
 | 
						
						
						
							|  |  |   this._closers[path].forEach(function(closer) {
 | 
						
						
						
							|  |  |     closer();
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  |   delete this._closers[path];
 | 
						
						
						
							|  |  |   this._getWatchedDir(sysPath.dirname(path)).remove(sysPath.basename(path));
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Public method: Adds paths to be watched on an existing FSWatcher instance
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // * paths     - string or array of strings, file/directory paths and/or globs
 | 
						
						
						
							|  |  | // * _origAdd  - private boolean, for handling non-existent paths to be watched
 | 
						
						
						
							|  |  | // * _internal - private boolean, indicates a non-user add
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Returns an instance of FSWatcher for chaining.
 | 
						
						
						
							|  |  | FSWatcher.prototype.add = function(paths, _origAdd, _internal) {
 | 
						
						
						
							|  |  |   var disableGlobbing = this.options.disableGlobbing;
 | 
						
						
						
							|  |  |   var cwd = this.options.cwd;
 | 
						
						
						
							|  |  |   this.closed = false;
 | 
						
						
						
							|  |  |   paths = flatten(arrify(paths));
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (!paths.every(isString)) {
 | 
						
						
						
							|  |  |     throw new TypeError('Non-string provided as watch path: ' + paths);
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (cwd) paths = paths.map(function(path) {
 | 
						
						
						
							|  |  |     var absPath;
 | 
						
						
						
							|  |  |     if (isAbsolute(path)) {
 | 
						
						
						
							|  |  |       absPath = path;
 | 
						
						
						
							|  |  |     } else if (path[0] === '!') {
 | 
						
						
						
							|  |  |       absPath = '!' + sysPath.join(cwd, path.substring(1));
 | 
						
						
						
							|  |  |     } else {
 | 
						
						
						
							|  |  |       absPath = sysPath.join(cwd, path);
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     // Check `path` instead of `absPath` because the cwd portion can't be a glob
 | 
						
						
						
							|  |  |     if (disableGlobbing || !isGlob(path)) {
 | 
						
						
						
							|  |  |       return absPath;
 | 
						
						
						
							|  |  |     } else {
 | 
						
						
						
							|  |  |       return normalizePath(absPath);
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // set aside negated glob strings
 | 
						
						
						
							|  |  |   paths = paths.filter(function(path) {
 | 
						
						
						
							|  |  |     if (path[0] === '!') {
 | 
						
						
						
							|  |  |       this._ignoredPaths[path.substring(1)] = true;
 | 
						
						
						
							|  |  |     } else {
 | 
						
						
						
							|  |  |       // if a path is being added that was previously ignored, stop ignoring it
 | 
						
						
						
							|  |  |       delete this._ignoredPaths[path];
 | 
						
						
						
							|  |  |       delete this._ignoredPaths[path + '/**'];
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       // reset the cached userIgnored anymatch fn
 | 
						
						
						
							|  |  |       // to make ignoredPaths changes effective
 | 
						
						
						
							|  |  |       this._userIgnored = null;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       return true;
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |   }, this);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   if (this.options.useFsEvents && FsEventsHandler.canUse()) {
 | 
						
						
						
							|  |  |     if (!this._readyCount) this._readyCount = paths.length;
 | 
						
						
						
							|  |  |     if (this.options.persistent) this._readyCount *= 2;
 | 
						
						
						
							|  |  |     paths.forEach(this._addToFsEvents, this);
 | 
						
						
						
							|  |  |   } else {
 | 
						
						
						
							|  |  |     if (!this._readyCount) this._readyCount = 0;
 | 
						
						
						
							|  |  |     this._readyCount += paths.length;
 | 
						
						
						
							|  |  |     asyncEach(paths, function(path, next) {
 | 
						
						
						
							|  |  |       this._addToNodeFs(path, !_internal, 0, 0, _origAdd, function(err, res) {
 | 
						
						
						
							|  |  |         if (res) this._emitReady();
 | 
						
						
						
							|  |  |         next(err, res);
 | 
						
						
						
							|  |  |       }.bind(this));
 | 
						
						
						
							|  |  |     }.bind(this), function(error, results) {
 | 
						
						
						
							|  |  |       results.forEach(function(item) {
 | 
						
						
						
							|  |  |         if (!item || this.closed) return;
 | 
						
						
						
							|  |  |         this.add(sysPath.dirname(item), sysPath.basename(_origAdd || item));
 | 
						
						
						
							|  |  |       }, this);
 | 
						
						
						
							|  |  |     }.bind(this));
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   return this;
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Public method: Close watchers or start ignoring events from specified paths.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // * paths     - string or array of strings, file/directory paths and/or globs
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Returns instance of FSWatcher for chaining.
 | 
						
						
						
							|  |  | FSWatcher.prototype.unwatch = function(paths) {
 | 
						
						
						
							|  |  |   if (this.closed) return this;
 | 
						
						
						
							|  |  |   paths = flatten(arrify(paths));
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   paths.forEach(function(path) {
 | 
						
						
						
							|  |  |     // convert to absolute path unless relative path already matches
 | 
						
						
						
							|  |  |     if (!isAbsolute(path) && !this._closers[path]) {
 | 
						
						
						
							|  |  |       if (this.options.cwd) path = sysPath.join(this.options.cwd, path);
 | 
						
						
						
							|  |  |       path = sysPath.resolve(path);
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     this._closePath(path);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     this._ignoredPaths[path] = true;
 | 
						
						
						
							|  |  |     if (path in this._watched) {
 | 
						
						
						
							|  |  |       this._ignoredPaths[path + '/**'] = true;
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     // reset the cached userIgnored anymatch fn
 | 
						
						
						
							|  |  |     // to make ignoredPaths changes effective
 | 
						
						
						
							|  |  |     this._userIgnored = null;
 | 
						
						
						
							|  |  |   }, this);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   return this;
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Public method: Close watchers and remove all listeners from watched paths.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Returns instance of FSWatcher for chaining.
 | 
						
						
						
							|  |  | FSWatcher.prototype.close = function() {
 | 
						
						
						
							|  |  |   if (this.closed) return this;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   this.closed = true;
 | 
						
						
						
							|  |  |   Object.keys(this._closers).forEach(function(watchPath) {
 | 
						
						
						
							|  |  |     this._closers[watchPath].forEach(function(closer) {
 | 
						
						
						
							|  |  |       closer();
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  |     delete this._closers[watchPath];
 | 
						
						
						
							|  |  |   }, this);
 | 
						
						
						
							|  |  |   this._watched = Object.create(null);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   this.removeAllListeners();
 | 
						
						
						
							|  |  |   return this;
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Public method: Expose list of watched paths
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Returns object w/ dir paths as keys and arrays of contained paths as values.
 | 
						
						
						
							|  |  | FSWatcher.prototype.getWatched = function() {
 | 
						
						
						
							|  |  |   var watchList = {};
 | 
						
						
						
							|  |  |   Object.keys(this._watched).forEach(function(dir) {
 | 
						
						
						
							|  |  |     var key = this.options.cwd ? sysPath.relative(this.options.cwd, dir) : dir;
 | 
						
						
						
							|  |  |     watchList[key || '.'] = Object.keys(this._watched[dir]._items).sort();
 | 
						
						
						
							|  |  |   }.bind(this));
 | 
						
						
						
							|  |  |   return watchList;
 | 
						
						
						
							|  |  | };
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Attach watch handler prototype methods
 | 
						
						
						
							|  |  | function importHandler(handler) {
 | 
						
						
						
							|  |  |   Object.keys(handler.prototype).forEach(function(method) {
 | 
						
						
						
							|  |  |     FSWatcher.prototype[method] = handler.prototype[method];
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | importHandler(NodeFsHandler);
 | 
						
						
						
							|  |  | if (FsEventsHandler.canUse()) importHandler(FsEventsHandler);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Export FSWatcher class
 | 
						
						
						
							|  |  | exports.FSWatcher = FSWatcher;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Public function: Instantiates watcher with paths to be tracked.
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // * paths     - string or array of strings, file/directory paths and/or globs
 | 
						
						
						
							|  |  | // * options   - object, chokidar options
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Returns an instance of FSWatcher for chaining.
 | 
						
						
						
							|  |  | exports.watch = function(paths, options) {
 | 
						
						
						
							|  |  |   return new FSWatcher(options).add(paths);
 | 
						
						
						
							|  |  | };
 |