// ///////////////////////////////////////
// PDSiteFramework class
// ///////////////////////////////////////
//members
PDSiteFramework.prototype._transport;
PDSiteFramework.prototype._channelId;
PDSiteFramework.prototype._user;
PDSiteFramework.prototype._page;
PDSiteFramework.prototype._browser;
PDSiteFramework.prototype._locale = 'en_us';

//constructor
function PDSiteFramework(transportUrl, keepAliveSeconds){
	this._me = this; //fixes callback issues
	this._transport = new PDTransport(transportUrl, keepAliveSeconds);
	this._channelId = 'framework';
	
	//store information about this page and retrieve a user from the cookie
	this.setBrowser(new PDBrowser());
	this.setPage(new PDPage());
	this.setUser(new PDUser());
} 

//methods
PDSiteFramework.prototype.setUser = function (user){this._user = user;}
PDSiteFramework.prototype.getUser = function (){return this._user;}
PDSiteFramework.prototype.setPage = function (page){this._page = page;}
PDSiteFramework.prototype.getPage = function (){return this._page;}
PDSiteFramework.prototype.setBrowser = function (browser){this._browser = browser;}
PDSiteFramework.prototype.getBrowser = function (){return this._browser;}

PDSiteFramework.prototype.registerModule = function (module){
	if(module){
		module.initialize(this._transport, this._user);
		return module;
	}
}

PDSiteFramework.prototype.login = function(user, pass, callback){
	var request = {username: user, password: pass}
	this._transport.transmitRequest(this._channelId, 'login', request, this.loginResponse.bindAsEventListener(this, callback));		
}

PDSiteFramework.prototype.loginResponse = function(objResponse, callback){
	//initialize and first destroy a previous user
	var user = new PDUser();
	user.destroy();

	if(objResponse.result == 'ok'){
		user._id = objResponse.id;
		user._typeId = objResponse.typeId;
		user._username = objResponse.username;
		user._displayName = objResponse.displayName;
		user._avatar = objResponse.profileImage;
		user._x = objResponse.x;
		user.save(24); //one day
		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);		
	}
}

//track event in analytics
PDSiteFramework.prototype.trackEvent = function(category, action, optional_label, optional_value){
	if(pageTracker != 'undefined'){
		pageTracker._trackEvent(category, action, optional_label, optional_value);
	}
}

// ///////////////////////////////////////
// PDTransport class
// ///////////////////////////////////////
PDTransport.prototype._transportURL;
PDTransport.prototype._requests;  //array of pending ajax requests
PDTransport.prototype._keepAliveSec;
PDTransport.prototype._keepAliveExecuter;
PDTransport.prototype._keepAliveEnvelope;

//constructor
function PDTransport(transportURL, keepAliveSec){
    this._transportURL = transportURL;
    this._requests = new Array();
    this._keepAliveSec = keepAliveSec;
}

//methods
PDTransport.prototype.getTransportURL = function(){
    return this._transportURL;
}

PDTransport.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 PDEnvelope(channelId, action, objMessage, receiveCallback);
	this._requests.push (requestEnvelope);
	requestEnvelope.sendMessage(this._transportURL);
}

PDTransport.prototype.transmitKeepalive = function(event){
	//send the keepalive message
	if (this._keepAliveEnvelope == null){
		var msgIgnore = {ignorejson: "1"};
		this._keepAliveEnvelope = new PDEnvelope("framework", "keepalive", msgIgnore , null);	
	}
	this._keepAliveEnvelope.sendMessage(this._transportURL);	
}

// ///////////////////////////////////////
// PDEnvelope class
// ///////////////////////////////////////
PDEnvelope.prototype._channelId;
PDEnvelope.prototype._action;
PDEnvelope.prototype._callback;
PDEnvelope.prototype._xmlMessage;
PDEnvelope.prototype._isFinished;
PDEnvelope.prototype._ajaxRequest;

