/**
 * chime control functions
 *
 * @author Jutta B&#246;gemann
 * @author <a href="mailto:fkoehler@oc.uni-kiel.de">Felix K&#246;hler</a>
 * @author <a href="mailto:vsctech@fiz-chemie.de">Eike Jordan</a>
 * @version  $Revision: 10996 $
 *
 * $HeadURL: http://trac.gruen.fiz-chemie.de/svn/vs/vscms/trunk/src/vsengine/static/javascript/control.js $
 *
 * Copyright 2002-2006 FIZ CHEMIE Berlin
 */

/**
 *  Changes:
 *
 *  Revision 1.9
 *
 *   object access via XML-DOM (not completely finished)
 *   debug breakpoints reduced since javascript shell (http://www.squarefree.com/shell/) is available
 */

/**
 * ToDo:
 */

// Array.prototype.contains = function(obj) {
//     var _listed = false;
//     for (var i = 0; i < this.length; i++) {
//         if (this[i] === obj) {
//             _listed = true;
//              break;
//            }
//     }
//     return _listed;
// };

var Control = {};
// TODO:
//      Funktionen, die sich ausschliesslich auf Reaktionsanimationen
//      beziehen aus Control herausnehmen. Eigenes Object "Reaktion"
//      dafuer implementieren als Unterklasse von Molecule. Damit wuerden
//      sich diverse Hilfsvariablen eruebrigen und
//      man koennte u.a. mehrere 3D-Reaktionsanimationen auf
//      einer Seite ausfuehren. Der Scope fuer globale/locale
//      Variablen waere eindeutig
//
//      Das jeweilige target (DOM-Node) waere auch besser
//      als Variablen im jeweiligen Molekuel-Object untergebracht, damit
//      wuerde das document.getElementById an vielen Stellen entfallen
//


Control.initialized = false;
Control.reaction = false;
Control.target = null;
Control.counter = 0;

Control.bAnimOn = false;
Control.bActive = false;       // variable to make sure only one action is started at a time (mutex object)
Control.animNrOfSteps = 0;     // number of remaining steps in the reaction
Control.current_animframe = 0; // actual structure in a xyz File
Control.chimeCommand;
Control.stepCounter = 0;

/**
 * Main function used by animated Chime/Jmol objects
 */
Control.execute = function(id, rasmolScript) {
    // In einer Reaktionsanimation wird der "applet-" Prefix in der init-Methode
    // gesetzt! Das ist noch nicht wirklich optimal.
    var animationID = Control.reaction ? id : "applet-" + id;
    logger.debug("[Control.execute] id: [" + animationID + "] - script: [" + rasmolScript + "]");
    var _element = document.getElementById(animationID);
    Control.target = _element;
    if (_element) {
        // normalize case; IE uppercase, all other browsers lowercase
        var _targetElement = _element.nodeName.toLowerCase();

        if (_targetElement == 'object') {
            try {
                logger.debug("[Control.execute] script applet: " + rasmolScript);
                _element.script(rasmolScript);
            } catch (e) {
                logger.error(e);
            }
        } else {
            var _remoteDocument, _buffer;
            if ( window.frames["vsiremote"] && window.frames["vsiremote"].document) {
                _remoteDocument = window.frames["vsiremote"].document;
            } else if (document.getElementById("vsiremote") && document.getElementById("vsiremote").contentDocument ) {
                _remoteDocument = document.getElementById("vsiremote").contentDocument;
            } else {
                alert("Control.execute error! Abort!");
                return;
            }

            // make sure document supports the 'writeln' method
            if (typeof(_remoteDocument.contentType) != 'undefined' && _remoteDocument.contentType != "text/html") {
                // Gecko with wrong mime-type
                logger.error("Error in remote document. Wrong content type. Exit.");
                return;
            } else {
                // other browsers
                if (typeof(_remoteDocument.writeln) == "undefined") {
                    logger.error("Error writing to remote document. Exit.");
                    return;
                }
            }

            logger.debug("[Control.execute] script embed: [" + animationID + "] " + rasmolScript);
            _remoteDocument.open();
            // Mapping auf Callback-Funktionen erforderlich fuer IE7/IE8
            _buffer = "<html>" +
                    "    <head>" +
                    "      <title>Anim_Control</title>" +
                    "      <script type='text/javascript'>" +
                    "        var JmolMsg = window.parent.JmolMsg; " +
                    "        var JSChangeAppletFrame = window.parent.JSChangeAppletFrame; " +
                    "        var JmolStructMsg = window.parent.JmolStructMsg; " +
                    "        var JmolFrameMsg = window.parent.JmolFrameMsg; " +
                    "        var JmolPickMsg = window.parent.JmolPickMsg; " +
                    "      </script>" +
                    "    </head>" +
                    "    <body id='remotejs'>" +
                    "      <embed" + "\n" + "type='application/x-spt' hidden='true'" +
                               "\n" + " width='10' height='10' button='push'" +
                               " target='" + animationID + "'\n" + " script='" + rasmolScript + "' immediate='1' />" +
                    "    </body>" +
                    "  </html>";

            if (!_remoteDocument.writeln) {
                alert("Error writing to remote frame!");
                return;
            }
            // it's recommended first to store everything in a variable and write only once!
            logger.debug("write animation trigger");
            _remoteDocument.writeln(_buffer);
            _remoteDocument.close();
        }
    } else {
        // Insgesamt 10 Versuche 200 ms zeitverzoegert,
        // um Applet/Chime zu laden.
        logger.debug("try to activate molecule " + animationID);
        Common.activateMolecule(id);
        logger.debug("try to relocate molecule " + animationID + " [" + Control.counter + "]");
        _element = document.getElementById(animationID);
        if (_element) {
            logger.debug("... success " + animationID + " created!")
            logger.debug("call Common.execute(" + id + ", " + rasmolScript + ")");
            Control.execute(id, rasmolScript);
            Control.counter = 0;
        } else {
            if (Control.counter < 10) {
                Control.counter++;
                window.setTimeout(function() {
                    logger.debug(Control.counter + " try to activate molecule " + animationID);
                    Control.execute(id, rasmolScript);
                }, 200);
            } else {
                logger.error("[Control.execute] target " + animationID + " not found");
                Control.counter = 0;
            }
        }
    }
};
/**
 * Callback funktion for chime/jmol animations
 */
