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/SBogers47/leden.ehbocranendonck.nl/resources/assets/js/site/presenceManager.js
/**
 * Can do api calls to the backend to mark a user as present
 */
class PresenceManager {
    /**
     * The constructor.
     *
     * @param inputSelector (string) must reference an text input element
     * @param courseId (int) the id of the course a user could check-in to
     * @param apiUrl The base url to the api to manage user presence without trailing slash.
     * Must support the following additional path suffixes: check-in
     */
    constructor(inputSelector, apiUrl = 'presence')
    {
        //Create and initialize properties
        this.input = null; //The text input element
        this.apiUrl = apiUrl;
        this.eventMap = {};
        this.userTrackingLists = {};

        //Delegate control to specialist parts of the class
        this.validateInputSelectorAndSetInputAndCourseId(inputSelector);
        this.enableListeners();
        this.focusOnInput();
    }

    /**
     * Focusses the the cursor to the input field and puts the cursor in the ind
     */
    focusOnInput()
    {
        this.input.focus();
        if(this.input.value !== '') {
            let value = this.input.value;
            this.input.value = '';
            this.input.value = value; //set that value back. Cursor is at the end of the input
        }
    }

    /**
     * Validates the inout selector to make sure it references an text input
     */
    validateInputSelectorAndSetInputAndCourseId(selector)
    {
        let element = document.querySelector(selector);
        console.log(element);
        if(!element) {
            console.error('PresenceManager: The input selector parameter was invalid. It does not match a text input element');
            return
        }

        if(!element.dataset.courseId) {
            console.error('PresenceManager: The input selector parameter was invalid. It does not have a courseId data attribute');
            return
        }

        this.courseId = element.dataset.courseId;
        this.input = element
    }

    /**
     * @param enable
     */
    enableListeners(enable = true) {
        if (enable) {
            this.input.addEventListener('keypress', this.reactOnlyToKeyPressFromKeyCode(this.markUserAsPresentUsingCode.bind(this), 13)); //13 = Enter key
            this.input.addEventListener('focusout', this.focusOnInput.bind(this));
        }
    }

    /**
     * Returns a function for an event listener to call and lets that function call the callback when the enter key is pressed
     *
     * @param callback
     * @param reactToKeyCode The keyCode to react to (see: https://gist.github.com/tylerbuchea/8011573 for example)
     * @returns {Function}
     */
    reactOnlyToKeyPressFromKeyCode(callback, reactToKeyCode)
    {
        return function(event) {
            let keyCode = event.keyCode;
            if(keyCode === reactToKeyCode) callback();
        }
    }

    /**
     * Marks a user as present for the current course using a code from a badge
     */
    markUserAsPresentUsingCode()
    {
        let self = this;

        let userCode = self.input.value;

        if(userCode != "") {
            axios.post(this.apiUrl + '/check-in', {
                userCode: userCode,
                courseId: self.courseId
            })
                .then(function (response) {
                    self.input.value = '';
                    if (response.data.hasOwnProperty('user'))
                        self.userCheckedInSuccessFully(response.data.user);
                    else
                        self.apiErrorOccurred('User was checked in successfully. But user information was not returned properly.')
                })
                .catch(function (error) {
                    self.input.value = '';
                    switch (error.response.status) {
                        case 400:
                            self.userFailedToCheckIn(userCode, error.response.data);
                            break;
                        default:
                            self.apiErrorOccurred(error.response.statusText)
                    }
                });
        }
    }

    /**
     * Triggered in response to markUserAsPresentUsingCode
     */
    userCheckedInSuccessFully(user)
    {
        let audio = new Audio('/audio/success.mp3');
        audio.play();
        this.updateTrackingLists();

        this.dispatchEvent('check-in', [user]);
    }

    /**
     * Triggered in response to markUserAsPresentUsingCode
     */
    userFailedToCheckIn(userCode, error)
    {
        let audio = new Audio('/audio/error.mp3');
        audio.play();
        this.updateTrackingLists();

        this.dispatchEvent('check-in-fail', [error]);
    }

    /**
     * Triggered in response to markUserAsPresentUsingCode
     */
    apiErrorOccurred(error)
    {
        let audio = new Audio('/audio/error.mp3');
        audio.play();

        this.dispatchEvent('api-error', [error]);
    }

