
// VERSION 0.6
// - removed browser detection, because it can be easily faked
// VERSION 0.5
// - added prepareEnvelopeXML()
// VERSION 0.4
// - added MlyRequestManager.clearEnvelope()

MOZ_ERROR_NS = "http://www.mozilla.org/newlayout/xml/parsererror.xml";

// * ***********************************************************
// * MlyUtil is a small wrapper utility for browser dependant
// * functions. It is used for XML compatibility functions and
// * utility functions for browser detection and JS import.
//   *********************************************************** */
var MlyUtil = {
    parser:     null,
    serializer: null,

    importCode: function( path ) {
        var pretoken, posttoken;
        pretoken = '<script src="';
        posttoken = '" type="text/javascript"><\/script>';

        document.write( pretoken + path + posttoken );
    },

    enableExternalURI: function() {
        if ( window.XULElement ) {
            netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
        }
    }
};

function mlyHttpRequest() {
    var retval = null;
    if ( window.XMLHttpRequest ) {
        retval = new XMLHttpRequest();
    }
    else if (ActiveXObject ) {
        // older MS IE
        retval = new ActiveXObject( "Msxml2.XMLHTTP" );
    }
    return retval;
}

function mlyXSLTProcessor() {
    var retval = null;
    if ( window.XSLTProcessor ) {
        retval = new XSLTProcessor();
    }
    else if ( ActiveXObject ) {
        retval = new ActiveXObject( "Msxml2.XSLTemplate.3.0" );
        // retval = new ActiveXObject( "MSXML2.DOMDocument" );
    }

    return retval;
}

function mlyCreateDocument() {
    var retval = null;
    if ( document.implementation && document.implementation.createDocument ) {
        retval = document.implementation.createDocument(null,null,null);
    }
    else if ( ActiveXObject ) {
        retval = new ActiveXObject( "Msxml2.DOMDocument" );        
    }
    return retval;
}

function mlyParseXMLString( strXML ) {
    var retval = null;

    if ( window.DOMParser ) {
        if ( MlyUtil.parser == null ) {
            MlyUtil.parser = new DOMParser();
        }
        var doc;
        doc = MlyUtil.parser.parseFromString( strXML, "text/xml" );
        if ( doc.documentElement.namespaceURI == MOZ_ERROR_NS ) {
            var error = new Error();
            error.message = doc.documentElement.lastChild.firstChild.nodeValue;
            throw(error);
        }
        retval = doc;
    }
    else if ( ActiveXObject ) {
        doc = new ActiveXObject( "Msxml2.DOMDocument" );
        doc.loadXML( strXML );
        // unless an error is thrown
        retval = doc;        
    }

    return retval;
}


function mlySerializeNode(node) {
    var retval = null;
    
    if ( window.XMLSerializer ) {
        if ( MlyUtil.serializer == null ) {
            MlyUtil.serializer = new XMLSerializer();
        }
        retval = MlyUtil.serializer.serializeToString( node );
    }
    else if ( ActiveXObject ) {
        retval = node.xml;
    }

    return retval;
}

function mlyPrepareStylesheet(xslstring) {
    var retval = null;

    var xsltproc = mlyXSLTProcessor();
    var xslDoc = null; 
    if ( window.XSLTProcessor ) {
        xslDoc   = mlyParseXMLString( xslstring );
        xsltproc.importStylesheet( xslDoc );
    }
    else if ( ActiveXObject ) {
        xslDoc   = new ActiveXObject( "Msxml2.FreeThreadedDOMDocument.3.0" );
        // parse the object data.
        xslDoc.loadXML(xslstring);
        xsltproc.stylesheet = xslDoc; 
    }

    return xsltproc;
}

// * ***********************************************************
// * MlyXSLTManager is a organiser class to handle XSLT specific 
// * parts of an application.
// * MlyXSLTManager loads XSLT stylesheets from URIs or XML DOMs
// * and keeps the stylesheets in a managed list. Also, 
// * MlyXSLTManager keeps a global list of XSLT parameters, that
// * is applied to each stylesheet during runtime.
// * The MlyXSLTManager provides several high level functions
// * both stylesheet and parameter management. 
// * Finally, MlyXSLTManager has a display function that makes 
// * XSLT driven DHTML a bit easier.
// * *********************************************************** */
function MlyXSLTManager() {
    this.document  = document;
    this.stylelist = new Array();
    this.paramlist = new Array();
    this.lastview  =  null;

}

