﻿function ChatChannel(sessionUserId, channelId, chatRootElement, name, parentTab, preferences, privateChat, shameRoom, guestMode) {
    this.parent = ChannelHandler;
    this.parent(channelId);

    this.sessionUserId = sessionUserId;
    this.channelId = channelId;
    this.name = name;
    this.parentTab = parentTab;
    this.preferences = preferences;
    this.privateChat = privateChat;
    this.shameRoom = shameRoom;
    this.guestMode = guestMode;
    this.subscribers = [];
    this.subscriberCount = 0;

    var channel = this;

    // Elements
    this.inputLine = $("#InputLine");
    this.channelBody = $("<div class='ChannelBody'></div>");
    $("#ChannelBodyWrap").append(this.channelBody);
    this.channelBody.hide();

    this.subscriberList = $("<div class='SubscriberList'></div>");
    $("#SubscriberListWrap").append(this.subscriberList);
    this.subscriberList.hide();

    this.subscriberDetailsArrow = $("<img class='UserDetailsArrow' src='/Images/UserDetailsArrow.png' style='' />");
    $("#ChannelBodyWrap").append(this.subscriberDetailsArrow);
    this.subscriberDetailsArrow.hide();

    this.minimalMode = false;
    this.hasFocus = false;
    this.highlight = false;
    this.totalLines = 0;
    this.maxLines = 200;
    this.scrollBottom = true;
    this.linesSinceAdvertisment = 0;
    this.selectedUserId = 0;
    this.selectUserFromClick = false;
    this.isModerator = false;
    this.disposed = false;
    this.debug = false;
    this.eventHandlerUrl = "/Channels/EventHandler.ashx";

    $.ajax({
        url: channel.eventHandlerUrl,
        data: { Command: Networker.ChatCommand.Join, ChannelId: channelId, "Private": privateChat },
        dataType: "json",
        success: function(response) { channel.handleJoinResponse(response); },
        error: function() { channel.handleJoinFailure(); }
    });


    if (privateChat) {
        this.subscriberList.addClass("Private");
    } else {
        // Handle subscriber list events
        this.channelBody.click(function(event) {
            channel.unselectSubscriber();
            if ($(event.target).is("a.User")) {
                if(!channel.minimalMode) {
                    channel.selectSubscriber(parseInt(event.target.name), true, true);
                }
                event.preventDefault();
            }
        });

        this.subscriberList.click(function(event) {
            if ($(event.target).is(".ShowSubscriber")) {
                channel.selectSubscriber($(event.target).parent().data("userId"), false, true);
                event.preventDefault();
            }
        });

        this.subscriberList.mousemove(function(event) {
            if ($(event.target).is(".ShowSubscriber") && $(event.target).parent().data("userId") != channel.selectedUserId && !(channel.selectedUserId && channel.selectUserFromClick)) {
                channel.selectSubscriber($(event.target).parent().data("userId"), false, false);
            }
        });

        this.subscriberList.scroll(function() {
            if (channel.cancelNextScroll) {
                if (!jQuery.browser.msie) {
                    channel.cancelNextScroll = false;
                }
                return;
            }
            channel.unselectSubscriber();
        });

        // Handle misc events
        this.channelBody.scroll(function() {
            var snapPixels = 15;
            if (channel.channelBody[0].scrollHeight - channel.channelBody.scrollTop() - snapPixels < channel.channelBody.innerHeight()) {
                channel.scrollBottom = true;
            } else {
                channel.scrollBottom = false;
            }
        });

    }

    this.getCreativeTimeout = setTimeout(function() { channel.getCreative(); }, 2 * 60 * 1000);
}

