// Configuration

// todo implement in standart manner
function callMeNow(url, type, id) {
    window.open(url + '?type=' + type + '&id=' + id, '_blank',
            'width=498,height=347,resizable=no,scrollbars=no,menubar=no,toolbar=no,' +
            'status=no,location=no,directories=no,copyhistory=no');
}

//adding convenient method to array 
//for removing element
Array.prototype.remove = function(s) {
   for (i = 0; i < this.length; i++) {
     if (s == this[i])
        this.splice(i, 1);
   }
 }

//checks whether element is of string type
function isString(elem) {
    if (typeof elem == 'string') {
        return true;
    }
    return false;
}

//if current page is under https - also convert url to https
function httpToHttpsIfNeed(url) {
	if (document.location.href.indexOf("https:") >= 0
			&& url.indexOf("http:") >= 0) {
		return url.replace(/http:/g, "https:");
	} else {
		return url;
	}
}

//convert array of urls refer remote server to 
//array of urls refer proxy
function convertUrlsToProxy(urls) {
    if (ConvertPage.proxyPath == "") {
        return urls;
    }
    var proxyUrls = new Array();
    for (var name in urls) {
        if (isString(urls[name])) {
            proxyUrls[name] = convertUrl(urls[name]);
        }
    }
    return proxyUrls;
}

//convert one url refers remote server
//to url refers proxy
function convertSingleUrl(url) {
    if (ConvertPage.proxyPath == "") {
        return url;
    }
    return convertUrl(url);
}

//convert url - don't use directly. 
//Use convertUrlsToProxy or convertSingleUrl instead
function convertUrl(url) {
    var domainRegExp = /https?:\/\/.*?\//;
    var proxyUrl = url.replace(domainRegExp, ConvertPage.server + ConvertPage.proxyPath);
    return proxyUrl;
}

function fillGroupsData(params) {
    var metaData = document.getElementsByTagName("meta");
    var convertGroups = "";

	convertGroups = window["convert_group"] || "";
    for (var i = 0; i < metaData.length; i++) {
        if (metaData[i].name == "WT.cg_n"
               || metaData[i].name == "WT.cg_s") {
            if (convertGroups.length > 0) {
                convertGroups = convertGroups + ",";
            }
            convertGroups = convertGroups + metaData[i].content;
        }
    }
    params["groups"] = convertGroups;
}

function fillBookingData(params) {
    var metaData = document.getElementsByTagName("meta");
    var findFirst = false;
    var findSecond = false;

    for (var i = 0; i < metaData.length; i++) {
        if (metaData[i].name == "WT.cg_n") {
            findFirst = (metaData[i].content == "booking");
        } else if (metaData[i].name == "WT.cg_s") {
            findSecond = (metaData[i].content == "book");
        }
    }  
    //if meta is set - get params
    if (findFirst && findSecond) {
        var bookingVars = new Array("DCSext.ORG", "DCSext.DST", "DCSext.PTP", "DCSext.FPC", "DCSext.DDT", "DCSext.RF", "DCSext.AF", "DCSext.TF", "DCSext.NBR", "DCSext.RDT", "DCSext.PAX", "DCSext.ACCOM",
                  "DCSext.PRO", "DCSext.CC", "DCSext.DF", "DCSext.TC", "DCSext.DLV", "DCSext.Miles", "DCSext.BookingChannel");
        for (var i = 0; i < metaData.length; i++) {
            for (var j = 0; j < bookingVars.length; j++) {
                if (metaData[i].name == bookingVars[j]) {
                	var val = metaData[i].content;
                	if (val.indexOf("preencoded") == 0) {
                		//remove "preencoded:" prefix
                		val = val.substring(11, val.length);
                	}
                	params['bp_' + bookingVars[j]] = val;
                	break;
                }
            }
        }
    }
}

//This function gets all visitor profile field values - from
//meta-tags or from convertVisitorParams variable - and sends them  to
//server. Some values can correspond to existing fields,
//for some values there is no fields. In first case, values will
//be assigned to fields. In second case, values will be ignored.
function fillProfileData(params) {
    var metaData = document.getElementsByTagName("meta");

    var paramsCount = 0;
    for (var i = 0; i < metaData.length; i++) {
        //get start of meta tag name
        var startStr = metaData[i].name.substring(4, 0);
        if (startStr == "CVT.") {
            var fieldName = metaData[i].name.substring(4, metaData[i].name.length);
			if(fieldName == "userID") {
				params['cvp_login'] = metaData[i].content;
			} else {
				params['cvp_' + fieldName] = metaData[i].content;
			}
            paramsCount++;
        }
    }

    //if meta tags didn't provide field values - use old 
    //way to get values - through var convertVisitorParams
    if (paramsCount == 0) {
        var convertVisitorParams = window["convertVisitorParams"];
        if (convertVisitorParams) {
            for (var name in convertVisitorParams) {
                params['cvp_' + name] = convertVisitorParams[name];
            }
        }
    }
}

///////////////////////////////////////////////////////////////////////////
// General purpose methods.
///////////////////////////////////////////////////////////////////////////

/**
 * Convert general purpose functionality
 */
var ConvertEngine = {

    nextSerialId: 0,
    elementIdPrefix: "convert_element_",
    visitorIdCookieName: "Engage1UID",
    chatDetached: false,

// Creates unique id for convert DOM element.
    createElementId: function() {
        var elementId;
        do {
            elementId = ConvertEngine.elementIdPrefix + (ConvertEngine.nextSerialId++);
        } while (document.getElementById(elementId))
        return elementId;
    },

// Extracts cookie value
    getCookie: function(name) {
        var result = null;
        var myCookie = " " + document.cookie + ";";
        var searchName = " " + name + "=";
        var startOfCookie = myCookie.indexOf(searchName);
        var endOfCookie;
        if (startOfCookie != -1) {
            startOfCookie += searchName.length;
            endOfCookie = myCookie.indexOf(";", startOfCookie);
            result = unescape(myCookie.substring(startOfCookie, endOfCookie));
        }
        return result;
    },

// Detaches the floating chat into a separate browser window
    detachChat: function() {
        ConvertEngine.chatDetached = true;

        var detachContainer = document.getElementById("detach");
        var detach = detachContainer.getElementsByTagName("div");
        EchoClientMessage.setActionValue(detach[0].id, "click");
        EchoServerTransaction.connect();

        ConvertEngine.openDetachedChat();
    },

    openDetachedChat: function() {
        window.open(ConvertPage.chatServletPath + "?detached=true&init=true&chatTemplate=" + ConvertPage.chatTemplateDetached, "detachedChat",
            "width=400,height=400,toolbar=no,location=no,directories=no," +
            "status=no,menubar=no,scrollbars=no,resizable=yes");
    },

// forces a repaint of the chat window in Safari
    repaintSafariScroll: function() {
        var tmp = document.createElement("div");
        document.body.appendChild(tmp);
    },

// Returns convert visitor id.
    getVisitorId: function() {
        return ConvertEngine.getCookie(ConvertEngine.visitorIdCookieName) || -1;
    },

// Adds event listener to the element.
    addEventListener: function(type, target, listener) {
        if (target.addEventListener) {
            target.addEventListener(type, listener, false);
        } else {
            target.attachEvent("on" + type, listener);
        }
    },

// Removes event listener to the element.
    removeEventListener: function(type, target, listener) {
        if (target.removeEventListener) {
            target.removeEventListener(type, listener, false);
        } else {
            target.detachEvent("on" + type, listener);
        }
    },

// Opens document in the specified window.
    openWindow: function(url, target) {
        window.open(url, target ? target : "_blank",
                "width=700,height=500,toolbar=yes,location=yes,directories=yes," +
                "status=yes,menubar=yes,scrollbars=yes,copyhistory=yes,resizable=yes");
    },

// Processes recursively all elements inside given container.
    processRecursive: function(element, processor) {
        for (var target = element.firstChild; target; target = target.nextSibling) {
            processor(target);
            if (target.nodeType == 1) {
                ConvertEngine.processRecursive(target, processor);
            }
        }
    },

// Makes convert client initialization.
    init: function() {
        ConvertLogger.init();
        ConvertImagePreload.init();
        ConvertChatInvitation.init();
        ConvertSurvey.init();
        ConvertMO.init();
        ConvertAnimation.init();
    }
}

