﻿/**********************************************************************
* This contains the general utility functions used throughout the site 
*
**********************************************************************/
var iScrollLimit = 1; //the point at which we are at the end of a scroll bar and need new data
var bShiftPressed = new Boolean(false); //whether or not the shift key is pressed
var sHeadDesignator = "_Header";
var sFootDesignator = "_Footer";
var iMessageTimeout = 0;
var iDynamicSelectTimeout = 0;
var recentEvent;//part of a global event handler

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ Page setup tools
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/

function docLoad() {
    /***********************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/15/09
    * 
    * Sets the event handlers for the document
    ***********************************************************/
    //document.onmousemove = docMove
    setEvents(document);
    document.onkeydown = function(event) { if ((!event ? window.event : event).keyCode == 16) bShiftPressed = true; }
    document.onkeyup = function(event) { if ((!event ? window.event : event).keyCode == 16) bShiftPressed = false; }
    buildPrototypes();
//    document.onfocus = function(e) { setGlobalEvent(e); }
//    document.onchange = function(e) { setGlobalEvent(e); }
//    document.onblur = function(e) { setGlobalEvent(e); }
//    document.onclick = function(e) { setGlobalEvent(e); }
    //document.body.onunload = docUnload;
}

//function setGlobalEvent(e) {
//    recentEvent = (!e ? window.event : e);
//    //innerText(document.getElementById("testme"), SourceElement(e).tagName);
//}

function buildPrototypes() {
    if (!String.prototype.findDotNet) {//only run this if they have not been set
        String.prototype.findDotNet = findDotNetElement; //takes one arg - tagname e.g. oElem='elemname'.findDotNet('select')
        String.prototype.trim = trimString; //e.g. sText=sText.trim()
        String.prototype.bz = stringBlankToZero; //e.g. sText=sText.bz('newstring')
        String.prototype.path = analyzePath; //e.g. sText=sText.path('c:\test.jpg').fileName
    }
}

function setEvents(oSet) {
    //set all our doc events
    //setDivEvents(oSet)
    //setInputEvents(oSet)
    //prepSchedulePage()
    alignTableHeaders();
}




//function setInputEvents(oSet) {
//    /***********************************************************
//    * Author:	Daniel Tweddell
//    * Revision:	4/15/09
//    * 
//    * Sets the event handlers search and data entry inputs
//    * oSet is optional so we can reset the events for a specific
//    * object and it's children
//    ***********************************************************/
//    if (!oSet) oSet = document
//    var oElemGrp = oSet.getElementsByTagName('INPUT')
//    for (var iElem = 0; iElem < oElemGrp.length; iElem++) {
//        oElem = oElemGrp[iElem];
//        setClearDescriptorElements(oElem);
//        setDynamicSelectElements(oElem);
//    }
//}

function alignTableHeaders(oContaining) {
    /***********************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/30/09
    * 
    * I cannot seem to get the widths of two columns in two 
    * different tables to match consistently, so I cheat.  I hate
    * inefficiency, so someday I'll see if I can find another 
    * way to do this.
    ***********************************************************/
    var oContainer = document;
    if (oContaining) {
        if (!oContaining.x) oContainer = oContaining;//make sure it's not an event
    }
    var yTables = oContainer.getElementsByTagName('TABLE');
    for (var iTable = 0; iTable < yTables.length; iTable++) {
        var oTable = yTables[iTable];
        if (oTable.id) {//only tables with ids
            if (oTable.id.slice(-1 * sHeadDesignator.length) != sHeadDesignator) {//only ones not assigned as headers
                alignSingleTableHeader(oTable);
            }
        }
    }
}

function alignSingleTableHeader(oTable) {
    /***********************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/30/09
    * 
    * Singled out so that I can do one at a time if I wish.
    ***********************************************************/
    if (oTable) {
        var oHeader = document.getElementById(oTable.id + sHeadDesignator);
        var oFooter = document.getElementById(oTable.id + sFootDesignator);
        if (oHeader || oFooter) {//does the header exist?
            //setTableWidth(oHeader, oTable)
            try {
                for (var iCell = 0; iCell < oTable.rows[0].cells.length; iCell++) {
                    if (oHeader) {
                        var oNarrowCell = oHeader.rows[0].cells[iCell]; //make sure the width matches the widest of the two
                        var oWideCell = oTable.rows[0].cells[iCell];
                        if (oNarrowCell.offsetWidth > oWideCell.offsetWidth) {
                            var oWideCell = oNarrowCell;
                            var oNarrowCell = oTable.rows[0].cells[iCell];
                        }
                        var iWidth = setCellWidth(oWideCell, oNarrowCell);
                    }
                    if (oFooter) setCellWidth(oWideCell, oFooter.rows[0].cells[iCell], iWidth)
                }
            } catch (e) {
                //ignore if fail
            }
        }
    }
}

function setCellWidth(oFrom, oTo, iWidth) {
    /***********************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/30/09
    * 
    * required : alignTableHeaders()
    ***********************************************************/
    try {
        if (!iWidth) iWidth = makeNumeric(oFrom.offsetWidth);
        oTo.style.width = iWidth + 'px'; //make the column widths match
        oTo.style.width = (makeNumeric( oTo.style.width) - (makeNumeric(oTo.offsetWidth) - iWidth))
    } catch (e) { }
}

function ValidateFormEntry(){
    /******************************************************************
    * Author: Daniel Tweddell
    * Revision:	4/30/09
    *
    * Validates that all required entries are present for all forms on 
    * the page.
    * control eg. required="Please enter the name of the company."
    ******************************************************************/
    for (var iForm = 0; iForm < document.forms.length; iForm++) {//look through all the forms
        ValidateSingleFormEntry(iForm);
    }
}

function ValidateSingleFormEntry(form) {
    /******************************************************************
    * Author: Daniel Tweddell
    * Revision:	4/30/09
    *
    * Validates that all required entries are present for a single form
    * control eg. required="Please enter the name of the company."
    put a ~ in where you want carriage returns
    ******************************************************************/
    var oForm;
    if (!form) form=0;//default to first form
    switch(typeof form){
        case 'object':
            oForm=form;
        break;
        case 'number':
            oForm=document.forms[form];
        break;
        default :
            oForm=document.forms[0];
        break
    }
    var oRequired;
    var sRequired = '';
    var bValidated = true;
    if (oForm){
        for (var iControl = 0; iControl < oForm.length; iControl++) {
            oRequired = oForm[iControl];
            if (oRequired) {
                sRequired=oRequired.getAttribute('required');
                if (sRequired) {//is it required
                    try {
                        if (!oRequired.value) {//is it empty
                            bValidated = false;
                            sAlert = sRequired.replace("~", '\r\n'); //set var so the carriage returns work
                            //alert(sAlert); //let the user know to fill it in
                            oRequired.style.backgroundColor = '#FFEEEE';
                            var oFocus = oRequired.onfocus; //get the focus event if there is one
                            if (oFocus) oRequired.onfocus = null; //focusing triggers the event and can cause an error
                            oRequired.focus(); //highlight the control
                            oRequired.onfocus = oFocus; //return it
                            break; //get out
                        } else {
                            oRequired.style.backgroundColor = '#FFFFFF';
                        }
                    } catch (err) {
                        bValidated = false;
                        //alert('Unable to validate the form.\r\n' + err.name + ":" + err.message);
                        //alert(oRequired.id)
                    }                        
                }
            }
        }
        return bValidated;
    }
}

//function docUnload() {
//    alert('unload');
//    XmlHttpPage('/content/logout.aspx');//closes the session
//}

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ Cross Browser compliance tools 
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/


function SourceElement(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	12/4/09
    * 
    * FF does not have a global event object like ie.  This helps compensate
    * ie is also uses srcElement and everyone else uses target.
    **************************************************************************************/
    try {
        var srcElem = null;
        if (window.event) {//see if there is a global event object
            event = window.event
            srcElem = event.srcElement;
        } else {
            srcElem = event.target;
        }
        return srcElem;
    } catch (err) { }
}

function getEvent(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	12/4/09
    * 
    * FF does not have a global event object like ie.  This helps compensate
    **************************************************************************************/
    if (window.event) event = window.event
    return event;
}

function innerText(oElement, sValue) {
    //no innerText in ff
    var retstr = '';
    if (sValue) {//set the value if it was sent
        if (oElement.innerText) {
            oElement.innerText = sValue;
        } else {//ie
            oElement.textContent = sValue;
        }
        retstr = sValue;
    } else {
        if (oElement) retstr = !oElement.innerText ? oElement.textContent : oElement.innerText;
    }
    return !retstr ? '' : retstr; //make sure we at least return a string
}

function innerHTML(oTarget, sContent, bAppend){
    if (document.getElementById && !document.all) {//is this a non ie browser
        var range = document.createRange();
        range.setStart(oTarget, 0);
        range.setEnd(oTarget, oTarget.childNodes.length);
        if (sContent) {//if there is content, save it if not return the content
            var htmlFrag = range.createContextualFragment(sContent);
            if (bAppend != true) {
                range.deleteContents();//need to test this as an alternative the line below.
                //while (oTarget.hasChildNodes()) oTarget.removeChild(oTarget.lastChild); //remove everything in the container
            }
            oTarget.appendChild(htmlFrag); //add the new html
        } else {
            sContent = range.toString();
        }
    } else {
        if (sContent) {
            if (bAppend == true) {
                oTarget.innerHTML += sContent;
            } else {
                oTarget.innerHTML = sContent;
            }
        }
        sContent = oTarget.innerHTML;
    }
    return sContent;
}

function findNode(oParent, nodeName, index) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	2/1/10
    * 
    * Browsers treat node tree transversal differently - numbering the nodes in their own
    * manner.  This finds the node of a matching name and returns it/
    * index allows you to specify a number other than the first matching node.
    **************************************************************************************/

    var oChild;
    var iCount = 0;
    if (!index) index = 0; //default to 0
    for (var iNode = 0; iNode < oParent.childNodes.length; iNode++) {
        if (oParent.childNodes[iNode].nodeName.toLowerCase() == nodeName.toLowerCase()) {
            if (iCount == index) {//see if it's the matching index
                oChild = oParent.childNodes[iNode];
                break;
            }
            iCount++;
        }
    }
    return oChild;
}

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ General Tools 
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/

var sAutoGenIdPrefix = "id";
var sWindowIdSuffix = "_window";