/*- Joining / Leaving ------------------------------------------------------------*/
ChatChannel.prototype.handleJoinResponse = function(response) {
    if (this.disposed) {
        return;
    }
    var channel = this;
    switch (response[0]) {
        case Networker.EventStatus.Accepted:
            for (var i in response[1]) {
                if (response[1].hasOwnProperty(i)) {
                    this.addSubscriber(response[1][i]);
                }
            }
            this.ready = true;
            this.isModerator = response[3];
            Networker.ChannelClient.updateChannels(response[2]);
            this.scrollToEnd(); // Force it to the end (looking at you, firefox)

            var message = "Welcome to the chat room <strong>" + this.name + "</strong>";
            if (this.debug) {
                message += "\nChannelId: " + this.channelId;
            }
            this.addLine("&nbsp;");
            this.addLine(message, "Welcome-Top");
            switch (response[4]) {
                case Networker.ChannelPolicy.EnforcedClean:
                    this.addLine("This is a <b style='color:red'>CLEAN</b>, English-only room <b>with bad word filtering.</b>.", " ");
                    this.addLine("<b>Things that are not allowed</b>: Spamming, advertising, bullying and fighting.", " ");
                    this.addLine("Keep it classy - Wireclub is a clean community - if you just want to cyber, get a private room!", "Welcome-Bottom");
                    break;
                case Networker.ChannelPolicy.Standard:
                    this.addLine("This is a <b style='color:red'>CLEAN</b>, English-only room <b>without bad word filtering.</b>", " ");
                    this.addLine("<b>Things that are not allowed</b>: Spamming, advertising, bullying and fighting.", " ");
                    this.addLine("Keep it classy - Wireclub is a clean community - if you just want to cyber, get a private room!", "Welcome-Bottom");
                    break;
                case Networker.ChannelPolicy.Unmoderated:
                    if (!this.guestMode) {
                        this.addLine("This is a <b style='color:red'>FREE-SPEECH</b> room. If you are easily offended you should not be here!", "Welcome-Bottom");
                    } else {
                        this.addLine("<img src='/Images/Icons/SmallAdd.gif' />&nbsp;<a target='_blank' href='http://www.wireclub.com/Chat-Rooms/'>Get your own chat room</a>", "Welcome-Bottom");
                    }
                    break;
            }
            break;

        case Networker.EventStatus.Suspended:
            window.chatWindow.leaveChannel(this.channelId, "You have been suspended from all chat rooms for 24 hours");
            break;

        case Networker.EventStatus.SuspendedFromChannel:
            window.chatWindow.leaveChannel(this.channelId, "The owner of this room or a moderator have kicked you out of this room.");
            break;

        case Networker.EventStatus.Creep:
            window.chatWindow.leaveChannel(this.channelId, "This room does not allow Creeps like you.");
            break;

        case Networker.EventStatus.DirtyMouth:
            window.chatWindow.leaveChannel(this.channelId, "This room does not allow Dirty-Mouths like you.");
            break;

        case Networker.EventStatus.Clone:
            window.chatWindow.leaveChannel(this.channelId, "Only one user per computer can be in a chat room at any given time.");
            break;

        case Networker.EventStatus.Logout:
            window.chatWindow.requestLogin();
            break;

        default:
            var message = "There was an error trying to join this chat room, try again later.";
            if (this.debug) {
                message += "\nChannelId: " + this.channelId;
                message += "\nName: " + this.name;
                message += "\nJoin response: " + response;
            }
            window.chatWindow.leaveChannel(this.channelId, message);
            break;
    }

    if (!this.privateChat) {
        var startRequestCount = Networker.ChannelClient.requestsCompleted;
        setTimeout(function() {
            if (Networker.ChannelClient.requestsCompleted == startRequestCount && Networker.ChannelClient.requestsCompleted > 0) {
                Networker.logError("Client sequence has not updated (v4) requests completed: " + Networker.ChannelClient.requestsCompleted + " response: " + response[0]);
                channel.addLine("There seems to be a problem connecting to this chat room, please try again later", "Error");
            }
        }, 80 * 1000);
    }
};

ChatChannel.prototype.handleJoinFailure = function(event, request, ajaxOptions, error) {
    window.chatWindow.leaveChannel(this.channelId, "There was an error trying to join this chat room.");
};

ChatChannel.prototype.leaveChannel = function() {
    this.disposed = true;
    clearTimeout(this.getCreativeTimeout);
    this.unselectSubscriber();

    $.get(this.eventHandlerUrl + "?Command=" + Networker.ChatCommand.Leave + "&ChannelId=" + this.channelId);

    // Cleanup
    if (this.channelBody) {
        this.channelBody.empty();
        this.channelBody.remove();
        this.channelBody = null;
    }

    if (this.subscriberList) {
        this.subscriberList.empty();
        this.subscriberList.remove();
        this.subscriberList = null;
    }

    if (this.parentTab) {
        this.parentTab.remove();
        this.parentTab = null;
    }
};

