/**
 * Hilfsfunktionen
 * Author: <a href="mailto:vsctech@fiz-chemie.de>Eike Jordan</a>
 *
 *
 * $HeadURL: http://trac.gruen.fiz-chemie.de/svn/vs/vscms/trunk/src/vsengine/static/javascript/DOMUtil.js $
 * $Revision: 11074 $
 *
 * Copyright 2001-2006 FIZ CHEMIE Berlin
 */

/**
 * String trim similar to Java String.trim()
 */
if (! String.prototype) {
    // TODO: Fallback Strategie fuer Browser Support. Diese Meldung muss nicht sein.
    alert("Red alert!!! This nice browser has no prototype capability. Well, that's groovy.");
}
String.prototype.trim = function(s) {
    var s = this;
    while (s.substring(0,1) == ' ') {
        s = s.substring(1, s.length);
    }
    while (s.substring(s.length-1, s.length) == ' ') {
        s = s.substring(0,s.length-1);
    }
    return s;
};

/**
 * static URL helper functions
 * Should behave exactly like the Java object de.vs.http.URLCoder
 */
var URLCoder = new Object();

URLCoder.format = function(/*String*/s) {
    var decimalFormat = "_00000";
    var s = String(s.charCodeAt(0));
    var dL = decimalFormat.length;
    var sL = s.length;
    var count = dL - sL - 1;
    while (sL < dL) {
        s = decimalFormat.charAt(count) + s;
        sL = s.length;
        count--;
    }
    return s;
};

URLCoder.encode = function(/*String*/string, /*Boolean*/ignoreCase) {
    var s = string.trim();
    if (ignoreCase != 'undefined' && ignoreCase == true) {
        s = s.toLowerCase();
    }
    var length = s.length;
    var buf = "";

    for (var i = 0; i < length; i++) {
        var c = s.charAt(i);

        buf += URLCoder.encodeChar(c);
    }

    return buf;
};

URLCoder.encodeChar = function(c) {

    if ((c >= 'A') && (c <= 'Z')) {
        return c;
    }

    if ((c >= 'a') && (c <= 'z')) {
        return c;
    }

    if ((c >= '0') && (c <= '9')) {
        return c;
    }

    return URLCoder.format(c);
};

// Remove trailing semicolon from string.
URLCoder.chopSemicolon = function(/*String*/s) {
    if (s[s.length -1] == ';') {
        s = s.substring(0,s.length -1);
    }
    return s;
};

/**
 * static DOM helper functions
 */
var DOMUtil = new Object();

/**
 * create an Element either in HTML or in XHTML context
 */
DOMUtil.createElement = function(/*String*/name) {
    var node;
    if (typeof(document.createElementNS) == "function") {
        node = document.createElementNS("http://www.w3.org/1999/xhtml", name);
    } else if (document.createElement) {
        node = document.createElement(name);
    } else {
        node = null;
    }
    return node;
};

// Append all text children of elementNode
DOMUtil.collectText = function(elementNode) {
   var s = "";
   var children = elementNode.childNodes;
   for (i=0; i < children.length; i++) {
       child = children[i];
       if (child.nodeType == 3) {
           s +=  child.nodeValue;
       }
   }
   return s;
};

/**
 * Return the text content of a element
 * in a browser independent way.
 * See org.w3c.dom.Node#getTextContent.
 */
DOMUtil.getTextContent = function (elementNode) {
    var result;
    if (elementNode.text) {
          result = elementNode.text;        // IE
    } else if (elementNode.textContent) {
        result = elementNode.textContent; // W3
    } else if (elementNode.innerText) {
        result = elementNode.innerText;   // Opera
    } else if (elementNode.getTextContent) {
        result = elementNode.getTextContent();
    } else if (elementNode.childNodes) {        // Konqueror
        result = DOMUtil.collectText(elementNode);
    }
    return result;
};

/**
 * get effective style sheet property value
 * more infos at http://www.oreillynet.com/lpt/a/3769
 */
DOMUtil.getElementStyle = function(obj, IEStyleProp, CSSStyleProp) {
//  ToDo: Safari, Opera Support
  var elem = obj;
  if (elem.currentStyle) {
    return elem.currentStyle[IEStyleProp];
  } else if (window.getComputedStyle) {
    var compStyle = window.getComputedStyle(elem, "");
    return compStyle.getPropertyValue(CSSStyleProp);
  }
  return "";
};

/**
 * return object array of elements with a certain className
 */
