/************************************************************************
* jaf * JUSTIN'S APPLICATION FRAMEWORK  *

Copyright (c) 2007-2008 Justin Grant

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**************************************************************************/

function jaf()
{
    //object references
    this.animate = new jafAnimate();
    this.tool    = new jafTool();
    this.backend = new jafBackend();
    this.check 	 = new jafCheck();
    this.doc     = new jafDoc();
    this.module  = new jafModule();
    this.widget  = new jafModule();
}



/***********************************
* jaf GLOBALS  *
************************************/

var checkMark = "url(jaf/img/checkMark_blue.jpg)";

/* ERROR CATCHING */
var erColor = '#336699';
var erCatch = new Array();
var erMsgs  = new Array();//erMsgs['methodKey']="message";

erMsgs['bDoor']     = "Ajax backDoor - There was an error processing a backend request.";
erMsgs['bTalk']     = "Ajax backTalk - @param `sendObj` is not in the correct format. Send To Reference: ";
erMsgs['bXML']      = "Ajax backTranslate_XML - @param `xmlObj` has no data. Send To Reference: ";
erMsgs['dObjID']    = "docObj - @param `id` is required for any Document Object. Type Reference: ";
erMsgs['dObjApd']   = "docObj - @param `html` is not in the correct format. Id: ";
erMsgs['docLink']   = "docLink - @param `html` is required as a display object. Id: ";
erMsgs['docImage']  = "docImage - @param `source` path is required to create an image object. Id: ";
erMsgs['sibReplace']= "docReplaceChildren - @param `html` is not in the correct format. ParentId:";
erMsgs['LText']     = "moduleLoadingText - @param `parentObj` is not a proper dom element. Id: ";

/* TESTING */
var ajaxProdScript = '_ajaxRating.php'; //default: false;
var ajaxTestScript = '/jaf/php/_ajaxTest.php';
var ajaxTestXML    = '/jaf/jaf.test.xml';

/* RATING SPECS */
var ratingTitles = new Array(); //!IMPORTANT! - for 5 scale only.
ratingTitles[1] = "Yuck!";
ratingTitles[2] = "I didn't like it.";
ratingTitles[3] = "It was ok.";
ratingTitles[4] = "It was good.";
ratingTitles[5] = "I'll add it to my list of favorites!";

var ratingObj = new Array();
ratingObj['size']     = 18;
ratingObj['spacer']   = 0;
ratingObj['blank']    = 'url(img/drink_noRating.jpg)'; //default url(/jaf/img/star_none.png)
ratingObj['average']  = 'url(img/drink_avgRating.jpg)';
ratingObj['user']     = 'url(img/drink_userRating.jpg)';

var ratingSpecs = new Array();
ratingSpecs['scale']    = 5;
ratingSpecs['stars']    = ratingObj;
ratingSpecs['titles']   = ratingTitles;
ratingSpecs['backend']  = (!ajaxProdScript)? ajaxTestScript:ajaxProdScript; //defaults to testScript

/* PROGRESS ITEM SPECS */
var loadTxtSpecs = new Array(); //loading text
loadTxtSpecs['node']       = document.createTextNode('');
loadTxtSpecs['increment']  = 1;
loadTxtSpecs['iterations'] = 3;
loadTxtSpecs['text']       = 'Loading';

/* FADE EFFECT SPECS */
var fadeSpecs = new Array();
fadeSpecs['fadeIn'] = true;
fadeSpecs['obj']    = new Object();


/* BACKEND RESPONSE HOLDERS */
var xmlResponse  = new Array();
var ymlResponse  = new Array();
var jsonResponse = new Array();


//Message Constants
EMAIL_INVALID       = "Email Address is Empty or Invalid";
EMAIL_CHK_FAILED    = "Email Confirmation Failed";

PASSWORD_MIN_LENGTH     = 8;
PASSWORD_LENGTH_FAILED  = "Password must be at least "+PASSWORD_MIN_LENGTH+" characters.";
PASSWORD_chk_FAILED     = "New passwords were not identical.";
PASSWORD_INVALID        = "Passwords may not contain spaces or special characters.";

PHONE_INVALID           = "Phone is incomplete or invalid.";

LOGIN_FAIL              = 'Email Address and/or Password is invalid';

REQUIRED_FIELD          = "Required Fields Missing";

CHANGEPASSWORD_FAIL     = "Your Original Password was Incorrect.";
CHANGEPASSWORD_OK       = "Your Password has been changed.";

CHANGEEMAIL_FAIL        = "Your Password was Incorrect.";
CHANGEEMAIL_OK          = "Your Email address has been changed.";

/**
* STANDARD GET ELEMENT (BY ID)
*
* @param {string} id
* @return {object}
*/
function getElement(id){return document.getElementById(id); }
var getObj = getElement;

//BROWSER DETECTION - initial stage
var operaBrowser = false;
if(navigator.userAgent.indexOf('Opera')>=0)operaBrowser=1;


/************************************
* jaf AJAX INTERFACE  *
*************************************/
function jafBackend()
{
    this.door  = backDoor;
    this.talk  = backTalk;
    this.tXML  = backTranslate_XML;
    /*this.tJSON = backTranslate_JSON;   //for future use. */
}

/**
* AJAX INTERFACE
*/
function backDoor()
{
    try { this.request = new XMLHttpRequest(); } //standard browsers
    catch (standard_fail)
    {
        try { this.request = new ActiveXObject("Microsoft.XMLHTTP");} //current IE
        catch (currentIE_fail)
        {
            try { this.request = new ActiveXObject("Msxml2.XMLHTTP"); } //old IE
            catch (oldIE_fail)
            {
                this.request = false;
                erCatch.push(erMsgs['bDoor']);
            }
        }
    }
}

/**
* AJAX BACKEND COMMUNICATION
*
* @param {string}backendURL - location of script or xml file
* @param {object}sendObj    - an array or.. string in format: 'postName=postValue' + '&postName2=postValue2' for sending to backend
* @param {object}actionFn   - function to execute upon success
* @param {boolean}synchronous (optional) in case of synchronous results need
* @return {object}rqst      - should contain the xmlhttp object generated by backDoor() to allow additional operations outside of jaf.
*/
function backTalk(backendURL, sendObj, actionFn, synchronous)
{
    var entry  = new backDoor();
    var rqst   = entry.request;
    var async  = !checkIsSet(synchronous);
    var gResponse;

    if(checkIsArray(sendObj))
    {
        var sendString = '';
        for(var key in sendObj)
        {
            if(sendString!=''){sendString+='&'; }

            sendString += key+'='+sendObj[key];
        }
        sendObj = sendString;
    }
    /* MODIFIED TO ALLOW 0-ARG POST
	else if(sendObj.toString().search('=')==-1)
    {
        erCatch.push(erMsgs['bTalk']+backendURL);
        return false;
    }
	*/

    rqst.open("POST", backendURL, async);
    rqst.onreadystatechange = function ()
    {
        if(rqst.readyState == 4)
        {
            gResponse = (checkIsXML(rqst.responseXML))? backTranslate_XML(rqst.responseXML):rqst.responseText;
            actionFn(gResponse);
        }
    }
    rqst.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    rqst.send(sendObj); //.send() needs to incur, but doesn't need the send object
	
	return rqst;
}

