/**
 * global_ajax expects 5 window variables to be set as configuration
 * 1. ga_initPath:      the search path variable that is used to load the page, if none, this will be empty string
 * 2. ga_enableHistory: switch to indicate whether location hash will be used as ajax breadcrumb
 * 3. ga_enableCache:   switch to indicate whether hidden textarea will be used to cache ajax results
 * 4. ga_hasAjaxContent:switch to indicate whether ajax request should be made when locaion hash is modified. turned off for all MY pages except for MYIP
 * 5. ga_initPageTitle :the initial page title, this will be used on browser history change, so browser have friendly list
 * 6. reFacet: for coming in from landing page, where we want to pre-select certain facet (i.e. Market Segment, Vehicle Type, or make Name
 */
if(!window.ga_currentSort) {
    window.ga_currentSort = "make_desc";
}
var ga_currentPath = false;
// used by make model finder.js to determine if sort operaion is required
var ga_setNewTitle = false;
// used by resetPageTitle to determine if new value is set to document.title, and should be cached
var openedCat = new HashMap();
// keep track all the opened faceted category
var initBM = new HashMap();
initBM.add("open", "");
// opened facet
initBM.add("p", "");
// path

function encode4URL(str2Encode) {
    return escape(str2Encode).replace(/\+/g, "%2B").replace(/\#/g, "%23");
}

function addSearchHash(aEle){
  var oATag = aEle;
  if (typeof aEle == 'string'){
   oATag = document.getElementById(aEle);
  }
    oATag.href = (ga_currentPath) ? oATag.href + "#search=" + encode4URL(makeAJAXParam([0,0,'p=' + ga_currentPath]))
        : oATag.href;
}

function setPageTitle(title){
    ga_setNewTitle = true;
    document.title = title;
}

function resetPageTitle(){
    if (window.titleCache) {
        var key = YAHOO.util.History.getCurrentState("search") + "&" + YAHOO.util.History.getCurrentState("resort");
        if (ga_setNewTitle){
            if (!titleCache.cacheExists(key)){
                titleCache.storeData(key, document.title);
            }
            ga_setNewTitle = false;
        } else if (titleCache.cacheExists(key)){
            document.title = titleCache.getData(key);
        }
    } else {
        document.title = window.ga_initPageTitle;
    }
}

function isDebugMode(){
    return ((location.search) && location.search.indexOf("debug=true") > 0);
}

function replaceDivAjax(elementId, ajaxUrl, animate) {
    var divReplacement = new DivReplaceObj(elementId, false, false);
    YAHOO.util.Connect.asyncRequest('GET', ajaxUrl, divReplacement, null);
}

/** Replaces a div using Yahoo animation animIn is the fade in animOut is the fade out */
function replaceDivContent (elementId, content, animate) {
    if (animate) {
        var animOut = new YAHOO.util.Anim(this.id, { opacity: { to: 0 } }, 1, YAHOO.util.Easing.easeOut);
        var animIn = new YAHOO.util.Anim(this.id, { opacity: { to: 1 } }, 1, YAHOO.util.Easing.easeIn);
        animOut.animate;
        document.getElementById(elementId).innerHTML = content;
        animIn.animate;
    } else {
        document.getElementById(elementId).innerHTML = content;
    }
}

/**
 * Create a simple logger, only work in Firefox with param.debug, eventually add Yahoo Logger into this
 * @param args
 */
var SimpleLogger = function() {
}
SimpleLogger.prototype = {
    constructor:SimpleLogger,
    debug: function(msg){
        if (isDebugMode() && window.console){
            window.console.debug(msg);
        }
    },
    printTimeLog: function(oTimeLogEle) {
        var ttlServer = 0;
        for (var _cnt = 0; _cnt < oTimeLogEle.childNodes.length; _cnt++) {
            var n = oTimeLogEle.childNodes[_cnt];
            if (n.childNodes[0]) {
                this.debug("Server AJAX Log: " + parseInt(n.childNodes[0].nodeValue) / 1000 + " seconds to process "
                        + n.getAttribute("key"));
                ttlServer += parseInt(n.childNodes[0].nodeValue) / 1000;
            }
        }
        return ttlServer;
    }
}

/**
 * An attempt to introduce some kind of framework to organize all the horizontal behaviors
 * jointpoints will be before, during, after the success callback; while around makeAjaxRequest method.
 * Since our scenario is very simple, we won't use generic pointcut approach, instead, advice will be coded into
 * the method
 */

/**
 * Base Advice Class. provide logger and base functions
 */
var AjaxHelperAdvice = function(){
    this.logger = new SimpleLogger();
}
AjaxHelperAdvice.prototype = {
    before:function() {
    },
    process:function() {
    },
    after:function() {
    }
}

/**
 * Cache Advice - implement the before and process methods
 */
var CacheAdvice = function(activeFlag, fnGetKey, oFormStorage){
    this.formStorage = oFormStorage;
    this.isActive = activeFlag;
    this.fnGetKey = fnGetKey;
}
CacheAdvice.prototype = new AjaxHelperAdvice();
CacheAdvice.prototype.constructor = CacheAdvice;
CacheAdvice.prototype.parent = AjaxHelperAdvice.prototype.constructor;
CacheAdvice.prototype.before = function() {
    if (this.isActive){
      this.formStorage.storeData(this.fnGetKey.call(), "true");
    }
}
CacheAdvice.prototype.process = function(oListener, content) {
    if (this.isActive){
      this.formStorage.storeData(this.fnGetKey.call() + "_" + oListener.id, content);
    }
}

/**
 * EDW Advice - implement before and after methods
 */
var EDWAdvice = function() {
}
EDWAdvice.prototype = new AjaxHelperAdvice();
EDWAdvice.prototype.constructor = EDWAdvice;
EDWAdvice.prototype.parent = AjaxHelperAdvice.prototype.constructor;
EDWAdvice.prototype.before = function() {
    // update the global timestamp for ads/edw
    YAHOO.Edmunds.Core.timestamp = new Date().getTime();
}
EDWAdvice.prototype.after = function() {
    // edw.js uses "ts" not "edwtimestamp"
    ts = YAHOO.Edmunds.Core.timestamp;
    edwtimestamp = ts;
    edwtid = getTID();
    try {
        var estr = new edd().toString();
        document.getElementById('ajaxPixel').src = estr;
    } catch(err) {
        this.logger.debug(err);
    }
}

/**
 * Omniture Advice - implement after methods
 */
var OmnitureAdvice = function() {
}
OmnitureAdvice.prototype = new AjaxHelperAdvice();
OmnitureAdvice.prototype.constructor = OmnitureAdvice;
OmnitureAdvice.prototype.parent = AjaxHelperAdvice.prototype.constructor;
OmnitureAdvice.prototype.after = function() {
    try {
        if(s)void(s.t())
    } catch(err) {
        this.logger.debug(err);
    }
}

/**
 * History Advice - implement process for makeAJAXCall
 */
var HistoryAdvice = function(key){
    this.key = key
}
HistoryAdvice.prototype = new AjaxHelperAdvice();
HistoryAdvice.prototype.constructor = HistoryAdvice;
HistoryAdvice.prototype.parent = AjaxHelperAdvice.prototype.constructor;
HistoryAdvice.prototype.process = function(ajaxHelper, searchParam) {
    if (window.ga_enableHistory) {
        YAHOO.util.History.navigate(this.key, searchParam);
    } else {
        ajaxHelper.operationInProgress = true;
        ajaxHelper.toggleWaitImageAndStatus(true);
        ajaxHelper.connect(facetAJAXURL + "?" + searchParam + ( (isDebugMode())? "&debug=true":"") );
    }
}

function AjaxHelperListener(id, readXMLAsStringFlag) {
    this.id = id;
    this.readXMLAsStringFlag = readXMLAsStringFlag;
    this.logger = new SimpleLogger();
}
AjaxHelperListener.prototype = {
    constructor: AjaxHelperListener,
    /**
     * Called to inform UI that loading is in progress
     * @param onLoading <boolean> - TRUE if loading is in starting, FALSE if loading is finishing
     */
    toggleLoad: function(onLoading) {
    },

    /**
     * Use by formStorage to get initial state of the Listener.
     * @return state <String> representation of the initial state
     */
    initValue: function() {
        if (document.getElementById(this.id)) {
            return document.getElementById(this.id).innerHTML;
        }
    },

    /**
     * To make the Listener element appear
     */
    reveal: function() {
        if (document.getElementById(this.id)) {
            document.getElementById(this.id).style.visibility = 'visible';
        }
    },

    /**
     * To hide the Listener element
     */
    stealth: function() {
        if (document.getElementById(this.id)) {
            document.getElementById(this.id).style.visibility = 'hidden';
        }
    },

    /**
     * Called by global Ajax when new event is received.  All object registered with global ajax must implement this method
     * @param response <Object> or <String> of the event
     */
    notify: function(response) {
        console.debug("ERROR... notify function is not implemented")
    },
    success: function() {
        console.debug("ERROR... success function is not implemented")
    },
    failure: function() {
        console.debug("ERROR... failure function is not implemented")
    },

    /**
     * create DOM element based on the parameters. this wil take care the IE compatibility issues
     * @param tag <String> - element type
     * @param id <String> - id of the element, false if none
     * @param attrMap <map> - key<String> attribute name, value<String> attribute value
     * @param text <String> - text node of the element
     * @return HTMLElement created
     */
    createElement: function (tag, id, attrMap, text) {
        var ele = document.createElement(tag);
        if (attrMap != null) {
            // name attribute cannot be change on dynamically created element (see msdn on NAME attribute)
            if (navigator.appVersion.toLowerCase().indexOf('msie') > -1 && attrMap['name']) {
                ele = document.createElement("<" + tag + " name='" + attrMap['name'] + "'></" + tag + ">");
                delete attrMap['name'];
            }

            for (var key in attrMap) {
                ele.setAttribute(key, attrMap[key]);
                // special create Element is need for IE, it is not honoring setAttribute method, and yes!!!
                // setAttribute is still required for onClick, and must be set beforehand
                if (navigator.appVersion.toLowerCase().indexOf('msie') > -1) {
                    if (key.toUpperCase() == "CLASS"){
                        ele.className = attrMap[key];
                    } else if (key.toUpperCase() == "TYPE") {
                        ele.type = attrMap[key];
                    } else if (key.toUpperCase() == "HREF") {
                        ele.href = attrMap[key];
                    } else if (key.toUpperCase() == "TITLE") {
                        ele.title = attrMap[key];
                    } else if (key.toUpperCase() == "BORDER") {
                        ele.border = attrMap[key];
                    } else if (key.toUpperCase() == "COLSPAN") {
                        ele.colSpan = attrMap[key];
                    } else if (key.toUpperCase() == "SRC") {
                        ele.src = attrMap[key];
                    } else if (key.toUpperCase() == "VALUE") {
                        ele.value = attrMap[key];
                    } else if (key.toUpperCase() == "ALT") {
                        ele.alt = attrMap[key];
                    } else if(key.toUpperCase() == "SIZE") {
                        ele.size = attrMap[key];
                    } else if(key.toUpperCase() == "MAXLENGTH") {
                        ele.maxLength = attrMap[key];
                    } else if (key.toUpperCase() == "VALIGN") {
                        ele.style.verticalAlign = attrMap[key];
                    } else if (key.toUpperCase() == "STYLE") {
                        var stylesAttr = attrMap[key].split(";");
                        var _len = stylesAttr.length;
                        for (var _idx=0; _idx < _len && stylesAttr[_idx].length > 0; _idx++){
                            var kp = stylesAttr[_idx].split(":");
                            var __key = kp[0].replace(/^\s*/,"").replace(/\s*$/,"").replace(/[-]/g, "");
                            var __val = kp[1].replace(/^\s*/,"").replace(/\s*$/,"");
                            __val = (!/^\d*$/.test(__val))? "'" + __val + "'" : __val;
                            eval("ele.style." + __key + "=" + __val + "");
                        }
                    } else if (key.toUpperCase() == "ONCLICK"){
                        ele.onclick = function(){
                          var ieSux = new Function(attrMap[key]);
                          ieSux();
                          return !(new RegExp("false[;\s]*$").test(attrMap[key]));
                        }
                    } else if(key.toUpperCase() == "ONKEYPRESS") {
                        ele.onkeypress = function(){
                          var ieSux = new Function(attrMap[key]);
                          return ieSux();
                        }
                    } else if(key.toUpperCase() == "ONCHANGE") {
                        YAHOO.util.Event.addListener(ele, 'change', function() {
                            var ieSux = new Function(this);
                            ieSux();
                            return ieSux();
                        }, attrMap[key], true);
                    }
                }
            }
        }
        if (id) {
            ele.id = id;
        }
        if (text) {
            ele.appendChild(document.createTextNode(text));
        }
        return ele;
    }
}

/**
 * DivReplaceObj is shared listener implementation for simple DIV content replacement & Appear Effect
 */
var DivReplaceObj = function(id, readXMLAsStringFlag, animate) {
    this.parent(id, readXMLAsStringFlag);
    if (animate != undefined) {
        this.animate = animate;
    } else {
        this.animate = false;
    }
}
DivReplaceObj.prototype = new AjaxHelperListener();
DivReplaceObj.prototype.constructor = DivReplaceObj;
DivReplaceObj.prototype.parent = AjaxHelperListener.prototype.constructor;
DivReplaceObj.prototype.initValue = function () {
    var ele = document.getElementById(this.id);
    return (ele)? ele.innerHTML : "";
}
DivReplaceObj.prototype.notify = function (responseEle, callbackParam) {
    replaceDivContent(this.id, responseEle, this.animate);
};
DivReplaceObj.prototype.success = function(successObject) {
    this.notify(successObject.responseText, null);
};
DivReplaceObj.prototype.failure = function(failureObject) {
    console.debug("Server AJAX Log: " + failureObject.statusText());
};

/**
 * AJAXHelper is the main AJAX framework to coordinate events & notifications to the listeners
 */
var AjaxHelper = function() {
    this.callbackParam = null;
    this.logger = new SimpleLogger();
    this.listeners = {};
    this.ads = [];
    this.operationInProgress = false;
    this.startTime = new Date();
    this.path = '';
    this.resort = '';
    this.hideContent4Ajax = window.ga_enableHistory && location.hash && location.hash.length > 1;
    // Checking mainly for the availability of ActiveX in IE6. Firewalls and high security settings will block ActiveX!
    this.activeXEnabled = !!YAHOO.util.Connect.getConnectionObject();
};
AjaxHelper.prototype = {
    constuctor: AjaxHelper,

    getContentAsString: function(parentNode) {
        return parentNode.xml != undefined ?
               this._getContentAsStringIE(parentNode) :
               this._getContentAsStringMozilla(parentNode);
    },

    _getContentAsStringIE: function(parentNode) {
        var contentStr = "";
        for (var i = 0; i < parentNode.childNodes.length; i++) {
            var n = parentNode.childNodes[i];
            if (n.nodeType == 4) {
                contentStr += n.nodeValue;
            }
            else {
                contentStr += n.xml;
            }
        }
        return contentStr;
    },

    _getContentAsStringMozilla: function(parentNode) {
        var xmlSerializer = new XMLSerializer();
        var contentStr = "";
        for (var i = 0; i < parentNode.childNodes.length; i++) {
            var n = parentNode.childNodes[i];
            if (n.nodeType == 4) { // CDATA node
                contentStr += n.nodeValue;
            }
            else {
                contentStr += xmlSerializer.serializeToString(n);
            }
        }
        return contentStr;
    },


    /**
     * Flow:
     *   1. if success, notify all listeners, and reset error message windows
     *   2. if error, display status message
     *   3. make AJAX Call
     *   4. reset status windows
     * @parameter response of the AJAX call
     */
    success: function(ajaxResponse) {
        var ttlBrowser = 0, ttlServer = 0;

        if (searchCacheAdvice) { searchCacheAdvice.before(); }
        // refresh edwtimestamp unless loading bookmarked state
        if (!this.hideContent4Ajax) { edwAdvice.before(); }

        var response = ajaxResponse.responseXML.getElementsByTagName("response");
        if (response == null || response.length != 1)
            return;

        var size = response[0].childNodes.length;
        for (var i = 0; i < size; i++) {
            var listenerEle = response[0].childNodes[i];
            if (listenerEle.nodeType != 1) {
                continue;
            }

            if (listenerEle.getAttribute("id") == 'timeLog') {
                ttlServer = this.logger.printTimeLog(listenerEle);
            }

            var listenerObj = this.listeners[listenerEle.getAttribute("id")];
            if (listenerObj != null) {
                var _sTime = new Date();
                var ajaxStrContent = this.getContentAsString(listenerEle);
                if (listenerObj.readXMLAsString) {
                    listenerObj.notify(listenerEle, this.callbackParam);
                } else {
                    listenerObj.notify(ajaxStrContent, this.callbackParam);
                }

                if (searchCacheAdvice) searchCacheAdvice.process(listenerObj, ajaxStrContent);
                listenerObj.reveal();

                var time = (new Date().getTime() - _sTime.getTime()) / 1000;
                ttlBrowser += time;
                this.logger.debug("Client AJAX Log: " + time + " second to process " + listenerEle.getAttribute("id"));
            }
        }

        // refresh ads/edw/omniture unless loading bookmarked state
        if (!this.hideContent4Ajax) {
            this.refreshAds();
        }

        this.toggleWaitImageAndStatus(false);
        this.operationInProgress = false;
        this.callbackParam = null;
        this.hideContent4Ajax = false;
        this.logger.debug("Client: " + ttlBrowser + ", Server: " + ttlServer + ".  Total time to execute :"
                    + (new Date().getTime() - this.startTime.getTime()) / 1000 + " second");
    },

    refreshAds: function(){
        resetGlobalRandNum();
        var adSize = this.ads.length;
        for (i=0; i<adSize; i++){
            this.ads[i].notify();
        }
        edwAdvice.after();
        omnitureAdvice.after();
    },

/**
 * @parameter listener object to be register
 *  required attributes: readXMLAsString - boolean to indicate want response in XML DOM or String
 *  required method: notify();
 */
    registerListener: function(listenerObj) {
        this.listeners[listenerObj.id] = listenerObj;
        if (!this.hideContent4Ajax) {
            listenerObj.reveal();
        }
    },

/**
 * @parameter oAdUnit object
 */
    registerAd: function(oAdUnit){
        this.ads.push(oAdUnit);
    },

/**
 * Flow:
 *   1. check if another AJAX call is on
 *   2. turn on busy message
 *   3. make AJAX Call
 *   4. reset error windows
 *  @parameter Parameter for the javascript callback methods
 *  @parameter Request Param for the AJAX call
 */
    makeAJAXCall: function(debugFlag, callbackObject, options) {
        // If ActiveX or XMLHttpRequest objects are available for use
        if (this.activeXEnabled) {
        if (! this.operationInProgress) {
            this.startTime = new Date();
            this.callbackParam = callbackObject;

            var searchParam = makeAJAXParam(arguments);
            if (searchHistoryAdvice) searchHistoryAdvice.process(this, searchParam);

            // adBug code
            try {
                // call custom event to close adbug flyout.  This is defined in flyout.js
                if (!YAHOO.lang.isUndefined(YAHOO.com.edmunds.flyout.closeFlyoutEvent)) {
                    YAHOO.com.edmunds.flyout.closeFlyoutEvent.fire();
                }
            } catch (err) {
            } // ignore case name sapce does not exist
        }
        } else {
            alert('Your browser\'s ActiveX controller may be blocked by a firewall or a high security setting. Please adjust your settings and try again.');
        }
    },

    /**
     * Make asyncRequest call on the URL
     */
    connect: function(url) {
        YAHOO.util.Connect.asyncRequest('GET', url, this);
    },

    failure: function(message, displayFlag) {
        this.toggleWaitImageAndStatus(false);
    },

    toggleWaitImageAndStatus: function(isDisplayOn) {
        for (var name in this.listeners) {
            if (this.listeners[name].toggleLoad) {
                this.listeners[name].toggleLoad(isDisplayOn);
            }
        }
    }
}
// create parameters used for AJAX request. it will output different version if history manager is enabled
function makeAJAXParam(args) {
    if (window.ga_enableHistory) {
        for (var idxOpt = 2; idxOpt < args.length; idxOpt++) {
            if (args[idxOpt].indexOf('p=') == 0) {
                ga_currentPath = args[idxOpt].substring(2);
            }
        }
        var params = [];
        var openCatStr = openedCat.keys().join("|");
        // foreach initBM param, if it exists in the argument, use argument, otherwise use initBM value
        initBM.each(function(key, value) {
            if (key == 'open') {
                params.push(key + '.eq.' + openCatStr);
            } else {
                var found = false;
                for (var ii = 2; ii < args.length && !found; ii++) {
                    var argPair = args[ii].split("=");
                    if (argPair[0] == key) {
                        params.push(argPair[0] + ".eq." + argPair[1]);
                        found = true;
                    }
                }
                if (!found) {
                    params.push(key + ".eq." + value);
                }
            }
        });
        return params.join(".amp.");
    } else {
        var params = [];
        if (fastFilter) {
            params.push('f=' + encode4URL(fastFilter));
        }
        if (fastQuery) {
            params.push('q=' + encode4URL(fastQuery));
        }
        if (reFacet) {
            params.push('reFacet=' + encode4URL(reFacet));
        }
        var openCatStr = openedCat.keys().join("|");
        params.push('open=' + openCatStr);
        for (var ii = 2; ii < args.length; ii++) {
            var pair = args[ii].split("=");
            params.push(pair[0] + "=" + encode4URL(pair[1]));
        }
        return params.join("&");
    }
}

/** callback for search history state
 * 1. update page title for more informative history
 * 2. Parse the path from search state and set it to global ajax helper. this variable is used by addSearchHash method
 * 3. Update the content. either make Ajax request if response is new and not cached. or used the cached response and update page
 * @param nState - changed search state
 */
function updSearchBM(nState) {
    // Checking mainly for the availability of ActiveX in IE6. Firewalls and high security settings will block ActiveX!
    var activeXEnabled = !!YAHOO.util.Connect.getConnectionObject();
    // If ActiveX or XMLHttpRequest objects are available for use
    if (activeXEnabled) {
    resetPageTitle();

    var pathEqStr;
    var nStateArray = (nState.split(".amp."));
    for (var strIdx = nStateArray.length - 1; strIdx >= 0; --strIdx) {
        if (nStateArray[strIdx].indexOf('p.eq.') == 0) {
            pathEqStr = nStateArray[strIdx];
            break;
        }
    }
    ga_currentPath = (pathEqStr)? pathEqStr.substr(5):window.ga_initPath;
    var isResponseCached = window.ga_enableCache && cntCache.cacheExists(nState);
    // no cached response, request content from server
    if (!isResponseCached && window.ga_hasAjaxContent) {
        g_AJAXHelper.operationInProgress = true;
        g_AJAXHelper.toggleWaitImageAndStatus(true);
        var paramStr = encode4URL(nState).replace(/\.amp\./g, "&").replace(/\.eq\./g, "=");
        if (fastFilter) paramStr += "&f=" + encode4URL(fastFilter);
        if (fastQuery) paramStr += "&q=" + encode4URL(fastQuery);
        if (reFacet) paramStr += "&reFacet=" + encode4URL(reFacet);
        g_AJAXHelper.connect(facetAJAXURL + "?" + paramStr);
    } else {
        // refresh edwtimestamp unless loading bookmarked state
        if (!g_AJAXHelper.hideContent4Ajax) { edwAdvice.before(); }
        // perform same operations as gAjax.success(), but skip all the advice since edw/omniture already refreshed on initial request
        for (var id in g_AJAXHelper.listeners) {
            var resTxt = cntCache.getData(nState + "_" + id);
            if (resTxt) {
                g_AJAXHelper.listeners[id].notify(resTxt);
                g_AJAXHelper.listeners[id].reveal();
            }
        }
        if(!g_AJAXHelper.hideContent4Ajax) {
            g_AJAXHelper.refreshAds();
        }
        g_AJAXHelper.toggleWaitImageAndStatus(false);
        g_AJAXHelper.operationInProgress = false;
    }
    } else {
        alert('Your browser\'s ActiveX controller may be blocked by a firewall or a high security setting. Please adjust your settings and try again.');
}
}

/**
 * callback for sort state
 * 1. update page title for more informative history
 * 2. set current sort to new sort state. this is used by upperfunnel result to know if content is already sorted or not
 * 3. all the upperfunnel result to resort (actual sort is performed depending on ga_currentSort)
 * @param sortId
 */
function updSortBM(sortId) {
    edwAdvice.before();
    resetPageTitle();
    ga_currentSort = sortId;
    if (window.resort) {
        resort(ga_currentSort);
        g_AJAXHelper.refreshAds();
    }
}

/**
 * this is the corresponding method to HistoryAdvice.process(). It pass the
 * @param sortId
 */
function setSortColumn (sortId) {
    if (window.ga_enableHistory) {
        YAHOO.util.History.navigate('resort', sortId);
    }
}
// create instances of caches, advices and ajaxHelper
var cntCache = false, titleCache = false, searchCacheAdvice = false, searchHistoryAdvice = false;
var edwAdvice = new EDWAdvice();
var omnitureAdvice = new OmnitureAdvice();
var g_AJAXHelper = new AjaxHelper();

if (window.ga_enableHistory) {
    searchHistoryAdvice = new HistoryAdvice("search");
    titleCache = new YAHOO.edmunds.core.storage.FormStorage("pageTitle", "===");
    if (window.ga_enableCache){
        cntCache = new YAHOO.edmunds.core.storage.FormStorage("pageStorage", "=");
        searchCacheAdvice = new CacheAdvice(window.ga_enableCache, function() {
            return YAHOO.util.History.getBookmarkedState("search")
        }, cntCache);
    }

    if (window.ga_currentSort) {
        YAHOO.util.History.register("resort", ga_currentSort, function(sortId) {
            updSortBM(sortId)
        });
    }
    var initSearchState = initBM.toQueryString();
	initSearchState = initSearchState.replace(/=/g, ".eq.");
	initSearchState = initSearchState.replace(/&/g, ".amp.");
    YAHOO.util.History.register("search", initSearchState, function(nState) {
        updSearchBM(nState);
    });

    YAHOO.util.History.onLoadEvent.subscribe(function() {
        var bmstate = YAHOO.util.History.getBookmarkedState("search");
        if (bmstate) {
            updSearchBM(bmstate);
        } else {
            resetPageTitle();
            ga_currentPath = window.ga_initPath;
            if (window.ga_enableCache){
              cntCache.storeData(initState, "true");
              for (var id in g_AJAXHelper.listeners) {
                cntCache.storeData(initState + "_" + id, g_AJAXHelper.listeners[id].initValue());
              }
            }
        }
        if (window.ga_currentSort) {
            var sortState = YAHOO.util.History.getBookmarkedState("resort");
            if (sortState) {
                window.ga_currentSort = sortState;
            }
        }
    });
}
