/**
 * @author Microcraft eLearning
 * @version 1.0
 * @copyright Microcraft eLearning 2010
 */
/**
 * WebAuthor runtime object
 */
//<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/>
Author = {
    goingBack: false,
    closeActionsDone: false,
    optionBMString: "",
    variableBMString: "",
    AP: false,
    isHTML5: true,
	isFlash9: false,
    userDefinedScormResult: false,
    popupQC: false,
	titleImage:{
		timer:null
	},
    login: {},
    frame: {
        useFrame: false,
        lessonFrame: "NONE",
        repeat: "NONE"
    },
    pressedKeyCode: 0,
    resultSent: false,
    autoZoom: true,
    zoomRatio: 1,
    version: "1.0",
    lessonName: "",
    location: "",
    resourcePath: "resources/",
    traineeID: "",
    qcURL: "",
    qcKey: 0,
    passRate: 100,
    setWebResult: false,
    bookMark: false,
    sendTo: "",
    networkFields: "0000000000",
    scorm: false,
    Testing: false,
    repeatLesson: false,
    eventFromVariableWatch: false,
    Test: {
        type: "full",
        startLessonNode: null,
        startElement: null,
        resourcePath: null,
        startAtRandom: false
    },
    current: {
        actionType: "",
        currentListXML: null,
        currentNode: null,
        currentBgStack: new Ext.util.MixedCollection,
        currentList: new Ext.util.MixedCollection,
        currentListPosition: 0,
        currentElement: null,
        currentElementXML: null,
        currentElementType: "",
        currentElementPosition: 0,
        currentElementResponses: new Ext.util.MixedCollection,
        currentActionPosition: 0,
        nextList: false,
        currentPlayingWMPObject: null,
        precedentObject: "none",
        randomOptionsCollection: new Ext.util.MixedCollection
    },
    keyNav: null,
    keyMap: null,
    actionKeyMap: new Ext.util.MixedCollection,
    clearBackgroundObjects: false,
    backgroundList: {
        list: new Ext.util.MixedCollection,
        updateProgressBar: function(){
            var newValue = 0;
            var elementCount = 1;
            if (Author.current.currentNode.type == "SEQUENCE" || (Author.current.currentNode.type == "RANDOM" && Author.Test.startAtRandom)) {
                elementCount = Author.current.currentNode.xml.getElementsByTagName("element").length;
            }
            else 
                if (Author.current.currentNode.type == "RANDOM" && !Author.Test.startAtRandom) {
                    elementCount = Author.current.randomOptionsCollection.getCount();
                }
            newValue = parseInt((Author.current.currentElementPosition + 1) * 100 / elementCount);
            if (isNaN(newValue)) 
                newValue = 0;
            var listCount = this.list.length;
            for (var index = 0; index < listCount; index++) {
                var object = this.list.itemAt(index);
                if (object.objectType == "PROGRESSBAR") {
                    object.setValue(newValue);
                    Author.current.currentList.add(object.id, object);
                    if (object.hide == "false") 
                        object.setVisible(true);
                    //  if (Author.current.currentNode.type == "MENU") 
                    //    object.setVisible(false);
                }
            }
        }
    },
    foregroundList: {
        list: new Ext.util.MixedCollection,
        show: function(){
            this.list.each(function(object, id, count){
                //alert(object.id +", "+object.addToForegroundAtElementPosition+", "+object.hide);
                if (object.hide == "false") {
                    object.setVisible(true);
                }
                if (Author.current.currentNode.type == "MENU") {
                
                    if ((object.objectType == "CUSTOMBUTTONOBJECT" || object.objectType == "HOTSPOTOBJECT" || object.objectType == "IMAGEBUTTONOBJECT") && (object.buttonType == "1" && object.buttonType == "2")) {
                        object.setVisible(object.hide == "false");
                    }
                    else {
                        object.setVisible(false);
                    }
                }
                Author.current.currentList.add(object.id, object);
            });
            Author.backToMenu = false;
        }
    },
    dividers: new Ext.util.MixedCollection,
    introductionList: {},
    interactionList: {},
    informationList: {},
    endList: {},
    lessonNodes: {
        nodes: new Ext.util.MixedCollection,
        getNode: function(id){
            return Author.lessonNodes.nodes.get(id);
        }
    },
    interactionTimer: null,
    actionTimer: null,
    mediaTimer: null,
    lastSoundID: "",
    pause: {
        countDown: 0,
        cancelType: 0,
        startTime: 0,
        key1: "none",
        key2: "none"
    },
    waitAudioFinish: false,
    doingInteraction: false,
    responseEnd: false,
    responseStart: 0,
    lessonFlow: "continue",
    inResponse: false,
    enableNextBtn: false,
    introductionListStat: new Ext.util.MixedCollection,
    variables: new Ext.util.MixedCollection,
    contants: new Ext.util.MixedCollection,
    variableNames: new Ext.util.MixedCollection,
    answers: {
        multipleChoice: new Ext.util.MixedCollection,
        trueFalse: null,
        yesNo: null,
        shortAnswer: "",
        clear: function(){
            this.multipleChoice.clear();
            this.trueFalse = null;
            this.yesNo = null;
            this.shortAnswer = "";
        }
    },
    response: {
        removeType: "REMOVE",
        startResponse: 0,
        endResponse: 0,
        clear: function(){
            this.removeType = "REMOVE";
            this.startResponse = 0;
            this.endResponse = 0;
        }
    },
    mediaList: new Ext.util.MixedCollection,
    soundObjIDMap: new Ext.util.MixedCollection,
    flvIDMap: new Ext.util.MixedCollection,
    lessonFinished: false,
    isSynch: true,
    isAudioOn: true
};

/**
 * Get the test information if testing from authbase.
 */
function getTestInfo(){
    // Need to be true to enable some functions that only available when Testing. E.g.:Variable watch. Reference window.
    Author.Testing = false;
    var testDesp = Author.lessonXMLDoc.getElementsByTagName("testInfo")[0];
    
    //publish, full or selection. (publish will set Author.Testing to false)
    Author.Test.type = testDesp.getElementsByTagName("testType")[0].childNodes[0].nodeValue;
    //lesson node id
    Author.Test.startLessonNode = parseInt(testDesp.getElementsByTagName("startLessonNode")[0].childNodes[0].nodeValue);
    //sequence number of the element
    Author.Test.startElement = parseInt(testDesp.getElementsByTagName("startElement")[0].childNodes[0].nodeValue);
    if (Author.Test.type == "publish") {
        Author.Testing = false;
    }
    else {
        Author.Testing = true;
    }
    try {
    
    
        //Initialise the bugTracker window
        Author.bugTracker = new Ext.Window({
            layout: 'fit',
            title: 'QC window',
            hidden: true,
            modal: true,
            closeAction: 'hide',
            width: 800,
            height: 500
        });
        
        Author.bugTracker.show();
        
        Author.bugTracker.setPosition(0, 0);
        
        Author.bugTrackerFrame = Author.bugTracker.body.createChild({
            tag: "iframe",
            style: "position:absolute;left:0px;top:0px;background-color:#fff;width:100%;height:100%"
        
        
        });
        Author.bugTracker.hide();
        // When QC Window hides, continue the lesson
        
        Author.bugTracker.addListener("hide", function(){
            var index = Author.current.currentList.getCount() - 1;
            for (; index >= 0; index--) {
                var el = Author.current.currentList.itemAt(index);
                if (el.objectType == "MEDIAOBJECT" && el.hideByQC) {
                    el.setVisible(true);
                    el.hideByQC = false;
                    continue;
                }
            }
            showList();
        });
        
    } 
    catch (err) {
        //alert(err.description);
        Author.bugTracker.hide();
    }
}

/**
 * Initialize the web lesson
 */
function initlizeLesson(){
    var lessonXML = "lesson.xml";
    Author.lessonXMLDoc = null;
    
    if (Ext.isIE || Ext.isGecko || Ext.isOpera) {
        if (window.ActiveXObject) {
            Author.lessonXMLDoc = new ActiveXObject("Microsoft.XMLDOM");
            Author.lessonXMLDoc.validateOnParse = false;
            Author.lessonXMLDoc.onreadystatechange = function(){
                switch (Author.lessonXMLDoc.readyState) {
                    case 0:
                        break;
                    case 1:
                        break;
                    case 2:
                        break;
                    case 3:
                        break;
                    case 4:
                        if (Author.lessonXMLDoc.parseError < 0) {
                            switch (Author.lessonXMLDoc.parseError.errorCode) {
                                case -2146697210:
                                    alert('Author cannot find the lesson description file.');
                                    break;
                                default:
                                    var errStr = "ErrorCode: " + Author.lessonXMLDoc.parseError.errorCode;
                                    errStr += "\nReason: " + Author.lessonXMLDoc.parseError.reason;
                                    errStr += "\nFilePosition: " + Author.lessonXMLDoc.parseError.filepos;
                                    errStr += "\nLine: " + Author.lessonXMLDoc.parseError.line;
                                    errStr += "\nLinePosition: " + Author.lessonXMLDoc.parseError.linepos;
                                    errStr += "\nsrcURL: " + Author.lessonXMLDoc.parseError.url;
                                    errStr += "\nSrcText: " + Author.lessonXMLDoc.parseError.srcText;
                                    alert(errStr);
                                    break;
                            }
                        }
                        else {
                            initlizeLesson2();
                        }
                        break;
                }
            }
            if (Author.lessonXMLDoc != null) {
                Author.lessonXMLDoc.async = true;
                
                if (!Author.lessonXMLDoc.load(Author.resourcePath + lessonXML)) {
                    alert("Invalid lesson description file: " + lessonXML);
                    // return;
                }
                return;
            }
            else {
                alert("Can't initlise XML parser.");
                return;
            }
        }
        else {
            if (document.implementation.createDocument) {
                Author.lessonXMLDoc = document.implementation.createDocument("", "", null);
            }
            if (Author.lessonXMLDoc != null) {
                Author.lessonXMLDoc.async = false;
                if (!Author.lessonXMLDoc.load(Author.resourcePath + lessonXML)) {
                    alert("Invalid lesson description file: " + lessonXML);
                    return;
                }
                initlizeLesson2()
            }
            else {
                alert("Can't initlise XML parser.");
                return;
            }
        }
    }
    else {
        var xmlhttp = new window.XMLHttpRequest();
        xmlhttp.open("GET", Author.resourcePath + lessonXML, false);
        xmlhttp.send(null);
        Author.lessonXMLDoc = xmlhttp.responseXML;
        if (xmlhttp.status != 200) {
            alert("Invalid lesson description file: " + lessonXML);
            return;
        }
        initlizeLesson2()
    }
    
}
function checkFlashSupport(){
	Author.isFlash9= DetectFlashVer(9, 0, 0);
}
function checkHTML5Cabability(){

    var html5 = false;
    // Author.isHTML5 = true;
    
    var test_audio = document.createElement("audio") //try and create sample audio element
    var test_video = document.createElement("video") //try and create sample video element
    html5 = ((test_audio.play) ? true : false) && ((test_video.play) ? true : false);
    Author.isHTML5 = Author.isHTML5 && html5;
    if (Ext.isGecko) {
        Author.isHTML5 = false;
    }
    
}

function initlizeLesson2(){
    Author.closeActionsDone = false;
    getTestInfo();
    checkHTML5Cabability();
	checkFlashSupport();
    Author.lessonName = trim(Author.lessonXMLDoc.getElementsByTagName("lesson")[0].attributes[0].nodeValue);
    Author.projectName = trim(Author.lessonXMLDoc.getElementsByTagName("lesson")[0].attributes[5].nodeValue);
    Author.projectName = Author.projectName.replace(" ", "_");
    Ext.getDoc().dom.title = Author.lessonName;
    
    // Load system variables and user variables
    LoadTheVariables();
    
    // When the lessonXML is loaded, remove the loading indicator.
    Ext.get("loadingDIV").remove();
    
    Author.lessonWidth = parseFloat(trim(Author.lessonXMLDoc.getElementsByTagName("lesson")[0].attributes[1].nodeValue));
    Author.lessonHeight = parseFloat(trim(Author.lessonXMLDoc.getElementsByTagName("lesson")[0].attributes[2].nodeValue));
    // alert(Author.lessonWidth +" X "+Author.lessonHeight);
	
	
    
    var bodyEl = Ext.getBody();
    Ext.getDoc().on("keydown", function(e){
        Author.pressedKeyCode = e.getCharCode();
    });
   
    Ext.getBody().on("contextmenu", Ext.emptyFn, null, {
        preventDefault: true
    });
    
    // Handle browser KeyPressed events
    Author.keyNav = new Ext.KeyNav(bodyEl, {
        "left": keyPressedHandle,
        "right": keyPressedHandle,
        "up": keyPressedHandle,
        "down": keyPressedHandle,
        //"tab": keyPressedHandle,
        "esc": keyPressedHandle,
        "pageUp": keyPressedHandle,
        "pageDown": keyPressedHandle,
        "del": keyPressedHandle,
        "home": keyPressedHandle,
        "end": keyPressedHandle
    });
    Author.keyMap = new Ext.KeyMap(bodyEl, [{
       // stopEvent: true,
        key: [45, 19, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 8], //8
        fn: function(key, e){
           // alert("Key pressed: " + e.getCharCode() + ", ALT: " + e.altKey + ", CTRL: " + e.ctrlKey + ", SHIFT: " + e.shiftKey);
            
            keyPressedHandle(e, true);
        }
    }]);
    checkScreenSize();
}

/**
 * Check if the user browser window is big enough to display the full size lesson.
 */
function checkScreenSize(){
    var screenWidth = Ext.lib.Dom.getViewWidth();
    var screenHeight = Ext.lib.Dom.getViewHeight();
    // Get the user setting from the cookie.
    Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
        expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365))
    }));
    var showScreenWarning = Ext.state.Manager.get(Author.projectName + "[SHOWSCREENWARNING]");
    if (typeof(showScreenWarning) == 'undefined') 
        showScreenWarning = true;
    
    if (Ext.isIE && typeof(Author.autoZoom) != 'undefined' && Author.autoZoom) {
        showScreenWarning = false;
    }
    
    if (showScreenWarning && (screenHeight <= Author.lessonHeight || screenWidth <= Author.lessonWidth)) {
    
        var messageStr = "Your screen resolution may prevent you seeing the full screen of this eLearning module.<br/><br/>";
        messageStr += "We would recommend you select full-screen mode now (F11 in Internet Explorer) and turn off the status bar. ";
        messageStr += "If you are using a screen less than 1024x768 pixels (such as a Netbook) we recommend setting the page Zoom to 75% or lower.";
        messageStr += "<br/><br/>";
        messageStr += "Click OK when you are ready to start the eLearning.";
        var screenWarning = new Ext.Window({
            layout: 'fit',
            title: 'Screen resolution warning',
            modal: true,
            hidden: true,
            closeAction: 'close',
            width: 410,
            height: 260,
            cls: "x-window x-window-plain x-window-dlg",
            resizable: false,
            buttons: [new Ext.Button({
                text: "OK",
                handler: function(b){
                    screenWarning.close();
                },
                style: "position:absolute;left:315px;top:200px"
            })]
        });
        
        screenWarning.show();
        screenWarning.center();
        screenWarning.icon = screenWarning.body.createChild({
            tag: "img",
            src: "lib/ext/resources/images/default/window/icon-warning.gif",
            style: "position:absolute;top:15px"
        });
        screenWarning.warningText = screenWarning.body.createChild({
            tag: "div",
            style: "position:absolute;left:60px;top:15px;width:320px;height:380px;font-size:10pt",
            html: messageStr
        });
        screenWarning.chkNoAgain = screenWarning.body.createChild({
            tag: "div",
            style: "position:absolute;top:200px;font-size:10pt;left:20px"
        });
        screenWarning.chkNoAgain.button = screenWarning.chkNoAgain.createChild({
            tag: "input",
            type: "checkbox",
            style: "position:absolute;"
        });
        screenWarning.chkNoAgain.button.on("click", function(e){
            var target = Ext.get(e.getTarget());
            if (target.dom.checked) {
                Ext.state.Manager.set(Author.projectName + "[SHOWSCREENWARNING]", false);
            }
            else {
                Ext.state.Manager.clear(Author.projectName + "[SHOWSCREENWARNING]");
            }
        })
        screenWarning.chkNoAgain.createChild({
            tag: "label",
            'for': screenWarning.chkNoAgain.button.id,
            html: "Don't show this warning again",
            style: "position:absolute;left:24px;width:200px;top:" + (Ext.isIE ? 2 : -2) + "px"
        });
        
        screenWarning.addListener("close", function(){
            continueInit1();
        })
    }
    else {
        continueInit1();
    }
}

function checkLogin(){
    var text = Author.login.promptWindow.getComponent('bodyContainer').getComponent('promptContainer').getComponent('inputField').getValue();
    var valid = false;
    var length = (Author.login.idLength == 0 ? "*" : Author.login.idLength);
    var prompt = "";
    switch (Author.login.idType) {
        case 0://anyChar
            var pattern = "^." + (length == "*" ? "*" : ("{" + length + "}")) + "$";
            prompt = "Please enter a " + (length == "*" ? "" : length + " characters") + " login.";
            break;
        case 1://alphanumeric
            var pattern = "^[\\w\\d]" + (length == "*" ? "*" : ("{" + length + "}")) + "$";
            prompt = "Please enter a " + (length == "*" ? "" : length + " alphanumerics") + " login."
            break;
        case 2://numeric
            var pattern = "^\\d" + (length == "*" ? "*" : ("{" + length + "}")) + "$";
            prompt = "Please enter a " + (length == "*" ? "" : length + " digitals") + " numeric login."
            break;
        case 3://alphabetic
            var length = (Author.login.idLength == 0 ? "*" : Author.login.idLength);
            var pattern = "^\\w" + (length == "*" ? "*" : ("{" + length + "}")) + "$";
            prompt = "Please enter a " + (length == "*" ? "" : length + " letters") + " login."
            break;
        case 4:
            var pattern = "^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$";
            prompt = "Please enter a valid Email address."
            break;
    }
    valid = validateLogin(pattern, text);
    if (valid) {
        Author.login.promptWindow.close();
        Author.traineeID = text;
        Author.variables.get(variableName("USERID")).value = Author.traineeID;
        continueInitLesson();
    }
    else {
        Author.login.promptWindow.getComponent('bodyContainer').getComponent('errMsg').el.dom.innerHTML = prompt;
        Author.login.promptWindow.getComponent('bodyContainer').getComponent('promptContainer').getComponent('inputField').focus(true, 100);
    }
}

/**
 * Continue the lesson initialization
 *
 * Connect to the scorm API, initialise some global variables, and get the userID from prompt if needed.
 */
function continueInit1(){
	 Ext.getDoc().on("keypress", keyPressedHandle);
    Ext.getDoc().on("click", mouseClickHandle);
    Ext.getDoc().on("dblclick", function(e){
        e.stopEvent();
    });
    Author.scorm = parseInt(Author.lessonXMLDoc.getElementsByTagName("lesson")[0].attributes[3].nodeValue) == -1;
    
    // If the lesson is a SCORM lesson, try to connect to SCORM APIs. 
    if (Author.scorm) {
        Author.scormType = parseInt(trim(Author.lessonXMLDoc.getElementsByTagName("lesson")[0].attributes[4].nodeValue));
        var tryAgain = false;
        do {
            if (ScormProcessInitialize()) {
                // Get the student information from scorm.
                Author.traineeID = ScormProcessGetValue("cmi.core.student_id");
                Author.variables.get(variableName("USERID")).value = Author.traineeID;
                var studentFullName = ScormProcessGetValue("cmi.core.student_name");
                Author.variables.get(variableName("FULLNAME")).value = studentFullName;
                // Get first name and last name from full name.
                var nameS = studentFullName.split(",");
                Author.variables.get(variableName("FIRSTNAME")).value = nameS[0];
                Author.variables.get(variableName("LASTNAME")).value = trim(nameS[1]);
                var completionStatus = ScormProcessGetValue("cmi.core.lesson_status");
                if (completionStatus == "not attempted") {
                    ScormProcessSetValue("cmi.core.lesson_status", "incomplete");
                }
                tryAgain = false;
                Author.scorm = true;
            }
            else {
                Author.scorm = false;
                tryAgain = confirm("Cannot connect to the SCORM API, do you want to try again?", "Cannot connect to the SCORM API");
            }
        }
        while (tryAgain)
    }
    
    // Load the web result information    
    var webResultNode = Author.lessonXMLDoc.getElementsByTagName("webResult")[0];
    Author.setWebResult = (trim(webResultNode.getElementsByTagName("sendWebResult")[0].childNodes[0].nodeValue).toLowerCase() == "true");
    if (Author.setWebResult) {
        Author.bookMark = true;
        Author.sendTo = webResultNode.getElementsByTagName("sendTo")[0].childNodes[0].nodeValue;
        Author.networkFields = webResultNode.getElementsByTagName("networkFields")[0].childNodes[0].nodeValue;
    }
    
    // Oterh lesson information
    var lessonInfoNode = Author.lessonXMLDoc.getElementsByTagName("lessonInfo")[0];
    try {
        Author.frame.useFrame = (trim(lessonInfoNode.getElementsByTagName("useFrame")[0].childNodes[0].nodeValue).toLowerCase() == "true");
        Author.frame.lessonFrame = trim(lessonInfoNode.getElementsByTagName("lessonFrame")[0].childNodes[0].nodeValue).toLowerCase();
        Author.frame.repeat = parseInt(trim(lessonInfoNode.getElementsByTagName("frameRepeat")[0].childNodes[0].nodeValue).toLowerCase());
        setLessonFrame();
    } 
    catch (err) {
    }
    Author.passRate = parseInt(trim(lessonInfoNode.getElementsByTagName("passRate")[0].childNodes[0].nodeValue).toLowerCase());
    Author.bookMark = (trim(lessonInfoNode.getElementsByTagName("bookMark")[0].childNodes[0].nodeValue).toLowerCase() == "true");
    try {
        Author.qcURL = trim(lessonInfoNode.getElementsByTagName("qcUrl")[0].childNodes[0].nodeValue);
        Author.qcKey = trim(lessonInfoNode.getElementsByTagName("qcKey")[0].childNodes[0].nodeValue);
    } 
    catch (err) {
    }
    Author.repeatLesson = (trim(lessonInfoNode.getElementsByTagName("repeatLesson")[0].childNodes[0].nodeValue).toLowerCase() == "true");
    if (Author.Testing && Author.Test.type != "full") {
	 	continueInit2();
		return;
	}
	
	try {
        Author.titleImage.src = lessonInfoNode.getElementsByTagName("titleImage")[0].childNodes[0].nodeValue;
		
        Author.titleImage.delay = parseFloat(lessonInfoNode.getElementsByTagName("titleImageDelay")[0].childNodes[0].nodeValue) * 1000;
        var bodyEl = Ext.getBody();
        Author.titleImage.div = bodyEl.createChild({
            style: "overflow:hidden;position:absolute;width:" + Author.lessonWidth + "px;height:" + Author.lessonHeight + "px;"
        });
        Author.titleImage.div.center();
        Author.titleImage.img = Author.titleImage.div.createChild({
            tag: "img",
            src: Author.resourcePath + Author.titleImage.src,
            style: "overflow:hidden;position:absolute;width:100%;height:100%;"
        });
        Author.titleImage.timer = setTimeout(continueInit2, Author.titleImage.delay);
        return;
    } 
    catch (err) {
        continueInit2();
    }
}