function XmlHttpPage(sPageName, objReplace, sQryString, fFollowing, fOnText, sMethod, sSend) {
    /******************************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	7/13/06
    * 
    * Updates are done through here and the direct manipulation of the DOM.  I am trying
    * to tightly control the amount of redundant data that the database has to process.
    * 
    * sPageName - The name of the page we are processing
    * objReplace - is the returned HTML going to be shown somewhere, if so this is where
    * sQryString - the data to send to the page
    * fFollowing - a function to run following the page load
    * **Probably best to ignore the last two
    * sMethod - POST or GET, post doesn't work consistantly for me, so I just use the qry string to send
    * sSend - works in conjunction with smethod - only useable for POST
    ******************************************************************************************************/
    XmlHttpReq = false; //default to fail
    if (window.XMLHttpRequest) {//try for non ie browsers
        try {
            XmlHttpReq = new XMLHttpRequest();
        } catch (e) {
            XmlHttpReq = false;
        }
    } else if (window.ActiveXObject) {//try for IE/Windows ActiveX version
        try {
            XmlHttpReq = new ActiveXObject("Msxml2.XMLHTTP"); //try the newer one first
        } catch (e) {
            try {// no go try the older one
                XmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {
                XmlHttpReq = false;
            }
        }
    }
    if (XmlHttpReq) {// see if anything worked
        if (!sQryString) {
            sQryString = '';
        } else {
            if (sQryString.slice(0, 1) != '&') {
                sQryString = '&' + sQryString; //add an and if the user didn't
            }
        }
        var randomItem = new Date(); //seed the randomizer
        sPageName += '?random=' + randomItem.getTime();  	// number of milisecods since 1970 - create a random number to prevent caching, hacky but worky
        sPageName += sQryString; //add any other query strings
        //alert(sPageName);
        if (!sMethod) sMethod = "GET"; //default to get, but it might be post
        XmlHttpReq.open(sMethod, sPageName, false);
        if (sMethod == "POST") xmlHttpReq.setRequestHeader('Content-Type', "application/x-www-form-urlencoded");
        //if (sMethod=="POST") xmlHttpReq.setRequestHeader('Content-Type', 'multipart/form-data');
        //xmlHttpReq.setRequestHeader("Content-Length", sData.length); 
        XmlHttpReq.onreadystatechange = XmlProcessCallback;
        if (!sSend) sSend = null; //assume that we are not sending anything unless the caller tells us
        XmlHttpReq.send(sSend);
        if (objReplace) {
            //alert(XmlHttpReq.responseText)
            var sText = XmlHttpReq.responseText;
            if (fOnText) sText = fOnText(sText); //run a fn on the text
            //alert(sText);
            try {
                //document.all.testme.value = sText//fill in the html the was returned if that is what the caller wants
                innerHTML(objReplace, sText); //fill in the html the was returned if that is what the caller wants
            } catch (e) {
                innerHTML(objReplace,'<div style="font-weight:bold;color:red">Unable to load page.<br/>' + e.description + '</div>');
            }
            setEvents(objReplace)//load all the default events for that newly added item
            if (fFollowing) fFollowing();
        } else {
        return XmlHttpReq.responseText; //return the value so we can maniplutate the data
        }
    }
}

function XmlProcessCallback() {
    /*****************************************************************************
    Ready state
    0 = uninitialized
    1 = loading
    2 = loaded
    3 = interactive
    4 = complete
    *****************************************************************************/
    //if (XmlHttpReq.readyState==4)Good
    //alert("There was a problem retrieving the XML data:\n"+req.statusText)
}

function makeNumeric(text, iDecimalPlaces, iDefault) {
    /**********************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/15/09
    * 
    * takes a string of letters and numbers and removes the letters
    * e.g. 5px = 5
    ***********************************************************/
    var conv = function() {
        var iDefault = (!arguments[2] ? 0 : arguments[2]);
        var sText=(arguments[0] + ' ').replace(/[^0-9|\.\-]/g, "");
        var iText = round(sText==''?iDefault:sText, arguments[1]);
        return (isNaN(iText) ? iDefault : iText); //return 0 if there is no numnbers
    }
    if (text && typeof text == 'object') {
        text.value = conv(text.value, iDecimalPlaces, iDefault);
    } else {
        return text = conv(text, iDecimalPlaces, iDefault);
    }
}

function makeDate(sDate, bAlert) {
    /**********************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/26/2010
    * 
    * Compensate for a user entering garbage for a date.
    ***********************************************************/
    var dTest = new Date(sDate);
    var dToday = new Date()
    var sNewDate='';
    var sMessage='Your date entry will be updated.';
    var fnTest=function(dTest){
        return (dTest.toString() == "NaN" || dTest.toString() == "Invalid Date")
    }
    if(fnTest(dTest)){
        //the date is invalid.  Now we look for a couple common entry short cuts - mm/dd and mm/yyyy
        var rDate;              //array to hold entered date parts
        var rDelim=['/','-'];   //common text date delimiters
        var sDelim='';
        for(var iDelim=0;iDelim<rDelim.length;iDelim++){
            sDelim=rDelim[iDelim];
            if (sDate.indexOf(sDelim)) {                //see which date parts the user entered
                rDate = sDate.split(sDelim);            //parse the entry if possible
                if(rDate.length==2){                    //anything but 2 and they're on their own
                    if(rDate[rDate.length-1].length==4){//assume a 4 digit year or we can't tell if it's supposed to be something else
                        sNewDate = rDate[0] + sDelim + '1' + sDelim + rDate[1]; //in case they entered mm/yyyy
                        break;
                    }else{
                        sNewDate = sDate + sDelim + dToday.getFullYear();       //in case they entered mm/dd, add the year
                        break;
                    }
                }
            }
        }
        dTest= new Date(sNewDate);
        if(fnTest(dTest)) dTest = dToday; //make it today if invalid
    }
    if (dToday.getFullYear() - dTest.getFullYear() >= 100) dTest.setFullYear(dTest.getFullYear()+100); //system is defaulting a 2 digit date to 100 years ago
    sNewDate=(dTest.getMonth()+1) + "/" + dTest.getDate() + "/" + dTest.getFullYear();
    if(bAlert && sMessage){
         if(sNewDate!=sDate) alert(sMessage);
    }
    return sNewDate;
}

function round(iValue, iDecimalPlaces) {
    /**********************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/24/09
    * 
    * Rounds a number to a given set of decimal places
    ***********************************************************/
    if (!iDecimalPlaces) iDecimalPlaces = 0;
    var iDivisor = Math.round(Math.pow(10, iDecimalPlaces));
    return Math.round(iValue * iDivisor) / iDivisor
}

function getStyle(oElm, sCssRule, bMakeNumeric) {
    /**********************************************************
    * Author: Robert Nyman
    * http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/	
    * 
    * Gets the style value or rule from an element
    ***********************************************************/
    var strValue = "";
    if (document.defaultView && document.defaultView.getComputedStyle) {//for firefox, opera etc
        strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(sCssRule);
    }
    else if (oElm.currentStyle) {//for ie
        sCssRule =
            sCssRule.replace(/\-(\w)/g,
                function(strMatch, p1) { return p1.toUpperCase(); }
            ); //remove the - and set the first letter to uppercase for some styles
        strValue = oElm.currentStyle[sCssRule];
    }
    if (bMakeNumeric == true) strValue = makeNumeric(strValue)
    return strValue;
}

function replaceClass(oElement, sOldClass, sNewClass) {
    /**********************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/15/09
    * 
    * replaces one of the classes in the list of them
    ***********************************************************/
    var regExp = eval("/\\b" + sOldClass + "\\b/g");
    oElement.className = oElement.className.replace(regExp, sNewClass);
}

function getElementCoords(objFind) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	7/1/06
    * 
    * Gets the coords of an element on screen.
    * By some genius named Martin Honnen http://javascript.faqts.com/ 
    **************************************************************************************/
    var coords = { x: 0, y: 0, h: 0, w: 0 };
    coords.h = objFind.offsetHeight;
    coords.w = objFind.offsetWidth;
    while (objFind) {
        coords.x += objFind.offsetLeft - objFind.scrollLeft;
        coords.y += objFind.offsetTop - objFind.scrollTop;
        objFind = objFind.offsetParent;
    }
    return coords;
}

function getAncestor(sTagName, oChild) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	08/03/06
    * 
    * Get the ancestor that matches the sent tag
    **************************************************************************************/
    //if (!oChild) oChild = SourceElement(); //set it nothing was sent
    sTagName = sTagName.toUpperCase();
    if (sTagName && oChild) {
        while (oChild.tagName != sTagName) {
            if (!oChild.parentNode) break;
            oChild = oChild.parentNode;
        }
    }
    return oChild
}

function getSibling(sTagName, oSibling) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	08/03/06
    * 
    * Get the sibling that matches the sent tag
    **************************************************************************************/
    var rSibling; //the object we'll be returning.
    sTagName = sTagName.toUpperCase();
    //if (!oSibling) oSibling = SourceElement(); //set it nothing was sent
    var oParent = oSibling.parentNode; //the child object we'll be looking at
    if (sTagName) {
        for (var iSibling = 0; iSibling < oParent.childNodes.length; iSibling++) {
            //var f = oParent.childNodes[iSibling].tagName
            if (oSibling != oParent.childNodes[iSibling]) {//don't get the item itself, get it's sibling
                if (oParent.childNodes[iSibling].tagName == sTagName) {
                    rSibling = oParent.childNodes[iSibling];
                    break
                }
            }
        }
    }
    return rSibling;
}

function getDescendant(sTagName, oParent, sClassName, sInnerText) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	08/03/06
    * 
    * Get the Descendant that matches the sent tag
    **************************************************************************************/
    var oChild; //the child object we'll be looking at
    var rChild; //the object we'll be returning.
    //depth(1)
    sTagName = sTagName.toUpperCase();
    if (oParent){// oParent = SourceElement(); //set it nothing was sent
        if (sTagName) {
            var iChildren = oParent.childNodes.length//get the number of children
            if (iChildren > 0) {
                for (var iChild = 0; iChild < iChildren; iChild++) {
                    oChild = oParent.childNodes[iChild];
                    //writeErr(oChild)
                    if ((oChild.tagName == sTagName) && ((!sClassName) || (sClassName == oChild.ClassName)) && ((!sInnerText) || (sInnerText == innerText(oChild)))) {
                        rChild = oChild
                        break; //we found one get out
                    } else if (oChild.childNodes) {
                        oChild = getDescendant(sTagName, oChild, sClassName, sInnerText);
                        //depth(-1)
                        if (oChild) {
                            rChild = oChild
                            break; //we found one get out
                        }
                    }
                }
            }
        }
    }

    return rChild;
}

function setDiv(event, sPageName, sQuery, sClassName, fnBeforeAppend, oTarget, bForceParent, sAlign, fnOnText) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/16/09
    * 
    * Creates a div and places a page in it.
    * sPageName - the name of the page to be loaded in the div this creates
    * sQuery - a query string to be applied to the page for criteria
    * oTarget - the element this div will be applied to.  Normally it would be the event
    *           source or it's parent, but in come cases you may want the div applied 
    *           elsewhere.
    * bForceParent - is a flag to let us know not to look to the event to find out which element
    *            is the source of the request.
    * sAlign - should be right or left, default is left
    *
    * Since the target item that this div will be applied to may already have styles that 
    * will affect the way this div is displayed (e.g. cell widths), there are a couple 
    * settings that we need to apply to the div prior to it being applied to the target.
    *
    * sClassName - a css class name to be applied to the div
    * fnBeforeAppend - function that needs to be run on the div contents before it is appended
    *
    **************************************************************************************/
    var oSelected = oTarget;
    var oParent = oTarget; //what object is this div being applied to?
    if (bForceParent==true) {//no event means, selected must be  the target
        if(oSelected.parentNode) oParent = oSelected.parentNode; //most often this is used on input elements which will not take a div, so we default to the parent element
    } else {//an event with no
        oSelected = SourceElement(event);
        if (!oParent) oParent = oSelected.parentNode;
    }
    var sCuror = null;
    if(oSelected.style) if(oSelected.style.cursor)sCursor = oSelected.style.cursor;//remember this for later
    var coords = getElementCoords(oSelected);//figure out where this needs to show up on screen
    var iBorder = makeNumeric(getStyle(oSelected, 'border-width'))//is there a border and how wide is it?
    //var oParent = oTarget;//what object is this div being applied to?
    //if (!oTarget) oParent = oSelected.parentNode; //most often this is used on input elements which will not take a div, so we default to the parent element

    if (!oParent.id) oParent.id = sAutoGenIdPrefix + randomNumber(); //in order to refer to this later we need to have an id - so create an ID if needed
    var oDiv = document.getElementById(oParent.id + sWindowIdSuffix); //see if the div already exists, if the div exists, return a pointer to it.
    if (!oDiv) {//if not create it and set an id we can use to find it later
        oDiv = document.createElement('DIV');
        oDiv.id = oParent.id + sWindowIdSuffix; //give the child an id so we can reference it later
        oSelected.style.cursor = 'wait'; //until the thing is loaded
        oDiv.className = sClassName;//apply the class
        //innerText(document.getElementById('testme'), sQuery)//fill in the html the was returned if that is what the caller wants
        XmlHttpPage(sPageName, oDiv, sQuery,null,fnOnText);
        if (fnBeforeAppend) {
            fnBeforeAppend(event,oDiv);
        }
        if (innerText(oDiv)) {
            oParent.appendChild(oDiv);
        } else {
            oDiv.parentNode.removeChild(oDiv)//if there is nothing loaded in it, don't load it
        }
        oDiv.style.pixelLeft = coords.x + (iBorder * 2); //place it where it should go
        oDiv.style.pixelTop = (coords.y + coords.h + (iBorder * 2));
        if (sAlign == "right") {//move it based on the requested alignment
            oDiv.style.pixelLeft -= getElementCoords(oDiv).w;
        } oSelected.style.cursor = ''; //until the thing is loaded//once it's loaded, set the cursor back
        oDiv.style.cursor = '';
    }
    return oDiv;
}

