/*
 * @(#)horizonj.js  3.3 10/11/01
 *
 * Copyright (c) 2000-2006 JSoft, Inc. All Rights Reserved.
 *
 * JSoft grants you ("Licensee") a non-exclusive license to use and modify
 * this software in source and binary code form, provided that i) this 
 * copyright notice and license appear on all copies of the software; and
 * ii) Licensee does not utilize the software in a manner which is
 * disparaging to JSoft.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. JSOFT AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL JSOFT OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF JSOFT HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */
 
/*
 * The purpose of the horizonj javascript utilities is to provide a set of
 * javascript APIs that support rich client applications.
 */

var HJ = {
    
    Version: '3.3',

    messages: [],
    addMessage: function(msg) { this.messages[this.messages.length] = msg; },  
    showMessages: function() { for (var i=0; i< this.messages.length; i++) { alert(this.messages[i]); } this.clearMessages(); },
    clearMessages: function() { this.messages = [] },
    debugWindow: null,
    debug: function() { if (!this.debugWindow) this.debugWindow = window.open('blank.html','hjdebug','height=600,width=300,scrollbars,resizable');
                        var clear = '<a href="#" onclick="document.body.innerHTML=\'\'">clear window</a><br/>';
                        this.debugWindow.document.writeln('<h2>HorizonJ Debug Window</h2><br/>'+clear);
                        this.debugWindow.focus(); },
    log: function(s) { if (this.debugWindow) this.debugWindow.document.writeln(s+'<br/>'); }
}

//HJ.debug();

document.HJ = HJ;

HJ.Util = {}              // Basic Utility Functions
HJ.Element = {}           // DOM Element Manipulation and Navigation
HJ.PublishSubscribe = {}  // PublishSubscribe
HJ.Event = {}             // Event 
HJ.DragDrop = {}          // Drag drop
HJ.Form = {}              // Extend Form Behaviors
HJ.ServerCall = {}        // Server Call (AJAX)
HJ.Table = {}             // Data Table Widget
HJ.AutoComplete = {}      // Auto-complete Widget
HJ.Window = {}            // Windows Simulation
HJ.Tree = {}              // Tree widget
HJ.List = {}              // List Widget
HJ.Container = {}         // Resizable UI containers

HJ.Private = {}           // Private functions not intended to be used outside the API

HJ.Constants = {
   
    FIELD_ERROR_COLOR: 'yellow',
    Functions: {
        EMPTY:         function() {},
        STOP_EVENT:    function(event) { HJ.Event.stop(event); },
        RETURN_FALSE:  function() { return false; },
        RETURN_TRUE:   function() { return true; }
   }
}

HJ.Config = {
    
    RESOURCE_PATH: 'hjresources/',
    setResourcePath: function(path) { HJ.Config.RESOURCE_PATH = path; }  // relative to html, must end with '/' or be blank
}

HJ.Browser = function() {
    
    this.isOpera = navigator.userAgent.indexOf('Opera') != -1;
    this.isIE = (!HJ.isOpera && navigator.userAgent.indexOf('MSIE') != -1);
    this.isNS = navigator.userAgent.indexOf('Netscape') != -1;
    this.isFF = navigator.userAgent.indexOf('Firefox') != -1;

    // methods used to make determinations that are sometimes browser specific
    
    this.isEvent = function (elementOrEvent) {
        return (elementOrEvent.innerHTML == undefined || elementOrEvent.srcElement != undefined)
    };
}

HJ.Browser = new HJ.Browser();

//---------- Global HJ functions ----------

/*
* Returns an element.  If a string is passed, get the element by id.  If an event
* is passed in return the target element, otherwise return the passed in element.
*/
HJ.el = function(element)
{
    if (typeof element == 'string')
    {
        if (document.getElementById) { return document.getElementById(element); }
        else if(document.all)        { return document.all[element]; }
        else if(document.layers)     { return document.layers[element]; }
        return null;
    }

//SCM TODO - support passing in an event to get target element ?    
//    if (HJ.Browser.isEvent(element))
//    {
//        return HJ.Event.getTargetElement(element);
//    }
//    else
    {
        return element;
    }
}

/*
* Get the parent of the element based on tag name or this element if the tag matches
*/

HJ.parent = function(element, tagName)
{
    var el = HJ.el(element);
    var lcTagName = tagName.toLowerCase();
    if (el.tagName && (el.tagName.toLowerCase() == lcTagName)) { return el; }
    var foundEl = HJ.Element.getParentByTagName(lcTagName, el);
    if (!foundEl) { alert('No ' + tagName + ' associated with element'); }
    return foundEl;
}

/*
* Get the form associated with this element
*/
HJ.form = function(element)
{
    var el = HJ.el(element);
    if (el.tagName && el.tagName.toLowerCase() == 'form') { return el; }
    if (el.form) { return el.form; }
    var form = HJ.Element.getParentByTagName('form', el);
    return form;
}

//---------- HJ.Util : All purpose Utility functions ----------

/*
* Check a string to see if it is all white space
*/
HJ.Util.isEmptyString = function(s)
{
    if (s == null) { return true; }
    
    for (var i=0; i <s.length; i++)
    {
        var c = s.charAt(i);
        if ((c!=' ') && (c!= '\n') && (c!='\t')) return false;
    }
    return true;
}

HJ.Util.trimWhiteSpace = function(s)
{
    //return s.replace(/^\s*|\s*$/g, '');
    var ss = s.replace(/^\s+/, '');
    return HJ.Util.trimTrailing(ss);
}

HJ.Util.trimLeading = function(s)
{
    return s.replace(/^\s+/, '');
}

HJ.Util.trimTrailing = function(s)
{
    //return s.replace(/\s+$/, '');   // for long strings causes Firefox to use 100% CPU for seconds
    if (s.length < 1000)
        return s.replace(/\s+$/, '');
    if (s.slice(-1).match(/\s/))
        return s.slice(0, -100) + s.slice(-100).replace(/\s+$/, '');
    return s;
}

HJ.Util.startsWithIgnoreCase = function(s, startsWith)
{
    return s.toLowerCase().indexOf(startsWith.toLowerCase()) == 0;
}

HJ.Util.startsWith = function(s, startsWith)
{
    return s.indexOf(startsWith) == 0;
}

HJ.Util.endsWithIgnoreCase = function(s, endsWith)
{
    if (endsWith.length > s.length) { return false; }
    return s.toLowerCase().indexOf(endsWith.toLowerCase()) == s.length-endsWith.length;
}

HJ.Util.endsWith = function(s, endsWith)
{
    if (endsWith.length > s.length) { return false; }
    return s.indexOf(endsWith) == s.length-endsWith.length;
}

/*
* Print an objects properties.  This is useful for debugging.
*/
HJ.Util.printObject = function(the_obj)
{
    var s=''; for (i in the_obj) {s+=i+':'+the_obj[i]+' ';};alert(s);
}

/*
* Get an integer value from a string that may contain "px"
*/

HJ.Util.getInteger = function(str)
{
    if (typeof str == 'number') return str;
    var idx = str.indexOf('px');
    if (idx == -1)
    {
        idx = str.indexOf('%');
    }
    if (idx != -1)
    {
        return parseInt(str.substring(0,idx));
    }
    var theInt = parseInt(str);
    return theInt;
}

HJ.Util.forEach = function(array, func)
{
    for (var i=0; i < array.length; i++)
    {
        func(array[i]);   
    }
}

HJ.Util.isArray = function(maybeArray)
{
    var isArray = (maybeArray != null && maybeArray != undefined &&
         typeof maybeArray == 'object' &&
         ((maybeArray.constructor != undefined && maybeArray.constructor == Array) ||
         (maybeArray.length != undefined && maybeArray.length != null && maybeArray.id == undefined
         && typeof maybeArray.length == 'number')
         ));
    return isArray;
}

function isArray(testObject) {       return testObject && !(testObject.propertyIsEnumerable('length')) && typeof testObject === 'object' && typeof testObject.length === 'number';}

//---------- HJ.Util.Keyboard : Handle keyboard events and hot keys

HJ.Util.Keyboard = {};
HJ.Util.Keyboard.enabled = false;

HJ.Util.Keyboard.enable = function()
{
    if (!HJ.Util.Keyboard.enabled)
    {
        HJ.Event.registerHandler(document, 'keypress', HJ.Util.Keyboard.handleKeyPress);
        HJ.Event.registerHandler(document, 'keydown', HJ.Util.Keyboard.handleKey);
        HJ.Event.registerHandler(document, 'keyup', HJ.Util.Keyboard.releaseKey);
        HJ.Util.Keyboard.enabled = true;
    }
}

HJ.Util.Keyboard.shiftKeyDown = false;
HJ.Util.Keyboard.controlKeyDown = false;
HJ.Util.Keyboard.altKeyDown = false;
HJ.Util.Keyboard.escapeKeyDown = false;

HJ.Util.Keyboard.KEY_NAME_MAP = {
            'backspace':8,
            'tab':9,
            'return':13,
            'enter':13,
            'shift':16,
            'ctrl':17,
            'control':17,
            'alt':18,
            'pause':19,
            'break':19,
            'capslock':20,
            'caps':20,
            'escape':27,
            'space':32,
            'pageup':33,
            'pagedown':34,
            'end':35,
            'home':36,
            'left':37,
            'up':38,
            'right':39,
            'down':40,
            'insert':45,
            'delete':46,
            'f1':112,
            'F1':112,
            'f2':113,
            'F2':113,
            'f3':114,
            'F3':114,
            'f4':115,
            'F4':115,
            'f5':116,
            'F5':116,
            'f6':117,
            'F6':117,
            'f7':118,
            'F7':118,
            'f8':119,
            'F8':119,
            'f9':120,
            'F9':120,
            'f10':121,
            'F10':121,
            'f11':122,
            'F11':122,
            'f12':123,
            'F12':123,
            'numlock':144,
            'num':144,
            'scrolllock':145
        };
        
HJ.Util.Keyboard.KEY_MAP = {}

// Create a reverse lookup map (keyed by keycode)
for (var i in HJ.Util.Keyboard.KEY_NAME_MAP)
{
    if (!HJ.Util.Keyboard.KEY_MAP[i])
    {
        HJ.Util.Keyboard.KEY_MAP[HJ.Util.Keyboard.KEY_NAME_MAP[i]]=i;
    }
}

HJ.Util.Keyboard.handleKeyPress = function(event)
{
    var e = event || window.event;
    var keyCode = e.keyCode || e.which;
    //window.status += '[kp-' + keyCode +']'
    if (keyCode == 16)
    var isShiftKey = e.shiftKey ? e.shiftKey : ( (keyCode == 16) ? true:false);
    isShiftKey == isShiftKey;// || HJ.Util.Keyboard.shiftKeyDown;
    var capsLockOn = (((keyCode >= 65 && keyCode <= 90) && !isShiftKey) ||
                               ((keyCode >= 97 && keyCode <= 122) && isShiftKey));
                               
    var ch = String.fromCharCode(keyCode).toLowerCase();
        
    if (capsLockOn)
    {
        ch = ch.toUpperCase();
    }
    
    //window.status += ch;
}

HJ.Util.Keyboard.handleKey = function(event)
{
    var e = event || window.event;
    var keyCode = e.keyCode || e.which;
    //window.status += '[kd-' + keyCode +']'
    
    var keyCodeStr = ''+keyCode;
    var keySequence = HJ.Util.Keyboard.KEY_MAP[keyCodeStr];

    if (!HJ.Util.Keyboard.KEY_MAP[keyCodeStr])
    {
        var ch = String.fromCharCode(keyCode).toLowerCase();
        
        if (HJ.Util.Keyboard.controlKeyDown ||
            HJ.Util.Keyboard.altKeyDown || HJ.Util.Keyboard.escKeyDown)
        {
            if (HJ.Util.Keyboard.shiftKeyDown) {
                keySequence = 'shift-';
            }
            if (HJ.Util.Keyboard.controlKeyDown) {
                keySequence = 'ctrl-';
            }
            if (HJ.Util.Keyboard.altKeyDown) {
                keySequence = 'alt-';
            }
            if (HJ.Util.Keyboard.escapeKeyDown) {
                keySequence = 'esc-';
            }
            
            keySequence += ch;
            //window.status += '<' + keySequence + '>';
            
            HJ.Util.Keyboard.fire(e, keySequence);
            
            // Prevent multiple fires (e.g. when hold down ctrl-1 for a while)          
            HJ.Util.Keyboard.controlKeyDown = false;
            HJ.Util.Keyboard.altKeyDown = false;
            HJ.Util.Keyboard.esacapeKeyDown = false;
        }
        return true;
    }

    HJ.Util.Keyboard.fire(e, keySequence);

    switch (keyCode)
    {
        case 32:   return; // space
        case 8:   /*window.status = window.status.substring(0,window.status.length-1);*/ return;
        
        case 16:  HJ.Util.Keyboard.shiftKeyDown = true; return; // Shift key
        case 17:  HJ.Util.Keyboard.controlKeyDown = true; return; // Control key
        case 18:  HJ.Util.Keyboard.altKeyDown = true;     return; // Alt key
        case 27:  HJ.Util.Keyboard.escapeKeyDown = true;  return; // Escape Key
    }
    
    //window.status += '<' + HJ.Util.Keyboard.KEY_MAP[keyCodeStr] + '>';    
}

HJ.Util.Keyboard.releaseKey = function(event)
{
    var e = event || window.event;
    var keyCode = e.keyCode || e.which;
    //window.status += '[ku-' + keyCode +']'
    
    switch (keyCode)
    {
        case 16:  HJ.Util.Keyboard.shiftKeyDown = false;
        case 17:  HJ.Util.Keyboard.controlKeyDown = false;
        case 18:  HJ.Util.Keyboard.altKeyDown = false;
        case 27:  HJ.Util.Keyboard.esacapeKeyDown = false;
    }
    return;
}

HJ.Util.Keyboard.fire = function(event,keySequence)
{
    var el = HJ.Event.getTargetElement(event);

    // Search for handlers input -> form -> document
    if (!el.keyboardEvents || !el.keyboardEvents[keySequence])
    {
        var form = HJ.form(el);
        if (!form || !form.keyboardEvents || !form.keyboardEvents[keySequence])
        {
            if (!document.keyboardEvents || !document.keyboardEvents[keySequence])
            {
                return false;
            }
            el = document;
        }
        else
        {
            el = form;
        }
    }   
        
    el.keyboardEvents[keySequence].apply(null, [event]);
}

    

HJ.Event.registerKeyboardHandler = function(element, keySequence, theFunc)
{
    HJ.Util.Keyboard.enable();
    
    var el = HJ.el(element);
    
    var keys = keySequence.split('-');

    if (!el.keyboardEvents) el.keyboardEvents = {};
    
    el.keyboardEvents[keySequence] = theFunc;
}


//---------- HJ.Element : Element manipulation and navigation ----------

/*
* Gets an array of class names for the element, or an empty array
*/
HJ.Element.getClassNames = function(elementOrId)
{
    var el = HJ.el(elementOrId);
    return ( el && el.className ? el.className.split(' ') : []);
}

/*
* Determine if the element has a given className in the list of class names.
*/
HJ.Element.isClass = function(elementOrId, className)
{
    var names = HJ.Element.getClassNames(elementOrId);
    
    for (var i=0; names && i < names.length; i++)
    {
        if (className == names[i])
        {
            return true;
        }
    }
    return false;
}

/*
* Get all elements that have any of the specified class names.  Start at the specified root
* or at the document level if no root is specified.
*/

HJ.Element.getElementsByClassName = function(classNames, root)
{
    var childElements;
    
    if (HJ.Util.isArray(root))
    {
        childElements = root;
    }
    else if (typeof root == 'function')  // in safari NodesList is typeof function
    {
		childElements = new Array();
		for (var i; i<root.length;i++)
		{
			childElements.push(root[i]);
		}    
    }
    else
    {           
        var theRootEl;
        theRootEl = ( root ? HJ.el(root) : document.body);
        if (!theRootEl) { return null; }
        childElements = HJ.Element.getAllElements(theRootEl);
    }
    
    var classNamesArray = (typeof classNames == 'string' ? classNames.split(' ') : classNames);
    
    var elements = new Array();

/* Reg exp matching seems to make no difference in performance
    var regExp;
    if (classNamesArray.length == 1)
    {
        regExp = new RegExp("(.*\\s+|^)"+classNames+"(\\s+.*|$)");
    }
*/    
    for (var i=0; i < childElements.length; i++)
    {
        var el = childElements[i];
        
        if (!el.className || el.className.length < 1) continue;
/*        
        // If there is one class, this should be much easier
        if (classNamesArray.length == 1)
        {
            if (regExp.test(el.className))
            {
                elements.push(el);
            }
            continue;
        }
*/    
        for (var j=0; j<classNamesArray.length; j++)
        {            
            if (HJ.Element.isClass(el,classNamesArray[j]))
            {
                elements.push(el);
                break;
            }
        }
    }
    return elements;
}

/*
* Get the first element that has any of the specified class names.  Start at the specified root
* or at the document level if no root is specified.
*/

HJ.Element.getFirstElementByClassName = function(classNames, root)
{
    var childElements;
    
    if (HJ.Util.isArray(root))
    {
        childElements = root;
    }
    else if (typeof root == 'function')  // in safari NodesList is typeof function
    {
		childElements = new Array();
		for (var i; i<root.length;i++)
		{
			childElements.push(root[i]);
		}    
    }
    else
    {           
        var theRootEl;
        theRootEl = ( root ? HJ.el(root) : document);
        if (!theRootEl) { return null; }
        childElements = HJ.Element.getAllElements(theRootEl);
    }
    
    var classNamesArray = (typeof classNames == 'string' ? classNames.split(' ') : classNames);
    
    var elements = new Array();
    
    for (var i=0; i < childElements.length; i++)
    {
        var el = childElements[i];
        
        if (!el.className || el.className.length < 1) continue;
        
        for (var j=0; j<classNamesArray.length; j++)
        {            
            if (HJ.Element.isClass(el,classNamesArray[j]))
            {
                return el;
            }
        }
    }
    return null;
}

/*
* Get the first element that has any of the specified tag and class names.  Start at the specified root
* or at the document level if no root is specified.
*/
HJ.Element.getFirstElementByTagAndClassName = function(tagName, classNames, root)
{
	var tags = HJ.Element.getElementsByTagName(tagName, root);
	if (tags == null || tags.length == 0) { return null; }

    var classNamesArray = (typeof classNames == 'string' ? classNames.split(' ') : classNames);

    for (var i=0; i < tags.length; i++)
    {
        var el = tags[i];
        
        if (!el.className || el.className.length < 1) continue;
        
        for (var j=0; j<classNamesArray.length; j++)
        {            
            if (HJ.Element.isClass(el,classNamesArray[j]))
            {
                return el;
            }
        }
    }
}

/*
* Get all child elements with the given tag name.  A root element must be specified.
*/
HJ.Element.getElementsByTagName = function(tagName, rootElement)
{
    var rootEl = HJ.el(rootElement);
    if (!rootEl) rootEl = document;
	return rootEl.getElementsByTagName(tagName);
}

/*
* Get all child elements with the given tag and class name.  A root element must be specified.
*/
HJ.Element.getElementsByTagAndClassName = function(tagName, className, rootElement)
{
    var rootEl = (rootElement ? HJ.el(rootElement) : document);
    var tagMatches = HJ.Element.getElementsByTagName(tagName, rootEl);
    return HJ.Element.getElementsByClassName(className, tagMatches);
}

HJ.Element.getElementsByClassAndTagName = function(className, tagName, rootElement)
{
    return HJ.Element.getElementsByTagAndClassName(tagName, className, rootElement);
}
/*
* Get all child elements.  This is all child nodes, not just the next level children.
*/
HJ.Element.getAllElements = function(rootElement)
{
    var rootEl = HJ.el(rootElement);
    return (rootEl.getElementsByTagName('*') || rootEl.all);
}

/*
* Get the next parent up with the specified tag name
*/
HJ.Element.getParentByTagName = function(tagName, element)
{
    var el = HJ.el(element);
    
    if (!el) 
    {
        alert('Error No Element - tag name:' + tagName + ' element: ' + el);
        return null;
    }
    
    var parentEl = el.parentNode;
    
    while (parentEl)
    {
        if (parentEl.tagName && parentEl.tagName.toLowerCase() == tagName.toLowerCase())
        {
            return parentEl;
        }
        parentEl = parentEl.parentNode;
    }
    return null;
}

HJ.Element.isParentOf = function(element, parentElement)
{
    var el = HJ.el(element);
    var parentToCheck = HJ.el(parentElement);

    var parentEl = el.parentNode;
    
    while (parentEl)
    {
        if (parentEl == parentToCheck) return true;
        parentEl = parentEl.parentNode;
    }
    return false;
}
/*
* Get the next parent up with the specified class name, if array of classNames, get first
*/
HJ.Element.getParentByClassName = function(element, classNames)
{
    var el = HJ.el(element);

    var parentEl = el.parentNode;
    var classNamesArray = (typeof classNames == 'string' ? classNames.split(' ') : classNames);
    
    while (parentEl)
    {
        if (!parentEl.tagName) break;
        if (parentEl.tagName.toLowerCase() == 'body') break;
        
        for (var i=0; i<classNamesArray.length; i++)
        {            
            if (HJ.Element.isClass(parentEl,classNamesArray[i]))
            {
                return parentEl;
            }
        }
        
        parentEl = parentEl.parentNode;
    }
    return null;
}

HJ.Element.getParentByTagAndClassName = function(element, tagName, className)
{
    var el = HJ.el(element);
    el = HJ.Element.getParentByTagName(tagName, el);
    
    while (el)
    {
        if (HJ.Element.isClass(el, className))
        {
            return el;
        }
        el = HJ.Element.getParentByTagName(tagName, el);
    }
    
    return null;
}

/*
* Given an element, get the parent's next child element of the same tagName.  For
* example, you can get the next row in a table, or next cell in a table row.
* Returns null if none can be found.
*/
HJ.Element.getNextSiblingByTagName = function(element, tagName)
{
    theElement = HJ.el(element);
    
    var parentEl = theElement.parentNode;
    
    for (var i=0; i < parentEl.childNodes.length; i++)
    {
        if (parentEl.childNodes[i] == theElement)
        {
            // found the current element in the list of child nodes
            for (i++; i<parentEl.childNodes.length; i++)
            {
                if (!parentEl.childNodes[i].tagName) { continue; } // skip text nodes
                
                if (parentEl.childNodes[i].tagName.toLowerCase() == tagName.toLowerCase())
                {
                    return parentEl.childNodes[i];
                }
            }
        }
    }
    return null;
    
    // Could implement this using while (node.nextSibling) instead
}

HJ.Element.getNextSiblingByTagAndClassName = function(element, tagName, className)
{
    theElement = HJ.el(element);
    
    var next = element.nextSibling;
    while (next)
    {
        if (next.tagName && next.tagName.toLowerCase() == tagName.toLowerCase() &&
            HJ.Element.isClass(next, className))
        {
            return next;
        }
        next = next.nextSibling;
    }
    return null;
}

HJ.Element.getPreviousSiblingByTagAndClassName = function(element, tagName, className)
{
    theElement = HJ.el(element);
    
    var prev = element.previousSibling;
    while (prev)
    {
        if (prev.tagName && prev.tagName.toLowerCase() == tagName.toLowerCase() &&
            HJ.Element.isClass(prev, className))
        {
            return prev;
        }
        prev = prev.previousSibling;
    }
    return null;
}

/*
* Insert a new node in the document relative to a given element.  The node
* is created from a string.
*/
HJ.Element.insertBeforeBegin = function(element, contentStr)
{
    var where = 'beforeBegin';
    element = HJ.el(element);
    if (element.insertAdjacentHTML)
    {
        element.insertAdjacentHTML(where, contentStr);
    }
    else
    {
        var range = element.ownerDocument.createRange();
        range.setStartBefore(element);
        var newEl = range.createContextualFragment(contentStr);
        element.parentNode.insertBefore(newEl, element);
    }
}
HJ.Element.insertAfterBegin = function(element, contentStr)
{
    var where = 'afterBegin';
    element = HJ.el(element);
    if (element.insertAdjacentHTML)
    {
      element.insertAdjacentHTML(where, contentStr);
    }
    else
    {
        alert('InsertAfterBegin Not Implemented');
    }
}
HJ.Element.insertBeforeEnd = function(element, contentStr)
{
    var where = 'beforeEnd';
    
    element = HJ.el(element);
    if (element.insertAdjacentHTML)
    {
        element.insertAdjacentHTML(where, contentStr);
    }
    else
    {
        alert('InsertBeforeEnd Not Implemented');
    }
}
HJ.Element.insertAfterEnd = function(element, contentStr)
{
    var where = 'afterEnd';
    element = HJ.el(element);
    if (element.insertAdjacentHTML)
    {
      element.insertAdjacentHTML(where, contentStr);
    }
    else
    {
        var range = element.ownerDocument.createRange();
        range.setStartAfter(element);
        var newEl = range.createContextualFragment(contentStr);
        element.parentNode.insertBefore(newEl, element.nextSibling);
    }
}

/*
* Add an additional class to the element.  It will be appended onto the original list
* of classNames separated by a space. 
*/
HJ.Element.addClass = function(the_element, newClassName)
{
    if (HJ.Element.isClass(the_element, newClassName)) { return; }
    the_element = HJ.el(the_element);
    the_element.className+=" " + newClassName;
}

/*
* Remove the last class from the list of class names or remove the specifed class.
*/
HJ.Element.removeClass = function(the_element, removeClass)
{
    the_element = HJ.el(the_element);
    
    var names = HJ.Element.getClassNames(the_element);
    
    var newClassName = '';
        
    if (removeClass)
    {
        if (!HJ.Element.isClass(the_element, removeClass)) { return; }
        // Remove specified
        for (var i=0; i < names.length; i++)
        {
            if (names[i] != removeClass)
            {
                if (newClassName.length > 0) { newClassName += ' '; }
                newClassName += names[i];
            }
        }
    }
    else
    {
        // Remove last
        if (names.length < 2)
        {
            the_element.className = '';
            return;
        }

        for (var i=0; i < names.length-1; i++)
        {
            newClassName += names[i];
            if (i < names.length-2) { newClassName += ' '; }
        }
    }
    
    the_element.className = newClassName;
}

HJ.Element.toggleClass = function(element, className)
{
    var el = HJ.el(element);
    if (HJ.Element.isClass(el, className)) { HJ.Element.removeClass( el, className);}
    else { HJ.Element.addClass( el, className);  }
}

HJ.Element.toggleHtml = function(element, html1, html2)
{
	var el = HJ.el(element);
	el.innerHTML = (el.innerHTML == html1 ? html2 : html1);
}

/*
* Remove this class from all descendant elements
*/
HJ.Element.removeClassDescendants = function(root, removeClass)
{
    var els = HJ.Element.getElementsByClassName(removeClass, root);
    
    for (var i=0; i<els.length; i++)
    {
        HJ.Element.removeClass(els[i],removeClass);
    }
}

HJ.Element.getStyle = function(elementOrId)
{
    var el = HJ.el(elementOrId);
    var style;
    
    if (document.getElementById || document.all) { return el.style; }
    else if(document.layers) { return el; }
    return null;
}

/*
 * Show an element.
 */
HJ.Element.show = function(elementOrId)
{
    var el = HJ.el(elementOrId);
    var style;
    
    if (document.getElementById || document.all)
    {
        style=el.style;
        if (style) style.visibility = "visible";
    }
    else if(document.layers)
    {
        style = el;
        if (style) el.visibility = "show";
    }
    
    HJ.Element.resetShadow(el);
    HJ.Private.fixIEOpacityBug(el);
//    HJ.Event.fire(el, 'visibilityChange', null);    
//    HJ.Event.fire(el, 'show', null);    
}

/*
 * Show an element.
 */
