// ///////////////////////////////////////
// BESiteFramework class
// ///////////////////////////////////////
//members
BESiteFramework.prototype._transport;
BESiteFramework.prototype._channelId;
BESiteFramework.prototype._user;
BESiteFramework.prototype._locale = 'en_us';

//constructor
function BESiteFramework(transportUrl, keepAliveSeconds){
	this._me = this; //fixes callback issues
	this._transport = new BETransport(transportUrl, keepAliveSeconds);
	this._channelId = 'framework';
	
	var user = new BEUser();
	user.tryLogin();	
	this.setUser(user);
};

//methods
BESiteFramework.prototype.setUser = function (user){this._user = user;}
BESiteFramework.prototype.getUser = function (){return this._user;}
BESiteFramework.prototype.registerModule = function (module){
	if(module){
		module.initialize(this._transport);
		return module;
	}
};

BESiteFramework.prototype.login = function(user, pass, callback){
	var request = {username: user, password: pass}
	this._transport.transmitRequest(this._channelId, 'login', request, this.loginResponse.bindAsEventListener(this, user, pass, callback));
};

BESiteFramework.prototype.loginResponse = function(objResponse, usern, pass, callback) {
    //first, clear the current user
    var user = new BEUser();

    if (objResponse.result == 'ok') {
		user.tryLogin();
        this.setUser(user); //save user

        //add the user to the response object that we can
        //pass back to the caller for further processing
        objResponse.user = user;
    }

    if (callback != null) {
        callback(objResponse);
    }
};

BESiteFramework.prototype.logout = function(callback){	
	this._transport.transmitRequest('framework', 'logout', {ignorejson:'1'}, this.logoutResponse.bindAsEventListener(this, callback));		
};

BESiteFramework.prototype.logoutResponse = function(objResponse, callback){
    this._user.destroy();

    if (callback != null) {
        callback(objResponse);
    }
};

//track event in analytics
BESiteFramework.prototype.trackEvent = function(category, action, optional_label, optional_value){
	if(pageTracker != 'undefined'){
		pageTracker._trackEvent(category, action, optional_label, optional_value);
	}
};

BESiteFramework.prototype.trackPageview = function(path){
	if(pageTracker != 'undefined'){
		pageTracker._trackPageview(path);
	}
};

BESiteFramework.prototype.trackTransaction = function(order, orderItems){
	if(pageTracker != 'undefined'){
        pageTracker._addTrans(
            order.orderId,
            order.affiliation,
            order.total,
            order.tax,
            order.shipping,
            order.city,
            order.state,
            order.country
        );
        
        for(var x=0; x < orderItems.length; x++){
            pageTracker._addItem(
                orderItems[x].orderId,
                orderItems[x].sku,
                orderItems[x].name,
                orderItems[x].category,
                orderItems[x].price,
                orderItems[x].qty
            );            
        }
        
        pageTracker._trackTrans();
	}
};

// ///////////////////////////////////////
// BEUser class
// ///////////////////////////////////////
BEUser.prototype._isLoggedIn;
BEUser.prototype._userData;

//constructor
function BEUser() {
	this._isLoggedIn = false;
    this._userData = {};
};

BEUser.prototype.tryLogin = function(){
    //try and read a userdata cookie
    var udCookie = BEreadCookie('pw_ud');
    this._userData = (udCookie != null && udCookie.length > 0) ? udCookie.evalJSON(true) : '';
    this._isLoggedIn = ( this._userData.id != null) ? true : false;
	
	if(!this._isLoggedIn){
	}    
};

BEUser.prototype.destroy = function() {
    this._userData = {};
    BEeraseCookie('pw_ud');
};

//methods
BEUser.prototype.isLoggedIn = function(){return BEgetBool(this._isLoggedIn);}
BEUser.prototype.getDisplayName = function(){return BEgetString(this._userData.displayName);}
// End of JavaScript BEUser class