DOMUtil.getElementsWithClassName = function(/*String*/elementName, /*String*/className, /*String*/nameSpace) {
    var allElements;
    if (!nameSpace) {
        logger.trace("[DOMUtil.getElementsWithClassName] - document.getElementsByTagName");
        allElements = document.getElementsByTagName(elementName);
    } else {
        logger.trace("[DOMUtil.getElementsWithClassName] - document.getElementsByTagName");
        allElements = document.getElementsByTagNameNS(nameSpace, elementName);
    }
    logger.trace("[DOMUtil.getElementsWithClassName] - allElements := [" + allElements.length + "]");
    var elemColl = new Array();
    for (i = 0; i < allElements.length; i++) {
        var currentElement = allElements[i];
        var currentClassNames = currentElement.className.split(" ");
        for (var currentClassName in currentClassNames) {
            logger.trace("[1][DOMUtil.getElementsWithClassName] - currentClassName := [" + currentClassNames[currentClassName] + "][" + className + "][" + currentElement.className + "][" + currentElement.getAttribute("class") + "]");
            if (currentClassNames[currentClassName] == className) {
                logger.trace("[2][DOMUtil.getElementsWithClassName] - currentClassName := [" + currentClassNames[currentClassName] + "][" + className + "][" + currentElement.className + "][" + currentElement.getAttribute("class") + "]");
                elemColl[elemColl.length] = currentElement;
            }
        }
    }
    logger.trace("[DOMUtil.getElementsWithClassName] - " + elemColl.length);
    return elemColl;
}

DOMUtil.url = window.location.href;
DOMUtil.server = window.location.protocol + "//" + window.location.host;

DOMUtil.getScope = function() {
    var scope = new String();
    var Search = /\/vsengine\/topics\/..\/(\w*)\/(.*)/;
    var matches = Search.exec(DOMUtil.url);
    if (!matches) {
        scope = "vlu";
    } else {
        scope = matches[1];
    }
    return scope;
};

DOMUtil.setparameter = function(target, ref, parameters) {
    var target = document.getElementById(target);
    var data = DOMUtil.server + Properties.media + ref + "?" + parameters;
    flash.setAttribute("data", data);
};

/**
 * create info message with download link for missing plugins
 * mode == "DOM" @return paragraph object
 * mode == "HTML" @return paragraph string
 */
DOMUtil.createMsg = function(/*String*/name, /*String*/mode) {
    var _plugin = Common.Plugins[name];
    var msg = "";
    if (mode == 'DOM') {
        msg = DOMUtil.createElement("p");
        var text = document.createTextNode(_plugin.msg[document.firstChild.lang] + " ");
        var link = DOMUtil.createElement("a");
        link.setAttribute("href", _plugin.download);
        link.appendChild(document.createTextNode(_plugin.download));
        msg.appendChild(text);
        msg.appendChild(link);
    } else if (mode == 'HTML') {
        msg += '<p>' + _plugin.msg[document.firstChild.lang] +
            ' <a href="' + _plugin.download + '">' + _plugin.download + '</a></p>';
    } else {
        logger.error("Unsupported mode " + mode);
        return "";
    }
    return msg;
};
/**
 * target: The id of the anchor link, where the object is appended.
 * ref: The objectid of the flash
 * parameters: Parameter string passed to the flash
 * altref: The objectid of the alt image
 */
DOMUtil.createFlashFromObjectid = function(target, ref, parameters, width, height, background, altref) {
    var href = Properties.media + ref;
    var althref = Properties.media + altref
    DOMUtil.createFlash(target, href, parameters, width, height, background, "false", althref, "");
};

/**
 * target: The id of the anchor link, where the object is appended.
 * ref: The objectid of the flv file
 * width, height: width and height of the video. Some px are added for the player. NOTE: Not yet implemented.
 * altref: The objectid of the alt image
 */

DOMUtil.createFlashVideo = function(target, ref, width, height, background, altref, subtitle) {

    var flv = Properties.media + ref;
    var althref;

    if (altref != "/vsengine/images/missing_flv_altref.png") {
        althref = Properties.media + altref;
    }
    else {
        althref = altref;
    }

    (subtitle) ? subtitle = "&captions=" + Properties.media + subtitle + "&usecaptions=true" : subtitle = "";

    //Groesse des FLV-Players mit einbeziehen, da sonst das Video verzerrt wird
    height = parseInt(height) + 20;

    var parameters = "";
    var flashvars = "file=" + flv + "&image=" + althref + "&config=/vsengine/applets/mediaplayer.xml" + subtitle;

    DOMUtil.createFlash(target, Properties.codebase + "/mediaplayer.swf", parameters, width, height, background, "true", althref, flashvars);
};

/**
 * target: The id of the anchor link, where the object is appended.
 * ref: The url of the flash
 * parameters: Parameter string passed to the flash
 * allowfullscreen: Value of the flash allowfullscreen attribute
 * altref: The url of the alt image
 */