function destroyDiv(iCloseTime, oDiv) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/16/09
    * 
    * When they click, close it
    **************************************************************************************/
    var iTimeoutVar=0;
    try {
        if (oDiv) {
            if (oDiv.tagName.toUpperCase() != 'DIV') oDiv = getAncestor('div', oDiv);
            //oDiv = (!oDiv) ? getAncestor('DIV', SourceElement()) : oDiv;
            iTimeoutVar = setTimeout(function() { oDiv.parentNode.removeChild(oDiv); }, iCloseTime);
        }
    } catch (e) { }
    return iTimeoutVar;//return it in case we need it
}

function randomNumber() {
    var sRrandom = new Date(); //seed the randomizer
    sRrandom = sRrandom.getTime();
    return sRrandom.toString().slice(2, 10);
}

function format(sValue, sType) {
    var sPrefix = '';
    sValue = sValue.toString().split('.')
    switch (sType) {
        case 'currency': //first see if we can't make this a currency
            sPrefix = '$';
            if (sValue[0].substr(0, 1) == '-') {
                sPrefix = '-$';
                sValue[0] = sValue[0].slice(1);
            }
    }
    sValue[1] = !sValue[1] ? '' : sValue[1]; //make it blank rather then undef
    if (sType) {
        sValue[1] = sValue[1] + '00';  //make sure we have a min of two 0s
        sValue[1] = '.' + sValue[1].substr(0, 2);  //grab only the last 2 digits
    }
    var rgx = /(\d+)(\d{3})/; //add in commas
    while (rgx.test(sValue[0])) {
        sValue[0] = sValue[0].replace(rgx, '$1' + ',' + '$2');
    }
    return sPrefix + sValue[0] + sValue[1];
}

function trimString(sInner) {
    //http://www.somacon.com/p355.php
    regEx= /^\s+|\s+$/g;
    if (sInner) regEx=/\s+/g;
    return this.toString().replace(regEx, "");
}

function stringBlankToZero(sReplacement) {
    /***********************************************************************
    * Author    Daniel Tweddell
    * Revision	8/3/09 
    * 
    * Replaces the string with something else if it's blank.
    ***********************************************************************/
    var sRetVal = this.toString();
    if (!sRetVal) sRetVal = sReplacement;
    return sRetVal;
}

function findDotNetElement(sTagName) {
    /***********************************************************************
    * Author    Daniel Tweddell
    * Revision	6/24/09 
    * 
    * dot net does this retarded thing where they change both the id and 
    * the name of the controls.  It sucks becuse now we have no way to 
    * directly reference the controls in JS.  This finds them.
    ***********************************************************************/
    var sElementName = this.toString();
    var oElem = document.getElementById(sElementName);//first see if .net changed the name at all
    if (!oElem) {
        var oGrp = document.getElementsByTagName(sTagName.toUpperCase());
        var bFound = 0;
        for (var iElem = 0; iElem < oGrp.length; iElem++) {
            oElem = oGrp[iElem];
            try {
                if (oElem.id.indexOf('_' + sElementName) > -1) {
                    bFound = 1
                    break//we found the control
                }
            } catch (e) { //ignore and continue
            }
        }
        if (bFound == 0) oElem = null;
    }
    return oElem;
}

function analyzePath() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	8/05/09
    * 
    * Gets the filename and info from a path
    **************************************************************************************/
    var sText = this.toString();
    var folders = null;
    var sFileName = '';
    var sFileType = '';
    var sDelimiter = "/";
    var sPath = "";
    if (sText.indexOf("\\") >= 0) sDelimiter = "\\";
    try {
        folders = sText.split(sDelimiter);
        sFileName = folders[folders.length - 1];
        sFileType = sFileName.slice(sFileName.indexOf(".") + 1);
        sPath = sText.slice(0, -1 * sFileName.length);
    } catch (e) {
        folders = sText;
        sFileName = sText;
        sFileType = sText;
    }
    this.folders = folders;
    this.path = sPath;
    this.fileName = sFileName;
    this.fileType = sFileType;
    return this;
}

function RepairFormText(sText) {
    /***********************************************************************
    * Author    Daniel Tweddell
    * Revision	7/07/09 
    * 
    * The form tags in the loaded text are causing the page to error when
    * loaded into the host, so here we replace the form tags with div tags.
    ***********************************************************************/
    try {
        sText = sText.replace(/[<<\/]form[^>]*id="?[^("| )]*[" >]/g, function(sMatch) { return sMatch.replace(/id="?[^("| )]*("|(?=>| ))/g, "") })//replace the id in the form tag
        sText = sText.replace(/(<|<\/)form( |>)/g, function(sMatch) { return sMatch.replace(/form/g, "div") })//replace the form tag
        sText = sText.replace(/__VIEWSTATE/g, function(sMatch) { return sMatch.replace(/__/g, "__XMLHTTP_") })//replace the viewstate control
    } catch (e) {
        sText = "Unable to load data\r\n" + e.description
    }
    return sText
}

function helpWindow(event) {
    /***********************************************************************
    * Author    Daniel Tweddell
    * Revision	11/18/09 
    * 
    * Opens a help window.
    ***********************************************************************/
    var oClicked = SourceElement(event)
    if (oClicked) {
        if (oClicked.title) {
            var sTitle=oClicked.title;
            var sPageName = "";
            switch (oClicked.title) {
                case "Mits Privacy Policy":
                    sPageName = "/content/ui/help/privacy.aspx";
                    break;
                case "Location and Manufacturer":
                    sPageName = "/content/ui/help/addressandmfgr.aspx";
                    break;
                case "Items to recycle":
                    sPageName = "/content/ui/help/recycleitems.aspx";
                    break;
                case "Locations Map":
                    sPageName = "/content/ui/help/locationlistmap.aspx";
                    break;
                case "Product Information Entry":
                    sPageName = "/content/ui/help/productinformation.aspx";
                    break;
                case "Product Dimensions":
                    sPageName = "/content/ui/help/productdimensions.aspx";
                    break;
                case "Shipping Label Information":
                    sPageName = "/content/ui/help/shippinginformation.aspx";
                    break;
                case "Upload Supporting Documentation":
                    alert("Supported document types are:Acrobat-PDF, MsWord-DOC(X), MsExcel-xls(x)");
                    //sPageName = "/content/ui/help/shippinginformation.aspx";
                    break;
                case "Invoice Item Entry":
                    alert("Add your invoice item.");
                    //sPageName = "/content/ui/help/shippinginformation.aspx";
                    break;
            }
            if (sPageName) setDiv(event, sPageName, null, 'floatingWindow helpWindow', function(event,oDiv) { setHelpTitle(oDiv, sTitle) });
        }
    }
}

function setHelpTitle(oDiv, sTitle) { 
    /***********************************************************************
    * Author    Daniel Tweddell
    * Revision	11/18/09 
    * 
    * Sets the help window title help window.
    ***********************************************************************/
    if (oDiv && sTitle) {
        var oHeader = getDescendant('th', oDiv);
        if (oHeader) {
            innerHTML(oHeader,sTitle,true);
            //oHeader.innerHTML += '&nbsp;:&nbsp;' + sTitle;
        }
    }
}

/*
function setWidth(){
	oTable=getAncestor('table',SourceElement());
	oDiv=getAncestor('div',oTable);
	alert(getStyle(oTable,'width'));	
	var i=prompt('Please enter the width',300)+'px';
	//oDiv.style.width=i;
	oTable.style.width=i;
}
*/

function showHiddenText(event) {
    /***********************************************************************
    * Author    Daniel Tweddell
    * Revision	12/1/09 
    * 
    * Occasionally we hide text to be shown or rehidden with a plus or minus
    * sign.  The hidden text should be the next sibling, a span
    ***********************************************************************/
    var oClicked = SourceElement(event);
    var oShow = getSibling('span', oClicked);
    var sDisplay = '';
    var sIndicator='-';
    if (innerText(oClicked) == '-') {
        sDisplay = 'none';
        sIndicator = '+';
    }
    if (innerText(oClicked) == '+' || innerText(oClicked) == '-') innerText(oClicked, sIndicator);
    oShow.style.display = sDisplay;    
}

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ Dynamic Select Lists
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/

function dynamicSelect() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/25/09
    * 
    * Rather than put the page name on the page, we put the type out there and then set
    * it in here so we can update it more easily if need be.
    * Arguments:
    * event - well... the event
    * sListType - the type of list.  If this is sent the othter properties and events are
    *             set in the case statement and no more aruments are necessary
    * fOnClick - an additional function to run on click
    * fOnBlur  - an additional function to if closed without selecting an item
    *
    * sPageName - the path and name of the page to open
    * sQueryString - the query string to limit the page's dataset
    * fnFollowing - an method to run on the markup after it is loaded before it's visible
    **************************************************************************************/
    var event = arguments[0];
    var oSelected = SourceElement(arguments[0]);
    var sName = null;
    var fOnClick = null;
    var dataCell = []; //when the user clicks on a row, data will be added to the control in the order specified in the array
    if (oSelected) {
        var oParent = oSelected.parentNode; //can't append a div to an input, so append to parent instead
        if (arguments.length > 4) {//user is opening an adhoc page
            //fill in later when I have one
        } else {//user is opening one of the defined pages
            var fOnClick = function(event) { return; };
            var fOnBlur = function(event) { return; };
            buildPrototypes();
            for (var arg = 2; arg < arguments.length; arg++) {
                switch (arg) {
                    case 2:
                        fOnClick = window[arguments[arg]];
                        break;
                    case 3:
                        fOnBlur = window[arguments[arg]];
                        break;
                }
            }
//            if (arguments[2]) fOnClick = window[arguments[2]];
//            if (arguments[3]) fOnBlur = window[arguments[3]];
            switch (arguments[1].toLowerCase()) {
//                case "postalcode":
//                    sName = "/content/dynamiclists/postalcode.aspx";
//                    dataCell = [1, 2, 0]; //This is the order I want to see the data
//                    oSelected.focus = null; //remove this reference now that we have set the proper one
//                    oSelected.onkeyup = function() { dynamicSelectPartialSearch(event, sName, function(event) { dynamicSelectClick(event, dataCell); fOnClick(event); }); };
//                    oSelected.onblur = function(event) { selectListLostFocus(event); fOnBlur(event); };
//                    break;
                case "manufacturer":
                    sName = "/content/dynamiclists/manufacturer.aspx";
                    dataCell = [1]; //This is the order I want to see the data
                    oSelected.focus = null; //remove this reference now that we have set the proper one
                    oSelected.onkeyup = function() { dynamicSelectPartialSearch(event, sName, function(event) { dynamicSelectClick(event, dataCell); fOnClick(event); }); };
                    oSelected.onblur = function(event) { selectListLostFocus(event); fOnBlur(event); };
                    break;
                case "productmodel":
                    sName = "/content/dynamiclists/productmodel.aspx";
                    dataCell = [1]; //This is the order I want to see the data
                    oSelected.focus = null; //remove this reference now that we have set the proper one
                    oSelected.onkeyup = function() { dynamicSelectPartialSearch(event, sName, function(event) { dynamicSelectClick(event, dataCell); fOnClick(event); }); };
                    oSelected.onblur = function(event) { selectListLostFocus(event); fOnBlur(event); };
                    break;
            }
        }
    }
}

function dynamicSelectPartialSearch(event, sPageName, fnOnClick) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/28/09
    * 
    * Waits for a moment before processing the search in case the user continues to type.
    * that way we don't search until their are done.
    **************************************************************************************/
    var oSelected = SourceElement(event);
    clearTimeout(iDynamicSelectTimeout); //clear out any keystrokes that were about to be processed
    iDynamicSelectTimeout = setTimeout(function() { dynamicSelectPartialSearchTimed(event, sPageName, fnOnClick, oSelected); }, 250);
}

