diff --git a/build-system/compile/sources.js b/build-system/compile/sources.js index eb333da855ad..ad170dfb4edc 100644 --- a/build-system/compile/sources.js +++ b/build-system/compile/sources.js @@ -30,6 +30,7 @@ const COMMON_GLOBS = [ 'third_party/inputmask/**/*.js', 'third_party/mustache/**/*.js', 'third_party/react-dates/bundle.js', + 'third_party/resize-observer-polyfill/ResizeObserver.install.js', 'third_party/set-dom/set-dom.js', 'third_party/subscriptions-project/*.js', 'third_party/timeagojs/**/*.js', @@ -41,7 +42,7 @@ const COMMON_GLOBS = [ 'node_modules/intersection-observer/package.json', 'node_modules/intersection-observer/intersection-observer.install.js', 'node_modules/resize-observer-polyfill/package.json', - 'node_modules/resize-observer-polyfill/resize-observer.install.js', + 'node_modules/resize-observer-polyfill/ResizeObserver.install.js', 'node_modules/promise-pjs/package.json', 'node_modules/promise-pjs/promise.mjs', 'node_modules/rrule/dist/es5/rrule.js', diff --git a/build-system/tasks/update-packages.js b/build-system/tasks/update-packages.js index b0ed39dd8e14..30e833d25de1 100644 --- a/build-system/tasks/update-packages.js +++ b/build-system/tasks/update-packages.js @@ -104,10 +104,10 @@ function patchIntersectionObserver() { function patchResizeObserver() { // Copies intersection-observer into a new file that has an export. const patchedName = - 'node_modules/resize-observer-polyfill/resize-observer.install.js'; + 'node_modules/resize-observer-polyfill/ResizeObserver.install.js'; let file = fs .readFileSync( - 'node_modules/resize-observer-polyfill/dist/ResizeObserver.global.js' + 'node_modules/resize-observer-polyfill/dist/ResizeObserver.js' ) .toString(); diff --git a/build-system/test-configs/dep-check-config.js b/build-system/test-configs/dep-check-config.js index adf98f0203c0..0bf9bf8d0a40 100644 --- a/build-system/test-configs/dep-check-config.js +++ b/build-system/test-configs/dep-check-config.js @@ -82,6 +82,7 @@ exports.rules = [ 'extensions/amp-list/**->third_party/set-dom/set-dom.js', 'extensions/amp-mustache/**/amp-mustache.js->third_party/mustache/mustache.js', 'extensions/amp-recaptcha-input/**/*.js->third_party/amp-toolbox-cache-url/dist/amp-toolbox-cache-url.esm.js', + 'extensions/amp-resize-observer-polyfill/**/*.js->third_party/resize-observer-polyfill/ResizeObserver.install.js', 'extensions/amp-story-360/0.1/amp-story-360.js->third_party/zuho/zuho.js', 'extensions/amp-subscriptions-google/**/*.js->third_party/subscriptions-project/swg.js', 'extensions/amp-subscriptions/**/*.js->third_party/subscriptions-project/aes_gcm.js', diff --git a/extensions/amp-resize-observer-polyfill/0.1/amp-resize-observer-polyfill.js b/extensions/amp-resize-observer-polyfill/0.1/amp-resize-observer-polyfill.js index d8cc0691e79a..4e4bde196a66 100644 --- a/extensions/amp-resize-observer-polyfill/0.1/amp-resize-observer-polyfill.js +++ b/extensions/amp-resize-observer-polyfill/0.1/amp-resize-observer-polyfill.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import {installResizeObserver} from 'resize-observer-polyfill/resize-observer.install'; +import {installResizeObserver} from '../../../third_party/resize-observer-polyfill/ResizeObserver.install'; import {registerServiceBuilder} from '../../../src/service'; import {upgradePolyfill} from '../../../src/polyfillstub/resize-observer-stub'; diff --git a/extensions/amp-resize-observer-polyfill/0.1/test/test-amp-resize-observer-polyfill.js b/extensions/amp-resize-observer-polyfill/0.1/test/test-amp-resize-observer-polyfill.js new file mode 100644 index 000000000000..b75ebb4a584d --- /dev/null +++ b/extensions/amp-resize-observer-polyfill/0.1/test/test-amp-resize-observer-polyfill.js @@ -0,0 +1,38 @@ +/** + * Copyright 2021 The AMP HTML Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ResizeObserverStub, + installStub, +} from '../../../../src/polyfillstub/resize-observer-stub'; +import {upgradeResizeObserverPolyfill} from '../amp-resize-observer-polyfill'; + +describes.realWin('amp-resize-observer-polyfill', {amp: false}, (env) => { + let win; + + beforeEach(() => { + win = env.win; + delete win.ResizeObserver; + installStub(win); + }); + + it('should install ResizeObserver polyfill', () => { + expect(win.ResizeObserver).to.equal(ResizeObserverStub); + upgradeResizeObserverPolyfill(win); + expect(win.ResizeObserver).to.exist; + expect(win.ResizeObserver).to.not.equal(ResizeObserverStub); + }); +}); diff --git a/third_party/resize-observer-polyfill/LICENSE b/third_party/resize-observer-polyfill/LICENSE new file mode 100644 index 000000000000..43ced45a3c5d --- /dev/null +++ b/third_party/resize-observer-polyfill/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Denis Rul + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/resize-observer-polyfill/README.amp b/third_party/resize-observer-polyfill/README.amp new file mode 100644 index 000000000000..dc1a544f6986 --- /dev/null +++ b/third_party/resize-observer-polyfill/README.amp @@ -0,0 +1,17 @@ +URL: https://github.com/que-etc/resize-observer-polyfill +Fork URL: https://github.com/dvoytenko/resize-observer-polyfill +License: MIT License +License File: https://github.com/que-etc/resize-observer-polyfill/blob/master/LICENSE + +Description: +A polyfill for the ResizeObserver API. + +The original [polyfill](https://github.com/que-etc/resize-observer-polyfill) has +been [forked](https://github.com/dvoytenko/resize-observer-polyfill) to enable +this polyfill to work across the boundary of Shadow DOM and same-origin +iframes. See https://github.com/que-etc/resize-observer-polyfill/pull/79 for +more details. As soon as this pull request has been merged, the AMP project +will switch to [NPM dependency](https://www.npmjs.com/package/resize-observer-polyfill). + +Local modifications: +- The installer is wrapped into `installResizeObserver` function. See `ResizeObserver.install.js`. diff --git a/third_party/resize-observer-polyfill/ResizeObserver.install.js b/third_party/resize-observer-polyfill/ResizeObserver.install.js new file mode 100644 index 000000000000..d6798c9892b5 --- /dev/null +++ b/third_party/resize-observer-polyfill/ResizeObserver.install.js @@ -0,0 +1,1163 @@ +export function installResizeObserver(global) { +(function (global, factory) { + (global.ResizeObserver = factory()); +}(global, (function () { 'use strict'; + + /** + * A collection of shims that provide minimal functionality of the ES6 collections. + * + * These implementations are not meant to be used outside of the ResizeObserver + * modules as they cover only a limited range of use cases. + */ + /* eslint-disable require-jsdoc, valid-jsdoc */ + var MapShim = (function () { + if (typeof Map !== 'undefined') { + return Map; + } + /** + * Returns index in provided array that matches the specified key. + * + * @param {Array} arr + * @param {*} key + * @returns {number} + */ + function getIndex(arr, key) { + var result = -1; + arr.some(function (entry, index) { + if (entry[0] === key) { + result = index; + return true; + } + return false; + }); + return result; + } + return /** @class */ (function () { + function class_1() { + this.__entries__ = []; + } + Object.defineProperty(class_1.prototype, "size", { + /** + * @returns {boolean} + */ + get: function () { + return this.__entries__.length; + }, + enumerable: true, + configurable: true + }); + /** + * @param {*} key + * @returns {*} + */ + class_1.prototype.get = function (key) { + var index = getIndex(this.__entries__, key); + var entry = this.__entries__[index]; + return entry && entry[1]; + }; + /** + * @param {*} key + * @param {*} value + * @returns {void} + */ + class_1.prototype.set = function (key, value) { + var index = getIndex(this.__entries__, key); + if (~index) { + this.__entries__[index][1] = value; + } + else { + this.__entries__.push([key, value]); + } + }; + /** + * @param {*} key + * @returns {void} + */ + class_1.prototype.delete = function (key) { + var entries = this.__entries__; + var index = getIndex(entries, key); + if (~index) { + entries.splice(index, 1); + } + }; + /** + * @param {*} key + * @returns {void} + */ + class_1.prototype.has = function (key) { + return !!~getIndex(this.__entries__, key); + }; + /** + * @returns {void} + */ + class_1.prototype.clear = function () { + this.__entries__.splice(0); + }; + /** + * @param {Function} callback + * @param {*} [ctx=null] + * @returns {void} + */ + class_1.prototype.forEach = function (callback, ctx) { + if (ctx === void 0) { ctx = null; } + for (var _i = 0, _a = this.__entries__; _i < _a.length; _i++) { + var entry = _a[_i]; + callback.call(ctx, entry[1], entry[0]); + } + }; + return class_1; + }()); + })(); + + /** + * Defines non-writable/enumerable properties of the provided target object. + * + * @param {Object} target - Object for which to define properties. + * @param {Object} props - Properties to be defined. + * @returns {Object} Target object. + */ + var defineConfigurable = (function (target, props) { + for (var _i = 0, _a = Object.keys(props); _i < _a.length; _i++) { + var key = _a[_i]; + Object.defineProperty(target, key, { + value: props[key], + enumerable: false, + writable: false, + configurable: true + }); + } + return target; + }); + + // Returns global object of a current environment. + var global$1 = (function () { + if (typeof global !== 'undefined' && global.Math === Math) { + return global; + } + if (typeof self !== 'undefined' && self.Math === Math) { + return self; + } + if (typeof window !== 'undefined' && window.Math === Math) { + return window; + } + // eslint-disable-next-line no-new-func + return Function('return this')(); + })(); + + /** + * Returns the global object associated with provided element. + * + * @param {Object} target + * @returns {Object} + */ + var getWindowOf = (function (target) { + // Assume that the element is an instance of Node, which means that it + // has the "ownerDocument" property from which we can retrieve a + // corresponding global object. + var ownerGlobal = target && target.ownerDocument && target.ownerDocument.defaultView; + // Return the local global object if it's not possible extract one from + // provided element. + return ownerGlobal || global$1; + }); + + /** + * Detects whether window and document objects are available in current environment. + */ + var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && window.document === document; + + // Placeholder of an empty content rectangle. + var emptyRect = createRectInit(0, 0, 0, 0); + /** + * Converts provided string to a number. + * + * @param {number|string} value + * @returns {number} + */ + function toFloat(value) { + return parseFloat(value) || 0; + } + /** + * Extracts borders size from provided styles. + * + * @param {CSSStyleDeclaration} styles + * @param {...string} positions - Borders positions (top, right, ...) + * @returns {number} + */ + function getBordersSize(styles) { + var positions = []; + for (var _i = 1; _i < arguments.length; _i++) { + positions[_i - 1] = arguments[_i]; + } + return positions.reduce(function (size, position) { + var value = styles['border-' + position + '-width']; + return size + toFloat(value); + }, 0); + } + /** + * Extracts paddings sizes from provided styles. + * + * @param {CSSStyleDeclaration} styles + * @returns {Object} Paddings box. + */ + function getPaddings(styles) { + var positions = ['top', 'right', 'bottom', 'left']; + var paddings = {}; + for (var _i = 0, positions_1 = positions; _i < positions_1.length; _i++) { + var position = positions_1[_i]; + var value = styles['padding-' + position]; + paddings[position] = toFloat(value); + } + return paddings; + } + /** + * Calculates content rectangle of provided SVG element. + * + * @param {SVGGraphicsElement} target - Element content rectangle of which needs + * to be calculated. + * @returns {DOMRectInit} + */ + function getSVGContentRect(target) { + var bbox = target.getBBox(); + return createRectInit(0, 0, bbox.width, bbox.height); + } + /** + * Calculates content rectangle of provided HTMLElement. + * + * @param {HTMLElement} target - Element for which to calculate the content rectangle. + * @returns {DOMRectInit} + */ + function getHTMLElementContentRect(target) { + // Client width & height properties can't be + // used exclusively as they provide rounded values. + var clientWidth = target.clientWidth, clientHeight = target.clientHeight; + // By this condition we can catch all non-replaced inline, hidden and + // detached elements. Though elements with width & height properties less + // than 0.5 will be discarded as well. + // + // Without it we would need to implement separate methods for each of + // those cases and it's not possible to perform a precise and performance + // effective test for hidden elements. E.g. even jQuery's ':visible' filter + // gives wrong results for elements with width & height less than 0.5. + if (!clientWidth && !clientHeight) { + return emptyRect; + } + var styles = getWindowOf(target).getComputedStyle(target); + var paddings = getPaddings(styles); + var horizPad = paddings.left + paddings.right; + var vertPad = paddings.top + paddings.bottom; + // Computed styles of width & height are being used because they are the + // only dimensions available to JS that contain non-rounded values. It could + // be possible to utilize the getBoundingClientRect if only it's data wasn't + // affected by CSS transformations let alone paddings, borders and scroll bars. + var width = toFloat(styles.width), height = toFloat(styles.height); + // Width & height include paddings and borders when the 'border-box' box + // model is applied (except for IE). + if (styles.boxSizing === 'border-box') { + // Following conditions are required to handle Internet Explorer which + // doesn't include paddings and borders to computed CSS dimensions. + // + // We can say that if CSS dimensions + paddings are equal to the "client" + // properties then it's either IE, and thus we don't need to subtract + // anything, or an element merely doesn't have paddings/borders styles. + if (Math.round(width + horizPad) !== clientWidth) { + width -= getBordersSize(styles, 'left', 'right') + horizPad; + } + if (Math.round(height + vertPad) !== clientHeight) { + height -= getBordersSize(styles, 'top', 'bottom') + vertPad; + } + } + // Following steps can't be applied to the document's root element as its + // client[Width/Height] properties represent viewport area of the window. + // Besides, it's as well not necessary as the itself neither has + // rendered scroll bars nor it can be clipped. + if (!isDocumentElement(target)) { + // In some browsers (only in Firefox, actually) CSS width & height + // include scroll bars size which can be removed at this step as scroll + // bars are the only difference between rounded dimensions + paddings + // and "client" properties, though that is not always true in Chrome. + var vertScrollbar = Math.round(width + horizPad) - clientWidth; + var horizScrollbar = Math.round(height + vertPad) - clientHeight; + // Chrome has a rather weird rounding of "client" properties. + // E.g. for an element with content width of 314.2px it sometimes gives + // the client width of 315px and for the width of 314.7px it may give + // 314px. And it doesn't happen all the time. So just ignore this delta + // as a non-relevant. + if (Math.abs(vertScrollbar) !== 1) { + width -= vertScrollbar; + } + if (Math.abs(horizScrollbar) !== 1) { + height -= horizScrollbar; + } + } + return createRectInit(paddings.left, paddings.top, width, height); + } + /** + * Checks whether provided element is an instance of the SVGGraphicsElement. + * + * @param {Element} target - Element to be checked. + * @returns {boolean} + */ + var isSVGGraphicsElement = (function () { + // Some browsers, namely IE and Edge, don't have the SVGGraphicsElement + // interface. + if (typeof SVGGraphicsElement !== 'undefined') { + return function (target) { return target instanceof getWindowOf(target).SVGGraphicsElement; }; + } + // If it's so, then check that element is at least an instance of the + // SVGElement and that it has the "getBBox" method. + // eslint-disable-next-line no-extra-parens + return function (target) { return (target instanceof getWindowOf(target).SVGElement && + typeof target.getBBox === 'function'); }; + })(); + /** + * Checks whether provided element is a document element (). + * + * @param {Element} target - Element to be checked. + * @returns {boolean} + */ + function isDocumentElement(target) { + return target === getWindowOf(target).document.documentElement; + } + /** + * Calculates an appropriate content rectangle for provided html or svg element. + * + * @param {Element} target - Element content rectangle of which needs to be calculated. + * @returns {DOMRectInit} + */ + function getContentRect(target) { + if (!isBrowser) { + return emptyRect; + } + if (isSVGGraphicsElement(target)) { + return getSVGContentRect(target); + } + return getHTMLElementContentRect(target); + } + /** + * Creates rectangle with an interface of the DOMRectReadOnly. + * Spec: https://drafts.fxtf.org/geometry/#domrectreadonly + * + * @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions. + * @returns {DOMRectReadOnly} + */ + function createReadOnlyRect(_a) { + var x = _a.x, y = _a.y, width = _a.width, height = _a.height; + // If DOMRectReadOnly is available use it as a prototype for the rectangle. + var Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object; + var rect = Object.create(Constr.prototype); + // Rectangle's properties are not writable and non-enumerable. + defineConfigurable(rect, { + x: x, y: y, width: width, height: height, + top: y, + right: x + width, + bottom: height + y, + left: x + }); + return rect; + } + /** + * Creates DOMRectInit object based on the provided dimensions and the x/y coordinates. + * Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit + * + * @param {number} x - X coordinate. + * @param {number} y - Y coordinate. + * @param {number} width - Rectangle's width. + * @param {number} height - Rectangle's height. + * @returns {DOMRectInit} + */ + function createRectInit(x, y, width, height) { + return { x: x, y: y, width: width, height: height }; + } + + /** + * Class that is responsible for computations of the content rectangle of + * provided DOM element and for keeping track of it's changes. + */ + var ResizeObservation = /** @class */ (function () { + /** + * Creates an instance of ResizeObservation. + * + * @param {Element} target - Element to be observed. + * @param {Node} rootNode - The root node of the element at the time + * of subscription. + */ + function ResizeObservation(target, rootNode) { + /** + * Broadcasted width of content rectangle. + * + * @type {number} + */ + this.broadcastWidth = 0; + /** + * Broadcasted height of content rectangle. + * + * @type {number} + */ + this.broadcastHeight = 0; + /** + * Reference to the last observed content rectangle. + * + * @private {DOMRectInit} + */ + this.contentRect_ = createRectInit(0, 0, 0, 0); + this.target = target; + this.rootNode = rootNode; + } + /** + * Updates content rectangle and tells whether it's width or height properties + * have changed since the last broadcast. + * + * @returns {boolean} + */ + ResizeObservation.prototype.isActive = function () { + var rect = getContentRect(this.target); + this.contentRect_ = rect; + return (rect.width !== this.broadcastWidth || + rect.height !== this.broadcastHeight); + }; + /** + * Updates 'broadcastWidth' and 'broadcastHeight' properties with a data + * from the corresponding properties of the last observed content rectangle. + * + * @returns {DOMRectInit} Last observed content rectangle. + */ + ResizeObservation.prototype.broadcastRect = function () { + var rect = this.contentRect_; + this.broadcastWidth = rect.width; + this.broadcastHeight = rect.height; + return rect; + }; + return ResizeObservation; + }()); + + var ResizeObserverEntry = /** @class */ (function () { + /** + * Creates an instance of ResizeObserverEntry. + * + * @param {Element} target - Element that is being observed. + * @param {DOMRectInit} rectInit - Data of the element's content rectangle. + */ + function ResizeObserverEntry(target, rectInit) { + var contentRect = createReadOnlyRect(rectInit); + // According to the specification following properties are not writable + // and are also not enumerable in the native implementation. + // + // Property accessors are not being used as they'd require to define a + // private WeakMap storage which may cause memory leaks in browsers that + // don't support this type of collections. + defineConfigurable(this, { target: target, contentRect: contentRect }); + } + return ResizeObserverEntry; + }()); + + /** + * A shim for the `Node.getRootNode()` API. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode for + * more info. + * + * @param {Node} node + * @returns {Node} + */ + function getRootNode(node) { + if (typeof node.getRootNode === 'function') { + return node.getRootNode(); + } + var n; + // eslint-disable-next-line no-empty + for (n = node; n.parentNode; n = n.parentNode) { } + return n; + } + + // Check if IntersectionObserver is available. + var intersectionObserverSupported = typeof IntersectionObserver !== 'undefined'; + var ResizeObserverSPI = /** @class */ (function () { + /** + * 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 {GlobalResizeObserverController} 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. + */ + function ResizeObserverSPI(callback, controller, callbackCtx) { + var _this = this; + /** + * Collection of resize observations that have detected changes in dimensions + * of elements. + * + * @private {Array} + */ + this.activeObservations_ = []; + /** + * Registry of the ResizeObservation instances. + * + * @private {Map} + */ + this.observations_ = new MapShim(); + /** + * The mapping between a root node and a set of targets tracked within + * this root node. + * + * @private {Map>} + */ + this.rootNodes_ = new MapShim(); + /** + * An instance of the intersection observer when available. There are a + * lot more browser versions that support the `IntersectionObserver`, but + * not the `ResizeObserver`. When `IntersectionObserver` is available it + * can be used to pick up DOM additions and removals more timely without + * significant costs. + * + * @private {IntersectionObserver} + */ + this.intersectionObserver_ = null; + 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; + if (intersectionObserverSupported) { + this.intersectionObserver_ = new IntersectionObserver(function () { return _this.checkRootChanges_(); }); + } + } + /** + * Starts observing provided element. + * + * @param {Element} target - Element to be observed. + * @returns {void} + */ + ResizeObserverSPI.prototype.observe = function (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".'); + } + var observations = this.observations_; + // Do nothing if element is already being observed. + if (observations.has(target)) { + return; + } + var rootNode = getControlledRootNode(target, target.ownerDocument); + observations.set(target, new ResizeObservation(target, rootNode)); + var rootNodeTargets = this.rootNodes_.get(rootNode); + if (!rootNodeTargets) { + rootNodeTargets = []; + this.rootNodes_.set(rootNode, rootNodeTargets); + this.controller_.addObserver(rootNode, this); + } + rootNodeTargets.push(target); + if (this.intersectionObserver_) { + this.intersectionObserver_.observe(target); + } + // Force the update of observations. + this.controller_.refresh(rootNode); + }; + /** + * Stops observing provided element. + * + * @param {Element} target - Element to stop observing. + * @returns {void} + */ + ResizeObserverSPI.prototype.unobserve = function (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".'); + } + var observations = this.observations_; + var observation = observations.get(target); + // Do nothing if element is not being observed. + if (!observation) { + return; + } + observations.delete(target); + if (this.intersectionObserver_) { + this.intersectionObserver_.unobserve(target); + } + // Disconnect the root if no longer used. + var rootNode = observation.rootNode; + var rootNodeTargets = this.rootNodes_.get(rootNode); + if (rootNodeTargets) { + var index = rootNodeTargets.indexOf(target); + if (~index) { + rootNodeTargets.splice(index, 1); + } + if (rootNodeTargets.length === 0) { + this.rootNodes_.delete(rootNode); + this.controller_.removeObserver(rootNode, this); + } + } + }; + /** + * Stops observing all elements. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.disconnect = function () { + var _this = this; + this.clearActive(); + this.observations_.clear(); + this.rootNodes_.forEach(function (_, rootNode) { + _this.controller_.removeObserver(rootNode, _this); + }); + this.rootNodes_.clear(); + if (this.intersectionObserver_) { + this.intersectionObserver_.disconnect(); + this.intersectionObserver_ = null; + } + }; + /** + * Collects observation instances the associated element of which has changed + * it's content rectangle. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.gatherActive = function () { + var _this = this; + this.checkRootChanges_(); + this.clearActive(); + this.observations_.forEach(function (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} + */ + ResizeObserverSPI.prototype.broadcastActive = function () { + // Do nothing if observer doesn't have active observations. + if (!this.hasActive()) { + return; + } + var ctx = this.callbackCtx_; + // Create ResizeObserverEntry instance for every active observation. + var entries = this.activeObservations_.map(function (observation) { + return new ResizeObserverEntry(observation.target, observation.broadcastRect()); + }); + this.callback_.call(ctx, entries, ctx); + this.clearActive(); + }; + /** + * Clears the collection of active observations. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.clearActive = function () { + this.activeObservations_.splice(0); + }; + /** + * Tells whether observer has active observations. + * + * @returns {boolean} + */ + ResizeObserverSPI.prototype.hasActive = function () { + return this.activeObservations_.length > 0; + }; + /** + * Check if any of the targets have changed the root node. For instance, + * an element could be moved from the main DOM to a shadow root. + * + * @private + * @returns {void} + */ + ResizeObserverSPI.prototype.checkRootChanges_ = function () { + var _this = this; + var changedRootTargets = null; + this.observations_.forEach(function (observation) { + var target = observation.target, oldRootNode = observation.rootNode; + var rootNode = getControlledRootNode(target, oldRootNode); + if (rootNode !== oldRootNode) { + if (!changedRootTargets) { + changedRootTargets = []; + } + changedRootTargets.push(target); + } + }); + if (changedRootTargets) { + changedRootTargets.forEach(function (target) { + _this.unobserve(target); + _this.observe(target); + }); + } + }; + return ResizeObserverSPI; + }()); + /** + * Find the most appropriate root node that should be monitored for events + * related to this target. + * + * @param {Node} target + * @param {Node} def + * @returns {Node} + */ + function getControlledRootNode(target, def) { + var rootNode = getRootNode(target); + // DOCUMENT_NODE = 9 + // DOCUMENT_FRAGMENT_NODE = 11 (shadow root) + if (rootNode.nodeType === 9 || + rootNode.nodeType === 11) { + return rootNode; + } + return def; + } + + /** + * A shim for the requestAnimationFrame which falls back to the setTimeout if + * first one is not supported. + * + * @returns {number} Requests' identifier. + */ + var requestAnimationFrame$1 = (function () { + if (typeof requestAnimationFrame === 'function') { + // It's required to use a bounded function because IE sometimes throws + // an "Invalid calling object" error if rAF is invoked without the global + // object on the left hand side. + return requestAnimationFrame.bind(global$1); + } + return function (callback) { return setTimeout(function () { return callback(Date.now()); }, 1000 / 60); }; + })(); + + // Defines minimum timeout before adding a trailing call. + var trailingTimeout = 2; + /** + * Creates a wrapper function which ensures that provided callback will be + * invoked only once during the specified delay period. + * + * @param {Function} callback - Function to be invoked after the delay period. + * @param {number} delay - Delay after which to invoke callback. + * @returns {Function} + */ + function throttle (callback, delay) { + var leadingCall = false, trailingCall = false, lastCallTime = 0; + /** + * Invokes the original callback function and schedules new invocation if + * the "proxy" was called during current request. + * + * @returns {void} + */ + function resolvePending() { + if (leadingCall) { + leadingCall = false; + callback(); + } + if (trailingCall) { + proxy(); + } + } + /** + * Callback invoked after the specified delay. It will further postpone + * invocation of the original function delegating it to the + * requestAnimationFrame. + * + * @returns {void} + */ + function timeoutCallback() { + requestAnimationFrame$1(resolvePending); + } + /** + * Schedules invocation of the original function. + * + * @returns {void} + */ + function proxy() { + var timeStamp = Date.now(); + if (leadingCall) { + // Reject immediately following calls. + if (timeStamp - lastCallTime < trailingTimeout) { + return; + } + // Schedule new call to be in invoked when the pending one is resolved. + // This is important for "transitions" which never actually start + // immediately so there is a chance that we might miss one if change + // happens amids the pending invocation. + trailingCall = true; + } + else { + leadingCall = true; + trailingCall = false; + setTimeout(timeoutCallback, delay); + } + lastCallTime = timeStamp; + } + return proxy; + } + + // Minimum delay before invoking the update of observers. + var REFRESH_DELAY = 20; + // A list of substrings of CSS properties used to find transition events that + // might affect dimensions of observed elements. + var transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight']; + // Check if MutationObserver is available. + var mutationObserverSupported = typeof MutationObserver !== 'undefined'; + /** + * The controller that tracks the resize-related events for the specified + * root node. The `GlobalResizeObserverController` uses a per-root-node + * instance of this class to track mutations and other events within the + * specified root. + */ + var ResizeObserverController = /** @class */ (function () { + /** + * Creates a new instance of ResizeObserverController. + * + * @private + * @param {Node} rootNode - The root node that this controller monitors. + * @param {GlobalResizeObserverController} globalController - The global + * controller for all roots. + */ + function ResizeObserverController(rootNode, globalController) { + /** + * The root node that this controller monitors. + * + * @private {Node} + */ + this.rootNode_ = null; + /** + * The global controller. + * + * @private {GlobalResizeObserverController} + */ + this.globalController_ = null; + /** + * Indicates whether DOM listeners have been added. + * + * @private {boolean} + */ + this.connected_ = false; + /** + * Tells that controller has subscribed for Mutation Events. + * + * @private {boolean} + */ + this.mutationEventsAdded_ = false; + /** + * Keeps reference to the instance of MutationObserver. + * + * @private {MutationObserver} + */ + this.mutationsObserver_ = null; + /** + * Monitors the shadow root host for size changes. + * + * @private {ResizeObserverSPI} + */ + this.hostObserver_ = null; + /** + * A list of connected observers. + * + * @private {Array} + */ + this.observers_ = []; + this.rootNode_ = rootNode; + this.globalController_ = globalController; + this.onTransitionEnd_ = this.onTransitionEnd_.bind(this); + this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY); + } + /** + * Adds observer to observers list. + * + * @param {ResizeObserverSPI} observer - Observer to be added. + * @returns {void} + */ + ResizeObserverController.prototype.addObserver = function (observer) { + if (!~this.observers_.indexOf(observer)) { + this.observers_.push(observer); + } + // Add listeners if they haven't been added yet. + if (!this.connected_) { + this.connect_(); + } + }; + /** + * Removes observer from observers list. + * + * @param {ResizeObserverSPI} observer - Observer to be removed. + * @returns {void} + */ + ResizeObserverController.prototype.removeObserver = function (observer) { + var observers = this.observers_; + var index = observers.indexOf(observer); + // Remove observer if it's present in registry. + if (~index) { + observers.splice(index, 1); + } + // Remove listeners if controller has no connected observers. + if (!observers.length && this.connected_) { + this.disconnect_(); + } + }; + /** + * Invokes the update of observers. It will continue running updates insofar + * it detects changes. + * + * @returns {void} + */ + ResizeObserverController.prototype.refresh = function () { + var changesDetected = this.updateObservers_(); + // Continue running updates if changes have been detected as there might + // be future ones caused by CSS transitions. + if (changesDetected) { + this.refresh(); + } + }; + /** + * Updates every observer from observers list and notifies them of queued + * entries. + * + * @private + * @returns {boolean} Returns "true" if any observer has detected changes in + * dimensions of it's elements. + */ + ResizeObserverController.prototype.updateObservers_ = function () { + // Collect observers that have active observations. + var activeObservers = this.observers_.filter(function (observer) { + return observer.gatherActive(), observer.hasActive(); + }); + // Deliver notifications in a separate cycle in order to avoid any + // collisions between observers, e.g. when multiple instances of + // ResizeObserver are tracking the same element and the callback of one + // of them changes content dimensions of the observed target. Sometimes + // this may result in notifications being blocked for the rest of observers. + activeObservers.forEach(function (observer) { return observer.broadcastActive(); }); + return activeObservers.length > 0; + }; + /** + * Initializes DOM listeners. + * + * @private + * @returns {void} + */ + ResizeObserverController.prototype.connect_ = function () { + // Do nothing if running in a non-browser environment or if listeners + // have been already added. + if (!isBrowser || this.connected_) { + return; + } + var rootNode = this.rootNode_; + var doc = rootNode.ownerDocument || rootNode; + var win = doc.defaultView; + // Subscription to the "Transitionend" event is used as a workaround for + // delayed transitions. This way it's possible to capture at least the + // final state of an element. + rootNode.addEventListener('transitionend', this.onTransitionEnd_, true); + if (win) { + win.addEventListener('resize', this.refresh, true); + } + if (mutationObserverSupported) { + this.mutationsObserver_ = new MutationObserver(this.refresh); + this.mutationsObserver_.observe(rootNode, { + attributes: true, + childList: true, + characterData: true, + subtree: true + }); + } + else { + rootNode.addEventListener('DOMSubtreeModified', this.refresh, true); + this.mutationEventsAdded_ = true; + } + // It's a shadow root. Monitor the host. + if (this.rootNode_.host) { + this.hostObserver_ = new ResizeObserverSPI(this.refresh, this.globalController_, this); + this.hostObserver_.observe(this.rootNode_.host); + } + this.connected_ = true; + }; + /** + * Removes DOM listeners. + * + * @private + * @returns {void} + */ + ResizeObserverController.prototype.disconnect_ = function () { + // Do nothing if running in a non-browser environment or if listeners + // have been already removed. + if (!isBrowser || !this.connected_) { + return; + } + var rootNode = this.rootNode_; + var doc = rootNode.ownerDocument || rootNode; + var win = doc.defaultView; + rootNode.removeEventListener('transitionend', this.onTransitionEnd_, true); + if (win) { + win.removeEventListener('resize', this.refresh, true); + } + if (this.mutationsObserver_) { + this.mutationsObserver_.disconnect(); + } + if (this.mutationEventsAdded_) { + rootNode.removeEventListener('DOMSubtreeModified', this.refresh, true); + } + if (this.hostObserver_) { + this.hostObserver_.disconnect(); + } + this.hostObserver_ = null; + this.mutationsObserver_ = null; + this.mutationEventsAdded_ = false; + this.connected_ = false; + }; + /** + * "Transitionend" event handler. + * + * @private + * @param {TransitionEvent} event + * @returns {void} + */ + ResizeObserverController.prototype.onTransitionEnd_ = function (_a) { + var _b = _a.propertyName, propertyName = _b === void 0 ? '' : _b; + // Detect whether transition may affect dimensions of an element. + var isReflowProperty = transitionKeys.some(function (key) { + return !!~propertyName.indexOf(key); + }); + if (isReflowProperty) { + this.refresh(); + } + }; + return ResizeObserverController; + }()); + + /** + * Singleton controller class which handles updates of ResizeObserver instances. + */ + var GlobalResizeObserverController = /** @class */ (function () { + function GlobalResizeObserverController() { + /** + * A mapping from a DOM root node and a respective controller. A root node + * could be the main document, a same-origin iframe, or a shadow root. + * See https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode + * for more info. + * + * @private {Map} + */ + this.rootNodeControllers_ = typeof WeakMap !== 'undefined' ? new WeakMap() : new Map(); + } + /** + * Adds observer to observers list. + * + * @param {Node} rootNode - The root node for which the observer is added. + * @param {ResizeObserverSPI} observer - Observer to be added. + * @returns {void} + */ + GlobalResizeObserverController.prototype.addObserver = function (rootNode, observer) { + var rootNodeController = this.rootNodeControllers_.get(rootNode); + if (!rootNodeController) { + rootNodeController = new ResizeObserverController(rootNode, this); + this.rootNodeControllers_.set(rootNode, rootNodeController); + } + rootNodeController.addObserver(observer); + }; + /** + * Removes observer from observers list. + * + * @param {Node} rootNode - The root node from which the observer is removed. + * @param {ResizeObserverSPI} observer - Observer to be removed. + * @returns {void} + */ + GlobalResizeObserverController.prototype.removeObserver = function (rootNode, observer) { + var rootNodeController = this.rootNodeControllers_.get(rootNode); + if (rootNodeController) { + rootNodeController.removeObserver(observer); + } + }; + /** + * Invokes the update of observers. It will continue running updates insofar + * it detects changes. + * + * @param {Node} rootNode - The root node to refresh. + * @returns {void} + */ + GlobalResizeObserverController.prototype.refresh = function (rootNode) { + var rootNodeController = this.rootNodeControllers_.get(rootNode); + if (rootNodeController) { + rootNodeController.refresh(); + } + }; + /** + * Returns instance of the GlobalResizeObserverController. + * + * @returns {GlobalResizeObserverController} + */ + GlobalResizeObserverController.getInstance = function () { + if (!this.instance_) { + this.instance_ = new GlobalResizeObserverController(); + } + return this.instance_; + }; + /** + * Holds reference to the controller's instance. + * + * @private {GlobalResizeObserverController} + */ + GlobalResizeObserverController.instance_ = null; + return GlobalResizeObserverController; + }()); + + // Registry of internal observers. If WeakMap is not available use current shim + // for the Map collection as it has all required methods and because WeakMap + // can't be fully polyfilled anyway. + var observers = typeof WeakMap !== 'undefined' ? new WeakMap() : new MapShim(); + /** + * ResizeObserver API. Encapsulates the ResizeObserver SPI implementation + * exposing only those methods and properties that are defined in the spec. + */ + var ResizeObserver = /** @class */ (function () { + /** + * Creates a new instance of ResizeObserver. + * + * @param {ResizeObserverCallback} callback - Callback that is invoked when + * dimensions of the observed elements change. + */ + function ResizeObserver(callback) { + if (!(this instanceof ResizeObserver)) { + throw new TypeError('Cannot call a class as a function.'); + } + if (!arguments.length) { + throw new TypeError('1 argument required, but only 0 present.'); + } + var controller = GlobalResizeObserverController.getInstance(); + var observer = new ResizeObserverSPI(callback, controller, this); + observers.set(this, observer); + } + return ResizeObserver; + }()); + // Expose public methods of ResizeObserver. + [ + 'observe', + 'unobserve', + 'disconnect' + ].forEach(function (method) { + ResizeObserver.prototype[method] = function () { + var _a; + return (_a = observers.get(this))[method].apply(_a, arguments); + }; + }); + + var index = (function () { + // Export existing implementation if available. + if (typeof global$1.ResizeObserver !== 'undefined') { + return global$1.ResizeObserver; + } + return ResizeObserver; + })(); + + return index; + +}))); +} diff --git a/third_party/resize-observer-polyfill/ResizeObserver.js b/third_party/resize-observer-polyfill/ResizeObserver.js new file mode 100644 index 000000000000..10f2761e2f15 --- /dev/null +++ b/third_party/resize-observer-polyfill/ResizeObserver.js @@ -0,0 +1,1163 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.ResizeObserver = factory()); +}(this, (function () { 'use strict'; + + /** + * A collection of shims that provide minimal functionality of the ES6 collections. + * + * These implementations are not meant to be used outside of the ResizeObserver + * modules as they cover only a limited range of use cases. + */ + /* eslint-disable require-jsdoc, valid-jsdoc */ + var MapShim = (function () { + if (typeof Map !== 'undefined') { + return Map; + } + /** + * Returns index in provided array that matches the specified key. + * + * @param {Array} arr + * @param {*} key + * @returns {number} + */ + function getIndex(arr, key) { + var result = -1; + arr.some(function (entry, index) { + if (entry[0] === key) { + result = index; + return true; + } + return false; + }); + return result; + } + return /** @class */ (function () { + function class_1() { + this.__entries__ = []; + } + Object.defineProperty(class_1.prototype, "size", { + /** + * @returns {boolean} + */ + get: function () { + return this.__entries__.length; + }, + enumerable: true, + configurable: true + }); + /** + * @param {*} key + * @returns {*} + */ + class_1.prototype.get = function (key) { + var index = getIndex(this.__entries__, key); + var entry = this.__entries__[index]; + return entry && entry[1]; + }; + /** + * @param {*} key + * @param {*} value + * @returns {void} + */ + class_1.prototype.set = function (key, value) { + var index = getIndex(this.__entries__, key); + if (~index) { + this.__entries__[index][1] = value; + } + else { + this.__entries__.push([key, value]); + } + }; + /** + * @param {*} key + * @returns {void} + */ + class_1.prototype.delete = function (key) { + var entries = this.__entries__; + var index = getIndex(entries, key); + if (~index) { + entries.splice(index, 1); + } + }; + /** + * @param {*} key + * @returns {void} + */ + class_1.prototype.has = function (key) { + return !!~getIndex(this.__entries__, key); + }; + /** + * @returns {void} + */ + class_1.prototype.clear = function () { + this.__entries__.splice(0); + }; + /** + * @param {Function} callback + * @param {*} [ctx=null] + * @returns {void} + */ + class_1.prototype.forEach = function (callback, ctx) { + if (ctx === void 0) { ctx = null; } + for (var _i = 0, _a = this.__entries__; _i < _a.length; _i++) { + var entry = _a[_i]; + callback.call(ctx, entry[1], entry[0]); + } + }; + return class_1; + }()); + })(); + + /** + * Defines non-writable/enumerable properties of the provided target object. + * + * @param {Object} target - Object for which to define properties. + * @param {Object} props - Properties to be defined. + * @returns {Object} Target object. + */ + var defineConfigurable = (function (target, props) { + for (var _i = 0, _a = Object.keys(props); _i < _a.length; _i++) { + var key = _a[_i]; + Object.defineProperty(target, key, { + value: props[key], + enumerable: false, + writable: false, + configurable: true + }); + } + return target; + }); + + // Returns global object of a current environment. + var global$1 = (function () { + if (typeof global !== 'undefined' && global.Math === Math) { + return global; + } + if (typeof self !== 'undefined' && self.Math === Math) { + return self; + } + if (typeof window !== 'undefined' && window.Math === Math) { + return window; + } + // eslint-disable-next-line no-new-func + return Function('return this')(); + })(); + + /** + * Returns the global object associated with provided element. + * + * @param {Object} target + * @returns {Object} + */ + var getWindowOf = (function (target) { + // Assume that the element is an instance of Node, which means that it + // has the "ownerDocument" property from which we can retrieve a + // corresponding global object. + var ownerGlobal = target && target.ownerDocument && target.ownerDocument.defaultView; + // Return the local global object if it's not possible extract one from + // provided element. + return ownerGlobal || global$1; + }); + + /** + * Detects whether window and document objects are available in current environment. + */ + var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && window.document === document; + + // Placeholder of an empty content rectangle. + var emptyRect = createRectInit(0, 0, 0, 0); + /** + * Converts provided string to a number. + * + * @param {number|string} value + * @returns {number} + */ + function toFloat(value) { + return parseFloat(value) || 0; + } + /** + * Extracts borders size from provided styles. + * + * @param {CSSStyleDeclaration} styles + * @param {...string} positions - Borders positions (top, right, ...) + * @returns {number} + */ + function getBordersSize(styles) { + var positions = []; + for (var _i = 1; _i < arguments.length; _i++) { + positions[_i - 1] = arguments[_i]; + } + return positions.reduce(function (size, position) { + var value = styles['border-' + position + '-width']; + return size + toFloat(value); + }, 0); + } + /** + * Extracts paddings sizes from provided styles. + * + * @param {CSSStyleDeclaration} styles + * @returns {Object} Paddings box. + */ + function getPaddings(styles) { + var positions = ['top', 'right', 'bottom', 'left']; + var paddings = {}; + for (var _i = 0, positions_1 = positions; _i < positions_1.length; _i++) { + var position = positions_1[_i]; + var value = styles['padding-' + position]; + paddings[position] = toFloat(value); + } + return paddings; + } + /** + * Calculates content rectangle of provided SVG element. + * + * @param {SVGGraphicsElement} target - Element content rectangle of which needs + * to be calculated. + * @returns {DOMRectInit} + */ + function getSVGContentRect(target) { + var bbox = target.getBBox(); + return createRectInit(0, 0, bbox.width, bbox.height); + } + /** + * Calculates content rectangle of provided HTMLElement. + * + * @param {HTMLElement} target - Element for which to calculate the content rectangle. + * @returns {DOMRectInit} + */ + function getHTMLElementContentRect(target) { + // Client width & height properties can't be + // used exclusively as they provide rounded values. + var clientWidth = target.clientWidth, clientHeight = target.clientHeight; + // By this condition we can catch all non-replaced inline, hidden and + // detached elements. Though elements with width & height properties less + // than 0.5 will be discarded as well. + // + // Without it we would need to implement separate methods for each of + // those cases and it's not possible to perform a precise and performance + // effective test for hidden elements. E.g. even jQuery's ':visible' filter + // gives wrong results for elements with width & height less than 0.5. + if (!clientWidth && !clientHeight) { + return emptyRect; + } + var styles = getWindowOf(target).getComputedStyle(target); + var paddings = getPaddings(styles); + var horizPad = paddings.left + paddings.right; + var vertPad = paddings.top + paddings.bottom; + // Computed styles of width & height are being used because they are the + // only dimensions available to JS that contain non-rounded values. It could + // be possible to utilize the getBoundingClientRect if only it's data wasn't + // affected by CSS transformations let alone paddings, borders and scroll bars. + var width = toFloat(styles.width), height = toFloat(styles.height); + // Width & height include paddings and borders when the 'border-box' box + // model is applied (except for IE). + if (styles.boxSizing === 'border-box') { + // Following conditions are required to handle Internet Explorer which + // doesn't include paddings and borders to computed CSS dimensions. + // + // We can say that if CSS dimensions + paddings are equal to the "client" + // properties then it's either IE, and thus we don't need to subtract + // anything, or an element merely doesn't have paddings/borders styles. + if (Math.round(width + horizPad) !== clientWidth) { + width -= getBordersSize(styles, 'left', 'right') + horizPad; + } + if (Math.round(height + vertPad) !== clientHeight) { + height -= getBordersSize(styles, 'top', 'bottom') + vertPad; + } + } + // Following steps can't be applied to the document's root element as its + // client[Width/Height] properties represent viewport area of the window. + // Besides, it's as well not necessary as the itself neither has + // rendered scroll bars nor it can be clipped. + if (!isDocumentElement(target)) { + // In some browsers (only in Firefox, actually) CSS width & height + // include scroll bars size which can be removed at this step as scroll + // bars are the only difference between rounded dimensions + paddings + // and "client" properties, though that is not always true in Chrome. + var vertScrollbar = Math.round(width + horizPad) - clientWidth; + var horizScrollbar = Math.round(height + vertPad) - clientHeight; + // Chrome has a rather weird rounding of "client" properties. + // E.g. for an element with content width of 314.2px it sometimes gives + // the client width of 315px and for the width of 314.7px it may give + // 314px. And it doesn't happen all the time. So just ignore this delta + // as a non-relevant. + if (Math.abs(vertScrollbar) !== 1) { + width -= vertScrollbar; + } + if (Math.abs(horizScrollbar) !== 1) { + height -= horizScrollbar; + } + } + return createRectInit(paddings.left, paddings.top, width, height); + } + /** + * Checks whether provided element is an instance of the SVGGraphicsElement. + * + * @param {Element} target - Element to be checked. + * @returns {boolean} + */ + var isSVGGraphicsElement = (function () { + // Some browsers, namely IE and Edge, don't have the SVGGraphicsElement + // interface. + if (typeof SVGGraphicsElement !== 'undefined') { + return function (target) { return target instanceof getWindowOf(target).SVGGraphicsElement; }; + } + // If it's so, then check that element is at least an instance of the + // SVGElement and that it has the "getBBox" method. + // eslint-disable-next-line no-extra-parens + return function (target) { return (target instanceof getWindowOf(target).SVGElement && + typeof target.getBBox === 'function'); }; + })(); + /** + * Checks whether provided element is a document element (). + * + * @param {Element} target - Element to be checked. + * @returns {boolean} + */ + function isDocumentElement(target) { + return target === getWindowOf(target).document.documentElement; + } + /** + * Calculates an appropriate content rectangle for provided html or svg element. + * + * @param {Element} target - Element content rectangle of which needs to be calculated. + * @returns {DOMRectInit} + */ + function getContentRect(target) { + if (!isBrowser) { + return emptyRect; + } + if (isSVGGraphicsElement(target)) { + return getSVGContentRect(target); + } + return getHTMLElementContentRect(target); + } + /** + * Creates rectangle with an interface of the DOMRectReadOnly. + * Spec: https://drafts.fxtf.org/geometry/#domrectreadonly + * + * @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions. + * @returns {DOMRectReadOnly} + */ + function createReadOnlyRect(_a) { + var x = _a.x, y = _a.y, width = _a.width, height = _a.height; + // If DOMRectReadOnly is available use it as a prototype for the rectangle. + var Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object; + var rect = Object.create(Constr.prototype); + // Rectangle's properties are not writable and non-enumerable. + defineConfigurable(rect, { + x: x, y: y, width: width, height: height, + top: y, + right: x + width, + bottom: height + y, + left: x + }); + return rect; + } + /** + * Creates DOMRectInit object based on the provided dimensions and the x/y coordinates. + * Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit + * + * @param {number} x - X coordinate. + * @param {number} y - Y coordinate. + * @param {number} width - Rectangle's width. + * @param {number} height - Rectangle's height. + * @returns {DOMRectInit} + */ + function createRectInit(x, y, width, height) { + return { x: x, y: y, width: width, height: height }; + } + + /** + * Class that is responsible for computations of the content rectangle of + * provided DOM element and for keeping track of it's changes. + */ + var ResizeObservation = /** @class */ (function () { + /** + * Creates an instance of ResizeObservation. + * + * @param {Element} target - Element to be observed. + * @param {Node} rootNode - The root node of the element at the time + * of subscription. + */ + function ResizeObservation(target, rootNode) { + /** + * Broadcasted width of content rectangle. + * + * @type {number} + */ + this.broadcastWidth = 0; + /** + * Broadcasted height of content rectangle. + * + * @type {number} + */ + this.broadcastHeight = 0; + /** + * Reference to the last observed content rectangle. + * + * @private {DOMRectInit} + */ + this.contentRect_ = createRectInit(0, 0, 0, 0); + this.target = target; + this.rootNode = rootNode; + } + /** + * Updates content rectangle and tells whether it's width or height properties + * have changed since the last broadcast. + * + * @returns {boolean} + */ + ResizeObservation.prototype.isActive = function () { + var rect = getContentRect(this.target); + this.contentRect_ = rect; + return (rect.width !== this.broadcastWidth || + rect.height !== this.broadcastHeight); + }; + /** + * Updates 'broadcastWidth' and 'broadcastHeight' properties with a data + * from the corresponding properties of the last observed content rectangle. + * + * @returns {DOMRectInit} Last observed content rectangle. + */ + ResizeObservation.prototype.broadcastRect = function () { + var rect = this.contentRect_; + this.broadcastWidth = rect.width; + this.broadcastHeight = rect.height; + return rect; + }; + return ResizeObservation; + }()); + + var ResizeObserverEntry = /** @class */ (function () { + /** + * Creates an instance of ResizeObserverEntry. + * + * @param {Element} target - Element that is being observed. + * @param {DOMRectInit} rectInit - Data of the element's content rectangle. + */ + function ResizeObserverEntry(target, rectInit) { + var contentRect = createReadOnlyRect(rectInit); + // According to the specification following properties are not writable + // and are also not enumerable in the native implementation. + // + // Property accessors are not being used as they'd require to define a + // private WeakMap storage which may cause memory leaks in browsers that + // don't support this type of collections. + defineConfigurable(this, { target: target, contentRect: contentRect }); + } + return ResizeObserverEntry; + }()); + + /** + * A shim for the `Node.getRootNode()` API. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode for + * more info. + * + * @param {Node} node + * @returns {Node} + */ + function getRootNode(node) { + if (typeof node.getRootNode === 'function') { + return node.getRootNode(); + } + var n; + // eslint-disable-next-line no-empty + for (n = node; n.parentNode; n = n.parentNode) { } + return n; + } + + // Check if IntersectionObserver is available. + var intersectionObserverSupported = typeof IntersectionObserver !== 'undefined'; + var ResizeObserverSPI = /** @class */ (function () { + /** + * 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 {GlobalResizeObserverController} 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. + */ + function ResizeObserverSPI(callback, controller, callbackCtx) { + var _this = this; + /** + * Collection of resize observations that have detected changes in dimensions + * of elements. + * + * @private {Array} + */ + this.activeObservations_ = []; + /** + * Registry of the ResizeObservation instances. + * + * @private {Map} + */ + this.observations_ = new MapShim(); + /** + * The mapping between a root node and a set of targets tracked within + * this root node. + * + * @private {Map>} + */ + this.rootNodes_ = new MapShim(); + /** + * An instance of the intersection observer when available. There are a + * lot more browser versions that support the `IntersectionObserver`, but + * not the `ResizeObserver`. When `IntersectionObserver` is available it + * can be used to pick up DOM additions and removals more timely without + * significant costs. + * + * @private {IntersectionObserver} + */ + this.intersectionObserver_ = null; + 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; + if (intersectionObserverSupported) { + this.intersectionObserver_ = new IntersectionObserver(function () { return _this.checkRootChanges_(); }); + } + } + /** + * Starts observing provided element. + * + * @param {Element} target - Element to be observed. + * @returns {void} + */ + ResizeObserverSPI.prototype.observe = function (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".'); + } + var observations = this.observations_; + // Do nothing if element is already being observed. + if (observations.has(target)) { + return; + } + var rootNode = getControlledRootNode(target, target.ownerDocument); + observations.set(target, new ResizeObservation(target, rootNode)); + var rootNodeTargets = this.rootNodes_.get(rootNode); + if (!rootNodeTargets) { + rootNodeTargets = []; + this.rootNodes_.set(rootNode, rootNodeTargets); + this.controller_.addObserver(rootNode, this); + } + rootNodeTargets.push(target); + if (this.intersectionObserver_) { + this.intersectionObserver_.observe(target); + } + // Force the update of observations. + this.controller_.refresh(rootNode); + }; + /** + * Stops observing provided element. + * + * @param {Element} target - Element to stop observing. + * @returns {void} + */ + ResizeObserverSPI.prototype.unobserve = function (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".'); + } + var observations = this.observations_; + var observation = observations.get(target); + // Do nothing if element is not being observed. + if (!observation) { + return; + } + observations.delete(target); + if (this.intersectionObserver_) { + this.intersectionObserver_.unobserve(target); + } + // Disconnect the root if no longer used. + var rootNode = observation.rootNode; + var rootNodeTargets = this.rootNodes_.get(rootNode); + if (rootNodeTargets) { + var index = rootNodeTargets.indexOf(target); + if (~index) { + rootNodeTargets.splice(index, 1); + } + if (rootNodeTargets.length === 0) { + this.rootNodes_.delete(rootNode); + this.controller_.removeObserver(rootNode, this); + } + } + }; + /** + * Stops observing all elements. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.disconnect = function () { + var _this = this; + this.clearActive(); + this.observations_.clear(); + this.rootNodes_.forEach(function (_, rootNode) { + _this.controller_.removeObserver(rootNode, _this); + }); + this.rootNodes_.clear(); + if (this.intersectionObserver_) { + this.intersectionObserver_.disconnect(); + this.intersectionObserver_ = null; + } + }; + /** + * Collects observation instances the associated element of which has changed + * it's content rectangle. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.gatherActive = function () { + var _this = this; + this.checkRootChanges_(); + this.clearActive(); + this.observations_.forEach(function (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} + */ + ResizeObserverSPI.prototype.broadcastActive = function () { + // Do nothing if observer doesn't have active observations. + if (!this.hasActive()) { + return; + } + var ctx = this.callbackCtx_; + // Create ResizeObserverEntry instance for every active observation. + var entries = this.activeObservations_.map(function (observation) { + return new ResizeObserverEntry(observation.target, observation.broadcastRect()); + }); + this.callback_.call(ctx, entries, ctx); + this.clearActive(); + }; + /** + * Clears the collection of active observations. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.clearActive = function () { + this.activeObservations_.splice(0); + }; + /** + * Tells whether observer has active observations. + * + * @returns {boolean} + */ + ResizeObserverSPI.prototype.hasActive = function () { + return this.activeObservations_.length > 0; + }; + /** + * Check if any of the targets have changed the root node. For instance, + * an element could be moved from the main DOM to a shadow root. + * + * @private + * @returns {void} + */ + ResizeObserverSPI.prototype.checkRootChanges_ = function () { + var _this = this; + var changedRootTargets = null; + this.observations_.forEach(function (observation) { + var target = observation.target, oldRootNode = observation.rootNode; + var rootNode = getControlledRootNode(target, oldRootNode); + if (rootNode !== oldRootNode) { + if (!changedRootTargets) { + changedRootTargets = []; + } + changedRootTargets.push(target); + } + }); + if (changedRootTargets) { + changedRootTargets.forEach(function (target) { + _this.unobserve(target); + _this.observe(target); + }); + } + }; + return ResizeObserverSPI; + }()); + /** + * Find the most appropriate root node that should be monitored for events + * related to this target. + * + * @param {Node} target + * @param {Node} def + * @returns {Node} + */ + function getControlledRootNode(target, def) { + var rootNode = getRootNode(target); + // DOCUMENT_NODE = 9 + // DOCUMENT_FRAGMENT_NODE = 11 (shadow root) + if (rootNode.nodeType === 9 || + rootNode.nodeType === 11) { + return rootNode; + } + return def; + } + + /** + * A shim for the requestAnimationFrame which falls back to the setTimeout if + * first one is not supported. + * + * @returns {number} Requests' identifier. + */ + var requestAnimationFrame$1 = (function () { + if (typeof requestAnimationFrame === 'function') { + // It's required to use a bounded function because IE sometimes throws + // an "Invalid calling object" error if rAF is invoked without the global + // object on the left hand side. + return requestAnimationFrame.bind(global$1); + } + return function (callback) { return setTimeout(function () { return callback(Date.now()); }, 1000 / 60); }; + })(); + + // Defines minimum timeout before adding a trailing call. + var trailingTimeout = 2; + /** + * Creates a wrapper function which ensures that provided callback will be + * invoked only once during the specified delay period. + * + * @param {Function} callback - Function to be invoked after the delay period. + * @param {number} delay - Delay after which to invoke callback. + * @returns {Function} + */ + function throttle (callback, delay) { + var leadingCall = false, trailingCall = false, lastCallTime = 0; + /** + * Invokes the original callback function and schedules new invocation if + * the "proxy" was called during current request. + * + * @returns {void} + */ + function resolvePending() { + if (leadingCall) { + leadingCall = false; + callback(); + } + if (trailingCall) { + proxy(); + } + } + /** + * Callback invoked after the specified delay. It will further postpone + * invocation of the original function delegating it to the + * requestAnimationFrame. + * + * @returns {void} + */ + function timeoutCallback() { + requestAnimationFrame$1(resolvePending); + } + /** + * Schedules invocation of the original function. + * + * @returns {void} + */ + function proxy() { + var timeStamp = Date.now(); + if (leadingCall) { + // Reject immediately following calls. + if (timeStamp - lastCallTime < trailingTimeout) { + return; + } + // Schedule new call to be in invoked when the pending one is resolved. + // This is important for "transitions" which never actually start + // immediately so there is a chance that we might miss one if change + // happens amids the pending invocation. + trailingCall = true; + } + else { + leadingCall = true; + trailingCall = false; + setTimeout(timeoutCallback, delay); + } + lastCallTime = timeStamp; + } + return proxy; + } + + // Minimum delay before invoking the update of observers. + var REFRESH_DELAY = 20; + // A list of substrings of CSS properties used to find transition events that + // might affect dimensions of observed elements. + var transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight']; + // Check if MutationObserver is available. + var mutationObserverSupported = typeof MutationObserver !== 'undefined'; + /** + * The controller that tracks the resize-related events for the specified + * root node. The `GlobalResizeObserverController` uses a per-root-node + * instance of this class to track mutations and other events within the + * specified root. + */ + var ResizeObserverController = /** @class */ (function () { + /** + * Creates a new instance of ResizeObserverController. + * + * @private + * @param {Node} rootNode - The root node that this controller monitors. + * @param {GlobalResizeObserverController} globalController - The global + * controller for all roots. + */ + function ResizeObserverController(rootNode, globalController) { + /** + * The root node that this controller monitors. + * + * @private {Node} + */ + this.rootNode_ = null; + /** + * The global controller. + * + * @private {GlobalResizeObserverController} + */ + this.globalController_ = null; + /** + * Indicates whether DOM listeners have been added. + * + * @private {boolean} + */ + this.connected_ = false; + /** + * Tells that controller has subscribed for Mutation Events. + * + * @private {boolean} + */ + this.mutationEventsAdded_ = false; + /** + * Keeps reference to the instance of MutationObserver. + * + * @private {MutationObserver} + */ + this.mutationsObserver_ = null; + /** + * Monitors the shadow root host for size changes. + * + * @private {ResizeObserverSPI} + */ + this.hostObserver_ = null; + /** + * A list of connected observers. + * + * @private {Array} + */ + this.observers_ = []; + this.rootNode_ = rootNode; + this.globalController_ = globalController; + this.onTransitionEnd_ = this.onTransitionEnd_.bind(this); + this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY); + } + /** + * Adds observer to observers list. + * + * @param {ResizeObserverSPI} observer - Observer to be added. + * @returns {void} + */ + ResizeObserverController.prototype.addObserver = function (observer) { + if (!~this.observers_.indexOf(observer)) { + this.observers_.push(observer); + } + // Add listeners if they haven't been added yet. + if (!this.connected_) { + this.connect_(); + } + }; + /** + * Removes observer from observers list. + * + * @param {ResizeObserverSPI} observer - Observer to be removed. + * @returns {void} + */ + ResizeObserverController.prototype.removeObserver = function (observer) { + var observers = this.observers_; + var index = observers.indexOf(observer); + // Remove observer if it's present in registry. + if (~index) { + observers.splice(index, 1); + } + // Remove listeners if controller has no connected observers. + if (!observers.length && this.connected_) { + this.disconnect_(); + } + }; + /** + * Invokes the update of observers. It will continue running updates insofar + * it detects changes. + * + * @returns {void} + */ + ResizeObserverController.prototype.refresh = function () { + var changesDetected = this.updateObservers_(); + // Continue running updates if changes have been detected as there might + // be future ones caused by CSS transitions. + if (changesDetected) { + this.refresh(); + } + }; + /** + * Updates every observer from observers list and notifies them of queued + * entries. + * + * @private + * @returns {boolean} Returns "true" if any observer has detected changes in + * dimensions of it's elements. + */ + ResizeObserverController.prototype.updateObservers_ = function () { + // Collect observers that have active observations. + var activeObservers = this.observers_.filter(function (observer) { + return observer.gatherActive(), observer.hasActive(); + }); + // Deliver notifications in a separate cycle in order to avoid any + // collisions between observers, e.g. when multiple instances of + // ResizeObserver are tracking the same element and the callback of one + // of them changes content dimensions of the observed target. Sometimes + // this may result in notifications being blocked for the rest of observers. + activeObservers.forEach(function (observer) { return observer.broadcastActive(); }); + return activeObservers.length > 0; + }; + /** + * Initializes DOM listeners. + * + * @private + * @returns {void} + */ + ResizeObserverController.prototype.connect_ = function () { + // Do nothing if running in a non-browser environment or if listeners + // have been already added. + if (!isBrowser || this.connected_) { + return; + } + var rootNode = this.rootNode_; + var doc = rootNode.ownerDocument || rootNode; + var win = doc.defaultView; + // Subscription to the "Transitionend" event is used as a workaround for + // delayed transitions. This way it's possible to capture at least the + // final state of an element. + rootNode.addEventListener('transitionend', this.onTransitionEnd_, true); + if (win) { + win.addEventListener('resize', this.refresh, true); + } + if (mutationObserverSupported) { + this.mutationsObserver_ = new MutationObserver(this.refresh); + this.mutationsObserver_.observe(rootNode, { + attributes: true, + childList: true, + characterData: true, + subtree: true + }); + } + else { + rootNode.addEventListener('DOMSubtreeModified', this.refresh, true); + this.mutationEventsAdded_ = true; + } + // It's a shadow root. Monitor the host. + if (this.rootNode_.host) { + this.hostObserver_ = new ResizeObserverSPI(this.refresh, this.globalController_, this); + this.hostObserver_.observe(this.rootNode_.host); + } + this.connected_ = true; + }; + /** + * Removes DOM listeners. + * + * @private + * @returns {void} + */ + ResizeObserverController.prototype.disconnect_ = function () { + // Do nothing if running in a non-browser environment or if listeners + // have been already removed. + if (!isBrowser || !this.connected_) { + return; + } + var rootNode = this.rootNode_; + var doc = rootNode.ownerDocument || rootNode; + var win = doc.defaultView; + rootNode.removeEventListener('transitionend', this.onTransitionEnd_, true); + if (win) { + win.removeEventListener('resize', this.refresh, true); + } + if (this.mutationsObserver_) { + this.mutationsObserver_.disconnect(); + } + if (this.mutationEventsAdded_) { + rootNode.removeEventListener('DOMSubtreeModified', this.refresh, true); + } + if (this.hostObserver_) { + this.hostObserver_.disconnect(); + } + this.hostObserver_ = null; + this.mutationsObserver_ = null; + this.mutationEventsAdded_ = false; + this.connected_ = false; + }; + /** + * "Transitionend" event handler. + * + * @private + * @param {TransitionEvent} event + * @returns {void} + */ + ResizeObserverController.prototype.onTransitionEnd_ = function (_a) { + var _b = _a.propertyName, propertyName = _b === void 0 ? '' : _b; + // Detect whether transition may affect dimensions of an element. + var isReflowProperty = transitionKeys.some(function (key) { + return !!~propertyName.indexOf(key); + }); + if (isReflowProperty) { + this.refresh(); + } + }; + return ResizeObserverController; + }()); + + /** + * Singleton controller class which handles updates of ResizeObserver instances. + */ + var GlobalResizeObserverController = /** @class */ (function () { + function GlobalResizeObserverController() { + /** + * A mapping from a DOM root node and a respective controller. A root node + * could be the main document, a same-origin iframe, or a shadow root. + * See https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode + * for more info. + * + * @private {Map} + */ + this.rootNodeControllers_ = typeof WeakMap !== 'undefined' ? new WeakMap() : new Map(); + } + /** + * Adds observer to observers list. + * + * @param {Node} rootNode - The root node for which the observer is added. + * @param {ResizeObserverSPI} observer - Observer to be added. + * @returns {void} + */ + GlobalResizeObserverController.prototype.addObserver = function (rootNode, observer) { + var rootNodeController = this.rootNodeControllers_.get(rootNode); + if (!rootNodeController) { + rootNodeController = new ResizeObserverController(rootNode, this); + this.rootNodeControllers_.set(rootNode, rootNodeController); + } + rootNodeController.addObserver(observer); + }; + /** + * Removes observer from observers list. + * + * @param {Node} rootNode - The root node from which the observer is removed. + * @param {ResizeObserverSPI} observer - Observer to be removed. + * @returns {void} + */ + GlobalResizeObserverController.prototype.removeObserver = function (rootNode, observer) { + var rootNodeController = this.rootNodeControllers_.get(rootNode); + if (rootNodeController) { + rootNodeController.removeObserver(observer); + } + }; + /** + * Invokes the update of observers. It will continue running updates insofar + * it detects changes. + * + * @param {Node} rootNode - The root node to refresh. + * @returns {void} + */ + GlobalResizeObserverController.prototype.refresh = function (rootNode) { + var rootNodeController = this.rootNodeControllers_.get(rootNode); + if (rootNodeController) { + rootNodeController.refresh(); + } + }; + /** + * Returns instance of the GlobalResizeObserverController. + * + * @returns {GlobalResizeObserverController} + */ + GlobalResizeObserverController.getInstance = function () { + if (!this.instance_) { + this.instance_ = new GlobalResizeObserverController(); + } + return this.instance_; + }; + /** + * Holds reference to the controller's instance. + * + * @private {GlobalResizeObserverController} + */ + GlobalResizeObserverController.instance_ = null; + return GlobalResizeObserverController; + }()); + + // Registry of internal observers. If WeakMap is not available use current shim + // for the Map collection as it has all required methods and because WeakMap + // can't be fully polyfilled anyway. + var observers = typeof WeakMap !== 'undefined' ? new WeakMap() : new MapShim(); + /** + * ResizeObserver API. Encapsulates the ResizeObserver SPI implementation + * exposing only those methods and properties that are defined in the spec. + */ + var ResizeObserver = /** @class */ (function () { + /** + * Creates a new instance of ResizeObserver. + * + * @param {ResizeObserverCallback} callback - Callback that is invoked when + * dimensions of the observed elements change. + */ + function ResizeObserver(callback) { + if (!(this instanceof ResizeObserver)) { + throw new TypeError('Cannot call a class as a function.'); + } + if (!arguments.length) { + throw new TypeError('1 argument required, but only 0 present.'); + } + var controller = GlobalResizeObserverController.getInstance(); + var observer = new ResizeObserverSPI(callback, controller, this); + observers.set(this, observer); + } + return ResizeObserver; + }()); + // Expose public methods of ResizeObserver. + [ + 'observe', + 'unobserve', + 'disconnect' + ].forEach(function (method) { + ResizeObserver.prototype[method] = function () { + var _a; + return (_a = observers.get(this))[method].apply(_a, arguments); + }; + }); + + var index = (function () { + // Export existing implementation if available. + if (typeof global$1.ResizeObserver !== 'undefined') { + return global$1.ResizeObserver; + } + return ResizeObserver; + })(); + + return index; + +})));