// File: $CallWave/Widgets/Shared/Utils.js
// Desc: Static Class of Utility functions
// $Revision: 37$
// $Date: 5/10/2007 7:25:11 PM$
// $Author: Donnie Tognazzini$

/******************************************************************************
  Functions
 ******************************************************************************/
function BaseObject( oPrototype, constructorName, name, jObjectExpression )
{
    try
    {
        $.extend( this, oPrototype );

        this.constructorName    = constructorName;
        this.name               = name;
        this.jObject            = null;

        this.setJObject( jObjectExpression );
    }
    catch ( e )
    {
        EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::base( )" );
    }
};

BaseObject.prototype =
{
    getDescription: function( )
    {
        try
        {
            if ( this.name )
            {
                if ( this.constructorName )
                {
                    return this.name + "[" + this.constructorName + "]";
                }

                return this.name;
            }

            if ( this.constructorName )
            {
                return this.constructorName;
            }

            if ( this.jObjectId && this.jObjectId != "document" )
            {
                return "[view on " + this.jObjectId + "]";
            }

            return "[unknown]";
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::getDescription( )" );
        }
    },

    setJObject: function( jObjectExpression )
    {
        try
        {
            if ( ! jObjectExpression )
            {
                this.jObject = null;
                delete this.jObjectId;

                return;
            }

            this.jObject = $( jObjectExpression );

            if ( jObjectExpression == document || this.jObject.length && this.jObject[ 0 ] == document )
            {
                this.jObjectId = "document";
            }
            else if ( typeof jObjectExpression == "string" && ( /^#\w+$/ ).test( jObjectExpression ) )
            {
                this.jObjectId = jObjectExpression.substr( 1 );
            }
            else if ( this.jObject.length )
            {
                this.jObjectId = this.jObject.attr( "id" );
            }
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::setJObject( )" );
        }
    },

    addProperty: function( propertyName, hasChangeEvent, defaultValue, initialValue )
    {
        try
        {
            if ( typeof propertyName == 'object' && propertyName.equals === undefined )
            {
                throw new Error( "equals( ) method not defined on \"" + propertyName + "\" 'this':= \"" + this.toString( ) + "\"" );
            }

            function IsEqual( lhs, rhs )
            {
                if ( lhs.equals !== undefined )
                {
                    return lhs.equals( rhs );
                }

                return lhs == rhs;
            }

            function GetWithDefault( value, defaultValue )
            {
                return value === undefined || value === null ? defaultValue : value;
            }

            var eventName = hasChangeEvent ? propertyName + "Change" : null;

            var container = this;

            this[ propertyName ] =
            {
                name            : propertyName,
                defaultValue    : defaultValue,
                hasChangeEvent  : GetWithDefault( hasChangeEvent, false ),
                eventName       : eventName,
                value           : GetWithDefault( initialValue, defaultValue ),

                get: function( )
                {
                    return this.value;
                },

                set: function( newValue )
                {
                    try
                    {
                        var oldValue = this.value;
                        this.value   = newValue;

                        if ( this.hasChangeEvent && ! IsEqual( oldValue, newValue ) )
                        {
                            container.jObject.trigger( this.eventName, [ oldValue, newValue ] );
                        }

                        return newValue;
                    }
                    catch ( e )
                    {
                        EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::addProperty( )::set( ): property name=\"" + this.name + "\"" );
                    }
                },

                toString: function( )
                {
                    return this.value == undefined ? "" : this.value.toString( );
                },

                valueOf: function( )
                {
                    return this.value == undefined ? null : this.value.valueOf( );
                },

                reset: function( )
                {
                    return this.set( this.defaultValue );
                },

                isReset: function( )
                {
                    return IsEqual( this.value, this.defaultValue );
                }
            };

            if ( hasChangeEvent && this[ "on" + eventName ] !== undefined )
            {
                this.bindToEvent( this.jObject, eventName );
            }
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::addProperty( )" );
        }
    },

    //  syntax:
    //      this.bindToEvent( [ eventTarget, ] eventName [, method ] )
    //      eventTarget defaults to document
    //      method defaults to this.on<eventName>
    bindToEvent: function( )
    {
        try
        {
            var eventTarget = UTILS.jDocument;
            var eventName   = '';
            var method      = '';

            if (arguments.length == 3 )
            {
                eventTarget = $( arguments[ 0 ] );
                eventName   = arguments[ 1 ];
                method      = arguments[ 2 ];
            }
            else if ( arguments.length == 1 )
            {
                eventName   = arguments[ 0 ];
                method      = this[ "on" + eventName ];
            }
            else if ( typeof arguments[ 0 ] == "string" )
            {
                eventName   = arguments[ 0 ];
                method      = arguments[ 1 ];
            }
            else
            {
                eventTarget = $( arguments[ 0 ] );
                eventName   = arguments[ 1 ];
                method      = this[ "on" + eventName ];
            }

            if ( typeof method != "function" )
            {
                method = this[ method ];
            }

            var self = this;
            var eventHandler = function( evt )
            {
                try
                {
                    method.apply( self, arguments );
                }
                catch( e )
                {
                    var objName         = self.getDescription( );
                    var eventTargetName = "[unknown]";
                    var eventName       = "[unknown]";

                    if ( evt )
                    {
                        if ( evt.target && evt.target.id )
                        {
                            eventTargetName = evt.target.id;
                        }
                        else if ( evt.target == document )
                        {
                            eventTargetName = "Global";
                        }

                        if ( evt.type )
                        {
                            eventName = evt.type;
                        }
                    }

                    EX_Log( "Error handling " + eventTargetName + " " + eventName + " event by " + objName + "\n=== Exception ===\n" + e.message );
                }
            };

            eventTarget.bind( eventName, eventHandler );

            return eventHandler.guid;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "BaseObject::bindToEvent( )" );
        }
    }
};