function JSChangeAppletFrame(/*String*/id, /*int*/anim_frameNo)  {

//function JSChangeAppletFrame(/*String*/id, /*int*/anim_frameNo, /*int*/fileNo,
//             /*int*/modelNo, /*int*/firstNo, /*int*/lastNo, /*int*/isAnimationRunning,
//             /*int*/animationDirection, /*int*/currentDirection)  {
//
//    var _id = id;                     // The name of the applet - MUST be included as the @name-Attribute
//    var _anim_frameNo = anim_frameNo; // the current frame number (0-based)
//    var _fileNo = fileNo;             // the current file number (1-based) - chime prepends the frame documentation string an appends on space char
//    var _modelNo = modelNo;           // The current model number within the current file (1-based) - Jmol only
//    var _firstNo = firstNo;           // The first frame of the animation range, expressed as fileNo*1000000+modelNo - Jmol only
//    var _lastNo = lastNo;             // The last frame of the animation range, expressed as fileNo*1000000+modelNo - Jmol only
//    var _isAnimationRunning = isAnimationRunning; // 0 (animation is off) or 1 (animation is on) - Jmol only
//    var _animationDirection = animationDirection; // 1 (animation direction +1) or -1 (animation direction -1) - Jmol only
//    var _currentDirection = currentDirection;     // 1 (forward) or -1 (reverse) - Jmol only
//
//    logger.debug("_id[" + _id + "]\n[" +
//        "_anim_frameNo[" + _anim_frameNo + "]\n[" +
//        "_fileNo[" + _fileNo + "]\n[" +
//        "_modelNo[" + _modelNo + "]\n[" +
//        "_firstNo[" + _firstNo + "]\n[" +
//        "_lastNo[" + _lastNo + "]\n[" +
//        "_isAnimationRunning[" + _isAnimationRunning + "]\n[" +
//        "_animationDirection[" + _animationDirection + "]\n[" +
//        "_currentDirection[" + _currentDirection + "]"
//    );

    if (!Control.initialized) { // should be Reaction.initialized
        logger.error("Animation not initialized")
        return;
    } else {
        logger.debug("JSChangeAppletFrame - id[" + id + "] anim_frameNo[" + anim_frameNo + "]");
    }
    var zero = ((document.getElementById(id).nodeName.toLowerCase() == 'embed') ? 0 : 1);
    logger.trace(zero);
    var frame = parseInt(anim_frameNo) + zero;

    logger.trace("[Control.JSChangeAppletFrame] - id: [" + id + "] frame: [" + frame + "][" + anim_frameNo + "]");

    Control.current_animframe = frame;
    if (typeof(anim_frame) == 'undefined') {
        logger.error("[Control.JSChangeAppletFrame] - animation array not available. Exit.");
        return;
    }
    // TODO: die ID stimmt nicht mehr mit der HTML-Struktur ueberein
    // das Beste wird wirklich sein, wenn ich alle benoetigten Elemente
    // einmal einsammele und den weiteren Ablauf einer Animation nur
    // noch ueber die eingesammelten Objecte laufen lasse - siehe "Control als Object".
    highlight(id);
    for (var i = 0; i < anim_frame.length; i++) {
        logger.trace("[Control.JSChangeAppletFrame] - frame: [" + frame + "][" + (anim_frame[i] - zero) + "]");
        if (frame == (anim_frame[i])) {
            Control.stepCounter = i;
            logger.trace("[Control.JSChangeAppletFrame] - Control.stepCounter: [" + Control.stepCounter + "]");
            if(Control.bAnimOn && !Control.bActive) {
                logger.trace("[Control.JSChangeAppletFrame] - Control.animNrOfSteps: [" + Control.animNrOfSteps + "][" + (Control.animNrOfSteps > 0) + "]");
                if (Control.animNrOfSteps > 0) {
                    --Control.animNrOfSteps;
                } else {
                    doStartStop(id);
                }
            }
        }
    }
    return true;
}