MlyXSLTManager.prototype = {
    stylelist: new Array(),
    paramlist: new Array(),
    document: null,
    lastview: null,

    setDisplayDoc: function( doc ) {
        if ( doc != null ) {
            this.document = doc;
        }
        else {
            this.document = document;
        }
    },

    setXSLParamList: function( paramlist ) {
        this.paramlist = paramlist;
    },

    findXSLParam: function( name ) {
        var i,id;
        id = -1;
        for ( i = 0; i < this.paramlist.length; i++ ) {
            if ( this.paramlist[i].name == name ) {
                id = i;
                break;
            }
        }
        return id;  
    },

    addXSLParam: function(name, value) {
        var id = this.findXSLParam( name );
        
        if ( id != -1 ) {
            this.paramlist[i].value = value;
        }
        else {
            var param = new Object();
            param.name = name;
            param.value = value;
            this.paramlist[this.paramlist.length] = param;
        }
    },

    removeXSLParam: function(name) {
        var id = this.findXSLParam( name );
        if ( id != -1 ) {
            var len = this.paramlist.length;
            // now shrink the paramlist
            if ( id != len - 1) {
                for ( i= id + 1; i < len - 1; i++ ) {
                    this.paramlist[i] = this.paramlist[i+1];
                }
            }
            this.paramlist.length = len - 1
        }
    },

    findStyle: function( name ) {
        var i,id;
        id = -1;
        for ( i = 0; i < this.stylelist.length; i++ ) {
            if ( this.stylelist[i].name == name ) {
                id = i;
                break;
            }
        }
        return id;  
    },

    prepareStyle: function(name) {
        var id = this.findStyle( name );
        if ( id != -1 ) {
            // alert( "found style object");
            var obj =  this.stylelist[id];
            if ( obj.proc == null ) {
                if ( obj.data == null ) {
                    // alert( "style object is not yet loaded");
                    this.loadStyleID(id);
                }

                if ( obj.data != null ) { // in case of errors while loading
                    // alert( "style object has been loaded");
                    obj.proc = mlyPrepareStylesheet( obj.data );
                    obj.data = null;
                }
            }
        }
    },
    
    loadStyle: function(name) {
        var id = this.findStyle( name );
        this.loadStyleID(id);
    },

    loadStyleID: function(id) {
        if ( id != -1 ) {
            var obj =  this.stylelist[id];
            if ( obj.data == null ) {
                // load only if XSLT has not been loaded yet.
                var xp =  mlyHttpRequest();
                
                xp.open( "GET", obj.uri, false );
                xp.send(null);

                // Status is 200 if files are loaded from the web
                // server if the files are stored on the client
                // machine, status will be 0.
                
                if ( xp.status == 200 || xp.status == 304 || xp.status == 0 ) {
                    // lets assume clients have a lot of memory ...
                    // alert( xp.status);
                    obj.data = xp.responseText;
                }
                
            }
        }
    },

    checkStylePrepared: function(id) {
        if ( id != -1 ) {
            var obj =  this.stylelist[id];
            if ( obj.proc == null ) {
                this.prepareStyle(obj.name);
            }
        }
    },

    addStyleURI: function(stylename, uri) {
        var style, doc, id, obj;
        id = this.findStyle( name );
        if ( id == -1 ) {
            obj = { name: stylename, uri: null, proc: null, data: null } 
            this.stylelist[this.stylelist.length] = obj;
        }
        else {
            obj = this.stylelist[id];
        }
        obj.uri = uri;
    },

    removeStyle: function(name) {
        var id = this.findStyle( name );
        if ( id != -1 ) {
            var len = this.stylelist.length;
            // now shrink the stylelist
            if ( id != len - 1) {
                for ( i= id + 1; i < len - 1; i++ ) {
                    this.stylelist[i] = this.stylelist[i+1];
                }
            }
            this.stylelist.length = len - 1
        }

    },

    prepareParameters: function(proc) {
        if ( proc != null ) {
            var i;

            if ( proc.setParameter ) {
                for ( i = 0; i < this.paramlist.length; i++ ) {
                    proc.setParameter( '', 
                                       this.paramlist[i].name, 
                                       this.paramlist[i].value );
                }
            }
            else if ( proc ) {
                for ( i = 0; i < this.paramlist.length; i++ ) {
                    proc.addParameter( this.paramlist[i].name,
                                       this.paramlist[i].value );
                }
            }
        }
    },

    getNewProcessor: function(obj) {
        var xslproc;
        if ( obj.proc.createProcessor ) {
            xslproc = obj.proc.createProcessor();
        }
        else {
            xslproc = obj.proc;
        }
        return xslproc;
    },
    
    // this function is required for other transformations (eg. for SOAP)
    transformToDocument: function(name,xmldoc) {
        if ( xmldoc == null )  { return null; }
        if ( name == null ) { return null; }

        var retval = null;
        var id = this.findStyle( name );

        if ( id != -1 ) {
            this.checkStylePrepared(id);
            var obj =  this.stylelist[id];

            var xslproc = this.getNewProcessor(obj);

            this.prepareParameters( xslproc );

            if ( window.XSLTProcessor ) {
                retval = xslproc.transformToDocument( xmldoc );
                xslproc.clearParameters(); // forget the parameters again!!
            }
            else {
                xslproc.input = xmldoc;
                xslproc.transform();
                retval = mlyParseXMLString(xslproc.output);
            }
        }
        
        return retval;
    },

    transformToFragmentExt: function( name, xmldoc, targetDoc, useDom ) {
        if ( xmldoc == null )  { return null; }
        if ( name == null ) { return null; }

        var retval = null;
        var id = this.findStyle( name );

        if ( id != -1 ) {
            this.checkStylePrepared(id);
            var obj =  this.stylelist[id];

            var xslproc = this.getNewProcessor(obj);

            this.prepareParameters( xslproc );

            var target = targetDoc;
            if ( target == null ) {
                target = this.document;
            }

            if ( window.XSLTProcessor ) {
                retval = xslproc.transformToFragment( xmldoc, target );
                xslproc.clearParameters(); // forget the parameters again!!
            }
            else if ( xslproc ) {
                xslproc.input = xmldoc;
                xslproc.transform();
                var txml = xslproc.output;
                
                if ( useDom ) {
                    retval = target.createDocumentFragment();
                    var tdoc =  mlyParseXMLString( txml );
                    var tn = target.importNode(tdoc.documentElement);
                    retval.appendChild(tn)
                }
                else {

                    // this limits the the use of stylesheets to
                    // produce document fragments. However, its the 
                    // only way to make IE be cooperative.

                    var tnode = target.createElement('div');
                    /// var tnode = target.createDocumentFragment();
                    tnode.innerHTML = txml;
                    retval = tnode.firstChild;
                }
            }
        }
        return retval;
    },

    // this function is needed for display XSL
    transformToFragment: function( name, doc ) {
        return this.transformToFragmentExt(name, doc, null, 0);
    },

    // transforms doc and uses the result fragment for display
    
    ///* The document fragment must contain only a single element:
    // * the body element. This is required, so we exchange the entire body 
    // * element of the data structure.
    // */
    display: function( name, tagid, doc ) {
        var parent, oldbody,frag;
        frag = this.transformToFragment( name, doc );
        if ( frag != null ) {
            this.lastview  = name;
            this.lasttagid = tagid
            oldbody = document.getElementById( this.lasttagid );  
            if ( oldbody != null ) {
                parent = oldbody.parentNode;
                parent.replaceChild( frag, oldbody );
            }
        }
    },

    displayList: function( name, tagid, doc, locwhere ) {
        var parent, oldbody, frag;
        frag = this.transformToFragment( name, doc );
        if ( frag != null ) {
            var listbody = document.getElementById( tagid );  
            if ( listbody != null ) {
                if ( locwhere ) {
                    listbody.insertBefore( frag, listbody.firstChild );
                }
                else {
                    listbody.insertBefore( frag, null );
                }
            }
        }
    },

    displayAfter: function( name, tagid, doc ) {
        var parent, oldbody,frag;
        frag = this.transformToFragment( name, doc );
        if ( frag != null ) {
            var listbody = document.getElementById( tagid );  
            if ( listbody != null ) {
                listbody.parentNode.insertBefore( frag, listbody.nextSibling );
            }
        }
    },

    redisplay: function( doc ) {
        var parent, oldbody,frag,name;
        name = this.lastview;
        frag = this.transformToFragment( name, doc );
        if ( frag != null ) {
            oldbody = document.getElementById( this.lasttagid );  
            parent = oldbody.parentElement;
            parent.replaceChild( frag, oldbody );
        }        
    },

    undisplay: function( tagid ) {
        var elem = document.getElementById( tagid );
        if ( elem != null ) {
            elem.parentNode.removeChild( elem );
        }
    },

    changeStyle: function( tagId, newStyle ) {
        var cssElement = document.getElementById(tagId);
        if ( cssElement != null ) {
            cssElement.className = newStyle;
        }
        else {
            // alert( 'no cssElement. Tag id was: ' + tagId );
        }
    },

    switchStyle: function( tagId, aStyle, bStyle ) {
        var cssElement = document.getElementById(tagId);
        if ( cssElement.className == aStyle ) {
            cssElement.className = bStyle;
        }
        else {
            cssElement.className = aStyle;
        }
    },

    switchBlocks: function( aTagId, bTagId, aStyle, bStyle ) {
        var cssAElement = document.getElementById(aTagId);
        var cssBElement = document.getElementById(bTagId);
        if ( cssAElement.className == aStyle ) {
            cssAElement.className = bStyle;
            cssBElement.className = aStyle;
        }
        else {
            cssAElement.className = aStyle;
            cssBElement.className = bStyle;
        }
    },

    getStyleName: function( tagId ) {
        var cssElement = document.getElementById(tagId);
        if ( cssElement != null ) {
            return cssElement.className;
        }
        return null;
    },

    testStyle: function( tagId, style ) {
        var cssElement = document.getElementById(tagId);
        var retval = 0;

        if ( cssElement == null ) {
            return null;
        }
        
        if ( cssElement.className == style ) {
            retval = 1;
        }
        return retval;
    },

    testStyleList: function( tagId, aStyleList ){
        var cssElement = document.getElementById(tagId);
        var retval = 0;
        if ( cssElement == null ) {
            return null;
        }
        var name = cssElement.className;
        for( var i=0; i<aStyleList.length; i++ ) {
            if ( name == aStyleList[i] ) {
                retval = i+1;
                break;
            }
        }
        return retval;        
    }
};

