var vanilla = {

    popup : function(p, h, w) {
        if (p != null) {
            var widgets = "toolbar=0,location=0,directories=0,status=0,menubar=0,scrollbars=1,resizable=1,height=" + h + ", width=" + w;
            var popupWin = window.open(p, "popupWin", widgets);
        }
    },

    /* This function was originally used to pass parameters from a stub form to a full form,
       prefilling the values without having to trigger processing resulting in error messages. */

    getParams : function() {
        var query = decodeURI(location.search.substring(1));
        if (!query.length) return;
        var params = new Array();
        var pairs = query.split('&');
        for (var i = 0; i < pairs.length; i++) {
            var nameVal = pairs[i].split('=');
            params[nameVal[0]] = decodeURIComponent(nameVal[1]);
        }
        return params;
    },

    /* The following two functions are for those clients who refuse to follow best practices
       and insist that links open in a new window. By setting the class name of links to
       "newwindow", we allow javascript to do the work and avoid deprecated target attributes.

       credit: Roger Johansson (modified)
       http://www.456bereastreet.com/archive/200605/using_javascript_instead_of_target_to_open_new_windows/ */

    openInNewWindow : function(e) {
        var event;
        if (!e) {
            event = window.event;
        } else {
            event = e;
        }

        // Abort if a modifier key is pressed
        if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) {
            return true;
        } else {
            // Change "_blank" to something like "newWindow" to load all links in the same new window
            var newWindow = window.open(this.getAttribute('href'), '_blank');
            if (newWindow) {
                if (newWindow.focus) {
                    newWindow.focus();
                }
                return false;
            }
            return true;
        }
    },


    /* This function is used on dropdown menus to send users to a new URL */

    jumpTo : function(select) {
        var url = select[select.selectedIndex].value;
        if (!url) return;
        window.location = url;
    },

    /* This function is used to toggle the values in certain form fields without field labels */

    swapValue : function( field, revert, defValue ) {
        if (defValue == undefined) defValue = field.defaultValue;
	    if (field.value == (revert ? '' : defValue)) {
	        field.value = (revert ? defValue : '');
	    }
	},
    
    formatTwitterText : function(txt) {
        var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
        txt = txt.replace(exp, '<a href="$1">$1</a>');
        exp = /[\@]+([A-Za-z0-9-_]+)/ig;
        txt = txt.replace(exp, '<a href="http://twitter.com/$1">@$1</a>');
        exp = /[\#]+([A-Za-z0-9-_]+)/ig;
        
        return txt;
    },


    /* The following method is deprecated and included only for template support
       as we move to jQuery */

    getElementById : function(id) {
        if (document.getElementById) {
            return document.getElementById(id);
        } else if (document.all) {
            return document.all[ id ];
        } else {
            return null;
        }
    },

    addLoadEvent : function(func) {
        var oldonload = window.onload;
        if (typeof window.onload != 'function') {
            window.onload = func;
        } else {
            window.onload = function() {
                if (oldonload) oldonload(); // use conditional to keep IE7 happy
                func();
            }
        }
    },

    strftime : function(format, date, locale) {
        if(! date ) date = new Date(); // default to today
        if( format == 'pretty' ) {
            return vanilla.pretty_date(date);
        }
        if(! locale) locale = 'en';
        var A_values, B_values;
        if( locale == 'en' ) {
            A_values = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
            B_values = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 
                        'September', 'October', 'November', 'December'];
        } else if( locale == 'es' ) {
            A_values = ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'],
            B_values = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto',
                        'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'];
        }

        var pattern = /%(a|A|b|B|d|m|M|H|l|p|P|y|Y)/;
        while(match = format.match(pattern)) {
            var repl;
            switch (match[1]) {
                case 'a' : repl = A_values[date.getDay()].substr(0,3); break;
                case 'A' : repl = A_values[date.getDay()]; break;
                case 'b' : repl = B_values[date.getMonth()].substr(0,3); break;
                case 'B' : repl = B_values[date.getMonth()]; break;
                case 'd' : repl = date.getDate(); break;
                case 'H' : repl = date.getHours(); break;
                case 'l' : repl = date.getHours(); if( repl > 12 ) repl = repl % 12; break;
                case 'm' : repl = date.getMonth() + 1; break;
                case 'M' : repl = date.getMinutes(); if( repl < 10 ) repl = '0' + repl; break;
                case 'p' : repl = (date.getHours() >= 12) ? 'pm' : 'am'; break;
                case 'P' : repl = (date.getHours() >= 12) ? 'PM' : 'AM'; break;
                case 'y' : repl = (date.getFullYear() + '').substr(2); break;
                case 'Y' : repl = date.getFullYear(); break;
            }
            format = format.replace(RegExp(match[0], 'g'), repl);
        }
        return format;
    },

    // stolen (and adapted) from http://ejohn.org/files/pretty.js
    pretty_date : function(date) {
        var diff = (((new Date()).getTime() - date.getTime()) / 1000);
        var day_diff = Math.floor(diff / 86400);
            
        if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
            return;
            
        return day_diff == 0 && (
                diff < 60 && "just now" ||
                diff < 120 && "1 minute ago" ||
                diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
                diff < 7200 && "1 hour ago" ||
                diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
            day_diff == 1 && "Yesterday" ||
            day_diff < 7 && day_diff + " days ago" ||
            day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
    },
    
    // escape HTML characters
    escape_html : function(str) {
        if(!str) return str;
        str = str.replace(/&/g,"&amp;");
        str = str.replace(/"/g,"&quot;");
        str = str.replace(/</g,"&lt;");
        str = str.replace(/>/g,"&gt;");
        return str;
    },

    // escape strings for use in JS
    escape_js : function(str) {
        if(!str) return str;
        str = str.replace(/\\/g,"\\\\");
        str = str.replace(/'/g,"\\'");
        str = str.replace(/"/g,'\\"');
        str = str.replace(/\n/g,"\\n");
        str = str.replace(/\r/g,"\\r");
        return str;
    },

    // very simple template strings that look like:
    // I have #{count} #{thing}
    // and are filled with a hash like { count: 10, thing : 'cookies' }
    template : function(string, values) {
        var pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
        while(match = string.match(pattern)) {
            string = string.replace(new RegExp(match[2], 'g'), values[match[3]]);
        }
        return string;
    },
    
    // like Perl's Number::Format::format_number
    format_number : function(num) {
        num += '';
        x = num.split('.');
        x1 = x[0];
        x2 = x.length > 1 ? '.' + x[1] : '';
        var rgx = /(\d+)(\d{3})/;
        while (rgx.test(x1)) {
            x1 = x1.replace(rgx, '$1' + ',' + '$2');
        }
        return x1 + x2;
    },

    // get the URL of this person's gravatar image (from an email string)
    gravatar_url : function(string, options) {
        if(!options) options = {};
        if(!options.size) options.size = 80;
        if(!options.rating) options.rating = 'g';
        if(!options.default_name) options.default_name = 'identicon';

        var hash = options.hash ? vanilla.md5_hex(string) : string;

        var url = 'http://www.gravatar.com/avatar/' + hash + '.jpg'
            + '?s=' + options.size
            + '&r=' + options.rating
            + '&d=' + encodeURIComponent(options.default_name);
        return url;
    },

    // a nice UI element to show how many characters are left for typing in a field
    text_counter : function(args) {
        if(!args.field_selector) {
            alert("You must provide a 'field_selector' to textarea_counter!");
            return;
        }
        if(args.max_length == null) {
            alert("You must provide a 'max_length' to textarea_counter!");
            return;
        }
        if(!args.message_selector) {
            alert("You must provide a 'message_selector' to textarea_counter!");
            return;
        }
        if(args.max_length == 0) return;
        if(!args.warn_length) args.warn_length = args.max_length / 10;

        var _update_msg_func = function(field) {
            var length = field.value.length;
            var remain = args.max_length - length;
            if( remain < 0 ) remaining = 0;
            // truncate if necessary
            if( args.truncate && length > args.max_length ) {
                field.value = field.value.substring(0, args.max_length);
            }

            // create the message
            var msg;
            if( remain <= args.warn_length ) {
                msg = args.warn_template || '<span class="warn">#{remain} characters left</span> (#{max} characters allowed)';
            } else {
                msg = args.message_template || '#{remain} characters left (#{max} characters allowed)';
            }
            jQuery(args.message_selector).html(vanilla.template(msg, { max:  args.max_length, remain: remain }));
        };

        // update when keys are pressed
        jQuery(args.field_selector).keyup(function(event) {
            var field = event.target;
            _update_msg_func(field);
        });

        // to handle copy-paste, we should update the message every second
        // just in case it's changed by not via a keyup event
        window.setInterval(function() {
            jQuery(args.field_selector).each(function() {
                _update_msg_func(this);
            });
        }, 1000);
    },

    // md5_hex, similar to Perl's Digest::MD5 taken from
    // http://www.webtoolkit.info/javascript-md5.html
    md5_hex : function(string) {
        function RotateLeft(lValue, iShiftBits) {
            return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
        }
     
        function AddUnsigned(lX,lY) {
            var lX4,lY4,lX8,lY8,lResult;
            lX8 = (lX & 0x80000000);
            lY8 = (lY & 0x80000000);
            lX4 = (lX & 0x40000000);
            lY4 = (lY & 0x40000000);
            lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
            if (lX4 & lY4) {
                return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
            }
            if (lX4 | lY4) {
                if (lResult & 0x40000000) {
                    return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
                } else {
                    return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
                }
            } else {
                return (lResult ^ lX8 ^ lY8);
            }
        }
     
        function F(x,y,z) { return (x & y) | ((~x) & z); }
        function G(x,y,z) { return (x & z) | (y & (~z)); }
        function H(x,y,z) { return (x ^ y ^ z); }
        function I(x,y,z) { return (y ^ (x | (~z))); }
     
        function FF(a,b,c,d,x,s,ac) {
            a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
            return AddUnsigned(RotateLeft(a, s), b);
        };
     
        function GG(a,b,c,d,x,s,ac) {
            a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
            return AddUnsigned(RotateLeft(a, s), b);
        };
     
        function HH(a,b,c,d,x,s,ac) {
            a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
            return AddUnsigned(RotateLeft(a, s), b);
        };
     
        function II(a,b,c,d,x,s,ac) {
            a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
            return AddUnsigned(RotateLeft(a, s), b);
        };
     
        function ConvertToWordArray(string) {
            var lWordCount;
            var lMessageLength = string.length;
            var lNumberOfWords_temp1=lMessageLength + 8;
            var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
            var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
            var lWordArray=Array(lNumberOfWords-1);
            var lBytePosition = 0;
            var lByteCount = 0;
            while ( lByteCount < lMessageLength ) {
                lWordCount = (lByteCount-(lByteCount % 4))/4;
                lBytePosition = (lByteCount % 4)*8;
                lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<<lBytePosition));
                lByteCount++;
            }
            lWordCount = (lByteCount-(lByteCount % 4))/4;
            lBytePosition = (lByteCount % 4)*8;
            lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
            lWordArray[lNumberOfWords-2] = lMessageLength<<3;
            lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
            return lWordArray;
        };
     
        function WordToHex(lValue) {
            var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
            for (lCount = 0;lCount<=3;lCount++) {
                lByte = (lValue>>>(lCount*8)) & 255;
                WordToHexValue_temp = "0" + lByte.toString(16);
                WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
            }
            return WordToHexValue;
        };
     
        function Utf8Encode(string) {
            string = string.replace(/\r\n/g,"\n");
            var utftext = "";
     
            for (var n = 0; n < string.length; n++) {
     
                var c = string.charCodeAt(n);
     
                if (c < 128) {
                    utftext += String.fromCharCode(c);
                }
                else if((c > 127) && (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
                else {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
     
            }
     
            return utftext;
        };
     
        var x=Array();
        var k,AA,BB,CC,DD,a,b,c,d;
        var S11=7, S12=12, S13=17, S14=22;
        var S21=5, S22=9 , S23=14, S24=20;
        var S31=4, S32=11, S33=16, S34=23;
        var S41=6, S42=10, S43=15, S44=21;
     
        string = Utf8Encode(string);
     
        x = ConvertToWordArray(string);
     
        a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
     
        for (k=0;k<x.length;k+=16) {
            AA=a; BB=b; CC=c; DD=d;
            a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
            d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
            c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
            b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
            a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
            d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
            c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
            b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
            a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
            d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
            c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
            b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
            a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
            d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
            c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
            b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
            a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
            d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
            c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
            b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
            a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
            d=GG(d,a,b,c,x[k+10],S22,0x2441453);
            c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
            b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
            a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
            d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
            c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
            b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
            a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
            d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
            c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
            b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
            a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
            d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
            c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
            b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
            a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
            d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
            c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
            b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
            a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
            d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
            c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
            b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
            a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
            d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
            c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
            b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
            a=II(a,b,c,d,x[k+0], S41,0xF4292244);
            d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
            c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
            b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
            a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
            d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
            c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
            b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
            a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
            d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
            c=II(c,d,a,b,x[k+6], S43,0xA3014314);
            b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
            a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
            d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
            c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
            b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
            a=AddUnsigned(a,AA);
            b=AddUnsigned(b,BB);
            c=AddUnsigned(c,CC);
            d=AddUnsigned(d,DD);
        }
     
        var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);
     
        return temp.toLowerCase();
    },
    /* vanilla.is_logged_in()
        returns true if the person is logged in, else returns false */
    is_logged_in : function() {
        return !!vanilla.cookie.auth_params().email;
    },
    /* vanilla.cookie namespace
        a bunch of cookie related helper methods */
    cookie : {
        AUTH_COOKIE_NAME        : 'auth_tkt',
        PERSISTENT_COOKIE_NAME  : 'ARCOS_PERSISTENT_LOGIN',
        /* vanilla.cookie.get(name)
            The following three functions get, set, and delete cookies. They were first added
            to support better splash-page handling.
            credit: http://www.echoecho.com/jscookies02.htm (modified) */
        get : function(name) {
            if (document.cookie.length > 0) {
                var begin = document.cookie.indexOf(name + '=');
                if (begin != -1) {
                    begin += name.length + 1;
                    var end = document.cookie.indexOf(";", begin);
                    if (end == -1) end = document.cookie.length;
                    return unescape(document.cookie.substring(begin, end));
                }
            }
            return null;
        },
        /* vanilla.cookie.set(name, value, days, path, domain) */
        set : function(name, value, expireDays, path, domain) {
            var expires = new Date ();
            expires.setTime(expires.getTime() + (expireDays * 24 * 3600 * 1000));
            document.cookie = name + "=" + escape(value) +
                ((expireDays == null) ? "" : "; expires=" + expires.toGMTString()) +
                ((path == null) ? "" : "; path=" + path) +
                ((domain == null) ? "" : "; domain=" + domain);
        },
        /* vanilla.cookie.del(name) */
        del : function(name) {
            if (vanilla.cookie.get(name)) {
                document.cookie = name + "=" +
                  "; expires=Thu, 01-Jan-70 00:00:01 GMT";
            }
        },
        /* vanilla.cookie.auth_params()
            Get the Base64 encoded data out of our mod_auth_tkt cookie and return it as a JS hash */
        _auth_params : null,
        auth_params : function() {
            if( vanilla.cookie._auth_params != null )
                return vanilla.cookie._auth_params;

            // no cookies for our domain at all
            var allcookies = document.cookie;
            if( allcookies == null || allcookies == '' ) {
                vanilla.cookie._auth_params = {};
                return vanilla.cookie._auth_params;
            }

            // first look for the auth cookie, which is base64 encoded
            var value = vanilla.cookie.get(vanilla.cookie.AUTH_COOKIE_NAME);
            if( value ) {
                // Base64 decode the value
                value = vanilla.base64.decode(value);
                // now just get the DATA portion of the mod_auth_tkt cookie
                value = value.substr(40);       // this removes the digest and timestamp
                var bits = value.split('!');    // breaks up the uid, tokens and user data
                value = bits[bits.length -1];
            }

            // if that failed, look for the persistent cookie, which is not base64 encoded
            if (value == null) {
                value = vanilla.cookie.get(vanilla.cookie.PERSISTENT_COOKIE_NAME);
                if( value ) {
                    // break off the hash and UID
                    var bits = value.split('!');
                    value = bits[bits.length -1];
                }
            }
            // if after all that we found something, break it up into pairs for param
            vanilla.cookie._auth_params = {};
            if (value != null) {
                // split it into name-value pairs and put into the params hash
                var pairs = value.split('&');
                for (var i = 0; i < pairs.length; i++) {
                    var splitPair = pairs[i].split('=');
                    vanilla.cookie._auth_params[decodeURIComponent(splitPair[0])] =
                        decodeURIComponent(splitPair[1]);
                }
            }

            return vanilla.cookie._auth_params;
        }
    },
    /* vanilla.last_action_data()
        returns a Javascript hash (object) containing the data that the user filled in on their last action */
    last_action_data : function() {
        var json_value = vanilla.cookie.get('arcos_last_action_data');
        return eval('(' + json_value + ')');
    },
    /* vanilla.base64.decode(string)
        Adapted from the GPL code found at http://ostermiller.org/calc/encode.html */
    base64 : {
        END_OF_INPUT : -1,
        CHARS : {
            'A' :  0, 'B' :  1, 'C' :  2, 'D' :  3, 'E' :  4, 'F' :  5, 'G' :  6, 'H' : 7,
            'I' :  8, 'J' :  9, 'K' : 10, 'L' : 11, 'M' : 12, 'N' : 13, 'O' : 14, 'P' : 15,
            'Q' : 16, 'R' : 17, 'S' : 18, 'T' : 19, 'U' : 20, 'V' : 21, 'W' : 22, 'X' : 23,
            'Y' : 24, 'Z' : 25, 'a' : 26, 'b' : 27, 'c' : 28, 'd' : 29, 'e' : 30, 'f' : 31,
            'g' : 32, 'h' : 33, 'i' : 34, 'j' : 35, 'k' : 36, 'l' : 37, 'm' : 38, 'n' : 39,
            'o' : 40, 'p' : 41, 'q' : 42, 'r' : 43, 's' : 44, 't' : 45, 'u' : 46, 'v' : 47,
            'w' : 48, 'x' : 49, 'y' : 50, 'z' : 51, '0' : 52, '1' : 53, '2' : 54, '3' : 55,
            '4' : 56, '5' : 57, '6' : 58, '7' : 59, '8' : 60, '9' : 61, '+' : 62, '/' : 63
        },
        CURRENT_STR : null,
        CURRENT_COUNT : null,
        decode : function(str) {
            vanilla.base64.CURRENT_STR = str;
            vanilla.base64.CURRENT_COUNT = 0;
            var result = "";
            var inBuffer = new Array(4);
            var done = false;
            while (!done && (inBuffer[0] = vanilla.base64._next_char()) != vanilla.base64.END_OF_INPUT
                && (inBuffer[1] = vanilla.base64._next_char()) != vanilla.base64.END_OF_INPUT){
                inBuffer[2] = vanilla.base64._next_char();
                inBuffer[3] = vanilla.base64._next_char();
                result += vanilla.base64._ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4));
                if (inBuffer[2] != vanilla.base64.END_OF_INPUT){
                    result +=  vanilla.base64._ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2));
                    if (inBuffer[3] != vanilla.base64.END_OF_INPUT){
                        result +=  vanilla.base64._ntos((((inBuffer[2] << 6)  & 0xff) | inBuffer[3]));
                    } else {
                        done = true;
                    }
                } else {
                    done = true;
                }
            }
            return result;
        },
        _next_char : function() {   
            if (!vanilla.base64.CURRENT_STR) return vanilla.base64.END_OF_INPUT;
            while (true){      
                if (vanilla.base64.CURRENT_COUNT >= vanilla.base64.CURRENT_STR.length) return vanilla.base64.END_OF_INPUT;
                var nextCharacter = vanilla.base64.CURRENT_STR.charAt(vanilla.base64.CURRENT_COUNT);
                vanilla.base64.CURRENT_COUNT++;
                if (vanilla.base64.CHARS[nextCharacter]){
                    return vanilla.base64.CHARS[nextCharacter];
                }
                if (nextCharacter == 'A') return 0;
            }
            return vanilla.base64.END_OF_INPUT;
        },
        _ntos : function(n) {
            n=n.toString(16);
            if (n.length == 1) n="0"+n;
            n="%"+n;
            return unescape(n);
        }
    }
}

