
( function( $ ) {

    /* attempts to execute a callback on page load against the specified selector(s) */
    $.attempt = function( selector, callback ) {
        $( function() {
            var selection = $( selector );
            if( selection.length ) {
                $.each( selection, function( i, element ) {
                    callback.call( $( element ) );
                } );
            }
        } );
    };

    /* gets or sets the specified cookie */
    $.cookie = function( name, value, options ) {
        if( typeof value != 'undefined' ) {
            options = options || {};
            if( value === null ) {
                value = '';
                options.expires = -1;
            }
            if( !options.path ) {
                options.path = '/';
            }
            var expires = '';
            if( options.expires && ( typeof options.expires == 'number' || options.expires.toUTCString ) ) {
                var date;
                if( typeof options.expires == 'number' ) {
                    date = new Date();
                    date.setTime( date.getTime() + ( options.expires * 24 * 60 * 60 * 1000 ) );
                } else {
                    date = options.expires;
                }
                expires = '; expires=' + date.toUTCString();
            }
            var path = ( options.path ? '; path=' + (options.path) : '' );
            var domain = ( options.domain ? '; domain=' + (options.domain) : '' );
            var secure = ( options.secure ? '; secure' : '' );
            document.cookie = [ name, '=', encodeURIComponent( value ), expires, path, domain, secure ].join( '' );
        } else if( document.cookie && document.cookie != '' ) {
            var cookies = document.cookie.split( ';' );
            for( var i = 0; i < cookies.length; i++ ) {
                var cookie = $.trim( cookies[ i ] );
                if( cookie.substring( 0, name.length + 1 ) == ( name + '=' ) ) {
                    return decodeURIComponent( cookie.substring( name.length + 1 ) );
                }
            }
        }
    };

} )( jQuery );


jQuery.fn.absbottom = function() {
    return Math.ceil( this.offset().top + this.outerHeight() );
};

jQuery.fn.expandable = function( minimum, maximum ) {
    var hcheck = !( jQuery.browser.msie || jQuery.browser.opera );
    var resize = function( e ) {
        e = e.target || e;
        var hdiff = 0;
        if( jQuery.browser.webkit ) {
            hdiff = $( e ).innerHeight() - $( e ).height();
        }
        var vlen = e.value.length, ewidth = e.offsetWidth;
        if( vlen != e.value_length || ewidth != e.box_width ) {
            if( hcheck && ( vlen < e.value_length || ewidth != e.box_width ) ) {
                e.style.height = '0px';
            }
            var h = Math.max( minimum, Math.min( e.scrollHeight, maximum ) );
            e.style.overflow = ( e.scrollHeight > h ? 'auto' : 'hidden' );
            e.style.height = ( h - hdiff ) + 'px';
            e.value_length = vlen;
            e.box_width = ewidth;
        }
        return true;
    };
    this.each( function() {
        if( this.nodeName.toLowerCase() == 'textarea' ) {
            resize( this );
            if( !this.initialized ) {
                this.initialized = true;
                jQuery( this ).bind( 'keyup', resize ).bind( 'focus', resize );
            }
        }
    } );
    return this;
};

jQuery.fn.once = function() {
    var self = this;
    if( !self.length ) return self;
    jQuery.each( arguments, function( index, method ) {
        method.call( self );
    } );
    return self;
};

jQuery.initialize = function( selector, callback ) {
    jQuery( function() {
        jQuery( selector ).once( callback );
    } );
};


jQuery.fn.currencyToFloat = function() {
    return parseFloat( this.text().slice( 1 ) );
};

