nus-mtp/attack-on-tutor

View on GitHub
public/javascripts/Lobby/lobby-angular-chat-controller.js

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * Controller for the chat functionalities within the lobby.
 * Front-end javascript code inside public folder.
 *
 * @module javascripts/lobby/lobby-angular-chat-controller
 */
angular.module('lobbyApp').controller ('chatCtrl', function ($scope, $window, socket) {
    $scope.socket = socket;

    $scope.chatMessage = "";
    $scope.selectedGroup = 0;
    $scope.defaultGroup = "";
    $scope.messages = {};
    $scope.typingMessages = [];
    
    //Fade timer for the typing messages.
    var FADE_TIME = 150; //ms
    var TYPING_TIMER_LENGTH = 400; //ms

    //Colors for the usernames.
    var COLORS = [
        '#e21400', '#91580f', '#f8a700', '#f78b00',
        '#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
        '#3b88eb', '#3824aa', '#a700ff', '#d300e7'
    ];
    var lastTypingTime;
    
    var typing = false;
    
    var username = $window.userId;

    /*
     *   Listeners for chat client.
     */
    
    //Whenever the server emits 'login', log the login message.
    socket.on ('login', function (data) {
        //Display the welcome message
        $scope.defaultGroup = data.defaultGroup;
        var message = "Welcome to the lobby!";
        logMessage ({
            'type' : 'log',
            'message': message
        }, data.defaultGroup);
        addParticipantsMessage (data);

        username = data.username;
    });

    //Whenever the server emits 'new message', update the chat body
    socket.on ('new message', function (data) {
        addChatMessage (data, data.group);
    });
    
    // Whenever the server emits 'user joined', log it in the chat body
    socket.on ('user joined', function (data) {
        logMessage ({
            'type' : 'log',
            'message' : data.username + ' joined'
        }, $scope.defaultGroup);
        addParticipantsMessage (data);
    });

    //Whenever the server emits 'user left', log it in the chat body
    socket.on ('user left', function (data) {
        logMessage ({
            'type' : 'log',
            'message' : data.username + ' left'
        }, $scope.defaultGroup);
        addParticipantsMessage (data);
        removeChatTyping (data);
    });

    //Whenever the server emits 'typing', show the typing message
    socket.on ('typing', function (data) {
        addChatTyping(data);
    });

    //Whenever the server emits 'stop typing', kill the typing message
    socket.on ('stop typing', function (data) {
        removeChatTyping(data);
    });

    //Add the new group to this client's chat channels.
    socket.on ('update users', function (data) {
        if (!$scope.messages[data.groupList[0]]) {
            $scope.messages[data.groupList[0]] = [];
        }
    });

    //Open this chat channel when this client has been added to a chat channel.
    socket.on ('added group', function (data) {
        if (!$scope.messages[data]) {
            $scope.messages[data] = [];
        }
    });

    //Clear the chat channel's data when this client has been removed from the group.
    socket.on ('deleted group', function (data) {
        $scope.selectedGroup = 0;
        delete $scope.messages[data];
    });

    socket.on ('damage shoutout', function (data) {
        var message = data.group + " doth dealt " + data.experience + " damage to the great beast.";
        for (var i = 0; i < socket.getSocketGroups().length; i++) {
            logMessage ({
                'type' : 'damagelog',
                'message' : message
            }, socket.getSocketGroups()[i]);
        }
    });

    socket.on ('experience payout', function (data) {
        //Display the welcome message
        $scope.defaultGroup = data.defaultGroup;
        for (var i = 0; i < socket.getSocketGroups().length; i++) {
            logMessage ({
                'type' : 'damagelog',
                'message' : data.message
            }, socket.getSocketGroups()[i]);
        }
    });
    
    /*
     *  Scope functions used by angular in the DOM.
     */

     /**
     * Set the group with the given index as the selected chat channel.
     *
     * @param {Integer} index
     */
    $scope.setSelectedGroup = function (index) {
        $scope.selectedGroup = index;
    };

     /**
     * Send a message to the server.
     */
    $scope.sendMessage = function () {
        var message = $scope.chatMessage;
        //Prevent markup from being injected into the message
        message = cleanInput(message);
        //If there is a non-empty message
        if (message) {
            $scope.chatMessage="";
            
            addChatMessage({
                'username': username,
                'message': message
            }, socket.getSocketGroups()[$scope.selectedGroup]);
            
            //Tell server to execute 'new message' and send along one parameter
            socket.emit ('new message', {
                'message' : message,
                'group' : socket.getSocketGroups()[$scope.selectedGroup]
            });
        }
        
        socket.emit('stop typing');
        typing = false;
    };

    /**
    * Get the color of a username through a hash function
    *
    * @param {String} username
    * @returns {String} color
    */
    $scope.getUsernameColor = function (username) {
        //Compute hash code
        var hash = 7;
        for (var i = 0; i < username.length; i++) {
            hash = username.charCodeAt(i) + (hash << 5) - hash;
        }
        
        //Calculate color
        var index = Math.abs(hash % COLORS.length);
        return COLORS[index];
    };
    
    /**
    * Update the server whenever this client starts/stops typing.
    */
    $scope.updateTyping = function () {
        if (!typing) {
            typing = true;
            //Send the typing event to other users with the group name of the chat channel this user is typing into.
            socket.emit ('typing', socket.getSocketGroups()[$scope.selectedGroup]);
        }
        
        //Timer to track when was the last keystroke by the user in the input field.
        lastTypingTime = (new Date()).getTime();
        setTimeout(function () {
            var typingTimer = (new Date()).getTime();
            var timeDiff = typingTimer - lastTypingTime;
            if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
                socket.emit ('stop typing');
                typing = false;
            }
        }, TYPING_TIMER_LENGTH);
    };
    
    /*
     *  Private functions used within the controller.
     */
    
    /**
    * Prevents input from having injected markup.
    *
    * @param {String} input
    * @returns {String} parsed
    */
    var cleanInput = function (input) {
        return $('<div/>').text(input).text();
    };

    /**
    * Insert message into chat log.
    *
    * @param {String} message
    * @param {String} groupname
    */
    var logMessage = function (message, groupname) {
        if (!$scope.messages[groupname]) {
            $scope.messages[groupname] = [];
        }
        $scope.messages[groupname].push(message);

        //Scroll chat window to the last received message.
        $('.chat-area')[0].scrollTop = $('.chat-area')[0].scrollHeight;
    };
    
    /**
    * Wrapper for logMessage used for logging participants entering/leaving lobby.
    *
    * @param {Object} data
    */
    var addParticipantsMessage = function (data) {
        var message = '';
        if (data.numUsers === 1) {
            message += "This is a lonely place.";
        } else {
            message += "There are " + data.numUsers + " players in the lobby";
        }
        logMessage ({
            'type' : 'log',
            'message' : message
        }, $scope.defaultGroup);
    };
    
    /**
    * Wrapper for logMessage to log messages from users.
    *
    * @param {Object} data
    * @param {String} groupname
    */
    var addChatMessage = function (data, groupname) {
        logMessage ({
            'user' : data.username,
            'type' : 'chat',
            'message' : data.message
        }, groupname);
    };
    
    /**
    * Wrapper for logMessage used for "[user] is typing" messages.
    *
    * @param {Object} data
    */
    var addChatTyping = function (data) {
        //Display only if this socket client is a member of a group that the message is being typed in.
        if (socket.getSocketGroups().indexOf (data.group) >= 0) {
            $scope.typingMessages.push({
                'user' : data.username,
                'message' : 'is typing in: ' + data.group
            });
        }
    };

    /**
    * Remove "[user] is typing" message belonging to a specific user.
    *
    * @param {Object} data
    */
    var removeChatTyping = function (data) {
        var indexToRemove = [];
        $scope.typingMessages.forEach (function (value, i) {
            if (value.user == data.username) {
                indexToRemove.push (i);
            }
        });
        indexToRemove.forEach (function (value, i) {
            $scope.typingMessages.splice (value, 1);
        });
    };
});