HJ.Element.hide = function(elementOrId)
{
    var el = HJ.el(elementOrId);
    if (!el) { return; }
    
    var style;

    if (document.getElementById || document.all)
    {
        style=el.style;
        if (style) style.visibility = "hidden";
    }
    else if(document.layers)
    {
        style = el
        if (style) style.visibility = "hide";
    }
    
    HJ.Private.fixIEOpacityBug(el);
    HJ.Element.resetShadow(el);

/*
    var el = HJ.el(elementOrId);
    if (!el) { return; }
    
    var style;

    if (document.getElementById || document.all)
    {
        style=el.style;
        if (style) {
        	if (style.visibility == 'hidden') { return; }
        	style.visibility = "hidden";
        }
    }
    else if(document.layers)
    {
        style = el
        if (style) { 
        	if (style.visibility == 'hide') { return; }
        	style.visibility = "hide";
    	}
    }
    HJ.Private.fixIEOpacityBug(el);
*/    
//    HJ.Event.fire(el, 'visibilityChange', null);    
//    HJ.Event.fire(el, 'hide', null);    
   
    return false;
}

/*
 * Expand an element.
 */
HJ.Element.expand = function(elementOrId)
{
	var el = HJ.el(elementOrId);
	if (!HJ.Element.isCollapsed(el)) { return; }
	HJ.Element.toggleVisibilityCollapse(el, true);
}

/**
*	Sets the default display - useful when an element starts off as "none";
*/
HJ.Element.setDisplay = function(el)
{
	var el = HJ.el(el);
	if (!el.tagName) { return; }
	
	var style = HJ.Element.getStyle(el);
	if (el.tagName.toLowerCase() == 'div') {
		style.display='block';
	} else if (el.tagName.toLowerCase() == 'span') {
		style.display='inline';
	} else if (el.tagName.toLowerCase() == 'tr') {
		style.display= (HJ.Browser.isFF ? 'table-row' : 'block');
	} else if (el.tagName.toLowerCase() == 'table') {
		style.display=(HJ.Browser.isFF ?  'table' : 'block');
	} else {
		style.display='block';
	} 
	// TODO - many more...
}

/*
 * Collapse an element.
 */
HJ.Element.collapse = function(elementOrId)
{
	var el = HJ.el(elementOrId);
    if (HJ.Element.isCollapsed(el)) { return; }
	
	HJ.Element.toggleVisibilityCollapse(el, true);
}

/*
* If the element is hidden, make it visible.  If it is visible, make it hidden.
* Calling this method does not collapse element.  That is, other elements following
* this one will retain their position
*/
HJ.Element.toggleVisibility = function(elementOrId)
{
    var el = HJ.el(elementOrId);
    
    if (!el) 
    { 
        HJ.addMessage('Element not found ' + elementOrId);
        HJ.showMessages();
    }
    
    var vis;
    var style;
    
    if (document.getElementById || document.all)
    {
        style = el.style;
        if (style) {
            vis = style.visibility;
            style.visibility = (vis=="hidden" ? "visible" : "hidden");
        }
    }
    else if(document.layers)
    {
        style = el;
        if (el) {
            vis = el.visibility;
            el.visibility = (vis=="hide" ? "show" : "hide");
        }
    }
    
    HJ.Private.fixIEOpacityBug(el);
    HJ.Element.resetShadow(el);   
    return false;
}

HJ.Element.isCollapsed = function(elementOrId)
{
    var el = HJ.el(elementOrId);
    var style = HJ.Element.getStyle(el);

    return ((el.offsetHeight==0&&el.offsetWidth==0) || (style && style.display=='none'));
}

HJ.Element.isScrolledVertical = function(elementOrId)
{
	var el = HJ.el(elementOrId);
	return (el.clientHeight > 0 && el.scrollHeight > el.clientHeight);
}

HJ.Element.isScrolledHorizontal = function(elementOrId)
{
	var el = HJ.el(elementOrId);
	return (el.clientWidth && el.scrollWidth > el.clientWidth);
}

HJ.Element.isScrolled = function(elementOrId)
{
	return HJ.Element.isScrolledVertical(elementOrId) || HJ.Element.isScrolledVertical(elementOrId);
}


// Get the first parent of the element that is scrolled
HJ.Element.getFirstScrolledVerticalParent = function(elementOrId)
{
	var el = HJ.el(elementOrId);
	while (el.offsetParent)
	{
		if (HJ.Element.isScrolledVertical(el.offsetParent))
		{
			return el.offsetParent;			
		}
		el = el.offsetParent;
	}
	return null;
}

HJ.Element.getOffsetTopFromParent = function(el, toOffsetParent )
{
	var offsetTop = 0;
	
	while (el.offsetParent)
	{
		offsetTop += el.offsetTop;
		
		el = el.offsetParent;
		
		if (el == toOffsetParent) break;
	}
	return offsetTop;	
}

// This method is intended to return true if ANY part of the element is visible
HJ.Element.isVisible = function(elementOrId)
{
	if (HJ.Element.isCollapsed(elementOrId)) return false;
	
	// TODO
	return true;
}

// This method checks to see if an elements height is cropped due to scrolling
// or visibility settings.
// TODO: Only the first scrolling parent is checked.
// TODO: Also check if width is cropped
HJ.Element.isCropped = function(elementOrId)
{
	if (HJ.Element.isCollapsed(elementOrId)) return true;
	
	var el = HJ.el(elementOrId);
	
	var offsetParent = HJ.Element.getFirstScrolledVerticalParent(el);
	var elOffsetTop = HJ.Element.getOffsetTopFromParent(el, offsetParent);
	
	if (el.offsetWidth == 0) { return true; }
	
	if (offsetParent && offsetParent.scrollTop != undefined)
	{
		if (offsetParent.clientHeight == 0) { return true; }
		// The parent is scrolled
		if (elOffsetTop < offsetParent.scrollTop)
		{
			return true;  // top is truncated, bottom may still be visible
		}
		
		if ((elOffsetTop + el.offsetHeight) > (offsetParent.scrollTop + offsetParent.clientHeight))
		{
			return true; // bottom is truncated, top may still be visible
		}
		
	}
	
	return false;
}

/*
* If the element is hidden, make it visible.  If it is visible, make it hidden.
* Calling this method will expand and collapse the element.  That is, other elements following
* this one will adjust their position when the element is expanded and collapsed.
*/

HJ.Element.toggleVisibilityCollapseSmooth = function(elementOrId)
{
    var el = HJ.el(elementOrId);
    var style = HJ.Element.getStyle(el);

    if (!style) {  // FF
    el.style = {}
    style = el.style;
    //  HJ.Element.toggleVisibilityCollapse(el);
    //  return;
    }

    if (style.display !='none')
    {
        // SHRINK IT
        HJ.Event.fire(el, 'beginCollapse', null);       
        var ht =el.offsetHeight;
        // Is there a height property?
//      if (style.height && style.height != '') {
//          var styleHt = style.height;  
//          ht = HJ.Util.getInteger(styleHt);
//      }

        if (el.origHeight == undefined) {
            el.origHeight = ht;
        }
                    
        style.height = '' + ht + 'px';
        style.overflow='hidden';
        shrink(el, ht);
    }
    else
    {
        // UNSHRINK IT

        HJ.Event.fire(el, 'beginExpand', null);     
        
        // Set the original height so it knows when to stop.
        // Is there an original height
/*      
        var styleHt = style.height;
        var ht = HJ.Util.getInteger(styleHt);
    
        if (el.origHeight == undefined) {
            // It started off collapsed
            el.origHeight = ht;
            style.height = "1px";
            ht=1;
        }
*/      
        if (el.origOverflow == undefined)
        {
            el.origOverflow = style.overflow;
        }
        
        if (el.origHeight == undefined)
        {
            // It started off collapsed, finding the ending stop height is tricky
            var ht = 50;
            
            // Is there a height property?
            
            if (style.height && style.height != '') {
                var styleHt = style.height;  
                ht = HJ.Util.getInteger(styleHt);
            }
/*          
            else
            {
            style.display='scroll';
                ht = el.scrollHeight;
            }
*/
//          alert('The height should be ' + ht);
            el.origHeight = ht;
        }

        style.height="1px";
        style.overflow='hidden';
        HJ.Element.toggleVisibilityCollapse(el, false);
        unshrink(el, 0);
    }
}
var STEP = 10;
var MS_INTERVAL = 5;

function shrink(el, ht)
{
    if (ht > 0)
    {
        el = HJ.el(el);
        var step = el.getAttribute("collapseStep");
        if (!step) { step = STEP; }
        var interval = el.getAttribute("collapseInterval");
        if (!interval) { interval = MS_INTERVAL; }
        ht = ht - step;
        if (ht <= 0) {
            HJ.Element.toggleVisibilityCollapse(el, false);
            return;
        }
        HJ.Element.getStyle(el).height = '' + ht + 'px';
        HJ.Event.fire(el, 'expanding', null);       
        setTimeout( function() { shrink(el, ht); }, interval);
    }
    HJ.Event.fire(el, 'endCollapse', null);     
}

function unshrink(el, ht)
{
    var anchor_name = el.anchorName;
    if (!anchor_name)
        anchor_name = el.getAttribute('anchorName');

    el = HJ.el(el);
    var step = el.getAttribute("collapseStep");
    if (!step) { step = STEP; }
    var interval = el.getAttribute("collapseInterval");
    if (!interval) { interval = MS_INTERVAL; }

    if (ht < el.origHeight+step-1)
    {
        ht = ht + step;
        //alert("" + ht + " " + el.origHeight);
        if (ht >= el.origHeight) {
//          HJ.Element.getStyle(el).height = '' + el.origHeight + 'px';
            if (!HJ.Element.isClass(el, 'fillbottom')) { 
                HJ.Element.getStyle(el).height = 'auto'; //??? maybe only if orig height not specified
            }
//          HJ.Element.getStyle(el).overflow='scroll';
            HJ.Element.getStyle(el).overflow='';
            HJ.Element.scrollTo(anchor_name);
            HJ.Event.fire(el, 'endExpand', null);       
            return;
        }
        HJ.Element.getStyle(el).height = '' + ht + 'px';
        HJ.Event.fire(el, 'shrinking', null);       
        setTimeout( function() { unshrink(el, ht); }, interval);
    }
    HJ.Element.scrollTo(anchor_name);
}

HJ.Element.scrollTo = function (anchor)
{
    // anchor might be either 'name' of <A> tag or some html element (or its id)
    if (HJ.el(anchor))
    {
    	var anchorPosY = HJ.Element.getPosition(HJ.el(anchor)).y
    	// Need to add the scroll offsets
    	var scrollOffsets = HJ.Element.getScrollOffsets(HJ.el(anchor));
    	anchorPosY += scrollOffsets[1];
    
        // Scroll the window, however, if the scrolling is in an element, you want to scroll the
        // element and not the window.
        // TODO: We have support for HJ.win, but not regular scrollable elements
        var win = HJ.win(anchor);
        if (win)
        {
            win.getWinContent().scrollTop = anchorPosY;
            return;
        }
        
        var container = HJ.Element.getParentByClassName(anchor, 'container fillbottom');

        if (container)
        {
            container.scrollTop = anchorPosY;
        }       
        else
        {
            window.scrollTo(0, anchorPosY);
        }
    }
    else if (typeof anchor == 'string')
        window.location = '#' + anchor;
};  

HJ.Element.toggleVisibilityCollapse = function(elementOrId, fireBeginEndEvents)
{
    var el = HJ.el(elementOrId);
    //var anchor_name = el.anchorName;
    //if (!anchor_name)
    //    anchor_name = el.getAttribute('anchorName');
    
    if (!el) 
    { 
        HJ.addMessage('Element not found ' + elementOrId);
        HJ.showMessages();
    }
    
    var vis;
    var style;
    
    if (document.getElementById || document.all)
    {
        style = el.style;
        if (style) {
            if (style.visibility=="hidden" || HJ.Element.isCollapsed(el))
            {
                if (!style.origDisplay) style.origDisplay = 'none'; // started off hidden
                
                if (fireBeginEndEvents != false) { HJ.Event.fire(el, 'beginExpand', null); }
                
                style.visibility = "visible";
                if (style.origDisplay=='')
                {
                	style.display = '';
                }
                else if (style.origDisplay =='none')
               	{
               		HJ.Element.setDisplay(el);
               	}
               	else
               	{
               		style.display = style.origDisplay;
               	}
                //HJ.Element.scrollTo(anchor_name);
                HJ.Event.fire(el, 'show', null);
                if (fireBeginEndEvents != false) { HJ.Event.fire(el, 'endExpand', null); }
            }
            else
            {
                if (fireBeginEndEvents != false) { HJ.Event.fire(el, 'beginCollapse', null); }
                
                style.visibility = "hidden";
                if (!style.origDisplay)  // started off displayed
                {
                    if (style.display == 'block' || style.display=='inline') style.origDisplay = style.display;
                    else style.origDisplay = '';
                }
                style.display="none";
                HJ.Event.fire(el, 'hide', null);
                if (fireBeginEndEvents != false) { HJ.Event.fire(el, 'endCollapse', null); }
            }            
        }
    }
    else if(document.layers)
    {
        style = el;
        if (style) {
            if (style.visibility=="hide" || style.display=="none")
            {
                if (fireBeginEndEvents != false) { HJ.Event.fire(el, 'beginExpand', null); }
                style.visibility = "show";
                style.display = (style.origDisplay ? style.origDisplay: "block");
                //HJ.Element.scrollTo(anchor_name);
                HJ.Event.fire(el, 'show', null);
                if (fireBeginEndEvents != false) { HJ.Event.fire(el, 'endExpand', null); }
            }
            else
            {
                if (fireBeginEndEvents != false) { HJ.Event.fire(el, 'beginCollapse', null); }
                style.visibility = "hide";
                style.origDisplay=style.display;
                style.display="none";
                HJ.Event.fire(el, 'hide', null);
                if (fireBeginEndEvents != false) { HJ.Event.fire(el, 'endCollapse', null); }
            }            
        }
    }
    
    HJ.Element.resetShadow(el);   
    HJ.Private.fixIEOpacityBug(el);
    HJ.Event.fire(el, 'visibilityChange', null);
   
    return false;
}

// This is a workaround for IE.  Popup divs do not cover up all input fields so we create an 'iframe',
// which covers up the input.  Add the class "opaque", to automatically create the shim.
HJ.Private.fixIEOpacityBug = function(el)
{
    if (!HJ.Browser.isIE) return; 
    
    el = HJ.el(el);
    
    if (!HJ.Element.isClass(el, 'opaque')) { return; } 
    if (!el.id) { return; }
    
    var shimid = '' + el.id + '_iframeshim';
    var shimel = HJ.el(shimid);
    
    if (shimel == null || shimel == undefined) {
        
        if (!el.shimElement)
        {
//            var shimStr = '<iframe id="' + el.id + '_iframeshim"' +
//                ' src="javascript:;" frameborder="0" scrolling="no"' +
//                ' style="z-index:499;position:absolute;left:0px;top:0px;display:none;"></iframe>';
            var shimStr = '<iframe id="' + el.id + '_iframeshim"' +
                ' src="' + HJ.Config.RESOURCE_PATH + 'spacer.gif" frameborder="0" scrolling="no"' +
                ' style="z-index:499;position:absolute;left:0px;top:0px;display:none;"></iframe>';

            HJ.Element.insertBeforeBegin(el,shimStr);
//            HJ.Element.insertAfterBegin(el,shimStr);
            el.shimElement = HJ.el(el.id + '_iframeshim');
        }
    }
    else
    {
        el.shimElement = shimel;  // save the shim element so we know its already there
    }
    
    var shimStyle = null;
    
    if (document.getElementById || document.all)
    {
        shimStyle = el.shimElement.style;
    }
    else if(document.layers)
    {
        shimStyle = el.shimElement;
    }
    
    if (shimStyle)
    {
        // Copy the properties to the shim
        shimStyle.top = el.style.top;
        shimStyle.left = el.style.left;
        shimStyle.width  = (el.offsetWidth + (HJ.Util.getInteger(el.style.borderLeftWidth) || 0) + (HJ.Util.getInteger(el.style.borderRightWidth) || 0)) +  'px';
        shimStyle.height = el.offsetHeight + 'px';

        if (el.style.zIndex && el.style.zIndex > 1)
        {
            shimStyle.zIndex = el.style.zIndex-1;
        }
        else
        {
            shimStyle.zIndex = 1;
            el.style.zIndex = 2;
        }
        
        
        shimStyle.display = el.style.display;
        shimStyle.visibility = el.style.visibility;
        
    }
}

/*
* Required for IE Opacity bug, call this if the page has any elements that must drag
* over an html select element
*/
HJ.Element.makeOpaqueElements = function(root)
{
    var els = HJ.Element.getElementsByClassName('opaque', root);
    for (var i = 0; i < els.length; i++)
    {
        HJ.Private.fixIEOpacityBug(els[i]);
    }
}    


/*
* Get the position relative to the entire document.
*/
HJ.Element.getPosition = function(element)
{
    var el = HJ.el(element);
    var x = 0;
    var y = 0;
    
    var obj = el;
    
    if (obj.offsetParent)
    {
        while (obj.offsetParent)
        {
            x += obj.offsetLeft;
            obj = obj.offsetParent;
        }
    }
    else if (el.x)
    {
        x += el.x;
    }
    
    obj = el;
    
    if (obj.offsetParent)
    {
        while (obj.offsetParent)
        {
            y += obj.offsetTop;
            obj = obj.offsetParent;
        }
    }
    else if (el.y)
        y += el.y;

    var scrollOffsets = HJ.Element.getScrollOffsets(el);
    
    x -= scrollOffsets[0];
    y -= scrollOffsets[1];

    return new HJ.Position(x,y);
}

/*
* Get the position for an element that is relative to its parent absolute element.
*/
HJ.Element.getRelativePosition = function(element)
{
    var el = HJ.el(element);
    var x = 0;
    var y = 0;
    
    var obj = el;
    
    if (obj.style.position == 'absolute')
    {
        return HJ.Element.getPosition(element);
    }
    
    if (obj.offsetParent)
    {
        while (obj.offsetParent && obj.style.position != 'absolute')
        {
            x += obj.offsetLeft;
            obj = obj.offsetParent;
        }
    }
    // TODO - THIS IS ABSOLUTE
    else if (el.x)
    {
        x += el.x;
    }
    
    obj = el;
    
    if (obj.offsetParent)
    {
        while (obj.offsetParent && obj.style.position != 'absolute')
        {
            y += obj.offsetTop;
            obj = obj.offsetParent;
        }
    }
    // TODO - THIS IS ABSOLUTE
    else if (el.y)
        y += el.y;

    var scrollOffsets = HJ.Element.getScrollOffsets(el);
    
    x -= scrollOffsets[0];
    y -= scrollOffsets[1];

    return new HJ.Position(x,y);
}


// TODO - This is the same as position ??

HJ.Element.getOffsets = function(element)
{
    var topOffset = 0;
    var leftOffset = 0;
    
    while (element)
    {
      topOffset += element.offsetTop  || 0;
      leftOffset += element.offsetLeft || 0;
      element = element.offsetParent;
    }
    
    return [leftOffset, topOffset];
}

HJ.Element.getScrollOffsets = function(element)
{
    var el = HJ.el(element);

    var scrollXOffset = 0;
    var scrollYOffset = 0;
    while (el)
    {
        if (el.scrollLeft || el.scrollTop)
        {
            scrollXOffset += el.scrollLeft;
            scrollYOffset += el.scrollTop;
        }
        el = el.parentNode;
  }
  return [ scrollXOffset, scrollYOffset ];
}



HJ.Element.setPosition = function(element, position)
{
    var el = HJ.el(element);

    var style;
    
    if (document.getElementById || document.all)
    {
        style=el.style;
    }
    else if(document.layers)
    {
        style = el;
    }
    
    style.left = position.x + 'px';
    style.top = position.y + 'px';
    
    HJ.Private.fixIEOpacityBug(el);
    HJ.Element.resetShadow(el);
}

/*
* Determines if two elements are overlapping,
* optional padding may be passed in (TODO:NOT IMPLEMENTED YET)
*/
HJ.Element.isOverlapping = function(element1, element2, el1x, el1y, el2x, el2y)
{
    var pos1 = HJ.Element.getPosition(element1);
    var pos2 = HJ.Element.getPosition(element2);

    if (arguments.length < 6) el2y = 0;
    if (arguments.length < 5) el2x = 0;
    if (arguments.length < 4) el1y = 0;
    if (arguments.length < 3) el2x = 0;
    
    var withinX = false;
    if (pos2.x > pos1.x && pos2.x < (pos1.x-element1.offsetWidth)) withinX = true;
    if (pos2.x < pos1.x && (pos2.x+element2.offsetWidth) > pos1.x) withinX = true;
    
    if (pos1.x > pos2.x && pos1.x < (pos2.x-element2.offsetWidth)) withinX = true;
    if (pos1.x < pos2.x && (pos1.x+element1.offsetWidth) > pos2.x) withinX = true;
    
    if (!withinX) return false;

    var withinY = false;
    if (pos2.y > pos1.y && pos2.y < (pos1.y-element1.offsetHeight)) withinY = true;
    if (pos2.y < pos1.y && (pos2.y+element2.offsetHeight) > pos1.y) withinY = true;
    
    if (pos1.y > pos2.y && pos1.y < (pos2.y-element2.offsetHeight)) withinY = true;
    if (pos1.y < pos2.y && (pos1.y+element1.offsetHeight) > pos2.y) withinY = true;
    
    return withinY;
}

HJ.Element.isPositionWithinElement = function(pos, element)
{
    var elPos = HJ.Element.getPosition(element);
    
    //var offsets = HJ.Element.getOffsets(element);
    //SCM TEMP var scrollOffsets = HJ.Element.getScrollOffsets(element);
    
//  return (elPos.x < pos.x && pos.x < (elPos.x+element.offsetWidth) &&
//          elPos.y < pos.y && pos.y < (elPos.y+element.offsetHeight));
    return (elPos.x < pos.x && pos.x < (elPos.x+element.offsetWidth) &&
            elPos.y < pos.y && pos.y < (elPos.y+element.offsetHeight));
}

/*
* Set the opacity of an element
*/
HJ.Element.setOpacity = function (element, opacity)
{
    var el = HJ.el(element);
    
    if (el.filters)
    {
        try {
            el.filters['alpha'].opacity = opacity * 100;
        } catch (e) { }
    } 
    else if (el.style.opacity)
    {
        el.style.opacity = opacity;
    }
}

HJ.Element.registerHandler = function(element, type, func, obj)
{
    HJ.Event.registerHandler(element, type, func, obj);
}

HJ.Element.toggleCollapseAllSiblings = function(element, opts)
{
    var el = HJ.el(element);
    while (el.nextSibling) 
    {
        HJ.Element.toggleVisibilityCollapseSmooth(el.nextSibling);
        el=el.nextSibling;
    }   
}

HJ.Element.toggleCollapseNextSiblings = function(element, howMany, opts)
{
    var num = (howMany || 1);
    var el = HJ.el(element);
    var i=0;
    while (el.nextSibling && i<num) 
    {
        if (el.nextSibling.nodeType != 3) // don't count text nodes
        {
            if (opts && opts.smooth == true )
            {
                HJ.Element.toggleVisibilityCollapseSmooth(el.nextSibling);
            } else {
                HJ.Element.toggleVisibilityCollapse(el.nextSibling);
            }
            i++;
        }
        el=el.nextSibling;
    }   
}

HJ.Element.toggleCollapseNextSibling = function(element, opts)
{
    HJ.Element.toggleCollapseNextSiblings(element, 1, opts);
}

HJ.Element.getLeftAndRightBorderWidth = function(element)
{
    var element = HJ.el(element);
    
    var leftAndRightBorders = element.offsetWidth - element.clientWidth;
    
    var debug = element.id && element.id=='flowSettingsContainer';
    //if (debug) alert('offsetw:'+element.offsetWidth+' scroll:'+element.scrollWidth);
    //if (debug) alert('overflowY:'+element.style.overflowY);
    
    if (!HJ.Browser.isIE)
    {
        if ((element.clientHeight < (element.scrollHeight-15-leftAndRightBorders) && element.style.overflowY != 'hidden')
            || element.style.overflowY == 'scroll')
        {
            leftAndRightBorders -= 15;
        }
    }
    else if (HJ.Browser.isIE)
    {
        if ((element.clientHeight < (element.scrollHeight-16-leftAndRightBorders) && element.style.overflowX != 'hidden')
            || element.style.overflowY == 'scroll')
        {
            leftAndRightBorders -= 16;
        }
    }

    //if (debug) alert('borders:'+leftAndRightBorders);

    return leftAndRightBorders;
}

HJ.Element.getTopAndBottomBorderWidth = function(element)
{
    var element = HJ.el(element);
    
    var topAndBottomBorders = element.offsetHeight - element.clientHeight;

    var debug = element.id && element.id=='FlowLib';
    
    //if (debug) alert('clienth:'+element.clientHeight+' scroll:'+element.scrollHeight);
    //if (debug) alert('of:'+element.style.overflowY);

    // subtract off the horz scroll bars
    if (!HJ.Browser.isIE)
    {
        //  alert('clientw:'+element.clientWidth+' scroolw:'+element.scrollWidth);
        if ((element.clientWidth < (element.scrollWidth-15-topAndBottomBorders) && element.style.overflowX != 'hidden')
            || element.style.overflowX == 'scroll')
        {
            topAndBottomBorders -= 15;
        }
    }
    else if (HJ.Browser.isIE)
    {
        if ((element.clientWidth < (element.scrollWidth-16-topAndBottomBorders) && element.style.overflowX != 'hidden')
            || element.style.overflowX == 'scroll')
        {
            topAndBottomBorders -= 16;
        }
    }
        
    return topAndBottomBorders;
}