/*- Line handling ----------------------------------------------------------------*/
ChatChannel.prototype.addLine = function(text, cssClass) {

    var shouldScroll = this.scrollBottom;
    var line = document.createElement('div');
    if (cssClass) {
        line.className = cssClass;
    }
    line.innerHTML = text;
    this.channelBody[0].appendChild(line);
    line = null;

    this.linesSinceAdvertisment++;
    this.totalLines++;
    if (this.totalLines > this.maxLines && shouldScroll) {
        this.channelBody[0].removeChild(this.channelBody[0].firstChild);
    }

    if (this.hasFocus) {
        if (shouldScroll) {
            this.scrollToEnd();

            // Firefox appears to reflow the layout after the scroll event, need to force it to the bottom
            var channel = this;
            setTimeout(function() {
                channel.scrollToEnd();
            }, 100);
        }

        if (this.preferences.ChatPlaySounds) {
            window.chatWindow.playNewMessageSound()
        }
    } else if (!this.highlight) {
        this.highlight = true;
        try {
            this.parentTab.animate({ backgroundColor: "#b8ef23" }, 200);
            this.parentTab.addClass("Highlight");
        } catch (ex) { }
    }
};

ChatChannel.prototype.error = function(text) {
    this.addLine(text, "Error");
};

ChatChannel.prototype.addUserLine = function(userId, text) {
    var user = this.subscribers[userId];
    var nameplate = "???";
    if (user) {
        nameplate = "<a class='User' href='#' name='" + userId + "'>" + user.Nickname + "</a>";
        if (user.Blocked) {
            return;
        }
        var font = Networker.Fonts[user.FontFace];
        var color = Networker.Colors[user.FontColor];
        if (userId == this.sessionUserId) {
            font = Networker.Fonts[this.preferences.ChatFont];
            color = Networker.Colors[this.preferences.ChatColor];
        }
    } else {
        return;
    }

    this.addLine("<strong>" + nameplate + ":</strong> " + "<span style='font-family:" + font + ";color:" + color + "'>" + text + "</span>");
};

ChatChannel.prototype.scrollToEnd = function() {
    this.channelBody.scrollTop(this.channelBody[0].scrollHeight);
};

/*- Chat Commands ----------------------------------------------------------------*/
ChatChannel.prototype.castSuspentionVote = function(userId) {
    var channel = this;
    $.get(channel.eventHandlerUrl, { Command: Networker.ChatCommand.CastSuspensionVote, UserId: userId, ChannelId: channel.channelId });
};

ChatChannel.prototype.suspendUser = function(userId) {
    var channel = this;
    $.get(channel.eventHandlerUrl, { Command: Networker.ChatCommand.SuspendUser, UserToSuspend: userId, ChannelId: channel.channelId });
};

ChatChannel.prototype.castReputationVote = function(receiverUserId, reputationVoteType) {
    var channel = this;
    $.ajax({
        url: channel.eventHandlerUrl,
        data: { Command: Networker.ChatCommand.CastReputationVote, ReceiverUserId: receiverUserId, ReputationType: reputationVoteType, ChannelId: channel.channelId },
        dataType: "json",
        success: function(response) {
            if (response[0] == Networker.EventStatus.Denied)
                alert('You can only vote 5 times per day!')
        },
        error: function() { channel.addLine("Error casting reputation vote", "Error"); }
    });
};

ChatChannel.prototype.addFriend = function(userId) {
    var channel = this;
    $.get(channel.eventHandlerUrl, { Command: Networker.ChatCommand.AddFriend, UserId: userId, ChannelId: channel.channelId });
};

ChatChannel.prototype.blockUser = function(userId) {
    var channel = this;
    $.get(channel.eventHandlerUrl, { Command: Networker.ChatCommand.BlockUser, UserId: userId, ChannelId: channel.channelId });
};

ChatChannel.prototype.unblockUser = function(userId) {
    var channel = this;
    $.get(channel.eventHandlerUrl, { Command: Networker.ChatCommand.UnblockUser, UserId: userId, ChannelId: channel.channelId });
};

