﻿/*
 * @package AJAX_Chat
 * @author Sebastian Tschan
 * @copyright (c) Sebastian Tschan
 * @license GNU Affero General Public License
 * @link https://blueimp.net/ajax/
 *
 * The SELFHTML documentation has been used throughout this project:
 * http://selfhtml.org
 *
 * Stylesheet and cookie methods have been inspired by Paul Sowden (A List Apart):
 * http://www.alistapart.com/stories/alternate/
 */

// AJAX Chat client side logic:
var ajaxChat = {

        settingsInitiated: null,
        styleInitiated: null,
        initializeFunction: null,
        finalizeFunction: null,
        loginChannelID: null,
        loginChannelName: null,
        timerRate: null,
        timer: null,
        ajaxURL: null,
        baseURL: null,
        regExpMediaUrl: null,
        dirs: null,
        startChatOnLoad: null,
        chatStarted: null,
        domIDs: null,
        dom: null,
        settings: null,
        nonPersistentSettings: null,
        unusedSettings: null,
        bbCodeTags: null,
        colorCodes: null,
        emoticonCodes: null,
        emoticonFiles: null,
        soundFiles: null,
        sounds: null,
        soundTransform: null,
        sessionName: null,
        cookieExpiration: null,
        cookiePath: null,
        cookieDomain: null,
        cookieSecure: null,
        chatBotName: null,
        chatBotID: null,
        allowUserMessageDelete: null,
        inactiveTimeout: null,
        privateChannelDiff: null,
        privateMessageDiff: null,
        showChannelMessages: null,
        messageTextMaxLength: null,
        userID: null,
        userName: null,
        userRole: null,
        channelID: null,
        channelName: null,
        channelSwitch: null,
        usersList: null,
        userNamesList: null,
        userMenuCounter: null,
        encodedUserName: null,
        userNodeString: null,
        ignoredUserNames: null,
        lastID: null,
        localID: null,
        lang: null,
        langCode: null,
        baseDirection: null,
        originalDocumentTitle: null,
        blinkInterval: null,
        httpRequest: null,

        init: function(config, lang, initSettings, initStyle, initialize, initializeFunction, finalizeFunction) {
                this.httpRequest                = new Object();
                this.usersList                        = new Array();
                this.userNamesList                = new Array();
                this.userMenuCounter        = 0;
                this.lastID                                = 0;
                this.localID                        = 0;
                this.lang                                = lang;
                this.initConfig(config);
                this.initDirectories();
                if(initSettings) {
                        this.initSettings();
                }
                if(initStyle) {
                        this.initStyle();
                }
                this.initializeFunction = initializeFunction;
                this.finalizeFunction = finalizeFunction;
                if(initialize) {
                        this.setLoadHandler();
                }
        },

        initConfig: function(config) {
                this.loginChannelID                        = config['loginChannelID'];
                this.loginChannelName                = config['loginChannelName'];
                this.timerRate                                = config['timerRate'];
                this.ajaxURL                                = config['ajaxURL'];
                this.baseURL                                = config['baseURL'];
                this.regExpMediaUrl                        = config['regExpMediaUrl'];
                this.startChatOnLoad                = config['startChatOnLoad'];
                this.domIDs                                        = config['domIDs'];
                this.settings                                = config['settings'];
                this.nonPersistentSettings        = config['nonPersistentSettings'];
                this.bbCodeTags                                = config['bbCodeTags'];
                this.colorCodes                                = config['colorCodes'];
                this.emoticonCodes                        = config['emoticonCodes'];
                this.emoticonFiles                        = config['emoticonFiles'];
                this.sessionName                        = config['sessionName'];
                this.cookieExpiration                = config['cookieExpiration'];
                this.cookiePath                                = config['cookiePath'];
                this.cookieDomain                        = config['cookieDomain'];
                this.cookieSecure                        = config['cookieSecure'];
                this.chatBotName                        = config['chatBotName'];
                this.chatBotID                                = config['chatBotID'];
                this.allowUserMessageDelete        = config['allowUserMessageDelete'];
                this.inactiveTimeout                = config['inactiveTimeout'];
                this.privateChannelDiff                = config['privateChannelDiff'];
                this.privateMessageDiff                = config['privateMessageDiff'];
                this.showChannelMessages        = config['showChannelMessages'];
                this.messageTextMaxLength        = config['messageTextMaxLength'];
        },

        initDirectories: function() {
                this.dirs = new Object();
                this.dirs['emoticons']         = this.baseURL+'img/emoticons/';
                this.dirs['flash']                = this.baseURL+'flash/';
        },

        initSettings: function() {
                this.settingsInitiated = true;
                this.unusedSettings = new Object();
                var cookie = this.readCookie(this.sessionName + '_settings');
                if(cookie) {
                        var settingsArray = cookie.split('&');
                        var setting,key,value,number;
                        for(var i=0; i<settingsArray.length; i++) {
                                setting = settingsArray[i].split('=');
                                if(setting.length == 2) {
                                        key = setting[0];
                                        value = this.decodeText(setting[1]);
                                        switch(value) {
                                                case 'true':
                                                        value = true;
                                                        break;
                                                case 'false':
                                                        value = false;
                                                        break;
                                                case 'null':
                                                        value = null;
                                                        break;
                                                default:
                                                        number = parseFloat(value);
                                                        if(!isNaN(number)) {
                                                                if(parseInt(number) == number) {
                                                                        value = parseInt(number);
                                                                } else {
                                                                        value = number;
                                                                }
                                                        }
                                        }
                                        if(this.inArray(this.nonPersistentSettings, key)) {
                                                // The setting is not used, store it for the persistSettings method:
                                                this.unusedSettings[key] = value;
                                        } else {
                                                this.settings[key] = value;
                                        }
                                }
                        }
                }
        },

        persistSettings: function() {
                if(this.settingsInitiated) {
                        var settingsArray = new Array();
                        for(var property in this.settings) {
                                if(this.inArray(this.nonPersistentSettings, property)) {
                                        if(this.unusedSettings && this.unusedSettings[property]) {
                                                // Store the unusedSetting previously stored:
                                                this.settings[property] = this.unusedSettings[property];
                                        } else {
                                                continue;
                                        }
                                }
                                settingsArray.push(property + '=' + this.encodeText(this.settings[property]));
                        }
                        this.createCookie(this.sessionName + '_settings', settingsArray.join('&'), this.cookieExpiration);
                }
        },

        getSettings: function() {
                return this.settings;
        },

        getSetting: function(key) {
                // Only return null if setting is null or undefined, not if it is false:
                for(var property in this.settings) {
                        if(property == key) {
                                return this.settings[key];
                        }
                }
                return null;
        },

        setSetting: function(key, value) {
                this.settings[key] = value;
        },

        initializeSettings: function() {
                if(this.settings['persistFontColor'] && this.settings['fontColor']) {
                        // Set the inputField font color to the font color:
                        if(this.dom['inputField']) {
                                this.dom['inputField'].style.color = this.settings['fontColor'];
                        }
                }
        },

        initialize: function() {
                this.setUnloadHandler();
                this.initializeDocumentNodes();
                this.loadPageAttributes();
                this.initColorCodes();
                this.initializeSettings();
                this.setSelectedStyle();
                this.customInitialize();
                if(typeof this.initializeFunction == 'function') {
                        this.initializeFunction();
                }
                if(!this.isCookieEnabled()) {
                        this.addChatBotMessageToChatList('/error CookiesRequired');
                } else {
                        if(this.startChatOnLoad) {
                                this.startChat();
                        } else {
                                this.setStartChatHandler();
                                this.requestTeaserContent();
                        }
                }

// Willkommensmessage:

this.addChatBotMessageToChatList('[b]Willkommen im Hoerbar Chat[/b]<img src="http://diehoerbar.net/forum/chat/img/emoticons/hi.gif" />');

        },





        requestTeaserContent: function() {
                var params = '&view=teaser';
                params += '&getInfos=' + this.encodeText('userID,userName,userRole');
                if(!isNaN(parseInt(this.loginChannelID))) {
                        params += '&channelID='+this.loginChannelID;
                } else if(this.loginChannelName !== null) {
                        params += '&channelName='+this.encodeText(this.loginChannelName);
                }
                this.updateChat(params);
        },

        setStartChatHandler: function() {
                if(this.dom['inputField']) {
                        this.dom['inputField'].onfocus = function() {
                                ajaxChat.startChat();
                                // Reset the onfocus event on first call:
                                ajaxChat.dom['inputField'].onfocus = '';
                        }
                }
        },

        startChat: function() {
                this.chatStarted = true;
                if(this.dom['inputField'] && this.settings['autoFocus']) {
                        this.dom['inputField'].focus();
                }
                this.loadFlashInterface();
                this.startChatUpdate();
        },

        loadPageAttributes: function() {
                var htmlTag                        = document.getElementsByTagName('html')[0];
                this.langCode                = htmlTag.getAttribute('lang')        ? htmlTag.getAttribute('lang')        : 'en';
                this.baseDirection        = htmlTag.getAttribute('dir')        ? htmlTag.getAttribute('dir')        : 'ltr';
        },

        setLoadHandler: function() {
                // Make sure initialize() is called on page load:
                  var onload = window.onload;
                if(typeof onload != 'function') {
                        window.onload = function() {
                                ajaxChat.initialize();
                        }
                } else {
                        window.onload = function() {
                                onload();
                                ajaxChat.initialize();
                        }
                }
        },

        setUnloadHandler: function() {
                // Make sure finalize() is called on page unload:
                  var onunload = window.onunload;
                if(typeof onunload != 'function') {
                        window.onunload = function() {
                                ajaxChat.finalize();
                        }
                } else {
                        window.onunload = function() {
                                ajaxChat.finalize();
                                onunload();
                        }
                }
        },

        updateDOM: function(id, str, prepend, overwrite) {
                var domNode = this.dom[id] ? this.dom[id] : document.getElementById(id);
                if(!domNode) {
                        return;
                }
                try {
                        // Test for validity before adding the string to the DOM:
                        domNode.cloneNode(false).innerHTML = str;
                        if(overwrite) {
                                domNode.innerHTML = str;
                        } else if(prepend) {
                                domNode.innerHTML = str + domNode.innerHTML;
                        } else {
                                domNode.innerHTML += str;
                        }
                } catch(e) {
                        this.addChatBotMessageToChatList('/error DOMSyntax '+id);
                        this.updateChatlistView();
                }
        },

        initializeDocumentNodes: function() {
                this.dom = new Object();
                for(var key in this.domIDs) {
                        this.dom[key] = document.getElementById(this.domIDs[key]);
                }
        },

        initColorCodes: function() {
                if(this.dom['colorCodesContainer']) {
                        for(var i=0; i<this.colorCodes.length; i++) {
                                this.updateDOM(
                                        'colorCodesContainer',
                                        '<a href="javascript:ajaxChat.setFontColor(\''
                                        + this.colorCodes[i]
                                        + '\');" style="background-color:'
                                        + this.colorCodes[i]
                                        + ';" title="'
                                        + this.colorCodes[i]
                                        + '"></a>'
                                        + "\n"
                                );
                        }
                }
        },

        startChatUpdate: function() {
                // Start the chat update and retrieve current user and channel info and set the login channel:
                var infos = 'userID,userName,userRole,channelID,channelName';

                var params = '&getInfos=' + this.encodeText(infos);
                if(!isNaN(parseInt(this.loginChannelID))) {
                        params += '&channelID='+this.loginChannelID;
                } else if(this.loginChannelName !== null) {
                        params += '&channelName='+this.encodeText(this.loginChannelName);
                }
                this.updateChat(params);
        },

        updateChat: function(paramString) {
                var requestUrl = this.ajaxURL
                                                + '&lastID='
                                                + this.lastID;
                if(paramString) {
                        requestUrl += paramString;
                }
                this.makeRequest(requestUrl,'GET',null);
        },



        loadFlashInterface: function() {
                if(this.dom['flashInterfaceContainer']) {
                        this.updateDOM(
                                'flashInterfaceContainer',
                                '<object id="ajaxChatFlashInterface" style="position:absolute; left:-100px;" '
                                +'classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" '
                                +'codebase="'
                                + window.location.protocol
                                +'//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" '
                                +'height="1" width="1">'
                                +'<param name="flashvars" value="bridgeName=ajaxChat"/>'
                                +'<param name="src" value="'+this.dirs['flash']+'FABridge.swf"/>'
                                +'<embed name="ajaxChatFlashInterface" pluginspage="'
                                + window.location.protocol
                                +'//www.macromedia.com/go/getflashplayer" '
                                +'src="'+this.dirs['flash']+'FABridge.swf" height="1" width="1" flashvars="bridgeName=ajaxChat"/>'
                                +'</object>'
                        );
                        FABridge.addInitializationCallback('ajaxChat', this.flashInterfaceLoadCompleteHandler);
                }
        },

        flashInterfaceLoadCompleteHandler: function() {
                ajaxChat.initializeFlashInterface();
        },

        initializeFlashInterface: function() {


                this.initializeCustomFlashInterface();
        },



        loadXML: function(str) {
                if(!arguments.callee.parser) {
                        try {
                                // DOMParser native implementation (Mozilla, Opera):
                                arguments.callee.parser = new DOMParser();
                        } catch(e) {
                                var customDOMParser = function() {}
                                if(navigator.appName == 'Microsoft Internet Explorer') {
                                        // IE implementation:
                                        customDOMParser.prototype.parseFromString = function(str, contentType) {
                                                if(!arguments.callee.XMLDOM) {
                                                        arguments.callee.XMLDOM = new ActiveXObject('Microsoft.XMLDOM');
                                                }
                                                arguments.callee.XMLDOM.loadXML(str);
                                                return arguments.callee.XMLDOM;
                                        }
                                } else {
                                        // Safari, Konqueror:
                                        customDOMParser.prototype.parseFromString = function(str, contentType) {
                                                if(!arguments.callee.httpRequest) {
                                                        arguments.callee.httpRequest = new XMLHttpRequest();
                                                }
                                                arguments.callee.httpRequest.open(
                                                        'GET',
                                                        'data:text/xml;charset=utf-8,'+encodeURIComponent(str),
                                                        false
                                                );
                                                arguments.callee.httpRequest.send(null);
                                                return arguments.callee.httpRequest.responseXML;
                                        }
                                }
                                arguments.callee.parser = new customDOMParser();
                        }
                }
                return arguments.callee.parser.parseFromString(str, 'text/xml');
        },

        getHttpRequest: function(identifier) {
                if(!this.httpRequest[identifier]) {
                        if (window.XMLHttpRequest) {
                                this.httpRequest[identifier] = new XMLHttpRequest();
                                if (this.httpRequest[identifier].overrideMimeType) {
                                        this.httpRequest[identifier].overrideMimeType('text/xml');
                                }
                        } else if (window.ActiveXObject) {
                                try {
                                        this.httpRequest[identifier] = new ActiveXObject("Msxml2.XMLHTTP");
                                } catch (e) {
                                        try {
                                                this.httpRequest[identifier] = new ActiveXObject("Microsoft.XMLHTTP");
                                        } catch (e) {
                                        }
                                }
                        }
                }
                return this.httpRequest[identifier];
        },

        makeRequest: function(url, method, data) {
                if (document.getElementById('statusIconContainer') != null ) {
                        document.getElementById('statusIconContainer').className = 'statusContainerOn';
                }
                try {
                        var identifier;
                        if(data) {
                                // Create up to 50 HTTPRequest objects:
                                if(!arguments.callee.identifier || arguments.callee.identifier > 50) {
                                        arguments.callee.identifier = 1;
                                } else {
                                        arguments.callee.identifier++;
                                }
                                identifier = arguments.callee.identifier;
                        } else {
                                identifier = 0;
                        }
                        this.getHttpRequest(identifier).open(method, url, true);
                        this.getHttpRequest(identifier).onreadystatechange = function() {
                                try {
                                        ajaxChat.handleResponse(identifier);
                                } catch(e) {
                                        try {
                                                clearTimeout(ajaxChat.timer);
                                        } catch(e) {
                                                //alert(e);
                                        }
                                        try {
                                                if(data) {
                                                        ajaxChat.addChatBotMessageToChatList('/error ConnectionTimeout');
                                                        ajaxChat.updateChatlistView();
                                                }
                                        } catch(e) {
                                                //alert(e);
                                        }
                                        try {
                                                ajaxChat.timer = setTimeout('ajaxChat.updateChat(null);', ajaxChat.timerRate);
                                        } catch(e) {
                                                //alert(e);
                                        }
                                }
                        };
                        if(method == 'POST') {
                                this.getHttpRequest(identifier).setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                        }
                        this.getHttpRequest(identifier).send(data);
                } catch(e) {
                        clearTimeout(this.timer);
                        if(data) {
                                this.addChatBotMessageToChatList('/error ConnectionTimeout');
                                this.updateChatlistView();
                        }
                        this.timer = setTimeout('ajaxChat.updateChat(null);', this.timerRate);
                }
        },

        handleResponse: function(identifier) {
                if (this.getHttpRequest(identifier).readyState == 4) {
                        if (this.getHttpRequest(identifier).status == 200) {
                                var xmlDoc = this.getHttpRequest(identifier).responseXML;
                                if (document.getElementById('statusIconContainer') != null) {
                                        document.getElementById('statusIconContainer').className = 'statusContainerOff';
                                }
                        } else {
                                 if (this.getHttpRequest(identifier).status != 0) {
               this.addChatBotMessageToChatList('/error ConnectionStatus '+this.getHttpRequest(identifier).status);
            }

                                this.updateChatlistView();
                                return false;
                        }
                }
                if(!xmlDoc) {
                        return false;
                }
                this.handleXML(xmlDoc);
                return true;
        },

        handleXML: function(xmlDoc) {
                this.handleInfoMessages(xmlDoc.getElementsByTagName('info'));
                this.handleOnlineUsers(xmlDoc.getElementsByTagName('user'));
                this.handleChatMessages(xmlDoc.getElementsByTagName('message'));
                this.channelSwitch = null;
                this.setChatUpdateTimer();
        },

                setChatUpdateTimer: function() {
                clearTimeout(this.timer);
                if(this.chatStarted) {
                        var timeout;
                        if(this.socketIsConnected) {
                                timeout = this.socketTimerRate;
                        } else {
                                timeout = this.timerRate;
                        }
                        this.timer = setTimeout('ajaxChat.updateChat(null);', timeout);
                }
        },

        handleInfoMessages: function(infoNodes) {
                var infoType, infoData;
                for(var i=0; i<infoNodes.length; i++) {
                        infoType = infoNodes[i].getAttribute('type');
                        infoData = infoNodes[i].firstChild ? infoNodes[i].firstChild.nodeValue : '';
                        this.handleInfoMessage(infoType, infoData);
                }
        },

        handleInfoMessage: function(infoType, infoData) {
                switch(infoType) {
                        case 'channelSwitch':
                                this.clearChatList();
                                this.clearOnlineUsersList();
                                this.setSelectedChannel(infoData);
                                this.channelName = infoData;
                                this.channelSwitch = true;
                                break;
                        case 'channelName':
                                this.setSelectedChannel(infoData);
                                this.channelName = infoData;
                                break;
                        case 'channelID':
                                this.channelID = infoData;
                                break;
                        case 'userID':
                                this.userID = infoData;
                                break;
                        case 'userName':
                                this.userName = infoData;
                                this.encodedUserName = this.scriptLinkEncode(this.userName);
                                this.userNodeString = null;
                                break;
                        case 'userRole':
                                this.userRole = infoData;
                                break;
                        case 'logout':
                                this.handleLogout(infoData);
                                break;

                        default:
                                this.handleCustomInfoMessage(infoType, infoData);
                }
        },

        handleOnlineUsers: function(userNodes) {
                if(userNodes.length) {
                        var index,userID,userName,userRole;
                        var onlineUsers = new Array();
                        for(var i=0; i<userNodes.length; i++) {
                                userID = userNodes[i].getAttribute('userID');
                                userName = userNodes[i].firstChild ? userNodes[i].firstChild.nodeValue : '';
                                userRole = userNodes[i].getAttribute('userRole');
                                onlineUsers.push(userID);
                                index = this.arraySearch(userID, this.usersList);
                                if(index === false) {
                                        this.addUserToOnlineList(
                                                userID,
                                                userName,
                                                userRole
                                        );
                                } else if(this.userNamesList[index] != userName) {
                                        this.removeUserFromOnlineList(userID, index);
                                        this.addUserToOnlineList(
                                                userID,
                                                userName,
                                                userRole
                                        );
                                }
                        }
                        // Clear the offline users from the online users list:
                        for(var i=0; i<this.usersList.length; i++) {
                                if(!this.inArray(onlineUsers, this.usersList[i])) {
                                        this.removeUserFromOnlineList(this.usersList[i], i);
                                }
                        }
                        this.setOnlineListRowClasses();
                }
        },

        handleChatMessages: function(messageNodes) {
                if(messageNodes.length) {
                        var userNode,userName,textNode,messageText;
                        for(var i=0; i<messageNodes.length; i++) {
                                userNode = messageNodes[i].getElementsByTagName('username')[0];
                                userName = userNode.firstChild ? userNode.firstChild.nodeValue : '';
                                textNode = messageNodes[i].getElementsByTagName('text')[0];
                                messageText = textNode.firstChild ? textNode.firstChild.nodeValue : '';
                                this.addMessageToChatList(
                                                new Date(messageNodes[i].getAttribute('dateTime')),
                                                messageNodes[i].getAttribute('userID'),
                                                userName,
                                                messageNodes[i].getAttribute('userRole'),
                                                messageNodes[i].getAttribute('id'),
                                                messageText,
                                                messageNodes[i].getAttribute('channelID'),
                                                messageNodes[i].getAttribute('ip')
                                );
                        }
                        this.updateChatlistView();
                        this.lastID = messageNodes[messageNodes.length-1].getAttribute('id');
                }
        },

        setSelectedChannel: function(channel) {
                if(this.dom['channelSelection']) {
                        // Replace the entities in the channel name with their character equivalent:
                        channel = this.decodeSpecialChars(channel);
                        var channelSelected = false;
                        for(var i=0; i<this.dom['channelSelection'].options.length; i++) {
                                if(this.dom['channelSelection'].options[i].value == channel) {
                                        this.dom['channelSelection'].options[i].selected = true;
                                        channelSelected = true;
                                        break;
                                }
                        }
                        // The given channel is not in the list, add it:
                        if(!channelSelected) {
                                var option = document.createElement('option');
                                var text = document.createTextNode(channel);
                                option.appendChild(text);
                                option.setAttribute('value', channel);
                                option.setAttribute('selected', 'selected');
                                this.dom['channelSelection'].appendChild(option);
                        }
                }
        },

        removeUserFromOnlineList: function(userID, index) {
                this.usersList.splice(index, 1);
                this.userNamesList.splice(index, 1);
                if(this.dom['onlineList']) {
                        this.dom['onlineList'].removeChild(this.getUserNode(userID));
                }
        },

        addUserToOnlineList: function(userID, userName, userRole) {
                this.usersList.push(userID);
                this.userNamesList.push(userName);
                if(this.dom['onlineList']) {
                        this.updateDOM(
                                'onlineList',
                                this.getUserNodeString(userID, userName, userRole),
                                (this.userID == userID)
                        );
                }
        },



        getUserNodeString: function(userID, userName, userRole) {
                if(this.userNodeString && userID == this.userID) {
                        return this.userNodeString;
                } else {
                        var encodedUserName = this.scriptLinkEncode(userName);
                        var str        = '<div id="'
                                        + this.getUserDocumentID(userID)
                                        + '"><a href="javascript:ajaxChat.toggleUserMenu(\''
                                        + this.getUserMenuDocumentID(userID)
                                        + '\', \''
                                        + encodedUserName
                                        + '\', '
                                        + userID
                                        + ');" class="'
                                        + this.getRoleClass(userRole)
                                        + '" title="'
                                        + this.lang['toggleUserMenu'].replace(/%s/, userName)
                                        + '">'
                                        + userName
                                        + '</a>'
                                        + '<ul class="userMenu" id="'
                                        + this.getUserMenuDocumentID(userID)
                                        + '"'
                                        + ((userID == this.userID) ?
                                                '>'+this.getUserNodeStringItems(encodedUserName, userID) :
                                                ' style="display:none;">')
                                        + '</ul>'
                                        +'</div>';
                        if(userID == this.userID) {
                                this.userNodeString = str;
                        }
                        return str;
                }
        },

        toggleUserMenu: function(menuID, userName, userID) {
                if(!document.getElementById(menuID).firstChild) {
                        this.updateDOM(
                                menuID,
                                this.getUserNodeStringItems(
                                        this.encodeText(this.addSlashes(this.getScriptLinkValue(userName))),
                                        userID
                                ),
                                false,
                                true
                        )
                }
                this.showHide(menuID);
                this.dom['chatList'].scrollBottom = this.dom['chatList'].scrollHeight;
        },

        getUserNodeStringItems: function(encodedUserName, userID) {
                var menu;
                if(encodedUserName != this.encodedUserName) {
                        menu         = '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/f '
                                        + encodedUserName
                                        + ' \');">'
                                        + this.lang['userMenuSendPrivateMessage']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/describe '
                                        + encodedUserName
                                        + ' \');">'
                                        + this.lang['userMenuDescribe']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/query '
                                        + encodedUserName
                                        + '\');">'
                                        + this.lang['userMenuOpenPrivateChannel']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/query\');">'
                                        + this.lang['userMenuClosePrivateChannel']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/invite '
                                        + encodedUserName
                                        + '\');">'
                                        + this.lang['userMenuInvite']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/uninvite '
                                        + encodedUserName
                                        + '\');">'
                                        + this.lang['userMenuUninvite']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/ignore '
                                        + encodedUserName
                                        + '\');">'
                                        + this.lang['userMenuIgnore']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/whereis '
                                        + encodedUserName
                                        + '\');">'
                                        + this.lang['userMenuWhereis']
                                        + '</a></li>';
                        if(this.userRole == 2 || this.userRole == 3 || this.userRole == 6) {
                                menu        += '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/kick '
                                                + encodedUserName
                                                + ' \');">'
                                                + this.lang['userMenuKick']
                                                + '</a></li>'
                                                + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/whois '
                                                + encodedUserName
                                                + '\');">'
                                                + this.lang['userMenuWhois']
                                                + '</a></li>';
                        }
                } else {
                        menu    = '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/away \');">'
                                        + this.lang['userMenuAFK']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/who\');">'
                                        + this.lang['userMenuWho']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/ignore\');">'
                                        + this.lang['userMenuIgnoreList']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/list\');">'
                                        + this.lang['userMenuList']
                                        + '</a></li>'
                                        + '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/roll \');">'
                                        + this.lang['userMenuRoll']
                                        + '</a></li>';

                        if(this.userRole == 1 || this.userRole == 3 || this.userRole == 5 || this.userRole == 6) {
                                menu        += '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/join\');">'
                                                + this.lang['userMenuEnterPrivateRoom']
                                                + '</a></li>';
                                if(this.userRole == 2 || this.userRole == 3 || this.userRole == 6) {
                                        menu        += '<li><a href="javascript:ajaxChat.sendMessageWrapper(\'/bans\');">'
                                                        + this.lang['userMenuBans']
                                                        + '</a></li>'
                                                        +'<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/comment \');">'
                                                        + this.lang['userMenuCom']
                                                        + '</a></li>'
                                                        + '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/team \');">'
                                                        + this.lang['userMenuTeam']
                                                        + '</a></li>'
                                                        + '<li><a href="javascript:ajaxChat.insertMessageWrapper(\'/nick \');">'
                                                        + this.lang['userMenuNick']
                                                        + '</a></li>'
                                                        +'<li><a style="cursor: pointer;" onclick=\'window.open("http://diehoerbar.net/Mediabox/chat_zh_admin/","Hoererzahlen","scrollbars=yes,width=180,height=80,resizable=yes,toolbar=no,status=no,directories=no,menubar=no,location=no, left=600,top=400");\'>Hörerzahlen</a></li>';

                                }

                        }
                }
                menu += this.getCustomUserMenuItems(encodedUserName, userID);
                return menu;
        },

        setOnlineListRowClasses: function() {
                if(this.dom['onlineList']) {
                        var node = this.dom['onlineList'].firstChild;
                        var rowEven = false;
                        while(node) {
                                this.setClass(node, (rowEven ? 'rowEven' : 'rowOdd'))
                                node = node.nextSibling;
                                rowEven = !rowEven;
                        }
                }
        },

        clearChatList: function() {
                while(this.dom['chatList'].hasChildNodes()) {
                        this.dom['chatList'].removeChild(this.dom['chatList'].firstChild);
                }
        },

        clearOnlineUsersList: function() {
                this.usersList = new Array();
                this.userNamesList = new Array();
                if(this.dom['onlineList']) {
                        while(this.dom['onlineList'].hasChildNodes()) {
                                this.dom['onlineList'].removeChild(this.dom['onlineList'].firstChild);
                        }
                }
        },

        getEncodedChatBotName: function() {
                if(typeof arguments.callee.encodedChatBotName == 'undefined') {
                        arguments.callee.encodedChatBotName = this.encodeSpecialChars(this.chatBotName);
                }
                return arguments.callee.encodedChatBotName;
        },

        addChatBotMessageToChatList: function(messageText) {
                this.addMessageToChatList(
                        new Date(),
                        this.chatBotID,
                        this.getEncodedChatBotName(),
                        4,
                        null,
                        messageText,
                        null
                );
        },

        addMessageToChatList: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
                // Prevent adding the same message twice:
                if(this.getMessageNode(messageID)) {
                        return;
                }
                if(!this.onNewMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip)) {
                        return;
                }
                this.updateDOM(
                        'chatList',
                        this.getChatListMessageString(
                                dateObject, userID, userName, userRole, messageID, messageText, channelID, ip
                        )
                )
        },

        getChatListMessageString: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
                var rowClass =         this.dom['chatList'].lastChild && this.getClass(this.dom['chatList'].lastChild) == 'rowOdd'
                                                ? 'rowEven' : 'rowOdd';
                var userClass = this.getRoleClass(userRole);
                var colon;
                if(messageText.indexOf('/action') == 0 || messageText.indexOf('/me') == 0 || messageText.indexOf('/privaction') == 0) {
                        userClass += ' action';
                        colon = ' ';
                } else {
                        colon = ': ';
                }
                var dateTime = this.settings['dateFormat'] ? '<span class="dateTime">'
                                                + this.formatDate(this.settings['dateFormat'], dateObject) + '</span> ' : '';
                return        '<div id="'
                                + this.getMessageDocumentID(messageID)
                                + '" class="'
                                + rowClass
                                + '">'
                                + this.getDeletionLink(messageID, userID, userRole, channelID)
                                + dateTime
                                + '<span class="'
                                + userClass
                                + '"'
                                + this.getChatListUserNameTitle(userID, userName, userRole, ip)
                                + ' dir="'
                                + this.baseDirection
                                + '" onclick="ajaxChat.insertText2(this.firstChild.nodeValue);">'
                                + userName
                                + '</span>'
                                + colon
                                + this.replaceText(messageText)
                                + '</div>';
        },

        getChatListUserNameTitle: function(userID, userName, userRole, ip) {
                return (ip != null) ? ' title="IP: ' + ip + '"' : '';
        },

        getMessageDocumentID: function(messageID) {
                return ((messageID === null) ? 'ajaxChat_lm_'+(this.localID++) : 'ajaxChat_m_'+messageID);
        },

        getMessageNode: function(messageID) {
                return ((messageID === null) ? null : document.getElementById(this.getMessageDocumentID(messageID)));
        },

        getUserDocumentID: function(userID) {
                return 'ajaxChat_u_'+userID;
        },

        getUserNode: function(userID) {
                return document.getElementById(this.getUserDocumentID(userID));
        },

        getUserMenuDocumentID: function(userID) {
                return 'ajaxChat_um_'+userID;
        },

        getInlineUserMenuDocumentID: function(menuID, index) {
                return 'ajaxChat_ium_'+menuID+'_'+index;
        },

        getDeletionLink: function(messageID, userID, userRole, channelID) {
                if(messageID !== null && this.isAllowedToDeleteMessage(messageID, userID, userRole, channelID)) {
                        if(!arguments.callee.deleteMessage) {
                                arguments.callee.deleteMessage = this.encodeSpecialChars(this.lang['deleteMessage']);
                        }
                        return        '<a class="delete" title="'
                                        + arguments.callee.deleteMessage
                                        + '" href="javascript:ajaxChat.deleteMessage('
                                        + messageID
                                        + ');"> </a>' // Adding a space - without any content Opera messes up the chatlist display
                }
                return '';
        },

        isAllowedToDeleteMessage: function(messageID, userID, userRole, channelID) {
                if(((((this.userRole == 1 || this.userRole == 5 || this.userRole == 6) && this.allowUserMessageDelete && (userID == this.userID ||
                        parseInt(channelID) == parseInt(this.userID)+this.privateMessageDiff ||
                        parseInt(channelID) == parseInt(this.userID)+this.privateChannelDiff)) ||
                        this.userRole == 2) && userRole != 3 && userRole != 4) || this.userRole == 3) {
                        return true;
                }
                return false;
        },

        onNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
                if(!this.customOnNewMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip)) {
                        return false;
                }
                if(this.ignoreMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip)) {
                        return false;
                }
                if(this.parseDeleteMessageCommand(messageText)) {
                        return false;
                }

        return true;
        },

        parseDeleteMessageCommand: function(messageText) {
                if(messageText.indexOf('/delete') == 0) {
                        var messageID = messageText.substr(8);
                        var messageNode = this.getMessageNode(messageID);
                        if(messageNode) {
                                var nextSibling = messageNode.nextSibling;
                                try {
                                        this.dom['chatList'].removeChild(messageNode);
                                        if(nextSibling) {
                                                this.updateChatListRowClasses(nextSibling);
                                        }
                                } catch(e) {
                                }
                        }
                        return true;
                }
                return false;
        },

        updateChatlistView: function() {
                if(this.dom['chatList'].childNodes && this.settings['maxMessages']) {
                        while(this.dom['chatList'].childNodes.length > this.settings['maxMessages']) {
                                this.dom['chatList'].removeChild(this.dom['chatList'].firstChild);
                        }
                }

                if(this.settings['autoScroll']) {
                this.dom['chatList'].scrollTop = this.dom['chatList'].scrollHeight;
                }
        },

        encodeText: function(text) {
                return encodeURIComponent(text);
        },

        decodeText: function(text) {
                return decodeURIComponent(text);
        },

        utf8Encode: function(plainText) {
                var utf8Text = '';
                for(var i=0; i<plainText.length; i++) {
                        var c=plainText.charCodeAt(i);
                        if(c<128) {
                                utf8Text += String.fromCharCode(c);
                        } else if((c>127) && (c<2048)) {
                                utf8Text += String.fromCharCode((c>>6)|192);
                                utf8Text += String.fromCharCode((c&63)|128);
                        } else {
                                utf8Text += String.fromCharCode((c>>12)|224);
                                utf8Text += String.fromCharCode(((c>>6)&63)|128);
                                utf8Text += String.fromCharCode((c&63)|128);
                        }
                }
                return utf8Text;
        },

        utf8Decode: function(utf8Text) {
                var plainText = '';
                var c,c2,c3;
                var i=0;
                while(i<utf8Text.length) {
                        c = utf8Text.charCodeAt(i);
                        if(c<128) {
                                plainText += String.fromCharCode(c);
                                i++;
                        } else if((c>191) && (c<224)) {
                                c2 = utf8Text.charCodeAt(i+1);
                                plainText += String.fromCharCode(((c&31)<<6) | (c2&63));
                                i+=2;
                        } else {
                                c2 = utf8Text.charCodeAt(i+1);
                                c3 = utf8Text.charCodeAt(i+2);
                                plainText += String.fromCharCode(((c&15)<<12) | ((c2&63)<<6) | (c3&63));
                                i+=3;
                        }
                }
                return plainText;
        },

        encodeSpecialChars: function(text) {
                if (!arguments.callee.regExp) {
                        arguments.callee.regExp = new RegExp('[&<>\'"]', 'g');
                }

                return text.replace(
                        arguments.callee.regExp,
                        this.encodeSpecialCharsCallback
                );
        },

        encodeSpecialCharsCallback: function(str) {
                switch(str) {
                        case '&':
                                return '&amp;';
                        case '<':
                                return '&lt;';
                        case '>':
                                return '&gt;';
                        case '\'':
                                // As &apos; is not supported by IE, we use &#39; as replacement for ('):
                                return '&#39;';
                        case '"':
                                return '&quot;';
                        default:
                                return str;
                }
        },

        decodeSpecialChars: function(text) {
                if (!arguments.callee.regExp) {
                        arguments.callee.regExp = new RegExp('(&amp;)|(&lt;)|(&gt;)|(&#39;)|(&quot;)', 'g');
                }

                return text.replace(
                        arguments.callee.regExp,
                        this.decodeSpecialCharsCallback
                );
        },

        decodeSpecialCharsCallback: function(str) {
                switch(str) {
                        case '&amp;':
                                return '&';
                        case '&lt;':
                                return '<';
                        case '&gt;':
                                return '>';
                        case '&#39;':
                                return '\'';
                        case '&quot;':
                                return '"';
                        default:
                                return str;
                }
        },

        inArray: function(haystack, needle) {
                var i = haystack.length;
                while(i--) {
                        if(haystack[i] === needle) {
                                return true;
                        }
                }
                return false;
        },

        arraySearch: function(needle, haystack) {
                var i = haystack.length;
                while(i--) {
                        if(haystack[i] === needle) {
                                return i;
                        }
                }
            return false;
        },

        stripTags: function(str) {
                if (!arguments.callee.regExp) {
                        arguments.callee.regExp = new RegExp('<\\/?[^>]+?>', 'g');
                }

                return str.replace(arguments.callee.regExp, '');
        },

        stripBBCodeTags: function(str) {
                if (!arguments.callee.regExp) {
                        arguments.callee.regExp = new RegExp('\\[\\/?[^\\]]+?\\]', 'g');
                }

                return str.replace(arguments.callee.regExp, '');
        },

        escapeRegExp: function(text) {
                if (!arguments.callee.regExp) {
                        var specials = new Array(
                                '^', '$', '*', '+', '?', '.', '|', '/',
                                '(', ')', '[', ']', '{', '}', '\\'
                        );
                        arguments.callee.regExp = new RegExp(
                                '(\\' + specials.join('|\\') + ')', 'g'
                        );
                }
                return text.replace(arguments.callee.regExp, '\\$1');
        },

        addSlashes: function(text) {
                // Adding slashes in front of apostrophs and backslashes to ensure a valid JavaScript expression:
                return text.replace(/\\/g, '\\\\').replace(/\'/g, '\\\'');
        },

        removeSlashes: function(text) {
                // Removing slashes added by calling addSlashes(text) previously:
                return text.replace(/\\\\/g, '\\').replace(/\\\'/g, '\'');
        },

        formatDate: function(format, date) {
                date = (date == null) ? new date() : date;

                return format
                .replace(/%Y/g, date.getFullYear())
                .replace(/%m/g, this.addLeadingZero(date.getMonth()+1))
                .replace(/%d/g, this.addLeadingZero(date.getDate()))
                .replace(/%H/g, this.addLeadingZero(date.getHours()))
                .replace(/%i/g, this.addLeadingZero(date.getMinutes()))
                .replace(/%s/g, this.addLeadingZero(date.getSeconds()));
        },

        addLeadingZero: function(number) {
                number = number.toString();
                if(number.length < 2) {
                        number = '0'+number;
                }
                return number;
        },

        getUserIDFromUserName: function(userName) {
                var index = this.arraySearch(userName, this.userNamesList);
                if(index !== false) {
                        return this.usersList[index];
                }
                return null;
        },

        getUserNameFromUserID: function(userID) {
                var index = this.arraySearch(userID, this.usersList);
                if(index !== false) {
                        return this.userNamesList[index];
                }
                return null;
        },

        getRoleClass: function(roleID) {
                switch(parseInt(roleID)) {
                        case 0:
                                return 'guest';
                        case 1:
                                return 'user';
                        case 2:
                                return 'moderator';
                        case 3:
                                return 'admin';
                        case 4:
                                return 'chatBot';
                         case 5:
                                 return 'vip';
                         case 6:
                                 return 'backstage';
                        default:
                                return 'default';
                }
        },

        handleInputFieldKeyPress: function(event) {
                if(event.keyCode == 13 && !event.shiftKey) {
                        this.sendMessage();
                        try {
                                event.preventDefault();
                        } catch(e) {
                                event.returnValue = false; // IE
                        }
                        return false;
                }
                return true;
        },