HJ.Element.shadowId = 0;
HJ.Element.addShadow = function(el)
{
    el = HJ.el(el);
    
    var shadowId = 'shadow_' + HJ.Element.shadowId++;
    
    var shadowEl = HJ.el(shadowEl);
    
    if (shadowEl == null || shadowEl == undefined) {
        
        if (!el.shadowElement)
        {
            var shadowStr = 
				'<div class="shadow" id="'+ shadowId + '">' + // <!-- DYNAMIC WIDTH, HEIGHT, POS, Z-INDEX -->
				'<div class="corner">' +
				 '<div class="sh o01"></div><div class="sh o02"></div><div class="sh o02"></div><div class="sh o04"></div><div class="sh o04"></div><div style="clear:both"></div>' +
				 '<div class="sh o02"></div><div class="sh o06"></div><div class="sh o08"></div><div class="sh o10"></div><div class="sh o12"></div><div style="clear:both"></div>' +
				 '<div class="sh o02"></div><div class="sh o08"></div><div class="sh o12"></div><div class="sh o20"></div><div class="sh o24"></div><div style="clear:both"></div>' +
				 '<div class="sh o04"></div><div class="sh o10"></div><div class="sh o20"></div><div class="sh o26"></div><div class="sh o32"></div><div style="clear:both"></div>' +
				 '<div class="sh o04"></div><div class="sh o12"></div><div class="sh o24"></div><div class="sh o32"></div><div class="sh o36"></div><div style="clear:both"></div>' +
				'</div>' +
				'<div class="topCenter">' +// <!-- DYNAMIC WIDTH -->
				 '<div class="sh o06"></div>' +
				 '<div class="sh o15"></div>' +
				 '<div class="sh o25"></div>' +
				 '<div class="sh o36"></div>' +
				 '<div class="sh o42"></div>' +
				'</div>' +
				'<div class="corner">' +
				 '<div class="sh o04"></div><div class="sh o04"></div><div class="sh o02"></div><div class="sh o02"></div><div class="sh o01"></div><div style="clear:both"></div>' +
				 '<div class="sh o12"></div><div class="sh o10"></div><div class="sh o08"></div><div class="sh o06"></div><div class="sh o02"></div><div style="clear:both"></div>' +
				 '<div class="sh o24"></div><div class="sh o20"></div><div class="sh o12"></div><div class="sh o08"></div><div class="sh o02"></div><div style="clear:both"></div>' +
				 '<div class="sh o32"></div><div class="sh o26"></div><div class="sh o20"></div><div class="sh o10"></div><div class="sh o04"></div><div style="clear:both"></div>' +
				 '<div class="sh o36"></div><div class="sh o32"></div><div class="sh o24"></div><div class="sh o12"></div><div class="sh o04"></div><div style="clear:both"></div>' +
				'</div>' +
				'<div style="clear:both"></div>' +
				'<div class="mid" style="width:11px;height:1px;">' + // <!--  DYNAMIC WIDTH, HEIGHT -->
				 '<div class="midSide">' +
				  '<div class="sh o06"></div>' +
				  '<div class="sh o15"></div>' +
				  '<div class="sh o25"></div>' +
				  '<div class="sh o36"></div>' +
				  '<div class="sh o42"></div>' +
				 '</div>' +
				 '<div class="midCenter sh o42" style="width:1px;height:2000px;overflow:hidden;float:left"></div>' + // <!-- DYNAMIC WIDTH -->
				 '<div class="midSide">' +
				  '<div class="sh o42"></div>' +
				  '<div class="sh o36"></div>' +
				  '<div class="sh o25"></div>' +
				  '<div class="sh o15"></div>' +
				  '<div class="sh o06"></div>' +
				 '</div>' +
				'</div>' +
				'<div style="clear:both"></div>' +
				'<div class="corner">' +
				 '<div class="sh o04"></div><div class="sh o12"></div><div class="sh o24"></div><div class="sh o32"></div><div class="sh o36"></div><div style="clear:both"></div>' +
				 '<div class="sh o04"></div><div class="sh o10"></div><div class="sh o20"></div><div class="sh o26"></div><div class="sh o32"></div><div style="clear:both"></div>' +
				 '<div class="sh o02"></div><div class="sh o08"></div><div class="sh o12"></div><div class="sh o20"></div><div class="sh o24"></div><div style="clear:both"></div>' +
				 '<div class="sh o02"></div><div class="sh o06"></div><div class="sh o08"></div><div class="sh o10"></div><div class="sh o12"></div><div style="clear:both"></div>' +
				 '<div class="sh o01"></div><div class="sh o02"></div><div class="sh o02"></div><div class="sh o04"></div><div class="sh o04"></div><div style="clear:both"></div>' +
				'</div>' +
				'<div class="bottomCenter">' + // <!-- DYNAMIC WIDTH -->
				 '<div class="sh o42"></div>' +
				 '<div class="sh o36"></div>' +
				 '<div class="sh o25"></div>' +
				 '<div class="sh o15"></div>' +
				 '<div class="sh o06"></div>' +
				'</div>' +
				'<div class="corner">' +
				 '<div class="sh o36"></div><div class="sh o32"></div><div class="sh o24"></div><div class="sh o12"></div><div class="sh o04"></div><div style="clear:both"></div>' +
				 '<div class="sh o32"></div><div class="sh o26"></div><div class="sh o20"></div><div class="sh o10"></div><div class="sh o04"></div><div style="clear:both"></div>' +
				 '<div class="sh o24"></div><div class="sh o20"></div><div class="sh o12"></div><div class="sh o08"></div><div class="sh o02"></div><div style="clear:both"></div>' +
				 '<div class="sh o12"></div><div class="sh o10"></div><div class="sh o08"></div><div class="sh o06"></div><div class="sh o02"></div><div style="clear:both"></div>' +
				 '<div class="sh o04"></div><div class="sh o04"></div><div class="sh o02"></div><div class="sh o02"></div><div class="sh o01"></div><div style="clear:both"></div>' +
				'</div>' +
				'</div>'; // <!--  end shadow -->

            HJ.Element.insertBeforeBegin(el,shadowStr);
            el.shadowElement = HJ.el(shadowId);
        }
    }
    else
    {
        el.shadowElement = shadowEl;  // save the shadow element so we know its already there
    }

    HJ.Element.resetShadow(el);
    
}

HJ.Element.resetShadow = function(el)
{
    el = HJ.el(el);
    if (!el.shadowElement) { return; }

	var shadowEl = el.shadowElement;
	
	if (!shadowEl.topCenterEl)
	{
		shadowEl.topCenterEl = HJ.Element.getFirstElementByTagAndClassName('div','topCenter',shadowEl);
	}
	if (!shadowEl.midEl)
	{
		shadowEl.midEl = HJ.Element.getFirstElementByTagAndClassName('div','mid',shadowEl);
	}
	if (!shadowEl.midCenterEl)
	{
		shadowEl.midCenterEl = HJ.Element.getFirstElementByTagAndClassName('div','midCenter',shadowEl);
	}
	if (!shadowEl.bottomCenterEl)
	{
		shadowEl.bottomCenterEl = HJ.Element.getFirstElementByTagAndClassName('div','bottomCenter',shadowEl);
	}
	var topCenterEl = shadowEl.topCenterEl;
	var midEl = shadowEl.midEl;
	var midCenterEl = shadowEl.midCenterEl;
	var bottomCenterEl = shadowEl.bottomCenterEl;
	
    var shadowStyle = null;
    var topCenterStyle = null;
    var midStyle = null;
    var midCenterStyle = null;
    var bottomCenterStyle = null;
    
    if (document.getElementById || document.all)
    {
        shadowStyle = el.shadowElement.style;
        topCenterStyle = topCenterEl.style;
        midStyle = midEl.style;
        midCenterStyle = midCenterEl.style;
        bottomCenterStyle = bottomCenterEl.style;
    }
    else if(document.layers)
    {
        shadowStyle = el.shadowElement;
        topCenterStyle = topCenterEl;
        midStyle = midEl;
        midCenterStyle = midCenterEl;
        bottomCenterStyle = bottomCenterEl;
    }
    
    if (shadowStyle)
    {
    	var relPos = new HJ.Position((el.offsetLeft || 0), (el.offsetTop  || 0));
    	
        var realTop = relPos.y;
        var realLeft = relPos.x;

        var shadowOffset = 4; // position corners are 5 but make the shadow 1px bigger
        var shadowSizeOffset = 1;
        
        // Copy the properties to the shadow
        shadowStyle.top = (realTop + shadowOffset) + "px";
        shadowStyle.left = (realLeft + shadowOffset) + "px";
        
        var shadowFullWidth = (shadowSizeOffset + el.offsetWidth);// + (HJ.Util.getInteger(el.style.borderLeftWidth) || 0) + (HJ.Util.getInteger(el.style.borderRightWidth) || 0));
        var shadowFullHeight = (shadowSizeOffset + el.offsetHeight);// + (HJ.Util.getInteger(el.style.borderTopWidth) || 0) + (HJ.Util.getInteger(el.style.borderBottomWidth) || 0));
        
        shadowStyle.width  =  shadowFullWidth + 'px';
        shadowStyle.height =  shadowFullHeight + 'px';

		var centerWidth = -10 + shadowFullWidth;
		if (centerWidth < 0) { centerWidth = 0; }
		centerWidth = centerWidth + 'px';
		
		topCenterStyle.width = centerWidth;
		midCenterStyle.width = centerWidth;
		bottomCenterStyle.width = centerWidth;

		midStyle.width = shadowStyle.width;
		
		var midHeight = -10 + shadowFullHeight;
		if (midHeight < 0) { midHeight = 0; }
		midStyle.height =  midHeight + 'px';
        
        if (el.style.zIndex && el.style.zIndex > 1)
        {
            shadowStyle.zIndex = el.style.zIndex-1;
        }
        else
        {
            shadowStyle.zIndex = 1;
            el.style.zIndex = 2;
        }
        
        shadowStyle.display = el.style.display;
        shadowStyle.visibility = el.style.visibility;
        
    }
}

HJ.Element.removeShadow = function(el)
{
    el = HJ.el(el);
    if (!el.shadowElement) { return; }
    
    if (el.shadowElement)
    {
        HJ.Element.hide(el.shadowElement);
        if (el.shadowElement.parentNode)
        {
            el.shadowElement.parentNode.removeChild(el.shadowElement);
        }
        el.shadowElement = null;
    }
}

//---------- HJ.Position ----------

/*
* Position
*/
HJ.Position = function(x, y)
{
    this.x = x; // left coordinate
    this.y = y; // top coordinate
}

//---------- HJ.PublishSubscribe ----------

HJ.PublishSubscribe.Published = new Array();

HJ.PublishSubscribe.getPublished = function(logical_name)
{
    for (var i=0; i<HJ.PublishSubscribe.Published.length; i++)
    {
        var publishedNode = HJ.PublishSubscribe.Published[i];
        if (publishedNode.id == logical_name)
        {
            return publishedNode;
        }
    }
    return null;
}

HJ.PublishSubscribe.subscribe = function(logical_name, observer, action)
{
    var publishedNode = HJ.PublishSubscribe.getPublished(logical_name);
    if (publishedNode != null)
    {
        // Are there any subscriptions?
        var found = false;
        var i = 1;
        for (i=1; i<publishedNode.subscribers.length; i++)
        {
            var subscribedNode = publishedNode.subscribers[i];
            if (subscribedNode.observer == observer)
            {
                subscribedNode.action = action;
                found = true;
                break;
            }
        }
        
        if (! found)
        {
            var aSubNode = new HJ.SubscribedNode('id', observer, action);
            publishedNode.subscribers[i] = aSubNode;
        }
    }
    else
    {
        var aSubNode = new HJ.SubscribedNode('id', observer, action);
        var publishedNode = new HJ.PublishedNode(logical_name);
        publishedNode.subscribers[0] = aSubNode;
        HJ.PublishSubscribe.Published[HJ.PublishSubscribe.Published.length] = publishedNode;
    }
}

HJ.PublishSubscribe.publish = function(logical_name, context)
{
    var publishedNode = HJ.PublishSubscribe.getPublished(logical_name);
    if (publishedNode != null)
    {
        // Are there any subscriptions?
        for (var i=0; i<publishedNode.subscribers.length; i++)
        {
            var subscribedNode = publishedNode.subscribers[i];
            subscribedNode.action(subscribedNode.observer, context);
        }
    }
}

HJ.PublishedNode = function(id)
{
    this.id          = id;
    this.subscribers = new Array();
}

HJ.SubscribedNode = function(id, observer, action)
{
    this.id          = id;
    this.observer    = observer;
    this.action      = action;
}

//---------- HJ.Event (help from Quirksmode.org) ----------

HJ.Event.getTargetElement = function(event)
{
    var e = event || window.event;
    
    var targetElement = e.customTarget || e.target || e.srcElement || e.currentTarget; // e.customTarget (HJ specific)
    
    if (targetElement.nodeType == 3)
    {
        // Safari bug, event on text node instead of element
        targetElement = targetElement.parentNode;
    }
    else if (targetElement.nodeType == 1 && targetElement.tagName.toLowerCase() == 'html')
    {
        targetElement = document;
    }
    
    return targetElement;
}

HJ.Event.getTargetElementByTagName = function(event, tagName)
{
    var el = HJ.Event.getTargetElement(event);
    
    if (el.tagName && el.tagName.toLowerCase() != tagName)
    {
        el = HJ.Element.getParentByTagName(tagName, el);
    }
    return el;
}

HJ.Event.getTargetElementByTagAndClassName = function(event, tagName, className)
{
    var el = HJ.Event.getTargetElement(event);
    
    if (el.tagName && el.tagName.toLowerCase() != tagName && !HJ.Element.isClass(el,className))
    {
        el = HJ.Element.getParentByTagAndClassName(el, tagName, className);
    }
    return el;
}

HJ.Event.getTargetElementByClassName = function(event, className)
{
    var el = HJ.Event.getTargetElement(event);
    
    if (!HJ.Element.isClass(el,className))
    {
        el = HJ.Element.getParentByClassName(el,className);
    }
    return el;
}

HJ.Event.registerHandler = function(element, type, func, obj)
{
    var el = HJ.el(element);
    
    var theFunc;
    if (typeof func == 'string')
    {
        theFunc = function(event) {
            window.eval(func);
            return false; // prevent onclick handlers from follow hrefs on a tags
            }
    }
    else
    {
        if (obj) { theFunc = function(event) { func.apply(obj, [event]); } }
        else
        theFunc = func;
    }
    
    if (el.addEventListener) {
      el.addEventListener(type, theFunc, false);  // bubble up 
    } else if (element.attachEvent) {
      el.attachEvent('on' + type, theFunc); // IE
    }
}
/*
* Custom event handlers
*/
HJ.Event.registerCustomHandler = function(element, type, func, obj)
{
    var el = HJ.el(element);
    
    var theFunc;
    if (typeof func == 'string')
    {
        theFunc = function(event) {
            window.eval(func);
            }
    }
    else
    {
        if (obj) { theFunc = function(event) { func.apply(obj, [event]); } }
        else
        theFunc = func;
    }
    
    if (!el.customEvents) el.customEvents = new Array();
    
    el.customEvents[el.customEvents.length] = [ type, theFunc ];
}

HJ.Event.fire = function(element, type, event) 
{
    var el = HJ.el(element);
    var e = event || window.event || {}; // empty event object if none
    
    if (!el.customEvents || el.customEvents.length == 0) return; // no listeners
    
    for (var i=0; i<el.customEvents.length; i++)
    {
        if (el.customEvents[i][0] == type)
        {
            if (e) { e.customTarget = element; }
            el.customEvents[i][1].apply(null, [e]);     
        }
    }
}

/*
* Get the mouse position, relative to the document (not the display window)
*/
HJ.Event.getMousePosition = function(event)
{
    var e = event || window.event;
    
    var posx = 0;
    var posy = 0;
    
    if (e.pageX || e.pageY)
    {
        posx = e.pageX;
        posy = e.pageY;
    }
    else if (e.clientX || e.clientY)
    {
        var scrollYOffset=0;    
        var scrollXOffset=0;    
        if (window.pageYOffset)
        {
              scrollYOffset = window.pageYOffset
              scrollXOffset = window.pageXOffset
        }
        else if (document.documentElement && document.documentElement.scrollTop) // IE Strict
        {
            scrollYOffset = document.documentElement.scrollTop
            scrollXOffset = document.documentElement.scrollLeft
        }
        else if (document.body) // IE Quirks
        {
              scrollYOffset = document.body.scrollTop
              scrollXOffset = document.body.scrollLeft
        }
        posx = e.clientX + scrollXOffset;
        posy = e.clientY + scrollYOffset;
    }
    
    return new HJ.Position(posx, posy);
}

/*
* Determines if the keypress, or keyup event was for a key that modifies text 
*/
HJ.Event.isTextKey = function(event)
{
    var e = event || window.event;

    var keyCode = e.keyCode || e.which;
    
    switch (keyCode)
    {
        //case 8:  // Backspace
        case 9:  // Tab
        case 13: // Enter
        case 16: // Shift key
        case 17: // Control key
        case 18: // Alt key
        case 27: // Escape Key
        case 37: // Left arrow
        case 38: // Up arrow
        case 39: // Right arrow
        case 40: // Down arrow
            return false;
    }
    return true;
}

/*
* Get the character of the key that was pressed
*/
HJ.Event.getKeyCharacter = function(event)
{
    var e = event || window.event;

    var keyCode = event.keyCode || e.which;
    //alert("keycode:" + keyCode + ' value:' + String.fromCharCode(keyCode));
    return String.fromCharCode(keyCode);
}

/*
* Determine if the event is a right click event
*/
HJ.Event.isRightClick = function(event)
{
    var e = event || window.event;
    
    return (e.which ? (e.which == 3) : (e.button == 2));
}

/*
* Stop event propagation
*/
HJ.Event.stop = function(event)
{
    var e = event || window.event;
    e.cancelBubble = true;
    if (e.stopPropagation)
    {
        e.stopPropagation();        
    }    
}
//---------- HJ.Form : Form validation ---------- 

// Extend all input elements

/*
* Give the form HJ behaviors and properties
*/
HJ.Form.extend = function(form)
{
    if(HJ.Form.isExtended(form))        // already been extended ?
        return;

    form.customValidate = null;
    
    form.async = false; // default to sumbitting the page
    form.submitState = false;
    
    form.submitAsyncSuccessFunc = HJ.Constants.Functions.EMPTY;
    form.submitAsyncFailureFunc = function() {
        if (form.message && form.message.length > 0)
        {
            alert(form.message);
        }
        else
        {
            alert("The system could not process your request.");
        }
    }
    
    form.validate = function() {
        
        if (!HJ.Form.baseValidate(form)) return false;

        if (form.customValidate) { if (!form.customValidate.apply()) return false; }
        
        return true;
        };
        
    /*
    * Submit the form data asynchronously without submitting the page.
    */
    form.submitAsync = function() {

            var serverCall = new HJ.ServerCall()
            
            var callback = function(serverCall) {
                form.enable();
                
                if (HJ.Util.trimWhiteSpace(serverCall.getResponseText()).length > 1)
                    window.eval(HJ.Util.trimWhiteSpace(serverCall.getResponseText()));
/*
                var respText = HJ.Util.trimWhiteSpace(serverCall.getResponseText());
                
                HJ.ServerCall.SecureEval(respText);
*/                
                if (form.hasErrors)
                {
                    form.submitAsyncFailureFunc.apply(null, [form]);
                    HJ.Event.fire(form, "error", null);
                }
                else
                {
                    form.submitAsyncSuccessFunc.apply(null, [form]);
                    form.origPostData = form.getPostData(); // Reset the post data state
                    HJ.Event.fire(form, "success", null);
                }
                
                form.submitState = false;
            }
            
            serverCall.sendPost(form.action, form.getPostData(), callback)
        };
        
    /**
    * return true if the form was submitted, false if there was a validation error.
    */
    form.submitSave = function() {
            if (form.submitState == true) {
                alert('The form has already been submitted.  Please wait.');
                return;
            }

            if (!form.validate()) return false;        
            form.submitState=true;
            form.disable();

            if (form.async) form.submitAsync.apply();
            else form.submit();
            return true;
        };
    
    form.submitDeleteConfirm = function()
    {
        return confirm('Are you sure?\n\nSelect OK to proceed with delete.\n');
    }
    
    /**
    * Call the form with mode=delete, delete=true
    */
    form.submitDelete = function() {
        
            if (form.submitDeleteConfirm)
            {
                if (!form.submitDeleteConfirm())
                {
                    return false;
                }
            }
            
            if (form.submitState == true) {
                alert('The form has already been submitted.  Please wait.');
                return;
            }
            form.submitState=true;
            
            if (!form['mode'])
            {
                var hidden = document.createElement('input');
                hidden.type='hidden';
                hidden.name='mode';
                hidden.value='delete';
                form.appendChild(hidden);
            }
            else
            {
                form['mode'].value='delete';
            }
            if (!form['delete'])
            {
                var hidden = document.createElement('input');
                hidden.type='hidden';
                hidden.name='delete';
                hidden.value='true'
                form.appendChild(hidden);
            }
            else
            {
                form['delete'].value='true';
            }
            
            form.disable();

            if (form.async) form.submitAsync.apply();
            else form.submit();
            return true;
        };
        
    form.isSubmitDelete = function()
    {
        if (form['mode'] && form['mode'].value == 'delete') return true;
        if (form['delete'] && form['delete'].value == 'true') return true;
        return false;
    }
    
    form.submitCancel = function() {
            if (!HJ.win(form)) window.history.go(-1);
            else return true;
        };
        
    form.disable = function ()
    {
        // TODO - only disable enabled forms ?!? (calling disable twice currently removes onclick handlers)

        HJ.Element.addClass(form, "disabled");
        
        var els = form.elements;
        for (var i=0; i<els.length; i++)
        {
            var el = els[i];
            if (el.disabled == false)
            {
                el.disabled = true;
                el.disabledSet = true;
            }
        }
        
        // This is only a partial solution, it does not get "registered" events
        // maybe a modal mask would be better and faster.
        var allFormElements = HJ.Element.getAllElements(form);
        for (var i=0; i<allFormElements.length; i++)
        {
            var el = allFormElements[i];
            if (el.onclick)
            {
                el.onclickDisable = el.onclick;
                el.onclick = 'return false;';
            }
            if (el.onmouseover)
            {
                el.onmouseoverDisable = el.onmouseover;
                el.onmouseover = '';
            }
            if (el.onmouseout)
            {
                el.onmouseoutDisable = el.onmouseout;
                el.onmouseout = '';
            }
        }
    }
        
    form.enable = function ()
    {
        HJ.Element.removeClass(form, "disabled");
        // TODO - only reenable disabled fields !!!
        
        var els = form.elements;
        for (var i=0; i<els.length; i++)
        {
            var el = els[i];
            if (el.disabledSet == true)
            {
                el.disabled = false;
                el.disabledSet = false;
            }
        }
        
        var allFormElements = HJ.Element.getAllElements(form);
        for (var i=0; i<allFormElements.length; i++)
        {
            var el = allFormElements[i];
            if (el.onclickDisable)
            {
                el.onclick = el.onclickDisable;
                el.onclickDisable = '';
            }
            if (el.onmouseoverDisable)
            {
                el.onmouseover = el.onmouseoverDisable;
                el.onmouseoverDisable = '';
            }
            if (el.onmouseoutDisable)
            {
                el.onmouseout = el.onmouseoutDisable;
                el.onmouseoutDisable = '';
            }
        }
    }

    // NOTE: do not attempt to implement form.isDisabled() because IE already defines an isDisabled property on the form element

    form.isEnabled = function()
    {
        return(!HJ.Element.isClass(form, "disabled"));
    }

    form.getPostData = function() {
    	return HJ.Form.getPostData(form.elements);
    }

    // Add listeners to all form elements

    form.elementListener = function(event) // invoked when any form element changes
    {
        var postData = form.getPostData();
        if (postData == form.lastEventPostData) { return; }
        event = event || {};
        event.customTarget = this;
        if (postData != form.origPostData)
        {
            HJ.Event.fire(form, "change", event);
        }
        else
        {
            HJ.Event.fire(form, "restore", event);
        }
        form.lastEventPostData = postData;
    }
    
    form.elementChangeListener = function(event)
    {
        // If an element changed, the form must have changed
        form.lastEventPostData = null;
        HJ.Event.fire(form, "change", event);
    }
    
    form.elementRestoreListener = function(event)
    {
        // element restore, check if form restored or changed
        form.elementListener(event);
    }

    form.prepareCustomEvents = function()
    {
        // inputs,selects,textareas fire element events
        form.origPostData = form.getPostData();
        form.isChanged = false;
        
        var els = form.elements;
        var anyElSet = false; // Don't re-register handlers that have already been registered
                              // This allows the method to be "re-called"
        
        for (var i=0; i<els.length; i++)
        {
            var alreadySet = false;
            var el = els[i];
            if (el.type && (el.type=='text' ||
                    el.type.toLowerCase() == 'textarea' ||
                    (el.tagName && el.tagName.toLowerCase() == 'textarea')) )
            {
                // text element onchange only fired upon "blur"
                if (el.origValue != undefined) {
                    el.origValue = el.value;
                    alreadySet = true;
                    anyElSet = true;
                }
                else
                {   
                    HJ.Event.registerHandler(el, "keyup", HJ.Form.textinputListener(el));
                }
            }
            else if (el.type && (el.type=='radio' || el.type=='checkbox'))
            {
                if (el.origCheckStatus != undefined)
                {
                    el.origCheckStatus = el.checked;
                    alreadySet = true;
                    anyElSet = true;
                }
                else
                {
                    HJ.Event.registerHandler(el, "click", HJ.Form.checkboxListener(el));
                    HJ.Event.registerHandler(el, "focus", HJ.Form.checkboxListener(el));
                    HJ.Event.registerHandler(el, "blur", HJ.Form.checkboxListener(el));
                }
            }
            else if (el.tagName && el.tagName.toLowerCase() == 'select')
            {
                if (el.origSelectedIdx != undefined) {
                    el.origSelectedIdx = el.selectedIndex;
                    alreadySet = true;
                    anyElSet = true;
                }
                else
                {
                    HJ.Event.registerHandler(el, "change", HJ.Form.selectListener(el));
                }
            }
            else if (el.type && el.type=='reset')
            {
                HJ.Event.registerHandler(form, "reset", form.elementListener, form);
            }
            else if (el.type && (el.type=='image' || el.type=='submit'))
            {
                // none
            }
            else
            {
                HJ.Event.registerHandler(el, "change", form.elementListener, form);
            }

            // Listen to change/restore events on the elements          
            if (!alreadySet)
            {
                HJ.Event.registerCustomHandler(el, "change", form.elementChangeListener, form);
                HJ.Event.registerCustomHandler(el, "restore", form.elementRestoreListener, form);
            }
        }

        if (anyElSet) { return; }
        
        HJ.Event.registerCustomHandler(form, "success", function(event) {
                    form.isChanged = false;
                    } );
        HJ.Event.registerCustomHandler(form, "change", function(event) {
                    form.isChanged = true;
                    } );
        HJ.Event.registerCustomHandler(form, "restore", function(event) {
                    form.isChanged = false;
                    } );
    }
    
    form.isModified = function()
    {
        return (form.isChanged == true);
        //return(form.getPostData() != form.origPostData); // too slow
    };

    form.prepareCustomEvents();
    
    HJ.Form.prepareDependencies(form);
   
}

// Get post data for a list of form elements

HJ.Form.getPostData = function(formElements)
{
    var data='';
    var els = formElements;
    
    var s=[]; //IE slow string concatenation
    var j=0;

    for (var i=0; i<els.length; i++)
    {
        var element = els[i];
        if (element.type)
        {
            if (element.type.toLowerCase() == 'radio' && !element.checked) { continue; }
            else if (element.type.toLowerCase() == 'checkbox' && (!element.checked || element.value == '')) { continue; }
        }
        s[j++]=element.name;
        s[j++]='=';
        s[j++]=escape(element.value);
        
        if (i < els.length-1) s[j++]='&';
    }

    return s.join('');        
}

// Form input element listeners fire custom "change" and "restore" events.
// The "change" event is different than the "onchange" event.

HJ.Form.textinputListener = function(input)
{
    input.origValue = input.value;
    return function(event)
    {
        // invoked when any text input or textarea changes
        var input = HJ.Event.getTargetElement(event);
        var value = input.value;
        if (value == input.lastValue) { return; }
        input.lastValue = value;
        if (value != input.origValue)
        {
            HJ.Event.fire(input, "change", event);
        }
        else
        {
            HJ.Event.fire(input, "restore", event);
        }
    }
}

HJ.Form.selectListener = function(select)
{
    select.origSelectedIdx = select.selectedIndex;
    return function(event)
    {   
        var select = HJ.Event.getTargetElement(event);
        var selectedIdx = select.selectedIndex;
        if (selectedIdx == select.lastSelectedIdx) { return; }
        select.lastSelectedIdx = selectedIdx;
        if (selectedIdx != select.origSelectedIdx)
        {
            HJ.Event.fire(select, "change", event);
        }
        else
        {
            HJ.Event.fire(select, "restore", event);
        }
    }
}

HJ.Form.checkboxListener = function(checkbox)
{
    checkbox.origCheckStatus = checkbox.checked;
    
    return function(event)
    {
        var checkbox = HJ.Event.getTargetElement(event);
        
        var checked = checkbox.checked;
        if (checked == checkbox.lastCheckStatus) { return; }
        checkbox.lastCheckStatus = checked;
        
        if (checked != checkbox.origCheckStatus)
        {
            HJ.Event.fire(checkbox, "change", event);
        }
        else
        {
            HJ.Event.fire(checkbox, "restore", event);
        }
        
        if (checkbox.type == 'radio' && checked)
        {
        	// We checked a radio - make sure last checked status is false for all other radios
        	var radios = HJ.form(checkbox)[checkbox.name];
        	for (var i=0;i<radios.length;i++)
        	{
        		if (radios[i] != checkbox)
        		{
        			radios[i].lastCheckStatus = false;
        		}
        	}
        }
    }
}

HJ.Form.extendAsync = function(form)
{
    HJ.Form.extend(form);
    form.async=true;
}

HJ.Form.confirmSaveOnExit = function(message)
{
    if (message == false)
    {
        window.onbeforeunload = null;
    }

    window.onbeforeunload = function(event)
    {
        var forms = HJ.Form.getModifiedForms();

        if(forms.length != 0)
        {
            if(!message)
                message = 'Some changes have not been saved.';

            return(message);
        }
    };
};

HJ.Form.getModifiedForms = function()
{
    var results = new Array();
    var forms = HJ.Element.getElementsByTagName('form');

    for(index = 0; index < forms.length; index += 1)
    {
        if(HJ.Form.isExtended(forms[index]) && forms[index].isModified())
            results.push(forms[index]);
    }

    return(results);
};

HJ.Form.isExtended = function(form)
{
    return(form.validate && form.submitAsyncSuccessFunc);
};

HJ.Form.Labels = {}; // hashtable; key=field name, value=label

HJ.Form.Labels.setLabel = function(fieldName, label) { HJ.Form.Labels[fieldName] = label };
HJ.Form.Labels.addLabel = function(fieldName, label) { HJ.Form.Labels[fieldName] = label };
                     