/**
* XML TRANSLATOR FOR AN XML RESPONSE
*
* @param {object} xmlObj
* @param {string} backendURL
* @param {string} parentKey - to prepend to the childname to represent the dimension of the array while maintaining 1-dimension
* @return {array} xmlResponse
*/
function backTranslate_XML(xmlObj, backendURL, parentKey)
{
    if(checkIsSet(backendURL))
    {
        if(!xmlObj.hasChildNodes())
        {
            erCatch.push(erMsgs['bXML']+backendURL);
            return false;
        }
        xmlResponse = new Array();
    }

    var xmlChildren = xmlObj.childNodes;
    var keyPrefix   = (checkIsSet(parentKey))? parentKey+'.':'';
    for(var childKey=0; childKey<xmlChildren.length; childKey++)
    {
        var xmlChild = xmlChildren[childKey];
        if(xmlChild.nodeType==1)
        {
            var childName = keyPrefix + xmlChild.nodeName;

            if(xmlChild.childNodes.length>1)
            {
                xmlResponse[childName] = new Array();
                xmlResponse[childName].push(backTranslate_XML(xmlChild, null, childName));
            }
            else
            {   xmlResponse[childName] = xmlChild.childNodes[0].nodeValue;  }
        }
    }
    return xmlResponse;
}



/************************************
* jaf CHECKING & DEBUGGING  *
*************************************/
function jafCheck()
{
    this.enter      = checkEnter;
    this.errors     = checkErrors;
    this.isArray   	= checkIsArray;
    this.isDefined 	= checkIsDefined;
    this.isDocObj  	= checkIsDocObj;
    this.isObject  	= checkIsObject;
    this.isSet  	= checkIsSet;
    this.isString  	= checkIsString;
    this.objDump    = checkObjectDump;
    this.htmlDump   = checkObjectDump_html;
    this.type 	   	= getType;
}

/**
* CHECK FOR THE TYPE OF AN ENTRY OBJECT
* @param {object} obj
* @return {string}objType
*/
function getType(obj)
{
    var objType = typeof obj;

    if(obj == undefined)
    {
        objType = "undefined";
    }
    else if(obj == null)
    {
        objType = "null";
    }
    else if(obj.setType != undefined && typeof(obj.setType) == 'String')
    {
        objType = obj.setType;
    }
    else if( obj.nodeType == 1)
    {
        objType = "domelement";
    }
    else //check for an array
    {
        if( obj.constructor!=null && obj.constructor==Array)
        {
            objType = "array";
        }
        else if(objType == "function" && (/^\[object.*\]$/i).test(obj.toString()))
        {
            objType = "object";
        }
    }
    //if none of the above conditions are met, the standard type will return
    return objType;
}
/**
* CHECK OBJECT TO SEE IF IT'S AN ARRAY
* @param {object} obj
* @return {boolean} 'return comparison results'
*/
function checkIsArray(obj){ return (getType(obj).toLowerCase() == 'array'); }
/**
* CHECK OBJECT TO SEE IF IT EXISTS AND IS NOT NULL
* @param {object} obj
* @return {boolean} 'return comparison results'
*/
function checkIsDefined(obj)
{
    var objType = getType(obj);
    return (objType != 'undefined' && objType != 'null');
}
/**
* CHECK OBJECT TO SEE IF IT'S A DOM OBJECT
* @param {object} obj
* @return {boolean} 'return comparison results'
*/
function checkIsDocObj(obj){ return (getType(obj).search('dom')!=-1); }
/**
* CHECK OBJECT TO SEE IF IT'S AN OBJECT
* @param {object} obj
* @return {boolean} 'return comparison results'
*/
function checkIsObject(obj){ return (getType(obj).toLowerCase() == 'object'); }
/**
* CHECK OBJECT TO SEE IF IT'S A STRING
* @param {object} obj
* @return {boolean} 'return comparison results'
*/
function checkIsString(obj){ return (getType(obj).toLowerCase() == 'string'); }
/**
* FORCE OBJECT TO BOOLEAN CHECK - INTERPRET NULL & UNDEFINED AS FALSE
* @param {object} obj
* @return {boolean} 'return comparison results'
*/
function checkIsSet(obj){ return (checkIsDefined(obj) && obj!=false); }
/**
* CHECK OBJECT TO SEE IF IT'S A NUMBER
* @param {object} obj
* @return {boolean} 'return comparison results'
*/
function checkIsNumeric(obj){ return !obj.NaN; }
/**
* CHECK OBJECT TO SEE IF IT'S AN XML OBJECT
* @param {object} obj
* @return {boolean} 'return comparison results'
*/
function checkIsXML(obj){ return (checkIsDefined(obj) && obj.childNodes.length>0); }

/**
* CHECK ERROR CATCHER FOR ERRORS
*/
function checkErrors()
{
    if(erCatch.length>0)
    { alert(erCatch.join("\n")); }
}

/**
* DUMP THE CONTENTS OF AN OBJECT/ARRAY INTO A STRING
* @param {object} obj
* @return {string} dumpString
*/
function checkObjectDump(obj)
{
    var z=0;
    var dumpString = '';

    for(var key in obj)
    {
        if(z<obj.length)
        {
            var item = obj[key];
            dumpString +=  key+": "+item+"\n";
        }
        z++;
    }
    return dumpString;
}

/**
* DUMP THE CONTENTS OF AN HTML OBJECT
* @param {object} obj
* @return {string} dumpString
*/
function checkObjectDump_html(obj)
{
    var dumpString = '';
    if (obj != null && obj.hasChildNodes())
    {
        var children = obj.childNodes;
        for(var key in children)
        {
            var child = children[key];

            if (child.childNodes)        { dumpString += checkObjectDump_html(child); }
            if (child.nodeValue != null) { dumpString += child.nodeValue; }
        }
    }
    return dumpString;
}