handleInputFieldKeyUp: function(event) {
                this.updateMessageLengthCounter();
        },

        updateMessageLengthCounter: function() {
                if(this.dom['messageLengthCounter']) {
                        this.updateDOM(
                                'messageLengthCounter',
                                this.dom['inputField'].value.length        + '/' + this.messageTextMaxLength,
                                false,
                                true
                        )
                }
        },



        sendMessage: function(text) {
                text = text ? text : this.dom['inputField'].value;
                if(!text) {
                        return;
                }
                text = this.parseInputMessage(text);
                if(text) {
                        clearTimeout(this.timer);
                        var message =         'lastID='
                                                        + this.lastID
                                                        + '&text='
                                                        + this.encodeText(text);
                        this.makeRequest(this.ajaxURL,'POST',message);
                }
                this.dom['inputField'].value = '';
                this.dom['inputField'].focus();

        },

        parseInputMessage: function(text) {
                if(text.charAt(0) == '/') {
                        var textParts = text.split(' ');
                        switch(textParts[0]) {
                                case '/ignore':
                                        text = this.parseIgnoreInputCommand(text, textParts);
                                        break;
                                default:
                                        text = this.parseCustomInputCommand(text, textParts);
                        }
                        if(text && this.settings['persistFontColor'] && this.settings['fontColor']) {
                                text = this.assignFontColorToCommandMessage(text, textParts);
                        }
                } else {
                        text = this.parseCustomInputMessage(text);
                        if(text && this.settings['persistFontColor'] && this.settings['fontColor']) {
                                text = this.assignFontColorToMessage(text);
                        }
                }
                return text;
        },

        assignFontColorToMessage: function(text) {
                return '[color='+this.settings['fontColor']+']'+text+'[/color]';
        },

        assignFontColorToCommandMessage: function(text, textParts) {
                switch(textParts[0]) {
                        case '/f':
                        case '/describe':
                                if(textParts.length > 2) {
                                        return        textParts[0]+' '+textParts[1]+' '
                                                        + '[color='+this.settings['fontColor']+']'
                                                        + textParts.slice(2).join(' ')
                                                        + '[/color]';
                                }
                                break;
                        case '/me':
                        case '/action':
                                if(textParts.length > 1) {
                                        return        textParts[0]+' '
                                                        + '[color='+this.settings['fontColor']+']'
                                                        + textParts.slice(1).join(' ')
                                                        + '[/color]';
                                }
                                break;
                }
                return text;
        },

        parseIgnoreInputCommand: function(text, textParts) {
                var ignoredUserNames = this.getIgnoredUserNames();
                if(textParts.length > 1) {
                        var userName = this.encodeSpecialChars(textParts[1]);
                        // Prevent adding the chatBot or current user to the list:
                        if(userName == this.userName || userName == this.getEncodedChatBotName()) {
                                // Display the list of ignored users instead:
                                return this.parseIgnoreInputCommand(null, new Array('/ignore'));
                        }
                        if(ignoredUserNames.length > 0) {
                                var i = ignoredUserNames.length;
                                while(i--) {
                                        if(ignoredUserNames[i] === userName) {
                                                ignoredUserNames.splice(i,1);
                                                this.addChatBotMessageToChatList('/ignoreRemoved '+userName);
                                                this.setIgnoredUserNames(ignoredUserNames);
                                                this.updateChatlistView();
                                                return null;
                                        }
                                }
                        }
                        ignoredUserNames.push(userName);
                        this.addChatBotMessageToChatList('/ignoreAdded '+userName);
                        this.setIgnoredUserNames(ignoredUserNames);
                } else {
                        if(ignoredUserNames.length == 0) {
                                this.addChatBotMessageToChatList('/ignoreListEmpty -');
                        } else {
                                this.addChatBotMessageToChatList('/ignoreList '+ignoredUserNames.join(' '));
                        }
                }
                this.updateChatlistView();
                return null;
        },

        getIgnoredUserNames: function() {
                if(!this.ignoredUserNames) {
                        var ignoredUserNamesString = this.getSetting('ignoredUserNames');
                        if(ignoredUserNamesString) {
                                this.ignoredUserNames = ignoredUserNamesString.split(' ');
                        } else {
                                this.ignoredUserNames = new Array();
                        }
                }
                return this.ignoredUserNames;
        },

        setIgnoredUserNames: function(ignoredUserNames) {
                this.ignoredUserNames = ignoredUserNames;
                this.setSetting('ignoredUserNames', ignoredUserNames.join(' '));
        },

        ignoreMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
                if(userID == this.chatBotID && messageText.charAt(0) == '/') {
                        var textParts = messageText.split(' ');
                        if(textParts.length > 1) {
                                switch(textParts[0]) {
                                        case '/invite':
                                        case '/uninvite':
                                        case '/roll':
                                                userName = textParts[1];
                                                break;
                                }
                        }
                }
                if(this.inArray(this.getIgnoredUserNames(), userName)) {
                        return true;
                }
                return false;
        },

        deleteMessage: function(messageID) {
                var messageNode = this.getMessageNode(messageID);
                if(messageNode) {
                        var originalClass = this.getClass(messageNode);
                        this.setClass(messageNode, originalClass+' deleteSelected');
                        if(confirm(this.lang['deleteMessageConfirm'])) {
                                var nextSibling = messageNode.nextSibling;
                                try {
                                        this.dom['chatList'].removeChild(messageNode);
                                        if(nextSibling) {
                                                this.updateChatListRowClasses(nextSibling);
                                        }
                                        this.updateChat('&delete='+messageID);
                                } catch(e) {
                                        this.setClass(messageNode, originalClass);
                                }
                        } else {
                                this.setClass(messageNode, originalClass);
                        }
                }
        },

        updateChatListRowClasses: function(node) {
                if(!node) {
                        node = this.dom['chatList'].firstChild;
                }
                if(node) {
                        var previousNode = node.previousSibling;
                        var rowEven = (previousNode && this.getClass(previousNode) == 'rowOdd') ? true : false;
                        while(node) {
                                this.setClass(node, (rowEven ? 'rowEven' : 'rowOdd'))
                                node = node.nextSibling;
                                rowEven = !rowEven;
                        }
                }
        },

        getClass: function(node) {
                if(typeof node.className != 'undefined') {
                        return node.className; // IE
                } else {
                        return node.getAttribute('class');
                }
        },

        setClass: function(node, className) {
                if(typeof node.className != 'undefined') {
                        node.className = className; // IE
                } else {
                        node.setAttribute('class', className);
                }
        },

        scriptLinkEncode: function(text) {
                return this.encodeText(this.addSlashes(this.decodeSpecialChars(text)));
        },

        scriptLinkDecode: function(text) {
                return this.encodeSpecialChars(this.removeSlashes(this.decodeText(text)));
        },

        getScriptLinkValue: function(value) {
                // This method returns plainText encoded values from javascript links
                // The value has to be utf8Decoded for MSIE and Opera:
                if(typeof arguments.callee.utf8Decode == 'undefined') {
                        switch(navigator.appName) {
                                case 'Microsoft Internet Explorer':
                                case 'Opera':
                                        arguments.callee.utf8Decode = true;
                                        return this.utf8Decode(value);
                                default:
                                        arguments.callee.utf8Decode = false;
                                        return value;
                        }
                } else if(arguments.callee.utf8Decode) {
                        return this.utf8Decode(value);
                } else {
                        return value;
                }
        },

        sendMessageWrapper: function(text) {
                this.sendMessage(this.getScriptLinkValue(text));
        },

        insertMessageWrapper: function(text) {
                this.insertText(this.getScriptLinkValue(text), true);
        },

        switchChannel: function(channel) {
                if(!this.chatStarted) {
                        this.clearChatList();
                        this.channelSwitch = true;
                        this.loginChannelID = null;
                        this.loginChannelName = channel;
                        this.requestTeaserContent();
                        return;
                }
                clearTimeout(this.timer);
                var message =         'lastID='
                                                + this.lastID
                                                + '&channelName='
                                                + this.encodeText(channel);
                this.makeRequest(this.ajaxURL,'POST',message);
                if(this.dom['inputField'] && this.settings['autoFocus']) {
                        this.dom['inputField'].focus();
                }
        },

        logout: function() {
                clearTimeout(this.timer);
                var message = 'logout=true';
                this.makeRequest(this.ajaxURL,'POST',message);
        },

        handleLogout: function(url) {
                window.location.href = url;
        },

        toggleSetting: function(setting, buttonID) {
                this.setSetting(setting, !this.getSetting(setting));
                if(buttonID) {
                        this.updateButton(setting, buttonID);
                }
        },

        updateButton: function(setting, buttonID) {
                var node = document.getElementById(buttonID);
                if(node) {
                        this.setClass(node, (this.getSetting(setting) ? 'button' : 'button off'))
                }
        },

        showHide: function(id, styleDisplay, displayInline) {
                var node = document.getElementById(id);
                if(node) {
                        if(styleDisplay) {
                                node.style.display = styleDisplay;
                        } else {
                                if(node.style.display == 'none') {
                                        node.style.display = (displayInline ? 'inline' : 'block');
                                } else {
                                        node.style.display = 'none';
                                }
                        }
                }
        },

        setPersistFontColor: function(bool) {
                this.settings['persistFontColor'] = bool;
                if(!this.settings['persistFontColor']) {
                        this.settings['fontColor'] = null;
                        if(this.dom['inputField']) {
                                this.dom['inputField'].style.color = '';
                        }
                }
        },

        setFontColor: function(color) {
                if(this.settings['persistFontColor']) {
                        this.settings['fontColor'] = color;
                        if(this.dom['inputField']) {
                                this.dom['inputField'].style.color = color;
                        }
                        if(this.dom['colorCodesContainer']) {
                                this.dom['colorCodesContainer'].style.display = 'none';
                                if(this.dom['inputField']) {
                                        this.dom['inputField'].focus();
                                }
                        }
                } else {
                        this.insert('[color=' + color + ']', '[/color]');
                }
        },

        insertText: function(text, clearInputField) {
                if(clearInputField) {
                        this.dom['inputField'].value = '';
                }
                this.insert(text, '');
        },
        // Function to add text for username like @ username say:
