if (!scriptURL) { var scriptURL = false; }
if (!refreshRate) { var refreshRate = 200; }

/*************************************************
    User Interface Elements
*/
var elements = [];
var rootDiv = false;
function getRootDiv() {
    if (rootDiv) {
        return rootDiv;
    } else {
        // This nonsense to work around IE's lack of fixed positioning
        var body = document.getElementsByTagName("body")[0];
        rootDiv = document.createElement("div");
        rootDiv.id = "root";
        body.appendChild(rootDiv);
        //        body.style.overflow = "hidden";
        //        rootDiv.style.height = "100%";
        //        rootDiv.style.overflow = "auto";
        return rootDiv;
    }
}
// abstract base class
function UIElement(name, left, top, width, height) {
    this.name = name;
    this.left = left;
    this.top = top;
    this.width = width;
    this.height = height;

    elements[this.name] = this;
    
    this.div = document.createElement("div");
    this.div.id = name;
    var root = getRootDiv();
    root.appendChild(this.div);

    this.mouseIsDown = false;

    this.drawImage = function( imgsrc, left, top, width, height ) {
        var img = document.createElement("img");
        this.div.appendChild(img);
        img.src = imgsrc;
        img.onmousedown = function() { return false; }
        img.onmousemove = function() { return false; }
        img.onmouseup = function() { return false; }
        img.width = width;
        img.height = height;
        img.style.position = "absolute";
        img.style.left = left + "px";
        img.style.top = top + "px";
        return img;
    }

    this.minx = left;
    this.miny = top;
    this.maxx = left + width;
    this.maxy = top + height;
    this.inRange = function(event) {
        var x = event.docX;
        var y = event.docY;
        return (x > this.minx) && (x < this.maxx) && (y > this.miny) && ( y < this.maxy);
    }

    this.handleEvent = function(event) { 
        if ( event.type == "mousedown" && this.inRange(event) ) {
            this.mouseIsDown = true;
            this.mouseDownHandler(event);
        } else if (event.type == "mouseup") {
            this.mouseIsDown = false;
            this.mouseUpHandler(event);
        } else if (event.type == "mousemove") {
            this.mouseMoveHandler(event);
        } else { }
    }
    this.mouseDownHandler = function( event ) {} 
    this.mouseUpHandler = function( event ) {}
    this.mouseMoveHandler = function( event ) {}

    this.setValue = function( string ) {}

    this.addLabel = function( string, style ) {
        var label = document.createElement("div");
        this.div.appendChild(label);
        label.innerHTML = string;
        label.style.position = "absolute";
        label.style.zIndex = 2;
        label.style.width = this.width + "px";
        label.style.height = 20 + "px";
        label.style.top = (this.miny - 20) + "px";
        label.style.left = this.minx + "px";
        label.style.margin.right = "auto";
        label.style.margin.left = "auto";
        label.style.textAlign = "center";
        if (style) {
            var stizyle = eval('('+style+')');
            label.style += style;
        }
    }
}

function Toggle(name, left, top, width, height, offimg, onimg) {
    width = width ? width : 33;
    height = height ? height : 34;
    offimg = offimg ? offimg : "pics/toggle-off.png";
    onimg = onimg ? onimg : "pics/toggle-on.png";

    var self = new UIElement(name, left, top, width, height);
    var img = self.drawImage(offimg, left, top, width, height);

    self.setValue = function( value ) {
        self.updateValue( value );
        if (!self.mouseIsDown) {
            self.redraw();
        }
    }
    self.updateValue = function( value ) {
        if (!value) {
            self.value = 0;
        } else {
            self.value = 1;
        }
    }

    self.redraw = function() {
        img.src = self.value ? onimg : offimg;
    }
    self.mouseDownHandler = function( event ) {
        if (self.value == 1) {
            self.updateValue(0);
        } else {
            self.updateValue(1);
        }
        self.redraw();
        sendData(self.name);
    }
}

