function OBJNAME(n) { '';return n; }

function chatLink(conversationId)
{
    var myself = this;
    var testObject = null;
    var convId = conversationId;
    var newRequest = null;
    var sendQueue = new Array();
    var timerId;
    var serviceLocation;
    var link;
    var lastHistory = 0;
    var checkTimer = 0;
    var loopTimer = 0;
    var transmitBody = '';
    var lastTransmit = 0;
    
    debugElement = null;
    
    serviceLocation = window.location.href;
    
    if(serviceLocation.indexOf('?')>0)
        serviceLocation = serviceLocation.substring(0, serviceLocation.indexOf('?') - 1);
    
    if(serviceLocation[serviceLocation.length - 1] == '/')
        serviceLocation = serviceLocation.substring(0, serviceLocation.length - 1);
        
    serviceLocation = serviceLocation + '?conversationId=' + conversationId;
    
    if((window.XMLHttpRequest) && (testObject = (new XMLHttpRequest())))
    { 
        newRequest = function() { return new XMLHttpRequest(); };
    }
    else if(window.ActiveXObject)
    { 
        var testNames = ['Msxml2.ServerXMLHTTP', 'Microsoft.ServerXMLHTTP', 'Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP']; 

        for(var i = 0; i < testNames.length; i++)
        {
            try
            {
                if((testObject = new ActiveXObject(testNames[i])))
                {
                    newRequest = new Function('return new ActiveXObject("' + testNames[i] + '");');
                    break;
                }
            }
            catch(e)
            {
                ;
            }
        }
        
        testNames = null;
    }

    if(testObject)
    { 
        testObject = null;
    } 
    else
    {
        newRequest = function() { return null; };
    }
    
    this.setDebugElement = function(element) { debugElement = element; }
    
    function outputDebug(text)
    {
        if(debugElement) { try { debugElement.innerHTML = debugElement.innerHTML + text; debugElement.scrollTop = debugElement.scrollHeight;} catch(e) {;}}
    }
    
    function outputDebugLine(text)
    {
        if(debugElement) { try { debugElement.innerHTML = debugElement.innerHTML + text + '<br />'; debugElement.scrollTop = debugElement.scrollHeight;} catch(e) {;}}
    }
    
    function checkLink()
    {
        if(!link)
        {
            link = newRequest();
            link.onreadystatechange = eventStateChanged;
            
            outputDebugLine('Physical link object created');
        }
    }
    
    function doTransmit()
    {
        try
        {
            link.open('POST', serviceLocation + '&action=say&history=' + lastHistory.toString() + '&fmt=uri', true);
            link.send(transmitBody);

            outputDebugLine('Physical link request sent');
            
            return true;
        }
        catch(e)
        {
            try
            {
                link.abort();
            }
            catch(e2)
            {
                ;
            }

            link = null;
        }
        
        return false;
    }

    function flushSend()
    {
        if(sendQueue.length > 0)
        {
            while(sendQueue.length > 0)
            {
                var nextItem = sendQueue.shift();
                
                // The server decodes this whole line with the legacy unescape() (see the "fmt=uri"
                // handling in WebChat.cs), not decodeURIComponent - encodeURIComponent produces
                // multi-byte UTF-8 %XX sequences that unescape() misreads one byte at a time
                // (mojibake for any non-ASCII character), so it must be encoded with the matching
                // legacy escape() instead.
                transmitBody = transmitBody + escape(nextItem.action + ':' + lastTransmit.toString() + ':' + nextItem.body) + '\n';
            }
        }
    }
    
    function processBodyLine(line)
    {
        var sep = line.indexOf(':');

        if(sep > 0)
        {
            var lineType = line.substr(0, sep).toLowerCase();
            var Parts = line.substr(++sep).split('&');
            var isNewHistoryEntry = false;

            if(lineType == 'history')
            {
                // The index is a plain decimal number, never percent-encoded - read and apply it
                // before decoding the rest of the fields, so a field that fails to decode can never
                // leave the cursor stuck (the server would otherwise keep re-sending this very same
                // line on every poll, forever).
                var index = parseInt(Parts[0]);

                if(index > lastHistory)
                {
                    lastHistory = index;
                    isNewHistoryEntry = true;
                }
            }

            try
            {
                // The server encodes fields with the legacy escape() (JScript) function, not
                // encodeURIComponent - decodeURIComponent throws on the %XX sequences it produces
                // for non-ASCII characters (e.g. accented letters), so it must be decoded with the
                // matching legacy unescape() instead.
                for(var i = 0; i < Parts.length; i++)
                    Parts[i] = unescape(Parts[i]);
            }
            catch(e)
            {
                outputDebugLine(e.message);
                return;
            }

            if(lineType == 'activate')
            {
                try
                {
                    myself.activateEvent.Invoke();
                }
                catch(e)
                {
                    outputDebugLine(e.message);
                }
            }
            if(lineType == 'wait')
            {
                try
                {
                    myself.waitEvent.Invoke(Parts[4]);
                }
                catch(e)
                {
                    outputDebugLine(e.message);
                }
            }
            if(lineType == 'history' && isNewHistoryEntry)
            {
                try
                {
                    myself.historyLineEvent.Invoke(Parts[1], Parts[2], Parts[3], Parts[4]);
                }
                catch(e)
                {
                    outputDebugLine(e.message);
                }
            }
        }
    }
    
    function eventStateChanged()
    {
        checkLink();
        
        outputDebugLine('Physical link state changed to ' + link.readyState.toString());
        
        if(link.readyState == 4)
        {
            if(checkTimer != 0)
            {
                window.clearTimeout(checkTimer);
                checkTimer = 0;
            }

            if(transmitBody != '')
            {
                transmitBody = '';
                lastTransmit++;
            }

            try
            {
                outputDebugLine('Got response: -------------------------------------------');
                outputDebugLine(link.responseText);
                outputDebugLine('---------------------------------------------------------');

                if(link.status == 200)
                {
                    var Lines = link.responseText.split('\n');
                    
                    for(var i = 0; i < Lines.length; i++)
                    {
                        var Line = Lines[i].replace('\r', '');
                        
                        processBodyLine(Line);
                    }
                }
            }
            catch(e)
            {
                ;
            }
            
            try
            {
                link.abort();
                link = null;
            }
            catch(e)
            {
                ;
            }

            loopTimer = window.setTimeout(loop, myself.pollInterval);
        }
    }
    
    function loop()
    {
        loopTimer = 0;

        if(checkTimer != 0)
        {
            window.clearTimeout(checkTimer);
            checkTimer = 0;
        }
        
        flushSend();

        checkLink();

        if(doTransmit())
        {
            checkTimer = window.setTimeout(loopTimeout, myself.pollInterval + 5000);
        }
        else
        {
            window.setTimeout(loop, myself.pollInterval);
        }
    }
    
    function loopTimeout()
    {
        checkTimer = 0;
        
        try
        {
            link.abort();
        }
        catch(e)
        {
            ;
        }
        
        link = null;
        
        window.setTimeout(loop, 1000);
    }
    
    function flush()
    {
        if(loopTimer != 0)
        {
            window.clearTimeout(loopTimer);
        }
        
        loop();
    }
    
    this.send = function(text) 
    {
        sendQueue.push({ action: 'say', body: text });
        flush();
    }
    
    this.disconnect = function() 
    {
        sendQueue.push({ action: 'disconnect', body: '' });
        flush();
    }
    
    this.pollInterval = 3000;
    
    this.historyLineEvent = new MulticastDelegate();
    this.activateEvent = new MulticastDelegate();
    this.waitEvent = new MulticastDelegate();
    
    sendQueue.push({ action: 'reset', body: '' });
    window.setTimeout(loop, 1000);
}