function TextInputBase( id, submitObjectId )
{
    this.id = id;
    this.jInput = $( "#" + id );

    this.jInput.keypress
    (
        function( e )
        {
            if ( e.keyCode == 13 )
            {
                $( "#" + submitObjectId ).trigger( "click" );
            }
        }
    );
}

function EmailInput( id, submitObjectId )
{
    EXT_extend( this, new TextInputBase( id, submitObjectId ) );

    this.GetValue = function( )
    {
        return this.jInput.val( );
    };
}

function PasswordInput( id, submitObjectId )
{
    EXT_extend( this, new TextInputBase( id, submitObjectId ) );
}

PasswordInput.prototype =
{
    GetValidatedInput : function( missingMessage, invalidMessage )
    {
        try
        {
            var password = this.jInput.val( );

            if ( password.length == 0 )
            {
                CW_GetMessageArea( ).display( missingMessage, CW_GetMessageArea( ).MESSAGE_TYPE_ALERT );

                return null;
            }

            if ( !( /^\d{4,10}$/ ).test( password ) )
            {
                this.jInput.val( "" );

                CW_GetMessageArea( ).display( invalidMessage, CW_GetMessageArea( ).MESSAGE_TYPE_ALERT );

                return null;
            }

            return password;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "PasswordInput::GetValidatedInput()" );
        }
    }
};

function NANPPhoneInput( id, submitObjectId )
{
    EXT_extend( this, new TextInputBase( id, submitObjectId ) );
}

NANPPhoneInput.prototype =
{
    GetValidatedInput : function( missingMessage, invalidMessage )
    {
        try
        {
            var outputPhone = this.jInput.val( ).replace( /^\s$/g, "" );

            if ( outputPhone.length == 0 )
            {
                this.jInput.val( "" );

                CW_GetMessageArea( ).display( missingMessage, CW_GetMessageArea( ).MESSAGE_TYPE_ALERT );

                return null;
            }

            var phoneNumber = UTILS.stripNonDigits( outputPhone );

            if ( !( /^[2-9]\d{9}$/ ).test( phoneNumber ) )
            {
                this.jInput.val( outputPhone );

                CW_GetMessageArea( ).display( invalidMessage, CW_GetMessageArea( ).MESSAGE_TYPE_ALERT );

                return null;
            }

            this.jInput.val( phoneNumber.substr( 0, 3 ) + "-" + phoneNumber.substr( 3, 3 ) + "-" + phoneNumber.substr( 6 ) );

            return phoneNumber;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "NANPPhoneInput::GetValidatedInput()" );
        }
    },

    Set : function( newValue )
    {
        if ( ! newValue )
        {
            this.jInput.val( "" );
            return;
        }

        if ( /^\d{3}-\d{3}-\d{4}$/.test( newValue ) )
        {
            this.jInput.val( newValue );
            return;
        }

        if ( /^\d{10}$/.test( newValue ) )
        {
            this.jInput.val( newValue.substr( 0, 3 ) + "-" + newValue.substr( 3, 3 ) + "-" + newValue.substr( 6 ) );
            return;
        }

        this.jInput.val( "" );
    }
};