function dynamicSelectPartialSearchTimed(event, sPageName, fnOnClick, oSelected) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/28/09
    * 
    * the onkeydown event handler for dynamic selects that update each time the user types
    * Arguments:
    * sPageName - the path and name of the page to open
    * fnFollowing - an method to run on the markup after it is loaded before it's visible
    **************************************************************************************/
    //var oSelected = SourceElement();
    if (oSelected) {
        var oParent = oSelected.parentNode; //can't append a div to an input, so append to parent instead
        if (oParent) {
            var oDiv = document.getElementById(oParent.id + sWindowIdSuffix);
            if (oDiv) oDiv.parentNode.removeChild(oDiv); //make sure there is no old one
            var sValue = oSelected.value;

            if (sValue) {
                sValue = "search=" + sValue;
                //this is confusing as heck.  Arguments in the case below refers not to this function, but to when this function is
                //called - as in when we run it from the callee.  Since that is the case, it takes the arg of oDiv from setDiv.
                var fClick = function() { configureSelectListContents(event, arguments[1], fnOnClick); };
                if (sPageName) {
                    var oDiv = setDiv(event, sPageName, sValue, 'dynamicSelect', fClick, oSelected,true); //create the div in the right place
                }
            }
        }
    }
    return;
    alert('hi');    
}

function dynamicSelectClick(event, dataCells, iCloseTime) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/28/09
    * 
    * An event handler for the dynamic select onclick event.  Basically it takes the text 
    * from a div or from several rows in a div and places them in the text box then closes
    * the div.
    * Arguments:
    * dataCells - an array containing the cells that contain the data to be placed in as
    *             input text
    * iCloseTime - the amount of time between when the user clicks to when the div closes
    *              default=0
    **************************************************************************************/
    var oClicked = SourceElement(event);
    var oDiv = getAncestor('DIV', oClicked);
    var oText = getSibling('INPUT', oDiv);
    clearTimeout(iDynamicSelectTimeout);//clear the timeout that hides the list on lost focus
    if (dataCells && oText) {
        oText.value = "";//clear it before we start putting stuff in it.
        if (dataCells.length > 1) {//we want several of the chosen cells to display in the box, not just one
            var oRow=  getAncestor('tr', oClicked);
            if (oRow) {
                for (var iCell = 0; iCell < dataCells.length; iCell++) {
                    try {
                        oText.value += innerText(oRow.cells[dataCells[iCell]]) + ' ';
                    } catch (e) { }
                }
                oText.value = oText.value.trim();
                dynamicSelectSetHiddenValue(oText, oRow, oClicked);//fill in any hidden values if needed for posting
            }
        } else {//we want one of the rows only
            var oRow=  getAncestor('tr', oClicked);
            if (oRow) {
                try {
                    oText.value =  innerText(oRow.cells[dataCells[0]]);
                } catch (e) { }
                dynamicSelectSetHiddenValue(oText, oRow, oClicked); //fill in any hidden values if needed for posting
            }
        }
    } else {
    if (oText) oText.value = innerText(oClicked);
    }
    if (!iCloseTime) iCloseTime = 0;
    destroyDiv(iCloseTime, oDiv);
}

function dynamicSelectSetHiddenValue(oText, oRow, oClicked) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/30/09
    * 
    * Often when saving a value we will need to know the id rather than the text the user 
    * sees.  If this is relevant the id will be hidden in the first cell of the displayed
    * row.  There should also be a hidden input with the same name as the selected input
    * except with a hid prefix and an ID on the end.  eg. txtMyControl and hidMyControlID
    **************************************************************************************/
    var sControlName = oText.name;    
    if (sControlName) {
        sControlName = sControlName.split("$");
        sControlName = sControlName[sControlName.length - 1];//get the last entry in case the control is a dot net one
        sControlName = 'hid' + sControlName.slice(3) + 'ID'
        var oHid = sControlName.findDotNet('input'); // document.getElementsByName(sControlName)
        if (oHid) {
            try {
                oHid.value = innerText(oRow.cells[0]); //alsways the first one
            } catch (e) { }
        }
    }
}

function configureSelectListContents(event, oDiv, fnOnClick) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/16/09
    * 
    * Build a dynamic select list and set each of the events for the table elements
    * Created in one place and moved to another so that sizing based on the cell width can
    * occur without being effected by stylesheet cascades
    * Arguments:
    * event - mmm I wonder what this is
    * oDiv - the div that contains the information that is the select list
    * fnOnClick - a function that happens when that user clicks on an item in the list.
    **************************************************************************************/
    var oTable = getDescendant('TABLE', oDiv);
    document.getElementsByTagName('TABLE')[0].rows[0].cells[0].appendChild(oDiv); //append to the doc so we are style free, then move it later
    if (oTable) {
        for (var iRow = 0; iRow < oTable.rows.length; iRow++) {
            var oRow = oTable.rows[iRow];
            oRow.onmouseover = function(event) { highlightSelection(event) };
            oRow.onmouseout = function(event) { highlightSelection(event) };
            oRow.style.cursor = 'hand';
            oRow.style.cursor = 'pointer';
            oRow.onclick = function(event) { fnOnClick ? fnOnClick(event) : closeSelectList(event, 0) };
            oRow.cells[0].style.whiteSpace = 'nowrap';
        }
        oDiv.style.width = (oTable.offsetWidth + 20) + "px"; //no horiz scroll bars please
        oTable.mouseout = function() { closeSelectList(event, 500) };
    } else {
        //show some kind of error
    }
    if (oDiv.firstChild.offsetHeight < oDiv.offsetHeight) oDiv.style.height = oDiv.firstChild.offsetHeight; //make sure the list is not too big for a few of items
}

function highlightSelection(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/16/09
    * 
    * Highlights the tr elements for our select list when the user mouses over them
    **************************************************************************************/
    event = getEvent(event);
    var oSet = SourceElement(event);
    switch (event.type) {
        case "mouseover":
            oSet.style.backgroundColor = '#afdbf5';
            //ShowText(getStyle(oSet,'font-size'))
            break
        case "mouseout":
            oSet.style.backgroundColor = '';
            break
    }
}

function selectListLostFocus(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	10/6/09
    * 
    * If the control that the list is attached to looses focus, either the user has selected 
    * an item in the list or they have moved elsewhere on the page. If the former, then the 
    * called method cancels the close until it has completed it's text manipulation, if the 
    * latter we wait for half a second and then destroy the list.
    **************************************************************************************/
    try {
        var oBlur = SourceElement(event).parentNode;
        var oDiv = document.getElementById(oBlur.id + sWindowIdSuffix);
    } catch (e) {

    }
    if(oDiv) iDynamicSelectTimeout = setTimeout(function() { destroyDiv(0, oDiv); }, 500);
}

function closeSelectList(event,iCloseTime) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/16/09
    * 
    * Destoy the select list.  When they reopen it, the data will be up to date.
    * this is the default function for closing the list after item selection.
    **************************************************************************************/
    var oClicked = SourceElement(event);
    var oDiv = getAncestor('DIV', oClicked);
    var oText = document.getElementById(oDiv.id.replace("",sWindowIdSuffix)); //getSibling('INPUT', oDiv);
    if (oText) oText.value = innerText(oClicked);
    if(!iCloseTime) iCloseTime=0;
    destroyDiv(iCloseTime, oDiv);
}

function findListItem(oText) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/16/09
    * 
    * When the user types, find the item in the list and scroll to it for the user.
    **************************************************************************************/
    var oSelect = getSibling('DIV', oText); //get the created div list
    if (oSelect) {
        var oList = getDescendant('TABLE', oSelect); //get the table in the div
        if (oList) {
            var iLen = oText.value.length;
            for (var iRow = 0; iRow < oList.rows.length; iRow++) {//look through all the items in the table till we find something that matches what is typed
                var oRow = oList.rows[iRow];
                //ShowText(oRow.innerText.substr(0, iLen).toLowerCase() +", " + oText.value.toLowerCase())
                if (innerText(oRow).substr(0, iLen).toLowerCase() == oText.value.toLowerCase()) {
                    oRow.style.backgroundColor = 'afdbf5'; //highlight
                    oSelect.scrollTop = (oRow.offsetHeight * iRow);
                    break//found it, get out
                } else {
                    oRow.style.backgroundColor = ''; //unhighlight
                }
            }
        }
    }
}

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ Input Tools
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/
//var sClearTextBoxDesignator = '-';

//function setClearDescriptorEvents(oSet) {
//    /**************************************************************************************
//    * Author:	Daniel Tweddell
//    * Revision:	4/22/09
//    * 
//    * Requires: className clearLabel
//    * 
//    * Sets the event handlers for input without label descriptors
//    * Rather than have a label for each search text box, I am putting text in the box that
//    * says what it is.  When the user focuses on it, the explanation disapears.
//    **************************************************************************************/
//    if (!oSet) oSet = document
//    var oElem = oSet.getElementsByTagName('INPUT')
//    for (var iElem = 0; iElem < oElem.length; iElem++) {
//        setClearDescriptorElements(oElem[iElem])
//    }
//}

//function setClearDescriptorElements(oSet) {
//    /**************************************************************************************
//    * Author:	Daniel Tweddell
//    * Revision:	4/22/09
//    * 
//    * Requires: className clearLabel
//    * 
//    * Sets the event handlers for input without label descriptors
//    * Rather than have a label for each search text box, I am putting text in the box that
//    * says what it is.  When the user focuses on it, the explanation disapears.
//    **************************************************************************************/
//    with (oSet) {
//        var sSeparator = "";
//        if (className.indexOf("clearLabel") != -1) {
//            if (onfocus != "") sSeparator = ";";
//            onfocus += sSeparator + function() { (this.value.slice(0, 1) == sClearTextBoxDesignator) && (this.value.slice(-1) == sClearTextBoxDesignator) ? this.value = '' : null };
//            //alert(onfocus)
//            //this.value.slice() };
//        }
//    }

//}

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ Transporter Tools 
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/

function transporterShedule(event, sType) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	8/20/09
    * 
    * Click on the calendar 'schedule' icon and we bring up a calendar with the 
    * appropriate day selected and showing
    **************************************************************************************/
    //get the id and the date clicked
    var sDate=0;
    try { sDate = innerText(SourceElement(event)); } catch (e) { }
    if (sDate.trim() == '') sDate = 0;
    var oDiv = setDiv(event, '/content/dynamiclists/calendar.aspx', '&date=' + sDate, 'transporterCalendar',null,null,null,null,RepairFormText);
    oDiv.id = 'TransportCalendar'; //set this so we can refer to it later
}

function __doPostBack(sControlName, sDate) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	8/20/09
    * 
    * DOT NETs event handler function name.
    * I use it to react to the calendar clicks - save or new month
    **************************************************************************************/
    if (sControlName == 'Calendar') {
        var oDiv = document.getElementById('TransportCalendar');
        if (sDate.slice(0, 1) == 'V') {//user is moving to the next month
            XmlHttpPage('/content/dynamiclists/Calendar.aspx', oDiv, '&date=' + makeNumeric(sDate))
        } else {
            XmlHttpPage('ManageSchedule.aspx', document.getElementById('UpdateMessageContainer'));
            destroyDiv(0, oDiv);
            //if green, refresh the list
        }
    }
}

//<script language="JavaScript" type="text/javascript">
//<!--
//var IE = document.all?true:false

//// Temporary variables to hold mouse x-y pos.s
//var tempX = 0
//var tempY = 0
//var e = new Object();
//function Test(e){
//document.Show.Show2.value=vic;

//// Main function to retrieve mouse x-y pos.s
//if (IE) { // grab the x-y pos.s if browser is IE
// tempX = event.clientX + document.body.scrollLeft
// tempY = event.clientY + document.body.scrollTop
//} else { // grab the x-y pos.s if browser is NS
//tempX = e.pageX
//tempY = e.pageY
//}
//// catch possible negative values in NS4
//if (tempX < 0){tempX = 0}
//if (tempY < 0){tempY = 0}
//// show the position values

//document.Show.Show1.value=tempX + ' ' + tempY;

//document.getElementById("popupWindow").style.left = (tempX + 15)+'px';
// document.getElementById("popupWindow").style.top = (tempY - 100)+'px';
//}

