var req;

function xmlReq(method, url, toSend, responseHandler) {
    req = false;
    // branch for native XMLHttpRequest object
    if(window.XMLHttpRequest && !(window.ActiveXObject)) {
        try {
            req = new XMLHttpRequest();
        }
        catch(e) {
            req = false;
        }
    // branch for IE/Windows ActiveX version
    }
    else if(window.ActiveXObject) {
       	try {
            req = new ActiveXObject("Msxml2.XMLHTTP");
      	}
      	catch(e) {
            try {
                req = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch(e) {
                req = false;
            }
        }
    }
    if(req) {
        req.onreadystatechange = responseHandler;
        req.open(method, url, true);
        req.setRequestHeader("content-type","application/x-www-form-urlencoded");
        req.send(toSend);
    }
    else {
        alert('Your browser does not seem to support XMLHttpRequest.');
    }
}

/* ajaxUpdateElement looks for this XML document structure:
    <response>
        <update>
            <element>divAjaxTest</element>
            <attributes>
                <attribute>
                    <name>style</name>
                    <value>color: green; text-decoration: underline;</value>
                </attribute>
            </attributes>
            <data>HTML returned</data>
            <options>
                <option>
                    <name>text</name>
                    <value>1</value>
                </option>
            </options>
        </update>
        ...
        <update>...</update>
        ...
    </response>

   It is assumed the root node (response in this example) is passed as the argument.
   The function will attempt to update the current document's named elements,
   their attributes and HTML contents.
 */
function ajaxUpdateElement(xmlRoot) {
    for (i = 0; i < xmlRoot.childNodes.length; i++) {
        var xmlNode = xmlRoot.childNodes.item(i);
        
        if (xmlNode.nodeType == 1 && xmlNode.nodeName == 'update') {
            var updateID = null;
            var updateElement = null;
            var elementAction = 'update';
            
            for (j = 0; j < xmlNode.childNodes.length; j++) {
                var xmlUpdateNode = xmlNode.childNodes.item(j);
                if (xmlUpdateNode.nodeType == 1 && xmlUpdateNode.nodeName == 'element') {
                    updateID = xmlUpdateNode.childNodes[0].nodeValue;
                    updateElement = document.getElementById(updateID);
                }
                else if (xmlUpdateNode.nodeType == 1 && xmlUpdateNode.nodeName == 'action') {
                    elementAction = xmlUpdateNode.childNodes[0].nodeValue;
                }
                else if (xmlUpdateNode.nodeType == 1 && xmlUpdateNode.nodeName == 'attributes') {
                    /*for (k = 0; k < xmlUpdateNode.childNodes.length; k++)
                    {
                        var xmlAttribNode = xmlUpdateNode.childNodes.item(k);
                        if (xmlAttribNode.nodeType == 1 && xmlAttribNode.nodeName == 'attribute')
                        {
                            var updateAttrName = null;
                            var updateAttrValue = null;
                            for (m = 0; m < xmlAttribNode.childNodes.length; m++)
                            {
                                var xmlAttribSubNode = xmlAttribNode.childNodes.item(m);
                                if (xmlAttribSubNode.nodeType == 1 && xmlAttribSubNode.nodeName == 'name')
                                {
                                    alert(xmlAttribSubNode.nodeName + ': ' + xmlAttribSubNode.childNodes[0].nodeValue);
                                }
                                else if (xmlAttribSubNode.nodeType == 1 && xmlAttribSubNode.nodeName == 'value')
                                {
                                    alert(xmlAttribSubNode.nodeName + ': ' + xmlAttribSubNode.childNodes[0].nodeValue);
                                }
                                
                                // Update attribute and reset
                                if (updateAttrName != null && updateAttrValue != null)
                                {
                                    // ... NOT IMPLEMENTED!
                                    updateAttrName == null;
                                    updateAttrValue == null;
                                }
                            }
                        }
                    }*/
                }
                else if (xmlUpdateNode.nodeType == 1 && xmlUpdateNode.nodeName == 'options') {
                    // Empty all options from select box
                    updateElement.options.length = 0;

                    for (k = 0; k < xmlUpdateNode.childNodes.length; k++)
                    {
                        var xmlAttribNode = xmlUpdateNode.childNodes.item(k);
                        if (xmlAttribNode.nodeType == 1 && xmlAttribNode.nodeName == 'option')
                        {
                            var optionName = null;
                            var optionValue = null;
                            for (m = 0; m < xmlAttribNode.childNodes.length; m++)
                            {
                                var xmlAttribSubNode = xmlAttribNode.childNodes.item(m);
                                if (xmlAttribSubNode.nodeType == 1 && xmlAttribSubNode.nodeName == 'name')
                                {
                                    optionName = xmlAttribSubNode.childNodes[0].nodeValue;
                                }
                                else if (xmlAttribSubNode.nodeType == 1 && xmlAttribSubNode.nodeName == 'value')
                                {
                                    optionValue = xmlAttribSubNode.childNodes[0].nodeValue;
                                }
                                
                                // Insert new option into select box
                                if (optionName != null && optionValue != null)
                                {
                                    updateElement.options[updateElement.options.length] = new Option(optionName, optionValue);
                                    optionName == null;
                                    optionValue == null;
                                }
                            }
                        }
                    }
                    if (updateElement.options.length > 0)
                    {
                        updateElement.selectedIndex = 0;
                    }
                }
                else if (xmlUpdateNode.nodeType == 1 && xmlUpdateNode.nodeName == 'data') {
                    switch (elementAction) {
                        case 'delete':
                            // Delete node
                            updateElement.parentNode.removeChild(updateElement);
                            break;
                        case 'update':
                            // Write innerHTML
                            if (updateID != null && updateElement) {
                                updateElement.innerHTML = xmlUpdateNode.childNodes[0].nodeValue;
                            }
                            break;
                        case 'reload':
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    }
}

/* ajaxGetMethodResult looks for this XML document structure:
    <response>
        <call>
            <method><?php echo $method; ?></method>
            <result><?php echo $result; ?></result>
        </call>
    </response>

   It is assumed the root node (response in this example) is passed as the argument.
   The function will extract the method name and result,
   returning them as an array: [method, result].
 */
function ajaxGetMethodResult(xmlRoot)
{
    var method = '';   // Method return text
    var result = '';   // Result return text

    for (i = 0; i < xmlRoot.childNodes.length; i++) {
        var xmlNode = xmlRoot.childNodes.item(i);
        
        if (xmlNode.nodeType == 1 && xmlNode.nodeName == 'call') {
            for (j = 0; j < xmlNode.childNodes.length; j++) {
                var xmlSubNode = xmlNode.childNodes.item(j);
                if (xmlSubNode.nodeType == 1 && xmlSubNode.nodeName == 'method') {
                    method = xmlSubNode.childNodes[0].nodeValue;
                }
                else if (xmlSubNode.nodeType == 1 && xmlSubNode.nodeName == 'result') {
                    result = xmlSubNode.childNodes[0].nodeValue;
                }
            }
        }
    }
    
    return [method, result];
}

/*function responseHandlerExample() {
    // Make sure the request is loaded (readyState = 4)
    if (req.readyState == 4) {
        // Make sure the status is "OK"
        if (req.status == 200) {
            // EXAMPLE:
            var swappableSection = document.getElementById('notesSection');
            var notes = req.responseXML.getElementsByTagName('note');
            var str = '';
            for(i=0; i < notes.length; i++)
            {
                var noteNode = notes.item(i);
                if(noteNode != null && noteNode.hasChildNodes())
                {
                    str += noteNode.getAttribute('name') + ': ';
                    str += noteNode.firstChild.nodeValue + '<br />';
                }
            }
            swappableSection.innerHTML = str;
        }
        else {
            alert("There was a problem retrieving the XML data:\n" + req.statusText);
        }
    }
}*/