DOMUtil.createFlash = function(target, ref, parameters, width, height, background, allowfullscreen, altref, flashvars) {
    logger.trace("createFlash [1] " + parameters);
    parameters = URLCoder.chopSemicolon(parameters);
    logger.trace("createFlash [2] " + parameters);

    var flashtarget = document.getElementById(target);
    logger.trace(target);
    var altimg = document.getElementById("img-" + target);

    if (document.all) {
        var flash = '<object ' +
                    'data="' + ref + '?' + parameters + '" ' +
                    'type="application/x-shockwave-flash" ' +
                    'id="' + ref + '" ' +
                    'width="' + width + '" ' +
                    'height="' + height + '" >';
        flash += '<param name="quality" value="high"/>';
        flash += '<param name="bgcolor" value="' + background + '"/>';
        flash += '<param name="base"    value="' + Properties.media + '/"/>';
        flash += '<param name="movie" value="' + ref + '?' + parameters + '"/>';
        flash += '<param name="allowfullscreen" value="' + allowfullscreen + '"/>';
        flash += '<param name="wmode" value="transparent"/>';
        if (flashvars) {
            flash += '<param name="flashvars" value="' + flashvars + '" />';
        }

        if (altimg) {
            flash += '<img href="' + altimg.src + '" alt="' + altimg.alt + '"/>';
            flash += DOMUtil.createMsg("Flash", "HTML");
        }
        flash += '</object>';
        if (document.write) {
            if (altimg) {
                flashtarget.removeChild(altimg);
            }
            document.write(flash);
        } else {
            flashtarget.innerHtml = flash;
        }

    } else {
        var flash = DOMUtil.createElement("object");
        var data = DOMUtil.server + ref + "?" + parameters;
        var type = "application/x-shockwave-flash";
        var id = ref;

        flash.setAttribute("data", data);
        flash.setAttribute("type", type);
        flash.setAttribute("id", id);
        flash.setAttribute("width", width + "px");logger.trace(width);
        flash.setAttribute("height", height + "px");logger.trace(height);
        flash.setAttribute("wmode", "opaque");
        if (flashvars) {
            var param = DOMUtil.createElement("param");
            param.setAttribute("name", "flashvars");
            param.setAttribute("value", flashvars);
            flash.appendChild(param);
        }

        var names = ["quality", "wmode", "bgcolor", "base", "movie", "allowfullscreen"];
        var values = ["high", "opaque", background, Properties.media, ref + "?" + parameters, allowfullscreen];
        for (name in names) {
            var param = DOMUtil.createElement("param");
            param.setAttribute("name", names[name]);
            param.setAttribute("value", values[name]);
            flash.appendChild(param);
        }
        if (altref) {
            var img = DOMUtil.createElement("img");
            img.setAttribute("src", altimg.src);
            img.setAttribute("alt", altimg.alt);
            flash.appendChild(img);
            flash.appendChild(DOMUtil.createMsg("Flash", "DOM"));
        }

        flashtarget.appendChild(flash);
        if (altimg) {
            flashtarget.removeChild(altimg);
        }
    }
};

DOMUtil.createVRMLEmbed = function(/*VRML*/model) {
    if (! model instanceof VRML) {
        logger.error("Wrong type for " + model);
        return;
    }

    var vrmlparent = document.getElementById(model.id);
    var altimg = document.getElementById("img-" + model.id);

    logger.debug(typeof(vrmlparent));
    logger.debug(typeof(altimg));
    var embed = '<embed src="' + model.source +
            '" type="' + model.mimetype +
            '" width="' + model.width +
            '" height="' + model.height +
            '">';
    //embed += '<img style="border: solid 4px red" src="' + altimg.src + '" alt="' + altimg.alt + '"/>';
    embed += '</embed>';
    vrmlparent.removeChild(altimg);
    vrmlparent.innerHTML = embed;
};

DOMUtil.createDirectorObject = function(/*Director*/model) {
    if (! model instanceof Director) {
        logger.error("Wrong type for " + model);
        return;
    }

    var box = document.getElementById(model.id);
    var altimg = document.getElementById("img-" + model.id);

    var embed = '<object ' +
            ' codebase="' + model.codebase +
            '" classid="' + model.classid +
            '" id="' + model.source +
            '" width="' + model.width +
            '" height="' + model.height +
            '">';
    embed += '<param name="src" value="' + model.source + '"/>';
    embed += '<embed ' +
        ' pluginspage="' + model.pluginspage +
        '" src="' + model.source +
        '" type="' + model.mimetype +
        '" width="' + model.width +
        '" height="' + model.height +
        '">' +
        '<div class="caution">' +
        'Shockwave for Director required! Please visit ' + model.pluginspage +
        '</div>' +
        '</embed>';
    embed += '</object>';
    box.removeChild(altimg);
    box.innerHTML = embed;
};


/**
  * Return a XMLHttpRequest for the different browser.
  * Return null, if creation failed.
  *
  * Tested with Firefox 1.5.0.6/Linux, Opera 9.0/Linux, IE 6.0 SP2/Win2000
  */
DOMUtil.createXMLHttpRequest = function() {
     var xmlHttp;
     if (typeof(XMLHttpRequest) != 'undefined') {
        xmlHttp = new XMLHttpRequest();
    }
    if (!xmlHttp) {
        try {
            xmlHttp  = new ActiveXObject("Msxml2.XMLHTTP");
        } catch(e) {
            try {
                xmlHttp  = new ActiveXObject("Microsoft.XMLHTTP");
            } catch(e) {
                xmlHttp  = null;
            }
        }
    }
    return xmlHttp;
};