//document.onmousemove=function(event){ Test(event); }

////-->
//</script></

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ Admin Menu 
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/


function adminMenuClick(event,lMenuItemID, sPageName, sQueryString, sMessage) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	3/26/09
    * 
    * Expand and contract the menu if a user clicks on a group, get the data if they click 
    * on an item.
    **************************************************************************************/
    var objClicked = SourceElement(event).parentElement; //start it out as nothing
    var menuItemID = objClicked.getAttribute("menuItemID");
    if (menuItemID) {
        aryMenuItemID = menuItemID.split(",");
        if (aryMenuItemID[0] == aryMenuItemID[1]) SelectMenuGroup(aryMenuItemID[0]); //before we look for a group, make sure we have a sub item
    }
}

function SelectMenuGroup(iMenuGroupID) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	3/26/09
    * 
    * When a menu item is clicked, show the group items and hide the rest.
    **************************************************************************************/
    var objTable = 'tAdminMenu'.findDotNet('table');
    adminMenuGroupResize(0, iMenuGroupID, objTable, true)//expand - expand and collapse on different threads so they happen at the same time
    adminMenuGroupResize(0, iMenuGroupID, objTable, false)//collapse
}

function adminMenuGroupResize(iLastItem, iMenuGroupID, objTable, bExpand) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/2/09
    * 
    * Expand and collapse the menu items at a given rate so that it's visible.  This fn
    * handles both on different threads.
    **************************************************************************************/
    for (var iRow = iLastItem; iRow < objTable.rows.length; iRow++) {//show or hide the appropriate rows
        var objCell = objTable.rows[iRow].cells[0]
        var menuItemID = objCell.getAttribute("menuItemID");
        if (menuItemID) {
            aryMenuItemID = menuItemID.split(","); //get the info to see what we'll do with it
            sDisplay = ((aryMenuItemID[0] == iMenuGroupID) || (aryMenuItemID[0] == aryMenuItemID[1])) ? "" : "none";
            if (objCell.style.display != sDisplay) {//see if the item needs changing
                if (bExpand == (sDisplay == "")) {//see if we are supposed to do it on this thread
                    objCell.style.display = sDisplay;
                    ItemExpandTimeout = setTimeout(function() { adminMenuGroupResize(iLastItem, iMenuGroupID, objTable, bExpand) }, 25)// set to run next iteration
                    break
                }
            }
        }
    }
}


function FindMenuItem(lMenuItemID) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	6/26/06
    * 
    * If we are requesting a click without actually clicking, we need to be able to 
    * reference the cell for it's information.  Here we find the cell and return a 
    * reference for a given menu item id.
    **************************************************************************************/
    var objTable = 'tAdminMenu'.findDotNet('table')
    for (var iRow = 0; iRow < objTable.rows.length; iRow++) {
        var objCell = objTable.rows[iRow].cells[0]
        if (objCell.getAttribute("MenuItemID") == lMenuItemID) {
            return objCell; //return the Cell and get out
        }
    }
}

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ Admin Pages
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/
var iCloseAttachedDocListTimeout = 0;
var iCloseAttachedDocListMiliseconds = new Date();
var InvoiceFloatWindowID= 'invoicelistfloatwindow';//use the same id for them all

function AttachAdditionalInvoiceFiles(event) {
    clearTimeout(iCloseAttachedDocListTimeout);//close the other floaters in that section
    var oDiv = document.getElementById(InvoiceFloatWindowID);
    destroyDiv(0, oDiv); //close any other open ones if we open another
    oDiv = setDiv(null, '/content/affiliate/invoice/AttachAdditionalFiles.aspx', null, 'floatingWindow helpWindow', function(event, oDiv) {setHelpTitle(oDiv, 'Attach Files'); },null,null,'right',RepairFormText);
    oDiv.id = InvoiceFloatWindowID; //use the same ids as the others so that the close each other when opened.
    //alert(document.forms.length);
    //document.all.mycode.value = document.body.innerHTML;
//    for (i = 0; i < document.forms[0].length; i++) {
//        try {
//            alert(document.forms[0][i].nodeName + ': ' + document.forms[0][i].id + ', ' + document.forms[0][i].name);
//        } catch (e) { }
//    }
}

function AttachedFileList(event, sQuery) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	5/18/2010
    * 
    * Load the list of attached files.
    **************************************************************************************/
    var oDiv = document.getElementById(InvoiceFloatWindowID);
    destroyDiv(0, oDiv);//close any other open ones if we open another
    oDiv = setDiv(event, '/content/affiliate/invoice/attachmentlist.aspx', sQuery, 'floatingWindow', RepairFormText);//open the new list
    oDiv.id = InvoiceFloatWindowID;//change the id so we can close it later
    //add a couple handlers so it will close
    if (oDiv.addEventListener)//non ie
    {
        oDiv.addEventListener('mousemove', closeAttachedDocList, false); //cancel close on mouse in
    }
    else if (oDiv.attachEvent) //ie
    {
        oDiv.attachEvent('onmousemove', closeAttachedDocList); //cancel close on mouse in
    }
}

function closeAttachedDocList() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	5/18/2010
    * 
    * when we mouse out of the div we close it unless the mouseout is because we hovered
    * over an image or hyperlink
    **************************************************************************************/
    var date = new Date();
    var closeTime = 3000;
    var miliseconds = date.getMilliseconds()
    if ((date.getTime() - iCloseAttachedDocListMiliseconds.getTime()) > (closeTime - 50)) { //see if it's time to renew our close timer
        iCloseAttachedDocListMiliseconds = date; //reset
        clearTimeout(iCloseAttachedDocListTimeout);
        iCloseAttachedDocListTimeout = destroyDiv(closeTime, document.getElementById(InvoiceFloatWindowID));
    }
}

function UserRoleAdminDetails() {
    var oDiv = 'AdminSiteUerRoles'.findDotNet('div');
    if (oDiv) {
        oDiv.style.display = (oDiv.style.display == 'none' ? '' : 'none');
    }
}

function UserRoleFocus(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	5/4/2010
    * 
    * Remember the value so we know what the user changed from.
    **************************************************************************************/
    var oSel = SourceElement(event);
    oSel.setAttribute('focusArgs', oSel.value);
}

function UserRoleSubmit(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	5/4/2010
    * 
    * When the user updates an item, we save the changes and update the role list accordingly
    **************************************************************************************/
    try {
        var oSel = SourceElement(event);
        var oRow = getAncestor('tr', oSel);
        var oTable = getAncestor('table', oRow);
        var focusArgs = oSel.getAttribute('focusArgs');
        if (focusArgs == '0') {//adding a new item
            var oCopy = DuplicateNode(oRow, 1);
            oRow.parentNode.insertBefore(oCopy, oRow.nextSibling);
            oSel.options[0].text = "-- Remove Role --"; //update the text so the user knows how to remove this role
            UserRoleUpdate(oTable, oSel.value, 0);
        } else if (oSel.value == '0') {
            oRow.parentNode.removeChild(oRow);
            UserRoleUpdate(oTable, focusArgs, 1);
        } else if (oSel.value != +oSel.getAttribute('focusArgs')) {
            UserRoleUpdate(oTable, focusArgs, 1);
            UserRoleUpdate(oTable, oSel.value, 0);
    }
        oSel.setAttribute('focusArgs', oSel.value); //reemmber this for next time
        InitializeFileTreeBranch()
    } catch (e) {
        alert('There was a script error updating the user role.');
    }
}

function UserRoleUpdate(oTable, sArgs, remove) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/29/2010
    * 
    * Check a box, save a value
    **************************************************************************************/
    if (sArgs) {
        var sMessage = XmlHttpPage("UserRolePermissionsUpdate.aspx", null, "&value=" + remove + "&params=" + sArgs);
        if (sMessage && oTable) {
            var oRow = oTable.insertRow(0);//add a row with the message in it.
            var oCell = oRow.insertCell(0);
            innerHTML(oCell, sMessage, false);
            setTimeout(function() { try { oRow.parentNode.removeChild(oRow); } catch (e) { } }, 5000);//remove it after a few seconds
        }
    }
}

function InitializeFileTreeBranch() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/29/2010
    * 
    * For the first load we need to add an server generated ID so we find it and submit it 
    * here.
    **************************************************************************************/
    docLoad();
    var oCell = 'SiteFileTree'.findDotNet('td');
    XmlHttpPage("UserFilePermissionsList.aspx", oCell, "&SiteItem=" + oCell.getAttribute("SiteItem"), null, RepairFormText);
}

function expandFileTreeBranch(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	7/30/09
    * Revision:	4/29/2010
    * 
    * Expands each branch of the tree dynamically with xmlhttp requests
    **************************************************************************************/
    var oSpan = getAncestor('span', SourceElement(event)) ;
    var oRow = getAncestor('tr', oSpan);
    var oNext = oRow.nextSibling;
    if (hasChildBranch(oRow) == 0) {//see if a branch has already been loaded - don't load it again if so
        //first create a blank row to hold the child branches
        innerText(oSpan, '-');
        var oTable = getAncestor('table', oSpan);//the parent table
        var iRowDepth = tableRowDepth(oRow);
        var oNewRow = oTable.insertRow(iRowDepth); //insert after selected
        var oNewCell = oNewRow.insertCell(0); //black place holder to indent the new branch
        var indent = 2;
        oNewCell.colSpan = indent;
        innerHTML(oNewCell, '&nbsp;');
        oNewCell = oNewRow.insertCell(1); //Insert cell to hold child branch table
        oNewCell.colSpan = oRow.cells.length-indent;
        //insert the child branches
        var itemID = oSpan.getAttribute("ItemID")
        if (itemID) XmlHttpPage("UserFilePermissionsList.aspx", oNewCell, "&SiteItem=" + itemID, null, RepairFormText);
        //now update the folder image
        var oCell = getAncestor('td', oSpan);
        updateFolderImage(oRow, "/images/icon/sm/folderopen.gif",1);
    } else {//a branch has already been loaded, show or hide based on the current status
        if (innerText(oSpan) == '-') {
            innerText(oSpan, '+');
            updateFolderImage(oRow, "/images/icon/sm/folderclosed.gif",0);
            if (oNext) oNext.style.display = "none";
        } else {
            innerText(oSpan, '-');
            updateFolderImage(oRow, "/images/icon/sm/folderopen.gif",1);
            if (oNext) oNext.style.display = "";
        }
    }
}

function updateFolderImage(oElement, url, visible) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	7/30/09
    * Revision:	4/29/2010
    * 
    * Updates an image - noteably the folder image from open to closed or vise versa
    **************************************************************************************/
    if (oElement) {
        var oImg = getDescendant('img', oElement);
        if (oImg) {
            oImg.src = url;
        }        
    }
}

function hasChildBranch(oRow) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	8/4/09
    * Revision:	4/29/2010
    * 
    * Test to see if the branch has children
    **************************************************************************************/
    var oNext = oRow.nextSibling;
    var iRetVal = 0;
    if (oNext) {//no next means no child
        if(typeof getDescendant('table',oNext)=='object') iRetVal = 1;
    }
    return iRetVal;
}

function tableRowDepth(oRow) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	8/4/09
    * Revision:	4/29/2010
    * 
    * Count how far down the tree we are  in the table so that we know where to insert a
    * new row.
    **************************************************************************************/
    var oFound = oRow;
    var iDepth = 0;
    while (oFound) {
        oFound = oFound.previousSibling;
        iDepth++;
    }
    return iDepth;
}

function BlankBranchText(sText) {
    /***********************************************************************
    * Author    Daniel Tweddell
    * Revision	8/6/09 
    * 
    * Shows no data instead of a blank in case there is none
    ***********************************************************************/
    if (!sText || sText.trim() == '') sText = "<i>No available data.</i>"
    return sText;
}

