src/dropbox.js
function () {
    // If fetchDelta was already called, and didn't finish, return the existing
    // promise instead of calling Dropbox API again
    if (this._fetchDeltaPromise) {
      return this._fetchDeltaPromise;
    }

    var args = Array.prototype.slice.call(arguments);
    var self = this;

    var fetch = function (cursor) {
      let url = 'https://api.dropboxapi.com/2/files/list_folder';
      let requestBody;

      if (typeof cursor === 'string') {
        url += '/continue';
        requestBody = { cursor };
      } else {
        requestBody = {
          path: PATH_PREFIX,
          recursive: true,
          include_deleted: true
        };
      }

      return self._request('POST', url, { body: requestBody }).then(function (response) {
        if (response.status === 401) {
          self.rs._emit('error', new Authorize.Unauthorized());
          return Promise.resolve(args);
        }

        if (response.status !== 200 && response.status !== 409) {
          return Promise.reject(new Error('Invalid response status: ' + response.status));
        }

        let responseBody;

        try {
          responseBody = JSON.parse(response.responseText);
        } catch (e) {
          return Promise.reject(new Error('Invalid response body: ' + response.responseText));
        }

        if (response.status === 409) {
          if (compareApiError(responseBody, ['path', 'not_found'])) {
            responseBody = {
              cursor: null,
              entries: [],
              has_more: false
            };
          } else {
            return Promise.reject(new Error('API returned an error: ' + responseBody.error_summary));
          }
        }

        if (!cursor) {
          //we are doing a complete fetch, so propagation would introduce unnecessary overhead
          self._revCache.deactivatePropagation();
        }

        responseBody.entries.forEach(function (entry) {
          var path = entry.path_lower.substr(PATH_PREFIX.length);

          if (entry['.tag'] === 'deleted') {
            // there's no way to know whether the entry was a file or a folder
            self._revCache.delete(path);
            self._revCache.delete(path + '/');
          } else if (entry['.tag'] === 'file') {
            self._revCache.set(path, entry.rev);
          }
        });

        self._fetchDeltaCursor = responseBody.cursor;
        if (responseBody.has_more) {
          return fetch(responseBody.cursor);
        } else {
          self._revCache.activatePropagation();        
          self._initialFetchDone = true;
        }
      }).catch((error) => {
        if (error === 'timeout' || error instanceof ProgressEvent) {
          // Offline is handled elsewhere already, just ignore it here
          return Promise.resolve();
        } else {
          return Promise.reject(error);
        }
      });
    };
    this._fetchDeltaPromise = fetch(self._fetchDeltaCursor).catch((error) => {
      if (typeof(error) === 'object' && 'message' in error) {
        error.message = 'Dropbox: fetchDelta: ' + error.message;
      } else {
        error = `Dropbox: fetchDelta: ${error}`;
      }
      this._fetchDeltaPromise = null;
      return Promise.reject(error);
    }).then(() => {
      this._fetchDeltaPromise = null;
      return Promise.resolve(args);
    });

    return this._fetchDeltaPromise;
  }
function () {
    // If fetchDelta was already called, and didn't finish, return the existing
    // promise instead of calling Dropbox API again
    if (this._fetchDeltaPromise) {
      return this._fetchDeltaPromise;
    }

    var args = Array.prototype.slice.call(arguments);
    var self = this;

    var fetch = function (cursor) {
      let url = 'https://api.dropboxapi.com/2/files/list_folder';
      let requestBody;

      if (typeof cursor === 'string') {
        url += '/continue';
        requestBody = { cursor };
      } else {
        requestBody = {
          path: PATH_PREFIX,
          recursive: true,
          include_deleted: true
        };
      }

      return self._request('POST', url, { body: requestBody }).then(function (response) {
        if (response.status === 401) {
          self.rs._emit('error', new Authorize.Unauthorized());
          return Promise.resolve(args);
        }

        if (response.status !== 200 && response.status !== 409) {
          return Promise.reject(new Error('Invalid response status: ' + response.status));
        }

        let responseBody;

        try {
          responseBody = JSON.parse(response.responseText);
        } catch (e) {
          return Promise.reject(new Error('Invalid response body: ' + response.responseText));
        }

        if (response.status === 409) {
          if (compareApiError(responseBody, ['path', 'not_found'])) {
            responseBody = {
              cursor: null,
              entries: [],
              has_more: false
            };
          } else {
            return Promise.reject(new Error('API returned an error: ' + responseBody.error_summary));
          }
        }

        if (!cursor) {
          //we are doing a complete fetch, so propagation would introduce unnecessary overhead
          self._revCache.deactivatePropagation();
        }

        responseBody.entries.forEach(function (entry) {
          var path = entry.path_lower.substr(PATH_PREFIX.length);

          if (entry['.tag'] === 'deleted') {
            // there's no way to know whether the entry was a file or a folder
            self._revCache.delete(path);
            self._revCache.delete(path + '/');
          } else if (entry['.tag'] === 'file') {
            self._revCache.set(path, entry.rev);
          }
        });

        self._fetchDeltaCursor = responseBody.cursor;
        if (responseBody.has_more) {
          return fetch(responseBody.cursor);
        } else {
          self._revCache.activatePropagation();        
          self._initialFetchDone = true;
        }
      }).catch((error) => {
        if (error === 'timeout' || error instanceof ProgressEvent) {
          // Offline is handled elsewhere already, just ignore it here
          return Promise.resolve();
        } else {
          return Promise.reject(error);
        }
      });
    };
    this._fetchDeltaPromise = fetch(self._fetchDeltaCursor).catch((error) => {
      if (typeof(error) === 'object' && 'message' in error) {
        error.message = 'Dropbox: fetchDelta: ' + error.message;
      } else {
        error = `Dropbox: fetchDelta: ${error}`;
      }
      this._fetchDeltaPromise = null;
      return Promise.reject(error);
    }).then(() => {
      this._fetchDeltaPromise = null;
      return Promise.resolve(args);
    });

    return this._fetchDeltaPromise;
  }
Fetches the revision of all the files from dropbox API and puts them
into ``_revCache``. These values can then be used to determine if
something has changed.

@private