// $Revision: 4$
// $Date: 2007-08-20 8:51:02 PM$

/******************************************************************************
  Functions
 ******************************************************************************/
var UTILS = {
    asXmlString: function( n, v, doNotEscape ) { return UTILS.asTag( n, '', v, false, doNotEscape ? null : 'xml' ); },

    asSingleTag: function( n, a ) { return UTILS.asTag( n, a, '', true ); },

    asTag: function( n, a, _v, asSingleTag, escapeAs ) {
        // escapeAs == undefined/null, 'xml', 'html'
        var v = _v;

        var attr = '';
        if (arguments.length == 2 && typeof a !== 'object')
            // just 2 params and 'a' not a collection of attribues so must be the value
            v = a;
        else
        {
            if (arguments.length == 2)
                v = '';

            for (var p in a)
                // Check for hasOwnProperty added to remove the possibility that extensions to String
                // get output as attributes in the return string.
                if (a.hasOwnProperty(p)) {
                    if (typeof a[p] != 'undefined' && (!a[p] || a[p].constructor != Function))
                        attr += ' ' + p + (a[p] === null ? '' : '="' + a[p] + '"');
                }
        }

        if (n && n.constructor == Function)
            return "";

        if ( escapeAs == 'xml' )
            v = UTILS.escapeXML( v );
        else if ( escapeAs == 'html' )
            v = UTILS.escapeHTML( v );

        return asSingleTag && (!v || !v.length) ?
            "<" + n + attr + "/>" :
            "<" + n + attr + ">" + v + "</" + n + ">";
    },

    stripNonDigits: function( s ) { return s.replace( /\D+/g, '' ); },

    trimLeadingAndTrailingWhiteSpace : function( stringToTrim )
    {
        return stringToTrim.replace(/^\s+|\s+$/g,"");
    },

    trimLeadingWhiteSpace : function(stringToTrim)
    {
        return stringToTrim.replace(/^\s+/,"");
    },

    trimTrailingWhiteSpace : function(stringToTrim)
    {
        return stringToTrim.replace(/\s+$/,"");
    },

    htmlRegExp  : /[<>&"]/g,
    htmlEscapes : { "<": "&lt;", ">": "&gt;", "&": "&amp;", "\"": "&quot;" },
    otherRegExp : /[<>&"']/g,
    otherEscapes: { "<": "&lt;", ">": "&gt;", "&": "&amp;", "\"": "&quot;", "'": "&apos;" },

    escapeXML: function( str )
    {
        // REMOVING FOR NOW.
        // Bug in previously shared code setting variable to null and breaking this.
        // Will escape-out special chars before we get here (needed for SSMS only);
        //return str ? str.toString( ).replace( this.otherRegExp, function( a, b ) { return this.otherEscapes[ b ]; } ) : "";
        return str;
    },

    escapeYahooWidget: this.escapeXML,

    escapeHTML: function( str )
    {
        return str ? str.toString( ).replace( this.htmlRegExp, function( a, b ) { return this.htmlEscapes[ b ]; } ) : "";
    },

    escapeUri: function( str )
    {
        return ( typeof encodeURIComponent == "undefined" ) ? escape( str ) : encodeURIComponent( str );
    },

    unescapeUri: function( str )
    {
        newStr = str.replace(/\+/g, " ");
        return ( typeof decodeURIComponent == "undefined" ) ? unescape( newStr ) : decodeURIComponent( newStr );
    },

    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;
    },

    getWithDefault: function( value, defaultValue )
    {
        return value === undefined || value === null ? defaultValue : value;
    },

    addProperty: function( obj, propertyName, hasChangeEvent, defaultValue, initialValue )
    {
        var eventName = hasChangeEvent ? propertyName + "Change" : null;
        obj[ propertyName ] =
        {
            name: propertyName,
            defaultValue: defaultValue,
            hasChangeEvent: UTILS.getWithDefault( hasChangeEvent, false ),
            eventName: eventName,
            value: UTILS.getWithDefault( initialValue, defaultValue ),

            "get": function( ) { return this.value; },

            "set": function( newValue )
            {
                var oldValue = this.value;
                this.value = newValue;
                if ( this.hasChangeEvent && !UTILS.equals( oldValue, newValue ) )
                    obj.jObject.trigger( this.eventName, [ oldValue, newValue ] );
                return newValue;
            },

            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 ( UTILS.equals( this.value, this.defaultValue) );
            }
        };

        if ( hasChangeEvent && ( "on" + eventName ) in obj )
            obj.bindToEvent( obj.jObject, eventName );
    },

    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 )
    {
        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( "" );
        }
    },

    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( )" );
        }
    },

    equals: function( o1, o2 )
    {
        return o1 == o2 || ( typeof o1 == "object" && typeof o2 == "object" && UTILS.toJSONString( o1 ) == UTILS.toJSONString( o2 ) );
    },

    jObjectInfo: function ( jObjectExpression )
    {
        if ( jObjectExpression )
        {
            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" );
                if ( !this.jObjectId )
                    this.jTagName = this.jObject.attr( "tagName" );
            }
        }
        else
            this.jObject = null;

        this.jObjectString = this.jObjectId
                          || ( "jObjectId" in this
                             ? "[anonymous " + this.jObject.attr( "tagName" ).toLowerCase( ) + "]"
                             : "[none]" );
    },

    base: function ( oPrototype, constructorName )
    {
        $.extend( this, oPrototype );
        this.constructorName = constructorName;
        this.bindToEvent = UTILS.bindToEvent;

        if ( !this.constructor.prototype )
            this.constructor.prototype = UTILS.base_prototype;
        else if ( !( "getDescription" in this.constructor.prototype ) )
            $.extend( this.constructor.prototype, UTILS.base_prototype );
    },

    base_prototype:
    {
        getDescription: function( )
        {
            if ( this.name && this.constructorName )
                return this.name + "[" + this.constructorName + "]";
            else if ( this.name )
                return this.name;
            else if ( this.constructorName )
                return this.constructorName;
            else if ( this.jObjectId && this.jObjectId != "document" )
                return "[view on " + this.jObjectId + "]";
            else
                return "[unknown]";
        },

        setJObject: function( jObjectExpression )
        {
            $.extend( this, new UTILS.jObjectInfo( jObjectExpression ) );
        }
    },

    inheritFromBase: function ( obj, name, parent, jObjectExpression )
    {
        if ( !obj.constructor.prototype || !( "getDescription" in obj.constructor.prototype ) )
        {
            var constructorName
              , oPrototype = {};

            if ( obj.constructor != Object )
            {
                var a = /^\W*function\s*([^\s(]+)\s*\(/.exec( obj.constructor.toString( ) );

                if ( a && a.length && a[ 1 ] != "Object" )
                {
                    constructorName = a[ 1 ];
                    if ( constructorName + "_prototype" in UTILS.root )
                        oPrototype = UTILS.root[ constructorName + "_prototype" ];
                }
            }

            var base = new UTILS.base( oPrototype, constructorName );
            if ( obj.constructor == Object )
                $.extend( obj, base );
            else if ( obj.constructor.prototype )
                $.extend( obj.constructor.prototype, base );
            else
                obj.constructor.prototype = base;
        }

        obj.setJObject ( jObjectExpression );
        obj.name = name;
        obj.parent = parent;
        return obj;
    },

    jDocument: $(),

    // 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 );
    },

    //  syntax:
    //      this.bindToEvent( [ eventTarget, ] eventName [, method ] )
    //      eventTarget defaults to document
    //      method defaults to this.on<eventName>
    bindToEvent: function( )
    {
        var eventTarget, eventName, method;
        if (arguments.length == 3 )
        {
            eventTarget = $( arguments[ 0 ] );
            eventName = arguments[ 1 ];
            method = arguments[ 2 ];
        }
        else if (arguments.length == 1 )
        {
            eventTarget = UTILS.jDocument;
            eventName = arguments[ 0 ];
        }
        else if ( typeof arguments[ 0 ] == "string" )
        {
            eventTarget = UTILS.jDocument;
            eventName = arguments[ 0 ];
            method = arguments[ 1 ];
        }
        else
        {
            eventTarget = $( arguments[ 0 ] );
            eventName = arguments[ 1 ];
        }

        method = method ? ( typeof method == "function" ? method: this[ method ] ) : this[ "on" + eventName ];

        var self = this
          , f = function( evt )
            {
                    method.apply( self, arguments );
/*          , f = function( evt )
            {
                try
                {
                    method.apply( self, arguments );
                }
                catch( e )
                {
                    var objName = self.getDescription( )
                      , eventTargetName = "[unknown]"
                      , 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_ASSERT_NO_EXCEPTIONS( e, "Error handling " + eventTargetName + " " + eventName + " event by " + objName );
                }
*/
            };
        eventTarget.bind( eventName, f );
        return f.guid;
    },
    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 );
    },

    addEnterSubmit: function( jObject, submitObjectId )
    {
        jObject.keypress
        (
            function( e )
            {
                if ( e.keyCode == 13 )
                    $( "#" + submitObjectId ).trigger( "click" );
            }
        );
    },

    VALIDATE_SUCCESS: 0,
    VALIDATE_MISSING: 1,
    VALIDATE_INVALID: 2,

    emailInput: function( id, submitObjectId )
    {
        this.id = id;
        this.jInput = $( "#" + id );

        if ( submitObjectId ) UTILS.addEnterSubmit( this.jInput, submitObjectId );

        this.validate = function( missingMessage, invalidMessage )
        {
            var email = this.jInput.val( )
              , retval = { status: UTILS.VALIDATE_SUCCESS, email: email };

            // no client-side validation implemented

            return retval;
        };
    },

    passwordInput: function( id, submitObjectId )
    {
        this.id = id;
        this.jInput = $( "#" + id );

        if ( submitObjectId ) UTILS.addEnterSubmit( this.jInput, submitObjectId );

        this.validate = function( missingMessage, invalidMessage )
        {
            var password = this.jInput.val( )
              , retval = { status: UTILS.VALIDATE_SUCCESS, password: password };

            if ( !password.length )
                retval.status = UTILS.VALIDATE_MISSING;

            else if ( !( /^\d{4,10}$/ ).test( password ) )
            {
                retval.status = UTILS.VALIDATE_INVALID;
                retval.password = "";
                this.jInput.val( "" );
            }

            if ( retval.status )
                CW_MessageArea.display( arguments[ retval.status - 1 ] );

            return retval;
        };
    },

    nanpPhoneInput: function( id, submitObjectId )
    {
        this.id = id;
        this.jInput = $( "#" + id );

        if ( submitObjectId ) UTILS.addEnterSubmit( this.jInput, submitObjectId );

        this.validate = function( missingMessage, invalidMessage )
        {
            var outputPhone = this.jInput.val( ).replace( /^\s*|\s*$/g, "" )
              , retval = { status: UTILS.VALIDATE_SUCCESS, phoneNumber: "" };

            if ( !outputPhone.length )
            {
                retval.status = UTILS.VALIDATE_MISSING;
                this.jInput.val( "" );
            }
            else
            {
                var phoneNumber = outputPhone.replace( /\D/g, "" );

                if ( !( /^[2-9]\d{9}$/ ).test( phoneNumber ) )
                {
                    retval.status = UTILS.VALIDATE_INVALID;
                    this.jInput.val( outputPhone );
                }
                else
                {
                    retval.phoneNumber = phoneNumber;
                    this.jInput.val( phoneNumber.substr( 0, 3 ) + "-" + phoneNumber.substr( 3, 3 ) + "-" + phoneNumber.substr( 6 ) );
                }
            }

            if ( retval.status )
                CW_MessageArea.display( arguments[ retval.status - 1 ] );

            return retval;
        };

        this.set = function( newValue )
        {
            if ( !newValue )
                this.jInput.val( "" );
            else if ( /^\d{3}-\d{3}-\d{4}$/.test( newValue )  )
                this.jInput.val( newValue );
            else if ( /^\d{10}$/.test( newValue ) )
                this.jInput.val( newValue.substr( 0, 3 ) + "-" + newValue.substr( 3, 3 ) + "-" + newValue.substr( 6 ) );
            else
                this.jInput.val( "" );
        };
    },

    PrefInfo: function( key, defaultValue )
    {
        this.key = key;
        this.defaultValue = defaultValue;
        this.toString = function( ) { return this.key; };
        this.reset = function( ) { this.set( this.defaultValue ); };
        if ( !( "get" in this ) )
        {
            // these two may be overridden:
            this.constructor.prototype.get = function( )
            {
                var value = UTILS.getNativePreference( this.key );
                if ( !value )
                    return this.defaultValue;
                return eval( "(" + value + ")" );
            };
            this.constructor.prototype.set = function( value )
            {
                if ( !value && value != "0" )
                    value = this.defaultValue;
                else if ( value.constructor != this.defaultValue.constructor )
                    value = new this.defaultValue.constructor( value );
                UTILS.setNativePreference( this.key, UTILS.toJSONString( value ) );
            };
        }
    },

    //associative array of PrefInfo
    PREFS : { },

    /*  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;

// expose some things in the global name space
var tag = UTILS.asTag
  , stag = UTILS.asSingleTag
  , UTILS_getAsXmlElementString = UTILS.asXmlString
  , UTILS_StripNonDigits = UTILS.stripNonDigits;