/**
* Confirms whether the Enter key was pressed (US-Ascii keyboard specific)
* @param {event} e The event object to look at to decide if the key was pressed.
* @return true if 'Enter' key was pressed, false otherwise.
* @type boolean
*/
function checkEnter(e)
{
    var key = null;

    if(e.which) { key = e.which;   }//Standard
    else        { key = e.keyCode; }//IE

    if(key == 13) { return true; }
    return false;
}


/************************************
* jaf DOM CREATION  *
*************************************/
function jafDoc()
{
    this.obj = documentObject;
    this.lnk = documentLink;
    this.img = documentImage;

    this.opacity         = documentOpacity;
    this.removeChildren  = documentRemoveChildren;
    this.replaceChildren = documentReplaceChildren;
    this.toggleCheckbox  = documentToggleCheckbox;
}

/**
* BASIC DOM OBJECT
* @param {string}id
* @param {string}type i.e('div', 'span', 'p')
* @param {string}cssClass
* @param {object}html i.e.(text or html)
*/
function documentObject(id, type, cssClass, content, isHTML)
{
    if(!checkIsString(id))
    {
        erCatch.push(erMsgs['dObjID']+type);
        return false;
    }
    this.obj = document.createElement((checkIsString(type))? type:'div');

    this.obj.id   = id;
    this.obj.name = id;
    this.setType  = 'domConstruct';

    if(checkIsString(cssClass)){this.obj.className = cssClass; }
    if(checkIsDefined(content))
    {
        if(checkIsString(content))
        {
            if(isHTML)
            {   this.obj.innerHTML = content;   }
            else
            {
                content = document.createTextNode(unescape(content));
                this.obj.appendChild(content);
            }
        }
        else if(!checkIsDocObj(content))
        {
            erCatch.push(erMsgs['dObjApd']+id);
            return;
        }
    }
}

/**
* HREF LINK OBJECT
* @param {string}id
* @param {string}url i.e('http://www.site.com')
* @param {string}cssClass
* @param {object}html i.e.(text or html)
*/
function documentLink(id, html, url, cssClass)
{
    var domObj = new documentObject(id ,'a', cssClass, html);

    this.obj = domObj.obj;
    
    this.obj.href = (checkIsString(url))? url:'';
    this.obj.style.borderWidth = '0px';
    this.obj.style.display     = 'block';
}

/**
* HREF IMAGE OBJECT
* @param {string}id
* @param {string}source i.e('/images/image.png')
* @param {string}cssClass
*/
function documentImage(id, source, cssClass)
{
    if(!checkIsDefined(source))
    {
        erCatch.push(erMsgs['docImage']+id);
        return false;
    }
    var domObj = new documentObject(id ,'img', cssClass);

    domObj.obj.src = (checkIsString(source))? source:false;

    domObj.obj.style.borderWidth = '0px';

    this.obj = domObj.obj;
}

/**
* SET TRANSPARENCY OF AN OBJECT
*
* @param {object} obj
* @param {int} opacity
*/
function documentOpacity(obj, opacity)
{
    obj.style.opacity    = opacity/100;
    obj.style.MozOpacity = opacity/100;
    obj.style.filter     = "alpha(opacity="+opacity+")";
}

/**
* REMOVE ALL OF A PARENTS CHILDREN
* @param {object} obj
* @return {boolean} 'return true if success'
*/
function documentRemoveChildren(obj)
{
    if(obj.hasChildNodes())
    {
        while(obj.childNodes.length>0)
        {
            obj.removeChild(obj.firstChild);
        }
    }
}

/**
* REPLACE ALL OF A PARENTS CHILDREN
* @param {object} obj
* @param {object} html - replacement object
* @return {boolean} 'return true if success'
*/
function documentReplaceChildren(obj, html)
{
    if(checkIsString(html))
    {
        html = document.createTextNode(html);
    }
    else if(!checkIsDocObj(html))
    {
        erCatch.push(erMsgs['sibReplace']+obj.id);
        return false;
    }
    documentRemoveChildren(obj);
    obj.appendChild(html);
}

/**
* STYLIZED CHECKBOX TO REPLACE STANDARD HTML CHECKBOX
* - WORKS IN CONJUNCTION WITH jaf.php: $jaf->dom->hidden() field created
*
* @param {object} displayObj - reference to self from the styled div (this) -
* @param {string} checkValueId - reference to hidden input tag for sending response to the backend -
*/
function documentToggleCheckbox(displayObj, checkValueId)
{
    var obj = document.getElementById(checkValueId);
    obj.value = (obj.value==0)? 1:0;

    displayObj.style.backgroundImage = (obj.value==0)? 'none':checkMark;
}

/**
* disallows submitting of forms from field
* 
*/ 
function documentFormBlock(onEnter)
{
    if(checkEnter(onEnter)) return false;
}