function continueInit2(){
	 var lessonInfoNode = Author.lessonXMLDoc.getElementsByTagName("lessonInfo")[0];
	  // If bookMark enabled and the lesson is not scorm lesson, get the userID.
    if (Author.bookMark && !Author.scorm) {
        Author.login.idType = -1;
        Author.login.prompt = trim(lessonInfoNode.getElementsByTagName("prompt")[0].childNodes[0].nodeValue);
        Author.login.idLength = parseInt(trim(lessonInfoNode.getElementsByTagName("IDLen")[0].childNodes[0].nodeValue));
        Author.login.idType = parseInt(trim(lessonInfoNode.getElementsByTagName("IDType")[0].childNodes[0].nodeValue));
        Author.login.hasMessage = true;
        try {
            Author.login.msgWidth = parseInt(trim(lessonInfoNode.getElementsByTagName("loginMsgWidth")[0].childNodes[0].nodeValue));
            Author.login.msg = lessonInfoNode.getElementsByTagName("loginMsg")[0].childNodes[0].nodeValue;
        } 
        catch (err) {
            Author.login.msgWidth = 250;
            Author.login.msg = "";
        }
        Author.login.msgWidth = Math.max(Author.login.msgWidth, 250);
        Author.login.count = 0;
        var prompt = Author.login.prompt;
        if (prompt == "none") 
            prompt = "User ID:";
        else {
            if (prompt.charAt(prompt.length - 1) != ":") {
                prompt += ":";
            }
        }
        // If testing web lesson from authbase, set the traineeID to "Test" and continue the lesson initialization.
        if (Author.Testing && Author.Test.type != "full") {
            Author.traineeID = "Test";
            Author.variables.get(variableName("USERID")).value = Author.traineeID;
            continueInitLesson();
        }
        // Else let the user input the userID.
        else {
            // Ext.MessageBox.prompt('Login', prompt, checkLogin);
            
            Author.login.promptWindow = new Ext.Window({
                applyTo: 'loginWindow',
                layout: 'fit',
                width: Author.login.msgWidth + 40,
                autoHeight: true,
                closable: false,
                closeAction: 'close',
                plain: false,
                draggable: true,
                title: "Login",
                resizable: false,
                border: false,
                
                listeners: {
                    "show": function(e){
                        e.getComponent('bodyContainer').getComponent('promptContainer').getComponent('inputField').focus(false, 100);
                    }
                },
                items: [new Ext.Container({
                    width: Author.login.msgWidth,
                    autoHeight: true,
                    bodyStyle: "background:transparent",
                    itemId: "bodyContainer",
                    style: {
                        padding: '5px'
                    },
                    items: [new Ext.Container({
                        width: Author.login.msgWidth,
                        autoHeight: true,
                        itemId: "msgContainer",
                        html: Author.login.msg
                    }), new Ext.Container({
                        style: "padding-top:5px;",
                        layout: 'hbox',
                        layoutConfig: {
                            align: 'middle',
                            pack: 'start'
                        },
                        itemId: "promptContainer",
                        items: [new Ext.form.Label({
                            html: prompt
                        }), {
                            xtype: 'spacer',
                            flex: 1
                        }, new Ext.form.TextField({
                            itemId: "inputField",
                            width: 210,
                            enableKeyEvents: true,
                            listeners: {
                                "keypress": function(txt, e){
                                    if (e.getKey() == 13) {
                                        checkLogin();
                                    }
                                }
                            }
                        })]
                    }), new Ext.form.Label({
                        style: "color:#f00;text-align:center;",
                        width: Author.login.msgWidth,
                        itemId: "errMsg"
                    }), new Ext.Container({
                        style: "margin-top:5px",
                        itemId: 'buttonPanel',
                        layout: 'hbox',
                        layoutConfig: {
                            align: 'middle',
                            pack: 'end'
                        },
                        items: [new Ext.Button({
                            style: "padding-right:1px",
                            text: 'OK',
                            width: 70,
                            handler: checkLogin
                        }), new Ext.Button({
                            text: 'Cancel',
                            width: 70,
                            handler: function(){
                                Author.login.promptWindow.close();
                                window.open('', '_self', '');
                                window.close();
                                top.window.close();
                            }
                        })]
                    })]
                })]
            });
            Author.login.promptWindow.show();
        }
    }
    else {
        continueInitLesson();
    }
}

function validateLogin(regex, text){
    var re = new RegExp(regex);
    //alert(regex+"\n"+text+"\n"+re.test(text));
    return re.test(text);
}

/**
 * Continue the lesson initialization.
 */
function continueInitLesson(){
	window.onunload = beforeWindowsClose;
    window.onbeforeunload = beforeWindowsClose;
    window.onerror = function(sMsg, sUrl, sLine){
        //alert(sMsg+"\n"+sUrl+"\n"+sLine);
        if (sLine == 1) 
            return true;
        else 
            return false;
    }
    var bodyEl = Ext.getBody();
    // Initialize the background object.
    Author.backgroundObject = new BackgroundClass(bodyEl);
    Author.resultSent = false;
    
    var startTime = new Date().getTime();
    // Initialize the background list and foreground list.
    var bgFxDelay = doLessonRoot();
    
    // Initialize the lesson options. [MENU|SEQUENCE|RANDOM]
    initlizeLessonNodes();
	if (Author.titleImage.div != null) {
		if (Author.titleImage.div.isVisible()) {
			Author.titleImage.div.remove();
		}
	}
    bgFxDelay -= (new Date().getTime() - startTime);
    
    // Start to play the lesson.
    setTimeout(showLessonNode, (bgFxDelay <= 0) ? 0 : bgFxDelay);
    // showLessonNode();
}

/**
 * Show the variable watch window
 */
function showVariableMonitor(){

    if (typeof(Author.variableWatch) != 'undefined') {
        Author.variableWatch.show();
        return;
    }
    
    var variableMonitorDiv = Ext.getBody().createChild({
        tag: "div",
        id: "variableMonitor",
        style: "position:absolute;left:0px;top:0px"
    });
    variableMonitorDiv.center();
    
    // create a Record constructor:
    
    var myStore = new Ext.data.Store({
        reader: new Ext.data.ArrayReader({
            idIndex: 0 // id for each record will be the first element
        }, new Ext.data.Record.create([{
            name: 'name',
            type: 'string'
        }, {
            name: 'type',
            type: 'string'
        }]))
    });
    var varStore = new Ext.data.Store({
        reader: new Ext.data.ArrayReader({
            idIndex: 0 // id for each record will be the first element
        }, new Ext.data.Record.create([{
            name: 'name',
            type: 'string'
        }]))
    });
    
    Author.variables.each(function(variable, index, length){
        varStore.add(new varStore.recordType({
            name: variable.name
        }));
    })
    
    var grid = new Ext.grid.EditorGridPanel({
        store: myStore,
        ref: '../grid',
        itemId: 'grid',
        cm: new Ext.grid.ColumnModel({
            // specify any defaults for each column
            columns: [{
                header: 'Variable',
                dataIndex: 'name',
                width: 150,
                align: 'center',
                sortable: true,
                editor: new Ext.form.ComboBox({
                    store: varStore,
                    displayField: 'name',
                    typeAhead: true,
                    mode: 'local',
                    forceSelection: true,
                    triggerAction: 'all',
                    emptyText: 'Select a variable...',
                    selectOnFocus: true,
                    allowBlank: false
                }),
                menuDisabled: true
            }, {
                id: 'value',
                header: 'Value',
                dataIndex: 'value',
                editor: new Ext.form.TextField({
                    preventMark: true
                }),
                align: 'center',
                sortable: false,
                menuDisabled: true
            }]
        }),
        selModel: new Ext.grid.RowSelectionModel(),
        autoExpandColumn: 'value',
        stripeRows: true,
        viewConfig: {
            markDirty: false
        },
        tbar: [{
            xtype: 'button',
            text: 'Add Watch',
            icon: './lib/images/add.gif',
            handler: function(e){
                var r = grid.getStore().recordType;
                var aVar = new r({
                    name: '',
                    value: ""
                });
                grid.stopEditing();
                myStore.add(aVar);
                grid.startEditing(myStore.getCount() - 1, 0);
            }
        }, {
            ref: '../removeBtn',
            icon: './lib/images/delete.gif',
            text: 'Remove Watch',
            disabled: true,
            handler: function(){
                grid.stopEditing();
                var s = grid.getSelectionModel().getSelections();
                for (var i = 0, r; r = s[i]; i++) {
                    myStore.remove(r);
                }
            }
        }, {
            icon: './lib/images/refresh.gif',
            text: 'refresh',
            handler: function(){
                grid.stopEditing();
                grid.getStore().each(updateRecord);
            }
        }],
        listeners: {
            'beforeedit': function(e){
                if (e.field == "value") {
                    var vID = variableName(e.record.get('name').toUpperCase());
                    if (typeof(vID) == 'undefined') {
                        e.cancel = true;
                    }
                    else {
                        if (!Author.variables.containsKey(vID)) {
                            e.cancel = true;
                        }
                        else {
                            //alert(Author.variables.get(vID).name+","+Author.variables.get(vID).userVariable);
                            e.cancel = !Author.variables.get(vID).userVariable;
                        }
                    }
                }
            },
            'afteredit': function(e){
                if (e.field == "name") {
                    updateRecord(e.record);
                    
                }
                else 
                    if (e.field == "value") {
                        var vName = e.record.get('name');
                        Author.variables.get(variableName(vName)).setValue(e.value);
                    }
            }
        }
    });
    grid.getSelectionModel().on('selectionchange', function(sm){
        grid.removeBtn.setDisabled(sm.getCount() < 1);
    });
    
    Author.variableWatch = new Ext.Window({
        title: 'Variable Watch',
        renderTo: 'variableMonitor',
        layout: 'fit',
        width: 400,
        height: 300,
        minWidth: 360,
        collapseFirst: true,
        collapsible: true,
        closeAction: 'hide',
        items: grid
    })
    Author.variableWatch.refresh = function(){
        if (this.isVisible()) {
            this.getComponent('grid').stopEditing();
            this.getComponent('grid').getStore().each(updateRecord);
        }
    }
    Author.variableWatch.show();
}

function updateRecord(record){
    var vName = record.get('name');
    record.set('value', getVariableValueByName(vName));
}

/**
 * Return a proper lesson node name for displaying in the reference window.
 * @param {Boolean} showElementCode
 * 					If false, the name will not contain the element code. Nodename should be in this format Code:Name
 * @param {String} nodeName
 */
function getProperNodeName(showElementCode, nodeName){
    if (showElementCode) {
        return nodeName;
    }
    else {
        var colonIndex = nodeName.indexOf(':');
        if (colonIndex > 0) {
            return trim(nodeName.substring(colonIndex + 1));
        }
        else {
            return nodeName;
        }
    }
}

/**
 * Show the reference window. (Quick references)
 * @param {Boolean} showElementCode - show element code or not
 * @param {Boolean} expandNode - lesson nodes expanded or not
 * @param {Boolean} showButtons - whether show synch and audio button or not
 * @param {String} windowTitle - the customized title for the reference window
 */
function showLessonStructure(showElementCode, expandNode, showButtons, windowTitle){
    if (typeof(showElementCode) == 'undefined') {
        showElementCode = false;
    }
    if (typeof(expandNode) == 'undefined') {
        expandNode = true;
    }
    if (typeof(showButtons) == 'undefined') {
        showButtons = false;
    }
    if (typeof(windowTitle) == 'undefined') {
        windowTitle = "Reference";
    }
    if (typeof(Author.structureWin) != 'undefined') {
        Author.structureWin.show();
        selectNode(Author.current.currentNode.id, Author.current.currentElementPosition, Author.structureWin.getComponent('tree').getRootNode());
        return;
    }
    
    var structureDiv = Ext.getBody().createChild({
        tag: "div",
        id: "structure",
        style: "position:absolute;left:0px;top:0px"
    });
    
    // structureDiv.center();
    structureDiv.setTop(Author.backgroundObject.content.getTop(true));
    var sHeight = (Ext.lib.Dom.getViewHeight() / 2 < 500 ? Ext.lib.Dom.getViewHeight() / 2 : 500);
    Author.structureWin = new Ext.Window({
        title: windowTitle,
        renderTo: 'structure',
        layout: 'border',
        width: 350,
        height: sHeight,
        minWidth: 214,
        closeAction: 'hide',
        collapseFirst: true,
        collapsible: true,
        items: [new Ext.Panel({
            region: 'north',
            height: 14,
            margins: '0',
            bodyBorder: false,
            border: false,
            bodyStyle: "background:transparent",
            margins: '0 0 0 2',
            items: [{
                xtype: 'label',
                html: 'Click on a screen title to display that screen.'
            }]
        }), {
            itemId: "tree",
            xtype: 'treepanel',
            id: 'tree-panel',
            region: 'center',
            margins: '1 1 1 1',
            autoScroll: true,
            rootVisible: false,
            root: new Ext.tree.TreeNode()
        }, new Ext.Panel({
        
            bodyStyle: "background-image:url(./lib/images/formBG.png)",
            region: 'south',
            height: showButtons ? 16 : 0,
            margins: '0',
            layout: 'hbox',
            layoutConfig: {
                align: 'top',
                pack: 'end',
                defaultMargins: {
                    top: 0,
                    right: 2,
                    bottom: 0,
                    left: 2
                }
            },
            border: false,
            items: [{
                //ref: '../audioBtn',
                xtype: 'checkbox',
                checked: Author.isAudioOn,
                
                handler: function(btn, state){
                    Author.isAudioOn = state;
                    if (!Author.isAudioOn) {
                        Author.structureWin.synchBtn.setValue(false);
                        Author.mediaList.eachKey(function(key, object){
                            if (object.objectType == "SOUNDOBJECT") {
                                if (Ext.isIE) {
                                    object.mediaObject.dom.settings.mute = true;
                                }
                            }
                        });
                    }
                }
            }, {
                xtype: 'label',
                id: "audioLabel",
                html: 'Voice-over',
                listeners: {
                    'afterrender': function(lb){
                        lb.tooltip = new Ext.ToolTip({
                            target: 'audioLabel',
                            shadow: 'drop',
                            showDelay: 500,
                            mouseOffset: [0, -15],
                            width: 120,
                            bodyStyle: "text-align:center",
                            html: 'Voice-over On/Off'
                        });
                    },
                    'click': function(){
                        alert("clicked");
                    }
                }
            }, {
                ref: '../synchBtn',
                xtype: 'checkbox',
                checked: Author.isSynch,
                handler: function(btn, state){
                    Author.isSynch = state;
                }
            }, {
                //Synchronise display to voice-over.
                xtype: 'label',
                id: "synchLabel",
                html: 'Synch',
                listeners: {
                    'afterrender': function(lb){
                        lb.tooltip = new Ext.ToolTip({
                            target: 'synchLabel',
                            shadow: 'drop',
                            showDelay: 500,
                            dismissDelay: 10000,
                            mouseOffset: [0, -15],
                            width: 240,
                            bodyStyle: "text-align:left",
                            html: 'Turn off synchronization with audio.<br/>The final state of the screen will be displayed.<br/>This may prevent you seeing  intermediate animations and may interrupt audio.'
                        });
                    }
                }
            }]
        })]
    });
    Author.structureWin.setPagePosition((screen.width * Author.zoomRatio - 350) / 2, (screen.height * Author.zoomRatio - sHeight) / 2);
    // Author.structureWin.center();
    Author.structureWin.show();
    var tree = Ext.getCmp("tree-panel");
    var lessonRoot = tree.root.appendChild(new Ext.tree.TreeNode({
        text: Author.lessonName,
        icon: ('./lib/images/lessonroot.GIF'),
        listeners: {
            'beforecollapse': function(){
                return false;
            }
        }
    }));
    lessonRoot.oID = -1;
    lessonRoot.ePos = -1;
    
    // Build the lesson reference window.
    for (var index = 0; index < Author.lessonNodes.nodes.getCount(); index++) {
        var aNode = Author.lessonNodes.nodes.itemAt(index);
        if (parseInt(aNode.parent) == 0) {
            if (aNode.showInReference) {
                aTreeNode = new Ext.tree.TreeNode({
                    text: getProperNodeName(showElementCode, aNode.name),
                    expanded: expandNode,
                    icon: ('./lib/images/' + aNode.type + '.GIF'),
                    listeners: {
                        'click': function(node, e){
                            e.stopEvent();
                            Author.clearBackgroundObjects = true;
                            clearScreen();
                            treeTravel(node.oID, node.ePos);
                            Author.current.currentNode = Author.lessonNodes.nodes.item(node.oID);
                            if (Author.current.currentNode.type == "RANDOM") {
                                Author.Test.startAtRandom = true;
                            }
                            Author.current.currentElementXML = null;
                            Author.current.currentElement = null;
                            Author.current.currentElementPosition = node.ePos;
                            Author.current.currentListPosition = 0;
                            Author.current.currentElementResponses.clear();
                            showLessonNode();
                        }
                    }
                });
                aTreeNode.oID = aNode.id;
                aTreeNode.ePos = -1;
                lessonRoot.appendChild(aTreeNode);
                addChildrens(aTreeNode, aNode, expandNode, showElementCode);
            }
            break;
        }
    }
    lessonRoot.expand();
    selectNode(Author.current.currentNode.id, Author.current.currentElementPosition, Author.structureWin.getComponent('tree').getRootNode());
    
}

/**
 * Add all the elements to the given node
 * @param {Object} curTreeNode
 * @param {Object} curNode
 * @param {Object} showElementCode
 */
function addElements(curTreeNode, curNode, showElementCode){
    var elements = curNode.xml.getElementsByTagName("element");
    var elementCount = elements.length;
    var elementType = curNode.xml.attributes[0].nodeValue;
    for (var index = 0; index < elementCount; index++) {
        var anEle = new Element(elements[index], index);
        if (anEle.showInReference) {
            aTreeNode = new Ext.tree.TreeNode({
                text: getProperNodeName(showElementCode, trim(anEle.name.replace("~*", ""))),
                leaf: true,
                icon: ('./lib/images/' + anEle.type + '.GIF'),
                listeners: {
                    'click': function(node, e){
                        e.stopEvent();
                        Author.clearBackgroundObjects = true;
                        clearScreen();
                        
                        treeTravel(node.oID, node.ePos);
                        Author.current.currentNode = Author.lessonNodes.nodes.item(node.oID);
                        if (Author.current.currentNode.type == "RANDOM") {
                            Author.Test.startAtRandom = true;
                        }
                        Author.current.currentElementXML = null;
                        Author.current.currentElement = null;
                        Author.current.currentElementPosition = node.ePos;
                        Author.current.currentListPosition = 0;
                        Author.current.currentElementResponses.clear();
                        showLessonNode();
                    }
                }
            });
            aTreeNode.oID = curNode.id;
            aTreeNode.ePos = index;
            curTreeNode.appendChild(aTreeNode);
        }
    }
}

/**
 * recursively add the child nodes for the given lesson node
 * @param {Object} curTreeNode
 * @param {Object} curNode
 * @param {Object} expandNode
 * @param {Object} showElementCode
 */
function addChildrens(curTreeNode, curNode, expandNode, showElementCode){
    if (curNode.children.getCount() == 0) {
        addElements(curTreeNode, curNode);
        return;
    }
    else {
        for (var index = 0; index < curNode.children.getCount(); index++) {
            var aNode = curNode.children.itemAt(index);
            if (aNode.showInReference) {
                aTreeNode = new Ext.tree.TreeNode({
                    text: getProperNodeName(showElementCode, aNode.name),
                    expanded: expandNode,
                    icon: ('./lib/images/' + aNode.type + '.GIF'),
                    listeners: {
                        'click': function(node, e){
                            e.stopEvent();
                            Author.clearBackgroundObjects = true;
                            clearScreen();
                            
                            treeTravel(node.oID, node.ePos);
                            Author.current.currentNode = Author.lessonNodes.nodes.item(node.oID);
                            
                            if (Author.current.currentNode.type == "RANDOM") {
                                Author.Test.startAtRandom = true;
                            }
                            Author.current.currentElementXML = null;
                            Author.current.currentElement = null;
                            Author.current.currentElementPosition = node.ePos;
                            Author.current.currentListPosition = 0;
                            Author.current.currentElementResponses.clear();
                            showLessonNode();
                        }
                    }
                });
                aTreeNode.oID = aNode.id;
                aTreeNode.ePos = -1;
                curTreeNode.appendChild(aTreeNode);
                addChildrens(aTreeNode, aNode, expandNode, showElementCode);
            }
        }
    }
}

/**
 * Get the parent option of the current option.
 */
function getParentNode(){
    return Author.current.currentNode.parent;
}

/**
 * Initialize the background list and foreground list.
 */