/**  Load url asynchronous using a XMLHttpRequest
  *  Parameters:
  *         url: The URL to load
  *         onload:  Callback, if the content is loaded
  *         onerror: Callback, if something goes wrong - optional
  *                  Inside the callbacks, the request object is available as this.req
  *
  *  TODO Parameter for method (POST/GET) and mime type overriding
  */
DOMUtil.ContentLoader = function(url,onload,onerror) {
    this.url = url;
    this.req = null;
    this.onload = onload;
    this.onerror = (onerror) ? onerror : this.defaultError;
    this.loadXMLDoc(url);
};

DOMUtil.ContentLoader.prototype = {
    loadXMLDoc:function(url) {
        this.req = DOMUtil.createXMLHttpRequest();

        if (this.req) {
            try {
                var loader = this;
                this.req.onreadystatechange = function() {
                    loader.onReadyState.call(loader);
                }
                // Not supported by Konqueror and IE
                // Make sure, the server returns text/xml
                if (this.req.overrideMimeType) {
                    this.req.overrideMimeType("text/xml");
                }
                this.req.open('GET',url,true);
                this.req.send(null);
             } catch (err) {
                 alert(err);
                 this.onerror.call(this);
             }
        }
    },

    onReadyState:function() {
        var req = this.req;
        var ready = req.readyState;
        if (ready == 4) { // Complete
            var httpStatus = req.status;
            if ((httpStatus == 200)||(httpStatus == 0)) {
                this.onload.call(this);
            } else {
                this.onerror.call(this);
            }
        }
    },

    defaultError:function() {
        alert("Error fetching data from " +  this.url
            + "\nreadyState: " + this.req.readyState
            + "\nstatus: " + this.req.status
            + "\nheaders: " + this.req.getAllResponseHeaders());
    }
};

/**
 * VRML object
 *   constructor with default values
 *   all other values must be set after initialization
 */
function VRML(/*String*/id, /*String*/source, /*Integer*/width, /*Integer*/height) {
    this.id = id;
    this.source = source;
    this.width = width;
    this.height = height;
    this.mimetype = "model/vrml";
    this.html = '<embed src="' + this.source +
            '" type="' + this.mimetype +
            '" width="' + this.width +
            '" height="' +  this.height +
            '">' +
            '</embed>';
};

VRML.prototype = {

    getHTML:function() {
        return this.html;
    },
    writeHTML:function() {
        var embed = this.getHTML();

        $("#" + this.id).html(embed);
//        $("#" + this.id).css({
//            "height": this.height + "px",
//            "width": this.width + "px"
//        });
    },
    bindHandler:function(modelNumber) {
        var imageID = "img-" + this.id;
        var width = browser.id.IE ? $("#" + this.id).width() - 3 : $("#" + this.id).width();
        var height = browser.id.IE ? $("#" + this.id).height() -3 : $("#" + this.id).height();
        var top = Math.round((height - 66) / 2);

        logger.debug("bindHandler called " + this.id);

        // Die Hoehe fuer IE/Opera zuruecksetzten!
//        if (browser.id.IE || browser.id.OP) {
//            var mb = "-" + (top-28) + "px";
//
//            $("#" + this.id).css({
//                "text-align": "center",
//                "position": "relative",
//                "margin-bottom": mb,
//                "height": height + "px"
//            });
//        }

        // selektiert den div-Wrapper
        if (browser.id.IE || browser.id.OP) {
            $("#" + this.id).bind("mouseover", function(e) {
                $("#" + this.id).css({
                    "cursor": "hand"
            });
            }).bind("mouseout", function(e) {
                $("#" + this.id).css({
                    "cursor": "auto"
                });
            });
        } else {
            $("#" + this.id).bind("mouseover", function(e) {
                $("#" + this.id).css({
                    "cursor": "pointer"
            });
            }).bind("mouseout", function(e) {
                $("#" + this.id).css({
                    "cursor": "auto"
                });
            });
        }

        $("#" + this.id).bind("click", function(e) {
            models[modelNumber].writeHTML();
        });
    }
};
/**
 * Director object
 *   constructor with default values
 *   all other values must be set after initialization
 */
function Director() {
    this.id = null;
    this.source = null;
    this.width = null;
    this.height = null;
    this.image = null;
    this.mimetype = "application/x-director";
    this.pluginspage = "http://www.macromedia.com/shockwave/download/";
    this.codebase = "http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=7,0,2,0";
    this.classid = "clsid:166B1BCA-3F9C-11CF-8075-444553540000";
    this.title = null;
};

Director.prototype = {

    setId:function(/*String*/id) {
        this.id = id;
    },

    setSource:function(/*String*/source) {
        this.source = source;
    },

    setImage:function(/*Object*/image) {
        this.image = image;
    },

    setTitle:function(/*String*/title) {
        this.title = title;
    },

    setWidth:function(/*String*/width) {
        this.width = width;
    },

    setHeight:function(/*String*/height) {
        this.height = height;
    },

    setMimetype:function(/*String*/mimetype) {
        this.mimetype = mimetype;
    }
};


/**
 * abstract applet class
 */
