/**
 * @author Microcraft eLearning
 * @version 1.0
 */
/**
 * WebAuthor runtime object
 */
Author = {
    pressedKeyCode: 0,
    resultSent: false,
    autoZoom: true,
    zoomRatio: 1,
    version: "0.1",
    lessonName: "",
    location: "",
    resourcePath: "resources/",
    traineeID: "",
    qcURL: "",
    qcKey: 0,
    removeDup: true,
    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){
                if (object.hide == "false") 
                    object.setVisible(true);
                if (Author.current.currentNode.type == "MENU") {
                
                    object.setVisible(false);
                }
                Author.current.currentList.add(object.id, object);
            });
            Author.backToMenu = false;
        }
    },
    dividers: new Ext.util.MixedCollection,
    interactionList: {},
    informationList: {},
    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,
    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,
    lessonFinished: false,
    closeMe: function(success){
        if (success) {
            Ext.MessageBox.alert('Result', 'Result has been sent successfully.', function(){
                window.open('', '_self', '');
                window.close();
            });
        }
        else {
            Ext.MessageBox.alert('Result', 'Result is not sent successfully.', function(){
                window.open('', '_self', '');
                window.close();
            });
        }
    }
};

/**
 * WebAuthor tries to find the "test-webLesson.xml" file in the same folder of authorLesson.html and read the test information.
 * If no such file is found, WebAuthor will run in the normal mode.
 */
function getTestInfo(){
    var resourcePath = null;
    Author.Testing = false;
    var TESTWEBLESSON = "test-webLesson.xml";
    try {
        var testDesp = null;
        if (Ext.isIE || Ext.isGecko || Ext.isOpera) {
            if (window.ActiveXObject) {
                testDesp = new ActiveXObject("Microsoft.XMLDOM");
            }
            else {
                if (document.implementation.createDocument) {
                    testDesp = document.implementation.createDocument("", "", null);
                }
            }
            if (testDesp != null) {
                testDesp.async = false;
                if (testDesp.load(TESTWEBLESSON)) {
                    Author.Testing = true;
                    Author.Test.type = testDesp.getElementsByTagName("testType")[0].childNodes[0].nodeValue;
                    Author.Test.startLessonNode = parseInt(testDesp.getElementsByTagName("startLessonNode")[0].childNodes[0].nodeValue);
                    Author.Test.startElement = parseInt(testDesp.getElementsByTagName("startElement")[0].childNodes[0].nodeValue);
                }
            }
            else {
                alert("Can't initlise XML parser.");
                return;
            }
        }
        else {
            var xmlhttp = new window.XMLHttpRequest();
            xmlhttp.open("GET", TESTWEBLESSON, false);
            xmlhttp.send(null);
            testDesp = xmlhttp.responseXML;
            if (testDesp != null && xmlhttp.status == 200) {
                Author.Testing = true;
                Author.Test.type = testDesp.getElementsByTagName("testType")[0].childNodes[0].nodeValue;
                Author.Test.startLessonNode = testDesp.getElementsByTagName("startLessonNode")[0].childNodes[0].nodeValue;
                Author.Test.startElement = testDesp.getElementsByTagName("startElement")[0].childNodes[0].nodeValue;
            }
        }
    } 
    catch (err) {
        // alert("getTestInfo : " + err.description);
        Author.Testing = false;
    };
    
    //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();
    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();
    })
}

/**
 * Initialize the web lesson
 */
function initlizeLesson(){
    var lessonXML = "lesson.xml";
    Author.lessonXMLDoc = null;
    
    // If "lesson.xml" doesn't exist or cannot load XMLParser, stop the lesson.
    
    if (Ext.isIE || Ext.isGecko || Ext.isOpera) {
        if (window.ActiveXObject) {
            Author.lessonXMLDoc = new ActiveXObject("Microsoft.XMLDOM");
        }
        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;
            }
        }
        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;
        }
    }
    
    Author.lessonName = trim(Author.lessonXMLDoc.getElementsByTagName("lesson")[0].attributes[0].nodeValue);
    Author.projectName = trim(Author.lessonXMLDoc.getElementsByTagName("lesson")[0].attributes[5].nodeValue);
    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);
    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,
            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();
    }
}

