/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/resizeobserver
 */
import global from './global';
/**
 * A helper class which instances allow performing custom actions when native DOM elements are resized.
 *
 * ```ts
 * const editableElement = editor.editing.view.getDomRoot();
 *
 * const observer = new ResizeObserver( editableElement, entry => {
 * 	console.log( 'The editable element has been resized in DOM.' );
 * 	console.log( entry.target ); // -> editableElement
 * 	console.log( entry.contentRect.width ); // -> e.g. '423px'
 * } );
 * ```
 *
 * It uses the [native DOM resize observer](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
 * under the hood.
 */
export default class ResizeObserver {
    /**
     * Creates an instance of the `ResizeObserver` class.
     *
     * @param element A DOM element that is to be observed for resizing. Note that
     * the element must be visible (i.e. not detached from DOM) for the observer to work.
     * @param callback A function called when the observed element was resized. It passes
     * the [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
     * object with information about the resize event.
     */
    constructor(element, callback) {
        // **Note**: For the maximum performance, this class ensures only a single instance of the native
        // observer is used no matter how many instances of this class were created.
        if (!ResizeObserver._observerInstance) {
            ResizeObserver._createObserver();
        }
        this._element = element;
        this._callback = callback;
        ResizeObserver._addElementCallback(element, callback);
        ResizeObserver._observerInstance.observe(element);
    }
    /**
     * Destroys the observer which disables the `callback` passed to the {@link #constructor}.
     */
    destroy() {
        ResizeObserver._deleteElementCallback(this._element, this._callback);
    }
    /**
     * Registers a new resize callback for the DOM element.
     */
    static _addElementCallback(element, callback) {
        if (!ResizeObserver._elementCallbacks) {
            ResizeObserver._elementCallbacks = new Map();
        }
        let callbacks = ResizeObserver._elementCallbacks.get(element);
        if (!callbacks) {
            callbacks = new Set();
            ResizeObserver._elementCallbacks.set(element, callbacks);
        }
        callbacks.add(callback);
    }
    /**
     * Removes a resize callback from the DOM element. If no callbacks are left
     * for the element, it removes the element from the native observer.
     */
    static _deleteElementCallback(element, callback) {
        const callbacks = ResizeObserver._getElementCallbacks(element);
        // Remove the element callback. Check if exist first in case someone
        // called destroy() twice.
        if (callbacks) {
            callbacks.delete(callback);
            // If no callbacks left for the element, also remove the element.
            if (!callbacks.size) {
                ResizeObserver._elementCallbacks.delete(element);
                ResizeObserver._observerInstance.unobserve(element);
            }
        }
        if (ResizeObserver._elementCallbacks && !ResizeObserver._elementCallbacks.size) {
            ResizeObserver._observerInstance = null;
            ResizeObserver._elementCallbacks = null;
        }
    }
    /**
     * Returns are registered resize callbacks for the DOM element.
     */
    static _getElementCallbacks(element) {
        if (!ResizeObserver._elementCallbacks) {
            return null;
        }
        return ResizeObserver._elementCallbacks.get(element);
    }
    /**
     * Creates the single native observer shared across all `ResizeObserver` instances.
     */
    static _createObserver() {
        ResizeObserver._observerInstance = new global.window.ResizeObserver(entries => {
            for (const entry of entries) {
                const callbacks = ResizeObserver._getElementCallbacks(entry.target);
                if (callbacks) {
                    for (const callback of callbacks) {
                        callback(entry);
                    }
                }
            }
        });
    }
}
/**
 * The single native observer instance shared across all {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
 */
ResizeObserver._observerInstance = null;
/**
 * A mapping of native DOM elements and their callbacks shared across all
 * {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
 */
ResizeObserver._elementCallbacks = null;
