/**
 * wr_lib.js
 *
 * Various functions and objects for White Recruitment
 *
 * Author: Terry Morgan <terry.morgan@marmaladeontoast.co.uk>
 **/
var $wr = window.$wr || {};

/**
 * For some reason spin.js doesn't work when included in the plugin.js file. Teh Ghey.
 **/

/**
 * Spin.js
 * http://fgnass.github.com/spin.js/
 **/
(function(C,v,w){function D(a,c){for(var d=~~((a[h]-1)/2),b=1;b<=d;b++)c(a[b*2-1],a[b*2])}function k(a){var c=v.createElement(a||"div");D(arguments,function(a,b){c[a]=b});return c}function l(a,c,d){d&&!d[E]&&l(a,d);a.insertBefore(c,d||null);return a}function N(a,c){var d=[f,c,~~(a*100)].join("-"),b="{"+f+":"+a+"}",e;if(!I[d]){for(e=0;e<n[h];e++)try{F.insertRule("@"+(n[e]&&"-"+n[e].toLowerCase()+"-"||"")+"keyframes "+d+"{0%{"+f+":1}"+c+"%"+b+"to"+b+"}",F.cssRules[h])}catch(g){}I[d]=1}return d}function G(a,
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            c){var d=a[o],b,e;if(d[c]!==w)return c;c=c.charAt(0).toUpperCase()+c.slice(1);for(e=0;e<n[h];e++)if(b=n[e]+c,d[b]!==w)return b}function m(a){D(arguments,function(c,d){a[o][G(a,c)||c]=d});return a}function O(a){D(arguments,function(c,d){a[c]===w&&(a[c]=d)});return a}var j="width",h="length",s="radius",g="lines",x="color",f="opacity",o="style",H="height",y="left",z="top",p="px",E="parentNode",A="position",J="absolute",t="transform",K="Origin",L="coord",u=o+"Sheets",n="webkit0Moz0ms0O".split(0),I={},
    M;l(v.getElementsByTagName("head")[0],k(o));var F=v[u][v[u][h]-1],u=function(a){this.opts=O(a||{},g,12,"trail",100,h,7,j,5,s,10,x,"#000",f,0.25,"speed",1)},B=u.prototype={spin:function(a){var c=this,d=c.el=c[g](c.opts);a&&l(a,m(d,y,~~(a.offsetWidth/2)+p,z,~~(a.offsetHeight/2)+p),a.firstChild);if(!M){var b=c.opts,e=0,i=20/b.speed,j=(1-b[f])/(i*b.trail/100),h=i/b[g];(function P(){e++;for(var a=b[g];a;a--){var k=Math.max(1-(e+a*h)%i*j,b[f]);c[f](d,b[g]-a,k,b)}c.Timeout=c.el&&C.setTimeout(P,50)})()}return c},
    stop:function(){var a=this.el;C.clearTimeout(this.Timeout);a[E]&&a[E].removeChild(a);this.el=w;return this}};B[g]=function(a){function c(c,b){return m(k(),A,J,j,a[h]+a[j]+p,H,a[j]+p,"background",c,"boxShadow",b,t+K,y,t,"rotate("+~~(360/a[g]*e)+"deg) translate("+a[s]+p+",0)","borderRadius","100em")}for(var d=m(k(),A,"relative"),b=N(a[f],a.trail),e=0,i;e<a[g];e++)i=m(k(),A,J,z,1+~(a[j]/2)+p,t,"translate3d(0,0,0)","animation",b+" "+1/a.speed+"s linear infinite "+(1/a[g]/a.speed*e-1/a.speed)+"s"),a.shadow&&
    l(i,m(c("#000","0 0 4px #000"),z,2+p)),l(d,l(i,c(a[x],"0 0 1px rgba(0,0,0,.1)")));return d};B[f]=function(a,c,d){a.childNodes[c][o][f]=d};var r="group0roundrect0fill0stroke".split(0);(function(){var a=m(k(r[0]),"behavior","url(#default#VML)");if(!G(a,t)&&a.adj){for(a=0;a<r[h];a++)F.addRule(r[a],"behavior:url(#default#VML)");B[g]=function(){function a(){return m(k(r[0],L+"size",i+" "+i,L+K,-e+" "+-e),j,i,H,i)}function d(d,h,i){l(n,l(m(a(),"rotation",360/b[g]*d+"deg",y,~~h),l(m(k(r[1],"arcsize",1),
    j,e,H,b[j],y,b[s],z,-b[j]/2,"filter",i),k(r[2],x,b[x],f,b[f]),k(r[3],f,0))))}var b=this.opts,e=b[h]+b[j],i=2*e,n=a(),o=~(b[h]+b[s]+b[j])+p,q;if(b.shadow)for(q=1;q<=b[g];q++)d(q,-2,"progid:DXImage"+t+".Microsoft.Blur(pixel"+s+"=2,makeshadow=1,shadow"+f+"=.3)");for(q=1;q<=b[g];q++)d(q);return l(m(k(),"margin",o+" 0 0 "+o,A,"relative"),n)};B[f]=function(a,d,b,e){e=e.shadow&&e[g]||0;a.firstChild.childNodes[d+e].firstChild.firstChild[f]=b}}else M=G(a,"animation")})();C.Spinner=u})(window,document);