/**
 * Continue the lesson initialization
 *
 * Connect to the scorm API, initialise some global variables, and get the userID from prompt if needed.
 */
function continueInit1(){

    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;
                var blankPos = studentFullName.indexOf(" ");
                Author.variables.get(variableName("FIRSTNAME")).value = studentFullName.substring(0, blankPos - 1);
                Author.variables.get(variableName("LASTNAME")).value = studentFullName.substring(blankPos + 1, studentFullName.length - 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");
    var lessonInfoNode = Author.lessonXMLDoc.getElementsByTagName("lessonInfo")[0];
    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.setWebResult) {
        Author.bookMark = true;
        Author.sendTo = webResultNode.getElementsByTagName("sendTo")[0].childNodes[0].nodeValue;
        Author.networkFields = webResultNode.getElementsByTagName("networkFields")[0].childNodes[0].nodeValue;
    }
    
    // If bookMark enabled and the lesson is not scorm lesson, get the userID.
    if (Author.bookMark && !Author.scorm) {
    
        var prompt = trim(lessonInfoNode.getElementsByTagName("prompt")[0].childNodes[0].nodeValue);
        var idLength = parseInt(trim(lessonInfoNode.getElementsByTagName("IDLen")[0].childNodes[0].nodeValue));
        var idType = parseInt(trim(lessonInfoNode.getElementsByTagName("IDType")[0].childNodes[0].nodeValue));
        if (prompt == "none") 
            prompt = "User ID:";
        else 
            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, function(btn, text){
                if (btn.toUpperCase() == "OK") {
                    Author.traineeID = text;
                    Author.variables.get(variableName("USERID")).value = Author.traineeID;
                    continueInitLesson();
                }
                else {
                    window.open('', '_self', '');
                    window.close();
                }
            });
        }
    }
    else {
        continueInitLesson();
    }
}

/**
 * Continue the lesson initialization.
 */
function continueInitLesson(){

    window.onunload = beforeWindowsClose;
    window.onbeforeunload = beforeWindowsClose;
    //Ext.EventManager.addListener(Ext.getDoc(),"keypress",keyPressedHandle);
    var bodyEl = Ext.getBody();
    Ext.getDoc().on("keydown", function(e){
        Author.pressedKeyCode = e.getCharCode();
    });
    Ext.getDoc().on("keypress", keyPressedHandle);
    Ext.getDoc().on("click", mouseClickHandle);
    Ext.getDoc().on("dblclick", function(e){
        e.stopEvent();
    });
    // 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
        fn: function(key, e){
            //alert("Key pressed: " + e.getCharCode() + ", ALT: " + e.altKey + ", CTRL: " + e.ctrlKey + ", SHIFT: " + e.shiftKey);
            e.stopEvent();
            e.preventDefault();
            e.stopPropagation()
            keyPressedHandle(e, true);
        }
    }]);
    
    // Initialize the background object.
    Author.backgroundObject = new BackgroundClass(bodyEl);
    Author.resultSent = false;
    
    // Initialize the background list and foreground list.
    doLessonRoot();
    
    // Initialize the lesson options. [MENU|SEQUENCE|RANDOM]
    initlizeLessonNodes();
    
    // Start to play the lesson.
    showLessonNode();
}

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',
        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.grid.stopEditing();
            this.grid.getStore().each(updateRecord);
        }
    }
    Author.variableWatch.show();
}

function updateRecord(record){
    var vName = record.get('name');
    record.set('value', getVariableValueByName(vName));
}

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;
		}
    }
}