///////////////////////////////////////////////////////////////////////////
// Code responsible for 'DOM loaded' event listening
///////////////////////////////////////////////////////////////////////////
// Based on ELO - Encapsulated Load Object, by Robert Nyman, http://www.robertnyman.com

var ConvertELO = {
    listeners: new Array(),

// (public) adds DOM content load listener
    addDOMLoadListener: function(listener) {
        ConvertELO.listeners[ConvertELO.listeners.length] = listener;
    },

// (private) invoked on DOM content load, probably multiple times
    fireDOMLoaded: function() {
        if (!ConvertELO.loaded) {
            ConvertELO.loaded = true;
            for (var i = 0; i < ConvertELO.listeners.length; i++) {
                try {
                    ConvertELO.listeners[i].call(window);
                } catch (e) {
                }
            }
        }
    }
};

// Mozilla/Opera 9
if (document.addEventListener) {
    document.addEventListener("DOMContentLoaded", ConvertELO.fireDOMLoaded, false);
}

// Safari
if (navigator.userAgent.search(/WebKit/i) != -1) {
    ConvertELO.timer = setInterval(function() {
        if (document.readyState.search(/loaded|complete/i) != -1) {
            clearInterval(ConvertELO.timer);
            ConvertELO.fireDOMLoaded();
        }
    }, 100);
}

// Others
ConvertEngine.addEventListener("load", window, ConvertELO.fireDOMLoaded);

///////////////////////////////////////////////////////////////////////////
// Convert page lifecycle
///////////////////////////////////////////////////////////////////////////