function ToggleBank(name, left, top, numToggles, width, height, offimg, onimg) {
    numToggles = numToggles ? numToggles : 1;
    width = width ? width : numToggles * 33;
    height = height ? height : 34;
    offimg = offimg ? offimg : "pics/toggle-off.png";
    onimg = onimg ? onimg : "pics/toggle-on.png";

    var self = new UIElement(name, left, top, width, height);
    var togwidth = width / numToggles;
    var Tog = function(index) {
        this.index = index;
        var togleft = left + (togwidth * index);
        this.img = self.drawImage(offimg, togleft, top, togwidth, height);
        this.set = function() {
            self.value[this.index] = self.value[this.index] == 1 ? 0 : 1;
        }
        this.redraw = function() {
            this.img.src = self.value[this.index] == 1 ? onimg : offimg;
        }
    }

    var togs = [];
    self.value = [];
    for (var i = 0; i < numToggles; i++) {
        togs.push(new Tog(i));
        self.value.push(0);
    }
    self.setValue = function( value ) {
        self.updateValue( value );
        if (!self.mouseIsDown) {
            self.redraw();
        }
    }
    self.updateValue = function( value ) {
        if (value && (value.length == self.value.length)) {
            self.value = value;
        }
    }
    self.redraw = function() {
        for (var i = 0; i < numToggles; i++) {
            togs[i].redraw();
        }
    }
    var current = -1;
    var previous = -1;
    self.mouseDownHandler = function( event ) {
        self.mouseMoveHandler( event );
    }
    self.mouseUpHandler = function( event ) {
        current = previous = -1;
    }
    self.mouseMoveHandler = function( event ) {
        if (self.mouseIsDown) {
            current = Math.min(togs.length, parseInt( (event.docX - left) / togwidth ));
            if (previous != current) {
                togs[ current ].set();
                togs[ current ].redraw();
                sendData(self.name);
                previous = current;
            }
        }
    }
    return self;
}

function Bang(name, left, top, width, height, offimg, onimg) {
    width = width ? width : 32;
    height = height ? height : 32;
    offimg = offimg ? offimg : "pics/bangoff.png";
    onimg = onimg ? onimg : "pics/bangon.png";

    self = new UIElement(name, left, top, width, height);
    self.img = self.drawImage( offimg, left, top, width, height );

    self.value = "bang";
    self.setValue = function() {}
    self.mouseDownHandler = function( event ) {
        sendData(self.name);
        self.img.src = onimg;
    }
    self.mouseUpHandler = function( event ) {
        self.img.src = offimg;
    }
    return self;
}


function MultiSlider(name, left, top, width, height, numSliders, range, background, knob, activeknob, interpolate) {
    width = width ? width : 100;
    height = height ? height : 100;
    numSliders = numSliders ? numSliders : 1;
    range = range ? range : 128;
    background = background ? background : "pics/multiSlider-bg.png";
    activeknob = activeknob ? activeknob : knob ? knob : "pics/pictSlider-activeknob.png";
    knob = knob ? knob : "pics/pictSlider-knob.png";

    interpolate = typeof interpolate != "undefined" ? interpolate : true;
    // Parasitic Inheritance
    var self = new UIElement(name, left, top, width, height);

    var Slider = function(index) {
        this.width = width / numSliders;
        this.height = 5;
        this.minx = left + index * this.width;
        this.maxx = this.minx + this.width;
        this.rawValue = top + height;
        this.knobimg = self.drawImage( knob, this.minx, this.rawValue - this.height, this.width, this.height);
        this.knobimg.style.zIndex = 1;
        this.move = function(docy) {
            this.knobimg.src = activeknob;
            this.rawValue = Math.max(top, Math.min(top + height, docy));
            this.redraw();
            updateValues();
        }
        this.redraw = function() {
            this.knobimg.style.top = Math.min(this.rawValue, top + height - this.height) + "px";
        }
        this.setValue = function(value) {
            this.rawValue = (1 - value / range) * height + top;
            // As this function is only called remotely, we don't want confusing redraws...
            if (!self.mouseIsDown) {
                this.redraw();
            }
        }
    }
    var updateValues = function() {
        for (var i = 0; i < numSliders; i++) {
            self.value[i] = (1 - (sliders[i].rawValue - top) / height) * range;
        }
    }
    self.setValue = function(vals) {
        if (numSliders == 1) {
            sliders[0].setValue( vals );
        } else {
            for (var i = 0; i < vals.length; i++) {
                sliders[i].setValue( vals[i] );
            }
        }
    }
    self.mouseDownHandler = function(event) {
        self.mouseMoveHandler(event);
    }

    var prevEvent = false;
    self.mouseMoveHandler = function(event) {
        if (self.mouseIsDown) {
            // this code is not in the slider object for efficiency's sake.
            var which = parseInt((event.docX - left) / (width / numSliders));
            if (which >= 0 && which < numSliders) {
                if (prevEvent && interpolate) {
                    // interpolate any sliders we missed.
                    var start = prevEvent.which;
                    var end = which;
                    var delta = (event.docY - prevEvent.docY) / (end - start);
                    // Draw curves in the right direction...
                    for (var i = start; (start > end ? i >= end : i < end) ; (start > end ? i-- : i++) ) {
                        sliders[i].move( prevEvent.docY + (i - start) * delta);
                    }
                }
                sliders[which].move(event.docY);
                sendData(name);
                prevEvent = { "which": which, "clientY": event.clientY, "docY": event.docY };
            }
        }
    }

    self.mouseUpHandler = function(event) {
        for (var i = 0; i < sliders.length; i++) {
            sliders[i].knobimg.src = knob;
        }
        prevEvent = false;
    }

    var bgimg = self.drawImage( background, left, top, width, height);
    bgimg.style.zIndex = 0;

    self.value = [];
    var sliders = [];
    for (var i = 0; i < numSliders; i++) {
        sliders.push(new Slider(i));
        self.value.push(0);
    }
    return self;


}



