mirror of
				https://github.com/SillyTavern/SillyTavern.git
				synced 2025-06-05 21:59:27 +02:00 
			
		
		
		
	fix MovingUI infinite loop on resize to Y edge
This commit is contained in:
		@@ -437,7 +437,7 @@ const saveUserInputDebounced = debounce(saveUserInput);
 | 
			
		||||
// Make the DIV element draggable:
 | 
			
		||||
 | 
			
		||||
export function dragElement(elmnt) {
 | 
			
		||||
    var hasBeenDraggedByUser = false;
 | 
			
		||||
    var isHeaderBeingDragged = false;
 | 
			
		||||
    var isMouseDown = false;
 | 
			
		||||
 | 
			
		||||
    var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
 | 
			
		||||
@@ -448,16 +448,16 @@ export function dragElement(elmnt) {
 | 
			
		||||
    var elmntName = elmnt.attr('id');
 | 
			
		||||
    console.debug(`dragElement called for ${elmntName}`);
 | 
			
		||||
    const elmntNameEscaped = $.escapeSelector(elmntName);
 | 
			
		||||
    console.debug(`dragElement escaped name: ${elmntNameEscaped}`);
 | 
			
		||||
    const elmntHeader = $(`#${elmntNameEscaped}header`);
 | 
			
		||||
 | 
			
		||||
    if (elmntHeader.length) {
 | 
			
		||||
        elmntHeader.off('mousedown').on('mousedown', (e) => {
 | 
			
		||||
            hasBeenDraggedByUser = true;
 | 
			
		||||
        elmntHeader.off('mousedown').on('mousedown', (e) => { //listener for drag handle repositioning
 | 
			
		||||
            isHeaderBeingDragged = true;
 | 
			
		||||
            isMouseDown = true;
 | 
			
		||||
            observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] });
 | 
			
		||||
            dragMouseDown(e);
 | 
			
		||||
        });
 | 
			
		||||
        $(elmnt).off('mousedown').on('mousedown', () => {
 | 
			
		||||
        $(elmnt).off('mousedown').on('mousedown', () => { //listener for resize
 | 
			
		||||
            isMouseDown = true;
 | 
			
		||||
            observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] });
 | 
			
		||||
        });
 | 
			
		||||
@@ -465,20 +465,19 @@ export function dragElement(elmnt) {
 | 
			
		||||
 | 
			
		||||
    const observer = new MutationObserver((mutations) => {
 | 
			
		||||
        const target = mutations[0].target;
 | 
			
		||||
        if (!$(target).is(':visible')
 | 
			
		||||
            || $(target).hasClass('resizing')
 | 
			
		||||
            || Number((String(target.height).replace('px', ''))) < 50
 | 
			
		||||
            || Number((String(target.width).replace('px', ''))) < 50
 | 
			
		||||
            || power_user.movingUI === false
 | 
			
		||||
            || isMobile()
 | 
			
		||||
        if (!$(target).is(':visible') //abort if element is invisible
 | 
			
		||||
            || $(target).hasClass('resizing') //being auto-resized by other JS code
 | 
			
		||||
            || Number((String(target.height).replace('px', ''))) < 50 //too short
 | 
			
		||||
            || Number((String(target.width).replace('px', ''))) < 50 //too narrow
 | 
			
		||||
            || power_user.movingUI === false // if MUI is not turned on
 | 
			
		||||
            || isMobile() // if it's a mobile screen
 | 
			
		||||
        ) {
 | 
			
		||||
            console.debug('aborting mutator');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        //console.debug(left + width, winWidth, hasBeenDraggedByUser, isMouseDown)
 | 
			
		||||
        const style = getComputedStyle(target); //use computed values because not all CSS are set by default
 | 
			
		||||
        height = parseInt(style.height) //target.offsetHeight;
 | 
			
		||||
        width = parseInt(style.width) //target.offsetWidth;
 | 
			
		||||
 | 
			
		||||
        const style = getComputedStyle(target);
 | 
			
		||||
        height = parseInt(style.height)
 | 
			
		||||
        width = parseInt(style.width)
 | 
			
		||||
        top = parseInt(style.top);
 | 
			
		||||
        left = parseInt(style.left);
 | 
			
		||||
        right = parseInt(style.right);
 | 
			
		||||
@@ -492,20 +491,6 @@ export function dragElement(elmnt) {
 | 
			
		||||
        const topbarstyle = getComputedStyle(topbar);
 | 
			
		||||
        topBarFirstX = parseInt(topbarstyle.marginInline);
 | 
			
		||||
        topBarLastY = parseInt(topbarstyle.height);
 | 
			
		||||
        /*
 | 
			
		||||
                console.log(`
 | 
			
		||||
                Observer
 | 
			
		||||
                 winWidth: ${winWidth}, winHeight: ${winHeight}
 | 
			
		||||
                 sheldWidth: sheldWidth
 | 
			
		||||
                 X: ${$(elmnt).css('left')}
 | 
			
		||||
                 Y: ${$(elmnt).css('top')}
 | 
			
		||||
                 MaxX: ${maxX}, MaxY: ${maxY}
 | 
			
		||||
                 height: ${height}
 | 
			
		||||
                 width: ${width}
 | 
			
		||||
                 Topbar 1st X: ${topBarFirstX}
 | 
			
		||||
                 TopBar lastX: topBarLastX
 | 
			
		||||
                 `);
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        //prepare an empty poweruser object for the item being altered if we don't have one already
 | 
			
		||||
        if (!power_user.movingUIState[elmntName]) {
 | 
			
		||||
@@ -513,48 +498,47 @@ export function dragElement(elmnt) {
 | 
			
		||||
            power_user.movingUIState[elmntName] = {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //only record position changes if caused by a user click-drag
 | 
			
		||||
        if (hasBeenDraggedByUser && isMouseDown) {
 | 
			
		||||
            power_user.movingUIState[elmntName].top = top;
 | 
			
		||||
            power_user.movingUIState[elmntName].left = left;
 | 
			
		||||
            power_user.movingUIState[elmntName].right = right;
 | 
			
		||||
            power_user.movingUIState[elmntName].bottom = bottom;
 | 
			
		||||
            power_user.movingUIState[elmntName].margin = 'unset';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //handle resizing
 | 
			
		||||
        if (!hasBeenDraggedByUser && isMouseDown) { //if user is dragging the resize handle (not in header)
 | 
			
		||||
            //console.debug('saw resize, NOT header drag');
 | 
			
		||||
        if (!isHeaderBeingDragged && isMouseDown) { //if user is dragging the resize handle (not in header)
 | 
			
		||||
            let imgHeight, imgWidth, imageAspectRatio;
 | 
			
		||||
            let containerAspectRatio = elmnt.height() / elmnt.width();
 | 
			
		||||
            let containerAspectRatio = height / width;
 | 
			
		||||
 | 
			
		||||
            //force the zoomed avatar container to always be the same aspect ratio as the inner image
 | 
			
		||||
            //force aspect ratio for zoomed avatars
 | 
			
		||||
            if ($(elmnt).attr('id').startsWith('zoomFor_')) {
 | 
			
		||||
                let zoomedAvatarImage = $(elmnt).find('.zoomed_avatar_img');
 | 
			
		||||
                imgHeight = zoomedAvatarImage.height();
 | 
			
		||||
                imgWidth = zoomedAvatarImage.width();
 | 
			
		||||
                imageAspectRatio = imgHeight / imgWidth;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (containerAspectRatio !== imageAspectRatio) {
 | 
			
		||||
                elmnt.css('height', imgHeight);
 | 
			
		||||
            }
 | 
			
		||||
                // Maintain aspect ratio
 | 
			
		||||
                if (containerAspectRatio !== imageAspectRatio) {
 | 
			
		||||
                    elmnt.css('width', elmnt.width());
 | 
			
		||||
                    elmnt.css('height', elmnt.width() * imageAspectRatio);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            //prevent resizing offscreen
 | 
			
		||||
            if (top + elmnt.height() >= winHeight) {
 | 
			
		||||
                //console.debug('resizing height to prevent offscreen');
 | 
			
		||||
                elmnt.css('height', winHeight - top - 1 + 'px');
 | 
			
		||||
            }
 | 
			
		||||
                // Prevent resizing offscreen
 | 
			
		||||
                if (top + elmnt.height() >= winHeight) {
 | 
			
		||||
                    elmnt.css('height', winHeight - top - 1 + 'px');
 | 
			
		||||
                    elmnt.css('width', (winHeight - top - 1) / imageAspectRatio + 'px');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            if (left + elmnt.width() >= winWidth) {
 | 
			
		||||
                //console.debug('resizing width to prevent offscreen');
 | 
			
		||||
                elmnt.css('width', winWidth - left - 1 + 'px');
 | 
			
		||||
                if (left + elmnt.width() >= winWidth) {
 | 
			
		||||
                    elmnt.css('width', winWidth - left - 1 + 'px');
 | 
			
		||||
                    elmnt.css('height', (winWidth - left - 1) * imageAspectRatio + 'px');
 | 
			
		||||
                }
 | 
			
		||||
            } else { //prevent divs that are not zoomedAvatars from resizing offscreen
 | 
			
		||||
 | 
			
		||||
                if (top + elmnt.height() >= winHeight) {
 | 
			
		||||
                    elmnt.css('height', winHeight - top - 1 + 'px');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (left + elmnt.width() >= winWidth) {
 | 
			
		||||
                    elmnt.css('width', winWidth - left - 1 + 'px');
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //prevent resizing from top left into the top bar
 | 
			
		||||
            if (top < topBarLastY && maxX >= topBarFirstX && left <= topBarFirstX
 | 
			
		||||
            ) {
 | 
			
		||||
                //console.debug('prevent topbar underlap resize');
 | 
			
		||||
            if (top < topBarLastY && maxX >= topBarFirstX && left <= topBarFirstX) {
 | 
			
		||||
                elmnt.css('width', width - 1 + 'px');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -563,11 +547,11 @@ export function dragElement(elmnt) {
 | 
			
		||||
            elmnt.css('top', top);
 | 
			
		||||
 | 
			
		||||
            //set a listener for mouseup to save new width/height
 | 
			
		||||
            elmnt.off('mouseup').on('mouseup', () => {
 | 
			
		||||
                console.debug(`Saving ${elmntName} Height/Width`);
 | 
			
		||||
            $(window).off('mouseup').on('mouseup', () => {
 | 
			
		||||
                console.log(`Saving ${elmntName} Height/Width`);
 | 
			
		||||
                // check if the height or width actually changed
 | 
			
		||||
                if (power_user.movingUIState[elmntName].width === width && power_user.movingUIState[elmntName].height === height) {
 | 
			
		||||
                    console.debug('no change detected, aborting save');
 | 
			
		||||
                if (power_user.movingUIState[elmntName].width === elmnt.width() && power_user.movingUIState[elmntName].height === elmnt.height()) {
 | 
			
		||||
                    console.log('no change detected, aborting save');
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -575,12 +559,26 @@ export function dragElement(elmnt) {
 | 
			
		||||
                power_user.movingUIState[elmntName].height = height;
 | 
			
		||||
                eventSource.emit('resizeUI', elmntName);
 | 
			
		||||
                saveSettingsDebounced();
 | 
			
		||||
                imgHeight = null;
 | 
			
		||||
                imgWidth = null;
 | 
			
		||||
                height = null;
 | 
			
		||||
                width = null;
 | 
			
		||||
 | 
			
		||||
                containerAspectRatio = null;
 | 
			
		||||
                imageAspectRatio = null;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //handle dragging hit detection
 | 
			
		||||
        if (hasBeenDraggedByUser && isMouseDown) {
 | 
			
		||||
            //prevent dragging offscreen
 | 
			
		||||
        //only record position changes if header is being dragged
 | 
			
		||||
        power_user.movingUIState[elmntName].top = top;
 | 
			
		||||
        power_user.movingUIState[elmntName].left = left;
 | 
			
		||||
        power_user.movingUIState[elmntName].right = right;
 | 
			
		||||
        power_user.movingUIState[elmntName].bottom = bottom;
 | 
			
		||||
        power_user.movingUIState[elmntName].margin = 'unset';
 | 
			
		||||
 | 
			
		||||
        //handle dragging hit detection to prevent dragging offscreen
 | 
			
		||||
        if (isHeaderBeingDragged && isMouseDown) {
 | 
			
		||||
 | 
			
		||||
            if (top <= 0) {
 | 
			
		||||
                elmnt.css('top', '0px');
 | 
			
		||||
            } else if (maxY >= winHeight) {
 | 
			
		||||
@@ -592,27 +590,14 @@ export function dragElement(elmnt) {
 | 
			
		||||
            } else if (maxX >= winWidth) {
 | 
			
		||||
                elmnt.css('left', winWidth - maxX + left - 1 + 'px');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //prevent underlap with topbar div
 | 
			
		||||
            /*
 | 
			
		||||
            if (top < topBarLastY
 | 
			
		||||
                && (maxX >= topBarFirstX && left <= topBarFirstX //elmnt is hitting topbar from left side
 | 
			
		||||
                    || left <= topBarLastX && maxX >= topBarLastX //elmnt is hitting topbar from right side
 | 
			
		||||
                    || left >= topBarFirstX && maxX <= topBarLastX) //elmnt hitting topbar in the middle
 | 
			
		||||
            ) {
 | 
			
		||||
                console.debug('topbar hit')
 | 
			
		||||
                elmnt.css('top', top + 1 + "px");
 | 
			
		||||
            }
 | 
			
		||||
            */
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check if the element header exists and set the listener on the grabber
 | 
			
		||||
        // Check if the element header exists and set the reposition listener on the grabber in the header
 | 
			
		||||
        if (elmntHeader.length) {
 | 
			
		||||
            elmntHeader.off('mousedown').on('mousedown', (e) => {
 | 
			
		||||
                console.debug('listener started from header');
 | 
			
		||||
                dragMouseDown(e);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
        } else { //if no header, put the listener on the elmnt itself.
 | 
			
		||||
            elmnt.off('mousedown').on('mousedown', dragMouseDown);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
@@ -620,7 +605,7 @@ export function dragElement(elmnt) {
 | 
			
		||||
    function dragMouseDown(e) {
 | 
			
		||||
 | 
			
		||||
        if (e) {
 | 
			
		||||
            hasBeenDraggedByUser = true;
 | 
			
		||||
            isHeaderBeingDragged = true;
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            pos3 = e.clientX; //mouse X at click
 | 
			
		||||
            pos4 = e.clientY; //mouse Y at click
 | 
			
		||||
@@ -652,35 +637,20 @@ export function dragElement(elmnt) {
 | 
			
		||||
        elmnt.css('margin', 'unset');
 | 
			
		||||
        elmnt.css('left', (elmnt.offset().left - pos1) + 'px');
 | 
			
		||||
        elmnt.css('top', (elmnt.offset().top - pos2) + 'px');
 | 
			
		||||
        elmnt.css('right', ((winWidth - maxX) + 'px'));
 | 
			
		||||
        elmnt.css('bottom', ((winHeight - maxY) + 'px'));
 | 
			
		||||
        /*         elmnt.css('right', ((winWidth - maxX) + 'px'));
 | 
			
		||||
                elmnt.css('bottom', ((winHeight - maxY) + 'px')); */
 | 
			
		||||
 | 
			
		||||
        // Height/Width here are for visuals only, and are not saved to settings
 | 
			
		||||
        // required because some divs do hot have a set width/height..
 | 
			
		||||
        // and will defaults to shrink to min value of 100px set in CSS file
 | 
			
		||||
        // Height/Width here are for visuals only, and are not saved to settings.
 | 
			
		||||
        // This is required because some divs do hot have a set width/height
 | 
			
		||||
        // and will default to shrink to min value of 100px set in CSS file
 | 
			
		||||
        elmnt.css('height', height);
 | 
			
		||||
        elmnt.css('width', width);
 | 
			
		||||
 | 
			
		||||
        /*console.log(`
 | 
			
		||||
elementDrag:
 | 
			
		||||
winWidth: ${winWidth}, winHeight: ${winHeight}
 | 
			
		||||
sheldWidth: sheldWidth
 | 
			
		||||
X: ${$(elmnt).css('left')}
 | 
			
		||||
Y: ${$(elmnt).css('top')}
 | 
			
		||||
MaxX: ${maxX}, MaxY: ${maxY}
 | 
			
		||||
height: ${height}
 | 
			
		||||
width: ${width}
 | 
			
		||||
Topbar 1st X: ${topBarFirstX}
 | 
			
		||||
TopBar lastX: topBarLastX
 | 
			
		||||
`);*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function closeDragElement() {
 | 
			
		||||
        console.debug('drag finished');
 | 
			
		||||
        hasBeenDraggedByUser = false;
 | 
			
		||||
        isHeaderBeingDragged = false;
 | 
			
		||||
        isMouseDown = false;
 | 
			
		||||
        $(document).off('mouseup', closeDragElement);
 | 
			
		||||
        $(document).off('mousemove', elementDrag);
 | 
			
		||||
@@ -690,6 +660,12 @@ TopBar lastX: topBarLastX
 | 
			
		||||
        observer.disconnect();
 | 
			
		||||
        console.debug(`Saving ${elmntName} UI position`);
 | 
			
		||||
        saveSettingsDebounced();
 | 
			
		||||
        top = null;
 | 
			
		||||
        left = null;
 | 
			
		||||
        right = null;
 | 
			
		||||
        bottom = null;
 | 
			
		||||
        maxX = null;
 | 
			
		||||
        maxY = null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user