/**
 * $wr.EventManager
 *
 * Handles the publishing and subscribing to of custom events.
 **/
$wr.EventManager = {
    publish: function(e, data) {
        if (typeof data === 'undefined') {
            data = null;
        }
        $(this).trigger(e, data);
    },
    subscribe: function(e, callback) {
        if (typeof callback !== 'undefined') {
            $(this).bind(e, callback);
        } else {
            throw "Undefined callback function passed to EventManager.subscribe.";
        }
    },
    unsubscribe: function(e) {
        $(this).unbind(e);
    }
};

/**
 * $wr.HomeTextTweaking()
 *
 * Some cosmetic tweaking on the homepage
 **/
$wr.HomeTextTweaking = function() {
    var jobs_avail, new_el, jobs_avail_words;

    jobs_avail = $('#jobs-available');
    jobs_avail_words = jobs_avail.text().split(' ');

    jobs_avail.html('');

    $(jobs_avail_words).each(function(idx, word) {
        new_el = $('<span>').text(word);
        jobs_avail.append(new_el);
    });
};

;(function($) {

    $.collapsible = function(el, options) {

        var defaults = {
            hideLabel: 'Hide',
            showLabel: 'Show',
            toggleClass: 'collapsible-toggle',
            togglePosition: 'after'
        };

        var plugin = this;
        plugin.settings = {}

        var init = function() {
            plugin.settings = $.extend({}, defaults, options);
            plugin.el = el;

            plugin.inner = plugin.el.find('.collapsible-inner');
            grabHeight();
            build();
        };

        var build = function() {
            var toggleText, toggleClass;

            if (plugin.el.hasClass('closed')) {
                plugin.open = false;
                toggleText = 'Show';
                plugin.el.css('height', 0);
                toggleClass = plugin.settings.toggleClass+' closed';
            } else {
                plugin.open = true;
                toggleText = 'Hide';
                toggleClass = plugin.settings.toggleClass+' open';
            }

            plugin.toggle = $('<a>').addClass(toggleClass).text(toggleText);
            plugin.toggle.click(onClick);

            if (plugin.settings.togglePosition === 'before') {
                plugin.el.before(plugin.toggle);
            } else {
                plugin.el.after(plugin.toggle);
            }
        };

        var onClick = function(e) {
            if (plugin.open) {
                plugin.el.animate({ 'height': 0 });
                plugin.open = false;
                plugin.toggle.text(plugin.settings.showLabel);
                plugin.toggle.removeClass('open');
                plugin.toggle.addClass('closed');
            } else {
                plugin.el.animate({ 'height': plugin.openHeight+'px' });
                plugin.open = true;
                plugin.toggle.text(plugin.settings.hideLabel);
                plugin.toggle.removeClass('closed');
                plugin.toggle.addClass('open');
            }
        };

        var grabHeight = function() {
            if (plugin.el.css('display') === 'none') {
                plugin.el.css('display', 'block');
                plugin.openHeight = plugin.el.height();
                plugin.el.css('display', 'none');
            } else {
                plugin.openHeight = plugin.el.height();
            }
        };

        // call the "constructor" method
        init();
    }
})(jQuery);


/**
 * $wr.Slideshow
 *
 * Proper slideshow. Non of this fading malarky.
 *
 * TODO: convert to Klass or a jquery plugin
 **/