Control.buttons = new Array();
//var button1;// Frame back
//var button2;// Step back
//var button3;// Start Stop
//var Control.buttons[4];// Step forward
//var button5;// Frame forward

/**
 * Init function for 3D reaction animation buttons
 */
function initButtonArray(target) {
    if (Control.buttons.length == 0) {
        logger.trace("[Control.initButtonArray] - " + target);
        var chimeFormId = 'chimeForm' + target;
        var chimeForm   = document.getElementById(chimeFormId);

        Control.buttons[0] = chimeForm[0];// Frame back
        Control.buttons[1] = chimeForm[1];// Step back
        Control.buttons[2] = chimeForm[2];// Start Stop
        Control.buttons[3] = chimeForm[3];// Step forward
        Control.buttons[4] = chimeForm[4];// Frame forward
    } else {
        return;
    }
}
/**
 * Set disable property
 */
function disableButtons() {
    logger.trace("[Control.disableButtons]");
    Control.buttons[0].disabled='disabled';Control.buttons[0].style.color='#F1F1F1';
    Control.buttons[1].disabled='disabled';Control.buttons[1].style.color='#F1F1F1';
    Control.buttons[3].disabled='disabled';Control.buttons[3].style.color='#F1F1F1';
    Control.buttons[4].disabled='disabled';Control.buttons[4].style.color='#F1F1F1';
    return;
}

/**
 * Unset disable property
 */
function enableButtons() {
    logger.trace("[Control.enableButtons] - Control.current_animframe: " + Control.current_animframe + " - " + typeof(Control.current_animframe));

    Control.buttons[0].disabled='';Control.buttons[0].style.color='#000000';
    Control.buttons[1].disabled='';Control.buttons[1].style.color='#000000';
    Control.buttons[3].disabled='';Control.buttons[3].style.color='#000000';
    Control.buttons[4].disabled='';Control.buttons[4].style.color='#000000';

    logger.trace("[Control.enableButtons] - anim_frame.length: " + anim_frame.length);
    logger.trace("[Control.enableButtons] - anim_frame[anim_frame.length-1]: " + anim_frame[anim_frame.length-1] + " - " + typeof(anim_frame[anim_frame.length-1]));
    logger.trace("[Control.enableButtons] - Control.current_animframe == anim_frame[anim_frame.length-1] [" + (Control.current_animframe == anim_frame[anim_frame.length-1]) + "]");
    logger.trace("[Control.enableButtons] - Control.current_animframe == anim_frame[0] [" + (Control.current_animframe == anim_frame[0]) + "]");
    logger.trace("[Control.enableButtons] - Control.bAnimOn [" + (Control.bAnimOn) + "]");

    if (Control.current_animframe == anim_frame[anim_frame.length-1]) {
        logger.trace("[Control.enableButtons] - Control.current_animframe == anim_frame[anim_frame.length-1]");
        Control.buttons[3].disabled='disabled';Control.buttons[3].style.color='#F1F1F1';
        Control.buttons[4].disabled='disabled';Control.buttons[4].style.color='#F1F1F1';
    } else if (Control.current_animframe == anim_frame[0]) {
        logger.trace("[Control.enableButtons] - Control.current_animframe == anim_frame[0]");
        Control.buttons[0].disabled='disabled';Control.buttons[0].style.color='#F1F1F1';
        Control.buttons[1].disabled='disabled';Control.buttons[1].style.color='#F1F1F1';
    } else if (Control.bAnimOn) {
        logger.trace("[Control.enableButtons] - Control.bAnimOn[" + Control.bAnimOn + "]. Call disableButtons");
        disableButtons();
    } else {
        logger.trace("[Control.enableButtons] - nothing to do. exit.");
    }
}

