HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/marisrental/boldt.tech/resources/js/kms/entity/preventNavigationController.js
/**
 * Tracks all inputs, selects and textareas in the given wrapper element for changes.
 * If the user clicked an button, submit input or an anchor the user fist must confirm that he's going to lose changes.
 */
class PreventNavigationController
{
    /**
     * inputs in the wrapper element will be tracked for changes
     *
     * @param WrapperElement {Element}
     */
    constructor(WrapperElement) {
        //Create properties
        this.inputs = []; //Inputs, textareas, selects
        this.wrapperElement = null;
        this.translation = {
            headerText: '',
            message: '',
            confirmText: '',
            cancelText: '',
        };
        this.changed = false; //Automatically set to true when one of the inputs was changed

        //validate stuff
        if(!WrapperElement) {
            console.warn('No wrapper element given. Not preventing navigation.');
            return;
        }
        if(!WrapperElement.dataset.translation) {
            console.warn('No translation present. Not preventing navigation when not all changes have been saved.');
            return;
        }
        this.wrapperElement = WrapperElement;

        //Delegate control to specialist parts of the class
        this.loadTranslation();
        this.inputs = this.findAllInputsIn(WrapperElement);
        this.interceptClicksOnNavigatableElements();
        this.addListenersToInputs(this.inputs)
    }

    /**
     * Returns true if the given input element is an HTMLElement of tag Input.
     *
     * @param InputElement {HTMLElement}
     * @returns {boolean}
     */
    isTrackableElement(InputElement) {
        return (InputElement.tagName === "INPUT" || InputElement.tagName === "SELECT" || InputElement.tagName === "TEXTAREA")
    }

    /**
     * Add listeners to inputs
     *
     * @param inputs {array}
     * @param add {boolean}
     */
    addListenersToInputs(inputs, add = true)
    {
        let nInputs = inputs.length;
        for(let currentInputNumber = 0; currentInputNumber < nInputs; currentInputNumber++)
        {
            if(add) {
                inputs[currentInputNumber].addEventListener('change', this.inputChanged.bind(this));
            } else {
                inputs[currentInputNumber].removeEventListener('change', this.inputChanged.bind(this));
            }
        }
    }

    /**
     * Triggered in response to a changed input
     *
     * @param event
     */
    inputChanged(event) {
        this.changed = true;
        console.log(this.changed);
    }


    /**
     * Loads the translations. They are defined on the wrapper element
     */
    loadTranslation()
    {
        this.translation = JSON.parse(this.wrapperElement.dataset.translation);
    }

    /**
     * Makes sure that clicks on links are intercepted and need to be confirmed first
     *
     * @param enable {boolean}
     */
    interceptClicksOnNavigatableElements(enable = true)
    {
        let links = document.getElementsByTagName('A');
        let nLinks = links.length;
        for(let linkNumber = 0; linkNumber < nLinks; linkNumber++)
        {
            let currentLink = links[linkNumber];

            let linkHref = currentLink.getAttribute('href');
            if(linkHref && linkHref.substr(0,1) !== "#") {
                new ConfirmationController(currentLink)
                    .setHeaderText(this.translation.headerText)
                    .setMessage(this.translation.message)
                    .setConfirmText(this.translation.confirmText)
                    .setCancelText(this.translation.cancelText)
                    .setConfirmCallback(function() {
                        window.location = linkHref;
                    })
                    .setOnlyConfirmIfTrueCallback((function (controller) {
                        return function()
                        {
                            return controller.hasChanged();
                        }
                    })(this))
            }
        }
    }

    /**
     * Returns true if one of the input has changed
     * @returns {boolean}
     */
    hasChanged()
    {
        return this.changed;
    }

    /**
     * Finds all html elements witch match elements specified in the isTrackableElement method
     *
     * @param WrapperElement {Element}
     * @param inputs
     * @returns {Array}
     */
    findAllInputsIn(WrapperElement, inputs = []) {
        let nChildren = WrapperElement.children.length;
        if(nChildren === 0) return inputs;

        for(let index = 0; index < nChildren; index++)
        {
            let currentChild = WrapperElement.children[index];
            if(this.isTrackableElement(currentChild)) {
                inputs.push(currentChild);
            }
            if(currentChild.children.length > 0) {
                let childInputs = this.findAllInputsIn(currentChild, inputs);
                inputs.concat(childInputs);
            }
        }

        return inputs;
    }
}