$wr.SlideShow = (function() {
    var autoScroller, controls, element, prev, next, index=0, slider, totalSlides, scroller, slideWidth, slides = [], lastSlide = null;

    function addListeners() {
        scroller.bind('transitionend webkitTransitionEnd OTransitionEnd', function(e){
            element.trigger('slide:loaded');
        });
    }

    function animate(reset) {
        var slideTo = -(index * slideWidth)+'px';

        if(reset) {
            scroller.fadeOut('fast', function(){
                scroller.css({ 'left':  slideTo});

                scroller.fadeIn('fast', function() {
                    element.trigger('slide:loaded');
                });
            });

        } else {
            scroller.animate({ 'left':  slideTo}, 'slow', 'swing', function() {
                element.trigger('slide:loaded');
            });
        }
    }

    function transition(dir) {
        var reset = false;

        // cache prev slide
        lastSlide = index;

        // determine next slide
        if (dir === 'back') {

            if (index) {
                index--;
            } else {
                index = totalSlides-1;
                reset = true;
            }

            //index = index ? index-1 : totalSlides-1;
        } else if (dir === 'forward') {

            if (index < (totalSlides-1)) {
                index++;
            } else {
                index = 0;
                reset = true;
            }

            //index = (index < (totalSlides-1)) ? index+1 : 0;
        } else if(typeof dir === 'integer') {
            index = dir;
        }

        // animate to next slide
        animate(reset);
    }

    function addControls() {
        var onClick = function(e) {
            // Stop the autoscroller
            window.clearInterval(autoScroller);

            if ($(e.target).hasClass('prev')) {
                transition('back');
            } else if ($(e.target).hasClass('next')) {
                transition('forward');
            }
        };

        controls = $('<div>').addClass('controls');
        element.append(controls);

        prev = $('<a>').addClass('control prev').text('prev').bind('click', onClick);
        next = $('<a>').addClass('control next').text('next').bind('click', onClick);
        controls.append(next, prev);
    }

    function build() {
        slideWidth = element.find('.slide').first().width();
        scroller = $('.scroller', element);
        $('.slide', scroller).each(function(idx, slide) {
            slides.push($(slide));
        });
        totalSlides = $('.slide', scroller).length;
        scroller.css('width', (totalSlides * slideWidth)+'px');
        addControls();
        addListeners();
    }

    return {
        init: function(el) {
            element = $('#'+el);
            build();

            autoScroller = window.setInterval(transition, 5000, 'forward');
        }
    };
})();

/**
 * $wr.SearchResultsList(el)
 *
 * Non AJAX search results list
 *
 * TODO: merge with AJAX search results list
 **/
$wr.SearchResultsList = function(el) {
    this.el = el;
    this.el.delegate('article', 'mouseleave', $.proxy(this.onMouseLeave, this))
        .delegate('article', 'mouseenter', $.proxy(this.onMouseEnter, this))
        .delegate('article', 'click', this.onClick);
    $('.view', this.el).css({
        opacity: 0,
        visibility: 'visible'
    });

    if ( ! Modernizr.opacity) {
        $('article', this.el).append($("<div>").addClass('mask').hide());
    }
};

$wr.SearchResultsList.prototype = {
    onMouseEnter: function(e) {
        var $article = $(e.currentTarget);

        $('article.active', this.el).removeClass('active');
        $article.addClass('active');

        if (Modernizr.opacity) {
            $('article:not(.active)', this.el).animate({ 'opacity':  0.2 }, {
                duration: 'fast',
                queue: false
            });
            $('.view', $article).animate({
                opacity: 1
            }, 'fast').css('zIndex', '300');
        } else {
            $('article:not(.active) .mask', this.el).show();
            $('.view', $article).show();
        }

    },
    onMouseLeave: function(e) {
        if (Modernizr.opacity) {
            $('article:not(.active)', this.el).animate({ 'opacity':  1 }, {
                duration: 'fast',
                queue: false
            });
            $('article.active .view', this.el).animate({
                opacity: 0
            }, 'fast');
        } else {
            $('article:not(.active) .mask', this.el).hide();
            $('article.active .view', this.el).hide();
        }

    },
    onClick: function(e) {
        e.preventDefault();
        window.location = $('a', this).attr('href');
    }
};

/**
 * Turns locations dropdown into a sexy times autocomplete field.
 *
 * @param Object options
 **/
(function($){
    $.fn.locationsAutocomplete = function(options) {
        // build main options before element iteration
        var opts = $.extend({}, $.fn.locationsAutocomplete.defaults, options);

        return this.each(function(){
            var $dropdown           = $(this),
                locations_parent    = $dropdown.parent(),
                location_input      = $('<input>').attr({ type: 'text', placeholder: opts.placeholder }),
                location_hidden     = $('<input>').attr({ type: 'hidden', id: 'locations', name: 'job_location_id' }),
                current_location_id = $dropdown.val(),
                current_location    = $('option[value='+current_location_id+']', $dropdown).text(),
                clear_button        = $('<a>').addClass('clear').text('clear').hide();

            function onClearClick() {
                location_hidden.val('');
                location_input.val('');
                clear_button.fadeOut();
            }

            location_input.focus(function() {
                $(this).val('');
            });

            $dropdown.remove();
            locations_parent.append(location_input);
            locations_parent.append(location_hidden);

            if (current_location && current_location != 'Any') {
                location_input.val(current_location);
                location_hidden.val(current_location_id);
                input_placeholder = current_location;
                clear_button.show();
            }

            if ($wr.oldIe) {
                location_input.val(opts.placeholder);
                location_input.blur(function(e) {
                    var $this = $(this);
                    if ($this.val() === '') {
                        $this.val(opts.placeholder);
                    }
                });
            }

            $.widget("custom.catcomplete", $.ui.autocomplete, {
                _renderMenu: function( ul, items ) {
                    var self = this, currentCategory = "";
                    $.each( items, function( index, item ) {
                        if ( item.category != currentCategory ) {
                            ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
                            currentCategory = item.category;
                        }
                        self._renderItem( ul, item );
                    });
                }
            });

            $(location_input).catcomplete({
                delay: 300,
                minLength: 2,
                focus: function(event, ui) {
                    location_input.val(ui.item.category+' > '+ui.item.label);
                    return false;
                },
                select: function(event, ui) {
                    location_input.val(ui.item.category+' > '+ui.item.label);
                    location_hidden.val(ui.item.id);
                    clear_button.fadeIn();
                    return false;
                },
                source: function(request, response) {
                    var filteredArray = $.map(job_locations, function(item) {
                        if( _.startsWith(item.label.toUpperCase(), request.term.toUpperCase())) {
                            return item;
                        } else {
                            return null;
                        }
                    });
                    response(filteredArray);
                }
            }).data("catcomplete")._resizeMenu = function () {
                var ul = this.menu.element;
                ul.outerWidth(this.element.outerWidth());
            };

            // Add clear button
            location_input.after(clear_button);
            clear_button.click(onClearClick);
        });
    };

    $.fn.locationsAutocomplete.defaults = {
        placeholder: 'Type a job location'
    };
}(jQuery));


