/*** 
ITEM = Creates the OPEN application parent object which the application is namespaced under.
PARAM =
VALUE = OPEN application parent object
***/
if(!window.OPEN) var OPEN = {};


/*** 
ITEM = Method extension to built-in Object.
PARAM = Any valid Object
VALUE = Returns the size( like array length ) of the object members
***/
Object.size = function(obj) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) size++;
    }
    return size;
};

//FIX IE TO HAVE array.indexOf()
//doesn't like to provide us with an array.indexOf() function, so, force it:
if(!Array.indexOf){
	Array.prototype.indexOf = function(obj){
		for(var i=0; i<this.length; i++){
			if(this[i]==obj){
				return i;
			}
		}
		return -1;
	}
}

/*** 
ITEM = Method to abstract event delegation
PARAM = An objec that defines the delegating element, and the function to run
VALUE = Returns a function closure holding the event state
***/
OPEN.eventDispatcher = function(rules){
    return function(e){
        var target = e.target;
        var targetNodeList = dojo.query(target);	
        for(var selector in rules){	
            if(targetNodeList.filter(selector).length){
                return rules[selector].apply(target, arguments);
            }
        }
    }
} 

/*** 
ITEM = Object to serialize / deserialize objects for cookie persistence
PARAM = Cookie name and Object to store, or cookie name to retrieve
VALUE = Stores a given object or returns the retrieved object
***/
OPEN.simplePersistence = {
    store: function(name, obj){
        dojo.cookie(name, dojo.toJson(obj), {expires: 7, path : "/"});
    },
    retrieve: function(name){
        return dojo.fromJson(dojo.cookie(name));
    }
};

/*** 
ITEM = Boolean flag used to notify the system that Card operations are the result of data being restored
from the cookie, if true. Otherwise, a user triggered an action that indicates the persistent state should update
PARAM = boolean true or false
VALUE = boolean true or false
***/
OPEN.restoreState = false;

/*** 
ITEM = Object to set/get page level variables with values set by AET
PARAM = 
VALUE = Stores a given object or returns the retrieved object
***/
OPEN.pageInfo = {
    pageName: "",
	context: "",
	contextPath: "",
	pageType: "",

	init: function() {
		if(typeof(aet)=="undefined" || typeof(aet.data)=="undefined") {
			//if aet.data does not exist (old implementation)
			
			if(typeof(aj_pageName)!="undefined") {
				this.pageName = aj_pageName;
			}
			try { this.context = aj_pageContext.toLowerCase(); } catch(e) {};
			this.contextPath = "ngaosbn";
			if(typeof(aj_PageType)!="undefined") {
				this.pageType = aj_PageType;
			}
			//change previously established global variables, done here for timing
			appContextPath = "/"+OPENINFO.contextPath+"/";
			jsContextPath = "/"+OPENINFO.contextPath+"/OAjs/";
		
			//label page with CSS class for specific targeting, if dojo is available
	        if(typeof(dojo)!="undefined") {
	            dojo.addClass(dojo.query("html")[0],this.context);
	        }
		} else {
			//if aet.data does exist...
			this.pageName = aet.data.page["name"];
			this.pageType = aet.data.page["type"];
			this.contextPath = "ngaosbn";
			try { this.context = aet.data.page["context"]; } catch(e) {};
		}
	}
};


/***  
ITEM = Parent object for utilities used on multiple pages
***/
OPEN.Utilities = {
	//pass an element to this to add an event listener that will add/remove class "hover" on mouseover/out
	//must be an array (dojo nodeList) so leave out [0]
	setHover: function(element) { //console.log("setHover");
		dojo.forEach(element,function(element,index,array) {
			dojo.connect(element,"onmouseover",function(element) {
				dojo.addClass(element.target,"hover");
			});	
			dojo.connect(element,"onmouseout",function(element) {
				dojo.removeClass(element.target,"hover");
			});
		});
	},
	
	//is this a flash page? do they have the right version? display the error if they dont
	checkFlash: function(){
        var flashError = document.getElementById("aj_flashError");
        if(document.getElementById("aj_flashContent")) {
            try{
                var playerVersion = swfobject.getFlashPlayerVersion(); // returns a JavaScript object
                var majorVersion = playerVersion.major; 
                if(majorVersion<8 || !majorVersion) {
                    flashError.style.display="block";
                } else {}
            }
            catch (err){
                flashError.style.display="block";
            }
        }
	},
	
	//cookie checker
	cookieCheck: function() {
	    dojo.cookie('test','test');
		var aj_cookies = dojo.cookie('test');
		if(aj_cookies != "test") {
			if(dojo.query("#aj_noCookies")[0]) {
				dojo.query("#aj_noCookies")[0].style.display="block";
			}
		}
	},
	
	// switch pertinent elements for secure paths
	changePathsSSL: function (){
		if(window.location.protocol == "https:"){
			var links = document.getElementsByTagName("a");
			for(var x = 0, ln = links.length; x < ln; x++ ){
				for(var item in SSLPaths){
					if( links[x].href.match(SSLPaths[item][0]) ){
						links[x].href = links[x].href.replace(/http:/i, "https:");
						links[x].href = links[x].href.replace(SSLPaths[item][0], SSLPaths[item][1]);
					}
				}
			}
			// now change the base getCardContextPath to secure for scripts that make use of it later.
			getCardContextPath = SSLPaths.getCardSecure[1];
		}
	},
	
	// dojo xhr helper functions.
	replaceNode: function (node, url){
		return {
			sync: true,
			url: url,       
			handleAs: "text",
			node: dojo.byId(node),
			load: function (data, xhr){
					if(xhr.args.node){
						var frag = document.createElement("div");
						frag.innerHTML = data;
						xhr.args.node.parentNode.replaceChild(frag, xhr.args.node);
					}
			},
			error: function(response, ioArgs){
				return response;	
			}
		};
	},

	appendToNode: function (node, url){
		return {
			sync: true,
			url: url,       
			handleAs: "text",
			node: dojo.byId(node),
			load: function (data, xhr){
					if(xhr.args.node){
						var frag = document.createElement("div");
						frag.innerHTML = data;
						xhr.args.node.appendChild(frag);
					}
			},
			error: function(response, ioArgs){
				return response;	
			}
		};
	},

	insertIntoNode: function (node, url){
		return {
			sync: true,
			url: url,       
			handleAs: "text",
			node: dojo.byId(node),
			load: function(data, xhr){
					if(xhr.args.node){
						xhr.args.node.innerHTML = "";
						xhr.args.node.innerHTML = data;
					}
			},
			error: function(response, ioArgs){
				return response;	
			}
		};
	},
	
	screenHeight: function () {
		return  window.innerHeight != null? window.innerHeight : document.documentElement && document.documentElement.clientHeight ?  document.documentElement.clientHeight : document.body != null? document.body.clientHeight : null;
	},
	
	screenTop: function() {
		return typeof window.pageYOffset != 'undefined' ?  window.pageYOffset : document.documentElement && document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ? document.body.scrollTop : 0;
	},
	
	/* turns a mutable collection, like a NodeList, into an array. useful because NodeLists are live
	and still part of the DOM, whereas an array will be stand alone copies of the objects */
	collectionToArray: function (collection) {
		var tmp = [];
		for(var i=0, len = collection.length; i < len; i++){ tmp.push(collection[i]); }
		return tmp;
	},
	
	/* pass element name to create and array of name/value objects for each attribute. */
	createNewElement: function(obj){
		var newElm = document.createElement(obj.element);
		if(typeof obj.attribs != "undefined")
			for(var i=0; i < obj.attribs.length; i++){
				for(property in obj.attribs[i]){
					newElm.setAttribute(property, obj.attribs[i][property]);
				}
			}
		return newElm;
	},
	
	/* remove children */
	removeChildrenFromNode: function (node) {
		if(node.hasChildNodes()) {
			while(node.childNodes.length >= 1 ) {
				node.removeChild(node.firstChild);       
			} 
		}
	}

}
/*
OPEN.init = function (){

  
};*/
/** TaskBar package **/
OPEN.TaskBar = {};

OPEN.TaskBar.init = function() {
	// construct the card popup menu
    OPENTB.CardMenu.init();
    OPENTB.CardMenu.Animation.init(); 
  
    // construct the card tray
    OPENTB.CardTray.init();
    OPENTB.CardTray.Animation.init();

    // wire up save-compare triggers that need to interact with the TaskBar
    OPENTB.SaveCompareTriggers.init();

    // get any cards we've persisted
    OPEN.TaskBar.RestoreTray();

    //adjust compare link according to number of cards in tray
    OPEN.TaskBar.CompareButton.init();

    dojo.query("#taskbar")[0].style.display="block";
};