ChatChannel.prototype.reportUser = function(userId) {
    this.unselectSubscriber();
    window.chatWindow.openDialog("/Chat/Report.aspx?Modal=true&ReportUserId=" + userId + "&ChannelId=" + this.channelId + "&Moderator=" + this.isModerator);
};

/*- Send Message -----------------------------------------------------------------*/
ChatChannel.prototype.sendLine = function(text) {
    this.previousInputText = "";
    text = jQuery.trim(text);
    if (text === '') {
        return;
    }

    var channel = this;

    $.ajax({
        url: channel.eventHandlerUrl,
        data: { ChannelId: this.channelId, msg: text, Command: Networker.ChatCommand.SendMessage },
        dataType: "json",
        success: function(response) { channel.handleSendLineResponse(response); },
        error: function() { channel.sendLineError(); }
    });
};

ChatChannel.prototype.handleSendLineResponse = function(response) {
    if (this.disposed) {
        return;
    }

    if (response === Networker.EventStatus.Logout) {
        window.chatWindow.showLogin();
        return;
    }

    if (response[0] === Networker.EventStatus.Flood) {
        this.addLine("Our anti-spam robot ate your message!");
    } else if (response[0] === Networker.EventStatus.Accepted) {
        this.addUserLine(this.sessionUserId, response[1]);
    } else {
        this.sendLineError();
    }
};

ChatChannel.prototype.sendLineError = function() {
    this.error("Your message could not be sent, try again in a minute or so");
};

/*- Subscriber List Handling -----------------------------------------------------*/
ChatChannel.prototype.unselectSubscriber = function() {
    if (this.selectedUserId) {
        var user = this.subscribers[this.selectedUserId];
        if (user && user.subscriberDetailsElement) { // Could have selected someone who has left
            user.subscriberDetailsElement.remove();
            user.subscriberDetailsElement = null;
            this.subscriberDetailsArrow.hide();
        }
        this.selectedUserId = 0;
        this.selectUserFromClick = false;

        if (this.unselectSubscriberTimeout) {
            clearTimeout(this.unselectSubscriberTimeout);
        }
    }
};

ChatChannel.prototype.createDetailsPanel = function(user, privateChat) {
    var result = "";

    if (this.guestMode) {
        result = "<img src='" + user.MicroImageUrl + "' />" + user.Nickname +
        (privateChat ? "" : (user.Moderator ? "<br />Room Owner" : "")) +
        "<br /><br />";
    } else {

        result = "<a target='_blank' href='/Users/" + encodeURIComponent(user.Username) + "'>" +
        "<img src='" + user.MicroImageUrl + "' />" +
        "</a>";

        if (privateChat) {
            result += "<br class='Clear' />";
        }

        result +=
            "<a target='_blank' href='/Users/" + encodeURIComponent(user.Username) + "'>" +
            user.Nickname + "<br />" +
            "</a>";

        result +=
        "<span>" +
        user.Gender + " / <a target='_Blank' href='/Chat/" + encodeURIComponent(user.Country) + "/" + encodeURIComponent(user.City) + "'>" + user.City + "</a>" + "<br />" +
        "Age: " + user.Age +
        (privateChat ? "" : (user.Moderator ? "<br />Room Owner" : "")) +
        "</span>" +
        "<br class='Clear' />";
    }

    return result;
}

ChatChannel.prototype.setUserBlockedStatus = function(userId, blocked) {
    var user = this.subscribers[userId];
    if (user) {
        user.Blocked = blocked;
        if (blocked) {
            user.subscriberElement.addClass("Blocked");
        } else {
            user.subscriberElement.removeClass("Blocked");
        }
    }
}

ChatChannel.prototype.setUserFriendStatus = function(userId, friend) {
    var user = this.subscribers[userId];
    if (user) {
        user.Friend = friend;
        if (friend) {
            user.subscriberElement.addClass("Friend");
        } else {
            user.subscriberElement.removeClass("Friend");
        }
    }
    if (this.privateChat) {
        $(".FriendButton").remove();
    }
}