function AbstractApplet(
             /*String*/id, /*String*/objectid, /*String*/mainclass,
             /*Integer*/width, /*Integer*/height) {

    this.id = null;       // id des parents in der HTML Ausgabedatei, dessen Inhalt durch das Applet/Molekuel ersetzt wird
    this.objectid = null; // objectid e.g. /vsc/de/ch/6/ac/punktsymmetrie/kristallsymmetrie/applet/kristallsymmetrie.jar
    this.mainClass = null;
    this.classid = null   // 'clsid:8AD9C840-044E-11D1-B3E9-00805F499D93' fuer den IE, 'java:this.applet.ClassName' fuer alle anderen
    this.mimetype = "application/x-java-applet" // default mimetype fuer Applets
    this.width = null;
    this.height = null;
    this.archive = null;  // /path/to/archive/archive.jar fuer den IE, archive.jar fuer alle anderen
    this.code = null;     // nur IE: this.applet.ClassName
    this.codebase = null; // nur alle Nicht-IE: /path/to/archive/
    this.parameters = {
    };
//    this.oidPattern = "(\/vsc(/\\w+)+)/((\\w+)\\.(\\w+))"; // Pattern fuer objectid
    this.oidPattern = "((/\\w+)+)/((\\w+)\\.(\\w+))"; // allgemeines Pattern fuer objectid
    this.errors = {}; // Hash fuer Fehlerhandling und akkurate Meldungen beim Serialisieren
};

