function applyConstraints(immediately, refPoint, /* boolean INTERNAL */ setMinMaxZoom) {
            if (!this.config.constrainViewport) {
                return this;
            }
            if (!(this.constraintBounds instanceof Malakh.Rectangle)) { // TODO a Malakh field?
                console.error('Can\'t apply constraints because constraintBounds is not set.');
                return this;
            }
            var scale, minZoomScale, maxZoomScale, pixelSize,

                adjustmentNeeded = false,
                whatToScale = 'height',

                config = this.malakh.config,

                cR = this.constraintBounds, // rectangle of constraints
                vR = this.getRectangle(), // viewport rectangle

                viewportRatio = vR.getAspectRatio(),
                constraintsRatio = cR.width / cR.height;

            if (viewportRatio < constraintsRatio) { // Empty borders on top and bottom.
                // We will turn this case into the latter one.
                whatToScale = 'width';
            }

            /// ZOOMING PART
            // Now we assume viewportRatio < constraintsRatio which means empty borders on sides.
            if (vR[whatToScale] * this.config.minVisibilityRatio > cR[whatToScale] || setMinMaxZoom) {
                // Too small, we need to zoom in.
                adjustmentNeeded = !setMinMaxZoom;
                scale = minZoomScale = vR[whatToScale] * this.config.minVisibilityRatio / cR[whatToScale];
            }
            if (!adjustmentNeeded || setMinMaxZoom) {
                // We check for `!adjustmentNeeded` just in case the image is so small it would fit in both scenarios;
                // we want to aviod flicker in some cases and we prefer zooming in too much than zooming out too much.
                pixelSize = this.getZoom() * this.minTiledImageWidthScale;
                if (pixelSize > config.maxTiledImageStretch || setMinMaxZoom) { // We've zoomed in too much
                    adjustmentNeeded = !setMinMaxZoom;
                    scale = maxZoomScale = config.maxTiledImageStretch / pixelSize;
                }
            }
            if (setMinMaxZoom) {
                this._minZoom = this.getZoom() * minZoomScale;
                this._maxZoom = this.getZoom() * maxZoomScale;
            }

            if (adjustmentNeeded) {
                this.zoomBy(scale, immediately, refPoint, true);
                vR = this.getRectangle();
            }

            if (setMinMaxZoom) { // all data collected, we're not really applying constraints now
                return this;
            }

            /// PANNING PART
            // The viewport rectangle gets adjusted to take margins into account but only for panning, not zooming.
            vR.scaleAroundCenter(1 - 2 * config.marginFactor);

            var parameterPairs = [
                {start: 'x', length: 'width'},
                {start: 'y', length: 'height'},
            ];
            for (var i = 0; i < 2; i++) {
                var pair = parameterPairs[i];
                var start = pair.start;
                var length = pair.length;

                if (vR[length] > cR[length]) {
                    if (config.centerWhenZoomedOut && config.marginFactor === 0) {
                        // If the image is zoomed out so that its height/width is
                        // smaller than constraints, center it.
                        // marginFactor === 0 is required because otherwise implementation would be inconsistent.
                        adjustmentNeeded = true;
                        vR[start] = cR[start] + cR[length] / 2 - vR[length] / 2;
                    } else if (vR[start] > cR[start]) {
                        // Just don't allow going too far outside of the container from the start side...
                        adjustmentNeeded = true;
                        vR[start] = cR[start];
                    } else if (vR[start] + vR[length] < cR[start] + cR[length]) {
                        // ...and from the end side.
                        adjustmentNeeded = true;
                        vR[start] = cR[start] + cR[length] - vR[length];
                    }
                } else if ((vR[start] < cR[start] &&
                    vR[start] + vR[length] < cR[start] + cR[length]) ||
                    (vR[start] > cR[start] &&
                        vR[start] + vR[length] > cR[start] + cR[length])) {
                    // Too far on the left/top.
                    adjustmentNeeded = true;

                    // We need to choose the smaller delta so that we don't make
                    // the image jump from side to side.
                    var delta1 = Math.abs(cR[start] - vR[start]);
                    var delta2 = Math.abs(cR[start] + cR[length] - vR[start] - vR[length]);
                    var delta = Math.min(delta1, delta2);

                    if (vR[start] > cR[start]) { // Restoring correct sign of delta.
                        delta = -delta;
                    }
                    vR[start] += delta;
                }
            }

            if (adjustmentNeeded) {
                // 'mouseAnimationTime' is needed because correcting a long animation using a short one
                // causes a jumping effect (and very visible one at that).
                this.panTo(vR.getCenter(), immediately, {
                    dontApplyConstraints: true,
                    animationTimeConfigParameter: 'mouseAnimationTime',
                });
            }

            return this;
        }