ChatChannel.prototype.attachUserDetailsButtonEvents = function(element, user) {
    var channel = this;
    element.find(".BlockButton").click(function(event) {
        event.preventDefault();
        if (confirm(user.Blocked ? 'Unblock ' + user.Nickname + '?' : 'Block ' + user.Nickname + '?')) {
            user.Blocked = !user.Blocked;

            if (!channel.guestMode) {
                if (user.Blocked) {
                    channel.blockUser(user.UserId);
                    if (channel.privateChat) {
                        window.chatWindow.leaveChannel(channel.channelId);
                    }
                } else {
                    channel.unblockUser(user.UserId);
                }
            }
        }

        window.chatWindow.setBlockedStatus(user.UserId, user.Blocked);
        channel.unselectSubscriber();
    });
    element.find(".KickButton").click(function(event) {
        event.preventDefault();
        if (channel.isModerator && confirm("Are you sure you want to kick " + user.Nickname + " out of this room?")) {
            channel.suspendUser(user.UserId);
        } else {
            if (confirm("Are you sure you want to give " + user.Nickname + " a suspension vote?")) {
                channel.castSuspentionVote(user.UserId);
                user.votedKick = true;
            }
        }
        channel.unselectSubscriber();
    });
    element.find(".CreepButton").click(function(event) {
        event.preventDefault();
        if (confirm('Are you sure ' + user.Nickname + ' is a little creepy? \n\r\n\rIf it is not true, you will get a creep vote instead...')) {
            channel.castReputationVote(user.UserId, Networker.ReputationType.Creep);
            channel.unselectSubscriber();
            user.votedCreep = true;
        }
    });
    if (!user.Friend) {
        element.find(".FriendButton").click(function(event) {
            event.preventDefault();
            if (confirm('Are you sure you want to send ' + user.Nickname + ' a friend request?')) {
                channel.addFriend(user.UserId);
                window.chatWindow.setFriendStatus(user.UserId, true);
                channel.unselectSubscriber();
            }
        });
    }
    element.find(".ReportButton").click(function(event) {
        event.preventDefault();
        channel.reportUser(user.UserId);
    });
    element.find(".PrivateChatButton").click(function(event) {
        event.preventDefault();
        window.chatWindow.requestPrivateChat(user.UserId);
        channel.unselectSubscriber();
    });
    element.find(".CloseButton").click(function(event) {
        channel.unselectSubscriber();
        event.preventDefault();
    });
}

ChatChannel.prototype.selectSubscriber = function(userId, scrollToView, fromClick) {
    if (this.disposed || !this.focus) {
        return;
    }

    var channel = this;
    this.unselectSubscriber();
    this.selectedUserId = userId;
    this.cancelNextScroll = true;

    var user = this.subscribers[userId];
    if (!user) {
        return;
    }

    if (scrollToView) {
        if (user.subscriberElement.position().top < this.subscriberList.position().top) {
            var topPadding = 2;
            this.subscriberList.scrollTop(this.subscriberList.scrollTop() + user.subscriberElement.position().top - this.subscriberList.position().top - topPadding);
        } else if (user.subscriberElement.position().top + user.subscriberElement.outerHeight() > this.subscriberList.position().top + this.subscriberList.outerHeight()) {
            this.subscriberList.scrollTop(this.subscriberList.scrollTop() + user.subscriberElement.position().top - this.subscriberList.position().top);
        }
    }

    var html =
        "<div class='SubscriberDetails  Subscriber" + user.UserId + (user.Blocked ? " Blocked" : "") + "'><div class='DetailsContent'>" +
        this.createDetailsPanel(user, false);

    html += "<div class='Buttons'>";

    if (userId !== this.sessionUserId) {
        html += "<a title='Block this user' class='BlockButton' href='#'>" + (user.Blocked ? "Unblock" : "Block") + "</a>";

        if (!user.votedKick) {
            html += "<a title='Vote to kick this person out of the room' class='KickButton' href='#'>Kick</a>";
        }

        if (!this.guestMode) {
            if (this.shameRoom == "false" && !user.votedCreep) {
                html += "<a title='Vote that this person is a creep' class='CreepButton' href='#'>Creep!</a>";
            }

            if (!user.Blocked) {
                html += "<a title='Start a private chat' class='PrivateChatButton' href='#'>Private</a>";
            }

            if (!user.Friend) {
                html += "<a class='FriendButton' href='#'>Add Friend</a>";
            }

            if (this.shameRoom == "false") {
                html += "<a title='Report this person' class='ReportButton' href='#'>Report</a>";
            }
        }
    }

    html += "<a class='CloseButton' href='#'>Close</a>";

    html += "</div>";

    html += "</div></div>";

    user.subscriberDetailsElement = $(html);
    $(document.body).append(user.subscriberDetailsElement);

    user.subscriberDetailsElement.css("top", user.subscriberElement.position().top);
    if (user.subscriberDetailsElement.position().top + user.subscriberDetailsElement.outerHeight() > this.subscriberList.position().top + this.subscriberList.outerHeight()) {
        user.subscriberDetailsElement.css("top", this.subscriberList.position().top + this.subscriberList.outerHeight() - user.subscriberDetailsElement.outerHeight());
    }

    if (user.subscriberDetailsElement.position().top < this.subscriberList.position().top) {
        user.subscriberDetailsElement.css("top", this.subscriberList.position().top);
    }

    this.subscriberDetailsArrow.show();
    this.subscriberDetailsArrow.css("top", user.subscriberElement.position().top + 3);

    this.attachUserDetailsButtonEvents(user.subscriberDetailsElement, user);

    if (fromClick == false) {
        user.subscriberDetailsElement.hover(
            function() {
                if (channel.unselectSubscriberTimeout) {
                    clearTimeout(channel.unselectSubscriberTimeout);
                }
            },
            function() {
                channel.unselectSubscriber(false);
            });
    }

    if (jQuery.browser.msie) {
        this.cancelNextScroll = false;
    }

    this.selectUserFromClick = fromClick;
    this.resetUnselectTimeout(fromClick);
};