// ///////////////////////////////////////
// BEBrowser class
// ///////////////////////////////////////
//members
BEBrowser.prototype._name;
BEBrowser.prototype._version;
BEBrowser.prototype._cookiesEnabled;
function BEBrowser() {
	var dataBrowser = [
		{string: navigator.userAgent,subString: "Chrome",identity: "Chrome"},
		{string: navigator.userAgent,subString: "OmniWeb",versionSearch: "OmniWeb/",identity: "OmniWeb"},
		{string: navigator.vendor,subString: "Apple",identity: "Safari",versionSearch: "Version"},
		{prop: window.opera,identity: "Opera"},
		{string: navigator.vendor,subString: "iCab",identity: "iCab"},
		{string: navigator.vendor,subString: "KDE",identity: "Konqueror"},
		{string: navigator.userAgent,subString: "Firefox",identity: "Firefox"},
		{string: navigator.vendor,subString: "Camino",identity: "Camino"},
		{string: navigator.userAgent,subString: "Netscape",identity: "Netscape"},
		{string: navigator.userAgent,subString: "MSIE",identity: "Explorer",versionSearch: "MSIE"},
		{string: navigator.userAgent,subString: "Gecko",identity: "Mozilla",versionSearch: "rv"},
		{string: navigator.userAgent,subString: "Mozilla",identity: "Netscape",versionSearch: "Mozilla"}
	];
	
	var browserName = '';
	var browserVersion = '';
	var versionSearchString = '';
	for (var i=0;i<dataBrowser.length;i++)	{
		if(browserName == ''){
			var dataString = dataBrowser[i].string;
			var dataProp = dataBrowser[i].prop;
			versionSearchString = dataBrowser[i].versionSearch || dataBrowser[i].identity;
			if (dataString) {
				if (dataString.indexOf(dataBrowser[i].subString) != -1){
					browserName = dataBrowser[i].identity;
				}
			}
			else if (dataProp){
				browserName = dataBrowser[i].identity;
			}
		}
	}
	
	var index = navigator.userAgent.indexOf(versionSearchString);
	if (index != -1){
		browserVersion = parseFloat(navigator.userAgent.substring(index+versionSearchString.length+1));	
	}else{
		index = navigator.appVersion.indexOf(versionSearchString);
		
		if(index != -1){
			browserVersion = parseFloat(navigator.appVersion.substring(index+versionSearchString.length+1));	
		}else{
			browserVersion = 'An unknown version';
		}
	}
	
	//set the name and version
	this._name = (browserName != '') ? browserName : 'An unknown browser';
	this._version = browserVersion;
	
	//test for ability to store client-side cookies
	var writeThis = BEcreateCookie('pw_test', 'test', 1);
	var readThis = BEreadCookie('pw_test')
	
	if(readThis == null){
		this._cookiesEnabled = false;
	}else{
		this._cookiesEnabled = true;
	}
}
BEBrowser.prototype.getName = function(){return this._name;}
BEBrowser.prototype.getVersion = function(){return this._version}
BEBrowser.prototype.hasCookies = function(){return this._cookiesEnabled;}
BEBrowser.prototype.isOutdated = function(){if(this._name == 'Explorer' && this._version < 7){return true;}else{return false;}}


// ///////////////////////////////////////
// BESafeHelpers
// ///////////////////////////////////////
function BEcreateCookie(name, value, days){if (days){var date = new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires = "; expires="+date.toGMTString();}else{var expires = '';}document.cookie = name+"="+value+expires+"; path=/";}
function BEreadCookie(name){var nameEQ = name + "=";var ca = document.cookie.split(';');for(var i=0;i < ca.length;i++) {var c = ca[i];while (c.charAt(0)==' '){c = c.substring(1,c.length);}if (c.indexOf(nameEQ) == 0){return c.substring(nameEQ.length,c.length);}}return null;}
function BEeraseCookie(name){BEcreateCookie(name, '', -1);}
function BEgetBool(prop){if(prop != null){return prop;}else{return false;}}
function BEgetString(prop){if(prop != null){return prop;}else{return '';}}
function BEgetInt(prop){if(prop != null && prop != 'undefined'){return prop;}else{return -1;}}
function BEFormatCurrency(amount){
    if(amount.toFixed){
        return '$' + amount.toFixed(2);
    }else{
        var i = parseFloat(amount);
	    if(isNaN(i)) { i = 0.00; }
	    var minus = '';
	    if(i < 0) { minus = '-'; }
	    i = Math.abs(i);
	    i = parseInt((i + .005) * 100);
	    i = i / 100;
	    s = new String(i);
	    if(s.indexOf('.') < 0) { s += '.00'; }
	    if(s.indexOf('.') == (s.length - 2)) { s += '0'; }
	    s = minus + s;
	    return '$' + s;        
    }
}

// ///////////////////////////////////////
// Transport class
// ///////////////////////////////////////

//fields
BETransport.prototype._transportURL;
BETransport.prototype._requests;  //array of pending ajax requests
BETransport.prototype._keepAliveSec;
BETransport.prototype._keepAliveExecuter;
BETransport.prototype._keepAliveEnvelope;

//constructor
function BETransport(transportURL, keepAliveSec){
    this._transportURL = transportURL;
    this._requests = new Array();
    this._keepAliveSec = keepAliveSec;
}

//methods
BETransport.prototype.getTransportURL = function(){
    return this._transportURL;
}

BETransport.prototype.transmitRequest = function(channelId, action, objMessage, receiveCallback){    
	//dispose of finished requests
	while (this._requests.length > 0 && this._requests[0].isFinished()){
		this._requests.shift();
	}
	
	//check for periodical executer for keepalives
	if (this._keepAliveExecuter == null && channelId == 'framework' && action == 'login'){	
		var eventListener = this.transmitKeepalive.bindAsEventListener(this);
		this._keepAliveExecuter = new PeriodicalExecuter(eventListener, this._keepAliveSec);
	}

	//todo include sessionData object so checksum can be calculated
	var requestEnvelope = new BEEnvelope(channelId, action, objMessage, receiveCallback);
	this._requests.push (requestEnvelope);
	requestEnvelope.sendMessage(this._transportURL);
}