function showLessonStructure(showElementCode, expandNode){
    if (typeof(showElementCode) == 'undefined') {
        showElementCode = false;
    }
    if (typeof(expandNode) == 'undefined') {
        expandNode = true;
    }
    
    if (typeof(Author.structureWin) != 'undefined') {
        Author.structureWin.show();
        selectNode(Author.current.currentNode.id, Author.current.currentElementPosition, Author.structureWin.tree.getRootNode());
        return;
    }
    
    var structureDiv = Ext.getBody().createChild({
        tag: "div",
        id: "structure",
        style: "position:absolute;left:0px;top:0px"
    });
    structureDiv.center();
    Author.structureWin = new Ext.Window({
        title: 'Reference',
        renderTo: 'structure',
        layout: 'border',
        width: 350,
        height: 500,
        closeAction: 'hide',
        collapseFirst: true,
        collapsible: true,
        items: [{
            ref: '../tree',
            xtype: 'treepanel',
            id: 'tree-panel',
            region: 'center',
            margins: '2 2 2 2',
            autoScroll: true,
            rootVisible: false,
            root: new Ext.tree.TreeNode()
        }]
    });
    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;
    
    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.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.tree.getRootNode());
    
}

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.currentElementPosition = node.ePos;
                        Author.current.currentListPosition = 0;
                        Author.current.currentElementResponses.clear();
                        showLessonNode();
                    }
                }
            });
            aTreeNode.oID = curNode.id;
            aTreeNode.ePos = index;
            curTreeNode.appendChild(aTreeNode);
        }
    }
}

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.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];
    
    // 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") {
            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);
    }
}

/**
 * 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;
        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 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.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.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);
            if (Author.current.currentNode.type == "RANDOM") {
                Author.Test.startAtRandom = true;
            }
            Author.current.currentElementXML = null;
            Author.current.currentElementPosition = savedElementPosition;
            Author.current.currentListPosition = 0;
        }
    }
    else {
        Author.current.currentNode = Author.lessonNodes.nodes.itemAt(0);
        Author.current.currentElementPosition = -1;
        Author.current.currentListPosition = 0;
    }
}


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.tree.getRootNode());
    }
    if (Author.current.currentNode.type == "MENU") {
        var lists = Author.current.currentNode.xml.getElementsByTagName("list");
        var listCount = lists.length;
        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.
                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);
                    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.currentElementXML = 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.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.currentElementPosition++;
                    //alert(Author.current.currentElementPosition);
                    showLessonNode();
                    return;
                }
                
                
                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.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.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
                    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) {
                    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);
                        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.currentElementXML = 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.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.currentElementPosition++;
                        showLessonNode();
                        return;
                    }
                    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.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.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){

    // 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;
    }
}

/**
 * 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;
    var after = text.replace(regex, function(name, vName){
        try {
            vName = trim(vName).toUpperCase();
            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));
            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;
        }
    });
    //alert(text +" >> "+ after);
    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(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");
    
    // 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();
}

/**
 * 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);
        }
    }
}

/**
 * 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){
    name = name.toUpperCase();
    var value = name;
    var id = variableName(name);
    if (typeof(id) == 'undefined') {
        value = getActionValueByName(name);
    }
    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 "CHECKBOXOBJECT":
                    if (action.button.dom.getAttribute("name").toUpperCase() == name.toUpperCase()) {
                        return new Boolean(action.button.dom.checked).toString();
                    }
                    break;
                case "RADIOBUTTONOBJECT":
                    if (action.button.dom.getAttribute("name").toUpperCase() == name.toUpperCase()) {
                        return new Boolean(action.button.dom.checked).toString();
                    }
                    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);
        }
    }
}

/**
 * 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);
}

/**
 * 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();
                
                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 "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) {
            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 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 == "INFORMATION") {
        Author.informationList.startIndex = Author.current.currentList.getCount();
    }
    if (listType == "INTERACTION") {
        Author.interactionList.startIndex = Author.current.currentList.getCount();
        Author.current.precedentObject = "none";
    }
    if (listType == "RESPONSE") {
    
        if (Author.response.removeType == "BEFORENEXT") {
        
            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);
        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);
    
}

/**
 * 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( Author.current.currentListXML.getElementsByTagName("action")[Author.current.currentActionPosition-1].attributes[0].nodeValue);
        }
        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") {
        //alert("information finished");
    }
    if (listType == "INTRODUCTION") {
    }
    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 == "RESPONSE") {
    
        Author.response.endResponse = Author.current.currentList.getCount() - 1;
        //alert("Response ends at:"+Author.response.endResponse);
        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() - 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() - 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.mediaObject.dom.controls.stop();
                object.mediaObject.dom.settings.mute = true;
                
            }
            object.remove();
        }
    });
    //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) {
            continue;
        }
        if (el.addToForeground && (Author.current.currentElementPosition >= el.addToForegroundAtElementPosition) && !Author.clearBackgroundObjects || (el.buttonType == "2" && el.click != "EXITLESSON")) {
            // alert(el.buttonType);
            continue;
        }
        
        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;
    if (actionType == "PROGRESSBAR") {
        return 0;
    }
    if (actionType == "SOUNDACTION") {
        //	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 == "SOUNDACTION") {
        var actionObject = new SoundClass(Author.backgroundObject.getContent(), action);
    }
    if (actionType == "PAUSE") {
        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 == "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);
    }
    if (actionType == "RADIOBUTTON") {
        var actionObject = new RadioButtonClass(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;
    }
    
    if (Author.current.currentList.containsKey(actionObject.id)) {
        if (Author.removeDup) {
            Author.current.currentList.get(actionObject.id).remove();
        }
    }
    
    Author.removeDup = true;
    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, "");
}

/**
 * Event handler of the browser keyPress event.
 *
 * @param {Object} e
 */