HJ.Form.Labels.getFieldLabel = function(field)
{
    // Check for a "label" attribute on the element
    var label = field.getAttribute('label');
    if (label && label.length > 0) { return label; }

    // Check the hashtable of labels
    label = HJ.Form.Labels[field.name];
    if (label) return label;
    
    // Check for parent HTML label elements
    var labelEl = null;
    var parentEl = field.parentNode;
    
	if (parentEl.tagName && parentEl.tagName.toLowerCase() == 'label')
	{
		labelEl = parentEl;		
	}
    
    if (labelEl) return labelEl.innerHTML.replace(/(<([^>]+)>)/ig,""); // remove HTML tags
    
    // Check all label elements in the form
    var labelEls = HJ.Element.getElementsByTagName('label', field.form);
    for (var i=0; i<labelEls.length; i++)
    {
        if (labelEls[i].htmlFor == field.id) {
        	var labelVal = labelEls[i].innerHTML;
        	labelVal = labelVal.replace(/(<([^>]+)>)/ig,""); // remove HTML tags  
        	return labelVal;
        }
    }
    
    // Last resort
    return field.name;
}

HJ.Form.Validators = new Array();

HJ.Form.addValidator = function(className, validatorFunc)
{
    for (var i=0; i<HJ.Form.Validators.length; i++)
    {
        if (HJ.Form.Validators[i][0] == className) return;
    }
    HJ.Form.Validators.push([className, validatorFunc]);
}

HJ.Form.baseValidate = function(form)
{
    var errorFields = new Array();
    
    var elements = form.elements;
    
    for (var j=0; j<elements.length; j++)
    {
    	if (elements[j].type == 'button' || elements[j].type == 'hidden' || elements[j].type == 'submit') { continue; }
        HJ.Form.setFieldColor(elements[j], 'default');
    }

    for (var i=0; i<HJ.Form.Validators.length; i++)
    {
        for (var j=0; j<elements.length; j++)
        {
            if (!HJ.Element.isClass(elements[j], HJ.Form.Validators[i][0])) continue;
            
            var fld = elements[j];
            
            // If its a radio, get the first field
            if (fld.type && fld.type.toLowerCase() == 'radio')
            {
                var radios = fld.form[fld.name];
                fld = radios[0]; // Always check the first radio only
            }
            
            if (fld.errorCode) { continue; }   // already has an error
            
            HJ.Form.Validators[i][1].apply(null, [fld]);
            
            if (fld.errorCode) errorFields.push(fld);
        }
    }

    if (errorFields.length < 1) return true;
    
    // Display all the errors. TODO - Make this extendable
    
    var errorMsg = 'Please correct the following errors:\n\n';
    var firstField = null;
    for (var i=0; i<errorFields.length; i++)
    {
        var field = errorFields[i];
        if (firstField == null && field.type && field.type.toLowerCase() == 'text') { firstField = field}
        errorMsg += field.errorCode + '\n';
        field.errorCode=null;
        HJ.Form.setFieldColor(field, HJ.Constants.FIELD_ERROR_COLOR);
    }
    
    alert(errorMsg);
    if (firstField) { firstField.focus(); }
    return false;
}

HJ.Form.FieldValidators = {};
/*
* A validator function must take a form field as a parameter
*/
HJ.Form.FieldValidators.required = function(field)
{
	if (HJ.Element.isCollapsed(field)) { return; } 
	
    var missing = false;  // is this field missing
    if (field.type != null && field.type.toLowerCase() == 'text')
    {
        // its a text field
        if (HJ.Util.isEmptyString(field.value)) {
            missing = true;
        }
    }
    else
    if (field.selectedIndex != null)
    {
        // its a select list
        if (field.selectedIndex == -1 ||
            HJ.Util.isEmptyString(field.options[field.selectedIndex].value))
        {
            missing = true;
        }
            
    }
    else if ((field.type && field.type.toLowerCase() == 'textarea') ||
             (field.tagName && field.tagName.toLowerCase() == 'textarea'))
    {
        // its a text area
        if (HJ.Util.isEmptyString(field.value)) {
            missing = true;
        }
    }
    else
    {
        var fieldArray = field;
        if (!field.length) fieldArray = field.form[field.name]; // Get the array 
        
        // Its a radio button
        // Determine if any of the radio buttons with the same name
        // are checked
            
        for (j=0; j<fieldArray.length; j++)
        {
            // at least one should have a checked value of true
            if (fieldArray[j].checked == true)
            {
                missing = false;
                break;
            }
            missing = true;
        }

    }  // end radio group processing

    if (missing)
    {
        field.errorCode = HJ.Form.Labels.getFieldLabel(field) + ' is required';
    }
}

HJ.Form.FieldValidators.email = function(field)
{
    var s = (typeof field == 'string' ? field : field.value);

    var temp = HJ.Util.trimTrailing(s);
    
    if (temp == '') { return; }
    if (temp.match(/[\(\)<>\[\]:;\\,]+/) // no special chars:  ()<>[]:;@\,."
        || !(temp.match(/^[\w\.\-\&]+\x40[\w\.\-]+\.(\w{2}|aero|arpa|asia|biz|cat|com|coop|cym|edu|geo|gov|info|int|jobs|mil|mobi|museum|name|net|org|post|pro|tel|travel)$/i)) 
                                         // allow - and & in mailbox part
                                         // not checking 2 character country codes
        || temp.match(/\.\./)            // 2 dots in a row
        || temp.match(/.*\x40.*\x40.*/)  // 2 @ signs
        || temp.charAt(0) == "."         // can't start with dot
         )
    {
        if (typeof field != 'string') {
        	field.errorCode = HJ.Form.Labels.getFieldLabel(field) + ' must be a valid email address';
        }
        return false;
    }
    return true;
}
// Validates a comma separated list of emails
HJ.Form.FieldValidators.emaillist = function(field)
{
	var s = field.value;
	
	var temp = HJ.Util.trimTrailing(s);
	if (temp == '') { return; }
    var emailArray = temp.split(','); 
    var badEmails = new Array();
    for (var i=0; i < emailArray.length; i++)
    {
    	var email = HJ.Util.trimWhiteSpace(emailArray[i]);
    	if (!HJ.Form.FieldValidators.email(email))
    	{
    		badEmails.push(email);
    	}
    }
    
    if (badEmails.length > 0)
    {
    	var errorMsg = HJ.Form.Labels.getFieldLabel(field) + ' contains a bad email address (';

	    for (var i=0; i < badEmails.length; i++)
    	{
	    	errorMsg += '"' + badEmails[i] + '"';
	    	
	    	if (i == badEmails.length-2) {
	    		errorMsg += ' and ';
	    	}
	    	else if ( i < badEmails.length-2)
	    	{
	    		errorMsg += ', ';
	    	}
    	}
    	errorMsg += ')';
    	field.errorCode = errorMsg;
    }
}

HJ.Form.FieldValidators.zipcode = function(field)
{
    var s = field.value;
    
    // empty field is valid
    var temp = HJ.Util.trimTrailing(s);
    if (temp == '') { return; }
    
    temp = s.replace(/\D/g, "")
    if (temp.match(/^\d{5}$|^\d{9}$/) == null)
    {
        field.errorCode = HJ.Form.Labels.getFieldLabel(field) + ' must be a valid zip code';
    }
}

HJ.Form.FieldValidators.phone = function(field)
{
    var s = field.value;
    
    // empty field is valid
    var temp = HJ.Util.trimTrailing(s);
    if (temp == '') { return; }
    
    temp = s.replace(/\D/g, "")
    if (( temp.length > 9 && temp.length < 26) == false)
    {
        field.errorCode = HJ.Form.Labels.getFieldLabel(field) + ' must be a valid phone number';
    }
}

HJ.Form.FieldValidators.posint = function(field)
{
    var i = parseInt(field.value);
    if (isNaN(i)) {
        field.errorCode = HJ.Form.Labels.getFieldLabel(field) + ' must be a positive integer';
    }
    if (i < 1) {
        field.errorCode = HJ.Form.Labels.getFieldLabel(field) + ' must be positive';
    }
}

HJ.Form.FieldValidators.creditcard = function(field)
{
    var s = field.value;
    var temp = HJ.Util.trimTrailing(s);
    if (temp == '') { return; }
    
    temp = s.replace(/\s/g, ""); // strip all whitespace
    
    if (temp.match(/\D/) ||
    	temp.length < 13 || temp.length > 16)
    {
        field.errorCode = HJ.Form.Labels.getFieldLabel(field) + ' must be a properly formatted credit card number';
    }
}

HJ.Form.FieldValidators.creditcardexpiration = function(field)
{
    var s = field.value;
    var temp = HJ.Util.trimTrailing(s);
    if (temp == '') { return; }
    
    if (temp.match(/^00/) ||
    	!temp.match(/^[0-1]{1}[0-9]{1}[0-9]{1}[0-9]{1}$/))
    {
        field.errorCode = HJ.Form.Labels.getFieldLabel(field) + ' must be in the format MMYY';
    }
}

HJ.Form.FieldValidators.creditcardsecuritycode = function(field)
{
    var s = field.value;
    var temp = HJ.Util.trimTrailing(s);
    if (temp == '') { return; }
    
    if (temp.match(/\D/) ||
    	temp.length < 3 || temp.length > 4)
    {
        field.errorCode = HJ.Form.Labels.getFieldLabel(field) + ' must be a 3 or 4 digit number';
    }
}


HJ.Form.addValidator("required", HJ.Form.FieldValidators.required);
HJ.Form.addValidator("email", HJ.Form.FieldValidators.email);
HJ.Form.addValidator("emaillist", HJ.Form.FieldValidators.emaillist);
HJ.Form.addValidator("zipcode", HJ.Form.FieldValidators.zipcode);
HJ.Form.addValidator("phone", HJ.Form.FieldValidators.phone);
HJ.Form.addValidator("posint", HJ.Form.FieldValidators.posint);
HJ.Form.addValidator("creditcard", HJ.Form.FieldValidators.creditcard);
HJ.Form.addValidator("ccexpiration", HJ.Form.FieldValidators.creditcardexpiration);
HJ.Form.addValidator("ccsecuritycode", HJ.Form.FieldValidators.creditcardsecuritycode);

/*
*  Make a field required
*/ 
HJ.Form.makeFieldRequired = function(element, name)
{
    var el = HJ.el(element);

    if (!el.type && el.length) el = el[0];  // first in a group of radio buttons
    
    if (!HJ.Element.isClass(el, "required"))
    {
        HJ.Element.addClass(el, "required");
    }
    el.setAttribute('label', name);
}

/*
* Set the field color on a form.  Used to indicate which fields are in error or temorarily disabled
*/
HJ.Form.setFieldColor = function(field, color)
{
    if (field.type != null && field.type.toLowerCase() == 'hidden') return;
    
    if (field[0])
    {
        if (!field.style && !field[0].style) return; // only IE supported
    }
    else
    {
        if (!field.style) return; // only IE supported
    }        
    
    var saveField = field;  // Field that saves the original
    if (field[0])
    {
        saveField = field[0]; // take the first radio button
    }

    var setColor = color;
    
    if (color == 'default')
    {
        setColor = saveField.backgroundColorOriginal;
    }
    else 
    if (!saveField.backgroundColorOriginal && !saveField.originalSet)
    {
        saveField.backgroundColorOriginal = saveField.style.backgroundColor;
        saveField.originalSet = true;
    }
    
    if (field.type != null && field.type.toLowerCase() == 'text')
    {
        field.style.backgroundColor = setColor;
    }
    else
    if (field.selectedIndex != null)
    {
        var firstOption = field.options[0]; 
        if (firstOption != undefined) firstOption.style.backgroundColor = setColor;
    }    
    else if ((field.type && field.type.toLowerCase() == 'textarea') ||
             (field.tagName && field.tagName.toLowerCase() == 'textarea'))
    {
        // its a text area
        field.style.backgroundColor = setColor;
    } else {
        // Its a radio button
        var fieldArray = field;
        if( HJ.Util.isArray(fieldArray) ) {
            
	        for (j=0; j<fieldArray.length; j++)
	        {
	             fieldArray[j].style.backgroundColor = setColor;
	        }
        } else {
        	field.style.backgroundColor = setColor;
        }
        
    }
}

HJ.Form.Dependency = function(dependentElement, dependsOnElement)
{
	this.dependentElement = dependentElement;
	this.dependsOnElement = dependsOnElement;
}

HJ.Form.Dependency.prototype.setDependency = function()
{
	var classTrue = this.dependentElement.getAttribute("dependentClassTrue");
	var classFalse = this.dependentElement.getAttribute("dependentClassFalse");
	
	// Assume one each for now, may want more than one
	
	var el = this.dependentElement;
	
	if (this.evaluateDependency())
	{
		if (classFalse) {HJ.Element.removeClass(el, classFalse); }
		if (classTrue)  {HJ.Element.addClass(el, classTrue); }
		
		// Special classes - built in behavior
		if (classFalse == 'disabled') el.disabled = false;
		if (classTrue == 'disabled') el.disabled = true;
		
		if (classTrue == 'show') HJ.Element.show(el);
		if (classTrue == 'expand') HJ.Element.expand(el);

		if (classTrue == 'hide') HJ.Element.hide(el);
		if (classTrue == 'collapse') HJ.Element.collapse(el);
	}
	else
	{
		if (classTrue)  {HJ.Element.removeClass(el, classTrue); }
		if (classFalse) {HJ.Element.addClass(el, classFalse); }
		
		// Special classes - built in behavior
		if (classFalse == 'disabled') el.disabled = true;
		if (classTrue == 'disabled') el.disabled = false;

		if (classFalse == 'show') HJ.Element.show(el);
		if (classFalse == 'expand') HJ.Element.expand(el);

		if (classFalse == 'hide') HJ.Element.hide(el);
		if (classFalse == 'collapse') HJ.Element.collapse(el);
	}
}

HJ.Form.Dependency.prototype.evaluateDependency = function()
{
	var dependentVals = this.dependentElement.getAttribute("dependentValue").split(',');

	var actualVal = new Array();
	
	var input = this.dependsOnElement;
	
	if (input.tagName && input.tagName.toLowerCase() == 'select')
	{
		actualVal[0] = input.options[input.selectedIndex].value;			
	}
	else if (input.tagName && ((input.tagName.toLowerCase() == 'input' && input.type == 'text') || input.tagName.toLowerCase() == 'textarea'))
	{
		actualVal[0] = input.value;
	}
	else if (input.tagName && input.tagName.toLowerCase() == 'input' && input.type == 'checkbox')
	{
		if (input.checked == true)
		{
			if (input.value && !HJ.Util.isEmptyString(input.value))
			{
				actualVal[0] = input.value;
			}
			else
			{
				actualVal[0] = 'checked';
			}
		}		
	}
	else if ((HJ.Util.isArray(input)||typeof input == 'function') && input[0].tagName && input[0].tagName.toLowerCase() == 'input' && input[0].type == 'radio')
	{
		for (var i=0; i<input.length; i++)
		{
			if (input[i].checked == true)
			{
				actualVal[0] = input[i].value;
				break;
			}		
		}
	}
	else if ((HJ.Util.isArray(input)||typeof input == 'function') && input[0].tagName && input[0].tagName.toLowerCase() == 'input' && input[0].type == 'checkbox')
	{
		for (var i=0; i<input.length; i++)
		{
			if (input[i].checked == true)
			{
				actualVal[actualVal.length] = input[i].value;
			}		
		}
	}
	
	for (var i=0; i<dependentVals.length; i++)
	{
		for (var j=0; j<actualVal.length; j++)
		{
			if (dependentVals[i] == actualVal[j] ||
				(dependentVals[i] == '*' && !HJ.Util.isEmptyString(actualVal[j]))) {
				return true;
			}
		}
	}
	return false;

}

HJ.Form.prepareDependencies = function(form)
{
 	form = HJ.el(form);
	
	form.dependencies = new Array();

	var dependentEls = HJ.Element.getElementsByClassName('dependent', form);
	
	HJ.Util.forEach(dependentEls, function(dependentElement) {
	
			var dependentInput = dependentElement.getAttribute("dependentInput");
			var dependsOnElement = form[dependentInput];
	
			var aDependency = new HJ.Form.Dependency(dependentElement, dependsOnElement);
			form.dependencies[form.dependencies.length] = aDependency;
			
			aDependency.setDependency();
			
			// Register the element listeners
			
			var elements = dependsOnElement;
			
			if (!HJ.Util.isArray(dependsOnElement))
			{
				elements = new Array();
				elements[0] = dependsOnElement;
			}

			HJ.Util.forEach(elements, function(eachElement) { 
					HJ.Event.registerCustomHandler(eachElement, 'change',
					     function(event) { aDependency.setDependency(); });
					     
					HJ.Event.registerCustomHandler(eachElement, 'restore',
					     function(event) { aDependency.setDependency(); });
				});
			
		});
	
}

//---------- HJ.Window ----------
/**
* Get the containing window
*/
HJ.win = function(el)
{
    return HJ.Window.getParentWindow(HJ.el(el));    
}

HJ.Window.enable = function()
{
    if (!HJ.Window.WindowManager.INSTANCE)
    {
        HJ.Window.WindowManager.INSTANCE = new HJ.Window.WindowManager();
    }
    return HJ.Window.WindowManager.INSTANCE;
}

/* Ctor function for the HJ Window Manager */

HJ.Window.WindowManager = function()
{
    HJ.DragDrop.enable();
    
    this.windows = new Array();
    this.activeWindow = null;

    HJ.Event.registerHandler(document, 'mousedown', this.engageWindow());
    HJ.Event.registerHandler(document, 'mousemove', this.resizeWindow());
    HJ.Event.registerHandler(document, 'mouseup', this.releaseWindow());
    
    HJ.Window.WindowManager.INSTANCE = this;
}

HJ.Window.WindowManager.INSTANCE = null;

HJ.Window.WindowManager.prototype.getMaxZIndex = function()
{
    var maxZIndex = 0;
    for (var i=0; i<this.windows.length; i++)
    {
        if (parseInt(this.windows[i].style.zIndex) > maxZIndex){
         maxZIndex = parseInt(this.windows[i].style.zIndex);
        }
    }
    return maxZIndex;   
}

/* Ctor function for the HJ Window prototype, which defines all common window behavior */

HJ.Window.Prototype = function() { }

HJ.Window.Prototype.prototype.save = function() { alert('save not implemented');}

HJ.Window.Prototype.prototype.close = function(event)
{
    this.closeWindow(event); 
}

HJ.Window.Prototype.prototype.closeWindow = function(event)
{
    HJ.Element.removeShadow(this);

    HJ.Element.hide(this);
    if (this.parentNode)
    {
        this.parentNode.removeChild(this);
    }
    
    if (this.opts.modal) { HJ.Window.removeModalBackground(); }
 
    
       
    // TODO
    // HJ.Event.fire(this, "close", event);
}
    
HJ.Window.Prototype.prototype.getForms = function()
{
    return this.getElementsByTagName('form');
}

HJ.Window.Prototype.prototype.getForm = function() {
    var forms = this.getForms();
    return (forms.length > 0 ? forms[0] : null);
}
        
HJ.Window.Prototype.prototype.setMenubar = function(menu)
{
    var menubar = HJ.Element.getFirstElementByClassName("winMenubar", this);
    if (!menubar)
    {
        menubar = document.createElement('span');
        menubar.className='winMenubar';
        menubar.style.width='100%';
        var menuToolbar = HJ.Element.getFirstElementByClassName('winMenuToolbar', this);
        menuToolbar.insertBefore(menubar, this.getWinContent());
    } else {
        menubar = menubar[0];
    }
            
    if (typeof menu == 'string')
    {
        menubar.innerHTML = menu;
    }
    else
    {
        menubar.appendChild(menu);
    }
}

HJ.Window.Prototype.prototype.closeMenu = function()
{
    HJ.Menu.closeMenu();
}

HJ.Window.Prototype.prototype.setToolbar = function(tools)
{
    var toolbar = HJ.Element.getFirstElementByClassName("winToolbar", this);
    if (!toolbar)
    {
        toolbar = document.createElement('span');
        toolbar.className='winToolbar';
        toolbar.style.width='100%';
        var menuToolbar = HJ.Element.getFirstElementByClassName('winMenuToolbar', this);
        menuToolbar.insertBefore(toolbar, this.getWinContent());
    } else {
        toolbar = toolbar[0];
    }
    
    if (typeof tools == 'string')
    {
        toolbar.innerHTML = tools;
    }
    else
    {
        toolbar.appendChild(tools);
    }
}

HJ.Window.Prototype.prototype.getWinContent = function()
{
    if (!this.winContent) {
        var contents = HJ.Element.getElementsByTagAndClassName('div','winContent',this);
        if (contents && contents.length > 0)
        {
            this.winContent = contents[0];
        }
        // alertnatively, this.winContent = HJ.Element.getFirstElementByClassName('winContent', this);
    }
    return this.winContent; // returns undefined if their is not one
}

HJ.Window.Prototype.prototype.maximize = function(event)
{
    this.origWidth=this.opts.width;
    this.origHeight=this.opts.height;
    
    this.opts.width="100%";
    this.opts.height="100%";
    
    var maxBtn = HJ.Element.getElementsByClassName('winMaximizeButton',this)[0];
    var restoreBtn = HJ.Element.getElementsByClassName('winRestoreButton',this)[0];
    
    HJ.Element.toggleVisibilityCollapse(maxBtn);
    HJ.Element.toggleVisibilityCollapse(restoreBtn);
    
    HJ.Window.setWindowSize(event, this);
    
}

HJ.Window.Prototype.prototype.restore = function(event)
{

    this.opts.width=this.origWidth;
    this.opts.height=this.origHeight;
    
    var maxBtn = HJ.Element.getElementsByClassName('winMaximizeButton',this)[0];
    var restoreBtn = HJ.Element.getElementsByClassName('winRestoreButton',this)[0];
    
    HJ.Element.toggleVisibilityCollapse(maxBtn);
    HJ.Element.toggleVisibilityCollapse(restoreBtn);
    
    HJ.Window.setWindowSize(event, this);
    
    HJ.Element.show(this); // IE opacity bug
}

HJ.Window.Prototype.prototype.refreshContent = function(url)
{
    var winDiv = this;
    var winContent = this.getWinContent();
    
    if (!url) winContent.innerHTML = this.opts.content;
    
    this.closeMenu();
    
    var contentUrl = url || this.contentUrl;
    if (!contentUrl) return;
    
    var serverCall = new HJ.ServerCall();
        
    // Must call showWin because of IE opacity bug
    var showWin = function (serverCall) { 
        HJ.Element.resetShadow(winDiv);
/* SCM - 05/30/07 - Determined that this is no longer necessary
        if (HJ.Browser.isIE)
        {
            var w = winDiv.offsetWidth;
            w -=1;
            winDiv.style.width=w+'px';
            winDiv.style.width=(w+1)+'px';
            HJ.Element.show(winDiv);
        }
*/
        // TODO
        // HJ.Event.fire(winDiv, "loaded");
        if (winDiv.opts.focusAt && HJ.el(winDiv.opts.focusAt)) {
            HJ.el(winDiv.opts.focusAt).focus();
        }    
    };
            
    serverCall.setResponseNode(winContent);
        
    serverCall.sendGet(contentUrl, showWin);
    
}

HJ.Window.Prototype = new HJ.Window.Prototype(); 

HJ.Window.createWindow = function(userOpts, contentUrl, parent, event)
{
    var winMgr = HJ.Window.enable();
    return winMgr.createWindow(userOpts, contentUrl, parent, event);
}

HJ.Window.WindowManager.prototype.createWindow = function(userOpts, contentUrl, parent, event)
{
    var opts = {};

    opts.type  = userOpts.type || "default";
    opts.modal = userOpts.modal || false;
    opts.title = userOpts.title || "New Window";
    opts.width = userOpts.width || "300px"; // e.g. 80px|80%|auto|inherit
    opts.height = userOpts.height || "auto"; // e.g. 80px|80%|auto|inherit
    opts.position = userOpts.position || "absolute"; // absolute|relative|absolute-scroll
    opts.top = userOpts.top || "0px";  // e.g. 80px|80%|center
    opts.left = userOpts.left || "0px"; // e.g. 80px|80%|center
    opts.content = userOpts.content || '&nbsp;';
    opts.contentAlign = userOpts.contentAlign || null; 
    opts.contentPadding = userOpts.contentPadding || null;
    opts.style = userOpts.style || 'Win';
    opts.menu = userOpts.menu || null;
    opts.status = userOpts.status || false;  // include the status bar on the bottom of the window
    opts.resize = (userOpts.resize==false ? false : true);
    opts.icon = userOpts.icon || null;
    opts.closeButton = userOpts.closeButton || null;
    //opts.closeButton = userOpts.closeButton || true;  // TODO:  These 3 button true or false?
    opts.minButton = userOpts.minButton || null;
    opts.maxButton = userOpts.maxButton || null;
    opts.resizeMinHeight = userOpts.resizeMinHeight || "1px";
    if (opts.resizeMinHeight == "0px") opts.resizeMinHeight = "1px";
    opts.resizeMinWidth = userOpts.resizeMinWidth || "100px";
    opts.allowMultiple = (userOpts.allowMultiple==false ? false : true);
    opts.draggable = (userOpts.draggable==false ? false : true);
    opts.focusAt = userOpts.focusAt;
    
    // TODO - new up a window and then add it to the this.windows array
    // then activate the window.
    
    if (opts.modal) { HJ.Window.setModalBackground(this.getMaxZIndex()); }
 
    var winDiv = document.createElement("div");

    winDiv.opts = opts;
    
    var className = "win opaque";
    if (opts.draggable) className += " draggable";
    
    winDiv.className=className;
    
    winDiv.id = "HJ.win" + this.windows.length;

    winDiv.winType = opts.type;

    winDiv.style.visibility = "hidden";
    winDiv.style.zIndex = this.getMaxZIndex()+1;
    if (winDiv.style.zIndex < 1000)  // pass in default ??
    {
        winDiv.style.zIndex=1000;
    }

    var parentEl = (parent ? HJ.el(parent) : document.body);        
    parentEl.appendChild(winDiv);
    
    // TODO:  Create a window Object and have it contain the winDiv????
    
    this.windows[this.windows.length] = winDiv;
    
    var initialContentHt = "auto";
    if (winDiv.opts.height != "auto")
    {
        initialContentHt = (HJ.Window.getWindowHeight(winDiv) || '300') + 'px';         
    }    
    
    var winStr = 
    '<div class="winTitle" style="z-index:10;width:100%;position:relative;">  \
      <table border="0" cellpadding="0" cellspacing="0" style="width:auto"> \
        <tr style="height:2px">\
          <td rowspan="2" style="height:10px;width:5px;vertical-align:top;text-align:left;">\
             <div ' + (opts.resize ? 'class="winResize topLeft" ' : '') + 'style="width:5px;height:10px;"></div>\
          </td>\
          <td colspan="1" ' + (opts.resize ? 'class="winResize top" ' : '') + 'style="vertical-align:top;\
             text-align:center;width:100%;height:2px;"></td>\
          <td rowspan="2" style="vertical-align:top;text-align:right;">\
              <div ' + (opts.resize ? 'class="winResize topRight" ' : '') + 'style="width:5px;height:10px;"></div>\
          </td>\
        </tr>\
        <tr>\
          <td class="' + (opts.draggable ? ' dragHandle' : '') +'" style="width:100%;overflow:hidden;height:10px;vertical-align:top">\
            <div class="winTitleText" style="float:left;"><span>'
          + opts.title
          //+ ' (' + this.windows.length + ')'
          + '</span></div>\
            <div style="float:right;clear:none;background-color:transparent;" class="winCloseButton" onmouseover="HJ.Element.addClass(this,\'hover\')" onmouseout="HJ.Element.removeClass(this,\'hover\');" onclick="HJ.win(this).close();HJ.Event.stop(event);return false;"></div>'
          + (opts.position == 'absolute-scroll' ?
            '<div style="float:right;clear:none;background-color:transparent;" class="winMaximizeButton" onmouseover="HJ.Element.addClass(this,\'hover\')" onmouseout="HJ.Element.removeClass(this,\'hover\');" onclick="HJ.win(this).maximize(event);return false;"></div>\
             <div style="display:none;float:right;clear:none;background-color:transparent;" class="winRestoreButton" onmouseover="HJ.Element.addClass(this,\'hover\')" onmouseout="HJ.Element.removeClass(this,\'hover\');" onclick="HJ.win(this).restore(event);return false;"></div>&nbsp;'
            : '') +
          '</td>\
        </tr>\
      </table>\
    </div>\
    <table border="0" cellpadding="0" cellspacing="0" width="100%">\
      <tr>\
        <td ' + (opts.resize ? 'class="winResize left" ' : '') + 'style="width:2px"></td>\
        <td class="winMenuToolbar" style="overflow:hidden;width:auto">\
          <div class="winContent" style="width:auto;z-index:10;position:relative;top:0px;margin-top:2px;height:'
          + initialContentHt + '">'
          + (opts.content || 'No content') +
          '</div>\
        </td>\
        <td ' + (opts.resize ? 'class="winResize right" ' : '') + 'style="width:2px;"></td>\
      </tr>\
    </table>';
    
    if (opts.status)
    {
    winStr += '\
    <div id="winStatusBar_win1" style="width:100%;z-index:10;position:relative;">\
      <table border="0" cellpadding="0" cellspacing="0" width="100%">\
        <tr>\
          <td rowspan="2" style="height:10px;width:5px;vertical-align:bottom;text-align:left;">\
            <div ' + (opts.resize ? 'class="winResize bottomLeft" ' : '') + 'style="width:5px;height:10px;"></div>\
          </td>\
          <td class="winStatus" style="overflow:hidden;width:98%;height:10px;vertical-align:middle">\
            \
          </td>\
          <td rowspan="2" style="vertical-align:bottom;text-align:right;">\
             <div ' + (opts.resize ? 'class="winResize bottomRight" ' : '') + '></div>\
          </td>\
        </tr>\
        <tr style="height:2px">\
          <td ' + (opts.resize ? 'class="winResize bottom" ' : '') + 'style="vertical-align:bottom;\
             text-align:center;width:100%;height:2px;"></td>\
        </tr>\
      </table>\
    </div>';
    } else {
    winStr += '\
      <table border="0" cellpadding="0" cellspacing="0" width="100% style="height:2px">\
        <tr style="height:2px">\
          <td style="height:2px;width:5px;vertical-align:bottom;text-align:left;">\
            <div ' + (opts.resize ? 'class="winResize bottomLeft" ' : '') + 'style="width:5px;height:2px;"></div>\
          </td>\
          <td ' + (opts.resize ? 'class="winResize bottom" ' : '') + 'style="vertical-align:bottom;width:98%;height:2px;"><div></div></td>\
          <td style="vertical-align:bottom;text-align:right;">\
             <div ' + (opts.resize ? 'class="winResize bottomRight" ' : '') + '></div>\
          </td>\
        </tr>\
      </table>\
      ';
    }
    winStr += '\
  </div>';
    
    winDiv.innerHTML=winStr;

    // Add behaviors to the window
    // Copy all common window behaviors from the Prototype
    
    for (behavior in HJ.Window.Prototype) {
        winDiv[behavior] = HJ.Window.Prototype[behavior];
    } 
  
    winDiv.setStatus = HJ.Constants.Functions.EMPTY;
    
    if (opts.status)
    {
        var statusEls = HJ.Element.getElementsByClassName('winStatus', winDiv);

        if (statusEls && statusEls.length > 0)
        {
            winDiv.setStatus = function(s) {
                statusEls[0].innerHTML = s;
                HJ.Element.resetShadow(winDiv);
            }
        }
    }
    
    if (contentUrl) { winDiv.contentUrl = contentUrl; }
    
    HJ.Element.show(winDiv);
    
    var winContent = winDiv.getWinContent();
    
    if (opts.contentAlign) winContent.style.textAlign = opts.contentAlign;
    if (opts.contentPadding) winContent.style.padding = opts.contentPadding;
    
    /* SCM - 05/30/07 - Determined that this is no longer necessary
    if (HJ.Browser.isIE && event)
    {
        // IE has scrolling difficulty when the content div is scrolling
        var showContent = function (event) {
            var w = winContent.offsetWidth;
            w -=1;
            winContent.style.width=w+'px';
            winContent.style.width=(w+1)+'px';
            };

        HJ.Event.registerHandler(winContent, 'scroll', showContent); //IE scroll problem with divs
    }
    */
        
    if (!winDiv.opts.draggable && !winDiv.opts.resizable)
    {
        // TODO - unregister these events when a window closes
        
        HJ.Event.registerHandler(window, 'resize', function(event) {
            HJ.Window.setWindowSize(event,winDiv);
            HJ.Element.show(winDiv);
             });
        HJ.Event.registerHandler(window, 'scroll', function(event) {
            HJ.Window.setWindowSize(event,winDiv);
            HJ.Element.show(winDiv);            
             });
    }
    
    winDiv.contentUrl = contentUrl;
    
    HJ.Window.setWindowSize(event, winDiv);
    
    HJ.Element.addShadow(winDiv);
    
    winDiv.refreshContent(contentUrl);
    
    return winDiv;
}