tippr = {

    modal_cache: {},

    /* animates the offer acceleration scale */
    animate_acceleration: function( element ) {
        var counter = element.find( '.counter' );
        var width = element.width() - counter.width();
        var start = parseFloat( element.find( '.start-value strong' ).text().slice( 1 ) );
        var end = parseFloat( element.find( '.end-value strong' ).text().slice( 1 ) );
        var value = parseFloat( element.find( '.counter .value' ).text().slice( 1 ) );
        var delta = ( value - start ) / ( end - start );
        counter.animate( { 'left': width * delta }, 'slow' );
    },

    /* attaches clipboard support to the specified element */
    attach_clipboard_support: function( inputid, linkid, content ) {
        if( FlashDetect && FlashDetect.installed ) {
            $( linkid ).show();
            ZeroClipboard.setMoviePath( '/static/swf/ZeroClipboard.swf' );
            var client = new ZeroClipboard.Client();
            client.setText( content );
            client.glue( linkid.substr( 1 ) );
            $( window ).bind( 'resize', function() {
                client.reposition();
                $( client.div ).css( 'z-index', '99999' );
            } );
            $( window ).bind( 'scroll', function() {
                client.reposition();
                $( client.div ).css( 'z-index', '99999' );
            } );
            client.show();
            //client.addEventListener( 'onComplete', function() {
            //    $( inputid ).effect( 'highlight', {}, 200 );
            //} );
            $( client.div ).css( 'z-index', '99999' );
        }
    },

    /* checks for a flag on the current session */
    check: function( key, remove ) {
        var value = $.cookie( key );
        if( remove ) {
            $.cookie( key, null );
        }
        return value;
    },

    /* presents a confirmation modal to the user */
    confirm: function( parameters ) {
        var modal = $( '#confirm-modal' );
        if( !modal.data( 'overlay' ) ) {
            this.create_modal( { modal: modal, prevent_close: true } );
        }
        if( parameters.title ) {
            modal.find( '#confirm-modal-title' ).text( parameters.title );
            modal.find( '.modal-header' ).show();
        } else {
            modal.find( '.modal-header' ).hide();
            modal.find( '#confirm-modal-title' ).text( '' );
        }
        var content = modal.find( '#confirm-modal-content' );
        if( parameters.content ) {
            content.text( parameters.content );
            content.show();
        } else {
            content.text( '' );
            content.hide();
        }
        modal.find( '#confirm-yes-button span' ).text( parameters.yes || 'Yes' );
        modal.find( '#confirm-no-button span' ).text( parameters.no || 'No' );
        modal.find( '#confirm-yes-button' ).click( function( event ) {
            modal.overlay().close();
            if( parameters.onyes ) {
                parameters.onyes();
            }
        } );
        modal.find( '#confirm-no-button' ).click( function( event ) {
            modal.overlay().close();
            if( parameters.onno ) {
                parameters.onno();
            }
        } );
        modal.overlay().load();
    },

    /* creates a modal */
    create_modal: function( parameters ) {
        var modal = parameters.modal;
        if( parameters.prevent_close ) {
            modal.find( '.modal-close-button' ).remove();
        }
        if( !modal.parents().length ) {
            $( 'body' ).append( modal );
        }
        if( !modal.data( 'overlay' ) ) {
            var options = {
                api: true,
                close: '.modal-close-button',
                top: parameters.top || '20%'
            }
            if( !parameters.prevent_expose ) {
                options.expose = {
                    color: '#000',
                    loadSpeed: 200,
                    opacity: 0.3
                };
            }
            if( parameters.prevent_close ) {
                options.closeOnClick = false;
                options.closeOnEsc = false;
            }
            if( parameters.remove_on_close ) {
                options.onClose = function( event ) {
                    modal.remove();
                };
            }
            modal.overlay( options );
        }
        return modal;
    },

    /* debugging support */
    debug: function() {
        if( window.location.search.search( '_jsdebug' ) >= 0 ) {
            $.each( arguments, function( i, argument ) {
                console.debug( argument );
            } );
        }
    },

    /* facebook api */
    facebook: function( parameters ) {
        var self = this;
        self.authenticating = false;
        self.callback = null;
        self.redirect = null;
        self.requested_properties = []
        $.extend( self, parameters );
        FB.init( {
            appId: self.api_key,
            cookie: true,
            status: true,
            xfbml: true
        } );
        FB.Event.subscribe( 'auth.sessionChange', self.handle_session );
        FB.provide( 'UIServer.Methods', {
            'permissions.request': {
                size: { width: 575, height: 240 },
                url: 'connect/uiserver.php',
                transform: function( call ) {
                    if( call.params.display == 'dialog' ) {
                        call.params.display = 'iframe';
                        call.params.channel = FB.UIServer._xdChannelHandler( call.id, 'parent.parent' );
                        call.params.cancel = FB.UIServer._xdNextHandler( call.cb, call.id, 'parent.parent', true );
                        call.params.next = FB.UIServer._xdResult( call.cb, call.id, 'parent.parent', false );
                    }
                    return call;
                }
            }
        } );
        self.originalPostTarget = FB.Content.postTarget;
        FB.Content.postTarget = function( opts ) {
            opts.params = FB.JSON.flatten( opts.params );
            self.originalPostTarget( opts );
        };
        if( self.debugging ) {
            console.info( 'Debugging facebook.' )
        }
    },

    /* attaches the specified flag to the current session */
    flag: function( key, value ) {
        $.cookie( key, value || 'true' );
        return value;
    },

    /* displays a flash message */
    flash: function( parameters ) {
        parameters = parameters || {};
        if( !parameters.text ) {
            parameters.text = 'There was an error processing your request. Please try again.';
        }
        if( !parameters.tags ) {
            parameters.tags = 'error';
        }
        if( parameters.tags == 'error' || parameters.tags == 'warning' ) {
            parameters.permanent = true;
        }
        var message = $( '<li class="' + parameters.tags + '"><a href="#" class="flash-message-close">close</a>' + parameters.text + '</li>' ).hide();
        var receiver = null;
        if( parameters.source ) {
            var container = parameters.source.parents( '.flash-message-container' ).first();
            if( container.length ) {
                receiver = container.find( '.flash-message-receiver' );
            }
        }
        if( !( receiver && receiver.length ) ) {
            receiver = $( '.flash-message-receiver' ).first();
        }
        if( !( receiver && receiver.length ) ) {
            return;
        }
        receiver.append( message );
        if( receiver.is( '.nofading' ) ) {
            message.show();
            if( !parameters.permanent ) {
                setTimeout( function() { message.remove(); }, 3000 );
            }
        } else {
            message.fadeIn( 500 );
            if( !parameters.permanent ) {
                setTimeout( function() { message.fadeOut( 500, function() { message.remove(); } ) }, 3000 );
            }
        }
        return message;
    },

    /* validated form implementation */
    form: function( parameters ) {
        var self = this;
        $.extend( this, parameters );
        this.button = parameters.submit_button || this.form.find( 'button' );
        this.errors = [];
        this.marks = {};
        this.messages = [];
        this.tooltip = null;
        this.form.find( 'input:text,input:password,textarea' ).bind( 'blur',
            function( event ) {
                var element = $( event.target );
                if( element && element.length ) {
                    self.revalidate( element );
                }
            }
        );
        this.form.find( 'select' ).bind( 'change',
            function( event ) {
                var element = $( event.target );
                if( element && element.length ) {
                    self.revalidate( element );
                }
            }
        );
        this.form.submit( function( event ) {
            return self.submit();
        } );
        if( this.hook_submit_button ) {
            this.button.click( function( event ) {
                return self.submit();
            } );
        }
    },

    /* ensures that external urls appear in new windows */
    externify: function() {
        var parts = document.location.hostname.split( '.' ), target = '';
        var root = parts[ parts.length - 2 ] + '.' + parts[ parts.length - 1 ];
        $.each( [ 'http://', 'https://' ], function( i, protocol ) {
            $( "a[href^='" + protocol + "']" ).each( function( i, element ) {
                parts = $( element ).attr( 'href' ).substr( protocol.length ).split( '/' )[ 0 ].split( '.' );
                target = parts[ parts.length - 2 ] + '.' + parts[ parts.length - 1 ];
                if( target != root ) {
                    $( element ).attr( 'target', '_blank' );
                }
            } );
        } );
    },

    /* ... */
    install_konami_listener: function( callback ) {
        var keys = [ 38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 13 ], position = 0;
        $( window ).keyup( function( event ) {
            if( event.keyCode == keys[ position ] ) {
                position++;
                if( position == keys.length ) {
                    callback();
                    position = 0;
                }
            } else {
                position = 0;
            }
        } );
    },

    /* loads a modal from the server and shows it */
    load_modal: function( parameters ) {
        var data = parameters.data, url = parameters.url;
        if( !data && !parameters.no_cache && !parameters.post && this.modal_cache[ url ] ) {
            var modal = this.modal_cache[ url ];
            if( parameters.onload ) {
                parameters.onload( modal );
            }
            if( parameters.title ) {
                modal.find( '.modal-header-title span' ).text( parameters.title );
            }
            if( parameters.show ) {
                modal.overlay().load();
            }
        } else {
            var self = this;
            $.ajax( {
                data: data,
                dataType: 'html',
                type: ( data || parameters.post ) ? 'POST' : 'GET',
                url: url,
                success: function( response ) {
                    var modal = $( response );
                    if( !data && !parameters.no_cache && !parameters.post ) {
                        self.modal_cache[ url ] = modal;
                    }
                    parameters.modal = modal
                    self.create_modal( parameters );
                    if( parameters.onload ) {
                        parameters.onload( modal );
                    }
                    if( parameters.title ) {
                        modal.find( '.modal-header-title span' ).text( parameters.title );
                    }
                    if( parameters.show ) {
                        modal.overlay().load();
                    }
                    if( !parameters.nofocus ) {
                        modal.find( 'input[type=text],input[type=password],textarea' ).first().focus();
                    }
                }
            } );
        }
    },

    /* submits a post via ajax */
    post: function( parameters ) {
        var data = '', self = this;
        if( parameters.data ) {
            data = $.param( parameters.data );
        }
        var spinner = parameters.spinner;
        if( spinner ) {
            self.spin_button( spinner, parameters.spin_text );
        }
        $.ajax( {
            cache: false,
            data: data,
            type: 'POST',
            url: parameters.url,
            success: function( response ) {
                if( spinner ) {
                    self.unspin_button( spinner );
                }
                if( response ) {
                    if( response.messages ) {
                        $.each( response.messages, function( i, item ) {
                            self.flash( { text: item.message, tags: item.tags } );
                        } );
                    }
                    if( response.response ) {
                        if( parameters.reload || parameters.reload_on_success ) {
                            window.location.reload();
                        } else if( parameters.onsubmit ) {
                            parameters.onsubmit( response.response );
                        }
                    } else {
                        if( parameters.reload || parameters.reload_on_failure ) {
                            window.location.reload();
                        } else if ( parameters.onfailure ) {
                            parameters.onfailure( response.error, response );
                        }
                    }
                } else {
                    self.flash();
                    if( parameters.onerror ) {
                        parameters.onerror();
                    }
                }
            },
            error: function() {
                if( spinner ) {
                    self.unspin_button( spinner );
                }
                self.flash();
                if( parameters.onerror ) {
                    parameters.onerror();
                }
            }
        } );
    },

    /* submits a status query to twitter */
    query_twitter: function( user, query, callback ) {
        jQuery.getJSON( window.location.protocol + '//twitter.com/statuses/user_timeline/' + user + '.json?callback=?&count=10',
            function( data ) { callback( data ); }
        );
    },

    /* redirects to ssl if necessary */
    require_ssl: function( secured, unsecured ) {
        if( window.location.protocol == 'https:' ) {
            if( secured ) {
                secured();
            }
        } else {
            if( unsecured ) {
                unsecured();
            }
            window.location = 'https:' + window.location.href.substr( 5 );
        }
    },

    /* spins the specified button */
    spin_button: function( button, text ) {
        var span = button.find( 'span' );
        if( span.length ) {
            button.queue( function( next ) {
                if( text ) {
                    button.data( 'normal-text', span.text() );
                    span.text( text );
                }
                span.prepend( $( '<img src="/static/img/misc/button-loading.gif" class="spinner"/>' ) );
                button.attr( 'disabled', true );
                next();
            } ).delay( 300 );
        }
    },

    /* shows a tooltip */
    tooltip: function( parameters ) {
        var div = $( '#default-tooltip' ), element = parameters.element;
        if( element && element.length ) {
            div.find( 'span' ).text( parameters.text );
            var api = element.tooltip( {
                api: true,
                events: { input: 'nothing,nothing', widget: 'nothing,nothing' },
                offset: parameters.offset || [ 0, 0 ],
                opacity: parameters.opacity || 1.0,
                position: parameters.position || 'top left',
                tip: '#default-tooltip',
                onBeforeShow: function( event, position ) {
                    var tip = event.target.getTip();
                    event.target.getConf().offset = [ -6, tip.outerWidth() ];
                }
            } );
            if( parameters.show ) {
                api.show();
            }
            return api;
        }
    },

    /* tracks an event */
    track_event: function( category, action, label ) {
        _gaq.push( [ '_trackEvent', category, action, label ] );
        var moment = new Date(), now = null;
        do {
            now = new Date();
        } while( now - moment < 300 );
    },

    /* tracks a page view */
    track_pageview: function( path ) {
        _gaq.push( [ '_trackPageview', path ] );
    },

    /* unspins the specified button */
    unspin_button: function( button ) {
        var span = button.find( 'span' );
        if( span.length ) {
            button.queue( function( next ) {
                span.find( 'img' ).remove();
                var text = button.data( 'normal-text' );
                if( text ) {
                    span.text( text );
                }
                button.attr( 'disabled', false );
                next();
            } );
        }
    },

    /* changes the current language in the current session & account */
    change_language: function(lang) {
        var redirect_to = window.location.protocol + '//' + window.location.host + window.location.pathname;
        if (window.location.search) {
            var lang_index = window.location.search.indexOf('lang');
            if (lang_index >= 0) {
                // strip out lang argument
                var trailing_querystring = '';
                var lang_end_index = window.location.search.indexOf('&', lang_index);
                if (lang_end_index !== -1) {
                    trailing_querystring = window.location.search.substring(lang_end_index + 1);
                }
                var stripped_querystring = window.location.search.substring(0, lang_index) + trailing_querystring;
                redirect_to += stripped_querystring;
                if (redirect_to[redirect_to.length - 1] !== '?' && redirect_to[redirect_to.length - 1] !== '&') {
                    redirect_to += '&';
                }
            }
            else {
                redirect_to += window.location.search + '&';
            }
        }
        else {
            redirect_to += '?';
        }
        redirect_to += 'lang=' + lang;
        if (window.location.hash) {
            redirect_to += window.location.hash;
        }
        window.location = redirect_to;
        return false;
    }
}