/*** 
ITEM = TaskBar Object
VALUE = Container for member objects, and other possible operations the TaskBar may need.
***/
OPEN.TaskBar.Bar = {
   /* init: function(){
        //console.log("TaskBar initialized");
    }*/
};

/*** 
ITEM = CardTray Object
VALUE = Controls the list of cards in the TaskBar
***/
OPEN.TaskBar.CardTray = {
    cards : [],
    cardtray: null,
	numSlots: "",

    init: function(){
        // store ref to CardTray
        var self = this;

		//based on context (CardMember or Prospect), set the amount of cards
		this.numSlots = 4;

        /*@@ dojo.dnd.manager is a Singleton object. Changes made here affect ALL dnd behaviors on a page.
            It is worth considering an attempt to subclass this to make unique instances. Possible? Don't know yet... */
        var dndManager = dojo.dnd.manager();
        
        /*@@ the avatar offsets interact with these dojo functions in a bad way. it is a bug and causes dnd operations to fail.
            the functions are defined in dojo.dnd.autoscroll. they define two constants:
            dojo.dnd.V_AUTOSCROLL_VALUE = 16; and dojo.dnd.H_AUTOSCROLL_VALUE = 16;
            that happen to be the same values as the dojo.dnd.Manager constants OFFSET_X and OFFSET_Y. These are somehow related
            when dojo calculates positioning for dragged nodes and the drop targets they can drop to. Oddly, this all worked fine
            and then the bug manifested. I stripped all code to the bare bones and the problem still persisted. It was then I
            narrowed down to the apparent cause, the overriding of the OFFSET_* defaults. So, disabling the autoScroll by
            overidding with empty functions ( null will not work. dojo loader expects functions ), stops the problem, allows
            us to set our OFFSETS, it also prevents the scrolling problem but only in IE. Would still like to find a way to
            constrain to viewport. Too bad dojo does not provide proper interfaces to overriding behaviors....
        */
        dojo.dnd.autoScroll = function(e){};
        dojo.dnd.autoScrollNodes = function(e){};
        
        // change avatar offsets
        dndManager.OFFSET_X = -25;
        dndManager.OFFSET_Y = -50;

        // suppress avatar node "helper" text
        dojo.dnd.Avatar.prototype._generateText = function(){ return ""};

        // create drag source, set horizontal to get drag targeting to work left to right, call creator to build nodes
        this.cardtray = new dojo.dnd.Source("cardtray", {horizontal: true, creator: this.nodeCreator});
        
        // Source insertNodes requires a data array to tell it how many items to make. Also provides node metadata.
        this.cardtray.insertNodes(false, this.nodeDataCreator() );
        
        // suppress draggable on non-image card items
        this.cardtray.forInItems(function(item, id, map){
            if(item.data.type === "addCardSlot" || item.data.type === "cardslot")
            dojo.removeClass(dojo.byId(id), "dojoDndItem");
        }); 
        
        // wire card "remove" feature
        dojo.query("#cardtray").onclick(
            OPEN.eventDispatcher( 
            { 'span': function(){ self.updateSelfRemove(this.id) } }
        ));
        
        // listen for drop event, and animate the dropped node
        //@@ the anon function runs in the scope of the dojo object, not 'this'
        dojo.subscribe("/dnd/drop", function(){
            OPEN.TaskBar.CardTray.Animation.animateCardIn(self.cardtray.getSelectedNodes()[0]);
        });
        // handles returning card to its default place when a drop occurs outside the drop Source.
        //@@ scope of this is changed to the div, and cardDivOut must account for an empty event obj.
        dojo.subscribe("/dnd/cancel", function(e){
            OPEN.TaskBar.CardTray.Animation.cardDivOut.apply(self.cardtray.getSelectedNodes()[0]);
        });

        dojo.subscribe("/CardMenu/updateAdd", this, this.updateRemove);
        dojo.subscribe("/CardMenu/updateRemove", this, this.updateAdd);
        
        //console.log("CardTray initialized");
    },
    
    // helper method used by dojo.dnd.Source constructor for creating the tray nodes
    // nodeCreator will use this object to determine the created node content
    nodeDataCreator: function(){
        var nodeData = [];
        var addCardSlotFlag = false;
        
        for(var i=0; i < this.numSlots; i++){
            // if card object..
            if( this.cards[i] ){ nodeData.push( this.cards[i] ); }
            // or insert add card object
            else if( !addCardSlotFlag ){
                nodeData.push( OPEN.Cards.CardAddSlot );
                addCardSlotFlag = true;
            }
            // or empty slot object
            else{
                nodeData.push( OPEN.Cards.CardEmptySlot );				
            }
        }
        return nodeData;
    },
    
    // helper method used by dojo.dnd.Source constructor for creating the tray nodes
    // works in conjunction with nodeDataCreator
    nodeCreator: function (item, hint){ //console.log(item);
        var divTag = document.createElement("div");
        // need actual node for avatar to work.
        var img = document.createElement("img");
        img.src = "/"+OPENINFO.contextPath+"/OAimages/clear.gif";
        dojo.addClass(img, item.cssClass);
        if(item.type === "cardslot"){ dojo.addClass(divTag, "stopEvt"); }
        divTag.appendChild(img);
        if(item.type === "card"){
            var removeImg = document.createElement("span");
            dojo.addClass(removeImg, "hide");
            removeImg.id = item.pmc;
            divTag.appendChild(removeImg);
        }
        return {node: divTag, data : item};
    },
    
    getCardByID: function(id){
        if(OPEN.TaskBar.CardTray.cards) {
            for(var i in this.cards)
            if( this.cards[i].pmc == id ) {
                return this.cards[i];
            }
            return null;
        }
    },
    
    numCardsCheck: function() {
        if(OPEN.TaskBar.CardTray.cards.length < OPEN.TaskBar.CardTray.numSlots) {
//            console.log("numCards less than 5, numCards = " + OPEN.TaskBar.CardTray.cards.length);
			if (dojo.hasClass("cardtray","noCards")) { 
				var cardImgNodesTest = dojo.query("#cardtray .stopEvt")
				var duration = 1000; var position = -25;
	            cardImgNodesTest.forEach(function(item){ 
					OPEN.TaskBar.CardTray.Animation.animateCardInSlow(item,duration,position);
					duration = (duration+1000);
					position = (position-25);
	            });
			}
			dojo.removeClass("savedCards","noCards");
			dojo.removeClass("cardtray", "noCards");
			dojo.byId("savedCards").innerHTML = "Saved<br/>Cards";		
            return true;
        } else { 
            //console.log("numCards == 5");
            return false; }
    },
    
    cardInTrayCheck: function(cardObj) {
        var flag;
        if( OPEN.TaskBar.CardTray.getCardByID(cardObj.pmc)) {
            flag = 1;
        } else {
            flag=0;
        }
        if(flag==1) { return true; }
        else { return false; }
    },
    
    updateSelfRemove: function(pmc){
        var cardObj;
        for(var i in this.cards)
        if( this.cards[i].pmc === pmc ){
            cardObj = this.cards.splice(i,1)[0];
            this.removeFromTray( cardObj );
            this.updatePersistence(this.cards);
            dojo.publish("/CardTray/updateSelfRemove", [ cardObj ]); 
            break;
        }
    },
    
    updateRemove: function(cardObj){
        for(var i in this.cards)
        if( this.cards[i].pmc === cardObj.pmc ){
            cardObj = this.cards.splice(i,1)[0];
            this.removeFromTray( cardObj );
            this.updatePersistence(this.cards);     
            break;
        }
    },
    
    updateAdd: function(cardObj){
        //console.log("CT: updateAdd");
		
        if(this.cards.length == 5) { 
            OPEN.TaskBar.CardMenu.prepareModal(cardObj);
        } else {
            this.cards.push( cardObj );
            this.addToTray( cardObj );
            this.updatePersistence(this.cards);
        }
    },
    
    updatePersistence: function(cards){
        // store current cards
        if( !OPEN.restoreState )
        OPEN.simplePersistence.store("aj_cardsInTray", cards);
    },
    
    addToTray: function(cardObj){
        // get add slot div, get tray, get next
        var addDiv = dojo.query("#cardtray .addCardSlot")[0].parentNode;
        var trayDiv = addDiv.parentNode;
        var nxtSib = addDiv.nextSibling;
        var newNodeRef; // store node for animation
        if( nxtSib ){
            // remove sib, add cardObj node, move it to the beginning
            trayDiv.removeChild( nxtSib );
            this.cardtray.insertNodes(null, [cardObj]);
            newNodeRef = trayDiv.lastChild;
            trayDiv.insertBefore(trayDiv.lastChild, addDiv);
        }
        else{
            // replace addDiv with new cardObj
            trayDiv.removeChild( addDiv );
            this.cardtray.insertNodes(null, [cardObj]);
            newNodeRef = trayDiv.lastChild;
        }
        OPEN.TaskBar.CardTray.Animation.animateCardIn(newNodeRef);
    },
    
    removeFromTray: function(cardObj){
        // get card div, get tray, look for addCard and add that if needed
        var selector = "#cardtray ." + cardObj.cssClass;
        var cardDiv = dojo.query( selector )[0].parentNode;
        var trayDiv = cardDiv.parentNode;
        
        OPEN.TaskBar.CardTray.Animation.animateCardOut(cardDiv,cardObj);
        
        if(OPEN.TaskBar.CardTray.cards.length < 5) {
            //this needs a better solution at some point. IE adds the slot before the old card fades, 
            //causing the bar to jump
            setTimeout("OPEN.TaskBar.CardTray.addCardSlot()",1000);
        }
        
    },
    
    addCardSlot: function(cardObj) {
        var trayDiv = dojo.query("#cardtray")[0];
        var slotDiv = document.createElement("div");
        var slotImg = document.createElement("img");
        slotImg.src = "/"+OPENINFO.contextPath+"/OAimages/clear.gif";
        var addDiv = dojo.query("#cardtray .addCardSlot")[0];
        if(addDiv){ 
            dojo.addClass(slotImg, "cardslot");
            dojo.addClass(slotDiv, "stopEvt"); 
        }
        else{ dojo.addClass(slotImg, "addCardSlot"); }
        slotDiv.appendChild( slotImg );
        trayDiv.appendChild( slotDiv );
    }
};

