'use strict';

var _getIterator2 = require('babel-runtime/core-js/get-iterator');

var _getIterator3 = _interopRequireDefault(_getIterator2);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

var _intervalFunction = require('./interval-function');

var _intervalFunction2 = _interopRequireDefault(_intervalFunction);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

(function (window, document) {
  'use strict';

  if (typeof window.ResizeObserver !== 'undefined') {
    return;
  }

  document.resizeObservers = [];

  /**
   * The content rect is defined in section 2.3 ResizeObserverEntry of the spec
   * @param target the element to calculate the content rect for
   * @return {{top: (Number|number), left: (Number|number), width: number, height: number}}
   *
   * Note:
   * Avoid using margins on the observed element. The calculation can return incorrect values when margins are involved.
   *
   * The following CSS will report incorrect width (Chrome OSX):
   *
   * <div id="outer" style="width: 300px; height:300px; background-color: green;overflow:auto;">
   *   <div id="observed" style="width: 400px; height:400px; background-color: yellow; margin:30px; border: 20px solid red; padding:10px;">
   *   </div>
   * </div>
   *
   * The calculated width is 280. The actual (correct) width is 340 since Chrome clips the margin.
   *
   * Use an outer container if you really need a "margin":
   *
   * <div id="outer" style="width: 300px; height:300px; background-color: green;overflow:auto; padding:30px;">
   *   <div id="observed" style="width: 400px; height:400px; background-color: yellow; margin: 0; border: 20px solid red; padding:10px;">
   *   </div>
   * </div>
   *
   * A more detailed explanation can be fund here:
   * http://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively
   */
  var getContentRect = function getContentRect(target) {
    var cs = window.getComputedStyle(target);
    var r = target.getBoundingClientRect();
    var top = parseFloat(cs.paddingTop) || 0;
    var left = parseFloat(cs.paddingLeft) || 0;
    var width = r.width - ((parseFloat(cs.marginLeft) || 0) + (parseFloat(cs.marginRight) || 0) + (parseFloat(cs.borderLeftWidth) || 0) + (parseFloat(cs.borderRightWidth) || 0) + left + (parseFloat(cs.paddingRight) || 0));
    var height = r.height - ((parseFloat(cs.marginTop) || 0) + (parseFloat(cs.marginBottom) || 0) + (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0) + top + (parseFloat(cs.paddingBottom) || 0));
    return { width: width, height: height, top: top, left: left };
  };

  var dimensionHasChanged = function dimensionHasChanged(target, lastWidth, lastHeight) {
    var _getContentRect = getContentRect(target),
        width = _getContentRect.width,
        height = _getContentRect.height;

    return width !== lastWidth || height !== lastHeight;
  };

  /**
   * ResizeObservation holds observation information for a single Element.
   * @param target
   * @return {{target: *, broadcastWidth, broadcastHeight, isOrphan: (function()), isActive: (function())}}
   * @constructor
   */
  var ResizeObservation = function ResizeObservation(target) {
    var _getContentRect2 = getContentRect(target),
        width = _getContentRect2.width,
        height = _getContentRect2.height;

    return {
      target: target,
      broadcastWidth: width,
      broadcastHeight: height,

      isOrphan: function isOrphan() {
        return !this.target || !this.target.parentNode;
      },
      isActive: function isActive() {
        return dimensionHasChanged(this.target, this.broadcastWidth, this.broadcastHeight);
      }
    };
  };

  /**
   * A snapshot of the observed element
   * @param target
   * @param rect
   * @return {{target: *, contentRect: *}}
   * @constructor
   */
  var ResizeObserverEntry = function ResizeObserverEntry(target, rect) {
    return {
      target: target,
      contentRect: rect
    };
  };

  /**
   * The ResizeObserver is used to observe changes to Element's content rect.
   */

  var ResizeObserver = function () {

    /**
     * Constructor for instantiating new Resize observers.
     * @param callback void (sequence<ResizeObserverEntry> entries). The function which will be called on each resize.
     * @throws {TypeError}
     */
    function ResizeObserver(callback) {
      (0, _classCallCheck3.default)(this, ResizeObserver);


      if (typeof callback !== 'function') {
        throw new TypeError('callback parameter must be a function');
      }

      this.callback_ = callback;
      this.observationTargets_ = [];
      this.activeTargets_ = [];

      document.resizeObservers.push(this);
    }

    /**
     * A list of ResizeObservations. It represents all Elements being observed.
     *
     * @return {Array}
     */


    (0, _createClass3.default)(ResizeObserver, [{
      key: 'observe',


      /**
       * Adds target to the list of observed elements.
       * @param {HTMLElement} target The target to observe
       */
      value: function observe(target) {
        if (target) {
          if (!(target instanceof HTMLElement)) {
            throw new TypeError('target parameter must be an HTMLElement');
          }
          if (!this.observationTargets_.find(function (t) {
            return t.target === target;
          })) {
            this.observationTargets_.push(ResizeObservation(target));
            resizeController.start();
          }
        }
      }

      /**
       * Removes target from the list of observed elements.
       * @param target The target to remove
       */

    }, {
      key: 'unobserve',
      value: function unobserve(target) {
        var i = this.observationTargets_.findIndex(function (t) {
          return t.target === target;
        });
        if (i > -1) {
          this.observationTargets_.splice(i, 1);
        }
      }

      /**
       * Stops the ResizeObserver instance from receiving notifications of resize changes.
       * Until the observe() method is used again, observer's callback will not be invoked.
       */

    }, {
      key: 'disconnect',
      value: function disconnect() {
        this.observationTargets_ = [];
        this.activeTargets_ = [];
      }

      /**
       * Removes the ResizeObserver from the list of observers
       */

    }, {
      key: 'destroy',
      value: function destroy() {
        var _this = this;

        this.disconnect();
        var i = document.resizeObservers.findIndex(function (o) {
          return o === _this;
        });
        if (i > -1) {
          document.resizeObservers.splice(i, 1);
        }
      }
    }, {
      key: 'deleteOrphansAndPopulateActiveTargets_',
      value: function deleteOrphansAndPopulateActiveTargets_() {

        // Works, but two iterations
        //this.observationTargets_ = this.observationTargets_.filter( resizeObervation => !resizeObervation.isOrphan());
        //this.activeTargets_ = this.observationTargets_.filter( resizeObervation => resizeObervation.isActive());

        // Same result as above, one iteration
        /*
        this.activeTargets_ = [];
        let n = this.observationTargets_.length-1;
        while(n >= 0) {
          if(this.observationTargets_[n].isOrphan()) {
            this.observationTargets_.splice(n, 1);
          }
          else if(this.observationTargets_[n].isActive()) {
            this.activeTargets_.push(this.observationTargets_[n]);
          }
          n -= 1;
        }
        */

        // Same result as above - but reduce is cooler :-)
        this.activeTargets_ = this.observationTargets_.reduceRight(function (prev, resizeObservation, index, arr) {
          if (resizeObservation.isOrphan()) {
            arr.splice(index, 1);
          } else if (resizeObservation.isActive()) {
            prev.push(resizeObservation);
          }
          return prev;
        }, []);
      }
    }, {
      key: 'broadcast_',
      value: function broadcast_() {
        this.deleteOrphansAndPopulateActiveTargets_();
        if (this.activeTargets_.length > 0) {
          var entries = [];
          var _iteratorNormalCompletion = true;
          var _didIteratorError = false;
          var _iteratorError = undefined;

          try {
            for (var _iterator = (0, _getIterator3.default)(this.activeTargets_), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
              var resizeObservation = _step.value;

              var rect = getContentRect(resizeObservation.target);
              resizeObservation.broadcastWidth = rect.width;
              resizeObservation.broadcastHeight = rect.height;
              entries.push(ResizeObserverEntry(resizeObservation.target, rect));
            }
          } catch (err) {
            _didIteratorError = true;
            _iteratorError = err;
          } finally {
            try {
              if (!_iteratorNormalCompletion && _iterator.return) {
                _iterator.return();
              }
            } finally {
              if (_didIteratorError) {
                throw _iteratorError;
              }
            }
          }

          this.callback_(entries);
          this.activeTargets_ = [];
        }
      }
    }, {
      key: 'observationTargets',
      get: function get() {
        return this.observationTargets_;
      }

      /**
       *  A list of ResizeObservations. It represents all Elements whose size has
       *  changed since last observation broadcast that are eligible for broadcast.
       *
       * @return {Array}
       */

    }, {
      key: 'activeTargets',
      get: function get() {
        return this.activeTargets_;
      }
    }]);
    return ResizeObserver;
  }();

  //let interval = require('./interval-function');

  /**
   * Broadcasts Element.resize events
   * @return {{start: (function()), stop: (function())}}
   * @constructor
   */


  var ResizeController = function ResizeController() {

    var shouldStop = function shouldStop() {
      return document.resizeObservers.findIndex(function (resizeObserver) {
        return resizeObserver.observationTargets.length > 0;
      }) > -1;
    };

    var execute = function execute() {
      //console.log('***** Execute');
      var _iteratorNormalCompletion2 = true;
      var _didIteratorError2 = false;
      var _iteratorError2 = undefined;

      try {
        for (var _iterator2 = (0, _getIterator3.default)(document.resizeObservers), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
          var resizeObserver = _step2.value;

          resizeObserver.broadcast_();
        }
      } catch (err) {
        _didIteratorError2 = true;
        _iteratorError2 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion2 && _iterator2.return) {
            _iterator2.return();
          }
        } finally {
          if (_didIteratorError2) {
            throw _iteratorError2;
          }
        }
      }

      return shouldStop();
    };

    var interval = (0, _intervalFunction2.default)(200);

    return {
      start: function start() {
        if (!interval.started) {
          //console.log('***** Start poll');
          interval.start(execute);
        }
      }
    };
  };

  window.ResizeObserver = ResizeObserver;

  var resizeController = ResizeController();
  //console.log('***** ResizeObserver ready');
})(window, document);
/**
 * An API for observing changes to Elementâ€™s size.
 *
 * @See https://wicg.github.io/ResizeObserver/
 * @ee https://github.com/pelotoncycle/resize-observer
 *
 */