﻿
/*

A windowing system in javascript.
Author: Robert Flack
Alpha release - Mar 28

Include this script and create a new instance of JSwindowGUI. Make sure to call initialize before creation of any windows.
i.e.
wgui = new JSwindowGUI();
wgui.initialize();
wgui.createWindow("This is a test","Test Window");
*/

var wgui;


function JSwindowGUI() {
    /**
    The window stack is initially empty, and is later filled with all
    created windows. This stack is used to relocate windows if the
    container object is lost, and also to z-order all of the windows
    appropriately when one window receives focus.
    */
    this.windowstack = null;

    /**
    The window GUI container object is the object to which all created
    window objects will be appended. Typically document.body should work
    unless you have specific needs.
    */
    this.container = document.body;

    /**
    The window GUI handler object is the object which will receive handler
    methods for mousemove to handle dragging and resizing the windows. As
    document.body only covers the actual used portion of the current page,
    I use document instead which covers all the available space.
    */
    this.handler = document;

    /** The default amount of padding inside a window's content layer. */
    this.defaultPadding = 5;

    /** 
    maxzindex should be higher than the z-indexes of all objects you
    want to see these windows in front of. Also, this must be high enough
    to support the number of windows you expect to have, as each window
    receives a z-index smaller and smaller than this the more windows
    there are.
    */
    this.maxzindex = 500;
    this.minzindex = 300;

    /* The minimum height and width of a window. Cannot be scaled below
    this size. */
    this.minwidth = 120;
    this.minheight = 100;

    /** This is the class name that will be applied to all windows */
    this.windowClass = 'JavascriptWindow';

    this.cellsizes = new Array();
    this.getDocumentObject = function() {
        if (document.documentElement && document.documentElement.scrollTop)
            return document.documentElement;
        else
            return document.body;
    };

    /* Useful for positioning windows in center of screen (works in IE + Firefox and others) */
    this.findScrollTop = function() {
        return this.getDocumentObject().scrollTop;
    };

    this.findScrollLeft = function() {
        return this.getDocumentObject().scrollLeft;
    };

    this.findScrollWidth = function() {
        if (document.documentElement && document.documentElement.scrollWidth)
            return document.documentElement.scrollWidth;
        else
            return document.body.scrollWidth;
    };

    this.findScrollHeight = function() {
        if (document.documentElement && document.documentElement.scrollHeight)
            return document.documentElement.scrollHeight;
        else
            return document.body.scrollHeight;
    };
    this.findWidth = function() {
        if (self.innerWidth)
            return self.innerWidth;
        else if (document.documentElement && document.documentElement.clientWidth)
            return document.documentElement.clientWidth;
        else
            return document.body.clientWidth;
    };

    this.findHeight = function() {
        if (self.innerHeight)
            return self.innerHeight;
        else if (document.documentElement && document.documentElement.clientHeight)
            return document.documentElement.clientHeight;
        else
            return document.body.clientHeight;
    };

    this.findCenterX = function() {
        return this.findScrollLeft() + Math.round(this.findWidth() / 2);
    };

    this.findCenterY = function() {
        return this.findScrollTop() + Math.round(this.findHeight() / 2);
    };

    /** This is a "skin" object for the windows. You can create an
    instance of this and modify the parameters, create your own, or modify
    this one to customize the appearance of the windows.
	
	@param skinpath		The path containing all of the images for this
    skin. Also an easy way to use the same skin
    object for different looks.
    */

    this.JSwindowGUISkin = function(skinpath) {
        //		this.modalbg=skinpath+'noisebg.gif';
        this.modalbg = skinpath + 'ditherbg.gif';
        this.bgcolor = '#666';
        this.bgopacity = 0.5;
        this.borderleft = 3;
        this.bordertop = 24;
        this.borderright = 9;
        this.borderbottom = 9;
        this.cellbg = new Array();
        for (i = 0; i < 3; i++) {
            this.cellbg[i] = new Array();
            for (j = 0; j < 3; j++)
                this.cellbg[i][j] = null;
        }
        this.cellbg[1][1] = '#fff';
        this.cellimg = new Array();
        this.cellimg[0] = new Array();
        this.cellimg[1] = new Array();
        this.cellimg[2] = new Array();
        this.cellimg[0][0] = skinpath + 'tl.gif';
        this.cellimg[0][1] = skinpath + 't.gif';
        this.cellimg[0][2] = skinpath + 'tr.gif';
        this.cellimg[1][0] = skinpath + 'l.gif';
        this.cellimg[1][1] = null;
        this.cellimg[1][2] = skinpath + 'r.gif';
        this.cellimg[2][0] = skinpath + 'bl.gif';
        this.cellimg[2][1] = skinpath + 'b.gif';
        this.cellimg[2][2] = skinpath + 'br.gif';
        this.closeimg = skinpath + 'X.gif';
        this.closeimgdepressed = skinpath + 'X2.gif';
        this.closeimgover = skinpath + 'X3.gif';
        this.closewidth = this.closeheight = 15;
        this.closemargintop = 5;
        this.closemarginright = 8;

        this.titlecolour = '#fff';
        this.titleweight = 'bold';
        this.titlemarginleft = 10;
        this.titlemargintop = 5;
        this.titlealign = 'left';
        this.titleheight = 17;

        this.iframeLessX = 3;
        this.iframeLessY = 3;
    };

    this.windowskin = new this.JSwindowGUISkin('../images/windowskin/newskin/');

    /** Browser check for certain functionalities */
    this.IE = document.all ? true : false;

    /** Sets the z-index of all windows according to their position on
    the stack. */
    this.setZOrder = function() {
        var curw = this.windowstack;
        var curz = this.maxzindex;
        while (curw != null) {
            curw.style.zIndex = curz;
            if (curz > this.minzindex)
                curz--;
            curw = curw.next;
        }
    }

    /**
    If for some reason your container object is removed or the windows
    become detached from it you can call this function to reattach all
    windows to the container object.
    */
    this.reattachWindows = function() {
        var curw;
        curw = this.windowstack;
        while (curw != null) {
            this.container.appendChild(curw);
            curw = curw.next;
        }
    }

    /** This function is useful in scripts to obtain the window containing
    any piece of code. I.e. in javascript you may wish to know information
    that is stored relative to the window or modify the window's properties
    */
    this.findParentWindow = function(elem) {
        var e = elem.parentNode;
        while (e != null && e.className != this.windowClass) {
            e = e.parentNode;
        }
        return e;
    }

    /**
    Creates a window and returns a handler to the newly created window. By
    default, the window will appear centered on the screen.

	@param windowcontents	The innerHTML value for the window's contents
    @param wtitle		The title to appear on the window
    @param wname		A textual name for the window that must be
    unique. If a window exists with this name its
    handle will be returned instead.
    @param wwidth		The width for the new window
    @param wheight		The height for the new window
    @param modal		Is this a modal window (background is grayed
    out and only this window can receive events.
    @return			A new window (div element) with the specified
    attributes
    */
    this.createWindow = function(windowcontents, wtitle, wname, wwidth, wheight, modal) {
        /* kindly alert the programmer if they have forgotten to
        initialize this object */
        if (!this.initialized) {
            alert("Please initialize JSwindowGUI before creating windows");
            return false;
        }
        var curw;

        /* Search for an existing window with the specified name. */
        if (wname != null && wname != '') {
            curw = this.windowstack;
            while (curw != null) {
                if (curw.name == wname) {
                    curw.moveToFront();
                    return curw;
                }
                curw = curw.next;
            }
        }
        var curtr;

        /* Create the div element for the window */
        curw = document.createElement('div');
        curw.className = this.windowClass;
        curw.name = wname;
        curw.minwidth = this.minwidth;
        curw.minheight = this.minheight;
        curw.style.position = 'absolute';
        curw.style.display = 'block';
        curw.windowgui = this;

        if (modal) {
            var bkgw, bkgh;
            curw.modal = true;
            curw.background = document.createElement('div');
            curw.background.style.position = 'absolute';
            if (this.windowskin.bgcolor)
                curw.background.style.background = this.windowskin.bgcolor;
            if (this.windowskin.modalbg)
                curw.background.style.backgroundImage = 'url("' + this.windowskin.modalbg + '")';
            curw.background.style.top = '-50px';
            curw.background.style.left = '-50px';
            curw.background.style.zIndex = this.maxzindex - 1;
            curw.background.style.opacity = this.windowskin.bgopacity;
            curw.background.style.mozOpacity = this.windowskin.bgopacity;
            curw.background.style.filter = 'alpha(opacity=' + (this.windowskin.bgopacity * 100) + ')';
            if (this.findScrollWidth() > this.findWidth())
                bkgw = this.findScrollWidth();
            else
                bkgw = this.findWidth();
            if (this.findScrollHeight() > this.findHeight())
                bkgh = this.findScrollHeight();
            else
                bkgh = this.findHeight();
            curw.background.style.width = (bkgw + 50) + 'px';
            curw.background.style.height = (bkgh + 50) + 'px';
            document.body.insertBefore(curw.background, document.body.childNodes[0]);
        }

        /* Internet Explorer does not properly support the z-index
        property: for some reason select input's appear above
        everything except for iframes. Thus we can use an iframe
        (which does happen to respect z-index) to hide any dropdown
        lists behind the window. */
        if (this.IE) {
            curw.iframe = document.createElement('iframe');
            this.container.appendChild(curw.iframe);
            curw.iframe.style.position = 'absolute';
            curw.iframe.lessX = this.windowskin.iframeLessX;
            curw.iframe.lessY = this.windowskin.iframeLessY;
        }
        //		curw.onselectstart=function(){return false;};

        /* The skin of the window is accomplished with a table */
        curw.windowtable = document.createElement('table');
        curw.windowtable.cellSpacing = 0;
        curw.windowtable.tbody = document.createElement('tbody');
        curw.windowtable.appendChild(curw.windowtable.tbody);

        curw.windowtable.tcells = new Array();
        for (i = 0; i < 3; i++) {
            curw.windowtable.tcells[i] = new Array();
            curtr = document.createElement('tr');
            for (j = 0; j < 3; j++) {
                curw.windowtable.tcells[i][j] = document.createElement('td');
                curw.style.overflow = 'hidden';
                if (this.windowskin.cellimg[i][j] != null) {
                    curw.windowtable.tcells[i][j].style.backgroundImage = "url('" + this.windowskin.cellimg[i][j] + "')";
                }
                if (this.windowskin.cellbg[i][j] != null) {
                    curw.windowtable.tcells[i][j].style.background = this.windowskin.cellbg[i][j];
                }
                curw.windowtable.tcells[i][j].guiwindow = curw;
                curtr.appendChild(curw.windowtable.tcells[i][j]);
                if (i != 1 || j != 1)
                    curw.windowtable.tcells[i][j].onselectstart = function() { return false; };
                if (j == 0)
                    curw.windowtable.tcells[i][j].style.width = this.windowskin.borderleft + 'px';
                if (j == 2)
                    curw.windowtable.tcells[i][j].style.width = this.windowskin.borderright + 'px';
                if (i == 0) {
                    curw.windowtable.tcells[i][j].style.height = this.windowskin.bordertop + 'px';
                    curw.windowtable.tcells[i][j].onmousedown = function(e) {
                        if (this.guiwindow.mouseaction == -1)
                            this.guiwindow.mouseaction = 0;
                        else
                            this.guiwindow.mouseaction = 1;
                        this.guiwindow.onmousedown(e);
                        return false;
                    }
                }
                if (i == 2)
                    curw.windowtable.tcells[i][j].style.height = this.windowskin.borderbottom + 'px';
            }
            curw.windowtable.tbody.appendChild(curtr);
        }

        // Create the close button

        curw.closebutton = document.createElement('div');
        curw.closebutton.setAttribute('id', 'closeButton');
        curw.closebutton.style.position = 'absolute';
        curw.closebutton.style.width = this.windowskin.closewidth + 'px';
        curw.closebutton.style.height = this.windowskin.closeheight + 'px';
        curw.closebutton.style.backgroundRepeat = "no-repeat";
        curw.closebutton.style.backgroundImage = "url('" + this.windowskin.closeimg + "')";
        curw.closebutton.img1 = this.windowskin.closeimg;
        curw.closebutton.img2 = this.windowskin.closeimgdepressed;
        curw.closebutton.img3 = this.windowskin.closeimgover;
        curw.closebutton.mright = (this.windowskin.closemarginright + this.windowskin.closewidth);
        curw.closebutton.style.top = this.windowskin.closemargintop + 'px';
        //		curw.closebutton.style.styleFloat=curw.closebutton.style.cssFloat='right';
        curw.closebutton.guiwindow = curw;
        curw.closebutton.onmousedown = function() {
            this.style.backgroundImage = "url('" + this.img2 + "')";
            this.guiwindow.mouseaction = -1;
            return false;
        }
        curw.closebutton.onmouseover = function() {
            this.style.backgroundImage = "url('" + this.img3 + "')";
        }
        curw.closebutton.onmouseup = curw.closebutton.onmouseout = function() {
            this.style.backgroundImage = "url('" + this.img1 + "')";
        }
        curw.closebutton.onclick = function() {
            this.guiwindow.close();
        }
        curw.windowtable.tcells[0][1].appendChild(curw.closebutton);

        // Create the window title

        curw.title = wtitle;
        curw.titlelabel = document.createElement('div');
        curw.titlelabel.style.position = 'absolute';
        curw.titlelabel.style.color = this.windowskin.titlecolour;
        curw.titlelabel.style.fontWeight = this.windowskin.titleweight;
        curw.titlelabel.style.display = 'block';
        curw.titlelabel.lessX = this.windowskin.borderleft + this.windowskin.borderright + this.windowskin.closewidth + this.windowskin.closemarginright + this.windowskin.titlemarginleft;
        if (this.IE)
            curw.titlelabel.lessX += this.windowskin.titlemarginleft;
        curw.titlelabel.style.left = this.windowskin.titlemarginleft + 'px';
        curw.titlelabel.style.top = this.windowskin.titlemargintop + 'px';
        /*		if (this.IE)
        curw.titlelabel.style.height=(this.windowskin.titleheight-this.windowskin.titlemargintop)+'px';
        else */
        curw.titlelabel.style.height = this.windowskin.titleheight + 'px';

        curw.titlelabel.style.textAlign = this.windowskin.titlealign;
        curw.titlelabel.style.cursor = 'default';
        curw.titlelabel.style.overflow = 'hidden';
        curw.titlelabel.appendChild(document.createTextNode(wtitle));
        curw.windowtable.tcells[0][1].appendChild(curw.titlelabel);

        curw.close = function() {
            if (curw.onclose()) {
                if (this.prev != null)
                    this.prev.next = this.next;
                if (this.next != null)
                    this.next.prev = this.prev;
                if (this == this.windowgui.windowstack) {
                    this.windowgui.windowstack = this.next;
                }
                if (this.iframe)
                    this.windowgui.container.removeChild(this.iframe);
                if (this.modal)
                    document.body.removeChild(this.background);
                this.windowgui.container.removeChild(this);
                this.closed = 'true';
            }
        };

        curw.windowtable.tcells[1][2].style.cursor = 'e-resize';
        curw.windowtable.tcells[1][2].onmousedown = function(e) {
            this.guiwindow.mouseaction = 2;
            this.guiwindow.onmousedown(e);
            return false;
        };

        curw.windowtable.tcells[2][0].style.cursor = curw.windowtable.tcells[2][1].style.cursor = 's-resize';
        curw.windowtable.tcells[2][0].onmousedown = curw.windowtable.tcells[2][1].onmousedown = function(e) {
            this.guiwindow.mouseaction = 3;
            this.guiwindow.onmousedown(e);
            return false;
        }

        curw.windowtable.tcells[2][2].style.cursor = 'se-resize';
        curw.windowtable.tcells[2][2].onmousedown = function(e) {
            this.guiwindow.mouseaction = 4;
            this.guiwindow.onmousedown(e);
            return false;
        }

        /* Construct the content element */
        curw.content = curw.windowtable.tcells[1][1];
        curw.content.lessX = this.windowskin.borderleft + this.windowskin.borderright;
        curw.content.lessY = this.windowskin.bordertop + this.windowskin.borderbottom;
        curw.content.divlayer = document.createElement('div');
        curw.content.divlayer.style.overflow = 'auto';
        curw.content.divlayer.style.textAlign = 'left';
        curw.content.divlayer.guiwindow = curw;
        curw.innerPadding = this.defaultPadding;

        curw.content.divlayer.innerHTML = windowcontents;
        curw.content.appendChild(curw.content.divlayer);

        curw.appendChild(curw.windowtable);

        /* Override this function to provide functionality when an
        ajax request completes and the page has loaded into the window
        */
        curw.ondisplaypage = function() {
            return true;
        }

        /* To simplify loading dynamic content into the windows, this
        function can serve as a listener for Ajax requests and will
        display the returned information as HTML content. */
        curw.ajaxlistener = function(ajaxobj, wnd) {
            var loadstr;
            var finalpage = 0;
            switch (ajaxobj.readyState) {
                case 0:
                    loadstr = "Initialized...";
                    break;
                case 1:
                    loadstr = "Connecting...";
                    break;
                case 2:
                    loadstr = "Connected...";
                    break;
                case 3:
                    loadstr = "Receiving Data...";
                    break;
                case 4:
                    if (ajaxobj.status == 200) {
                        finalpage = 1;
                        loadstr = ajaxobj.responseText;
                        var bodypos, bodyend, endbodypos;
                        var bodypos = loadstr.toLowerCase().indexOf('<body');
                        if (bodypos >= 0) {
                            bodyend = loadstr.indexOf('>', bodypos);
                            endbodypos = loadstr.toLowerCase().indexOf('</body>', bodyend);
                            loadstr = loadstr.substring(bodyend + 1, endbodypos);
                        }
                    } else {
                        loadstr = ajaxobj.statusText;
                    }
                    break;
            }
            wnd.content.divlayer.innerHTML = loadstr;
            if (finalpage == 1) wnd.ondisplaypage();
            return true;
        }

        /* Everytime properties are changed, this function must be
        called to update the physical div's position with respect
        to the specified values. */
        curw.updateposition = function() {
            this.style.top = this.y + 'px';
            this.style.left = this.x + 'px';
            if (this.iframe) {
                this.iframe.style.top = this.y + 'px';
                this.iframe.style.left = this.x + 'px';
            }
        }

        /* When the properties are changed for the window, call this
        funciton which will update all of the window's elements'
        positions and sizes. */
        curw.update = function() {
            this.style.top = this.y + 'px';
            this.style.left = this.x + 'px';
            this.style.width = this.width + 'px';
            this.style.height = this.height + 'px';
            this.content.divlayer.style.width = (this.width - this.content.lessX - 2 * this.innerPadding) + 'px';
            this.content.style.width = (this.width - this.content.lessX) + 'px';
            this.content.divlayer.style.height = (this.height - this.content.lessY - 2 * this.innerPadding) + 'px';
            this.content.style.height = (this.height - this.content.lessY) + 'px';
            this.content.divlayer.style.padding = this.innerPadding + 'px';
            this.titlelabel.style.width = (this.width - this.titlelabel.lessX) + 'px';
            this.closebutton.style.left = (this.width - this.closebutton.mright) + 'px';
            if (this.iframe) {
                this.iframe.style.top = this.y + 'px';
                this.iframe.style.left = this.x + 'px';
                this.iframe.style.width = (this.width - this.iframe.lessX) + 'px';
                this.iframe.style.height = (this.height - this.iframe.lessY) + 'px';
            }
        }

        /* Default width and height for a window */
        if (wwidth == null)
            curw.width = 300;
        else
            curw.width = wwidth;
        if (wheight == null)
            curw.height = 300;
        else
            curw.height = wheight;

        curw.windowgui = this;
        curw.mouseaction = 0;

        curw.getcoords = function(e) {
            if (this.windowgui.IE) { // grab the x-y pos.s if browser is IE
                this.mouseX = event.clientX + document.body.scrollLeft;
                this.mouseY = event.clientY + document.body.scrollTop;
            }
            else {  // grab the x-y pos.s if browser is NS
                this.mouseX = e.pageX;
                this.mouseY = e.pageY;
            }
        }

        /* Move this window to the front of the stack, and z-order */
        curw.moveToFront = function() {
            // Move this window to the front
            if (this != this.windowgui.windowstack) {
                if (this.prev != null)
                    this.prev.next = this.next;
                if (this.next != null)
                    this.next.prev = this.prev;

                this.next = this.windowgui.windowstack;
                this.next.prev = this;
                this.prev = null;
                this.windowgui.windowstack = this;
            }
            // Step through and apply z-indexes
            this.windowgui.setZOrder();
        }

        /* If the user clicks within the window, it should receive
        focus. Also, remember the cursor's position in case the user
        is dragging the title bar or one of the resizing handles */
        curw.onmousedown = function(e) {
            this.getcoords(e);
            this.windowgui.handler.selwindow = this;
            this.diffX = this.mouseX - this.x;
            this.diffY = this.mouseY - this.y;
            this.diffSX = this.mouseX - this.width;
            this.diffSY = this.mouseY - this.height;
            this.moveToFront();

            this.windowgui.handler.onselectstart = function() { return false; }
            this.onfocus();
        }

        /* Attempt to center the window on the page. Depends heavily
        on the browser supported functions. */
        curw.center = function() {
            this.x = this.windowgui.findCenterX() - Math.round(curw.width / 2);
            this.y = this.windowgui.findCenterY() - Math.round(curw.height / 2);
            this.update();
        }

        curw.center();

        this.container.appendChild(curw);
        if (this.windowstack == null) {
            curw.next = null;
            curw.prev = null;
            this.windowstack = curw;
        } else {
            curw.next = this.windowstack;
            this.windowstack.prev = curw;
            this.windowstack = curw;
        }
        this.setZOrder();
        curw.onresize = function() { return true; }; // Event handler for window resize
        curw.onclose = function() { return true; }; // Event handler for closing window
        curw.onfocus = function() { return true; }; // Event handler called when window receives focus
        return curw;
    }

    /* Initialize the windowGUI object by preparing the handler object
    with the necessary functions to respond to window events */
    this.initialize = function() {
        if (!(document && document.createElement && document.createTextNode)) return false;      
        this.container.windowgui = this;
        this.handler.selwindow = null;
        this.handler.onmousemove = function(e) {
            if (this.selwindow != null) {
                var curw = this.selwindow;

                if (curw.mouseaction == 1)	// Currently moving window
                {
                    curw.getcoords(e);
                    curw.x = curw.mouseX - curw.diffX;
                    curw.y = curw.mouseY - curw.diffY;
                    if (curw.x < 0) curw.x = 0;
                    if (curw.y < 0) curw.y = 0;
                    curw.updateposition();
                } else if (curw.mouseaction > 0 && curw.mouseaction <= 4)	// Resize window
                {
                    curw.getcoords(e);
                    if (curw.mouseaction == 2 || curw.mouseaction == 4)
                        curw.width = curw.mouseX - curw.diffSX;
                    if (curw.mouseaction >= 3)
                        curw.height = curw.mouseY - curw.diffSY;
                    if (curw.width < curw.minwidth) curw.width = curw.minwidth;
                    if (curw.height < curw.minheight) curw.height = curw.minheight;
                    curw.update();
                    curw.onresize();
                }
            }
        }
        this.handler.onmouseup = function() {
            if (this.selwindow != null) {
                var curw = this.selwindow;
                curw.mouseaction = 0;
                this.onselectstart = function() { return true; }
            }
        }
        this.initialized = true;
        return this;
    }
};

function SetupWindowGUI() {
    wgui = new JSwindowGUI();
    wgui.initialize();
}

if (window.attachEvent) { window.attachEvent('onload', SetupWindowGUI); }
else if (window.addEventListener) { window.addEventListener("load", SetupWindowGUI, false); }
else { window.onload = SetupWindowGUI; }