var UTILS =
{
    asXmlString: function( name, value, doNotEscape )
    {
        return UTILS.AsTag( name,
                            {},
                            doNotEscape ? value : UTILS.escapeXML( value ) );
    },

    asSingleTag: function( name, attributesHash )
    {
        return UTILS.AsTag( name, attributesHash, '' );
    },

    /*****************************************************************
        DownRevAsTag
            Accepted parameters: variableName[variableType]

            name[string], value[! object]

            name[string], attributes[object]

            name[string], attributes[object], value, asSingleTag[boolean], escapeAs[String]

    *****************************************************************/
    DownRevAsTag: function( )
    {
        try
        {
            if ( arguments.length < 2 )
            {
                throw new Error( "UTILS::DownRevAsTag( ) - Wrong number of arguments: " + arguments.length );
            }

            var name = arguments[0];

            if ( name && name.constructor === Function )
            {
                return "";
            }

            if ( arguments.length == 2 && typeof arguments[1] !== 'object' )
            {
                return UTILS.GetMarkupTag( name, '', arguments[1] );
            }

            // Set all the parameters
            var attributes  = arguments[1];

            var value       = null;
            var asSingleTag = null;
            var escapeAs    = null;

            if ( arguments.length > 2 )
            {
                value       = arguments[2];
                asSingleTag = arguments[3];
                escapeAs    = arguments[4];
            }

            // Escape the value
            if ( escapeAs == 'xml' )
            {
                value = UTILS.escapeXML( value );
            }
            else if ( escapeAs == 'html' )
            {
                value = UTILS.escapeHTML( value );
            }

            return UTILS.AsTag( name, attributes, value );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::DownRevAsTag( )" );
        }
    },

    GetMarkupTag: function( name, attributeString, value )
    {
        try
        {
            if ( ! value || value.length == 0 )
            {
                return "<" + name + attributeString + "/>";
            }

            return "<" + name + attributeString + ">" + value + "</" + name + ">";
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::GetMarkupTag( )" );
        }
    },

    AsTag: function( name, attributesHash, value )
    {
        try
        {
            if ( arguments.length != 3 )
            {
                throw new Error( "UTILS::AsTag( ) - Wrong number of arguments: " + arguments.length );
            }

            // Serialize the attribute hash
            var attributeString = '';

            for ( var attributeName in attributesHash )
            {
                var attributeValue = attributesHash[attributeName];

                if ( attributeValue === undefined )
                {
                    continue;
                }

                // skip functions
                if ( attributeValue !== null && attributeValue.constructor === Function )
                {
                    continue;
                }

                attributeString += ' ' + attributeName;

                // add the value if it's non-null
                if ( attributeValue !== null )
                {
                    attributeString += '="' + attributeValue + '"';
                }
            }

            // Put it all together
            return UTILS.GetMarkupTag( name, attributeString, value );
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::AsTag( )" );
        }
    },

    stripNonDigits: function( s ) { return s.replace( /\D/g, '' ); },

    htmlRegExp  : /[<>&"]/g,
    htmlEscapes : { "<": "&lt;", ">": "&gt;", "&": "&amp;", "\"": "&quot;" },
    otherRegExp : /[<>&"']/g,
    otherEscapes: { "<": "&lt;", ">": "&gt;", "&": "&amp;", "\"": "&quot;", "'": "&apos;" },

    escapeXML: function( str )
    {
        if ( str === null || str === undefined )
        {
            return "";
        }

        return str.toString( ).replace( this.otherRegExp, function( a ) { return UTILS.otherEscapes[ a ]; } );
    },

    escapeHTML: function( str )
    {
        if ( str === null || str === undefined )
        {
            return "";
        }

        return str.toString( ).replace( this.htmlRegExp, function( a ) { return UTILS.htmlEscapes[ a ]; } );
    },

    escapeUri: function( str )
    {
        return ( typeof encodeUriComponent == "undefined" ) ? escape( str ) : encodeUriComponent( str );
    },

    unescapeUri: function( str )
    {
        return ( typeof decodeUriComponent == "undefined" ) ? unescape( str ) : decodeUriComponent( str );
    },

    propertiesAsXml: function( obj, properties, topProperty, prefix )
    {
        var propsAsXml = '';
        var propertyPrefix = (prefix || prefix === '') && typeof prefix !== 'undefined' ? prefix : 'm_';
        for (var p in properties)
        {
            if (properties instanceof Array && obj !== properties)
                // convert to value in the properties array
                p = properties[p];

            var thisProperty = propertyPrefix + p;
            // use asXML() if it is available.
            propsAsXml += obj[thisProperty] && obj[thisProperty].asXML ?
                obj[thisProperty].asXML() :                // Rely on asXML to decide what to escape
                UTILS.asXmlString( p, obj[thisProperty] ); // This is a simple tag.  Escape by default
        }

        // wrap in a <topProperty> tag if there is a topProperty (do not escape in this case)
        return (topProperty && typeof topProperty !== 'undefined') ?
                UTILS.asXmlString( topProperty, propsAsXml, true ) :
                propsAsXml;
    },

    defaultArrayToJSON: function( a )
    {
        var s = [ ];
        for ( var i = 0; i < a.length; i++ )
            s.push( UTILS.toJSONString( a[ i ] ) );

        return "[" + s.join( "," ) + "]";
    },

    sparseArrayToJSON: function( a )
    {
        var s = [ "(function(){var a=[];" ];
        for ( var i in a )
            s.push( "a[", i, "]=", UTILS.toJSONString( a[ i ] ), ";" );
        s.push( "return a})()" );
        return s.join( "" );
    },

    listArrayToJSON: function( a )
    {
        var s = [ ];
        for ( var i in a )
            if ( a[ i ] === 0 || ( a[ i ] && a[ i ].constructor != Function ) )
                s.push( UTILS.toJSONString( a[ i ] ) );

        return "[" + s.join( "," ) + "]";
    },

    toJSONString: function( o, arrayEncoder )
    {
        try
        {
            if ( o === undefined )
            {
                return "undefined";
            }
            if ( o === null )
            {
                return "null";
            }

            switch ( o.constructor )
            {
            case Function:
                return "";

            case Number:
            case Boolean:
                return o.toString( );

            case Date:
                return "new Date(" + o.valueOf( ) + ")";

            case Array:
                if ( arrayEncoder )
                {
                    return arrayEncoder( o );
                }

                return UTILS.defaultArrayToJSON( o );

            case String:
                if ( /["\\\x00-\x1f]/.test( o ) )
                {
                    return "\"" +
                           o.replace( /([\x00-\x1f\\"])/g,
                                      function(a, b) { return "\\u00" + b.charCodeAt().toString( 16 ); } ) +
                           "\"";
                }

                return "\"" + o + "\"";

            default:
            }

            var s = [ ];
            for ( var prop in o )
            {
                var ss = UTILS.toJSONString( o[ prop ] );
                if ( ss )
                {
                    s.push( ",", UTILS.toJSONString( prop ), ":", ss );
                }
            }

            if ( ! s.length && o.hasOwnProperty( "length" ) )
            {
                return UTILS.defaultArrayToJSON( o );
            }

            s[ 0 ] = "{";
            s.push( "}" );

            return s.join( "" );
        }
        catch( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::toJSONString( )" );
        }
    },

    toFriendlyArguments: function( o )
    {
        var args = [];

        for ( var i = 0; i < o.length; i++ )
            args.push( o[ i ] );

        var ret = { arguments: args, caller : o.caller, callee: o.callee };
        return ret;
    },

    dump: function(o) {
        return UTILS.toJSONString( o );
    },

    SET_INNER_HTML_DEFAULT_COLOR: "#000000",

    SetInnerHTML : function( divId, __innerHTML, color )
    {
        try
        {
            var innerHTML = __innerHTML;
            var url = innerHTML.match( "www[/./?/&/=a-zA-Z0-9]*[^\.]" );

            if ( url && url.length )
            {
                innerHTML = UTILS.asTag( 'a',
                                         { href: 'javascript:void(0)', onClick: "OpenURL( 'http://" + url + "' );" },
                                         innerHTML );
            }

            document.getElementById( divId ).style.color = ( color !== undefined ? color : UTILS.SET_INNER_HTML_DEFAULT_COLOR );
            document.getElementById( divId ).innerHTML = innerHTML;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::SetInnerHTML( )" );
        }
    },

    inheritFromBase: function ( obj, name, jObjectExpression )
    {
        try
        {
            var constructorName = '';
            var oPrototype      = {};

            if ( obj.constructor !== Object )
            {
                var contructorNameMatches = /^\W*function\s*([^\s(]+)\s*\(/.exec( obj.constructor.toString( ) );

                if ( contructorNameMatches && contructorNameMatches.length == 2 && contructorNameMatches[ 1 ] != "Object" )
                {
                    constructorName = contructorNameMatches[ 1 ];

                    if ( UTILS.root[ constructorName + "_prototype" ] !== undefined )
                    {
                        oPrototype = UTILS.root[ constructorName + "_prototype" ];
                    }
                }
            }

            $.extend( obj, new BaseObject( oPrototype, constructorName, name, jObjectExpression ) );

            return obj;
        }
        catch ( e )
        {
            EX_ASSERT_NO_EXCEPTIONS( e, "UTILS::inheritFromBase( )" );
        }
    },

    // raise( [ targetObject, ] eventName [ , arguments to pass )
    raise: function( eventName )
    {
        var eventTarget, args = UTILS.toFriendlyArguments( arguments ).arguments;

        if ( typeof arguments[ 0 ] == "string" )
            eventTarget = UTILS.jDocument;
        else
        {
            eventTarget = $( args.shift( ) );
        }
        eventTarget.trigger.apply( eventTarget, args );
    },


    unbind: function( target, eventName, guid )
    {
        if ( guid )
            $( target ).unbind( eventName, { guid: guid } );
        else
            $( target ).unbind( eventName );
    },

    unbindAll: function( target, eventName )
    {
        $( target ).unbind( eventName ).find( "*" ).unbind( eventName );
    },

    /*  getNativePreference, setNativePreference
            Intended to be overridden.
            Routines to get current preference values.
    */
    _oPrefs: { },

    getNativePreference: function( key )
    {
        return this._oPrefs[ key ];
    },

    setNativePreference: function( key, value )
    {
        this._oPrefs[ key ] = value;
    },

/*  adjustHeight
        Intended to be overridden
        When an event occurs that requires more vertical screen screen real estate, call this.
        Returns true if height is adjusted
*/
    adjustHeight: function( )
    {
        return false;
    },

    cookies: null,

    getCookie: function( cookieName )
    {
        if ( !this.cookies )
        {
            if ( !document.cookie )
                return "";
            var cs = document.cookie.split( ";" );
            this.cookies = {};

            for ( var i = 0 ; i < cs.length; i++ )
            {
                var c = cs[ i ].split( "=" );
                this.cookies[ c[ 0 ] ] = this.unescapeUri( c[ 1 ] );
            }
        }
        return this.cookies[ cookieName ] || "";
    }
};

UTILS.root = this;
if ( typeof $ != "undefined" )
    UTILS.jDocument = $();

// expose some things in the global name space
var tag = UTILS.DownRevAsTag
  , stag = UTILS.asSingleTag
  , UTILS_getAsXmlElementString = UTILS.asXmlString
  , UTILS_StripNonDigits = UTILS.stripNonDigits;