function MulticastDelegate()
{
    var members = new Array();
    
    this.Add = function()
                    {
                        var target, method;
                        
                        if(arguments.length == 1)
                        {
                            target = null;
                            method = arguments[0];
                            
                            for(var i = 0; i < members.length; i++)
                            {
                                if(members[i].target == null && members[i].method == method)
                                {
                                    return;
                                }
                            }
                            
                            
                        }
                        else
                        {
                            target = arguments[0];
                            method = arguments[1];
                            
                            for(var i = 0; i < members.length; i++)
                            {
                                if(members[i].target == target && members[i].method == method)
                                {
                                    return;
                                }
                            }
                        }
                        
                        members.push({'target':target, 'method':method});
                    };

    this.Remove = function(target, method)
                    {
                        for(var i = 0; i < members.length; i++)
                        {
                            if(members[i].target == target && (arguments.length < 2 || members[i].method == method))
                            {
                                var old = members.splice(i, 1);
                                
                                old[0].target = null;
                                old[0].method = null;
                                
                                break;
                            }
                        }
                    };

    this.Clear = function()
                    {
                        for(var i = 0; i < members.length; i++)
                        {
                            members[i].target = null;
                            members[i].method = null;
                        }
                        
                        members = new Array();
                    };
                    
    this.Invoke = function()
                    {
                        for(var i = 0; i < members.length; i++)
                        {
                            try
                            {
                                members[i].method.apply(members[i].target, arguments);
                            }
                            catch(e)
                            {
                                ;
                            }
                        }
                    };
}