ChatChannel.prototype.resetUnselectTimeout = function(fromClick) {
    var channel = this;
    if (this.unselectSubscriberTimeout) {
        clearTimeout(this.unselectSubscriberTimeout);
    }
    this.unselectSubscriberTimeout = setTimeout(function() { channel.unselectSubscriber(); }, 1000 * (fromClick ? 15 : 3));
};

ChatChannel.prototype.addSubscriber = function(user) {

    if (!this.subscribers[user.UserId]) {
        this.subscribers[user.UserId] = user;
        this.subscriberCount++;

        if (this.privateChat && user.UserId == this.sessionUserId) {
            return;
        }

        // Check if this is a known user
        if (window.chatWindow.users[user.UserId]) {
            user.Friend = window.chatWindow.users[user.UserId].isFriend;
            user.Blocked = window.chatWindow.users[user.UserId].isBlocked;
        }

        var previousSelection = this.selectedUserId;
        var position = null;

        // Find the position to insert this user
        if (!this.privateChat) {
            if (this.subscribers.length > 0) {
                var sorted = [];
                for (var subscriber in this.subscribers) {
                    if (this.subscribers[subscriber]) {
                        sorted.push(this.subscribers[subscriber]);
                    }
                }

                sorted.sort(function(a, b) { return a.Nickname.toLowerCase() < b.Nickname.toLowerCase() ? -1 : 1; });

                for (var subscriber in sorted) {
                    if (user.Nickname.toLowerCase() > sorted[subscriber].Nickname.toLowerCase()) {
                        position = sorted[subscriber];
                    }
                }
            }
        }

        if (this.privateChat) {
            var html = "<div class='Subscriber" + user.UserId + "'>" +
                "<div class='Subscriber'>" +
                this.createDetailsPanel(user, true) +
                "</div>" +
                "<div class='Buttons'>" +
                    "<a target='_blank' href='/Users/" + encodeURIComponent(user.Username) + "'>View Profile</a>";

            if (!user.Friend) {
                html += "<a class='FriendButton' href='#'>Add Friend</a>";
            }

            html += "<a class='BlockButton' href='#'>Block</a>" +
                "</div></div>";
        } else {
            var tooltip = "";
            if (!this.guestMode) {
                tooltip = user.Gender + " / " + user.City + " /  " + user.Age;
            }

            var html = "<div class='Subscriber Subscriber" + user.UserId + "'>" +
                    "<a class='ShowSubscriber' href='#' title='" + tooltip + "'>" + "<img class='Mini' src='" + user.MicroImageUrl + "' />" + user.Nickname + "</a>" +
                "</div>";
        }

        user.subscriberElement = $(html);
        user.subscriberElement.data("userId", user.UserId);

        if (position == null) {
            this.subscriberList.prepend(user.subscriberElement);
        } else {
            position.subscriberElement.after(user.subscriberElement);
        }

        if (this.privateChat) {
            this.attachUserDetailsButtonEvents(user.subscriberElement, user);
        }

        if (user.Blocked) {
            user.subscriberElement.addClass("Blocked");
        }

        if (user.Moderator) {
            user.subscriberElement.addClass("Moderator");
        }

        if (user.Friend) {
            user.subscriberElement.addClass("Friend");
        }

        if (previousSelection) {
            this.selectSubscriber(previousSelection, true, this.selectUserFromClick);
        }
    }
};

