<commit_before>﻿/// <reference path="Scripts/jquery-1.7.js" />
/// <reference path="Scripts/jQuery.tmpl.js" />
/// <reference path="Scripts/jquery.cookie.js" />

$(function () {
    var chat = $.connection.chat;
    var Keys = { Up: 38, Down: 40, Esc: 27 };

    $.fn.isNearTheEnd = function () {
        return this[0].scrollTop + this.height() >= this[0].scrollHeight;
    };

    $.fn.resizeMobileContent = function () {
        if ($.mobile) {
            this.find('embed')
                .attr('width', 250)
                .attr('height', 202);
        }
        return this;
    };

    function formatTime(dt) {
        var ap = "";
        var hr = dt.getHours();

        if (hr < 12) {
            ap = "AM";
        }
        else {
            ap = "PM";
        }

        if (hr == 0) {
            hr = 12;
        }

        if (hr > 12) {
            hr = hr - 12;
        }

        var mins = padZero(dt.getMinutes());
        var seconds = padZero(dt.getSeconds());
        return hr + ":" + mins + ":" + seconds + " " + ap;
    }

    function padZero(s) {
        s = s.toString();
        if (s.length == 1) {
            return "0" + s;
        }
        return s;
    }

    function toLocal(dts) {
        var s = dts.substr('/Date('.length);
        var ticks = parseInt(s.substr(0, s.length - 2));
        var dt = new Date(ticks);
        return formatTime(dt);
    }

    function clearMessages(roomId) {
        $('#messages-' + roomId).html('');
    }

    function refreshMessages(roomId) { refreshList($('#messages-' + roomId)); }

    function clearUsers(roomId) {
        $('#users-' + roomId).html('');
    }

    function refreshUsers(roomId) { refreshList($('#users-' + roomId)); }

    function refreshList(list) {
        if (list.is('.ui-listview')) {
            list.listview('refresh');
        }
    }

    function markUserInactive(user) {
        id = 'u-' + user.Id;
        if (user.Active === false) {
            $('.' + id).fadeTo('slow', 0.5);
        }
    }

    function addMessage(content, type, room) {
        var m = $('.messages.current');
        if (room) {
            var roomId = getRoomId(room);
            m = $('#messages-' + roomId);
        }
        var nearEnd = m.isNearTheEnd();

        var converter = new Showdown.converter();
        var html = converter.makeHtml(content);
        var e = $('<li/>').html(html).appendTo(m);

        refreshMessages();

        if (type) {
            e.addClass(type);
        }

        // notifications are not that important (issue #79)
        if (type !== 'notification') {
            updateUnread();
        }

        if (nearEnd) {
            scrollToBottom();
        }
        return e;
    }

    function scrollToBottom() {
        var messages = $('.messages.current');
        messages.scrollTop(messages[0].scrollHeight);
    }

    function getRoomId(room) {
        return escape(room.toLowerCase()).replace(/[^a-z0-9]/, '_');
    }

    window.scrollToBottom = scrollToBottom;

    chat.joinRoom = function (room) {

        var roomId = getRoomId(room);
        addRoom(roomId, room);
        showRoom(roomId);

        clearUsers(roomId);

        chat.getUsers(room)
            .done(function (users) {
                $.each(users, function () {
                    chat.addUser(this, true);
                    markUserInactive(this);
                });

                refreshUsers();

                $('#new-message').focus();
            });

        chat.getRecentMessages(room)
            .done(function (messages) {
                $.each(messages, function () {
                    chat.addMessage(this, true);
                });
            });

        addMessage('Entered ' + room, 'notification');
        updateCookie();
    };


    chat.markInactive = function (users) {
        $.each(users, function () {
            markUserInactive(this);
        });
    };

    chat.updateActivity = function (user) {
        var id = 'u-' + user.Id;
        $('.' + id).fadeTo('slow', 1);
    };

    chat.showRooms = function (rooms) {
        addMessage('<h3>Rooms</h3>');
        if (!rooms.length) {
            addMessage('No rooms available', 'notification');
        }
        else {
            $.each(rooms, function () {
                addMessage(this.Name + ' (' + this.Count + ')');
            });
        }
        addMessage('<br/>');
    };

    chat.addMessageContent = function (id, content) {
        var nearEnd = $('.messages.current').isNearTheEnd();

        var e = $('#m-' + id).append(content)
                             .resizeMobileContent();

        updateUnread();

        if (nearEnd) {
            setTimeout(function () {
                scrollToBottom();
            }, 150);
        }
    };

    chat.addMessage = function (message, noScroll) {
        var currentUserName = $.cookie('username');
        var re = new RegExp("\\b@?" + currentUserName.replace(/\./, '\\.') + "\\b", "i");

        var converter = new Showdown.converter();
        var html = converter.makeHtml(message.Content);

        var data = {
            name: message.User.Name,
            hash: message.User.Hash,
            message: html,
            id: message.Id,
            when: toLocal(message.When),
            highlight: re.test(message.Content) ? 'highlight' : ''
        };

        var roomId = getRoomId(message.Room);
        var nearEnd = $('#messages-' + roomId).isNearTheEnd();
        var e = $('#new-message-template').tmpl(data)
                                          .appendTo($('#messages-' + roomId))
                                          .resizeMobileContent();
        refreshMessages(roomId);

        updateUnread(roomId);

        if (!noScroll && nearEnd) {
            scrollToBottom();
        }
    };

    chat.addUser = function (user, exists) {
        // remove all users that are leaving
        $('.user.removing').remove();

        var data = {
            name: user.Name,
            hash: user.Hash,
            id: user.Id
        };

        if (user.Id === this.id) {
            addMessage('Your name is now ' + user.Name, 'notification', user.Room);
            updateCookie();
        };

        var room = user.Room;
        if (!room) {
            return;
        }

        var roomId = getRoomId(room);

        // if user already listed in room
        if ($('#users-' + roomId + ' li.u-' + user.Id).length > 0) {
            return;
        }

        var e = $('#new-user-template').tmpl(data)
                                       .appendTo($('#users-' + roomId));

        refreshUsers(roomId);

        if (!exists && this.id !== user.Id) {
            addMessage(user.Name + ' just entered ' + room, 'notification', room);
        }

        updateCookie();
    };

    chat.changeUserName = function (user, oldName, newName) {
        var roomId = getRoomId(user.Room);
        $('#users-' + roomId + ' .u-' + user.Id).replaceWith(
                $('#new-user-template').tmpl({
                    name: user.Name,
                    hash: user.Hash,
                    id: user.Id
                })
        );

        refreshUsers();

        if (user.Id === this.id) {
            return;
        }
        else {
            addMessage(oldName + '\'s nick has changed to ' + newName, 'notification', user.Room);
        }
    };

    chat.userNameChanged = function (newName) {
        addMessage('Your name is now ' + newName, 'notification');
        updateCookie();
    };

    chat.changeGravatar = function (currentUser) {

        if (currentUser.Room) {
            var roomId = getRoomId(currentUser.Room);
            $('#users-' + roomId + ' .u-' + currentUser.Id).replaceWith(
                $('#new-user-template').tmpl({
                    name: currentUser.Name,
                    hash: currentUser.Hash,
                    id: currentUser.Id
                })
            );
        }
        refreshUsers();

        if (currentUser.Id === this.id) {
            return;
        }
        else {
            addMessage(currentUser.Name + "'s gravatar changed.", 'notification', currentUser.Room);
        }
    };

    chat.gravatarChanged = function (currentUser) {
        addMessage('Your gravatar has been set.', 'notification');
        chat.hash = currentUser.Hash;
        updateCookie();
    };

    chat.setTyping = function (currentUser, isTyping) {
        var roomId = getRoomId(currentUser.Room);
        if (isTyping) {
            $('#users-' + roomId + ' li.u-' + currentUser.Id).addClass('typing');
        }
        else {
            $('#users-' + roomId + ' li.u-' + currentUser.Id).removeClass('typing');
        }
    };

    chat.showCommands = function (commands) {
        addMessage('<h3>Help</h3>');
        $.each(commands, function () {
            addMessage(this.Name + ' - ' + this.Description);
        });
        addMessage('<br />');
    };

    chat.showUsersInRoom = function (room, names) {
        addMessage("<h3> Users in " + room + "</h3>");
        if (names.length === 0) {
            addMessage("Room is empty");
        }
        else {
            $.each(names, function () {
                addMessage("- " + this);
            });
        }
    };

    chat.listUsers = function (users) {
        if (users.length === 0) {
            addMessage("<h3>No users matched your search</h3>");
        } else {
            addMessage("<h3> The following users match your search </h3>");
            addMessage(users.join(", "));
        }
    };

    chat.showUsersRoomList = function (user, rooms) {
        if (rooms.length == 0) {
            addMessage("<h3>" + user + " is not in any rooms</h3>");
        } else {
            addMessage("<h3>" + user + " is in the following rooms</h3>");
            addMessage(rooms.join(", "));
        }
    };

    chat.sendMeMessage = function (name, message) {
        addMessage('*' + name + ' ' + message, 'notification');
    };

    chat.sendPrivateMessage = function (from, to, message) {
        addMessage('<emp>*' + from + '* &raquo; *' + to + '*</emp> ' + message, 'pm');
    };

    chat.nudge = function (from, to) {
        function shake(n) {
            var move = function (x, y) {
                parent.moveBy(x, y);
            };
            for (var i = n; i > 0; i--) {
                for (var j = 1; j > 0; j--) {
                    move(i, 0);
                    move(0, -i);
                    move(-i, 0);
                    move(0, i);
                    move(i, 0);
                    move(0, -i);
                    move(-i, 0);
                    move(0, i);
                    move(i, 0);
                    move(0, -i);
                    move(-i, 0);
                    move(0, i);
                }
            }
            return this;
        };
        $("body").effect("pulsate", { times: 3 }, 300);
        window.setTimeout(function () {
            shake(20);
        }, 300);
        addMessage('*' + from + ' nudged ' + (to ? 'you' : 'the room'), to ? 'pm' : 'notification');
    };

    chat.leave = function (user) {
        var room = user.Room;
        var roomId = getRoomId(room);

        if (this.id !== user.Id) {

            // remove user from specified room
            $('#users-' + roomId + ' li.u-' + user.Id).addClass('removing').fadeOut('slow', function () {
                $(this).remove();
            });

            refreshUsers();

            addMessage(user.Name + ' left ' + room, 'notification', room);
        }
        else {
            removeRoom(roomId);
            showRoom('lobby');
            updateCookie();

            addMessage('You have left ' + room, 'notification');

            this.room = null;
        }
    };

    $('#send-message').submit(function () {
        var command = $('#new-message').val();
        chat.room = getCurrentRoom();
        if (command) {
            chat.send(command)
            .fail(function (e) {
                addMessage(e, 'error');
            });

            // Immediately mark as not-typing when sending
            clearTimeout(chat.typingTimeoutId);
            chat.typingTimeoutId = 0;
            chat.typing(false);
            updateChatHistory(command);

            $('#new-message').val('');
            $('#new-message').focus();
        }

        return false;
    });

    var typingTimeoutId = 0;
    $('#new-message').keypress(function (e) {
        chat.room = getCurrentRoom();
        // If not in a room, don't try to send typing notifications
        if (chat.room === null) {
            return;
        }

        // Clear any previous timeout
        if (chat.typingTimeoutId > 0) {
            clearTimeout(chat.typingTimeoutId);
        }
        // Otherwise, mark as typing
        else {
            chat.typing(true);
        }

        // Set timeout to turn off
        chat.typingTimeoutId = setTimeout(function () {
            chat.typingTimeoutId = 0;
            chat.typing(false);
        }, 3000);
    });

    $('#new-message').keydown(function (e) {
        // cycle through the history 
        var key = (e.keyCode ? e.keyCode : e.which);
        switch (key) {
            case Keys.Up:
                historyLocation -= 1;
                if (historyLocation < 0) {
                    historyLocation = chatHistory.length - 1;
                }
                $(this).val(chatHistory[historyLocation]);
                break;

            case Keys.Down:
                historyLocation = (historyLocation + 1) % chatHistory.length;
                $(this).val(chatHistory[historyLocation]);
                break;

            case Keys.Esc:
                $(this).val('');
                break;
        }
    });

    $(window).blur(function () {
        chat.focus = false;
    });

    $(window).focus(function () {
        chat.focus = true;
        chat.unread = 0;
        document.title = 'SignalR Chat';
    });

    function updateUnread(roomId) {
        if (chat.focus === false) {
            if (!chat.unread) {
                chat.unread = 0;
            }
            chat.unread++;
        }
        var currentId = getCurrentRoomId();
        if (roomId && currentId !== roomId) {
            var $tab = $('#tabs-' + roomId);
            $tab.addClass('unread');
            var room = $tab.data('name');
            var unread = $tab.data('unread');
            if (!unread) {
                unread = 0;
            }
            unread++;
            $tab.text('(' + unread + ') ' + room).data('unread', unread);
        }

        updateTitle();
    }

    function updateTitle() {
        if (chat.unread === 0) {
            document.title = 'SignalR Chat';
        }
        else {
            document.title = '(' + chat.unread + ') SignalR Chat ';
        }
    }

    function updateCookie() {
        $.cookie('userid', chat.id, { path: '/', expires: 30 });
        $.cookie('username', chat.name, { path: '/', expires: 30 });

        var rooms = $('#tabs li')
                    .filter(function (index) { return index > 0; })    // skip 1st tab (Lobby)
                    .map(function () { return $(this).data('name'); })
                    .get()
                    .join(';');
        $.cookie('userroom', rooms, { path: '/', expires: 30 });

        if (chat.hash) {
            $.cookie('userhash', chat.hash, { path: '/', expires: 30 });
        }
    }

    $(window).focus();

    addMessage('Welcome to the SignalR IRC clone', 'notification');
    addMessage('Type /help to see the list of commands', 'notification');
<commit_msg>Changed SignalR Chat to JabbR<commit_after>﻿/// <reference path="Scripts/jquery-1.7.js" />
/// <reference path="Scripts/jQuery.tmpl.js" />
/// <reference path="Scripts/jquery.cookie.js" />

$(function () {
    var chat = $.connection.chat;
    var Keys = { Up: 38, Down: 40, Esc: 27 };

    $.fn.isNearTheEnd = function () {
        return this[0].scrollTop + this.height() >= this[0].scrollHeight;
    };

    $.fn.resizeMobileContent = function () {
        if ($.mobile) {
            this.find('embed')
                .attr('width', 250)
                .attr('height', 202);
        }
        return this;
    };

    function formatTime(dt) {
        var ap = "";
        var hr = dt.getHours();

        if (hr < 12) {
            ap = "AM";
        }
        else {
            ap = "PM";
        }

        if (hr == 0) {
            hr = 12;
        }

        if (hr > 12) {
            hr = hr - 12;
        }

        var mins = padZero(dt.getMinutes());
        var seconds = padZero(dt.getSeconds());
        return hr + ":" + mins + ":" + seconds + " " + ap;
    }

    function padZero(s) {
        s = s.toString();
        if (s.length == 1) {
            return "0" + s;
        }
        return s;
    }

    function toLocal(dts) {
        var s = dts.substr('/Date('.length);
        var ticks = parseInt(s.substr(0, s.length - 2));
        var dt = new Date(ticks);
        return formatTime(dt);
    }

    function clearMessages(roomId) {
        $('#messages-' + roomId).html('');
    }

    function refreshMessages(roomId) { refreshList($('#messages-' + roomId)); }

    function clearUsers(roomId) {
        $('#users-' + roomId).html('');
    }

    function refreshUsers(roomId) { refreshList($('#users-' + roomId)); }

    function refreshList(list) {
        if (list.is('.ui-listview')) {
            list.listview('refresh');
        }
    }

    function markUserInactive(user) {
        id = 'u-' + user.Id;
        if (user.Active === false) {
            $('.' + id).fadeTo('slow', 0.5);
        }
    }

    function addMessage(content, type, room) {
        var m = $('.messages.current');
        if (room) {
            var roomId = getRoomId(room);
            m = $('#messages-' + roomId);
        }
        var nearEnd = m.isNearTheEnd();

        var converter = new Showdown.converter();
        var html = converter.makeHtml(content);
        var e = $('<li/>').html(html).appendTo(m);

        refreshMessages();

        if (type) {
            e.addClass(type);
        }

        // notifications are not that important (issue #79)
        if (type !== 'notification') {
            updateUnread();
        }

        if (nearEnd) {
            scrollToBottom();
        }
        return e;
    }

    function scrollToBottom() {
        var messages = $('.messages.current');
        messages.scrollTop(messages[0].scrollHeight);
    }

    function getRoomId(room) {
        return escape(room.toLowerCase()).replace(/[^a-z0-9]/, '_');
    }

    window.scrollToBottom = scrollToBottom;

    chat.joinRoom = function (room) {

        var roomId = getRoomId(room);
        addRoom(roomId, room);
        showRoom(roomId);

        clearUsers(roomId);

        chat.getUsers(room)
            .done(function (users) {
                $.each(users, function () {
                    chat.addUser(this, true);
                    markUserInactive(this);
                });

                refreshUsers();

                $('#new-message').focus();
            });

        chat.getRecentMessages(room)
            .done(function (messages) {
                $.each(messages, function () {
                    chat.addMessage(this, true);
                });
            });

        addMessage('Entered ' + room, 'notification');
        updateCookie();
    };


    chat.markInactive = function (users) {
        $.each(users, function () {
            markUserInactive(this);
        });
    };

    chat.updateActivity = function (user) {
        var id = 'u-' + user.Id;
        $('.' + id).fadeTo('slow', 1);
    };

    chat.showRooms = function (rooms) {
        addMessage('<h3>Rooms</h3>');
        if (!rooms.length) {
            addMessage('No rooms available', 'notification');
        }
        else {
            $.each(rooms, function () {
                addMessage(this.Name + ' (' + this.Count + ')');
            });
        }
        addMessage('<br/>');
    };

    chat.addMessageContent = function (id, content) {
        var nearEnd = $('.messages.current').isNearTheEnd();

        var e = $('#m-' + id).append(content)
                             .resizeMobileContent();

        updateUnread();

        if (nearEnd) {
            setTimeout(function () {
                scrollToBottom();
            }, 150);
        }
    };

    chat.addMessage = function (message, noScroll) {
        var currentUserName = $.cookie('username');
        var re = new RegExp("\\b@?" + currentUserName.replace(/\./, '\\.') + "\\b", "i");

        var converter = new Showdown.converter();
        var html = converter.makeHtml(message.Content);

        var data = {
            name: message.User.Name,
            hash: message.User.Hash,
            message: html,
            id: message.Id,
            when: toLocal(message.When),
            highlight: re.test(message.Content) ? 'highlight' : ''
        };

        var roomId = getRoomId(message.Room);
        var nearEnd = $('#messages-' + roomId).isNearTheEnd();
        var e = $('#new-message-template').tmpl(data)
                                          .appendTo($('#messages-' + roomId))
                                          .resizeMobileContent();
        refreshMessages(roomId);

        updateUnread(roomId);

        if (!noScroll && nearEnd) {
            scrollToBottom();
        }
    };

    chat.addUser = function (user, exists) {
        // remove all users that are leaving
        $('.user.removing').remove();

        var data = {
            name: user.Name,
            hash: user.Hash,
            id: user.Id
        };

        if (user.Id === this.id) {
            addMessage('Your name is now ' + user.Name, 'notification', user.Room);
            updateCookie();
        };

        var room = user.Room;
        if (!room) {
            return;
        }

        var roomId = getRoomId(room);

        // if user already listed in room
        if ($('#users-' + roomId + ' li.u-' + user.Id).length > 0) {
            return;
        }

        var e = $('#new-user-template').tmpl(data)
                                       .appendTo($('#users-' + roomId));

        refreshUsers(roomId);

        if (!exists && this.id !== user.Id) {
            addMessage(user.Name + ' just entered ' + room, 'notification', room);
        }

        updateCookie();
    };

    chat.changeUserName = function (user, oldName, newName) {
        var roomId = getRoomId(user.Room);
        $('#users-' + roomId + ' .u-' + user.Id).replaceWith(
                $('#new-user-template').tmpl({
                    name: user.Name,
                    hash: user.Hash,
                    id: user.Id
                })
        );

        refreshUsers();

        if (user.Id === this.id) {
            return;
        }
        else {
            addMessage(oldName + '\'s nick has changed to ' + newName, 'notification', user.Room);
        }
    };

    chat.userNameChanged = function (newName) {
        addMessage('Your name is now ' + newName, 'notification');
        updateCookie();
    };

    chat.changeGravatar = function (currentUser) {

        if (currentUser.Room) {
            var roomId = getRoomId(currentUser.Room);
            $('#users-' + roomId + ' .u-' + currentUser.Id).replaceWith(
                $('#new-user-template').tmpl({
                    name: currentUser.Name,
                    hash: currentUser.Hash,
                    id: currentUser.Id
                })
            );
        }
        refreshUsers();

        if (currentUser.Id === this.id) {
            return;
        }
        else {
            addMessage(currentUser.Name + "'s gravatar changed.", 'notification', currentUser.Room);
        }
    };

    chat.gravatarChanged = function (currentUser) {
        addMessage('Your gravatar has been set.', 'notification');
        chat.hash = currentUser.Hash;
        updateCookie();
    };

    chat.setTyping = function (currentUser, isTyping) {
        var roomId = getRoomId(currentUser.Room);
        if (isTyping) {
            $('#users-' + roomId + ' li.u-' + currentUser.Id).addClass('typing');
        }
        else {
            $('#users-' + roomId + ' li.u-' + currentUser.Id).removeClass('typing');
        }
    };

    chat.showCommands = function (commands) {
        addMessage('<h3>Help</h3>');
        $.each(commands, function () {
            addMessage(this.Name + ' - ' + this.Description);
        });
        addMessage('<br />');
    };

    chat.showUsersInRoom = function (room, names) {
        addMessage("<h3> Users in " + room + "</h3>");
        if (names.length === 0) {
            addMessage("Room is empty");
        }
        else {
            $.each(names, function () {
                addMessage("- " + this);
            });
        }
    };

    chat.listUsers = function (users) {
        if (users.length === 0) {
            addMessage("<h3>No users matched your search</h3>");
        } else {
            addMessage("<h3> The following users match your search </h3>");
            addMessage(users.join(", "));
        }
    };

    chat.showUsersRoomList = function (user, rooms) {
        if (rooms.length == 0) {
            addMessage("<h3>" + user + " is not in any rooms</h3>");
        } else {
            addMessage("<h3>" + user + " is in the following rooms</h3>");
            addMessage(rooms.join(", "));
        }
    };

    chat.sendMeMessage = function (name, message) {
        addMessage('*' + name + ' ' + message, 'notification');
    };

    chat.sendPrivateMessage = function (from, to, message) {
        addMessage('<emp>*' + from + '* &raquo; *' + to + '*</emp> ' + message, 'pm');
    };

    chat.nudge = function (from, to) {
        function shake(n) {
            var move = function (x, y) {
                parent.moveBy(x, y);
            };
            for (var i = n; i > 0; i--) {
                for (var j = 1; j > 0; j--) {
                    move(i, 0);
                    move(0, -i);
                    move(-i, 0);
                    move(0, i);
                    move(i, 0);
                    move(0, -i);
                    move(-i, 0);
                    move(0, i);
                    move(i, 0);
                    move(0, -i);
                    move(-i, 0);
                    move(0, i);
                }
            }
            return this;
        };
        $("body").effect("pulsate", { times: 3 }, 300);
        window.setTimeout(function () {
            shake(20);
        }, 300);
        addMessage('*' + from + ' nudged ' + (to ? 'you' : 'the room'), to ? 'pm' : 'notification');
    };

    chat.leave = function (user) {
        var room = user.Room;
        var roomId = getRoomId(room);

        if (this.id !== user.Id) {

            // remove user from specified room
            $('#users-' + roomId + ' li.u-' + user.Id).addClass('removing').fadeOut('slow', function () {
                $(this).remove();
            });

            refreshUsers();

            addMessage(user.Name + ' left ' + room, 'notification', room);
        }
        else {
            removeRoom(roomId);
            showRoom('lobby');
            updateCookie();

            addMessage('You have left ' + room, 'notification');

            this.room = null;
        }
    };

    $('#send-message').submit(function () {
        var command = $('#new-message').val();
        chat.room = getCurrentRoom();
        if (command) {
            chat.send(command)
            .fail(function (e) {
                addMessage(e, 'error');
            });

            // Immediately mark as not-typing when sending
            clearTimeout(chat.typingTimeoutId);
            chat.typingTimeoutId = 0;
            chat.typing(false);
            updateChatHistory(command);

            $('#new-message').val('');
            $('#new-message').focus();
        }

        return false;
    });

    var typingTimeoutId = 0;
    $('#new-message').keypress(function (e) {
        chat.room = getCurrentRoom();
        // If not in a room, don't try to send typing notifications
        if (chat.room === null) {
            return;
        }

        // Clear any previous timeout
        if (chat.typingTimeoutId > 0) {
            clearTimeout(chat.typingTimeoutId);
        }
        // Otherwise, mark as typing
        else {
            chat.typing(true);
        }

        // Set timeout to turn off
        chat.typingTimeoutId = setTimeout(function () {
            chat.typingTimeoutId = 0;
            chat.typing(false);
        }, 3000);
    });

    $('#new-message').keydown(function (e) {
        // cycle through the history 
        var key = (e.keyCode ? e.keyCode : e.which);
        switch (key) {
            case Keys.Up:
                historyLocation -= 1;
                if (historyLocation < 0) {
                    historyLocation = chatHistory.length - 1;
                }
                $(this).val(chatHistory[historyLocation]);
                break;

            case Keys.Down:
                historyLocation = (historyLocation + 1) % chatHistory.length;
                $(this).val(chatHistory[historyLocation]);
                break;

            case Keys.Esc:
                $(this).val('');
                break;
        }
    });

    $(window).blur(function () {
        chat.focus = false;
    });

    $(window).focus(function () {
        chat.focus = true;
        chat.unread = 0;
        document.title = 'JabbR';
    });

    function updateUnread(roomId) {
        if (chat.focus === false) {
            if (!chat.unread) {
                chat.unread = 0;
            }
            chat.unread++;
        }
        var currentId = getCurrentRoomId();
        if (roomId && currentId !== roomId) {
            var $tab = $('#tabs-' + roomId);
            $tab.addClass('unread');
            var room = $tab.data('name');
            var unread = $tab.data('unread');
            if (!unread) {
                unread = 0;
            }
            unread++;
            $tab.text('(' + unread + ') ' + room).data('unread', unread);
        }

        updateTitle();
    }

    function updateTitle() {
        if (chat.unread === 0) {
            document.title = 'JabbR';
        }
        else {
            document.title = '(' + chat.unread + ') JabbR ';
        }
    }

    function updateCookie() {
        $.cookie('userid', chat.id, { path: '/', expires: 30 });
        $.cookie('username', chat.name, { path: '/', expires: 30 });

        var rooms = $('#tabs li')
                    .filter(function (index) { return index > 0; })    // skip 1st tab (Lobby)
                    .map(function () { return $(this).data('name'); })
                    .get()
                    .join(';');
        $.cookie('userroom', rooms, { path: '/', expires: 30 });

        if (chat.hash) {
            $.cookie('userhash', chat.hash, { path: '/', expires: 30 });
        }
    }

    $(window).focus();

    addMessage('Welcome to the SignalR IRC clone', 'notification');
    addMessage('Type /help to see the list of commands', 'notification');

{'fraction_non_alphanumeric': 0.15622701838529177, 'fraction_numerical': 0.006906474820143885, 'mean_word_length': 1.4530196078431372, 'pattern_counts': {'":': 4, '<': 51, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2582453', 'n_tokens_mistral': 9264, 'n_tokens_neox': 8699, 'n_words': 1875}