/*** 
ITEM = CardTray Animation Object
VALUE = Indicates to user they can remove a card, or drag it. Triggered by Mouse Over events.
***/
OPEN.TaskBar.CardTray.Animation = {
    init: function(){
        // get item lists
        var cardtray = dojo.query("#cardtray");
        
        cardtray.onmouseover(
            OPEN.eventDispatcher( 
                {'img': this.cardImgOver}
        ));
        
        cardtray.onmouseout(
            OPEN.eventDispatcher( 
                {'img': this.cardImgOut, 'div': this.cardDivOut}
        ));
        
        cardtray.onclick(
            OPEN.eventDispatcher( 
                {'.addCardSlot': OPEN.TaskBar.CardMenu.Animation.show}
        ));
        
        // attach class as 'switch' to suppress event when a drag starts, remove when dropped
        dojo.subscribe("/dnd/start", cardtray[0], function(){
            var cardImgNodes = dojo.query("div img", this);
            cardImgNodes.forEach(function(item){ 
                dojo.addClass(item, "stopEvt"); 
            });
        });
        dojo.subscribe("/dnd/drop", cardtray[0], function(){
            var cardImgNodes = dojo.query("div img", this);
            cardImgNodes.forEach(function(item){
                if(!dojo.hasClass(item, "cardslot"))
                dojo.removeClass(item, "stopEvt");
            });
        });
    },

    // order of statements is important. first, check if drag, then check if non-cards.
    specialEvents: function(obj, evt){
        // stop event if drag is in progress
        if( dojo.hasClass(obj, "stopEvt") ){
            dojo.stopEvent(evt);
            return true;
        } 
        // stop animation( but allow events to propagate) if not a card img, handle addCardSlot display
        if( dojo.hasClass(obj, "addCardSlot") || dojo.hasClass(obj, "cardslot") ){
            if( dojo.hasClass(obj, "addCardSlot"))
            dojo.toggleClass(obj, "addCardSlotOver");
            return true;
        }
        // if moused to child, stop event
        if( dojo.isDescendant(evt.relatedTarget, obj) ){
            dojo.stopEvent(evt);
            return true;
        }      
        return false;
    },
    
    animateCardIn: function(node) {
//		console.log("animateCardIn, node = " + node);
        dojo.animateProperty({
        node: node, duration: 1000,
        properties:{
            top: { start:-25, end:0 }, opacity: { start:0, end:1 }
        },
        easing: dojo.fx.easing.bounceOut
        }).play();
    },

    animateCardInSlow: function(node,duration,start) {
//		console.log("animateCardInSlow, node = " + node + ", duration = " + duration);
        dojo.animateProperty({
        node: node, duration: duration,
        properties:{
            top: { start:start, end:0 }, opacity: { start:0, end:1 }
        },
        easing: dojo.fx.easing.bounceOut
        }).play();
    },
    
    animateCardOut: function(node) {
//		console.log("animateCardOut, node = " + node);
        dojo.animateProperty({
        node: node, duration: 1000,
        properties:{
            top: { start:0, end:-25 }, opacity: { start:1, end:0 }
        },
        onEnd: function(){ 
                node.parentNode.removeChild(node); 
            }
        }).play();
    },

    cardImgOver: function(e){
        if( OPEN.TaskBar.CardTray.Animation.specialEvents(this, e) ) return;

        var animProps = {top: { start:0, end:-15 }, opacity: { start: .65, end: 1 } };
        // if moving from remove to card, after first entering this card area, just do opacity
        if(this.style.position == "absolute"){ animProps = { opacity: { start: .65, end: 1 } }; }

        dojo.animateProperty({
            node: this, duration: 200, properties:animProps,
            beforeBegin: function() {
                this.node.style.position = "absolute";
                this.node.style.cursor = "move";
                dojo.query("span", this.node.parentNode).removeClass("hide");
            } 
        }).play();
    },
    
    cardImgOut: function(e){
        if(OPEN.TaskBar.CardTray.Animation.specialEvents(this, e) ) return;
        // if mouseout from raised card to page content, execute cardDivOut( and switching the context to the div )
        if( !dojo.isDescendant(e.relatedTarget, this.parentNode.parentNode) ){
            dojo.stopEvent(e); 
            OPEN.TaskBar.CardTray.Animation.cardDivOut.apply(this.parentNode, [e]);
            return;
        }
        
        // prevents delegate from causing this to fire when a drag to a card triggers it
        // after dropping the dragged item. because this "moves" the card you are dropping
        // on to, it makes this fire as you are over the card.
        if( !dojo.isDescendant(e.relatedTarget, this.parentNode)){
            dojo.stopEvent(e);
            return;
        }

        dojo.animateProperty({
            node: this, duration: 200,
            properties:{ opacity: { start: 1, end: .65 } }
        }).play();
    },
    
    cardDivOut: function(e){
        //@@ can be called from two contexts. a mouseout from the cardDiv, or from a drop event NOT on the drop Source
        //@@ this means there will be NO event object for the context. See CardTray.init subscriber for this case.
        if(e)
            if(OPEN.TaskBar.CardTray.Animation.specialEvents(this, e) ) return;

        var img = dojo.query("img", this)[0];
        // @@ inside animateProperty, context of 'this' is no longer the div
        var thisDiv = this;
        dojo.animateProperty({
            node: img, duration: 1000,
            properties:{
                top: { start:-15, end:0 }, opacity: { start: .65, end: 1 }
            },
            easing: dojo.fx.easing.bounceOut,
            beforeBegin: function() { dojo.query("span", thisDiv).addClass("hide"); },
            onEnd: function() { this.node.style.position = "static"; }
        }).play();
    }
};