/**
         * <p>Moves the viewport so that the user doesn't go too far away. Without it it's easy
         * to "lose" the image.
         *
         * <p>NOTE: Since viewport doesn't have any information about pixel density in particular DZIs,
         * without the <code>maxLevelExp</code> parameter it can't protect from zooming in too much but
         * only from zooming out or dragging the constraints rectangle outside of the viewport. Zooming
         * in has to be handled separately. Thus, it's advised to NOT USE this method directly but
         * to use controller's <code>applyConstraints</code> method instead. This method ignores the
         * <code>this.config.constrainViewport</code> parameter since it's checked in controller's
         * method anyway.
         *
         * @param {boolean} [immediately=false]
         * @param {Malakh.Point} [refPoint]
         */
Moves the viewport so that the user doesn't go too far away. Without it it's easy
to "lose" the image.

NOTE: Since viewport doesn't have any information about pixel density in particular DZIs,
without the maxLevelExp parameter it can't protect from zooming in too much but
only from zooming out or dragging the constraints rectangle outside of the viewport. Zooming
in has to be handled separately. Thus, it's advised to NOT USE this method directly but
to use controller's applyConstraints method instead. This method ignores the
this.config.constrainViewport parameter since it's checked in controller's
method anyway.

@param {boolean} [immediately=false]
@param {Malakh.Point} [refPoint]
Moves the viewport so that the user doesn ' t go too far away . Without it it ' s easy to " lose " the image . NOTE : Since viewport doesn ' t have any information about pixel density in particular DZIs without the maxLevelExp parameter it can ' t protect from zooming in too much but only from zooming out or dragging the constraints rectangle outside of the viewport . Zooming in has to be handled separately . Thus it ' s advised to NOT USE this method directly but to use controller ' s applyConstraints method instead . This method ignores the this . config . constrainViewport parameter since it ' s checked in controller ' s method anyway . @param { boolean } [ immediately = false ] @param { Malakh . Point } [ refPoint ]
function applyConstraints(immediately, refPoint,  setMinMaxZoom) {
            if (!this.config.constrainViewport) {
                return this;
            }
            if (!(this.constraintBounds instanceof Malakh.Rectangle)) { 
                console.error('Can\'t apply constraints because constraintBounds is not set.');
                return this;
            }
            var scale, minZoomScale, maxZoomScale, pixelSize,
                adjustmentNeeded = false,
                whatToScale = 'height',
                config = this.malakh.config,
                cR = this.constraintBounds, 
                vR = this.getRectangle(), 
                viewportRatio = vR.getAspectRatio(),
                constraintsRatio = cR.width / cR.height;
            if (viewportRatio < constraintsRatio) { 
                whatToScale = 'width';
            }
            if (vR[whatToScale] * this.config.minVisibilityRatio > cR[whatToScale] || setMinMaxZoom) {
                adjustmentNeeded = !setMinMaxZoom;
                scale = minZoomScale = vR[whatToScale] * this.config.minVisibilityRatio / cR[whatToScale];
            }
            if (!adjustmentNeeded || setMinMaxZoom) {
                pixelSize = this.getZoom() * this.minTiledImageWidthScale;
                if (pixelSize > config.maxTiledImageStretch || setMinMaxZoom) { 
                    adjustmentNeeded = !setMinMaxZoom;
                    scale = maxZoomScale = config.maxTiledImageStretch / pixelSize;
                }
            }
            if (setMinMaxZoom) {
                this._minZoom = this.getZoom() * minZoomScale;
                this._maxZoom = this.getZoom() * maxZoomScale;
            }
            if (adjustmentNeeded) {
                this.zoomBy(scale, immediately, refPoint, true);
                vR = this.getRectangle();
            }
            if (setMinMaxZoom) { 
                return this;
            }
            vR.scaleAroundCenter(1 - 2 * config.marginFactor);
            var parameterPairs = [
                {start: 'x', length: 'width'},
                {start: 'y', length: 'height'},
            ];
            for (var i = 0; i < 2; i++) {
                var pair = parameterPairs[i];
                var start = pair.start;
                var length = pair.length;
                if (vR[length] > cR[length]) {
                    if (config.centerWhenZoomedOut && config.marginFactor === 0) {
                        adjustmentNeeded = true;
                        vR[start] = cR[start] + cR[length] / 2 - vR[length] / 2;
                    } else if (vR[start] > cR[start]) {
                        adjustmentNeeded = true;
                        vR[start] = cR[start];
                    } else if (vR[start] + vR[length] < cR[start] + cR[length]) {
                        adjustmentNeeded = true;
                        vR[start] = cR[start] + cR[length] - vR[length];
                    }
                } else if ((vR[start] < cR[start] &&
                    vR[start] + vR[length] < cR[start] + cR[length]) ||
                    (vR[start] > cR[start] &&
                        vR[start] + vR[length] > cR[start] + cR[length])) {
                    adjustmentNeeded = true;
                    var delta1 = Math.abs(cR[start] - vR[start]);
                    var delta2 = Math.abs(cR[start] + cR[length] - vR[start] - vR[length]);
                    var delta = Math.min(delta1, delta2);
                    if (vR[start] > cR[start]) { 
                        delta = -delta;
                    }
                    vR[start] += delta;
                }
            }
            if (adjustmentNeeded) {
                this.panTo(vR.getCenter(), immediately, {
                    dontApplyConstraints: true,
                    animationTimeConfigParameter: 'mouseAnimationTime',
                });
            }
            return this;
        }