// Handles page load/unload focus/blur page lifecycle events.
var ConvertPage = {
    echoForm: null,
    pageId: new Date().getTime(),
//    siteId: 1000, //TODO: DO NOT Commit!
    siteId: "1003",
    server: window.location.href.match(/https?:\/\/.*?\//),
    appContextRoot: null,
    appServletPath: null,
    chatServletPath: null,
    chatTemplateFloating: null,
    chatTemplateDetached: null,
    inlineRedirectServletPath: null,
    reactiveChatType: null,
    embeddedChat: null,
    blurred: false,
    pageInfoTimerId: null,
    proxyPath: "convert/convert.php?",
    remoteServer: "http://cci.convertmarketing.net/",
    sendMailAlloved : false,
    //this list should contains ids of inline engagements, on which user
    //didn't react
    inlineEngagementsList: null,


// Includes Echo2 js, adds listeners to the window
    init: function() {
        ConvertPage.webAppContextRoot = ConvertPage.server + ConvertPage.proxyPath + "1003_Convert";
        ConvertPage.appContextRoot = ConvertPage.server + ConvertPage.proxyPath + "Convert_client_1003";
        ConvertPage.appServletPath = ConvertPage.appContextRoot + "/app";
        ConvertPage.chatServletPath = ConvertPage.appContextRoot + "/chat";
        ConvertPage.detachedchatServletPath = ConvertPage.appContextRoot + "/d_chat";
        ConvertPage.inlineRedirectServletPath = ConvertPage.webAppContextRoot + "/inline/Accept";
        ConvertPage.getIpServletPath = httpToHttpsIfNeed(ConvertPage.remoteServer + "1003_Convert" + "/OnlineInfo/");
        ConvertPage.bulkEmailServletPath = httpToHttpsIfNeed(ConvertPage.webAppContextRoot + "/BulkEmailVerifier");
        ConvertPage.chatTemplateFloating = httpToHttpsIfNeed("http://cci.convertmarketing.net/everestonline/chatTemplate.html");
        ConvertPage.chatTemplateDetached = httpToHttpsIfNeed("http://cci.convertmarketing.net/everestonline/chatTemplate.html");
        ConvertPage.reactiveChatType = "float"; //"float", "frame", or "detach"
        ConvertPage.embeddedChat = ConvertPage.server  + ConvertPage.proxyPath + "Convert_client_1003" + "/embeddedChat.html";

        ConvertPage.getIp();
        ConvertPage.inlineEngagementsList = new Array();
        /*******
        we no longer need to init an embedded chat page differently and
        the content_pane can keep polling for engagements
        *************
        if (parent.frames && parent.frames["convert_chat_frame"]) {
            ConvertELO.addDOMLoadListener(ConvertPage.initPassive);
        } else {
            document.write('<script src="' + ConvertPage.appServletPath +
                           '?serviceId=Echo.ClientEngine" type="text/javascript"><\/script>');
            ConvertELO.addDOMLoadListener(ConvertPage.initActive);
        }
        *********/
        document.write('<script src="' + ConvertPage.appServletPath
                       + '?serviceId=Echo.ClientEngine" type="text/javascript"><\/script>');
        ConvertELO.addDOMLoadListener(ConvertPage.initActive);
    },

// Initialization for standard mode
    initActive: function() {
    	//send page info after DOM loading, because in case of proxy,
    	//ConvertPage.ip is initialized with DOM (see getIp() method)
        ConvertPage.sendPageInfo(true);

        var form = document.createElement("form");
        form.id = "c_root";
        form.action = "#";
        form.onsubmit = "return false";
        ConvertPage.echoForm = form;

        var windowComponent = document.getElementById("window");
        if (!windowComponent) {
            windowComponent = document.body;
            windowComponent.id = "window";
        }
        windowComponent.appendChild(form);

        var div = document.createElement("div");
        div.innerHTML =  httpToHttpsIfNeed('\n<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '
	               + 'codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" '
	               + 'width="1" height="1" id="convertFlashPlayer" align="middle"> '
	               + '<param name="allowScriptAccess" value="sameDomain" /> '
	               + '<param name="movie" value="' + ConvertPage.appContextRoot + '/convert.swf" /> '
	               + '<param name="quality" value="high" /> '
	               + '<param name="bgcolor" value="#ffffff" /> '
	               + '<embed src="' + ConvertPage.appContextRoot + '/convert.swf" quality="high" bgcolor="#ffffff" width="1" '
		       + 'height="1" name="convertFlashPlayer" align="middle" '
		       + 'allowScriptAccess="sameDomain" type="application/x-shockwave-flash" '
		       + 'pluginspage="http://www.macromedia.com/go/getflashplayer" '
		       + 'name="flashPlayer" /></object>\n ');
        document.body.appendChild(div);


        ConvertEngine.addEventListener("unload", window, ConvertPage.onUnload);
        ConvertEngine.addEventListener("focus", window, ConvertPage.onFocus);
        ConvertEngine.addEventListener("blur", window, ConvertPage.onBlur);

        /* Follwing lines causes troubles on Ridegear:
        NOTE if you uncomment this line, do not forgot about modify  EchoTextComponent.prototype.processFocus and
          EchoTextComponent.prototype.processBlur methods in WebContainer\nextapp\echo2\webcontainer\resource\js\TextComponent.js file

            if (window.attachEvent) {  //only do this for IE
                //window.attachEvent("onclick", handleFocus);
                var inputs = document.getElementsByTagName("input");
                for (var i = 0; i < inputs.length; i++) {
                    inputs[i].attachEvent("onfocus", handleFocus);
                    inputs[i].attachEvent("onblur", handleBlur);
                }
                var textareas = document.getElementsByTagName("textarea");
                for (var i = 0; i < textareas.length; i++) {
                    textareas[i].attachEvent("onfocus", handleFocus);
                    textareas[i].attachEvent("onblur", handleBlur);
                }
            }
        */

        EchoClientEngine.init(ConvertPage.appServletPath, true); //boolean is for Echo2 debug mode
        //Replace default Echo response handler to avoid exception if documentElement is null
        EchoAsyncMonitor.responseHandler = function(conn) {
            if (conn.getResponseXml().documentElement != null && "true" == conn.getResponseXml().documentElement.getAttribute("request-sync")) {
                // Server is requesting synchronization: Initiate server transaction.
                EchoServerTransaction.connect();
            } else {
                // Server does not require synchronization: restart countdown to next poll request.
               EchoAsyncMonitor.start();
            }
        };
        ConvertEngine.init();
    },

// Initialization for the case when embedded chat presented on the page
    initPassive: function() {
        var links = document.getElementsByTagName("a");
        for (var i = 0; i < links.length; i++) {
            var href = links[i].href;
            if (href && href.indexOf("http") == 0 && href.indexOf(ConvertPage.server) == -1) {
                links[i].target = "_blank";
            }
        }
    },

// Sends page info to the server. Used for initial requests and Echo2 app recovery
    sendPageInfo: function(withBooking) {
        ConvertPage.pageInfoTimerId = null;
        var params = {
            htmlservice: false,
            //groups: convertGroups,
            site: ConvertPage.siteId,
            referrer: document.referrer,
            reactiveChatType: ConvertPage.reactiveChatType,
            proactiveChatEmbedded: false,
            embeddedChatPage: ConvertPage.embeddedChat,
            chatTemplate: ConvertPage.chatTemplateFloating,
            placeOnHoldMsg: "Please stand by, a site representative has been paged.",

            pageId: ConvertPage.pageId,
            pageURL: document.location.href,
            title: document.title,
            chatButton: window.chatButton,
            visitorEmail: window.visitorEmail,
            visitorLogin: window.visitorLogin,
            purchased: window["convert_shopping_cart_purchased"],
            value: window["convert_shopping_cart_total_value"],
            id: window["convert_shopping_cart_order_id"],
            count: window["convert_shopping_cart_items_count"]
        }

        if (ConvertPage.ip) {
            params.ip = ConvertPage.ip;
        }

        var items = window["convert_shopping_cart_items"];
        if (params.count && items) {
            for (var i = 0; i < params.count; i++) {
                params['s' + i] = items[i][sku];
                params['p' + i] = items[i][price];
                params['q' + i] = items[i][quantity];
            }
        }

        fillProfileData(params);
        fillGroupsData(params);

        if (withBooking) {
	        fillBookingData(params);
	    }

        ConvertLogger.log("Sending page info. PageId is " + params.pageId);

        //there is no handler for "pageLoad"
        //new ConvertClientMessage("pageLoad", params).send(true);
        new ConvertRequest(ConvertPage.appServletPath).send(params, true);
    },

// Notifies server about page has been unloaded
    onUnload: function() {
        //send "No Response" for all inline engagements
        for (var i = 0; i < ConvertPage.inlineEngagementsList.length; i++) {
            var engagementId = ConvertPage.inlineEngagementsList[i];
            new ConvertEngagementResult(engagementId, ConvertEngagementResult.noResponse).send();
        }
        ConvertPage.inlineEngagementsList = new Array();
        new ConvertClientMessage("pageUnload", {
            pageUrl: document.location.href,
            pageId: ConvertPage.pageId
        }).send(true);
        // force reload frames
        for (var i=0; i<frames.length; i++) {
            frames[i].history.go(0);
        }
    },

// Handles window focus gain events. Restores synch with server if necessary.
    onFocus: function(evt) {
        ConvertLogger.log("Focus gained. Timer: " + EchoAsyncMonitor.timeoutId + "; blurred: " + ConvertPage.blurred);
        var target = window.event ? window.event.srcElement : evt.target;
        if (target && target.nodeType == 1) {
            ConvertLogger.log("Event source element: " + target.tagName);
        }

        if (!EchoAsyncMonitor.timeoutId && ConvertPage.blurred) {
            ConvertLogger.log("Restoring synchronization");

            EchoAsyncMonitor.connect();
            if (!target || !target.tagName || (target.tagName != "TEXTAREA" && target.tagName != "INPUT")) {
                ConvertLogger.log("Current state requesting");
                ConvertPage.sendPageInfo(false);
                new ConvertRequest(ConvertPage.appServletPath).send({opState: true});
            }

            ConvertPage.blurred = false;
        }
    },

// Handles window focus gain events. Restores synch with server if necessary.
    onBlur: function(evt) {
        ConvertLogger.log("Focus lost. Timer: " + EchoAsyncMonitor.timeoutId + "; blurred: " + ConvertPage.blurred);
        var target = window.event ? window.event.srcElement : evt.target;
        if (target && target.nodeType == 1) {
            ConvertLogger.log("Event source element: " + target.tagName);
        }

        if (EchoAsyncMonitor.timeoutId && !ConvertPage.blurred) {
            ConvertLogger.log("Stopping synchronization")

            EchoAsyncMonitor.stop();

            ConvertPage.blurred = true;
        }
    },

// Dynamically includes a CSS file into the web page
    includeCSS: function(cssUrl) {
	//css should be download from local site, not remote through proxy
        //var proxyCssUrl = convertSingleUrl(cssUrl);
        var head = document.getElementsByTagName("head")[0];
        var node = document.createElement("link");
        node.type = "text/css";
        //if page is https - modify css url also to https
        node.href = httpToHttpsIfNeed(cssUrl);
        node.media = "all";
        node.rel = "Stylesheet";
        head.appendChild(node);
    },

// Recovers Echo2 application on server side.
    recoverEcho: function() {
        if (!ConvertPage.pageInfoTimerId) {
            ConvertLogger.log("Recovering Echo2 application");
            var root = document.getElementById("window");
            var toDelete = new Array();
            for (var element = root.firstChild; element; element = element.nextSibling) {
                if (EchoDomPropertyStore.getPropertyValue(element.firstChild, "component")) {
                    toDelete[toDelete.length] = element;
                }
            }
            for (var i = 0; i < toDelete.length; i++) {
                root.removeChild(toDelete[i]);
            }

            EchoClientMessage.reset();
            EchoClientMessage.setInitialize();
            EchoClientAnalyzer.analyze();
            EchoServerTransaction.postProcess();
            EchoServerTransaction.connect();
            ConvertPage.pageInfoTimerId = setTimeout("ConvertPage.sendPageInfo()", 500);
        }
    },

    getIp: function() {
        if (ConvertPage.proxyPath == "") {
            return;
        }
        
        //now retrieve ip using dynamic script with src that points to onlineInfoServlet
        //below - old approach, that uses XmlHttpRequest. But because of Amtrak 
        //servers configuration, old approach doesn't return proper ip
        var url = ConvertPage.getIpServletPath + "get_my_ip";
        var head = document.getElementsByTagName("head")[0];
        var ipScript = document.createElement("script");
        ipScript.src = url;
        head.appendChild(ipScript);
        
        //var request = new ConvertRequest("").createRequest();
        //request.open("GET", url, false);
        //request.send(null);
        //ConvertPage.ip = request.responseText.replace('\n', '');
    },

    checkFramedChatOpened: function(redirectUrl) {
        if (!parent.ConvertEngineConfig) {
             window.location = redirectUrl;
        }
    }
}

///////////////////////////////////////////////////////////////////////////
// Convert image preload
///////////////////////////////////////////////////////////////////////////

// Loads images used for 'hover' buttons state rendering to prevent
// buttons disappearing on first 'mouse over' event
var ConvertImagePreload = {
    imageList: ["/images/chat/send_hover.jpg", "/images/chat/close_hover.jpg"],

    init: function() {
        var imageList = ConvertImagePreload.imageList;
        for (var i = 0; i < imageList.length; i++) {
            var image = document.createElement("img");
            var src = ConvertPage.appContextRoot + imageList[i];
            image.src = src;
            imageList[i] = image;
            ConvertLogger.log(src + " cached");
        }
    }
};

///////////////////////////////////////////////////////////////////////////
// Convert transport
///////////////////////////////////////////////////////////////////////////

// Request constructor
function ConvertRequest(url, resultHandler, errorHandler) {
    this.url = url;
    this.resultHandler = resultHandler;
    this.errorHandler = errorHandler;
    ConvertLogger.log("ConvertRequest object created for url: " + url);
}

// XMLHttpRequest object's ready state value when interactions completed
ConvertRequest.READY_STATE_COMPLETE = 4;

// Request methods
ConvertRequest.prototype = {

// (public) Sends parameters to server
    send: function(parameters, blocking, method) {
        var request = this.createRequest();
        if (request) {
            request.open(method || "POST", this.url, !blocking);
            request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            var thisInstance = this;
            request.onreadystatechange = function() {
                thisInstance.processAjaxResponse(request);
            }
            var queryString = this.createQueryString(parameters);
            ConvertLogger.log("Sending " + (blocking ? "" : "non-") + "blocking request: " + queryString);
            request.send(queryString);
            ConvertLogger.log("Sent")
        } else {
            ConvertLogger.log("Fatal error: Unable to create XMLHttpRequest object");
        }
    },

// (private) creates XmlHttpRequest object
     createRequest: function() {
        var request = null;
        if (window.XMLHttpRequest) {
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) {
            var aVersions = [ "MSXML2.XMLHTTP.7.0", "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 < aVersions.length; i++) {
		    	try {
	         	    request = new ActiveXObject(aVersions[i]);
					break;
	            } catch (oError) {
					//alert("error");
			    }
            }
        }
        return request;
    },


// (private) joins parameters associative array into urlencoded string)
    createQueryString: function(parameters) {
        var result = "";
        if (parameters) {
            for (var name in parameters) {
                var value = parameters[name];
                if (typeof value != "undefined") {
                    result += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(value);
                }
            }
        }
        return result.substring(1);
    },

// (private) transport callback method
    processAjaxResponse: function(request) {
        ConvertLogger.log("Request object callback called with " + request.readyState + " ready state");
        if (request.readyState == ConvertRequest.READY_STATE_COMPLETE) {
            var status = request.status;
            if (!status || status == 200) {
                ConvertLogger.log("Request is succeeded");
                if (this.resultHandler) {
                    this.resultHandler(request);
                }
            } else {
                ConvertLogger.log("Request failed: " + request.statusText + " (HTTP error code: " + status + ")");
                if (this.errorHandler) {
                    this.errorHandler(request);
                }
            }
        }
    }
}

///////////////////////////////////////////////////////////////////////////
// Object responsible for elements positioning and animation
///////////////////////////////////////////////////////////////////////////

// Creates object responsible for target moving across the screen.
// To position element [and start movement] you must instantiate this object and invoke it's start() method.
// Parameters (corner, x and y are required):
// target: HTML element to move (if element is Echo2 WindowPane container, appropriate handling will be used).
// corner: one of "Upper Left", "Upper Right", "Lower Right" or "Lower Left";
// x, y: Original target coordinates;
// direction: movement direction, one of "None", "Up - Down" or "Left - Right";
// speed: movement speed (pixels per second)
// tripCount: full treeps count before movement stops;
function ConvertAnimation(target, corner, x, y, direction, speed, tripCount) {
    if (target.animation) {
        target.animation.dispose();
    }
    target.animation = this;
    this.target = target;

    corner = corner.split(" ");
    this.xPosition = new ConvertAnimation.Position(corner[1] == "Left", x, "scrollWidth", "scrollLeft");
    this.yPosition = new ConvertAnimation.Position(corner[0] == "Upper", y, "scrollHeight", "scrollTop");

    this.turnCount = ConvertAnimation.abs(tripCount) * 2;
    var step = ConvertAnimation.abs(speed) * ConvertAnimation.period * .001;
    if (direction == "Left - Right") {
        this.xPosition.step = step;
    } else if (direction == "Up - Down") {
        this.yPosition.step = step;
    }
}

ConvertAnimation.period = 40;
ConvertAnimation.instances = new Array();

ConvertAnimation.abs = function(value) {
    value = parseInt(value);
    return isNaN(value) ? 0 : Math.abs(value);
}

// (public) starts animation system timer
ConvertAnimation.init = function() {
    setInterval("ConvertAnimation.process()", ConvertAnimation.period);
}

// (private) processes elements on timeout
ConvertAnimation.process = function() {
    var animations = ConvertAnimation.instances;
    for (var i = 0, size = animations.length; i < size; i++) {
        var animation = animations[i];
        if (animation) {
            animation.process();
        }
    }
}

ConvertAnimation.prototype = {
// (public) use this to start element moving
// you must dispose the animation when it doesn't make a sense any more
    start: function() {
        var index = 0;
        var animations = ConvertAnimation.instances;
        for (var size = animations.length; index < size; index++) {
            if (!animations[index]) {
                break;
            }
        }
        ConvertAnimation.instances[index] = this;
    },

// (private) arranges target on the screen
    process: function() {
        this.setPosition();
        if (this.xPosition.step == 0 && this.yPosition.step == 0) {
            this.dispose();
        }
        if (this.xPosition.hasBeenTurned || this.yPosition.hasBeenTurned) {
            this.turnCount--;
            if (this.turnCount == 0) {
                this.xPosition.step = 0;
                this.yPosition.step = 0;
            }
        }
    },

// (public) use this if you need to set elment position only once
// You haven't to dispose animation after this method usage
    setPosition: function() {
        var target = this.target;
        var x = this.xPosition.next(target);
        var y = this.yPosition.next(target);
        var component = EchoDomPropertyStore.getPropertyValue(target, "component");
        if (component) {
            component.setPosition(x + this.xPosition.scroll.scrollLeft, y + this.yPosition.scroll.scrollTop);
            //component.redraw();
        }
        target.style.left = x + "px";
        target.style.top = y + "px";
    },

// (public) use this when animated element removed from screen
    dispose: function() {
        this.target.animation = null;
        var animations = ConvertAnimation.instances;
        for (var i = 0, size = animations.length; i < size; i++) {
            if (animations[i] == this) {
                animations[i] = null;
            }
        }
    }
}

// Class that controls target position along axis
ConvertAnimation.Position = function(strict, offset, sizeProperty, scrollProperty) {
    this.step = 0;
    this.position = 0;
    this.strict = strict;
    this.offset = ConvertAnimation.abs(offset);
    this.sizeProperty = sizeProperty;
    this.scrollProperty = scrollProperty;
    this.size = {scrollWidth : 0, scrollHeight : 0};
    this.scroll = {scrollLeft : 0, scrollTop : 0};
}

// Calculates new target position, sets hasBeenTurned flag
ConvertAnimation.Position.prototype.next = function(target) {
    this.hasBeenTurned = false;
    this.calculateViewportSize();
    this.calculateScrollingOffset();
    var viewPortSize = this.size[this.sizeProperty];
    var viewPortOffset = this.scroll[this.scrollProperty];
    var targetSize = target[this.sizeProperty];
    var maxPosition = viewPortSize - targetSize - (2 * this.offset);
    var newPosition = 0;
    if (maxPosition > this.step) {
        newPosition = this.position + this.step;
        if (newPosition > maxPosition) {
            newPosition = 0;
            this.hasBeenTurned = true;
            this.strict = !this.strict;
        }
    }
    this.position = newPosition;
    if (this.strict) {
        return viewPortOffset + this.offset + newPosition;
    } else {
        return viewPortSize + viewPortOffset - this.offset - targetSize - newPosition;
    }
}

//Calculate current viewport size
ConvertAnimation.Position.prototype.calculateViewportSize = function() {
    if (typeof window.innerWidth != 'undefined') {
        this.size.scrollWidth = window.innerWidth;
        this.size.scrollHeight = window.innerHeight;
    } else if (typeof document.documentElement != 'undefined'
            && typeof document.documentElement.clientWidth !=
               'undefined' && document.documentElement.clientWidth != 0) {
        this.size.scrollWidth = document.documentElement.clientWidth;
        this.size.scrollHeight = document.documentElement.clientHeight;
    } else {
        this.size.scrollWidth = document.getElementsByTagName('body')[0].clientWidth;
        this.size.scrollHeight = document.getElementsByTagName('body')[0].clientHeight;
    }
}
//Calculate current viewport scrolling offset
ConvertAnimation.Position.prototype.calculateScrollingOffset = function () {
    if (self.pageYOffset) {
        // all except Explorer
        this.scroll.scrollLeft = self.pageXOffset;
        this.scroll.scrollTop = self.pageYOffset;
    } else if (document.documentElement && document.documentElement.scrollTop) {
        // Explorer 6 Strict
        this.scroll.scrollLeft = document.documentElement.scrollLeft;
        this.scroll.scrollTop = document.documentElement.scrollTop;
    }
    else if (document.body) {
        // all other Explorers
        this.scroll.scrollLeft = document.body.scrollLeft;
        this.scroll.scrollTop = document.body.scrollTop;
    }
}


///////////////////////////////////////////////////////////////////////////
// Logger
///////////////////////////////////////////////////////////////////////////

// Logging subsystem
var ConvertLogger = {

// keyphrase to type to open log pane: "convertdebug"
    key: [67, 79, 78, 86, 69, 82, 84, 68, 69, 66, 85, 71],
    typePosition: 0,

    messages: new Array(),
    logElement: null,

// (public) Use this method to append log messages to display
    log: function(message) {
        if (ConvertLogger.logElement) {
            ConvertLogger.append(message);
        } else {
            ConvertLogger.messages[ConvertLogger.messages.length] = message;
        }
    },

// (private) listens visitor key typing to open debug pane on keyphrase
    onKeyDown: function(evt) {
        var event = window.event || evt;
        if (event.keyCode == ConvertLogger.key[ConvertLogger.typePosition++]) {
            if (ConvertLogger.typePosition == ConvertLogger.key.length) {
                ConvertLogger.flush();
            }
        } else {
            ConvertLogger.typePosition = 0;
        }
    },

// (private) prepares logger layout, flushes collected messages
    flush: function() {
        var logElement = document.createElement("div");
        ConvertLogger.logElement = logElement;
        logElement.style.overflow = "scroll";
        logElement.style.border = "1px solid Black";
        logElement.style.backgroundColor = "#ccffcc";
        logElement.style.position = "absolute"
        logElement.style.right = "50px";
        logElement.style.bottom = "50px";
        logElement.style.width = "300px";
        logElement.style.height = "450px";
        document.body.appendChild(logElement);

        var messages = ConvertLogger.messages;
        ConvertLogger.messages = undefined;
        for (var i = 0; i < messages.length; i++) {
            ConvertLogger.append(messages[i], logElement);
        }
    },

// (private) appends message to the logElement, scrolls logElement to the bottom
    append: function(message) {
        var now = new Date();
        var time = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds()
        var messageDiv = document.createElement("div");
        messageDiv.innerHTML = "<b>" + time + "</b>&nbsp;" + message;
        ConvertLogger.logElement.appendChild(messageDiv);
        ConvertLogger.logElement.scrollTop = 1000000;
    },

    init: function() {
        ConvertEngine.addEventListener("keydown", document, ConvertLogger.onKeyDown);
    }
}

///////////////////////////////////////////////////////////////////////////
// Popup engagement container
///////////////////////////////////////////////////////////////////////////

// todo take implementation based on Echo2 WindowPane

///////////////////////////////////////////////////////////////////////////
// Inline engagement container
///////////////////////////////////////////////////////////////////////////
/**
 * Inline engagements wrapper class.
 */
function ConvertEngagementWrapper(elementId, engagementId, noResponseTimeout, keepContentAfterResponse, isManual) {
    ConvertLogger.log("New engagement wrapper creation. Element id: " + elementId);
    // bad realisation. this is done to be consistent with applet.
    var element = document.getElementById(elementId);
    if (!element) {
        ConvertLogger.log("Container not found for engagement, sing dummy container");
        new ConvertEngagementResult(engagementId, ConvertEngagementResult.noResponse).send();
        element = ConvertEngagementWrapper.dummyContainer;
        noResponseTimeout = 0;
    }
    // disposing old engagement
    if (element.engagement) {
        ConvertLogger.log("Element already contains engagement");
        element.engagement.dispose();
    }
    // references
    this.element = element;
    this.isManual = isManual;
    element.engagement = this;
    element.engagementId = engagementId;
    // protecting original content
    element.originalDisplay = element.style.display;
    element.originalContent = element.innerHTML;
    // no reponse timeout
    if (noResponseTimeout) {
        ConvertLogger.log("'No response' timer enabled, timeout: " + noResponseTimeout);
        element.noResponseTimerId = window.setTimeout(
                "ConvertEngagementWrapper.processNoResponse('" + elementId + "')",
                noResponseTimeout * 1000);
    }
    this.keepContentAfterResponse = keepContentAfterResponse;
}

ConvertEngagementWrapper.dummyContainer = document.createElement("div");

// Finds element by id and disposes attached engagement.
// Sends 'No response' engagement result to the server.
ConvertEngagementWrapper.processNoResponse = function(elementId) {
    var element = document.getElementById(elementId);
    new ConvertEngagementResult(element.engagementId, ConvertEngagementResult.noResponse).send();
    element.engagement.dispose();
}

ConvertEngagementWrapper.prototype = {
// Sets engagement content.
    setHtmlContent: function(content) {
        this.element.innerHTML = (content ? content : "");
    },

// Replaces current element content by given dom elements.
    setDomContent: function(elements) {
        ConvertLogger.log("Setting DOM content for eng. wrapper: " + elements.length + " fetched");
        var baseElement = this.element;
        while (baseElement.hasChildNodes()) {
            baseElement.removeChild(baseElement.firstChild);
        }
        for (var i = 0; i < elements.length; i++) {
            baseElement.appendChild(elements[i]);
        }
    },

// Replaces element engagement content with original one.
    dispose: function() {
        var element = this.element;
        if (element && element.engagementId != undefined) {
            ConvertLogger.log("Disposing engagement " + element.engagementId);
            window.clearTimeout(element.noResponseTimerId);

            // remove reference to engagement
            element.engagementId = undefined;
            element.engagement = undefined;

            // restore elemet content/visibility
            if (!this.keepContentAfterResponse) {
                element.style.display = element.originalDisplay;
                element.innerHTML = element.originalContent;
            }

            // unregister old listeners
            var allEventListeners = this.eventListeners;
            if (allEventListeners) {
                for (var type in allEventListeners) {
                    var eventListeners = allEventListeners[type];
                    for (var i = 0; i < eventListeners.length; i++) {
                        ConvertEngine.removeEventListener(type, element, eventListeners[i]);
                    }
                }
            }
        }
        this.element = undefined;
    },

// Adds event listener to representative component.
// Listener will be removed by dispose() method.
    addEventListener: function(type, listener) {
        ConvertLogger.log("Adding " + type + " events listener to engagement " + this.engagementId + " wrapper");
        var allEventListeners = this.eventListeners;
        if (allEventListeners == undefined) {
            this.eventListeners = allEventListeners = new Array();
        }
        var eventListeners = allEventListeners[type];
        if (eventListeners == undefined) {
            allEventListeners[type] = eventListeners = new Array();
        }
        ConvertEngine.addEventListener(type, this.element, listener);
        eventListeners[eventListeners.length] = listener;
    }
}

///////////////////////////////////////////////////////////////////////////
// Marketing offer handler functions.
///////////////////////////////////////////////////////////////////////////

/**
 * Marketing offer engagement.
 */
var ConvertMO = {

    popupContainer: null,
    popupContainerId: "marketing_offer_popup",

    init: function() {
        ConvertLogger.log("Marketing offer engine initialization");
        var div = document.createElement("div");
        ConvertMO.popupContainer = div;
        div.id = ConvertMO.popupContainerId;
        div.style.position = "absolute";
        div.style.display = "none";
        document.body.appendChild(div);
    },

// (public) Embeds inline MO
    showInlineMO: function(elementId, engagementId, imageSrc, targetUrl, timeout) {

        ConvertLogger.log("Inline marketing offer fetched for element " + elementId);

        var engagement = new ConvertEngagementWrapper(elementId, engagementId, timeout, true);
        engagement.setDomContent([ConvertMO.createMOImage(convertSingleUrl(imageSrc), convertSingleUrl(targetUrl))]);
        ConvertEngagementResult.registerStartTime(engagementId);
    },

// (public) Opens popup MO
    showPopupMO: function(engagementId, imageSrc, targetUrl, timeout, x, y, corner, movement, speed, tripCount) {

        ConvertLogger.log("Popup marketing offer fetched for target url: " + targetUrl);

        var engagement = new ConvertEngagementWrapper(ConvertMO.popupContainerId, engagementId, timeout);
        var image = ConvertMO.createMOImage(convertSingleUrl(imageSrc), convertSingleUrl(targetUrl));
        engagement.setDomContent([image]);

        new ConvertAnimation(ConvertMO.popupContainer, corner, x, y, movement, speed, tripCount).start();
        ConvertMO.popupContainer.style.display = "inline";
        ConvertEngagementResult.registerStartTime(engagementId);
    },

// Creates marketing offer element.
// Result is hyperlink element or just image.
    createMOImage: function(imageSrc, targetUrl) {
        ConvertLogger.log("Marketing offer image creation, image URL: " + imageSrc);
        var image = document.createElement("img");
        image.onclick = ConvertMO.acceptMO;
        image.src = imageSrc;
        image.targetUrl = targetUrl;
        image.style.cursor = "pointer";
        image.style.border = "0px none Black";
        return image;
    },

// Sends 'accept' engagement result. Disposes engagement.
    acceptMO: function() {
        ConvertLogger.log("Marketing offer accepted");
        var element = this;
        while (element && !element.engagement) {
            element = element.parentNode;
        }
        if (element) {
            new ConvertEngagementResult(element.engagementId, ConvertEngagementResult.accept).send();
            element.engagement.dispose();
            if (element.animation) {
	            element.animation.dispose();
            }
        }
        if (this.targetUrl) {
            ConvertEngine.openWindow(this.targetUrl);
        }
    }
}

///////////////////////////////////////////////////////////////////////////
// Chat button handler functions.
///////////////////////////////////////////////////////////////////////////

/**
 * Convert chat invitation engagement.
 */
var ConvertChatInvitation = {

// handler names
    inlineHandlerName: "inlineChatInvitation",
    dhtmlHandlerName: "dhtmlChatInvitation",
    mailHandlerName: "mailChatInvitation",

// container for dhtml engagement
    dhtmlContainer: null,
// dhtml engagement contaner Id
    dhtmlContainerId: "dhtml_invite",

// possible operator states
    awayOpState: "awayOpState",
    availOpState: "availOpState",
    pagedOpState: "pagedOpState",
// current operator state
    currentOperatorState: "awayOpState",
// flag indicating visitor can make chat request
    engagementsAreActive: false,
// flag indicating that chat is opened
    isChatOpened: false,

    init: function() {
        ConvertLogger.log("Chat invitation engine initialization");
        // create dhtml chat invitation container
        var div = document.createElement("div");
        ConvertChatInvitation.dhtmlContainer = div;
        div.id = ConvertChatInvitation.dhtmlContainerId;
        div.style.position = "absolute";
        div.style.display = "none";
        document.body.appendChild(div);
        this.operatorStateImageUrls = new Array();
        this.chatInvitationImages = new Array();
        this.engagementIds = new Array();
    },

// Updates images (inline chat invitation buttons) to reflect operator state.
    setOperatorState : function(operatorState) {
        ConvertChatInvitation.currentOperatorState = operatorState;
        ConvertChatInvitation.engagementsAreActive = (operatorState == ConvertChatInvitation.availOpState);
        var cursor = (ConvertChatInvitation.engagementsAreActive || ConvertPage.sendMailAlloved) ? "pointer" : "default";
        for (var i = 0, length = this.engagementIds.length; i < length; i++) {
        	var engId = this.engagementIds[i];
            var image = this.chatInvitationImages[engId];
	        var src = this.operatorStateImageUrls[engId][operatorState];
	        if (src) {
        	    image.style.display = "inline";
    	        image.style.cursor = cursor;
	            image.src = src;
	        } else {
        	    image.style.display = "none";
	        }
        }
        if (!ConvertChatInvitation.engagementsAreActive) {
            var dhtmlEngagement = ConvertChatInvitation.dhtmlContainer;
            if (dhtmlEngagement.engagement && !dhtmlEngagement.engagement.isManual) {
                dhtmlEngagement.engagement.dispose();
            }
        }
    },

    /**
     * Updates state of chat invite engagements buttons
     */
    updateState : function () {
        this.setOperatorState(ConvertChatInvitation.currentOperatorState);
    },

// Invoked when user clicks on chat invitation image.
// Method ensures that chat isn't opened and operator is available,
// then sends chat request to the server and set operator status to 'paged'
// Method parameters are optional.
    sendChatRequest: function(engagementId, handlerName, isManual) {
        if (!ConvertChatInvitation.isChatOpened && (ConvertChatInvitation.engagementsAreActive || isManual)) {
            if (engagementId) {
                new ConvertEngagementResult(engagementId, ConvertEngagementResult.accept).send();
            }
            if (handlerName != ConvertChatInvitation.dhtmlHandlerName) {
                handlerName = ConvertChatInvitation.inlineHandlerName;
            }
            var params = new Object();
            params.engagementId = engagementId;
            new ConvertClientMessage(handlerName, params).send();
            ConvertChatInvitation.setOperatorState(ConvertChatInvitation.pagedOpState);
        } else {
            ConvertLogger.log("Operator is paged, unavailable or already talking to you");
        }
    },

// Sets flag, that indicates if chat is opened
    setChatOpened: function(opened) {
        ConvertChatInvitation.isChatOpened = opened;
    },

//////// Inline invitations

// image name for inline invitation images
    operatorImageName: "convertChatInvitationImage",
// operator state image urls
    operatorStateImageUrls: null,
    chatInvitationImages: null,
    engagementIds:null,

// Inserts chat invitation button into specified element.
// If operator state image url is known, uses this url.
// If operator image url isn't known (default chat invitation
// for instance) image 'display' style property will be set to 'none'.
    addInlineChatInvitation: function(elementId, engagementId, timeout, urls) {
        ConvertLogger.log("Adding inline chat invitation, engagement Id:" + engagementId);
        // create image
        var proxyUrls = convertUrlsToProxy(urls);
        this.operatorStateImageUrls[engagementId] = proxyUrls;
        this.engagementIds[this.engagementIds.length] = engagementId;
        var image = document.createElement("img");
        image.style.border = "0px none White";
        image.name = ConvertChatInvitation.operatorImageName;
        if (proxyUrls[ConvertChatInvitation.currentOperatorState]) {
            image.src = proxyUrls[ConvertChatInvitation.currentOperatorState];
            if (ConvertChatInvitation.engagementsAreActive || ConvertPage.sendMailAlloved) {
                image.style.cursor = "pointer";
            }
        } else {
            image.style.display = "none";
        }
        this.chatInvitationImages[engagementId] = image;
        // show engagement
        var engagement = new ConvertEngagementWrapper(elementId, engagementId);
        engagement.setDomContent([image]);
        engagement.addEventListener("click", ConvertChatInvitation.onClickInline);
        ConvertEngagementResult.registerStartTime(engagementId);
    },

// Mouse clicks listener. Fires 'invitation accepted' event to server.
    onClickInline: function(event) {

        //Operator mailing form processing
        if(!ConvertChatInvitation.engagementsAreActive && !ConvertChatInvitation.isChatOpened) {
            //There are no engagements active -> visitor can try send offline email to operator
                var params = new Object();
                new ConvertClientMessage(ConvertChatInvitation.mailHandlerName, params).send();
	        return;
        }

        ConvertLogger.log("Inline chat invitation clicked");
        var element = window.event ? window.event.srcElement : event.target;
        while (element && !element.engagement) {
            element = element.parentNode;
        }
        if (element) {
            ConvertChatInvitation.sendChatRequest(element.engagementId, ConvertChatInvitation.inlineHandlerName);
        }

        if (ConvertPage.reactiveChatType == "detach") {
            ConvertEngine.openDetachedChat();
        } else {
            window.focus();  //make sure browser window stays focused for polling
        }
    },

//////// DHTML invitations

// Opens dhtml chat invitation
    showDhtmlChatInvitation: function(
            engagementId, imageUrl, timeout,
            closeX, closeY, closeWidth, closeHeight,
            x, y, corner, movement, speed, tripCount, isManual)
    {
        ConvertLogger.log("DHTML engagement fetched");
        var engagement = new ConvertEngagementWrapper(ConvertChatInvitation.dhtmlContainerId, engagementId, timeout, null, isManual);
        var element = engagement.element;
        element.engagementId = engagementId;

        new ConvertAnimation(ConvertChatInvitation.dhtmlContainer, corner, x, y, movement, speed, tripCount);

        var imageMapDiv = document.createElement("div");

        var mapName = ConvertEngine.createElementId();
        var imageMapCode = '<map name="' + mapName + '"><area shape="rect" coords="' +
                           closeX + "," + closeY + "," + (closeX + closeWidth) + "," + (closeY + closeHeight) +
                           '" href="javascript:ConvertChatInvitation.killDhtmlInvitation()">'
        imageMapCode += '<area shape="rect" coords="0,0,10000,10000' +
                        '" href="javascript:ConvertChatInvitation.acceptDhtmlInvitation()">';
        imageMapDiv.innerHTML = imageMapCode;

        var image = document.createElement("img");
        engagement.setDomContent([imageMapDiv, image]);

        image.style.border = "0px none White";
        image.useMap = "#" + mapName;
        image.onload = ConvertChatInvitation.onImageLoad;
        image.src = convertSingleUrl(imageUrl);

        element.style.display = "inline";
        ConvertEngagementResult.registerStartTime(engagementId);
    },

    onImageLoad: function() {
        this.onload = null;
        ConvertLogger.log("DHTML engagement image loaded");
        ConvertChatInvitation.dhtmlContainer.animation.start();
    },

// Sends 'accept' result for dhtml invitation.
// Removes invitation element from screen.
    acceptDhtmlInvitation: function() {
        ConvertLogger.log("DHTML engagement accepted");
        var element = ConvertChatInvitation.dhtmlContainer;
        var engagementId = element.engagementId;
        if (engagementId) {
            ConvertChatInvitation.sendChatRequest(engagementId, ConvertChatInvitation.dhtmlHandlerName, element.engagement.isManual);
            if (element.engagement) {
                element.engagement.dispose();
            }
            element.animation.dispose();
        }
    },

// Sends 'kill' result for dhtml invitation.
// Removes invitation element from screen.
    killDhtmlInvitation: function() {
        ConvertLogger.log("DHTML engagement killed");
        var element = ConvertChatInvitation.dhtmlContainer;
        if (element.engagement) {
            new ConvertEngagementResult(element.engagementId, ConvertEngagementResult.kill).send();
            element.engagement.dispose();
            element.animation.dispose();
        }
    }
}

///////////////////////////////////////////////////////////////////////////
// Convert form/survey engine.
///////////////////////////////////////////////////////////////////////////

var ConvertSurvey = {

    popupContainerId: "survey_popup",

// hidden input element name, that value is real form target
    realFormTarget: "convertFormTaget",

// form validator question element attribute names
    questionCaption: "convertQuestionCaption",
    emailQuestion: "convertEmailQuestion",
    bulkEmailRestricted: "convertBulkEmailRestricted",
    requiredQuestion: "convertRequiredQuestion",

// Creates div for popup survey
    init: function() {
        ConvertLogger.log("Survey engine initialization");
        var div = document.createElement("div");
        div.id = ConvertSurvey.popupContainerId;
        div.style.border = "1px solid Black";
        div.style.zIndex = "10";
        div.style.background = "White";
        div.style.padding = "3px";
        div.style.position = "absolute";
        div.style.display = "none";
        //div.style.maxWidth = "300px";
        //div.style.maxHeight = "500px";
        div.style.display = "none";
        document.body.appendChild(div);
    },

// Places survey form into the document.
    showSurvey: function(engagementId, message, timeout, location, xOffset, yOffset) {
        try {
            var isPopup = (xOffset != undefined);
            ConvertLogger.log("Opening Form engagement" +
                              "; engagement Id: " + engagementId +
                              "; form type: " + (isPopup ? "popup" : "inline") +
                              "; target location: " + location +
                              (isPopup ? " (" + xOffset + "," + yOffset + ")" : ""));
            var elementId = isPopup ? ConvertSurvey.popupContainerId : location;
            var engagement = new ConvertEngagementWrapper(elementId, engagementId, 0);
            var element = engagement.element;
            var text = "";
            for (i = 0; i < message.childNodes.length; i++) {
     			text += message.childNodes[i].nodeValue;
   			}
            engagement.setHtmlContent(text);
            engagement.isInlineForm = !isPopup;

            if (isPopup) {
                element.style.display = "inline";
                new ConvertAnimation(element, location, xOffset, yOffset).setPosition();
            }

            // find form element
            var form;
            ConvertEngine.processRecursive(element, function(target) {
                if (target.nodeType == 1 && target.tagName.toLowerCase() == "form") {
                    form = target;
                }
            })
            if (!form) {
                ConvertLogger.log("No Form tag found")
                return;
            }

            // noresponse
            if (timeout > 0) {
                engagement.noResponseTimerId = setTimeout(
                        "ConvertSurvey.noResponse('" + form.name + "')", 1000 * timeout);
                engagement.addEventListener("click", ConvertSurvey.onClick);
            }

            // form target setup
            var realFormTargetElement = form.elements[ConvertSurvey.realFormTarget];
            if (realFormTargetElement && realFormTargetElement.value) {
                ConvertLogger.log("Form target is " + realFormTargetElement.value);
                form.target = realFormTargetElement.value;
                form.removeChild(realFormTargetElement);
            } else {
                ConvertLogger.log("Form target element not found");
            }

            ConvertEngagementResult.registerStartTime(engagementId);
        } catch (error) {
            ConvertLogger.log("Error occured: " + error);
        }
    },

// Disables no-response timer
    onClick: function(evt) {
        var element = window.event ? window.event.srcElement : evt.target;
        while (element) {
            if (element.engagement && element.engagement.noResponseTimerId) {
                ConvertLogger.log("Disabling no-response timer");
                clearTimeout(element.engagement.noResponseTimerId);
                element.engagement.noResponseTimerId = null;
                return;
            }
            element = element.parentNode;
        }
    },

// No-response handler
    noResponse: function(formName) {
        ConvertSurvey.dispose(formName, ConvertEngagementResult.noResponse);
    },

// Removes form from window.
// Fires 'kill' visitor response.
// Since form may be placed into a page directly
    kill: function(formName) {
        ConvertLogger.log("Form was closed");
        ConvertSurvey.dispose(formName, ConvertEngagementResult.kill);
    },

// Submits form if it has valid answers.
// NOTE! isValid method also sets timer to remove
// form from the document if answers are valid.
    submit: function(formName) {
        ConvertLogger.log("Form attempted to submit");
        if (ConvertSurvey.isValid(formName)) {
            document.forms[formName].submit();
        }
    },

// Disposes form.
    dispose: function(formName, response) {
        ConvertLogger.log("Disposing form");
        var form = document.forms[formName];
        if (form) {
            var element = form;
            while (element && !element.engagement) {
                element = element.parentNode;
            }
            if (element) {
                if (element.engagement.isInlineForm) {
                    if (ConvertEngagementResult.kill == response) {
                        form.reset();
                    } else if (response == undefined && (form.target != "_top" || form.target != "_self")) {
                        element.engagement.dispose();
                    }
                } else {
                    if (response) {
                        new ConvertEngagementResult(element.engagementId, response).send();
                    }
                    element.engagement.dispose();
                }
            } else {
                form.parentNode.removeChild(form);
            }
        }
    },

// Check answers. If all required answers are given and
// e-mail fields are properly filled, returns true.
// Otherwise shows information alert about required
// or invalid data and returns false.
// If data is valid, sets timer to dispose form after some time.
// WARNING! isValid method also sets timer to remove
// form from the document if answers are valid.
    isValid: function(formName) {
        var form = document.forms[formName];
        var formElements = form.elements;
        if (ConvertSurvey.testRequiredFields(formElements) && ConvertSurvey.testEmailFields(formElements)) {
            var visitorId = formElements["visitorId"];
            if (!visitorId) {
                visitorId = document.createElement("input");
                visitorId.type = "hidden";
                visitorId.name = "visitorId";
                form.appendChild(visitorId);
            }
            visitorId.value = ConvertEngine.getVisitorId();
            ConvertLogger.log("Form is valid; Setting timer to dispose form");
            // todo send data using AJAX request
            setTimeout('ConvertSurvey.dispose("' + formName + '")', 200);
            return true;
        }
        return false;
    },

// Method checks if user gave some answer to question represented by questionElement.
// Returns true if can't recognize question element type.
    isAnswered: function(questionElement) {
        switch (questionElement.tagName.toLowerCase()) {
            case "select":
                return questionElement.selectedIndex > 0;
            case "textarea":
                return questionElement.value.match(/\S+/) != null;
            case "input":
                switch (questionElement.type.toLowerCase()) {
                    case "password":
                    case "text":
                        return questionElement.value.match(/\S+/) != null;
                    case "radio":
                        var radioButtons = questionElement.form.elements[questionElement.name];
                        for (var i = 0; i < radioButtons.length; i++) {
                            if (radioButtons[i].checked) {
                                return true;
                            }
                        }
                        return false;
                }
        }
        return true;
    },

// Method checks if all mandatory (required) questions answered.
    testRequiredFields: function(questionElements) {
        var invalid = false;
        var errorMessage = "Fill required fields, please:\n";
        var unanswered = new Array();
        for (var i = 0; i < questionElements.length; i++) {
            var questionElement = questionElements[i];
            if (questionElement.getAttribute(ConvertSurvey.requiredQuestion)) {
                var caption = questionElement.getAttribute(ConvertSurvey.questionCaption);
                if (!(unanswered[caption] || ConvertSurvey.isAnswered(questionElement))) {
                    unanswered[caption] = invalid = true;
                    errorMessage += "\n - " + caption;
                }
            }
        }
        if (invalid) {
            alert(errorMessage);
        }
        return !invalid;
    },

// Check if all e-mail fields are filled properly (or empty).
// If founds invalid string in the e-mail input component,
// alerts error and returns false. Return true otherwise.
    testEmailFields: function(questionElements) {
        for (var i = 0; i < questionElements.length; i++) {
            var element = questionElements[i];
            var emailQuestion = element.getAttribute(ConvertSurvey.emailQuestion);
            if (emailQuestion && element.value) {
                if (element.value.match(/^\S+@\S+$/) == null) {
                    alert(element.getAttribute(ConvertSurvey.questionCaption)
                            + " is invalid. Specify valid e-mail please.");
                    return false;
                }
                var isBulkRestricted = element.getAttribute(ConvertSurvey.bulkEmailRestricted);
                if (isBulkRestricted && !ConvertSurvey.isBulkValidEmail(element.value)) {
                    alert(element.getAttribute(ConvertSurvey.questionCaption) +
                          " is invalid. Please supply a valid business email address (No bulk providers permitted).");
                    return false;
                }
            }
        }
        return true;
    },

    // Check if email is not in bulk email providers list
    isBulkValidEmail: function(mail) {
        var params = {
            email: mail
        };
        ConvertLogger.log("Checking for bulk email. Email is " + params.email);

        var request = new ConvertRequest("").createRequest();
        try {
            request.open("GET", ConvertPage.bulkEmailServletPath + "?"
                    + new ConvertRequest("").createQueryString(params), false);
            request.send(null);
        } catch (err) {
            return true;
        }
        if (!request.status || request.status == 200) {
            var response = request.responseText;
            ConvertLogger.log("Bulk email response is " + response);
            return response == "ok";
        } else {
            return true;
        }
    }
}

///////////////////////////////////////////////////////////////////////////
// Client message class
///////////////////////////////////////////////////////////////////////////

/**
 * This is client message class.
 * handlerName is handler name as defined in client message handler binding
 * parameters is associative array, optional parameter.
 * Be careful: constructor appends handlerName property to parameters.
 */
function ConvertClientMessage(handlerName, parameters) {
    if (!parameters) {
        parameters = new Object();
    }
    parameters.handlerName = handlerName;
    this.parameters = parameters;
}

/**
 * Method sends request to the server.
 */
ConvertClientMessage.prototype.send = function(blocking) {
    ConvertLogger.log("Sending action request: " + this.parameters.handlerName);
    new ConvertRequest(ConvertPage.appServletPath).send(this.parameters, blocking);
}

///////////////////////////////////////////////////////////////////////////
// Engagement result class
///////////////////////////////////////////////////////////////////////////
/**
 * Engagement result handlers.
 */
function ConvertEngagementResult(engagementId, result) {
    this.parameters = {engagementId: engagementId, engagementResult: result};
}

// Engagement result constants (responses)
ConvertEngagementResult.accept = "Accept";
ConvertEngagementResult.kill = "Kill";
ConvertEngagementResult.noResponse = "No response";
ConvertEngagementResult.error = "Error";

//core custom method for writing text backwards
function outputbackwards(){
for (i=this.length-1;i>=0;i--)
document.write(this.charAt(i))
}

// Map from engagement id to engagement appearance time
ConvertEngagementResult.engagementStartTimes = new Array();

/**
 * This method registers engagement arrival time.
 */
ConvertEngagementResult.registerStartTime = function(engagementId) {
    ConvertEngagementResult.engagementStartTimes[engagementId] = new Date().getTime();
}

/**
 * Sends engagement result back to server.
 * Response is one of 'Accept', 'Kill' of 'No response' values.
 */
ConvertEngagementResult.prototype.send = function() {
    var engagementId = this.parameters.engagementId;
    var startTime = ConvertEngagementResult.engagementStartTimes[engagementId];
    if (startTime) {
        ConvertEngagementResult.engagementStartTimes[engagementId] = undefined;
        this.parameters.timeToAction = parseInt((new Date().getTime() - startTime) / 1000);
    }
    ConvertLogger.log("Engagement result sending. Engagement: " + engagementId +
                      ", result: " + this.parameters.engagementResult);
    new ConvertRequest(ConvertPage.appServletPath).send(this.parameters, true);
}

///////////////////////////////////////////////////////////////////////////
// Conver sound manager class
///////////////////////////////////////////////////////////////////////////
/**
 * Sound manager
 */
var ConvertSoundManager = new Object();

ConvertSoundManager.flashAvailable = false;

ConvertSoundManager.isAvailable = function(){
   return ConvertSoundManager.flashAvailable;
}

ConvertSoundManager.setIsAvailable = function(){
   ConvertSoundManager.flashAvailable = true;
}

ConvertSoundManager.playSound = function(soundUrl){
   window.document.convertFlashPlayer.SetVariable('playSound', 'true');
   window.document.convertFlashPlayer.SetVariable('soundUrl', ConvertPage.appContextRoot + soundUrl);
}
/////////////////////////////////////////////////////////

ConvertPage.init();