    /**
     * Registers a event to a callback and returns the a reference to the event handler for that specific event
     *
     * @param event (string)
     * @param callback (callable)
     */
    on(event, callback)
    {
        if(!this.eventMap.hasOwnProperty(event)) this.eventMap[event] = [];
        return this.eventMap[event].push(callback);
    }

    /**
     * Call event callbacks
     *
     * @param event (string)
     * @param eventArgs (array) an array of arguments to pass to the callback
     */
    dispatchEvent(event, eventArgs) {
        if(!this.eventMap.hasOwnProperty(event)) return;
        for (cb of this.eventMap[event])
        {
            if(eventArgs && eventArgs.length > 0) {
                cb.apply(this, eventArgs)
            } else {
                cb.call(this);
            }
        }
    }

    /**
     * Updates ul or ol elements registered with the trackCheckedInUsersInList method
     * with present and subscribed users
     */
    updateTrackingLists()
    {
        let self = this;

        if(self.userTrackingLists.hasOwnProperty('present') && self.userTrackingLists.present.length > 0) {
            this.getPresentUsers(function (users) {
                for (let listElement of self.userTrackingLists['present']) {
                    self.updateListElementWithUsers(listElement, users);
                }
            });
        }

        if(self.userTrackingLists.hasOwnProperty('subscribed') && self.userTrackingLists.subscribed.length > 0) {
            this.getSubscribedUsers(function (users) {
                for (let listElement of self.userTrackingLists['subscribed']) {
                    self.updateListElementWithUsers(listElement, users);
                }
            });
        }
    }

    /**
     * Updates a ul or ol element with li's that match the items in the user array which each have a first_name and last_name property
     *
     * @param listElement
     * @param users
     */
    updateListElementWithUsers(listElement, users)
    {
        console.log(listElement, users);
        while( listElement.firstChild ){
            listElement.removeChild( listElement.firstChild );
        }
        for(let user of users) {
            let userListItem = document.createElement('li');
            if(!user.hasOwnProperty('first_name') || !user.hasOwnProperty('last_name')) {
                console.error('Could not create an li element with user data.')
                continue;
            }
            userListItem.innerText = user.first_name+' '+user.last_name;
            listElement.append(userListItem);
        }
    }

    /**
     * @param callback a callback function that will retrieve an array of present users
     */
    getPresentUsers(callback)
    {
        this.getUsersForACertainState('present', callback);
    }

    /**
     * @param callback a callback function that will retrieve an array of subscribed users
     */
    getSubscribedUsers(callback)
    {
        this.getUsersForACertainState('subscribed', callback);
    }

    /**
     * @param state (string) 'present' or 'subscribed'
     * @param callback a callback function that will retrieve an array of users
     */
    getUsersForACertainState(state, callback)
    {
        let self = this;

        axios.get(this.apiUrl+'/tracking/'+state+'/'+self.courseId)
            .then(function (response) {
                if(response.data.hasOwnProperty('users'))
                    callback.call(this, response.data.users);
                else
                    self.apiErrorOccurred('Could not get the "'+state+'" users')
            })
            .catch(function (error) {
                console.log(error);
                switch (error.response.status)
                {
                    default:
                        self.apiErrorOccurred(error.response.statusText)
                }
            });
    }

    /**
     * Marks an ul or li that can be selected with the given selector so that it
     * automatically will be updated with the present users
     *
     * @param selector (string)
     */
    trackCheckedInUsersInList(selector)
    {
        let presentUsersList = document.querySelector(selector);
        if(presentUsersList) {
            if(!this.userTrackingLists.hasOwnProperty('present')) this.userTrackingLists['present'] = [];
            this.userTrackingLists['present'].push(presentUsersList);
        } else {
            console.error('The selector "'+selector+'" does not reference a ul or li and therefore it cannot be used to track the users that are checked-in for the current training');
        }

        this.updateTrackingLists();
    }

    /**
     * Marks an ul or li that can be selected with the given selector so that it
     * automatically will be updated with the subscribed users
     *
     * @param selector
     */
    trackSubscribedUsersInList(selector)
    {
        let subscribedUsersList = document.querySelector(selector);
        if(subscribedUsersList) {
            if(!this.userTrackingLists.hasOwnProperty('subscribed')) this.userTrackingLists['subscribed'] = [];
            this.userTrackingLists['subscribed'].push(subscribedUsersList);
        } else {
            console.error('The selector "'+selector+'" does not reference a ul or li and therefore it cannot be used to track the users that are signed up for the current training');
        }

        this.updateTrackingLists();
    }
}