/*** 
ITEM = CardMenu Object
VALUE = Controls the popup menu of cards for the user to choose to add to the CardTray
***/
OPEN.TaskBar.CardMenu = {
    cards : [],
    menu: null,
    swapCard : null,
    
    init: function(){
        this.menu = dojo.query("#addCardMenu ul")[0];
        this.cardMenuLoader(this, this.menu);
        dojo.subscribe("/CardTray/updateSelfRemove", this, this.updateAdd);
        dojo.subscribe("/SaveCompare/saved", this, this.updateRemove);
        dojo.subscribe("/SaveCompare/unsaved", this, this.updateAdd);
        dojo.subscribe("/TaskBar/restoreTray", this, this.updateRestore);
        
        dojo.query("#addCardMenu ul").onclick(
        OPEN.eventDispatcher( 
            {'li': this.updateSelfRemove}
        ));
	
		
        //console.log("CardMenu initialized");
    },
    
    cardMenuLoader: function(menuObj, menuNode){
		//console.log('in cardMenuLoader OPEN = ' + OPEN);
        var CD = OPEN.Cards.CardData, CARDS = OPEN.Cards;
        // create card and push on to card menu array, sort
        for(var key in CD){        
            menuObj.cards.push( new CARDS.Card(key, CD[key]) );
        }
        menuObj.cards.sort(CARDS.sortCards);
        // now build and insert nodes from the list
        for(var i in menuObj.cards){ 
			if(typeof(menuObj.cards[i])!="function") {
	            var li = document.createElement("li");
	            dojo.addClass(li, menuObj.cards[i].cssClass);
//		    	li.setAttribute("title","hellow2" + menuObj.cards[i].legalName);
				var a = document.createElement("a");
//	        	if(!dojo.isIE=="6") {
				    a.setAttribute("title",menuObj.cards[i].legalName);
//				}
//				li.appendChild(a);
	            menuNode.appendChild(li);
			}
        }
    },
    
    getCardByID: function(id){
        for(var i in this.cards)
        if( this.cards[i].pmc === id )
        return this.cards[i];
        return null;
    },

    updateAdd: function(cardObj){ console.log(cardObj);
        // store card, sort, prepare node
        console.log("CM: updateAdd");
        this.cards.push( cardObj );
        this.cards.sort(OPEN.Cards.sortCards);          
        var li = document.createElement("li");
//	    li.setAttribute("title","hellow1" + cardObj.legalName);
		var a = document.createElement("a");
		dojo.addClass(a, cardObj.cssClass);
//        if(!dojo.isIE=="6") {
		    a.setAttribute("title",cardObj.legalName);
//		}
//		li.appendChild(a);
		//console.log('appending child a to li in cardtray, cssClass=' + cardObj.cssClass);
		
//        if(!dojo.isIE=="6") {
//		    li.setAttribute("title",cardObj.legalName);
//		}
        dojo.addClass(li, cardObj.cssClass);
        // insert card into menu
        for(var i in this.cards){
			console.log(i);
            if(this.cards[i].ordinal > cardObj.ordinal){
                var sibling = dojo.query("." + this.cards[i].cssClass, this.menu)[0];
                this.menu.insertBefore(li, sibling);
                break;
            } else {

                this.menu.appendChild(li);

            }

        }
        dojo.publish("/CardMenu/updateAdd", [ cardObj ]);
    },
    
    updateRemove: function(cardObj){
        //console.log("CM: updateRemove ");
		//console.log(cardObj);
        if(OPEN.TaskBar.CardTray.numCardsCheck()) {
            
            if(!OPEN.TaskBar.CardTray.cardInTrayCheck(cardObj)) {
	            for(var i in this.cards){
	                if( this.cards[i].pmc == cardObj.pmc ){
	                    var child = dojo.query("li." + this.cards[i].cssClass, this.menu)[0];
	                    if(child) {
	                      this.menu.removeChild(child);
	                      cardObj = this.cards.splice(i,1)[0];
	                    }
	                    break;
	                } 
	            }
	            dojo.publish("/CardMenu/updateRemove", [cardObj]);
            }
			if(OPEN.TaskBar.CardMenu.swapCard!=null) {
			    OPEN.TaskBar.CardMenu.swapCard="";
			}
        } else { console.log("PREPARE");
            this.prepareModal(cardObj);
        }
    },
    
    updateSelfRemove: function(e){
        //console.log("updateSelfRemove");
        if(OPEN.TaskBar.CardTray.numCardsCheck) {
            var STCM = OPEN.TaskBar.CardMenu;
            for(var i in STCM.cards){
                if( STCM.cards[i].cssClass == this.className ){
                    STCM.updateRemove(STCM.cards[i]);
                    break;
                }
            }
            OPEN.TaskBar.CardMenu.Animation.hide(e);
            if(dojo.query(".aj_cardNameFlyout")!="") {
                OPEN.TaskBar.CardMenu.Animation.hideNameFlyout();
            }
        } else {}
    },
    
    updateRestore: function(cardsObj){ //console.log("updateRestore")
        for(var i in cardsObj){ 
			if(typeof(cardsObj[i])=="object") {
				this.updateRemove(cardsObj[i]); 
			}
		}
        OPEN.restoreState = false;
    },
    
    prepareModal: function(swapCard) { //console.log("CARD MENU :: prepareModal()");
        var modalContentDiv = $("#modal_tooManyCardsError");
        var modalContentParent = $("#modal_tooManyCardsError .aj_modalWindowBody"); 
        var trayParent = $("#aj_modalWindowRemoveCards");
        //store reference to content div for passing to Modal functions
        var content = modalContentDiv;
        
        // get card to swap, store it for later use
        this.swapCard = swapCard;
        
        //remove old card content if there is any
        trayParent.empty();

        // create modal tray
        var cloneTray = OPENU.createNewElement( {
            element:"div", 
            attribs:[{id: "clonedTray"}]
        });

        // create modal cards and markup
        for(var i=0; i < OPEN.TaskBar.CardTray.cards.length; i++){
            // get card...
            var card = OPEN.TaskBar.CardTray.cards[i];   
            // wrapper holds all the pieces, it gets special id and keys for later use
            var wrapper = OPENU.createNewElement( {
                element:"div", 
                attribs:[{"id": "clone_" + card.cssClass},{"key": card.pmc}]
            });
            var cardElement = OPENU.createNewElement({
                element: "div",
                attribs:[{"id": "_"+card.cssClass},{"key": card.pmc}]
            });
            //cardElement.setAttribute("class",card.cssClass);
            
            wrapper.className="aj_card_wrapper";
            // the card name blurb
            var cardTxt = OPENU.createNewElement( {
                element:"div"
            });
            cardTxt.className="aj_remTxtBox";
			cardTxt.innerHTML = card.legalName;
            
            // spacer row, works great in FF, IE needs help still...
            var blocker = OPENU.createNewElement( { element:"div" } );
            blocker.className="aj_modalblock";
            
            // the checkbox to select a card to remove, uses key as the value
            var chkbox = OPENU.createNewElement( {
                element:"input",
                attribs:[{"type": "checkbox", "name": "aj_remCard", "value": i, "id": "aj_remCard"+i}]
            });

			var lbl = OPENU.createNewElement( { 
				element:"label",
				attribs:[{"for": "aj_remCard"+i}]
			});
            lbl.appendChild( document.createTextNode(" remove card") );
			
            
            // attach it all... 
            wrapper.appendChild(cardElement);
            $(cardElement).addClass("aj_remImg").addClass(card.cssClass);
            wrapper.appendChild(cardTxt);
            wrapper.appendChild(blocker);
            wrapper.appendChild(chkbox);
			wrapper.appendChild(lbl);
            cloneTray.appendChild(wrapper);
            //attach the remove card text node last
        }
        // attach it all... 
        $("#aj_modalWindowRemoveCards").append(cloneTray);
		// wire up the continue button for action when card limit is exceeded and user makes changes
        // in modal dialog
		var continueHandler = function(e) {
            var chkboxes = $("#aj_modalRemoveForm input");
            var cardObjs = [];
            var callFlag = 0;
            for(var i=0; i < chkboxes.length; i++){ 
                if(chkboxes[i].checked){
                    cardObjs.push(OPEN.TaskBar.CardTray.cards[ chkboxes[i].value ]);
                    callFlag++;
                }
            }
            if(callFlag>=1) {
                for(var i=0; i < callFlag; i++) {
                    OPEN.TaskBar.CardMenu.updateAdd(cardObjs[i]);
                }
				//add the currently selected card( the one they tried to add after having max already )
				swapCardTimer = setTimeout("OPEN.TaskBar.CardMenu.updateRemove(OPEN.TaskBar.CardMenu.swapCard)",1000);
				$(this).unbind("click",continueHandler);
                // close too many cards dialog
                OPEN.Modal.hide(OPEN.Modal.cardTrayError);
	   			$('#aj_onecardrequired_error').attr("class","hide");
            } else { 
     			$('#aj_onecardrequired_error').attr("class","show");
            }
        };
		
		$("#aj_modal_continue").bind('click', continueHandler);
		
        // close too many cards dialog
        $("#modal_tooManyCardsError .aj_modal_cancel").click(function() {
   			$('#aj_onecardrequired_error').attr("class","hide");
			OPEN.TaskBar.CardMenu.swapCard="";
			if(!!continueHandler) {
			    $(this).unbind("click",continueHandler);
			}
        });
        OPEN.Modal.show(OPEN.Modal.cardTrayError);
        
    }
};
/*** 
ITEM = CardMenu Animation Object
VALUE = Controls logic for scrolling/dropping from CardMenu.
***/
OPEN.TaskBar.CardMenu.Animation = {
    aj_scrollTimerDown: null,
    aj_scrollTimerUp: null,
    
    init: function(){
        var cardMenuObj = dojo.query("#addCardMenu");
        var STCA = OPEN.TaskBar.CardMenu.Animation;

        cardMenuObj.onmouseover(function(e) {
            OPEN.eventDispatcher({
                '#addCardDown': STCA.scrollDown,
                '#addCardUp': STCA.scrollUp
            })(e);
        });

        cardMenuObj.onmouseout(function(e){
            if(e.target.id=="addCardDown"){
                e.stopPropagation;
                STCA.scrollStop(e);
            } 
            else if(e.target.id=="addCardUp"){
                e.stopPropagation;
                STCA.scrollStop(e);
            } 
            else if(e.target.id=="addCardMenu" && !dojo.isDescendant(e.relatedTarget, this)){
                STCA.hide(e);
            } 
            else {
                dojo.stopEvent(e);
                return;
            }
        });
        //prepare name flyout handlers
        dojo.query("#addCardMenu").onmouseover(function(e) {
            OPEN.eventDispatcher( 
            { 'li': function(e){ OPEN.TaskBar.CardMenu.Animation.showNameFlyout(e); } }
            )(e);
        });
        dojo.query("#addCardMenu").onmouseout(function(e) {
            OPEN.eventDispatcher( 
            { 'li': function(e){ OPEN.TaskBar.CardMenu.Animation.hideNameFlyout(e); } }
            )(e);
        });
    },

    scrollUp: function(e){
        var listEl=dojo.query("#addCardMenu div#cardScroller")[0];
        if(listEl.scrollTop != 0){
            listEl.scrollTop--;
            this.aj_scrollTimerUp = setTimeout("OPEN.TaskBar.CardMenu.Animation.scrollUp()", 1);
        } 
        else { OPEN.TaskBar.CardMenu.Animation.scrollStop(); }
    },
    
    scrollDown: function(e) {
        var listEl=dojo.query("#addCardMenu div#cardScroller")[0];
        if (dojo.query("div#cardScroller ul")[0].clientHeight != listEl.scrollTop) {
            listEl.scrollTop++;
            this.aj_scrollTimerDown = setTimeout("OPEN.TaskBar.CardMenu.Animation.scrollDown()", 1);
        } 
        else{ OPEN.TaskBar.CardMenu.Animation.scrollStop(); }
    },
    
    scrollStop: function() {
        if(this.aj_scrollTimerUp!=null) {
            clearTimeout(this.aj_scrollTimerUp);
        }
        if(this.aj_scrollTimerDown!=null){
            clearTimeout(this.aj_scrollTimerDown);
        }
    },
    
    show: function(){
        var addCard_xy = dojo.coords(this);
        var cardMenu = dojo.query("#addCardMenu")[0];
        cardMenu.style.left=addCard_xy.x+"px";
        cardMenu.style.height="162px";
        dojo.toggleClass(cardMenu,"menuShow");
    },
    
    hide: function(e){
        if(dojo.isDescendant(e.relatedTarget, this)){ dojo.stopEvent(e); } 
        else { dojo.toggleClass("addCardMenu","menuShow"); }
    },
    
    showNameFlyout: function(e){
        /*if(dojo.query(".aj_cardNameFlyout")!="") {
                    OPEN.TaskBar.CardMenu.Animation.hideNameFlyout();
                }*/
        var STCM = OPEN.TaskBar.CardMenu;
        for(var i in STCM.cards){
            if( STCM.cards[i].cssClass == e.target.className ){
                var p = document.createElement("p");
                //p.setAttribute("class","aj_cardNameFlyout");
                dojo.addClass(p,"aj_cardNameFlyout");
                p.innerHTML = STCM.cards[i].legalName;
                //var p_xy = dojo.coords(e.target);
                //var xy = {"x": p_xy.x, "y":p_xy.y};
                //console.log(xy);
                var appendParent = dojo.query("#taskbar_align")[0];
                var addCardXOffset = dojo.query("#addCardMenu")[0].style.left;
                addCardXOffset=addCardXOffset.replace(/px/,"");
                addCardXOffset=addCardXOffset-170;
                addCardXOffset=addCardXOffset+"px";
                p.style.left=addCardXOffset;
                appendParent.appendChild(p);
                //set timeout for fadeout of flyout after 5 seconds
                break;
            }
        }
    },
    
    hideNameFlyout: function(e){
        var flyout = dojo.query(".aj_cardNameFlyout")[0];
        flyout.parentNode.removeChild(flyout);
    }
};