function updateLeafValue(event, sArgs) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	4/29/2010
    * 
    * Check a box, save a value
    **************************************************************************************/
    var oBox = SourceElement(event);
    //var sArgs = oBox.getAttribute("arguments")
    if (oBox && sArgs) {
        var sMessage = XmlHttpPage("UserFilePermissionsUpdate.aspx", null, "&value=" + oBox.checked + "&params=" + sArgs);
        if(sMessage){
            var oMessage = 'UpdateMessageContainer'.findDotNet('td');
            //clearTimeout(iMessageTimeout);
            if (oMessage) innerHTML(oMessage, sMessage, false);
           setTimeout(function() { try { innerHTML(oMessage, "&nbsp;", false); } catch (e) { } }, 5000);
        }
    }
}

function TableTabs(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/08/09
    * 
    * Since I will be using tabs in several places in the doc, this is the manager for them.
    * It shows and hides the content based on the clicked tab.  It automatically counts the
    * number of tabs and finds the correct item to display.
    * Usage: Used within a table with 2 rows and 1 column.
    *   1.  first row contains the tab name text
    *       a.  put each tab in it's own span
    *       b.  make the default bold
    *       c.  put TableTabs(event) as the onclick event
    *   2.  second row has divs for each tab content
    *       a.  create a div for each tab content
    *       b.  order them the same as the tabs
    *       c.  make the default visible and the rest display:none
    *   3.  send page and querystring as arguments if you want to load a page in the 
    *       respective div before showing it.
    **************************************************************************************/
    var oTabs = new tabInfo(event)
    if (oTabs) {
        var sDisplay = "none";
        var sBold = "normal";
        for (var iTab = 0; iTab < oTabs.TabCount; iTab++) {
            sDisplay = "none";
            sBold = "normal";
            if (iTab == oTabs.SelectedTab) {
                sDisplay = "";
                sBold = "bold";
            }
            oTabs.TabContentsParent.children[iTab].style.display = sDisplay;            
            oTabs.TabParent.children[iTab].style.fontWeight = sBold;
        }
        if (arguments.length > 1) {//see if we want a page loaded in here
            XmlHttpPage(arguments[1], oTabs.TabContentsParent.children[oTabs.SelectedTab], arguments[2], alignTableHeaders);
        }
    }
}

function tabInfo(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/08/09
    * 
    * Works in conjunction with TableTabs(event)
    * Gathers all the needed information to manipulate the tab structure
    **************************************************************************************/
    try {
        var oTabParent = getAncestor('td', SourceElement(event));
        this.SelectedTab = SelectedTab(event,oTabParent);
        this.TabCount = oTabParent.children.length;
        this.TabParent = oTabParent;
        this.TabContentsParent = getDescendant('td', getAncestor('tr', oTabParent).nextSibling);
    } catch (e) {
        alert('There was an error accessing the tabs, please refresh your page and try again.');
    }
}


function SelectedTab(event,oTabParent) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/08/09
    * 
    * Works in conjunction with TableTabs(event)
    * Finds the selected tab
    **************************************************************************************/
    try {
        var iPlace = 0;
        var oTab = getAncestor('span', SourceElement(event));
        for (var iTab = 0; iTab < oTabParent.children.length; iTab++) {
            if (oTab == oTabParent.children[iTab]) break;
            iPlace++;
        }
        return iPlace;
    } catch (e) {
        return 0;
    }
}

function selectListItem(event) {
    //selects the item on the the list
    oDiv = getAncestor('div', SourceElement(event));
    destroyDiv(0, oDiv);
}

function shipmentDetailsEdit(event) {
    subSectionDetailsEdit(event,"ManageShipmentDetails.aspx");
}

function invoiceDetailsEdit(event) {
    subSectionDetailsEdit(event,"ManageInvoiceDetails.aspx");
}

function subSectionDetailsEdit(event,sLoadPage) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/08/09
    * 
    * Expands the details of a shipment when the row is clicked on
    **************************************************************************************/
    var sBorder = "solid 1px #0B57A3";
    var oSrc = SourceElement(event);
    var sChar = innerText(oSrc);
    var oRow = getAncestor('tr', oSrc);
    var oTable = getAncestor('table', oRow);
    if (sChar == "-") {
        sChar = "+";
        oTable.deleteRow(getCurrentRowDepth(oRow)); //remove the following row
        sBorder = "";
    } else {
        sChar = "-";
        var oNewRow = oTable.insertRow(getCurrentRowDepth(oRow)); //insert after selected
        var oNewCell = oNewRow.insertCell(0);
        innerHTML(oNewCell,"&nbsp;");//create a blank cell to indent
        oNewCell.style.borderLeft = sBorder; //border for recognizable area
        oNewCell.style.borderBottom = sBorder;        
        oNewCell = oNewRow.insertCell(1);//cell for the data
        oNewCell.colSpan = oRow.childNodes.length - 1;//span the remaining cols
        oNewCell.style.borderColor = "";
        oNewCell.style.borderRight = sBorder; //border for recognizable area
        oNewCell.style.borderBottom = sBorder;
        setDiv(event,sLoadPage, null, null, null, oNewCell);
        setSubSectionDetailsEditBgColor(oRow, oNewRow);
    }
    setSubSectionDetailsEditBorder(oRow, sBorder);
    innerText(SourceElement(event), sChar);
}

function setSubSectionDetailsEditBorder(oRow, sBorder) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/09/09
    * 
    * Change the borders of the clicked row
    **************************************************************************************/
    if (oRow.cells) {
        var sDivider = sBorder;
        if(sDivider){//replace the line type with dotted
            sDivider = 'dotted '+sBorder.split(' ').slice(1).join(' ');
        }
        var oCell = oRow.cells[0];
        var iCellCount=oRow.cells.length;
        oCell.style.borderLeft = sBorder;
        oRow.cells[iCellCount-1].style.borderRight = sBorder;
        for (var iCell = 0; iCell < iCellCount; iCell++) {
            oCell = oRow.cells[iCell];
            oCell.style.borderTop = sBorder;
            oCell.style.borderBottom = sDivider;
        }
    }
}

function setSubSectionDetailsEditBgColor(oRow, oNewRow) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/08/09
    * 
    * Make sure the background color matches
    **************************************************************************************/
    if (oRow.currentStyle) {
         oNewRow.style.backgroundColor=oRow.currentStyle['backgroundColor'];
    }

}

function getCurrentRowDepth(oRow) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	8/4/09
    * 
    * Count how far down the tree we are  in the table so that we know where to insert a
    * new row.
    **************************************************************************************/
    var oFound = oRow;
    var iDepth = 0;
    while (oFound) {
        oFound = oFound.previousSibling;
        iDepth++;
    }
    return iDepth;
}

function getCollectionPlanPage(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	8/4/09
    * 
    * For now since we have only one plan this.
    **************************************************************************************/
    var sMunicipality = innerText(SourceElement(event)).trim(true);
    var sPage = sMunicipality+'.aspx';
    if (sMunicipality != 'Washington') sPage='NoPlan.aspx';
    XmlHttpPage('plan/' + sPage, document.getElementById('AdminMunicipalityPlanDataEntryContainer'), '&mun=' + sMunicipality);
}

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ Admin Pages - New Invoice
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/

// a var to hold the invoice layout
var Invoice = { productCol: 1, serviceCol: 2, qtyTypeCol: 3, volumeCol: 4, rateCol: 5, totalsCol: 6, firstRow: 1, lastDataRow: -3, newItemRow: -2 }

function newFileUpload(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	5/11/2010
    * 
    * Adds a new place to save another file
    **************************************************************************************/
    var oRow = getAncestor('tr', SourceElement(event));
    if (oRow) {
        var oCopyRow = oRow.previousSibling;
        var oCopy = DuplicateNode(oCopyRow, 1);
        oRow.parentNode.insertBefore(oCopy, oRow);
        var oControl = getDescendant('input', oCopy);
        if(oControl) {
            var randomItem = new Date(); //seed the randomizer
            oControl.id=randomItem.getTime();//miliseconds = rand enough for no dups on page
        }
    } 
}

function newInvoiceItem(oImage){
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/19/10
    * 
    * Adds a new invoice line item, updates the ids and repopulates the events on the controls
    **************************************************************************************/
    var oTable = getAncestor('table', oImage);
    var oChange;
    if(oTable){
        var oCopyRow = oTable.rows[oTable.rows.length + (Invoice.lastDataRow-1)];
        var oCopy = DuplicateNode(oCopyRow, 1);
        var oLastRow = oTable.rows[oTable.rows.length + (Invoice.newItemRow-1)];
        oLastRow.parentNode.insertBefore(oCopy, oLastRow); 
        try{
            for (var iCell = Invoice.serviceCol; iCell <= Invoice.qtyTypeCol; iCell++) {// the first cells are select lists that need their listeners and values updated
                oChange=findNode(oCopy.cells[iCell],'select');
                oChange.onchange = findNode(oCopyRow.cells[iCell],'select').onchange; //copy the event from the select list
                oChange.value = 0; //clear the value
                oChange.options.length = 1;//clear alll but the first
            }
            oChange=findNode(oCopy.cells[Invoice.volumeCol],'input');
            oChange.onkeyup = findNode(oCopyRow.cells[Invoice.volumeCol],'input').onkeyup; //copy the event from the text box
            oChange.value = ""; //clear the value
            for (var iCell = Invoice.rateCol; iCell <= Invoice.totalsCol; iCell++) {//the last cells are values
                findNode(oCopy.cells[iCell],'#text').nodeValue = '$0.00'; //reset the text
            }
        } catch (e) {
            alert("An error occured while creating a new row.\r\nThe totals may not display correctly\r\nbut the invoice should save normally.\r\nPlease click on the questions link at the bottom and tell us about this error.");
        }
    }
}

function DuplicateNode(oOrig, appendId) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/20/10
    * 
    * Duplicates a node.  Events will not be duplicated in ff and opera so it has to be 
    * done manually in the calling function
    **************************************************************************************/
    var oCopy = oOrig.cloneNode(true);
    UpdateCopiedElement(oCopy, appendId);
    return oCopy;
}

function UpdateCopiedElement(oCopy, appendId) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/20/10
    * 
    * Duplicated nodes maintain the same 'unique' id as the copied node so we have to 
    * change them.  This recursively traverses through each node and child changing an id
    * if there is one.
    **************************************************************************************/
    var oNode;
    if (!appendId) appendId = 1;
    for(var iNode=0;iNode<oCopy.childNodes.length;iNode++){
        oNode = oCopy.childNodes[iNode];
        if (oNode.id) {
            oNode.id = oNode.id + appendId;
        }
        if (oNode.childNodes.length > 0) {
            UpdateCopiedElement(oNode, appendId);
        }
    }
}

function deleteInvoiceItem(oImage) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/20/10
    * 
    * Removes an item from the list of items the user has created.
    **************************************************************************************/
    if (confirm("Are you sure you want to delete this invoice item line?\r\nThis cannot be undone.")) {
        var oRow = getAncestor('tr', oImage);
        var oTable = getAncestor('table', oImage);
        try {
            if (oTable.rows.length == 5) {//this is the only row don't delete, just clear it.
                var oSelect = null;
                for (var iCell = Invoice.productCol; iCell <= Invoice.qtyTypeCol; iCell++) {// the first cells are select lists
                    oSelect = findNode(oRow.cells[iCell], 'select'); //find the select
                    oSelect.value=oSelect.options[0].value //clear the value
                }
                findNode(oRow.cells[Invoice.volumeCol],'input').value = ""; //clear the value
                for (var iCell = Invoice.rateCol; iCell <= Invoice.totalsCol; iCell++) {//the last cells are values
                    findNode(oRow.cells[iCell],'#text').nodeValue = '$0.00'; //reset the text
                }
            } else {
                oRow.parentNode.removeChild(oRow);
            }
        } catch (e) {
        alert("An error occured while deleting this row.\r\nYou can clear the contents manually to achieve the same result.\r\nPlease click on the questions link at the bottom and tell us about this error.");
        }
    }
}

