self.carscom = self.carscom || {};

/*
Used to validate ZIP codes and create an in-page popup (HTML must be separately provided)
that will display on validation errors, and that can also be invoked as a standalone ZIP entry
from a simple link.

Call the ZipCapture function (via document.ready()) on the HTML component that will serve
as the ZIP capture dialog for the page, e.g.:  $('#zipDialog').ZipCapture().  The function
will locate any form on the page with an input named "zc" (the Cars magic zipcode paramter)
and attach to any element on those forms marked with a special "submitter" class.  The function
will also locate any element on the page marked with a special "trigger" class and intercept
clicks on them to pop up the ZIP dialog.  (Trigger class elements will NOT show the ZIP dialog
if they have an HREF that already shows a valid ZIP param [named, of course, "zc"].  This allows 
server-side code to attempt to populate a URL with a possibly cookied ZIP without have to do 
extra checking.)

Zip capture will use the ALT text of the invoking link, if it has been provided, to customize 
the message shown in the ZIP dialog.  It will use the HREF of the invoking link to change the 
window location when a ZIP code has passed validation. (Write the HREF with a dummy "zc" param
in the query string and the param will be filled in with the validated ZIP.)  Alternatively, 
links can be provided with a callback function that ZipCapture will invoke when a ZIP code has 
passed validation.  Register the callback on a given jQuery-wrapped link (or set of links) with 
the exported registerOnSuccess function.

Any element, not merely a link, can be marked with the "submit" and "trigger" classes.  The default
validation-success function looks for an HREF attribute on the invoking element; if HREF is 
undefined, the current page will be reloaded, with the expected "zc" query param modified to use
the validated ZIP.  Otherwise, a non-link element that invokes zip capture will have to be provided
its own callback function in order to change the page.

Custom parameters can be passed to the ZipCapture function (in a params object):
@param  submitterClass  CSS class name for links (within ZIP entry forms) that will submit ZIP input 
                        to the zip capture validator.  The submitter link within the ZIP dialog itself
                        must also be marked with this class.  Default is "zc-submit".  
@param  triggerClass    CSS class name for simple links that will invoke the ZIP entry dialog.  Default is
                        "zc-trigger".
@param  closeClass      CSS class name for links that will close the ZIP dialog.  Default is "zc-close".

Exported functions in carscom.zipCapture:
@function show              Shows the ZIP dialog, with an optional message
@function close             Closes the ZIP dialog
@function validate          Validates a ZIP code (pass the ZIP to the function as a simple string)
@function registerOnSuccess Associates one or more form links (as a jQuery-wrapped object) with a callback function,
                            to be invoked when a ZIP entry passes validation.
*/
(function($) {
    //Param params:  Allows caller to pass through jqModal parameters (only partially implemented--see init function)
    $.fn.ZipCapture = function(params) {
        var zipper = new $.zipper(this, params);
        carscom.zipCapture = {
            //showDialog and closeDialog intended for debugging only
            /*
            showDialog: function(message) { zipper.show(message) },
            closeDialog: function() { zipper.close() },*/
            validate: function(data) { zipper.validate(data) },
            registerOnSuccess: function($target, callback) { zipper.registerOnSuccess($target, callback) }
        }
        return this;
    }
    
    $.zipper = function($dialog, params) {
        //All recognized (non-jqModal) params should be initialized in this area, for easy reference
        this.params = params || {};
        this.params.userRadius = this.params.userRadius || 30;
        
        this.$dialog = $.extend(true, $dialog, {
            actualForm: $dialog.is('form')? $dialog : $dialog.find('form:has(input[name = "zc"])'),
            zipInput: $('input[name = "zc"]', $dialog),
            title: $('h2', $dialog),
            msgLabel: $('.message .alt-replace', $dialog),
            errLabel: $('.error', $dialog),
            submitter: $('.'+(this.params.submitterClass || 'zc-submit'), $dialog),
            citySelect: $('.message select', $dialog),
            defaultMessage: $('.message .alt-replace', $dialog).html() //Save this in case we need to replace it later
        });
        this.init();
    }
    
    $.zipper.prototype = {
        //Param message:  may be string (for input label) or object (with "label" and "error" strings)
        show: function(message, eventData) {
            if (typeof message == 'string') {
                this.$dialog.msgLabel.html(message);
                this.$dialog.errLabel.html('');
            }
            else {
                message = message || {};
                this.$dialog.msgLabel.html(message.label || this.$dialog.defaultMessage);
                this.$dialog.errLabel.html(message.error || '');
            }
            //Dialog title is not customized, but switched on whether or not dialog was initially invoked from a bad ZIP submission
            if (eventData && !eventData.isSubmitEvent) this.$dialog.title.html( this.messages.DEFAULT_TITLE );
            this.$dialog.citySelect.val('');
            this.$dialog.is(':visible') || this.$dialog.jqmShow();
        },
        close: function() {
            this.$dialog.jqmHide();
            this.$dialog.errLabel.html('');
        },
        /*Param eventData:  may be string (to pass ZIP directly for validation) or event object
                The eventData param MUST be an event object if validate() has been called from an outside
                page component (i.e., a form).  The string flavor of the param is meant to be used by
                client code, to pass through a ZIP for simple validation.
        */
        validate: function(eventData) {
            var me = this;
            var testValue, href = this.$dialog.submitter.attr('href'), label = this.$dialog.msgLabel.html(), eventSource, messageOnError = (typeof eventData != 'object');
            
            if (typeof eventData == 'object') { //Will always have been invoked from outside capture dialog
                eventSource = eventData.currentTarget;
                testValue = $(eventSource).parents('form').find('input[name = "zc"]').val();
                
                this.setDialogSubmitState(eventSource);

                this.$dialog.title.html(this.messages.ERROR_TITLE);
                label = $(eventSource).attr('alt') || this.$dialog.defaultMessage; //Change label to invoker's ALT attribute, if provided
                href = eventSource.href;
            }
            else if (typeof eventData == 'string') {
                testValue = eventData;
            }
            else { //with no params, this is a re-submit from within the capture dialog
                testValue = this.$dialog.zipInput.val();
                eventSource = this.$dialog.submitter;
            }

            if (/[0-9]{5}/.test(testValue)) {
                $.get("/go/includes/_zipValidityTest.jsp", {'zcTest': testValue}, function(check) {
                    if (check.isValid) {
                        if (eventSource && $(eventSource).data('onsuccess') ) {
                            $(eventSource).data('onsuccess').call(eventSource, testValue);
                        }
                        else me.doDefaultSuccess(href, testValue);
                    }
                    else {
                        me.doDefaultFailure(testValue, {
                            label: label,
                            error: messageOnError && me.messages.VALIDITY_ERROR
                        });
                    }
                },'json');
            }
            else {
                me.doDefaultFailure(testValue, {
                    label: label,
                    error: messageOnError && me.messages.FORMAT_ERROR
                });
            }
        },
        registerOnSuccess: function($target, callback) {
            $target.each(function() {
                $(this).data('onsuccess', callback);
            });
        },
        doDefaultSuccess: function(href, zipVal) {
            $('input[name = "zc"]').val(zipVal);
            var zcReplace = (/zc=([^&#]*?)(&|#|$)/gi).exec(href || location.search);
            var re = new RegExp('zc='+zcReplace[1], 'gi');
            //Reloads current page (with changed zip param) if no HREF has been passed in
            location.href = (typeof href == 'undefined'? location.pathname + location.search.replace(re, "zc="+zipVal) : href.replace(re, "zc="+zipVal));
        },
        doDefaultFailure: function(badZip, message) {
            var me = this;
            this.$dialog.zipInput.val(badZip);
            //Gotta wrap this in setTimeout to make the select work in all cases
            setTimeout( function() {me.$dialog.zipInput.get(0).select();}, 50);
            this.show(message);
        },
        setDialogSubmitState: function(invoker) {
            //Clear any previous state in dialog submit button, and slave it to invoker
            this.$dialog.submitter.removeData('onsuccess');
            this.$dialog.submitter.attr('href', invoker.href);
            $(invoker).data('onsuccess') && this.$dialog.submitter.data('onsuccess', $(invoker).data('onsuccess'));
        },
        validateKey: function(evt) {
            if (evt.which == 13) {
                this.$dialog.submitter.click();
            }
            else if (/\D/.test(this.$dialog.zipInput.val())) {
                this.$dialog.errLabel.html(this.messages.KEYSTROKE_ERROR);
            }
            else {
                this.$dialog.errLabel.html('');
            }
        },
        init: function() {
            var me = this;
            //Set up jqModal here, rather than forcing caller to do it
            $('head').append('<script type="text/javascript" src="/js/lib/jquery/plugins/jqModal.js"></script>');
            $('head').append('<link rel="stylesheet" type="text/css" href="/js/lib/jquery/plugins/jqModal.css" />');
            
            var jqmParams = {
                overlay: this.params.overlay || 50, 
                modal: this.params.modal || true, 
                toTop: this.params.toTop || true,
                closeClass: this.params.closeClass || 'zc-close'
            }
            this.$dialog.title.html(this.messages.DEFAULT_TITLE);
            this.$dialog.addClass('jqmWindow');
            this.$dialog.jqm(jqmParams);
            
            //Event setup in capture dialog
            this.$dialog.zipInput.keyup(function(evt) { me.validateKey(evt) });
            this.$dialog.submitter.click(function(evt) { me.validate();evt.preventDefault(); });
            this.$dialog.actualForm.submit(function(evt) { me.$dialog.submitter.click();evt.preventDefault(); })
            this.$dialog.citySelect.change(function() { me.$dialog.zipInput.val(
                $(this).val().match(/^[0-9]{5}/)[0]
            );me.$dialog.errLabel.html('')});
            //Events on input forms
            $('form:has(input[name = "zc"])').not(this.$dialog.actualForm).each(function() {
                $('.'+ (me.params.submitterClass || 'zc-submit'), this).click(function(evt) { me.validate( $.extend(evt, {isSubmitEvent: true}) );evt.preventDefault(); });
            }).submit( function(evt) {evt.preventDefault();} );
            //Events on triggering elements
            $('.'+(me.params.triggerClass || 'zc-trigger')).click(function(evt) { 
                if (/zc=[0-9]{5}/.test(this.href)) /*Don't show, we already have a zip*/ return true;
                me.setDialogSubmitState(this);
                me.show({ 'label': $(this).attr('alt') }, $.extend(evt, {isSubmitEvent: false}) );
                evt.preventDefault();
            });
        },
        messages: {
            DEFAULT_TITLE: "Please Enter Your ZIP Code",
            ERROR_TITLE: "Invalid or Missing ZIP Code",
            KEYSTROKE_ERROR: "Please enter numbers only.",
            FORMAT_ERROR: "Please enter a valid five-digit ZIP code.",
            VALIDITY_ERROR: "We could not recognize the ZIP code you entered.<br>Please enter a valid ZIP code and try again."
        }
    }
})(jQuery);