var vanilla = {
    
    has_swfobject : true,

    roundStr : '<b class="cn tl"></b><b class="cn tr"></b><b class="cn bl"></b><b class="cn br"></b>',
    
    contentDivId : 'content',

    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;
            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));
        var params = [];
        var pairs = query.split(/[;&]/);
        var i;
        for (i = 0; i < pairs.length; i++) {
            var nameVal = pairs[i].split('=');
            params[nameVal[0]] = decodeURIComponent(nameVal[1]);
        }
        return params;
    },

    /* 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;
    },

    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;
    },

    // used to autogenerate slugs in journals etc.
    text_to_slug : function(str) {
        return str.toLowerCase().replace(/[^a-zA-Z0-9]+/g,'_');
    },
    
    // 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 text_counter!");
            return;
        }
        if (args.max_length === null) {
            alert("You must provide a 'max_length' to text_counter!");
            return;
        }
        if (!args.message_selector) {
            alert("You must provide a 'message_selector' to text_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();
    },

    elide : function(str, length) {
        if (str.length <= length) {
            return str;
        }
        var char = '';
        var pat = /\s/;
        while (!char.match(pat)) {
            char = str.charAt(length);
            length++;
        }
        return str.substring(0, length - 1) + '&#8230;';    
    },

    /* 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;
            }
            vanilla.cookie._auth_params = {};

            // no cookies for our domain at all
            var allcookies = document.cookie;
            if ( allcookies === null || allcookies == '' ) {
                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 we have any group information, get it too
                if( bits.length == 3 ) {
                    vanilla.cookie._auth_params['arcos_permission_groups'] = bits[1].split(',');
                }
            }

            // 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
            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);
        }
    },
    
    /* the following functions are deprecated, but are present to help
       with backwards compatability when updating old installs to the
       new JS framework */
       
    addClassName : function( el, className ) {
        jQuery(el).addClass(className);
    },

    removeClassName : function( el, className ) {
        jQuery(el).removeClass(className);
    },
    
    getElementById : function(id) {
        if (document.getElementById) {
            return document.getElementById(id);
        } else if (document.all) {
            return document.all[ id ];
        } else {
            return null;
        }
    },

    getElementsByClassName : function( clName ) {
        var elements = [];
        var pattern = new RegExp("\\b" + clName + "\\b");
        var els = document.getElementsByTagName('*');
        for (var i = 0; i < els.length; i++) {
            var classes = els[i].className;
            if ( pattern.test( classes ) ) elements.push( els[i] );
        }
        return elements;
    },

    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;
        }
    },

    getNewWindowLinks : function() {
        if ( !document.getElementsByTagName && !document.createElement ) return false;
    
        // Change this to the text you want to use to alert the user that a new window will be opened
        var strNewWindowAlert = " (opens in a new window)";

        // Find all links
        var links = document.getElementsByTagName( 'a' );
        for ( var i = 0; i < links.length; i++ ) {
            var link = links[i];
            if ( /\bnewwindow\b/.test(link.className) ) {
                // Create an em element containing the new window warning text
                var objWarningText = document.createElement( "em" );
                objWarningText.appendChild( document.createTextNode( strNewWindowAlert ) );
                link.appendChild( objWarningText );
                link.onclick = vanilla.openInNewWindow;
            }
        }
    },

    hasAuthCookie : function() {
        var pos = document.cookie.indexOf('auth_tkt=');
        if ( pos != -1 ) {
            return true;
        } else {
            return false;
        }
    },

    /* 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) */

    getCookie : 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;
    },

    setCookie : function( name, value, expireDays, path ) {
        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 );
    },

    delCookie : function( name ) {
        if ( getCookie( name ) ) {
            document.cookie = name + "=" +
              "; expires=Thu, 01-Jan-70 00:00:01 GMT";
        }
    },

    enableHover : function() {
        var nodes = vanilla.getElementById( "mainnav" ).getElementsByTagName( "LI" );
        for ( i = 0; i < nodes.length; i++ ) {
            var node = nodes[i];
            node.onmouseover = function() {
                vanilla.addClassName( this, "over" );
            }
            node.onmouseout = function() {
                vanilla.removeClassName( this, "over" );
            }
        }
    },

    makeRoundedButtons : function() {
        var els = vanilla.getElementsByClassName( "roundbutton" );

        for (var i = 0; i < els.length; i++) {
            var el = els[i];

            /* Change class name for background images */
            vanilla.removeClassName( el, "roundbutton" );
            vanilla.addClassName( el, "roundedbutton" );
            
            /* gather up the children */
            var children = el.childNodes;
            var childElements = new Array();
            for ( var j = 0; j < children.length; j++ ) {
                if ( children[j].nodeType == 1 ) {
                    childElements.push( children[j] );
                }
            }

            /* create new divs and append */
            var div1 = document.createElement( "div" );
            var div2 = document.createElement( "div" );
            var div3 = document.createElement( "div" );

            for ( var j = 0; j < childElements.length; j++ ) {
                div3.appendChild( childElements[j] );
            }

            div2.appendChild( div3 );
            div1.appendChild( div2 );
            el.appendChild( div1 );
        }
    },

    makeRoundedBoxes : function() {
        var els = vanilla.getElementsByClassName( "roundbox" );

        for (var i = 0; i < els.length; i++) {
            var el = els[i];

            /* Change class name for background images */
            vanilla.removeClassName( el, "roundbox" );
            vanilla.addClassName( el, "roundedbox" );

            /* gather up the children */
            var children = el.childNodes;
            var childElements = new Array();
            for ( var j = 0; j < children.length; j++ ) {
                if ( children[j].nodeType == 1 ) {
                    childElements.push( children[j] );
                }
            }

            /* create top and bottom divs */
            var topdiv = document.createElement( "div" );
            topdiv.className = "top";
            var topinner = document.createElement( "div" );
            topinner.appendChild( document.createComment( "ie6 fix" ) );
            topdiv.appendChild( topinner );

            var btmdiv = document.createElement( "div" );
            btmdiv.className = "bottom";
            var btminner = document.createElement( "div" );
            btminner.appendChild( document.createComment( "ie6 fix" ) );
            btmdiv.appendChild( btminner );


            /* wrap children in middle divs */
            var middiv1 = document.createElement( "div" );
            middiv1.className = "middle";
            var middiv2 = document.createElement( "div" );
            var middiv3 = document.createElement( "div" );

            for ( var j = 0; j < childElements.length; j++ ) {
                middiv3.appendChild( childElements[j] );
            }

            middiv2.appendChild( middiv3 );
            middiv1.appendChild( middiv2 );

            /* put them all together */
            el.appendChild( topdiv );
            el.appendChild( middiv1 );
            el.appendChild( btmdiv );
        }
    }
};

