function MouseMove () {
    this.control = null;
    this.position = new Point(0, 0);
    this.metaKeys = null;
    this.timeStamp = 0;
}

function Control() {
    this.determineElements = function () {
        this.element = getCurrentScript().parentNode;
        this.element.control = this;
        this.menu = this.element.getElementsByTagName("ol")[1];
        this.image = this.element.getElementsByTagName("img")[0];
        this.link = this.image.parentNode;

        var divisions = this.element.getElementsByTagName("div");

        this.focusedObject = divisions[0];
        this.hint = divisions[1];
    }

    this.sendEvents = function () {
        this.isDigesting = true;

        var object = this;
        var xmlRequest = newXMLRequest();
        var events = "<events>";

        for (var i = 0; i < this.events.length; i++)
            events += this.events[i];

        events = events + "</events>";
        this.events = new Array();

        xmlRequest.open("POST", this.url, true);
	    xmlRequest.setRequestHeader("Content-Type", "application/xml");
        xmlRequest.onreadystatechange = function () { object.handleEventResponse(xmlRequest); };
        xmlRequest.send(events);
    }

    this.handleEventResponse = function (xmlRequest) {
        if (xmlRequest.readyState == 4) {
            var response = xmlRequest.responseXML;

            var focusedObject = response.getElementsByTagName("focusedObject")[0].getAttribute("value");
            this.focusedObject.innerHTML = focusedObject;
            setNodeClassEnabled(this.focusedObject, "visible", focusedObject != "");

            var hintElement = response.getElementsByTagName("hint")[0];
            var hint = hintElement.getAttribute("value");
            var hintHTMLElement = this.hint;

            hintHTMLElement.innerHTML = hint;
            setNodeClassEnabled(hintHTMLElement, "visible", hint != "");
            hintHTMLElement.style.left = hintElement.getAttribute("x") + "px";
            hintHTMLElement.style.top = hintElement.getAttribute("y") + "px";

            var imageElements = response.getElementsByTagName("image");

            if (imageElements.length > 0) {
                var image = this.image;
                var object = this;

                image.onload = function () {
                    object.handleDigestedEvents();

                    if (object.isEditing)
                        object.fireOnChange();
                };
                
                image.src = imageElements[0].getAttribute("uri");
            }
            else
                this.handleDigestedEvents();
        }
    }

    this.handleDigestedEvents = function () {
        if (this.events.length > 0)
            this.sendEvents();
        else
            this.isDigesting = false;
    }

    this.addEvent = function (event, parameters) {
        this.events.push("<" + event + " " + parameters + "/>");

        if (!this.isDigesting)
            this.sendEvents();
    }

    this.mouseDown = function (event) {
        var position = event.getPosition();
        var metaKeys = event.getMetaKeys();

        this.link.focus();
        this.image.style.cursor = "default";
        this.addEvent(
            "mouseDown",
            "x=\"" + position.x + "\" "  +
            "y=\"" + position.y + "\" " +
            "button=\"" + event.getButton() + "\" " +
            metaKeys.asXMLAttributes()
        );

        event.stopHandling();
    }

    this.mouseUp = function (event) {
        var position = event.getPosition();
        var metaKeys = event.getMetaKeys();

        this.addEvent(
            "mouseUp",
            "x=\"" + position.x + "\" " +
            "y=\"" + position.y + "\" " +
            "button=\"" + event.getButton() + "\" " +
            metaKeys.asXMLAttributes()
        );

        event.stopHandling();
    }

    this.mouseMove = function (event) {
        var position = event.getPosition();
        var metaKeys = event.getMetaKeys();

        this.addEvent(
            "mouseMove",
            "x=\"" + position.x + "\" " +
            "y=\"" + position.y + "\" " +
            metaKeys.asXMLAttributes()
        );

        event.stopHandling();
    }

    this.mouseOut = function (event) {
        this.addEvent("mouseLeave", "");
    }

    this.handleKey = function (event, keyEventName) {
        var keyCode = event.getKey();

        if (keyCode < 16 || keyCode > 18) {
            var metaKeys = event.getMetaKeys();

            this.addEvent(
                keyEventName,
                "keyCode=\"" + keyCode + "\" " +
                metaKeys.asXMLAttributes()
            );
        }
    }

    this.keyDown = function (event) {
        this.handleKey(event, "keyDown");
    }

    this.keyUp = function (event) {
        this.handleKey(event, "keyUp");
    }

    this.keyPress = function (event) {
        this.handleKey(event, "keyPress");
    }

    this.attachEventHandlers = function () {
        var image = this.image;
        var link = this.link;
        var object = this;

        if (!isInternetExplorer6()) {
            image.onmousedown = function (event) { object.mouseDown(getEvent(event)); };
            image.onmouseup = function (event) { object.mouseUp(getEvent(event)); };
            image.onmousemove = function (event) { object.mouseMove(getEvent(event)); };
            image.onmouseout = function (event) { object.mouseOut(getEvent(event)); };

            link.onkeydown = function (event) { object.keyDown(getEvent(event)); };
            link.onkeyup = function (event) { object.keyUp(getEvent(event)); };
            link.onkeypress = function (event) { object.keyPress(getEvent(event)); };
        }
    }

    this.toggleEditing = function (event) {
        this.isEditing = !this.isEditing;
        this.updateEditingIndicators();

        var object = this;
        var xmlRequest = newXMLRequest();
        var command = "<setEditing enabled=\"" + booleanToXML(this.isEditing) + "\"/>";

        xmlRequest.open("POST", this.url, true);
	    xmlRequest.setRequestHeader("Content-Type", "application/xml");
        xmlRequest.send(command);
   }

    this.addEditMenuOption = function(caption, isEditing) {
        this.isEditing = isEditing; 

        var option = document.createElement("li");
        var span = document.createElement("span");
        var link = document.createElement("a");
        var object = this;

        link.href = "javascript:void(0);";
        link.innerHTML = caption;
        
        link.onclick = function (event) {
            object.toggleEditing(getEvent(event));
            collapse(object.menu);
        };

        span.appendChild(link);
        option.appendChild(span);

        this.menu.insertBefore(option, this.menu.childNodes[0]);
        this.editMenuOption = option;
        this.updateEditingIndicators();
    }

    this.updateEditingIndicators = function () {
        setNodeClassEnabled(this.editMenuOption, "current", this.isEditing);
        setNodeClassEnabled(this.element, "editing", this.isEditing);
    }

    this.registerOnChangeListener = function (listener) {
        this.onChangeListeners.push(listener);
    }

    this.fireOnChange = function () {
        for (var i = 0; i < this.onChangeListeners.length; i++)
            this.onChangeListeners[i]();
    }

    this.events = new Array();
    this.isDigesting = false;
    this.onChangeListeners = new Array();

    this.determineElements();
    this.url = this.image.src.split("?")[0];

    this.attachEventHandlers();
}