/**
 * Start/stop function for 3D animations called by JSChangeAppletFrame
 */
function doStartStop(id) {
    logger.debug("[Control.doStartStop] - " + id);
    var resume = ((document.getElementById(id).nodeName.toLowerCase() == 'embed') ? "on" : "resume");
    initButtonArray(id);
    Control.execute(id, 'anim direction +1');
    if (Control.bAnimOn) {
        logger.debug("[Control.doStartStop] - stop animation");
        Control.bAnimOn = false;
        Control.execute(id, "animation off");
        Control.buttons[2].value = " Start ";
        enableButtons();
    } else {
        logger.debug("[Control.doStartStop] - Control.stepCounter: [" + Control.stepCounter + "][" + (anim_frame.length - 1) + "]");
        if (Control.stepCounter < (anim_frame.length - 1)) {
            Control.bAnimOn = true;
            Control.execute(id, "animation " + resume);
            Control.buttons[2].value = " Stop ";
            disableButtons();
        }  else {
            logger.debug("[Control.doStartStop] - rewind animation");
            Control.bAnimOn = false;
            Control.execute(id, "anim frame 1");
            Control.buttons[2].value = " Start ";
            Control.stepCounter = 0;
            enableButtons();
        }
    }
}

/**
 * One step back (step as author determined)
 */
function doStepBack(animation) {
    logger.debug("[Control.doStepBack] - " + animation);
    // make sure to properly synchronize
    if (Control.bAnimOn || Control.bActive) return;
    Control.bActive = true;
    if (Control.stepCounter > 0) {
        Control.stepCounter--;
        script = 'anim frame ' + anim_frame[Control.stepCounter];
        Control.execute(animation, script);
    }
    Control.bActive = false;
}

/**
 * One step forward (step as author determined)
 */
function doStepForward(animation) {
    logger.debug("[Control.doStepForward] - " + animation);
    if (Control.bAnimOn || Control.bActive) return;
    Control.bActive = true;
    if (Control.stepCounter < anim_frame.length) {
        Control.stepCounter ++;
        script = 'anim frame ' + anim_frame[Control.stepCounter];
        Control.execute(animation, script);
    }
    Control.bActive = false;
}

/**
 * Single animation frame back
 */
function doFrameBack(targetChime) {
    logger.debug("[Control.doFrameBack] - " + targetChime)
    if (Control.bAnimOn || Control.bActive) return;
    Control.bActive = true;
    Control.chimeCommand = 'anim direction -1; anim frame next; anim direction +1';
    Control.execute(targetChime, Control.chimeCommand);
    Control.bActive = false;
}

/**
 * Single animation frame forward
 */
function doFrameForward(animation) {
    logger.debug("[Control.doFrameForward] - " + animation);
    if (Control.bAnimOn || Control.bActive) return;
    Control.bActive = true;
    if (Control.stepCounter < anim_frame.length) {
        script = 'anim direction +1; anim frame next';
        Control.execute(animation, script);
    }
    Control.bActive = false;
}

/**
 * Jump to first animation frame
 */
function doFirstFrame(animation) {
    logger.debug("[Control.doFirstFrame] - " + animation);
    if (Control.bAnimOn || Control.bActive) return;
    Control.bActive = true;
    Control.execute(animation, "anim frame 1");
    Control.bActive = false;
}

/**
 * Jump to frame animframeNo
 */
function doGoFrame(animation, frame) {
    logger.debug("[Control.doGoFrame] - " + animation + " frame: [" + frame + "]");
    if (Control.bAnimOn || Control.bActive) {
        logger.error("[Control.doGoFrame] - animation already active. exit.");
        return;
    }
    Control.bActive = true;
    script = 'anim frame ' + frame;
    script += ';anim off';
    Control.execute(animation, script);
    Control.bActive = false;
}

/**
 * Jump to a certain intermediate step (as determined by the author)
 */