insertText2: function(text, clearInputField) {
if(clearInputField) {
this.dom['inputField'].value = '';
}
this.insert('/f '+text+' ' ,'');
},

        insertBBCode: function(bbCode) {
                switch(bbCode) {
                        case 'url':
                                var url = prompt(this.lang['urlDialog'], 'http://');
                                if(url)
                                        this.insert('[url=' + url + ']', '[/url]');
                                else
                                        this.dom['inputField'].focus();
                                break;
                        default:
                                this.insert('[' + bbCode + ']', '[/' + bbCode + ']');
                }
        },

        insert: function(startTag, endTag) {
                this.dom['inputField'].focus();
                // Internet Explorer:
                if(typeof document.selection != 'undefined') {
                        // Insert the tags:
                        var range = document.selection.createRange();
                        var insText = range.text;
                        range.text = startTag + insText + endTag;
                        // Adjust the cursor position:
                        range = document.selection.createRange();
                        if (insText.length == 0) {
                                range.move('character', -endTag.length);
                        } else {
                                range.moveStart('character', startTag.length + insText.length + endTag.length);
                        }
                        range.select();
                }
                // Firefox, etc. (Gecko based browsers):
                else if(typeof this.dom['inputField'].selectionStart != 'undefined') {
                        // Insert the tags:
                        var start = this.dom['inputField'].selectionStart;
                        var end = this.dom['inputField'].selectionEnd;
                        var insText = this.dom['inputField'].value.substring(start, end);
                        this.dom['inputField'].value =         this.dom['inputField'].value.substr(0, start)
                                                                                        + startTag
                                                                                        + insText
                                                                                        + endTag
                                                                                        + this.dom['inputField'].value.substr(end);
                        // Adjust the cursor position:
                        var pos;
                        if (insText.length == 0) {
                                pos = start + startTag.length;
                        } else {
                                pos = start + startTag.length + insText.length + endTag.length;
                        }
                        this.dom['inputField'].selectionStart = pos;
                        this.dom['inputField'].selectionEnd = pos;
                }
                // Other browsers:
                else {
                        var pos = this.dom['inputField'].value.length;
                        this.dom['inputField'].value =         this.dom['inputField'].value.substr(0, pos)
                                                                                        + startTag
                                                                                        + endTag
                                                                                        + this.dom['inputField'].value.substr(pos);
                }
        },

        replaceText: function(text) {
                try{
                        text = this.replaceLineBreaks(text);
                        if(text.charAt(0) == '/') {
                                text = this.replaceCommands(text);
                        } else {
                                text = this.replaceBBCode(text);
                                text = this.replaceHyperLinks(text);
                                text = this.replaceEmoticons(text);
                        }
                        text = this.breakLongWords(text);
                        text = this.replaceCustomText(text);
                } catch(e){
                        //alert(e);
                }
                return text;
        },

        replaceCommands: function(text) {
                try {
                        if(text.charAt(0) != '/') {
                                return text;
                        }
                        var textParts = text.split(' ');
                        switch(textParts[0]) {
                                case '/login':
                                        return this.replaceCommandLogin(textParts);
                                case '/logout':
                                        return this.replaceCommandLogout(textParts);
                                case '/channelEnter':
                                        return this.replaceCommandChannelEnter(textParts);
                                case '/channelLeave':
                                        return this.replaceCommandChannelLeave(textParts);
                                case '/privmsg':
                                        return this.replaceCommandPrivMsg(textParts);
                                case '/privmsgto':
                                        return this.replaceCommandPrivMsgTo(textParts);
                                case '/privaction':
                                        return this.replaceCommandPrivAction(textParts);
                                case '/privactionto':
                                        return this.replaceCommandPrivActionTo(textParts);
                                case '/me':
                                case '/action':
                                        return this.replaceCommandAction(textParts);
                                case '/invite':
                                        return this.replaceCommandInvite(textParts);
                                case '/inviteto':
                                        return this.replaceCommandInviteTo(textParts);
                                case '/uninvite':
                                        return this.replaceCommandUninvite(textParts);
                                case '/uninviteto':
                                        return this.replaceCommandUninviteTo(textParts);
                                case '/queryOpen':
                                        return this.replaceCommandQueryOpen(textParts);
                                case '/queryClose':
                                        return this.replaceCommandQueryClose(textParts);
                                case '/ignoreAdded':
                                        return this.replaceCommandIgnoreAdded(textParts);
                                case '/ignoreRemoved':
                                        return this.replaceCommandIgnoreRemoved(textParts);
                                case '/ignoreList':
                                        return this.replaceCommandIgnoreList(textParts);
                                case '/ignoreListEmpty':
                                        return this.replaceCommandIgnoreListEmpty(textParts);
                                case '/kick':
                                        return this.replaceCommandKick(textParts);
                                case '/who':
                                        return this.replaceCommandWho(textParts);
                                case '/whoChannel':
                                        return this.replaceCommandWhoChannel(textParts);
                                case '/whoEmpty':
                                        return this.replaceCommandWhoEmpty(textParts);
                                case '/list':
                                        return this.replaceCommandList(textParts);
                                case '/bans':
                                        return this.replaceCommandBans(textParts);
                                case '/bansEmpty':
                                        return this.replaceCommandBansEmpty(textParts);
                                case '/unban':
                                        return this.replaceCommandUnban(textParts);
                                case '/whois':
                                        return this.replaceCommandWhois(textParts);
                                case '/whereis':
                                        return this.replaceCommandWhereis(textParts);
                                case '/roll':
                                        return this.replaceCommandRoll(textParts);
                                case '/nick':
                                        return this.replaceCommandNick(textParts);
                                case '/error':
                                        return this.replaceCommandError(textParts);
                                default:
                                        return this.replaceCustomCommands(text, textParts);
                        }
                } catch(e) {
                        //alert(e);
                }
                return text;
        },

        replaceCommandLogin: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['login'].replace(/%s/, textParts[1])
                                + '</span>';
        },

        replaceCommandLogout: function(textParts) {
                var type = '';
                if(textParts.length == 3)
                        type = textParts[2];
                return        '<span class="chatBotMessage">'
                                + this.lang['logout' + type].replace(/%s/, textParts[1])
                                + '</span>';

        },

        replaceCommandChannelEnter: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['channelEnter'].replace(/%s/, textParts[1])
                                + '</span>';

        },

        replaceCommandChannelLeave: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['channelLeave'].replace(/%s/, textParts[1])
                                + '</span>';
        },

        replaceCommandPrivMsg: function(textParts) {
                var privMsgText = textParts.slice(1).join(' ');
                privMsgText = this.replaceBBCode(privMsgText);
                privMsgText = this.replaceHyperLinks(privMsgText);
                privMsgText = this.replaceEmoticons(privMsgText);
                return        '<span class="privmsg">'
                                + this.lang['privmsg']
                                + '</span> '
                                + privMsgText;
        },

        replaceCommandPrivMsgTo: function(textParts) {
                var privMsgText = textParts.slice(2).join(' ');
                privMsgText = this.replaceBBCode(privMsgText);
                privMsgText = this.replaceHyperLinks(privMsgText);
                privMsgText = this.replaceEmoticons(privMsgText);
                return        '<span class="privmsg">'
                                + this.lang['privmsgto'].replace(/%s/, textParts[1])
                                + '</span> '
                                + privMsgText;
        },

        replaceCommandPrivAction: function(textParts) {
                var privActionText = textParts.slice(1).join(' ');
                privActionText = this.replaceBBCode(privActionText);
                privActionText = this.replaceHyperLinks(privActionText);
                privActionText = this.replaceEmoticons(privActionText);
                return        '<span class="action">'
                                + privActionText
                                + '</span> <span class="privmsg">'
                                + this.lang['privmsg']
                                + '</span> ';
        },

        replaceCommandPrivActionTo: function(textParts) {
                var privActionText = textParts.slice(2).join(' ');
                privActionText = this.replaceBBCode(privActionText);
                privActionText = this.replaceHyperLinks(privActionText);
                privActionText = this.replaceEmoticons(privActionText);
                return        '<span class="action">'
                                + privActionText
                                + '</span> <span class="privmsg">'
                                + this.lang['privmsgto'].replace(/%s/, textParts[1])
                                + '</span> ';
        },

        replaceCommandAction: function(textParts) {
                var actionText = textParts.slice(1).join(' ');
                actionText = this.replaceBBCode(actionText);
                actionText = this.replaceHyperLinks(actionText);
                actionText = this.replaceEmoticons(actionText);
                return        '<span class="action">'
                                + actionText
                                + '</span>';
        },

        replaceCommandInvite: function(textParts) {
                var inviteText = this.lang['invite']
                                                        .replace(/%s/, textParts[1])
                                                        .replace(
                                                                /%s/,
                                                                '<a href="javascript:ajaxChat.sendMessageWrapper(\'/join '
                                                                + this.scriptLinkEncode(textParts[2])
                                                                + '\');" title="'
                                                                + this.lang['joinChannel'].replace(/%s/, textParts[2])
                                                                + '">'
                                                                + textParts[2]
                                                                + '</a>'
                                                        );
                return        '<span class="chatBotMessage">'
                                + inviteText
                                + '</span>';
        },

        replaceCommandInviteTo: function(textParts) {
                var inviteText = this.lang['inviteto']
                                                        .replace(/%s/, textParts[1])
                                                        .replace(/%s/, textParts[2]);
                return        '<span class="chatBotMessage">'
                                + inviteText
                                + '</span>';
        },

        replaceCommandUninvite: function(textParts) {
                var uninviteText = this.lang['uninvite']
                                                        .replace(/%s/, textParts[1])
                                                        .replace(/%s/, textParts[2]);
                return        '<span class="chatBotMessage">'
                                + uninviteText
                                + '</span>';
        },

        replaceCommandUninviteTo: function(textParts) {
                var uninviteText = this.lang['uninviteto']
                                                        .replace(/%s/, textParts[1])
                                                        .replace(/%s/, textParts[2]);
                return        '<span class="chatBotMessage">'
                                + uninviteText
                                + '</span>';
        },

        replaceCommandQueryOpen: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['queryOpen'].replace(/%s/, textParts[1])
                                + '</span>';
        },

        replaceCommandQueryClose: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['queryClose'].replace(/%s/, textParts[1])
                                + '</span>';
        },

        replaceCommandIgnoreAdded: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['ignoreAdded'].replace(/%s/, textParts[1])
                                + '</span>';
        },

        replaceCommandIgnoreRemoved: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['ignoreRemoved'].replace(/%s/, textParts[1])
                                + '</span>';
        },

        replaceCommandIgnoreList: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['ignoreList'] + ' '
                                + this.getInlineUserMenu(textParts.slice(1))
                                + '</span>';
        },

        replaceCommandIgnoreListEmpty: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['ignoreListEmpty']
                                + '</span>';
        },

        replaceCommandKick: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['logoutKicked'].replace(/%s/, textParts[1])
                                + '</span>';
        },

        replaceCommandWho: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['who'] + ' '
                                + this.getInlineUserMenu(textParts.slice(1))
                                + '</span>';
        },

        replaceCommandWhoChannel: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['whoChannel'].replace(/%s/, textParts[1]) + ' '
                                + this.getInlineUserMenu(textParts.slice(2))
                                + '</span>';
        },

        replaceCommandWhoEmpty: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['whoEmpty']
                                + '</span>';
        },

        replaceCommandList: function(textParts) {
                var channels = textParts.slice(1);
                var listChannels = new Array();
                var channelName;
                for(var i=0; i<channels.length; i++) {
                        channelName = (channels[i] == this.channelName) ? '<b>'+channels[i]+'</b>' : channels[i];
                        listChannels.push(
                                '<a href="javascript:ajaxChat.sendMessageWrapper(\'/join '
                                + this.scriptLinkEncode(channels[i])
                                + '\');" title="'
                                + this.lang['joinChannel'].replace(/%s/, channels[i])
                                + '">'
                                + channelName
                                + '</a>'
                        );
                }
                return        '<span class="chatBotMessage">'
                                + this.lang['list'] + ' '
                                + listChannels.join(', ')
                                + '</span>';
        },

        replaceCommandBans: function(textParts) {
                var users = textParts.slice(1);
                var listUsers = new Array();
                for(var i=0; i<users.length; i++) {
                        listUsers.push(
                                '<a href="javascript:ajaxChat.sendMessageWrapper(\'/unban '
                                + this.scriptLinkEncode(users[i])
                                + '\');" title="'
                                + this.lang['unbanUser'].replace(/%s/, users[i])
                                + '">'
                                + users[i]
                                + '</a>'
                        );
                }
                return        '<span class="chatBotMessage">'
                                + this.lang['bans'] + ' '
                                + listUsers.join(', ')
                                + '</span>';
        },

        replaceCommandBansEmpty: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['bansEmpty']
                                + '</span>';
        },

        replaceCommandUnban: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['unban'].replace(/%s/, textParts[1])
                                + '</span>';
        },

        replaceCommandWhois: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['whois'].replace(/%s/, textParts[1]) + ' '
                                + textParts[2]
                                + '</span>';
        },

        replaceCommandWhereis: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['whereis'].replace(/%s/, textParts[1]).replace(
                                                                /%s/,
                                                                '<a href="javascript:ajaxChat.sendMessageWrapper(\'/join '
                                                                + this.scriptLinkEncode(textParts[2])
                                                                + '\');" title="'
                                                                + this.lang['joinChannel'].replace(/%s/, textParts[2])
                                                                + '">'
                                                                + textParts[2]
                                                                + '</a>'
                                                        )
                                + '</span>';
        },

        replaceCommandRoll: function(textParts) {
                var rollText = this.lang['roll'].replace(/%s/, textParts[1]);
                rollText = rollText.replace(/%s/, textParts[2]);
                rollText = rollText.replace(/%s/, textParts[3]);
                return        '<span class="chatBotMessage">'
                                + rollText
                                + '</span>';
        },

        replaceCommandNick: function(textParts) {
                return        '<span class="chatBotMessage">'
                                + this.lang['nick'].replace(/%s/, textParts[1]).replace(/%s/, textParts[2])
                                + '</span>';

        },


        replaceCommandError: function(textParts) {
                var errorMessage = this.lang['error'+textParts[1]];
                if(!errorMessage) {
                        errorMessage = 'Error: Unknown.';
                } else if(textParts.length > 2) {
                        errorMessage = errorMessage.replace(/%s/, textParts.slice(2).join(' '));
                }
                return        '<span class="chatBotErrorMessage">'
                                + errorMessage
                                + '</span>';
        },

        getInlineUserMenu: function(users) {
                var menu = '';
                for(var i=0; i<users.length; i++) {
                        if(i>0) {
                                menu += ', ';
                        }
                        menu        += '<a href="javascript:ajaxChat.toggleUserMenu(\''
                                        + this.getInlineUserMenuDocumentID(this.userMenuCounter, i)
                                        + '\', \''
                                        + this.scriptLinkEncode(users[i])
                                        + '\', null);" title="'
                                        + this.lang['toggleUserMenu'].replace(/%s/, users[i])
                                        + '" dir="'
                                        + this.baseDirection
                                        + '">'
                                        + ((users[i] == this.userName) ? '<b>'+users[i]+'</b>' : users[i])
                                        + '</a>'
                                        + '<ul class="inlineUserMenu" id="'
                                        + this.getInlineUserMenuDocumentID(this.userMenuCounter, i)
                                        + '" style="display:none;">'
                                        + '</ul>';
                }
                this.userMenuCounter++;
                return menu;
        },

        containsUnclosedTags: function(str) {
                if (!arguments.callee.regExpOpenTags || !arguments.callee.regExpCloseTags) {
                        arguments.callee.regExpOpenTags                = new RegExp('<[^>\\/]+?>', 'gm');
                        arguments.callee.regExpCloseTags        = new RegExp('<\\/[^>]+?>', 'gm');
                }
                var openTags        = str.match(arguments.callee.regExpOpenTags);
                var closeTags        = str.match(arguments.callee.regExpCloseTags);
                // Return true if the number of tags doesn't match:
                if((!openTags && closeTags) ||
                        (openTags && !closeTags) ||
                        (openTags && closeTags && (openTags.length != closeTags.length))) {
                        return true;
                }
                return false;
        },

        breakLongWords: function(text) {
                if(!this.settings['wordWrap'])
                        return text;
                var newText = '';
                var charCounter = 0;
                var currentChar, withinTag, withinEntity;

                for(var i=0; i<text.length; i++) {
                        currentChar = text.charAt(i);

                        // Check if we are within a tag or entity:
                        if(currentChar == '<') {
                                withinTag = true;
                                // Reset the charCounter after newline tags (<br/>):
                                if(i>5 && text.substr(i-5,4) == '<br/')
                                        charCounter = 0;
                        } else if(withinTag && i>0 && text.charAt(i-1) == '>') {
                                withinTag = false;
                                // Reset the charCounter after newline tags (<br/>):
                                if(i>4 && text.substr(i-5,4) == '<br/')
                                        charCounter = 0;
                        } else if(currentChar == '&') {
                                withinEntity = true;
                        } else if(withinEntity && i>0 && text.charAt(i-1) == ';') {
                                withinEntity = false;
                                // We only increase the charCounter once for the whole entiy:
                                charCounter++;
                        }

                        if(!withinTag && !withinEntity) {
                                // Reset the charCounter if we encounter a word boundary:
                                if(currentChar == ' ' || currentChar == '\n' || currentChar == '\t') {
                                        charCounter = 0;
                                } else {
                                        // We are not within a tag or entity, increase the charCounter:
                                        charCounter++;
                                }
                                if(charCounter > this.settings['maxWordLength']) {
                                        // maxWordLength has been reached, break here and reset the charCounter:
                                        newText += this.getBreakString();
                                        charCounter = 0;
                                }
                        }
                        // Add the current char to the text:
                        newText += currentChar;
                }

                return newText;
        },

        getBreakString: function() {
                // Returns the character sequence used to wrap long words
                if(typeof arguments.callee.breakString == 'undefined') {
                        arguments.callee.breakString = '&#8203;';
                }
                return arguments.callee.breakString;
        },

        replaceBBCode: function(text) {
                if(!this.settings['bbCode']) {
                        // If BBCode is disabled, just strip the text from BBCode tags:
                        if (!arguments.callee.regExpStripBBCode) {
                                arguments.callee.regExpStripBBCode = new RegExp(
                                        '\\[(?:\\/)?(\\w+)(?:=([^<>]*?))?\\]',
                                        'gm'
                                );
                        }
                        return text.replace(
                                arguments.callee.regExpStripBBCode,
                                ''
                        );
                }
                // Remove the BBCode tags:
                if (!arguments.callee.regExp) {
                        arguments.callee.regExp = new RegExp(
                                '\\[(\\w+)(?:=([^<>]*?))?\\](.+?)\\[\\/\\1\\]',
                                'gm'
                        );
                }
                return text.replace(
                        arguments.callee.regExp,
                        this.replaceBBCodeCallback
                );
        },

        replaceBBCodeCallback: function(str, p1, p2, p3) {
                // Only replace predefined BBCode tags:
                if(!ajaxChat.inArray(ajaxChat.bbCodeTags, p1)) {
                        return str;
                }
                // Avoid invalid XHTML (unclosed tags):
                if(ajaxChat.containsUnclosedTags(p3)) {
                        return str;
                }
                switch(p1) {
                        case 'color':
                                return ajaxChat.replaceBBCodeColor(p3, p2);
                        case 'url':
                                return ajaxChat.replaceBBCodeUrl(p3, p2);
                        case 'quote':
                                return ajaxChat.replaceBBCodeQuote(p3, p2);
                        case 'code':
                                return ajaxChat.replaceBBCodeCode(p3);
                        case 'u':
                                return ajaxChat.replaceBBCodeUnderline(p3);
                        default:
                                return ajaxChat.replaceCustomBBCode(p1, p2, p3);
                }
        },

        replaceBBCodeColor: function(content, attribute) {
                if(this.settings['bbCodeColors']) {
                        // Only allow predefined color codes:
                        if(!attribute || !this.inArray(ajaxChat.colorCodes, attribute))
                                return content;
                        return         '<span style="color:'
                                        + attribute + ';">'
                                        + this.replaceBBCode(content)
                                        + '</span>';
                }
                return content;
        },

        replaceBBCodeUrl: function(content, attribute) {
                var url;
                if(attribute)
                        url = attribute.replace(/\s/gm, this.encodeText(' '));
                else
                        url = this.stripBBCodeTags(content.replace(/\s/gm, this.encodeText(' ')));
                if (!arguments.callee.regExpUrl) {
                        arguments.callee.regExpUrl = new RegExp(
                                '^(?:(?:http)|(?:https)|(?:ftp)|(?:irc)):\\/\\/',
                                ''
                        );
                }
                if(!url || !url.match(arguments.callee.regExpUrl))
                        return content;
                return         '<a href="'
                                + url
                                + '" onclick="window.open(this.href); return false;">'
                                + this.replaceBBCode(content)
                                + '</a>';
        },

        replaceBBCodeQuote: function(content, attribute) {
                if(attribute)
                        return        '<span class="quote"><cite>'
                                        + this.lang['cite'].replace(/%s/, attribute)
                                        + '</cite><q>'
                                        + this.replaceBBCode(content)
                                        + '</q></span>';
                return         '<span class="quote"><q>'
                                + this.replaceBBCode(content)
                                + '</q></span>';
        },

        replaceBBCodeCode: function(content) {
                // Replace vertical tabs and multiple spaces with two non-breaking space characters:
                return         '<code>'
                                + this.replaceBBCode(content.replace(/\t|(?:  )/gm, '&#160;&#160;'))
                                + '</code>';
        },

        replaceBBCodeUnderline: function(content) {
                return         '<span style="text-decoration:underline;">'
                                + this.replaceBBCode(content)
                                + '</span>';
        },

        replaceHyperLinks: function(text) {
                if(!this.settings['hyperLinks']) {
                        return text;
                }
                if(!arguments.callee.regExp) {
                        arguments.callee.regExp = new RegExp(
                                '(^|\\s|>)((?:(?:http)|(?:https)|(?:ftp)|(?:irc)):\\/\\/[^\\s<>]+)(<\\/a>)?',
                                'gm'
                        );
                }
                return text.replace(
                        arguments.callee.regExp,
                        // Specifying an anonymous function as second parameter:
                        function(str, p1, p2, p3) {
                                // Do not replace URL's inside URL's:
                                if(p3) {
                                        return str;
                                }
                                return         p1
                                                + '<a href="'
                                                + p2
                                                + '" onclick="window.open(this.href); return false;">'
                                                + p2
                                                + '</a>';
                        }
                );
        },

        replaceLineBreaks: function(text) {
                if (!arguments.callee.regExp) {
                        arguments.callee.regExp = new RegExp('\\n',        'g');
                }
                if(!this.settings['lineBreaks']) {
                        return text.replace(arguments.callee.regExp, ' ');
                } else {
                        return text.replace(arguments.callee.regExp, '<br/>');
                }
        },

        replaceEmoticons: function(text) {
                if(!this.settings['emoticons']) {
                        return text;
                }
                if(!arguments.callee.regExp) {
                        var regExpStr = '^(.*)(';
                        for(var i=0; i<this.emoticonCodes.length; i++) {
                                if(i!=0)
                                        regExpStr += '|';
                                regExpStr += '(?:' + this.escapeRegExp(this.emoticonCodes[i]) + ')';
                        }
                        regExpStr += ')(.*)$';
                        arguments.callee.regExp = new RegExp(regExpStr, 'gm');
                }
                return text.replace(
                        arguments.callee.regExp,
                        this.replaceEmoticonsCallback
                );
        },

        replaceEmoticonsCallback: function(str, p1, p2, p3) {
                if (!arguments.callee.regExp) {
                        arguments.callee.regExp = new RegExp('(="[^"]*$)|(&[^;]*$)', '');
                }
                // Avoid replacing emoticons in tag attributes or XHTML entities:
                if(p1.match(arguments.callee.regExp)) {
                        return str;
                }

                               if(p2) {

                        var R_F = "'";
                        var index = ajaxChat.arraySearch(p2, ajaxChat.emoticonCodes);
                        return         ajaxChat.replaceEmoticons(p1)
                                                                +       '<a href="javascript:ajaxChat.insertText(' + R_F
                                +   p2
                                +        R_F + ');"><img src="'
                                +        ajaxChat.dirs['emoticons']
                                +        ajaxChat.emoticonFiles[index]
                                +        '" alt="'
                                +        p2
                                +         '" onload="ajaxChat.updateChatlistView();"/></a>'
                                                                +         ajaxChat.replaceEmoticons(p3);


                                             }

                return str;

        return text;


        },

        getActiveStyle: function() {
                var cookie = this.readCookie(this.sessionName + '_style');
                var style = cookie ? cookie : this.getPreferredStyleSheet();
                return style;
        },

        initStyle: function() {
                this.styleInitiated = true;
                this.setActiveStyleSheet(this.getActiveStyle());
        },

        persistStyle: function() {
                if(this.styleInitiated) {
                        this.createCookie(this.sessionName + '_style', this.getActiveStyleSheet(), this.cookieExpiration);
                }
        },

        setSelectedStyle: function() {
                if(this.dom['styleSelection']) {
                        var style = this.getActiveStyle();
                        var styleOptions = this.dom['styleSelection'].getElementsByTagName('option');
                        for(var i=0; i<styleOptions.length; i++) {
                                if(styleOptions[i].value == style) {
                                        styleOptions[i].selected = true;
                                        break;
                                }
                        }
                }
        },

        getSelectedStyle: function() {
                var styleOptions = this.dom['styleSelection'].getElementsByTagName('option');
                if(this.dom['styleSelection'].selectedIndex == -1) {
                        return styleOptions[0].value;
                } else {
                        return styleOptions[this.dom['styleSelection'].selectedIndex].value;
                }
        },

        setActiveStyleSheet: function(title) {
                var i, a, main;
                var titleFound = false;
                for(i=0; (a = document.getElementsByTagName('link')[i]); i++) {
                        if(a.getAttribute('rel').indexOf('style') != -1 && a.getAttribute('title')) {
                                a.disabled = true;
                                if(a.getAttribute('title') == title) {
                        a.disabled = false;
                        titleFound = true;
                                }
                        }
                }
                if(!titleFound && title != null) {
                   this.setActiveStyleSheet(this.getPreferredStyleSheet());
                }
        },

        getActiveStyleSheet: function() {
                var i, a;
                for(i=0; (a = document.getElementsByTagName('link')[i]); i++) {
                        if(a.getAttribute('rel').indexOf('style') != -1 && a.getAttribute('title') && !a.disabled) {
                                return a.getAttribute('title');
                        }
                }
                return null;
        },

        getPreferredStyleSheet: function() {
                var i,a;
                for(i=0; (a = document.getElementsByTagName('link')[i]); i++) {
                        if(a.getAttribute('rel').indexOf('style') != -1
                                && a.getAttribute('rel').indexOf('alt') == -1
                                && a.getAttribute('title')
                                ) {
                                return a.getAttribute('title');
                        }
                }
                return null;
        },


        switchLanguage: function(langCode) {
                window.location.search = '?lang='+langCode;
        },

        createCookie: function(name,value,days) {
                var expires = '';
                if(days) {
                        var date = new Date();
                        date.setTime(date.getTime()+(days*24*60*60*1000));
                        expires = '; expires='+date.toGMTString();
                }
                var path = '; path='+this.cookiePath;
                var domain = this.cookieDomain ? '; domain='+this.cookieDomain : '';
                var secure = this.cookieSecure ? '; secure' : '';
                document.cookie = name+'='+encodeURIComponent(value)+expires+path+domain+secure;
        },

        readCookie: function(name) {
                if(!document.cookie)
                   return null;
                var nameEQ = name + '=';
                var ca = document.cookie.split(';');
                for(var i=0; i<ca.length; i++) {
                        var c = ca[i];
                        while(c.charAt(0) == ' ') {
                                c = c.substring(1, c.length);
                        }
                        if(c.indexOf(nameEQ) == 0) {
                                return decodeURIComponent(c.substring(nameEQ.length, c.length));
                        }
                }
                return null;
        },

        isCookieEnabled: function() {
                this.createCookie(this.sessionName + '_cookie_test', true, 1);
                var cookie = this.readCookie(this.sessionName + '_cookie_test');
                if(cookie) {
                        // Unset the test cookie:
                        this.createCookie(this.sessionName + '_cookie_test', true, -1);
                        // Cookie test successfull, return true:
                        return true;
                }
                return false;
        },

        finalize: function() {
                if(typeof this.finalizeFunction == 'function') {
                        this.finalizeFunction();
                }
                // Ensure the socket connection is closed on unload:

                this.persistSettings();
                this.persistStyle();
                this.customFinalize();
        },

        // Override to perform custom actions on flash initialization:
        initializeCustomFlashInterface: function() {
        },

        // Override to handle custom info messages
        handleCustomInfoMessage: function(infoType, infoData) {
        },

        // Override to add custom initialization code
        // This method is called on page load
        customInitialize: function() {
        },

        // Override to add custom finalization code
        // This method is called on page unload
        customFinalize: function() {
        },

        // Override to add custom user menu items:
        // Return a string with list items ( <li>menuItem</li> )
        // encodedUserName contains the userName ready to be used for javascript links
        // userID is only available for the online users menu - not for the inline user menu
        // use (encodedUserName == this.encodedUserName) to check for the current user
        getCustomUserMenuItems: function(encodedUserName, userID) {
                return '';
        },

        // Override to parse custom input messages:
        // Return replaced text
        // text contains the whole message
        parseCustomInputMessage: function(text) {
                return text;
        },

        // Override to parse custom input commands:
        // Return parsed text
        // text contains the whole message, textParts the message split up as words array
        parseCustomInputCommand: function(text, textParts) {
                return text;
        },

        // Override to replace custom text:
        // Return replaced text
        // text contains the whole message
        replaceCustomText: function(text) {
                return text;
        },

        // Override to replace custom commands:
        // Return replaced text for custom commands
        // text contains the whole message, textParts the message split up as words array
        replaceCustomCommands: function(text, textParts) {
                return text;
        },

        // Override to replace custom BBCodes:
        // Return replaced text and call replaceBBCode recursively for the content text
        // tag contains the BBCode tag, attribute the BBCode attribute and content the content text
        // This method is only called for BBCode tags which are in the bbCodeTags list
        replaceCustomBBCode: function(tag, attribute, content) {
                return '<' + tag + '>' + this.replaceBBCode(content) + '</' + tag + '>';
        },

        // Override to perform custom actions on new messages:
        // Return true if message is to be added to the chatList, else false
        customOnNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) {
                return true;
        }



}