// add to site.js if any of these needs to be activated
// vanilla.addLoadEvent( vanilla.getNewWindowLinks );
// vanilla.addLoadEvent( vanilla.makeRoundedButtons );
// vanilla.addLoadEvent( vanilla.makeRoundedBoxes );
// vanilla.addLoadEvent( vanilla.enableHover );

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

jQuery.noConflict();
jQuery(document).ready(function($) {

    vanilla.document_ready = true;

    // use traditional serialization that our servers understand
    $.ajaxSetup({traditional: true});

    // simple tabs
    $('.tab-content').hide();
    $('.tab-content:first').show();
    $('ul.tabs li:first').addClass('active');
    $('ul.tabs li').click(function() {
        $('ul.tabs li').removeClass('active');
        $('.tab-content').hide();
        $(this).addClass('active');

        var target = $(this).find('a').attr('href');
        $(target).fadeIn();
        return false;
    });
    
    // set and unset values in amount fields on contribute forms
    $("input[name=amount]").not("#amount_other_btn").click(function() {
        $("#amount_other").val("");
    });
    $("#amount_other").blur(function() {
        if ($(this).val() == "") {
            $("#amount_other_btn").attr("checked", false);
        } else {
            $("#amount_other_btn").attr("checked", true);
        }
    });

    // add a class to the body element for browser-specific style-hooks
    $("body").addClass([$.userAgent.os, $.userAgent.browser, $.userAgent.browserVersionX, $.userAgent.browserVersion].join(' '));
    if ($.userAgent.borderRadius) {
        $('body').addClass('border_radius');
    }

    // focus on first form field
    if ($('body.no_form_focus').length === 0) {
        if ($('#messages').length > 0) {
            $('body.is_form #' + vanilla.contentDivId + ' input.err:visible:enabled:first').each(function() {
                $(this).focus();
            });
        } else {
            $('body.is_form #' + vanilla.contentDivId + ' input:visible:enabled:first').each(function() {
                $(this).focus();
            });
        }
    }

    // even out widths of form inputs
    // we skip widths of elements that might be display: none since jQuery returns 0 for those
    // instead, we try to see if there is a css width associated and use that instead
    if ($.userAgent.os === 'windows') {
        if ($.userAgent.browserVersionX === 'firefox3') {
            $('form select.input').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w); // ???
                }
            });
        }
        else if ($.userAgent.browserVersionX === 'chrome6') {
           $('form select.input').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w - 2);
                }
            });
            $('form textarea').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w - 2);
                }
            });
        }
        else if ($.userAgent.browserVersionX === 'chrome5') {
           $('form select.input').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w - 2);
                }
            });
            $('form textarea').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w - 2);
                }
            });
        }
        else if ($.userAgent.browserVersionX === 'safari5') {
           $('form select.input').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w + 22);
                }
            });
            $('form textarea').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w - 2);
                }
            });
        }
        else if ($.userAgent.browserVersionX === 'safari4') {
           $('form select.input').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w + 22);
                }
            });
            $('form textarea').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w + 2);
                }
            });
        }
    }
    if ($.userAgent.os === 'mac') {
        if (($.userAgent.browser == 'firefox') && ($.userAgent.verisonX >= 3)) {
            $('form textarea').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w + 4);
                }
            });
        }
        else if ($.userAgent.browserVersionX === 'safari4') {
            $('form textarea').each(function() {
                var w = $(this).width() || parseInt($(this).css('width'));
                if (!isNaN(w) && (w > 0)) {
                    $(this).width(w + 4);
                }
            });
        }
    }    

    // handler for checkbox on membership pages
    $("#billing_info_same").change( function() {
        if ($("#billing_info_same").attr("checked")) {
            $("#billing_info").hide();
        } else {
            $("#billing_info").show();
        }
    });
    
    // handle inputs for tribute giving
    // don't prefill on initial page load
    if($('input[name=dedicate_gift]').length > 0) {
        if ($('#messages').length === 0) {
            $('input[name=dedicate_gift]').attr('checked', false);
            $('input[name=dedicate_type]:checked').attr('checked', false);
            $('input[name=dedicate_name]').val('');
            $('input[name=dedicate_acknowledgment_name]').val('');
            $('input[name=dedicate_acknowledgment_address1]').val('');
            $('input[name=dedicate_acknowledgment_city]').val('');
            $('input[name=dedicate_acknowledgment_state]').val('');
            $('input[name=dedicate_acknowledgment_zip]').val('');
        }
    }
    // otherwise show dedicate fields if box is checked
    if($('input[name=dedicate_gift]').attr('checked')) {
        $('#dedicate_fields').show();
    }    
    // add event handler for checkbox to toggle display
    $('input[name=dedicate_gift]').click( function() {
        if ($('input[name=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>"));
        }
    });
    
    // wrap IE6/7 hr tags in a div for x-browser styling
    if (($.userAgent.browserVersionX == 'explorer6') || ($.userAgent.browserVersionX == 'explorer7')) {
        $('hr').each(function() {
            $(this).wrap('<div class="hr"></div>');
            $(this).parent().css('border-bottom-width', $(this).css('height')).css('border-bottom-color', $(this).css('color'));
        });
    }

    // open certain classed links in new window
    $("a.newwindow").css("padding-right", "18px").css("background", "url(/images/newwindow.png) no-repeat right");
    $('a.newwindow, a.newwindow_noicon').attr('target', '_blank').attr('title', 'Opens in a new window');

    
    // use custom error messages
    $("#custom_messages li").each(function() {
        if (this.className) {
            $("#messages li." + this.className).html($(this).html());
        }
    });
    
    // rounded corners
    $(".round").addClass("boxc").append(vanilla.roundStr);
    $(".round5").addClass("boxc boxc5").append(vanilla.roundStr);
    
    // enable nav hover for dropdowns in IE6
    $("#topnav li").hover(
        function () {
            $(this).addClass("over");
        },
        function () {
            $(this).removeClass("over");
        }
    );

    // swap values in forms that won't show errors
    $("input.swap").focus(function() {
        vanilla.swapValue(this, false);
    });
    $("input.swap").blur(function() {
        vanilla.swapValue(this, true);
    });
    
    // special case: year in date custom fields
    $("input.input_year").focus(function() {
        vanilla.swapValue(this, false, 'Year');
    });
    $("input.input_year").blur(function() {
        vanilla.swapValue(this, true, 'Year');
    });
    
    // format twitter text
    $('.twitter_friends span.msgtxt').each(function() {
        $(this).html(vanilla.formatTwitterText($(this).html()));
    });

    // make last visible item in a listbox "lastitem" despite what markup says
    $('.listbox .listeditem:visible:last').addClass('lastitem');

    // hide preview bars
    $('.preview_notice a.hide_preview').click(function() {
        $('.preview_notice').hide();
    });
    
    // autogenerate slugs on certain forms
    $('form#directory_listing_edit #name').keyup(function() {
        $('form#directory_listing_edit #slug').val(vanilla.text_to_slug(this.value));
    });
    $('form#journal_form input#title').keyup(function() {
        $('form#journal_form input#slug').val(vanilla.text_to_slug(this.value));
    });
    $('form#event_listing_edit input#name').keyup(function() {
        $('form#event_listing_edit input#slug').val(vanilla.text_to_slug(this.value));
    });

    // add debug field to all forms
    $('form').append($('<input type="hidden" name="debug" />')).val(
        '[' + navigator.userAgent + '] [' + navigator.platform + ']'
    );


    /* the following code is deprecated in 7.60 in accordance with template and other changes */

    // 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");
    });
    
    /* ids for these forms have changed */
    
    // set and unset values in amount fields on contribute forms
    if ($('#id_amount_other_btn').length > 0) {
        $("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);
            }
        });
    }

    // 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();
        }
    });


});