function PDEnvelope(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);
	
}

PDEnvelope.prototype.isFinished = function(){
	return (this._isFinished);
}

PDEnvelope.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>";
}

PDEnvelope.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
				    });	
				    
				    
}

PDEnvelope.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;
}

PDEnvelope.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(); //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;
}

PDEnvelope.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;
}

PDEnvelope.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;
}

// ///////////////////////////////////////
// PDUser class
// ///////////////////////////////////////
PDUser.prototype._isLoggedIn;
PDUser.prototype._username;
PDUser.prototype._displayName;
PDUser.prototype._typeId;
PDUser.prototype._id;
PDUser.prototype._x;
PDUser.prototype._avatar;
PDUser.prototype._hasDigest;

//constructor
function PDUser() {
	this._isLoggedIn = (this.readCookie('pd2_i') != null && this.readCookie('pd2_i') != '') ? true : false;
	
	if(this._isLoggedIn){
		this._id = this.readCookie('pd2_i');
		this._username = this.readCookie('pd2_u');
		this._displayName = this.readCookie('pd2_n');
		this._typeId = this.readCookie('pd2_t');
		this._avatar = this.readCookie('pd2_a');
		this._hasDigest = this.readCookie('pd2_n1');
		this._x = this.readCookie('pd2_x');	
	}
}

PDUser.prototype.destroy = function(){
	this.eraseCookie('pd2_i');
	this.eraseCookie('pd2_t');
	this.eraseCookie('pd2_u');
	this.eraseCookie('pd2_n');
	this.eraseCookie('pd2_a');
	this.eraseCookie('pd2_n1');
	this.eraseCookie('pd2_x');
}

PDUser.prototype.save = function(expires){
	this.createCookie('pd2_i', this._id, expires);
	this.createCookie('pd2_t', this._typeId, expires);
	this.createCookie('pd2_u', this._username, expires);
	this.createCookie('pd2_n', this._displayName, expires);	
	this.createCookie('pd2_a', this._avatar, expires);
	this.createCookie('pd2_n1', this._hasDigest, expires);
	this.createCookie('pd2_x', this._x, expires);	
}