OPEN.TaskBar.RestoreTray = function(){
	var cardTrayIsEmpty = true;
    // restore any cards from prior session
    var storedCards = OPEN.simplePersistence.retrieve("aj_cardsInTray");
    if(storedCards) { 
    	if (storedCards.length > 0) { 
			cardTrayIsEmpty = false;
		}
    }
	//console.log('cardTray RestoreTray');	

    if(cardTrayIsEmpty) {
		//console.log('cardTrayIsEmpty (empty) = ' + cardTrayIsEmpty);
		//do alternate 'start here' layer here via CSS manipulation
		dojo.addClass("savedCards", "noCards");
		dojo.byId("savedCards").innerHTML = "Want to Compare Multiple Cards?";		
		dojo.addClass("cardtray", "noCards");
	} else { 
	//	console.log('storedCards.length (1 or more) = ' + storedCards.length);
        OPEN.restoreState = true;
		dojo.byId("savedCards").innerHTML = "Saved<br/>Cards";		
        dojo.publish("/TaskBar/restoreTray", [storedCards]);
    }
};

/*** 
ITEM = ExternalItems Object
VALUE = Controls logic for items that need to interact with, or be aware of, change in the CardTray.
***/
OPEN.TaskBar.SaveCompareTriggers = {
    init: function(){
        // saveCompare delegate
        dojo.query("body").onclick(
        OPEN.eventDispatcher( 
            { ".add-to-saved-cards" : this.updateSaveCompare ,
			  ".btn_save_to_compare" : this.updateSaveCompare }
        ));
        dojo.subscribe("/CardMenu/updateRemove", this, this.updateCardMenuChanged);
        dojo.subscribe("/CardMenu/updateAdd", this, this.updateCardMenuChanged);
    },
    
    updateSaveCompare: function(e){ //console.log("updateSaveCompare");
		e.preventDefault();
		var cardObj, classes, nodeID;
        // if it's in the tray, check tray list
		
		classes = this.className.split(/ /);
		nodeID = classes[0];
		
        if( dojo.hasClass(this, "saveCompare_saved") ){
            cardObj = OPENTCT.getCardByID( nodeID );
            if( cardObj == null ) return;
            OPENTCM.updateAdd(cardObj);
			//dojo.publish("/SaveCompare/saved", [ cardObj ]);
        } // or it's in the menu, so check its list
        else{
            cardObj = OPENTCM.getCardByID( nodeID );
          if( cardObj == null ) return;
            OPENTCM.updateRemove(cardObj);
			//dojo.publish("/SaveCompare/unsaved", [ cardObj ]);
        }
    },
    
    updateCardMenuChanged: function(cardObj){ //console.log("updateCardMenuChanged");
	//console.log(cardObj);
		//dojo.forEach(OPENTCT.cards,function(itm,idx,arr) {
			var spansToFlip = dojo.query("."+cardObj.pmc);
			if(typeof(spansToFlip)!="undefined" && spansToFlip!=[]) {
				dojo.forEach(spansToFlip, function(item,index,array) {
					dojo.toggleClass(item, "saveCompare_saved");
				});
			}
		//});
    }
};

OPEN.TaskBar.CompareButton = {
	compareBtnURL: "/business-credit-cards/business-card-compare?p=ctray&pmccodes=",
	compareBtnSecureURL: "/business-credit-cards-secure/business-card-compare?p=ctray&pmccodes=", 

    // if two or more cards in tray, create query string value of comma-delimited keys to pass to compare page
    init: function(){
        this.buttonEl= dojo.query("#compareButton a")[0];
        this.handleState();
        dojo.subscribe("/CardTray/updateSelfRemove", this, this.handleState);
        dojo.subscribe("/CardMenu/updateRemove", this, this.handleState);
        dojo.subscribe("/CardMenu/updateAdd", this, this.handleState);
        dojo.query("#compareButton a").onclick(function(e) {
            OPEN.TaskBar.CompareButton.cancelCompareLink(e);
        });
    },

    // compare link should only be active if there are two or more cards
    handleState: function() {
        var cNodes = OPEN.TaskBar.CardTray.cards;
        var str = "";
        var button = this.buttonEl;
        var SJTBCB = OPEN.TaskBar.CompareButton;

        if(cNodes.length >= 2){
            for(var i=0,ln=cNodes.length; i < ln; i++){
                str += cNodes[i].pmc + ",";
            }
            str = str.slice(0, str.length -1);
            str = encodeURIComponent(str);
            if(window.location.pathname.match("secure")) {
                button.setAttribute("href", SJTBCB.compareBtnSecureURL + str);
            } else {
                button.setAttribute("href", SJTBCB.compareBtnURL + str);
            }
            dojo.removeClass(button, "disabled");
        }
        else{
            OPEN.TaskBar.CompareButton.buttonEl.setAttribute("href", "");
            dojo.addClass(button, "disabled");
        }
    },
    // cancel compare button default link action if it is in disabled mode
    cancelCompareLink: function(e){ 
        if(OPEN.TaskBar.CardTray.cards.length < 2){
            dojo.stopEvent(e);
        }
    }
};