vanilla.addLoadEvent(function() {
    vanilla.onload = true;
});

jQuery.noConflict();
jQuery(document).ready(function($) {
    
    vanilla.document_ready = true;

    // enable tabs on lte and call radio forms
    $("#tab-tp a").click(function() {
        $("#tab-wt").removeClass("show");
        $("#text-wt").removeClass("show");
        $("#tab-tp").addClass("show");
        $("#text-tp").addClass("show");
    });
    $("#tab-wt a").click(function() {
        $("#tab-tp").removeClass("show");
        $("#text-tp").removeClass("show");
        $("#tab-wt").addClass("show");
        $("#text-wt").addClass("show");
    });

    // set and unset values in amount fields on contribute forms
    $("input[name=amount]").not("#id_amount_other_btn").click(function() {
        $("#id_amount_other").val("");
    });
    $("#id_amount_other").blur(function() {
        if ($(this).val() == "") {
            $("#id_amount_other_btn").attr("checked", false);
        } else {
            $("#id_amount_other_btn").attr("checked", true);
        }
    });

    // add a class to the body element for browser-specific style-hooks
    var classStr = "unknown_ua";
    if (jQuery.browser.safari) {
        classStr = "safari";
    } else if (jQuery.browser.opera) {
        classStr = "opera";
    } else if (jQuery.browser.msie) {
        classStr = "msie";
    } else if (jQuery.browser.mozilla) {
        classStr = "mozilla";
    }
    $("body").addClass(classStr);

    // focus on first form field
    $("body.is_form input:visible:enabled:first").focus();

    // handler for checkbox on membership pages
    $("#id_billing_info_same").change( function() {
        if ($("#id_billing_info_same").attr("checked")) {
            $("#billing_info").hide();
        } else {
            $("#billing_info").show();
        }
    });
    
    // handle checkbox for tribute giving
    $("#id_dedicate_gift").click( function() {
        if ($("#id_dedicate_gift").attr("checked")) {
            $("#dedicate_fields").show();
        } else {
            $("#dedicate_fields").hide();
        }
    });

    // jazz up ampersands in headers (http://patrickhaney.com/thinktank/2008/08/19/automatic-awesompersands)
    $("h1:contains('&'), h2:contains('&'), h3:contains('&')", document.body).contents().each(function() {
        if( this.nodeType == 3 ) {
            $(this).replaceWith(this.nodeValue.replace(/&/g, "<span class='amp'>&</span>"));
        }
    });

    // open certain classed links in new window
    $("a.newwindow").each(function() {
        $(this).css("padding-right", "18px").css("background", "url(/images/newwindow.png) no-repeat right").click(vanilla.openInNewWindow);
    });
    $("a.newwindow_noicon").each(function() {
        $(this).click(vanilla.openInNewWindow);
    });
    
    // use custom error messages
    $("#custom_messages li").each(function() {
        $("#messages li." + this.className).html($(this).html());
    });
    
    // rounded corners
    var roundStr = '<b class="cn tl"></b><b class="cn tr"></b><b class="cn bl"></b><b class="cn br"></b>';
    $(".round").addClass("boxc").append(roundStr);
    
    // enable nav hover for dropdowns in IE6
    $("#topnav li").hover(
        function () {
            $(this).addClass("over");
        },
        function () {
            $(this).removeClass("over");
        }
    );
    
    // format twitter text
    $('.twitter_friends span.msgtxt').each(function() {
        $(this).html(vanilla.formatTwitterText($(this).html()));
    });
});