function doLessonRoot(){
    var rootXML = Author.lessonXMLDoc.getElementsByTagName("lessonRoot")[0];
    var bgFXDelay = 0;
    
    // Initialize the background list.
    var backgroundListXML = rootXML.getElementsByTagName("list")[0];
    Author.current.currentListXML = backgroundListXML;
    var actions = backgroundListXML.getElementsByTagName("action");
    for (var index = 0; index < actions.length; index++) {
        var action = actions[index];
        var actionType = action.attributes[0].nodeValue;
        if (actionType == "PROGRESSBAR") {
            var actionObject = new ProgressbarClass(Author.backgroundObject.getContent(), action);
        }
        if (actionType == "BACKGROUND") {
            bgFXDelay = Author.backgroundObject.readConfig(action);
            continue;
        }
        if (actionType == "TEXTACTION") {
            var actionObject = new TextClass(Author.backgroundObject.getContent(), action);
        }
        if (actionType == "TXTEXTACTION") {
            var actionObject = new TXTextClass(Author.backgroundObject.getContent(), action);
        }
        if (actionType == "IMAGEACTION") {
            var actionObject = new ImageClass(Author.backgroundObject.getContent(), action);
        }
        actionObject.addToBackground = true;
        actionObject.addToForeground = false;
        actionObject.addToBackgroundAtElementPosition = -1;
        actionObject.readConfig(action);
        actionObject.setVisible(actionObject.hide == "false");
        Author.backgroundList.list.add(actionObject.id, actionObject);
    }
    
    // Initialize the foreground list.
    var foregroundListXML = rootXML.getElementsByTagName("list")[1];
    Author.current.currentListXML = backgroundListXML;
    var actions = foregroundListXML.getElementsByTagName("action");
    for (var index = 0; index < actions.length; index++) {
        var action = actions[index];
        var actionType = action.attributes[0].nodeValue;
        if (actionType == "CUSTOMBUTTON") {
            var actionObject = new CustomButtonClass(Author.backgroundObject.getContent(), action);
            actionObject.zorder(99);
        }
        if (actionType == "HOTSPOT") {
            var actionObject = new HotSpotClass(Author.backgroundObject.getContent(), action);
            actionObject.zorder(99);
        }
        actionObject.addToBackground = false;
        actionObject.addToForeground = true;
        actionObject.readConfig(action);
        actionObject.setVisible(actionObject.hide == "false");
        Author.foregroundList.list.add(actionObject.id, actionObject);
    }
    return bgFXDelay;
}

/**
 * Travel the list and show all the actions which are added to the background list.
 * This is used when testing the web lesson from a selected option or element.
 *
 * @param {Object} listXML
 */
function travelList(listXML, testing){
    Author.travellingList = true;
    if (typeof(testing) == 'undefined') {
        testing = false;
    }
    // Author.current.currentElementPosition = -1;
    Author.current.currentListXML = listXML;
    var actions = listXML.getElementsByTagName("action");
    var actionCount = actions.length;
    var interval = 0;
    var index = 0;
    //alert(listXML.attributes[0].nodeValue);
    while (index < actionCount) {
    
        var action = actions[index++];
        var actionType = action.attributes[0].nodeValue;
        
        if (actionType == "VISIBLEACTION") {
            var actionObject = new VisibleClass();
            actionObject.readConfig(action, true);
            Author.current.currentList.add(actionObject.id, actionObject);
        }
        if (actionType == "CHANGEBACKGROUND") {
            Author.backgroundObject.change(action);
        }
        if (actionType == "VARIABLE") {
            var actionObject = new VariableClass();
            actionObject.readConfig(action);
            Author.current.currentList.add(actionObject.id, actionObject);
        }
        
        if (actionType == "TXTEXTACTION") {
            var actionObject = new TXTextClass(Author.backgroundObject.getContent(), action);
            if (actionObject.addToBackground) {
                actionObject.readConfig(action);
                Author.current.currentList.add(actionObject.id, actionObject);
            }
            else 
                actionObject.remove();
        }
        if (actionType == "TEXTACTION") {
            var actionObject = new TextClass(Author.backgroundObject.getContent(), action);
            if (actionObject.addToBackground) {
                actionObject.readConfig(action);
                Author.current.currentList.add(actionObject.id, actionObject);
            }
            else 
                actionObject.remove();
        }
        if (actionType == "IMAGEACTION") {
            var actionObject = new ImageClass(Author.backgroundObject.getContent(), action);
            if (actionObject.addToBackground) {
                actionObject.readConfig(action);
                Author.current.currentList.add(actionObject.id, actionObject);
            }
            else 
                actionObject.remove();
        }
        if (testing) {
            alert(actionType + ":" + trim(action.getElementsByTagName("id")[0].childNodes[0].nodeValue));
        }
    }
    Author.travellingList = false;
}

/**
 * Travel from the root of the lesson structure to the selected node or element.
 * This is used for testing lesson from selection.
 *
 * @param {Object} cNode  selected option ID
 * @param {Object} cElement  selected element position
 */
function treeTravel(cNode, cElement){
    Author.current.currentElementPosition = -1;
    var treeNodes = new Ext.util.MixedCollection;
    var treeNode = Author.lessonNodes.nodes.item(cNode);
    var curNode = Author.lessonNodes.nodes.item(cNode);
    
    treeNode = Author.lessonNodes.nodes.get(treeNode.parent);
    if (typeof(treeNode) == 'undefined') 
        return;
    // Travel back to the root
    while (treeNode.parent != 0) {
        treeNodes.add(treeNode.id, treeNode);
        treeNode = Author.lessonNodes.nodes.get(treeNode.parent);
    }
    
    treeNodes.add(treeNode.id, treeNode);
    
    // Travel the nodes from the root to the selected node
    while (treeNodes.getCount() > 0) {
        treeNode = treeNodes.removeAt(treeNodes.getCount() - 1);
        
        var lists = treeNode.xml.getElementsByTagName("list");
        var listCount = lists.length;
        var listXML = null;
        for (var index = 0; index < listCount; index++) {
            listXML = lists[index];
            var listType = listXML.attributes[0].nodeValue;
            if (listType == "INTRODUCTION") {
                var show = listXML.attributes[2].nodeValue;
                if (show == "ONCE") {
                    Author.introductionListStat.add(treeNode.id, "true");
                }
            }
            if (listType == "MENUINFORMATION") {
                break;
            }
            listXML = null;
        }
        if (listXML != null) {
            travelList(listXML);
        }
    }
    // all the parent nodes finished, start to go through the elements in the current node
    Author.current.currentNode = curNode;
    for (var ePos = -1; ePos < cElement; ePos++) {
        //alert("ePos: "+ePos+" , cElement: "+cElement);
        if (ePos == -1) {
            Author.current.currentBgStack.clear();
            var curMenuBgObject = {
                epos: -1,
                type: Author.backgroundObject.type,
                color: Author.backgroundObject.bgColor,
                image: Author.backgroundObject.bgImage
            }
            // currentBgStack is used to keep a stack of all the background changes within a lesson node. 
            // When going forwards and backwards, the lesson will refer to the currentBgStack to set the correct background for the element. 
            Author.current.currentBgStack.add("-1", curMenuBgObject);
            var introductionLists = curNode.xml.getElementsByTagName("list");
            if (introductionLists.length > 0) {
                var listXML = introductionLists[0];
                if (listXML.attributes[0].nodeValue == "INTRODUCTION") {
                    var show = listXML.attributes[2].nodeValue;
                    if (show == "ONCE") {
                        Author.introductionListStat.add(curNode.id, "true");
                    }
                }
                clearScreen();
                Author.foregroundList.show();
                Author.backgroundList.updateProgressBar();
                travelList(listXML);
            }
            continue;
        }
        Author.current.currentElementXML = null;
        Author.current.currentElement = null;
        var elements = curNode.xml.getElementsByTagName("element");
        var elementCount = elements.length;
        // alert("elementCount: "+elementCount);
        if (Author.current.currentElementXML == null) {
            Author.current.currentElementXML = elements[ePos];
            if (!parserAndEvalActionCondition(Author.current.currentElementXML, true)) {
                continue;
            }
            Author.current.currentListPosition = 0;
        }
        var elementType = Author.current.currentElementXML.attributes[0].nodeValue;
        Author.current.currentElementType = elementType;
        Author.current.currentElementPosition = ePos;
        Author.current.currentElement = new Element(Author.current.currentElementXML, Author.current.currentElementPosition);
        if (elementType == "INFORMATION") {
            var lists = Author.current.currentElementXML.getElementsByTagName("list");
            var listXML = lists[0];
            clearScreen();
            Author.foregroundList.show();
            Author.backgroundList.updateProgressBar();
            travelList(listXML);
        }
        else {
            //Elements
            var lists = Author.current.currentElementXML.getElementsByTagName("list");
            var listCount = lists.length;
            for (var lIndex = 0; lIndex < listCount; lIndex++) {
                var listXML = lists[lIndex];
                var listType = listXML.attributes[0].nodeValue;
                if (listType == "INFORMATION") {
                    clearScreen();
                    Author.foregroundList.show();
                    Author.backgroundList.updateProgressBar();
                    travelList(listXML);
                }
            }
        }
    }
    treeNodes.clear();
    treeNodes = null;
}

/**
 * Initlize the lesson node collection and set the starting node.
 */
function initlizeLessonNodes(){
    var BMString = Ext.state.Manager.get(Author.projectName + "[" + Author.traineeID + "]", "");
    if (BMString != "") {
        var bmStr = BMString.split("||");
        Author.optionBMString = bmStr[0];
        Author.variableBMString = bmStr[1];
    }
    
    var lessonNodes = Author.lessonXMLDoc.getElementsByTagName("lessonNode");
    
    
    for (var index = 0; index < lessonNodes.length; index++) {
        var nodeXML = lessonNodes[index];
        var lessonNode = new LessonNode(nodeXML);
        lessonNode.readBookmark(Author.optionBMString);
        Author.lessonNodes.nodes.add(lessonNode.id, lessonNode);
        Author.introductionListStat.add(lessonNode.id, "false");
    }
    // If Testing from a selected element, treetravel all the parent node and elements, and locate the current element to the selected element.
    if (Author.Testing && Author.Test.type != "full") {
        var startNode = Author.lessonNodes.nodes.item(Author.Test.startLessonNode);
        
        // If the testing information is not valid, start the lesson from the beginning.
        if (typeof(startNode) == 'undefined') {
            Author.current.currentNode = Author.lessonNodes.nodes.itemAt(0);
            Author.current.currentNode.endListShown = false;
            Author.current.currentElementPosition = -1;
            Author.current.currentListPosition = 0;
        }
        else {
            var elements = startNode.xml.getElementsByTagName("element");
            var elementCount = elements.length;
            var ePos = 0;
            if (Author.Test.startElement != -1) {
                for (; ePos < elementCount; ePos++) {
                    if (elements[ePos].attributes[1].nodeValue == Author.Test.startElement) {
                        break;
                    }
                }
                Author.current.currentElementPosition = ePos;
            }
            else {
                Author.current.currentElementPosition = -1;
            }
            var savedElementPosition = Author.current.currentElementPosition;
            treeTravel(Author.Test.startLessonNode, savedElementPosition);
            
            Author.current.currentNode = Author.lessonNodes.nodes.item(Author.Test.startLessonNode);
            Author.current.currentNode.endListShown = false;
            if (Author.current.currentNode.type == "RANDOM") {
                Author.Test.startAtRandom = true;
            }
            Author.current.currentElementXML = null;
            Author.current.currentElement = null;
            Author.current.currentElementPosition = savedElementPosition;
            Author.current.currentListPosition = 0;
        }
    }
    else {
        Author.current.currentNode = Author.lessonNodes.nodes.itemAt(0);
        Author.current.currentNode.endListShown = false;
        Author.current.currentElementPosition = -1;
        Author.current.currentListPosition = 0;
    }
}

/**
 * Hightlight the current node and element in the reference window
 * @param {Object} oID
 * @param {Object} ePos
 * @param {Object} node
 */
function selectNode(oID, ePos, node){
    //   alert(oID + ", " + ePos + ", " + node.getPath() + "\n" + node.oID + ", " + node.ePos);
    if (node.oID == oID && node.ePos == ePos) {
        node.select();
        return true;
    }
    var cs = node.childNodes;
    for (var i = 0, len = cs.length; i < len; i++) {
        if (selectNode(oID, ePos, cs[i])) {
            return true;
        }
    }
    return false;
}

/**
 * show the current lesson node|option
 */