HJ.Window.setWindowSize = function(event, winDiv)
{
    // Resize and position the window based on the size opts
    
    winDiv = winDiv || HJ.Window.getParentWindow(event);
    
    var opts = winDiv.opts;
    winDiv.style.width  = opts.width;
    
    winDiv.style.position = "absolute";

    // this was called from a window resize, adjust the win
    var winContent = winDiv.getWinContent();
    if (winContent && winDiv.opts.height != 'auto')
    {
        var contentHt = HJ.Window.getWindowHeight(winDiv)-0;
        if (contentHt < 1) contentHt = 1;
        // Subtract 40 to account for title, status bar ????
        winContent.style.height=contentHt + 'px'
    } 
    
//TODO - figure this out
    if (HJ.Browser.isIE && winContent && (winContent.clientHeight < winContent.scrollHeight))
    {
        // scrollbars are showing in IE - need to subtract 16px;
 //     winContent[0].style.width = (winContent[0].offsetWidth - 16) + 'px'
//      winContent[0].style.width = (winContent[0].clientWidth) + 'px'
  //    winContent[0].style.width = 'auto';
    } 
    
    if(opts.position == 'absolute-scroll' ||
       opts.position == 'absolute' || !event || event == undefined)
    {
        HJ.Window.setLeft(winDiv);
        HJ.Window.setTop(winDiv);
    
    }
    else
    {
        var mousePos = HJ.Event.getMousePosition(event);
        winDiv.style.left = (mousePos.x + HJ.Util.getInteger(opts.left)) + 'px';  
        winDiv.style.top = (mousePos.y + HJ.Util.getInteger(opts.top)) + 'px';  
    }

    HJ.Element.resetShadow(winDiv);
}

HJ.Window.setLeft = function(winDiv)
{
    var scrollPos = getScrollXY();
    var opts = winDiv.opts;
    
    if(opts.position == 'absolute')
    {
        scrollPos[0]=0;
        scrollPos[1]=0;
    }
    if (opts.left == 'center' || opts.left == 'middle')
    {
        var realWinWidth = HJ.Util.getInteger(winDiv.style.width);
        if (winDiv.style.width.indexOf('%') != -1)
        {
            realWinWidth = parseInt('' + ((realWinWidth * HJ.Element.getBrowserWindowWidth()) / 100));
        }
        winDiv.style.left = (parseInt('' + ((HJ.Element.getBrowserWindowWidth()/2)- (realWinWidth/2)))+ scrollPos[0])+"px";
     }
     else if (typeof opts.left == 'string' && opts.left.indexOf("%") != -1)
     {
        winDiv.style.left = (parseInt('' + ((HJ.Element.getBrowserWindowWidth() * HJ.Util.getInteger(opts.left))/100))+ scrollPos[0])+"px";
     }
     else
     {
        winDiv.style.left = HJ.Util.getInteger(opts.left) + scrollPos[0];
     }
}

HJ.Window.setTop = function(winDiv)
{
    var scrollPos = getScrollXY();
    var opts = winDiv.opts;

    if(opts.position == 'absolute')
    {
        scrollPos[0]=0;
        scrollPos[1]=0;
    }
     if (opts.top == 'center' || opts.top == 'middle')
     {
        var realWinHt = HJ.Util.getInteger(winDiv.opts.height);
        
        if (winDiv.opts.height.indexOf('%') != -1)
        {
/*      
            realWinHt = parseInt('' + ((realWinHt * HJ.Element.getBrowserWindowHeight()) / 100));
            realWinHt += 50; // TODO - account for window title status, etc. correctly
*/
            realWinHt = winDiv.offsetHeight;            
        }
        if (realWinHt > HJ.Element.getBrowserWindowHeight())
        {
            winDiv.style.top = 1 + scrollPos[1];
        }
        else
        {
            winDiv.style.top = (parseInt('' + ((HJ.Element.getBrowserWindowHeight()/2)- (realWinHt/2)))+ scrollPos[1])+"px";
        }
     }
     else if (typeof opts.top == 'string' && opts.left.indexOf("%") != -1)
     {
        winDiv.style.top = (parseInt('' + ((HJ.Element.getBrowserWindowHeight() * HJ.Util.getInteger(opts.top))/100))+ scrollPos[1])+"px";
     }
     else
     {
        winDiv.style.top = HJ.Util.getInteger(opts.top) + scrollPos[1];    
     }
}

HJ.Window.getWindowHeight = function(winDiv)
{
    var num = HJ.Util.getInteger(winDiv.opts.height);
    if (winDiv.opts.height.indexOf("%") == -1)
    {
        return num;
    }
    else
    {
        return parseInt('' + ((HJ.Element.getBrowserWindowHeight() * num) / 100));
    }
}

HJ.Window.WindowManager.prototype.engageWindow = function() {
    var windowMgr = this;
    
    return function(event)
    {
        // Determine if we clicked inside a window
        
        var element = HJ.Event.getTargetElement(event);
        
        var win = null;
        
        if (HJ.Element.isClass(element, 'win'))
        {
            win = element;
        }
        else
        {
            // Check if clicked within a child element of a handle
            win = HJ.Element.getParentByClassName(element, 'win');
        }
        
        if (!win)
        {
            return;  // propagate event
        }
        
        // Was the resize area clicked
        
        if (!windowMgr.currentZIndex)
        {
            if (windowMgr.activeWindow)
            {
                windowMgr.currentZIndex=windowMgr.activeWindow.style.zIndex;   
            }
            else
            {
                windowMgr.currentZIndex=1000; // ok this isn't perfect, but it will do for now
            }
        }
        windowMgr.currentZIndex += 2; // allow 1 for a shadow
        
        win.style.zIndex=windowMgr.currentZIndex;

        windowMgr.activeWindow = win;
        if (!windowMgr.activeWindow.winContent)
        {
            var winContent = win.getWinContent();
            if (winContent)
            {
                windowMgr.activeWindow.winContent = winContent;
            } 
        }
        
        if (HJ.Element.isClass(element, 'winResize'))
        {
            if (!win.opts.resize) { return; }
            // Engage resizing

            win.engageResize = true;
            HJ.Element.addClass(win, 'resizing');
            
            // Prevent IE text selection when resizing
            document.body.ondrag = HJ.Constants.Functions.RETURN_FALSE;
            document.body.onselectstart = HJ.Constants.Functions.RETURN_FALSE;
            
            win.style.position='absolute';
            
            var winPos = HJ.Element.getRelativePosition(win);
            var mousePos = HJ.Event.getMousePosition(event);

//                    dd.mouseOffsets = [(mousePos.x-pos.x), (mousePos.y-pos.y)];

            win.resizeStartWinPos = winPos;
//            win.resizeStartOffset = new HJ.Position(win.offsetLeft, win.offsetTop);
            win.resizeStartOffset = new HJ.Position(mousePos.x-win.offsetLeft, mousePos.y-win.offsetTop);
//            alert(win.offsetLeft + ' ' + win.offsetTop);
//            alert(winPos.x + ' ' + winPos.y);
            win.resizeStartMousePos = mousePos;
            
            if (HJ.Element.isClass(element, 'bottomRight'))
                win.resizeHandle='bottomRight';
            else if (HJ.Element.isClass(element, 'top'))
                win.resizeHandle='top';
            else if (HJ.Element.isClass(element, 'bottom'))
                win.resizeHandle='bottom';
            else if (HJ.Element.isClass(element, 'left'))
                win.resizeHandle='left';
            else if (HJ.Element.isClass(element, 'right'))
                win.resizeHandle='right';
            else if (HJ.Element.isClass(element, 'bottomLeft'))
                win.resizeHandle='bottomLeft';
            else if (HJ.Element.isClass(element, 'topLeft'))
                win.resizeHandle='topLeft';
            else if (HJ.Element.isClass(element, 'topRight'))
                win.resizeHandle='topRight';
            
            win.resizeStartWidth = win.offsetWidth;
            win.resizeStartHeight = win.offsetHeight;
            
            if (win.winContent)
            {
                win.winContent.resizeStartWidth=win.winContent.offsetWidth;
                win.winContent.resizeStartHeight=win.winContent.offsetHeight;
            }
            
            HJ.Event.stop(event);
        }
        HJ.Element.show(win); // Required for IE opacity bug
    }
}

HJ.Window.WindowManager.prototype.resizeWindow = function() {
    var windowMgr = this;
    
    return function(event)
    {
        var win = windowMgr.activeWindow;
        
        if (!win || !win.engageResize == true)
        {
            return;
        }
 
        var mousePos = HJ.Event.getMousePosition(event);
        
        // neg - win shrink, pos - win grow
        var adjustX = mousePos.x - win.resizeStartMousePos.x;
        var adjustY = mousePos.y - win.resizeStartMousePos.y;

        if (win.resizeHandle=='topLeft' ||
            win.resizeHandle=='left' ||
            win.resizeHandle=='bottomLeft')
        {
            adjustX=0-adjustX; 
        }
        
        if (win.resizeHandle=='topLeft' ||
            win.resizeHandle=='top' ||
            win.resizeHandle=='topRight')
        {
            adjustY=0-adjustY;
        }
        
        var minResizeWidth = 150;
        
        if (win.opts) { minResizeWidth = HJ.Util.getInteger(win.opts.resizeMinWidth); }
                
        if (win.resizeStartWidth + adjustX < minResizeWidth)
        {
            adjustX = minResizeWidth-win.resizeStartWidth;
        }
        
        var minResizeHeight = 1;
        
        if (win.opts) { minResizeHeight = HJ.Util.getInteger(win.opts.resizeMinHeight); }
        
        if (win.winContent)
        {
            if (win.winContent.resizeStartHeight + adjustY < minResizeHeight)
            {
                adjustY = minResizeHeight - win.winContent.resizeStartHeight;
            }
        }
        else if (win.resizeStartHeight + adjustY < 20)
        {
            adjustY = 20-win.resizeStartHeight;
        }
        
//        if (newWidth < 100) newWidth=100;
//        if (newHeight < 20) newHeight=20;
        
        if (win.resizeHandle=='bottom' || win.resizeHandle=='top')
        {
            adjustX=0;
        }
        if (win.resizeHandle=='right' || win.resizeHandle=='left')
        {
            adjustY=0;
        }
        
        var newWidth=win.resizeStartWidth + adjustX;
        var newHeight=win.resizeStartHeight + adjustY;
        
        //window.status='width:' + newWidth + ',height:' + newHeight;
        
        if (win.style.width == (newWidth + 'px') && win.style.height == (newHeight + 'px'))
        {
    //        return;
        }
        
        win.style.width= newWidth + 'px';
        win.style.height= newHeight + 'px';
        
        if (win.winContent)
        {
            var newContentWidth=win.winContent.resizeStartWidth+adjustX; 
            var newContentHeight=win.winContent.resizeStartHeight+adjustY;
            
            win.winContent.style.width = newContentWidth + 'px';
            win.winContent.style.height = newContentHeight + 'px';
        }        

        if (win.resizeHandle == 'bottomLeft' || 
            win.resizeHandle == 'left' ||
            win.resizeHandle == 'topLeft' ||
            win.resizeHandle == 'topRight' ||
            win.resizeHandle == 'top')
        {
            window.status='orig:' + win.resizeStartWinPos.x + ' ' + win.resizeStartWinPos.y;
            var newElPos = new HJ.Position(win.resizeStartWinPos.x, win.resizeStartWinPos.y);
            
//            alert('new:' + newElPos.x + ' ' + newElPos.y);
            
//            newElPos.x -= win.resizeStartOffset.x;
//            newElPos.y -= win.resizeStartOffset.y;

//            alert('adjust:' + adjustX + ' ' + adjustY);
            
            if (win.resizeHandle=='topLeft' ||
                win.resizeHandle=='left' ||
                win.resizeHandle=='bottomLeft')
            {
                newElPos.x -= adjustX; 
            }
            
            if (win.resizeHandle=='topLeft' ||
                win.resizeHandle=='top' ||
                win.resizeHandle=='topRight')
            {
                newElPos.y -= adjustY; 
            }
            
            window.status='new:' + newElPos.x + ' ' + newElPos.y;
            HJ.Element.setPosition(win, newElPos);
        }
        
        HJ.Element.show(win); // Required for IE opacity bug
        HJ.Element.resetShadow(win);
    }
}

HJ.Window.WindowManager.prototype.releaseWindow = function() {
    var windowMgr = this;
    
    return function(event)
    {
        if (windowMgr.activeWindow && windowMgr.activeWindow.engageResize == true)
        {
            windowMgr.activeWindow.engageResize = false;
            
            HJ.Element.removeClass(windowMgr.activeWindow, 'resizing');
            
            HJ.Event.stop(event);
            
            // Prevent IE text selection when dragging
            document.body.ondrag = null;
            document.body.onselectstart = null;    
        }
    }
}

HJ.Window.getParentWindow = function(elementOrEvent)
{
    var el = null;
    if (HJ.Browser.isEvent(elementOrEvent))
    {
        el = HJ.Event.getTargetElement(elementOrEvent);
    }
    else
    {
        el = HJ.el(elementOrEvent);
    }
    return HJ.Element.getParentByClassName(el, 'win');
}

HJ.Window.setModalBackground = function(zIndex)
{
    var modalBg = document.createElement('div');
    modalBg.id='HJModalBackground';
    modalBg.style.width='100%';
    modalBg.style.position='absolute';
    if (zIndex)
    {
        modalBg.style.zIndex=zIndex;
    }
    modalBg.className='winModalBackground';
    
    document.body.appendChild(modalBg);
    HJ.Window.setModalBackgroundDims();
    
    HJ.Event.registerHandler(modalBg, 'click', HJ.Constants.Functions.STOP_EVENT);
    HJ.Event.registerHandler(modalBg, 'mouseover', HJ.Constants.Functions.STOP_EVENT);
    HJ.Event.registerHandler(modalBg, 'mouseout', HJ.Constants.Functions.STOP_EVENT);

    HJ.Event.registerHandler(window, 'scroll', HJ.Window.setModalBackgroundDims);
    HJ.Element.setPosition(modalBg, new HJ.Position(0,0));
    HJ.Event.registerHandler(window, 'resize', HJ.Window.setModalBackgroundDims);
    
    HJ.Util.forEach(HJ.Element.getElementsByTagName('select'), function(select) {
            if (select.disabled == false)
            {
                select.disabled = true;
                select.disabledModalSet = true;
                HJ.Element.addClass(select, 'winModalBackground');
            }
        }); 
    
}

HJ.Window.setModalBackgroundDims = function(event)
{
    var modalBg = HJ.el('HJModalBackground');
    if (!modalBg) return;

    var scrollXY = getScrollXY();
    var ht = HJ.Element.getBrowserWindowHeight() + getScrollXY()[1];
    var wd = HJ.Element.getBrowserWindowWidth() + getScrollXY()[0];
    
    modalBg.style.height=ht + 'px';
    modalBg.style.width=wd + 'px';
}

HJ.Window.removeModalBackground = function()
{
    var modalBg = HJ.el('HJModalBackground');
    if (modalBg) { modalBg.parentNode.removeChild(modalBg); }
    
    HJ.Util.forEach(HJ.Element.getElementsByTagName('select'), function(select) {
           if (select.disabledModalSet == true)
            {
                select.disabled = false;
                select.disabledModalSet = false;
                HJ.Element.removeClass(select, 'winModalBackground');
            }
        }); 
}

HJ.Element.moveWithScroll = function(element)
{
    var el = HJ.el(element);
    var origScrollPos = getScrollXY();

	el.basePosition = HJ.Element.getPosition(el);
    
    HJ.Event.registerHandler(window, 'scroll', function() {
            var scrollPos = getScrollXY();
            
            var newPos = new HJ.Position(el.basePosition.x + (scrollPos[0]),
                                         el.basePosition.y + (scrollPos[1]));
                                         
            el.baseScrollPosX = scrollPos[0];
            el.baseScrollPosY = scrollPos[1];
            
            HJ.Element.setPosition(el, newPos);
        }
    );
}

HJ.Element.moveWithScrollWORKMODAL = function(element)
{
    var el = HJ.el(element);
    
    HJ.Event.registerHandler(window, 'scroll', function() {
            var origPos = HJ.Element.getPosition(el);
            if (!el.basePos) { el.basePos = origPos; }
            var scrollPos = getScrollXY();
            var newPos = new HJ.Position(el.basePos.x, el.basePos.y);
            newPos.x += scrollPos[0];
            newPos.y += scrollPos[1];
            HJ.Element.setPosition(el, newPos);
        }
    );
}


HJ.Element.moveWithResize = function(element)
{
    var el = HJ.el(element);
    el.baseWinHeight = HJ.Element.getBrowserWindowHeight();
    el.baseWinWidth = HJ.Element.getBrowserWindowWidth();
    
    HJ.Event.registerHandler(window, 'resize', function() {
            var origPos = HJ.Element.getPosition(el);
            
            // Get the window size
            var winHt = HJ.Element.getBrowserWindowHeight();
            var winWid = HJ.Element.getBrowserWindowWidth(); 
            
            var newPos = new HJ.Position(origPos.x + (winWid - el.baseWinWidth),
                                         origPos.y + (winHt - el.baseWinHeight));
                                         
            el.baseWinHeight = winHt;
            el.baseWinWidth = winWid,
            
            HJ.Element.setPosition(el, newPos);
        }
    );
}

HJ.Element.getBrowserWindowHeight = function()
{
    if (typeof( window.innerHeight ) == 'number') return window.innerHeight;
    else if (document.documentElement && document.documentElement.clientHeight) return document.documentElement.clientHeight;
    else if (document.body && document.body.clientHeight) return document.body.clientHeight;
}

HJ.Element.getBrowserWindowWidth = function()
{
    if (typeof( window.innerWidth ) == 'number') return window.innerWidth;
    else if (document.documentElement && document.documentElement.clientWidth) return document.documentElement.clientWidth;
    else if (document.body && document.body.clientWidth) return document.body.clientWidth;
}

//http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
function getScrollXY() {
  var scrOfX = 0, scrOfY = 0;
  if( typeof( window.pageYOffset ) == 'number' ) {
    //Netscape compliant
    scrOfY = window.pageYOffset;
    scrOfX = window.pageXOffset;
  } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
    //DOM compliant
    scrOfY = document.body.scrollTop;
    scrOfX = document.body.scrollLeft;
  } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
    //IE6 standards compliant mode
    scrOfY = document.documentElement.scrollTop;
    scrOfX = document.documentElement.scrollLeft;
  }
  return [ scrOfX, scrOfY ];
}

//---------- HJ.DragDrop ----------

// The methods grabbed(), dragging(), and doneDragging are sample call backs from
// for the drag-drop API.

var statusElement;

function startGrab(event)
{
    HJ.DragDropHandler.INSTANCE.stopDragging();
    window.status='do not allow drag';
}


function grabbed(event)
{
    var s = 'grabbed';
    window.status=s;
    
    var els = HJ.Element.getElementsByClassName('winStatus', HJ.DragDropHandler.INSTANCE.dragElement);
    if (els && els.length > 0)
    {
        statusElement = els[0]; // TODO - using a global status element for now.
        statusElement.innerHTML = s;
    }
}

function dragging(event)
{
    window.status='dragging';    

    var s = 'move to ';
    var pos = HJ.DragDropHandler.INSTANCE.newElementPos;
    if (pos)
    {
        s += 'x:' + pos.x + 'y:' + pos.y;
    }
    
    window.status = s;
    if (statusElement)
    {
     //   statusElement.innerHTML = s;        
    }
}

function doneDragging(event)
{
    var s = 'let go';
    window.status = s;    
    if (statusElement)
    {
        statusElement.innerHTML = s;        
    }
}

/*
* Enable the Drag Drop. Call once per page.
*/
HJ.DragDrop.enable = function()
{
    if (!HJ.DragDropHandler.INSTANCE)
    {
        HJ.DragDropHandler.INSTANCE = new HJ.DragDropHandler();
        
        // Example of how to set callbacks
        //HJ.DragDropHandler.INSTANCE.setGrabCallback(grabbed);
        //HJ.DragDropHandler.INSTANCE.setDragCallback(dragging);
        //HJ.DragDropHandler.INSTANCE.setLetGoCallback(doneDragging);
        
//      HJ.Event.registerCustomHandler(HJ.DragDropHandler.INSTANCE, "beginGrab", HJ.Constants.Functions.RETURN_FALSE);
//      HJ.Event.registerCustomHandler(HJ.DragDropHandler.INSTANCE, "beginGrab", startGrab);
//      HJ.Event.registerCustomHandler(HJ.DragDropHandler.INSTANCE, "grab", grabbed);
//      HJ.Event.registerCustomHandler(HJ.DragDropHandler.INSTANCE, "drag", dragging);
//      HJ.Event.registerCustomHandler(HJ.DragDropHandler.INSTANCE, "beginDrop", HJ.Constants.Functions.RETURN_FALSE);
//      HJ.Event.registerCustomHandler(HJ.DragDropHandler.INSTANCE, "drop", doneDragging);
    }
    return HJ.DragDropHandler.INSTANCE;
}

/*
* Constructor
*/
HJ.DragDropHandler = function()
{
    this.grabDraggableCallback = null;
    this.dragCallback = null;
    this.letGoDraggableCallback = null;
    
    this.dragElement;
    this.dropZones; // valid drop zones for the current drag element
    this.newElementPos;
    this.mouseOffsets; // Offset from mouse position to top left corner of draggable element
    this.dropHandlers = new Array();

    HJ.Event.registerHandler(document, 'mousedown', this.grabDraggable());
    HJ.Event.registerHandler(document, 'mousemove', this.drag());
    HJ.Event.registerHandler(document, 'mouseup', this.letGoDraggable());
    
}

HJ.DragDropHandler.INSTANCE = null;

HJ.DragDropHandler.isDragging = function()
{
    return HJ.DragDropHandler.INSTANCE && HJ.DragDropHandler.INSTANCE.dragElement!=null;
}

HJ.DragDropHandler.prototype.getPosition = function()
{
    return (this.dragElement ? HJ.getPosition(this.dragElement) : new HJ.Position(0,0));
}

HJ.DragDropHandler.prototype.setGrabCallback = function(func)
{
    HJ.Event.registerCustomHandler(HJ.DragDropHandler.INSTANCE, "grab", func);
}

HJ.DragDropHandler.prototype.setDragCallback = function(func)
{
    HJ.Event.registerCustomHandler(HJ.DragDropHandler.INSTANCE, "drag", func);
}

HJ.DragDropHandler.prototype.setLetGoCallback = function(func)
{
    HJ.Event.registerCustomHandler(HJ.DragDropHandler.INSTANCE, "drop", func);
}

HJ.DragDropHandler.prototype.registerDropHandler = function(dragClass, dropClass, method, dragRoot, dropRoot, dropTag, ignoreCropped)
{
    var map;
    if (dragClass.dragClass) { map = dragClass; }
    else
    {
        map = {dragClass: dragClass, dropClass: dropClass, method: method, dragRoot: dragRoot, dropRoot: dropRoot, dropTag: dropTag, ignoreCropped: (ignoreCropped||false)};
    }
    this.dropHandlers.push(map);
}

HJ.DragDropHandler.prototype.stopDragging = function() {
    this.dragElement = this.dropZones = this.mouseOffsets = this.newElementPos = null;
}