function doGoIntermediate(targetChime,StepNr) {
    logger.debug("[Control.doGoIntermediate] - " + targetChime + " - " + StepNr);
//    logger.trace("[Control.doGoIntermediate] - " + typeof(Control.current.toggle));
//    Control.current._state = "clicked";
//    logger.trace("[Control.current] - current:[" + Control.current._current +
//             "] state:[" + Control.current._state + "] cursor:[" + Control.current._cursor + "]");
    doGoFrame(targetChime, anim_frame[StepNr]);
}

/**
 * Jump to a certain step position (as determined by the author)
 */
function doGoStep (animation,step) {
    logger.debug("[doGoStep] " + animation + " - " + step);
    // not working properly :-/
    // 3DTest   alert('doGoStep got animation: [' + animation + ']\n\tstep: [' + step + ']\ndoGoStep calls initButtonArray with: [' + animation +']');
    initButtonArray(animation);
    if (Control.bActive || Control.bAnimOn) return;
    Control.bActive = true;
    Control.stepCounter = step;
    var a = anim_frame[Control.stepCounter] + 1;
    script = 'anim frame ' + a;
    script += ';anim off';
    script += ';anim on';
    Control.bAnimOn = true;
    Control.execute(animation, script);
    Control.buttons[2].value = " Stop ";
    disableButtons();
    Control.bActive = false;
}

/**
 * Highlight the active animation image; called by JSChangeAppletFrame
 */
function highlight(target) {
    logger.trace("[Control.highlight] - " + target);
    var i, j = 0;
    initButtonArray(target);
    var images3D = DOMUtil.getElementsWithClassName('img', 'images3D' + target);
    for (var i=0; i < images3D.length; ++i) {
        logger.trace("[Control.highlight] - set image[" + i + "] color from " + images3D[i].style.borderColor + " to #C1C1C1");
        images3D[i].style.borderColor = '#C1C1C1';
    }
    if (Control.current_animframe == anim_frame[anim_frame.length-1]) {
        logger.trace("[Control.highlight] - Control.current_animframe == anim_frame[anim_frame.length-1]");
        if (!Control.bAnimOn) {
            enableButtons();
        }
        Control.buttons[3].disabled='disabled';Control.buttons[3].style.color='#F1F1F1';
        Control.buttons[4].disabled='disabled';Control.buttons[4].style.color='#F1F1F1';
    } else if (Control.current_animframe == anim_frame[0]) {
        logger.trace("[Control.highlight] - Control.current_animframe == anim_frame[0]");
        if (!Control.bAnimOn) {
            enableButtons();
        }
        Control.buttons[0].disabled='disabled';Control.buttons[0].style.color='#F1F1F1';
        Control.buttons[1].disabled='disabled';Control.buttons[1].style.color='#F1F1F1';
    } else {
        enableButtons();
    }

    for (i = 0; i < anim_frame.length; i++) {
        if (Control.current_animframe <= anim_frame[i]) {
            j =  ( Control.current_animframe == anim_frame[i] ? 2*i : 2*i-1 );
            break;
        }
        j =  (Control.current_animframe == anim_frame[i] ? 2*i : 2*i-1);
        if (j > images3D.length ) {
            return;
        }
     }
     logger.trace("[Control.highlight] - j := [" + j + "] " + typeof(images3D[j]));
    try {
        images3D[j].style.borderColor='#FF0000';
        current = images3D[j].style.borderColor;
    } catch(e) {
        logger.error("[Control.highlight] - " + e);
    }
}

/**
 * Animation init
 */
function initChime(target) {
    var _target = "applet-" + target;
    var animation = document.getElementById(_target);
    Control.target = animation;
    if (!Control.initialized) {
        if (animation && animation.nodeName) {
            logger.debug("[initChime] " + _target + " - " + animation.nodeName);
        } else {
            logger.error("Initialization failure " + _target);
            return;
        }
        Control.reaction = true;
        Control.execute(_target, "anim fps 4");
        Control.initialized = true;
        doGoIntermediate(_target, '0');
    }
}

/**
 * Callback function
 */
function JmolMsg(id, message, state) {
    logger.trace('[JmolMsg] id: [' + id + "] message: [" + message + "][" + state + "]");
}

/**
 * Callback function
 */
function JmolStructMsg(id) {
    logger.trace('[JmolStructMsg] id: [' + id + "]");
}

/**
 * Callback function
 */
