(function($) {
    $.fn.prettyForm = function(settings) {
        var config = { 'imagePath': '/img/form', 'spriteImage': 'field-sprite.gif' };

        if (settings) $.extend(config, settings);

        // If this is the first time the plugin has been used on this page,
        // set up the global data container and preload all the images
        if (!window.$prettyForm_Data) {
            // This is a global to hold the current tab index
            // so that if we have multiple forms the order will follow across.
            window.$prettyForm_Data = { tabIndex: 1 };

            var img = [config['spriteImage'],
                        'select-arrow.gif',
                        'ta-bl.gif',
                        'ta-br.gif',
                        'ta-tl.gif',
                        'ta-tr.gif',
                        'ta-focus-bl.gif',
                        'ta-focus-br.gif',
                        'ta-focus-tl.gif',
                        'ta-focus-tr.gif'];

            for (var i = 0; i < img.length; i++) {
                var loader = new Image();
                loader.src = config['imagePath'] + '/' + img[i];
            }
        }

        // Internal shortcut to allow selection of elements with a 
        // period character in the ID
        function safeSelector(attr) {
            return attr.replace(/\./gi, '\\.');
        }

        // Internal shortcut to convert periods to underscores for MVC input names
        function mvcId(id) {
            return id.replace(/\./gi, '_');
        }

        // Call this helper function to recalculate vertical offsets after form
        // elements' vertical position changes (e.g. showing and hiding areas)
        $.extend({
            prettyFormDoLayout: function(effect) {
                $('.checkbox, .radio').each(function() {

                    var field = $(this);

                    var offset = field.find('label:first').position();
                    field.find('input').css({ top: offset.top });
                });

                $('.select').each(function() {

                    var field = $(this);

                    var select = field.find('.select-input');
                    var offset = select.position();
                    field.find('select:first').css({ top: offset.top });

                    field.find('.select-options').css({ top: (offset.top + select.outerHeight()),
                        left: (offset.left + 6), width: (select.outerWidth() - 44)
                    });
                });
            }
        });

        this.each(function() {
            var f = $(this);

            // We only add the class here, with script, so that script-disabled users will see the unstyled form
            f.addClass('customform');

            $('.info', f).wrapInner('<span />').hide();

            // Replace text fields      
            $('input[type=text]', f).each(function() {
                var field = $(this);
                field.replaceWith('<span class="input text">' +
                                  '    <span class="text-input">' +
                                  '        <span><span><input id="' + mvcId(field.attr('name')) + '" name="' + field.attr('name') + '" type="text" value="' + field.val() + '" /></span></span>' +
                                  '    </span>' +
                                  '</span>');
            });

            // Replace textarea fields      
            $('textarea', f).each(function() {
                var field = $(this);
                field.replaceWith('<span class="input textarea">' +
                                  '    <span class="textarea-input">' +
                                  '        <span><textarea wrap="virtual" id="' + mvcId(field.attr('name')) + '" name="' + field.attr('name') + '">' + field.val() + '</textarea></span>' +
                                  '        <span class="tr"></span><span class="bl"></span>' +
                                  '    </span>' +
                                  '</span>');
            });

            // Replace checkboxes
            $('input[type=checkbox]', f).each(function() {
                var field = $(this);
                field.replaceWith('<span class="input checkbox">' +
                                  '    <label class="option-check' + ((field.is(':checked')) ? ' selected' : '') + '"><span>Yes</span></label>' +
                                  '    <label class="option-uncheck' + ((!field.is(':checked')) ? ' selected' : '') + '"><span>No</span></label>' +
                                  '    <input type="checkbox" id="' + mvcId(field.attr('name')) + '" name="' + field.attr('name') + '" value="' + ((field.attr('value')) ? field.val() : 'true') + '" ' + ((field.is(':checked')) ? ' checked=\"checked\"' : '') + '/>' +
                                  '</span>');

                // Set the position of the original checkbox (roughly) so the screen doesn't scroll upward when it is focused
                var cb = $('#' + mvcId(field.attr('name')));
                var offset = cb.parent().find('label:first').offset();
                cb.css({ top: offset.top });
            });

            // Add handler for off-screen field so keyboard selection works
            // We can't chain this to the binding above because the checkboxes are actually replaced, not just wrapped
            $('input[type=checkbox]', f).bind('change', function(e) {
                var input = $(this);
                input.parent().find('label').removeClass('selected');

                if (input.is(':checked'))
                    input.parent().find('.option-check').addClass('selected');
                else
                    input.parent().find('.option-uncheck').addClass('selected');
            });

            // Replace radio buttons
            $('p:has(input[type=radio])', f).each(function() {
                var para = $(this);
                // The first label will be an overall label for the radio group, so append everything after it
                var label = para.find('label:first');
                var checked = para.find('input:checked');
                var labelText = label.text();
                label.remove();

                var html = para.html();
                para.html('<label>' + labelText + '</label><span class="input radio">' + html + '</span>');

                var labels = $('.radio label', para).wrapInner('<span></span>');
                labels.addClass('option-c');
                $(labels[0]).removeClass('option-c').addClass('option-l'); // First label in group
                $(labels[labels.length - 1]).removeClass('option-c').addClass('option-r'); // Last label in group

                // If one of the radio buttons is checked, select the correct label
                if (checked.length)
                    $('label[for=' + checked.attr('id') + ']').addClass('selected');

                // Set the position of the original inputs (roughly) so the screen doesn't scroll upward when they are focused
                var offset = $(labels[0]).position();
                $('input[type=radio]', para).css({ top: offset.top });
            });

            $('select', f).each(function() {

                var field = $(this);

                var oldOptions = field.html(); // Store the old options
                var selected = field.find('option:selected'); // Save the selected text/value of the select
                var fieldOptions = [], index = 0;

                // Create an anchor element for each option in the existing select
                field.find('option').each(function() {
                    var option = $(this);
                    // We did store the value data in 'title' attr but we don't want it showing on rollover
                    fieldOptions.push('<a href="#" value="' + option.val() + '"><span>' + option.text() + '</span></a>');
                });

                field.replaceWith('<span class="input select">' +
                                  '    <span class="select-input" id="select-' + mvcId(field.attr('name')) + '">' +
                              	  '        <span><span><span>' + selected.text() + '</span> <a href="#" class="button"></a></span></span>' +
                              	  '    </span>' +
                              	  '    <span class="select-options" id="select-' + mvcId(field.attr('name')) + '-options">' +
                              	           fieldOptions.join('') +
                              	  '    </span>' +
                                  '    <select id="' + mvcId(field.attr('name')) + '" name="' + field.attr('name') + '">' + oldOptions + '</select>' +
                                  '</span>');

                // Position the select options below the custom select
                var select = $('#select-' + mvcId(field.attr('name')));
                var offset = select.position();

                // console.log(offset);
                //console.log(select.position());

                // Set the position of the original select so the screen doesn't scroll upward when it is focused
                $('#' + mvcId(field.attr('name'))).css({ top: offset.top });

                $('#select-' + mvcId(field.attr('name')) + '-options').css({ top: (offset.top + select.outerHeight()),
                    left: (offset.left + 6)
                });
            });

            // Change the custom select when the off-screen original changes (keyboard support)
            // We can't chain this to the binding above because the selects are actually replaced, not just wrapped
            $('select', f).bind('keyup', function(e) {
                var input = $(this);
                $('#select-' + mvcId(input.attr('id')) + ' span span').html('<span>' + input.find(':selected').text() + '</span> <a href="#" class="button"></a>');
            });

            // Click handler for the drop-down menu arrow (show select options)
            $('.select').bind('click', function(e) {
                $(this).find('select').focus();

                var opts = $(this).find('.select-options');

                // Add a temporary attribute to the select we're using, so the next line doesn't hide it
                opts.attr('open_select', 1);

                $('.select .select-options[open_select!=1]').hide();

                // We have to do it this way instead of using toggle() because of (surprise) an IE bug
                if (opts.css('display') != 'block')
                    opts.css('display', 'block');
                else
                    opts.css('display', 'none');

                // Remove the identifier attribute
                opts.removeAttr('open_select');

                return false;
            });

            // Replace submit element with nice button
            $('input[type=submit]', f).each(function() {
                $(this).replaceWith('<span class="input submit">' +
                                    '    <a class="submit-input" ' + (($(this).attr('id')) ? 'id="' + $(this).attr('id') + '" ' : '') + 'href="#">' +
                                    '        <span><span>' + $(this).val() + '</span></span>' +
                                    '    </a>' +
                                    '    <span class="loader"><img src="/content/img/site/ajax-loader.gif" width="16" height="16" /></span>' +
                                    '    <input type="submit" value="Submit" />' +
                                    '</span>');
            });

            // For submit button click handler
            $('.submit-input').bind('click', function() {
                // $(this).next('.loader').show();
                $(this).closest('form').submit();
                return false;
            });

            // Click handler for checkboxes and radio buttons
            $(".option-check, .option-uncheck, .option-l, .option-c, .option-r").bind('click', function() {
                var label = $(this);
                var parent = label.parent();
                $('label', parent).removeClass('selected');
                label.addClass('selected');

                if ($('input[type=checkbox]', parent).length) // If the parent contains a checkbox
                    $('input[type=checkbox]', parent).attr('checked', (label.hasClass('option-check'))).focus().trigger('change'); // If the option-check label was clicked, check, otherwise uncheck
                else // This is a radio button - check the one which the clicked label is associated with
                    $('#' + label.attr('for'), parent).attr('checked', true);

                $('.input').removeClass('focused');
                $(this).closest('.input').addClass('focused');
            });

            // Fix for WebKit's crap handling of the change event
            $('input[type=radio]', f).bind('focus', function(e) {

                //log('radio focus checked: ' + this.checked + ', was_checked: ' + this.was_checked);

                // Horrible kludge because IE doesn't focus the selected radio button when you tab into the group
                if ($.browser.msie && !this.checked && !this.firstaccess) {
                    //log('initially focusing selected radio button');

                    $('input[name=' + safeSelector(this.name) + ']:checked').focus();

                    // reset all the was_checked properties
                    $("input[name=" + safeSelector(this.name) + "]").each(function() {
                        this.firstaccess = true;
                    });
                }

                // if this isn't checked then no option is yet selected. bail
                if (!this.checked) return;

                // if this wasn't already checked, manually fire a change event
                if (!this.was_checked) {
                    $(this).change();
                }
            });

            // Add handler for off-screen fields so keyboard selection works
            // We can't chain this to the binding above because the radio buttons are actually replaced, not just wrapped
            $('input[type=radio]', f).bind('change', function(e) {

                //log('radio change');

                // Shortcut if already checked to stop IE firing duplicate event
                if (this.was_checked) {
                    e.stopImmediatePropagation();
                    return;
                }

                // Reset all the was_checked properties
                $('input[name=' + safeSelector(this.name) + ']').each(function() {
                    this.was_checked = this.checked;
                });

                var input = $(this);
                $('label', input.parent()).removeClass('selected');
                $('label[for=' + input.attr('id') + ']').addClass('selected');
            });

            // Attach taborder to each input in proper order (can't do this when converting input HTML because we
            // do it by type and not necessarily by order of appearance within the form
            $('.input').each(function() {
                var input = $(this);

                switch (input.attr('class').split(' ')[1]) {
                    case 'text':
                        input.find('input[type=text]').attr('tabindex', ($prettyForm_Data.tabIndex++));
                        break;
                    case 'textarea':
                        input.find('textarea').attr('tabindex', ($prettyForm_Data.tabIndex++));
                        break;
                    case 'select':
                        input.find('select').attr('tabindex', ($prettyForm_Data.tabIndex++));
                        break;
                    case 'radio':
                        input.find('input[type=radio]').attr('tabindex', ($prettyForm_Data.tabIndex++));
                        break;
                    case 'checkbox':
                        input.find('input[type=checkbox]').attr('tabindex', ($prettyForm_Data.tabIndex++));
                        break;
                    case 'submit':
                        input.find('a').attr('tabindex', ($prettyForm_Data.tabIndex++));
                        break;
                    default:
                        break;
                }
            });

            // Cancel blur of parent select when an option is clicked
            $('.select a').bind('focus', function(e) {
                $(this).parent().parent().find('select').focus();
                e.preventDefault();
            });

            // Add focus and blur events to show focus rectangle
            $('.input input, .input select, .input textarea, .input a').not('.select-options a').bind('focus', function() {
                $('.input').removeClass('focused');
                var inputPara = $(this).closest('.input');
                inputPara.addClass('focused');

                // Hide all the info boxes
                $('.info').hide();

                // If there's an info box for this input, then position it and show it
                var info = inputPara.parent().find('.info');
                if (info.length) info.show().css({ top: $(this).position().top });
            });


            ///////////////////////////////////////////////////////////////////
            // Begin stuff for the custom list/selects
            ///////////////////////////////////////////////////////////////////

            $('.list-select', f).each(function() {

                var field = $(this);

                var oldOptions = field.html(); // Store the old options
                var selected = field.find('a:first'); // Save the selected text/value of the select
                var fieldOptions = [], index = 0;

                // Create an anchor element for each option in the existing select
                field.find('a').each(function() {
                    var option = $(this);
                    // We did store the value data in 'title' attr but we don't want it showing on rollover
                    fieldOptions.push('<a href="' + option.attr('href') + '"><span>' + option.text() + '</span></a>');
                });

                field.replaceWith('<span class="input list-select">' +
                                  '    <span class="select-input" id="select-' + mvcId(field.attr('id')) + '">' +
                              	  '        <span><span><span>Please select...</span> <a href="#" class="button"></a></span></span>' +
                              	  '    </span>' +
                              	  '    <span class="select-options" id="select-' + mvcId(field.attr('id')) + '-options">' +
                              	           fieldOptions.join('') +
                              	  '    </span>' +
                                  '</span>');

                // Position the select options below the custom select
                var select = $('#select-' + mvcId(field.attr('id')));
                var offset = select.position();

                $('#select-' + mvcId(field.attr('id')) + '-options').css({ top: (offset.top + select.outerHeight()),
                    left: (offset.left + 6)
                });
            });

            // Click handler for the drop-down menu arrow (show select options)
            $('.list-select').bind('click', function(e) {
                $(this).find('select').focus();

                var opts = $(this).find('.select-options');

                // Add a temporary attribute to the select we're using, so the next line doesn't hide it
                opts.attr('open_select', 1);

                $('.select .select-options[open_select!=1]').hide();

                // We have to do it this way instead of using toggle() because of (surprise) an IE bug
                if (opts.css('display') != 'block')
                    opts.css('display', 'block');
                else
                    opts.css('display', 'none');

                // Remove the identifier attribute
                opts.removeAttr('open_select');

                return false;
            });

            ///////////////////////////////////////////////////////////////////
            // End stuff for the custom list/selects
            ///////////////////////////////////////////////////////////////////

            // This handler detects a click anywhere in the document
            // If the click isn't on/in the currently open select, close all selects on the page
            // If user clicks another select, this will not fire but the click handler for that select will close all others
            $(document).bind('click', function(e) {

                if ($(e.target).is('.select, .select *, .list-select, .list-select')) return;
                $('.select .select-options, .list-select .select-options').removeAttr('open_select').hide();

            });

            $('.list-select .select-options a').bind('click', function() {
                window.location = $(this).attr('href');
            });

            // Bind the click event for select options
            $('.select .select-options a').bind('click', function() {
                var option = $(this);
                var selectName = option.parent().attr('id').split('-')[1];
                $('#select-' + mvcId(selectName) + ' span span').html('<span>' + option.text() + '</span> <a href="#" class="button"></a>');
                $('#' + mvcId(selectName)).val(option.attr('value'));
                $('#' + mvcId(selectName)).trigger('itemselected');
                option.parent().hide();
                return false;
            });

        });

        return this;
    };

})(jQuery);