$.extend( tippr.facebook.prototype, {

    /* executes one of two callbacks depending on session status */
    check: function( success, failure ) {
        FB.getLoginStatus( function( response ) {
            if( response.session ) {
                success();
            } else {
                failure();
            }
        } );
    },

    /* debugging support */
    debug: function() {
        if( this.debugging ) {
            $.each( arguments, function( i, argument ) {
                console.debug( argument );
            } );
        }
    },

    /* handles session changes from facebook */
    handle_session: function( response ) {
        var self = this, session = response.session;
        if( session && !self.authenticated ) {
            $.ajax( {
                cache: false,
                context: self,
                dataType: 'json',
                type:'POST',
                url: self.login_url,
                success: self.query_permissions,
                error: self.failed
            } );
        }
    },

    /* attempts to login via facebook connect */
    login: function( next ) {
        var self = this;
        if( next ) {
            self.redirect = next;
        }
        FB.getLoginStatus( function( response ) {
            if( !response.session && !self.authenticated && !self.authenticating ) {
                self.authenticating = true;
                FB.login( self.handle_session );
            } else {
                self.handle_session( response );
            }
        } );
    },

    /* attempts to logout */
    logout: function() {
        var self = this;
        if( login_method == 'facebook' ) {
            try {
                FB.getLoginStatus( function( response ) {
                    if( response.session ) {
                        FB.logout( function( response ) {
                            window.location = self.logout_url;
                        } );
                    } else {
                        window.location = self.logout_url;
                    }
                } );
            } catch( exception ) {
                window.location = self.logout_url;
            }
        } else {
            window.location = self.logout_url;
        }
    },

    /* prompts the user for information we couldn't get from facebook */
    prompt_user: function( uid, fields, values ) {
        var self = this;
        self.debug( 'prompting user to connect account' );
        tippr.load_modal( {
            data: { fields: fields.join( ',' ) },
            prevent_close: true,
            show: true,
            url: '/r/modal/connect-account/',
            onload: function( modal ) {
                var form = new tippr.form( {
                    form: modal.find( '#connect-account-form' ),
                    reset_on_success: true,
                    spin_submit_button: true,
                    spin_text: 'Connecting',
                    submit_with_ajax: true,
                    use_inline_marks: true,
                    onsubmit: function( form, response ) {
                        modal.overlay().close();
                        $.extend( values, response );
                        self.update_account( uid, values );
                    }
                } );
            }
        } );
    },

    /* publishes an attachment to this account's stream */
    publish: function( parameters ) {
        var actions = parameters.actions || [];
        FB.ui( {
            attachment: parameters.attachment,
            method: 'stream.publish',
            message: parameters.message || '',
            user_message_prompt: parameters.prompt || 'Share this with your friends!'
        } );
    },

    /* queries the permissions of the current facebook user */
    query_permissions: function( requests ) {
        var self = this, uid = FB.getSession().uid, fields = [];
        if( $.inArray( 'zipcode', requests ) >= 0 ) {
            fields.push( requests.pop( 'zipcode' ) );
        }
        self.debug( 'querying permissions for uid = ' + uid, requests );
        FB.Data.query( 'select email,publish_stream from permissions where uid = ' + uid ).wait( function( rows ) {
            var permissions = [], row = rows[ 0 ];
            if( row[ 'email' ] != '1' ) {
                permissions.push( 'email' );
            }
            if( row[ 'publish_stream' ] != '1' ) {
                permissions.push( 'publish_stream' );
            }
            if( permissions.length > 0 ) {
                FB.ui( { method: 'permissions.request', perms: permissions.join( ',' ) }, function( response ) {
                    self.debug( response );
                    if( $.inArray( 'email', permissions ) >= 0 && response.perms.search( 'email' ) < 0 ) {
                        fields.push( 'email' );
                    }
                    self.query_profile( uid, requests, fields );
                } );
            } else {
                self.query_profile( uid, requests, fields );
            }
        } );
    },

    /* queries the profile of the current facebook user */
    query_profile: function( uid, requests, fields ) {
        var self = this, values = {};
        if( requests.length > 0 ) {
            FB.Data.query( 'select ' + requests.join( ',' ) + ' from user where uid = ' + uid ).wait( function( rows ) {
                if( rows.length > 0 ) {
                    $.each( rows[ 0 ], function( field, value ) {
                        if( value ) {
                            values[ field ] = value;
                        } else if( field != 'username' ) {
                            fields.push( field );
                        }
                    } );
                } else {
                    fields = fields.concat( requests );
                }
                if( fields.length > 0 ) {
                    self.prompt_user( uid, fields, values );
                } else {
                    self.update_account( uid, values );
                }
            } );
        } else if( fields.length > 0 ) {
            self.prompt_user( uid, fields, values );
        } else if( !$.isEmptyObject( values ) ) {
            self.update_account( uid, values );
        } else {
            self.redirect_user();
        }
    },

    /* redirects the user once authentication is completed */
    redirect_user: function() {
        if( this.callback ) {
            this.callback();
        } else if( this.redirect ) {
            window.location = this.redirect;
        } else {
            window.location = window.location;
        }
    },

    /* queries a particular permission */
    request_permissions: function( permissions, callback ) {
        FB.ui( { method: 'permissions.request', perms: permissions.join( ',' ) }, function( response ) {
            callback( response.perms );
        } );
    },

    /* shares a url via facebook */
    share: function( url ) {
        FB.ui( { method: 'stream.share', u: url } );
    },

    /* updates our account with the profile information from facebook */
    update_account: function( uid, values ) {
        var self = this;
        values[ 'uid' ] = uid;
        tippr.post( {
            data: values,
            url: self.update_url,
            onsubmit: function( response ) {
                if( response.result == 'merge' ) {
                    tippr.load_modal( {
                        data: { target: response.target },
                        prevent_close: true,
                        url: '/r/modal/merge-account/',
                        onload: function( modal ) {
                            var form = new tippr.form( {
                                form: modal.find( '#merge-account-form' ),
                                extra_data: {
                                    facebookid: values.uid,
                                    facebook_username: values.username,
                                    fullname: values.name,
                                    zipcode: values.zipcode
                                },
                                reload_on_success: true,
                                spin_submit_button: true,
                                spin_text: 'Merging Accounts',
                                submit_with_ajax: true,
                                use_inline_marks: true
                            } );
                            modal.overlay().load();
                            modal.find( 'input[name=password]' ).focus();
                        }
                    } );
                } else {
                    self.redirect_user();
                }
            }
        } );
    }

} );