HJ.DragDropHandler.prototype.grabDraggable = function() {
    var dd = this;
    
    return function(event)
    {
        if (HJ.Event.isRightClick(event)) { return; }
        
        if (dd.dragElement) { return; }
        
        var element = HJ.Event.getTargetElement(event);
        
        if (HJ.Element.isClass(element, 'draggable'))
        {
            dd.dragElement = element;
        }
        else if (HJ.Element.isClass(element, 'dragHandle'))
        {
            dd.dragElement = HJ.Element.getParentByClassName(element, 'draggable');
            
        }
        else
        {
            // Check if clicked within a child element of a handle
            var handle = HJ.Element.getParentByClassName(element, 'dragHandle');
            if (handle)
            {
                dd.dragElement = HJ.Element.getParentByClassName(element, 'draggable');   
            }
        }
        
        if (!dd.dragElement) { 
            return;  // propagate event, continue looking for draggables
        }

        HJ.Event.fire(dd, "beginGrab", event);
        
        if (!dd.dragElement || dd.dragElement == null ) { 
            return;  // beginGrab handler may have prohibited dragging so we need to check
        }

        if (HJ.Element.isClass(dd.dragElement, 'dragGhost'))
        {
            var ghost = dd.dragElement.cloneNode(true);
            document.body.appendChild(ghost);
            
            HJ.Element.addClass(dd.dragElement, 'dragging');
            ghost.host = dd.dragElement;
            
            HJ.Element.addClass(ghost, 'ghost');
            
            // position the ghost at the original position
            var ghostPos = HJ.Element.getPosition(dd.dragElement);
            var scrollOffsets = HJ.Element.getScrollOffsets(dd.dragElement);

			ghostPos.x += scrollOffsets[0];
            
            HJ.Element.setPosition(ghost, ghostPos);
            
            dd.dragElement = ghost;
            
            var mousePos = HJ.Event.getMousePosition(event);
            dd.mouseOffsets = [(mousePos.x-ghostPos.x), (mousePos.y-ghostPos.y)]; 

        }
        else
        {
            /*
            if (dd.dragElement.style == undefined ||
                dd.dragElement.style.position == undefined ||
                dd.dragElement.style.position == '' ||
                dd.dragElement.style.position == 'static' ||
                (dd.dragElement.style.position == 'fixed' && !HJ.Browser.isFF))
            {
                dd.dragElement.style.position='absolute';
                // The above line fixes some scenarios of staic elements but breaks others !
                // better to just make draggables be absolute or relative in stylesheet or html
            }
            var pos = HJ.Element.getPosition(dd.dragElement);
            */

            var pos = new HJ.Position(dd.dragElement.offsetLeft, dd.dragElement.offsetTop);
            var mousePos = HJ.Event.getMousePosition(event);
            dd.mouseOffsets = [(mousePos.x-pos.x), (mousePos.y-pos.y)]; 
        }
        
        //alert('mouseoffsetx:' + dd.mouseOffsets[0] + 'mouseoffsety:' + dd.mouseOffsets[1]); alert('top:' + dd.dragElement.style.top + ' left:'+ dd.dragElement.style.left + ' offsetWidth:' + dd.dragElement.offsetWidth + ' offsetHeight:' + dd.dragElement.offsetHeight + ' docX:' + pos.x + ' docY:' + pos.y);

        dd.dragElement.style.position='absolute';
        
        HJ.Element.addClass(dd.dragElement, 'dragging');
        
        // find drop zones, we do this on each grab because zones could be changing
        
        dd.dropZones = new Array();

        var handler;    
        for (var j=0; j<dd.dropHandlers.length; j++)
        {
            handler = dd.dropHandlers[j];
            if (!HJ.Element.isClass(dd.dragElement, handler.dragClass)) continue;
            if (handler.dragRoot && !HJ.Element.isParentOf((dd.dragElement.host ? dd.dragElement.host : dd.dragElement),handler.dragRoot)) { continue; }
            var dropRoot = handler.dropRoot;
            
            var dropZones;
            if (handler.dropTag)
            {
	         	dropZones = HJ.Element.getElementsByTagAndClassName(handler.dropTag, handler.dropClass, handler.dropRoot);
            }
            else
            {
	         	dropZones = HJ.Element.getElementsByClassName(handler.dropClass, handler.dropRoot);
    		}
            if (dropZones) {
            	for (var k=0;k<dropZones.length;k++)
            	{
            		if ((handler.ignoreCropped == true && !HJ.Element.isCropped(dropZones[k])) ||
            			(handler.ignoreCropped != true && HJ.Element.isVisible(dropZones[k]))
            		)
            		{
	            		dd.dropZones.push(dropZones[k]);
	            	}
            	}
            }
            HJ.Util.forEach(dropZones, function(dropZone) {HJ.Element.addClass(dropZone, 'dropAvail'); } );
        }

        // add default drop zones - THIS IS CAUSING WINDOW DRAGGING TO BE SLOW IN LARGE DOCS
        //var dropZones = HJ.Element.getElementsByClassName('dropZone', (handler ? handler.dropRoot : null));
        //if (dropZones) { for (var k=0;k<dropZones.length;k++) dd.dropZones.push(dropZones[k]); }

        HJ.Event.stop(event);
        
        if (HJ.Browser.isFF && window.find)
            window.find("mUsT bE nOt eVeR fOuNd");    // Firefox - dummy search cancels started selection
        else {
            // Prevent IE text selection when dragging
            document.body.ondrag = HJ.Constants.Functions.RETURN_FALSE;
            document.body.onselectstart = HJ.Constants.Functions.RETURN_FALSE;
        }
     
        // TODO: The following code is intended to prevent text selection in FF, but it doesn't seem to work.
        //if (typeof document.body.onselectstart != "undefined")
        //  document.body.onselectstart = HJ.Constants.Functions.RETURN_FALSE; // IE
        //else if (typeof document.body.style.MozUserSelect != "undefined")
        //  document.body.style.MozUserSelect = "none";                        // Firefox
        //else
        //  document.body.onmousedown = HJ.Constants.Functions.RETURN_FALSE;   // Safari and Opera?
        
//        if (dd.grabDraggableCallback) { dd.grabDraggableCallback.apply(); }

        HJ.Event.fire(dd, "grab", event);

    }
}

HJ.DragDropHandler.prototype.drag = function()
{
    var dd = this;
    
    return function(event)
    {
        if (!dd.dragElement) { return; }
        
        var newElPos = HJ.Event.getMousePosition(event);
        
        newElPos.x -= dd.mouseOffsets[0]; 
        newElPos.y -= dd.mouseOffsets[1]; 
       
        dd.newElementPos = newElPos;
        
        HJ.Element.setPosition(dd.dragElement, newElPos);

        // If the mouse is in drop zone, then activate the drop zone that the mouse if over,
        // if not, active the drop zone based on overlapping with the drag element

        var activeDropZones = new Array();

        for (var i=0; i<dd.dropZones.length; i++)
        {
            HJ.Element.removeClass(dd.dropZones[i],'active');
            // Determine if the there is overlap with a drop zone
            if (HJ.Element.isPositionWithinElement(newElPos, dd.dropZones[i]))
            {
                // is this a valid drop zone for the type
                if (dd.dropZones[i].ondrop) // a generic handler
                {
                    activeDropZones.push(dd.dropZones[i]);
                }
                else
                {
                    for (var j=0; j<dd.dropHandlers.length; j++)
                    {
                        var handler = dd.dropHandlers[j];
                        if (!HJ.Element.isClass(dd.dragElement, handler.dragClass)) continue;
                        if (!HJ.Element.isClass(dd.dropZones[i], handler.dropClass)) continue;
                        activeDropZones.push(dd.dropZones[i]);
                    }
                }
            }
        }
        
        if (activeDropZones.length == 0)
        {
        
        for (var i=0; i<dd.dropZones.length; i++)
        {
            HJ.Element.removeClass(dd.dropZones[i],'active');
            // Determine if the there is overlap with a drop zone
            if (HJ.Element.isOverlapping(dd.dragElement, dd.dropZones[i],10,10,10,10))
            {
                // is this a valid drop zone for the type
                if (dd.dropZones[i].ondrop) // a generic handler
                {
                    activeDropZones.push(dd.dropZones[i]);
                }
                else
                {
                    for (var j=0; j<dd.dropHandlers.length; j++)
                    {
                        var handler = dd.dropHandlers[j];
                        if (!HJ.Element.isClass(dd.dragElement, handler.dragClass)) continue;
                        if (!HJ.Element.isClass(dd.dropZones[i], handler.dropClass)) continue;
                        activeDropZones.push(dd.dropZones[i]);
                    }
                }
            }
        }

        }
        
        if (activeDropZones.length > 0)
        {
            HJ.Element.addClass(activeDropZones[activeDropZones.length-1],'active'); // make last one active
        }
        
        HJ.Event.stop(event);
        
        //if (dd.dragCallback) { dd.dragCallback.apply(); }
        HJ.Event.fire(dd, "drag", event);
    }
}

HJ.DragDropHandler.prototype.letGoDraggable = function()
{
    var dd = this;
    
    return function(event)
    {
        if (!dd.dragElement) { return; }
        
        HJ.Event.fire(dd, "beginDrop", event);
        
        if (HJ.Element.isClass(dd.dragElement, 'dragGhost'))
        {
            HJ.Element.removeClass(dd.dragElement.host,'dragging');
            HJ.Element.hide(dd.dragElement);
            dd.dragElement.parentNode.removeChild(dd.dragElement);
        }
        else
        {
            HJ.Element.removeClass(dd.dragElement, 'dragging');
        }
        
        for (var i=dd.dropZones.length-1; i>=0; i--)
        {
            var dropZone = dd.dropZones[i];
            if (HJ.Element.isClass(dropZone,'active'))
            {
                if (dropZone.ondrop)
                {
                    dropZone.ondrop.apply(dropZone, [dd.dragElement, dropZone]);
                }
                for (var j=0; j<dd.dropHandlers.length; j++)
                {   var handler = dd.dropHandlers[j];
                    if (!HJ.Element.isClass(dd.dragElement, handler.dragClass)) continue;
                    if (!HJ.Element.isClass(dropZone, handler.dropClass)) continue;
                    //alert('dropped a ' + handler.dragClass + ' onto a ' + handler.dropClass);
                    handler.method.apply(null, [dd.dragElement, dropZone]);
                    break; // only do one drop
                }
            }
            HJ.Element.removeClass(dropZone,'active');
            HJ.Element.removeClass(dd.dropZones[i],'dropAvail');
        }
        
        HJ.Event.stop(event);
        
        // Prevent IE text selection when dragging
        document.body.ondrag = null;
        document.body.onselectstart = null;    
        
        //if (dd.letGoDraggableCallback) { dd.letGoDraggableCallback.apply(); }
        HJ.Event.fire(dd, "drop", event);
        dd.dragElement = dd.dropZones = dd.mouseOffsets = dd.newElementPos = null;
    }
}    


//---------- Data Table Widget ----------

/*
* Show the detail rows
*/

HJ.Table.ROW_DETAIL_CLASS = 'tblRowDetail';

HJ.Table.showRowDetails = function (rowEl)
{
    var rowEl = HJ.Element.getNextSiblingByTagName(HJ.el(rowEl), 'tr');

    var thePopupEls = HJ.Element.getElementsByClassName(HJ.Table.ROW_DETAIL_CLASS, rowEl);
    var thePopup = thePopupEls[0];
    
    // Hide any other details that are currently visible
    var parentEl = rowEl.parentNode;
    var allRowDetails = HJ.Element.getElementsByClassName(HJ.Table.ROW_DETAIL_CLASS, parentEl);
    for (var i=0; i<allRowDetails.length; i++)
    {
        if (allRowDetails[i] != thePopup)
        {
            HJ.Element.hide(allRowDetails[i]);
        }
    }
    
    // Show/Hide the requested detail row
    HJ.Element.toggleVisibility(thePopup); 

// TODO:  This is a good concept, but it doesn't work, instead of displaying an overlay,
// insert right into the table cell.  Maybe set the div position to be relative may do the trick.
/*
    var td = rowEl.getElementsByTagName('td')[0];
    if (!td.show) { td.show = true; } else { td.show = false; }
    
    if (td.show)
        td.innerHTML = thePopup.innerHTML ? thePopup.innerHTML : '' ;    
    else
        td.innerHTML = '';
*/
}

//---------- HJ.ServerCall : Server Call (AJAX) ----------

HJ.ServerCallManager = {callCount: 0, calls: new Array()};

/*
*  ServerCall class contructor function
*/
HJ.ServerCall = function ()
{
    this.timestamp = 0;
    this.callCount = HJ.ServerCallManager.callCount++;


    // SCM: TODO - comment out until deal with mem cleanup 
    HJ.ServerCallManager.calls[this.callCount]=this;

    try
    {
        if(window.XMLHttpRequest) {
            this.request = new XMLHttpRequest(); // branch for native XMLHttpRequest object
        } else if(window.ActiveXObject) {
            this.request = new ActiveXObject("Msxml2.XMLHTTP"); // branch for IE/Windows ActiveX version
        }
        else
        {
            alert('This browser is not supported');   
        }
    } catch(e) {
        alert('This browser is not supported');
    }
    return this;
}

HJ.ServerCall.prototype.sendPost = function (url,form_data,callback) {
    
    this.method = "POST";
    this.formData = form_data;
    this.url = url;
    
    this.onComplete = callback;
    try {
        this.request.open("POST",url,true);
    } catch(e) {
        // Possibly the result of cross domain scripting
        if (url.indexOf('?') == -1) url+='?'; else url+='&';
        this.sendGetNoXmlHttpRequest(url + form_data, callback);
        return;
    }
    
    this.request.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
    this.request.setRequestHeader("Content-length", form_data.length);    
    if (!HJ.Browser.isIE) 
    {
    	this.request.setRequestHeader('Connection','close'); // required for Firefox (old versions)
    	// setting in IE causes some 12152 (time out) errors with some http server configs
    }

    if (this.resultNode != undefined && this.resultNode != null)
    {
        this.request.onreadystatechange = HJ.ServerCall.PopulateResultNode(this);   
    }
    else
    {
        this.request.onreadystatechange = HJ.ServerCall.InvokeComplete(this);
    }
    
    try {
        this.request.send(form_data);
    } catch(e) {
        // Possibly the result of cross domain scripting
        if (url.indexOf('?') == -1) url+='?'; else url+='&';
        this.sendGetNoXmlHttpRequest(url + form_data, callback);
        return;
    }
    
}
        
HJ.ServerCall.prototype.sendGet = function(url,callback) {
    
    this.method = "GET";
    this.url = url;
    
    this.timestamp = new Date().getTime();
    this.onComplete = callback;
    try {
        this.request.open("GET",url,true);
    } catch (e) {
        // Possibly the result of cross domain scripting
        this.sendGetNoXmlHttpRequest(url, callback);
        return;
    }
    if (this.resultNode != undefined && this.resultNode != null)
    {
        this.request.onreadystatechange = HJ.ServerCall.PopulateResultNode(this);   
    }
    else
    {
        this.request.onreadystatechange = HJ.ServerCall.InvokeComplete(this);
    }
    
    try {
        this.request.send(null);
    } catch (e) {
        // Possibly the result of cross domain scripting
        this.sendGetNoXmlHttpRequest(url, callback);
        return;
    }
    
}

HJ.ServerCall.prototype.attemptResend = function()
{
   	if (this.resend == true)
   	{
   		if (confirm('The resend attempt to the server failed. Would you like to try again?'))
   		{
			this.resend=false;
			this.doResend();        		
   		}
   	}
   	else if (confirm ('The browser has temporarily lost connectivity to the server. Would you like to try again?'))
   	{
		this.doResend();
    }
}

HJ.ServerCall.prototype.doResend = function()
{
    if (this.resend == true) { return false; }
    this.resend = true;
    if (this.method == "GET")
    {
        this.sendGet(this.url, this.onComplete);    
    }
    else if (this.method == "POST")
    {
        this.sendPost(this.url, this.formData, this.onComplete);    
    }
    return true;
}

//var ajaxresults = "";
HJ.ServerCall.prototype.sendGetNoXmlHttpRequest = function(url, callback) {

    ajaxresults = "";

    var scriptEl = HJ.el('AJAXScript');
    if (scriptEl)
    {
        scriptEl.parentNode.removeChild(scriptEl);
    }
    // Go get it...
    var e = document.createElement("script");
    e.setAttribute('id','AJAXScript');      
    if (url.indexOf('?') == -1) url+='?'; else url+='&';
    e.type="text/javascript";
    document.getElementsByTagName("head")[0].appendChild(e);
    
/*  
    var thisServerCall = this;
    // This only works with FireFox
    HJ.Event.registerHandler(e, "load", function(event) {
            HJ.ServerCall.invokeComplete(thisServerCall);
        });
*/      
    // now call the server  
    e.src = url + 'ajaxalt=' + this.callCount;
}
        
HJ.ServerCall.prototype.isComplete = function() {
    if (this.altResponseText) return true;  
//TODO    
//    var now = new Date().getTime();
//    var duration = now - this.timestamp;
//    if (duration > 1000)
//    {
//        alert('did not finish in time ' + duration);
//        this.request.abort();
//    }
    
    // Check Ready states for complete status
    // 0 = uninitialized
    // 1 = loading
    // 2 = loaded
    // 3 = interactive
    // 4 = complete
    //if (this.request == null || this.request == undefined ) { return false; }
    //if (this.request.readyState == null || this.request.readyState == undefined ) { return false; }
    //alert ('return ' + this.request.readyState + ' - ' + this.request.status);
    if (this.request.readyState != 4 ) { return false; }
//TODO - blank out func        this.request.onreadystatechange = function() {};
   	try {
    if (this.request.status != 200 && this.request.status != 0)
    {
        switch (this.request.status) { 
        case 404: 
        case 500: 
        case 503:
	        alert('request failed with status code ' + this.request.status + ' - ' + this.request.statusText);
            return false; 
        case 12029: 
        case 12030: 
        case 12031: 
        case 12152: 
        case 12159: 
        	this.attemptResend();
        	return false;
        }
    }
	} catch (e) {
		this.attemptResend();
		return false;
	}
    return true;
}

HJ.ServerCall.prototype.setResponseNode = function(node) {
    this.resultNode = node;
}

HJ.ServerCall.TIME_OUT_TOKEN = '<title>Login</title>';      // cannot be declared before the HJ.ServerCall constructor
HJ.ServerCall.TIME_OUT_RANGE = 300;

HJ.ServerCall.prototype.getResponseText = function()
{
    var text = this.altResponseText || this.request.responseText;
    
    if(HJ.ServerCall.TIME_OUT_TOKEN != '')
    {
        var regex = new RegExp('.*' + HJ.ServerCall.TIME_OUT_TOKEN + '.*');

        if(regex.test(text.substr(0, HJ.ServerCall.TIME_OUT_RANGE)))
        {
            window.location.href = window.location.href;    // reload the page to invoke UserLogin
            return('');
        }
    }

    return(HJ.Util.trimWhiteSpace(text));
}

HJ.ServerCall.prototype.getResponseXML = function() { return this.request.responseXML }

/**
* Get the response as a JavaScript object.
*
* The server may respond with a "true", "false", "null", a string, a number, or 
* a JSON object or array. e.g {"key":"key1"} or [{"key":"key1"},{"key":"key2"}]
*/
HJ.ServerCall.prototype.getResponseObject = function() {
    
    // Maybe - if the return format is in XML, convert it to an object
    // Maybe - use a JSON parser instead of eval()
    
    var respText = this.getResponseText();
    HJ.log(respText);
    if (respText=='true') return true;
    if (respText=='false') return false;
    if (respText=='null') return null;
    var ret;
    //window.eval('ret = ' + respText);
    //if (!ret) { ret = window.eval(respText); }
    ret = HJ.ServerCall.SecureEval(respText);
    return ret;
}

/**
* Convenience method for replacing a node with the results from a server call.
*/
HJ.ServerCall.replaceNode = function(el, url, callback)
{
    var serverCall = new HJ.ServerCall();
    serverCall.setResponseNode(HJ.el(el));
    serverCall.sendGet(url, callback);
}

HJ.ServerCall.CrossSiteUrl = function(url)
{
    this.xsurl = 'XSInclude.jsp?url=' + escape(url);
}

/**
* Convenience method for replacing a node with the results from a server call.
*/
HJ.ServerCall.replaceNode = function(el, url, callback)
{
    var serverCall = new HJ.ServerCall();
    serverCall.setResponseNode(HJ.el(el));
    if (url.xsurl) url = url.xsurl;
    serverCall.sendGet(url, callback);
}



HJ.ServerCall.PopulateResultNode = function(theCall) {
    
    return function()
    {
        HJ.ServerCall.populateResultNode(theCall);
    }
}

HJ.ServerCall.populateResultNode = function(theCall) {
    
    if (!theCall.isComplete()) { return; }

    purge(theCall.resultNode.innerHTML);
    theCall.resultNode.innerHTML = theCall.getResponseText();
    //var resultsXMLNode = theCall.getResponseXML().getElementsByTagName("div").item(0);

    // The response might contain script elements, if so, execute them.
    
    // PROBLEM:  If the returning document fragment contains JavaScript elements,
    // they do not get automatically executed when the content is appended to the
    // response node.
    
    // Get all script nodes and execute the strings as Javascript.
    // But there are limitations....
    // - Each js statement must be separated by semicolon (";")
    // - The javascript cannot define a global function.  Instead, add the
    //   function to an accessible object like window, document, or HJ.
    //   For example:
    //   if (!document.mySaveForm) document.mySaveForm = function() { .... }
    
    var scripts = HJ.Element.getElementsByTagName('script', theCall.resultNode);
    try {
    
    for (var i=0; i < scripts.length; i++)
    {
        var jsStr = scripts[i].innerHTML;
        var htmlCmt = jsStr.indexOf('<!--');
        if (htmlCmt > -1) jsStr = jsStr.slice(htmlCmt+4);
            
     //alert('going to execute ' + i +':' + jsStr);
     try {
         eval(jsStr);
     } catch(e) {
         alert('could not execute javascript using the eval() function:' + jsStr);
         alert(e.toString());
     }
    }

     } catch(e) {
        // Sometimes the page or fragment is closed, or being closed.  This causes an error
        // when iterating through scripts in IE
     }

    if (theCall.onComplete != undefined && theCall.onComplete != null)
    {
        // Call the oncomplete pass the server call as a default argument
        theCall.onComplete.apply(theCall, [theCall]);
    }
}

HJ.ServerCall.InvokeComplete = function(theCall) {
    
    return function()
    {
        HJ.ServerCall.invokeComplete(theCall);
    };
}

HJ.ServerCall.invokeComplete = function(theCall) {
    if (!theCall.isComplete()) { return; }
    // Call the oncomplete pass the server call as a default argument
    theCall.onComplete.apply(theCall, [theCall]);
}

HJ.ServerCallManager.invokeComplete = function(callIndex)
{
    var theCall = HJ.ServerCallManager.calls[callIndex];
    if (theCall)
    {
        if (!theCall.altResponseText) {
            theCall.altResponseText=';';
        }
        /*
        else
        {                        
//done on server            theCall.altResponseText = theCall.altResponseText.replace(/&#039;/g,"'");
        }
        */
        //alert(theCall.altResponseText);   
        if (theCall.resultNode != null)
        {
            HJ.ServerCall.populateResultNode(theCall);
        }
        else
        {
            HJ.ServerCall.invokeComplete(theCall);
        }
    }
}