function PictSlider(name, left, top, width, height, rangeX, rangeY, background, knob, activeknob) {
    // initialize defaults
    width = width ? width : 100;
    height = height ? height : 100;
    background = background ? background : "pics/pictSlider-bg.png";
    activeknob = activeknob ? activeknob : knob ? knob : "pics/pictSlider-activeknob.png";
    knob = knob ? knob : "pics/pictSlider-knob.png";
    rangeX = rangeX ? rangeX : 128;
    rangeY = rangeY ? rangeY : 128;
    
    // Parasitic inheritance
    var self = new UIElement(name, left, top, width, height);

    var bgimg = self.drawImage( background, left, top, width, height );
    bgimg.style.zIndex = 0;
    //TODO: figure out a good way to allow sizing of knob
    self.knobimg = self.drawImage( knob, left, top, 16, 16 );
    self.knobimg.style.zIndex = 1;
	self.activeknob = activeknob;
	self.knob = knob;

    // window-relative coordinates of cursor
    self.x = left;
    self.y = top;

    self.mouseDownHandler = function( event ) {
        self.knobimg.src = activeknob;
        self.moveCursor( event.docX, event.docY );
    }
    self.mouseMoveHandler = function( event ) {
        self.moveCursor( event.docX, event.docY );
    }
    self.mouseUpHandler = function( event ) {
        self.knobimg.src = knob;
    }
    self.moveCursor = function( x, y ) {
        if (self.mouseIsDown) {
            self.x = Math.max(self.minx, Math.min(self.maxx, x));
            self.y = Math.max(self.miny, Math.min(self.maxy, y));
            self.updateValue();
            sendData(self.name);
        }
    }
    self.updateValue = function() {
        var scaledX = (self.x - self.minx) / (self.maxx - self.minx) * rangeX;
        var scaledY = (self.y - self.miny) / (self.maxy - self.miny) * rangeY;
        self.value = [scaledX, scaledY];
        self.redraw();
    }

    self.redraw = function() {
        // don't let cursor escape!
        self.knobimg.style.left = Math.max(self.minx, Math.min(self.maxx - self.knobimg.width, self.x - self.knobimg.width / 2)) + "px";
        self.knobimg.style.top = Math.max(self.miny, Math.min(self.maxy - self.knobimg.height, self.y - self.knobimg.height / 2)) + "px";
    }

    self.setValue = function(parts) {
        var unscaledX = parseFloat(parts[0]);
        var unscaledY = parseFloat(parts[1]);
        self.x = unscaledX / rangeX * (self.maxx - self.minx) + self.minx;
        self.y = unscaledY / rangeY * (self.maxy - self.miny) + self.miny;
        if (!self.mouseIsDown) {
            self.redraw();
        }
    }
    return self;
}