function JmolPickMsg(id, message, atomnr) {
    logger.trace('[JmolPickMsg] id: [' + id + "] message: [" + message + "] atomnr: [" + atomnr + "]");
}

/**
 * Callback function
 */
function JmolFrameMsg(id, frame) {
    logger.trace('[JmolFrameMsg] id: [' + id + "] frame: [" + frame + "]");
}

function JmolButtonMsg(bi, after) {
    logger.trace("ButtonCallBack - [" + bi + "] - [" + after +"]");
}
Control.current = null;
Control.Color = function(target) {

    _target = target;
    _current = null;
    _state = null;
    _cursor = target.style.cursor; // TODO: Cursor wird noch nicht richtig angezeigt.
    logger.trace("[Control.Color] - init - [" + _current + "][" + _state + "][" + _cursor + "]");

};

Control.Color.prototype = {

    toggle : function(color) {

        if (Control.bAnimOn) { // 1
            _current = _target.style.borderColor;
            _target.style.cursor = "default";
            _state = "run";
            logger.trace("[Control.color.toggle] - 1 - current: [" + _current +
             "] state: [" + _state + "] cursor: [" + _cursor + "]");
            // Todo: failsave hover
            return;
        } else if (_state == "run") { // 2
            _current = _target.style.borderColor;
            _state = "lazy";
            _target.style.cursor = _cursor;
            logger.trace("[Control.color.toggle] - 2 - current: [" + _current +
             "] state: [" + _state + "] cursor: [" + _cursor + "]");
            return;
        } else if (_state == "clicked") { // 3
            logger.trace("[Control.color.toggle] - 3 - current: [" + _current +
             "] state: [" + _state + "] cursor: [" + _cursor + "]");
            return;
        } else if (!color) { // 4
            _target.style.borderColor = _current;
            _target.style.cursor = _cursor;
            logger.trace("[Control.color.toggle] - 4 - current: [" + _current +
             "] state: [" + _state + "] cursor: [" + _cursor + "]");
        } else { // 5
            _current = _target.style.borderColor;
            _target.style.borderColor = color;
            _target.style.cursor = _cursor;
            logger.trace("[Control.color.toggle] - 5 - current:[" + _current +
             "] state:[" + _state + "] cursor:[" + _cursor + "]");
        }

    }

};

var Jmol = {};
Jmol.archive = "JmolApplet.jar";

Jmol.command = 0;
Jmol.commands = new Array();

function ScriptInput(event) {
    var code;
    var target;

    if (!event) var event = window.event;

    if (event.target) target = event.target;
    else if (event.srcElement) target = event.srcElement;
    if (target.nodeType == 3) target = target.parentNode;

    if (event.keyCode) code = event.keyCode;
    else if (event.which) code = event.which;

    var character = String.fromCharCode(code);
    var id = target.id.substring(6, target.id.length);
    if (code == 13) { // return
        Control.execute(id, target.value);
        if (!Jmol.commands.contains(target.value)) {
            Jmol.commands.push(target.value);
            Jmol.command = Jmol.commands.length;
        }
        target.value = "";
    } else if (code == 37) { // left arrow
        for (var i = 0; i < Jmol.commands.length; i++) {
            logger.trace(Jmol.commands[i] + " - " + Jmol.command + " - last command executed: " + Jmol.commands[Jmol.command]);
        }
    } else if (code == 38) { // up arrow
        if (Jmol.command == 0) {
            Jmol.command = Jmol.commands.length - 1;
        } else {
            Jmol.command--;
        }
        for (var i = 0; i < Jmol.commands.length; i++) {
            logger.trace(Jmol.commands[i] + " - " + Jmol.command + " - " + Jmol.commands[Jmol.command]);
        }
        target.value = Jmol.commands[Jmol.command];
    } else if (code == 40) { // down arrow
        if (Jmol.command == Jmol.commands.length - 1) {
            Jmol.command = 0;
        } else {
            Jmol.command++;
        }
        for (var i = 0; i < Jmol.commands.length; i++) {
            logger.trace(Jmol.commands[i] + " - " + Jmol.command + " - " + Jmol.commands[Jmol.command]);
        }
        target.value = Jmol.commands[Jmol.command];
    } else if (code == 8 && event.ctrlKey) {
        if (target.value == Jmol.commands[Jmol.command]) {
            logger.trace("delete current command entry: " + Jmol.commands[Jmol.command]);
        } else {
            logger.trace("do nothing");
            return false;
        }
    } else {
        logger.trace("[" + code + "] - [" + character + "] - [" + id + "]");
    }
}