PDUser.prototype.createCookie = function(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=/";}
PDUser.prototype.readCookie = function(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;}
PDUser.prototype.eraseCookie = function(name){this.createCookie(name, '', -1);}

//methods
PDUser.prototype.isLoggedIn = function(){return PDgetBool(this._isLoggedIn);}
PDUser.prototype.getId = function(){return PDgetInt(this._id);}
PDUser.prototype.getDisplayName = function(){return PDgetString(this._displayName);}
PDUser.prototype.getTypeId = function(){return PDgetInt(this._typeId);}
PDUser.prototype.getProfileEditUrl = function(){return '/profiles/' + escape(PDgetString(this._username)) + '_edit_' + PDgetInt(this._id) + '.aspx';}
PDUser.prototype.getAvatar = function(){return PDgetString(this._avatar);}
PDUser.prototype.getHasDigest = function(){return PDgetInt(this._hasDigest);}
// End of JavaScript PDUser class

// ///////////////////////////////////////
// PDBrowser class
// ///////////////////////////////////////
//members
PDBrowser.prototype._name;
PDBrowser.prototype._version;
PDBrowser.prototype._cookiesEnabled;
function PDBrowser() {
	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 = this.createCookie('pd2_test', 'test', 1);
	var readThis = this.readCookie('pd2_test')
	
	if(readThis == null){
		this._cookiesEnabled = false;
	}else{
		this._cookiesEnabled = true;
	}
}
PDBrowser.prototype.getName = function(){return this._name;}
PDBrowser.prototype.getVersion = function(){return this._version}
PDBrowser.prototype.hasCookies = function(){return this._cookiesEnabled;}
PDBrowser.prototype.isOutdated = function(){
	if(this._name == 'Explorer' && this._version < 7){
		return true;
	}else{
		return false;
	}
}
PDBrowser.prototype.createCookie = function(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=/";}
PDBrowser.prototype.readCookie = function(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;}

// ///////////////////////////////////////
// PDPage class
// ///////////////////////////////////////
//members
PDPage.prototype._typeName;
PDPage.prototype._baseUrl;
//constructor
function PDPage() {
	var location = window.location.pathname;
	
	if(location == '/default.aspx' || location == '/'){
		this._typeName = 'Home';
		this._baseUrl = '/';
	}else if(location.indexOf('/blog/') != -1 || location.indexOf('/tags/') != -1 || location.indexOf('/cell-phone-list/') != -1){
		this._typeName = 'News and Rumors';
		this._baseUrl = '/cell-phone-research/blog/';
	}else if(location.indexOf('/cell-phone-videos/') != -1){
		this._typeName = 'Videos';
		this._baseUrl = '/cell-phone-videos/';
	}else if(location.indexOf('/cell-phone-reviews/') != -1){
		this._typeName = 'Reviews';
		this._baseUrl = '/cell-phone-reviews/';
	}else if(location.indexOf('/community/') != -1){
		this._typeName = 'Research Tools';
		this._baseUrl = '/community/';
	}else{
		this._typeName = 'Shopping and Savings';
		this._baseUrl = '/cell-phone-shopping/';
	}
}
//methods

PDPage.prototype.getTypeName = function(){return this._typeName;}
PDPage.prototype.getBaseUrl = function(){return this._baseUrl}
// End of JavaScript PDPage class

// ///////////////////////////////////////
// PDSafeHelpers
// ///////////////////////////////////////
function PDgetBool(prop){if(prop != null){return prop;}else{return false;}}
function PDgetString(prop){if(prop != null){return prop;}else{return '';}}
function PDgetInt(prop){if(prop != null && prop != 'undefined'){return prop;}else{return -1;}}


function PDFormatMonthNameShort(month){if (month == 0){return "Jan";}else if (month == 1){return "Feb";}else if (month == 2){return "Mar";}else if (month == 3){return "Apr";}else if (month == 4){return "May";}else if (month == 5){return "Jun";}else if (month == 6){return "Jul";}else if (month == 7){return "Aug";}else if (month == 8){return "Sep";}else if (month == 9){return "Oct";}else if (month == 10){return "Nov";}else{return "Dec";}}
function PDFormatShortDate(date){
	return PDFormatDate(date, false);
}
function PDFormatDate(dateString, appendTime){
	if (dateString == null || dateString == '' ) {
		return '';
	}
	else {
		var date = new Date(dateString || "");
		var today = new Date();
		var ret = new Array();
		var months = { en_us: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]};
		var am_pm = {en_us: [ "am", "pm" ] };
		var locale = 'en_us';
		
		if (date.getFullYear() == today.getFullYear()){
			//sent this year
			if ( (date.getDate() == today.getDate() && date.getMonth() == today.getMonth())){
				//sent today, append the time
				appendTime = true;
			}else{
				//not sent today, append the month and date
				ret.push( months[locale][date.getMonth()] + ' ' + date.getDate() );
			}
		}
	
		if (date.getFullYear() != today.getFullYear()){
			//not sent this year; append the year as well as the m/d
			ret.push( months[locale][date.getMonth()] + ' ' + date.getDate() );
			ret.push(date.getFullYear().toString().substr(2))
		}
	
		if (appendTime)	{
			var hours = date.getHours();
			var mins = (date.getMinutes() < 10) ? '0' + date.getMinutes() : date.getMinutes() ;
			var ampmsuffix = (date.getHours() < 12) ? am_pm[locale][0] : am_pm[locale][1]; 
			var sTime = hours + ':' + mins  + ' ' + ampmsuffix;
			ret.push (sTime); 		
		}
	
		return ret.join(', ');


	}
}