function applyConstraints ( immediately , refPoint , setMinMaxZoom ) { if ( ! this . config . constrainViewport ) { return this ; } if ( ! ( this . constraintBounds instanceof Malakh . Rectangle ) ) { console . error ( 'Can\'t apply constraints because constraintBounds is not set.' ) ; return this ; } var scale , minZoomScale , maxZoomScale , pixelSize , adjustmentNeeded = false , whatToScale = 'height' , config = this . malakh . config , cR = this . constraintBounds , vR = this . getRectangle ( ) , viewportRatio = vR . getAspectRatio ( ) , constraintsRatio = cR . width / cR . height ; if ( viewportRatio < constraintsRatio ) { whatToScale = 'width' ; } if ( vR [ whatToScale ] * this . config . minVisibilityRatio > cR [ whatToScale ] || setMinMaxZoom ) { adjustmentNeeded = ! setMinMaxZoom ; scale = minZoomScale = vR [ whatToScale ] * this . config . minVisibilityRatio / cR [ whatToScale ] ; } if ( ! adjustmentNeeded || setMinMaxZoom ) { pixelSize = this . getZoom ( ) * this . minTiledImageWidthScale ; if ( pixelSize > config . maxTiledImageStretch || setMinMaxZoom ) { adjustmentNeeded = ! setMinMaxZoom ; scale = maxZoomScale = config . maxTiledImageStretch / pixelSize ; } } if ( setMinMaxZoom ) { this . _minZoom = this . getZoom ( ) * minZoomScale ; this . _maxZoom = this . getZoom ( ) * maxZoomScale ; } if ( adjustmentNeeded ) { this . zoomBy ( scale , immediately , refPoint , true ) ; vR = this . getRectangle ( ) ; } if ( setMinMaxZoom ) { return this ; } vR . scaleAroundCenter ( 1 - 2 * config . marginFactor ) ; var parameterPairs = [ { start : 'x' , length : 'width' } , { start : 'y' , length : 'height' } , ] ; for ( var i = 0 ; i < 2 ; i ++ ) { var pair = parameterPairs [ i ] ; var start = pair . start ; var length = pair . length ; if ( vR [ length ] > cR [ length ] ) { if ( config . centerWhenZoomedOut && config . marginFactor === 0 ) { adjustmentNeeded = true ; vR [ start ] = cR [ start ] + cR [ length ] / 2 - vR [ length ] / 2 ; } else if ( vR [ start ] > cR [ start ] ) { adjustmentNeeded = true ; vR [ start ] = cR [ start ] ; } else if ( vR [ start ] + vR [ length ] < cR [ start ] + cR [ length ] ) { adjustmentNeeded = true ; vR [ start ] = cR [ start ] + cR [ length ] - vR [ length ] ; } } else if ( ( vR [ start ] < cR [ start ] && vR [ start ] + vR [ length ] < cR [ start ] + cR [ length ] ) || ( vR [ start ] > cR [ start ] && vR [ start ] + vR [ length ] > cR [ start ] + cR [ length ] ) ) { adjustmentNeeded = true ; var delta1 = Math . abs ( cR [ start ] - vR [ start ] ) ; var delta2 = Math . abs ( cR [ start ] + cR [ length ] - vR [ start ] - vR [ length ] ) ; var delta = Math . min ( delta1 , delta2 ) ; if ( vR [ start ] > cR [ start ] ) { delta = - delta ; } vR [ start ] += delta ; } } if ( adjustmentNeeded ) { this . panTo ( vR . getCenter ( ) , immediately , { dontApplyConstraints : true , animationTimeConfigParameter : 'mouseAnimationTime' , } ) ; } return this ; }
<p>Moves the viewport so that the user doesn't go too far away.
<p > Moves the viewport so that the user doesn ' t go too far away .
/* boolean INTERNAL */ // TODO a Malakh field? // rectangle of constraints // viewport rectangle // Empty borders on top and bottom. // We will turn this case into the latter one. /// ZOOMING PART // Now we assume viewportRatio < constraintsRatio which means empty borders on sides. // Too small, we need to zoom in. // We check for `!adjustmentNeeded` just in case the image is so small it would fit in both scenarios; // we want to aviod flicker in some cases and we prefer zooming in too much than zooming out too much. // We've zoomed in too much // all data collected, we're not really applying constraints now /// PANNING PART // The viewport rectangle gets adjusted to take margins into account but only for panning, not zooming. // If the image is zoomed out so that its height/width is // smaller than constraints, center it. // marginFactor === 0 is required because otherwise implementation would be inconsistent. // Just don't allow going too far outside of the container from the start side... // ...and from the end side. // Too far on the left/top. // We need to choose the smaller delta so that we don't make // the image jump from side to side. // Restoring correct sign of delta. // 'mouseAnimationTime' is needed because correcting a long animation using a short one // causes a jumping effect (and very visible one at that).