AbstractApplet.prototype = {

    setId:function(/*String*/id) {
        this.id = id;
    },

    setCode:function(/*String*/code) {
        this.code = code;
    },

    setCodeBase:function(/*String*/codebase) {
        this.codebase = codebase;
    },

    setArchive:function(/*String*/archive) {
        this.archive = archive;
    },

    setObjectID:function(/*String*/objectid) {
        var re = new RegExp(this.oidPattern);
        var match = re.exec(objectid);
        var debug = logger.getLevel();

        this.objectid = objectid;

// reaktivieren ??
//         if (match == null) {
//             logger.error("wrong objectid " + objectid);
//             this.setError("objectid", "wrong objectid " + objectid);
//         } else {
//             this.objectid = objectid;
//         }
    },

    /**
     * RegexFilter fuer die benoetigten Komponenten aus der objectid
     *   - path -- der Pfad
     *   - file -- der Dateiname mit Extension; fuer den parameter "archive"
     *   - extension -- Dateiendung
     */
    getOIDComponent:function(/*String*/component) {
        var oidComponent = null;
        var re = new RegExp(this.oidPattern);// TODO: oidPattern ist eigentlich fuer echte objectids '/vsc/...'
        var match = re.exec(this.objectid);
            //      match[1] = path
            //      match[3] = file
            //      match[5] = extension

        if (this.objectid == null) {
            // Leider wird der AppletConstructor bereits
            // beim Initialisieren eines Molecules ohne Parameter
            // aufgerufen
        } else if (match == null) {
            logger.error("No Match " + re + " " + this.objectid);
        } else if (component == 'codebase') {
            logger.debug("return match[1] " + match[1]);
            oidComponent = match[1];
        } else if (component == 'archive') {
            logger.debug("return match[3] " + match[3]);
            oidComponent = match[3];
        } else if (component == 'extension') {
            logger.debug("return match[5] " + match[5]);
            oidComponent = match[5];
        } else {
            logger.error("no such component " + component);
        }
        return oidComponent;
    },

    /**
     * Browserspezifische Einstellungen
     *   - classid
     *   - code
     *   - codebase
     *   - archive
     */
    setEnvironment:function(/*String*/objectid) {

        this.setObjectID(objectid);

        if (browser.id.IE) {
            this.setClassID("clsid:8AD9C840-044E-11D1-B3E9-00805F499D93");
            this.setArchive(this.objectid);
            this.setCode(this.mainClass);
        } else {
            this.setClassID("java:" + this.mainClass);
            this.setArchive(this.getOIDComponent("archive"));
            this.setCodeBase(this.getOIDComponent("codebase"));
        }
    },

    setMainClass:function(/*String*/mainclass) {
        this.mainClass = mainclass;
    },

    setClassID:function(/*String*/classid) {
            this.classid = classid;
    },

        // title / image alt attribute
    setTitle:function(/*String*/title) {
        this.title = title;
    },

        // applet width
    setWidth:function(/*Integer*/width) {
        this.width = parseInt(width);
    },

        // applet height
    setHeight:function(/*Integer*/height) {
        this.height = parseInt(height);
    },

        // applet background color
    setBackground:function(/*String*/background) {
        this.background = background;
    },

    /**
     * Add parameters to the applet
     * @param parameters - named Array of parameters {"name": "value", "name": "value"}
     */
    setParameters:function(/*Hash*/parameters) {
        for (var param in parameters) {
            logger.debug(param + " - " + parameters[param]);
            this.setParameter(param, parameters[param]);
        }
    },

    /**
     * Add parameter, replace markup chars by numeric entities
     */
    setParameter:function(/*String*/name, /*String*/value) {
        this.parameters[name] = value.replace(/>/g, "&#62;")
                                     .replace(/</g, "&#60;")
                                     .replace(/"/g, "&#34;");
    },

    getParameter:function(/*String*/name) {
        return this.parameters[name];
    },

    setError:function(/*String*/name, /*String*/value) {
        this.errors[name] = value;
    },

    setMimetype:function(/*String*/mimetype) {
        this.mimetype = mimetype;
    },

    setHTML:function(/*String*/html) {
        this.html = html;
    },

    getHTML:function() {
        return this.html;
    },

   /**
    * bind MouseHandler to HTML wrapper
    */
    bindHandler:function(appletNumber) {
        if (browser.plugins.java()) {
            logger.debug("select wrapper div " + this.id);
            $("#" + this.id).bind("mouseover", function(e) {
                var imageID = "img-" + this.id;

                var width = $("#" + this.id).width() - 2;
                var height = $("#" + this.id).height() - 2;

                var top = Math.round((height - 66) / 2);
                var left = Math.round((width - 66) / 2);
                var replacement = '<div style="width:' + width + 'px;height:' + height + 'px;' +
                                      ' text-align: center;border: solid 1px #FFCC00;' +
                                      ' font-size: 2em; font-weight: bold"' +
                                      ' id="load' + appletNumber + '">' +
                                      '<img src="/vsengine/images/applet-loader.gif"' +
                                          ' style="position: relative; top: ' + top + 'px;"/>' +
                                  '</div>';
                logger.trace("replace image " + imageID + " with " + replacement);
                $("#" + imageID).replaceWith(replacement);

                logger.debug("activate applet");
                $("#load" + appletNumber).fadeOut(1000, function() {
                    applets[appletNumber].writeHTML();
                });

            });

        }
    },

    /**
     * replace content of HTML wrapper with applet node; unbind all mouse-handlers
     */
    writeHTML:function() {
        var applet = this.getHTML();

        if (this.renderer == 'chime' && browser.plugins.chime()) {
            $("#" + this.id).html(applet);
            $("#" + this.id).css({
                "height": this.height + "px",
                "width": this.width + "px"
            });
        } else {
            if (browser.plugins.java()) {
                // convert applet string to DOM node
                //var object = (new DOMParser()).parseFromString(applet, "text/xml").documentElement;
                $("#" + this.id).unbind("mouseover mouseout click").html(applet);
                $("#" + this.id).css({
                    "height": this.height + "px",
                    "width": this.width + "px"
                });
            } else {
                logger.error("Can't display applet " + this.id + ". No suitable plugin found.");
            }
        }
    },

    appendHTML:function() {
        var applet = this.getHTML();

        if (browser.plugins.java()) {
            $("#" + this.id).append(applet);
        }
    }
};

/**
 * Class for "real" applets
 */
Applet.prototype = new AbstractApplet();
Applet.constructor = Applet;

function Applet(/*String*/id, /*String*/objectid, /*String*/mainclass,
                /*Integer*/width, /*Integer*/height, /*Array*/parameters) {

    this.setMainClass(mainclass);
    this.setEnvironment(objectid);
    this.setId(id);
    this.setWidth(width);
    this.setHeight(height);
    this.setParameters(parameters);

    var _html = "";
    if (browser.id.IE) {
        _html += '<object classid="' + this.classid + '" id="applet-' + this.id + '" type="' + this.mimetype +
                       '" height="' + this.height + '" width="' + this.width + '">';
        _html += '  <param name="code" value="' + this.code + '"/>';
        _html += '  <param name="archive" value="' + this.archive + '"/>';
    } else {
        _html += '<object classid="' + this.classid + '" id="applet-' + this.id + '" ' +
                      'archive="' + this.archive + '" ' +
                      'type="' + this.mimetype + '" height="' + this.height + '" width="' + this.width + '">';
        _html += '  <param name="archive" value="' + this.archive + '"/>';
        _html += '  <param name="codebase" value="' + this.codebase + '"/>';
    }

    for (var param in this.parameters) {
        _html += '  <param name="' + param + '" value="' + this.parameters[param] + '"/>';;
    }
    _html += '</object>';
    this.setHTML(_html);

};
//http://wwwchem.uwimona.edu.jm/spectra/JSpecView/nmranim/aspirinJ/aspirin.html
/**
 * Class for Molecules (JmolApplet/Chime)
 */
Molecule.prototype = new Applet();
Molecule.constructor = Molecule;
Molecule.prototype.parent = Applet.constructor;

function Molecule(/*String*/id, /*Integer*/width, /*Integer*/height,
                  /*String*/model, /*String*/script, /*String*/scope,
                  /*String*/renderer, /*Array*/parameters) {

    var objectid = Properties.codebase + "/JmolApplet.jar";
    var mainclass = "JmolApplet";

    this.renderer = "chime";
    this.scope = scope;
    this.model = model;
    this.script = null;
    this.extension = this.getExtension();
    this.replacement = null;
    this.setRenderer(renderer);
    this.setDefaultParameters();
    // similar to super() in Java
    this.parent.call(this, id, objectid, mainclass, width, height, parameters);

    this.setScript(script);

    var _html = "";
    if (this.renderer == 'chime') {
        logger.error("embed id applet-" + this.id);
        // setMimetype only if renderer == "chime"
        this.setExtensionMimeType(this.extension);
        _html += '<embed name="applet-' + this.id + '" id="applet-' + this.id + '" type="' + this.mimetype + '"' +
                     ' src="' + Properties.media + this.model + '"' +
                     ' height="' + this.height + '" width="' + this.width + '"';
        for (var param in this.parameters) {
            _html += ' ' + param + '="' + this.parameters[param] + '"';
        }
        _html += '/>';

    } else {

        if (browser.id.IE) {
            _html += '<object classid="' + this.classid + '" name="applet-' + this.id + '" id="applet-' + this.id + '" type="' + this.mimetype +
                           '" height="' + this.height + '" width="' + this.width + '">';
            _html += '  <param name="code" value="' + this.code + '"/>';
            _html += '  <param name="archive" value="' + this.archive + '"/>';
        } else {
            _html += '<object classid="' + this.classid + '" name="applet-' + this.id + '" id="applet-' + this.id + '" ' +
                          'archive="' + this.archive + '" ' +
                          'type="' + this.mimetype + '" height="' + this.height + '" width="' + this.width + '">';
            _html += '  <param name="archive" value="' + this.archive + '"/>';
            _html += '  <param name="codebase" value="' + this.codebase + '"/>';
        }

        for (var param in this.parameters) {
            _html += '  <param name="' + param + '" value="' + this.parameters[param] + '"/>';
        }
        _html += '</object>';

    }

    this.setHTML(_html);

};

Molecule.prototype.markactive = function() {
    $("#" + this.id).css({
        "border": "solid 2px red"
    });
};

/**
 * @return extension for any supported formats or null
 */
Molecule.prototype.getExtension = function() {
    var ext_pattern = /\w.+\.(mol|pdb|xyz|cub|gamout)/;
    var result = ext_pattern.exec(this.model);
    var extension = null;
    if (result) {
        extension = result[1];
    }
    return extension;
};

Molecule.prototype.setReplacement = function(/*String*/replacement) {
    this.replacement = replacement;
};

Molecule.prototype.getReplacement = function() {
    return this.replacement;
};

/**
 * set the mimetype depending on the molecule extension
 */
Molecule.prototype.setExtensionMimeType = function(/*String*/extension) {

    // setMimetype nur, wenn renderer == "chime"
    if (extension == "mol") {
        this.setMimetype("chemical/x-mdl-molfile");
    } else if (extension == "xyz") {
        this.setMimetype("chemical/x-xyz");
    } else if (extension == "pdb") {
        this.setMimetype("chemical/x-pdb");
    } else if (extension == 'cub') {
        this.setMimetype("chemical/x-gaussian-cube");
    } else if (extension == "gamout") {
        this.setMimetype("chemical/x-gamess-output");
    } else {
        logger.error("No mimetype available for " + extension);
    }
    logger.debug("current mimetype is: [" + this.mimetype + "]");
};

/**
 * bind MouseHandler to HTML wrapper
 */
Molecule.prototype.bindHandler = function(moleculeNumber) {
    var imgID;
    var replacement;
    if (browser.plugins.java()) {

        var imageID = "img-" + this.id;
        var width = browser.id.IE ? $("#" + this.id).width() - 3 : $("#" + this.id).width();
        var height = browser.id.IE ? $("#" + this.id).height() -3 : $("#" + this.id).height();
        var top = Math.round((height - 66) / 2);

        logger.trace("height [" + $("#" + this.id).height() + "][" + height + "]");

        replacement = '<div style="width:' + width + 'px;height:' + height + 'px;' +
                          ' text-align: center;border: solid 1px #FFCC00;' +
                          ' font-size: 2em; font-weight: bold"' +
                          ' id="load' + this.id + '">' +
                          '<img src="/vsengine/images/applet-loader.gif"' +
                              ' style="position: relative; top: ' + top + 'px;"/>' +
                      '</div>';
        molecules[moleculeNumber].setReplacement(replacement);

        var overlayImg = '<img src="/vsengine/images/play_qu_64x64.gif" height="64" width="64" id="overlay-' + this.id +
                '" style="position: relative; top:-' + Math.round((this.height / 2) + 34 ) +
                'px"/>';

        logger.trace("append overlay");
        $("#" + this.id).append(overlayImg);

        // Die Hoehe fuer IE/Opera zuruecksetzten!
        if (browser.id.IE || browser.id.OP) {
            var mb = "-" + (top-28) + "px";
            logger.trace("margin-bottom [" + mb + "][" + $("#" + this.id).css("margin-bottom") + "]");

            $("#" + this.id).css({
                "text-align": "center",
                "position": "relative",
                "margin-bottom": mb,
                "height": height + "px"
            });
        }

        logger.trace("height [" + $("#" + this.id).height() + "][" + height + "]");
        logger.trace("margin-bottom [" + mb + "][" + $("#" + this.id).css("margin-bottom") + "]");

        // selektiert den div-Wrapper
        $("#" + this.id).bind("mouseover", function(e) {
            $("#" + this.id).css({
                "height": height + "px"
            });
            $("#" + imageID).attr({
                title: "load applet"
            }).css({
                "cursor": "pointer",
                "opacity": "0.6"
            });
            $("#overlay-" + this.id).css({
                "cursor": "pointer",
                "opacity": "1.0"
            });
            if (browser.id.IE || browser.id.OP) {
                $("#" + imageID).css({
                    "cursor": "hand",
                    "filter": "alpha(opacity=60)"
                });
                $("#overlay-" + this.id).css({
                    "cursor": "hand",
                    "filter": "alpha(opacity=100)"
                });
            }
        }).bind("mouseout", function(e) {
            $("#" + this.id).css({
                "height": height + "px"
            });
            $("#" + imageID).css({
                "cursor": "auto",
                "opacity": "0.4"
            });
            $("#overlay-" + this.id).css({
                "cursor": "auto",
                "opacity": "0.9"
            });
            if (browser.id.IE || browser.id.OP) {
                $("#" + imageID).css({
                    "filter": "alpha(opacity=40)"
                });
                $("#overlay-" + this.id).css({
                    "filter": "alpha(opacity=90)"
                });
            }
        }).bind("click", function(e) {
            $("#overlay-" + this.id).remove();
            $("#" + this.id).css({
                "margin-bottom": "2px"
            });
            $("#img-" + this.id).replaceWith(molecules[moleculeNumber].getReplacement());
            $("#load" + this.id).fadeOut(1000, function() {
                molecules[moleculeNumber].writeHTML();
            });
        });
    }
};

/**
 * Plugin Policy (lowest priority first)
 *
 * 1. default plugin: chime
 * 2. use document settings
 * 3. if Gamess output file: always use jmol
 * 4. if the client does not support chime: use jmol
 *
 */
Molecule.prototype.setRenderer = function(/*String*/renderer) {
    if (renderer != null) {
        if (this.extension == 'gamout') { // no gamout support for Chime
            this.renderer = 'jmol';
        } else if (renderer == 'chime' && !browser.plugins.chime()) {
            this.renderer = 'jmol';
        } else {
            this.renderer = renderer;
        }
    }
};

Molecule.prototype.setScript = function(/*String*/script) {
    /**
     * Default settings used Jmol's "chime" mode
     * Set overall defaults in order to behave Jmol more similar like Rasmol/Chime;
     * including default color scheme, axes orientation, zero-based XYZ numbering,
     * no spacefill, and simple wireframe. Applied immediately; should be used
     * prior to file loading.
     */
    var jmol_defaults = "set showScript on;" +
                        "set zeroBasedXyzRasmol on;" +
                        "set perspectivedepth false;" +
                        "set axesOrientationRasmol on;";
    var mol_correction = ";rotate z 180; rotate y 180;";
    var xyz_correction = ";select all; zoom 100;";

    if (this.renderer == 'jmol') {
        logger.debug("jmol correction for " + this.extension + " - " + this.scope + " as follows")
        // orientation correction for mol
        if (this.extension == 'mol') {
            logger.debug("add " + mol_correction);
            script = mol_correction + script;
        } else if (this.extension == 'xyz' && this.scope == 'reaction3d') {
            logger.debug("add " + xyz_correction);
            script = script + xyz_correction;
        }
        // add "chime" mode settings
        script = jmol_defaults + "load " + Properties.media + this.model + ";" +
            "select *; cpk off;" + script; //  set frank false;
    }

    this.setParameter("script", script);
};

/**
 * default parameters
 */
Molecule.prototype.setDefaultParameters = function() {

    this.setParameter("MessageCallback", "JmolMsg");
    this.setParameter("AnimFrameCallback", "JmolFrameMsg");
    this.setParameter("LoadStructCallback", "JmolStructMsg");
    this.setParameter("PickCallback", "JmolPickMsg");

    if (this.renderer == 'jmol') {
        this.setParameter("mayscript", "true");
        this.setParameter("scriptable", "true");
        this.setParameter("debugscript", "true");

        this.setParameter("progressbar", "true");
        this.setParameter("progresscolor", "blue");
        this.setParameter("boxmessage", "Downloading JmolApplet ...");
        this.setParameter("boxbgcolor", "black");
        this.setParameter("boxfgcolor", "white");
    }
};

/**
 * Load a random VLU into current window.
 * A list of VLUs is available at
 * /vsengine/javascript/randomvlu.js.
 * This file is loaded using Ajax
 * and evaluated-
 */
DOMUtil.openRandomVLU = function() {
    callback = function() {
        eval(this.req.responseText);
        self.location = '/vsengine/vlu' + vlus[Math.round(Math.random()*vlus.length)] + '.html';
    };
    new DOMUtil.ContentLoader("/vsengine/javascript/randomvlu.js",callback);
};