function keyPressedHandle(e, fnKeys){
    if (typeof(fnKeys) == 'undefined' || fnKeys !== true) {
        fnKeys = false;
    }
    if (Author.pressedKeyCode > 111 && Author.pressedKeyCode < 124) {
        fnKeys = true;
    }
    Author.pressedKeyCode = 0;
    if (Author.bugTracker.isVisible()) 
        return;
    if (Author.doingInteraction) {
        if (e.getCharCode() == 13) {
            var target = Ext.get(e.getTarget());
            if (target.dom.getAttribute("objectType") == "TEXTFIELDOBJECT") {
                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;
    }
    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 != "") && Author.Testing) {
    
        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();
        var eName = Author.current.currentNode.name;
        if (Author.current.currentNode.type != "MENU" && Author.current.currentElement != null) {
            eName = Author.current.currentElement.name;
        }
        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){

    if (Author.bugTracker.isVisible()) 
        return;
    
    if (typeof(Author.variableWatch) != 'undefined' && Author.variableWatch.isVisible() && e.within('variableMonitor')) 
        return;
    
    if (typeof(Author.structureWin) != 'undefined' && Author.structureWin.isVisible() && e.within('structure')) 
        return;
    //alert(e.getTarget().className.indexOf("x-combo-list-item"));
    if (e.getTarget().className.indexOf("x-combo-list-item") > -1) 
        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(){
    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;
        
        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(){
                        window.open('', '_self', '');
                        window.close();
                    });
                }
                else {
                    Ext.MessageBox.alert('Result', 'Result has been sent successfully.', function(){
                        window.open('', '_self', '');
                        window.close();
                    });
                }
            } 
            catch (err) {
                Ext.MessageBox.alert('Result', 'Result has been sent successfully.', function(){
                    window.open('', '_self', '');
                    window.close();
                });
            }
        });
        
    }
    else {
        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();
                    window.open('', '_self', '');
                    window.close();
                }
            }
        });
    }
}

function setHideFxToElement(fxObject, effect, duration){
    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 (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) {
        case 82:
            //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;
            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 83:
            //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;
            fxElement.setLocation(x2, y2);
            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 85:
            //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;
            fxElement.setLocation(x2, y2);
            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 84:
            //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;
            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);
            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);
            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);
            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);
            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" && !Ext.isGecko) {
                fxElement = fxObject.text;
            }
            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(){
    Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
        expires: new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 365))
    }));
    Author.lessonNodes.nodes.eachKey(function(key, object){
        object.saveBookmark();
    });
    Author.variables.eachKey(function(key, object){
        object.saveBookmark();
    });
    endScorm();
}

/**
 * Send values back to scorm when exiting the lesson.
 */
function endScorm(){
    if (Author.scorm) {
        ScormProcessSetValue("cmi.core.score.raw", Author.variables.get(variableName("%SCORE")).value.toString());
        var lessonTime = ConvertMilliSecondsToSCORMTime(new Date() - Author.variables.get(variableName("STARTTIME")).value);
        ScormProcessSetValue("cmi.core.session_time", lessonTime);
        var stdScore = Author.variables.get(variableName("%SCORE")).value;
        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);
            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());
            
            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.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;
        }
    });
    
    getTestInfo();
    initlizeLesson();
});
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;
}