function showLessonNode(){

    if (typeof(Author.structureWin) != 'undefined' && Author.structureWin.isVisible()) {
        selectNode(Author.current.currentNode.id, Author.current.currentElementPosition, Author.structureWin.getComponent('tree').getRootNode());
    }
    if (Author.current.currentNode.type == "MENU") {
        var lists = Author.current.currentNode.xml.getElementsByTagName("list");
        var listCount = lists.length;
        
        var count = 0;
        if (Author.current.currentElementResponses.getCount() == 0) {
            for (var index = 0; index < listCount; index++) {
                var type = lists[index].attributes[0].nodeValue;
                if (type == "RESPONSE") {
                    var condition = parserCondition(lists[index]);
                    var responseObject = new ResponseClass(condition, lists[index])
                    var id = lists[index].attributes[1].nodeValue;
                    Author.current.currentElementResponses.add(id, responseObject);
                }
                else {
                    count++;
                }
            }
            listCount = count - (Author.current.currentNode.endList == null ? 0 : 1);
            Author.variables.get(variableName("ATTEMPT")).setValue(0);
            Author.answers.clear();
        }
        else {
            listCount = lists.length - Author.current.currentElementResponses.getCount() - (Author.current.currentNode.endList == null ? 0 : 1);
        }
        
        
        if (Author.current.currentListPosition >= listCount) {
            return;
        }
        var listXML = lists[Author.current.currentListPosition++];
        var listType = listXML.attributes[0].nodeValue;
        
        if (listType == "INTRODUCTION") {
        
            var show = listXML.attributes[2].nodeValue;
            if (show == "ONCE") {
                /*
                 if (Author.current.currentNode.parent == 0) {
                 Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
                 // Cookies expires in 1 hrs
                 expires: new Date(new Date().getTime() + (1000 * 60 * 60))
                 }));
                 var skipLessonIntroduction = Ext.state.Manager.get(Author.projectName + "[skipLessonIntroduction]");
                 if (typeof(skipLessonIntroduction) == 'undefined') {
                 //save Menu Location
                 Ext.state.Manager.set(Author.projectName + "[skipLessonIntroduction]", true);
                 }
                 else
                 if (skipLessonIntroduction) {
                 Author.introductionListStat.add(Author.current.currentNode.id, "true");
                 }
                 }
                 */
                if (Author.introductionListStat.get(Author.current.currentNode.id) == "false") {
                    Author.introductionListStat.add(Author.current.currentNode.id, "true");
                }
                else {
                    listXML = lists[Author.current.currentListPosition++];
                    listType = listXML.attributes[0].nodeValue;
                }
            }
        }
        if (listType == "RESPONSE") {
            alert("Response list ??? why you are here !?");
        }
        perpareAndShowList(listXML, !(listType == "MENUINTERACTION"));
    }
    else 
        if (Author.current.currentNode.type == "SEQUENCE" || (Author.current.currentNode.type == "RANDOM" && Author.Test.startAtRandom)) {
            if (Author.current.currentElementPosition == -1) {
                Author.current.currentNode.optionStart = new Date();
                Author.current.currentBgStack.clear();
              
                var curMenuBgObject = {
                    epos: -1,
                    type: Author.backgroundObject.type,
                    color: Author.backgroundObject.bgColor,
                    image: Author.backgroundObject.bgImage
                }
                
                // currentBgStack is used to keep a stack of all the background changes within a lesson node. 
                // When going forwards and backwards, the lesson will refer to the currentBgStack to set the correct background for the element. 
                Author.current.currentBgStack.add("-1", curMenuBgObject);
                
                Author.current.currentElementPosition++;
                var introductionLists = Author.current.currentNode.xml.getElementsByTagName("list");
                
                if (introductionLists.length > 0) {
                    var listXML = introductionLists[0];
                    if (listXML.attributes[0].nodeValue == "INTRODUCTION") {
                        var show = listXML.attributes[2].nodeValue;
                        if (show == "ONCE") {
                            if (Author.introductionListStat.get(Author.current.currentNode.id) == "false") {
                                Author.introductionListStat.add(Author.current.currentNode.id, "true");
                                perpareAndShowList(listXML, true);
                                return;
                            }
                        }
                        else {
                            perpareAndShowList(listXML, true);
                            return;
                        }
                    }
                }
            }
            
            var elements = Author.current.currentNode.xml.getElementsByTagName("element");
            var elementCount = elements.length;
            
           
            if (Author.current.currentElementPosition >= elementCount) {
                // Secquence finished, try to go to the menu node.
                if (Author.current.currentNode.showEndOnCompletion && !Author.current.currentNode.endListShown) {
                    Author.current.currentNode.endListShown = true;
                    perpareAndShowList(Author.current.currentNode.endList, true);
                    return;
                }
                Author.current.currentNode.finished = true;
                Author.Test.startAtRandom = false;
                var preNodeID = Author.current.currentNode.id;
                var parentID = getParentNode();
                if (parentID != 0) {
                    // If menu node exists, set currentNode to the menu node and set the background for the menu node.
                    Author.current.currentNode = Author.lessonNodes.nodes.get(parentID);
                    Author.current.currentNode.endListShown = false;
                    while (Author.current.currentBgStack.getCount() > 0) {
                        var bgObject = Author.current.currentBgStack.last();
                        Author.current.currentBgStack.remove(bgObject);
                        if (Author.current.currentBgStack.getCount() == 0) {
                            Author.backgroundObject.setBg(bgObject);
                        }
                    }
                }
                if (preNodeID == Author.current.currentNode.id) {
                    // preNodeID == Author.current.currentNode.id means the secquence doesn't have a parent menu. Lesson finished.
                    // If lesson is set to repeat repeat the lesson else show a finish message box.
                    if (Author.repeatLesson) {
                        Author.current.currentNode = Author.lessonNodes.nodes.itemAt(0);
                        Author.current.currentNode.endListShown = false;
                        Author.current.currentElementXML = null;
                        Author.current.currentElement = null;
                        Author.current.currentElementType = "";
                        Author.current.currentElementResponses.clear();
                        Author.current.currentElementPosition = -1;
                        Author.current.currentListPosition = 0;
                        Author.current.randomOptionsCollection.clear();
                        showLessonNode();
                        return;
                    }
                    else {
                        Author.lessonFinished = true;
                        Ext.MessageBox.show({
                            title: Author.projectName,
                            msg: '<p style="text-align:center">Lesson is finished.</p>',
                            width: 300,
                            buttons: Ext.MessageBox.OK
                        });
                        return;
                    }
                }
                Author.clearBackgroundObjects = true;
                Author.current.currentElementXML = null;
                Author.current.currentElement = null;
                Author.current.currentElementType = "";
                Author.current.currentElementResponses.clear();
                Author.current.currentElementPosition = -1;
                Author.current.currentListPosition = 0;
                Author.current.randomOptionsCollection.clear();
                showLessonNode();
                return;
            }
            
            if (Author.current.currentElementXML == null) {
                // start to show the element
                if (Author.current.currentBgStack.getCount() > 0) {
                    // try to set the correct background to the element
                    var bgObject = Author.current.currentBgStack.last();
                    if (Author.current.currentElementPosition < bgObject.epos) {
                        Author.current.currentBgStack.remove(bgObject);
                        if (Author.current.currentBgStack.getCount() > 0) {
                            Author.backgroundObject.setBg(bgObject);
                        }
                        else {
                            Author.backgroundObject.setOriginal();
                        }
                    }
                }
                  
                // Get the element XML
                Author.current.currentElementXML = elements[Author.current.currentElementPosition];
                Author.current.currentElement = new Element(Author.current.currentElementXML, Author.current.currentElementPosition);
                Author.current.currentNode.addElement(Author.current.currentElement);
                
                
                if (!parserAndEvalActionCondition(Author.current.currentElementXML, true)) {
                    // If the condition of the element is not achieved, skip the element.
                    Author.current.currentElementXML = null;
                    Author.current.currentElement = null;
                    if (Author.goingBack) {
                        Author.current.currentElementPosition--;
                        if (Author.current.currentElementPosition < 0) {
                            Author.current.currentNode = Author.lessonNodes.nodes.get(getParentNode());
                            Author.current.currentNode.endListShown = false;
                            while (Author.current.currentBgStack.getCount() > 0) {
                                var bgObject = Author.current.currentBgStack.last();
                                Author.current.currentBgStack.remove(bgObject);
                                if (Author.current.currentBgStack.getCount() == 0) {
                                    Author.backgroundObject.setBg(bgObject);
                                }
                            }
                            // Author.backgroundObject.setOriginal();
                            Author.current.currentElementXML = null;
                            Author.current.currentElement = null;
                            Author.current.currentElementType = "";
                            Author.current.currentElementResponses.clear();
                            Author.current.currentElementPosition = -1;
                            Author.current.currentListPosition = 0;
                            Author.clearBackgroundObjects = true;
                            Author.backToMenu = true;
                            Author.current.randomOptionsCollection.clear();
							Author.goingBack=false;
                        }
                    }
                    else {
                        Author.current.currentElementPosition++;
                    }
					// alert(Author.current.currentElementPosition+", "+ elementCount);
                    //alert(Author.current.currentElementPosition);
                    showLessonNode();
                    return;
                }
                
                Author.goingBack = false;
                Author.current.currentListPosition = 0;
                Author.interactionList.startIndex = -1;
                Author.informationList.startIndex = -1;
            }
            
            var elementType = Author.current.currentElementXML.attributes[0].nodeValue;
            Author.current.currentElementType = elementType; // maybe not used any more.
            Author.responseEnd = true;
            Author.inResponse = false;
            
            if (elementType == "INFORMATION") {
                // Show an information element
                var lists = Author.current.currentElementXML.getElementsByTagName("list");
                var listCount = 1;
                if (Author.current.currentListPosition >= listCount) {
                    Author.current.currentElementXML = null;
                    Author.current.currentElement = null;
                    Author.current.currentElementType = "";
                    Author.current.currentElementResponses.clear();
                    Author.response.clear();
                    Author.current.currentElementPosition++;
                    showLessonNode();
                    return;
                }
                var listXML = lists[Author.current.currentListPosition++];
                perpareAndShowList(listXML, true);
            }
            else {
                //Show other type of elements
                var lists = Author.current.currentElementXML.getElementsByTagName("list");
                var listCount = lists.length;
                var count = 0;
                if (Author.current.currentElementResponses.getCount() == 0) {
                    // Count the lists in the elements exclude response lists.
                    for (var index = 0; index < listCount; index++) {
                        var type = lists[index].attributes[0].nodeValue;
                        if (type == "RESPONSE") {
                            // Initialize every response object in the element
                            var condition = parserCondition(lists[index]);
                            var responseObject = new ResponseClass(condition, lists[index])
                            var id = lists[index].attributes[1].nodeValue;
                            Author.current.currentElementResponses.add(id, responseObject);
                        }
                        else {
                            count++;
                        }
                    }
                    listCount = count;
                    Author.variables.get(variableName("ATTEMPT")).setValue(0);
                    Author.answers.clear();
                }
                else {
                    listCount = lists.length - Author.current.currentElementResponses.getCount();
                }
                
                if (Author.current.currentListPosition >= listCount) {
                    // If element finished, go to the next element
                    Author.current.currentElementXML = null;
                    Author.current.currentElement = null;
                    Author.current.currentElementType = "";
                    Author.current.currentElementResponses.clear();
                    Author.response.clear();
                    Author.current.currentElementPosition++;
                    showLessonNode();
                    return;
                }
                
                // Show the lists in the element
                var listXML = lists[Author.current.currentListPosition++];
                var listType = listXML.attributes[0].nodeValue;
                perpareAndShowList(listXML, !(listType == "INTERACTION"));
            }
        }
        else 
            if (Author.current.currentNode.type == "RANDOM" && !Author.Test.startAtRandom) {
                if (Author.current.currentElementPosition == -1) {
                    Author.current.currentNode.optionStart = new Date();
                    Author.current.currentBgStack.clear();
                    Author.current.currentElementPosition++;
                    var introductionLists = Author.current.currentNode.xml.getElementsByTagName("list");
                    if (introductionLists.length > 0) {
                        var listXML = introductionLists[0];
                        if (listXML.attributes[0].nodeValue == "INTRODUCTION") {
                            var show = listXML.attributes[1].nodeValue;
                            if (show == "ONCE") {
                                if (Author.introductionListStat.get(Author.current.currentNode.id) == "false") {
                                    Author.introductionListStat.add(Author.current.currentNode.id, "true");
                                    perpareAndShowList(listXML, true);
                                    return;
                                }
                            }
                            else {
                                perpareAndShowList(listXML, true);
                                return;
                            }
                        }
                    }
                }
                
                if (Author.current.randomOptionsCollection.getCount() == 0) {
                    // Randomise the elements in the random option.
                    // Build ID to index map
                    Author.current.currentNode.elements.clear();
                    var elements = Author.current.currentNode.xml.getElementsByTagName("element");
                    var elementCount = elements.length;
                    var idMaps = new Ext.util.MixedCollection;
                    for (var i = 0; i < elementCount; i++) {
                        idMaps.add(elements[i].attributes[1].nodeValue, i);
                    }
                    
                    // start to make randomOptionCollection.
                    var randomiseList = trim(Author.current.currentNode.xml.attributes[5].nodeValue).toLowerCase();
                    var noDupOption = trim(Author.current.currentNode.xml.attributes[6].nodeValue).toLowerCase();
                    var listSegments = Author.current.currentNode.xml.getElementsByTagName("listSegment");
                    var listSegmentsCount = listSegments.length;
                    // randomise the elements based on the list segments.
                    for (var listIndex = 0; listIndex < listSegmentsCount; listIndex++) {
                        var listSegXML = listSegments[listIndex];
                        var listOptions = listSegXML.getElementsByTagName("optionID");
                        var rand = trim(listSegXML.attributes[0].nodeValue).toLowerCase();
                        var pick = listOptions.length;
                        if (rand == "false") {
                            // If the list segment is not random, add the elements in sequence
                            for (var opIndex = 0; opIndex < pick; opIndex++) {
                                var added = Author.current.randomOptionsCollection.add(Author.current.randomOptionsCollection.getCount(), idMaps.get(listOptions[opIndex].childNodes[0].nodeValue));
                            }
                        }
                        else {
                            // otherwise, randomise the elements in the list segment
                            pick = parseInt(trim(listSegXML.attributes[1].nodeValue));
                            var opCandidates = new Ext.util.MixedCollection;
                            for (var i = 0; i < listOptions.length; i++) {
                                opCandidates.add(i, listOptions[i].childNodes[0].nodeValue);
                            }
                            for (var opIndex = 0; opIndex < pick; opIndex++) {
                                var pickCandidate = Math.ceil(Math.random() * 100) % opCandidates.getCount();
                                var added = Author.current.randomOptionsCollection.add(Author.current.randomOptionsCollection.getCount(), idMaps.get(opCandidates.itemAt(pickCandidate)));
                                if (noDupOption == "true") {
                                    opCandidates.removeAt(pickCandidate)
                                }
                            }
                            opCandidates.clear();
                            opCandidates = null;
                        }
                    }
                    idMaps.clear();
                    idMaps = null;
                }
                
                var elements = Author.current.currentNode.xml.getElementsByTagName("element");
                var elementCount = Author.current.randomOptionsCollection.getCount();
                
                if (Author.current.currentElementPosition >= elementCount) {
                    if (Author.current.currentNode.showEndOnCompletion && !Author.current.currentNode.endListShown) {
                        Author.current.currentNode.endListShown = true;
                        perpareAndShowList(Author.current.currentNode.endList, true);
                        return;
                    }
                    Author.current.currentNode.finished = true;
                    Author.Test.startAtRandom = false;
                    var preNodeID = Author.current.currentNode.id;
                    var parentID = getParentNode();
                    if (parentID != 0) {
                        Author.current.currentNode = Author.lessonNodes.nodes.get(parentID);
                        Author.current.currentNode.endListShown = false;
                        while (Author.current.currentBgStack.getCount() > 0) {
                            var bgObject = Author.current.currentBgStack.last();
                            Author.current.currentBgStack.remove(bgObject);
                            if (Author.current.currentBgStack.getCount() == 0) {
                                Author.backgroundObject.setBg(bgObject);
                            }
                        }
                    }
                    if (preNodeID == Author.current.currentNode.id) {
                        if (Author.repeatLesson) {
                            Author.current.currentNode = Author.lessonNodes.nodes.itemAt(0);
                            Author.current.currentNode.endListShown = false;
                            Author.current.currentElementXML = null;
                            Author.current.currentElement = null;
                            Author.current.currentElementType = "";
                            Author.current.currentElementResponses.clear();
                            Author.current.currentElementPosition = -1;
                            Author.current.currentListPosition = 0;
                            Author.current.randomOptionsCollection.clear();
                            showLessonNode();
                            return;
                        }
                        else {
                            Author.lessonFinished = true;
                            Ext.MessageBox.show({
                                title: Author.projectName,
                                msg: '<p style="text-align:center">Lesson is finished.</p>',
                                width: 300,
                                buttons: Ext.MessageBox.OK
                            });
                            return;
                        }
                        return;
                    }
                    Author.clearBackgroundObjects = true;
                    Author.current.currentElementXML = null;
                    Author.current.currentElement = null;
                    Author.current.currentElementType = "";
                    Author.current.currentElementResponses.clear();
                    Author.response.clear();
                    Author.current.currentElementPosition = -1;
                    Author.current.currentListPosition = 0;
                    Author.current.randomOptionsCollection.clear();
                    showLessonNode();
                    return;
                }
                
                if (Author.current.currentElementXML == null) {
                    Author.current.currentElementXML = elements[Author.current.randomOptionsCollection.itemAt(Author.current.currentElementPosition)];
                    Author.current.currentElement = new Element(Author.current.currentElementXML, Author.current.currentElementPosition);
                    Author.current.currentNode.addElement(Author.current.currentElement);
                    if (!parserAndEvalActionCondition(Author.current.currentElementXML, true)) {
                        //alert("List condition not achieved!");
                        Author.current.currentElementXML = null;
                        Author.current.currentElement = null;
                        if (Author.goingBack) {
                            Author.current.currentElementPosition--;
                            if (Author.current.currentElementPosition < 0) {
                                Author.current.currentNode = Author.lessonNodes.nodes.get(getParentNode());
                                Author.current.currentNode.endListShown = false;
                                while (Author.current.currentBgStack.getCount() > 0) {
                                    var bgObject = Author.current.currentBgStack.last();
                                    Author.current.currentBgStack.remove(bgObject);
                                    if (Author.current.currentBgStack.getCount() == 0) {
                                        Author.backgroundObject.setBg(bgObject);
                                    }
                                }
                                // Author.backgroundObject.setOriginal();
                                Author.current.currentElementXML = null;
                                Author.current.currentElement = null;
                                Author.current.currentElementType = "";
                                Author.current.currentElementResponses.clear();
                                Author.current.currentElementPosition = -1;
                                Author.current.currentListPosition = 0;
                                Author.clearBackgroundObjects = true;
                                Author.backToMenu = true;
                                Author.current.randomOptionsCollection.clear();
                            }
                        }
                        else {
                            Author.current.currentElementPosition++;
                        }
                        showLessonNode();
                        return;
                    }
                    Author.goingBack = false;
                    Author.current.currentListPosition = 0;
                    Author.interactionList.startIndex = -1;
                    Author.informationList.startIndex = -1;
                }
                
                var elementType = Author.current.currentElementXML.attributes[0].nodeValue;
                Author.current.currentElementType = elementType;
                Author.responseEnd = false;
                if (elementType == "INFORMATION") {
                    var lists = Author.current.currentElementXML.getElementsByTagName("list");
                    var listCount = lists.length;
                    if (Author.current.currentListPosition >= listCount) {
                        Author.current.currentElementXML = null;
                        Author.current.currentElement = null;
                        Author.current.currentElementType = "";
                        Author.current.currentElementResponses.clear();
                        Author.response.clear();
                        Author.current.currentElementPosition++;
                        showLessonNode();
                        return;
                    }
                    var listXML = lists[Author.current.currentListPosition++];
                    perpareAndShowList(listXML, true);
                }
                else {
                    var lists = Author.current.currentElementXML.getElementsByTagName("list");
                    var listCount = lists.length;
                    var count = 0;
                    if (Author.current.currentElementResponses.getCount() == 0) {
                        for (var index = 0; index < listCount; index++) {
                            var type = lists[index].attributes[0].nodeValue;
                            if (type == "RESPONSE") {
                                var condition = parserCondition(lists[index]);
                                var responseObject = new ResponseClass(condition, lists[index])
                                var id = lists[index].attributes[1].nodeValue;
                                Author.current.currentElementResponses.add(id, responseObject);
                            }
                            else {
                                count++;
                            }
                        }
                        listCount = count;
                        Author.variables.get(variableName("ATTEMPT")).setValue(0);
                        Author.answers.clear();
                    }
                    else {
                        listCount = lists.length - Author.current.currentElementResponses.getCount();
                    }
                    if (Author.current.currentListPosition >= listCount) {
                        Author.current.currentElementXML = null;
                        Author.current.currentElement = null;
                        Author.current.currentElementType = "";
                        Author.current.currentElementResponses.clear();
                        Author.response.clear();
                        Author.current.currentElementPosition++;
                        showLessonNode();
                        return;
                    }
                    var listXML = lists[Author.current.currentListPosition++];
                    var listType = listXML.attributes[0].nodeValue;
                    perpareAndShowList(listXML, !(listType == "INTERACTION"));
                }
            }
}


/**
 * Replace VB6 functions with Javascript functions and evaluate their value.
 * @param {String} chunk VB6 function chunk. e.g: UCASE("abc"), INT(2*4.6)
 */
function replaceAndEvalFunc(chunk){

    var regex = /DATA\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            vName = vName.toUpperCase();
            if (Author.contants.containsKey(vName)) {
                return Author.contants.get(vName);
            }
            else {
                return "$(" + vName + ")";
            }
        });
        return chunk;
    }
    
    // String Functions
    var regex = /UCASE\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            return vName.toUpperCase();
        });
        return chunk;
    }
    var regex = /LCASE\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            return vName.toLowerCase();
        });
        return chunk;
    }
    var regex = /TRIM\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            return trim(vName);
        });
        return chunk;
    }
    var regex = /LEN\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            return vName.length;
        });
        return chunk;
    }
    var regex = /LEFT\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            var params = vName.split(",");
            return params[0].substring(0, parseInt(params[1]));
        });
        return chunk;
    }
    var regex = /RIGHT\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            var params = vName.split(",");
            return params[0].substring(vName.length - parseInt(params[1]));
        });
        return chunk;
    }
    var regex = /INSTR\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            var params = vName.split(",");
            if (params.length == 2) 
                return params[0].indexOf(params[1]);
            else 
                if (params.length >= 3) 
                    return params[1].indexOf(params[2], parseInt(params[0]));
        });
        return chunk;
    }
    var regex = /MID\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            var params = vName.split(",");
            if (params.length == 2) 
                return params[0].substring(parseInt(params[1]) - 1);
            else 
                if (params.length == 3) 
                    return params[0].substring(parseInt(params[1]) - 1, parseInt(params[1]) + parseInt(params[2]) - 1);
        });
        return chunk;
    }
    
    // Numeric Functions
    var regex = /INT\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            vName = eval(vName);
            return parseInt(vName);
        });
        return chunk;
    }
    var regex = /ROUND\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            vName = eval(vName);
            return Math.round(parseFloat(vName));
        });
        return chunk;
    }
    var regex = /SUM\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            values = vName.split(",");
            var sum = 0;
            for (var i = 0; i < values.length; i++) {
                if (!isNaN(values[i])) {
                    sum += parseFloat(values[i]);
                }
            }
            return sum;
        });
        return chunk;
    }
    var regex = /MAX\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            values = vName.split(",");
            var max = -99999999999;
            for (var i = 0; i < values.length; i++) {
                if (!isNaN(values[i])) {
                    var temp = parseFloat(values[i]);
                    if (temp > max) {
                        max = temp;
                    }
                }
            }
            return max;
        });
        return chunk;
    }
    var regex = /MIN\((.*)\)/gi;
    if (regex.test(chunk)) {
        chunk = chunk.replace(regex, function(name, vName){
            values = vName.split(",");
            var min = 99999999999;
            for (var i = 0; i < values.length; i++) {
                if (!isNaN(values[i])) {
                    var temp = parseFloat(values[i]);
                    if (temp < min) {
                        min = temp;
                    }
                }
            }
            return min;
        });
        return chunk;
    }
}

/**
 * Evaluate the value of an expression.
 * @param {String} expression E.g.: INT(3.5*2.6) * 12
 */
function evalExpression(expression){

    var temp = expression;
    var curStartIndex, startBrk, endBrk, functionName, exp;
    functionName = "";
    do {
        curStartIndex = 0;
        startBrk = expression.indexOf("(");
        if (startBrk == -1) {
            return expression;
        }
        var i = startBrk + 1;
        do {
            i = expression.indexOf("(", i);
            if (i >= 0) {
                curStartIndex = startBrk + 1;
                startBrk = i;
                i = i + 1;
            }
        }
        while (i >= 0)
        endBrk = expression.indexOf(")", startBrk);
        if (endBrk == -1) {
            displayError("Invalid expression:\n" + expression + "\nThe expression seems to be missing a closing bracket");
            return 0;
        }
        functionName = trim(expression.substring(curStartIndex, startBrk));
        exp = expression.substring(curStartIndex, endBrk + 1);
        
        if (functionName != "" || exp != "") {
            var chunk = "";
            if (functionName == "") {
                chunk = eval(exp);
            }
            else {
                chunk = replaceAndEvalFunc(expression.substring(curStartIndex, endBrk + 1));
            }
            var result = expression.substring(0, curStartIndex) + chunk + expression.substring(endBrk + 1);
            expression = result;
        }
    }
    while (functionName != "" || exp != "");
    return expression;
}

/**
 * Replace the variables within the text with their values.
 * E.g.: Today is [DATE].
 *
 * @param {String} text
 */
function replaceVariableNameWithValue(text){

    var regex = /\[-(\d+)\]/gi;
    text = text.replace(regex, function(name, vName){
        var actionID = vName.match(/(\d+)/gi)[0];
        var action = Author.current.currentList.get(actionID);
        if (typeof(action) == 'undefined') {
            return "";
        }
        else {
            return "[" + action.name + "]";
        }
    })
    var regex = /\[([%|&|\w|\s]+)\]/gi;
    do {
        var after = text.replace(regex, function(name, vName){
            //alert("name:"+name+"\nName:"+vName);
            try {
                vName = trim(vName);
                var regex = /(\d+)/;
                if (vName.search(regex) >= 0) {
                    if (Author.variables.containsKey(vName)) {
                        vName = Author.variables.get(vName).name;
                    }
                }
                var thisVar = Author.variables.get(variableName(vName.toUpperCase()));
                var replaced = getVariableValueByName(vName);
                if (typeof(thisVar) != 'undefined') {
                    if (thisVar.variableType == 0) {
                        try {
                            replaced = eval(replaced);
                        } 
                        catch (err) {
                        }
                    }
                }
                return replaced;
            } 
            catch (err) {
                alert("Error in replaceVariableNameWithValue:\n" + name + ", " + vName + "\n==========================\n" + err.name + " : " + err.description);
                return name;
            }
        });
        
        var condition = (after != text) && (after.search(regex) >= 0);
        // alert("Text: "+text+"\nAfter: "+after+"\nafter != text :"+(condition));
        if (condition) {
            text = after;
        }
    }
    while (condition);
    //alert(after.search(regex));
    
    
    return after;
}

/**
 * Parser and evaluate the condition of an action or an element.
 * If condition achieved return True, else return false.
 *
 * @param {Object} action
 * @param {boolean} isElement
 */
function parserAndEvalActionCondition(action, isElement){

    var conditionTag;
    if (isElement) {
        conditionTag = "elementCondition";
    }
    else {
        conditionTag = "condition";
    }
    var condition = action.getElementsByTagName(conditionTag)[0];
    if (condition == null) 
        return true;
    try {
        var composedCondition = true;
        var conditionChunk = false;
        var keyword = condition.attributes[0].nodeValue;
        var attempt = condition.attributes[1].nodeValue;
        var filter = condition.attributes[2].nodeValue;
        var upperCase = (filter.charAt(0) == "C");
        var wildCards = (filter.charAt(1) == "W");
        var removeSpaces = (filter.charAt(2) == "S");
        var numeric = (filter.charAt(3) == "N");
        if (keyword == "IF") {
            var bodys = condition.getElementsByTagName("body");
            var count = bodys.length;
            for (var index = 0; index < count; index++) {
                var connector = bodys[index].attributes[0].nodeValue;
                var variable = bodys[index].attributes[1].nodeValue;
                var operator = bodys[index].attributes[2].nodeValue;
                
                switch (connector) {
                    case "AND":
                        connector = "&&";
                        break;
                    case "OR":
                        connector = "||";
                        break;
                }
                switch (operator) {
                    case "":
                        operator = "==";
                        break;
                    case "<>":
                        operator = "!=";
                        break;
                    case "{}":
                        operator = "!!";
                        break;
                }
                
                var value = replaceVariableNameWithValue(bodys[index].attributes[3].nodeValue);
                var upcase = "";
                // if (upperCase) {
                value = value.toUpperCase();
                upcase = ".toString().toUpperCase()";
                // }
                var noSpace = "";
                if (removeSpaces) {
                    value = value.replace(/\s/g, "");
                    noSpace = ".toString().replace(/\\s/g,'')";
                }
                
                
                if (wildCards) {
                    value = value.replace(/\*/g, ".*");
                    value = value.replace(/\?/g, ".{1}");
                    value = "/" + value + "/";
                    conditionChunk = eval("(getVariableValueByName('" + variable + "')" + upcase + noSpace + ".search(" + value + ")>=0)");
                }
                else {
                    if (variable == 'CORRECT' || variable == 'INCORRECT') {
                        conditionChunk = eval(" (getVariableValueByName('" + variable + "'))");
                    }
                    else {
                        if (operator == "!!") {
                            conditionChunk = eval(" (getVariableValueByName('" + variable + "')" + upcase + noSpace + ".indexOf(\"" + value + "\") >= 0)");
                        }
                        else {
                            if (numeric) 
                                conditionChunk = eval(" eval((getVariableValueByName('" + variable + "')) " + operator + " " + value + ")");
                            else {
                                conditionChunk = eval(" (getVariableValueByName('" + variable + "')" + upcase + noSpace + " " + operator + " \"" + value + "\")");
                                //  alert(" (getVariableValueByName('" + variable + "')" + upcase + noSpace + " " + operator + " \"" + value + "\")"+"\n"+ getVariableValueByName( variable) + upcase + noSpace);
                            }
                        }
                    }
                }
                //alert(composedCondition + connector + conditionChunk);
                composedCondition = eval(composedCondition + connector + conditionChunk);
            }
        }
        // alert(composedCondition + "\n" + eval(composedCondition));
        return composedCondition;
    } 
    catch (err) {
        alert("Error in EvalActionCondition:\n" + composedCondition + "\n==========================\n" + err.name + " : " + err.description + "\n==========================\n" + action.xml);
        return true;
    }
}

/**
 * Parse the condition of a responselist.
 *
 * @param {Object} responseListXML
 */