/**
* VALIDATE AND SUBMIT FORM
*
* Naming Conventions:
*
* FIELDS
* fieldID           = formId  + '_' + `UniqueIdentifier`;
* email field       = formId  + '_email'
* password field    = formId  + '_pwd'
* new pword field   = formId  + '_pwd_new'
* phone             = formId  + '_phone'
*
* REQUIRE FIELD
* requiredField     = `uniquePrefix`  + '_rqd'
*
* FIELD CONFIRMATION
* fieldConfirm      = fieldID + '_chk'
*
* ALERTS
* formMessageHolder = formId  + '_msg'
* fieldText         = fieldID + '_txt'
*
* @param {domelement} formId  ::  The dom object that contains the form.
* @param {boolean} onEnter :: (if true) while in this element check event for return button press.  
* @param {boolean} isChecked :: default state
*/
function documentFormSubmit(formId, onEnter, callback)
{
    var emailError;
    var pwdError;
    var rqdError;
    var erFocus = false;

    /**
    * MANAGE FIELD HEADER ON ERROR EVENT
    * @param object fieldObj (required)
    * @param boolean isError (required)
    * @param boolean multipleFields (optional) - i.e. phone numbers with separate fields for area code and prefix
    */
    _erNotice = function(fieldObj, isError, multipleFields)
    {
        if(fieldObj)
        {
            if(!erFocus && isError)
            {
                if(typeof(fieldObj.select) == 'function')    
                    fieldObj.select();
                else
                    fieldObj.focus();
                    
                erFocus = true;
            }
        
            var headerId  = (multipleFields==true)? (fieldObj.id).replace(/[0-9]/,'') : fieldObj.id ;
            headerId += '_txt';
            
            fieldObj.style.borderColor = (isError)? erColor : '' ;
            
            if(getObj(headerId)!=null) 
            {  
                getObj(headerId).style.color = (isError)? erColor : '' ;
            }
        }
    }

    /**
    * CHECK EMAIL ADDRESS - report error
    * @param object emailObj (required)
    */
    _CheckEmail = function(emailObj)
    {
        if(emailObj)
        {
            var error       = false;
            var confirmObj  = getObj(emailObj.id+'_chk');

            if(confirmObj && confirmObj.value != emailObj.value)
            {
                formErrors.push(EMAIL_CHK_FAILED);
                error = true;
            }
            else
            {
                var emailFilter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;

                if(emailFilter.test(emailObj.value)!=1)
                {
                    //add error to error list
                    emailError = error = true;
                }
            }
            //manage field header accordingly
            _erNotice(confirmObj, error);
            _erNotice(emailObj, error);
        }
    }

    /**
    * CHECK PASSWORD FIELDS
    * - Check length
    * - Check for confirmation field
    * 
    * @param boolean pwdObj (required)
    * @param string pwdStatus (optional)
    */
    _CheckPassword = function(pwdObj, pwdStatus)
    {
        if(pwdObj)
        {
            var error        = false;
            var pwdLength    = pwdObj.value.length;
            var confirmObj   = getObj(pwdObj.id+'_chk');

            if(confirmObj && confirmObj.value != pwdObj.value)
            {
                error = PASSWORD_chk_FAILED;
            }
            else if(pwdLength < PASSWORD_MIN_LENGTH)
            {
                error = PASSWORD_LENGTH_FAILED;
            }

            if(error)
            {
                if(pwdStatus == '_new' && error.search('New ')==-1)
                {
                    error = error.replace('Password', 'New Password');
                }
                pwdError = error;
            }

            _erNotice(pwdObj, error);
            _erNotice(confirmObj, error);
        }
    }

    /**
    * CHECK REQUIRED FIELD
    * @param object rqdObj (required)
    */
    _CheckRequired = function(rqdObj)
    {
        if(rqdObj)
        {
            var error = false;
            /*
            var header = getObj(rqdObj.id+'_txt');
            var headerTxt = header.innerHTML;
            */
            
            if(rqdObj.value=='' || rqdObj.value.search('Enter')!=-1)
            {
                //if(headerTxt.search(/[*]/)==-1){ header.innerHTML = headerTxt+'*';}
                rqdError = error = true;
            }
            /*else
            {
                header.innerHTML= headerTxt.replace('*','');
            }*/

            _erNotice(rqdObj, error);
        }
    }

    /**
    * CHECK PHONE
    * checks for numeric entry
    * checks for proper length depending on entry position
    *
    * @param object phoneObj (required)
    */
    _CheckPhone = function(phoneObj)
    {
        if(phoneObj)
        {
            //for later
            //_erNotice(phoneObj, phoneError, true);
        }
    }

    var gSubmitted;
    if(onEnter!=null && !checkEnter(onEnter)){ return false; }

    var formDOM  = getObj(formId);

    //Validate  
    if(checkIsDocObj(formDOM) && !checkIsDefined(gSubmitted))
    { 
        formErrors = new Array();
        pwdFields  = new Array();

        var validNodes   = "INPUT::SELECT::TEXTAREA";
        var formElements = formDOM.elements;


        //multipleField Errors
        var rqdError   = false;
        var emailError = false;
        var pwdError   = false;

        for(var id in formElements)
        {  
        
            var element = formElements[id];
            var isValid = element!=null && element.nodeName!=null && validNodes.search(element.nodeName)!=-1;

            if(isValid && element.id.search('_chk')==-1 && ( element.parentNode.parentNode.parentNode.parentNode.id.search(formDOM.id)!=-1 || callback == null ) )
            {
            
                if(element.id.search('_rqd')!=-1)//fieldID: uniquePrefix + '_rqd'
                {
                    _CheckRequired(element);
                }
                else if(element.id.search('_email')!=-1)//fieldID: formId + '_email'
                {
                    _CheckEmail(element);
                }
                else if(element.id.search('_pwd')!=-1)//fieldID: formId + '_pwd'
                {
                    pwdFields[id] = element;
                    var check_chk = element.id+'_chk';
                    if(getObj(check_chk)) pwdFields[check_chk] = getObj(check_chk);
                    
                    var pwdStatus = (element.id.search('_new')!=-1)? '_new':'';
                    
                    _CheckPassword(element, pwdStatus);
                    
                    if(pwdError){ formErrors.push(pwdError); }
                }
                else if (element.id.search('_phone')!=-1)
                {
                    _CheckPhone(element);
                }
            }
            
            if(element != null && element.parentNode != undefined)
            {
                //alert(element.parentNode.parentNode.parentNode.parentNode.id.search(formDOM.id)==-1);
            }
        }
        //multipleField errors
        if(rqdError)  { formErrors.push(REQUIRED_FIELD); }
        if(emailError){ formErrors.push(EMAIL_INVALID);  }

        if(formErrors.length > 0) //check for errors
        {
            documentMessage(formId, formErrors);
            return false;
        }
        else
        {
            if(pwdFields.length)
            {
                for(var i in pwdFields)
                {
                    //aaaaaaaa
                    pwdFields[i].value = toolBase64(pwdFields[i].value);
                    //alert(pwdFields[i].value);
                }
            }
            
            documentMessage(formId);
            
            if(callback == null) 
            {
                formDOM.submit();
            }
            else
            {
                if( callback == true )
				{
					return true;
				}
				callback( callback );
            }
            
            gSubmitted=true;
        }
    }
}



/**
* RETURNS STYLED MESSAGE TO SPECIFIED PARENT OBJECT
* @param {string} parentID The forms base ID to find the forms message holder.
* @param {mixed} messsage - object of 1 message or list of messages.
* @param {string} status If 'fail' then the text will be styled in error format, otherwise in a success format.
*/
function documentMessage(formId, messsage)
{
	var msg_counter = 0;
	var messsage_id = formId+'_msg';
	
    _AppendMessage  = function(msgObj, msg)
    {
        var msgText = new documentObject(formId+'_msg_txt_'+msg_counter++, null, 'msg msg_'+formId, msg);
        msgText.obj.style.width = '100%';
        msgText.obj.style.color = erColor;
        msgObj.appendChild(msgText.obj);

    }//end append message method

    if(checkIsDocObj(getObj(messsage_id)))
    {
        var msgObj = getObj(messsage_id); //check for form message holder
        animateFade(msgObj);
    }
 	else { return null; }

    //strip current contents of message holder
    documentRemoveChildren(msgObj);

    //append messages or message
    if(checkIsArray(messsage) && messsage.length > 0)
    {
        for(var i = 0; i < messsage.length; i++)
        { _AppendMessage(msgObj, messsage[i]); }
    }
    else if(checkIsString(messsage))
    { _AppendMessage(msgObj, messsage); }
}