ChatChannel.prototype.removeSubscriber = function(userId, reason) {
    var channel = this;
    if (userId == this.selectedUserId) {
        this.unselectSubscriber();
    }

    this.subscribers[userId] = null;
    this.subscriberCount--;
    this.subscriberList.find(".Subscriber" + userId).remove();
    if (userId == this.sessionUserId) {
        if (reason == Networker.LeaveType.IdleTimeout) {
            setTimeout(function() {
                window.chatWindow.leaveChannel(channel.channelId, "You have left the room due to inactivity.");
            }, 1);
        }
        else if (reason == Networker.LeaveType.ConnectionTimeout) {
            setTimeout(function() {
                window.chatWindow.leaveChannel(channel.channelId, "Something terrible happened and you have been disconnected from this room.");
            }, 1);
        }

        if (channel.guestMode) {
            document.location = document.location;
        }
    }

    this.selectSubscriber(this.selectedUserId, true, this.selectUserFromClick);
};

ChatChannel.prototype.updateSubscriber = function(userId) {
    var channel = this;
    $.ajax({
        url: channel.eventHandlerUrl,
        data: { Command: Networker.ChatCommand.UpdateSubscriber, UserId: userId },
        dataType: "json",
        success: function(response) { channel.handleUpdateSubscriberResponse(response); }
    });
};

ChatChannel.prototype.handleUpdateSubscriberResponse = function(response) {
    var subscriber = this.subscribers[response.UserId];
    if (subscriber) {
        subscriber.Friend = response.Friend;
        subscriber.Blocked = response.Blocked;

        if (subscriber.Friend) {
            subscriber.subscriberElement.addClass("Friend");
        } else {
            subscriber.subscriberElement.removeClass("Friend");
        }
    }
};

ChatChannel.prototype.showSubscriberMessages = function() {
    return this.preferences.ChatJoinLeaveMessages == 0 || (this.preferences.ChatJoinLeaveMessages == 1 && this.subscriberCount <= 10);
}

