PMA.UI Documentation by Pathomation

view/controls/rotationControl.js

import { Control } from "ol/control";
import { Viewport } from "../viewport";

/**
 * Displays an interface that allows the user to rotate the viewport
 * @param {object} opt_options Options to initialize the rotation control
 * @param {Viewport} opt_options.pmaViewport Viewport instance this control belongs to
 * @param {string} [opt_options.resetTipLabel] Label for reset rotation button
 * @param {string} [opt_options.flipHorizontallyTipLabel] Label for horizontal flip button
 * @param {string} [opt_options.flipVerticallyTipLabel] Label for vertical flip button
 * @param {boolean} [opt_options.collapsed] Whether the control starts collapsed
 */
export class RotationControl extends Control {
    constructor(opt_options) {
        let options = opt_options || {};
        options.collapsed = options.collapsed === true;

        let tipLabel;
        let label;
        let label_;

        label_ = document.createElement("span");
        if (options.collapsed) {
            label_.innerHTML = "»";
        }
        else {
            label_.innerHTML = "«";
        }

        let collapseButton = document.createElement("button");
        collapseButton.type = "button";
        collapseButton.title = "Viewport rotation";
        collapseButton.appendChild(label_);

        let element = document.createElement("div");
        element.className = "ol-rotation ol-control ol-unselectable";

        let degree = document.createElement("span");
        degree.innerHTML = "&#176";
        degree.className = "ol-degree";

        let rotationText = document.createElement("input");
        rotationText.id = "rotationText";
        rotationText.type = "text";
        rotationText.value = 0;
        rotationText.maxLength = 5;
        rotationText.size = 1;

        let rotationSlider = document.createElement("input");
        rotationSlider.title = "Drag to rotate the image (Alt+Drag)";
        rotationSlider.id = "rotation";
        rotationSlider.type = "range";
        rotationSlider.min = -180;
        rotationSlider.max = 180;
        rotationSlider.value = 0;
        rotationSlider.step = 1;

        tipLabel = options.resetTipLabel
            ? options.resetTipLabel
            : "Reset rotation";
        label = '<i class="fa fa-long-arrow-up" aria-hidden="true"></i>';
        label_ = document.createElement("span");
        label_.innerHTML = label;

        let resetButton = document.createElement("button");
        resetButton.type = "button";
        resetButton.title = tipLabel;
        resetButton.appendChild(label_);

        tipLabel = options.flipHorizontallyTipLabel
            ? options.flipHorizontallyTipLabel
            : "Flip horizontally";
        label = '<i class="fa fa-code" aria-hidden="true"></i>';
        label_ = document.createElement("span");
        label_.innerHTML = label;

        let flipHorizontalButton = document.createElement("button");
        flipHorizontalButton.type = "button";
        flipHorizontalButton.title = tipLabel;
        flipHorizontalButton.appendChild(label_);

        tipLabel = options.flipVerticallyTipLabel
            ? options.flipVerticallyTipLabel
            : "Flip vertically";
        label = '<i class="fa fa-code fa-rotate-90" aria-hidden="true"></i>';
        label_ = document.createElement("span");
        label_.innerHTML = label;

        let flipVerticalButton = document.createElement("button");
        flipVerticalButton.type = "button";
        flipVerticalButton.title = tipLabel;
        flipVerticalButton.appendChild(label_);

        if (!options.collapsed) {
            element.appendChild(rotationText);
            element.appendChild(degree);
            element.appendChild(rotationSlider);
            element.appendChild(resetButton);
            element.appendChild(flipHorizontalButton);
            element.appendChild(flipVerticalButton);
        }

        element.appendChild(collapseButton);

        super({
            element: element,
            target: options.target,
        });

        this.collapsed = options.collapsed;
        let localChange = false;

        const handleInput = function (e) {
            localChange = true;
            e.preventDefault();
            e.stopPropagation();

            let t = rotationText.value.replace(/[^\d-]/g, "");
            if (t == "-") {
                return;
            }
            if (t == "") {
                t = 0;
            }
            let v = parseInt(t);
            var da = v % 360;
            v = ((2 * da) % 360) - da;

            rotationText.value = v;
            rotationSlider.value = v;
            resetButton.firstChild.firstChild.style = `transform: rotate(${v}deg)`;
            this.pmaViewport.setPosition({ rotation: v * 0.0174532925 });
            localChange = false;
        };

        const handleRotation = function (e) {
            if (localChange) {
                return;
            }
            let v = Math.floor(
                this.pmaViewport.getPosition().rotation / 0.0174532925
            );
            var da = v % 360;
            v = ((2 * da) % 360) - da;

            rotationText.value = v;
            rotationSlider.value = v;
            resetButton.firstChild.firstChild.style = `transform: rotate(${v}deg)`;
            if (this.pmaViewport.getFlip().horizontally) {
                flipHorizontalButton.classList.add("selected");
            } else {
                flipHorizontalButton.classList.remove("selected");
            }
            if (this.pmaViewport.getFlip().vertically) {
                flipVerticalButton.classList.add("selected");
            }
            else {
                flipVerticalButton.classList.remove("selected");
            }
        };

        const handleReset = function (e) {
            e.preventDefault();
            e.stopPropagation();

            rotationText.value = 0;
            rotationSlider.value = 0;
            resetButton.firstChild.firstChild.style = `transform: rotate(0deg)`;
            this.pmaViewport.setPosition({ rotation: 0 });
            resetButton.blur();
            resetButton.hideFocus = true;
            resetButton.style.outline = "none";
        };

        const handleFlipHorizontally = function (e) {
            e.preventDefault();
            e.stopPropagation();

            this.pmaViewport.setFlip(
                !this.pmaViewport.getFlip().horizontally,
                this.pmaViewport.getFlip().vertically
            );
            if (this.pmaViewport.getFlip().horizontally) {
                flipHorizontalButton.classList.add("selected");
            } else {
                flipHorizontalButton.classList.remove("selected");
            }
            flipHorizontalButton.blur();
            flipHorizontalButton.hideFocus = true;
            flipHorizontalButton.style.outline = "none";
        };

        const handleFlipVertically = function (e) {
            e.preventDefault();
            e.stopPropagation();

            this.pmaViewport.setFlip(
                this.pmaViewport.getFlip().horizontally,
                !this.pmaViewport.getFlip().vertically
            );

            if (this.pmaViewport.getFlip().vertically) {
                flipVerticalButton.classList.add("selected");
            }
            else {
                flipVerticalButton.classList.remove("selected");
            }

            flipVerticalButton.blur();
            flipVerticalButton.hideFocus = true;
            flipVerticalButton.style.outline = "none";
        };

        this.collapseControl = function (e) {
            if (e) {
                e.preventDefault();
                e.stopPropagation();
            }

            this.collapsed = !this.collapsed;
            if (this.collapsed) {
                element.removeChild(flipVerticalButton);
                element.removeChild(flipHorizontalButton);
                element.removeChild(resetButton);
                element.removeChild(rotationSlider);
                element.removeChild(degree);
                element.removeChild(rotationText);
                collapseButton.firstChild.innerHTML = "»";
            }
            else {
                element.insertBefore(flipVerticalButton, element.firstChild);
                element.insertBefore(flipHorizontalButton, element.firstChild);
                element.insertBefore(resetButton, element.firstChild);
                element.insertBefore(rotationSlider, element.firstChild);
                element.insertBefore(degree, element.firstChild);
                element.insertBefore(rotationText, element.firstChild);
                collapseButton.firstChild.innerHTML = "«";
            }
        };

        let handleChange = function (e) {
            localChange = true;
            e.preventDefault();
            e.stopPropagation();

            rotationText.value = rotationSlider.value;
            resetButton.firstChild.firstChild.style = `transform: rotate(${rotationSlider.value}deg)`;
            this.pmaViewport.setPosition({
                rotation: rotationSlider.value * 0.0174532925,
            });

            localChange = false;
        };

        this.pmaViewport = options.pmaViewport;

        rotationSlider.addEventListener(
            "input",
            handleChange.bind(this),
            false
        );

        resetButton.addEventListener("click", handleReset.bind(this), false);
        collapseButton.addEventListener("click", this.collapseControl.bind(this), false);
        flipHorizontalButton.addEventListener(
            "click",
            handleFlipHorizontally.bind(this),
            false
        );

        flipVerticalButton.addEventListener(
            "click",
            handleFlipVertically.bind(this),
            false
        );

        rotationText.addEventListener("input", handleInput.bind(this), false);
        this.pmaViewport.listen("viewchanged", handleRotation.bind(this));
    }

    /**
     * Sets the OpenLayers map this control handles. This is automatically called by OpenLayers
     * @param {ol.Map} map 
     */
    setMap(map) {
        super.setMap(map);
    }

    /**
     * Gets the collapsed state of the control
     * @return {boolean} True if the control is currently collapsed
     */
    getCollapsed() {
        return this.collapsed;
    }

    /**
     * Sets the collapsed state of the control
     * @param {boolean} collapsed - True to collapse the control, otherwise false
     */
    setCollapsed(collapsed) {
        if (this.getCollapsed() != collapsed) {
            this.collapseControl();
        }
    }
}