function parserCondition(responseListXML){
    var condition = responseListXML.getElementsByTagName("condition")[0];
    var composedCondition = "";
    var keyword = condition.attributes[0].nodeValue;
    var attempt = condition.attributes[1].nodeValue;
    var filter = condition.attributes[2].nodeValue;
    var upperCase = (filter.charAt(0) == "C");
    var wildCards = (filter.charAt(1) == "W");
    var removeSpaces = (filter.charAt(2) == "S");
    var numeric = (filter.charAt(3) == "N");
    if (keyword == "IF") {
        composedCondition = composedCondition + "(true) ";
        var bodys = condition.getElementsByTagName("body");
        var count = bodys.length;
        for (var index = 0; index < count; index++) {
            var connector = bodys[index].attributes[0].nodeValue;
            var variable = bodys[index].attributes[1].nodeValue;
            var operator = bodys[index].attributes[2].nodeValue;
            switch (connector) {
                case "AND":
                    connector = "&&";
                    break;
                case "OR":
                    connector = "||";
                    break;
            }
            switch (operator) {
                case "":
                    operator = "==";
                    break;
                case "<>":
                    operator = "!=";
                    break;
                case "{}":
                    operator = "!!";
                    break;
            }
            var value = bodys[index].attributes[3].nodeValue;
            var upcase = "";
            // if (upperCase) {
            value = value.toUpperCase();
            upcase = ".toString().toUpperCase()";
            //}
            var noSpace = "";
            if (removeSpaces) {
                value = value.replace(/\s/g, "");
                noSpace = ".toString().replace(/\\s/g,'')";
            }
            var cond = "";
            if (wildCards) {
                value = value.replace(/\*/g, ".*");
                value = value.replace(/\?/g, ".{1}");
                value = "/" + value + "/";
                if (variable == 'CORRECT') {
                    cond = "(checkAnswer(" + removeSpaces + "," + wildCards + "))";
                }
                else 
                    if (variable == 'INCORRECT') {
                        cond = "!(checkAnswer(" + removeSpaces + "," + wildCards + "))";
                    }
                    else {
                        cond = "(getVariableValueByName('" + variable + "')" + upcase + noSpace + ".search(" + value + ")>=0)";
                    }
            }
            else {
                if (variable == 'CORRECT') {
                    cond = "(checkAnswer(" + removeSpaces + "," + wildCards + "))";
                }
                else 
                    if (variable == 'INCORRECT') {
                        cond = "!(checkAnswer(" + removeSpaces + "," + wildCards + "))";
                    }
                    else {
                    
                        if (operator == "!!") {
                            cond = " (getVariableValueByName('" + variable + "')" + upcase + noSpace + ".indexOf(\"" + value + "\") >= 0)";
                            
                        }
                        else {
                            if (numeric) 
                                cond = " eval((getVariableValueByName('" + variable + "')) " + operator + " " + value + ")";
                            else 
                                cond = " (getVariableValueByName('" + variable + "')" + upcase + noSpace + " " + operator + " \"" + value + "\")";
                        }
                    }
            }
            composedCondition = composedCondition + connector + cond;
        }
        
        if (attempt != "ALL") {
            composedCondition = "(" + composedCondition + ") && (new Number(getVariableValueByName(\"ATTEMPT\")) == " + attempt + " )";
        }
        composedCondition = composedCondition + ";";
        //alert(composedCondition);
    }
    else 
        if (keyword == "ELSE") {
            if (attempt != "ALL") {
                //composedCondition = composedCondition + "(true) && (Author.systemVariables.attempt == " + attempt + " );";
                composedCondition = composedCondition + "(true) && (new Number(getVariableValueByName(\"ATTEMPT\")) == " + attempt + " );";
            //alert(composedCondition);
            }
        }
    return composedCondition;
}

/**
 * Load author system variables and user variables.
 */
function LoadTheVariables(){
    Author.variables.clear();
    Author.variableNames.clear();
    
    // Load system variables.
    LoadVariable("ANSWER", "0000");
    LoadVariable("CORRECT", "0001");
    LoadVariable("INCORRECT", "0002");
    LoadVariable("SELECTEDVALUE", "0003");
    LoadVariable("SELECTEDNAME", "0004");
    LoadVariable("ATTEMPT", "0005");
    LoadVariable("OPSCORE", "0006");
    LoadVariable("OPTOTAL", "0007");
    LoadVariable("SCORE", "0008");
    LoadVariable("TOTAL", "0009");
    LoadVariable("%SCORE", "0010");
    LoadVariable("CORRECTANSWER", "0011");
    LoadVariable("QUESTIONANSWER", "0012");
    LoadVariable("RESPONSETIME", "0013");
    LoadVariable("DATE", "0014");
    LoadVariable("DAY", "0015");
    LoadVariable("MONTH", "0016");
    LoadVariable("YEAR", "0017");
    LoadVariable("TIME", "0018");
    LoadVariable("FIRSTNAME", "0019");
    LoadVariable("LASTNAME", "0020");
    LoadVariable("FULLNAME", "0021");
    LoadVariable("USERID", "0022");
    LoadVariable("LESSON", "0023");
    LoadVariable("COLOURS", "0024");
    LoadVariable("TIMEOUT", "0025");
    LoadVariable("RESOLUTION", "0026");
    LoadVariable("TIMER", "0027");
    LoadVariable("STARTTIME", "0028");
    LoadVariable("CHECKBOXES", "0029");
    LoadVariable("HYPERTEXT", "0030");
    LoadVariable("LEFTMOUSE", "0031");
    LoadVariable("RIGHTMOUSE", "0032");
    LoadVariable("ELAPSEDTIME", "0033");
    LoadVariable("RUNTOTAL", "0034");
    LoadVariable("RUNOPTOTAL", "0035");
    LoadVariable("RUN%SCORE", "0036");
    LoadVariable("ELEMENTNUMBER", "0037");
    LoadVariable("OPTIONTIME", "0038");
    LoadVariable("AICCSCORE", "0039");
    LoadVariable("AICCLSTATUS", "0040");
    LoadVariable("AICCLOCATION", "0041");
    LoadVariable("AICCCREDIT", "0042");
    LoadVariable("AICCTIME", "0043");
    LoadVariable("AICCDATA", "0044");
    LoadVariable("SCORM_STUDENT_ID", "0045");
    LoadVariable("SCORM_STUDENT_NAME", "0046");
    LoadVariable("SCORM_LESSON_LOCATION", "0047");
    LoadVariable("SCORM_CREDIT", "0048");
    LoadVariable("SCORM_LESSON_STATUS", "0049");
    LoadVariable("SCORM_ENTRY", "0050");
    LoadVariable("SCORM_SCORE_RAW", "0051");
    LoadVariable("SCORM_TOTAL_TIME", "0052");
    LoadVariable("SCORM_LESSON_MODE", "0053");
    LoadVariable("SCORM_EXIT_REASON", "0054");
    LoadVariable("SCORM_SESSION_TIME", "0055");
    LoadVariable("SCORM_COMMENTS", "0056");
    LoadVariable("SCORM_COMMENTS_FROM_LMS", "0057");
    LoadVariable("SCORM_MAX_TIME_ALLOWED", "0058");
    LoadVariable("SCORM_TIME_LIMIT_ACTION", "0059");
    LoadVariable("SCORM_LANGUAGE", "0060");
    LoadVariable("SCORM_SERVERPATH", "0061");
    LoadVariable("SCORM_EXECUTABLELESSON", "0062");
    LoadVariable("AUTHORLOCK", "0063");
    LoadVariable("LESSONSCORE", "0064");
    LoadVariable("LESSONTOTAL", "0065");
    LoadVariable("WEBINFO", "0067");
    LoadVariable("FLASHRESULT", "0068");
    LoadVariable("ELEMENTNAME", "9999");
    
    
    // Initialize the values of some system variables.
    Author.variables.get(variableName("LESSONSCORE")).value = "0";
    Author.variables.get(variableName("LESSONTOTAL")).value = "0";
    Author.variables.get(variableName("STARTTIME")).value = new Date();
    Author.variables.get(variableName("FULLNAME")).value = "Trainee STUDENT";
    Author.variables.get(variableName("FIRSTNAME")).value = "Trainee";
    Author.variables.get(variableName("LASTNAME")).value = "STUDENT";
    Author.variables.get(variableName("WEBINFO")).value = "";
    Author.variables.get(variableName("COLOURS")).value = Math.pow(2, 32);
    
    // Load user variables.
    loadUserVariables();
    LoadConstantVariables();
}

/**
 * Load the user variables from lessonXML.
 */
function loadUserVariables(){
    try {
        var userVariableXML = Author.lessonXMLDoc.getElementsByTagName("userVariables")[0];
        var userVariables = userVariableXML.getElementsByTagName("variable");
        var userVarCount = userVariables.length;
    } 
    catch (err) {
        // alert("LoadUserVariables : "+err.description);
        return;
    }
    for (var i = 0; i < userVarCount; i++) {
        try {
            LoadUserVariable(userVariables[i]);
        } 
        catch (err) {
            alert("LoadUserVariables : " + err.description);
        }
    }
}

function LoadConstantVariables(){

    try {
        var constantVariableXML = Author.lessonXMLDoc.getElementsByTagName("constants")[0];
        var constantVariables = constantVariableXML.getElementsByTagName("constantVariable");
        var varCount = constantVariables.length;
    } 
    catch (err) {
        //alert("LoadConstantVariables : "+err.description);
        return;
    }
    //alert(varCount);
    for (var i = 0; i < varCount; i++) {
    
        try {
            LoadConstantVariable(constantVariables[i]);
        } 
        catch (err) {
            alert("LoadConstantVariables : " + err.description);
        }
    }
}

/**
 * Get the variable object with variable name of name.
 *
 * @param {Object} name
 */
function variableName(name){
    return Author.variableNames.get(name);
}

/**
 * Get the value of a variable or an interaction action.
 *
 * @param {Object} name
 */
function getVariableValueByName(name){
    var value = name;
    var id = variableName(name.toUpperCase());
    if (typeof(id) == 'undefined') {
        value = getActionValueByName(name.toUpperCase());
    }
    else 
        value = getVariableValueByID(id);
    return (typeof(value) == 'undefined') ? ("[" + name + "]") : value;
}

/**
 * Get the interaction action value of the action with name of name.
 *
 * @param {Object} name
 */
function getActionValueByName(name){

    var actionCount = Author.current.currentList.getCount();
    for (var index = actionCount - 1; index >= 0; index--) {
        var action = Author.current.currentList.itemAt(index);
        try {
            switch (action.objectType) {
                case "CUSTOMBUTTONOBJECT":
                    if (action.content.dom.getAttribute("name").toUpperCase() == name.toUpperCase()) {
                    
                        if (action.content.dom.getAttribute("toggleBtn") == "true") {
                        
                            return action.content.dom.getAttribute("toggled") == "true" ? "True" : "False";
                        }
                        else {
                            return action.content.dom.getAttribute("value");
                        }
                    }
                    break;
                case "CHECKBOXOBJECT":
                    if (action.button.dom.getAttribute("name").toUpperCase() == name.toUpperCase()) {
                        //alert(action.button.dom.checked);
                        return (action.button.dom.checked.toString().toUpperCase() == "TRUE") ? "True" : "False";
                    }
                    break;
                case "RADIOBUTTONOBJECT":
                    if (action.button.dom.getAttribute("name").toUpperCase() == name.toUpperCase()) {
                        return (action.button.dom.checked.toString().toUpperCase() == "TRUE") ? "True" : "False";
                    }
                    break;
                case "COMBOBOXOBJECT":
                    if (action.combobox.name.toUpperCase() == name.toUpperCase()) {
                        return action.combobox.getRawValue();
                    }
                    break;
                case "TEXTFIELDOBJECT":
                    if (action.content.dom.getAttribute("name").toUpperCase() == name.toUpperCase()) {
                        var value = action.content.dom.value;
                        return (value == null) ? "" : value;
                    }
                    break;
                case "HOTSPOTOBJECT":
                    if (action.content.dom.getAttribute("name").toUpperCase() == name.toUpperCase()) {
                        var value = action.content.dom.getAttribute("value");
                        return (value == null) ? "" : value;
                    }
                    break;
            }
        } 
        catch (err) {
            alert("getActionValueByName::" + err.description);
        }
    }
}

function getVariableByID(id){
    if (!Author.variables.containsKey(id)) {
        return null;
    }
    else {
        return Author.variables.get(id);
    }
}

/**
 * Get the variable value by variable ID.
 *
 * @param {Object} id
 */
function getVariableValueByID(id){
    if (!Author.variables.containsKey(id)) {
        return id;
    }
    else {
        return Author.variables.get(id).getValue();
    }
}

/**
 * Initialize a system variable of the given name and id.
 *
 * @param {Object} name
 * @param {Object} id
 */
function LoadVariable(name, id){
    name = name.toUpperCase();
    var v = new Variable();
    v.init(name, id);
    if (parseInt(id) > 38) {
        v.variableType = 1;
    }
    if (v.name == "DATE") {
        v.variableType = 1;
    }
    if (v.name == "TIME") {
        v.variableType = 1;
    }
    if (v.name == "ANSWER") {
        v.variableType = 1;
    }
    Author.variables.add(id, v);
    Author.variableNames.add(name, id);
}

/**
 * Initialize a user variable by the configuration XML.
 *
 * @param {Object} cfgXML
 */
function LoadUserVariable(cfgXML){
    var v = new Variable();
    v.readConfig(cfgXML);
    Author.variables.add(v.id, v);
    Author.variableNames.add(v.name, v.id);
}

function LoadConstantVariable(cfgXML){
    var key = cfgXML.attributes[0].nodeValue.toUpperCase();
    var value = cfgXML.attributes[1].nodeValue;
    Author.contants.add(key, value);
    
}

/**
 * Check if the answer of a question element is correct.
 */
function checkAnswer(removeSpaces, wildCards){
    if (typeof(removeSpaces) == "undefined") {
        removeSpaces = false;
    }
    else {
        removeSpaces = eval(removeSpaces);
    }
    if (typeof(wildCards) == "undefined") {
        wildCards = false;
    }
    else {
        wildCards = eval(wildCards);
    }
    try {
        var elementType = Author.current.currentElementXML.attributes[0].nodeValue;
    } 
    catch (err) {
    
        return "--";
    }
    switch (elementType) {
        case "MULTIPLECHOICE":
            var correctAnswers = Author.current.currentElementXML.getElementsByTagName("correctAnswer");
            var count = correctAnswers.length;
            var correct = true;
            var tempMulti = Author.answers.multipleChoice.clone();
            for (var index = 0; index < count; index++) {
                var aAnswer = correctAnswers[index].childNodes[0].nodeValue.toUpperCase();
                // alert(aAnswer);
                if (Author.answers.multipleChoice.containsKey(aAnswer)) {
                    Author.answers.multipleChoice.removeKey(aAnswer);
                }
                else {
                    Author.answers.multipleChoice.clear();
                    correct = false;
                    break;
                }
            }
            if (Author.answers.multipleChoice.getCount() > 0) {
                correct = false;
            }
            Author.answers.multipleChoice = tempMulti.clone();
            return correct;
            break;
        case "TRUEFALSE":
            var correctAnswer = Author.current.currentElementXML.getElementsByTagName("correctAnswer")[0].childNodes[0].nodeValue.toLowerCase();
            return (Author.answers.trueFalse == correctAnswer)
            break;
        case "YESNO":
            var correctAnswer = Author.current.currentElementXML.getElementsByTagName("correctAnswer")[0].childNodes[0].nodeValue.toLowerCase();
            return (Author.answers.yesNo == correctAnswer)
            break;
        case "SHORTANSWER":
            var tempAnswer = Author.answers.shortAnswer;
            var correctAnswers = Author.current.currentElementXML.getElementsByTagName("correctAnswer");
            var count = correctAnswers.length;
            for (var index = 0; index < count; index++) {
                Author.answers.shortAnswer = trim(Author.answers.shortAnswer).toLowerCase();
                var aAnswer = correctAnswers[index].childNodes[0].nodeValue.toLowerCase();
                if (removeSpaces) {
                    Author.answers.shortAnswer = Author.answers.shortAnswer.replace(/\s/g, "");
                    aAnswer = trim(aAnswer).toString().replace(/\\s/g, '');
                }
                if (wildCards) {
                    aAnswer = aAnswer.replace(/\*/g, ".*");
                    aAnswer = aAnswer.replace(/\?/g, ".{1}");
                    aAnswer = "/" + aAnswer + "/";
                    // alert("\"" + Author.answers.shortAnswer + "\".search(" + aAnswer + ")>=0" + "\n" + eval("\"" + Author.answers.shortAnswer + "\".search(" + aAnswer + ")>=0"));
                    if (eval("\"" + Author.answers.shortAnswer + "\".search(" + aAnswer + ")>=0")) {
                        Author.answers.shortAnswer = tempAnswer;
                        return true;
                    }
                }
                else {
                    if (aAnswer == Author.answers.shortAnswer) {
                        Author.answers.shortAnswer = tempAnswer;
                        return true;
                    }
                }
                Author.answers.shortAnswer = tempAnswer;
                
            }
            
            return false;
            break;
    }
}

/**
 * Make the answer variable based on the user's answer.
 */
function makeAnswerVariable(){

    var startIndex = Author.interactionList.startIndex;
    var endIndex = Author.interactionList.endIndex;
    var theAnswer = "";
    // alert(startIndex+", "+endIndex);
    for (var index = startIndex; index <= endIndex; index++) {
        var action = Author.current.currentList.itemAt(index);
        try {
        
            if (Author.current.precedentObject == action.objectType && action.isPrecedent == true) {
                switch (action.objectType) {
                    case "HOTSPOTOBJECT":
                        theAnswer = theAnswer + action.content.dom.getAttribute("value") + ",";
                        break;
                    case "CHECKBOXOBJECT":
                        
                        if (action.button.dom.checked) {
                            theAnswer = theAnswer + action.button.dom.getAttribute("name") + ",";
                        }
                        break;
                    case "RADIOBUTTONOBJECT":
                        if (action.button.dom.checked) {
                            theAnswer = theAnswer + action.button.dom.getAttribute("name") + ",";
                        }
                        break;
                    case "COMBOBOXOBJECT":
                        theAnswer = theAnswer + action.combobox.getRawValue() + ",";
                        break;
                    case "TEXTFIELDOBJECT":
                        theAnswer = theAnswer + action.content.dom.value + ",";
                        break;
                    case "CUSTOMBUTTONOBJECT":
                        if (action.content.dom.getAttribute("toggleBtn") == "true" && action.content.dom.getAttribute("toggled") == "true") {
                            theAnswer = theAnswer + action.content.dom.getAttribute("value") + ",";
                        }
                        break;
                }
            }
        } 
        catch (err) {
            alert(action + ", " + index);
        }
    }
    if (theAnswer != "" && theAnswer != ",") {
        theAnswer = theAnswer.substr(0, theAnswer.length - 1);
    }
    else {
        theAnswer = "NEXT";
    }
    Author.variables.get(variableName('ANSWER')).setValue(theAnswer);
}


function evalResponseCondition(responseListXML){
    var condition = responseListXML.getElementsByTagName("condition")[0];
    var composedCondition = true;
    var conditionChunk = false;
    var keyword = condition.attributes[0].nodeValue;
    var attempt = condition.attributes[1].nodeValue;
    var filter = condition.attributes[2].nodeValue;
    var upperCase = (filter.charAt(0) == "C");
    var wildCards = (filter.charAt(1) == "W");
    var removeSpaces = (filter.charAt(2) == "S");
    var numeric = (filter.charAt(3) == "N");
    if (keyword == "IF") {
        var bodys = condition.getElementsByTagName("body");
        var count = bodys.length;
        for (var index = 0; index < count; index++) {
            var connector = bodys[index].attributes[0].nodeValue;
            var variable = bodys[index].attributes[1].nodeValue;
            var operator = bodys[index].attributes[2].nodeValue;
            switch (connector) {
                case "AND":
                    connector = "&&";
                    break;
                case "OR":
                    connector = "||";
                    break;
            }
            switch (operator) {
                case "":
                    operator = "==";
                    break;
                case "<>":
                    operator = "!=";
                    break;
                case "{}":
                    operator = "!!";
                    break;
            }
            
            var value = bodys[index].attributes[3].nodeValue;
            var upcase = "";
            // if (upperCase) {
            value = value.toUpperCase();
            upcase = ".toString().toUpperCase()";
            //}
            var noSpace = "";
            if (removeSpaces) {
                value = value.replace(/\s/g, "");
                noSpace = ".toString().replace(/\\s/g,'')";
            }
            
            var cond = "";
            if (wildCards) {
                value = value.replace(/\*/g, ".*");
                value = value.replace(/\?/g, ".{1}");
                value = "/" + value + "/";
                if (variable == 'CORRECT') {
                    conditionChunk = eval("(checkAnswer(" + removeSpaces + "," + wildCards + "))");
                }
                else 
                    if (variable == 'INCORRECT') {
                        conditionChunk = ("!(checkAnswer(" + removeSpaces + "," + wildCards + "))");
                    }
                    else {
                        conditionChunk = eval("(getVariableValueByName('" + variable + "')" + upcase + noSpace + ".search(" + value + ")>=0)");
                    }
            }
            else {
                if (variable == 'CORRECT') {
                    conditionChunk = eval("(checkAnswer(" + removeSpaces + "," + wildCards + "))");
                }
                else 
                    if (variable == 'INCORRECT') {
                        conditionChunk = eval("!(checkAnswer(" + removeSpaces + "," + wildCards + "))");
                    }
                    else {
                    
                        if (operator == "!!") {
                            conditionChunk = eval(" (getVariableValueByName('" + variable + "')" + upcase + noSpace + ".indexOf(\"" + value + "\") >= 0)");
                            
                        }
                        else {
                            if (numeric) {
                                try {
                                    conditionChunk = eval(" eval((getVariableValueByName('" + variable + "')) " + operator + " " + value + ")");
                                } 
                                catch (err) {
                                    conditionChunk = eval(" (getVariableValueByName('" + variable + "')" + upcase + noSpace + " " + operator + " \"" + value + "\")");
                                }
                            }
                            else 
                                conditionChunk = eval(" (getVariableValueByName('" + variable + "')" + upcase + noSpace + " " + operator + " \"" + value + "\")");
                        }
                    }
            }
            composedCondition = eval(composedCondition + connector + conditionChunk);
        }
        
        if (attempt != "ALL") {
            composedCondition = eval("(" + composedCondition + ") && (new Number(getVariableValueByName(\"ATTEMPT\")) == " + attempt + " )");
        }
        // alert(composedCondition);
    }
    else 
        if (keyword == "ELSE") {
            if (attempt != "ALL") {
                //composedCondition = composedCondition + "(true) && (Author.systemVariables.attempt == " + attempt + " );";
                //alert(composedCondition);
                composedCondition = eval(composedCondition + " && (new Number(getVariableValueByName(\"ATTEMPT\")) == " + attempt + " );");
            // alert(composedCondition);
            }
        }
    return composedCondition;
}

/**
 * Check the response and load the response list with true condition.
 */