HJ.ServerCall.SecureEval = function(evalText)
{
    // prevent hack described in (http://www.fortifysoftware.com/servlet/downloads/public/JavaScript_Hijacking.pdf)
    // The server must include the first line of returned JSON to be something like:
    // alert('Security Warning: A malicious web site is trying to hack yourwebsite.com (please call us).'); while(1);
    
    var json = HJ.Util.trimWhiteSpace(evalText);
    json = HJ.Util.trimLeading(json);
    
    // strip out hacker warning 
    var regex = /alert\('Security Warning.*/;
    
    
    /* During development, may want to prevent server side from returning JSON without a Hacker warning.
    if (!regex.test(json))
    {
        alert('The server implementation of this AJAX request is not secure.');
    }
    */
    json = json.replace(regex, '');
    
    if (json.substr(0,9) == "while(1);") {
        json = json.substring(10);
    }
    
    return json.parseJSON();
}

//---------- HJ.AutoComplete: Autocomplete widget ----------

/*
* Constructor Function
*/
HJ.AutoComplete = function(div, action, onSelectFunc, inputFieldsArray, opts)
{
    this.listBox = HJ.el(div);
    this.listBox.autocomplete = this;  // Give the results div a reference back to the auto complete object
    this.action = action;
    
    this.onSelectFunc = onSelectFunc;

    // Set option defaults
    this.opts = (opts ? opts : {});
    
    if (!this.opts.idName)       { this.opts.idName       = 'id'; }
    if (!this.opts.displayName)  { this.opts.displayName  = 'display'; }
    if (!this.opts.maxDisplay)   { this.opts.maxDisplay   = 100; }
    if (!this.opts.textNoMatch)  { this.opts.textNoMatch  = 'No matches.'; }
    if (!this.opts.textOneMatch) { this.opts.textOneMatch = 'One match.'; } 
    if (!this.opts.textMatches)  { this.opts.textMatches  = 'matches.'; } 
    if (!this.opts.textKeepType) { this.opts.textKeepType = 'matches. Keep typing.'; } 
    
// TODO: ?? Can't initialize this.inputs because we can't do it until the doc has been loaded.    
    
    var form = HJ.Element.getParentByTagName('form', this.listBox);
    
    this.inputs = new Array();
    
    if (inputFieldsArray)
    {
        for (var i=0; i<inputFieldsArray.length; i++)
        {
            var anInput = form[inputFieldsArray[i]];
            if (!anInput) { alert('HJ.AutoComplete bad input parameter ' + inputFieldsArray[i]); continue; }
            this.inputs.push(anInput);
        }
    }
    else
    {
        var formInputs = HJ.Element.getElementsByTagName('input', form);
        var formSelects = HJ.Element.getElementsByTagName('select',form);
        
        for (var i=0; i<formInputs.length; i++)
        {
            this.inputs.push(formInputs[i]);
        }
        for (var i=0; i<formSelects.length; i++)
        {
            this.inputs.push(formSelects[i]);
        }
    }
    
    var eventHandler = this.onUserTyping();
    
    for (var i=0; i<this.inputs.length; i++)
    {
        var anInput = this.inputs[i];
        if (anInput.tagName.toLowerCase() == 'input')
        {
            anInput.onkeyup = eventHandler;
            if (anInput.captureEvents) anInput.captureEvents(Event.CLICK);
            anInput.autocomplete = "off";
        }
        else if (anInput.tagName.toLowerCase() == 'select')
        {
            anInput.onchange = eventHandler;
        }
    }
    
    this.results = null; // an array
}

HJ.AutoComplete.prototype.onUserTyping = function()
{
    var theAutoComplete = this;
    
    return function(event) {
        
        var el = HJ.Event.getTargetElement(event);
        
        if (!HJ.Event.isTextKey(event)) return;
        
        if (el.lastValue && HJ.Util.startsWithIgnoreCase(el.value, el.lastValue) && theAutoComplete.results)
        {
            // No need to go to the server, we already have the results
            if (theAutoComplete.results.length != 0) // don't fetch from server if we know there won't be any
            {
                //var filteredResults = theAutoComplete.results;
                //theAutoComplete.populateList(filteredResults);
                theAutoComplete.refresh();  // TODO - Filter the list and call populate list instead of refresh (special handling reqd for %)
            }
        }
        else
        {
            theAutoComplete.refresh();
        }
        
        el.lastValue = el.value;
        HJ.Event.stop(event);
    };
}

HJ.AutoComplete.prototype.close = function()
{
    this.listBox.innerHTML='';
    HJ.Element.hide(this.listBox);
}

HJ.AutoComplete.prototype.refresh = function()
{
    var someInput = false;
    for (var i=0; i< this.inputs.length; i++)
    {
        if (this.inputs[i].value.length > 0)
        {
            someInput = true;
            break;
        }
    }
    if (!someInput)
    {
        this.listBox.innerHTML='';
        HJ.Element.hide(this.listBox);
        return;
    }
            
    // TODO: In the future, we could just filter from our cached results.  We could do this if the object contains, all the
    // for fields {id:123, fullName:'Mendoza, Steve', LastName:'Mendoza',FirstName....}
    // or if the results are tagged {id:123, fullName:'<LastName>Mendoza</LastName>, <FirstName>Steve</FirstName>'}
        
    var parms = '';
//    var parmsArray = new Array();
    for (var i=0; i < this.inputs.length; i++)
    {
        parms += (parms.length == 0 && this.action.indexOf('?') < 1 ? '?' : '&');
        parms += this.inputs[i].name + '=' + escape(this.inputs[i].value);
//        var parmsNVP = { name: this.inputs[i].name, value: escape(this.inputs[i].value };
//        parmsArray.push(parmsNVP);
    }
    
    var serverCall = new HJ.ServerCall();
            
//    serverCall.parmsArray = parmsArray;
    serverCall.sendGet(this.action + parms, this.fillAutoCompleteList());
}

HJ.AutoComplete.prototype.fillAutoCompleteList = function()
{
    var theAutoComplete = this;
    
    return function (theServerCall) {
        // Data returned as an array of objects
        theAutoComplete.results = theServerCall.getResponseObject();
        theAutoComplete.populateList(theAutoComplete.results);
    };
}

HJ.AutoComplete.prototype.populateList = function populateList(returnData)
{
    var innerHTML = '';
    
    innerHTML+='<table border=0 cellpadding=0 cellspacing=0 class="autoComplete">';
    innerHTML+='<tr><td class="autoCompleteMsg">';
    
    if (returnData.length == 0)
    {
        innerHTML += '<span style="float:left">' + this.opts.textNoMatch + '</span>';
        innerHTML += '<a class="close" href="#" onclick="HJ.parent(this,\'div\').autocomplete.close();return false;">close</a>';
        innerHTML += '</td></tr></table>';
        
        this.listBox.innerHTML=innerHTML;
        HJ.Element.show(this.listBox);
        return;   
    }
    
    // Check for too many results.  Format - [{count:5003}]

    var tooMany = 0;
    var tooManyMsg = null;
    
    if (returnData.length==1 && returnData[0].count) {
        tooMany = returnData[0].count;
        tooManyMsg = returnData[0].message || null;
    }
    else if (returnData.length > (this.opts.maxDisplay ? this.opts.maxDisplay : 100)) {
        tooMany = returnData.length;
    }
    
    if (tooMany)
    {
        if (!tooManyMsg)
        {
            tooManyMsg = '' + tooMany + ' ' + this.opts.textKeepType;
        }
        innerHTML += '<span style="float:left">' + tooManyMsg + '</span>';
        innerHTML += '<a class="close" href="#" onclick="HJ.parent(this,\'div\').autocomplete.close();return false;">close</a>';
        innerHTML += '</td></tr></table>';
        
        this.listBox.innerHTML=innerHTML;
        HJ.Element.show(this.listBox);
        return;   
    }
    
    if (returnData.length == 1)
    {
        innerHTML += '<span style="float:left">' + this.opts.textOneMatch + '</span>';
        innerHTML += '<a class="close" href="#" onclick="HJ.parent(this,\'div\').autocomplete.close();return false;">close</a>';
    }
    else
    {
        innerHTML += '<span style="float:left">' + returnData.length + ' ' + this.opts.textMatches + '</span>';
        innerHTML += '<a class="close" href="#" onclick="HJ.parent(this,\'div\').autocomplete.close();return false;">close</a>';
    }
    innerHTML+='</td></tr>';

    for (var i=0; i<returnData.length; i++)
    {
        var idData = returnData[i][this.opts.idName];
        var displayData = returnData[i][this.opts.displayName];
        
        // replace the display data, showing what characters matched, the text
        // must be wrapped in tags corresponding to each input field (e.g. <FirstName>Steve</FirstName>)

        for (var j=0; j< this.inputs.length; j++)
        {
            var matchText = this.inputs[j].value;
            matchText = matchText.replace(/%/g,"");
            if (matchText.length < 1) continue;
            if (matchText==".") continue;
    
            var startTag="<" + this.inputs[j].name + ">";
            var endTag="</" + this.inputs[j].name + ">";
            var startRegExp=new RegExp(startTag,"i");
            var endRegExp=new RegExp(endTag,"i");

            var idx1 = displayData.search(startRegExp);
            var idx2 = displayData.search(endRegExp);
            
            if (idx1>-1 & idx2>-1)
            {
                idx1 += startTag.length;
            }
            else
            {
                continue;
            }
            
            var txt = displayData.substring(idx1, idx2);
            var fullText = startTag + txt + endTag;
            // TODO: Bug - This doesn't work if the fullText has a "(" in it because it is a special regex char
            var replaceFullRegExp = new RegExp(fullText);
            var replaceRegExp = new RegExp(startTag + "|" + endTag);
            txt = txt.replace(replaceRegExp, "");
            
            var regExp = new RegExp(matchText,"i");
            var replaceTxt = txt.match(regExp);
            var newInnerTxt = txt.replace(regExp, "<span class='match'>" + replaceTxt +"</span>");
            
            displayData = displayData.replace(replaceFullRegExp, newInnerTxt);
        }

        var funcStr = '';
        if (typeof this.onSelectFunc == 'string')
        {
            funcStr = this.onSelectFunc + '(\'' + idData + '\',event)';
        } else {
            funcStr = 'HJ.parent(this, \'div\').autocomplete.onSelectFunc(\'' + idData + '\',event)';
        }
        
        innerHTML += '<tr ' +
           'onmouseover="HJ.Element.addClass(this, \'tblHover\');" onmouseout="HJ.Element.removeClass(this)" onblur="HJ.Element.removeClass(this)"' +
           'onclick="' + funcStr + '">';
        innerHTML += '<td><a href="#" onclick="' + funcStr + ';HJ.Event.stop(event);return false;" onfocus="HJ.Element.addClass(HJ.Element.getParentByTagName(\'tr\',this), \'tblHover\');"  onblur="HJ.Element.removeClass(HJ.Element.getParentByTagName(\'tr\', this))">';
        innerHTML += displayData;
        innerHTML += '</a></td></tr>';
    }
    innerHTML +='</table>';
    
    this.listBox.innerHTML=innerHTML;
    HJ.Element.show(this.listBox);
}

//---------- HJ.Tree: Tree widget ----------

HJ.treeNode = function(elementOrEvent)
{
    var el = null;
    
    if (HJ.Browser.isEvent(elementOrEvent))
    {
        // if the event was a click on a context menu, get the tree node associated with it
        var menu = HJ.menu(elementOrEvent);
        while (menu) {
            if (menu.treeNode)return menu.treeNode;
            menu = HJ.menu(menu); // check parent menu
        }
        el = HJ.Event.getTargetElement(elementOrEvent);
    }
    else
    {
        elementOrEvent = elementOrEvent.host || elementOrEvent; // check if element is a ghost (drag/drop)
        el = HJ.el(elementOrEvent);
        if (el.treeNode) { return el.treeNode; }
    }
    return HJ.Tree.getTreeNode(el);
}

/*
* Constructor Function
*/
HJ.Tree = function(elementDiv, label)
{
    var el = HJ.el(elementDiv);
    
    this.domElement = el;
    this.rootNode = null;
    
    this.treeStyle = HJ.Tree.Styles.DEFAULT;
    
    this.editorUrl = "/hjtree_edit"
    
    this.typeLookup = new Array(); // contains arrays (0 - type, 1 - node opts ) 
    
    // Preload images
    var img = new Image();
    for (imgsrc in this.treeStyle) { img.src = HJ.Config.RESOURCE_PATH + imgsrc; }

    this.onAddSuccess = HJ.Constants.Functions.EMPTY_FUNCTION;
    this.onAddFailure = HJ.Constants.Functions.EMPTY_FUNCTION;
    this.onDropSuccess = HJ.Constants.Functions.EMPTY_FUNCTION;
    this.onDropFailure = HJ.Constants.Functions.EMPTY_FUNCTION;
    this.onDeleteSuccess = HJ.Constants.Functions.EMPTY_FUNCTION;
    this.onDeleteFailure = HJ.Constants.Functions.EMPTY_FUNCTION;
}

HJ.Tree.Styles = {
    DEFAULT: {
        VerticalLineImage: 'treevertline.gif',
        NodeBlankImage: 'treeblank.gif',
        NodeImage: 'treenode.gif',   
        NodeEmptyImage: 'treeemptynode.gif',
        NodeLastImage: 'treenodelast.gif',
        OpenNodeImage: 'treeopennode.gif',
        OpenNodeLastImage: 'treeopennodelast.gif',
        ClosedNodeImage: 'treeclosednode.gif',
        ClosedNodeLastImage: 'treeclosednodelast.gif',
        
        OpenFolderImage: 'treeopenfolder.gif',
        ClosedFolderImage: 'treeclosedfolder.gif',
        DocImage: 'treedoc.gif',

        WaitingImage: 'treewaiting.gif',
        SavingImage: 'treesaving.gif'
    }
};

HJ.Tree.prototype.setEditorUrl = function(url)
{
    this.editorUrl = url;
}

HJ.Tree.prototype.addNodeType = function(type, opts)
{
    var entry = [type, opts];
    this.typeLookup[this.typeLookup.length] = entry;
}

HJ.Tree.prototype.getNodeOptions = function(type)
{
    for (var i=0; i<this.typeLookup.length; i++)
    {
        if (type == this.typeLookup[i][0]) return this.typeLookup[i][1];   
    }
    return null;
}

HJ.Tree.prototype.setAddCallbacks = function(successFunc, failureFunc)
{
    if (successFunc) this.onAddSuccess = successFunc;
    if (failureFunc) this.onAddFailure = failureFunc;
}

HJ.Tree.prototype.setDropCallbacks = function(successFunc, failureFunc)
{
    if (successFunc) this.onDropSuccess = successFunc;
    if (failureFunc) this.onDropFailure = failureFunc;
}

HJ.Tree.prototype.setDeleteCallbacks = function(successFunc, failureFunc)
{
    if (successFunc) this.onDeleteSuccess = successFunc;
    if (failureFunc) this.onDeleteFailure = failureFunc;
}

HJ.Tree.prototype.render = function(redraw)
{
    this.rootNode.render((redraw?true:false)); // recursively draw the tree
}

HJ.Tree.prototype.reRender = function()
{
    this.rootNode.render(true); // recursively redraw the tree
}

HJ.Tree.prototype.addNode = function(treeNodeOrId, label, opts, isLeaf)
{
    if (!this.rootNode)
    {
        this.rootNode = new HJ.TreeNode(treeNodeOrId, label, opts, false);
        this.rootNode.parentTree = this;
        this.rootNode.expanded=true;
        return this.rootNode;
    }
    
    return this.rootNode.addNode(treeNodeOrId, label, opts, (isLeaf?true:false));
}

HJ.Tree.prototype.buildTreeNodes = function(obj, parentTreeNode)
{
    var parent = parentTreeNode || this;
    
    var opts = obj.opts || this.getNodeOptions(obj.dataObject.objType.value || obj.dataObject.objType);
    
    var treeNode = parent.addNode(obj.id, obj.label, opts, obj.isLeaf);
    treeNode.setDataObject(obj.dataObject);
 
    for (var i=0; obj.children && i<obj.children.length; i++)
    {
        this.buildTreeNodes(obj.children[i], treeNode);   
    }

    return treeNode;
}

HJ.Tree.prototype.addMenu = function(menuid, menuArray, onopen)
{
    // No context path for menu for now
    HJ.Menu.create(menuArray, '', this.defineMenu(menuid, onopen));
}

HJ.Tree.prototype.defineMenu = function(menuid, onopen)
{
    // Convert a 'div' into a menu or create the div if it doesn't exist
    // a context menu will go into this div.
    
    var treeContextMenu = HJ.el(menuid);
    
    if (!treeContextMenu)
    {
        treeContextMenu = document.createElement('div');
        document.body.appendChild(treeContextMenu);
    }
    else
    {
        if (treeContextMenu.parentNode != document.body)
        {
            treeContextMenu.parentNode.removeChild(treeContextMenu);
            document.body.appendChild(treeContextMenu);
        }
    }
    treeContextMenu.id=menuid; // this id is used in the tree node opts.menuName
    treeContextMenu.style.position='absolute';
    treeContextMenu.style.visibility='hidden';
    HJ.Element.addClass(treeContextMenu, 'menu');
    
//    HJ.Element.registerHandler(treeContextMenu, 'mouseover', function(event) { HJ.Element.show(treeContextMenu); });
//    HJ.Element.registerHandler(treeContextMenu, 'mouseout', function(event) { HJ.Element.hide(treeContextMenu); });

    treeContextMenu.onopen = onopen || null;
    
    return treeContextMenu;
}

HJ.Tree.getTreeNode = function(elementOrId)
{
    var el = HJ.el(elementOrId);

    var treeNodeDiv = HJ.Element.getParentByTagAndClassName(el,'div','treeNode');
    return (treeNodeDiv ? treeNodeDiv.treeNode : null);
}

HJ.Tree.openContextMenu = function(event)
{
    HJ.Menu.closeMenu();  // close any open menus
    var treeNode = HJ.treeNode(event);
    if (!treeNode.menuName) { alert('menu name not set'); }
    var menu = HJ.el(treeNode.menuName);
    if (!menu) { alert("can't find menu " + treeNode.menuName); }
    menu.treeNode = treeNode;  // Associate the treeNode to menu

    if (menu.onopen)
    {
        menu.onopen.apply(null, [menu,treeNode]);
    }

    // Call the DHTML menu to initialize the menu
    HJ.Menu.buttonClick(event, menu.id);
    
    HJ.Element.setPosition(menu, HJ.Event.getMousePosition(event));
    HJ.Element.show(menu);

    return false; // to prevent an a link from poceeding
}

HJ.Tree.onDropOnTree = function(dropped, droppedOn)
{
    var droppedOnTreeNode = HJ.treeNode(droppedOn);
    droppedOnTreeNode.setSaving();

    var dropped = dropped.host || dropped;
    HJ.Element.addClass(dropped, 'disabled');
    
    var droppedTreeNode = HJ.treeNode(dropped);
    
    var dropDataObject = droppedTreeNode.dataObject;
    var droppedOnDataObject = droppedOnTreeNode.dataObject;
        
    var serverCall = new HJ.ServerCall();

    var key2 = (dropDataObject.key2!=null && dropDataObject.key2!=undefined ? (dropDataObject.key2.value || dropDataObject.key2) : '');
    var key3 = (dropDataObject.key3!=null && dropDataObject.key3!=undefined ? (dropDataObject.key3.value || dropDataObject.key3) : '');
    var key4 = (dropDataObject.key4!=null && dropDataObject.key4!=undefined ? (dropDataObject.key4.value || dropDataObject.key4) : '');
    var key5 = (dropDataObject.key5!=null && dropDataObject.key5!=undefined ? (dropDataObject.key5.value || dropDataObject.key5) : '');
                   
    var pkey2 = (droppedOnDataObject.key2!=null && droppedOnDataObject.key2!=undefined ? (droppedOnDataObject.key2.value || droppedOnDataObject.key2) : '');
    var pkey3 = (droppedOnDataObject.key3!=null && droppedOnDataObject.key3!=undefined ? (droppedOnDataObject.key3.value || droppedOnDataObject.key3) : '');
    var pkey4 = (droppedOnDataObject.key4!=null && droppedOnDataObject.key4!=undefined ? (droppedOnDataObject.key4.value || droppedOnDataObject.key4) : '');
    var pkey5 = (droppedOnDataObject.key5!=null && droppedOnDataObject.key5!=undefined ? (droppedOnDataObject.key5.value || droppedOnDataObject.key5) : '');
                   
    var postData = 'key1='+ (dropDataObject.key1.value || dropDataObject.key1) +
                   '&key2='+ key2+
                   '&key3='+ key3+
                   '&key4='+ key4+
                   '&key5='+ key5+
                   '&type='+ droppedTreeNode.dragDropClass +
                   '&parentKey1='+(droppedOnDataObject.key1.value||droppedOnDataObject.key1) +
                   '&parentKey2='+ pkey2+
                   '&parentKey3='+ pkey3+
                   '&parentKey4='+ pkey4+
                   '&parentKey5='+ pkey5+
                   '&parentType=' + droppedOnTreeNode.dragDropClass;
    
    var tree = droppedOnTreeNode.parentTree;
    
    serverCall.sendPost(tree.editorUrl, postData,
                    function(serverCall) { 
                        droppedOnTreeNode.setExpandImage();
                        
                        var updatedDataObject = serverCall.getResponseObject();
                        if (updatedDataObject == false || updatedDataObject.message)
                        {
                            tree.onDropFailure.apply(tree, [droppedTreeNode,updatedDataObject]);
                        }
                        else
                        {
                            if (updatedDataObject != true) // server might return true or object
                            {
                                droppedTreeNode.dataObject = updatedDataObject;
                            }
                            HJ.Tree.dropOnTree(dropped, droppedOn);
                            tree.onDropSuccess.apply(tree, [droppedTreeNode]);
                        }
                        HJ.Element.removeClass(dropped, 'disabled');
                    }
                    )
    
    return;
}

HJ.Tree.dropOnTree = function(dropped, dropZone)
{
    var droppedElement = (dropped.host ? dropped.host : dropped); // dropped.host means we dropped a ghost
    
    var droppedTreeNode = HJ.Tree.getTreeNode(droppedElement);
    var newParentTreeNode = HJ.Tree.getTreeNode(dropZone);
    
    if (droppedTreeNode == newParentTreeNode) {
        return; // can't drop on self
    }
    
    if (droppedTreeNode) {
        droppedTreeNode.remove();
        droppedTreeNode.parentTreeNode.render(true); // re-render the from tree node
    }
    else {
        // was not originally in the tree
        droppedElement.parentNode.removeChild(droppedElement);
    }
    
    newParentTreeNode.addNode(droppedTreeNode);
    
    // re-render the to tree node
    
    if (droppedTreeNode.isLeaf)
    {
        newParentTreeNode.render(true); // re-render
    }
    else
    {
        newParentTreeNode.render(true); // re-render
    }

}

HJ.Tree.onTreeMenuDelete = function(event)
{
    HJ.Menu.closeMenu();
    
    var menu = HJ.menu(event);
    var deletedTreeNode = menu.treeNode;
    
    return HJ.Tree.deleteTreeNode(deletedTreeNode);
}

HJ.Tree.deleteTreeNode = function(deletedTreeNode)
{
    if (!confirm('Are you sure you want to delete ' + deletedTreeNode.label + '?')) return false;
    
    deletedTreeNode.setSaving();
    HJ.Element.addClass(deletedTreeNode, 'disabled');
    
    var serverCall = new HJ.ServerCall();

    var key2 = (deletedTreeNode.dataObject.key2!=null && deletedTreeNode.dataObject.key2!=undefined ? (deletedTreeNode.dataObject.key2.value || deletedTreeNode.dataObject.key2) : '');
    var key3 = (deletedTreeNode.dataObject.key3!=null && deletedTreeNode.dataObject.key3!=undefined ? (deletedTreeNode.dataObject.key3.value || deletedTreeNode.dataObject.key3) : '');
    var key4 = (deletedTreeNode.dataObject.key4!=null && deletedTreeNode.dataObject.key4!=undefined ? (deletedTreeNode.dataObject.key4.value || deletedTreeNode.dataObject.key4) : '');
    var key5 = (deletedTreeNode.dataObject.key5!=null && deletedTreeNode.dataObject.key5!=undefined ? (deletedTreeNode.dataObject.key5.value || deletedTreeNode.dataObject.key5) : '');
    
    var postData = 'key1='+
                   (deletedTreeNode.dataObject.key1.value || deletedTreeNode.dataObject.key1) +
                   '&key2='+ key2+
                   '&key3='+ key3+
                   '&key4='+ key4+
                   '&key5='+ key5+
                   '&type='+ deletedTreeNode.dragDropClass +
                   '&mode=delete';
    
    var tree = deletedTreeNode.parentTree;
    
    serverCall.sendPost(tree.editorUrl, postData,
                    function(serverCall) { 
                        deletedTreeNode.setExpandImage();
                        
                        var responseObject = serverCall.getResponseObject();
                        
                        if (responseObject == true) // the jsp returns "true" or a failure message
                        {
                            deletedTreeNode.remove();
                            tree.onDeleteSuccess.apply(tree, [deletedTreeNode]);
                        }
                        else
                        {
                            tree.onDeleteFailure.apply(tree, [deletedTreeNode, responseObject]);
                        }
                    }
                    )
}

HJ.Tree.onTreeMenuAdd = function onTreeMenuAdd(event, addNodeType)
{
    HJ.Menu.closeMenu();
  
    var parentTreeNode = HJ.treeNode(event);
    HJ.Tree.addTreeNode(parentTreeNode, addNodeType);
}

HJ.Tree.addTreeNode = function(parentTreeNode, addNodeType)
{
    parentTreeNode.setSaving();
    HJ.Element.addClass(parentTreeNode, 'disabled');
    
    var serverCall = new HJ.ServerCall();
    
    var pkey2 = (parentTreeNode.dataObject.key2!=null && parentTreeNode.dataObject.key2!=undefined ? (parentTreeNode.dataObject.key2.value || parentTreeNode.dataObject.key2) : '');
    var pkey3 = (parentTreeNode.dataObject.key3!=null && parentTreeNode.dataObject.key3!=undefined ? (parentTreeNode.dataObject.key3.value || parentTreeNode.dataObject.key3) : '');
    var pkey4 = (parentTreeNode.dataObject.key4!=null && parentTreeNode.dataObject.key4!=undefined ? (parentTreeNode.dataObject.key4.value || parentTreeNode.dataObject.key4) : '');
    var pkey5 = (parentTreeNode.dataObject.key5!=null && parentTreeNode.dataObject.key5!=undefined ? (parentTreeNode.dataObject.key5.value || parentTreeNode.dataObject.key5) : '');
                   
    var postData = 'parentKey1=' + escape(parentTreeNode.dataObject.key1.value || parentTreeNode.dataObject.key1) +
                   '&parentKey2='+ pkey2+
                   '&parentKey3='+ pkey3+
                   '&parentKey4='+ pkey4+
                   '&parentKey5='+ pkey5+
                   '&parentType='+ parentTreeNode.dragDropClass +
                   '&desc='+ escape('New') + '&mode=add';
                   
    if (addNodeType) postData += '&type=' + addNodeType;
                   
    var tree = parentTreeNode.parentTree;
    
    serverCall.sendPost(tree.editorUrl, postData,
                    function(serverCall) { 
                        // The server returns a javascript dataobject that represents the new
                        // add node.  Returns false if not successful.
                        
                        var newDataObject = serverCall.getResponseObject();
                        if (newDataObject && newDataObject.key1)
                        {
                            var key=newDataObject.key1.value || newDataObject.key1;
                            var desc=newDataObject.desc.value || newDataObject.desc;
                            
                            var opts = tree.getNodeOptions(addNodeType);
                            
                            var newTreeNode = parentTreeNode.addNode(key, desc, opts);
                            newTreeNode.setDataObject(newDataObject);
                            parentTreeNode.expand();
                            parentTreeNode.renderChildren();
                            tree.onAddSuccess.apply(tree, [newTreeNode]);
                        }
                        else
                        {
                            tree.onAddFailure.apply(tree, [newDataObject]);
                        }
                        parentTreeNode.setExpandImage();
                    }
                    )
}



/*
* Constructor Function
*/
HJ.TreeNode = function(id, label, opts, isLeaf)
{
    this.id = id;
    this.label = label;

    var opts = (opts ? opts : {});

    this.isLeaf = (isLeaf || opts.isLeaf ? true : false);
    
    this.menuName       = opts.menuName || null;
    this.onclick        = opts.onclick || null;  // one one of menuName and onclick should be set
    
    this.collapsedImage = opts.collapsedImage || HJ.Config.RESOURCE_PATH + (this.isLeaf ? HJ.Tree.Styles.DEFAULT.DocImage : HJ.Tree.Styles.DEFAULT.ClosedFolderImage);
    this.expandedImage  = opts.expandedImage  || HJ.Config.RESOURCE_PATH + (this.isLeaf ? HJ.Tree.Styles.DEFAULT.DocImage : HJ.Tree.Styles.DEFAULT.OpenFolderImage);
    
    this.draggable = (opts.dragDropClass ? true : false); 
    if (opts.draggable == true)
    {
        this.draggable = true;
    }
    if (opts.draggable == false)
    {
        this.draggable = false;
    }
    
    this.expanded       = opts.expanded || false;
    this.dragDropClass  = opts.dragDropClass || (this.isLeaf?'leaf':'folder');
    
    this.parentTree = null;
    this.parentTreeNode = null;
    this.level = 0;
    this.domElement = null;
    
    this.dataObject = {id: id }; // default the DOM id to be the same as data id.
                                 // The data id is reset when setDataObject is called
    
    this.nodes = new Array(); // children nodes
}

HJ.Tree.createNode = function(id, label, opts)
{
    return new HJ.TreeNode(id, label, opts, false);
}

HJ.Tree.createLeafNode = function(id, label, opts)
{
    return new HJ.TreeNode(id, label, opts, true);
}

HJ.TreeNode.prototype.setDataObject = function(dataObj) { this.dataObject = dataObj; }

HJ.TreeNode.prototype.getQueryParameters = function()
{
    var s = '';
    for (var attr in this.dataObject) { s += attr + '=' + escape(this.dataObject[attr]) + '&'; }
    return s;
}

HJ.TreeNode.prototype.getPostData = function()
{
    var s = '';
    for (var attr in this.dataObject) { s += attr + '=' + escape(this.dataObject[attr]) + '&'; }
    return s;
}

HJ.TreeNode.prototype.addNode = function(treeNodeOrId, label, opts, isLeaf)
{
    var treeNode = null;
    if (arguments.length == 1) // Called adding a node
    {
        treeNode = treeNodeOrId;
    }
    else
    {
        treeNode = new HJ.TreeNode(treeNodeOrId, label, opts, (isLeaf?true:false) );
    }
    
    treeNode.parentTree = this.parentTree;
    treeNode.parentTreeNode = this;
    treeNode.setLevel();
   
    this.nodes.push(treeNode);
    
    return treeNode;
}

HJ.TreeNode.prototype.setLevel = function()
{
    this.level = this.parentTreeNode.level + 1;
    
    if (!this.isLeaf && this.nodes && this.nodes.length > 0)
    {
        HJ.Util.forEach(this.nodes, function(node) {node.setLevel();});
    }
}

HJ.TreeNode.prototype.addLeafNode = function(treeNodeOrId, label, opts)
{
    return this.addNode(treeNodeOrId, label, opts, true);
}

HJ.TreeNode.prototype.remove = function(treeNode)
{
    if (treeNode)
    {
        var newChildren = new Array();
        for (var i=0; i<this.nodes.length; i++)
        {
            if (treeNode.id == this.nodes[i].id)
            {
                this.domElement.removeChild(treeNode.domElement);
                continue;
            }
            newChildren.push(this.nodes[i]);
        }
        this.nodes = newChildren;
        this.renderChildren();
        return;
    }
    // Remove self from parent
    this.parentTreeNode.remove(this);
    this.parentTreeNode.setExpandImage();
    
}

HJ.TreeNode.prototype.isLast = function(treeNode)
{
    if (treeNode)
    {
        if (!this.parentTreeNode) return true; // root node
        var siblings = this.parentTreeNode.nodes;
        return (treeNode.id == siblings[siblings.length-1].id);
    }
    return this.isLast(this);
}

HJ.TreeNode.prototype.getParentTreeNode = function(levelsup)
{
    var parentNode = this.parentTreeNode;
    
    if (levelsup == 1) return parentNode;
    
    for (var i=1; i < levelsup; i++)
    {
        parentNode = parentNode.parentTreeNode;
    }
    return parentNode;
}

HJ.TreeNode.prototype.reRender = function()
{
    this.render(true);
}

HJ.TreeNode.prototype.render = function(redraw)
{
    if (!this.domElement || !redraw)
    {
        this.domElement = document.createElement('div');
        this.domElement.id = this.id;
        
        HJ.Element.addClass(this.domElement, 'treeNode');
    
        this.domElement.treeNode = this;
        
        if (!this.parentTreeNode)
        {
            // It is the root node
            this.parentTree.domElement.appendChild(this.domElement);
        }
        else
        {
            this.parentTreeNode.domElement.appendChild(this.domElement);
        }
    }

    var s =
        '<table border="0" cellpadding="0" cellspacing="0">' +
        '<tr>';
    
    for (var i=0; i < this.level; i++)
    {
        s += '<td>';
        
        if (!this.getParentTreeNode(this.level-i).isLast())
        {
            s += '<img src="' + HJ.Config.RESOURCE_PATH + this.parentTree.treeStyle.VerticalLineImage + '">';
        }
        else
        {
            s += '<img src="' + HJ.Config.RESOURCE_PATH + this.parentTree.treeStyle.NodeBlankImage + '">';
        }
        s += '</td>';
    }
    
    s +='<td><img src="';
    
    if (this.nodes.length == 0 ) this.expanded=false;
    
    if (this.isLast())
    {
        s += HJ.Config.RESOURCE_PATH + (this.nodes.length==0 ? this.parentTree.treeStyle.NodeLastImage : (this.expanded ? this.parentTree.treeStyle.OpenNodeLastImage : this.parentTree.treeStyle.ClosedNodeLastImage));
    }
    else
    {
        s += HJ.Config.RESOURCE_PATH + (this.nodes.length==0 ? this.parentTree.treeStyle.NodeEmptyImage : (this.expanded ? this.parentTree.treeStyle.OpenNodeImage : this.parentTree.treeStyle.ClosedNodeImage));
    }
    
    s += '" class="expand"';
    s += '" border="0"></td>';
    
    s += '<td>';
    
    if (this.draggable)
    {
        s +=
           '<span class="' + this.dragDropClass + ' dragHandle draggable dragGhost">' +
           '<table border="0" cellpadding="0" cellspacing="0">' +
           '<tr><td><img class="treeNodeImage" src="' + ((!redraw || !this.expanded)?this.collapsedImage:this.expandedImage) + '" border="0"></td>' +
           '<td><td nowrap="nowrap" style="white-space:nowrap;"><a href="#" class="' + (this.isLeaf?'treeLeaf':'treeFolder') + '">' + this.label + '</a></td></tr>' +
           '</table></span>';
    }
    else
    {
        s +=
           '<table border="0" cellpadding="0" cellspacing="0">' +
           '<tr><td><img class="treeNodeImage" src="' + ((!redraw || !this.expanded)?this.collapsedImage:this.expandedImage) + '" border="0"></td>' +
           '<td><td nowrap="nowrap" style="white-space:nowrap;"><a href="#" class="' + (this.isLeaf?'treeLeaf':'treeFolder') + '">' + this.label + '</a></td></tr>' +
           '</table>';
    }
    
    s += '</td></tr></table>';
        
    this.domElement.innerHTML = s;
    
    if (this.menuName)
    {
        var aTag = HJ.Element.getElementsByTagName('a',this.domElement)[0];
        HJ.Event.registerHandler(aTag, 'mouseup', HJ.Tree.openContextMenu);
        if (HJ.Browser.isFF)
        {
            HJ.Event.registerHandler(aTag, 'mousedown', HJ.Tree.openContextMenu);  // required for Firefox
        }            
        HJ.Event.registerHandler(aTag, 'click', HJ.Constants.Functions.RETURN_FALSE);  // prevent href follow through
        // disable the menu's default right click context menu (IE only)
        HJ.Event.registerHandler(aTag, 'contextmenu', HJ.Constants.Functions.RETURN_FALSE);
    }
    else if (this.onclick)
    {
        var aTag = HJ.Element.getElementsByTagName('a',this.domElement)[0];
        HJ.Event.registerHandler(aTag, 'click', this.onclick);
    }
    var expandLink = HJ.Element.getElementsByClassName('expand', this.domElement);
    
    if (expandLink && expandLink.length > 0) {
        HJ.Event.registerHandler(expandLink[0], 'click', this.toggleExpand, this);
        if (!this.menuName && !this.onclick)
        {
            // If theres no menu and no onclick, then expand the element
            var aTag = HJ.Element.getElementsByTagName('a',this.domElement)[0];
            HJ.Event.registerHandler(aTag, 'click', this.toggleExpand, this);
        }
    }

//    if (!this.isLeaf) {
//        alert('expanded=' + this.expanded + ' children='+this.nodes.length);
//    }
    
    if (this.expanded)
    {
        for (var i = 0; i < this.nodes.length; i++)
        {
            this.nodes[i].render();
        }
    }    
}

HJ.TreeNode.prototype.renderChildren = function()
{
    // remove all treeNodes
    var treeNodeDivs = HJ.Element.getElementsByClassName('treeNode',this.domElement);
    for (var i=0; i<treeNodeDivs.length; i++)
    {
        treeNodeDivs[i].parentNode.removeChild(treeNodeDivs[i]);
    }

    if (this.expanded)
    {
        for (var i = 0; i < this.nodes.length; i++)
        {
            this.nodes[i].render();
        }
    }
}

HJ.TreeNode.prototype.isExpanded = function()
{
    return this.expanded;
}    

HJ.TreeNode.prototype.expand = function() {
    HJ.Element.removeClass(this.domElement, 'collapsed');
    HJ.Element.addClass(this.domElement, 'expanded');
  
    this.expanded = true;
    
    var theTreeNodeImg = HJ.Element.getFirstElementByClassName('treeNodeImage', this.domElement);
    if (theTreeNodeImg && this.expandedImage && this.nodes.length > 0) theTreeNodeImg.src = this.expandedImage;
}

HJ.TreeNode.prototype.collapse = function() {
    HJ.Element.removeClass(this.domElement, 'expanded');
    HJ.Element.addClass(this.domElement, 'collapsed');

    this.expanded = false;
    
    var theTreeNodeImg = HJ.Element.getFirstElementByClassName('treeNodeImage', this.domElement);
    if (theTreeNodeImg) theTreeNodeImg.src = this.collapsedImage;
}

HJ.TreeNode.prototype.toggleExpand = function(event)
{
    var theImg = HJ.Event.getTargetElement(event);
    var theDomNode = HJ.Event.getTargetElementByTagAndClassName(event,'div','treeNode');
    var theTreeNode = theDomNode.treeNode;
    
    if (theTreeNode.isExpanded())
    {
        theTreeNode.collapse();
    }
    else
    {
        theTreeNode.expand();
    }
    
    this.setExpandImage();
    
    for(var i=0; i < theTreeNode.nodes.length; i++)
    {
        var child = theTreeNode.nodes[i];
        if (theTreeNode.isExpanded())
        {
            if (child.rendered)
            {
                HJ.Element.show(child.domElement);
                child.domElement.style.display='block';
            }
            else
            {
                // render the children
                child.render();
            }
        }
        else
        {
            // hide children
            child.saveHTML = child.domElement.innerHTML;
            HJ.Element.hide(child.domElement);
            
            child.domElement.style.overflow='hidden';
            child.domElement.style.display='none';
            child.rendered=true;
        }
    }
}

HJ.TreeNode.prototype.setWaiting = function()
{
    var img = HJ.Element.getFirstElementByClassName("expand", this.domElement);
    img.src=HJ.Config.RESOURCE_PATH + this.parentTree.treeStyle.WaitingImage;
}

HJ.TreeNode.prototype.setSaving = function()
{
    var img = HJ.Element.getFirstElementByClassName("expand", this.domElement);
    img.src=HJ.Config.RESOURCE_PATH + this.parentTree.treeStyle.SavingImage;
}

HJ.TreeNode.prototype.setExpandImage = function()
{
    var img = HJ.Element.getFirstElementByClassName("expand", this.domElement);

    if (this.nodes.length == 0)
    {
       img.src = HJ.Config.RESOURCE_PATH + (this.isLast() ? this.parentTree.treeStyle.NodeLastImage : this.parentTree.treeStyle.NodeImage);
    }
    else if (this.expanded)
    {
       img.src = HJ.Config.RESOURCE_PATH + (this.isLast() ? this.parentTree.treeStyle.OpenNodeLastImage : this.parentTree.treeStyle.OpenNodeImage);
    }
    else // not expanded
    {
       img.src = HJ.Config.RESOURCE_PATH + (this.isLast() ? this.parentTree.treeStyle.ClosedNodeLastImage: this.parentTree.treeStyle.ClosedNodeImage);
    }
}

//---------- HJ.List: List widget ----------

HJ.List.enable = function(root)
{
    var rootEl = (root ? HJ.el(root) : document);
    var lists = HJ.Element.getElementsByTagAndClassName('ol','orderedList',rootEl);
    
    HJ.Util.forEach(lists, function(list) { HJ.List.makeReorderable(list); }); 

    lists = HJ.Element.getElementsByTagAndClassName('ol','addableList',rootEl);
    
    HJ.Util.forEach(lists, function(list) { HJ.List.makeAddable(list); }); 
}

HJ.List.makeAddable = function(list, serverUrl)
{
    list = HJ.el(list);

    if (serverUrl)
    {
        list.serverUrlAdd = serverUrl;
    }        

    if (list.isAddable) { return; } // already addable
    
    // Add handlers to the 3 possible links
    var aAddMore = HJ.Element.getElementsByClassName('listAddMore',list)[0]; 
    var aAddSave = HJ.Element.getElementsByClassName('listAddSave',list)[0];
    var aAddDone = HJ.Element.getElementsByClassName('listAddDone',list)[0];

    if (aAddMore) HJ.Event.registerHandler(aAddMore, 'click', function(event) {
            HJ.List.listAddMore(aAddMore);
        } );
    if (aAddSave) HJ.Event.registerHandler(aAddSave, 'click', function(event) {
            HJ.List.listAddSave(aAddSave);
        } );
    if (aAddDone) HJ.Event.registerHandler(aAddDone, 'click', function(event) {
            HJ.List.listAddDone(aAddDone);
        } );

    if (aAddMore || aAddSave || aAddDone)
    {
        // Check for a form for submitting the add
        var form = HJ.form(list);
        if (form)
        {
            HJ.Event.registerHandler(form, 'submit', function(event) {
                HJ.List.listAddSave(aAddSave); HJ.Event.stop(event);return false;
                } );
        }   
    }
            
    list.isAddable = true;
}

HJ.List.listAddMore = function(clickedEl)
{
    HJ.Element.toggleVisibilityCollapse(clickedEl);
    var el=clickedEl;
    HJ.Element.toggleCollapseNextSibling(clickedEl);

    HJ.form(clickedEl).getElementsByTagName('input')[0].focus(); // TODO - first text field (not hidden fields)
}

HJ.List.listAddSave = function(clickedEl)
{

    var theOl = HJ.Element.getParentByTagName('ol',clickedEl);
    var thisDiv = HJ.Element.getParentByTagName('div',clickedEl);
    var newLi = document.createElement('li');
    var form = HJ.form(clickedEl);
    var formEl = form.getElementsByTagName('input')[0]; // TODO - first text field (not hidden fields)
    
    if (HJ.Util.trimWhiteSpace(formEl.value) == '') return false; // TODO - trimWhitespace
    
   if (theOl.serverUrlAdd)
   {
       var newVal = formEl.value;
       var serverCall = new HJ.ServerCall();
       HJ.Form.extend(form);
       form.disable();
       serverCall.sendPost(theOl.serverUrlAdd, 
                '&add=' + formEl.value,
                function(serverCall)
                {
                    var text = document.createTextNode(newVal);
                    newLi.appendChild(text);
                    theOl.insertBefore(newLi, thisDiv);
                    HJ.List.makeReorderListItem(newLi);
                    
                    // Update the li values 
                    var lis = HJ.Element.getElementsByTagName('li', theOl);
                    for (var i=0; i < lis.length; i++)
                    {
                        lis[i].value = i+1;
                    }
                                        
                    form.enable();
                    formEl.value='';
                    formEl.focus();
                    if (theOl.onAddSuccess) //TODO change to add success
                    {
                        theOl.onAddSuccess.apply(null, [theOl]);           
                    }
                });
    }
    else
    {
        var text = document.createTextNode(formEl.value);
        newLi.appendChild(text);
        
        HJ.List.makeReorderListItem(newLi);
        theOl.insertBefore(newLi, thisDiv);

        // Update the li values 
        var lis = HJ.Element.getElementsByTagName('li', theOl);
        for (var i=0; i < lis.length; i++)
        {
            lis[i].value = i+1;
        }
        
        formEl.value='';
        formEl.focus();
    }
}

HJ.List.listAddDone = function(clickedEl)
{
    var theDiv = HJ.Element.getParentByTagName('div',clickedEl);
    HJ.Element.toggleVisibilityCollapse(HJ.Element.getFirstElementByClassName('listAddMore',theDiv));
    HJ.Element.toggleVisibilityCollapse(HJ.Element.getParentByClassName(clickedEl, 'addListItem'));
}

HJ.List.makeReorderable = function(list, serverUrl)
{
    list = HJ.el(list);
    
    if (list.isReorderable) { return; } // already reorderable
    
    var dragDrop = HJ.DragDrop.enable();
    
    if (!HJ.Element.isClass('orderedList')) { HJ.Element.addClass(list, 'orderedList'); }
    
    var lis = HJ.Element.getElementsByTagName('li', list);

    for (var i=0; i<lis.length; i++)
    {
         if (lis[i].parentNode != list) { continue; } // direct children only
         HJ.List.makeReorderListItem(lis[i]);
    }

    //TODO  Don't allow the draggable to be dropped on another list
    //TODO  Only Allow vertical dragging up to the top and bottom of list
    
    if (!HJ.List.regDDHandler) // prevent registering more than once !!!
                               // TODO modify the dd registerDropHandler to not care
    {
        dragDrop.registerDropHandler('orderedListItem', 'orderedListItem', HJ.List.reorderListHandler );
        HJ.List.regDDHandler = true;
    }
    
    if (list.reorderDragDropClass)
    {
        dragDrop.registerDropHandler(list.reorderDragDropClass, list.reorderDragDropClass, HJ.List.reorderListHandler );
    }

    if (serverUrl)
    {
        list.serverUrlReorder = serverUrl;
    }        
    list.isReorderable = true;
}

HJ.List.makeReorderListItem = function(li)
{
     if (HJ.Element.isClass(li, 'addListItem')) { return; }
     if (li.parentNode.reorderDragDropClass)
     {
         if (!HJ.Element.isClass(li, li.parentNode.reorderDragDropClass))
         {
            HJ.Element.addClass(li, li.parentNode.reorderDragDropClass);
         }
     }
     else if (!HJ.Element.isClass(li, 'orderedListItem'))
     {
        HJ.Element.addClass(li, 'orderedListItem');
     }
     if (!HJ.Element.isClass(li, 'draggable')) { HJ.Element.addClass(li, 'draggable'); }
     if (!HJ.Element.isClass(li, 'dragGhost')) { HJ.Element.addClass(li, 'dragGhost'); }

     if (!li.reorderMouseover)
     {   
         HJ.Event.registerHandler(li, 'mouseover', function(event) {
                    if (HJ.DragDropHandler.isDragging()) return;
                    var li = HJ.Event.getTargetElement(event);
                    HJ.Element.addClass(li,'orderedListItemHover');
                });  
        li.reorderMouseover=true;
     }

     if (!li.reorderMouseout)
     {   
        HJ.Event.registerHandler(li, 'mouseout', function(event) {
                    var li = HJ.Event.getTargetElement(event);
                    HJ.Element.removeClass(li,'orderedListItemHover');
                });  
        li.reorderMouseout=true;
     }
     
}

HJ.List.reorderListHandler = function(droppedLi, droppedOnLi)
{
   var droppedLi = droppedLi.host || droppedLi;
   var orderedList = droppedLi.parentNode;

   if (droppedLi == droppedOnLi) { return; }
   
   var allLis = HJ.Element.getElementsByTagName('li', orderedList);
   var lis = new Array();
    
   for (var i=0; i < allLis.length; i++)
   {
     if (allLis[i].parentNode != orderedList) { continue; } // direct children only
     lis.push(allLis[i]);
   }
    
   for (var i=0; i < lis.length; i++)
   {
        lis[i].value = i+1;
   }

   if (orderedList.serverUrlReorder)
   {
       if (!confirm('Save reorder')) { return; }
   
       var serverCall = new HJ.ServerCall();
       serverCall.sendPost(orderedList.serverUrlReorder, 
                '&old=' + droppedLi.value + '&new=' + droppedOnLi.value,
                function(serverCall)
                {
                    HJ.List.reorderList(orderedList, droppedLi, droppedOnLi);
                    if (orderedList.onReorderSuccess)
                    {
                        orderedList.onReorderSuccess.apply(null, [orderedList]);
                        
                        //Fire a reorder event on the dropped
                        HJ.Event.fire(droppedLi, "reorder", {customTarget: droppedLi });
                        //Fire a reorder event on the list
                        HJ.Event.fire(orderedList, "reorder", {customTarget: orderedList});
                    }
                });
    }
    else
    {
        HJ.List.reorderList(orderedList, droppedLi, droppedOnLi);
        
        //Fire a reorder event on the dropped
        HJ.Event.fire(droppedLi, "reorder", {customTarget: droppedLi });
        //Fire a reorder event on the list
        HJ.Event.fire(orderedList, "reorder", {customTarget: orderedList});
    }
    
}

HJ.List.reorderList = function(orderedList, droppedLi, droppedOnLi)
{
    // for now, ensure the lists are the same
    if (droppedLi.parentNode != droppedOnLi.parentNode) { alert('dropping on different list not allowed'); return; }

    orderedList.removeChild(droppedLi);
    orderedList.insertBefore(droppedLi, droppedOnLi);

   var allLis = HJ.Element.getElementsByTagName('li', orderedList);
   var lis = new Array();
    
   for (var i=0; i < allLis.length; i++)
   {
     if (allLis[i].parentNode != orderedList) { continue; } // direct children only
     lis.push(allLis[i]);
   }


    for (var i=0; i < lis.length; i++)
    {
        lis[i].value = i+1;
    }
}

HJ.List.setReorderCallBacks = function(list, successFunc)
{
    if (successFunc) list.onReorderSuccess = successFunc;
}
/* Not finished 
HJ.List.openContextMenu = function(event)
{
    HJ.Menu.closeMenu();  // close any open menus
    var li = HJ.Event.getTargetElement(event);
    
    var ol = HJ.Event.getTargetElementByTagName(event,'ol');
    
    // get the menu (on the ol)
    
    if (!ol.menu) return;
    
    var menu =  ol.menu;

    if (menu.onopen)
    {
        menu.onopen.apply(null, [menu,li]);
    }

    // Call the DHTML menu to initialize the menu
    HJ.Menu.buttonClick(event, menu.id);
    
    HJ.Element.setPosition(menu, HJ.Event.getMousePosition(event));
    HJ.Element.show(menu);

    return false; // to prevent an a link from poceeding
}
*/

//---------- HJ.Container: Resizeable UI containers ----------

/**
* Fill the div height to the bottom of the browswer window
*/
HJ.Container.initialize = function(theDivContainer, event)
{
    theDivContainer = HJ.el(theDivContainer);
    
    // I turn padding off because it causes FF and Operat to break.  If you want
    // padding, include another interior div that is not a  container.
    theDivContainer.style.paddingLeft = "0px";
    theDivContainer.style.paddingRight = "0px";
    theDivContainer.style.paddingTop = "0px";
    theDivContainer.style.paddingBottom = "0px";

    if (HJ.Element.isClass(theDivContainer.parentNode, 'container'))
    {
    
        // Set the top/left position of the element
        var previousSiblingContainer = HJ.Element.getPreviousSiblingByTagAndClassName(theDivContainer, 'div', 'container');
        
        // Skip over hidden ones because top and left are always 0      
        while(previousSiblingContainer && previousSiblingContainer.style.display == 'none')
        {
            previousSiblingContainer = HJ.Element.getPreviousSiblingByTagAndClassName(previousSiblingContainer, 'div', 'container');
        }
        
        if (previousSiblingContainer && 
            ((HJ.Element.isClass(previousSiblingContainer, "fillbottom")
              && !HJ.Element.isClass(previousSiblingContainer, "fillright"))
               || HJ.Element.isClass(theDivContainer, "right")))
        {
            // Place this container at the top right corner of the previous
            
            theDivContainer.style.left = (previousSiblingContainer.offsetWidth + previousSiblingContainer.offsetLeft + 0) + 'px';
        }
        else
        if (previousSiblingContainer)
        {
        
//          alert(theDivContainer.innerHTML);
//          alert('left:' + previousSiblingContainer.offsetLeft + " top:" + (previousSiblingContainer.offsetTop + previousSiblingContainer.offsetHeight + 0));
        
            if (previousSiblingContainer.offsetLeft == 0 && (previousSiblingContainer.offsetTop + previousSiblingContainer.offsetHeight)==0)
            {
//              alert('prev sibling ' + previousSiblingContainer.style.display + ' ' +previousSiblingContainer.innerHTML);
            }
        
            // Place this container at the lower left right corner of the previous
            theDivContainer.style.left = previousSiblingContainer.offsetLeft + 'px';
            theDivContainer.style.top = (previousSiblingContainer.offsetTop + previousSiblingContainer.offsetHeight + 0) + 'px';
        }
        
        // TODO - not currently supporting a fill right with a fixed width container on the right.
    }
    
    if (HJ.Element.isClass(theDivContainer, 'fillbottom'))
    {
        HJ.Container.fillHeightToParent(theDivContainer);
    }
    
    if (HJ.Element.isClass(theDivContainer, 'fillright'))
    {
        HJ.Container.fillWidthToParent(theDivContainer);
    }

    HJ.Util.forEach(theDivContainer.childNodes, function(child) {
            if (!HJ.Element.isClass(child, 'container')) return;
            HJ.Container.initialize(child);
        });  
}

HJ.Container.fillHeightToParent = function(theDiv)
{
    if (!HJ.Element.isClass(theDiv.parentNode, 'container'))
    {
        // Top level container, size to the browser window
        var realDivHt = theDiv.offsetHeight;  // IE, no border FF+Opera with borders
        
        var browserWinHeight = HJ.Element.getBrowserWindowHeight();
        var newHeight = browserWinHeight-theDiv.offsetTop;
        
        //if (!HJ.Browser.isIE)
        {
            var topAndBottomBorders = HJ.Element.getTopAndBottomBorderWidth(theDiv);
            newHeight -= (topAndBottomBorders);
        }
        
        theDiv.style.height=newHeight+'px';
        return;
    }
    
    var parentHeight = theDiv.parentNode.offsetHeight;
    var newHeight = parentHeight;
    
    var currentTop = HJ.Util.getInteger(theDiv.style.top);

//    if (!HJ.Browser.isIE)
    {
        var topAndBottomBorders = HJ.Element.getTopAndBottomBorderWidth(theDiv);        
        newHeight -= (topAndBottomBorders);
    }

    var parentTopAndBottomBorders = theDiv.parentNode.offsetHeight-theDiv.parentNode.clientHeight;
    var myTopAndBottomBorders = theDiv.offsetHeight-theDiv.clientHeight

    // Get the height of any sibling containers and subtract their height
    
    if (HJ.Element.isClass(theDiv, 'fillright'))
    {
        // check for fixed width divs below
        var nextSibling = theDiv.nextSibling;
        var nextHt = 0;
        while (nextSibling)
        {
            if (HJ.Element.isClass(nextSibling, 'container')
                && !HJ.Element.isClass(nextSibling, 'right')
                && (nextSibling.style.display != 'none'))
            {
                nextHt += nextSibling.offsetHeight;
            }
            nextSibling = nextSibling.nextSibling;
        }
        newHeight -= nextHt;
    }
        
    newHeight -= (parentTopAndBottomBorders); 
    newHeight -=  theDiv.offsetTop;
    
    if (newHeight < 0) newHeight = 0;
    theDiv.style.height= newHeight + 'px';
}

HJ.Container.fillWidthToParent = function(theDiv)
{
    if (!HJ.Element.isClass(theDiv.parentNode, 'container'))
    {
        return;
    }
    var parentWidth = theDiv.parentNode.offsetWidth;
    var newWidth = parentWidth - theDiv.offsetLeft;

    //if (!HJ.Browser.isIE)
    {
        var leftAndRightBorders = HJ.Element.getLeftAndRightBorderWidth(theDiv);        
        newWidth -= (leftAndRightBorders); 
    }

    if (HJ.Element.isClass(theDiv, 'fillbottom'))
    {
        // check for fixed width divs to the right
        var nextSibling = theDiv.nextSibling;
        var nextWidth = 0;
        while (nextSibling)
        {
            if (HJ.Element.isClass(nextSibling, 'container')  && HJ.Element.isClass(nextSibling, 'fillbottom'))
            {
                nextWidth += nextSibling.offsetWidth;
            }
            nextSibling = nextSibling.nextSibling;
        }
        newWidth -= nextWidth;
    }

    var parentLeftAndRightBorders = HJ.Element.getLeftAndRightBorderWidth(theDiv.parentNode);
    
    newWidth -= (parentLeftAndRightBorders); 

    if (newWidth < 0) newWidth = 0;
    theDiv.style.width=newWidth + 'px';
}




// External JavaScript Functions: Helper functions from other sites

// The purge() method was taken from  http://javascript.crockford.com/memory/leak.html
// Call purge() before setting innerHTML or calling removeChild to avoid memory leaks in IE.
function purge(d) {
    var a = d.attributes, i, l, n;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            n = a[i].name;
            if (typeof d[n] === 'function') {
                d[n] = null;            
            }
        }
     }
     a = d.childNodes;
     if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            purge(d.childNodes[i]);
        }
     }
}
// END code from http://javascript.crockford.com/memory/leak.html

