"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.bindTo = void 0; var size_js_1 = require("./size.js"); var device_pixel_ratio_js_1 = require("./device-pixel-ratio.js"); var DevicePixelContentBoxBinding = /** @class */ (function () { function DevicePixelContentBoxBinding(canvasElement, transformBitmapSize, options) { var _a; this._canvasElement = null; this._bitmapSizeChangedListeners = []; this._suggestedBitmapSize = null; this._suggestedBitmapSizeChangedListeners = []; // devicePixelRatio approach this._devicePixelRatioObservable = null; // ResizeObserver approach this._canvasElementResizeObserver = null; this._canvasElement = canvasElement; this._canvasElementClientSize = (0, size_js_1.size)({ width: this._canvasElement.clientWidth, height: this._canvasElement.clientHeight, }); this._transformBitmapSize = transformBitmapSize !== null && transformBitmapSize !== void 0 ? transformBitmapSize : (function (size) { return size; }); this._allowResizeObserver = (_a = options === null || options === void 0 ? void 0 : options.allowResizeObserver) !== null && _a !== void 0 ? _a : true; this._chooseAndInitObserver(); // we MAY leave the constuctor without any bitmap size observation mechanics initialized } DevicePixelContentBoxBinding.prototype.dispose = function () { var _a, _b; if (this._canvasElement === null) { throw new Error('Object is disposed'); } (_a = this._canvasElementResizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect(); this._canvasElementResizeObserver = null; (_b = this._devicePixelRatioObservable) === null || _b === void 0 ? void 0 : _b.dispose(); this._devicePixelRatioObservable = null; this._suggestedBitmapSizeChangedListeners.length = 0; this._bitmapSizeChangedListeners.length = 0; this._canvasElement = null; }; Object.defineProperty(DevicePixelContentBoxBinding.prototype, "canvasElement", { get: function () { if (this._canvasElement === null) { throw new Error('Object is disposed'); } return this._canvasElement; }, enumerable: false, configurable: true }); Object.defineProperty(DevicePixelContentBoxBinding.prototype, "canvasElementClientSize", { get: function () { return this._canvasElementClientSize; }, enumerable: false, configurable: true }); Object.defineProperty(DevicePixelContentBoxBinding.prototype, "bitmapSize", { get: function () { return (0, size_js_1.size)({ width: this.canvasElement.width, height: this.canvasElement.height, }); }, enumerable: false, configurable: true }); /** * Use this function to change canvas element client size until binding is disposed * @param clientSize New client size for bound HTMLCanvasElement */ DevicePixelContentBoxBinding.prototype.resizeCanvasElement = function (clientSize) { this._canvasElementClientSize = (0, size_js_1.size)(clientSize); this.canvasElement.style.width = "".concat(this._canvasElementClientSize.width, "px"); this.canvasElement.style.height = "".concat(this._canvasElementClientSize.height, "px"); this._invalidateBitmapSize(); }; DevicePixelContentBoxBinding.prototype.subscribeBitmapSizeChanged = function (listener) { this._bitmapSizeChangedListeners.push(listener); }; DevicePixelContentBoxBinding.prototype.unsubscribeBitmapSizeChanged = function (listener) { this._bitmapSizeChangedListeners = this._bitmapSizeChangedListeners.filter(function (l) { return l !== listener; }); }; Object.defineProperty(DevicePixelContentBoxBinding.prototype, "suggestedBitmapSize", { get: function () { return this._suggestedBitmapSize; }, enumerable: false, configurable: true }); DevicePixelContentBoxBinding.prototype.subscribeSuggestedBitmapSizeChanged = function (listener) { this._suggestedBitmapSizeChangedListeners.push(listener); }; DevicePixelContentBoxBinding.prototype.unsubscribeSuggestedBitmapSizeChanged = function (listener) { this._suggestedBitmapSizeChangedListeners = this._suggestedBitmapSizeChangedListeners.filter(function (l) { return l !== listener; }); }; DevicePixelContentBoxBinding.prototype.applySuggestedBitmapSize = function () { if (this._suggestedBitmapSize === null) { // nothing to apply return; } var oldSuggestedSize = this._suggestedBitmapSize; this._suggestedBitmapSize = null; this._resizeBitmap(oldSuggestedSize); this._emitSuggestedBitmapSizeChanged(oldSuggestedSize, this._suggestedBitmapSize); }; DevicePixelContentBoxBinding.prototype._resizeBitmap = function (newSize) { var oldSize = this.bitmapSize; if ((0, size_js_1.equalSizes)(oldSize, newSize)) { return; } this.canvasElement.width = newSize.width; this.canvasElement.height = newSize.height; this._emitBitmapSizeChanged(oldSize, newSize); }; DevicePixelContentBoxBinding.prototype._emitBitmapSizeChanged = function (oldSize, newSize) { var _this = this; this._bitmapSizeChangedListeners.forEach(function (listener) { return listener.call(_this, oldSize, newSize); }); }; DevicePixelContentBoxBinding.prototype._suggestNewBitmapSize = function (newSize) { var oldSuggestedSize = this._suggestedBitmapSize; var finalNewSize = (0, size_js_1.size)(this._transformBitmapSize(newSize, this._canvasElementClientSize)); var newSuggestedSize = (0, size_js_1.equalSizes)(this.bitmapSize, finalNewSize) ? null : finalNewSize; if (oldSuggestedSize === null && newSuggestedSize === null) { return; } if (oldSuggestedSize !== null && newSuggestedSize !== null && (0, size_js_1.equalSizes)(oldSuggestedSize, newSuggestedSize)) { return; } this._suggestedBitmapSize = newSuggestedSize; this._emitSuggestedBitmapSizeChanged(oldSuggestedSize, newSuggestedSize); }; DevicePixelContentBoxBinding.prototype._emitSuggestedBitmapSizeChanged = function (oldSize, newSize) { var _this = this; this._suggestedBitmapSizeChangedListeners.forEach(function (listener) { return listener.call(_this, oldSize, newSize); }); }; DevicePixelContentBoxBinding.prototype._chooseAndInitObserver = function () { var _this = this; if (!this._allowResizeObserver) { this._initDevicePixelRatioObservable(); return; } isDevicePixelContentBoxSupported() .then(function (isSupported) { return isSupported ? _this._initResizeObserver() : _this._initDevicePixelRatioObservable(); }); }; // devicePixelRatio approach DevicePixelContentBoxBinding.prototype._initDevicePixelRatioObservable = function () { var _this = this; if (this._canvasElement === null) { // it looks like we are already dead return; } var win = canvasElementWindow(this._canvasElement); if (win === null) { throw new Error('No window is associated with the canvas'); } this._devicePixelRatioObservable = (0, device_pixel_ratio_js_1.createObservable)(win); this._devicePixelRatioObservable.subscribe(function () { return _this._invalidateBitmapSize(); }); this._invalidateBitmapSize(); }; DevicePixelContentBoxBinding.prototype._invalidateBitmapSize = function () { var _a, _b; if (this._canvasElement === null) { // it looks like we are already dead return; } var win = canvasElementWindow(this._canvasElement); if (win === null) { return; } var ratio = (_b = (_a = this._devicePixelRatioObservable) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : win.devicePixelRatio; var canvasRects = this._canvasElement.getClientRects(); var newSize = // eslint-disable-next-line no-negated-condition canvasRects[0] !== undefined ? predictedBitmapSize(canvasRects[0], ratio) : (0, size_js_1.size)({ width: this._canvasElementClientSize.width * ratio, height: this._canvasElementClientSize.height * ratio, }); this._suggestNewBitmapSize(newSize); }; // ResizeObserver approach DevicePixelContentBoxBinding.prototype._initResizeObserver = function () { var _this = this; if (this._canvasElement === null) { // it looks like we are already dead return; } this._canvasElementResizeObserver = new ResizeObserver(function (entries) { var entry = entries.find(function (entry) { return entry.target === _this._canvasElement; }); if (!entry || !entry.devicePixelContentBoxSize || !entry.devicePixelContentBoxSize[0]) { return; } var entrySize = entry.devicePixelContentBoxSize[0]; var newSize = (0, size_js_1.size)({ width: entrySize.inlineSize, height: entrySize.blockSize, }); _this._suggestNewBitmapSize(newSize); }); this._canvasElementResizeObserver.observe(this._canvasElement, { box: 'device-pixel-content-box' }); }; return DevicePixelContentBoxBinding; }()); function bindTo(canvasElement, target) { if (target.type === 'device-pixel-content-box') { return new DevicePixelContentBoxBinding(canvasElement, target.transform, target.options); } throw new Error('Unsupported binding target'); } exports.bindTo = bindTo; function canvasElementWindow(canvasElement) { // According to DOM Level 2 Core specification, ownerDocument should never be null for HTMLCanvasElement // see https://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#node-ownerDoc // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return canvasElement.ownerDocument.defaultView; } function isDevicePixelContentBoxSupported() { return new Promise(function (resolve) { var ro = new ResizeObserver(function (entries) { resolve(entries.every(function (entry) { return 'devicePixelContentBoxSize' in entry; })); ro.disconnect(); }); ro.observe(document.body, { box: 'device-pixel-content-box' }); }) .catch(function () { return false; }); } function predictedBitmapSize(canvasRect, ratio) { return (0, size_js_1.size)({ width: Math.round(canvasRect.left * ratio + canvasRect.width * ratio) - Math.round(canvasRect.left * ratio), height: Math.round(canvasRect.top * ratio + canvasRect.height * ratio) - Math.round(canvasRect.top * ratio), }); }