function ZoomImage(elements) {
    let backdrop;
    let imageElement;
    let imageMoving = false;
    let zoomed = false;

    const padding = 60;
    let initialScale = 1;
    let currentScale = initialScale;
    let dblClickScale = 4;
    let zoomFactor = 0.5;
    let maxZoom = 8;
    let initialPos;
    let lastY, lastX, lastDistance;

    for (let i = 0; i < elements.length; i++) {
        elements[i].addEventListener('click', onClickEvent);
    }

    function onClickEvent(event) {
        event.preventDefault();
        event.stopPropagation();
        createAndAddBackdrop();
        createAndAddImage(event.target.currentSrc);
    }

    function closeZoom() {
        document.getElementsByTagName('html')[0].classList.remove('no-overflow');
        document.getElementsByTagName('body')[0].removeChild(backdrop);
    }

    function createAndAddBackdrop() {
        // backdrop will be added to body
        backdrop = document.createElement('div');
        // we add the class for style override purpose
        backdrop.classList.add('imgzoom-backdrop');

        // backdrop style
        backdrop.style.position = 'fixed';
        backdrop.style.top = '0';
        backdrop.style.left = '0';
        backdrop.style.width = '100vw';
        backdrop.style.height = '100vh';
        backdrop.style.zIndex = '1040';
        backdrop.style.backgroundColor = 'rgba(0,0,0,0.8)';
        backdrop.style.display = 'flex';
        backdrop.style.alignItems = 'center';
        backdrop.style.justifyContent = 'center';
        backdrop.style.overflow = 'auto';
        backdrop.addEventListener('click', closeZoom);

        let body = document.getElementsByTagName('body')[0];
        // append the backdrop to body
        body.appendChild(backdrop);
        // block scroll for html
        document.getElementsByTagName('html')[0].classList.add('no-overflow');
    }

    function createAndAddImage(src) {
        let windowWidth = window.innerWidth - padding;
        let windowHeight = window.innerHeight - padding;

        imageElement = document.createElement('img');
        imageElement.src = src;
        imageElement.style.transition = 'all 300ms';
        imageElement.style.flexGrow = '0';
        imageElement.style.flexShrink = '0';
        imageElement.addEventListener('load', function () {
            let imageWidth = imageElement.naturalWidth;
            let scaledHeight = imageElement.naturalHeight;
            // image was loaded, calculate the scale and add the sizes

            // if the natural width and height are bigger than the screen we scale down the element
            if (imageWidth > windowWidth) {
                // we need to scale the image
                initialScale = windowWidth / imageWidth;
                scaledHeight = scaledHeight * initialScale;
            }

            if (scaledHeight > windowHeight) {
                initialScale = windowHeight / scaledHeight;
            }

            currentScale = initialScale;
            imageElement.style.transform = 'scale(' + initialScale + ')';
        });

        imageElement.addEventListener('click', function (event) {
            event.stopPropagation();
            event.preventDefault();
            closeZoom();
        });

        imageElement.addEventListener('dblclick', function () {
            if (!zoomed) {
                zoomed = true;
                currentScale = dblClickScale;
                imageElement.style.transform = 'scale(' + dblClickScale + ')';
            } else {
                zoomed = false;
                // scale to initial scale
                currentScale = initialScale;
                imageElement.style.transform = 'scale(' + initialScale + ')';
                imageElement.style.marginLeft = '0';
                imageElement.style.marginTop = '0';
            }
        });

        // imageElement.addEventListener('wheel', function (event) {
        //     event.stopPropagation();
        //     stepZoom(event.deltaY < 0);
        // }, {capture: true, passive: true});

        // mouse events
        imageElement.addEventListener("mouseup", zoomLeave);
        imageElement.addEventListener('mouseleave', zoomLeave);
        imageElement.addEventListener("mousemove", imageMove);
        imageElement.addEventListener("mousedown", zoomEnter);

        // touch events
        imageElement.addEventListener("touchend", zoomLeave);
        imageElement.addEventListener("touchstart", zoomEnter);
        imageElement.addEventListener("touchmove", function (event) {
            event.preventDefault();
            event.stopPropagation();

            // this is a pinch
            if (event.touches.length === 2) {
                let distance = Math.hypot((event.touches[0].pageX - event.touches[1].pageX),
                    (event.touches[0].pageY - event.touches[1].pageY));

                if (!lastDistance) {
                    lastDistance = distance
                }
                if (Math.abs(distance - lastDistance) > 20) {
                    if (lastDistance < distance) {
                        stepZoom(true);
                    } else {
                        stepZoom(false)
                    }
                    lastDistance = distance;
                }
            } else {
                // simple move
                imageMove(event);
            }
        });

        backdrop.appendChild(imageElement);
    }

    function zoomLeave(event) {
        event.stopPropagation();
        event.preventDefault();

        imageMoving = false;
        lastX = undefined;
        lastY = undefined;
        lastDistance = undefined;
        imageElement.style.transition = 'all 300ms';
    }

    function zoomEnter(event) {
        event.stopPropagation();
        event.preventDefault();

        // we can move the image only if the height or width is larger than the screen
        if ((event.target.width * currentScale) > window.innerWidth ||
            (event.target.height * currentScale) > window.innerHeight) {
            imageMoving = true;
            imageElement.style.transition = 'none';
        }
        if (imageMoving) {
            let initialMarginLeft = Number((imageElement.style.marginLeft || '0px').replace('px', ''));
            let initialMarginTop = Number((imageElement.style.marginTop || '0px').replace('px', ''));

            const clientX = event.clientX || event.touches[0].clientX;
            const clientY = event.clientY || event.touches[0].clientY;

            initialPos = {
                x: clientX,
                y: clientY,
                marginLeft: initialMarginLeft,
                marginTop: initialMarginTop
            };
        }
    }

    function stepZoom(zoomIn) {
        let zScale;
        if (zoomIn) {
            // zoom in
            zScale = currentScale + zoomFactor;

            if (zScale > maxZoom) {
                zScale = maxZoom;
            }
        } else {
            // zoom out
            zScale = currentScale - zoomFactor;
            if (zScale < initialScale) {
                zScale = initialScale;

                imageElement.style.marginLeft = '0';
                imageElement.style.marginTop = '0';
            } else {
                if (currentScale > initialScale) {
                    let initialMarginLeft = Number((imageElement.style.marginLeft || '0px').replace('px', ''));
                    let initialMarginTop = Number((imageElement.style.marginTop || '0px').replace('px', ''));
                    let stepPerZoom = currentScale - initialScale / zoomFactor;
                    let stepLeft = initialMarginLeft / stepPerZoom;
                    let stepTop = initialMarginTop / stepPerZoom;

                    imageElement.style.marginLeft = initialMarginLeft - stepLeft + 'px';
                    imageElement.style.marginTop = initialMarginTop - stepTop + 'px';
                }
            }
        }

        zoomed = zScale > initialScale;
        imageElement.style.transform = 'scale(' + zScale + ')';
        currentScale = zScale;
    }

    function imageMove(event) {
        event.preventDefault();
        event.stopPropagation();
        if (!imageMoving) {
            return;
        }

        const clientX = event.clientX || event.touches[0].clientX;
        const clientY = event.clientY || event.touches[0].clientY;

        // take care of mouse or touch move
        let trX = clientX - initialPos.x;
        let trY = clientY - initialPos.y;

        let rect = imageElement.getBoundingClientRect();

        if (!lastX) {
            lastX = initialPos.marginLeft + trX;
        }

        if (!lastY) {
            lastY = initialPos.marginTop + trY;
        }

        if (lastX > initialPos.marginLeft + trX) {
            // we scroll to the left
            if (rect.right > window.innerWidth) {
                imageElement.style.marginLeft = initialPos.marginLeft + trX + 'px';
                lastX = initialPos.marginLeft + trX;
            }
        } else {
            // we scroll to the right
            if (rect.left < 0) {
                imageElement.style.marginLeft = initialPos.marginLeft + trX + 'px';
                lastX = initialPos.marginLeft + trX;
            }
        }

        if (lastY > initialPos.marginTop + trY) {
            // we scroll up
            if (rect.bottom > window.innerHeight) {
                imageElement.style.marginTop = initialPos.marginTop + trY + 'px';
                lastY = initialPos.marginTop + trY;
            }
        } else {
            // we scroll down
            if (rect.top < 0) {
                imageElement.style.marginTop = initialPos.marginTop + trY + 'px';
                lastY = initialPos.marginTop + trY;
            }
        }
    }
}