$.extend( tippr.form.prototype, {

    clear: function() {
        this.errors = []; this.tooltip = null;
        this.form.find( 'input:text,input:password,select,textarea' ).removeClass( 'invalid' );
        $.each( this.marks, function( name, para ) {
            para.hide();
        } );
        $.each( this.messages, function( i, message ) {
            message.remove();
        } );
        this.messages = [];
    },

    flash: function( parameters ) {
        parameters[ 'source' ] = this.form;
        this.messages.push( tippr.flash( parameters ) );
    },
            
    focus: function( element ) {
        element.focus();
        if( element.is( 'input[type=text],input[text=password],textarea' ) ) {
            element.select();
        }
    },

    mark: function( name, message ) {
        if( this.use_inline_marks ) {
            var para = this.marks[ name ];
            if( para ) {
                para.text( message );
                para.show();
            } else {
                this.marks[ name ] = $( '<p class="form-error">' + message + '</p>' );
                this.form.find( '[name=' + name + ']' ).parents( 'p' ).after( this.marks[ name ] );
            }
        } else {
            var input = this.form.find( '[name=' + name + ']' );
            this.tooltip = tippr.tooltip( { element: input, show: true, text: message } );
        }
    },

    postsubmit: function() {
        var self = this;
        if( self.spin_submit_button ) {
            tippr.unspin_button( self.button );
        }
        if( self.hide_on_submit ) {
            $( self.hide_on_submit ).show();
        }
    },

    presubmit: function() {
        var self = this;
        if( self.spin_submit_button ) {
            tippr.spin_button( self.button, self.spin_text );
        }
        if( self.hide_on_submit ) {
            $( self.hide_on_submit ).hide();
        }
    },

    reset: function() {
        this.form.resetForm();
    },

    revalidate: function( element ) {
        var value = element.val();
        if( element.is( '.invalid' ) ) {
            var name = element.attr( 'name' );
            if( value.length > 0 && value != element.data( 'previous-value' ) ) {
                element.removeClass( 'invalid' );
                if( this.use_inline_marks ) {
                    if( this.marks[ name ] ) {
                        this.marks[ name ].hide();
                    }
                } else {
                    if( this.tooltip ) {
                        this.tooltip.hide();
                    }
                }
            }
        }
    },

    submit: function() {
        var self = this;
        try {
            self.clear();
            self.presubmit();
            if( self.submit_with_ajax ) {
                self.form.ajaxSubmit( {
                    cache: false,
                    data: self.extra_data,
                    dataType: 'json',
                    success: function( response ) {
                        if( response ) {
                            if( response.messages ) {
                                $.each( response.messages, function( i, item ) {
                                    self.flash( { 
                                        text: item.message,
                                        tags: item.tags,
                                        source: self.form
                                    } );
                                } );
                            }
                            if( response.fields ) {
                                self.clear();
                                self.update( response.fields, true );
                            }
                            if( response.response ) {
                                self.clear();
                                if( self.reset_on_success ) {
                                    self.form.resetForm();
                                }
                                if( self.reload_on_success ) {
                                    window.location = window.location;
                                } else if( self.onsubmit ) {
                                    self.onsubmit( self, response.response );
                                }
                            } else {
                                if( self.onfailure ) {
                                    self.onfailure( self, response.error );
                                }
                            }
                            if( !( response.response && ( self.reload_on_success || !self.unspin_on_success ) ) ) {
                                self.postsubmit();
                            }
                        } else {
                            tippr.flash( { source: self.form } );
                            if( self.onerror ) {
                                self.onerror( self );
                            }
                            self.postsubmit();
                        }
                    },
                    error: function() {
                        tippr.flash( { source: self.form } );
                        if( self.onerror ) {
                            self.onerror( self );
                        }
                        self.postsubmit();
                    }
                } );
            } else {
                return true;
            }
        } catch( exception ) {
            tippr.flash( { source: self.form } );
        }
        return false;
    },

    unmark: function( name ) {
        if( this.use_inline_marks ) {
            var para = this.marks[ name ];
            if( para ) {
                para.hide();
            }
        } else {
            this.tooltip.hide();
            this.tooltip = null;
        }
    },

    update: function( errors, validate ) {
        if( errors && errors.length > 0 ) {
            this.errors = this.errors.concat( errors );
            if( validate ) {
                this.validate();
            }
        }
    },

    validate: function() {
        var self = this;
        if( self.errors.length > 0 ) {
            $.each( self.errors, function( i, error ) {
                var element = self.form.find( '[name=' + error[ 0 ] + ']' );
                element.addClass( 'invalid' );
                element.data( 'previous-value', element.val() );
                if( self.use_inline_marks ) {
                    self.mark( error[ 0 ], error[ 1 ] );
                }
            } );
            var first = self.errors[ 0 ];
            if( !self.use_inline_marks ) {
                self.mark( first[ 0 ], first[ 1 ] );
            }
            if( self.focus_first_error ) {
                self.focus( self.form.find( '[name=' + first[ 0 ] + ']' ) );
            }
        }
    }
} );