// Code from json.org

/*
    json.js
    2006-04-28

    This file adds these methods to JavaScript:

        object.toJSONString()

            This method produces a JSON text from an object. The
            object must not contain any cyclical references.

        array.toJSONString()

            This method produces a JSON text from an array. The
            array must not contain any cyclical references.

        string.parseJSON()

            This method parses a JSON text to produce an object or
            array. It will return false if there is an error.
*/
/*
(function () {
    var m = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        s = {
            array: function (x) {
                var a = ['['], b, f, i, l = x.length, v;
                for (i = 0; i < l; i += 1) {
                    v = x[i];
                    f = s[typeof v];
                    if (f) {
                        v = f(v);
                        if (typeof v == 'string') {
                            if (b) {
                                a[a.length] = ',';
                            }
                            a[a.length] = v;
                            b = true;
                        }
                    }
                }
                a[a.length] = ']';
                return a.join('');
            },
            'boolean': function (x) {
                return String(x);
            },
            'null': function (x) {
                return "null";
            },
            number: function (x) {
                return isFinite(x) ? String(x) : 'null';
            },
            object: function (x) {
                if (x) {
                    if (x instanceof Array) {
                        return s.array(x);
                    }
                    var a = ['{'], b, f, i, v;
                    for (i in x) {
                        v = x[i];
                        f = s[typeof v];
                        if (f) {
                            v = f(v);
                            if (typeof v == 'string') {
                                if (b) {
                                    a[a.length] = ',';
                                }
                                a.push(s.string(i), ':', v);
                                b = true;
                            }
                        }
                    }
                    a[a.length] = '}';
                    return a.join('');
                }
                return 'null';
            },
            string: function (x) {
                if (/["\\\x00-\x1f]/.test(x)) {
                    x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                        var c = m[b];
                        if (c) {
                            return c;
                        }
                        c = b.charCodeAt();
                        return '\\u00' +
                            Math.floor(c / 16).toString(16) +
                            (c % 16).toString(16);
                    });
                }
                return '"' + x + '"';
            }
        };

    Object.prototype.toJSONString = function () {
        return s.object(this);
    };

    Array.prototype.toJSONString = function () {
        return s.array(this);
    };
})();
*/

String.prototype.parseJSON = function () {
    try {
        return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
                this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
            eval('(' + this + ')');
    } catch (e) {
        return false;
    }
};

// END code from json.org
        



//------  Support for Backward Compatibility

function registerRequiredField(element, name) {return HJ.Form.makeFieldRequired(element, name);}
function setFieldColor(field, color) {return HJ.Form.setFieldColor(field,color);}
function validate() { if (!document.forms[0].validate) { HJ.Form.extend(document.forms[0]); } return document.forms[0].validate(); }
function validateRequiredFields(requiredFields) { if (!document.forms[0].validate) { HJ.Form.extend(document.forms[0]); } return HJ.Form.validateRequiredFields(requiredFields); }

function addCss(the_element, newClassName) { HJ.Element.addClass(the_element, newClassName); }
function restoreCss(the_element) { return HJ.Element.removeClass(the_element); }

function showElement(elementOrId, vis) {if (vis) { return HJ.Element.show(elementOrId, vis); } else { return HJ.Element.hide(elementOrId, vis); }}
function toggleElementVisibility(elementOrId) { return HJ.Element.toggleVisibility(elementOrId); }