/**
* reset form except for the hidden fields
* 
* 
*/ 
function documentFormClear(formId)
{
    var formDOM  = getObj(formId);
    
    if(checkIsDocObj(formDOM))
    {
        var validNodeTypes   = "text::select::textarea";
        var formElements = formDOM.elements;

        for(var id in formElements)
        {
            var element = formElements[id];
            var isValid = element!=null && element.nodeName!=null && validNodeTypes.search(element.type)!=-1;
            
            if(isValid)
            {
                element.value = '';
            }
        }
    }
}

/************************************
* jaf TOOLS  *
*************************************/
function jafTool()
{
    this.base64      = toolBase64;
    this.cookieJar   = toolCookieJar;
    this.cookieMaker = toolCookieMake;
    this.cookieTrash = toolCookieTrash;
    this.getItemId   = toolGetItemId;
    this.toggle      = toolToggleBool;
    this.mousePos    = toolMousePosition;
    //this.md5       = toolMD5; add later
}

/**
* ENCODE AND DECODE BETWEEN ASCII AND BASE 64
* @param {string} input - string to encode/decode.
* @param {boolean} decode (Optional) - if true, it will expect a base64 to decode
* @return {string} output - translated string
*/
function toolBase64(input, decode)
{
    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var output = "";
    var character1, character2, character3;
    var encoded1, encoded2, encoded3, encoded4;
    var i = 0;

    if(!checkIsSet(decode))//encode
    {
        do //piece through and convert string to base64
        {
            character1 = input.charCodeAt(i++);
            character2 = input.charCodeAt(i++);
            character3 = input.charCodeAt(i++);

            encoded1 = character1 >> 2;
            encoded2 = ((character1 & 3) << 4) | (character2 >> 4);
            encoded3 = ((character2 & 15) << 2) | (character3 >> 6);
            encoded4 = character3 & 63;

            if(isNaN(character2))       { encoded3 = encoded4 = 64; }
            else if(isNaN(character3))  { encoded4 = 64;            }

            output += keyStr.charAt(encoded1) + keyStr.charAt(encoded2) + keyStr.charAt(encoded3) + keyStr.charAt(encoded4);
            // output +
        }
        while (i < input.length);
    }
    else//decode
    {
        // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        do //piece through and break down converted string back to ascii
        {
            encoded1 = keyStr.indexOf(input.charAt(i++));
            encoded2 = keyStr.indexOf(input.charAt(i++));
            encoded3 = keyStr.indexOf(input.charAt(i++));
            encoded4 = keyStr.indexOf(input.charAt(i++));

            character1 = (encoded1 << 2) | (encoded2 >> 4);
            character2 = ((encoded2 & 15) << 4) | (encoded3 >> 2);
            character3 = ((encoded3 & 3) << 6) | encoded4;

            output += String.fromCharCode(character1);// output +

            if (encoded3 != 64){ output += String.fromCharCode(character2); }// output +
            if (encoded4 != 64){ output += String.fromCharCode(character3); }// output +
        }
        while (i < input.length);
    }
    return output;
}

/**
* LIST OF CURRENT COOKIES
* @return {array}cookies - associative array of current cookies
*/
function toolCookieJar()
{
    var cookies = new Array();

    var cookieList = document.cookie.split(';');
    for(var c in cookieList)
	
    {
		alert('hello');
		var cookie = cookieList[c].split('=');
        cookies[cookie[0].toString()] = cookie[1];
    }
    return cookies;
}


/**
* MAKE A BROWSER COOKIE
* @param {string} id    - cookie index
* @param {string} value - cookie value
* @param {int} days     - life of the cookie
*/
function toolCookieMake(id, value, days)
{
    document.cookie = id + "=" + escape(value);
    if(checkIsSet(days) && !isNaN(days))
    {
        var life = new Date();
        life.setDate(life.getDate()+days);

        document.cookie += "; expires=" + life.toUTCString() + ";";
    }
    document.cookie += ";path=./";
	//alert(document.cookie);
}

/**
* THROW AWAY A COOKIE
*
* @param {mixed} id - cookie index
*/
function toolCookieTrash(cookieObj, value)
{
    if(checkIsArray(cookieObj))
    {
        for(var key in cookieObj)
        {
            toolCookieMake(key,value,-1);
        }
        return true;
    }
    else if(checkIsString(cookieObj))
    {
        toolCookieMake(cookieObj, value,-1);
        return true;
    }
    else return false;
}

/**
* EXTRACT ITEMS ID FROM THE OBJECT ID STRING
* !IMPORTANT! - Assumes and relies on the object's ID is in the format: itemId+'_ObjectSuffix'
* @return {string} `parsed id string`
*/
function toolGetItemId(parentObj)
{
    if(parentObj==null){ return 0; }

    var idArray = (parentObj.id).split('_');
    return idArray[0];
}


/**
* TOGGLE OBJECT VALUE (TRUE/FALSE) or TOGGLE BOOLEAN VALUE
*
* @param {mixed} obj
*/
function toolToggleBool(obj)
{
    if(checkIsObject(obj))
    {
        obj.value = !obj.value;
    }
    else{ return !obj; }
}

/**
* Grabs the current mouse location within the browser window including if the window was scrolled.
* @type array
* @return associative Array containing the indices 'x' and 'y' which correspond to each axis mouse location
*/
function toolMousePosition(e) 
{
    if(!e) var e = window.event;
    var position = new Array(2);
    position['x']= 1;
    position['y']= 1;  
      
    //IE 6
    /*if(e.pageX || e.pageY)
    {
        position['x'] = e.pageX;
        position['y'] = e.pageY;
    }
    else */
    if(e.clientX || e.clientY)
    {
        position['x'] = e.clientX + (document.documentElement.scrollLeft ?  document.documentElement.scrollLeft : document.body.scrollLeft);
        position['y'] = e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
    }
    else
    {
        alert('Firefox needs event parameter in event function call');
    }
    return position;
}






/************************************
* jaf ANIMATION  *
*************************************/
function jafAnimate()
{
    this.fade   = animateFade;
    this.hover  = animateHover;
    this.move   = animateMove;
}

/**
* FADE AFFECT
*
* @param {object} obj
* @param {boolean} out - will default to null == false == 'fade in'
*/
function animateFade(obj, out)
{
    if(checkIsSet(obj))
    {
        gFadeStep = 0;
        fadeSpecs['fadeIn'] = !checkIsSet(out);
        fadeSpecs['obj']    = obj; //(checkIsObject(obj.obj))? obj.obj:
    }

    if(gFadeStep <= 100)
    {
        documentOpacity(fadeSpecs['obj'], (fadeSpecs['fadeIn'])? gFadeStep:100-gFadeStep);
        gFadeStep+=5;
        t = setTimeout("animateFade()", 5);
        /*
        gFadeStep+=20;
        t = setTimeout("animateFade()", 10);
        */
    }
    else
    {
        //documentOpacity(fadeSpecs['obj'], (fadeSpecs['fadeIn'])? 100:0);
        gFadeStep = 0;
    }
}