function checkResponse(){
    if (Author.interactionTimer != null) {
        window.clearTimeout(Author.interactionTimer);
    }
    var attempts = new Number(getVariableValueByName("ATTEMPT"));
    Author.variables.get(variableName('ATTEMPT')).setValue(++attempts);
    var conditionsCount = Author.current.currentElementResponses.getCount();
    var hasValidResponse = false;
    //alert(conditionsCount);
    for (var index = 0; index < conditionsCount; index++) {
        var responseObject = Author.current.currentElementResponses.itemAt(index);
        var con = false;
        if (responseObject.condition == "") 
            con = true;
        else {
            try {
                // con = eval(responseObject.condition);
                con = evalResponseCondition(responseObject.responseListXML);
                // alert(responseObject.condition + " : " + con + ", answer:" + getVariableValueByName("ANSWER")+", attempt:"+new Number(getVariableValueByName("ATTEMPT")));
            } 
            catch (err) {
                // alert("Error in CheckResponse:\n" + responseObject.condition + "\n==========================\n" + err.name + " : " + err.description + "\n==========================\n" + responseObject.responseListXML.xml);
                con = false;
            }
        }
        if (con) {
            if (Author.current.currentNode.type != "MENU") {
                Author.current.currentElement.scoreEarned = responseObject.score;
            }
            responseHandler(responseObject.responseListXML);
            hasValidResponse = true;
            break;
        }
    }
    if (hasValidResponse) 
        return;
    else {
        //alert("no Valid Response");
        showLessonNode();
        
    }
}

/**
 * Show a given response list.
 *
 * @param {Object} responseListXML
 */
function responseHandler(responseListXML){
    Author.responseEnd = false;
    Author.inResponse = true;
    // Author.responseStart = Author.current.currentList.getCount();
    perpareAndShowList(responseListXML, false);
}

function showLessonEndList(listXML){
    Author.clearBackgroundObjects = true;
    Author.doingLessonEnding = true;
    Author.current.currentListXML = listXML;
    clearScreen();
    Author.current.currentActionPosition = 0;
    
    showList();
}

/**
 * Function that shows all the actions in a list.
 *
 * @param {Object} listXML
 * @param {boolean} cls If True clear the screen before showing the list.
 */
function perpareAndShowList(listXML, cls){
    var listType = listXML.attributes[0].nodeValue;
    Author.current.currentListXML = listXML;
    if (listType == "INTRODUCTION") {
        Author.introductionList.startIndex = Author.current.currentList.getCount();
    }
    if (listType == "INFORMATION") {
        Author.informationList.startIndex = Author.current.currentList.getCount();
    }
    if (listType == "INTERACTION") {
        Author.interactionList.startIndex = Author.current.currentList.getCount();
        Author.current.precedentObject = "none";
    }
    if (listType == "ENDLIST") {
        Author.endList.startIndex = Author.current.currentList.getCount();
    }
    if (listType == "RESPONSE") {
    
        if (Author.response.removeType == "BEFORENEXT") {
            // alert("BeforeShowList:"+ Author.response.endResponse+", "+ Author.response.startResponse);
            for (var index = Author.response.endResponse; index >= Author.response.startResponse; index--) {
                var obj = Author.current.currentList.removeAt(index);
                if (obj != false) 
                    obj.remove();
            }
            Author.response.clear();
        }
        var lessonFlow = trim(listXML.getElementsByTagName("lessonFlow")[0].childNodes[0].nodeValue);
        Author.response.startResponse = Author.current.currentList.getCount();
        if (lessonFlow == "repeat") {
            var removeType = trim(listXML.getElementsByTagName("remove")[0].childNodes[0].nodeValue);
            Author.response.removeType = removeType;
            //  Author.response.startResponse = Author.current.currentList.getCount();
        }
        // alert(Author.response.startResponse+", "+Author.response.endResponse);
    
    }
    Author.current.currentActionPosition = 0;
    if (cls) {
        clearScreen();
        Author.foregroundList.show();
    }
    Author.backgroundList.updateProgressBar();
    
    
    showList();
}

function setInteractionTimeout(timeoutValue){
    if (Author.inResponse) 
        return;
    Author.interactionTimer = setTimeout(function(){
        //alert("timeout");
        Author.variables.get(variableName('SELECTEDVALUE')).setValue("timeout");
        Author.variables.get(variableName('SELECTEDNAME')).setValue("TIMEOUT");
        Author.variables.get(variableName('TIMEOUT')).setValue("TRUE");
        checkResponse();
        return;
    }, timeoutValue * 1000);
    
}

function stopCurrentResponse(){
    var listType = Author.current.currentListXML.attributes[0].nodeValue;
    var actions = Author.current.currentListXML.getElementsByTagName("action");
    var actionCount = actions.length;
    //alert("stop");
    if (listType == "RESPONSE") {
        clearTimeout(Author.actionTimer);
        Author.current.currentActionPosition = actionCount;
        Author.response.endResponse = Author.current.currentList.getCount() - 1;
        Author.response.removeType = "BEFORENEXT";
        Author.responseEnd = true;
        Author.inResponse = false;
    }
}

/**
 * Show the actions in Author.current.currentListXML one by one.
 */
function showList(){
    Author.pause = null;
    var listType = Author.current.currentListXML.attributes[0].nodeValue;
    var actions = Author.current.currentListXML.getElementsByTagName("action");
    var actionCount = actions.length;
    var interval = 0;
    Author.enableNextBtn = false;
    /*
     if (Author.infoPanelVisible) {
     Author.infoPanelContent.body.dom.innerHTML = getLessonInfo();
     }
     */
    // If have actions remaining, call showList itself to show the action.
    while (Author.current.currentActionPosition < actionCount) {
        try {
            interval = doAction();
        } 
        catch (err) {
            interval = 0;
            alert("Action: " + Author.current.currentListXML.getElementsByTagName("action")[Author.current.currentActionPosition - 1].attributes[0].nodeValue + " is not processed correctly.\nError description:" + err.description);
        }
        if (interval > 0) {
            Author.actionTimer = setTimeout(showList, interval);
            return;
        }
        if (interval == -2) {
            return;
        }
    };
    //	alert(Author.current.currentNode.type);
    
    // When list finished, come here.
    
    if (listType == "INFORMATION") {
        if (!Author.isSynch && Author.current.currentElementType == "INFORMATION") {
            var lastObj = Author.current.currentList.last();
            if (!(actionCount == 1 && lastObj.objectType == "SPECIAL" && lastObj.specialType == 13)) {
                Author.lessonFlow = "continue";
                Author.enableNextBtn = true;
                Author.current.currentElement.anyClick = true;
                Author.current.currentElement.anyKey = true;
                return;
            }
        }
    }
    if (listType == "INTRODUCTION") {
        Author.introductionList.endIndex = Author.current.currentList.getCount() - 1;
    }
    
    if (listType == "INTERACTION") {
        //Author.interactionList.endIndex = Author.interactionList.startIndex + actionCount - 1;
        Author.interactionList.endIndex = Author.current.currentList.getCount() - 1;
        Author.lessonFlow = "continue";
        
        Author.enableNextBtn = true;
        Author.variables.get(variableName('TIMEOUT')).setValue("FALSE");
        if (Author.current.currentElement.timeout) {
            setInteractionTimeout(Author.current.currentElement.timeoutValue);
        }
        return;
    }
    if (listType == "ENDLIST") {
        Author.endList.endIndex = Author.current.currentList.getCount() - 1;
        if (Author.doingLessonEnding) {
            Author.doingLessonEnding = false;
            endLesson(actionCount == 0);
        }
    }
    if (listType == "RESPONSE") {
    
        Author.response.endResponse = Author.current.currentList.getCount() - 1;
        
        if (Author.current.currentNode.type == "MENU") {
            //	alert(Author.response.startResponse+", "+Author.response.endResponse);
            //  Author.response.removeType = "BEFORENEXT";
            Author.response.removeType = "BEFORENEXT";
            Author.responseEnd = true;
            Author.inResponse = false;
            /*
             for (var index = Author.response.endResponse; index >= Author.response.startResponse; index--) {
             var obj = Author.current.currentList.itemAt(index);
             if (obj.addToBackground && (Author.current.currentElementPosition >= obj.addToBackgroundAtElementPosition) && !Author.clearBackgroundObjects) {
             continue;
             }
             obj.remove();
             var obj = Author.current.currentList.removeAt(index);
             }
             Author.response.clear();
             */
            return;
        }
        try {
            var countAttempt = trim(Author.current.currentListXML.getElementsByTagName("countAttempt")[0].childNodes[0].nodeValue).toLowerCase() == "true";
            if (!countAttempt) {
                var attempts = new Number(getVariableValueByName("ATTEMPT"));
                Author.variables.get(variableName('ATTEMPT')).setValue(--attempts);
                // alert("CountAttempt: "+ countAttempt + "\n Attempts: "+ attempts);
            }
        } 
        catch (err) {
        }
        var lessonFlow = trim(Author.current.currentListXML.getElementsByTagName("lessonFlow")[0].childNodes[0].nodeValue);
        Author.lessonFlow = lessonFlow;
        if (lessonFlow == "continue") {
            Author.responseEnd = true;
            Author.inResponse = false;
            Author.response.clear();
            showLessonNode();
            return;
        }
        
        if (Author.response.removeType == "REMOVE") {
        
            for (var index = Author.response.endResponse; index >= Author.response.startResponse; index--) {
                var obj = Author.current.currentList.itemAt(index);
                if (obj.addToBackground && (Author.current.currentElementPosition >= obj.addToBackgroundAtElementPosition) && !Author.clearBackgroundObjects) {
                    continue;
                }
                obj.remove();
                var obj = Author.current.currentList.removeAt(index);
            }
            Author.response.clear();
        }
        else 
            if (Author.response.removeType == "BEFORENEXT") {
            
            
            }
        for (var index = 0; index < Math.min(Author.responseStart, Author.current.currentList.getCount()); index++) {
            action = Author.current.currentList.itemAt(index);
            switch (action.objectType) {
                case "CHECKBOXOBJECT":
                    action.button.dom.checked = false;
                    break;
                case "RADIOBUTTONOBJECT":
                    action.button.dom.checked = false;
                    break;
                case "TEXTFIELDOBJECT":
                    break;
            }
        }
        Author.answers.multipleChoice.clear();
        Author.answers.trueFalse = null;
        Author.answers.yesNo = null;
        Author.answers.shortAnswer = "";
        Author.variables.get(variableName('ANSWER')).setValue("");
        Author.responseEnd = true;
        Author.inResponse = false;
        Author.lessonFlow = "continue";
        var repeat = trim(Author.current.currentListXML.getElementsByTagName("repeat")[0].childNodes[0].nodeValue);
        var flag = repeat.substring(0, 4);
        
        switch (flag) {
            case "NONE":
                break;
            case "INTE":
                var resetInteraction = (trim(Author.current.currentListXML.getElementsByTagName("resetInteraction")[0].childNodes[0].nodeValue).toLowerCase() == "true");
                if (resetInteraction) {
                    var actionCount = 0;
                    //	alert(Author.interactionList.endIndex+" vs "+(Author.current.currentList.getCount() - (Author.response.endResponse - Author.response.startResponse + 1) - 1))
                    // for (var index = Author.interactionList.endIndex; index >= Author.interactionList.startIndex; index--) {
                    for (var index = Author.current.currentList.getCount() - (Author.response.endResponse - Author.response.startResponse + 1) - 1; index >= Author.interactionList.startIndex; index--) {
                        var obj = Author.current.currentList.removeAt(index);
                        obj.remove();
                        actionCount++;
                    }
                    Author.response.endResponse -= actionCount;
                    Author.response.startResponse -= actionCount;
                    Author.interactionList.startIndex = Author.current.currentList.getCount() - 1;
                    Author.current.precedentObject = "none";
                //alert("reseted Interaction");
                }
                var lists = Author.current.currentElementXML.getElementsByTagName("list");
                var listCount = lists.length;
                for (var index = 0; index < listCount; index++) {
                    var type = lists[index].attributes[0].nodeValue;
                    if (type == "INTERACTION") {
                        if (resetInteraction) 
                            Author.current.currentActionPosition = 0;
                        else 
                            Author.current.currentActionPosition = lists[index].getElementsByTagName("action").length;
                        Author.current.currentListXML = lists[index];
                        Author.current.currentListPosition = index + 1;
                        
                        showList();
                    }
                }
                break;
            case "ACTI":
				for (var index = Author.current.currentList.getCount() - (Author.response.endResponse - Author.response.startResponse + 1) - 1; index >= Author.informationList.startIndex; index--) {
                        var obj = Author.current.currentList.removeAt(index);
                        obj.remove();
                        actionCount++;
                    }
                    Author.response.endResponse -= actionCount;
                    Author.response.startResponse -= actionCount;
                //for (var index = Author.current.currentList.getCount() - 1; index >= Author.informationList.startIndex; index--) {
                 //   var obj = Author.current.currentList.removeAt(index);
                 //   obj.remove();
               // }
                var lists = Author.current.currentElementXML.getElementsByTagName("list");
                var listCount = lists.length;
                for (var index = 0; index < listCount; index++) {
                    var type = lists[index].attributes[0].nodeValue;
                    if (type == "INFORMATION") {
                        Author.current.currentActionPosition = 0;
                        Author.current.currentListXML = lists[index];
                        Author.current.currentListPosition = index + 1;
                        showList();
                    }
                }
                break;
            case "DIVI":
                var dividerObj = Author.dividers.get(trim(repeat.substr(8)));
                // alert("repeat from divider: " + dividerObj.id + "\n" + dividerObj.currentListPosition);
                //alert("ListTotal: " + Author.current.currentList.getCount());
                 for (var index = Author.current.currentList.getCount() - (Author.response.endResponse - Author.response.startResponse + 1) - 1; index >= dividerObj.actionPosition; index--) {
                        var obj = Author.current.currentList.removeAt(index);
                        obj.remove();
                        actionCount++;
                    }
                    Author.response.endResponse -= actionCount;
                    Author.response.startResponse -= actionCount;
               // for (var index = Author.current.currentList.getCount() - 1; index >= dividerObj.currentListPosition; index--) {
                    // var obj = Author.current.currentList.itemAt(index);
                    //alert(obj.id + " : " + obj.objectType + " , " + Author.current.currentList.getCount());
                  //  var obj = Author.current.currentList.removeAt(index);
                   // obj.remove();
               // }
                // alert("ListTotal: " + Author.current.currentList.getCount());
                Author.current.currentListPosition = dividerObj.listPosition;
                Author.current.currentActionPosition = dividerObj.actionPosition;
                
                Author.current.currentListXML = Author.current.currentElement.xml.getElementsByTagName("list")[Author.current.currentListPosition++];
                showList();
                break;
        }
        return;
    }
    if (interval != -2) 
        showLessonNode();
}

/**
 * Clear the screen.
 */
function clearScreen(){
    Author.mediaList.eachKey(function(key, object){
        if (typeof(key) == 'undefined' || typeof(object) == 'undefined') {
        }
        else {
            if (Ext.isIE && object.objectType != "FLVVideo" && !Author.isHTML5) {
                object.mediaObject.dom.controls.stop();
                object.mediaObject.dom.settings.mute = true;
            }
            if (Author.isHTML5) {
                try {
                    var theAudioObject = document.getElementById(object.mediaID);
                    
                    theAudioObject.stop();
                } 
                catch (err) {
                
                }
            }
            object.remove();
        }
    });
	Author.currentSoundID="";
    //Author.mediaList.clear();
    var index = Author.current.currentList.getCount() - 1;
    for (; index >= 0; index--) {
        var el = Author.current.currentList.itemAt(index);
        
        if (el.objectType == "PROGRESSBAR") {
            if (Author.clearBackgroundObjects) {
                el.setVisible(el.hide == "false");
            }
            continue;
        }
		
		
        if (el.addToBackground && (Author.current.currentElementPosition >= el.addToBackgroundAtElementPosition) && !Author.clearBackgroundObjects ) {
         //  alert(Author.current.currentElementPosition +","+ el.addToBackgroundAtElementPosition);
		   if ( (Author.current.currentElementPosition < el.addElementPosition)){
		   	  el.setVisible(false);
		   }
		    continue;
        }
        
        if (el.addToForeground) {
            var keepTheButton;
            keepTheButton = (Author.current.currentElementPosition >= el.addToForegroundAtElementPosition);
            keepTheButton = keepTheButton || (el.objectType == "CUSTOMBUTTONOBJECT" || el.objectType == "HOTSPOTOBJECT" || el.objectType == "IMAGEBUTTONOBJECT") && (el.buttonType == "1" || el.buttonType == "2");
            // 	alert(el.id +": "+keepTheButton+","+Author.clearBackgroundObjects);
            if (!Author.clearBackgroundObjects || keepTheButton) {
                // alert(el.buttonType);
                //	el.setVisible((Author.current.currentElementPosition >= el.addToForegroundAtElementPosition)||el.hide == "false");
                continue;
            }
            //alert(el.id);
        }
        el.remove();
        Author.current.currentList.removeAt(index);
    }
    Author.clearBackgroundObjects = false;
    Author.variables.get(variableName('ATTEMPT')).setValue(0);
}

/**
 * Display an action, return the interval before showing the next action.
 */
function doAction(){
    var action = Author.current.currentListXML.getElementsByTagName("action")[Author.current.currentActionPosition++];
    var actionType = action.attributes[0].nodeValue;
    Author.current.actionType = actionType;
    
    var listType = Author.current.currentListXML.attributes[0].nodeValue;
    if (!parserAndEvalActionCondition(action, false)) 
        return 0;
    // Author.backgroundObject.setSelectionEnable(false);
    var theActionID = trim(action.getElementsByTagName("id")[0].childNodes[0].nodeValue);
    if (Author.current.currentList.containsKey(theActionID)) {
        Author.current.currentList.get(theActionID).remove();
    }
    
    if (actionType == "PROGRESSBAR") {
        return 0;
    }
    if (actionType == "SPECIAL") {
        var actionObject = new SpecialClass(action);
        if (actionObject.specialType == 11 || actionObject.specialType == 0) 
            return -2;
    }
    if (actionType == "BACKGROUND") {
        return Author.backgroundObject.readConfig(action);
    }
    if (actionType == "CHANGEBACKGROUND") {
        var interval = Author.backgroundObject.change(action);
        var removeActions = true;
        try {
            removeActions = (cfgXML.attributes[1].nodeValue).toLowerCase() == "true";
        } 
        catch (err) {
        }
        // if (removeActions) 
        // Author.foregroundList.show();
        return interval;
    }
    if (actionType == "MEDIAACTION") {
        var actionObject = new MediaClass(Author.backgroundObject.getContent(), action);
    }
    
    if (actionType == "FLVPLAYERACTION") {
        //return 0;
        var actionObject = new FLVVideoClass(Author.backgroundObject.getContent(), action);
        //Author.current.currentList.add(actionObject.id, actionObject);
    
    }
    if (actionType == "SOUNDACTION") {
        //return 0;
        if (!Author.isAudioOn) 
            return 0;
        var actionObject = new SoundClass(Author.backgroundObject.getContent(), action);
        // If browser is not IE, no need to call readConfig() function.
        if (!Author.isHTML5) {
            if (!Ext.isIE) {
                Author.current.currentList.add(actionObject.id, actionObject);
                return 3600 * 24 * 1000;
            }
        }
    }
    if (actionType == "PAUSE") {
        //alert(Author.isSynch);
        if (!Author.isSynch) 
            return 0;
        var actionObject = new PauseClass();
    }
    if (actionType == "VARIABLE") {
        var actionObject = new VariableClass();
    }
    if (actionType == "VISIBLEACTION") {
        var actionObject = new VisibleClass();
    }
    if (actionType == "WEBACTION") {
        var actionObject = new WebElementClass(Author.backgroundObject.getContent());
        if (listType == "RESPONSE") 
            actionObject.toFront();
    }
    
    if (actionType == "DRAWINGACTION") {
        var actionObject = new DrawingClass(Author.backgroundObject.getContent(), action);
        if (listType == "RESPONSE") 
            actionObject.toFront();
    }
    if (actionType == "FLASHACTION") {
        var actionObject = new FlashClass(Author.backgroundObject.getContent(), action);
        if (listType == "RESPONSE") 
            actionObject.toFront();
    }
    if (actionType == "TXTEXTACTION") {
        var actionObject = new TXTextClass(Author.backgroundObject.getContent(), action);
        if (listType == "RESPONSE") 
            actionObject.toFront();
    }
    if (actionType == "TEXTACTION") {
        var actionObject = new TextClass(Author.backgroundObject.getContent(), action);
        if (listType == "RESPONSE") 
            actionObject.toFront();
    }
    if (actionType == "IMAGEACTION") {
        var actionObject = new ImageClass(Author.backgroundObject.getContent(), action);
        if (listType == "RESPONSE") 
            actionObject.toFront();
    }
    if (actionType == "DIVIDER") {
        var actionObject = new DividerClass();
    }
    if (actionType == "CHANGEACTION") {
        var actionObject = new ChangeAction();
    }
    if (actionType == "CUSTOMBUTTON") {
        var actionObject = new CustomButtonClass(Author.backgroundObject.getContent(), action);
    }
    if (actionType == "IMAGEBUTTON") {
        var actionObject = new ImageButtonClass(Author.backgroundObject.getContent(), action);
    }
    if (actionType == "DDBUTTON") {
        var actionObject = new DDButtonClass(Author.backgroundObject.getContent(), action);
    }
    if (actionType == "TEXTFIELD") {
        var actionObject = new TextFieldClass(Author.backgroundObject.getContent(), action);
        //	Author.backgroundObject.setSelectionEnable(true);
    }
    if (actionType == "RADIOBUTTON") {
        var actionObject = new RadioButtonClass(Author.backgroundObject.getContent(), action);
    }
    if (actionType == "COMBOBOX") {
        var actionObject = new ComboboxClass(Author.backgroundObject.getContent(), action);
    }
    if (actionType == "CHECKBOX") {
        var actionObject = new CheckboxClass(Author.backgroundObject.getContent(), action);
    }
    if (actionType == "HOTSPOT") {
        var actionObject = new HotSpotClass(Author.backgroundObject.getContent(), action);
    }
    
    actionObject.addToBackground = false;
    actionObject.addToForeground = false;
    var interval = actionObject.readConfig(action);
    
    if (actionType == "CHANGEACTION") {
        Author.current.currentList.add(actionObject.id, actionObject);
        return interval;
    }
    
    Author.current.currentList.add(actionObject.id, actionObject);
    if (typeof(Author.variableWatch) != 'undefined') {
        Author.variableWatch.refresh();
    }
    
    return interval;
}