/*** 
ITEM = Object to handle all Modal operations
***/
OPEN.Modal = {
    width: "0px",
    height: "0px",
	
	//modal object references, set during init
	receiveEmail: {},
	cardTrayError: {},
    
    init: function() { 
		var self = this;
		
        //CONFIG: individually init so we save references for closing/opening in the future.
		if($("#receiveEmailOffers").length>0) {
			this.receiveEmail = $("#receiveEmailOffers").data("objRef",( 
				$("#receiveEmailOffers").dialog({ draggable: false, resizeable: false, zIndex: 3999, autoOpen: false, modal: true, width: 651  }) 
			));
		}
		if($("#modal_tooManyCardsError").length>0) {
			this.cardTrayError = $("#modal_tooManyCardsError").data("objRef",( 
				$("#modal_tooManyCardsError").dialog({ draggable: false, resizeable: false, zIndex: 3999, autoOpen: false, modal: true, width: 663 })
			));
		}
        
        //fancify for good browsers
        if(jQuery.browser.msie==false) {
            $(".aj_modal").each( function(i) { $(this).data("objRef").dialog("option","show","explode");
            $(this).data("objRef").dialog("option","hide","explode"); });
        }
		
		//AUTO: init cancel buttons/CTAs
		$(".aj_modal_cancel").click(function(e) { 
			e.preventDefault();
			OPEN.Modal.hide($(e.target).closest(".aj_modal"));
		});
    },
    
	center: function modalCenter() { //console.log("center");
		var top = ($(window).scrollTop() + (document.documentElement.clientHeight/2)) - (OPEN.Modal.active.closest(".ui-dialog").height()/2);
		OPEN.Modal.active.closest(".ui-dialog").css("top",top);
	},
	
    setContent: function(contentReference) { //console.log("SET CONTENT"); console.log(contentReference);
        var modalContentParent = dojo.query(".aj_modalWindowBody")[0];
        //check for existing content; if there is any, dump it
        if(modalContentParent.hasChildNodes()) {
            var contentChild = modalContentParent.childNodes[0];
            var contentStoreParent = dojo.query("#modalContentPieces")[0];
            
            var removedContent = modalContentParent.removeChild(contentChild);
            contentStoreParent.appendChild(removedContent);
        }
        //grab content piece and append it
        content = contentReference.parentNode.removeChild(contentReference);
        modalContentParent.appendChild(content);
    },
    
    show: function(modalDivObj) { 
		var self = this;
		modalDivObj.data("objRef").dialog("open");
        
		//save reference on OPEN.Modal object to active modal
		this.active = modalDivObj;
		
		//center it
		setTimeout(OPEN.Modal.center,700);
		
		//attach scroll event so we can keep the thing centered in the viewport while open
		$(window).bind("scroll",self.center);
    },
    
    hide: function(modalDivObj) { 
		var self = this;
		modalDivObj.data("objRef").dialog("close");
		
		//unbind centering function so we don't eat up memory senselessly
		$(window).unbind("scroll",self.center);
    }
};

OPEN.receiveEmailOffers = {
	buttonElement: "",
	contentDivId: "receiveEmailOffers",
	
	init: function() { //console.log("BEGINNNNNNNNN");
		//event handler
		$("#signUp").click(function(e) { //console.log("EMAIL SHOW");
            e.preventDefault();
			OPEN.receiveEmailOffers.prepareModal();
		});
	
		$("#receiveEmailOffersSubmit").bind("click",function(e) {  //console.log("CLICK");
			e.preventDefault();
			OPEN.receiveEmailOffers.validate();
		});
			
		$("#receiveEmailOffers .aj_modal_cancel").bind("click",OPEN.receiveEmailOffers.reset);

	},
    
    reset: function() { console.log("reset");
			$("#aj_signupFrm")[0].reset();
            $(".aj_emailerrormsg").addClass("hide");
			$("#aj_thanksmsg").addClass("hide");
			$(".aj_popcontent").removeClass("hide");
    },
	
	prepareModal: function() {
		OPEN.Modal.show(OPEN.Modal.receiveEmail);
	},
	
	validate: function receiveEmailValidate() { //console.log("validate");
	  //INIT
	  var error = false, pattern;
		//if(! regexp: function(value,pattern)) {
		
		$("#aj_signupFrm input[type='text']").each(function(i) { //console.log(this);
			//get regexp from markup
			pattern = $(this).attr("regExp");
			//pass to validator
			
			//console.log(pattern);
			//console.log(OPEN.validation.regexp(pattern));
			//console.log($(this)[0].value.length);
			
			if(this.value.length>0) { //console.log($(this).attr("id")+" LENGTH : "+this.value.length);
				if($(this).attr("regExp")) { console.log($(this).attr("id")+" REGEXP: YES");
					if(!OPEN.validation.regexp(this.value,pattern)) { //console.log($(this).attr("id")+" REGEXP: FAILED :"+pattern);
						$("#aj_"+$(this).attr("id")+"_error").removeClass("hide");
						error = true;
					} else { //console.log($(this).attr("id")+" REGEXP: PASSED :"+pattern);
						$("#aj_"+$(this).attr("id")+"_error").addClass("hide");
					}
				} else { //console.log($(this).attr("id")+" REGEXP: NONE");
					$("#aj_"+$(this).attr("id")+"_error").addClass("hide");
				}
			} else { //console.log($(this).attr("id")+" LENGTH : TOO SHORT");
				$("#aj_"+$(this).attr("id")+"_error").removeClass("hide");
				error = true;
			}
			
			if(!($("#email")[0].value==$("#aj_cemailadd")[0].value)) {
				$("#aj_cemailadd_error").removeClass("hide");
				error = true;
			} else {
				$("#aj_cemailadd_error").addClass("hide");
			}
			
		});
	
		if (error == true) {
			error = false;
		}
		else {
			$('#receiveEmailOffers .aj_popcontent').addClass('hide');
			$('#aj_thanksmsg').addClass('aj_popbody');
			$('#aj_thanksmsg').removeClass('hide');
			document.aj_signupFrm.submit();
		}
	}

};