/**
* HOVER FEATURE - BACKGROUND COLOR
*
* @param {object} obj
* @param {string} mouseoverColor
*/
function animateHoverBg(obj, mouseoverColor)
{
    obj.backColor             = obj.style.backgroundColor;
    obj.style.backgroundColor = mouseoverColor;
    obj.onmouseout = function(){ this.style.backgroundColor = this.backColor;  }
}

/**
* HOVER FEATURE
*
* @param {object} obj
* @param {string} mouseoverColor
*/
function animateHover(obj, mouseoverColor)
{
    obj.backColor   = obj.style.color;
    obj.style.color = mouseoverColor;
    obj.onmouseout  = function(){ this.style.color = this.backColor;  }
}


/**
* HOVER FEATURE - BACKGROUND IMAGE
*
* @param {object} obj
* @param {string} image_address
*/
function animateHoverImage(obj, image_address)
{
    if(checkIsString(obj))
    {
        obj = getObj(obj);
    }
    
    obj.backImage             = obj.style.backgroundImage;
    obj.style.backgroundImage = 'url('+image_address+')';
    obj.onmouseout = function(){ obj.style.backgroundImage = obj.backImage;  }
}

/**
* TOGGLE FEATURE - BACKGROUND IMAGE
*
* references obj.backImage set in animateHoverImage()
* 
* @param {object} obj
* @param {string} alt_image_address
* @param {string} def_image_address (optional)
*/
function animateSelectImage(obj, alt_image_address, def_image_address)
{
    obj.onmouseout = function(){ return false;  }
    
    var alt_img_url = 'url('+alt_image_address+')';
    var def_img_url = (def_image_address!=undefined)? 'url('+def_image_address+')' : 'none' ;
    
    //obj.backImage set in animateHoverImage()
    var useDefault = (obj.style.backgroundImage.search(alt_image_address) != -1 || obj.backImage == alt_img_url);
    
    obj.style.backgroundImage = (useDefault)? def_img_url : alt_img_url ;    
}



//CONCEPT
function animateMove(obj, out)
{
    if(checkIsSet(obj))
    {
        gFadeStep = 0;
        fadeSpecs['fadeIn'] = !checkIsSet(out);
        fadeSpecs['obj']    = obj;
    }

    if(gFadeStep <= 100)
    {
        documentOpacity(fadeSpecs['obj'].obj, (fadeSpecs['fadeIn'])? gFadeStep:100-gFadeStep);
        fadeSpecs['obj'].obj.style.marginLeft = (gFadeStep*(2+40/100))+'px';
        fadeSpecs['obj'].obj.style.marginTop = (gFadeStep*(1-100/140))+'px';
        gFadeStep++;
        t = setTimeout("animateMove()", 0);
    }
    else{gFadeStep = 0;}
}




/************************************
* jaf MODULES  *
*************************************/
function jafModule()
{
    this.loadingText     = moduleLoadingText;
    this.rater           = moduleRater;
    this.scrollbox       = moduleScrollBox;
}

/**
* DYNAMIC "loading..." STRING OBJECT
* @param {object} parentObj
* @param {string} msgText
*/
function moduleLoadingText(parentObj, msgText)
{
    if(checkIsDocObj(parentObj))
    {
        gLoadStep = 0;
        this.obj  = loadTxtSpecs['node'];
        this.obj.nodeValue = (checkIsDefined(msgText))? msgText:loadTxtSpecs['text'];
        this.obj.nodeValue+= '.';
    }

    if(typeof(gLoadStep)=='undefined')
    {
        erCatch.push(erMsgs['LText']+parentObj.id);
        this.obj  = loadTxtSpecs['node'];
        loadTxtSpecs['node'].nodeValue = '*checkloading';
        return;
    }
    else if(gLoadStep <= (loadTxtSpecs['iterations']*4))
    {
        if(gLoadStep%4==0)
        { loadTxtSpecs['node'].nodeValue = loadTxtSpecs['node'].nodeValue.replace('...', '');  }
        else
        { loadTxtSpecs['node'].nodeValue += '.'; }

        gLoadStep++;
        t = setTimeout("moduleLoadingText()", loadTxtSpecs['increment']*1000);
    }
    else{gLoadStep = 0;}
}


