var CZ = {};
(function($) {
    // So console logging functions won't return errors in browsers that don't support it.
    // TODO: Remove this block for production
    if (!window.console) {
        window.console = {};
        console.log = function() { return; };
        console.dir = function() { return; };
    }

    function getCZObj() { return window.CZ; }

    // Useful extensions to the Date object prototype
    $.extend(Date.prototype, {
        get12HourTime: function() {
            var h = this.getHours();
            if (h > 12) h -= 12;
            return h;
        },

        isAmPm: function() {
            var h = this.getHours();
            if (h === 0 || h < 12) return 'am';
            else return 'pm';
        },

        getZeroPaddedMinutes: function() {
            var m = this.getMinutes();
            return (m < 10 ? '0' + m : m);
        }
    });

    $.extend(CZ, {
        _preloadedImages: [],
        _lastTS: null,
        _rpFirstRun: true,
        _rpCount: 0,

        //        // Pathing variables
        //        _hostPath: (location.host.indexOf('conspiracy') === -1 ? '/' : '/mti/html-comps/'), // TODO: For Production, change this value to the base URL path or "/" if site is hosted at root level
        //        _realPeopleDataPath: 'data/realpeople.json', // Path to JSON file that populates the Real People module data
        //        _couponPath: 'print/coupon', // URL Path to the coupon popup
        //        _zipCheckPath: 'data/check_zip.php', // URL Path that validates the visitor's ZIP code for the account creation form
        //        _phoneCheckPath: 'data/check_phone.php', // URL Path that validates the visitor's Phone Number for the account creation form
        //        _emailCheckPath: 'data/check_email.php', // URL Path that validates the visitor's Email for the account creation form
        //        _clipToPouchPath: 'data/cliptopouch.json', // URL Path for the clip to pouch
        //        _removeFromPouchPath: 'data/removefrompouch.json', // URL Path for the clip to pouch
        //        _feedbackPath: 'data/feedback.php', // URL path for feedback from Redeemed coupons
        //        _localCDPPath: 'detail/localdetail/', // URL Path for the Local CDP page. This path has the Coupon's UID appended to it at the end. Append trailing slash to this URL
        //        _onlineCDPPath: 'detail/onlinedetail/', // URL Path for the Online CDP page. This path has the Coupon's UID appended to it at the end. Append trailing slash to this URL
        //        _clipperCDPPath: 'detail/clipperdetail/', // URL Path for the Clipper CDP page. This path has the Coupon's UID appended to it at the end. Append trailing slash to this URL
        //        
        //        _validatorIgnoreClass: 'ignore', // Used for the validators


        // Pathing variables
        _hostPath: (location.host.indexOf('localhost') === -1 ? '/' : '/ClipZoneWeb/'), // TODO: For Production, change this value to the base URL path or "/" if site is hosted at root level
        //_realPeopleDataPath: 'data/realpeople.json', // Path to JSON file that populates the Real People module data
        _realPeopleDataPath: 'functions_ajax/RealPeople.aspx', // Path to JSON file that populates the Real People module data
        //        _homeVideoPath: 'video/cz_home_placeholder.swf', // Path to the SWF video file that appears on the homepage
        //        _couponPath: 'print.aspx?couponid=', // URL Path to the coupon popup
        _zipCheckPath: 'functions_ajax/CheckZip.aspx', // URL Path that validates the visitor's ZIP code for the account creation form
        _phoneCheckPath: 'functions_ajax/CheckPhone.aspx', // URL Path that validates the visitor's Phone Number for the account creation form
        _emailCheckPath: 'functions_ajax/CheckEmail.aspx', // URL Path that validates the visitor's Email for the account creation form
        //        _clipToPouchPath: 'functions_ajax/ClipToPouch.aspx?couponid=', // URL Path for the clip to pouch
        //        _removeFromPouchPath: 'functions_ajax/RemoveFromPouch.aspx?couponid=', // URL Path for the clip to pouch
        _feedbackPath: 'data/feedback.php', // URL path for feedback from Redeemed coupons
        _localCDPPath: 'FindLocalCDP.aspx', // URL Path for the Local CDP page. This path has any GET params appended to the end based on classes applied to the quicklook element. "?" does need to be added here.
        _onlineCDPPath: 'FindOnlineCDP.aspx', // URL Path for the Online CDP page. This path has any GET params appended to the end based on classes applied to the quicklook element. "?" does need to be added here.
        _clipperCDPPath: 'PouchClipperCDP.aspx', // URL Path for the Clipper CDP page. This path has any GET params appended to the end based on classes applied to the quicklook element. "?" does need to be added here.
        
        _validatorIgnoreClass: 'ignore', // Used for the validators

        initTabs: function() {
            var $tabs = $('.tabs');
            if ($tabs.length > 0) {
                console.log('initTabs'); // TODO: Remove for Production
                $tabs.parent().tabs();
            }
        },

        initWatermarks: function() {
            $('label.overlabel').overlabel();
            return;
        },

        // Carousel code
        initCarousel: function() {
            var $c = $('#carousel');
            if ($c.length > 0) {
                console.log('initCarousel'); // TODO: Remove for production
                if ($c.hasClass('mini')) {
                    $('#carousel .inner').jCarouselLite({
                        btnPrev: '.controls .prev',
                        btnNext: '.controls .next',
                        circular: true,
                        speed: 350,
                        visible: 1,
                        afterLoad: function(e) { $(e[0]).addClass('active'); },
                        beforeStart: function(e) { $(e[0]).removeClass('active').fadeTo('normal', 0.5).unbind('mouseover').unbind('mouseout'); },
                        afterEnd: function(e) { $(e[0]).fadeTo('fast', 1.0, function() { $(this).addClass('active'); }); }

                    });
                } else {
                    $('#carousel .inner').jCarouselLite({
                        btnPrev: '.controls .prev',
                        btnNext: '.controls .next',
                        circular: true,
                        speed: 350,
                        visible: 3,
                        afterLoad: function(e) { $(e[1]).addClass('active'); },
                        beforeStart: function(e) { $(e[1]).removeClass('active').fadeTo('normal', 0.5).unbind('mouseover').unbind('mouseout'); },
                        afterEnd: function(e) { $(e[1]).fadeTo('normal', 1.0, function() { $(this).addClass('active'); }); }
                    });
                    $('#carousel .inner li').filter(':not(.active)').fadeTo('fast', 0.5);
                }
            }
            return;
        },

        // Home Feature code
        initHomeFeatures: function() {
            $f = $('#features');
            if ($f.length === 1) {
                console.log('initHomeFeatures');  // TODO: Remove for Production
                var t = this, $s = $('.slides li', $f), $c = $('.controls', $f);
                $s.hide().eq(0).show();
                if ($s.length === 1) return;
                else {
                    $cl = $c.append('<ol></ol>').find('ol').eq(0);
                    $cl.append('<li><a href="#" class="prev swap" title="Previous Feature">Previous Feature</a></li>').find('.prev').click(t._showPrevFeature);
                    $s.each(function() {
                        var $t = $(this), tid = $t.get(0).id, fnum = parseInt(tid.split('_')[1]);
                        $cl.append('<li><a href="#' + tid + '" class="jump_to swap">Feature ' + fnum + '</a></li>').find('.jump_to').click(t._jumpToFeature);
                    });
                    $cl.append('<li><a href="#" class="next swap" title="Next Feature">Next Feature</a></li>').find('.next').click(t._showNextFeature);
                    t._updateFeatureNav($s.filter(':visible').attr('id'));
                }
                t._featureCounter = window.setInterval(function() { $('#features a.next').click(); }, 5000);

            }
        },
        _showPrevFeature: function(e) {
            e.preventDefault();
            var $sl = $('#features .slides li');
            var prevId = $sl.parent().find('li:visible').prev().attr('id');
            if (!prevId) prevId = $sl.parent().find('li:last-child').attr('id');
            getCZObj()._showFeature(prevId);
        },
        _showNextFeature: function(e) {
            e.preventDefault();
            var $sl = $('#features .slides li');
            var nextId = $sl.parent().find('li:visible').next().attr('id');
            if (!nextId) nextId = $sl.parent().find('li:first-child').attr('id');
            getCZObj()._showFeature(nextId);
        },
        _jumpToFeature: function(e) {
            e.preventDefault();
            var t = getCZObj();
            if ($(e.target).hasClass('selected')) return false;
            else getCZObj()._showFeature($(e.target).attr('href').split('#')[1]);
        },
        _showFeature: function(eid) {
            console.log('_showFeature');  // TODO: Remove for Production
            var $fl = $('#features .slides'), t = getCZObj();
            t._updateFeatureNavDots(eid);
            $fl.find('li:visible').fadeOut('slow');
            $fl.find('li#' + eid).eq(0).fadeIn('slow');
            t._resetAutoRotateCounter();
        },
        _updateFeatureNav: function(eid) {
            console.log('_updateFeatureNav');  // TODO: Remove for Production
            // Update dots
            this._updateFeatureNavDots(eid);
        },
        _updateFeatureNavDots: function(eid) {
            var $f = $('#features'), $c = $('.controls', $f);
            $c.find('a.jump_to').removeClass('selected').filter('[href$=' + eid + ']').addClass('selected');
        },
        _resetAutoRotateCounter: function() {
            window.clearInterval(this._featureCounter);
            this._featureCounter = window.setInterval(function() { $('#features a.next').click(); }, 5000);
        },

        loadFlashVideo: function(eid, vPath, w, h) {
            var t = this, v = document.getElementById(eid);
            if (v) $(v).flash({
                swf: t._hostPath + vPath,
                width: w,
                height: h,
                params: {
                    wmode: 'transparent'
                }
            }).css({
                width: w + 'px',
                height: h + 'px',
                overflow: 'hidden',
                position: 'relative'
            });
        },

        initButtonRollovers: function() {
            console.log('initButtonRollovers'); // TODO: Remove for production
            var t = this, b = $('input[type="image"]'), e = null, tmp = '';
            var path, ext, src, img, himg, id;
            t._btnHovers = [];
            t._btnDefaults = [];
            for (var i = 0, il = b.length; i < il; i++) {
                console.log('initButtonRollovers loop:', i); // TODO: Remove for production
                e = $(b[i]);
                id = e.attr('id'); // The button's ID
                src = e.attr('src'); // Full src attribute
                tmp = src.split('/');
                img = tmp.pop(); // Default image filename
                path = tmp.join('/'); // Path to image
                tmp = img.split('.');
                ext = tmp[1]; // Image file extension
                himg = tmp[0] + '_hover.' + ext; // Hover image filename
                // Log paths for switching
                t._btnHovers[id] = new Image();
                t._btnHovers[id].src = path + '/' + himg;
                t._btnDefaults[id] = new Image();
                t._btnDefaults[id].src = src;

                e.mouseover(function() {
                    var $t = $(this);
                    $t.attr('src', t._btnHovers[$t.attr('id')].src);
                }).mouseout(function() {
                    var $t = $(this);
                    $t.attr('src', t._btnDefaults[$t.attr('id')].src);
                });
            }
        },

        initDropdownFX: function() {
            console.log('initDropdownFX'); // TODO: Remove for production
            $('#mainnav > ul > li, #getclipzone').hover(
                function() {
                    // Must only apply to hidden ul or else the dropdown will "bounce"
                    $(this).children('ul:hidden').slideDown('normal');
                },
                function() {
                    $(this).children('ul').slideUp('normal');
                }
            );
        },

        initBrowserCompat: function() {
            // Emulate generated content in IE6/7
            if ($.browser.msie && parseFloat($.browser.version) <= 7.0) {
                $('.morelink').append(' &gt;');
                $('.uplink').prepend('&lt; ');
            }
            return;
        },

        // Real People Module code
        initRealPeopleModule: function() {
            var t = this;
            if ($('#realpeople').length === 1) {
                console.log('initRealPeopleModule'); // TODO: Remove for production
                t.realPeopleModuleGo();
            }
        },
        realPeopleModuleGo: function(ts) {
            console.log('realPeopleModuleGo'); // TODO: Remove for production
            ts = ts ? { 'ts': ts} : null;
            var t = this;

            // TODO: Remove below dev functionality for production and see next TODO to enable for production
            // Begin Dev functionality
            //            var url = 'data/realpeople.json';
            //            if (t._rpCount % 2 === 0 && t._rpCount % 3 === 0) url = 'data/realpeople.json';
            //            else if (t._rpCount % 2 === 0 || t._rpCount === 1) url = 'data/realpeople2.json';
            //            else if (t._rpCount % 3 === 0) url = 'data/realpeople3.json';
            //            else url = 'data/realpeople.json';
            //            $.getJSON(url, ts,
            // End Dev functionality

            // TODO: Production: unccoment line below. _realPeopleData is set above in the properties
            $.getJSON(t._hostPath + t._realPeopleDataPath, ts,
                function(data) {
                    if (data.response) {
                        // New items present
                        var tmp;

                        // Set last timestamp for later check
                        t._lastTS = data.items[data.items.length - 1].ts;

                        // Grab list wrapper for further use
                        var sList = $('#realpeople .scroller ol');

                        // If we're past the first run-through, delete all scrolled off elements before continuing to prevent mem overload
                        if (t._rpCount > 0) {
                            tmp = sList.children('li').length - 3;
                            sList.children("li:lt(" + tmp + ")").remove();
                            sList.css('top', '0px');
                        }

                        // Append new entries
                        $.each(data.items, t._appendRealPeopleEntries);

                        // Initiate animation
                        var sHeight = sList.height();
                        var liHeight = sList.children('li').height();
                        var scrollTo = sHeight - (liHeight * 3);
                        var dur = ((data.items.length) * 3) * 1000;
                        var checkDelay = (data.items.length >= 10 ? 5 : 15) * 1000;
                        sList.animate({ top: '-' + scrollTo + 'px' }, {
                            duration: dur,
                            easing: 'linear',
                            complete: function() {
                                t._rpCount++;
                                window.setTimeout(function() {
                                    t.realPeopleModuleGo(t._lastTS);
                                }, checkDelay);
                            }
                        });

                    } else {
                        // Nothing new. Check again in 15 seconds
                        window.setTimeout(function() {
                            t._rpCount++;
                            t.realPeopleModuleGo(t._lastTS);
                        }, 15000);
                    }
                }
            );
        },
        _appendRealPeopleEntries: function(i, item) {
            console.log('_appendRealPeopleEntries:', i); // TODO: Remove for production
            var d = new Date(item.ts * 1000);
            var m = d.getZeroPaddedMinutes();
            var oDate = d.get12HourTime() + ':' + m + d.isAmPm();

            $("<li></li>").append('<strong>' + oDate + '</strong>').append(' ' + item.loc + ' - ' + item.msg).appendTo('#realpeople .scroller ol');
        },

        initPrintControls: function() {
            var l = $('.print'), t = this;
            if (l.length > 0) {
                console.log('initPrintControls'); // TODO: Remove for production
                l.click(function() {
                    // window.print();
                    //window.open(t._hostPath + t._couponPath, 'coupon', 'width=550,location=no,menubar=no,status=no,toolbar=no,scrollbars=yes');
                    window.open(t._hostPath + $(this).attr('href'), 'coupon', 'width=550,location=no,menubar=no,status=no,toolbar=no,scrollbars=yes');
                    return false;
                });
            }
        },

        initOffsiteLinks: function() {
            $('a.offsite').click(function() {
                window.open($(this).attr('href'));
                return false;
            });
        },

        initCloseControls: function() {
            var l = $('.close');
            if (l.length > 0) {
                console.log('initCloseControls'); // TODO: Remove for production
                l.click(function() {
                    window.close();
                    return false;
                });
            }
        },

        // Cliplink and Quicklook code
        initClipLinkControls: function() {
            var c = $('.cliplink:not(.static)');
            if (c.length > 0) {
                console.log('initClipLinkControls'); // TODO: Remove for production
                var t = this;
                c.click(function(ev) {
                    ev.preventDefault();
                    var $t = $l = $(this);

                    // If the cliplink is in a quicklook popup, grab static cliplink in listing to transform both
                    if ($t.is('.quicklook .cliplink')) $l = $t.parents('.quicklook').parent().find('.cliplink');

                    $.getJSON($t.attr('href'), {}, function(data, status) {
                        if (data.response === true) {
                            if ($t.hasClass('add')) $l.removeClass('add').addClass('remove').text('Remove from Pouch');
                            else if ($t.hasClass('remove')) $l.removeClass('remove').addClass('add').text('Clip to Pouch');
                            $l.attr('href', data.actionURL);
                        }
                        else if (data.message) alert(data.message);
                    });
                });
            }
        },
        initQuickLookPopups: function() {
            var q = $('.quicklook');
            if (q.length > 0) {
                console.log('initQuickLookPopups'); // TODO: Remove for production
                q.hide();
                this._initQuickLookRollover();
            }
        },
        _initQuickLookRollover: function() {
            var t = this;
            $('.quicklook').click(function(e) {
                if (!($(e.target).hasClass('cliplink'))) {
                    var $t = $(this), $p = $t.parent(), paramSrc = $t.attr('class').split(/ +/).slice(1), params = {};

                    for (var i = 0, il = paramSrc.length; i < il; i++) {
                        var p = paramSrc[i], tmp = p.split('_');
                        params[tmp[0]] = tmp[1];
                    }

                    if ($p.hasClass('localcoupon')) location.href = t._hostPath + t._localCDPPath + '?' + $.param(params);
                    else if ($p.hasClass('onlinecoupon')) location.href = t._hostPath + t._onlineCDPPath + '?' + $.param(params);
                    else if ($p.hasClass('clippercoupon')) location.href = t._hostPath + t._clipperCDPPath + '?' + $.param(params);
                }

            }).parent().hover(
                function() {
                    var $t = $(this);
                    if ($.browser.msie) $t.children('.quicklook').show();
                    else $t.children('.quicklook').fadeIn('normal');
                },
                function() {
                    var $t = $(this);
                    if ($.browser.msie) $t.children('.quicklook').hide();
                    else $t.children('.quicklook').fadeOut('normal');
                }
            );
        },

        initRedeemedButtons: function() {
            var $r = $('#redeem_feedback'), t = this;
            if ($r.length === 1) {
                console.log('initRedeemedButtons'); // TODO: Remove for production

                $r.find('input.button').click(function() {
                    var $t = $(this), v = $t.val(), cid = $('#guid').val();
                    $.getJSON(t._hostPath + t._feedbackPath, { "f": v, "guid": cid }, function(data, status) {
                        if (data.response) $r.html(data.message);
                        else alert(data.message);
                    });
                });
            }
        },

        initSendToFriend: function() {
            $('.displayPopup').openDOMWindow({
                eventType: 'click',
                width: 529,
                height: 363,
                borderColor: '#D5D5D5',
                borderSize: 4,
                windowSource: 'iframe',
                windowPadding: 0,
                positionType: 'centered',
                modal: 1
            });
        },

        // Categories & Retailers accordion code
        initTableAccordion: function() {
            var $tables = $('.settings_table');
            if ($tables.length > 0) {
                console.log('initTableAccordion'); // TODO: Remove for Production
                var t = this;
                // Enable Accordion functionality AND set it so all are collapsed except the first on page load
                $tables.find('tbody.subcategories').hide().eq(0).show();
                $tables.find('span.toggle').removeClass('collapsed expanded').addClass('collapsed').click(function() {
                    var $t = $(this);
                    var $c = $t.parents('tbody').next('tbody.subcategories');
                    if ($t.hasClass('expanded')) {
                        $t.removeClass('expanded').addClass('collapsed');
                        $c.hide();
                        t._setCollapsedParentColumnCounter(t._getToggleColumnParents($t));
                    } else if ($t.hasClass('collapsed')) {
                        $t.removeClass('collapsed').addClass('expanded');
                        $c.show(null, function() {
                            $(this).removeAttr('style');
                        });
                        t._setExpandedParentColumnControls(t._getToggleColumnParents($t));
                    }
                }).eq(0).removeClass('collapsed').addClass('expanded');
                t._setCollapsedParentColumnCounter(t._getToggleColumnParents($tables.find('span.toggle.collapsed')));
                t._setExpandedParentColumnControls(t._getToggleColumnParents($tables.find('span.toggle.expanded')));
            }

        },
        _getToggleColumnParents: function($target) {
            return $target.parents('tr').find('td:not(.title)');
        },
        _getToggleColumnParentType: function($parent) {
            return $parent.attr('class').split(' ')[0];
        },
        _setCollapsedParentColumnCounter: function($parents) {
            var t = this;
            $parents.each(function() {
                var $t = $(this), c;
                $t.removeClass('empty');
                c = t._countAllChecked($t, t._getToggleColumnParentType($t));
                if (c === 0) $t.addClass('empty');
                $t.html(c + ' selected');
            });

        },
        _setExpandedParentColumnControls: function($parents) {
            var t = this;
            $parents.each(function() {
                var $t = $(this), c;
                $t.removeClass('empty');
                $t.html('<a href="#" class="all">all</a> | <a href="#" class="none">none</a>');
            });

        },
        _countAllChecked: function($parent, type) {
            return $parent.parents('tbody.parent').next('tbody.subcategories').find('td.' + type + ' input.toggle[checked]:not([disabled])').length;
        },

        // Categories & Retailers all/none code
        initCategoriesRetailersControls: function() {
            var $sf = $('#settings_form');
            if ($sf.length === 1) {
                console.log('initCategoriesRetailersControls'); // TODO: Remove for Production
                var t = this;
                $('a.all', $sf).live('click', function() {
                    var $t = $(this);
                    t._setChecboxState(t._getAllEnabledChildrenTogglesByType($t), true);
                    return false;
                });

                $('a.none', $sf).live('click', function() {
                    var $t = $(this);
                    t._setChecboxState(t._getAllEnabledChildrenTogglesByType($t), false);
                    return false;
                });

                $('td.prefs input.toggle', $sf).each(function() {
                    t._enableDisableCategoriesRetailersToggles($(this));
                }).click(function() {
                    t._enableDisableCategoriesRetailersToggles($(this));
                });
            }
        },
        _getToggleType: function($target) {
            return $target.parents('td').attr('class').split(' ')[0];
        },
        _getAllEnabledChildrenTogglesByType: function($target) {
            var type = this._getToggleType($target);
            return $target.parents('tbody.parent').next('tbody.subcategories').find('td.' + type + ' input.toggle:not([disabled])');
        },
        _setChecboxState: function($els, bool) {
            var t = this;
            $els.each(function() {
                var $t = $(this), type = $t.parent('td').attr('class');
                if (bool) $t.attr('checked', 'checked');
                else $t.removeAttr('checked');
                if (type === 'prefs') t._enableDisableCategoriesRetailersToggles($t);
            });
        },
        //        _enableDisableCategoriesRetailersToggles: function($el) {
        //            //var t = this, $others = $el.parents('tr').find('td:not(.title,.prefs) input.toggle');
        //            var t = this, $others = $el.parents('tr').find('td:not(.title,.prefs)').children('input.toggle');
        //            $others.each(function() {
        //                var $t = $(this);
        //                if ($el.get(0).checked) $t.removeAttr('disabled');
        //                else $t.removeAttr('checked').attr('disabled', 'disabled');
        //            });
        //        }
        _enableDisableCategoriesRetailersToggles: function($el) {
            var t = this,
                $others = $el.parents('tr').children('td'),
                isChecked = $el.get(0).checked;

            $others.each(function() {
                var $t = $(this);
                if ($t.hasClass('title') || $t.hasClass('prefs')) return;
                else {
                    $i = $t.children('input.toggle');
                    if (isChecked) $i.removeAttr('disabled');
                    else $i.removeAttr('checked').attr('disabled', 'disabled');
                }
            });
        },
        initClearTextFieldDefaults: function() {
            var t = this;
            if ($('#history_table').length === 1) {
                $('#history_table input.text').focus(function() {
                    var $t = $(this);
                    if ($t.val() === 'enter') $t.removeClass(t._validatorIgnoreClass).val('');
                }).blur(function() {
                    var $t = $(this);
                    if ($.trim($t.val()) === '') $t.addClass(t._validatorIgnoreClass).val('enter');
                });
            }
        },

        // Form validation init wrapper and code
        initFormValidation: function() {
            //this.initContactFormValidation();
            //this.initAccountEditFormValidation();
            //this.initCreateFormValidation();
            //this.initCouponHistoryValidation();            
            validate_form_contact($, this);
            validate_form_acct_create($, this);
            validate_form_acct_edit_email($, this);
            validate_form_acct_edit_password($, this);
            validate_form_reset_password($, this);
            validate_form_acct_edit_profile($, this);
            validate_form_invite_friend($, this);
            validate_form_coupon_history($, this);
            validate_form_merchant_signup($, this);            
        },
        initContactFormValidation: function() {
            var t = this;
            if ($('#contact_form').length === 1) {
                console.log('initContactFormValidation');  // TODO: Remove for Production
                $('form').validate({
                    rules: {
                        contact_name: {
                            required: true,
                            maxlength: 16
                        },
                        contact_email: {
                            required: true,
                            email: true,
                            maxlength: 254
                        },
                        contact_topic: {
                            required: true
                        },
                        contact_message: {
                            required: true,
                            maxlength: 600
                        }
                    },
                    messages: {
                        contact_name: {
                            required: "Enter name.",
                            maxlength: $.validator.format("Max {0} characters.")
                        },
                        contact_email: {
                            required: "Enter valid email.",
                            email: "Enter valid email.",
                            maxlength: $.validator.format("Max {0} characters.")
                        },
                        contact_topic: {
                            required: "Select a topic."
                        },
                        contact_message: {
                            required: "Enter message.",
                            maxlength: $.validator.format("Max {0} characters.")
                        }
                    },
                    onkeyup: false,
                    focusCleanup: true,
                    focusInvalid: false,
                    errorPlacement: function(e, el) {
                        if (el.attr('type') === 'checkbox') return;
                        else e.insertAfter(el);
                    },
                    success: function(el) {
                        el.addClass('validation valid').text('Okay!');
                    }
                });
            }
        },
        initAccountEditFormValidation: function() {
            var t = this;
            if ($('#acctedit_form').length === 1) {
                console.log('initAccoutnEditFormValidation'); // TODO: Remove for production
                $('form').validate({
                    rules: {
                        firstname: {
                            required: true,
                            maxlength: 16
                        },
                        lastname: {
                            maxlength: 50
                        },
                        phone: {
                            required: true,
                            phone: true,
                            remote: t._hostPath + t._phoneCheckPath
                        },
                        address1: {
                            maxlength: 50
                        },
                        address2: {
                            maxlength: 50
                        },
                        city: {
                            maxlength: 50
                        },
                        zip: {
                            required: true,
                            digits: true,
                            rangelength: [5, 5]
                        }
                    },
                    messages: {
                        firstname: {
                            required: "Please enter first name.",
                            maxlength: $.validator.format("First Name cannot be longer than than {0} characters.")
                        },
                        lastname: {
                            maxlength: $.validator.format("Last Name cannot be longer than than {0} characters.")
                        },
                        phone: {
                            required: "Please enter a valid, 10-digit mobile phone number.",
                            phone: "Please enter a valid, 10-digit mobile phone number.",
                            remote: "Account with this number already exists."
                        },
                        address1: {
                            maxlength: $.validator.format("Street Address 1 cannot be longer than than {0} characters.")
                        },
                        address2: {
                            maxlength: $.validator.format("Street Address 2 cannot be longer than than {0} characters.")
                        },
                        city: {
                            maxlength: $.validator.format("City cannot be longer than than {0} characters.")
                        },
                        zip: {
                            required: "Please enter a valid zip code.",
                            digits: "Please enter a valid zip code.",
                            rangelength: "Please enter a valid zip code."
                        }
                    },
                    onkeyup: false,
                    focusCleanup: true,
                    focusInvalid: false,
                    errorPlacement: function(e, el) {
                        if (el.attr('type') === 'checkbox') e.appendTo(el.parent());
                        else e.insertAfter(el);
                    },
                    success: function(el) {
                        el.addClass('validation valid').text('Okay!');
                    }
                });
            }
        },
        initCreateFormValidation: function() {
            var t = this;
            if ($('#accountform').length === 1) {
                console.log('initCreateFormValidation'); // TODO: Remove for production
                $('form').validate({
                    rules: {
                        phonetype: {
                            required: true
                        },
                        zip: {
                            required: true,
                            digits: true,
                            rangelength: [5, 5]
                        },
                        firstname: {
                            required: true,
                            maxlength: 16
                        },
                        lastname: {
                            maxlength: 50
                        },
                        phone1: {
                            required: true,
                            phone: true                            
                        },
                        acctemail: {
                            required: true,
                            email: true,
                            maxlength: 254,
                            remote: t._hostPath + t._emailCheckPath
                        },
                        acctemail2: {
                            required: true,
                            equalTo: "#acctemail",
                            email: true,
                            maxlength: 254
                        },
                        acctpassword: {
                            required: true,
                            nowhitespace: true,
                            minlength: 6,
                            maxlength: 16
                        },
                        acctpassword2: {
                            required: true,
                            equalTo: "#acctpassword",
                            nowhitespace: true,
                            minlength: 6,
                            maxlength: 16
                        },
                        captcha: {
                            required: true,
                            equalTo: "#captchaval"                            
                        },
                        agreeterms: {
                            required: true
                        }
                    },
                    messages: {
                        phonetype: {
                            required: "Please select phone."
                        },
                        zip: {
                            required: "Enter valid zip.",
                            rangelength: "Enter valid zip.",
                            digits: "Enter valid zip."
                        },
                        firstname: {
                            required: "Please enter your first name.",
                            maxlength: $.validator.format("First Name cannot be longer than than {0} characters.")
                        },
                        lastname: {
                            maxlength: $.validator.format("Last Name cannot be longer than than {0} characters.")
                        },
                        phone1: {
                            required: "Please enter a valid, 10-digit mobile phone number.",
                            phone: "Please enter a valid, 10-digit mobile phone number."
                            //remote: "Account with this number already exists."
                        },
                        acctemail: {
                            required: "Please enter a valid email address.",
                            email: "Please enter a valid email address.",
                            maxlength: $.validator.format("Email address cannot be longer than {0} characters."),
                            remote: "Account with this email address already exists."
                        },
                        acctemail2: {
                            required: "Please enter a valid email address.",
                            equalTo: "Email Addresses do not match.",
                            email: "Please enter a valid email address.",
                            maxlength: $.validator.format("Email address cannot be longer than {0} characters.")
                        },
                        acctpassword: {
                            required: "Please create a password.",
                            nowhitespace: "Password may not contain spaces.",
                            minlength: "Password must be at least six characters.",
                            maxlength: $.validator.format("Password cannot be longer than {0} characters")
                        },
                        acctpassword2: {
                            required: "Please re-enter the password.",
                            nowhitespace: "Password may not contain spaces.",
                            minlength: "Password must be at least six characters.",
                            maxlength: $.validator.format("Password cannot be longer than {0} characters"),
                            equalTo: "Passwords do not match."
                        },
                        captcha: {
                            required: "Entry not correct. Please try again.",
                            equalTo: "Entry not correct. Please try again."                            
                        },
                        agreeterms: {
                            required: "Please read and agree with the Terms of Use and Privacy Policy."
                        }
                    },
                    onkeyup: false,
                    focusCleanup: true,
                    focusInvalid: false,
                    errorPlacement: function(e, el) {
                        if (el.attr('type') === 'checkbox') e.appendTo(el.parent());
                        else e.insertAfter(el);
                    },
                    success: function(el) {
                        if (/phonetype|carrier|agreeterms|keepinloop|zip/.test(el.attr('for'))) el.remove();
                        else el.addClass('validation valid').text('Okay!');
                    }
                });
            }
        },
        initCouponHistoryValidation: function() {
            var t = this;
            if ($('#history_form').length === 1) {
                console.log('initCouponHistoryValidation'); // TODO: Remove for production
                var dynamicRules = {};
                $('#history_form input.text').each(function() {
                    dynamicRules[this.id] = 'number';
                });
                $('form').validate({
                    rules: dynamicRules,
                    messages: {},
                    ignore: '.' + t._validatorIgnoreClass,
                    onkeyup: false,
                    focusCleanup: false,
                    focusInvalid: false,
                    invalidHandler: function(form, validator) {
                        var errors = validator.numberOfInvalids();
                        if (errors) {
                            $('#instructions span').addClass(this.errorClass);
                        }
                    },
                    highlight: function(el, errClass) {
                        $(el).add('#instructions span').addClass(errClass);
                    },
                    unhighlight: function(el, errClass) {
                        $(el).removeClass(errClass);
                        if ($('#history_form input.text.error').length === 0) $('#instructions span').removeClass(errClass);
                    },
                    errorPlacement: function(error, el) {
                        return;
                    },
                    success: function(el) {
                        el.remove();
                    }
                });
            }
        },

        // Image Preloader code
        preloadImages: function(list) {
            for (var i = 0, il = list.length; i < il; i++) {
                this._preloadImage(list[i]);
            }
        },
        _preloadImage: function(imgPath) {
            console.log(imgPath, 'loaded'); // TODO: Remove for production
            var tmp = new Image();
            tmp.src = imgPath;
            this._preloadedImages.push(tmp);
        },

        initScrollingMap: function() {
            var $m = $('#map.scroller'); // The map to scroll
            if ($m.length > 0) {
                console.log('initScrollingMap');
                var $w = $(window), // Window object wrapped in jQ
                    $r = $m.parent(), // The container of the map
                    rTopMargin = parseInt($r.css('marginTop')),
                    $p = $r.prev(), // The container of the column that the map is in. Used to calculate the furthest down the map can scroll
                    pHeight = $p.innerHeight(), // The height of the previous column
                    elTop = $m.offset().top, // The position of the map's container in px
                    elHeight = $m.height(), // The height of the map
                    dockMap = document.getElementById('dockmap'), // The checkbox to toggle map scrolling
                    topMargin, // Used to store the top margin value to animate to
                    winTop; // Used to store the current scroll position of the window


                window.scrollMap = false;

                $(window).scroll(function() {
                    winTop = $w.scrollTop();

                    window.scrollMap = !(dockMap.checked);

                    // Calculate the top margin to apply
                    if (winTop > elTop) topMargin = (winTop - elTop) + 15;
                    else topMargin = 0;

                    if (window.scrollMap) {
                        // If the top margin is too big, fix the top margin to a constant. I realize this could be cleaned up, but I can't seem to get it to work that way
                        if (topMargin > (pHeight - elHeight - rTopMargin)) $m.stop().animate({ 'marginTop': (pHeight - elHeight - rTopMargin) + 'px' }, 'slow');
                        else $m.stop().animate({ "marginTop": topMargin + "px" }, "slow");
                    }
                });
            }

        }

    });

})(jQuery);

