
/**
* Mutes firebug console code and errors on browsers where firebug is not installed
*/
if (!window.console || !console.firebug) {
    var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
    "group", "groupEnd", "groupCollapsed", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];

    window.console = {};
    for (var i = 0; i < names.length; ++i) {
        window.console[names[i]] = function() { };
    }
}


// global closure to isolate compare from the rest of lexus.com
(function() {
    /**
    * Initialization code for the application. First chunk runs when the script
    * is loaded, code wrapped in dom ready event runs when the dom is ready.
    */


    /**
    * CONSTANTS
    * The constants are used throughout the site
    */
    var VEHICLE_IMG_ROOT = '/images/comparator/lexus_vehicles/';
    var LAYOUT_IMG_ROOT = '/images/comparator/layout/';

    /**
    * The state explicitly defines the current values for the application. State 
    * values are always stored explicitly, not in view objects (like form controls)
    */
    var state = function() {
        var _comparisonType = null; // current type of comparison (competitive lexus-to-lexus)
        var _primaryModel = null; // primary lexus model we're comparing other vehicles to
        var _primaryStyle = null; // primary style of lexus model
        var _singleStyle = false;
        var _step = '';

        /* 
        * Array of competitors we're comparing the lexus to. Called competitors but 
        * can consist of lexus vehicles as well
        */
        var _competitors = [], _defaultCompetitors = [];

        /*
        * utility log function, produces readable output in firebug
        */
        function _log(action) {
            console.groupCollapsed('State Updated: ' + action);
            console.log('Comparison Type: ' + _comparisonType);
            console.log('Primary Model: ' + _primaryModel);
            console.log('Primary Style: ' + _primaryStyle);
            console.group('Competitors');
            console.log(_competitors[0] || 'empty');
            console.log(_competitors[1] || 'empty');
            console.log(_competitors[2] || 'empty');
            console.groupEnd();
            console.log('Step: ' + _step);
            console.groupEnd();
        }

        /*
        * Getters
        */
        /**
        * Returns the current comparison type: competitive, lexus2lexus or null if not set
        * @return {String} 
        */
        function _getComparisonType() {
            return _comparisonType;
        }

        function _setComparisonTypeCode(typeId) {
            _comparisonType = typeId;
        }

        function _getPrimaryModelCode() {
            return _primaryModel;
        }

        function _getPrimaryStyleId() {
            return _primaryStyle;
        }

        function _setPrimaryStyleId(styleId) {
            _primaryStyle = styleId;
        }

        function _getStep() {
            return _step;
        }

        function _setStep(step) {
            _step = step;

            document.fire('step:set', { step: step, comparisonType: _comparisonType });
        }

        function _getSingleStyle() {
            return _singleStyle;
        }

        function _setSingleStyle(styleId) {
            _singleStyle = styleId;
        }

        /**
        * 
        * @param {Object} type
        */
        var _setComparisonType = function(type) {
            _comparisonType = type;

            _log('comparisonType:set');


            // fire off the event
            document.fire('comparisonType:set', { comparisonType: type });
        };

        /**
        * Sets the primary model from model id
        * @modelId(int) the model id
        */
        var _setPrimaryModel = function(modelCode) {
            // set the primaryModel
            var prevModelCode = _primaryModel;
            _primaryModel = modelCode;

            _log('primaryModel:set');

            // fire off the event
            document.fire('primaryModel:set', { modelCode: modelCode, prevModelCode: prevModelCode });
        };

        /**
        * Sets primary style from style id
        */
        function _setPrimaryStyle(styleId) {
            // Set the eventType depending on whether we are setting or changing the primary style
            var eventType = (_primaryStyle) ? ('primaryStyle:changed') : ('primaryStyle:set');

            _primaryStyle = styleId;

            if (_getStep() != 'viewresults') {
                _setStep('competitorselect');
            }

            _log(eventType);

            // fire off the event with the style id and the style description
            document.fire(eventType, { lexusStyleId: styleId, styleInfo: model.getStyleInfoFromStyleId(styleId), step: _getStep(), comparisonType: _comparisonType, primaryModel: _primaryModel });
        }

        /**
        * Will add the competitor to the 
        * @param {Object} competitor
        */
        function _addCompetitor(competitor) {
            if (_competitors.length < 3) {
                if (_primaryStyle == competitor.trimId || _competitors.find(function(comp) { return comp.trimId == competitor.trimId; })) {
                    document.fire('error:duplicateCompetitor');
                    return;
                }

                _competitors.push(competitor);
                _log('competitor:added');

                // fire the competitors changed event so the UI can refresh
                document.fire('competitors:changed', { competitors: _competitors, defaultCompetitors: _defaultCompetitors, index: _competitors.length - 1, step: _getStep(), ignoreHistory: true });
            } else {
                document.fire('error:competitorsFull');
            }
        }

        /**
        * 
        * @param {Object} competitor
        * @param {Object} index
        */
        function _changeCompetitor(competitor, index) {
            if (_primaryStyle == competitor.trimId || _competitors.find(function(comp) { return comp.trimId == competitor.trimId; })) {
                document.fire('error:duplicateCompetitor');
                return;
            }

            _competitors.splice(index, 1, competitor);

            _log('competitor:changed');

            // fire the competitors changed event so the UI can refresh
            document.fire('competitors:changed', { competitors: _competitors, defaultCompetitors: _defaultCompetitors, index: index, step: _getStep(), ignoreHistory: true });
        }

        /**
        * Removes a competitor at the specified index, changes the length of the competitor array.
        * @param {Object} index
        */
        function _removeCompetitor(index) {
            var ret = _competitors.splice(index, 1);
            _log('competitor:removed');

            // fire the competitors changed event so the UI can refresh
            document.fire('competitors:changed', { competitors: _competitors, defaultCompetitors: _defaultCompetitors, index: index, step: _getStep(), ignoreHistory: true });
        }


        /**
        * A special case method for removing the primary vehicle in lexus to lexus comparisons. The primary
        * vehicle is replaced by the spliced first entry from competitors
        */
        function _removePrimaryVehicle() {
            // splice the first competitor vehicle
            var newPrimary = _competitors.shift();

            _setPrimaryModel(newPrimary.modelCode);

            _setPrimaryStyle(newPrimary.trimId);
        }

        /*
        * When the dom is loaded, listen for Custom Events from the UI
        */
        document.observe('dom:loaded', function() {
            // observe when the application loads
            document.observe('compare:loaded', function(evt) {
                // set the intial model
                _setPrimaryModel(evt.memo.modelCode);

                // set the comparison type, if it's present
                if (evt.memo.comparisonType) {
                    _setComparisonType(evt.memo.comparisonType);

                    // otherwise, show the comparison select view
                } else {
                    view.show('viewSelectComparisonType');
                    //view.hideLoadingWrapper();

                    if (dhtmlHistory != null) {
                        dhtmlHistory.add(new Date().getTime().toString(), { callback: function() {
                            _competitors = [];
                            _removeCompetitor(0);
                            state.setStep('');
                            if (!state.getSingleStyle()) {
                                state.setPrimaryStyleId(null);
                            }
                            view.show('viewSelectComparisonType');
                        }
                        });
                    }
                }
            });

            // observe comparison selection
            document.observe("comparisonType:selected", function(evt) {

                // set the comparison type in the state

                _setComparisonType(evt.memo.type);

                var _stepHistory = null;

                if (evt.memo.source == 'comparisonSelect') {
                    _stepHistory = 'styleselect';
                    _setStep(_stepHistory);
                }
                else {
                    _stepHistory = _step;
                }

                if (!state.getSingleStyle()) {
                    var _comparisonView = (_comparisonType == 'competitive') ? ('viewCompSelectStyle') : ('viewLexusSelectStyle');

                    view.show(_comparisonView);

                    if (dhtmlHistory != null) {
                        dhtmlHistory.add(new Date().getTime().toString(), { callback: function() {
                            document.fire('error:resetSelectErrors');
                            _setStep(_stepHistory);
                            view.show(_comparisonView);
                        }
                        });
                    }
                }
            });

            // observe primary model selection
            document.observe("primaryModel:selected", function(evt) {
                _setPrimaryModel(evt.memo.modelCode);
            });

            // observe style selection
            document.observe("primaryStyle:selected", function(evt) {
                _setPrimaryStyle(evt.memo.lexusStyleId);
            });

            // observe when the key competitors are loaded
            document.observe("keyComps:loaded", function(evt) {
                // set the default competitors
                _defaultCompetitors = evt.memo.competitors;

                // if the current step is not viewresults
                if (_getStep() != 'viewresults') {

                    _competitors = [];

                    // change the key competitors (hardcoded to 3)
                    for (var i = 0; i < 3; i++) {
                        _changeCompetitor(evt.memo.competitors[i], i);
                    }
                }
            });

            // When the user clicks the "reset to pre-selected competitors" link, overwrite the current set of competitors with the defaultCompetitors for the current trim
            document.observe('keyComps:reset', function() {
                for (var i = 0; i < 3; i++) {
                    _changeCompetitor(_defaultCompetitors[i], i);
                }

                // fire the competitors changed event so the UI can refresh
                document.fire('competitors:changed', { competitors: _competitors, defaultCompetitors: _defaultCompetitors, index: null });
            });

            // observe when someone uses the UI to remove a competitor
            document.observe('competitor:removed', function(evt) {
                // remove the competitor at the specified index
                _removeCompetitor(evt.memo.index);
            });

            // observe when someone uses the UI to change a competitor
            document.observe('competitor:selected', function(evt) {
                var competitor = model.getStyleInfoFromStyleId(evt.memo.value);

                // if we've been passed an index, then we're replacing an existing competitor
                if (evt.memo.index !== null) {
                    _changeCompetitor(competitor, evt.memo.index);
                } else {
                    // otherwise, we're just adding it
                    _addCompetitor(competitor);
                }
            });

            document.observe('comparison:requested', function(evt) {
                // package up the needed data, pass it the the model for the request
                var data = {
                    comparisonType: evt.memo.comparisonType,
                    competitors: _competitors,
                    lexusVehicle: model.getStyleInfoFromStyleId(_primaryStyle),
                    primaryModel: _primaryModel,
                    ignoreHistory: evt.memo.ignoreHistory
                };

                console.debug(data.lexusVehicle)


                // if they are doing a lexus series compare, get the styles for the series
                if (evt.memo.comparisonType == 'lexusseries') {
                    _comparisonType = 'lexusseries';
                    model.getLexusStyles(_primaryModel, function(series) {
                        data.seriesstyles = series.styles;
                    });
                }

                // set the step to "viewresults" and run the comparison
                _setStep('viewresults');

                // TODO do some error handling
                var valid = true;
                switch (evt.memo.comparisonType) {
                    case 'competitive':
                        // if there isn't at least one competitor, set valid to false
                        break;

                    case 'lexus2lexus':
                        // if there isn't at least one competitor, set valid to false
                        break;

                    case 'lexusseries':
                        // if the series only has one model, don't show it
                        break;
                }

                if (valid) {
                    document.fire('comparison:run', data);
                }
            });

            // when the user clicks "edit" from the competitor select pane, reset the primary style, send them back to the selection pane
            document.observe('primaryStyle:reset', function(evt) {
                // reset the primary style
                var _historyPrimaryStyle = _primaryStyle;
                _primaryStyle = null;

                // clear out the competitors
                _competitors = [];
                //_defaultCompetitors = [];
                _removeCompetitor(0);

                // set the step
                _setStep('styleselect');

                var _comparisonView = (_comparisonType == 'competitive') ? ('viewCompSelectStyle') : ('viewLexusSelectStyle');

                // show the view
                view.show(_comparisonView);

                if (dhtmlHistory != null) {
                    dhtmlHistory.add(new Date().getTime().toString(), { callback: function() {
                        _setPrimaryStyleId(_historyPrimaryStyle);
                        _setStep('styleselect');
                        view.show(_comparisonView);
                    }
                    });
                }
            });

            // when the user removes the primary vehicle in the lexus to lexus comparison results view
            document.observe('primaryVehicle:removed', _removePrimaryVehicle);

            document.observe('competitor:reset', function(evt) {
                _setStep('competitorselect');
                view.show('viewCompSelectVehicles');
                document.fire('competitor:addDefault', { comparisonType: evt.memo.comparisonType });
            });

            document.observe('competitor:addDefault', function(evt) {
                document.fire('competitor:removed', { index: 0 });
                _competitors = [];
                if (evt.memo.comparisonType == 'competitive') {
                    for (i = 0; i < _defaultCompetitors.length && i < 3; i++) {
                        _addCompetitor(_defaultCompetitors[i], i);
                    }
                }
            });

            // when the user confirms the start over button
            document.observe('startOver:confirmed', function(evt) {
                // reset the competitors
                var _view = null;
                var _stepHistory = null;
                switch (_comparisonType) {
                    case 'competitive':
                        // change the step
                        _setStep('competitorselect');

                        view.show('viewCompSelectVehicles');
                        view.showLoadingWrapper();
                        // for competitive comparisons, reset to default competitors
                        _competitors = [];
                        for (i = 0; i < _defaultCompetitors.length && i < 3; i++) {
                            _addCompetitor(_defaultCompetitors[i], i);
                        }
                        view.hideLoadingWrapper();
                        _view = 'viewCompSelectVehicles';
                        _stepHistory = 'competitorselect';
                        break;

                    case 'lexus2lexus':
                        _comparisonType = 'lexus2lexus';
                        _setStep('competitorselect');
                        _competitors = [];
                        for (i = 2; i >= 0; i--) {
                            _removeCompetitor(i);
                        }
                        view.show('viewLexusSelectVehicles');
                        _view = 'viewLexusSelectVehicles';
                        _stepHistory = 'competitorselect';
                        break;

                    case 'lexusseries':
                        // reset the primary style and competitors
                        _primaryStyle = null;


                        _competitors = [];

                        _step = null;

                        // set the model
                        var _temp = _primaryModel;
                        _primaryModel = null;
                        _setPrimaryModel(_temp);

                        // set the comparison type to initialize the series select
                        _setComparisonType('lexus2lexus');

                        view.show('viewLexusSelectStyle');
                        _view = 'viewLexusSelectStyle';
                        break;

                }

                // hide the loading
                view.hideLoadingWrapper();

                if (dhtmlHistory != null) {
                    if (!evt.memo.ignoreHistory && _view != null) {
                        dhtmlHistory.add(new Date().getTime().toString(), { callback: function() {
                            if (_stepHistory != null) {
                                _setStep(_stepHistory);
                            }
                            view.show(_view);
                        }
                        });
                    }
                }
            });


            document.observe('competitiveComparison:selected', function(evt) {
                // set the comparison type
                _setComparisonType('competitive');

                // change the step
                _setStep('competitorselect');

                // change the primary model
                document.fire('primaryModel:selected', evt.memo);

                // change the primary style
                document.fire('primaryStyle:selected', evt.memo);

                // show the view
                view.show('viewCompSelectVehicles');

                // hide the loading
                view.hideLoadingWrapper();

                if (dhtmlHistory != null) {
                    dhtmlHistory.add(new Date().getTime().toString(), { callback: function() {
                        _setStep('competitorselect');
                        view.show('viewCompSelectVehicles');
                    }
                    });
                }
            });

        });

        return {
            getComparisonType: _getComparisonType,
            setComparisonType: _setComparisonTypeCode,
            getPrimaryModelCode: _getPrimaryModelCode,
            setPrimaryModel: _setPrimaryModel,
            getPrimaryStyleId: _getPrimaryStyleId,
            setPrimaryStyleId: _setPrimaryStyleId,
            getSingleStyle: _getSingleStyle,
            setSingleStyle: _setSingleStyle,
            getStep: _getStep,
            setStep: _setStep
        };
    } ();

    // top level objects need access to one another, but will hide their internals
    /**
    * The model provides data services to the rest of the app. It communicates with 
    * the vehicle data services and does client side request caching.
    */
    var model = function() {
        /*
        * Collection of paths to data resources
        */
        var _paths = {
            allmodels: '/lexusSite/comparator/getAllModels.do',
            yearmakemodels: '/lexusSite/comparator/getModelsForYearMake.do',
            trimsmodelid: '/lexusSite/comparator/getTrimsForModelId.do',
            trimsmodelcode: '/lexusSite/comparator/getTrimsForTmsModelCode.do',
            keycomps: '/lexusSite/comparator/getCompetitorTrims.do',
            compadv: '/lexusSite/comparator/getCompAdvXhtml.do',
            comparisonreport: '/lexusSite/comparator/getComparisonReport.do',
            seriesselect: '/lexusSite/comparator/getComparisonReport.do'
        };

        /*
        * This is a general purpose Ajax cache that stores the entire response object from the server.
        * It ensures we never hit the service twice for the same request.
        */
        var _cache = {};

        /*
        * The style cache is a more specific kind of cache. It's  a hash table that uses the styleId/trimId as a key
        * and stores all information needed to display the vehicle.
        */
        var _styleCache = {};

        /*
        * The _objectCache stores formatted objects saving on
        * expensive iterator operations
        */
        var _objectCache = {};


        function _clearCache() {
            _cache = {};
        }

        /**
        * _loadData is a wrapper for Ajax.Request. It sets several standard options and provides client side
        * request caching.
        * @param {Object} parameters
        * @param {Function} callback
        * @param {Function} create
        */
        function _loadData(url, params) {
            // extend default options with params
            var options = Object.extend({
                evalJSON: 'force',
                method: 'get'
            }, params);

            // create the url
            var _url = url + '?' + $H(options.parameters).toQueryString();

            // check the cache using the url as a key
            if (_cache[_url] !== undefined) {
                options.onCreate();
                options.onSuccess(_cache[_url]);
            }
            else {
                // get the data from the json service
                new Ajax.Request(_url, {
                    method: options.method,
                    evalJSON: options.evalJSON,
                    onSuccess: options.onSuccess.wrap(function(success, transport) {
                        // fire the error if we get the "<error/>"
                        if (transport.responseText.split('</error>').length > 1) {
                            document.fire('loadData:error', { 'error': transport.responseText });
                        }
                        // cache the results in the generalized "ajax" cache
                        _cache[transport.request.url] = transport;

                        // call the rest of the onsuccess functionality
                        success(transport, options);
                    }),
                    onCreate: options.onCreate,
                    onException: function(request, excp) {
                        console.error(excp);
                        document.fire('loadData:error', { 'error': excp });
                    },
                    onFailure: function(transport) {
                        console.error(transport.responseText);
                        document.fire('loadData:error', { 'error': transport.responseText });
                    }
                });

            }

        }

        /*
        * Case specific loaders are essentially wrappers around _loadData. They provide insertion points for
        * custom event firing and other ancillary logic.
        */
        function _loadSeriesSelect(modelCode, callback) {
            // call _loadData
            _loadData(_paths.seriesselect, {
                parameters: {
                    tmsModelCode: modelCode,
                    languageId: 1,
                    formatCode: 'js',
                    reportTypeCode: 'selectseries'

                },
                onSuccess: function(transport) {
                    // store the style information using the styleId as a key for easy access
                    // we add redundant information to the style object so they can be passed
                    // alongside competitor styles
                    var _styles = [];
                    $A(transport.responseJSON).each(function(style, order) {
                        // if the style exists in the style cache, just add order and key features to it
                        if (!_styleCache[style.trimId]) {
                            // this is a shallow copy
                            _styleCache[style.trimId] = Object.clone(style);

                            // add convenience data
                            _styleCache[style.trimId].modelCode = modelCode;
                            _styleCache[style.trimId].modelName = lexusVehicles[modelCode].modelName;
                            _styleCache[style.trimId].resultName = lexusVehicles[modelCode].resultName || lexusVehicles[modelCode].modelName;
                            _styleCache[style.trimId].year = lexusVehicles[modelCode].year;
                            _styleCache[style.trimId].numStyles = transport.responseJSON.length;
                        }

                        // clone the keyfeatures property
                        _styleCache[style.trimId].keyfeatures = Object.clone(style.keyfeatures);

                        // assign the order property
                        _styleCache[style.trimId].order = order;

                        // add a reference to the styleCache entry to "styles" for the callback
                        _styles.push(_styleCache[style.trimId]);
                    });

                    console.debug(_styles)

                    // format the data we'll be passing to event listeners and the callback
                    var data = {
                        modelCode: modelCode,
                        modelName: lexusVehicles[modelCode].modelName,
                        resultName: lexusVehicles[modelCode].resultName || lexusVehicles[modelCode].modelName,
                        year: lexusVehicles[modelCode].year,
                        numStyles: transport.responseJSON.length,
                        styles: _styles
                    };


                    // fire the loaded event, passing in modelCode, modelName and anything else listeners might need to render styles
                    document.fire('lexusSeriesSelect:loaded', data);

                    // if there's a callback, fire it
                    if (callback) {
                        callback(data);
                    }
                },
                onCreate: function() {
                    // fire "loading" event
                    document.fire('lexusSeriesSelect:loading', {
                        modelCode: modelCode
                    });
                }
            });
        }

        /*
        * Case specific loaders are essentially wrappers around _loadData. They provide insertion points for
        * custom event firing and other ancillary logic.
        */
        function _loadLexusStyles(modelCode, callback) {
            // call _loadData
            _loadData(_paths.trimsmodelcode, {
                parameters: {
                    tmsModelCode: modelCode
                },
                onSuccess: function(transport) {
                    // store the style information using the styleId as a key for easy access
                    // we add redundant information to the style object so they can be passed
                    // alongside competitor styles
                    var _styles = [];
                    $A(transport.responseJSON).each(function(style) {
                        // if the style exists in the style cache, just add order and key features to it
                        if (!_styleCache[style.trimId]) {
                            // this is a shallow copy
                            _styleCache[style.trimId] = Object.clone(style);

                            // add convenience data
                            _styleCache[style.trimId].modelCode = modelCode;
                            _styleCache[style.trimId].modelName = lexusVehicles[modelCode].modelName;
                            _styleCache[style.trimId].resultName = lexusVehicles[modelCode].resultName || lexusVehicles[modelCode].modelName;
                            _styleCache[style.trimId].year = lexusVehicles[modelCode].year;
                            _styleCache[style.trimId].numStyles = transport.responseJSON.length;
                        }
                        else {
                            for (var i in style) {
                                _styleCache[style.trimId][i] = style[i];
                            }
                        }

                        // add a reference to the styleCache entry to "styles" for the callback
                        _styles.push(_styleCache[style.trimId]);
                    });


                    // format the data we'll be passing to event listeners and the callback
                    var data = {
                        modelCode: modelCode,
                        modelName: lexusVehicles[modelCode].modelName,
                        resultName: lexusVehicles[modelCode].resultName || lexusVehicles[modelCode].modelName,
                        year: lexusVehicles[modelCode].year,
                        numStyles: transport.responseJSON.length,
                        styles: _styles
                    };

                    // fire the loaded event, passing in modelCode, modelName and anything else listeners might need to render styles
                    document.fire('lexusStyles:loaded', data);

                    // if there's a callback, fire it
                    if (callback) {
                        callback(data);
                    }
                },
                onCreate: function() {
                    // fire "loading" event
                    document.fire('lexusStyles:loading', {
                        modelCode: modelCode
                    });
                }
            });
        }


        function _loadCompetitors(lexusStyleId) {
            // call _loadData
            _loadData(_paths.keycomps, {
                parameters: {
                    trimId: lexusStyleId
                },
                onSuccess: function(transport) {
                    // cash the competitors in the style cache
                    _cache[transport.request.url].responseJSON.each(function(comp) {
                        _styleCache[comp.trimId] = comp;
                    });

                    function _keyCompsLoaded() {
                        // fire the loaded event
                        document.fire('keyComps:loaded', {
                            lexusStyleId: lexusStyleId,
                            competitors: _cache[transport.request.url].responseJSON
                        });
                    }

                    setTimeout(_keyCompsLoaded, 1);
                },
                onCreate: function() {
                    // fire "loading" event
                    document.fire('keyComps:loading', {
                        lexusStyleId: lexusStyleId
                    });
                }
            });
        }


        /**
        * _loadAllModels does just that, loads a relatively large object from the json service containing all the models in all the
        * years for all the makes that Lexus wants to appear in competitor lists. This model data can then be used to
        * load the styles for a particular Make/Year/Model
        */
        function _loadAllModels() {
            // call _loadData
            _loadData(_paths.allmodels, {
                onSuccess: function(transport) {
                    // fire the loaded event
                    document.fire('allmodels:loaded', {
                        makes: _getMakes()
                    });
                },
                onCreate: function() {
                    // fire "loading" event
                    document.fire('allmodels:loading', {});
                }
            });
        }

        /**
        * Gets the styles from service via modelId
        * @param {Object} modelId
        * @param {Function} callback
        */
        function _loadCompetitorStyles(styleInfo, callback) {
            // call _loadData
            _loadData(_paths.trimsmodelid, {
                parameters: {
                    modelId: styleInfo.modelId
                },
                onSuccess: function(transport) {
                    // TODO this expensive little operation to reverse lookup the year is why I need to modify the feed
                    styleInfo.year = _getYearsFromMakeModel(styleInfo.makeName, styleInfo.modelName).find(function(year) {
                        return year.modelId == styleInfo.modelId;
                    }).year;
                    // cache the results for easy lookups
                    _cache[transport.request.url].responseJSON.each(function(style) {
                        // cache by trimId, with all the infor for easy retrieval from the style cache
                        style.makeName = styleInfo.makeName;
                        style.modelName = styleInfo.modelName;
                        style.year = styleInfo.year;
                        _styleCache[style.trimId] = style;
                    });

                    // if there's a callback, fire the callback
                    if (callback) {
                        callback(_cache[transport.request.url].responseJSON);
                    }

                    // fire the loaded event
                    document.fire('competitorStyles:loaded', {
                        styles: _cache[transport.request.url].responseJSON
                    });
                },
                onCreate: function() {
                    // fire "loading" event
                    document.fire('competitorStyles:loading', {});
                }
            });

        }

        function _loadComparisonReport(evt) {
            // some default params
            var params = {
                languageId: 1,
                formatCode: 'xhtml',
                reportTypeCode: evt.memo.comparisonType
            };
            // construct for url paramaters based on event data
            switch (evt.memo.comparisonType) {
                case 'competitive':
                    Object.extend(params, {
                        trimIdCsvList: evt.memo.lexusVehicle.trimId + ',' + evt.memo.competitors.pluck('trimId').join(','),
                        tmsModelCode: evt.memo.lexusVehicle.modelCode
                    });
                    break;

                case 'lexus2lexus':
                    Object.extend(params, {
                        trimIdCsvList: evt.memo.lexusVehicle.trimId + ',' + evt.memo.competitors.pluck('trimId').join(',')
                    });

                    break;


                case 'lexusseries':
                    Object.extend(params, {
                        tmsModelCode: evt.memo.primaryModel
                    });

                    break;
            }


            // call _loadData
            _loadData(_paths.comparisonreport, {
                parameters: params,
                evalJSON: false,
                onSuccess: function(transport) {
                    // fire the loaded event
                    document.fire('comparisonReport:loaded', {
                        lexusVehicle: evt.memo.lexusVehicle,
                        competitors: evt.memo.competitors,
                        seriesstyles: evt.memo.seriesstyles,
                        reportHTML: _cache[transport.request.url].responseText,
                        comparisonType: evt.memo.comparisonType,
                        ignoreHistory: evt.memo.ignoreHistory
                    });
                },
                onCreate: function() {
                    // fire "loading" event	
                    document.fire('comparisonReport:loading', {});
                }
            });
        }


        /**
        * _getLexusStyles is one of the few publically exposed methods in model. It's used by the UI to easily populate drop
        * downs.
        * @param {Object} modelCode the modelCode
        * @param {Function} callback
        */
        function _getLexusStyles(modelCode, callback) {
            _loadLexusStyles(modelCode, callback);
        }

        /**
        * _getStyleInfoFromStyleId checks the cache
        * @param {Object} styleId
        */
        function _getStyleInfoFromStyleId(styleId) {
            return _styleCache[styleId];
        }

        /**
        * Returns all makeNames from service as an array of strings
        * @return {Array}
        */
        function _getMakes() {
            if (!_objectCache.makes) {
                _objectCache.makes = $H(_cache[_paths.allmodels + '?'].responseJSON).collect(function(make) {
                    return make.key;
                });
            }
            return _objectCache.makes;
        }

        /**
        * getModelsFromMakeName accepts a make (the name of a company) and returns an array of models
        * @param {Object} makeName
        * @return {Array}
        */
        function _getModelsFromMakeName(makeName) {
            if (!_objectCache[makeName]) {
                _objectCache[makeName] = $H(_cache[_paths.allmodels + '?'].responseJSON[makeName]).collect(function(model) {
                    return model.key;
                });
            }
            return _objectCache[makeName];
        }

        /**
        *
        * @param {String} makeName
        * @param {String} modelName
        * @return {Array}
        */
        function _getYearsFromMakeModel(makeName, modelName) {
            // return the years as an array of year/model id objects
            var key = makeName + modelName;
            if (!_objectCache[key]) {
                _objectCache[key] = $H(_cache[_paths.allmodels + '?'].responseJSON[makeName][modelName]).collect(function(year) {
                    return {
                        year: year.key,
                        modelId: year.value.modelId
                    };
                });
            }
            return _objectCache[key];
        }

        /**
        * Passes an array of trims to callback parameter
        * @param {Int} modelId
        * @param {Function} callback
        */
        function _getStylesFromModelId(modelId, callback) {
            // return the styles as an array of styleId/Name objects
            if (!_objectCache[modelId]) {
                _loadCompetitorStyles(modelId, callback);
            }
        }

        document.observe('dom:loaded', function() {
            /*
            * Custom Event Observers
            * Observe state changes, request data from the services as needed.
            */

            // when the primary model is set in the state
            document.observe('primaryModel:set', function(evt) {
                // load the styles for that model
                if (state.getStep() != 'viewresults') {
                    _loadSeriesSelect(evt.memo.modelCode);
                }
            });

            // Load all models when the primaryStyle is set
            document.observe('primaryStyle:set', function(evt) {
                _loadAllModels();
            });


            // listening for style selection
            document.observe('primaryStyle:set', function(evt) {
                if (evt.memo.comparisonType == 'lexusseries') {
                    _loadComparisonReport({ comparisonType: 'lexusseries', primaryModel: state.getPrimaryModelCode() });
                } else if (evt.memo.comparisonType != 'lexus2lexus') {
                    _loadCompetitors(evt.memo.lexusStyleId);
                }
            }).observe('primaryStyle:changed', function(evt) {

                if (evt.memo.comparisonType == 'lexusseries') {
                    _loadComparisonReport({ comparisonType: 'lexusseries', primaryModel: state.getPrimaryModelCode() });
                } else if (evt.memo.comparisonType != 'lexus2lexus') {
                    _loadCompetitors(evt.memo.lexusStyleId);
                }
            });

            // here we're simply passing the comparison request on to the loadComparisonReport
            document.observe('comparison:run', _loadComparisonReport);
        });


        // give public access to methods declared in this scope, to give view and state access
        return {
            getStyleInfoFromStyleId: _getStyleInfoFromStyleId,
            getLexusStyles: _getLexusStyles,
            getModelsFromMakeName: _getModelsFromMakeName,
            getYearsFromMakeModel: _getYearsFromMakeModel,
            getStylesFromModelId: _getStylesFromModelId,
            clearCache: _clearCache
        };
    } ();





    /**
    * Initializers and updators for the view (ie the markup/css)
    */
    var view = function() {

        var overlay = function() {
            var _iFrameEl = null, _helperIFrame = null, _helperIFramePath = '', _parentBody, _parentDoc;

            // listen for the overlayIframe loaded event, which will fire when the iframe we insert next loads
            document.observe('overlayIFrame:loaded', function() {
                //alert('iframe initialized')
                // mark the iframe as initialized
                window.parent.frames['overlayIFrame'].initialized = true;
                /*
                * UTILITY METHODS
                * These are pointers to $ and $$ for the overlay iframe and $ for the parent window
                */
                $O = window.parent.frames['overlayIFrame'].$;
                $$O = window.parent.frames['overlayIFrame'].$$;
                $overlayBody = $$O('body')[0];

                // initialize overlays
                _initOverlaySelectLexus();
                _initOverlaySelectVehicle();
                _initStartOverOverlay();
                _initErrorOverlay();


                // NOW the app is fully loaded!

                // when the application is loaded, there's a set of params that we may recieve
                // modelCode {String} modelCode is all lower case, no spaces. Since Lexus.com doesn't have a general Compare landing, this should always be set.
                // comparisonType {String} - Possible options are competitive and lexus2lexus. Parameter is optional, the user may land on the selection page.
                // mode {String} - Used for switching between several display types including the default, lexus hosted one, the dealers page include. 

                // get the url parameters
                var urlParams = window.location.search.toString().toQueryParams();
                document.fire('compare:loaded', { modelCode: urlParams.modelCode, comparisonType: urlParams.type, mode: urlParams.mode });
            });


            // listen for the dom to be loaded, set up "helper" functions that will insert, hide and show the iframe element on the parent page
            document.observe('dom:loaded', function() {
                //alert('dom:loaded event observed in overlay')
                // try to read the parent window's location string. this will generate an exception if the application is being loaded
                // on a third party domain
                try {
                    window.parent.location.toString();
                } catch (excp) {
                    // if an exception is generated, it's a third party domain
                    var thirdPartyDomain = true;
                }

                // if we are denied access to the parent window's location property, the app is being accessed from a third party domain
                if (thirdPartyDomain) {
                    // create the path to the domain's helper iframe
                    _helperIFramePath = 'http://' + window.location.hash.toString().split('domain=')[1] + '/Redirect1_lexusIFrameHelper.do';

                    // create the helper iframe with the "insert" command, it will insert the overlay iframe on load
                    $$('body')[0].insert('<iframe src="' + _helperIFramePath + '?insert" height="1" width="1" style="display:none" id="helperIFrame">');

                    // get a reference to the helper iframe
                    _helperIFrame = $('helperIFrame');


                    // if we have access to the location, this is on a lexus domain and we can directly access the parent dom
                } else {
                    _iFrameEl = window.parent.$('overlayIFrame'); // get a reference to the iFrame element

                    // the parent body
                    _parentBody = window.parent.document.getElementsByTagName('body')[0];
                    _parentDoc = window.parent.document.documentElement;
                }
            });


            /**
            * Loads the modal overlay w/ the show command
            */
            var _show = function() {
                if (_iFrameEl) {
                    // use hardcoded style assignment because this is an element on the parent page
                    _iFrameEl.style.display = 'block';
                    _parentBody.style.overflow = 'hidden';
                    _parentDoc.style.overflow = 'hidden';
                } else {
                    _helperIFrame.src = _helperIFramePath + '?show';
                }
            };

            /**
            * Loads the modal overlay w/ the hide command
            */
            var _hide = function() {
                if (_iFrameEl) {
                    // use hardcoded style assignment because this is an element on the parent page
                    _iFrameEl.style.display = 'none';
                    _parentBody.style.overflow = '';
                    _parentDoc.style.overflow = '';
                } else {
                    _helperIFrame.src = _helperIFramePath + '?hide';
                }
            };

            return {
                show: _show,
                hide: _hide
            };
        } ();

        /**
        * Initialize the view. Called when the dom is loaded.
        */
        document.observe('dom:loaded', function() {
            //alert('dom:loaded event observed in view')
            /*
            * DOM REFERENCES
            */
            $body = $$('body')[0];


            // initialize the mode selection interface			
            _initComparisonModeSelect();

            _initViewSelectStyle();
            // initialize the competitive comparison views
            _initViewCompSelectVehicles();

            // initialize the lexus2lexus comparison views
            _initViewLexusSelectVehicles();

            _initViewResults();

            // When the model is set, give the wrapper a class of the modelCode
            document.observe('primaryModel:set', function(evt) {
                // load the styles for that model
                $('wrapper').removeClassName(evt.memo.prevModelCode);
                $('wrapper').addClassName(evt.memo.modelCode);
            });
        });



        var _initComparisonModeSelect = function() {
            /*
            * DOM REFERENCES
            */
            var viewSelectComparisonType = $('viewSelectComparisonType');

            /*
            * Now, attach event listeners to the element and its children
            */
            // When the user clicks the Start Competitve Comparison button...
            $('btnStartCompetitiveComparison').observe('click', function(evt) {
                evt.stop();
                // show the load wrapper
                showLoadingWrapper();

                // fire the comparison type event
                document.fire("comparisonType:selected", { type: 'competitive', source: 'comparisonSelect' });
            });

            // When the user clicks the Start Lexus-to-Lexus Comparison button...
            $('btnStartLexusComparison').observe('click', function(evt) {
                evt.stop();

                // show the load wrapper
                showLoadingWrapper();

                // fire the comparison type event
                document.fire("comparisonType:selected", { type: 'lexus2lexus', source: 'comparisonSelect' });
            });

        };



        var _initViewSelectStyle = function() {
            /*
            * DOM REFERENCES
            */
            var _viewSelectStyle, _styleList, _modelNameSpan, _numStyles;
            var _comparisonTypeSelected = false, _loaded = false, _singleStyle = false;

            _viewSelectStyle = $('viewSelectStyle');


            // listen for a compare all request
            _viewSelectStyle.down('a.compare_all').observe('click', function(evt) {
                evt.stop();

                document.fire('comparison:requested', { comparisonType: 'lexusseries' });
            });

            _styleList = _viewSelectStyle.down('ul');
            _modelNameSpan = _viewSelectStyle.down('span.primaryModelName');
            _numStyles = _viewSelectStyle.down('span.numStyles');

            // Use event bubbling to capture button clicks
            _styleList.observe('click', function(evt) {
                evt.stop();

                var target = evt.element();
                if (target.tagName == 'A') {
                    // Let the state know that the primary style has been selected
                    state.setComparisonType($body.className == 'viewLexusSelectStyle' ? 'lexus2lexus' : 'competitive');
                    document.fire('primaryStyle:selected', { lexusStyleId: target.name });
                }
            });



            /*
            * TEMPLATES
            */
            var _styleTpl = new Template([
				'<li>',
					'<img src="/images/comparator/lexus_vehicles/#{trimId}_lg.jpg" alt="#{trimName}" height="100" width="200" />',

					'<h4>#{year} #{modelName} #{trimName}</h4>',

					'<a href="#" name="#{trimId}" class="btn btn_select">SELECT</a>',

					'<ul>',
						'<li>#{keyfeatures[0]}</li>',
						'<li>#{keyfeatures[1]}</li>',
						'<li>#{keyfeatures[2]}</li>',
						'<li>#{keyfeatures[3]}</li>',
						'<li>#{keyfeatures[4]}</li>',
					'</ul>',
				'</li>'].join(''));



            /*
            * CUSTOM EVENTS
            */
            document.observe('lexusSeriesSelect:loaded', function(evt) {
                _loaded = true;

                // if there's only one style, select it and return, don't bother updating the div
                if (evt.memo.numStyles == 1) {
                    _singleStyle = evt.memo.styles[0].trimId;
                    state.setSingleStyle(_singleStyle);

                    if (_comparisonTypeSelected) {
                        document.fire('primaryStyle:selected', { lexusStyleId: _singleStyle });
                    }
                } else {
                    state.setSingleStyle(false);
                    // update the view
                    _update(evt.memo.modelName, evt.memo.modelCode, evt.memo.styles);

                    if (_comparisonTypeSelected) {
                        // if the series select report is loaded, hide the loading wrapper
                        //hideLoadingWrapper();
                    }

                }

                hideLoadingWrapper();
            });

            document.observe('comparisonType:set', function(evt) {
                _comparisonTypeSelected = true;

                // if the series select report is loaded, show the view
                if (_singleStyle) {
                    document.fire('primaryStyle:selected', { lexusStyleId: _singleStyle });
                    return;
                }

                // show it
                var _comparisonView = (evt.memo.comparisonType == 'competitive') ? ('viewCompSelectStyle') : ('viewLexusSelectStyle');
                _show(_comparisonView);


                // and hide the loading wrapper
                if (_loaded) {
                    hideLoadingWrapper();
                }
            });



            /*
            * FUNCTIONS
            */
            /**
            * Updates the view: sets the model name and number of styles in the title, populates the trim info
            * @param {Object} modelName
            * @param {Object} styles
            */
            function _update(modelName, modelCode, styles) {
                // Update the model name in the header
                _modelNameSpan.update(modelName);

                // this is somewhat ugly, but it's the only place where we need to convert an integer to english
                _numStyles.update(['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'][styles.length - 1]);

                _styleList.update(styles.map(function(style) {
                    style.modelName = lexusVehicles[modelCode].resultName || modelName;
                    style.year = lexusVehicles[modelCode].year;
                    return _styleTpl.evaluate(style);
                }).join(''));

                _rendered = true;
            }

        };





        var _initViewCompSelectVehicles = function() {
            /*
            * DOM REFERENCES
            */
            var competitorTiles = $$('#viewCompSelectVehicles div.results_tiles div');
            var _competitorSelectErrors = $$('#viewCompSelectVehicles p.error');
            var _imgCompSelectedStyle = $('imgCompSelectedStyle');
            var _CompStyleDescription = $('CompStyleDescription');
            var _compMeta = $$('.comp_meta')[0];

            /*
            * LOCAL CONSTANTS
            */

            var _selectedCompetitor = false;

            /*
            * TEMPLATES
            */
            var competitorTileTpl = new Template([
					'<a href="?action=change&index=#{index}" class="change_vehicle_lnk" title="Select a different competing vehicle">Change</a>',
					'<span style="line-height:0">&nbsp</span>', // empty, short span to correct insane ie 6 display bug
					'<a href="?action=remove&index=#{index}" class="btn btn_close" title="Remove this vehicle">X</a>',
					'<img src="#{imageUrl}" width="120" height="60" alt="#{trimName}" />',
					'<p>#{year} #{makeName} #{displayModelName} #{trimName}</p>'
			].join(''));

            var emptyCompTileTpl = new Template('<span></span><a href="?action=add" class="btn add_new_competitor" title="Add a vehicle to compare">ADD ANOTHER VEHICLE</a>');


            /*
            * BROWSER EVENTS
            */
            // observe the "edit" link click primary model
            $('lnkEditCompStyle').observe('click', function(evt) {
                evt.stop();

                // send the user back to the select style view
                document.fire('primaryStyle:reset', { comparisonType: 'competitive' });
            });

            // observe click interactions with the competitor tiles
            competitorTiles.each(function(tile) {
                tile.observe('click', function(evt) {
                    evt.stop(); // stop default event

                    var el = evt.element();

                    // if the element clicked is an anchor
                    if (el.tagName == 'A') {
                        var params = el.href.toQueryParams();

                        // switch on the action param, index is need when we're removing or
                        // changing a vehicle, but not when we're adding one
                        switch (params.action) {
                            case 'remove':
                                // fire the remove competitor event
                                document.fire('competitor:removed', { index: params.index });
                                break;

                            case 'change':
                                // show the overlay
                                document.fire('selectCompetitor:initiated', { index: params.index });
                                break;

                            case 'add':
                                // show the overlay
                                document.fire('selectCompetitor:initiated', { index: null });
                                break;
                        }
                    }

                });
            });


            // observe the "reset" to pre-selected competitors link
            $('lnkResetCompetitors').observe('click', function(evt) {
                evt.stop();

                // fire the keyComps:reset event
                document.fire('keyComps:reset');
            });



            // observe the "compare selected models" button click
            $('btnCompCompareSelectedModels').observe('click', function(evt) {
                evt.stop();

                // validate that there is at least one selected vehicle

                if (!_selectedCompetitor) {
                    _competitorSelectErrors.invoke('hide');
                    _competitorSelectErrors[1].show();
                    return;
                }

                // fire the compare event
                document.fire('comparison:requested', { comparisonType: 'competitive' });
            });




            /*
            * CUSTOM EVENTS
            */
            // This view will be shown when the primary style is set in the state, but 
            // only if the comparison type is competitive
            document.observe('primaryStyle:set', function(evt) {
                if (state.getComparisonType() == 'competitive') {
                    _show('viewCompSelectVehicles');

                    // hide the loading wrapper
                    hideLoadingWrapper();
                }
            });

            /*
            * When the primary style is selected or changed, populate the header
            */
            document.observe('primaryStyle:selected', function(evt) {
                _updateStyleHeader(evt.memo.lexusStyleId);

                var _comparisonType = state.getComparisonType();
                var _viewName = (_comparisonType == 'competitive' ? 'viewCompSelectVehicles' : 'viewLexusSelectVehicles');
                var _stepHistory = null;
                var _historyPrimaryStyle = null;

                if (!state.getSingleStyle()) {
                    _stepHistory = (_comparisonType == 'competitive' ? 'competitorselect' : null);
                    _historyPrimaryStyle = evt.memo.lexusStyleId;
                }

                view.show(_viewName);
                hideLoadingWrapper();

                if (dhtmlHistory != null) {
                    dhtmlHistory.add(new Date().getTime().toString(), { callback: function() {
                        document.fire('startOver:confirmed', { ignoreHistory: true });
                        document.fire('error:resetSelectErrors');

                        if (_historyPrimaryStyle != null) {
                            state.setPrimaryStyleId(_historyPrimaryStyle);
                        }
                        state.setStep(_stepHistory);
                        view.show(_viewName);
                    }
                    });
                }
            });
            document.observe('primaryStyle:changed', function(evt) {
                _updateStyleHeader(evt.memo.lexusStyleId);
            });

            /*
            * When the selected key competitors are changed, populate the key competitors
            */
            document.observe('competitors:changed', function(evt) {
                if (state.getComparisonType() == 'competitive') {
                    _updateCompetitors(evt.memo.competitors);
                }
            });

            /*
            * When new key competitors are loaded, repopulate the key comps
            */
            document.observe('keyComps:loaded', function(evt) {
                _updateCompetitors(evt.memo.competitors);
            });

            // show the loading overlay
            document.observe('comparisonReport:loading', function(evt) {
                showLoadingWrapper();
            });

            // when a duplicate competitor is added,show an error
            document.observe('error:duplicateCompetitor', function(evt) {
                _competitorSelectErrors.invoke('hide');
                _competitorSelectErrors[2].show();
            });

            document.observe('error:resetSelectErrors', function(evt) {
                _competitorSelectErrors.invoke('hide');
            });

            /*
            * FUNCTIONS
            */
            /**
            * 
            * @param {Object} competitors
            */
            function _updateCompetitors(competitors) {
                // create an error variable to track whether we have one or more selected competitors
                var maxHeight = 0;
                _selectedCompetitor = false;

                // loop through competitors object 3 times
                for (var i = 0; i < 3; i++) {
                    // if there is actually a competitor, use the competitorTileTpl
                    if (competitors[i]) {
                        competitors[i].index = i;
                        competitors[i].displayModelName = competitors[i].resultName || competitors[i].modelName;

                        // evaluate the competitor tile template and update the markup, making the tile active
                        competitorTiles[i].setStyle({ height: '142px' }).update(competitorTileTpl.evaluate(competitors[i])).removeClassName('empty_tile').addClassName('comp_tile');

                        // a competitor exists, hide the error
                        _selectedCompetitor = true;
                        _competitorSelectErrors.invoke('hide');
                    } else {
                        // otherwise, use the empty tile template
                        competitorTiles[i].setStyle({ height: '142px' }).update(emptyCompTileTpl.evaluate({ index: i })).addClassName('empty_tile').removeClassName('comp_tile');
                    }

                    maxHeight = Math.max(competitorTiles[i].getHeight(), maxHeight);
                }

                // make the vehicle tiles the same height
                competitorTiles.invoke('setStyle', { height: maxHeight + 'px' });
                _compMeta.setStyle({ height: maxHeight + 'px' });
            }

            function _updateStyleHeader(styleId) {
                // update the header text
                var styleInfo = model.getStyleInfoFromStyleId(styleId);
                _CompStyleDescription.update(styleInfo.year + " " + styleInfo.resultName + " " + styleInfo.trimName);

                // update the image
                _imgCompSelectedStyle.src = VEHICLE_IMG_ROOT + styleId + '_lg.jpg';
            }

        };

        var _initViewLexusSelectVehicles = function() {
            /*
            * DOM REFERENCES
            */
            var _lexusTiles = $$('#viewLexusSelectVehicles div.results_tiles div');
            var _lnkEditLexusStyle = $('lnkEditLexusStyle');
            var _imgLexusSelectedStyle = $('imgLexusSelectedStyle');
            var _LexusStyleDescription = $('LexusStyleDescription');
            var _selectLexusModel = $('selectLexusModel');
            var _selectLexusStyle = $('selectLexusStyle');
            var _btnAddLexusVehicle = $('btnAddLexusVehicle');
            var _lexusSelectModelStyle = $('lexusSelectModelStyle');
            var _compMeta = $$('.comp_meta')[1];


            var _lexusSelectErrors = $$('#viewLexusSelectVehicles p.error');
            var _lexusSelectError = $$('#viewLexusSelectVehicles p.error')[0];
            var _lexusDuplicateCompetitor = $('lexusDuplicateCompetitor');
            var _lexusRemoveCompetitor = $('lexusRemoveCompetitor');
            var _lexusSelectCompetitor = $('lexusSelectCompetitor');


            /*
            * INITIALIZE MARKUP
            */
            // init the modelSelect with lexus models select and cache a reference to the options. add a "select style" option to the element
            var _modelSelectOptions = _initLexusModelsSelect(_selectLexusModel);



            /*
            * TEMPLATES
            */
            var lexusTileTpl = new Template([
					'<a href="?action=remove&index=#{index}" class="btn btn_close" title="Remove this vehicle">X</a>',
					'<span style="line-height:0">&nbsp</span>', // empty, short span to correct insane ie 6 display bug
					'<a href="?action=remove&index=#{index}" title="Clear" class="link_clear" >Clear</a>',
					'<img src="/images/comparator/lexus_vehicles/#{trimId}_sm.jpg" width="120" height="60"  alt="#{trimName}" />',
					'<p>#{year} #{makeName} #{displayModelName} #{trimName}</p>'
			].join(''));

            var emptyLexusTileTpl = new Template('<p>Make a selection<br /> above</p>');


            /*
            * BROWSER EVENTS
            */
            // observe the "edit" link click primary model
            _lnkEditLexusStyle.observe('click', function(evt) {
                evt.stop();

                // send the user back to the select style view
                document.fire('primaryStyle:reset', { comparisonType: 'lexus2lexus' });
            });

            // When the user selects a model, update the style select
            _selectLexusModel.observe('change', function(evt) {
                // populate the lexus styles
                _initLexusStylesSelect(_selectLexusStyle, evt.element().value);
            });



            // When the user clicks the add button, check that they've selected a model and style 
            // and that there are less then 3 selected vehicles
            _btnAddLexusVehicle.observe('click', function(evt) {
                evt.stop();
                // hide errors
                _lexusSelectErrors.invoke('hide');

                // check that there aren't already 3 lexus vehicles selected
                if (_lexusTiles.all(function(tile) { return tile.className == 'lexus_tile'; })) {
                    _lexusRemoveCompetitor.show();
                    return;
                }

                // since they're adding a vehicle, hide the "select competitor" error
                _lexusSelectCompetitor.hide();

                // validate the form data
                if (!_selectLexusModel.value.empty() && !_selectLexusStyle.value.empty()) {
                    _lexusSelectModelStyle.hide();

                    document.fire('competitor:selected', { value: _selectLexusStyle.value, index: null });

                    // reset the fields
                    _selectLexusModel.selectedIndex = 0;
                    _selectLexusStyle.update('<option value="">Select Style</option>').selectedIndex = 0;
                    _selectLexusStyle.disable();
                } else {
                    _lexusSelectModelStyle.show();
                }

            });


            // observe click interactions with the competitor tiles, in this case only remove is possible
            _lexusTiles.each(function(tile, index) {
                tile.observe('click', function(evt) {
                    evt.stop(); // stop default event

                    var el = evt.element();

                    // if the element clicked is an anchor
                    if (el.tagName == 'A') {
                        // hide the "remove competitor" error
                        _lexusRemoveCompetitor.hide();
                        document.fire('competitor:removed', { index: index });
                    }

                });
            });

            // observe the "compare selected models" button click
            $('btnL2LCompareSelectedModels').observe('click', function(evt) {
                evt.stop();
                // validate that they've selected at least one competitor
                if (_lexusTiles.all(function(tile) { return tile.className == 'select_tile'; })) {
                    _lexusSelectErrors.invoke('hide');
                    _lexusSelectCompetitor.show();
                    return;
                }

                // fire the compare event
                document.fire('comparison:requested', { comparisonType: 'lexus2lexus' });
            });



            // listen for a compare all request
            $$('#viewLexusSelectVehicles a.compare_all')[0].observe('click', function(evt) {
                evt.stop();

                document.fire('comparison:requested', { comparisonType: 'lexusseries' });
            });


            /*
            * CUSTOM EVENTS
            */
            // This view will be shown when the primary style is set in the state, but 
            // only if the comparison type is competitive
            document.observe('primaryStyle:set', function(evt) {
                if (state.getComparisonType() == 'lexus2lexus') {
                    _show('viewLexusSelectVehicles');

                    // hide the loading wrapper
                    hideLoadingWrapper();
                }
            });

            /*
            * When the primary style is selected or changed, populate the header
            */
            document.observe('primaryStyle:selected', function(evt) {
                _updateStyleHeader(evt.memo.lexusStyleId);
            });
            document.observe('primaryStyle:changed', function(evt) {
                _updateStyleHeader(evt.memo.lexusStyleId);
            });

            // show the loading overlay
            document.observe('comparisonReport:loading', function(evt) {
                if (state.getComparisonType() == 'lexus2lexus') {
                    showLoadingWrapper();
                }
            });

            document.observe('competitors:changed', function(evt) {
                if (state.getComparisonType() != 'competitive') {
                    _updateCompetitors(evt.memo.competitors);
                }
            });

            // when a duplicate competitor is added,show the corresponding error
            document.observe('error:duplicateCompetitor', function(evt) {
                _lexusSelectErrors.invoke('hide');
                _lexusDuplicateCompetitor.show();
            });



            /*
            * FUNCTIONS
            */
            /**
            * 
            * @param {Object} competitors
            */
            function _updateCompetitors(competitors) {
                var maxHeight = 0;

                // loop through competitors object 3 times
                for (var i = 0; i < 3; i++) {
                    // if there is actually a competitor, use the lexusTileTpl
                    if (competitors[i]) {
                        competitors[i].index = i;
                        competitors[i].displayModelName = competitors[i].resultName || competitors[i].modelName;
                        // evaluate the competitor tile template and update the markup, making the tile active
                        _lexusTiles[i].update(lexusTileTpl.evaluate(competitors[i])).removeClassName('select_tile').addClassName('lexus_tile');

                        //maxHeight = Math.max(lexusTiles[i].getHeight(),maxHeight);

                    } else {
                        // otherwise, use the empty tile template
                        _lexusTiles[i].update(emptyLexusTileTpl.evaluate({})).addClassName('select_tile').removeClassName('lexus_tile');
                    }
                }


                // make the vehicle tiles the same height
                //lexusTiles.invoke('setStyle', {height: maxHeight + 'px'});
                //_compMeta.setStyle({height: maxHeight + 'px'});


            }

            function _updateStyleHeader(styleId) {
                // update the header text
                var styleInfo = model.getStyleInfoFromStyleId(styleId);
                _LexusStyleDescription.update(styleInfo.year + " " + styleInfo.resultName + " " + styleInfo.trimName);

                // update the image
                _imgLexusSelectedStyle.src = VEHICLE_IMG_ROOT + styleId + '_lg.jpg';
            }
        };



        /**
        * Because of the amount of shared functionality between the two competitive results tab, they are both managed 
        * by this function.
        */
        var _initViewResults = function() {
            /*
            * DOM REFERENCES
            */
            // general
            var _resultsOverviewPrintBtn = $('resultsOverviewPrintBtn');
            var _resultsAdvantagesPrintBtn = $('resultsAdvantagesPrintBtn');
            var _resultsLexusStylesPrintBtn = $('resultsLexusStylesPrintBtn');
            var _resultsVehicles = $$('.results_vehicles');

            // competitive/lexus2lexus overview
            var _comparisonTopicsMenu = $$('div.ov_heading ul')[0];
            var _compTiles = $$('#viewResultsOverview .empty_tile');
            var _lexusTile = $$('#viewResultsOverview .lexus_tile')[0];
            var _overviewTable = $$('#resultsOverviewTableWrapper table')[0];
            var _overviewTableWrapper = $('resultsOverviewTableWrapper');
            var _overviewTableTitle = $$('#viewResultsOverview .table_summary')[0];
            var _lexusTableTitle = $$('#viewResultsOverview .table_wide_summary')[0];
            var _compOvTab = $('compOvTab');
            var _compAdvTab = $('compAdvTab');
            var _compAdvLnk = $('compAdvLnk');
            var _overviewScroller = null;

            // competitive advantages
            var _resOvTab = $('resOvTab');
            var _resAdvTab = $('resAdvTab');
            var _btnBackToOverview = $('btnBackToOverview');
            var _advCompTiles = $$('#viewCompResultsAdvantages .empty_tile');
            var _advLexusTile = $$('#viewCompResultsAdvantages .lexus_tile')[0];
            var _resultsAdvantagesHeaderLexus = $$('#resultsAdvantagesHeader span')[0];
            var _resultsAdvantagesHeaderCompetitor = $$('#resultsAdvantagesHeader span')[1];
            var _advantagesTableWrapper = $('resultsAdvantagesTableWrapper');
            var _advantagesTable = $$('#resultsAdvantagesTableWrapper table')[0];
            var _advantagesTableRows;
            var _advantagesScroller = null;

            // lexusseries
            var _viewLexusResultsAllStyles = $('viewLexusResultsAllStyles');
            var _lexusTilesAllStyles = _viewLexusResultsAllStyles.getElementsBySelector('div.lexus_tile');
            var _allStylesTable = $$('#resultsL2LTableWrapper table')[0];
            var _showAllModelLnk = $('showAllModelLnk');
            var _seriesScroller = null;



            /*
            * LOCAL SETTINGS
            */
            var _reportLoaded = false;
            var _compHeaders = [];
            var _touchAdv, _touchOv;
            var _comparisonType;
            var _view;
            var _numStyles, _stylesShown;
            var _printLnks;



            /*
            * TEMPLATES
            */
            var _lexusTileTpl = new Template([
							'<a href="?action=change&index=#{index}&trimId=#{trimId}" class="change_vehicle_lnk">Change</a>&nbsp;',
							'<a href="?action=remove&index=#{index}" class="btn btn_close">X</a>&nbsp;',
							'<img src="/images/comparator/lexus_vehicles/#{trimId}_sm.jpg" width="120" height="60" alt="#{trimName}" />',
							'<p>#{year} #{displayModelName} #{trimName}</p>',
							'<a href="?action=advantages&index=#{index}" class="btn see_advantages">SEE ADVANTAGES FOR THIS VEHICLE</a>',
							'<a href="/lexusConfigApp/index.jsp?modelName=#{BYLModelName}&modelCode=#{modelId}" class="btn btn_build_#{modelCode}" title="Build Your #{modelName}">Build Your #{modelName}</a>',
							'<a href="/lexus/LexusSearchSubmitWithMap.do?searchType=zipcode&radius=50%2C100%2C250&dealersMapWidth=515&dealersMapHeight=225&dealersMapZoom=9&distanceColumn=yes&pmaOnlyFlag=true&zipCode=#{zip}&action=dealer" class="btn btn_find_dealer" title="Find A Dealer">Find A Dealer</a>',
							'<a href="?action=competitive&modelCode=#{modelCode}&lexusStyleId=#{trimId}" class="btn btn_competitive_comparison" title="Competitive Comparison">Competitive Comparison</a>',
							'<a href="?action=series&modelCode=#{modelCode}" class="btn btn_compare_#{modelCode}_series" title="Compare #{modelCode} Series" style="#{display}">Compare #{modelCode} Series</a>'
				].join(''));

            var _compTileTpl = new Template([
							'<a href="?action=change&index=#{index}&trimId=#{trimId}" class="change_vehicle_lnk">Change</a>&nbsp;',
							'<a href="?action=remove&index=#{index}" class="btn btn_close">X</a>&nbsp;',
							'<img src="#{imageUrl}" width="120" height="60" alt="#{trimName}" />',
							'<p>#{year} #{makeName} #{displayModelName} #{trimName}</p>',
							'<a href="?action=advantages&index=#{index}" class="btn see_advantages">SEE ADVANTAGES FOR THIS VEHICLE</a>'
				].join(''));

            var _emptyTileTpl = new Template([
							'<span></span>',
							'<a href="?action=add" class="btn add_new_competitor">ADD ANOTHER VEHICLE</a>'
				].join(''));

            var _advHeaderTpl = new Template('#{year} #{makeName} #{modelName} #{trimName}&nbsp;');


            /*
            * BROWSER EVENTS
            */
            // When the user clicks on a comparison topic in the menu, show the corresponding table
            _comparisonTopicsMenu.observe('click', function(evt) {
                evt.stop();

                var el = evt.element();

                if (el.tagName == 'A') {
                    _overviewTable.className = el.id;
                    _comparisonTopicsMenu.className = el.id;

                    // touch the overview td
                    if (Prototype.Browser.IE) {
                        _touchOv.style.display = 'none';
                        _touchOv.style.display = 'block';
                    }

                    // set the table title
                    _overviewTableTitle.update(el.innerHTML);
                    _lexusTableTitle.update(el.innerHTML);

                    // refresh the scroller
                    window.setTimeout(function() {
                        _overviewScroller.refresh();
                    });

                    // fire the analytics event
                    document.fire('comparisonTopic:selected', { topic: el.innerHTML, comparisonTypeName: (_comparisonType == 'competitive') ? ('Competitive Compare') : ('L-L Compare') });
                }
            });

            // When the user clicks a link on one of the vehicle tiles, respond by showing the appropriate overlay
            // or removing the appropriate vehicle. 
            _compTiles.invoke('observe', 'click', onClickVehicleTile);
            _advCompTiles.invoke('observe', 'click', onClickVehicleTile);
            _lexusTile.observe('click', onClickVehicleTile);
            _advLexusTile.observe('click', onClickVehicleTile);
            _lexusTilesAllStyles.invoke('observe', 'click', onClickVehicleTile);

            // Ignore clicks from the "overview" tab
            _compOvTab.observe('click', function(evt) { evt.stop(); });

            // Show the comparison advantages view
            _compAdvTab.observe('click', function(evt) {
                evt.stop();

                _show('viewCompResultsAdvantages');

                window.setTimeout(function() {
                    _advantagesScroller.refresh();
                    equalizeHeight(_advCompTiles.concat([_advLexusTile]), '158px');
                }, 0);

                // fire analytics event
                document.fire('advantagesTab:loaded');
            });
            _compAdvLnk.observe('click', function(evt) {
                evt.stop();

                _show('viewCompResultsAdvantages');

                window.setTimeout(function() {
                    _advantagesScroller.refresh();
                    equalizeHeight(_advCompTiles.concat([_advLexusTile]), '158px');
                }, 0);

                // fire analytics event
                document.fire('advantagesTab:loaded');
            });

            // Ignore clicks from the current tabs
            _resAdvTab.observe('click', function(evt) { evt.stop(); });

            // Show the other view
            _resOvTab.observe('click', function(evt) {
                evt.stop();

                window.setTimeout(function() {
                    _overviewScroller.refresh();
                    equalizeHeight(_compTiles.concat([_lexusTile]), '158px');
                }, 10);

                _show('viewResultsOverview');
            });


            _btnBackToOverview.observe('click', function(evt) {
                evt.stop();

                window.setTimeout(function() {
                    _overviewScroller.refresh();
                    equalizeHeight(_compTiles.concat([_lexusTile]), '158px');
                }, 10);

                _show('viewResultsOverview');
            });

            // observe the "show all __ models" click.
            // TODO implement some kind of "initial state" in css so this can be replaced by a class switch
            _showAllModelLnk.observe('click', showAllSeriesTiles);

            function showAllSeriesTiles(evt) {
                if (evt) {
                    evt.stop();
                }

                // reset the number of vehicles
                _stylesShown = _numStyles;
                console.debug('numVehicles: ' + _stylesShown)

                // show the first x vehicles
                _lexusTilesAllStyles.invoke('hide');
                _allStylesTable.getElementsBySelector('td').invoke('hide');
                _allStylesTable.getElementsBySelector('td.first').invoke('show');
                for (var i = 0; i < _numStyles; i++) {
                    _lexusTilesAllStyles[i].show();
                    _allStylesTable.getElementsBySelector('td.col_' + i).invoke('show');
                }


                if (_numStyles == 1) {
                    _resultsVehicles.invoke('removeClassName', 'double');
                    _resultsVehicles.invoke('removeClassName', 'triple');
                    _resultsVehicles.invoke('addClassName', 'single');
                } else if (_numStyles == 2) {
                    _resultsVehicles.invoke('removeClassName', 'single');
                    _resultsVehicles.invoke('addClassName', 'double');
                    _resultsVehicles.invoke('removeClassName', 'triple');
                } else if (_numStyles == 3) {
                    _resultsVehicles.invoke('removeClassName', 'single');
                    _resultsVehicles.invoke('removeClassName', 'double');
                    _resultsVehicles.invoke('addClassName', 'triple');
                } else {
                    _resultsVehicles.invoke('removeClassName', 'single');
                    _resultsVehicles.invoke('removeClassName', 'double');
                    _resultsVehicles.invoke('removeClassName', 'triple');
                }

            }

            // When the print links are click, fire the analytics event
            _resultsOverviewPrintBtn.observe('click', function(evt) {
                document.fire('printLink:clicked', { 'viewName': 'Overview', 'comparisonType': _comparisonType });
            });
            _resultsAdvantagesPrintBtn.observe('click', function(evt) {
                document.fire('printLink:clicked', { 'viewName': 'Advantages', 'comparisonType': _comparisonType });
            });
            _resultsLexusStylesPrintBtn.observe('click', function(evt) {
                document.fire('printLink:clicked', { 'view': 'Series Compare', 'comparisonType': _comparisonType });
            });

            /*
            * CUSTOM EVENTS
            */
            // update the vehicle tiles when the competitive report has been loaded
            document.observe('comparisonReport:loaded', function(evt) {
                _comparisonType = evt.memo.comparisonType;
                var _stepHistory = 'viewresults';
                var _comparisonTypeHistory = null;

                switch (_comparisonType) {
                    case 'competitive':
                        _view = ($body.className == 'viewCompResultsAdvantages') ? ('viewCompResultsAdvantages') : ('viewResultsOverview');
                        //_stepHistory = 'competitorselect';
                        _comparisonTypeHistory = _comparisonType;
                        // update the vehicle tiles
                        _updateCompetitiveTiles(evt.memo.lexusVehicle, evt.memo.competitors);

                        // update the results table
                        var resultsData = evt.memo.reportHTML.split('<tbody id="AdvantageText">');
                        _updateResultsOverviewTable(resultsData[0]);

                        window.setTimeout(function() {
                            if (_overviewScroller === null) {
                                _overviewScroller = scroller(_overviewTableWrapper, {
                                    "innerDivSelector": "table"
                                });
                            } else {
                                _overviewScroller.refresh();
                            }
                        }, 10);

                        // create the headers for this set
                        // update the competitor headers
                        _updateCompHeaders(evt.memo.competitors);

                        // update the results advantages table. 
                        _updateResultsAdvantagesTable('<tbody id="AdvantageText">' + resultsData[1]);

                        window.setTimeout(function() {
                            if (_advantagesScroller === null) {
                                _advantagesScroller = scroller(_advantagesTableWrapper, {
                                    "innerDivSelector": "table"
                                });
                            } else {
                                _advantagesScroller.refresh();
                            }
                        }, 10);

                        // initialize the touch element
                        _touchAdv = $$('#resultsAdvantagesTableWrapper td')[0];
                        _touchOv = $$('#resultsOverviewTableWrapper td')[0];

                        // set the header to the first competitor
                        _resultsAdvantagesHeaderCompetitor.update(_compHeaders[0]);

                        _advCompTiles.each(function(tile) { tile.className = (tile.className == 'empty_tile') ? ('empty_tile') : ('inactive_tile'); });
                        _advCompTiles[0].className = 'comp_tile';
                        window.setTimeout(function() {
                            _advantagesTable.className = "show_1";
                            if (Prototype.Browser.IE) {
                                _touchAdv.style.display = 'none';
                                _touchAdv.style.display = 'block';
                            }

                        }, 0)


                        // write the url to the print buttons
                        // overview
                        if (evt.memo.competitors.length > 1) {
                            // remove the single class
                            _resultsVehicles.invoke('removeClassName', 'single');

                        } else {
                            // add the "single_vehicle" class to the results vehicle
                            _resultsVehicles.invoke('addClassName', 'single');
                        }

                        _resultsOverviewPrintBtn.href = "/lexusSite/comparator/getComparisonReport.do?languageId=1&trimIdCsvList=" + evt.memo.lexusVehicle.trimId + '%2C' + evt.memo.competitors.pluck('trimId').join('%2C') + "&formatCode=pdf&reportTypeCode=competitive";

                        //create the advantages print links
                        _printLnks = evt.memo.competitors.collect(function(comp) {
                            return "/lexusSite/comparator/getComparisonReport.do?languageId=1&trimIdCsvList=" + evt.memo.lexusVehicle.trimId + '%2C' + comp.trimId + "&formatCode=pdf&reportTypeCode=advantages";
                        });
                        _resultsAdvantagesPrintBtn.href = _printLnks[0];

                        // update the lexus advantages headers
                        _resultsAdvantagesHeaderLexus.update(_advHeaderTpl.evaluate(evt.memo.lexusVehicle));

                        // fire analytics event for loading of view
                        document.fire('competitiveComparison:loaded', evt.memo);
                        break;

                    case 'lexus2lexus':
                        _view = 'viewLexusResults';
                        //_stepHistory = 'styleselect';
                        _comparisonTypeHistory = _comparisonType;

                        if (evt.memo.competitors.length > 0) {
                            // remove the single class
                            _resultsVehicles.invoke('removeClassName', 'single');
                        } else {
                            // add the "single_vehicle" class to the results vehicle
                            _resultsVehicles.invoke('addClassName', 'single');
                        }

                        // update the vehicle tiles
                        _updateLexus2LexusTiles(evt.memo.lexusVehicle, evt.memo.competitors);

                        // update the results table
                        _updateResultsOverviewTable(evt.memo.reportHTML);

                        window.setTimeout(function() {
                            if (_overviewScroller === null) {
                                _overviewScroller = scroller(_overviewTableWrapper, {
                                    "innerDivSelector": "table"
                                });
                            } else {
                                _overviewScroller.refresh();
                            }
                        }, 10);

                        // update the print button
                        _resultsOverviewPrintBtn.href = "/lexusSite/comparator/getComparisonReport.do?languageId=1&trimIdCsvList=" + evt.memo.lexusVehicle.trimId + '%2C' + evt.memo.competitors.pluck('trimId').join('%2C') + "&formatCode=pdf&reportTypeCode=lexus2lexus";

                        _touchOv = $$('#resultsOverviewTableWrapper td')[0];

                        // fire the analytics event
                        document.fire('lexus2lexusComparison:loaded', evt.memo);
                        break;

                    case 'lexusseries':
                        _view = 'viewLexusResultsAllStyles';
                        //_stepHistory = 'styleselect';
                        _comparisonTypeHistory = ($body.className == 'viewLexusSelectVehicles') ? 'lexus2lexus' : 'competitive';

                        // get the order from the reportHTML
                        var regexp = /\d{5}(?=<\/td>)/g;
                        var orderArr = evt.memo.reportHTML.match(regexp);

                        _updateLexusSeriesTiles(evt.memo.seriesstyles, orderArr);
                        _numStyles = orderArr.length;
                        console.debug(_numStyles)

                        showAllSeriesTiles();

                        // update the results table
                        _updateResultsAllStylesTable(evt.memo.reportHTML);

                        // update the "show all"
                        _showAllModelLnk.update('Show All ' + evt.memo.seriesstyles[0].modelName + ' Models');

                        window.setTimeout(function() {
                            if (_seriesScroller === null) {
                                _seriesScroller = scroller($('resultsL2LTableWrapper'), {
                                    "innerDivSelector": "table"
                                });
                            } else {
                                _seriesScroller.refresh();
                            }
                        }, 10);
                        _resultsLexusStylesPrintBtn.href = '/lexusSite/comparator/getComparisonReport.do?languageId=1&tmsModelCode=' + evt.memo.seriesstyles[0].modelCode + '&formatCode=pdf&reportTypeCode=lexusseries';

                        document.fire('seriescompare:loaded', evt.memo);
                        break;
                }

                // hide the loading wrapper
                hideLoadingWrapper();

                // show the view
                _show(_view);

                if (dhtmlHistory != null) {
                    if (!evt.memo.ignoreHistory) {
                        dhtmlHistory.add(new Date().getTime().toString(), { callback: function() {
                            if (_stepHistory != null) {
                                state.setStep(_stepHistory);
                            }
                            state.setComparisonType(_comparisonTypeHistory);
                            if (_comparisonTypeHistory == 'lexus2lexus') {
                                document.fire('comparison:requested', { comparisonType: 'lexus2lexus', ignoreHistory: true });
                            }
                            view.show(_view);
                        }
                        });
                    }
                }
            });


            // observe a change on the primary style. we don't look for changes in the primary model because
            // that will always be followed by a change int the primary style
            document.observe('primaryStyle:changed', function(evt) {
                if (evt.memo.step == 'viewresults') {
                    // fire a report request
                    document.fire('comparison:requested', { comparisonType: _comparisonType });
                }
            });

            // observe competitor changes
            document.observe('competitors:changed', function(evt) {
                if (evt.memo.step == 'viewresults') {
                    // fire the compare event
                    document.fire('comparison:requested', { comparisonType: _comparisonType, ignoreHistory: evt.memo.ignoreHistory });
                }
            });



            /*
            * FUNCTIONS
            */
            // tile updators
            function _updateCompetitiveTiles(lexusVehicle, competitors) {
                function _update(tile, index) {
                    var tileStr, className;
                    if (competitors[index]) {
                        competitors[index].index = index + 1;
                        if (competitors[index].makeName == 'Lexus') {
                            competitors[index].BYLModelName = competitors[index].modelName.toUpperCase().replace(' ', '_');
                            competitors[index].displayModelName = competitors[index].resultName || lexusVehicle.modelName;
                            tileStr = _lexusTileTpl.evaluate(competitors[index]);
                            className = 'lexus_tile';
                        } else {
                            tileStr = _compTileTpl.evaluate(competitors[index]);
                            className = 'comp_tile';
                        }
                    } else {
                        tileStr = _emptyTileTpl.evaluate({ index: index });
                        className = 'empty_tile';
                    }

                    tile.update(tileStr);
                    tile.className = className;
                }

                // update the lexusVehicle, if passed
                if (lexusVehicle) {
                    lexusVehicle.index = 0;
                    lexusVehicle.BYLModelName = lexusVehicle.modelName.toUpperCase().replace(' ', '_');
                    lexusVehicle.displayModelName = lexusVehicle.resultName || lexusVehicle.modelName;
                    _lexusTile.update(_lexusTileTpl.evaluate(lexusVehicle));
                    _advLexusTile.update(_lexusTileTpl.evaluate(lexusVehicle));
                }

                _compTiles.each(_update);


                window.setTimeout(function() {
                    equalizeHeight(_compTiles.concat([_lexusTile]), '158px');
                }, 0);

                _advCompTiles.each(_update).each(function(tile, i) {
                    tile.className = (i === 0) ? ('comp_tile') :
														(competitors[i]) ? ('inactive_tile') :
														('empty_tile');
                });

                window.setTimeout(function() {
                    equalizeHeight(_advCompTiles.concat([_advLexusTile]), '158px');
                }, 0);
            }

            /**
            * Update the lexus2lexus results vehicle tiles
            * @param {Object} lexusVehicle
            * @param {Object} competitors
            */
            function _updateLexus2LexusTiles(lexusVehicle, competitors) {
                function _update(tile, index) {
                    var tileStr, className;
                    if (competitors[index]) {
                        competitors[index].index = index + 1;
                        competitors[index].BYLModelName = competitors[index].modelName.toUpperCase().replace(' ', '_');
                        competitors[index].displayModelName = competitors[index].resultName || lexusVehicle.modelName;
                        competitors[index].display = (competitors[index].numStyles == 1) ? ('display:none;') : ('');
                        tileStr = _lexusTileTpl.evaluate(competitors[index]);
                        className = 'lexus_tile';
                    }
                    else {
                        tileStr = _emptyTileTpl.evaluate({
                            index: null
                        });
                        className = 'empty_tile';
                    }

                    tile.update(tileStr);
                    tile.className = className;
                }


                _compTiles.each(_update);

                lexusVehicle.index = 0;
                lexusVehicle.BYLModelName = lexusVehicle.modelName.toUpperCase().replace(' ', '_');
                lexusVehicle.displayModelName = lexusVehicle.resultName || lexusVehicle.modelName;
                lexusVehicle.display = (lexusVehicle.numStyles == 1) ? ('display:none;') : ('');
                _lexusTile.update(_lexusTileTpl.evaluate(lexusVehicle));

                _advLexusTile.update(_lexusTileTpl.evaluate(lexusVehicle));

                window.setTimeout(function() {
                    equalizeHeight(_compTiles.concat([_lexusTile]), '180px');
                }, 0);
            }

            function _updateLexusSeriesTiles(lexusVehicles, orderArr) {

                _lexusTilesAllStyles.each(function(lexusTile, index) {
                    // get the lexus vehicle for this slot
                    var lexusVehicle = lexusVehicles.find(function(vehicle) {
                        return vehicle.trimId == orderArr[index];
                    });


                    if (lexusVehicle) {
                        lexusVehicle.index = index;
                        lexusVehicle.BYLModelName = lexusVehicle.modelName.toUpperCase().replace(' ', '_');
                        lexusVehicle.displayModelName = lexusVehicle.resultName || lexusVehicle.modelName;
                        lexusTile.update(_lexusTileTpl.evaluate(lexusVehicle));
                    } else {
                        lexusTile.className = 'empty_tile';
                        console.debug(lexusVehicle)
                    }
                });
            }


            function _updateCompHeaders(competitors) {
                competitors.each(function(comp, index) {
                    _compHeaders[index] = _advHeaderTpl.evaluate(comp);
                });
            }

            function onClickVehicleTile(evt) {
                evt.stop();
                var el = evt.element();

                // if they've clicked an anchor
                if (el.tagName == 'A') {
                    var params = el.href.toQueryParams();

                    switch (params.action) {
                        case 'add':
                            //if the comparison type is competitive, show the selectCompetitor overlay
                            	if (_comparisonType == 'competitive') 
				{
                                	document.fire('selectCompetitor:initiated', { index: null });
                            	} 
				else 
				{
                                	// they are trying to add a lexus vehicle
                                	document.fire('editLexusStyle:initiated', { view: 'results', index: null, comparisonType: _comparisonType });
                            	}
                            break;

                        case 'series':
                            // set the primary model to the series
                            state.setPrimaryModel(params.modelCode);

                            document.fire('comparison:requested', { comparisonType: 'lexusseries' });
                            break;

                        case 'remove':
                            if (_comparisonType == 'lexusseries') {
                                // we don't want to load new comparison results, just hide the columne they removed
                                // hide the vehicle tile
                                _lexusTilesAllStyles[params.index].hide();

                                // decrement the shown vehicle count
                                _stylesShown--;

                                // remove the class name for the wraping div
                                _resultsVehicles.invoke('removeClassName', 'single');
                                _resultsVehicles.invoke('removeClassName', 'double');
                                _resultsVehicles.invoke('removeClassName', 'triple');

                                // and add the appropriate class name depending on the # of vehicles shown
                                if (_stylesShown == 1) {
                                    _resultsVehicles.invoke('addClassName', 'single');
                                } else if (_stylesShown == 2) {
                                    _resultsVehicles.invoke('addClassName', 'double');
                                } else if (_stylesShown == 3) {
                                    _resultsVehicles.invoke('addClassName', 'triple');
                                }

                                // hide the data column
                                _allStylesTable.getElementsBySelector('td.col_' + params.index).invoke('hide');
                                _allStylesTable.getElementsBySelector('td.col_' + (parseInt(params.index, 10) + 4)).invoke('show');

                                // update the print link

                            } else if (_comparisonType == 'lexus2lexus') {
                                if (params.index == 0) {
                                    document.fire('primaryVehicle:removed');
                                } else {
                                    document.fire('competitor:removed', { index: params.index - 1 });
                                }
                            } else {
                                document.fire('competitor:removed', { index: params.index - 1 });
                            }
                            break;

                        case 'advantages':
                            // this is an advantages button, change the class to comp_tile, the others to "inactive_tile"
                            _advCompTiles.each(function(tile) { tile.className = (tile.className == 'empty_tile') ? ('empty_tile') : ('inactive_tile'); });
                            _advCompTiles[params.index - 1].className = 'comp_tile';

                            // change the caption of the advantages table to describe the current comparison
                            _resultsAdvantagesHeaderCompetitor.update(_compHeaders[params.index - 1]);

                            // change the id of the advantages table to show the correct column
                            var colNum = parseInt(params.index, 10);

                            // render the rows
                            _advantagesTable.className = "show_" + colNum;

                            _renderAdvantagesRows(colNum);

                            // update the scroller
                            window.setTimeout(function() {
                                _advantagesScroller.refresh();
                            }, 10);


                            // change the print button
                            _resultsAdvantagesPrintBtn.href = _printLnks[params.index - 1];

                            // "tickle" the table for ie so it'll update it
                            // for ie6, get a cell that we can "touch" after changing columns
                            if (Prototype.Browser.IE) {
                                _touchAdv.style.display = 'none';
                                _touchAdv.style.display = 'block';
                            } else {
                                _advantagesTable.hide();
                                window.setTimeout(function() {
                                    _advantagesTable.show();
                                }, 10);
                            }
				// fire analytics event (rmistry)
				document.fire('advantages:clicked', evt.memo);
                            break;

			// rmistry: overlay
                        case 'change':
                            	if (_comparisonType == 'competitive' && params.index > 0) 
				{
                                	document.fire('selectCompetitor:initiated', { view: 'results', index: params.index - 1 });
                            	} 
				else 
				{
                                	document.fire('editLexusStyle:initiated', { view: 'results', index: params.index, trimId: params.trimId, comparisonType: _comparisonType, tab: (_view == 'viewResultsOverview') ? ('Overview') : ('Advantages')});
                            	}

                            break;

                        case 'competitive':
                            // select the new model and comparison type
                            document.fire('competitiveComparison:selected', { modelCode: params.modelCode, lexusStyleId: params.lexusStyleId });
                            break;

                        default:
                            // fire an event for the link
                            document.fire('comparisonLink:clicked', {
                                'className': el.className,
                                'comparisonTypeName': (_comparisonType == 'competitive') ? ('Competitive Compare') : ('L-L Compare'),
                                'comparisonType': _comparisonType
                            });

                            // this is a link to another site feature, set the parent window it that url
                            window.parent.location = el.href;
                            break;

                    }

                }
            }



            /**
            * Replaces the contents of the results table with the newly loaded data
            * @param {Object} reportHTML
            */
            function _updateResultsOverviewTable(reportHTML) {
                // update the table with the report data
                _overviewTable.update(reportHTML);
            }

            function _updateResultsAdvantagesTable(reportHTML) {
                _advantagesTable.update(reportHTML);


                // cache the table's rows
                _advantagesTableRows = _advantagesTable.getElementsBySelector('tr');

                _renderAdvantagesRows(1);
            }

            function _updateLexus2LexusTable(reportHTML) {
                _overviewTable.update(reportHTML);
            }

            function _updateResultsAllStylesTable(reportHTML) {
                _allStylesTable.update(reportHTML);
            }

            function _renderAdvantagesRows(column) {
                var className = 'even';
                _advantagesTableRows.each(function(row) {
                    var cell = row.getElementsBySelector('td')[column + 1];
                    if (!cell.innerHTML.empty()) {
                        className = (className == 'odd') ? ('even') : ('odd');
                        row.className = className;
                    } else {
                        row.className = 'empty';
                    }
                });
            }

		return
		{
			view: _view
		};

        };




        /**
        * _initOverlaySelectLexus initializes events and behavior for the overlay allowing users
        * to change the base lexus model and style they're comparing against
        * 
        */
        var _initOverlaySelectLexus = function() {
            /*
            * DOM REFERENCES
            */
            var _closeBtn = $$O('#overlaySelectLexus a.overlay_close')[0];
            var _overlayHeader = $$O('#overlaySelectLexus p.overlay_header span')[0];
            var _compareBtn = $O('btnSelectPrimaryVehicle');
            var _modelSelect = $O('selectPrimaryModel'), _modelSelectOptions;
            var _styleSelect = $O('selectPrimaryStyle'), _styleSelectOptions;
            var _imgSelectedLexus = $O('imgSelectedLexus');
            var _selectLexusModelStyle = $O('selectLexusModelStyle');

            // the model and style when the overlay is shown. we use this to determine whether or not to
            // fire a changed event
            var _initialModel, _initialStyle, _index;

            /*
            * INITIALIZE MARKUP
            */
            // init the modelSelect with lexus models select and cache a reference to the options
            _modelSelectOptions = _initLexusModelsSelect(_modelSelect);



            /*
            * BROWSER EVENTS
            */
            // Hide the overlay when the close button is clicked.
            _closeBtn.observe('click', function(evt) {
                evt.stop();

                // hide the error msg
                _selectLexusModelStyle.hide();

                _hide();
            });



            // Update the style select element when a model is selected.
            _modelSelect.observe('change', function(evt) {
                // lock the style select
                _styleSelect.update('Loading ...').disable();

                // update the styles
                _initLexusStylesSelect(_styleSelect, evt.element().value, _imgSelectedLexus);

            });

            _styleSelect.observe('change', function(evt) {
                // if the style select value isn't empty
                var styleId = evt.element().value;
                if (styleId) {
                    // update the image
                    _imgSelectedLexus.src = VEHICLE_IMG_ROOT + styleId + '_lg.jpg';
                }
            });

            // Update the primary vehicle when the compare button is clicked, then hide the overlay
            _compareBtn.observe('click', function(evt) {
                evt.stop();
                var _valid = !_modelSelect.value.empty() && !_styleSelect.value.empty();
                var _dirty = _modelSelect.value != _initialModel || _styleSelect.value != _initialStyle;

		var tabName = $body.className; 


                // flag an error if either field is empty
                if (!_valid) {
                    _selectLexusModelStyle.show();
                    // otherwise, if the data is "dirty", figure out what to do w/ it
                } else {
                    if (_dirty) {
                        // if index is set and not zero, we are changing a competitor
                        if (_index === 0) {
                            // fire the primary model and primary style selected events
                            var lexusStyleId = _styleSelect.value;
                            document.fire('primaryModel:selected', { modelCode: _modelSelect.value });
                            document.fire('primaryStyle:selected', { lexusStyleId: lexusStyleId });
                        } else if (_index === null) {
                            document.fire('competitor:selected', { index: null, compType: 'lexus', value: _styleSelect.value });
                        } else {
                            document.fire('competitor:selected', { index: _index - 1, compType: 'lexus', value: _styleSelect.value });
                        }
                    }

			// rmistry analytics
			document.fire('compareButtonOverlay:clicked', { comparisonType: state.getComparisonType(), tab:tabName});

                    // hide the overlay
                    _selectLexusModelStyle.hide();
                    _hide();
                }

            });



            /*
            * CUSTOM EVENTS
            */
            // observe the initiation of the change lexus model
            document.observe('editLexusStyle:initiated', _show);

            document.observe('singleStyle:loaded', function(evt) {
                _imgSelectedLexus.src = VEHICLE_IMG_ROOT + evt.memo.styleId + '_lg.jpg';
            });

            /*
            * FUNCTIONS
            */
            /**
            * shows this particular overlay
            */
            function _show(evt) {
                _index = (evt.memo.index !== null) ? (parseInt(evt.memo.index, 10)) : (null);

                // if the index is zero, this is the primary vehicle we are editing
                if (_index === 0) {
                    _initialModel = state.getPrimaryModelCode();
                    _initialStyle = state.getPrimaryStyleId();
                } else if (_index > 0) {
                    // they are changing an existing competing vehicle
                    _initialStyle = evt.memo.trimId;
                    _initialModel = model.getStyleInfoFromStyleId(_initialStyle).modelCode;
                } else {
                    // they are adding a vehicle
                    _initialModel = '';
                    _initialStyle = '';
                }

                // update the overlay
                _update(_initialStyle);

                // show the overlay iframe in the container page
                $O('overlaySelectLexus').show();
                overlay.show();
            }

            /**
            * Utility function for hiding this overlay
            */
            function _hide() {
                // hide the overlay
                $O('overlaySelectLexus').hide();
                overlay.hide();
            }

            /**
            * Utility function for updating this overlay
            */
            function _update(styleId) {
                if (styleId) {
                    // get the current model code and style from the state
                    var styleInfo = model.getStyleInfoFromStyleId(styleId);

                    // update the current selection text
                    _overlayHeader.update(styleInfo.year + ' ' + styleInfo.modelName + ' ' + styleInfo.trimName);

                    // set the model and style selects for that styleId
                    _selectOption(_modelSelect, styleInfo.modelCode);
                    _styleSelect.enable();
                    // update the style select for that model
                    _initLexusStylesSelect(_styleSelect, styleInfo.modelCode);
                    _selectOption(_styleSelect, styleId);

                    // set the src of the image
                    _imgSelectedLexus.src = VEHICLE_IMG_ROOT + styleId + '_lg.jpg';
                } else {
                    // update the current selection text
                    _overlayHeader.update('None');

                    // reset the model and style selects, lock the style select
                    _selectOption(_modelSelect, '');
                    _selectOption(_styleSelect, '');
                    _styleSelect.disable();

                    // set the src of the image
                    _imgSelectedLexus.src = LAYOUT_IMG_ROOT + 'make_selection.gif';
                }

            }
        };


        var _initOverlaySelectVehicle = function() {
            /*
            * DOM REFERENCES
            */
            var _overlaySelectVehicle = $O('overlaySelectVehicle');
            var _selectVehicleSubnavBtns = $$O('#selectVehicleSubnav a.btn');
            var _closeBtn = $$O('#overlaySelectVehicle a.overlay_close')[0];
            var _overlayHeader = $$O('#overlaySelectVehicle .overlay_header span')[0];
            var _btnOtherKeyCompetitors = $O('btnOtherKeyCompetitors');
            var _btnOtherLexusVehicles = $O('btnOtherLexusVehicles');
            var _otherVehiclesErrorMsg = $$O('#addOtherCompetitors p.error')[0];
            var _btnOtherVehicles = $O('btnOtherVehicles');

            var _subOverlays = $$O('#overlaySubWrapper div');
            var _selectVehicleInstructions = $O('selectVehicleInstructions');
            var _addKeyCompetitors = $O('addKeyCompetitors');
            var _addLexusVehicle = $O('addLexusVehicle');
            var _addOtherCompetitors = $O('addOtherCompetitors');

            var _keyComps = $$O('#overlaySelectVehicle table')[0];

            var _lexusVehicleSelectModel = $O('lexusVehicleSelectModel');
            var _lexusVehicleSelectStyle = $O('lexusVehicleSelectStyle'), _styleSelectOptions;
            var _lexusVehicleErrorMsg = $$O('#addLexusVehicle p.error')[0];
            var _btnAddLexusVehicle = $O('btnAddLexusVehicle');

            var _competitorVehicleSelectMake = $O('competitorVehicleSelectMake');
            var _competitorVehicleSelectModel = $O('competitorVehicleSelectModel');
            var _competitorVehicleSelectYear = $O('competitorVehicleSelectYear');
            var _competitorVehicleSelectStyle = $O('competitorVehicleSelectStyle');
            var _btnAddCompetitorVehicle = $O('btnAddCompetitorVehicle');

            var _index = null; // this is the index of the competitor we are adding or replacing

            var _currentCompetitors;


            /*
            * INITIALIZE MARKUP
            */
            // init the modelSelect and cache a reference to its options
            var _modelSelectOptions = _initLexusModelsSelect(_lexusVehicleSelectModel);
            _lexusVehicleSelectModel.selectedIndex = 0;
            _lexusVehicleSelectStyle.disable();



            /*
            * BROWSER EVENTS
            */
            // close button
            _closeBtn.observe('click', function(evt) {
                // stop the click and hide the overlay
                evt.stop();
                _hide();
            });

            // vehicle type buttons
            _btnOtherKeyCompetitors.observe('click', function(evt) {
                evt.stop(evt);

                _selectVehicleSubnavBtns.invoke('removeClassName', 'active');

                _btnOtherKeyCompetitors.addClassName('active');
                // show the "otherKeyComps" view by toggling the body class
                _subOverlays.invoke('hide');
                _addKeyCompetitors.show();
            });

            _btnOtherLexusVehicles.observe('click', function(evt) {
                evt.stop(evt);

                _subOverlays.invoke('hide');
                // update the lexus vehicles select
                // remove the active state from the other buttons
                _selectVehicleSubnavBtns.invoke('removeClassName', 'active');

                _btnOtherLexusVehicles.addClassName('active');
                // show the "otherLexusVehicles" 
                _addLexusVehicle.show();
            });

            _btnOtherVehicles.observe('click', function(evt) {
                evt.stop(evt);

                _selectVehicleSubnavBtns.invoke('removeClassName', 'active');

                _btnOtherVehicles.addClassName('active');
                _subOverlays.invoke('hide');
                // show the "otherKeyComps" 
                _addOtherCompetitors.show();
            });

            /*
            * Key Competitors
            */
            // observe the add button clicks by observing the enclosing table and looking for anchor clicks
            _keyComps.observe('click', function(evt) {
                evt.stop(); // stop the default action
                var el = evt.element();

                // if they've clicked an anchor
                if (el.tagName == 'A') {
                    // fire the competitor selected event
                    document.fire('competitor:selected', { index: _index, compType: 'key', value: el.name });

			// rmistry -- fire analytics event
			document.fire('addButtonOverlay:clicked', { step: state.getStep() });

                    // then hide the overlay
                    _hide();
                }
            });

            /*
            * Other competitors
            */
            // make select change
            _competitorVehicleSelectMake.observe('change', function(evt) {
                // if a make is selected
                var makeName = evt.element().value;

                // if the makeName isn't empty, go get the models for that make and populate the model select
                if (!makeName.empty()) {
                    _competitorVehicleSelectModel.update('<option value="">Select</option>' + model.getModelsFromMakeName(makeName).collect(function(model) {
                        return '<option value="' + model + '">' + model + '</option>';
                    }).join('')).enable();
                } else {
                    // otherwise, clear and disable the model select
                    _competitorVehicleSelectModel.update('<option value="">Select</option>').disable();
                }

                // clear and disable the year and style selects
                _competitorVehicleSelectYear.update('<option value="">Select</option>').disable();
                _competitorVehicleSelectStyle.update('<option value="">Select</option>').disable();
            });

            // model select change
            _competitorVehicleSelectModel.observe('change', function(evt) {
                // if a model is selected
                var modelName = evt.element().value;
                var makeName = _competitorVehicleSelectMake.value;

                if (!modelName.empty()) {
                    // get the years for this model, populate the year select and enable it
                    _competitorVehicleSelectYear.update('<option value="">Select</option>' + model.getYearsFromMakeModel(makeName, modelName).collect(function(year) {
                        return '<option value="' + year.modelId + '">' + year.year + '</option>';
                    }).join('')).enable();
                }
            });

            // year select change
            _competitorVehicleSelectYear.observe('change', function(evt) {
                // update the year select to say "loading"
                _competitorVehicleSelectStyle.update('<option value="">Loading ...</option>');

                // wrap up al the competitor stats into an object so the cache can give it back to us as a fully 
                // filled out trim//TODO Modify the trim so it returns that data
                var style = {
                    makeName: _competitorVehicleSelectMake.value,
                    modelName: _competitorVehicleSelectModel.value,
                    modelId: _competitorVehicleSelectYear.value
                };

                // get the styles for this model year
                model.getStylesFromModelId(style, function(styles) {
                    // update the competitor style select
                    _competitorVehicleSelectStyle.update('<option value="">Select</option>' + styles.collect(function(style) {
                        return '<option value="' + style.trimId + '">' + style.trimName + '</option>';
                    }).join('')).enable();
                });
            });

            // add button
            _btnAddCompetitorVehicle.observe('click', function(evt) {
                evt.stop(evt);

                // validate the data
                if (!_competitorVehicleSelectMake.value.empty() && !_competitorVehicleSelectModel.value.empty() && !_competitorVehicleSelectStyle.value.empty() && !_competitorVehicleSelectYear.value.empty()) {
                    // hide the overlay
                    _hide();

                    // add the competitor vehicle
                    document.fire('competitor:selected', { index: _index, compType: 'other', value: _competitorVehicleSelectStyle.value });
		    
			// rmistry -- fire analytics event
			document.fire('addButtonOverlay:clicked', { step: state.getStep() });

                    // hide the error msg
                    _otherVehiclesErrorMsg.hide();

                    // reset the form
                    _competitorVehicleSelectMake.selectedIndex = 0;
                    _competitorVehicleSelectModel.selectedIndex = 0;
                    _competitorVehicleSelectModel.disable();
                    _competitorVehicleSelectStyle.selectedIndex = 0;
                    _competitorVehicleSelectStyle.disable();
                    _competitorVehicleSelectYear.selectedIndex = 0;
                    _competitorVehicleSelectYear.disable();

                } else {
                    // show the error
                    _otherVehiclesErrorMsg.show();
                }
            });

            /*
            * Other Lexus
            */
            // model select change
            _lexusVehicleSelectModel.observe('change', function(evt) {
                // populate the 
                _initLexusStylesSelect(_lexusVehicleSelectStyle, evt.element().value);
                _lexusVehicleSelectStyle.enable();
            });

            // add button
            _btnAddLexusVehicle.observe('click', function(evt) {
                evt.stop();

                // validate the data
                if (!_lexusVehicleSelectStyle.value.empty()) {
                    // hide the overlay
                    // TODO consolidate all the overlay hide functions
                    _hide();

                    // add the lexus vehicle
                    document.fire('competitor:selected', { index: _index, compType: 'lexus', value: _lexusVehicleSelectStyle.value });

			// rmistry -- fire analytics event
			document.fire('addButtonOverlay:clicked', { step: state.getStep() });

                    _lexusVehicleErrorMsg.hide();

                    // reset the form
                    _lexusVehicleSelectModel.selectedIndex = 0;
                    _lexusVehicleSelectStyle.selectedIndex = 0;
                    _lexusVehicleSelectStyle.disable();

                } else {
                    _lexusVehicleErrorMsg.show();
                }
            });



            /*
            * CUSTOM EVENTS
            */
            // listen for key competitors being loaded
            document.observe('competitors:changed', function(evt) {
                // update the key comps table
                // this is an expensive operation because you have to filter the key competitors
                _updateKeyCompsTable(evt.memo.competitors, evt.memo.defaultCompetitors);
                _currentCompetitors = evt.memo.competitors;
            });

            // listen for all models being loaded and populate the select
            document.observe('allmodels:loaded', function(evt) {
                // populate the makes and enable the select
                _competitorVehicleSelectMake.update('<option value="">Select</option>' + evt.memo.makes.collect(function(make) {
                    return '<option value="' + make + '">' + make + '</option>';
                }).join('')).enable();
            });

            // When the user wants to add or change a competitor, show this overlay
            document.observe('selectCompetitor:initiated', _show);

            /*
            * TEMPLATES
            */
            var otherKeyCompsTpl = new Template([
				'<tr>',
					'<td><a href="#" class="btn add_light" name="#{trimId}" title="Add a vehicle">ADD</a></td>',
					'<td>#{year} #{makeName} #{modelName} #{trimName}</td>',
				'</tr>'
			].join(''));

            var _headerTpl = new Template('#{year} #{makeName} #{modelName} #{trimName}');


            /*
            * FUNCTIONS
            */
            function _show(evt) {
                // set the index of the slot we'll be changing
                _index = evt.memo.index;

                // set the current vehicle in the header
                var comp = _currentCompetitors[_index] || {};
                _overlayHeader.update(_headerTpl.evaluate(comp));

                // show the overlay iframe in the container page
                $overlayBody.className = 'selectVehicleInstructions';
                _overlaySelectVehicle.show();
                _selectVehicleInstructions.show();
                overlay.show();
            }

            function _hide() {
                // hide the overlays
                _subOverlays.invoke('hide');
                _overlaySelectVehicle.hide();
                _selectVehicleSubnavBtns.invoke('removeClassName', 'active');

                // hide the iframe
                overlay.hide();
            }

            /**
            * Updates the "other key competitors" list in the Select Vehicle overlay
            * @param {Array} competitors
            * @param {Array} defaultCompetitors
            */
            function _updateKeyCompsTable(competitors, defaultCompetitors) {
                // change the key competitors


                // TODO: profile this code for optimization, especially for IE6
                var filteredCompetitors = defaultCompetitors.reject(function(defaultComp) {
                    return competitors.any(function(comp) {
                        return (defaultComp.trimId == comp.trimId);
                    });
                });

                if (filteredCompetitors.length > 0) {
                    _keyComps.update(filteredCompetitors.collect(function(competitor, i) {
                        competitor.index = i;
                        return otherKeyCompsTpl.evaluate(competitor);
                    }).join(''));

                } else {
                    _keyComps.update('<strong>All key competitors are already selected.</strong>');
                }

            }


        };

        /*
        * The start over confirmation overlay is shown whenever a user clicks on one of the start over buttons.
        * We put the event listeners for it in here so it's all in one place.
        */
        var _initStartOverOverlay = function() {
            var _overlayStartOver = $O('overlayStartOver');
            var _viewName;

            /*
            * BROWSER EVENTS
            */
            // When a stat over button is clicked, show the confirmation dialog
            $$('a.start_over').invoke('observe', 'click', function(evt) {
                evt.stop();

                _viewName = {
                    "viewResultsOverview": "CC Overview Start Over ",
                    "viewCompResultsAdvantages": "CC Advantages Start Over ",
                    "viewLexusResults": "LL Model Compare Start Over ",
                    "viewLexusResultsAllStyles": "LL Series Compare Start Over "
}[$body.className];

			// rmistry
			document.fire('startOver:loaded', { comparisonType: state.getComparisonType() });

                    // show the overlay
                    $overlayBody.className = 'overlayStartOver';
                    $O('overlayStartOver').show();
                    overlay.show();
	
                });


                $$O('#overlayStartOver a.overlay_close')[0].observe('click', function(evt) {
                    evt.stop();

                    // hide the overlay
                    _overlayStartOver.hide();
                    overlay.hide();

                    // fire the start over cancel
                    document.fire('startOver:cancel', { viewName: _viewName + "Cancel" });
                });

                $O('btnConfirmStartOver').observe('click', function(evt) {
                    evt.stop();

                    // fire the "reset" event
                    document.fire('startOver:confirmed', { comparisonType: state.getComparisonType(), viewName: _viewName + "Continue" });

                    // hide the overlay
                    _overlayStartOver.hide();
                    overlay.hide();
                });
            };
            /*
            * The start over confirmation overlay is shown whenever a user clicks on one of the start over buttons.
            * We put the event listeners for it in here so it's all in one place.
            */
            var _initErrorOverlay = function() {
                var _overlayError = $O('overlayError');
                var _errorMsg = $$O('#overlayError p.error')[0];

                /*
                * BROWSER EVENTS
                */
                // When a load data error is fired, show the overlay error
                document.observe('loadData:error', function(evt) {

                    _errorMsg.update(evt.memo.error);

                    // show the overlay
                    $overlayBody.className = 'overlayError';
                    $O('overlayError').show();
                    overlay.show();

                    view.hideLoadingWrapper();
                });

                $$O('#overlayError a.overlay_close')[0].observe('click', function(evt) {
                    evt.stop();

                    // send them to the landing page for this model
                    _overlayError.hide();
                    overlay.hide();

                });
            };



            /*
            * Utility methods that populate the several instances of Lexus model and 
            * style selects
            */
            // set up a cache for generated style markup
            var _stylesMarkup = {};

            /**
            * Initializes a lexus model select. modelEl is a reference to your select element. Returns an array of 
            * options from the newly populated element.
            * @param {DOMElement} modelEl
            * @return {NODEList}
            */
            function _initLexusModelsSelect(modelEl) {
                // populate the model drop down by turning the modelCodeToModelName object into a string of select options
                // TODO cache this string somewhere so we're not making it everytime with such a gnarly loop
                modelEl.update('<option value="" selected>Select Model</option>' + $H(lexusVehicles).collect(function(model) { return '<option value="' + model.key + '" >' + model.value.modelName + '</option>'; }).join(''));


                return modelEl.childElements();
            }

            /**
            * A setter for select elements
            * @param {Object} optionEls
            * @param {Object} value
            */
            function _selectOption(selectEl, value) {
                var optionEls = selectEl.getElementsBySelector('option');

                optionEls.each(function(option, i) {
                    if (option.value == value) {
                        selectEl.selectedIndex = i;
                    }
                });

            }

            /**
            * Update the style select element
            * @param {Object} modelCode
            * @param {Object} selectEl
            */
            function _initLexusStylesSelect(selectEl, modelCode) {
                if (!modelCode) {
                    return;
                }
                // check the styles cache,
                if (_stylesMarkup[modelCode]) {
                    // if the markup is there render it
                    selectEl.update(_stylesMarkup[modelCode]);

                    // unlock the style select
                    selectEl.enable();
                } else {
                    // hit the model for style data, pass it a callback to update 
                    model.getLexusStyles(modelCode, function(data) {
                        // if there is only one style for this model
                        if (data.styles.length == 1) {
                            selectEl.update('<option value="' + data.styles[0].trimId + '" selected="selected">' + data.styles[0].trimName + '</option>');
                            document.fire('singleStyle:loaded', { styleId: data.styles[0].trimId });
                        } else {
                            // update the style select element
                            selectEl.update('<option value="" selected>Select Style</option>' + data.styles.collect(function(style) { return '<option value="' + style.trimId + '" >' + style.trimName + '</option>'; }).join(''));
                        }

                        // unlock the style select
                        selectEl.enable();
                    });
                }

                return selectEl.childElements();
            }

            function showLoadingWrapper() {
                $('wrapperLoading').addClassName('loading');
            }

            function hideLoadingWrapper() {
                $('wrapperLoading').removeClassName('loading');
            }



            /**
            * Shows a view by setting the class of the body.
            * @param {String} viewId
            */
            function _show(viewId) {
                $body.className = viewId;
            }

            function scroller(divEl, options) {
                // overload the divEl with prototype
                divEl = $(divEl);
                options = options || {};

                // extend default options
                var settings = Object.extend({
                    "scrollerClass": "scroller",
                    "handleClass": "scroller_handle",
                    "handleTopClass": "scroller_handle_top",
                    "handleBottomClass": "scroller_handle_bottom",
                    "topClass": "scroller_top",
                    "bottomClass": "scroller_bottom",
                    "activatedClass": "activated",
                    "innerDivSelector": '.content_inner',
                    "minHandleHeight": '10',
                    "template": '<div class="#{scrollerClass}"> <div  class="#{topClass}"></div> <div  class="#{handleClass}"><div class="#{handleTopClass}"></div><div class="#{handleBottomClass}"></div></div> <div class="#{bottomClass}"></div> </div>'

                }, options);
                var scrollerTemplate = new Template(settings.template);
                var scrollerMarkup = scrollerTemplate.evaluate(settings);


                // change the div's overflow to hidden
                divEl.addClassName(settings.activatedClass);

                // insert the markup for the scrollbar
                if (!divEl.getElementsBySelector('.' + settings.scrollerClass).size() > 0) {
                    divEl.insert(scrollerMarkup, 'top');
                }

                // get a reference to the inner div
                var innerDiv = divEl.getElementsBySelector(settings.innerDivSelector)[0];

                // get a reference to the scroller and it's child elements
                var scrollerEl = divEl.down('.' + settings.scrollerClass);
                var scrollerTop = divEl.down('.' + settings.topClass);
                var scrollerBottom = divEl.down('.' + settings.bottomClass);
                var scrollerHandle = divEl.down('.' + settings.handleClass);

                /*
                * initialize positional type stuff here
                */
                var contentHeight = innerDiv.getHeight();
                var scrollbarHeight = scrollerEl.getHeight() - scrollerTop.getHeight() - scrollerBottom.getHeight();
                var scrollHandleHeight = Math.max(((scrollbarHeight / contentHeight) * scrollbarHeight), settings.minHandleHeight);
                var scrollRatio = contentHeight / scrollerEl.getHeight();

                if (contentHeight < scrollbarHeight) {
                    scrollerHandle.hide();
                } else {
                    scrollerHandle.show();
                }

                if (scrollHandleHeight) {
                    scrollerHandle.setStyle({ 'height': scrollHandleHeight + 'px' });
                }

                /* drag/drop code */
                // initialize dragging to false
                // start a window mousemove event to keep track of the cursor
                var _mouse = { x: null, y: null };
                document.observe('mousemove', function(evt) {
                    _mouse.x = evt.pointerX();
                    _mouse.y = evt.pointerY();

                    if (_dragging) {
                        onDrag();
                    }
                });

                var _dragging = false, _startY;
                var _scrolling = false, _dir;

                function startDrag() {
                    _dragging = true;
                    _startY = _mouse.y;
                }


                var _count = 0;
                // the top bound of the scrollbar is the offset of the scrollbar plus the height of the top arrow button
                var _scrollBarBounds = {
                    top: scrollerTop.getHeight(),
                    bottom: scrollerBottom.offsetTop - scrollerHandle.getHeight()
                };

                var _contentBounds = {
                    top: -(contentHeight - divEl.getHeight()),
                    bottom: 0
                };
                function onDrag() {
                    // calculate the difference between where the mouse started and ended 
                    var diff = _mouse.y - _startY;

                    _scroll(diff);

                    _startY = _mouse.y;
                }

                function _scroll(diff) {
                    // apply the bounded top to the scrollHandle
                    scrollerHandle.setStyle({ 'top': Math.min(Math.max(_scrollBarBounds.top, scrollerHandle.offsetTop + diff), _scrollBarBounds.bottom) + 'px' });

                    // move the content
                    var newTop = (parseInt(innerDiv.getStyle('top').split('px')[0], 10) - (diff * scrollRatio));
                    newTop = Math.min(Math.max(newTop, _contentBounds.top), _contentBounds.bottom);
                    innerDiv.setStyle({ 'top': newTop + 'px' });
                }

                function _onScroll() {
                    _scroll(_dir * 10);
                }

                function endDrag() {
                    _dragging = false;
                }

                function _startScroll(dir) {
                    _scrolling = true;
                    _dir = dir;

                    new PeriodicalExecuter(function(pe) {
                        if (!_scrolling) {
                            pe.stop();
                        }

                        _onScroll();
                    }, 0.1);
                }

                function _endScroll() {
                    _scrolling = false;
                }

                function _refresh() {
                    // reset the base paramaters
                    contentHeight = innerDiv.getHeight();
                    scrollbarHeight = scrollerEl.getHeight() - scrollerTop.getHeight() - scrollerBottom.getHeight();

                    if (contentHeight <= scrollbarHeight) {
                        scrollerHandle.hide();
                    } else {
                        scrollerHandle.show();
                        scrollHandleHeight = Math.max(((scrollbarHeight / contentHeight) * scrollbarHeight), settings.minHandleHeight);
                        scrollerHandle.setStyle({ 'height': scrollHandleHeight + 'px' });
                    }

                    scrollRatio = contentHeight / scrollbarHeight;

                    _scrollBarBounds = {
                        top: scrollerTop.getHeight(),
                        bottom: scrollerBottom.offsetTop - scrollerHandle.getHeight()
                    };

                    _contentBounds = {
                        top: -(contentHeight - divEl.getHeight()),
                        bottom: 0
                    };

                    // move the content and scroll bar to the top
                    innerDiv.setStyle({ 'top': '0px' });
                    scrollerHandle.setStyle({ 'top': _scrollBarBounds.top + 'px' });
                }


                // 
                scrollerTop.observe('click', function(evt) {
                    _scroll(-10);
                }).observe('mousedown', function(evt) { _startScroll(-1); });

                scrollerBottom.observe('click', function(evt) {
                    _scroll(10);
                }).observe('mousedown', function(evt) { _startScroll(1); });

                scrollerEl.observe('mousedown', function(evt) {
                    evt.stop();
                    var target = evt.element();

                    // if it's the scrollhandle, start the drag
                    if (target == scrollerHandle || target.up() == scrollerHandle) {
                        startDrag();
                    }
                });

                function onWheel(evt) {
                    evt.stop();
                    var delta = 0;
                    if (evt.wheelDelta) {
                        delta = evt.wheelDelta / 120;
                        if (window.opera) delta = -delta;
                    } else if (evt.detail) { delta = -evt.detail / 3; }
                    delta = Math.round(delta); //Safari Round

                    _scroll(-delta * 10)
                }
                Event.observe(divEl, "mousewheel", onWheel, false);
                Event.observe(divEl, "DOMMouseScroll", onWheel, false); // Firefox

                document.observe('mouseup', function(evt) {
                    evt.stop();
                    // end the drag, if any
                    endDrag();
                    _endScroll();
                });

                return {
                    refresh: _refresh
                };
            }



            /**
            * Takes a collection of elements and makes them all the same height.
            * 
            * @param {Element Collection} elements
            * @param {Int} minHeight
            */
            function equalizeHeight(elements, minHeight) {
                // make sure the collection of elements is an array
                elements = $A(elements);

                // loop through the elements and get the max height
                var maxHeight = elements.invoke('setStyle', { 'height': (minHeight.length > 0) ? (minHeight) : ('auto') }).max(function(el) {
                    return el.getHeight();
                });

                // set the maxHeight on each element
                elements.invoke('setStyle', { 'height': maxHeight + 'px' });
            }

            return {
                show: _show,
                hideLoadingWrapper: hideLoadingWrapper,
                showLoadingWrapper: showLoadingWrapper
            };

        } ();


	
        // analytics is abstracted to this section
        var analytics = function() 
	{
		var modelName;
		var modelYear;
		var trimName;
		var tabName;

            	// load listeners when dom:ready fires, before anything else has fired.
            	document.observe('dom:loaded', function() 
		{
                	// Comparison Type Set
                	document.observe('comparisonType:set', function(evt) 
			{
				modelName = getOmnitureModelName(lexusVehicles[state.getPrimaryModelCode()].modelName);
				modelYear = lexusVehicles[state.getPrimaryModelCode()].year;

                    		if (evt.memo.comparisonType == 'competitive') 
				{
					if(!state.getSingleStyle())
					{
						fireTag(2187.1,{'<model_name>':modelName,'<model_year>':modelYear});
					}
                    		} 
				else if (evt.memo.comparisonType == 'lexus2lexus') 
				{
					if(!state.getSingleStyle())
					{
						fireTag(2190.1,{'<model_name>':modelName,'<model_year>':modelYear});
					}
                    		}
                	});

                	// capture other "page load" events by listening for step set  primaryStyle:selected
			document.observe('primaryStyle:set', function(evt) 
			{
				modelName = getOmnitureModelName(lexusVehicles[state.getPrimaryModelCode()].modelName);
				modelYear = lexusVehicles[state.getPrimaryModelCode()].year;
				trimName = model.getStyleInfoFromStyleId(state.getPrimaryStyleId()).trimName;

             			switch (evt.memo.step) 
				{
                        		// on page load of select vehicles for comparison step                                                                                               
                        		case 'competitorselect':
                            		if (evt.memo.comparisonType == 'competitive') 
			    		{
						fireTag(2188.1,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName});
                            		}
                            		else if (evt.memo.comparisonType == 'lexus2lexus') 
					{
						fireTag(2191.1,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName});
                            		}
                            		break;
                    		}
                	});

                	// on page load of competitive compare summary overview tab
                	document.observe('competitiveComparison:loaded', function(evt) 
			{
				fireTag(2189.1,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<compare_make_1>':evt.memo.competitors[0].makeName,'<compare_model_1>':evt.memo.competitors[0].modelName,'<compare_make_2>':evt.memo.competitors[1].makeName, '<compare_model_2>':evt.memo.competitors[1].modelName});
                	});

                	// on page load of each competitive comparison topic
                	document.observe('comparisonTopic:selected', function(evt) 
			{
				if (evt.memo.comparisonTypeName == 'Competitive Compare') 
				{
					fireTag('2189.10',{'<model_name>':modelName,'<topic_name>':evt.memo.topic.replace(/\&amp;/g,'&')});
				}
				else if (evt.memo.comparisonTypeName == 'L-L Compare') 
				{
					fireTag('2192.10',{'<model_name>':modelName,'<topic_name>':evt.memo.topic.replace(/\&amp;/g,'&')});
				}
                	});

                	document.observe('comparisonLink:clicked', function(evt) 
			{
                    		if (evt.memo.className.split('btn_find_dealer').length > 1) 
				{
					if (evt.memo.comparisonType == 'competitive') 
					{	                    		
						fireTag(2189.6,{'<model_name>':modelName,'<tab_name>':($body.className == 'viewResultsOverview') ? ('Overview') : ('Advantages')});
					}
					else if (evt.memo.comparisonType == 'lexus2lexus') 
					{
						fireTag(2192.7,{'<model_name>':modelName,'<tab_name>':'Results'});
					}
					else
					{
						fireTag(2192.7,{'<model_name>':modelName,'<tab_name>':'Series Compare'});
					}
				} 
				else 
				{                     
					if (evt.memo.comparisonType == 'competitive') 
					{	                    		
						fireTag(2189.5,{'<model_name>':modelName,'<tab_name>':($body.className == 'viewResultsOverview') ? ('Overview') : ('Advantages')});
					}
					else if (evt.memo.comparisonType == 'lexus2lexus') 
					{
						fireTag(2192.6,{'<model_name>':modelName,'<tab_name>':'Results'});
					}
					else 
					{
						fireTag(2192.6,{'<model_name>':modelName,'<tab_name>':'Series Compare'});
					}
				}
                	});

                	// Print Links (onClick)
                	document.observe('printLink:clicked', function(evt) 
			{
				if (evt.memo.comparisonType == 'competitive') 
				{
					fireTag(2189.9,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':($body.className == 'viewResultsOverview') ? ('Overview') : ('Advantages')});
				}
				else if (evt.memo.comparisonType == 'lexus2lexus') 
				{
					fireTag(2192.5,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Results'});
				}
				else
				{
					fireTag(2192.5,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Series Compare'});
				}
                	});

                	// Competitive Comparison - Advantages tab
                	document.observe('advantagesTab:loaded', function(evt) 
			{
				fireTag(2189.2,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName});
                	});

                	// Competitive Comparison - Advantages button
                	document.observe('advantages:clicked', function(evt) 
			{
				fireTag(2189.11,{'<model_name>':modelName});
                	});

                	// on page load of Lexus compare summary overview tab
                	document.observe('lexus2lexusComparison:loaded', function(evt) 
			{
				fireTag(2192.1,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName});
                	});

                	// on page load of series compare
                	document.observe('seriescompare:loaded', function(evt) 
			{
				modelName = getOmnitureModelName(lexusVehicles[state.getPrimaryModelCode()].modelName);
				modelYear = lexusVehicles[state.getPrimaryModelCode()].year;

				fireTag(2192.2,{'<model_name>':modelName,'<model_year>':modelYear});				
                	});

			document.observe('selectCompetitor:initiated', function(evt)
			{
				if(evt.memo.view == 'results')
				{
					fireTag(2189.3,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':($body.className == 'viewResultsOverview') ? ('Overview') : ('Advantages')});
				}
				else
				{
					fireTag(2188.3,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName});
				}
			});

			document.observe('editLexusStyle:initiated', function(evt)
			{
				if (evt.memo.comparisonType == 'competitive') 
				{
					fireTag(2189.12,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':($body.className == 'viewResultsOverview') ? ('Overview') : ('Advantages')});
				}
				else if (evt.memo.comparisonType == 'lexus2lexus') 
				{
					fireTag(2192.11,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Results'});
				}
				else if (evt.memo.comparisonType == 'lexusseries') 
				{
					fireTag(2192.11,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Series Compare'});
				}				
			});		

			document.observe('addButtonOverlay:clicked', function(evt)
			{
				if(evt.memo.step == 'viewresults')
				{
					fireTag(2189.4,{'<model_name>':modelName,'<tab_name>':($body.className == 'viewResultsOverview') ? ('Overview') : ('Advantages')});
				}
				else
				{
					fireTag(2188.2,{'<model_name>':modelName});
				}
			});
			
			document.observe('compareButtonOverlay:clicked', function(evt)
			{
				// reset the model 
				modelName = getOmnitureModelName(lexusVehicles[state.getPrimaryModelCode()].modelName);
				modelYear = lexusVehicles[state.getPrimaryModelCode()].year;
				trimName = model.getStyleInfoFromStyleId(state.getPrimaryStyleId()).trimName;

				if (evt.memo.comparisonType == 'competitive') 
				{
					fireTag(2189.13,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':(evt.memo.tab == 'viewResultsOverview') ? ('Overview') : ('Advantages')});
					//fireTag(2189.13,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':evt.memo.tab});
				}
				else if (evt.memo.comparisonType == 'lexus2lexus') 
				{
					fireTag(2192.12,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Results'});
				}
				else if (evt.memo.comparisonType == 'lexusseries') 
				{
					fireTag(2192.12,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Series Compare'});
				}			
			});

			document.observe('startOver:loaded', function(evt) 
			{
				tabName = ($body.className == 'viewResultsOverview') ? ('Overview') : ('Advantages');

				if (evt.memo.comparisonType == 'competitive') 
				{
					fireTag(2189.7,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':tabName});
				}
				else if (evt.memo.comparisonType == 'lexus2lexus') 
				{
					fireTag(2192.3,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Results'});
				}
				else if (evt.memo.comparisonType == 'lexusseries') 
				{
					fireTag(2192.3,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Series Compare'});
				}
                	});

                	document.observe('startOver:confirmed', function(evt) 
			{
				if (evt.memo.comparisonType == 'competitive') 
				{
					fireTag(2189.8,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':tabName});
					fireTag(2188.1,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName});
				}
				else if (evt.memo.comparisonType == 'lexus2lexus') 
				{
					fireTag(2192.4,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Results'});
					fireTag(2191.1,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName});
				}
				else if (evt.memo.comparisonType == 'lexusseries') 
				{
					fireTag(2192.4,{'<model_name>':modelName,'<model_year>':modelYear,'<trim>':trimName,'<tab_name>':'Series Compare'});
					fireTag(2190.1,{'<model_name>':modelName,'<model_year>':modelYear});
				}
				
                	});
            	});
        } ();
	// END OF ANALYTICS CLOSURE

    })();

	// remove spaces and change Hybrid to "h"
	function getOmnitureModelName(modelName)
	{
		var omnModelName = modelName.split(' ').join('');	
		omnModelName = omnModelName.replace("Convertible", "C");	
		return omnModelName.replace("Hybrid", "h");
	}


    /**
    * Global function that demo links in results table can access
    */
    function openDemo(url, type) {
        var windowArgs = {
            demo1: 'height=550,width=670',
            demo2: 'height=545,width=672',
            demo3: 'height=333,width=660',
            tutorial: 'height=685,width=830',
            link: 'height=580,width=830'
        };

	var modelName = getURLParam('model', url);
	var videoTitle = getURLParam('demo', url);

	if($body.className == 'viewResultsOverview')
	{
		fireTag(2189.14,{'<model_name>':modelName,'<video_title>':videoTitle});
	}
	else
	{
		fireTag(2192.13,{'<model_name>':modelName,'<video_title>':videoTitle});
	}

        var demoPopup = window.open(url, 'demo', windowArgs[type]);
	
        if (window.focus) {
            demoPopup.focus();
        }
    }

	function getURLParam( name, url )
	{
  		name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  		var regexS = "[\\?&]"+name+"=([^&#]*)";
  		var regex = new RegExp( regexS );
  		var results = regex.exec( url );
  		if( results == null )
    			return "";
  		else
    			return results[1];
	}
