var Analytics = ( function () {

    var AnalyticsObj = {
        subscribers: {},
        listeners: {},
        fromConfigListeners: [],
        seenMemoWithTag: {},
        isEnabled: false,
        // enabledModules: new Array(),
        enabledModules: [],
        bvLoaded: 0,
        uniqueEvents: [],
        lastQVloc: '',
        lastQVkey: '',
        currentTags: [],

        // RPC_METHODS_ALLOWED: new Hash({
        RPC_METHODS_ALLOWED: {
            "prodcat" : 1,
            "generic" : 1,
            "cart" : 1,
            "rpc.form" : 1,
            "search" : 1,
            "email.signup" : 1
        },

        _pushBV: function(){
            // console.log("push BV");
            if (!$.isArray(this.jsEventsWaited) ) {
                return null;
            }
            // Analytics.jsEventsWaited.each(function(EVENT){
            for (var i=0, len=this.jsEventsWaited.length; i<len; i++) {
                var EVENT = this.jsEventsWaited[i];
                if (EVENT.after && EVENT.after === 'bv:loaded'){
                    this._addFrontendEvent(EVENT);
                }
            }
        },

        addPendingTags: function(newTags ){
            this.pendingTags = newTags;
            this.execTags();
            return this;
        },

        _addStaticListeners: function(){
            var self = this;
            // document.observe("dom:loaded", function(){}
            $(document).ready(function(e) {

                self.isEnabled = (typeof ANALYTICS_ENABLED != "undefined") ? ANALYTICS_ENABLED : false;
                self.enabledModules = (typeof ANALYTICS_MODULES != "undefined" ) ? ANALYTICS_MODULES : [];

                // CONVERSION EVENTS - If conversion events exist add handler as directed and form input for location 
                self.jsEvents = (typeof CONVERSION_EVENTS != "undefined" ) ? CONVERSION_EVENTS : [];
                self.jsEvents = self.jsEvents.concat( (typeof JS_EVENTS != "undefined" ) ? JS_EVENTS : [] );
                self.jsPixelEvents = (typeof JS_PIXEL_EVENTS != "undefined" ) ? JS_PIXEL_EVENTS : new Object();        

                for (var module in self.jsPixelEvents) {
                    var pxEvtsArray = self.jsPixelEvents[module];
                    for (var i=0, len=pxEvtsArray.length; i<len; i++) {
                        var CEVENT = pxEvtsArray[i];
                        self.jsEvents = self.jsEvents.concat( (typeof CEVENT != "undefined" ) ? CEVENT : [] );
                    }
                }

                self.jsEventsWaited = [];
                for (var i=0, len=self.jsEvents.length; i<len; i++) {
                    var CEVENT = self.jsEvents[i];
                    CEVENT.hookIds = [];  // allow for mapping one event to many elements
                    //Check for events to wait on, loading iframes, bv etc to be custom handled in static listeners
                    //if the ele is expected to exist on load, then the event handlers can be set up right away in addFrontendEvent
                    if (CEVENT.after){
                        self.jsEventsWaited.push(CEVENT);
                    }else{
                        self._addFrontendEvent(CEVENT); 
                    } 
                }

                $('[locurl]').bind ( 'click', function(e) {
                    var params = {};
                    if (e.target){
                        var loc = $(e.target).attr('locurl');
                        if (!!loc) {
                            location_params = loc.split(",");
                            params['TYPE_LOCATION'] = location_params[0];
                            params['PRODUCT_KEY'] = location_params[1];
                        }
                    }
                });

            }); // end page load event handler

            //Search hook for frontend and soon Endeca
            $(document).bind( "search:results", function(event, res){
                self.addPendingTags({"CoreMetrics":{"dom:loaded":[{"params":[res.pageid,res.keywords,res.cat,'"' + res.count +'"',null],"tag":"cmCreatePageviewTag"}]}});
            });

            // BV loaded and ready this is just a place to handle the event, tags come from the backend config. 
            $(document).bind( "bv:loaded", function() {
                if (self.bvLoaded == 1){
                   return;
                }
                self.bvLoaded = 1;

                //console.log("BV IS LOADED",self.jsEventsWaited);

                for (var i=0, len=self.jsEventsWaited.length; i<len; i++) {
                    var EVENT = self.jsEventsWaited[i];
                    if (EVENT.after && EVENT.after == 'bv:loaded') {
                        self._addFrontendEvent(EVENT);
                    }
                }
            });

            $(document).bind( "bv:created", function() {
                // console.log("BV CREATED IS LOADED",self.jsEventsWaited);
                for (var i=0, len=self.jsEventsWaited.length; i<len; i++) {
                    var EVENT = self.jsEventsWaited[i];
                    if (EVENT.after && EVENT.after == 'bv:created'){
                        self._addFrontendEvent(EVENT);
                    }
                }
            });

            $(document).bind( "MPP:productQV", function() {
                // console.log("QV IS LOADED",self.jsEventsWaited);
                setTimeout("Analytics._pushBV()", 4000);
            });

/*
            // our RPC hook
            $(document).bind( 'RPC:RESULT', function(evt, obj, requestArgs) {

                console.log('Analytics: Got RPC:RESULT ', obj, requestArgs);

                var rpcRequestArray, rpcResponseArray;
                var requestMethod, requestId;
                if (typeof obj.request === "undefined") {
                    return null;
                }
                rpcRequestArray = (obj.request.parameters.JSONRPC != null) ?
                    // obj.request.parameters.JSONRPC.evalJSON() :
                    JSON.parse(obj.request.parameters.JSONRPC) :
                    null;
                if (rpcRequestArray) {
                    // rpcResponseArray = obj.responseText.evalJSON();
                    rpcResponseArray = JSON.parse(obj.responseText);
                    if (rpcResponseArray) {
                        for (var i=0, len=rpcRequestArray.length; i<len; i++) {
                            var rpcRequest = rpcRequestArray[i];
                            requestMethod = rpcRequest.method;
                            requestId = rpcRequest.id;
                            console.log("Analytics handling RPC request:  ", requestMethod, " with id: ", requestId);
                            // We can do special handlers for requests here or filter out non handled requests... 
                            if ( !self.RPC_METHODS_ALLOWED[requestMethod] ) {
                                // console.log("Analytics skipped ", requestMethod);
                            } else {
                                // Make sure we have the response for this request (id's must match).
                                // var myRpcResponse = rpcResponseArray.find(function(rpcResponse){
                                //     return rpcResponse.id == requestId
                                // });
                                var myRpcResponse = null;
                                for (var j=0, jlen=rpcResponseArray.length; j<jlen; j++) {
                                    if (rpcResponseArray[j].id == requestId) {
                                        myRpcResponse = rpcResponseArray[j];
                                        break;
                                    }
                                }
                                if (myRpcResponse && myRpcResponse.result != null) {
                                    console.log("Analytics will handle ", myRpcResponse.result.data.Analytics);
                                    var newTags = myRpcResponse.result.data.Analytics;
                                    self.addPendingTags(newTags);
                                }
                            }
                        } // end rpcRequestArray loop
                    } // end if (rpcResponseArray)
                } // end if (rpcRequestArray)
            }); // end document.bind( 'RPC:RESULT' )
*/

            // New RPC hook

            $(document).bind( 'RPC:RESULT', function(evt, responseObj, requestArgs, requestId) {

                // console.log('Analytics: Got RPC:RESULT ', responseObj, requestArgs, requestId);

                var requestMethod = (requestArgs && requestArgs.method) ? requestArgs.method : 'rpc.form';

                if ( !self.RPC_METHODS_ALLOWED[requestMethod] ) {
                    return false;
                }

                if ( !responseObj || !responseObj.responseText ) {
                    return false;
                }

                var myRpcResponse;
                var rpcResponseArray = $.parseJSON(responseObj.responseText);
                if ($.isArray(rpcResponseArray)) {
                    for (var j=0, jlen=rpcResponseArray.length; j<jlen; j++) {
                        if (rpcResponseArray[j].id == requestId) {
                            myRpcResponse = rpcResponseArray[j];
                            break;
                        }
                    }

                    if (myRpcResponse && myRpcResponse.result != null) {
                        // console.log("Analytics will handle ", myRpcResponse.result.data.Analytics);
                        var newTags = myRpcResponse.result.data.Analytics;
                        self.addPendingTags(newTags);
                    }
                }

            });


            //Page data already exists hook
            $(document).bind( 'PAGEDATA:RESULT', function(evt, obj) {
                console.log("GOT PAGE DATA ",obj);

                var catalog_path = obj;
                var path;
                try {
                    path = eval ('page_data.' + catalog_path);
                } catch (e) {
                    console.log('Analytics: page_data path doesnt exist: ', catalog_path);
                }
                if (path && path.rpcdata){
                    if (path.rpcdata.result){
                        if (path.rpcdata.result.Analytics){
                            var newTags = eval ('page_data.' + catalog_path + '.rpcdata.result.Analytics');
                            self.addPendingTags(newTags);
                        }
                    }
                }
            });

        }, // end _addStaticListeners
  
        _handleIO: function(catalog_path) {
            // console.log("IO PATH ",catalog_path);
            if (catalog_path == "catalog.spp.recommendedProducts") {
                // console.log("IO SPP RUNNING");
                //cm rec request
                rec_products_default = eval ('page_data.' + catalog_path + '.rpcdata.products');
                main_product = page_data.catalog.spp.rpcdata.products[0];
                //console.log("Default rec products",rec_products_default); 
                //console.log("Main product ",main_product);
                //cmRecRequest("ProdPage",main_product.PRODUCT_ID,main_product.DEFAULT_CAT_ID);
                //cmDisplayRecs();
            }
        },

        addDynamicListener: function(taggingModule, myEvent, TagBlocks) {
            var self = this;
            var id = "default";
            // TagBlocks.each(function(tagBlock)
            for (var i=0, len=TagBlocks.length; i<len; i++) {
                var tagBlock = TagBlocks[i];
                if (typeof tagBlock.memo != "undefined") {
                    id = tagBlock.memo;
                }
                if (!self.subscribers[id]) { self.subscribers[id] = {}; }
                if (!self.subscribers[id][taggingModule]) { self.subscribers[id][taggingModule] = {}; };
                if (!self.subscribers[id][taggingModule][myEvent]) {
                    self.subscribers[id][taggingModule][myEvent] = new Array();
                } else {
                     // console.log( "Event is defined ", self.subscribers[id][taggingModule][myEvent] );
                }
                if (!self.seenMemoWithTag[id]) {
                    self.seenMemoWithTag[id] = {};
                }
                if (!self.seenMemoWithTag[id][tagBlock.tag]) {
                    self.seenMemoWithTag[id][tagBlock.tag] = 1;
                    self.subscribers[id][taggingModule][myEvent].push(tagBlock);
                    console.log( "pushed Event for: (id:", id, ")(taggingModule:", taggingModule, ")(event:", myEvent, ") -> ", tagBlock);
                }

            } // end for loop in TagBlocks

            // Attach the listener for this event type.
            if (!self.listeners[myEvent]) {
                self.listeners[myEvent] = 1;

                var handler = function(evt, memo) {
                    console.log("Analytics: Running window event: ", myEvent, " with memo: ", memo);
                    var myId = memo;
                    // self.enabledModules.each(function(taggingModule)
                    for (var i=0, len=self.enabledModules.length; i<len; i++ ) {
                        var taggingModule = self.enabledModules[i];
                        // console.log("for tagging module: ", taggingModule);
                        // if (typeof self.subscribers[myId][taggingModule] != "undefined")
                        //    if (self.subscribers[myId][taggingModule][myEvent]) 
                        if (typeof self.subscribers[myId][taggingModule] === "undefined"
                                        || !self.subscribers[myId][taggingModule][myEvent]) {
                            continue;
                        }
                        if ( taggingModule.match("CoreMetrics") ) {
                            self.execEventTagBlocks( self.subscribers[myId][taggingModule][myEvent] );
                        } else { 
                            self.execImageBlocks(  self.subscribers[myId][taggingModule][myEvent] ); 
                        }
                    }
                }; // end handler

                // document.observe(myEvent, function(evt)
                // if (myEvent === "dom:loaded") { // special handling for "dom:loaded" event - call jQuery's ready() function
                //    $(document).ready.call( self, handler );
                // } else {
                    // console.log('Binding to event ', myEvent);
                    $(document).bind( myEvent, handler );                
                // }
            } // end if (!self.listeners[myEvent])
        }, // end this.addDynamicListener

        execTags: function () {
            // var self = this;
            // if (typeof self.pendingTags == "object") {
            //     Object.keys(self.pendingTags).each(function(taggingModule){
            //         if (typeof taggingModule != "object" && self.pendingTags[taggingModule] == 'notag') {
            //                 return;
            //         }
            if ( typeof this.pendingTags !== "object") {
                return null;
            }
            for (var taggingModule in this.pendingTags) {
                if (typeof taggingModule !== "object" && this.pendingTags[taggingModule] === 'notag') {
                    continue;
                }
                // Object.keys(self.pendingTags[taggingModule]).each(function(myEvent){
                for (var myEvent in this.pendingTags[taggingModule]) {
                    // console.log("Analytics: module / event ", taggingModule, myEvent);
                    if (myEvent != 'dom:loaded'){
                        // register this event with our collection of listeners.
                        this.addDynamicListener(taggingModule, myEvent, this.pendingTags[taggingModule][myEvent]);
                    } else {
                        // incoming tagging events under the 'dom:loaded' label can be executed straightaway.
                        if ( taggingModule.match("CoreMetrics") ) {
                            this.execEventTagBlocks( this.pendingTags[taggingModule][myEvent] );
                        } else {
                            this.execImageBlocks( this.pendingTags[taggingModule][myEvent] );
                        }
                    }
                } // end for (var myEvent in self.pendingTags[taggingModule])
            } // end for (var taggingModule in this.pendingTags)
        }, // end this.execTags

        execImageBlocks: function(tagBlocks) {
            // these often have a rnd number, gen that here to make sure it isn't used over and over as a global
            var axel = Math.random() + "";
            var num = axel * 1000000000000000000;
            // tagBlocks.each(function(tagBlock){
            for (var i=0, len=tagBlocks.length; i<len; i++) {
                var tagBlock = tagBlocks[i];
                var tag_url = new Image();
                tagBlock = tagBlock.replace("rndnum",num);
                console.log("exec tag image ",tagBlock);
                tag_url.src = tagBlock;
            }
        },

        execEventTagBlocks: function(tagBlocks) {
            // tagBlocks.each(function(tagBlock){
            for (var i=0, len=tagBlocks.length; i<len; i++) {
                var tagBlock = tagBlocks[i];
                if (!tagBlock.params || !tagBlock.tag) {
                    continue;
                }
                console.log( "Analytics.execEventTagBlocks about to execute tag: ", tagBlock.tag, " with params: ", tagBlock.params );
                if (typeof window[tagBlock.tag] == "undefined") {
                    // console.log( "The Tagging Module function is not found: ", tagBlock.tag );
                    continue;
                }
                window[tagBlock.tag].apply(this, tagBlock.params);
            };
        },

        _addFrontendEvent: function (EVENT) {
            // if ( $(EVENT.domID) == null && EVENT.event != "dom:loaded" ){
            //     //console.log("CM ELEMENT does not EXIST! ",EVENT.domID);
            //     // removed by not deleted yet EF 
            //     //return;
            // } 
            if (EVENT.event == "dom:loaded"){
                this.execTagsbyType(EVENT);
            } else {
                if (EVENT.domID || (EVENT.attachAttr && EVENT.attachValue) ){
                    this._attachFrontendEvent(EVENT);  
                }
            }
        },

        /*
         * This method returns the ID of a given element. if the element has no ID, it creates a unique ID & adds it to the element.
         * Intended to replicate prototype's Element#identify() function.
         * @private
         */
        eleIdCount: 0,
        identifyElement: function(ele, prefix) {
            var myPrefix = prefix || "ele_"
            if ( $(ele).attr('id') ) return ele.id;
            do {
                this.eleIdCount++;
                var id = myPrefix + '_' + this.eleIdCount;
            } while( $('#' + id).length > 0 );
            $(ele).attr('id', id);
            return ele.id;
        },

        _attachFrontendEvent: function (EVENT) {
            // EVENT.attachAttr && EVENT.attachValue are required unless EVENT.domID is specified(for legacy cases with email signups)
            if (EVENT.domID) {
                 EVENT.attachAttr = 'id';
                 EVENT.attachValue = EVENT.domID;
            }
            if (typeof EVENT.attachTag == "undefined") {
                 EVENT.attachTag = '';
            }
            if (!EVENT.attachAttr || !EVENT.attachValue) {
                return null;
            }
            // console.log ( "got ",EVENT.attachTag,EVENT.attachAttr,EVENT.attachValue,EVENT.event);
            // console.log (EVENT);

            var theseListeners = [];
            // $$(''+ EVENT.attachTag + '[' + EVENT.attachAttr + '="' + EVENT.attachValue + '"]').each(function (ele){
            //     var dups = EVENT.hookIds.indexOf( $(ele).identify() );
            //     if ( dups < 0 ){
            //         EVENT.hookIds.push( $(ele).identify() ); // if id doesn't exist give it one to use for matching in event handlers  
            //         self.fromConfigListeners.push(ele); // keep a full current list for ref
            //         theseListeners.push(ele); // but don't dupilicate events
            //     }
            // });
            var selector = ''+ EVENT.attachTag + '[' + EVENT.attachAttr + '="' + EVENT.attachValue + '"]';
            var self = this;
            // $(selector).each( function (ele) {
            $.each($(selector), function(idx, ele){
                var dups = $.inArray( self.identifyElement(ele), EVENT.hookIds);
                if ( dups < 0 ){
                    EVENT.hookIds.push( self.identifyElement(ele) ); // if id doesn't exist give it one to use for matching in event handlers  
                    self.fromConfigListeners.push(ele); // keep a full current list for ref
                    theseListeners.push(ele); // but don't dupilicate events
                }
            });

            // theseListeners.each( function(ele){
            for (var i=0, len=theseListeners.length; i<len; i++) {
                var ele = theseListeners[i];
                // console.log("DOM ID IS ",EVENT.domID,EVENT,ele);
                if ($.inArray( ele, self.uniqueEvents) != -1) {
                    return;
                }
                self.uniqueEvents.push(ele);
                // ele.observe( EVENT.event, function (evt) {
                $(ele).bind( EVENT.event, function (evt) {
                    // console.log("running event ",evt);
                    // which events match in the current list?  
                    var cevents;
                    // self.jsEvents.each(function(CEVENT){
                    for (var j=0, jlen=self.jsEvents.length; j<jlen; j++) {
                        var CEVENT = self.jsEvents[j];
                        // CEVENT.hookIds.each(function(hooks){
                        for (var k=0, klen=CEVENT.hookIds.length; k<klen; k++) {
                            var hooks = CEVENT.hookIds[k];
                            var nodecheck = null;
                            if (hooks == evt.target.id){
                                nodecheck = evt.target.id;
                            } 
                            // // check for all parents as event may have been down from the configured event
                            // if (hooks == evt.target.parentNode.id) {
                            //     nodecheck = evt.target.parentNode.id; 
                            // }
                            // var i=1;
                            // while (arr = evt.target.up(i)){
                            //      if (hooks == arr.id){
                            //          nodecheck = arr.id;
                            //          break;
                            //      }
                            //     i = i+1;
                            // }
                            // i=1;
                            // while (arr = evt.target.parentNode.up(i)){
                            //      if (hooks == arr.id){
                            //          nodecheck = arr.id;
                            //          break;
                            //      }
                            //     i = i+1;
                            // }
                            // check for all parents as event may have been down from the configured event
                            var matchingParent = $(evt.target).parents("#" + hooks).first();
                            if (matchingParent.length > 0) {
                                nodecheck = matchingParent.attr("id");
                            }
                            if (nodecheck) {
                                cevents = CEVENT;
                                // console.log("HOOK MATCHED ",hooks,CEVENT);
                                // now we know the event and have the data, exec the tag according to type
                                self.execTagsbyType(CEVENT);
                            }
                        } // end for loop in CEVENT.hookIds
                        // ele.stopObserving();
                    } // end for loop in self.jsEvents
                }); // end $(ele).bind( EVENT.event )
            } // end for loop in theseListeners
        }, // end this._attachFrontendEvent


        // tags specificly connected to user actions and tags more closely connected to frontend events vs backend data, like prodcat    
        /*
        cmCreatePageElementTag(elementID, elementCategory,attributes) 
        cmCreateManualPageviewTag(pageID, categoryID,DestinationURL,ReferringURL) 
        cmCreateManualLinkClickTag(href,name,pageID) 
        cmCreateManualImpressionTag(pageID, trackSP, trackRE) 
        cmCreateErrorTag(pageID, categoryID) 
        */
        execTagsbyType: function(CEVENT) {

            // now config can use any JS var as param value.
            // Object.keys(CEVENT).each(function(param){
            //         if ( param.match("_") ){
            //             // console.log("match eval type param");
            //             // console.log ( CEVENT[param]  );
            //             newParam = param.replace(/_/,'');
            //             CEVENT[newParam] = eval( CEVENT[param] );
            //         }
            // });

            for (var param in CEVENT) {
                if ( param.match("_") ) {
                    console.log("match eval type param");
                    console.log ( CEVENT[param]  );
                    newParam = param.replace(/_/,'');
                    CEVENT[newParam] = eval( CEVENT[param] );
                }
            }

            if (CEVENT.type == 'conversion_event') {
                if (CEVENT.points < 1){
                    CEVENT.points = '"0"';
                }
                this.addPendingTags({"CoreMetrics":{"dom:loaded":[{"params":[CEVENT.eventID, CEVENT.actionType, CEVENT.cat, CEVENT.points,CEVENT.attributes],"tag":"cmCreateConversionEventTag"}]}});
            }
            if (CEVENT.type == 'element'){
                this.addPendingTags({"CoreMetrics":{"dom:loaded":[{"params":[CEVENT.elementID,CEVENT.elementCategory,CEVENT.attributes],"tag":"cmCreatePageElementTag"}]}});
            }
            if (CEVENT.type == 'mpageview'){
                this.addPendingTags({"CoreMetrics":{"dom:loaded":[{"params":[CEVENT.pageID,CEVENT.categoryID,CEVENT.DestinationURL,CEVENT.ReferringURL],"tag":"cmCreateManualPageviewTag"}]}});
            }
            if (CEVENT.type == 'mlinkclick'){
                this.addPendingTags({"CoreMetrics":{"dom:loaded":[{"params":[CEVENT.href,CEVENT.name,CEVENT.pageID],"tag":"cmCreateManualLinkClickTag"}]}});
            }
            if (CEVENT.type == 'mimpression'){
                this.addPendingTags({"CoreMetrics":{"dom:loaded":[{"params":[CEVENT.pageID,CEVENT.trackSP,CEVENT.trackRE],"tag":"cmCreateManualImpressionTag"}]}});
            }
            if (CEVENT.type == 'error'){
                this.addPendingTags({"CoreMetrics":{"dom:loaded":[{"params":[CEVENT.pageID,CEVENT.categoryID],"tag":"cmCreateErrorTag"}]}});
            }
            if (CEVENT.type == 'img'){
                this.execImageBlocks([CEVENT.src]);
            }
        },

        // find an attribute up from evt.target 
        findAttrUp: function(element, attr) {
            // if (element && element != "undefined") {
            if ($(element).length < 1) {
                return null;
            }
            //  current_element = element;
            //  if ( current_element.parentNode ){
            //      if ( current_element.parentNode.hasAttribute(attr) ){
            //          if ( current_element.parentNode.readAttribute(attr) ){
            //              console.log("Analytics CURRENT node HAS ATTR ");
            //              return current_element.parentNode.readAttribute(attr);
            //          } 
            //      }
            //  }   
            //  // find element that contains attr
            //  var nodecheck;
            //  var i=1;                                   
            //  while (arr = current_element.up(i)){            
            //      if ( arr.readAttribute(attr) ){
            //          nodecheck = arr;               
            //          break;                            
            //      }                                         
            //      i = i+1;                               
            // }                                          
            // i=1;                                       
            // while (arr = current_element.parentNode.up(i)){ 
            //     if ( arr.readAttribute(attr) ){                 
            //         nodecheck = arr;               
            //         break;                            
            //     }                                     
            //     i = i+1;                               
            // }                                          
            var matchingParent = $(element).parents( "[" + attr + "]" ).first();
            if (matchingParent.length > 0) {
                nodecheck = matchingParent[0];
            }
            // console.log("Analytics: loc node",nodecheck);
            return nodecheck;
        },

        // find element that contains attr
        findAttrDown: function(element,attr) {
            var nodecheck = [];
            // i=0;
            // while (arr = element.down(i)){
            //     if ( arr.readAttribute(attr) ){
            //         nodecheck.push(arr);
            //     }
            //     i = i + 1;
            // }
            var matchingChildren = $(element).children( "[" + attr + "]" );
            if (matchingChildren.length > 0) {
                nodecheck = matchingChildren;
            }
            return nodecheck;
        },

        // Possible special funtions for front end (reserved for future use)
        onDivShow: function() {},
        onFrameUpdate: function() {},
        onJsRedirect: function() {},

        crossSellCallback: function() {
            // empty function
        },

        getCrossSellData: function(args) {
            // console.log("cross sell call data called");
            var zone = '';
            if (typeof args.callback === "function") {
                this.crossSellCallback = args.callback;
            }
            if ( args.pageContext){
                if (args.pageContext == 'spp'){
                    zone = 'ProdPage';
                }
                if (args.pageContext == 'cart'){
                    zone ='CartPg1';
                }
            }else{
                return false;
            } 

            cmRecRequest(zone,PRODUCT_ID,CATEGORY_ID);
            cmDisplayRecs(); // calls either _ProdPage_zp or 
        },

        parseCrossSellData: function(rawCmDataArray) {
            // console.log("============rawCmDataArray=============");
            // console.dir(rawCmDataArray);
            // rawCmData --> parsedData (convert CM data to PG/BX JSON format)
            if (!$.isArray(rawCmDataArray)) {
                return null;
            }
            var parsedProductData = { products:[] };
            for (var i=0, len=rawCmDataArray.length; i<len; i++) {
                var rawProductDataArray = rawCmDataArray[i];
                if (!$.isArray(rawProductDataArray)) {
                    continue;
                }
                var prod = { skus: [ {} ] };
                var rawProd = rawProductDataArray[4].split("|");
                var rawSku = rawProductDataArray[5].split("|");
                var rawUrl = rawProductDataArray[6].split("---");
                // console.log("raw rec Url ", rawUrl);

                //if url is undef, it could be because of an old format product, skip to next rec as a last resort. 
                if (rawUrl == ""){
                    continue;
                }

                prod.url = rawUrl[1];
 
                for (var j=0, jlen=rawProd.length; j<jlen; j++) {
                    var keyVal = rawProd[j].split("---");
                    prod[keyVal[0]] = keyVal[1];
                }

                for (var j=0, jlen=rawSku.length; j<jlen; j++) {
                    var keyVal = rawSku[j].split("---");
                    prod["skus"][0][keyVal[0]] = keyVal[1];
                }
           
                parsedProductData.products[i] = prod;
            }
        
            return parsedProductData;
        },
 
        /* var _ProdPage_zp = function(rawCmData) {
        var parsedData = analytics.parseCallbackData(rawCmData);
        analytics.crossSellCallback(parsedData);
        };
        */

        _ProdPage_zp: function(a_product_ids, 
                zone,
                symbolic,
                target_id,
                category,
                rec_attributes,
                target_attributes,
                target_header_txt) {

            if (symbolic !== '_NR_') {
                var parsedData = this.parseCrossSellData(rec_attributes);
                this.crossSellCallback(parsedData);
            } else {
                this.crossSellCallback();// no recs use defaults 
            }
        },

        _CartPg1_zp: function(a_product_ids,
                zone,
                symbolic,
                target_id,
                category,
                rec_attributes,
                target_attributes,
                target_header_txt) {

            if (symbolic !== '_NR_') {
                var parsedData = this.parseCrossSellData(rec_attributes);
                this.crossSellCallback(parsedData);
                //this.crossSellCallback();
            } else {
                this.crossSellCallback();// no recs use defaults
            }
        },

        echoTags: function(url) {
            this.currentTags.push(url);
            console.log("CM TAG-",url);
        },

        getTags: function() {
            ret = this.currentTags;
            this.currentTags = [];
            alert(ret);
            return ret;
        },

        assignLocEvents: function(element, attr) {
            if ( $(element).length < 1) {
                return null;
            } 
            var nodecheck = this.findAttrDown(element,attr);  
            if (nodecheck.length < 1) {
                return null;
            }
            // nodecheck.each(function (element) { 
            for (var i=0, len=nodecheck.length; i<len; i++) {
                var element = nodecheck[i];
                $(element).bind( 'click', function(e) {
                    params = {};
                    loc = $(e.target).attr('loctmpl');
                    if (loc) {
                        location_params = loc.split(",");
                        params['TYPE_LOCATION'] = location_params[0];
                        params['PRODUCT_KEY'] = location_params[1];
                    }
                    if (params['TYPE_LOCATION']) {
                        // el.productView.reportProductView(params);
                    } else {
                        loc = Analytics.findAttrUp(e.target,'loctmpl');
                        if (loc) {
                            location_params = loc.split(",");
                            params['TYPE_LOCATION'] = location_params[0];
                            params['PRODUCT_KEY'] = location_params[1];
                            params['URL_CLICK'] = 1;
                            // el.productView.reportProductView(params);
                        }
                    }
                }); // end $(element).bind( 'click')
            }
        } // end this.assignLocEvents

    }; // end var AnalyticsObj

    AnalyticsObj._addStaticListeners();

    return AnalyticsObj;

} ) ();