//$wr.LocationsAutocomplete = function(locations_dropdown) {
//    var input_placeholder   = 'Type a job location';
//
//    var locations_parent    = locations_dropdown.parent();
//    var location_input      = $('<input>').attr({ type: 'text', placeholder: input_placeholder });
//    var location_hidden     = $('<input>').attr({ type: 'hidden', id: 'locations', name: 'job_location_id' });
//    var current_location_id = locations_dropdown.val();
//    var current_location    = $('option[value='+current_location_id+']', locations_dropdown).text();
//
//    location_input.focus(function() {
//        $(this).val('');
//    });
//
//    locations_dropdown.remove();
//    locations_parent.append(location_input);
//    locations_parent.append(location_hidden);
//
//    if (current_location && current_location != 'Any') {
//        location_input.val(current_location);
//        location_hidden.val(current_location_id);
//        input_placeholder = current_location;
//    }
//
//    if ($wr.oldIe) {
//        location_input.val(input_placeholder);
//        location_input.blur(function(e) {
//            var $this = $(this);
//            if ($this.val() === '') {
//                $this.val(input_placeholder);
//            }
//        });
//    }
//
//    $.widget("custom.catcomplete", $.ui.autocomplete, {
//        _renderMenu: function( ul, items ) {
//            var self = this, currentCategory = "";
//            $.each( items, function( index, item ) {
//                if ( item.category != currentCategory ) {
//                    ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
//                    currentCategory = item.category;
//                }
//                self._renderItem( ul, item );
//            });
//        }
//    });
//
//    $(location_input).catcomplete({
//        delay: 300,
//        minLength: 2,
//        focus: function(event, ui) {
//            location_input.val(ui.item.category+' > '+ui.item.label);
//            return false;
//        },
//        select: function(event, ui) {
//            location_input.val(ui.item.category+' > '+ui.item.label);
//            location_hidden.val(ui.item.id);
//            return false;
//        },
//        source: function(request, response) {
//            var filteredArray = $.map(job_locations, function(item) {
//                if( _.startsWith(item.label.toUpperCase(), request.term.toUpperCase())) {
//                    return item;
//                } else {
//                    return null;
//                }
//            });
//            response(filteredArray);
//        }
//    })
//    .data("catcomplete")._resizeMenu = function () {
//        var ul = this.menu.element;
//        ul.outerWidth(this.element.outerWidth());
//    };
//};



/**
 * $wr.ContactPanel()
 *
 * The mahoosive great sliding contact us panel.
 *
 * TODO: convert to Klass or a jquery plugin
 **/