/**
 * Trim the white spaces at the begining and the end of a given string.
 *
 * @param {String} str
 */
function trim(str){
    return str.replace(/(^\s*)|(\s*$)/g, "");
}

function validInput(maskChar, pressedChar){
    switch (maskChar) {
        case "#":
            var pattern = "^\\d$";
            break;
        case "?":
            var pattern = "^\\w$";
            break;
        case "&":
            var pattern = "^.$";
            break;
        case "A":
            var pattern = "^[\\w\\d]$";
            break;
    }
    var re = new RegExp(pattern);
    return re.test(pressedChar);
}

function isMaskChar(maskChar){
    var masks = "#&?A";
    return (masks.indexOf(maskChar) >= 0);
}

function getFilledMask(maskStr, text, prompt){
    if (maskStr.length == 0) 
        return "";
    var n = 0
    var textIndex = 0;
    var newResult = "";
    while (n < maskStr.length) {
        var curChar = maskStr.charAt(n);
        if (curChar == "\\") {
            newResult += maskStr.charAt(n + 1);
            n++;
        }
        else {
            if (isMaskChar(curChar)) {
                if (textIndex < text.length) {
                    newResult += text.charAt(textIndex);
                    textIndex++;
                }
                else {
                    newResult += prompt;
                }
            }
            else {
                newResult += curChar;
            }
        }
        n++;
    }
    return newResult;
}

function getNextMaskChar(maskStr, curText){
    var n = 0
    var count = 0;
    while (n < maskStr.length) {
        var curChar = maskStr.charAt(n);
        if (curChar == "\\") {
            n++;
        }
        else {
            if (isMaskChar(curChar)) {
                count++;
                if (count > curText.length) {
                    return curChar;
                }
            }
        }
        n++;
    }
}

function getPromptCount(maskStr){
    var n = 0
    var count = 0;
    while (n < maskStr.length) {
        var curChar = maskStr.charAt(n);
        if (curChar == "\\") {
            n++;
        }
        else {
            if (isMaskChar(curChar)) {
                count++;
            }
        }
        n++;
    }
    return count;
}

function setInputCurPos(target, maskStr, realText){
    var n = 0
    var count = 0;
    var textPos = 0;
    var pos = -1;
    while (n < maskStr.length && pos < 0) {
        var curChar = maskStr.charAt(n);
        if (curChar == "\\") {
            n++;
        }
        else {
            if (isMaskChar(curChar)) {
                count++;
                if (count > realText.length) {
                    pos = textPos;
                }
            }
        }
        textPos++;
        n++;
    }
    if (pos < 0) 
        pos = maskStr.length;
    
    if (target.dom.setSelectionRange) {
        target.dom.focus();
        target.dom.setSelectionRange(pos, pos);
    }
    else 
        if (target.dom.createTextRange) {
            var r = target.dom.createTextRange();
            r.moveStart('character', pos);
            r.collapse(true);
            r.select();
        }
}

/**
 * Event handler of the browser keyPress event.
 *
 * @param {Object} e
 */
function keyPressedHandle(e, fnKeys){
	try {
        if (Author.login.promptWindow.isVisible()) {
			
			return;
		}
    } 
    catch (err) {
    }
    if (typeof(fnKeys) == 'undefined' || fnKeys !== true) {
        fnKeys = false;
    }
    if (Author.pressedKeyCode > 111 && Author.pressedKeyCode < 124) {
        fnKeys = true;
    }
    Author.pressedKeyCode = 0;
    try {
        if (Author.bugTracker.isVisible()) 
            return;
    } 
    catch (err) {
    }
	if (Author.titleImage.timer != null){
		window.clearTimeout(Author.titleImage.timer);
		Author.titleImage.timer = null;
		continueInit2();
		return;
	}
	
	
   
    if (Author.doingInteraction) {
    
        var target = Ext.get(e.getTarget());
        if (target.dom.getAttribute("objectType") == "TEXTFIELDOBJECT") {
        
            var validCharStr = (target.dom.getAttribute("validationChars"));
            var maxLength = (target.dom.getAttribute("maxLength"));
            
            ////Mask
            
            var maskStr = target.dom.getAttribute("maskStr");
            var prompt = target.dom.getAttribute("prompt");
            
            if (e.getCharCode() == 46) {
                e.stopEvent();
                return;
            }
            if (e.getCharCode() == 8) {
                if (maskStr.length > 0) {
                    e.preventDefault();
                    var realtext = target.dom.getAttribute("realValue");
                    if (realtext.length > 1) {
                        realtext = realtext.slice(0, realtext.length - 1);
                        target.dom.setAttribute("realValue", realtext);
                        target.dom.value = getFilledMask(maskStr, realtext, prompt);
                    }
                    else {
                        target.dom.setAttribute("realValue", "");
                        realtext = "";
                        target.dom.value = getFilledMask(maskStr, "", prompt);
                    }
                    setInputCurPos(target, maskStr, realtext);
                    e.stopEvent();
                    return;
                }
                else {
                    var curtext = target.dom.value.toString()
                    if (curtext.length > 1) {
                        target.dom.value = curtext.slice(0, curtext.length - 1);
                    }
                    else {
                        target.dom.value = "";
                    }
                }
                e.stopEvent();
                return;
            }
            
            if (maskStr.length > 0) {
                var curValue = target.dom.getAttribute("realValue");
                if (curValue.length < getPromptCount(maskStr)) {
                
                    var pressedChar = String.fromCharCode(e.getCharCode());
                    var maskChar = getNextMaskChar(maskStr, curValue);
                    
                    if (!validInput(maskChar, pressedChar)) {
                        pressedChar = "";
                        e.stopEvent();
                        return;
                    }
                    
                    var realValue = curValue + pressedChar;
                    target.dom.setAttribute("realValue", realValue);
                    target.dom.value = getFilledMask(maskStr, realValue, prompt);
                    setInputCurPos(target, maskStr, realValue);
                }
                
                e.stopEvent();
                return;
            }
            
            ////Mask
			
			
            if (validCharStr != "") {
                var regex = eval("/[" + validCharStr + "]/gi");
                var keyPressed = String.fromCharCode(e.getCharCode());
                if (keyPressed.search(regex) < 0) {
                    e.stopEvent();
                }
            }
            if (maxLength != -1) {
                if (target.dom.value.toString().length == maxLength) {
                    e.stopEvent();
                }
            }
            if (e.getCharCode() == 13) {
                try {
                    var elementType = Author.current.currentElementXML.attributes[0].nodeValue;
                    if (elementType == "SHORTANSWER") {
                        Author.answers.shortAnswer = target.dom.value.toLowerCase();
                    }
                } 
                catch (err) {
                }
                Author.doingInteraction = false;
                makeAnswerVariable();
                checkResponse();
                //  target.dom.value = "";
                Author.backgroundObject.getContent().focus();
                return;
            }
        }
        return;
    }
    var target = Ext.get(e.getTarget());
    if (target.dom.getAttribute("objectType") == "FlashObject") {
        return;
    }
    e.stopEvent();
    //alert(e.getCharCode());
    var keyCode = String.fromCharCode(e.getCharCode()).toUpperCase().charCodeAt(0);
    var keyShift = (e.shiftKey ? 1 : 0) + (e.ctrlKey ? 2 : 0) + (e.altKey ? 4 : 0);
    
    if (keyShift == 0) 
        keyShift = 8;
    
    if (fnKeys && keyCode > 79 && keyCode < 91) {
        keyCode += 32;
    }
    var keyCodeStr = keyCode + "," + keyShift;
    try {
        e.browserEvent.keyCode = 0;
    } 
    catch (err) {
        // alert(keyCodeStr);
    }
    if (keyCodeStr == (Author.qcKey + ",8") && (Author.qcURL != "")) {
    
        var eName = Author.current.currentNode.name;
        if (Author.current.currentNode.type != "MENU" && Author.current.currentElement != null) {
            eName = Author.current.currentElement.name;
        }
        if (Author.popupQC) {
            window.open('' + Author.qcURL + "?eName=" + eName, 'winqc');
        }
        else {
            window.clearTimeout(Author.actionTimer);
            
            var index = Author.current.currentList.getCount() - 1;
            for (; index >= 0; index--) {
                var el = Author.current.currentList.itemAt(index);
                if (el.objectType == "MEDIAOBJECT") {
                    el.hideByQC = false;
                    if (el.visible) {
                        el.setVisible(false);
                        el.hideByQC = true;
                    }
                }
            }
            Author.bugTracker.show();
            
            Author.bugTrackerFrame.dom.src = Author.qcURL + "?eName=" + eName;
            
        }
        
        /*
         Author.infoPanelVisible = !Author.infoPanelVisible;
         Author.infoPanelContent.setVisible(Author.infoPanelVisible);
         if (Author.infoPanelVisible) {
         Author.infoPanelContent.body.dom.innerHTML = getLessonInfo();
         }
         */
        return;
    }
    if (keyCodeStr == "123,1" && Author.Testing) {
        showLessonStructure();
    }
    if (keyCodeStr == "122,1" && Author.Testing) {
        showVariableMonitor();
        Author.variableWatch.refresh();
    }
    var object = Author.actionKeyMap.item(keyCodeStr);
    
    if (typeof object != 'undefined' && object.visible) {
        fireEvent(object.content.dom, "click");
        return;
    }
    
    if (Author.pause != null) {
        if ((Author.pause.cancelType == 1 || Author.pause.cancelType == 2) || ((Author.pause.cancelType == 4) && (keyCodeStr == Author.pause.key1 || keyCodeStr == Author.pause.key2))) {
            Author.pause = null;
            pauseVideoes();
            window.clearTimeout(Author.actionTimer);
            showList();
        }
        return;
    }
    
    if (Author.current.currentElement && Author.current.currentElement.anyKey) {
        if (Author.lessonFinished) {
            return;
        }
        Author.variables.get(variableName('SELECTEDVALUE')).setValue("NEXT");
        Author.variables.get(variableName('SELECTEDNAME')).setValue("NEXT");
        if (Author.pause != null && Author.pause.cancelType != 0) {
            Author.pause = null;
            pauseVideoes();
            window.clearTimeout(Author.actionTimer);
            showList();
        }
        else {
            if (Author.enableNextBtn == false) 
                return;
            if (Author.current.currentListXML.attributes[0].nodeValue == "INTERACTION" || Author.lessonFlow == "repeat") 
                makeAnswerVariable();
            if (Author.variables.get(variableName('ANSWER')).getValue() == null || Author.variables.get(variableName('ANSWER')).getValue() == "") 
                Author.variables.get(variableName('ANSWER')).setValue("NEXT");
            checkResponse();
        }
    }
    return;
}

/**
 * Fire an event on a HTML element
 *
 * @param {Object} element
 * @param {Object} event
 */
function fireEvent(element, event){
    if (document.createEventObject) {
        var evt = document.createEventObject();
        return element.fireEvent('on' + event, evt)
    }
    else {
        var evt = document.createEvent("HTMLEvents");
        evt.initEvent(event, true, true);
        return !element.dispatchEvent(evt);
    }
}

/**
 * Event handler of the browser monseclick event.
 *
 * @param {Object} e
 */
function mouseClickHandle(e){
	
    try {
        if (Author.login.promptWindow.isVisible()) {
			return;
		}
    } 
    catch (err) {
    }
    try {
        if (Author.bugTracker.isVisible()) 
            return;
    } 
    catch (err) {
    }
    try {
        if (typeof(Author.variableWatch) != 'undefined' && Author.variableWatch.isVisible() && e.within('variableMonitor')) 
            return;
    } 
    catch (err) {
    }
    try {
        if (typeof(Author.structureWin) != 'undefined' && Author.structureWin.isVisible() && e.within('structure')) 
            return;
    } 
    catch (err) {
    }
    //alert(e.getTarget().className.indexOf("x-combo-list-item"));
    try {
        if (e.getTarget().className.indexOf("x-combo-list-item") > -1) 
            return;
    } 
    catch (err) {
    }
    try {
        if (e.getTarget().tagName.toLowerCase() == 'video') 
            return;
    } 
    catch (err) {
    }
	if (Author.titleImage.timer != null){
		window.clearTimeout(Author.titleImage.timer);
		Author.titleImage.timer = null;
		continueInit2();
		return;
	}
    e.stopEvent();
   
    if (Author.pause != null) {
        if (Author.pause.cancelType == 1 || Author.pause.cancelType == 3) {
            Author.pause = null;
            //	alert("mouseClicked");
            pauseVideoes();
            window.clearTimeout(Author.actionTimer);
            showList();
        }
        return;
    }
    if (Author.current.currentElement != null && Author.current.currentElement.anyClick) {
        if (Author.lessonFinished) {
            return;
        }
        Author.variables.get(variableName('SELECTEDVALUE')).setValue("NEXT");
        Author.variables.get(variableName('SELECTEDNAME')).setValue("NEXT");
        
        if (Author.pause != null && Author.pause.cancelType != 0) {
            Author.pause = null;
            pauseVideoes();
            window.clearTimeout(Author.actionTimer);
            showList();
        }
        else {
            if (Author.enableNextBtn == false) 
                return;
            if (Author.current.currentListXML.attributes[0].nodeValue == "INTERACTION" || Author.lessonFlow == "repeat") 
                makeAnswerVariable();
            if (Author.variables.get(variableName('ANSWER')).getValue() == null || Author.variables.get(variableName('ANSWER')).getValue() == "") 
                Author.variables.get(variableName('ANSWER')).setValue("NEXT");
            checkResponse();
        }
    }
}

/**
 * Pauser the current playing video.
 */
function pauseVideoes(){
    // alert("PauseVideos");
    if (Ext.isMac) {
        try {
            Author.current.currentPlayingWMPObject.dom.children[3].Stop();
        } 
        catch (err) {
        }
    }
    else {
        try {
            Author.current.currentPlayingWMPObject.controls.pause();
        } 
        catch (err) {
            //alert(err.description);
        }
    }
}



/**
 * Things to be done when the lesson ends.
 */
function endLesson(showAD){
    if (typeof(showAD) == 'undefined') {
        showAD = true;
    }
    Author.current.currentNode = Author.lessonNodes.nodes.itemAt(0);
    if (Author.current.currentNode.showEndOnCompletion && !Author.current.currentNode.endListShown) {
        Author.current.currentNode.endListShown = true;
        showLessonEndList(Author.current.currentNode.endList);
    }
    if (Author.doingLessonEnding) {
        return;
    }
    if (Author.pause != null && Author.pause.cancelType != 0) {
        Author.pause = null;
        window.clearTimeout(Author.actionTimer);
    }
    Author.mediaList.eachKey(function(key, object){
        if (Ext.isIE) {
            object.mediaObject.dom.controls.stop();
            object.mediaObject.dom.settings.mute = true;
        }
    });
    
    if (Author.setWebResult && !Author.resultSent) {
        var v1, v2, v3, v4, v5, v6, v7, v8, v9, v10;
        v1 = parseInt(Author.networkFields.substring(0, 1));
        v2 = parseInt(Author.networkFields.substring(1, 2));
        v3 = parseInt(Author.networkFields.substring(2, 3));
        v4 = parseInt(Author.networkFields.substring(3, 4));
        v5 = parseInt(Author.networkFields.substring(4, 5));
        v6 = parseInt(Author.networkFields.substring(5, 6));
        v7 = parseInt(Author.networkFields.substring(6, 7));
        v8 = parseInt(Author.networkFields.substring(7, 8));
        v9 = parseInt(Author.networkFields.substring(8, 9));
        v10 = parseInt(Author.networkFields.substring(9, 10));
        var resultLine = "";
        resultLine += "?TraineeID=" + Author.traineeID;
        
        if (v1 == 1) 
            resultLine += "&L=" + Author.lessonName + "_HTML";
        if (v2 == 1) 
            resultLine += "&SD=" + Ext.util.Format.date(Author.variables.get(variableName("STARTTIME")).value);
        if (v3 == 1) 
            resultLine += "&ED=" + Ext.util.Format.date(new Date());
        if (v4 == 1) 
            resultLine += "&ST=" + Ext.util.Format.date(Author.variables.get(variableName("STARTTIME")).value, "g:i:s A");
        if (v5 == 1) 
            resultLine += "&ET=" + Ext.util.Format.date(new Date(), "g:i:s A");
        if (v6 == 1) 
            resultLine += "&S=" + Author.variables.get(variableName("LESSONSCORE")).value;
        if (v7 == 1) 
            resultLine += "&T=" + Author.variables.get(variableName("LESSONTOTAL")).value;
        if (v8 == 1) {
            var pass = "N";
            if (parseInt(Author.variables.get(variableName("LESSONTOTAL")).value) > 0) {
                if (parseInt(Author.variables.get(variableName("LESSONSCORE")).value) / parseInt(Author.variables.get(variableName("LESSONTOTAL")).value) * 100 > Author.passRate) {
                    pass = "P";
                }
                else {
                    pass = "F";
                }
            }
            resultLine += "&P=" + pass;
        }
        if (v9 == 1) 
            resultLine += "&PR=" + Author.passRate;
        if (v10 == 1) 
            resultLine += Author.variables.get(variableName("WEBINFO")).value;
        //this line is for australia post only	
        //resultLine += "&strWCC="+ top.frGlobal.gintWorkcentre + top.frGlobal.gintCounter;
        
        var requestURL = Author.sendTo + resultLine;
        
        var httpIFrame = Author.backgroundObject.getContent().createChild({
            tag: "iframe",
            frameborder: Ext.isIE ? "0" : "1",
            style: "position:absolute;background-color:#fff;width:0px;height:0px;left:-9999px;top:-9999px"
        })
        httpIFrame.dom.src = eval(Ext.encode(requestURL));
        httpIFrame.on("load", function(e){
            Author.resultSent = true;
            try {
                iText = httpIFrame.dom.contentWindow.document.body.innerText;
                if (iText.indexOf("Page not found:") >= 0) {
                    Ext.MessageBox.alert('Result', 'Result is not sent successfully.', function(){
                        if (Author.AP) {
                            window.location = '../default.htm';
                        }
                        else {
                            window.open('', '_self', '');
                            window.close();
                            top.window.close();
                        }
                    });
                }
                else {
                    Ext.MessageBox.alert('Result', 'Result has been sent successfully.', function(){
                        if (Author.AP) {
                            window.location = '../default.htm';
                        }
                        else {
                            window.open('', '_self', '');
                            window.close();
                            top.window.close();
                        }
                    });
                }
            } 
            catch (err) {
                Ext.MessageBox.alert('Result', 'Result has been sent successfully.', function(){
                    if (Author.AP) {
                        window.location = '../default.htm';
                    }
                    else {
                        window.open('', '_self', '');
                        window.close();
                        top.window.close();
                    }
                });
            }
        });
    }
    else {
        if (showAD) {
            Ext.MessageBox.show({
                msg: '<p align = "center">Developed by <a href="http://www.microcraft.org">Microcraft</a>.</p>',
                width: 350,
                wait: true,
                waitConfig: {
                    interval: 50,
                    duration: 4000,
                    increment: 70,
                    fn: function(){
                        Ext.MessageBox.hide();
                        if (Author.AP) {
                            window.location = '../default.htm';
                        }
                        else {
                            window.open('', '_self', '');
                            window.close();
                            top.window.close();
                        }
                    }
                }
            });
        }
        else {
            if (Author.AP) {
                window.location = '../default.htm';
            }
            else {
                window.open('', '_self', '');
                window.close();
                top.window.close();
            }
        }
    }
}