function InvoiceTotals(oItem) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/20/10
    * 
    * Loop through the table and calculate some totals for the bottom of the form.
    **************************************************************************************/
    var oTable = getAncestor('table', oItem); //get the parent table
    var oRow;
    var fVolume = 0;
    var fTotal = 0;
    for (var iRow = Invoice.firstRow; iRow < oTable.rows.length + Invoice.lastDataRow; iRow++) {//skip the title row at the top, the 'add new' row and totals rows at the bottom.
        oRow = oTable.rows[iRow];
        fVolume += makeNumeric(findNode(oRow.cells[Invoice.volumeCol],'input').value,2); //get the volumes
        fTotal += makeNumeric(findNode(oRow.cells[Invoice.totalsCol],'#text').nodeValue,2); //get the totals
    }
    findNode(document.getElementById('InvoiceVolumeTotals'),'#text').nodeValue = format(fVolume);
    findNode(document.getElementById('InvoiceAmountTotals'),'#text').nodeValue = format(fTotal, 'currency');
}

function InvoiceItem(oItem) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/20/10
    * 
    * Go through the invoice item rows and set a reference for each item so that we can
    * change the values as needed.
    **************************************************************************************/
    this.row = getAncestor('tr', oItem);
    if (!this.region) this.region = "selRegion".findDotNet('select');
    if (!this.product) this.product = findNode(this.row.cells[Invoice.productCol], 'select');
    if (!this.service) this.service = findNode(this.row.cells[Invoice.serviceCol], 'select');
    if (!this.qtyType) this.qtyType = findNode(this.row.cells[Invoice.qtyTypeCol], 'select');
    if (!this.volume) this.volume = findNode(this.row.cells[Invoice.volumeCol], 'input');
    if (!this.rate) this.rate = findNode(this.row.cells[Invoice.rateCol], '#text');
    if (!this.total) this.total = findNode(this.row.cells[Invoice.totalsCol], '#text');
    var rate=this.rate;
    var total=this.total;
    this.resetTotals = function() {
        rate.nodeValue = '$0.00';
        total.nodeValue = '$0.00';
        InvoiceTotals(oItem);
    }
}

function invoiceItemProductUpdate(oUpdated) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/22/10
    * 
    * If you change the product, the following select lists are dependant and need be updated 
    * as well and then the totals need to be updated accordingly.
    **************************************************************************************/
    oItem=new InvoiceItem(oUpdated);
    var sQuery='&Reg='+oItem.region.value+'&Prod='+oItem.product.value;
    var sText = XmlHttpPage('/content/dynamiclists/InvoiceService.aspx', null, sQuery);
    var aryOpt = sText.split(';');
    var optNew;
    oItem.service.options.length = 1;//remove all options but the first.
    oItem.qtyType.options.length = 1; //remove all options but the first.
    oItem.resetTotals();
    for (var iOpt = 0; iOpt < aryOpt.length; iOpt++) {
        sText = aryOpt[iOpt].split(":")[0];//reuse ths var
        if (sText) {
            optNew = document.createElement('option');
            optNew.value = aryOpt[iOpt].split(":")[0];
            optNew.text = aryOpt[iOpt].split(":")[1];
            if (document.all) {//ie
                oItem.service.options.add(optNew); //append at end
            } else {//non ie
                oItem.service.options.add(optNew, oItem.service.options.length); //append at end
            }
        }
    }
    SelectFirst(oItem);
}

function invoiceItemServiceUpdate(oUpdated) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/22/10
    * 
    * If you change the service, the following select lists are dependant and need be updated 
    * as well and then the totals need to be updated accordingly.
    **************************************************************************************/
    oItem = new InvoiceItem(oUpdated);
    var sQuery = '&Reg=' + oItem.region.value + '&Prod=' + oItem.product.value + '&Svc=' + oItem.service.value.split(',')[0];
    var sText = XmlHttpPage('/content/dynamiclists/InvoiceQtyType.aspx', null, sQuery);
    var aryOpt = sText.split(';');
    var optNew;
    oItem.qtyType.options.length = 1; //remove all options but the first.
    oItem.resetTotals();
    for (var iOpt = 0; iOpt < aryOpt.length; iOpt++) {
        sText = aryOpt[iOpt].split(":")[0]; //resuse ths var
        if (sText) {
            optNew = document.createElement('option');
            optNew.value = aryOpt[iOpt].split(":")[0];
            optNew.text = aryOpt[iOpt].split(":")[1];
            if (document.all) {//ie
                oItem.qtyType.options.add(optNew); //append at end
            } else {//non ie
                oItem.qtyType.options.add(optNew, oItem.service.options.length); //append at end
            }
        }
    }
    SelectFirst(oItem);
}

function recalcInvoiceItemAmount(oChanged, Item) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/20/10
    * 
    * If you update the volume amount, we need to recalculate the totals.
    **************************************************************************************/
    if (!Item) Item = new InvoiceItem(oChanged);
    var volume = makeNumeric(Item.volume.value);
    Item.volume.value = volume; //reset this in case the user stuck some garbage in there
    var rate = makeNumeric(Item.rate.nodeValue,3);
    Item.total.nodeValue = format(volume * rate, 'currency');
    InvoiceTotals(oChanged);
}

function InvoiceItemRate(Item) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/20/10
    * 
    * The rate and a couple other necessities are in the select list value so that we can
    * use them to save the correct item and update the totals accordingly.
    **************************************************************************************/
    var aryProd = Item.qtyType.value.split(',');
    var rate = 0;
    if (aryProd) {
        rate = parseFloat(aryProd[1]);
    }
    if (!rate) rate = 0;
    return rate;
}

function invoiceItemQtyTypeUpdate(oUpdated) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/20/10
    * 
    * If you change the product, the rate is in the select list value and will update
    * then the totals need to be updated accordingly.
    **************************************************************************************/
    var Item = new InvoiceItem(oUpdated);
    var rate = InvoiceItemRate(Item);
    Item.rate.nodeValue = format(rate, 'currency');
    recalcInvoiceItemAmount(oUpdated, Item);
    //alert(Item.qtyType.innerHTML);
}

function SelectFirst(oItem) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/22/10
    * 
    * Often the select lists will be updated with only one choice.  Rather than forcing
    * the user to manually enter the one thing that displays in each list, we'll select
    * that one for them.
    **************************************************************************************/
    var oSelect;
    for (var iCell = Invoice.productCol; iCell <= Invoice.qtyTypeCol; iCell++) {// the first cells are select lists
        oSelect = findNode(oItem.row.cells[iCell],'select');
        if (oSelect.value.split(",")[0] == 0) {
            if (oSelect.options.length == 2) {//the default option and one extra - in other words only one choice
                oSelect.options[1].selected = true;
                if (oSelect.onchange) {
                    oSelect.onchange(oSelect)
                }
            } else {
                break;//if we can't choose just one, stop here
            }
        }
    }
}

function submitInvoice() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/25/10
    * 
    * Go through the for and create an xml document representing the invoice so we can 
    * save it.
    **************************************************************************************/
    var bSubmit=false;
    if (ValidateSingleFormEntry() && validateDateOrder()) {
        bSubmit=true;
        try{
            var xml='';
            var oTable = document.getElementById("invoiceItemTable");
            var oNode;
            for (var iRow = 0; iRow < oTable.rows.length; iRow++) {
                oRow = oTable.rows[iRow];
                if(findNode(oRow.cells[Invoice.productCol],'select')){//maks sure it's a data row.
                    xml += '<item';//begin the xml node
                    for (var iCell = Invoice.productCol; iCell <= Invoice.totalsCol; iCell++) {// the first cells are select lists that need their listeners and values updated
                        oNode = oRow.cells[iCell]
                        switch (iCell) {
                            case Invoice.productCol:
                                xml += ' product="' + findNode(oNode, 'select').value.split(',')[0] + '" ';
                                //xml += ' product="' + selectedText(findNode(oNode, 'select')) + '" ';
                                break;
                            case Invoice.serviceCol:
                                xml += ' service="' + findNode(oNode, 'select').value.split(',')[0] + '" ';
                                //xml += ' service="' + selectedText(findNode(oNode, 'select')) + '" ';
                                break;
                            case Invoice.qtyTypeCol:
                                xml += ' qtytype="' + findNode(oNode, 'select').value.split(',')[0] + '" ';
                                //xml += ' qtytype="' + selectedText(findNode(oNode, 'select')) + '" ';
                                break;
                            case Invoice.volumeCol:
                                xml += ' volume="' + makeNumeric(findNode(oNode,'input').value, 4) + '" ';
                                break;
                            case Invoice.rateCol:
                                xml += ' rate="' + makeNumeric(findNode(oNode,'#text').nodeValue, 4) + '" ';
                                break;
                        }
                    }
                    xml += '/>\r\n';//complete the xml node
                }
            }
            "invoiceItemList".findDotNet('input').value = encodeURI(encodeURIComponent(xml))
            "hidSaveInvoice".findDotNet('input').value = 1;//a flag to let us know to process this information
        }catch(e){
            alert("There was an error gathering the form data.\r\nPlease check your entries.");
            bSubmit=false;
        }
    }
    return bSubmit;
}

function validateDateOrder() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/26/10
    * 
    * Make sure the from date is not after the to date.
    **************************************************************************************/
    var bValidated = true;
    try{
        var oInvoiceFrom = "InvoiceDateFrom".findDotNet('input')
        var dFrom = new Date(inputValue(oInvoiceFrom));
        var dTo = new Date(inputValue("InvoiceDateTo".findDotNet('input')));
        if (dFrom >= dTo) {
            alert("Your From-Date appears to be after or the same as your To-Date.");
            bValidated = false;
        } 
    }catch(e){}
    return bValidated;
}

function inputValue(oInput) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/25/10
    * 
    * Return the value from an input
    **************************************************************************************/
    var text='';
    if(oInput)text=oInput.value;
    return text;
}

function selectedText(oSelect) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/25/10
    * 
    * Return the selected text from a select.
    **************************************************************************************/
    var text='';
    if(oSelect)text=oSelect.options[oSelect.selectedIndex].text;
    return text;
}

//function SetUserView() {
//    var oSel = 'SiteUserView'.findDotNet('select');
//    if (oSel) {
//        alert(oSel.value);
//    }
//}

//function ChangeInvoiceToAffiliate() {
//    alert ("ChangeInvoiceToAffiliate() line 2037");
//}

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ Consumer Tools 
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/
var geocoder;
var map;
var address;
var directionDisplay;

function ProductModel_Click(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	10/19/09
    * 
    * When the user chooses a product type from the list, we want to fill in the rest of
    * the information
    * Cell order: ConsumerProductTypeDetailID, ModelNumber, ProductTypeDescription, 
                  ProductLength, ProductLengthQuantityID, ProductWidth, ProductWidthQuantityID, 
                  ProductHeight, ProductHeightQuantityID, ProductWeight, ProductWeightQuantityID
    **************************************************************************************/
    var oRow = getAncestor('tr', SourceElement(event));
    for (var iCell = 2; iCell < oRow.cells.length; iCell++) {//the first two are used already
        //try {
            oUpd = oRow.cells[iCell].getAttribute("ControlName").findDotNet("Input");
            if (oUpd) {
                oUpd.value = innerText(oRow.cells[iCell]);
            } else {
                oUpd = oRow.cells[iCell].getAttribute("ControlName").findDotNet("Select-one");
                if (oUpd) {
                    oUpd.value = innerText(oRow.cells[iCell]);
                }
            }
        //} catch (e) { }
    }  
}

function setProductType(event){
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	10/10/09
    * 
    * Save the product Type
    **************************************************************************************/
    var oClicked = SourceElement(event);
    if (!address) address = new addressControls();
    try {
        address.productType.value = innerText(getSibling('label', oClicked));
    } catch (e) { }
}

function addressControls() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	10/9/09
    * 
    * finds the controls since .net had to rename them for me.  Loads values in them if
    * the value was sent
    **************************************************************************************/