gRatingCallBack = false;
/**
* RATING MODULE
* @param {string} itemId
* @param {int} avgRating
* @param {int} userRating
* @param {function} actionFn (action to incur when setting a rating item)
*/
function moduleRater(itemId, avgRating, userRating, keepUserRating, actionFn)
{
    avgRating = (checkIsSet(avgRating))? avgRating:0;
    var scale            = ratingSpecs['scale'];
    var starSize         = ratingSpecs['stars']['size'];
    var starSpacer       = ratingSpecs['stars']['spacer'];
    var currentRating    = (userRating>0)? userRating:avgRating;//public avg if no userRating
    var defaultStarType  = (userRating>0)? 'user':'average';
    var currentRatingNum = Math.floor(currentRating);
    var raterObjId       = itemId+"_rater";

    if(checkIsSet(actionFn)){gRatingCallback = actionFn; }
    
    //INITIALIZE RATING OBJECT
    var rateObj = new documentObject(raterObjId);

    //RATING OBJECT STYLE
    rateObj.obj.style.cursor   = "pointer";
    rateObj.obj.style.height   = starSize+"px";
    rateObj.obj.style.width    = ((starSize+starSpacer*2)*scale)+"px";

    //FOR WHEN MOUSE LEAVES RATING AREA
    var mouseOut = function() { _MouseOver(raterObjId, currentRatingNum, defaultStarType); }
    try //Standard browsers:`onmouseout`
    { rateObj.obj.onmouseout   = mouseOut; }
    catch(StandardBrowserErr)//IE:`onmouseleave`
    { rateObj.obj.onmouseleave = mouseOut; alert('test'); }

    //CREATE STAR ITEMS (to scale: ie. 5 or 10)
    for(var x=1; x<=scale; x++)
    {
        var starID   = x+'_'+raterObjId;
        var starType = (x <= currentRatingNum)? defaultStarType:'blank'; //use default until passed current rating
        starObj      = new documentObject(starID, 'div');

        //STAR OBJECT STYLE
        starObj.obj.style.height            = starObj.obj.style.width = (starSpacer+starSize)+'px';
        starObj.obj.style.marginLeft        = (starSize*(x-1)+starSpacer)+'px';
        starObj.obj.style.marginRight       = '-1px';
        starObj.obj.style.position          = 'absolute';
        starObj.obj.style.cssFloat          = 'left';
        starObj.obj.style.display           = 'inline'; //IE compatibility
        starObj.obj.style.overflow          = 'hidden'; //IE compatibility
        starObj.obj.style.backgroundRepeat  = 'no-repeat';
        starObj.obj.style.backgroundPosition= 'left bottom';
        starObj.obj.style.backgroundImage   = ratingSpecs['stars'][starType];

        //rating star events
        if(scale==5){starObj.obj.title = ratingSpecs['titles'][x]; }
        starObj.obj.onmouseover = function() { _MouseOver(raterObjId, currentRatingNum, defaultStarType, this); }
        starObj.obj.onclick     = function()
        {
            var ratingParams       = new Array();
            var loadingText        = new moduleLoadingText(rateObj.obj, 'calculating');
            ratingParams['item']   = itemId;
            ratingParams['rating'] = toolGetItemId(this);

            //grab parent obj, replace rateObj with animated loading text
            var parentObj = rateObj.obj.parentNode;
            parentObj.replaceChild(loadingText.obj, rateObj.obj);

            //@param {object}`result` holds the response from the server.
            var talkResponse = function(result)//append new rating object function
            {
                var average    = (checkIsSet(keepUserRating))? result.avg : result;
                var thisUser   = (checkIsSet(keepUserRating))? result.user : 0;
                //special case actions based on a rating click
                gRatingCallback(itemId, result);
                var newRateObj = new moduleRater(itemId, average, thisUser); //new documentObject('test', null, null, result);//

                //parentObj.appendChild(document.createTextNode(result+' '));
                var getNewRating = function(){ parentObj.replaceChild(newRateObj.obj, loadingText.obj); }
                var t2 = setTimeout(getNewRating, 1000);
            }
            backTalk(ratingSpecs['backend'], ratingParams, talkResponse);
        }
        rateObj.obj.appendChild(starObj.obj);
    }
    this.obj = rateObj.obj;

    /**
    * MOUSEOVER FEATURE FOR STARS.
    * @param {string} raterObjId
    * @param {int} currentRating
    * @param {string} currentStarType (keep track of original state)
    * @param {object} obj - star object
    */
    _MouseOver = function(raterObjId, currentRating, currentStarType, starObj)
    {
        var starType;
        var starRank = toolGetItemId(starObj);

        //loop through needed stars to respond to mouse action
        for(var i=ratingSpecs['scale']; i>0; i--)
        {
            var starId    = i+'_'+raterObjId;
            var chgState  = (i>starRank && currentStarType!='user' || starObj==null);
            var setRating = (chgState)? currentRating:starRank;
            var setType   = (chgState)? currentStarType:'user';
            var newType   = (i>setRating)? 'blank':setType;

            getElement(starId).style.backgroundImage = ratingSpecs['stars'][newType];
        }
    }
}



/**
* SCROLL WINDOW MODULE
* 
* DERIVED FROM CODE BY - Alf Magne Kalleland - www.dhtmlgoodies.com
* - WORKING BUT NOT FINISHED - conversion to an object is not complete.
* 
* @param {string} parentId - should be depricated - jtg
* @param {object} content (optional) - if null, it will create an object to append children to
* @param {int} height - pixel height of the scroll window
* @param {int} width - pixel width of the scroll window
*
* * @param {int} boxTopPosition - pixel height reference of top position
*/ 
var contentHeight = new Array();
var visibleContentHeight = new Array();

var gScrollerObj = new Array(); //scroll handle
var gScrollBar   = new Array();
var gBoxContent  = new Array();

var scrollHandleHeight = new Array();
var scrollBarTop    = new Array();
var scrollerTop     = new Array();
var eventYPos       = new Array();

var scrollDirection = new Array();
var scrollSpeed = 1;
var scrollDelay = 5;

var scrollActive = new Array();
var scrollMoveToActive = new Array();
var scrollMoveToYPosition = new Array();

var slider = new Array();
var scroller = new Array();

var contentBox = new Array();
var scrollParent = new Array();