function setHideFxToElement(fxObject, effect, duration){
    if (!Author.isSynch) {
        fxObject.setVisible(false);
        return 0;
    }
    if (effect == 0 || Author.travellingList) {
        fxObject.setVisible(false);
        return 0;
    }
    if (duration < 0.2) 
        duration = 0.2;
    // effect=4;
    var fxElement = fxObject.content;
    var pageXY = Author.backgroundObject.content.getXY();
    var xy = fxElement.getOffsetsTo(Author.backgroundObject.content);
    var hasShadow = false;
    if (fxObject.objectType == "IMAGEOBEJCT") {
        fxElement = fxObject.img;
        if (fxObject.hasShadow == "true") {
            hasShadow = true;
            fxObject.setShadowVisible(false);
        }
    }
    switch (effect) {
        case 1:
            //Rotate Left 
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1;
            /*
         if (hasShadow) {
         x2 = x2 - fxObject.left;
         y2 = y2 - fxObject.top;
         }
         */
            fxElement.shift({
                width: 0,
                height: height,
                x: x1,
                y: y1,
                remove: false,
                duration: duration,
                endOpacity: 0,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 2:
            //Rotate Right
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1 + width;
            var y2 = y1;
            /*
         if (hasShadow) {
         x2 = x2 - fxObject.left;
         y2 = y2 - fxObject.top;
         }
         */
            fxElement.shift({
                width: 0,
                height: height,
                x: x2,
                y: y2,
                duration: duration,
                endOpacity: 0,
                remove: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 3:
            //Rotate Bottom
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1 + height;
            /*
         if (hasShadow) {
         x2 = x2 - fxObject.left;
         y2 = y2 - fxObject.top;
         }
         */
            fxElement.shift({
                width: width,
                height: 0,
                x: x1,
                y: y1,
                duration: duration,
                endOpacity: 0,
                remove: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 4:
            //Rotate Top		
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1;
            /*
         if (hasShadow) {
         x2 = x2 - fxObject.left;
         y2 = y2 - fxObject.top;
         }
         */
            fxElement.shift({
                width: width,
                height: 0,
                x: x1,
                y: y1 + height,
                duration: duration,
                endOpacity: 0,
                remove: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 5:
            //TL-BR
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1;
            /*
         if (hasShadow) {
         x2 = x2 - fxObject.left;
         y2 = y2 - fxObject.top;
         }
         */
            fxElement.shift({
                width: 0,
                height: 0,
                x: x1,
                y: y1,
                duration: duration,
                endOpacity: 0,
                remove: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 6:
            //BL-TR
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1 + height;
            /*
         if (hasShadow) {
         x2 = x2 - fxObject.left;
         y2 = y2 - fxObject.top;
         }
         */
            fxElement.shift({
                width: 0,
                height: 0,
                x: x1,
                y: y1 + height,
                duration: duration,
                endOpacity: 0,
                remove: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 7:
            //TR-BL
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1 + width;
            var y2 = y1;
            /*
         if (hasShadow) {
         x2 = x2 - fxObject.left;
         y2 = y2 - fxObject.top;
         }
         */
            fxElement.shift({
                width: 0,
                height: 0,
                x: x1 + width,
                y: y1,
                duration: duration,
                endOpacity: 0,
                remove: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 8:
            //BR-TL
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1 + width;
            var y2 = y1 + height;
            /*
         if (hasShadow) {
         x2 = x2 - fxObject.left;
         y2 = y2 - fxObject.top;
         }
         */
            fxElement.shift({
                width: 0,
                height: 0,
                x: x1 + width,
                y: y1 + height,
                duration: duration,
                remove: false,
                endOpacity: 0,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 9: //Zoom In
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1 + parseInt(width / 2);
            var y2 = y1 + parseInt(height / 2);
            /*
         if (hasShadow) {
         x2 = x2 - fxObject.left;
         y2 = y2 - fxObject.top;
         }
         */
            fxElement.shift({
                easing: 'easeOut',
                width: 0,
                height: 0,
                x: x2,
                y: y2,
                duration: duration,
                endOpacity: 0,
                remove: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 10: // Switch Off
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            fxElement.switchOff({
                duration: duration,
                remove: false,
                useDisplay: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        case 11: //Puff
            fxElement.puff({
                easing: 'easeOut',
                duration: duration,
                remove: false,
                useDisplay: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
            break;
        default: //Fade Out
            fxElement.fadeOut({
                endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
                easing: 'easeOut',
                duration: duration,
                remove: false,
                useDisplay: false,
                callback: function(){
                    fxElement.setOpacity(1);
                    //fxElement.setLocation(x1,y1);
                    fxElement.setStyle("left", ((xy[0]) / Author.zoomRatio) + "px");
                    fxElement.setStyle("top", ((xy[1]) / Author.zoomRatio) + "px");
                    fxElement.setWidth(width);
                    fxElement.setHeight(height);
                    fxObject.setVisible(false);
                }
            });
    }
    return duration * 1000;
}

/**
 * Apply effects to an object.
 *
 * @param {Object} fxObject
 * @param {Object} effect
 * @param {Object} duration
 */
function setFxToElement(fxObject, effect, duration){
    //return;
    if (!Author.isSynch) 
        return 0;
    if (effect == 0 || Author.travellingList) 
        return 0;
    if (duration < 0.2) 
        duration = 0.2;
    var fxElement = fxObject.content;
    var pageXY = Author.backgroundObject.content.getXY();
    var xy = fxElement.getOffsetsTo(Author.backgroundObject.content);
    var hasShadow = false;
    if (fxObject.objectType == "IMAGEOBEJCT") {
        fxElement = fxObject.img;
        if (fxObject.hasShadow == "true") {
            hasShadow = true;
            fxObject.setShadowVisible(false);
        }
    }
    switch (effect) {
        //9,10,51,52
        //84,83,82,81
        case 68:
            
            var highlightColor = "ff0000";
            switch (fxObject.objectType) {
                case "BACKGROUNDOBEJCT":
                    break;
                case "IMAGEOBEJCT":
                    highlightColor = fxObject.baseColor;
                    break;
                case "TEXTOBJECT":
                    highlightColor = fxObject.baseColor;
                    break;
                case "TXTEXTOBJECT":
                    highlightColor = fxObject.baseColor;
                    break;
            }
            fxElement.frame(highlightColor, 3, {
                duration: duration / 4
            });
            break;
        case 53://82:
            //Rotate Left //Slide Right
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1;
            fxElement.setLocation(x2, y2);
            fxElement.setWidth(1);
            fxElement.shift({
                width: width,
                height: height,
                x: x1,
                y: y1,
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        //fxObject.content.applyStyles("overflow:visible");
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
        case 52://83:
            //Rotate Right //Slide Left
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1 + width;
            var y2 = y1;
            fxElement.setLocation(x2, y2);
            if (fxObject.objectType != "BACKGROUNDOBEJCT") {
                xy = fxElement.getOffsetsTo(Author.backgroundObject.content);
                x1 = pageXY[0] + xy[0] - width;
            }
            fxElement.setWidth(1);
            fxElement.shift({
                width: width,
                height: height,
                x: x1,
                y: y1,
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        //  fxObject.content.applyStyles("overflow:visible");
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
        case 9://85:
            //Rotate Bottom //Slide up
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1 + height;
            fxElement.setLocation(x2, y2);
            if (fxObject.objectType != "BACKGROUNDOBEJCT") {
                xy = fxElement.getOffsetsTo(Author.backgroundObject.content);
                y1 = pageXY[1] + xy[1] - height;
            }
            fxElement.setHeight(1);
            fxElement.shift({
                width: width,
                height: height,
                x: x1,
                y: y1,
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        //   fxObject.content.applyStyles("overflow:visible");
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
        case 10://84:
            //Rotate Top //Slide down		
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1;
            fxElement.setLocation(x2, y2);
            fxElement.setHeight(1);
            fxElement.shift({
                width: width,
                height: height,
                x: x1,
                y: y1,
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        //   fxObject.content.applyStyles("overflow:visible");
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
        case 76:
            //TL-BR
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1;
            fxElement.setLocation(x2, y2);
            xy = fxElement.getOffsetsTo(Author.backgroundObject.content);
            fxElement.setHeight(1);
            fxElement.setWidth(1);
            fxElement.shift({
                width: width,
                height: height,
                x: x1,
                y: y1,
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        //   fxObject.content.applyStyles("overflow:visible");
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
        case 78:
            //BL-TR
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1;
            var y2 = y1 + height;
            fxElement.setLocation(x2, y2);
            if (fxObject.objectType != "BACKGROUNDOBEJCT") {
                xy = fxElement.getOffsetsTo(Author.backgroundObject.content);
                y1 = pageXY[1] + xy[1] - height;
            }
            fxElement.setHeight(1);
            fxElement.setWidth(1);
            fxElement.shift({
                width: width,
                height: height,
                x: x1,
                y: y1,
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        //   fxObject.content.applyStyles("overflow:visible");
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
        case 77:
            //TR-BL
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1 + width;
            var y2 = y1;
            fxElement.setLocation(x2, y2);
            if (fxObject.objectType != "BACKGROUNDOBEJCT") {
                xy = fxElement.getOffsetsTo(Author.backgroundObject.content);
                x1 = pageXY[0] + xy[0] - width;
            }
            fxElement.setHeight(1);
            fxElement.setWidth(1);
            fxElement.shift({
                width: width,
                height: height,
                x: x1,
                y: y1,
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        // fxObject.content.applyStyles("overflow:visible");
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
        case 79:
            //BR-TL
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1 + width;
            var y2 = y1 + height;
            fxElement.setLocation(x2, y2);
            if (fxObject.objectType != "BACKGROUNDOBEJCT") {
                xy = fxElement.getOffsetsTo(Author.backgroundObject.content);
                x1 = pageXY[0] + xy[0] - width;
                y1 = pageXY[1] + xy[1] - height;
            }
            fxElement.setHeight(1);
            fxElement.setWidth(1);
            fxElement.shift({
                width: width,
                height: height,
                x: x1,
                y: y1,
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        //  fxObject.content.applyStyles("overflow:visible");
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
        case 24: //Zoom 
            var width = fxElement.getWidth();
            var height = fxElement.getHeight();
            var x1 = pageXY[0] + xy[0];
            var y1 = pageXY[1] + xy[1];
            var x2 = x1 + parseInt(width / 2);
            var y2 = y1 + parseInt(height / 2);
            fxElement.setLocation(x2, y2);
            if (fxObject.objectType != "BACKGROUNDOBEJCT") {
                xy = fxElement.getOffsetsTo(Author.backgroundObject.content);
                x1 = pageXY[0] + xy[0] - parseInt(width / 2);
                y1 = pageXY[1] + xy[1] - parseInt(height / 2);
            }
            fxElement.setWidth(1);
            fxElement.setHeight(1);
            fxElement.shift({
                width: width,
                height: height,
                x: x1,
                y: y1,
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        // fxObject.content.applyStyles("overflow:visible");
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
        default: // Fade In
            if ((fxObject.objectType == "TEXTOBJECT" || fxObject.objectType == "TXTEXTOBJECT") && !Ext.isGecko) {
                fxElement = fxObject.text;
                fxObject.backgroundFadein(duration * 1000);
            }
            fxElement.fadeIn({
                duration: duration,
                callback: function(){
                    if (fxObject.objectType == "IMAGEOBEJCT") {
                        // fxObject.content.applyStyles("overflow:visible");
                        //fxObject.content.setOpacity(0.1);
                        if (hasShadow) 
                            fxObject.setShadowVisible(true);
                    }
                }
            });
            break;
    }
    return duration * 1000;
}

/**
 * Get the score that the user achieved in the lesson.
 */
function getLessonScore(){
    var lessonScore = 0;
    for (var i = 0; i < Author.lessonNodes.nodes.getCount(); i++) {
        var aNode = Author.lessonNodes.nodes.itemAt(i);
        if (aNode.scoring) {
            lessonScore += aNode.getOptionScore();
        }
    }
    return lessonScore;
}

/**
 * Get the possible max score of all the options the user completed.
 */
function getLessonTotal(){
    var lessonTotal = 0;
    for (var i = 0; i < Author.lessonNodes.nodes.getCount(); i++) {
        var aNode = Author.lessonNodes.nodes.itemAt(i);
        if (aNode.scoring) {
            lessonTotal += aNode.getOptionMaxScore();
        }
    }
    return lessonTotal;
}

/**
 * Save lesson status.
 */
function beforeWindowsClose(){
    if (Author.closeActionsDone) 
        return;
    
    
    var bookMarkStr = "";
    var optionBMString = "";
    if (Author.bookMark) {
        Author.lessonNodes.nodes.eachKey(function(key, object){
            optionBMString += object.bookmarkString();
        });
    }
    
    var variableBMString = "";
    Author.variables.eachKey(function(key, object){
        variableBMString += object.bookmarkString();
    });
    
    bookMarkStr = optionBMString + "||" + variableBMString;
    Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
        expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365))
    
    }));
    
    Ext.state.Manager.set(Author.projectName + "[" + Author.traineeID + "]", bookMarkStr);
    
    endScorm();
    Author.closeActionsDone = true;
}

/**
 * Send values back to scorm when exiting the lesson.
 */
function endScorm(){
    if (Author.scorm) {
        if (!Author.userDefinedScormResult) {
            ScormProcessSetValue("cmi.core.score.raw", getVariableValueByName("%SCORE").toString());
        }
        
        var lessonTime = ConvertMilliSecondsToSCORMTime(new Date() - Author.variables.get(variableName("STARTTIME")).value);
        ScormProcessSetValue("cmi.core.session_time", lessonTime);
        
        var stdScore = parseInt(getVariableValueByName("%SCORE"));
        if (Author.userDefinedScormResult) {
            stdScore = parseInt(getVariableValueByName("SCORM_SCORE_RAW"));
        }
        //alert("Score:" + stdScore + "\npassRate:" + Author.passRate + "\n" + (stdScore >= Author.passRate));
        if (stdScore >= Author.passRate) {
        
            ScormProcessSetValue("cmi.core.lesson_status", "passed");
        }
        else {
            ScormProcessSetValue("cmi.core.lesson_status", "failed");
        }
        ScormProcessFinish();
    }
}

/**
 * Convert millisends to the scorm time format.
 *
 * @param {int} intTotalMilliseconds
 * @param {Object} blnIncludeFraction
 */
function ConvertMilliSecondsToSCORMTime(intTotalMilliseconds, blnIncludeFraction){
    var intHours;
    var intintMinutes;
    var intSeconds;
    var intMilliseconds;
    var intHundredths;
    var strCMITimeSpan;
    
    if (blnIncludeFraction == null || blnIncludeFraction == undefined) {
        blnIncludeFraction = true;
    }
    
    //extract time parts
    intMilliseconds = intTotalMilliseconds % 1000;
    intSeconds = ((intTotalMilliseconds - intMilliseconds) / 1000) % 60;
    intMinutes = ((intTotalMilliseconds - intMilliseconds - (intSeconds * 1000)) / 60000) % 60;
    intHours = (intTotalMilliseconds - intMilliseconds - (intSeconds * 1000) - (intMinutes * 60000)) / 3600000;
    
    /*
     deal with exceptional case when content used a huge amount of time and interpreted CMITimstamp
     to allow a number of intMinutes and seconds greater than 60 i.e. 9999:99:99.99 instead of 9999:60:60:99
     note - this case is permissable under SCORM, but will be exceptionally rare
     */
    if (intHours == 10000) {
        intHours = 9999;
        
        intMinutes = (intTotalMilliseconds - (intHours * 3600000)) / 60000;
        if (intMinutes == 100) {
            intMinutes = 99;
        }
        intMinutes = Math.floor(intMinutes);
        
        intSeconds = (intTotalMilliseconds - (intHours * 3600000) - (intMinutes * 60000)) / 1000;
        if (intSeconds == 100) {
            intSeconds = 99;
        }
        intSeconds = Math.floor(intSeconds);
        
        intMilliseconds = (intTotalMilliseconds - (intHours * 3600000) - (intMinutes * 60000) - (intSeconds * 1000));
    }
    
    //drop the extra precision from the milliseconds
    intHundredths = Math.floor(intMilliseconds / 10);
    
    //put in padding 0's and concatinate to get the proper format
    strCMITimeSpan = ZeroPad(intHours, 4) + ":" + ZeroPad(intMinutes, 2) + ":" + ZeroPad(intSeconds, 2);
    
    if (blnIncludeFraction) {
        strCMITimeSpan += "." + intHundredths;
    }
    
    //check for case where total milliseconds is greater than max supported by strCMITimeSpan
    if (intHours > 9999) {
        strCMITimeSpan = "9999:99:99";
        
        if (blnIncludeFraction) {
            strCMITimeSpan += ".99";
        }
    }
    
    return strCMITimeSpan;
}

/**
 * ZeroPad a given number
 *
 * @param {Object} intNum
 * @param {Object} intNumDigits
 */
function ZeroPad(intNum, intNumDigits){

    var strTemp;
    var intLen;
    var i;
    
    strTemp = new String(intNum);
    intLen = strTemp.length;
    
    if (intLen > intNumDigits) {
        strTemp = strTemp.substr(0, intNumDigits);
    }
    else {
        for (i = intLen; i < intNumDigits; i++) {
            strTemp = "0" + strTemp;
        }
    }
    
    return strTemp;
}

/**
 * When the document is ready, initialize the lesson.
 */
Ext.onReady(function(){

    //return;
    Ext.EventManager.onWindowResize(function(width, height){
    
        if (Author.lessonHeight == 0 || Author.lessonWidth == 0) 
            return;
        if (typeof(Author.backgroundObject) == 'undefined') 
            return;
        Author.backgroundObject.center();
    });
    
    Ext.override(Ext.dd.DDProxy, {
        startDrag: function(x, y){
            if (Author.inResponse || Author.lessonFinished) 
                return;
            var dragEl = Ext.get(this.getDragEl());
            var el = Ext.get(this.getEl());
            dragEl.applyStyles({
                border: '',
                zoom: Author.zoomRatio,
                'z-index': 2000
            });
            
            dragEl.update(el.dom.innerHTML);
            el.setVisible(false);
            dragEl.setStyle("filter", "alpha(opacity=100)");
        },
        onDragOver: function(e, targetId){
            if (Author.inResponse || Author.lessonFinished) 
                return;
            if (Ext.get(targetId).dom.getAttribute("type") == "HOTSPOT") {
                var target = Ext.get(targetId);
                this.lastTarget = target;
            }
        },
        onDragOut: function(e, targetId){
            if (Author.inResponse || Author.lessonFinished) 
                return;
            if (Ext.get(targetId).dom.getAttribute("type") == "HOTSPOT") {
                var el = Ext.get(this.getEl());
                var target = Ext.get(targetId);
                if (target.dom.getAttribute("content") == el.id) {
                    target.dom.setAttribute("content", "NOTHING");
                    target.dom.setAttribute("value", target.dom.getAttribute("orginalValue"));
                }
                this.lastTarget = null;
            }
        },
        endDrag: function(){
            if (Author.inResponse || Author.lessonFinished) 
                return;
            var dragEl = Ext.get(this.getDragEl());
            var el = Ext.get(this.getEl());
            el.setVisible(true);
            if (this.lastTarget) {
                var dropzone = Ext.get(this.lastTarget);
                if (dropzone.dom.getAttribute("content") != "NOTHING") {
                    var oldContent = Ext.get(dropzone.dom.getAttribute("content"));
                    // oldContent.moveTo(oldContent.dom.getAttribute("orginalX"), oldContent.dom.getAttribute("orginalY"), true);
                    oldContent.setStyle("left", oldContent.dom.getAttribute("orginalX") + "px");
                    oldContent.setStyle("top", oldContent.dom.getAttribute("orginalY") + "px");
                }
                dropzone.set({
                    "content": el.id,
                    "value": el.dom.getAttribute("value")
                });
                //  alert(dropzone.getStyle("left")+","+dropzone.getStyle("top")+"\n"+dropzone.getX()+", "+dropzone.getY());
                el.setStyle("left", dropzone.getStyle("left"));
                el.setStyle("top", dropzone.getStyle("top"));
                //el.setStyle("z-index",1000);
                //el.setX(dropzone.getX() + (dropzone.getWidth() - el.getWidth()) / 2);
                // el.setY(dropzone.getY() + (dropzone.getHeight() - el.getHeight()) / 2);
                
                var variable = dropzone.dom.getAttribute("name");
                var value = dropzone.dom.getAttribute("value");
                Author.variables.get(variableName('SELECTEDVALUE')).setValue(value);
                Author.variables.get(variableName('SELECTEDNAME')).setValue(variable);
                if (dropzone.dom.getAttribute("respondToDrop") == "true") {
                    checkResponse();
                }
            }
            else {
                //alert(el.dom.getAttribute("miss"));
                if (el.dom.getAttribute("miss") == "stay") {
                    var pageXY = Author.backgroundObject.content.getXY();
                    var xy = dragEl.getXY();
                    el.setStyle("left", ((xy[0] - pageXY[0]) / Author.zoomRatio) + "px");
                    el.setStyle("top", ((xy[1] - pageXY[1]) / Author.zoomRatio) + "px");
                }
                else {
                    el.setStyle("left", el.dom.getAttribute("orginalX") + "px");
                    el.setStyle("top", el.dom.getAttribute("orginalY") + "px");
                }
            }
            this.lastTarget = null;
        }
    });
    
    setTimeout(function(){
    
        initlizeLesson();
    }, 500);
    
});
function getLessonInfo(){
    var info = "<div style ='font-family:Arial; line-height:15pt;font-size:10pt;text-align:center' ><table width ='100%' style='border-width: 1px;	order-color: gray;border-collapse: separate;s'>";
    info += "<tr><td ALIGN='left' width='50px'>Option</td><td ALIGN='center' ><font  style='font-weight:bold'>" + Author.current.currentNode.name + "</font><br/> <font style = 'font-size:8pt;font-style:italic'>(" + Author.current.currentNode.type + ")</font></td></tr>";
    if (Author.current.currentNode.type != "MENU" && Author.current.currentElement != null) {
        info += "<tr><td ALIGN='right'>Element</td><td><font  style='font-weight:bold'>" + Author.current.currentElement.name + "</font><br/> <font style = 'font-size:8pt;font-style:italic'>(" + Author.current.currentElement.type + ")</font></td></tr>";
    }
    info += "</table></div>";
    return info;
}

function displayError(errorMsg){
    errorMsg = "WARNING:\n\n" + errorMsg;
    errorMsg += ("\n\nELEMENT: " + Author.current.currentElement.name + "\nLIST: " + Author.current.currentListXML.attributes[0].nodeValue);
    alert(errorMsg);
}

function isUrl(s){
    var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
    return regexp.test(s);
}


function isCapslock(e){

    e = (e) ? e : window.event;
    
    var charCode = false;
    if (e.which) {
        charCode = e.which;
    }
    else 
        if (e.keyCode) {
            charCode = e.keyCode;
        }
    
    var shifton = false;
    if (e.shiftKey) {
        shifton = e.shiftKey;
    }
    else 
        if (e.modifiers) {
            shifton = !!(e.modifiers & 4);
        }
    
    if (charCode >= 97 && charCode <= 122 && shifton) {
        return true;
    }
    
    if (charCode >= 65 && charCode <= 90 && !shifton) {
        return true;
    }
    return false;
}

function ccos(angle){

    return Math.cos(Math.PI * angle / 180);
}

function ssin(angle){

    return Math.sin(Math.PI * angle / 180);
}

function getCheckBoxValues(){
    var checkBoxStr = "";
    for (var index = Author.interactionList.startIndex; index <= Author.interactionList.endIndex; index++) {
        var action = Author.current.currentList.itemAt(index);
        if (action.objectType == "CHECKBOXOBJECT") {
            checkBoxStr += (action.button.dom.checked) ? "T" : "F";
        }
    }
    return checkBoxStr;
}

function curve(p, c){
    var k = (Math.atan((1 - 0.5) * c * Math.PI) - Math.atan((0 - 0.5) * c * Math.PI)) / Math.PI;
    return -Math.atan((p - 0.5) * c * Math.PI) / (Math.PI * k) + 0.5;
}

/**
 * Set the background for the lesson frame.
 */
function setLessonFrame(){
    if (Author.frame.useFrame && Author.frame.lessonFrame != "none") {
        var bodyEl = Ext.getBody();
        bodyEl.setStyle("background-image", "url(" + Author.resourcePath + Author.frame.lessonFrame + ")");
        var repeat = new Array("repeat-x", "repeat-y", "repeat", "no-repeat");
        bodyEl.setStyle("background-repeat", repeat[Author.frame.repeat]);
        if (repeat[Author.frame.repeat] == "no-repeat") {
            bodyEl.setStyle("background-position", "50% 50%");
        }
    }
}

function flashResult(params){ //flashResult
    if (typeof(Author.doFlashResponse) == 'function') {
        Author.doFlashResponse(params);
    }
    
}

function setFlashVariable(params){ //flashResult
    if (typeof(Author.doFlashResponse) == 'function') {
        Author.doFlashResponse(params);
    }
}