//    try {
        if (!this.productType) this.productType = 'hidProductType'.findDotNet('input');
        if (!this.streetAddress) this.streetAddress = 'hidAddress'.findDotNet('input');
        if (!this.city) this.city = 'hidCity'.findDotNet('input');
        if (!this.stateProvince) this.stateProvince = 'hidStateProvince'.findDotNet('input');
        if (!this.postalCode) this.postalCode = 'hidPostalCode'.findDotNet('input');
        if (!this.latitude) this.latitude = 'hidLatitude'.findDotNet('input');
        if (!this.longitude) this.longitude = 'hidLongitude'.findDotNet('input');
        if (!this.fullAddress) this.fullAddress = 'txtInputAddress'.findDotNet('input');
        this.fill = function() {
            if (arguments) {
                var geoResults = arguments[0];
                var point = geoResults.geometry.location;
                address.latitude.value = point.lat();
                address.longitude.value = point.lng();
                address.streetAddress.value = (getAddressPart(geoResults.address_components, 'number') + ' ' + //save a few items
                                      getAddressPart(geoResults.address_components, 'street')).trim();
                address.city.value = getAddressPart(geoResults.address_components, 'city');
                address.stateProvince.value = getAddressPart(geoResults.address_components, 'state');
                address.postalCode.value = getAddressPart(geoResults.address_components, 'postalcode');
                var fullAddress = (address.streetAddress.value + ' ' + address.city.value + ' ' + address.stateProvince.value + ', ' + address.postalCode.value).trim();
                if (fullAddress == ',') fullAddress = "";
                address.fullAddress.value = fullAddress;
//                XmlHttpPage('ConsumerProductType.aspx', document.getElementById("ConsumerDropoffProductsContainer"),
//                    '&city=' + address.city.value +
//                    '&state=' + address.stateProvince.value +
//                    '&postalcode=' + address.postalCode.value, null, RepairFormText);                
            }
        }
        this.validate = function() {
            return Boolean((address.latitude.value) && (address.longitude.value) && (address.stateProvince.value));
        }
        this.message = "We were unable to process the address you entered.\r\nPlease check it before continuing.";
        this.isExecuting = false; //is the geocode get executing - needed so that we can prevent other actions from happening until it's complete
        this.afterExecuting = function() { return null; }; //if executing, this will be assigned the function that needs to wait and then will be run with execution is complete - it's a generic for now
//    } catch (e) {}
}

function geocodeUpdate(event) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	10/6/09
    * 
    * When the user updates their information we get the latitude and longitude for the
    * entered address and so know where to direct their request based on location and
    * services offered.
    **************************************************************************************/
	if (!address) address = new addressControls();
	address.isExecuting = true;
	geocoder = new google.maps.Geocoder();
    geocoder.geocode({ 'address': address.fullAddress.value }, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            address.fill(results[0]);
            address.isExecuting = false;
            address.afterExecuting();
        } else {
            alert(address.message + "\r\n" + status);
            address.isExecuting = false;
        }
    });
}

function getAddressPart(address_components, name) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	10/9/09
    * 
    * Looks through googles address components and grabs us the specific pieces we need
    * if they are there.
    ******************************************************************0********************/
    var component = "";
    var address = null;
    var adminLevel = "";
    switch (name) {
        case 'postalcode':
            adminLevel = 'postal_code';
            break;
        case 'country':
            adminLevel = 'country';
            break;
        case 'state':
            adminLevel = 'administrative_area_level_1';
            break;
        case 'county':
            adminLevel = 'administrative_area_level_2';
            break;
        case 'city':
            adminLevel = 'locality';
            break;
        case 'street':
            adminLevel = 'route';
            break;
        case 'number':
            adminLevel = 'street_number';
            break;
    }            
    for (var iPart = 0; iPart < address_components.length; iPart++) {
        address = address_components[iPart];
        if (address) {
            if (address.types.length>0) {
                if (address.types[0] == adminLevel) {
                    component = address.short_name;
                    break;
                }
            }
        }
    }
    return component;
}

function productTypesVerifyEntry() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	10/1/09
    * 
    * Checks that the user has entered product types.
    **************************************************************************************/
    var bReturn = false;
    var oRadio;
    try{
        oRadio = document.forms[0]['rdProductTypeID'.findDotNet('input').name];
    }catch(e){}
    if (oRadio) {
        for (var iRd = 0; iRd < oRadio.length; iRd++) {
            if (oRadio[iRd].checked == true) {
                bReturn = true;
                break;
            }
        }
    }
    if (bReturn == false) {
        alert("You have not selected any items for recycling.\r\nPlease select one or more items.")
    }
    return bReturn;
}

function productTypeSubmitForm(sAction) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	10/1/09
    * 
    * Submit a form via a hyperlink -before submitting we need to make sure the we are
    * not trying to still get an address.
    **************************************************************************************/
    if (sAction) {
        var form = document.forms[0];
        if (form) form.action = sAction;
        if (!address) address = new addressControls();//see if the object has even been created
        if (address.isExecuting == true) {//see if we are still looking up the address
            address.afterExecuting = productTypeSubmitFormAfterGetExecute; //set the function so it submits after the lookup is complete
        } else {
            if (!address.stateProvince.value && address.fullAddress.value) {//if the hidden controls are not filled and yet there is data in the address control, we need to look it up again.
                geocodeUpdate(event)
                address.afterExecuting = productTypeSubmitFormAfterGetExecute; //set the function so it submits after the lookup is complete
            } else {
                productTypeSubmitFormAfterGetExecute();//all is well, submit
            }     
        }
    }
}

function productTypeSubmitFormAfterGetExecute() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	10/1/09
    * 
    * Submit a form via a hyperlink - first makes sure that all the requisite data is in
    * place.
    **************************************************************************************/
    var form = document.forms[0];
    if (form) {
        if (ValidateSingleFormEntry(form)) {
            if (productTypesVerifyEntry()) {
                if (address.validate()) {//if all is well submit
                    form.submit();
                } else {
                    alert(address.message);
                }
            }
        }
    }
}

function submitValidatedForm(sAction) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/30/09
    * 
    * Submit a form via a hyperlink
    **************************************************************************************/
    if (sAction) {
        var form = document.forms[0];
        Page_ClientValidate();
        Page_ClientValidate();
        if (form) {
            if (ValidateSingleFormEntry(form) && Page_IsValid) {
                form.action = sAction;
                form.submit();
            }
        }
    }
}

function submitForm(sAction) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	9/30/09
    * 
    * Submit a form via a hyperlink
    **************************************************************************************/
    if (sAction) {
        var form = document.forms[0];
        if (form) {
            form.action = sAction;
            form.submit();
        }
    }
}

function selectMapLocation(event) {
    /**************************************************************************************
    * Revision:	10/5/09
    * 
    * Adds move the map center to a map pointer when a letter on the left hand list is clicked
    **************************************************************************************/
    var oClicked = SourceElement(event);
    var oAddress = "";
    var sAddress = "";
    try {
        oAddress = getAncestor('tr', oClicked);
    } catch (e) { }
    if (oAddress) {
        var OrigLatLng = getOriginLatLng();
        var DestLatLng = new google.maps.LatLng(oAddress.getAttribute("Latitude") - 0, oAddress.getAttribute("Longitude") - 0);
        //map.panTo(DestLatLng)
        directionsDisplay = new google.maps.DirectionsRenderer();
        directionsDisplay.setMap(map);
        directionsDisplay.setPanel(document.getElementById("googleMapDirections"));
        var dirRequest = {
            origin: OrigLatLng,  
            destination: DestLatLng,
            travelMode: google.maps.DirectionsTravelMode.DRIVING,
            unitSystem: google.maps.DirectionsUnitSystem.IMPERIAL,  
            provideTripAlternatives: false
        };
        var directionsService = new google.maps.DirectionsService();
        directionsService.route(dirRequest, function(result, status) {
            if (status == google.maps.DirectionsStatus.OK) {
                directionsDisplay.setDirections(result);
            } else {
                alert('We are unable to retrieve any routing information at this time.');
            }
        });
    }
}

function MapDropoffLocations() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	12/2/09
    * 
    * Takes the locations on screen and maps them in relation to the current users location
    * with pointers on each.
    **************************************************************************************/
    var geocoder = new google.maps.Geocoder();
    var latlng = getOriginLatLng();//get the latlng of the current users location
    if (latlng) {
        var myOptions = {
            /*zoom: 10,*/
            center: latlng,
            mapTypeId: google.maps.MapTypeId.ROADMAP

        };
        map = new google.maps.Map(document.getElementById("ConsumerLocationMapContainer"), myOptions);
        geocoder.geocode({ latLng: latlng }, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                createMarker(latlng); //create the user location marker
                markLocations(latlng);
            } else {
                alert(address + ' not found');
            }
        });
    } else {
        innerHTML(document.getElementById('ConsumerLocationMapContainer'),
        "<br/>There was an error retrieving your map.&nbsp;&nbsp;" +
        "If <a href=\"javascript:location.reload(true)\">reloading or refreshing</a> this page does not solve this issue," +
        "Please contact us via the link at the bottom of the page.<br/>" +
        "Thanks<br/>" +
        "MITS Development Team.");
    }
}

function getOriginLatLng() {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	12/2/09
    * 
    * Gets the coords for the users current location.
    **************************************************************************************/
    buildPrototypes();
    var oGeoCode = null;
    var Latitude = 0;
    var Longitude = 0;
    var oAddress = 'txtSearchAddress'.findDotNet('input');
    oGeoCode = 'hidfLatitude'.findDotNet('input');
    if (oGeoCode) {
        Latitude = oGeoCode.value;
    }
    oGeoCode = 'hidfLongitude'.findDotNet('input');
    if (oGeoCode) {
        Longitude = oGeoCode.value;
    }
    if (Latitude != 0 && Longitude != 0) {
        var latlng = new google.maps.LatLng(Latitude, Longitude);
    }
    return latlng;
}

function markLocations(latlng) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	12/2/09
    * 
    * Loops through the list of locations and add markers to each as well as zooming the
    * map so that the all fit.
    **************************************************************************************/
    var map_bounds = new google.maps.LatLngBounds();
    map_bounds.extend(latlng);    //make sure the users location is visible on the map
    var oRows = document.getElementById('ConsumerLocationListContainer').children[0].rows;
    for (var iCurrentRow = 0; iCurrentRow < oRows.length; iCurrentRow++) {
        var currentRow = oRows[iCurrentRow];
        var sTitle = '';
        var sLetter = '';
            if (oRows[iCurrentRow].cells.length>1) {
                sTitle = innerText(oRows[iCurrentRow].cells[1]);
                sLetter = innerText(oRows[iCurrentRow].cells[0]);
            }
            Latitude = currentRow.getAttribute('Latitude');
            Longitude = currentRow.getAttribute('Longitude');
        if (Latitude && Latitude) {
            map_bounds.extend(new google.maps.LatLng(Latitude, Longitude));
            createMarker(new google.maps.LatLng(Latitude, Longitude), sLetter, sTitle);
        }
    }

    map.fitBounds(map_bounds);
}

function createMarker(point, sLetter, sTitle) {
    /**************************************************************************************
    * Revision:	10/8/09
    * 
    * Create a lettered icon for this point using our icon class
    **************************************************************************************/
    //default marker a little different than the others
    var sUrl = 'http://maps.google.com/mapfiles/';
    var icon = new google.maps.MarkerImage(sUrl+'arrow.png');
    var shadow = new google.maps.MarkerImage(sUrl + 'arrowshadow.png');
    if (sLetter) {        
        //var letter = String.fromCharCode("O".charCodeAt(0) + index);//a lettered icon
        icon = new google.maps.MarkerImage('http://www.google.com/mapfiles/marker' + sLetter + '.png');
        shadow = new google.maps.MarkerImage(sUrl + 'shadow50.png');
    }
    markerOptions = { icon: icon, shadow: shadow, position: point, map: map, title: sTitle }; // Set up our MarkerOptions object
    var marker = new google.maps.Marker(markerOptions);
    return marker;
}

/*
▐▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀▌
▐ OEM Tools 
▐▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄█▀█▄▌
*/

function incrementSummaryYear(sDirection) {
    /**************************************************************************************
    * Author:	Daniel Tweddell
    * Revision:	1/8/10
    * 
    * Increment the viewable data year.
    **************************************************************************************/
    var oYear = "SummaryYear".findDotNet('input');
    var iYear=parseInt(oYear.value);
    try {
        iYear = iYear + parseInt(sDirection == 'left' ? -1 : 1);
        oYear.value = iYear;
        document.forms[0].submit();
    } catch (e) {
        alert('An error has occured.  The system was unable to change current year.');
    }
}

