// If the scheduler is not customized via `etch.setScheduler`, an instance of
// this class will be used to schedule updates to the document. The
// `updateDocument` method accepts functions to be run at some point in the
// future, then runs them on the next animation frame.
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var DefaultScheduler = (function () {
  function DefaultScheduler() {
    _classCallCheck(this, DefaultScheduler);

    this.updateRequests = [];
    this.readRequests = [];
    this.pendingAnimationFrame = null;
    this.performUpdates = this.performUpdates.bind(this);
    this.performingUpdates = false;
  }

  // Enqueues functions that write to the DOM to be performed on the next
  // animation frame. Functions passed to this method should *never* read from
  // the DOM, because that could cause synchronous reflows.

  _createClass(DefaultScheduler, [{
    key: "updateDocument",
    value: function updateDocument(fn) {
      this.updateRequests.push(fn);
      if (!this.pendingAnimationFrame) {
        this.pendingAnimationFrame = window.requestAnimationFrame(this.performUpdates);
      }
    }
  }, {
    key: "readDocument",
    value: function readDocument(fn) {
      this.readRequests.push(fn);
      if (!this.pendingAnimationFrame) {
        this.pendingAnimationFrame = window.requestAnimationFrame(this.performUpdates);
      }
    }

    // Returns a promise that will resolve at the end of the next update cycle,
    // after all the functions passed to `updateDocument` and `updateDocumentSync`
    // have been run.
  }, {
    key: "getNextUpdatePromise",
    value: function getNextUpdatePromise() {
      var _this = this;

      if (!this.nextUpdatePromise) {
        this.nextUpdatePromise = new Promise(function (resolve) {
          _this.resolveNextUpdatePromise = resolve;
        });
      }
      return this.nextUpdatePromise;
    }

    // Performs all the pending document updates. If running these update
    // functions causes *more* updates to be enqueued, they are run synchronously
    // in this update cycle without waiting for another frame.
  }, {
    key: "performUpdates",
    value: function performUpdates() {
      while (this.updateRequests.length > 0) {
        this.updateRequests.shift()();
      }

      // We don't clear the pending frame until all update requests are processed.
      // This ensures updates requested within other updates are processed in the
      // current frame.
      this.pendingAnimationFrame = null;

      // Now that updates are processed, we can perform all pending document reads
      // without the risk of interleaving them with writes and causing layout
      // thrashing.
      while (this.readRequests.length > 0) {
        this.readRequests.shift()();
      }

      if (this.nextUpdatePromise) {
        var resolveNextUpdatePromise = this.resolveNextUpdatePromise;
        this.nextUpdatePromise = null;
        this.resolveNextUpdatePromise = null;
        resolveNextUpdatePromise();
      }
    }
  }]);

  return DefaultScheduler;
})();

exports["default"] = DefaultScheduler;
module.exports = exports["default"];