//method must be added to omniture object to evaluate ajax calls, i think, to make it self-sufficient
OPEN.Omniture = {
	marketCode: "",
	pageName: "",
	omnCustomArray: [],
	
    init: function() {
		
		//get the right map based on our page
		var src = (OPENINFO.context=="cardmember") ? "aj_omn_"+OPENINFO.pageType+"_cm.js" : "aj_omn_"+OPENINFO.pageType+".js";
		var map = document.createElement("script");
		map.setAttribute("src",jsContextPath+src);
		dojo.query("head")[0].appendChild(map);

		dojo.connect(document.body,'onclick',function(e){ 
			//below preventDefault for use during configuration only. 
			
				//e.preventDefault(); 
				//console.log(e.target);
			
			OPENOMN.omniListener(e); 
		});
		
		try { this.pageName=omn_pagename; } catch(e) { console.log("ERROR: couldn't set omniture pagename value!"); }
		this.marketCode = "US:Acq:OPEN:";
		
		this.loadCheck();
	},
	//execute only if global map has loaded. store count on function object.
	loadCheck: function() { //console.log("loadcheck() "+this.count);
		var globalMap = OPENOMN.globalTrackedItems;
		var pageMap = OPENOMN.pageTrackedItems;
		if(typeof(globalMap)!="undefined" && globalMap.size!=0 && typeof(pageMap)!="undefined" && pageMap.size!=0) {
			OPENOMN.attrbAssigner(OPENOMN.globalTrackedItems);
			OPENOMN.attrbAssigner(OPENOMN.pageTrackedItems);
			OPENOMN.customTracking();
		} else {
			if(typeof(this.count)=="undefined") { this.count=0; }
			if(this.count<8) {
				setTimeout("OPENOMN.loadCheck();",1000);
				this.count+=1;
			} else {
				//atempt anyway, in case we got global map but no page map.
				OPENOMN.attrbAssigner(OPENOMN.globalTrackedItems);
			}
		}
	},
	customTracking: function() {
		//init custom tracking using array built by page-specific js files
		for(i=0;i<OPENOMN.omnCustomArray.length;i++) {
			OPENOMN.omnCustomArray[i]();
		}
	},
	//pass this function an object with queryString : trackingValue pairs. 
	//see page-specific maps, or documentation for syntax and rules
	//ex: { "#calc-reset-btn" : "Click>Calculator>Reset" }
	attrbAssigner: function(map) { 
		for(var i in map) {
			var nodeList = dojo.query(i);
			if(nodeList!="" && nodeList.length==1) {
				nodeList[0].setAttribute("omnValue",map[i]);
			} else {
				dojo.forEach(nodeList,function(current,index,array) {
					if(typeof(map[i])=="object") {
						current.setAttribute("omnValue",map[i][index]);
					} else {
						current.setAttribute("omnValue",map[i])
					}
				});
			}
		}
	},
    omniListener: function(e) {
		//console.log("omni item: "+e.target+" id:"+e.target.id);
		//check for attrb
		if(typeof(e.target.getAttribute("omnvalue"))!="undefined" && e.target.getAttribute("omnvalue")) {
			if(typeof(omn_rmaction)!="undefined") {
				omn_rmaction(OPENOMN.marketCode+OPENOMN.pageName,e.target.getAttribute("omnvalue"));
				console.log(OPENOMN.marketCode+OPENOMN.pageName+":"+e.target.getAttribute("omnvalue"));
			} else {
				console.log("OMN_RMACTION UNDEFINED, STRING TO FOLLOW:");
				console.log(OPENOMN.marketCode+OPENOMN.pageName+":"+e.target.getAttribute("omnvalue"));
			}
		}
	},
	//used to add tracking to elements created on the fly. Public method.
	addTracking: function(selector,value,index /* in case of ID-less element that is part of a group of nodes */) {
	
		if(typeof(index)=="undefined") { index = 0; }
		//console.log("selector: "+selector+" value: "+value+" index: "+index);
		dojo.query(selector)[index].setAttribute("omnValue",value);
	}
    
};
var OPENOMN = OPEN.Omniture;

OPEN.Nav = {
    pageName: OPEN.pageInfo.pageName,
    cancelReference: null,
	contentPanel: "",
	
    slideOpen: function() {
		dojo.fx.wipeIn({node: OPENNav.contentPanel,duration:400,onEnd: function(){ OPENNav.contentPanel.style.overflow="visible";} }).play();
	},
	slideClosed: function() {
		dojo.fx.wipeOut({node: OPENNav.contentPanel,duration:400,onEnd: function() {
	
			}
		}).play();
	},				
	open: function(tab,navButton) {
        if(dojo.query("#aj_nav .active").length == 0 ){           
			dojo.addClass(navButton, "active");
			dojo.addClass(tab, "show");
			OPENNav.slideOpen(); 
        } else { 
			dojo.removeClass(dojo.query("#aj_nav .active")[0], "active");
			dojo.removeClass(dojo.query(".show")[0], "show"); 
			dojo.addClass(navButton, "active");
			dojo.addClass(tab, "show");
			OPENNav.slideOpen();
        }
	},
	/*close: function(tab) { //not used yet, made stub available
		var slideClosed = dojo.fx.wipeOut({node: OPENNav.contentPanel,duration:400});
	},*/
	
    init: function() {
		//change the link of the 1st tab
		dojo.addClass( dojo.query("#aj_nav #aj_tab1 a")[0],"hide");
		dojo.removeClass( dojo.query("#aj_nav #aj_tab1 a")[1],"hide");
		
		this.contentPanel = dojo.byId("aj_navContent");
        var tabTriggers = dojo.query(".aj_tabNav");
		
		//add correct path to apply links
		var pmc;
		
		dojo.query(".aj_apply_card").forEach(function(item,index,array) {
			pmc = item.parentNode.parentNode.parentNode.id.replace(/aj_navCard_/,"");
			if(typeof(aj_applydomain)!="undefined") {
				item.href=aj_applydomain+OPEN.Cards.CardData[pmc].applyURL;
			} else {
				item.href=OPEN.Cards.CardData[pmc].applyURL;
			}
		});
		
		//if this is the right kind of compare page, place the active class on the nav button
		if(dojo.hasClass(dojo.body(),"business-card-compare") && !dojo.hasClass("container","quiz-compare") && !dojo.hasClass("container","tray-compare") && !dojo.hasClass("container","ultimate-apply-default")) {
			dojo.addClass("aj_tab2", "active");
			dojo.addClass("aj_tab1","nobgimg");
		}
		
        // use classes as handles to toggle whether nav is open or closed
        // id is the tab div id
        tabTriggers.onclick(function(e){
			e.preventDefault();
            var tabContent = dojo.query("#" + this.id + "_content" )[0];
			dojo.addClass("aj_tab1","greybg");  
			dojo.addClass("aj_tab4","greybg"); 
            if( dojo.query("#aj_nav .active").length == 0 ){           
				//console.log("nothing is active, we will open");
                dojo.addClass(this, "active");
                dojo.addClass(tabContent, "show");
				if (this.id ==  "aj_tab2") { 
					dojo.addClass("aj_tab1","nobgimg"); 
					dojo.addClass("aj_tab2","nobgimg"); 
				} else { 
					dojo.addClass("aj_tab2","nobgimg");
					dojo.addClass("aj_tab2","greybg");
					dojo.addClass("aj_tab3","nobgimg");
				}
				dojo.removeClass("aj_nav_bottom_below", "hide");
				dojo.removeClass(tabContent, "hide");
                OPENNav.slideOpen(); 
            }
            else if( dojo.hasClass(this, "active") ){ 
				//console.log('closing the currently-open tab, this = ' + this.id);
				dojo.removeClass("aj_tab1","greybg"); 
				dojo.removeClass("aj_tab4","greybg"); 
				if (this.id ==  "aj_tab2") { 
					dojo.removeClass("aj_tab1","nobgimg"); 
					dojo.removeClass("aj_tab2","nobgimg"); 
				} else { 
					dojo.removeClass("aj_tab2","nobgimg");
					dojo.removeClass("aj_tab2","greybg");
					dojo.removeClass("aj_tab3","nobgimg");
				}
				dojo.addClass("aj_nav_bottom_below", "hide");
                OPENNav.slideClosed(); 
                dojo.removeClass(this, "active");
				
                setTimeout(function() { dojo.removeClass(tabContent, "show"); dojo.addClass(tabContent, "hide"); },500); 
            }
            else {
				//console.log('one tab was already open, close it & open the other one, this = ' + this.id);
				if (this.id ==  "aj_tab2") { 
					dojo.addClass("aj_tab1","nobgimg"); 
					dojo.addClass("aj_tab2","nobgimg"); 
					dojo.removeClass("aj_tab3","nobgimg");
					dojo.removeClass("aj_tab2","greybg");
				} else { 
					dojo.removeClass("aj_tab1","nobgimg"); 
					dojo.addClass("aj_tab2","nobgimg");
					dojo.addClass("aj_tab2","greybg");
					dojo.addClass("aj_tab3","nobgimg");
					dojo.removeClass("aj_tab3","greybg");
				}
                var lastTab = dojo.query("#aj_nav .active")[0];
                var lastTabContent = dojo.query("#" + lastTab.id + "_content" )[0];

                dojo.removeClass(lastTab, "active");
                dojo.removeClass(lastTabContent, "show");
                dojo.addClass(lastTabContent, "hide");
				
                dojo.addClass(this, "active");
                dojo.addClass(tabContent, "show");
				dojo.removeClass(tabContent, "hide");
            }     
        });
		//add flyouts for tab3
		this.addFlyoutHandlers();
        // determine if tab 2 is displayed
        this.handleTab2ContentVisisble(); 
      //get element to stop link action on in nav based on active page
        this.setCancelReference();
        //event listener for canceling
        if(OPEN.Nav.cancelReference) {
            this.cancelNavLinkAction();  
        } 
		
		var moreOptions = dojo.query("#aj_tab2_content ul li.parent");
		moreOptions.onmouseenter(function(e){
			dojo.removeClass("aj_moreoptions","hide");
		});

		moreOptions.onmouseleave(function(e){
			dojo.addClass("aj_moreoptions","hide");
		});

		//init queryString if present
		this.queryStringOpen();
    },
	
	queryStringOpen: function() {
		//coming from server variable
		if(typeof(aj_tabToOpen)!='undefined' && aj_tabToOpen!="null") {
			var navTab = dojo.query("#aj_tab"+aj_tabToOpen)[0];
			var tab = dojo.query("#aj_tab"+aj_tabToOpen+"_content")[0];
			
			//console.log("aj_tabToOpen: "+aj_tabToOpen);
			//console.log("navTab: "+navTab);
			//console.log("tab: "+tab);

			if(dojo.hasClass(dojo.query("#aj_tab2_content")[0],"aj_showNavContent") || (aj_tabToOpen!=2 && aj_tabToOpen!=3)) {
				//console.log("skip");
			} else {
				OPEN.Nav.open(tab,navTab);
			}
		} else {
			console.log("aj_tabToOpen not set");
		}
		
		//coming from querystring, for modals
		if(typeof(aj_modalToOpen)!="undefined" && aj_modalToOpen && aj_modalToOpen!="null") {
			if(aj_modalToOpen=="rsvp") {
				setTimeout("OPENOMNurney.Nav.Tab4.respondToMailOffer.prepareModal()",1000);
			}
		} else {
			//console.log("aj_modalToOpen not set");
		}
    },
    
    setCancelReference: function(){
        //PRODUCT
        //pageType replacement, when ready
        if(OPENU.pageType=="product") {
            //console.log("product page CANCEL");
            var pmc = dojo.query(".apply-now .aj_save_compare")[0];
            pmc = pmc.id;
            pmc = pmc.replace(/p/,"_");
            pmc = "#aj_pmc"+pmc;
            this.cancelReference = pmc;
        }
        //FLASH PAGES
        else if(OPENU.pageType=="quiz" || OPENU.pageType=="hubandspoke") {
            
            
        }
        //COMPARE
        else if(OPENU.pageType=="compare") {
            //console.log("compare cards CANCEL");
            var pageURL = window.location.pathname;
            var tabs = dojo.query("a[id*='aj_tab2']");
            for(var i=0;i<tabs.length;i++) {
                var tabHrefUnique = tabs[i].href.split('/');
                if(pageURL.match(tabHrefUnique[5])) { this.cancelReference="#"+tabs[i].id;  } else {}
            }
        } else {}
        //console.log("cancelCurrentPageLink DONE");
    },
    
    cancelNavLinkAction: function () {
        var cancelItem = dojo.query(this.cancelReference);
        cancelItem.onclick(function(e){
            e.preventDefault();
            dojo.stopEvent(e); 
        });
    },
    
    handleTab2ContentVisisble: function(){ //DEPRECATED IN FAVOR OF CSS
	
	
    },
	
	addFlyoutHandlers: function (){
		// Add card flyout rollovers for tab 3.
		
		/* used to obtain viewport information for positioning of popup layers */
		function aj_getYCoord(id) {
			for (var ly=0; id != null; ly += id.offsetTop, id=id.offsetParent);
			return {y:ly}
		}
		//flyouts
		$('div.aj_flyout').prepend('<div class=\'aj_pop_top\'></div>');
		$('div.aj_flyout').append('<div class=\'aj_pop_bot\'></div>');	
		$('div.aj_flyout').prepend('<div class=\'aj_poparrow\'></div>');	

		$(".aj_cardListImg").tooltip({ 
		    position: ['bottom', 'right'], 
			offset: [-30,0],
			tip: '.aj_flyout',
		    opacity: 1,
   			effect: 'toggle',
			delay: 30
		});
	}
};
OPENNav = OPEN.Nav;

