if(typeof HD.REO == "undefined" || !HD.REO) {
	/**
	 * HD REO namespace, the base object for all Rider's Edge Online JavaScipt classes.
	 * @static
	 */
	HD.REO = {
		mapPointUrl : 'http://go.mappoint.net/harleydavidson/PrxResults.aspx',
		//mapPointUrl : 'http://preview.mappoint.net/HarleyDavidson/PrxResults.aspx',
		/**
		 * Analytics object for Rider's Edge Online, contains all of the tracking strings and functions.
		 */
		analytics : {
		 	/** String template for initial page load. */
			searchPage : '/Get+On+A+Bike/Learn+To+Ride/RE+Online+Reservations/_COURSE_TYPE/Search',
			/** String template for search results. */
			searchResultsPage : '/Get+On+A+Bike/Learn+To+Ride/RE+Online+Reservations/_COURSE_TYPE/Search+Results/_LOCATION/_NUM_OF_RESULTS',
			/** String template for no search results. */
			noResultsPage : '/Get+On+A+Bike/Learn+To+Ride/RE+Online+Reservations/_COURSE_TYPE/Search+Results/No+Results/_LOCATION',
			/** String template for MSF link click. */
			msfLink : '/Get+On+A+Bike/Learn+To+Ride/RE+Online+Reservations/_COURSE_TYPE/Search+Results/No+Results/MSF+Link+Click',
			/** String template for dealer class list. */
			dealerClassList : '/Get+On+A+Bike/Learn+To+Ride/RE+Online+Reservations/_COURSE_TYPE/Dealer+Class+List/_DEALER_ID',
			/** String template for CA disclaimer. */
			standardProgramNotice : '/Get+On+A+Bike/Learn+To+Ride/RE+Online+Reservations/_COURSE_TYPE/Standard+Program+Notice',
			/** String template for CA disclaimer agreement. */
			standardProgramNoticeContinueAction : '/Get+On+A+Bike/Learn+To+Ride/RE+Online+Reservations/_COURSE_TYPE/Standard+Program+Notice/_YES_NO',
			/** String template for enrollment forms. */
			signupForm : '/Get+On+A+Bike/Learn+To+Ride/RE+Online+Reservations/_COURSE_TYPE/Form/_REGISTRATION_TYPE/_PART',
			/** String template for confirmation page. */
			signupConfirmation : '/Get+On+A+Bike/Learn+To+Ride/RE+Online+Reservations/_COURSE_TYPE/Confirmation/_REGISTRATION_TYPE',
			
			/**
			 * Returns formatted string for initial page load.
			 * 
			 * @param {String} courseType course type
			 */
			getSearchPage : function(courseType) {
		 		return HD.REO.analytics.searchPage.
		 			replace(/_COURSE_TYPE/, courseType);
	 		},

			/**
			 * Returns formatted string for search results.
			 * 
			 * @param {String} courseType course type
			 */
	 		getSearchResultsPage : function(courseType, location, numOfResults) {
	 			return HD.REO.analytics.searchResultsPage.
	 				replace(/_COURSE_TYPE/, courseType).
	 				replace(/_LOCATION/, location).
	 				replace(/_NUM_OF_RESULTS/, numOfResults);
	 		},
	 		
			/**
			 * Returns formatted string for no search results.
			 * 
			 * @param {String} courseType course type
			 */
	 		getNoResultsPage : function(courseType, location) {
		 		return HD.REO.analytics.noResultsPage.
	 				replace(/_COURSE_TYPE/, courseType).
	 				replace(/_LOCATION/, location);
	 		},
	 		
			/**
			 * Returns formatted string for MSF link click.
			 * 
			 * @param {String} courseType course type
			 */
	 		getMsfLink : function(courseType) {
	 			return HD.REO.analytics.msfLink.
	 				replace(/_COURSE_TYPE/, courseType);
	 		},
	 		
			/**
			 * Returns formatted string for dealer class list.
			 * 
			 * @param {String} courseType course type
			 */
	 		getDealerClassList : function(courseType, dealerId) {
	 			return HD.REO.analytics.dealerClassList.
	 				replace(/_COURSE_TYPE/, courseType).
	 				replace(/_DEALER_ID/, dealerId);
	 		},
	 		
			/**
			 * Returns formatted string for CA disclaimer.
			 * 
			 * @param {String} courseType course type
			 */
	 		getStandardProgramNotice : function(courseType) {
	 			return HD.REO.analytics.standardProgramNotice.
	 				replace(/_COURSE_TYPE/, courseType);
	 		},
	 		
			/**
			 * Returns formatted string for CA disclaimer agreement.
			 * 
			 * @param {String} courseType course type
			 */
	 		getStandardProgramNoticeContinueAction : function(courseType, yesNo) {
	 			return HD.REO.analytics.standardProgramNoticeContinueAction.
	 				replace(/_COURSE_TYPE/, courseType).
	 				replace(/_YES_NO/, yesNo);
	 		},
	 		
			/**
			 * Returns formatted string for enrollment forms.
			 * 
			 * @param {String} courseType course type
			 */
	 		getSignupForm : function(courseType, registrationType, part) {
	 			return HD.REO.analytics.signupForm.
	 				replace(/_COURSE_TYPE/, courseType).
	 				replace(/_REGISTRATION_TYPE/, registrationType).
	 				replace(/_PART/, part);
	 			
	 		},
	 		
			/**
			 * Returns formatted string for confirmation page.
			 * 
			 * @param {String} courseType course type
			 */
	 		getSignupConfirmation : function(courseType, registrationType) {
	 			return HD.REO.analytics.signupConfirmation.
	 				replace(/_COURSE_TYPE/, courseType).
	 				replace(/_REGISTRATION_TYPE/, registrationType);
	 		}
		},

		/**
		 * Session servlet's relative URL.
		 */
		sessionServletUrl : '/ReoService/services/SessionServlet',
		
		/**
		 * Window configuration for dealer detail popup.
		 */
		dealerWindowConfig : 'width=620,height=500,scrollbars=yes,resizable=yes,toolbar=no,location=no,menubar=no,directories=no,status=no',
		
		/**
		 * Enrollment status codes.
		 */
		ENROLLMENT_STATUS_CODES : {
		 	CLASS_HAS_WAITLIST : "CLASS_HAS_WAITLIST_",
		 	CLASS_IS_FULL : "CLASS_IS_FULL",
		 	VALIDATION_SUCCESS : "VALIDATION_SUCCESS",
		 	STUDENT_VALIDATION_ERROR : "STUDENT_VALIDATION_ERROR",
		 	ENROLLMENT_SUCCESS : "ENROLLMENT_SUCCESS",
		 	STUDENT_ENROLLED_IN_NRC : "STUDENT_ENROLLED_IN_NRC",
		 	STUDENT_ENROLLED_IN_SRC  : "STUDENT_ENROLLED_IN_SRC",
		 	MAPPOINT_VALIDATION_FAILED : "MAPPOINT_VALIDATION_FAILED"
	 	},
		
		/**
		 * REO template, contains variables used by multiple REO widgets.
		 */
		template : {
			sortByDate : 'startDate',
			sortByDistance : 'distance',
			
			/**
			 * Course sub type labels mapping
			 */
			courseTypesLabels : [
	       	    null,
	       		"New Rider",
	       		"Skilled Rider"
	       	],

			getMessage : function(classs, header, contents) {
				if(contents != null && contents.length > 0) {
					contents = ['<ul>',
									'<li>', contents.join('</li><li>'), '</li>',
								'</ul>'].join('');
				}
				return ['<div class="', classs, ' hdMessage">',
							'<h3>', header, '</h3>',
							contents,
						'</div>'].join('');
			}
		},
		
		/**
		 * Populate form with data stored in session.  The method will not execute 
		 * if data for a given form is not present in session.  Optional callback is
		 * executed upon successful form population.
		 * 
		 * @param {String} formId The id under which the form data is to be stored
		 * @param {Function} callback (Optional) The callback to execute once the from is populated
		 */
		populateFormData : function(formId, callback) {
			SessionService.getAttribute(formId, function(sessionObject) {
				if(sessionObject != null) {
					var content = sessionObject.content;
					for(var field in content) {
						HD.util.Dom.setValue(field, content[field]);
					}
					if(callback != null) {
						callback();
					}
				}
			});
		},
		
		/**
		 * Submits enrollment forms.
		 * 
		 * @param {String|HTMLElement} formEl the form element to submit
		 * @param {int} formNumber the portion of the form being submitted
		 * @param {String} nextPage relative URL to the next page 
		 */
		submitForm : function(formEl, formNumber, nextPage) {
			var errorEls = HD.util.Dom.getByClass("hdErrorIndicator", "div");
			if(errorEls.length > 1) {
				HD.util.Dom.hide(errorEls[1]);
			}
			HD.util.Common.submitForm(formEl, HD.REO.sessionServletUrl + "?objectKey=" + formEl.id, function(response){
				EnrollmentService.enroll({formNumber : formNumber, formId : formEl.id}, function(enrollmentResponse) {
					HD.REO.handleEnrollmentResponse(enrollmentResponse, nextPage);
				});
			});
		},
		
		/**
		 * Handles enrollment form submissions, forwards to the next page or displays error/warning messages depending on the outcome.
		 * 
		 * @param {Object} enrollmentResponse enrollment response
		 * @param {String} nextPage relative URL to the next page 
		 */
		handleEnrollmentResponse : function(enrollmentResponse, nextPage) {
			var statusCodes = HD.REO.ENROLLMENT_STATUS_CODES;
			var statusCode = enrollmentResponse.statusCode;
			if((statusCode == statusCodes.VALIDATION_SUCCESS || statusCode == statusCodes.ENROLLMENT_SUCCESS) 
					|| (statusCode == statusCodes.CLASS_HAS_WAITLIST && enrollmentResponse.waitlistRegistration)) {
				window.location = HD.util.Common.getForwardUrl(nextPage, HD.REO.paramsToPropagate, "https");
			} else {
				var dom = HD.util.Dom;
				var errorIndicatorEl = dom.getByClass("hdErrorIndicator", "div")[0];
				if(statusCode == statusCodes.STUDENT_VALIDATION_ERROR) {
//					//////////////
//					var errorMessages = enrollmentResponse.errors;
//					if(validationErrors.length > 0) {
//						HD.util.Validation.showErrors(formEl, formValidator, validationErrors);
//						HD.util.Dom.removeClass(errorIndicatorEl, "hdHidden");
//						HD.util.Dom.scrollTo(errorIndicatorEl);
//						var errorHtml = ["<h3><span>Please provide all of the required information below</span></h3>", "<ul>"];
//						for(var i = 0, len = errorMessages.length; i < len; i++) {
//							errorHtml.push("<li>" + errorMessages[i] + "</li>");
//						}
//						errorHtml.push("</ul>");
//						errorIndicatorEl.innerHTML = errorHtml.join("");
//					} else {
//						HD.util.Validation.hideErrors(formEl, formValidator);
//						HD.util.Dom.addClass(errorIndicatorEl, "hdHidden");
//						HD.REO.submitForm(formEl, formNumber, nextUrl);
//					}
//					////////////
					dom.removeClass(errorIndicatorEl, "hdHidden");
					dom.scrollTo(errorIndicatorEl);
				} else {
					dom.addClass(errorIndicatorEl, "hdHidden");
					HD.REO.addClass("hdEnrollmentError");
					if(statusCode == statusCodes.CLASS_IS_FULL) {
						HD.REO.addClass("hdClassFull");
					} else if(!enrollmentResponse.waitlistRegistration && statusCode == statusCodes.CLASS_HAS_WAITLIST) {
						HD.REO.addClass("hdClassFull");
						HD.REO.addClass("hdHasWaitlist");
					} else if(statusCode == statusCodes.STUDENT_ENROLLED_IN_NRC) {
						HD.REO.addClass("hdRegisteredNRC");
					} else if(statusCode == statusCodes.STUDENT_ENROLLED_IN_SRC) {
						HD.REO.addClass("hdRegisteredSRC");
					}
				}
			}
		},
		
		/**
		 * Adds class name to the wrapper element.
		 * 
		 * @param {String} className the class name to add 
		 */
		addClass : function(className) {
			HD.util.Dom.addClass("contentwrapper", className);
		},
		
		/**
		 * Formats phone number object as a string.
		 * 
		 * @param {Object} phone the phone object
		 * @param {String} format (Optional) the phone format, i.e. '(NPA) NXX-STATION'
		 * @return formatted phone number string
		 */
		formatPhone : function(phone, format) {
			var common = HD.util.Common;
			if(!common.hasValue(format)) {
				format = "(NPA) NXX-STATION";
			}
			
			format = format.replace(/NPA/, phone.npa);
			format = format.replace(/NXX/, phone.nxx);
			format = format.replace(/STATION/, phone.station);
			return format;
		},
		
		/**
		 * Returns the URL to a dealer information popup.
		 * 
		 * @param {Object} dealer the dealer
		 * @return the URL to the dealer's information popup
		 */
		getDealerUrl : function(dealer) {
			return HD.REO.mapPointUrl + '?&LL=en-US&brand=hre&ridersearch=1&FC=RidersEdge&HDid=' + dealer.id;
		},
		
		/**
		 * Constructs JS to open dealer information popup.
		 * 
		 * @param {Object} dealer the dealer
		 * @return the JS to open dealer information popup
		 */
		getDealerHref : function(dealer) {
			return 'javascript:window.open(\'' + HD.REO.getDealerUrl(dealer) + '\', \'\', \'' + HD.REO.dealerWindowConfig + '\')';
		},
		
		/**
		 * The list of URL parameters to propagate between pages.
		 */
		paramsToPropagate : ["locale", "dwp_dealerid", "dealerid", "ret_page", "expLvl", "dlr_loc"],
		
		initErrorHandler : function() {
			HD.util.DWR.getErrorMessage = function(message) {
				return HD.REO.template.getMessage("hdError", "An error has occurred, please try again later.", []);
			};
			DWREngine.setErrorHandler(HD.util.DWR.handleError);
		},
		
		validateForm : function(formEl, formNumber, nextUrl, isPreventForward) {
			var formValidator = HD.REO.Enrollment.formValidator;
			var errors = HD.util.Validation.validateForm(formEl, formValidator);
			var validationErrors = errors.formErrors;
			var errorMessages = errors.errorMessages;
			var errorIndicatorEl = HD.util.Dom.getByClass("hdErrorIndicator", "div")[0];
			if(validationErrors.length > 0) {
				HD.util.Validation.showErrors(formEl, formValidator, validationErrors);
				HD.util.Dom.removeClass(errorIndicatorEl, "hdHidden");
				HD.util.Dom.scrollTo(errorIndicatorEl);
				var errorHtml = ["<h3><span>Please provide all of the required information below</span></h3>", "<ul>"];
				for(var i = 0, len = errorMessages.length; i < len; i++) {
					errorHtml.push("<li>" + errorMessages[i] + "</li>");
				}
				errorHtml.push("</ul>");
				errorIndicatorEl.innerHTML = errorHtml.join("");
			} else if(!isPreventForward) {
				HD.util.Validation.hideErrors(formEl, formValidator);
				HD.util.Dom.addClass(errorIndicatorEl, "hdHidden");
				HD.REO.submitForm(formEl, formNumber, nextUrl);
			}
		},
		
		setupDealerView : function(view, overrideClass) {
			var isDwpDealer = false;
			var isDealerView = false;
			var common = HD.util.Common;
			var dealerId = common.getRequestParam("dwp_dealerid");
			var dealerLoc = common.getRequestParam("dlr_loc");
			if(!common.hasValue(dealerId)) {
				dealerId = common.getRequestParam("dealerid");
			} else {
				isDwpDealer = true;
			}
			if(common.hasValue(dealerId)) {
				isDealerView = true;
				view.addClass(HD.REO.View.CSS_CLASSES.DEALER_VIEW);
				if(common.hasValue(dealerLoc)) {
					view.addClass(HD.REO.View.CSS_CLASSES.DEALER_LOCATOR_VIEW);
				} else {
					if(common.hasValue(overrideClass)) {
						view.addClass(overrideClass);
					} else {
						view.addClass(HD.REO.View.CSS_CLASSES[isDwpDealer ? "DWP_DEALER_VIEW" : "NON_DWP_DEALER_VIEW"]);
					}
				}
			}
			return isDealerView;
		}
	};
}
