From d59f2022b9582a9a310c8a93eb3b4e0bbab097a5 Mon Sep 17 00:00:00 2001
From: toasted-nutbread <toasted-nutbread@users.noreply.github.com>
Date: Sat, 11 Jan 2020 14:21:10 -0500
Subject: Allow viewport to be used for constraining popup position

---
 ext/fg/js/popup.js | 114 +++++++++++++++++++++++++++++++++++------------------
 1 file changed, 75 insertions(+), 39 deletions(-)

(limited to 'ext/fg/js')

diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js
index 6c98491b..710d95cd 100644
--- a/ext/fg/js/popup.js
+++ b/ext/fg/js/popup.js
@@ -258,6 +258,7 @@ class Popup {
             Popup._getPositionForVerticalText
         );
 
+        const viewport = Popup._getViewport(optionsGeneral.popupScaleRelativeToVisualViewport);
         const scale = this._contentScale;
         const scaleRatio = this._containerSizeContentScale === null ? 1.0 : scale / this._containerSizeContentScale;
         this._containerSizeContentScale = scale;
@@ -265,8 +266,7 @@ class Popup {
             elementRect,
             Math.max(containerRect.width * scaleRatio, optionsGeneral.popupWidth * scale),
             Math.max(containerRect.height * scaleRatio, optionsGeneral.popupHeight * scale),
-            document.body.clientWidth,
-            window.innerHeight,
+            viewport,
             scale,
             optionsGeneral,
             writingMode
@@ -353,49 +353,49 @@ class Popup {
         }
     }
 
-    static _getPositionForHorizontalText(elementRect, width, height, maxWidth, maxHeight, offsetScale, optionsGeneral) {
-        let x = elementRect.left + optionsGeneral.popupHorizontalOffset * offsetScale;
-        const overflowX = Math.max(x + width - maxWidth, 0);
-        if (overflowX > 0) {
-            if (x >= overflowX) {
-                x -= overflowX;
-            } else {
-                width = maxWidth;
-                x = 0;
-            }
-        }
-
+    static _getPositionForHorizontalText(elementRect, width, height, viewport, offsetScale, optionsGeneral) {
         const preferBelow = (optionsGeneral.popupHorizontalTextPosition === 'below');
-
+        const horizontalOffset = optionsGeneral.popupHorizontalOffset * offsetScale;
         const verticalOffset = optionsGeneral.popupVerticalOffset * offsetScale;
-        const [y, h, below] = Popup._limitGeometry(
+
+        const [x, w] = Popup._getConstrainedPosition(
+            elementRect.right - horizontalOffset,
+            elementRect.left + horizontalOffset,
+            width,
+            viewport.left,
+            viewport.right,
+            true
+        );
+        const [y, h, below] = Popup._getConstrainedPositionBinary(
             elementRect.top - verticalOffset,
             elementRect.bottom + verticalOffset,
             height,
-            maxHeight,
+            viewport.top,
+            viewport.bottom,
             preferBelow
         );
-
-        return [x, y, width, h, below];
+        return [x, y, w, h, below];
     }
 
-    static _getPositionForVerticalText(elementRect, width, height, maxWidth, maxHeight, offsetScale, optionsGeneral, writingMode) {
+    static _getPositionForVerticalText(elementRect, width, height, viewport, offsetScale, optionsGeneral, writingMode) {
         const preferRight = Popup._isVerticalTextPopupOnRight(optionsGeneral.popupVerticalTextPosition, writingMode);
         const horizontalOffset = optionsGeneral.popupHorizontalOffset2 * offsetScale;
         const verticalOffset = optionsGeneral.popupVerticalOffset2 * offsetScale;
 
-        const [x, w] = Popup._limitGeometry(
+        const [x, w] = Popup._getConstrainedPositionBinary(
             elementRect.left - horizontalOffset,
             elementRect.right + horizontalOffset,
             width,
-            maxWidth,
+            viewport.left,
+            viewport.right,
             preferRight
         );
-        const [y, h, below] = Popup._limitGeometry(
+        const [y, h, below] = Popup._getConstrainedPosition(
             elementRect.bottom - verticalOffset,
             elementRect.top + verticalOffset,
             height,
-            maxHeight,
+            viewport.top,
+            viewport.bottom,
             true
         );
         return [x, y, w, h, below];
@@ -424,23 +424,36 @@ class Popup {
         }
     }
 
-    static _limitGeometry(positionBefore, positionAfter, size, limit, preferAfter) {
-        let after = preferAfter;
-        let position = 0;
-        const overflowBefore = Math.max(0, size - positionBefore);
-        const overflowAfter = Math.max(0, positionAfter + size - limit);
+    static _getConstrainedPosition(positionBefore, positionAfter, size, minLimit, maxLimit, after) {
+        size = Math.min(size, maxLimit - minLimit);
+
+        let position;
+        if (after) {
+            position = Math.max(minLimit, positionAfter);
+            position = position - Math.max(0, (position + size) - maxLimit);
+        } else {
+            position = Math.min(maxLimit, positionBefore) - size;
+            position = position + Math.max(0, minLimit - position);
+        }
+
+        return [position, size, after];
+    }
+
+    static _getConstrainedPositionBinary(positionBefore, positionAfter, size, minLimit, maxLimit, after) {
+        const overflowBefore = minLimit - (positionBefore - size);
+        const overflowAfter = (positionAfter + size) - maxLimit;
+
         if (overflowAfter > 0 || overflowBefore > 0) {
-            if (overflowAfter < overflowBefore) {
-                size = Math.max(0, size - overflowAfter);
-                position = positionAfter;
-                after = true;
-            } else {
-                size = Math.max(0, size - overflowBefore);
-                position = Math.max(0, positionBefore - size);
-                after = false;
-            }
+            after = (overflowAfter < overflowBefore);
+        }
+
+        let position;
+        if (after) {
+            size -= Math.max(0, overflowAfter);
+            position = Math.max(minLimit, positionAfter);
         } else {
-            position = preferAfter ? positionAfter : positionBefore - size;
+            size -= Math.max(0, overflowBefore);
+            position = Math.min(maxLimit, positionBefore) - size;
         }
 
         return [position, size, after];
@@ -479,6 +492,29 @@ class Popup {
             // NOP
         }
     }
+
+    static _getViewport(useVisualViewport) {
+        if (useVisualViewport) {
+            const visualViewport = window.visualViewport;
+            if (visualViewport !== null && typeof visualViewport === 'object') {
+                const left = visualViewport.offsetLeft;
+                const top = visualViewport.offsetTop;
+                return {
+                    left,
+                    top,
+                    right: left + visualViewport.width,
+                    bottom: top + visualViewport.height
+                };
+            }
+        }
+
+        return {
+            left: 0,
+            top: 0,
+            right: document.body.clientWidth,
+            bottom: window.innerHeight
+        };
+    }
 }
 
 Popup.outerStylesheet = null;
-- 
cgit v1.2.3