function MlyRequestManager(baseuri) {
    this.baseURI = baseuri;
    this.async   = false;
    this.sendType= "GET";
}

MlyRequestManager.prototype = {
    baseURI: "",
    sendType: "GET",
    requestURI: "",

    envelope: null,
    entries: 0,

    rpcresult: null,
    rpcstatus: 0,

    async: false,

    prepareURI: function(URI, bSecure) {
        var tProto = "http://";
        if ( bSecure == true ) {
            tProto = "https://";   
        }
        this.requestURI = tProto + this.baseURI + URI;

        // alert( this.requestURI );
    },
    
    addToEnvelope: function(pName, pValue) {
        if ( this.envelope == null ) {
            this.envelope = new Array();
        }

        this.entries++;
        var len = this.envelope.length;
        this.envelope[len] = pName;
        this.envelope[len + 1] = pValue;
    },
    
    prepareEnvelope: function() {
        var i = 0;
        var len = 2 * this.entries;
        var retval = "";
        var aParams = new Array();
        var str;

        if ( len ) {
            for ( i = 0; i < len; i = i+2 ) {
                str = encodeURIComponent(this.envelope[i]) 
                    + "=" 
                    + encodeURIComponent( this.envelope[i + 1] );
                aParams.push( str );
            }
            retval = aParams.join('&');
        }
        return retval;
    },

    prepareEnvelopeXML: function() {
        var i = 0;
        var len = 2 * this.entries;
        var retval = "<envelope>";
        var aParams = new Array();
        var str;

        if ( len ) {
            for ( i = 0; i < len; i = i+2 ) {
                str = "<param>"
                    + "<name>"  + this.envelope[i] + "</name>"  
                    + "<value>" + this.envelope[i + 1] + "</value>"
                    + "</param>";
                aParams.push( str );
            }
            retval = retval + aParams.join('');
        }

        retval = retval + "</envelope>";
        return retval;
    },

    clearEnvelope: function() {
        this.envelope = null;
    },

    request: function(envelope, bXML) {
        var httpreq = mlyHttpRequest();

        var tEnvelope = envelope;

        // in case of asynchronous requests we'll have to set 
        // the finalizer callback.
        if ( tEnvelope == null && this.envelope != null ) {
            // bXML = false;
            if ( bXML == false ) {
                tEnvelope = this.prepareEnvelope();
            }
            else {
                tEnvelope = this.prepareEnvelopeXML();
            }
        }

        if ( tEnvelope != null ) {
            this.sendType = "POST";
        }

        // alert( "GET URL " + this.requestURI );
        // alert( tEnvelope );

        httpreq.open( this.sendType, this.requestURI, this.async );

        if ( tEnvelope != null ) {
            if ( bXML == false ) {
                httpreq.setRequestHeader( "Content-Type", 
                                          "application/x-www-form-urlencoded" );
            }
            else {
                httpreq.setRequestHeader( "Content-Type", 
                                          "text/xml" );
            }
        }

        httpreq.send(tEnvelope);


        // alert( "HTTP STATUS: " + httpreq.status );

        this.rpcstatus = httpreq.status;
        this.rpcresult = mlyParseXMLString( httpreq.responseText );
        this.requestURI = "";   
        this.sendType = "GET";

        this.envelope = null;
        this.entries = 0;
    },

    fetchURI: function( URI ) {
        this.prepareURI( URI );
        this.request( null, false );
        return this.rpcresult;
    }
};