// automatically append CSRF token from cookie into ajax request headers
// shamelessly copied from Django docs
$(document).ajaxSend(function(event, xhr, settings) {
    function sameOrigin(url) {
        // indicates if given url is the same origin as the current document
        // url could be relative or scheme relative or absolute
        var host = document.location.host; // host + port
        var protocol = document.location.protocol;
        var sr_origin = '//' + host;
        var origin = protocol + sr_origin;
        // Allow absolute or scheme relative URLs to same origin
        return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
            (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
            // or any other URL that isn't scheme relative or absolute i.e relative.
            !(/^(\/\/|http:|https:).*/.test(url));
    }
    function safeMethod(method) {
        // indicates if method may be run safely without side affects
        // (ie, no POST, PUT, DELETE, etc request types)
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    if (! safeMethod(settings.type) && sameOrigin(settings.url)) {
        xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
    }
});

$.each( [ 'create-account', 'signin' ], function( i, target ) {
    $.initialize( '#' + target + '-link', function() {
        this.click( function( event ) {
            tippr.require_ssl(
                function() {
                    tippr.load_modal( { show: true, url: '/r/modal/' + target + '/' } );
                },
                function() {
                    tippr.flag( 'request-modal', target );
                }
            );
            return false;
        } );
    } );
} );

function close_channel_selector( event ) {
    var target = $( event.target );
    if( target.parents( '#channel-selector' ).length === 0 ) {
        $( '#channel-selector' ).removeClass( 'enabled' );
        $( 'body' ).unbind( 'click', close_channel_selector );
    }
};

$.attempt( '#channel-selector', function() {
    var self = this;
    this.find( 'h2 a' ).click( function() {
        if( !self.is( '.enabled' ) ) {
            $( 'body' ).click( close_channel_selector );
            self.addClass( 'enabled' );
        } else {
            $( '#channel-selector' ).removeClass( 'enabled' );
            $( 'body' ).unbind( 'click', close_channel_selector );
        }
        return false;
    } );
} );

$.initialize( '.twitter-stream', function() {
    var self = this;
    tippr.query_twitter( 'tippr', '', function( data ) {
        self.find( '.twitter-status span' ).text( data[ 0 ].text );
    } );
} );

$.initialize( '.login-required', function() {
    this.each( function( i, element ) {
        element = $( element );
        element.click( function( event ) {
            tippr.load_modal( { show: true, url: '/r/modal/signin/', onload: function( modal ) {
                modal.find( 'input[name=redirect]' ).attr( 'value', element.attr( 'href' ) );
            } } );
            return false;
        } );
    } );
} );

$.initialize( '.toggle-block', function() {
    this.each( function( i, element ) {
        element = $( element );
        element.click( function( event ) {
            if( element.is( '.active' ) ) {
                element.removeClass( 'active' );
                element.find( '~ .togglable-block' ).addClass( 'hidden' );
            } else {
                element.addClass( 'active' );
                element.find( '~ .togglable-block' ).removeClass( 'hidden' );
            }
        } );
    } );
} );

$.initialize( '.custom-select-box', function() {
    this.each( function( i, element ) {
        var select = $( element ).find( 'select' ), span = $( '<span/>' );
        select.focus( function() {
            $( this ).parent().addClass( 'focused' );
        } );
        select.blur( function() {
            $( this ).parent().removeClass( 'focused' );
        } );
        select.bind( 'change keyup', function() {
            span.text( $( this ).find( 'option:selected' ).text() );
        } );
        select.before( span ).change();
    } );
} );

$.initialize( '.field-with-inline-label label', function() {
    this.each( function( i, element ) {
        $( element ).inFieldLabels();
    } );
} );

$( '.flash-message-close' ).live( 'click', function() {
    $( this ).parent().remove();
    return false;
} );

$( '.offer-snapshot' ).live( 'click', function() {
    window.location = $( this ).find( 'a' ).first().attr( 'href' );
} );

$( '.snapshot-content' ).live( 'click', function() {
    window.location = $( this ).find( 'a' ).first().attr( 'href' );
} );
$('.offer-exhibit').live('click', function() {
    window.location = $(this).find('a').first().attr('href');
});

$('a.change-language').live('click', function () {
    return tippr.change_language( $(this).attr('data-lang') );
});

$('select.change-language').live('change', function() {
    return tippr.change_language( this.value );
});