/*************************************************
    Events
*/


function getScroll() {
    var x, y;
    if( typeof( window.pageYOffset ) == 'number' ) {
        //Firefox, etc.
        x = window.pageXOffset;
        y = window.pageYOffset;
    } else if( document.body && ( document.body.eft || document.body.scrollTop ) ) {
        //DOM compliant
        x = document.body.scrollLeft;
        y = document.body.scrollTop;
    } else if( document.documentElement &&
      ( document.documentElement.eft || document.documentElement.scrollTop ) ) {
        //IE6 standards compliant mode
        x = document.documentElement.scrollLeft;
        y = document.documentElement.scrollTop;
    }
	if (!x) {
		x = 0;
	}
	if (!y) {
		y = 0;
	}
    return [x, y];
}

function handleEvent(event) {
    var scroll = getScroll();
	
    event = event ? event : window.event;
    var customEvent = {
        "clientX": event.clientX,
        "clientY": event.clientY,
        "type": event.type,
        "docX": event.clientX + (scroll[0] ? scroll[0] : 0),
        "docY": event.clientY + (scroll[1] ? scroll[1] : 0)
    };
    //XXX Evil browser detection; workaround for safari bug.  This should
    // disappear.  Can't figure out any other way to do this - safari simply
    // reports the wrong value for clientX and clientY.
    if (navigator.vendor == "Apple Computer, Inc.") {
        customEvent.clientX -= scroll[0];
        customEvent.clientY -= scroll[1];
        customEvent.docX -= scroll[0];
        customEvent.docY -= scroll[1];
    }
    
    for (el in elements) {
        elements[el].handleEvent(customEvent);
    }
}

function loadMaxJax() { 
    document.body.onselectstart = function() { return false; }
    document.body.ondrag = function() { return false; }
	document.onmousemove = handleEvent;
	document.onmouseup   = handleEvent;
	document.onmousedown = handleEvent;

	// Ping for state on load
	setTimeout('makeHttpRequest( null, returns )', refreshRate);
}


/*************************************************
    Debug
*/

function debug(stuff) {
    var d = document.getElementById("debug")
    if (!d) {
        alert(stuff + "\n" + "Additionally, debug print div could not be found.")
    }
    else {
        d.innerHTML += stuff + "<br />";
    }
}
function debugobj(obj) {
    var el = document.getElementById("debug")
    if (!el) {
        alert("Debug div could not be found.");
    } else {
        for (a in obj) {
            el.innerHTML += a + " => " + obj[a] + "<br />";
        }
    }
}
function infoprint(stuff) {
    document.getElementById("info").innerHTML = stuff;
}
function infoprintobj(obj) {
	var el = document.getElementById("info");
	if (!el) {
		alert("Info div could not be found.");
	} else {
		el.innerHTML = "";
		for (a in obj) {
			el.innerHTML += a + " => " + obj[a] + "<br />";
		}
	}
}

/*************************************************
    AJAX
*/
var http_request = false;
function getHttpRequestObj() {
    if (http_request) {
        return;
    }
    if (window.XMLHttpRequest) { // Mozilla, Safari,...
        http_request = new XMLHttpRequest();
        //if (http_request.overrideMimeType) {
        //    http_request.overrideMimeType('text/xml');
        //}
    } else if (window.ActiveXObject) { // IE
        try {
            http_request = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                http_request = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {}
        }
    }
    if (!http_request) {
        alert('Giving up :( Cannot create an XMLHTTP instance.  Try using a different browser.');
        return false;
    }
}
                    
var cached_query = false;
function sendData( element ) {
    var queryStr = "";
    queryStr += element + "=" + elements[element].value;
    // completed or uninitialized, good to go... otherwise cache last.
    if (http_request.readyState == 4 || http_request.readyState == 0) {
        makeHttpRequest( queryStr, returns );
        cached_query = false;
    } else {
        cached_query = queryStr;
    }
}
function makeHttpRequest( queryStr, handler ) {
    if (!queryStr) { 
        queryStr = "" 
    }
    // timestamp is necessary to prevent browser cacheing
    queryStr += "&_timestamp=" + new Date().getTime();

    if (scriptURL) {
        http_request = false;
        getHttpRequestObj();
        http_request.onreadystatechange = handler;
        http_request.open('POST', scriptURL, true);
        http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        http_request.send(queryStr);
    }
}