$wr.ContactPanel = (function() {
    var contact_panel, contact_us_link, close_button, contact_form_container, main_content;

    function addListeners (argument) {
        contact_us_link.click(showPanel);
        close_button.click(hidePanel);
        contact_panel.submit(onSubmit);
    }

    function positionPanel() {
        contact_panel.css({
            display: 'block',
            top: '-848px'
        });
    }

    function showPanel(e) {
        e.preventDefault();

        main_content.animate({
            opacity: 0.3
        }, 800);

        contact_panel.animate({
            top: 0
        }, 800, 'easeOutCirc', function() {
            close_button.animate({
                    bottom: '-35px',
                    opacity: 1
                }, 300, 'easeOutCirc'
            );
        });
    }

    function hidePanel(e) {
        e.preventDefault();
        close_button.animate({
                bottom: 0,
                opacity: 0
            }, 300, 'easeInCirc',
            function() {
                contact_panel.animate({
                        top: '-848px'
                    }, 500, 'easeInCirc',
                    function() {
                        main_content.animate({
                            opacity: 1
                        }, 500, function() {
                            main_content.css('filter', 'none');
                        });
                    });
            }
        );
    }

    function onSubmit(e) {
        e.preventDefault();

        var form = contact_form_container.find('form');

        form.ajaxStart(function() {
            contact_form_container.animate({'opacity': 0.1}, 'fast');
        });

        $.post('/contacts/send', $(form).serialize(), function(data) {
            // Track the page view
            if (data.status) {
                _gaq.push(['_trackPageview', '/contactnav/success']);
            }
            contact_form_container.html(data.response);
            contact_form_container.animate({'opacity': 1}, 'fast');
        })
        .error(function(e, jqxhr, settings, exception) {
            contact_form_container.animate({'opacity': 1}, 'fast');
        });
    }

    return {
        init: function() {
            main_content    = $('#container');
            contact_panel   = $('#contact-panel');
            close_button    = contact_panel.find('a.close');
            close_button.css('opacity', 0);
            contact_us_link = $('nav.primary a[href*=contact_us]');
            contact_form_container = $('#contact-form-container');

            addListeners();
            positionPanel();
        }
    };
})();

/**
 * $wr.ValidateLocation()
 *
 * Validate the location field.
 *
 * TODO: merge into LocationsAutocomplete.
 **/
$wr.ValidateLocation = function() {
    var locations = $('#locations');

    if (locations.val() === '') {
        var parent = locations.parent();
        var error = $('<div></div>').addClass('msg error').css('opacity', 0).text('Please select a location');
        parent.append(error);
        error.animate({opacity: 1}, 'fast');
        return false;
    } else {
        return true;
    }
};

/**
 * $wr.PageComponent(el[, config])
 *
 **/
$wr.PageComponent = klass({
    initialize: function(el, config) {
        this.config = {};
        this.defaultConfig = {};
        if (typeof el !== 'undefined' || !el) {
            this.el = $(el);
        } else {
            this.el = false;
        }
        this.loadConfig(config);
        this.evm = $wr.EventManager;
    },
    loadConfig: function(config) {
        this.config = _.extend(this.defaultConfig , config || {});
    }
});

/**
 * $wr.Form(el[, config])
 *
 **/
$wr.Form = $wr.PageComponent.extend({
    initSelects: function() {
        $("select.replace", this.el).selectBox();
    },
    tearDownSelects: function() {
        $("select.replace", this.el).selectBox('destroy');
    }
});

/**
 * $wr.AjaxForm(el[, config])
 *
 **/
$wr.AjaxForm = $wr.Form.extend({
    init: function(el,config) {
        this.el = $(el);
        var form = $('form:first', this.el);
        form.live('submit', _.bind(this.onSubmit, this));
        this.setUpdateUrl(form.attr('action'));
    },
    onSubmit: function(e) {
        e.preventDefault();
        if (this.validate()) {
            this.ajax();
        }
    },
    setUpdateUrl: function(url) {
        this.updateUrl = url;
    },
    validate: function() {
        return true;
    },
    update: function(content) {
        if (typeof content !== 'undefined' || content !== '') {
            this.el.html(content);
        }
    },
    onAjaxStart: function() {},
    onAjaxError: function(e, jqxhr, settings, exception) {
        log(jqxhr);
    },
    ajax: function() {
        this.onAjaxStart();
        $.post(this.updateUrl, $('form:first', this.el).serialize(), _.bind(function(data) {
            this.onPostSuccess(data);
        }, this)).error(this.onAjaxError);
    },
    onPostSuccess: function(data) {}
});

/**
 * $wr.RefineSearch(el[, config])
 *
 * Functionality for the non AJAX refine search form
 **/
$wr.RefineSearch = $wr.Form.extend({
    initialize: function(el, config) {
        this.supr(el, config);
        $('#locations', this.el).locationsAutocomplete();
        this.initSelects();
    },
    onSubmit: function(e) {
        this.supr(e);
    }
});

/**
 * $wr.AjaxRefineSearch(el[, config])
 *
 * Functionality for the AJAX refine search form
 **/
$wr.AjaxRefineSearch = $wr.AjaxForm.extend({
    init: function(el, config) {
        this.supr(el, config);
        $('#locations', this.el).locationsAutocomplete();
        this.initSelects();
        $('#floated-panel').portamento({
            wrapper: $('.search')
        });
    },
    onSubmit: function(e) {
        this.supr(e);
    },
    onAjaxStart: function(e) {
        this.evm.publish('resultsrefresh:start');
    },
    onPostSuccess: function(data) {
        if (data.total_results > 0) {
            this.evm.publish('searchresults:update', [data]);
        } else {
            this.evm.publish('search:noresults');
        }
    },
    getCriteria: function() {
        return $('form', this.el).serializeArray();
    }
});