function moduleScrollBox(parentId, content, height, width, boxTopPosition, scrollerHeight, scrollerWidth, contentColor, sliderColor, scrollerColor)
{
    scrollerWidth  = (checkIsSet(scrollerWidth))? scrollerWidth:5;
    scrollerHeight = (checkIsSet(scrollerHeight))? scrollerHeight:40;
    
    //create pieces of the scrollBox
    scrollParent[parentId] = getElement(parentId);
    contentBox[parentId] = new documentObject(parentId+'_contentBox');
    slider[parentId]     = new documentObject(parentId+'_slider');
    gScrollBar[parentId] = new documentObject(parentId+'_scrollBar');
    scroller[parentId]   = new documentObject(parentId+'_scroller');
    
    if(checkIsDocObj(content))
    { gBoxContent[parentId] = content; }
    else
    {
        boxContent = new documentObject(parentId+'_content', null, null, content, 'isHTML');
        gBoxContent[parentId] = boxContent.obj;
    }
    contentHeight[parentId] = Number(gBoxContent[parentId].style.height.replace("px", ""));
    
    gScrollerObj[parentId] = scroller[parentId].obj;

    slider[parentId].obj.appendChild(gScrollBar[parentId].obj);
    gScrollBar[parentId].obj.appendChild(gScrollerObj[parentId]);
    contentBox[parentId].obj.appendChild(gBoxContent[parentId]);

    //style scrollBox pieces
    contentBox[parentId].obj.className      = 'floatLeft';//not portable - needs fixing -JTG
    contentBox[parentId].obj.style.position = 'relative';
    contentBox[parentId].obj.style.cssFloat = 'left';
    contentBox[parentId].obj.style.height   = '100%';

    gBoxContent[parentId].style.position    = 'relative';
    gBoxContent[parentId].style.lineHeight  = '120%';
    gBoxContent[parentId].style.fontSize    = '12px';
    gBoxContent[parentId].style.width       = '100%';
    gBoxContent[parentId].style.top         = '0px';
    if((contentHeight[parentId]-height) < 10){  gScrollBar[parentId].obj.style.display = 'none';  }

    slider[parentId].obj.className           = 'floatLeft'; //not portable - needs fixing -JTG
    slider[parentId].obj.style.cssFloat      = 'left';
    slider[parentId].obj.style.width         = '15px';
    slider[parentId].obj.style.marginLeft    = '15px';
    slider[parentId].obj.style.paddingBottom = '2px';

    
    gScrollBar[parentId].obj.style.position  = 'relative';
    gScrollBar[parentId].obj.style.width     = scrollerWidth+'px';

    scroller[parentId].obj.style.position   = 'absolute';
    scroller[parentId].obj.style.cursor     = 'pointer';
    scroller[parentId].obj.style.marginTop  = '2px';
    scroller[parentId].obj.style.width      = scrollerWidth+'px';
    scroller[parentId].obj.style.height     = scrollerHeight+'px';

    //post to parent item
    scrollParent[parentId].appendChild(contentBox[parentId].obj);
    scrollParent[parentId].appendChild(slider[parentId].obj);

    //public methods
    this.setAccentColor = function(color)
    {
        gScrollBar[parentId].obj.style.borderColor   = color;
        scroller[parentId].obj.style.backgroundColor = color;
        contentBox[parentId].obj.style.borderColor   = color;
    }
    
    this.setBoxWidth = function(width)
    {
        scrollParent[parentId].style.width   = width + 'px';
        contentBox[parentId].obj.style.width = width-30 + 'px';
    }

    this.setBoxHeight = function(height)
    {
        scrollParent[parentId].style.height   = height + 'px';
        contentBox[parentId].obj.style.height = height + 'px';
        slider[parentId].obj.style.height     = height + 'px';
        gScrollBar[parentId].obj.style.height = height + 'px';
    }

    //private methods
    this._scrollDisable = function()
    { 
        scrollActive[parentId] = scrollMoveToActive[parentId] = false; 
    }

    this._initScroll = function(e)
    {
        if(document.all && !operaBrowser) e = event;
        scrollerTop[parentId]  = gScrollerObj[parentId].offsetTop;
        eventYPos[parentId]    = e.clientY;
        scrollActive[parentId] = true;
    }

    this._ScrollAction = function(e)
    {
        if(document.all && !operaBrowser) e = event;
        
        if(!scrollActive[parentId] || (e.button!=1 && document.all)) return; 
        
        var topPos = scrollerTop[parentId] + e.clientY - eventYPos[parentId];
        
        if(topPos<0){ topPos=0; }
        
        if(topPos/1>visibleContentHeight[parentId]-(scrollHandleHeight[parentId]+4)/1)
        { topPos = visibleContentHeight[parentId]-(scrollHandleHeight[parentId]+4); }
        
        gScrollerObj[parentId].style.top = topPos + 'px';
        gBoxContent[parentId].style.top  = 0 - Math.floor((contentHeight[parentId]) * ((topPos)/(visibleContentHeight[parentId]-scrollHandleHeight[parentId])))+'px';
    }

    this._scrollToMouse = function(e)
    {
        scrollToMouseAction = function()
        {
            if(!scrollMoveToActive[parentId] || scrollActive[parentId])return;
            var topPos = gScrollerObj[parentId].style.top.replace('px','');
            topPos = topPos/1 + scrollDirection[parentId];
            if(topPos<0)
            {
                topPos=0; scrollMoveToActive[parentId]=false;
            }
            if(topPos/1>visibleContentHeight[parentId]-(scrollHandleHeight[parentId]+4)/1){
                topPos = visibleContentHeight[parentId]-(scrollHandleHeight[parentId]+4);
                scrollMoveToActive[parentId]=false;
            }
            if(scrollDirection[parentId]<0 && topPos<scrollMoveToYPosition[parentId]-scrollHandleHeight[parentId]/2)return;
            if(scrollDirection[parentId]>0 && topPos>scrollMoveToYPosition[parentId]-scrollHandleHeight[parentId]/2)return;
            gScrollerObj[parentId].style.top = topPos + 'px';
            gBoxContent[parentId].style.top = 0 - Math.floor((contentHeight[parentId]) * ((topPos)/(visibleContentHeight[parentId]-scrollHandleHeight[parentId])))+'px'
            setTimeout('scrollToMouseAction()',scrollDelay);
        };

        if(document.all && !operaBrowser)e = event;
        scrollMoveToActive[parentId] = true;
        scrollMoveToYPosition[parentId] = e.clientY - scrollBarTop[parentId];
        //alert();
        scrollDirection[parentId] = (gScrollerObj[parentId].offsetTop/1 > scrollMoveToYPosition[parentId])? scrollSpeed*-2: scrollSpeed*2;
        scrollToMouseAction();
    }

    contentBox[parentId].obj.style.backgroundColor = (checkIsDefined(contentColor))? contentColor:'#FFFFFF';
    gScrollBar[parentId].obj.style.backgroundColor = (checkIsDefined(sliderColor))? sliderColor:'#EB4B71';
    this.setAccentColor((checkIsDefined(scrollerColor))? scrollerColor:'pink');
    this.setBoxWidth((checkIsDefined(width))? width:300);
    this.setBoxHeight((checkIsDefined(height))? height:400);


    visibleContentHeight[parentId]        = gScrollBar[parentId].obj.offsetHeight;
    contentHeight[parentId]               = gBoxContent[parentId].offsetHeight - visibleContentHeight[parentId];
    scrollHandleHeight[parentId]          = gScrollerObj[parentId].offsetHeight;
    scrollBarTop[parentId]                = (checkIsSet(boxTopPosition) && checkIsNumeric(boxTopPosition))? boxTopPosition : gScrollBar[parentId].obj.offsetTop;
    gScrollerObj[parentId].onmousedown    = this._initScroll;
    scrollParent[parentId].onmousemove    = this._ScrollAction;
    scrollParent[parentId].onmouseup      = this._scrollDisable;
    gScrollerObj[parentId].onmouseup      = this._scrollDisable;
    
    contentBox[parentId].onselectstart  = function(){return false; }
    scrollParent[parentId].onselectstart  = function(){return false; }
    gScrollerObj[parentId].onselectstart  = function(){return false; }
    gScrollBar[parentId].onselectstart = function(){return false; }

    if(document.all)
    { document.body.onmouseup = this._scrollDisable; }
    else
    { document.documentElement.onmouseup = this._scrollDisable; }

    gScrollBar[parentId].obj.onmousedown = this._scrollToMouse;
}

/************************************
* jaf EXAMPLE  *
*************************************

//new jaf object
var jaf = new jaf();
//jaf globals
var obj = jaf.doc.obj;
var lnk = jaf.doc.lnk;
var img = jaf.doc.img;
var rater = jaf.module.rater;
var fader = jaf.animate.fade;

//assign a module
var ratingItem = new rater(125433,6,0);
getElement('divElement').appendChild(ratingItem);

*************************************/