import {Map} from './shims/es6-collections.js';
|
import ResizeObservation from './ResizeObservation.js';
|
import ResizeObserverEntry from './ResizeObserverEntry.js';
|
import getWindowOf from './utils/getWindowOf.js';
|
|
export default class ResizeObserverSPI {
|
/**
|
* Collection of resize observations that have detected changes in dimensions
|
* of elements.
|
*
|
* @private {Array<ResizeObservation>}
|
*/
|
activeObservations_ = [];
|
|
/**
|
* Reference to the callback function.
|
*
|
* @private {ResizeObserverCallback}
|
*/
|
callback_;
|
|
/**
|
* Public ResizeObserver instance which will be passed to the callback
|
* function and used as a value of it's "this" binding.
|
*
|
* @private {ResizeObserver}
|
*/
|
callbackCtx_;
|
|
/**
|
* Reference to the associated ResizeObserverController.
|
*
|
* @private {ResizeObserverController}
|
*/
|
controller_;
|
|
/**
|
* Registry of the ResizeObservation instances.
|
*
|
* @private {Map<Element, ResizeObservation>}
|
*/
|
observations_ = new Map();
|
|
/**
|
* Creates a new instance of ResizeObserver.
|
*
|
* @param {ResizeObserverCallback} callback - Callback function that is invoked
|
* when one of the observed elements changes it's content dimensions.
|
* @param {ResizeObserverController} controller - Controller instance which
|
* is responsible for the updates of observer.
|
* @param {ResizeObserver} callbackCtx - Reference to the public
|
* ResizeObserver instance which will be passed to callback function.
|
*/
|
constructor(callback, controller, callbackCtx) {
|
if (typeof callback !== 'function') {
|
throw new TypeError('The callback provided as parameter 1 is not a function.');
|
}
|
|
this.callback_ = callback;
|
this.controller_ = controller;
|
this.callbackCtx_ = callbackCtx;
|
}
|
|
/**
|
* Starts observing provided element.
|
*
|
* @param {Element} target - Element to be observed.
|
* @returns {void}
|
*/
|
observe(target) {
|
if (!arguments.length) {
|
throw new TypeError('1 argument required, but only 0 present.');
|
}
|
|
// Do nothing if current environment doesn't have the Element interface.
|
if (typeof Element === 'undefined' || !(Element instanceof Object)) {
|
return;
|
}
|
|
if (!(target instanceof getWindowOf(target).Element)) {
|
throw new TypeError('parameter 1 is not of type "Element".');
|
}
|
|
const observations = this.observations_;
|
|
// Do nothing if element is already being observed.
|
if (observations.has(target)) {
|
return;
|
}
|
|
observations.set(target, new ResizeObservation(target));
|
|
this.controller_.addObserver(this);
|
|
// Force the update of observations.
|
this.controller_.refresh();
|
}
|
|
/**
|
* Stops observing provided element.
|
*
|
* @param {Element} target - Element to stop observing.
|
* @returns {void}
|
*/
|
unobserve(target) {
|
if (!arguments.length) {
|
throw new TypeError('1 argument required, but only 0 present.');
|
}
|
|
// Do nothing if current environment doesn't have the Element interface.
|
if (typeof Element === 'undefined' || !(Element instanceof Object)) {
|
return;
|
}
|
|
if (!(target instanceof getWindowOf(target).Element)) {
|
throw new TypeError('parameter 1 is not of type "Element".');
|
}
|
|
const observations = this.observations_;
|
|
// Do nothing if element is not being observed.
|
if (!observations.has(target)) {
|
return;
|
}
|
|
observations.delete(target);
|
|
if (!observations.size) {
|
this.controller_.removeObserver(this);
|
}
|
}
|
|
/**
|
* Stops observing all elements.
|
*
|
* @returns {void}
|
*/
|
disconnect() {
|
this.clearActive();
|
this.observations_.clear();
|
this.controller_.removeObserver(this);
|
}
|
|
/**
|
* Collects observation instances the associated element of which has changed
|
* it's content rectangle.
|
*
|
* @returns {void}
|
*/
|
gatherActive() {
|
this.clearActive();
|
|
this.observations_.forEach(observation => {
|
if (observation.isActive()) {
|
this.activeObservations_.push(observation);
|
}
|
});
|
}
|
|
/**
|
* Invokes initial callback function with a list of ResizeObserverEntry
|
* instances collected from active resize observations.
|
*
|
* @returns {void}
|
*/
|
broadcastActive() {
|
// Do nothing if observer doesn't have active observations.
|
if (!this.hasActive()) {
|
return;
|
}
|
|
const ctx = this.callbackCtx_;
|
|
// Create ResizeObserverEntry instance for every active observation.
|
const entries = this.activeObservations_.map(observation => {
|
return new ResizeObserverEntry(
|
observation.target,
|
observation.broadcastRect()
|
);
|
});
|
|
this.callback_.call(ctx, entries, ctx);
|
this.clearActive();
|
}
|
|
/**
|
* Clears the collection of active observations.
|
*
|
* @returns {void}
|
*/
|
clearActive() {
|
this.activeObservations_.splice(0);
|
}
|
|
/**
|
* Tells whether observer has active observations.
|
*
|
* @returns {boolean}
|
*/
|
hasActive() {
|
return this.activeObservations_.length > 0;
|
}
|
}
|