/**
 * $wr.JobAlert(el[, config])
 *
 * Functionality for the job alert signup form
 **/
$wr.JobAlert = $wr.AjaxForm.extend({
    init: function(el, config){
        this.supr(el, config);
        this.inner = $('<div>').addClass('job-alert-inner').html(this.el.html());
        this.el.html(this.inner);
        this.mask = $('<div>').addClass('mask').hide();
        if (Modernizr.opacity) {
            this.mask.css('opacity', 0.8).hide();
        } else {
            this.mask.css('backgroundColor', '#fff').hide();
        }

        this.el.append(this.mask);
        this.loading = new $wr.Spinner(this.el);
        this.infoToggle = $('.header a', this.el);
        this.infoToggle.live('click', _.bind(this.onInfoClick, this));

        this.info = $('#alert-info');
    },
    onSubmit: function(e) {
        this.supr(e);
        this.mask.show();
        this.loading.show();
    },
    onPostSuccess: function(response) {
        this.mask.hide().css('filter', 'none');
        this.loading.hide();

        if (response.alert_saved) {
            this.evm.publish('jobalert:saved', response.notification);
            $('div.error', this.el).hide();
        } else {
            this.inner.html(response.content);
        }
    },
    onInfoClick: function(e) {
        if (this.info.hasClass('visible')) {
            this.info.slideUp(500, 'easeInOutQuad', _.bind(function() {
                this.info.removeClass('visible');
            }, this));
        } else {
            this.info.slideDown(500, 'easeInOutQuad', _.bind(function() {
                this.info.addClass('visible');
            }, this));
        }
    },
    updateCriteria: function(criteria) {
        var alert_form = $('form', this.el);

        _.each(criteria, function(field) {
            if(field.name !== '_form_type') {
                $('input[name='+field.name+']', alert_form).val(field.value);
            }
        });
    }
});

/**
 * $wr.Spinner(el[, config])
 *
 * Wrapper class for spin.js
 **/
$wr.Spinner = $wr.PageComponent.extend({
    initialize: function(el, config) {
        this.defaultConfig = {
            lines: 12, // The number of lines to draw
            length: 7, // The length of each line
            width: 3, // The line thickness
            radius: 10, // The radius of the inner circle
            color: '#999', // #rbg or #rrggbb
            speed: 1, // Rounds per second
            trail: 100, // Afterglow percentage
            shadow: false, // Whether to render a shadow
            beforeShow: false,
            afterShow: false,
            beforeHide: false,
            afterHide: false
        };

        this.supr(null, config);

        var spin = new Spinner(this.config).spin();
        this.spinner = $(spin.el);
        this.spinner.addClass('spinner');
        this.spinner.hide();

        if (typeof el !== 'undefined' || ! el) {
            this.attach(el);
        }
    },
    attach: function(el) {
        $(el).append(this.spinner);
    },
    show: function() {
        if(this.config.beforeShow) {
            this.config.beforeShow();
        }

        this.spinner.css('display', 'block');

        if (Modernizr.opacity) {
            this.spinner.animate({
                opacity: 1
            }, 200, _.bind(function() {
                if(this.config.afterShow) {
                    this.config.afterShow();
                }
            }, this));
        } else {
            this.spinner.show();
            if(this.config.afterShow) {
                this.config.afterShow();
            }
        }

    },
    hide: function() {
        if(this.config.beforeHide) {
            this.config.beforeHide();
        }

        if (Modernizr.opacity) {
            this.spinner.animate({
                opacity: 0
            }, 200, _.bind(function() {
                if(this.config.afterShow) {
                    this.config.afterShow();
                }
            }, this));
        } else {
            this.spinner.hide();
            if(this.config.afterHide) {
                this.config.afterHide();
            }
        }
    }
});

/**
 * $wr.Overlay(el[, config])
 *
 * Wrapper class for jQuery tools Overlay
 **/
$wr.Overlay = $wr.PageComponent.extend({
    initialize: function() {
        this.defaultConfig = {
            mask: {
                color: '#fff',
                loadSpeed: 200,
                opacity: 0.9
            },
            closeOnClick: true,
            load: false,
            overlayContentClass: 'overlay-content',
            validTypes: ['no-results', 'job-alert-saved', 'job-apply-success']
        };
        this.loadConfig();
        this.build();
    },
    build: function() {
        this.overlayContent = $('<div>').addClass(this.config.overlayContentClass).hide();
        this.overlayContentInner = $('<div>').addClass(this.config.overlayContentClass+'-inner');
        this.overlayContent.append(this.overlayContentInner);
        $('body').append(this.overlayContent);
        $(this.overlayContent).overlay({
            mask: {
                color: this.config.mask.color,
                loadSpeed: this.config.mask.loadSpeed,
                opacity: this.config.mask.opacity
            },
            closeOnClick: this.config.closeOnClick,
            load: this.config.load
        });
        this.overlay = $(this.overlayContent).data("overlay");
        this.overlayContent.bind('click', _.bind(this.close, this))
    },
    update: function(content) {
        this.overlayContentInner.html(content);
    },
    removeClasses: function() {
        var classes = this.overlayContent.attr('class').split(' ');
        _.each(_.intersection(classes, this.validTypes), function(idx, _class) {
            this.overlayContent.removeClass(_class);
        });
    },
    setType: function(type) {
        this.removeClasses();
        this.overlayContent.addClass(type);
    },
    load: function() {
        this.overlay.load();
    },
    close: function() {
        this.overlay.close();
    }
});