BETransport.prototype.transmitKeepalive = function(event){
	//send the keepalive message
	if (this._keepAliveEnvelope == null){
		var msgIgnore = {ignorejson: "1"};
		this._keepAliveEnvelope = new BEEnvelope("framework", "keepalive", msgIgnore , null);	
	}
	this._keepAliveEnvelope.sendMessage(this._transportURL);	
}

//
//Javascript Envelope class
BEEnvelope.prototype._channelId;
BEEnvelope.prototype._action;
BEEnvelope.prototype._callback;
BEEnvelope.prototype._xmlMessage;
BEEnvelope.prototype._isFinished;
BEEnvelope.prototype._ajaxRequest;

function BEEnvelope(channelId, action, objMessage, receiveCallback){
	this._channelId = channelId;
	this._action = action;
	this._callback = receiveCallback;
	this._isFinished = false;
	this._ajaxRequest;
	
	var jsonMessage =  Object.toJSON(objMessage);	
	this._xmlMessage = this.toXML(channelId, action, jsonMessage);
	
}

BEEnvelope.prototype.isFinished = function(){
	return (this._isFinished);
}

BEEnvelope.prototype.toXML = function (channelId, action, myJsonMarkup){
	//concatentate to xml document, ready for transmission
	//todo: calculate checksum
	return  "<msg channelid='" + channelId + "' dir='request' action='" + action + "' checksum='123' > \n" +
				"<![CDATA["  + myJsonMarkup + "]]>\n" + 
		 	"</msg>";
}

BEEnvelope.prototype.sendMessage = function (transportURL){
	//sends xml message to server and logs callback response
	var thisEnvelope = this;
	thisEnvelope._ajaxRequest = new Ajax.Request(transportURL,
				  	{
				    	method:'post',
	  					parameters: {sData: thisEnvelope._xmlMessage},
				    	onSuccess: thisEnvelope.onSuccess,
				    	onFailure: thisEnvelope.onFail,
				    	envelope: thisEnvelope      //workaround to 'this' object being incorrect on ajax callback
				    });	
				    
				    
}

BEEnvelope.prototype.onFail = function (response){
	//send response (fail) //soap error; cannot contact server, etc.
	var thisEnvelope = response.request.options.envelope;
	responseMessage = {
		"result": "fail",
		"message": "There was a communication or server error."
	};

	if (thisEnvelope._callback){
		thisEnvelope._callback(responseMessage);
	}
	thisEnvelope._isFinished = true;
}

BEEnvelope.prototype.onSuccess = function (response){
	//send response (success)	
	var returnXML = response.responseText.toString();
	var thisEnvelope = response.request.options.envelope;
	var responseMessage;
	
	var channelId = thisEnvelope.extractAttribute(returnXML, "channelid");
	var action = thisEnvelope.extractAttribute(returnXML, "action");
	var direction = thisEnvelope.extractAttribute(returnXML, "dir");
	
	//ensure channel, message, direction, etc. as expected
	if (channelId == thisEnvelope._channelId && action == thisEnvelope._action && "response" == direction ){
		var jsonResponse = thisEnvelope.extractJsonMarkup(returnXML);
		responseMessage = jsonResponse.evalJSON(true); //convert to JSON, sanitizing if needed
	}
	else{
		responseMessage = {
			"result": "invalid",
			"message": "The server response had an invalid channel id, action, or direction."
		};
	}
	
	if (thisEnvelope._callback){
		thisEnvelope._callback(responseMessage);
	}
	
	thisEnvelope._isFinished = true;
}

BEEnvelope.prototype.extractAttribute = function (xmlMarkup, attributeName){
	//send response (success)
	var sReturn = "";
	
	var matchAttrib = " " + attributeName; //prepend space for more accurate search
	var quote = '"';
	
	var parts = xmlMarkup.split(">"); //cut off everything after the element body
	if (parts.length > 1){
		var sTag = parts[1].replace(/ =/g, "=");
		//remove spaces around '=' symbols so they become split correctly
		while (sTag.search(" =") > -1 || sTag.search ("= ") > -1 ){
			sTag = sTag.replace(/ =/g, "=");
			sTag = sTag.replace(/= /g, "=");
		}
		var attribArray = sTag.split(" ");
		for (var i = 0; i < attribArray.length; i++){
			var sLine = attribArray[i].strip();
			if (sLine.search(attributeName) > -1){
				//found the attribute - extract the value
				var lineParts = sLine.split("=");
				sReturn = lineParts[1].strip();
				sReturn = sReturn.replace(quote, "");
				sReturn = sReturn.replace(quote, "");
				break;
			}
		}
	}
	
	return sReturn;
}

BEEnvelope.prototype.extractJsonMarkup = function (xmlMarkup){
	//parses json markup from response
	var sReturn = "";
	var parts = xmlMarkup.split("<![CDATA[");
	if (parts.length == 2){
		var sLine = parts[1]; //json part
		parts = sLine.split("]]>")
		if (parts.length == 2){
			sReturn = parts[0];
		}
	}
	
	return sReturn;
}