/*- Main message processor -------------------------------------------------------*/
ChatChannel.prototype.processMessage = function(type, sequence, data, requestedSequence) {
    if (this.disposed) {
        return;
    }
    var channel = this;
    try {
        switch (type) {
            case Networker.ChatMessageType.Text:
                if (requestedSequence === 0 || data[0] !== this.sessionUserId) {
                    if (sequence === 0 && !this.subscribers[data[0]]) {
                        return; // Skip past lines from people who are gone
                    }
                    this.addUserLine(data[0], data[1]);
                }
                break;

            case Networker.ChatMessageType.Join:
                if (requestedSequence !== 0 && !this.subscribers[data.UserId]) {
                    this.addSubscriber(data);
                }

                var reputation = "";
                if (data.Reputation.length > 0)
                    reputation = " (" + data.Reputation + ")";

                if (data.UserId != this.sessionUserId) {
                    if (this.showSubscriberMessages()) {
                        this.addLine(data.Nickname + reputation + " joined the room", "Control");
                    }
                }
                break;

            case Networker.ChatMessageType.Leave:
                var userId = data[0];
                var reason = data[1];
                if (this.showSubscriberMessages() && this.subscribers[userId] && userId != this.sessionUserId) {
                    var message = this.subscribers[userId].Nickname + " left the room";
                    if (this.debug && reason == Networker.LeaveType.User) {
                        message += " (user quit)";
                    }
                    this.addLine(message, "Control");
                } else {
                    if (this.debug) {
                        this.addLine("User " + userId + " left the room", "Control");
                    }
                }

                if (requestedSequence !== 0) {
                    this.removeSubscriber(userId, reason);
                }
                break;

            case Networker.ChatMessageType.UpdateUser:
                if (requestedSequence === 0) {
                    break;
                }

                var user = this.subscribers[data.UserId];
                if (user) {
                    user.FontColor = data.FontColor;
                    if (data.FontFace) {
                        user.FontFace = data.FontFace;
                    }
                    if (data.FontColor) {
                        user.FontColor = data.FontColor;
                    }
                }
                break;

            case Networker.ChatMessageType.SuspendUser:
                if (data === this.sessionUserId) {
                    window.chatWindow.leaveChannel(this.channelId, "You have been suspended from this room for 24 hours.");
                } else {
                    if (this.subscribers[data]) {
                        this.addLine(this.subscribers[data].Nickname + " has been suspended", "Control");
                    }
                }
                break;

            case Networker.ChatMessageType.Disconnect:
                if (this.debug) {
                    alert("Disconnecting from channel...");
                }
                window.chatWindow.leaveChannel(this.channelId);
                break;

            case Networker.ChatMessageType.Notification:
                this.addLine(data);
                break;

        }
    } catch (ex) {
        var message = "Error processing message 2: " + Networker.formatException(ex) + "\nType: " + type + "\nSequence: " + sequence + "\nData: " + data + "\nFocus: " + this.hasFocus + "\nXXZZXX ";
        setTimeout(function() {
            window.chatWindow.logError(message);
        }, 1);
    }
};

/*- Advertising ------------------------------------------------------------------*/
ChatChannel.prototype.getCreative = function() {
    var channel = this;
    if (!this.disposed && this.linesSinceAdvertisment >= 15 && this.hasFocus) {
        $.ajax({
            url: channel.eventHandlerUrl,
            data: { Command: Networker.ChatCommand.GetCreative },
            dataType: "html",
            success: function(response) { channel.handleGetCreativeResponse(response); }
        });
    } else {
        setTimeout(function() { channel.getCreative(); }, 5 * 60 * 1000);
    }
};

ChatChannel.prototype.handleGetCreativeResponse = function(response) {
    if (this.disposed) {
        return;
    }

    if (response.length > 0) {
        this.linesSinceAdvertisment = 0;
        this.addLine(response, "Ad");
    }
    var channel = this;
    setTimeout(function() { channel.getCreative(); }, 5 * 60 * 1000);
};

/*- UI Utilities ------------------------------------------------------------------*/
ChatChannel.prototype.focus = function() {
    this.hasFocus = true;
    if (this.highlight) {
        this.parentTab.removeClass("Highlight");
        this.parentTab.css("background-color", "");
        this.highlight = false;
    }

    if (this.previousInputText && this.previousInputText.length > 0) {
        this.inputLine.val(this.previousInputText);
        this.previousInputText = "";
    } else {
        this.inputLine.val("");
    }

    this.channelBody.show();
    this.subscriberList.show();
};

ChatChannel.prototype.blur = function() {
    this.hasFocus = false;
    this.unselectSubscriber();
    this.previousInputText = this.inputLine.val();
    this.channelBody.hide();
    this.subscriberList.hide();
};

ChatChannel.prototype.handleResize = function(headerHeight) {

    if ($(window).width() < 400) {
        $("#SubscriberListWrap").hide();
        this.minimalMode = true;
    } else {
        $("#SubscriberListWrap").show();
        this.minimalMode = false;
    }

    var inputAreaHeight = window.chatWindow.inputAreaWrap.height();
    this.channelBody.height($(window).height() - headerHeight - inputAreaHeight - 15);
    this.channelBody.css("background-position", "left " + (headerHeight + 5) + "px");
    this.subscriberList.height($(window).height() - headerHeight - 15);
    window.chatWindow.inputAreaWrap.width(this.channelBody.outerWidth());
    window.chatWindow.inputLine.width(this.channelBody.outerWidth() - 80);

    this.scrollToEnd();
};