/**
 * $wr.JobSearch()
 *
 * Non AJAX job search search logic
 **/
$wr.JobSearch = function() {
    var evm = $wr.EventManager,
        overlay = new $wr.Overlay(),
        onJobAlertSaved = function(e, data) {
            overlay.setType('job-alert-saved');
            overlay.update(data);
            overlay.load();
        };

    if ($('#main-search').length) {
        $('#main-search #locations', this.el).locationsAutocomplete();
        $("#main-search select.replace").selectBox();
    }

    new $wr.SearchResultsList($('.search .results'));

    if ($('.refine-search').length) {
        var refineSearch = new $wr.RefineSearch('.refine-search');
    }

    if ($('.job-alert').length) {
        var jobAlert = new $wr.JobAlert();
        jobAlert.init('#job-alert');
    }

    evm.subscribe('jobalert:saved', onJobAlertSaved);
}

/**
 * $wr.HomepageSearch()
 *
 * Homepage AJAX search logic
 **/
$wr.HomepageSearch = {

    init: function() {
        // Set up all the various components
        this.mainSearch    = new this.MainSearch();
        this.resultsPanel  = new this.ResultsPanel(null, { insertAfter: '#intro' });
        this.refineSearch  = new $wr.AjaxRefineSearch();
        this.jobAlert      = new $wr.JobAlert();
        this.jobCount      = new this.JobCount('#job-count');
        this.overlay       = new $wr.Overlay();
        this.evm           = $wr.EventManager;

        // Register event handlers
        this.evm.subscribe('jobalert:saved', _.bind(this.onJobAlertSaved, this));
        this.evm.subscribe('search:noresults', _.bind(this.onNoResults, this));
        this.evm.subscribe('mainsearch:loaded', _.bind(this.onSearchPanelLoad, this));
        this.evm.subscribe('searchresults:update', _.bind(this.onResultsUpdate, this));

        // Initialise the main search box and thus kick off this crazy thang
        this.mainSearch.init('#main-search');
    },

    /**
     * $wr.HomepageSearch.onSearchPanelLoad(e[,data])
     *
     * Handler for mainsearch:loaded event
     **/
    onSearchPanelLoad: function(e, data) {
        if (data.total_results > 0) {
            var callback = function() {
                this.refineSearch.init('.search .refine-search');
                this.jobAlert.init('.search .job-alert');

                var fn = _.bind(function() {
                    var scroll = function() {
                        $.scrollTo('#homepage-search-results-container', {
                            duration: 300,
                            easing: 'easeInOutQuad'
                        });
                    };
                    this.mainSearch.hide(scroll);
                    this.evm.unsubscribe('jobcount:updated');
                }, this);
                this.evm.subscribe('jobcount:updated', fn);
                this.jobCount.update(data.total_results);
            };
            this.resultsPanel.update(data, _.bind(callback, this));
        } else {
            this.evm.publish('search:noresults');
        }
    },

    /**
     * $wr.HomepageSearch.onResultsUpdate(e[,data])
     *
     * Handler for searchresults:update event
     **/
    onResultsUpdate: function(e, data) {
        this.resultsPanel.updateResults(data);
        this.jobAlert.updateCriteria(this.refineSearch.getCriteria());
        this.jobCount.update(data.total_results);
    },

    /**
     * $wr.HomepageSearch.onJobAlertSaved(e[,data])
     *
     * Handler for jobalert:saved event
     **/
    onJobAlertSaved: function(e, data) {
        this.overlay.setType('job-alert-saved');
        this.overlay.update(data);
        this.overlay.load();
    },

    /**
     * $wr.HomepageSearch.onNoResults(e[,data])
     *
     * Handler for search:noresults event
     **/
    onNoResults: function(e, data) {
        this.overlay.setType('no-results');
        this.overlay.update('<h1>We currently have 0 results that match your search</h1><h2>Please refine your search filter</h2>');
        this.overlay.load();
    },

    /**
     * $wr.HomepageSearch.JobCount(el[,config])
     *
     * The big number at the top of the home page, representing either the
     * total number of available jobs of the number of jobs returned to the
     * AJAX search.
     **/
    JobCount: $wr.PageComponent.extend({
        update: function(value) {
            var fn = function() {
                this.el.text(_.lpad(value.toString(), 4, '0'));
                this.el.animate({
                    opacity: 1
                }, 1000, _.bind(function() {
                    this.evm.publish('jobcount:updated');
                }, this));
            };

            this.el.animate({
                opacity: 0
            }, 300, _.bind(fn, this));
        }
    }),

    /**
     * $wr.HomepageSearch.MainSearch(el[,config])
     *
     * Functionality for the main search form at the top of homepage
     **/
    MainSearch: $wr.AjaxForm.extend({
        init: function(el, config) {
            this.supr(el, config);

            this.loading = $('<div>').addClass('loading').text('loading').css('opacity', 0);
            this.el.append(this.loading);
            this.el.ajaxStart(_.bind(function() {
                this.loading.animate({
                    opacity: 1
                }, 500);
            }, this)).ajaxComplete(_.bind(function() {
                this.loading.animate({
                    opacity: 0
                }, 500);
            }, this));

            $('#locations', this.el).locationsAutocomplete();
            this.initSelects();
        },
        hide: function(callback) {
            this.el.effect('blind', null, 300, callback);
        },
        onKeywordsFocus: function(e) {

        },
        onKeywordsBlur: function(e) {

        },
        onPostSuccess: function(data) {
            this.evm.publish('mainsearch:loaded', data);
        }}),

    /**
     * $wr.HomepageSearch.ResultsList(el[,config])
     *
     * Functionality for the AJAX search results list
     **/
    ResultsList: $wr.PageComponent.extend({
        initialize: function(el, config) {
            this.supr(el, config);
            this.evm.subscribe('resultsrefresh:start', _.bind(this.onRefreshStart, this));
            this.evm.subscribe('search:noresults', _.bind(this.onNoResults, this));
            $('nav.pagination a', this.el).live('click', _.bind(this.onClick, this));
        },
        onClick: function(e) {
            e.preventDefault();
            this.evm.publish('resultsrefresh:start');
            $.post($(e.target).attr('href'), null, _.bind(function(data) {
                this.update(data);
            }, this)).error(this.onAjaxError);
        },
        onNoResults: function() {
            this.loading.animate({ opacity: 0 }, 200, _.bind(function() {
                this.loading.hide();
                this.el.animate({
                    opacity: 1
                }, 300);
            }, this));
        },
        onRefreshStart: function() {
            this.loading = $('<div>').addClass('loading').text('Loading...').hide();
            this.el.before(this.loading);

            this.el.animate({ opacity: 0 }, 300, _.bind(function() {
                $(this.loading).css({
                    display: 'none',
                    opacity: 0
                }).show().animate({
                        opacity: 1
                    }, 200);
            }, this));
            this.evm.publish('resultslist:hidden');
        },
        onContentUpdate: function(argument) {
            this.el.animate({ opacity: 1 }, 300);
            $.localScroll({
                duration: 500,
                easing: 'easeInOutQuad'
            });
            this.evm.publish('resultslist:visible');
            new $wr.SearchResultsList(this.el);
        },
        update: function(response) {
            this.loading.animate({ opacity: 0 }, 200, _.bind(function() {
                this.loading.remove();
                this.el.html(response.content);
                this.onContentUpdate();
            }, this));
        }
    }),

    /**
     * $wr.HomepageSearch.ResultsList(el[,config])
     *
     * Functionality for the AJAX search results panel (contains results list, refine search and job alert)
     **/
    ResultsPanel: $wr.PageComponent.extend({
        initialize: function(el, config) {
            this.supr(el, config);
            this.el = $('<section>').attr('id', 'homepage-search-results-container').css({ height: 0 });
            this.results_container_inner = $('<div>').addClass('inner');
            this.el.append(this.results_container_inner);
            $(this.config.insertAfter).after(this.el);
        },
        setUpdateUrl: function(url) {
            this.updateUrl = url;
        },
        updateResults:function(content) {
            this.resultsList.update(content);
        },
        update: function(response, onLoad) {
            if (typeof response !== 'undefined' || response !== '') {
                var onComplete = _.bind(function() {
                    this.results_container_inner.html(response.content);
                    this.resultsList = new $wr.HomepageSearch.ResultsList('.results');

                    // If a valid callback has been passed, execute it
                    if(typeof onLoad !== 'undefined') {
                        onLoad();
                    }

                    // Resize and fade in the container
                    this.el.animate({
                        height: this.results_container_inner.height()+'px',
                        opacity: 1
                    }, 500, 'easeInOutQuad');

                    $.localScroll({
                        duration: 500,
                        easing: 'easeInOutQuad'
                    });
                    new $wr.SearchResultsList(this.el);
                }, this);

                this.el.animate({ opacity: 0 }, 500, onComplete);
            }
        }
    })
};