function returns() {
    if (http_request.readyState == 4) {
        if (cached_query) {
            makeHttpRequest(cached_query, returns);
            cached_query = false;
        } else {
            if (http_request.status == 200) {
                var values = parse_json(http_request.responseText);
                for (value in values) {
                    el = elements[value];
                    if (el) {
                        el.setValue(values[value]);
                    }
                }
            }         }
        clearTimeout();
        setTimeout('makeHttpRequest( null, returns )', refreshRate);
    } 
}

/*
    Parse a JSON text, producing a JavaScript value.
    from json.org
*/
function parse_json(text) {
    return (/^(\s+|[,:{}\[\]]|"(\\["\\\/bfnrtu]|[^\x00-\x1f"\\]+)*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null)+$/.test(text)) &&
        eval('(' + text + ')');
}

//--------------------------  Attic 
function NumberBox(name, left, top, width, height, isFloat, minimum, maximum, color, background, activebg) {
    alert("NumberBox is unfinished non-functional abandoned code");
    width = width ? width : 30;
    height = height ? height : 20;
    isFloat = typeof isFloat != "undefined" ? isFloat : true;
    minimum = minimum ? minimum : Number.NEGATIVE_INFINITY;
    maximum = maximum ? maximum : Number.POSITIVE_INFINITY;
    color = color ? color : "black";
    background = background ? background : "pics/number-bg.png";
    activebg = activebg ? activebg : "pics/number-activebg.png";
    
    // Parasitic inheritance
    var self = new UIElement(name, left, top, width, height);

    var bgimg = self.drawImage( background, left, top, width, height );
    bgimg.style.zIndex = 2;

    var num = document.createElement("div");
    num.id = name + "_numfield";
    num.style.position = "absolute";
    num.style.left = left + "px";
    num.style.top = top + "px";
    num.style.color = color;
    num.style.border = "1px solid red";
    num.style.zIndex = 3;
    num.width = width;
    num.height = height;
    self.div.appendChild(num);

    self.setValue = function( value ) {
        self.truevalue = value;
        if (isFloat) {
            self.value = value;
        } else {
            self.value = Math.floor(value);
        }
        if (parseInt(self.value) == self.value) {
            num.innerHTML = parseInt(self.value);
        } else if (self.value > -1 && self.value < 1) {
            num.innerHTML = self.value.toPrecision(2);
        } else if (self.value > -10 && self.value < 10) {
            num.innerHTML = self.value.toPrecision(3);
        } else {
            num.innerHTML = self.value.toPrecision(4);
        }
        if (num.innerHTML.charAt( num.innerHTML.length - 1 ) == "0" && num.innerHTML != "0") {
            num.innerHTML = num.innerHTML.substring(0, num.innerHTML.length - 1);
        }
        num.innerHTML += "&nbsp;";
    }

    var startY = 0;
    var interval = 0.01;
    self.mouseDownHandler = function(event) {
        startY = event.clientY;
        part = (event.clientX - left) / num.width;
        if (part <= 0.3) {
            interval = 1;
        } else if (part >= 0.3 && part <= 0.6) {
            if (self.value < 10 || self.value > -10) {
                interval = 0.1;
            } else {
                interval = 1;
            }
        } else if (part >= 0.6) {
            if (self.value > 100 || self.value < -100) {
                interval = 1;
            } else if (self.value > 10 || self.value < -10) {
                interval = 0.1;
            } else {
                interval = 0.01;
            }
        }
    }
    self.mouseMoveHandler = function(event) {
        if (self.mouseIsDown) {
            var y = event.clientY;
            if (y < startY) {
                self.setValue( self.truevalue + interval );
            } else if (y > startY) {
                self.setValue( self.truevalue - interval );
            } 
            startY = y;
            sendData(self.name);
        }
    }
    self.setValue(0);
}