OPEN.tooltips = {
    init: function() {
		//remove any illegal/unhelpful classes that come from BAU markup reused from other builds (it sneaks in)
		$(".sj_tooltipLegal .aj_tooltip").parent().removeClass("sj_tooltipLegal");
		$(".sj_tooltipLegal sup .aj_tooltip").parent().parent().removeClass("sj_tooltipLegal");
		
		//LEGAL FOOTNOTES
		OPEN.tooltips.fetch(
			$("#aj_footnotes li > span"), //container
			"<span class=\'aj_footnote\'><div class=\'aj_footnote_bot\'><span class=\'aj_footnote_mid\'><a href=\'#\' class=\'close\'>Close</a><span class=\'aj_footnote_scroller\'><div class=\'aj_poparrow\'></div></span></span></div></span>", //template
			".aj_footnote_scroller" //selector statement to find element in template to append to
		);
		
		//MARKETING FOOTNOTES
		OPEN.tooltips.fetch(
			$("#aj_footnotesMarketing li > span"), 
			"<span class=\'aj_footnote\'><div class=\'aj_footnote_bot\'><span class=\'aj_footnote_mid\'><a href=\'#\' class=\'close\'>Close</a><span class=\'aj_footnote_scroller\'><div class=\'aj_poparrow\'></div></span></span></div></span>",
			".aj_footnote_scroller"
		);
		
		OPEN.tooltips.attach($(".aj_tooltip, .sj_tooltipLegal, .sj_tooltipMarketing")); 

		$(".aj_tooltipCardTray").tooltip({
				api: true,
				position: ['top', 'left'], 
				offset: [0,0],
			    opacity: 1,
	   			effect: 'toggle',
				lazy: false,
				delay: 30,
				onShow: function(event) { 
					var tip = this.getTip();
					var x = this.getTrigger().position().left;
					var y = this.getTrigger().position().top;
					var width = tip.outerWidth();
					var height = tip.outerHeight()-45;
					tip.css({top: (y-height)+'px', left: (x-width)+'px'});
				}
		});
	}
};

OPEN.tooltips.attach = function($list) {
	//get triggers, then forEach to assign the tooltip code
	$list.tooltip({
			position: ['bottom', 'right'], 
			offset: [-70,0],
		    opacity: 1,
   			effect: 'toggle',
			lazy: false,
			delay: 30
		}).dynamic( { 
			classNames: 'aj_footnotepostop aj_footnoteposright aj_footnoteposbottom aj_footnoteposleft',
			top: { 
				offset: [0,-110]
			},
			right: {
				offset: [0,0]
			},
			bottom: { 
				offset: [0,-110]
			},
			left: {
				offset: [0,0]
			}
			
    });

	$(".aj_footnote a.close").click(function (e) {
		e.preventDefault();
		$(".aj_footnote:visible").css("display","none").css("visibility","hidden");
	});
};

//ABSTRACTED
OPEN.tooltips.fetch = function(container,template,attachTo){
	//make copy of arguments to leave them accessible in inner function which also has its own set of arguments
	var outerArguments = arguments;
	
	//for each item in the container list, grab the html content, apply the template, and attach it to its matching node in the document
	container.each(function(i) { 
		//make a NEW copy of the template for each iteration, otherwise the same DOM snippet will just get moved around and only one tooltip will be generated, ultimately
		var $templateCopy = $(template),
		CSSclass = $(this).attr("class"),
		html = $(this).html();
		
		//find the tooltip trigger associated with this tooltip. should be done just based on class; however, generated HTML by server side
			//was not made as we had requested, so we have to check a few attributes... if HTML is cleaned up, this could be eliminated.
			//it should be running off of the href.
		
		var $sibling = $("a[href='#"+CSSclass+"']").eq(0);
		
		//if it wasnt an href, try id...
		if ($sibling.length==0) { 
			$sibling = $("span[id='"+CSSclass+"']").eq(0);
		}
		
		//if it wasn't an id, try rel...
		if ($sibling.length==0) { 
			$sibling = $("span[rel='"+CSSclass+"']").eq(0);
		}
		
		//if no attachTo selector was specified, map a function over the template to find the element of greatest depth, and attach there
		if(outerArguments.length<3) {
			template.map(function(i,el) {
				if($(el).children().length==0) {
					//console.log(el);
					return true;
				} else {
					return el;
				}
			
			}).eq(0).append(html);
		} else {
			$(attachTo,$templateCopy).append(html);
		}
		
		//attach the node to the parent of the sibling we found earlier, and we're good to go.
		if(typeof($sibling)!='undefined' && $sibling != '') {
			var $ref = $sibling.parent();
			$ref.append($templateCopy);
			
			//block click events inside the tooltip, just in case.
			$templateCopy.click(function(e){ e.preventDefault(); e.stopPropagation(); });
		}
	});
};

//TINY VALIDATION
OPEN.validation = {
	//for zip codes
	zip: function(value) {
		var pattern = /\D/g;
		error = (value.match(pattern)) ? "fail" : "pass";
		if(error=="pass") {
			error = (value.length==5) ? "pass" : "fail";
		}
		return error;
	},
	//pass any value and any regexp *DOES NOT CHECK LENGTH*
	regexp: function(value,pattern) {
		error = (value.match(pattern)) ? true : false;
		return error;
	}
};

