regjs
angular.module('templates.registration', ['templates/registration.html']);
angular.module("templates/registration.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("templates/registration.html",
"\n" +
"
\n" +
" \n" +
" \n" +
" \n" +
"
\n" +
"\n" +
"\n" +
"
\n" +
"There was a problem creating your account
\n" + "Please call Player Services to verify your identity and complete signup at {{regSupportPhone}}.
\n" + "\n" + " Call Us\n" + "\n" + "- \n" +
"
- Driver's License OR Government Issued ID \n" + "
- Social Security Card OR Legal Tax Document \n" + "
- Utility Bill, showing a valid address \n" + "
You will need the following:
\n" + "You may fax these documents to expedite the process at 1-859-223-9141.
\n" + "\n" +
"
\n" + " Cancel\n" + "
\n" +
"");
}]);
angular.module('templates.terms', ['templates/terms.html']);
angular.module("templates/terms.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("templates/terms.html",
"Account Help
\n" + "Need help signing up at {{affiliateDisplayName}}?
\n" +
" We can help! Call us at: {{supportPhone}}
\n" +
"\n" +
" Call Us\n" + " Cancel\n" + "
\n" +
"
");
}]);
angular.module('terms', ['modal', 'templates.terms'])
.value('version', '1.0.1');
angular.module('registration', [
'ngCookies',
'ngRoute',
'modal',
'templates.registration',
'terms',
'ui.utils',
'profiling',
'bonusEngine',
'tooltip'
])
.value('version', '1.0.1');
if (typeof window.console == 'undefined') window.console = {};
if (typeof console.log == 'undefined') console.log = function() {};
angular.module('registration').controller('registrationController', ['$scope', '$timeout', '$window', '$routeParams', '$http', '$cookies', 'bonusEngineSvc', 'featToggle',
function($scope, $timeout, $window, $routeParams, $http, $cookies, bonusEngine, featToggle) {
var TRACKING_REGISTRATION_ABANDONED_KEY = 'tracking_registration_abandoned';
// Process PromoCode as BonusCode, if FT : "BNSVPRM" is active
$scope.validatePromoAsBonus = false;
// Loading FT BNSVPRM
var bonusViaPromoCode = featToggle.hasFeature('CLASSIC', '1.0', 'BNSVPRM', Cdi.AppConfig.WS.CDI_SAID);
bonusViaPromoCode.then(
function (data) {
$scope.$evalAsync(function () {
if (data.hasFeature === true) {
$scope.validatePromoAsBonus = true;
}
});
},
function (err) {
}
);
// Map of days in month.
$scope.dobMonths = ['MM'];
$scope.dobDays = ['DD'];
$scope.dobYears = ['YYYY'];
$scope.monthDayMap = {
'1' : 31,
'2' : 'leap',
'3' : 31,
'4' : 30,
'5' : 31,
'6' : 30,
'7' : 31,
'8' : 31,
'9' : 30,
'10' : 31,
'11' : 30,
'12' : 31
};
$scope.content = {};
$scope.content.states = [
{"v":"","n":"State"},
{"v":"AL","n":"Alabama"},
{"v":"AK","n":"Alaska"},
{"v":"AS","n":"American Samoa"},
{"v":"AZ","n":"Arizona"},
{"v":"AR","n":"Arkansas"},
{"v":"CA","n":"California"},
{"v":"CO","n":"Colorado"},
{"v":"CT","n":"Connecticut"},
{"v":"DE","n":"Delaware"},
{"v":"DC","n":"District of Columbia"},
{"v":"FL","n":"Florida"},
{"v":"GA","n":"Georgia"},
{"v":"GU","n":"Guam"},
{"v":"HI","n":"Hawaii"},
{"v":"ID","n":"Idaho"},
{"v":"IL","n":"Illinois"},
{"v":"IN","n":"Indiana"},
{"v":"IA","n":"Iowa"},
{"v":"KS","n":"Kansas"},
{"v":"KY","n":"Kentucky"},
{"v":"LA","n":"Louisiana"},
{"v":"ME","n":"Maine"},
{"v":"MD","n":"Maryland"},
{"v":"MA","n":"Massachusetts"},
{"v":"MI","n":"Michigan"},
{"v":"MN","n":"Minnesota"},
{"v":"MS","n":"Mississippi"},
{"v":"MO","n":"Missouri"},
{"v":"MT","n":"Montana"},
{"v":"NE","n":"Nebraska"},
{"v":"NV","n":"Nevada"},
{"v":"NH","n":"New Hampshire"},
{"v":"NJ","n":"New Jersey"},
{"v":"NM","n":"New Mexico"},
{"v":"NY","n":"New York"},
{"v":"NC","n":"North Carolina"},
{"v":"ND","n":"North Dakota"},
{"v":"MP","n":"Northern Mariana Islands"},
{"v":"OH","n":"Ohio"},
{"v":"OK","n":"Oklahoma"},
{"v":"OR","n":"Oregon"},
{"v":"PA","n":"Pennsylvania"},
{"v":"PR","n":"Puerto Rico"},
{"v":"RI","n":"Rhode Island"},
{"v":"SC","n":"South Carolina"},
{"v":"SD","n":"South Dakota"},
{"v":"TN","n":"Tennessee"},
{"v":"TX","n":"Texas"},
{"v":"UM","n":"U.S. Minor Outlying Islands"},
{"v":"VI","n":"U.S. Virgin Islands"},
{"v":"UT","n":"Utah"},
{"v":"VT","n":"Vermont"},
{"v":"VA","n":"Virginia"},
{"v":"WA","n":"Washington"},
{"v":"WV","n":"West Virginia"},
{"v":"WI","n":"Wisconsin"},
{"v":"WY","n":"Wyoming"}
];
//$scope.isMobile = !!TSM; // This is throws error in..
if(typeof TSM !=="undefined") {
$scope.isMobile = true;
} else {
$scope.isMobile = false;
}
$scope.knownRestrictedStates = [];
$scope.knownRestrictedZips = [];
// Variable to store last ZIP we geopopulated city and state with.
$scope.lastZipLookup = '';
$scope.lastCity = '';
$scope.lastState = '';
// Username availability check.
$scope.availCheck = {
lastCheckedValue : ''
};
// Models for doing data binding with form.
$scope.models = { "cam_username" : "", "cam_password" : "" };
$scope.errors = {}; // Validation errors for associated models.
$scope.validated = {}; // Fields that have passed so far.
// Config options
$scope.doneInitializing = false;
$scope.config = false;
$scope.signupFields = []; // Used for quicker lookups
$scope.firedVPVs = [];
$scope.specialCases = ['ssn', 'dob']; // Cases with advanced handling.
$scope.textualInputTypes = ['text', 'number', 'tel'];
$scope.isFinalValidation = false;
$scope.hasRetried = false;
$scope.showFormError = false;
$scope.submitForm = false;
$scope.submitClass = "registration-button";
$scope.disableSubmit = false;
$scope.outstandingRARequest = false;
$scope.queueRA = false;
$scope.raTimeoutPromise = null;
$scope.preventMulipleSubmits = false; // Prevent multiple submits
// Defaults for field config options
$scope.formerAddressCheck = true; // Check for former address
$scope.ssnMode = 4; // SSN Mode, 0, 4, or 9
$scope.minAge = 18;
// Defaults for post-registration behaviors.
$scope.register = {};
$scope.autoLogin = {};
/**
* initializeForm
* Load dropdown options and affiliate-specific configuration for this registration.
*
* NOTE: This function is run automatically once when this controller is loaded.
*/
$scope.initializeForm = function() {
// Mark initialization flag appropriately.
$scope.doneInitializing = false;
$scope.fetchConfig();
// set our abandoned registration flag so we can record if a user left a registration before submitting
if(window.hasOwnProperty('localStorage')) {
localStorage.setItem(TRACKING_REGISTRATION_ABANDONED_KEY, true);
}
};
/**
* fetchConfig
* Load the affiliate-specific configuration for this registration and initialize from it.
*
* NOTE: This function is run automatically once when this controller is loaded.
*/
$scope.fetchConfig = function() {
// GET request for affiliate-specific file.
$scope.isNative = false;
if(typeof Cdi != "undefined" &&
typeof Cdi.AppConfig != "undefined" &&
typeof Cdi.AppConfig.isNative != "undefined" &&
Cdi.AppConfig.isNative) {
$scope.isNative = true;
configLoc = "resources/shared/extlibs/tsui/widgets/registration/" + Cdi.AppConfig.WS.CDI_SAID + "-mobile.json?v=3.18.8";
} else if ($scope.isMobile) {
configLoc = "/resources/shared/extlibs/tsui/widgets/registration/" + Cdi.AppConfig.WS.CDI_SAID + "-mobile.json?v=3.18.8";
} else {
configLoc = "/tsui/widgets/config/registration/1.0.1/" + Cdi.AppConfig.WS.CDI_SAID + ".json?v=3.18.8";
}
$http.get(configLoc).then(function(response) {
// TODO - migrate to use getMasterData
// Load data for select dropdowns.
if(response.hasOwnProperty('data') && response.data.hasOwnProperty('content')) {
if(response.data.content.hasOwnProperty('states')) {
$scope.content.states = response.data.content.states;
}
}
// Load field configuration data.
if(response.data.hasOwnProperty('config')) {
// Put the config data into controller $scope.
$scope.config = response.data.config;
// Initialize the form elements using config.
$scope.initializeFromConfig();
}
else {
$scope.config = false;
console.log("Reg: Config data not found in response.");
}
}, function() {
$scope.config = false;
console.log("Reg: Unable to request configuration data.");
});
};
/**
* initializeFromConfig
* Initialize the form component per the loaded configuration file.
*/
$scope.initializeFromConfig = function() {
// Make sure config exists.
if($scope.hasOwnProperty('config') && $scope.config !== false) {
// Promo code variable population
// Check $routeParams first.
$scope.models['promo_code'] = typeof $routeParams.promo_code != 'undefined' ? $routeParams.promo_code : '';
// Check regular style query params
if($scope.models['promo_code'] == '') {
var queryParams = $scope.getUrlVars();
if(queryParams.hasOwnProperty('promo_code') && queryParams['promo_code'].length > 0) {
if($scope.isMobile) {
var promoCode = queryParams['promo_code'];
var getPC = promoCode.split("#");
$scope.models['promo_code'] = getPC[0];
} else {
$scope.models['promo_code'] = queryParams['promo_code'];
}
}
/**
* US10291 - Native app saves all URL params into sessionStorage as JSON
* object.
* - Added by Alpesh 07/07/2015
*/
try {
if(sessionStorage.getItem('reg_urlParams')) {
console.log('TSM - Registration URL params detected in sessionStorage');
var queryParams = JSON.parse(sessionStorage.getItem('reg_urlParams'));
// Saving here just in case needed in future
$scope.models['urlParams'] = queryParams;
if(queryParams.hasOwnProperty('promo_code') && queryParams.promo_code.length > 0) {
console.log('TSM - Promocode found and saved into angular model');
$scope.models['promo_code'] = queryParams.promo_code;
}
}
} catch(e) {
console.log('Error Occured while reading sessionStorage Data');
console.log(e)
}
// - End of code added for US10291
}
if($scope.models['promo_code'].length > 0) {
console.log("Promo code detected: " + $scope.models['promo_code']);
}
// set the platform value
$scope.models['platform'] = Cdi.Core.Analytics.currentPlatform;
// get the origin value
var origin = localStorage.getItem('origin');
$scope.models['origin'] = origin != null ? origin : '';
// set the udid value to be passed to php
$scope.getBFGUDID(function(udid) {
$scope.models['udid'] = udid;
});
// set the IDFA value to be passed to php
$scope.getIDFA(function(idfa) {
$scope.models['idfa'] = idfa;
});
// Loop through each set of fields in the config.
// The multiple sets are used to break field inputs into different columns, allowing more versatile
// responsive behaviors.
var i, c;
var signupFields = [];
if($scope.config.hasOwnProperty('fields')) {
for(c = 0; c < $scope.config.fields.length; c++) {
for(i = 0; i < $scope.config.fields[c].length; i++) {
// Set maxLength for text/password inputs, if present.
if(!$scope.config.fields[c][i].hasOwnProperty('type') || $scope.config.fields[c][i].type == 'text' || $scope.config.fields[c][i].type == 'password') {
if(!$scope.config.fields[c][i].hasOwnProperty('maxLength')) {
$scope.config.fields[c][i].maxLength = '';
}
}
// Populate signupFields for a quick lookup reference of field names present in the form.
if($scope.config.fields[c][i].hasOwnProperty('name')) {
signupFields.push($scope.config.fields[c][i].name);
}
// Load data for select dropdowns.
if($scope.config.fields[c][i].hasOwnProperty('options')) {
// TODO update mapping method to play with getMasterData
// Replace content.states with state list in config.
if($scope.config.fields[c][i].options == "content.states" && $scope.hasOwnProperty('content') && $scope.content.hasOwnProperty('states')) {
$scope.config.fields[c][i].options = $scope.content.states;
}
// Preselect first option for option lists to prevent blank option from being generated by Angular.
$scope.models[$scope.config.fields[c][i].name] = $scope.config.fields[c][i].options[0].v;
}
}
}
}
// Other form data.
$scope.signupFields = signupFields;
// Whether or not form is intended to support former address checking.
if($scope.config.hasOwnProperty('formerAddressCheck')) {
$scope.formerAddressCheck = $scope.config.formerAddressCheck;
}
// SSN Mode to start in.
if($scope.config.hasOwnProperty('ssnMode')) {
$scope.ssnMode = $scope.config.ssnMode;
}
// GETtable endpoint with HTML of ToS.
if($scope.config.hasOwnProperty('termsSource')) {
$scope.termsSource = $scope.config.termsSource;
}
// Where to redirect user to upon successful registration (no autologin)
if($scope.config.hasOwnProperty('register')) {
$scope.register = $scope.config.register;
}
if($scope.config.hasOwnProperty('autoLogin')) {
$scope.autoLogin = $scope.config.autoLogin;
}
// Initialize DOB section if present in signup fields.
if($scope.inArray('dob', $scope.signupFields)) {
$scope.initializeDobDateInputs();
}
// Get Affiliate phone number.
if($scope.config.hasOwnProperty('supportPhone')) {
$scope.supportPhone = $scope.config.supportPhone;
}
// Get Affiliate name.
if($scope.config.hasOwnProperty('affiliateDisplayName')) {
$scope.affiliateDisplayName = $scope.config.affiliateDisplayName;
}
// Get Failed Registration Phone name.
if($scope.config.hasOwnProperty('regSupportPhone')) {
$scope.regSupportPhone = $scope.config.regSupportPhone;
}
// Affiliate marketing cookie checks
// Income Access
if(typeof $cookies["IA_BTAG"] !== 'undefined') {
var parts = $cookies["IA_BTAG"].split(":");
if(parts.length == 2) {
$scope.models['ia_affid'] = parts[0];
$scope.models['ia_btag'] = parts[1];
}
} else if ($scope.models['urlParams']) {
var urlParams = $scope.models['urlParams'];
$scope.models['ia_affid'] = urlParams.affid;
$scope.models['ia_btag'] = urlParams.btag;
console.log("affid:" + $scope.models['ia_affid']);
console.log("btag:" + $scope.models['ia_btag']);
}
// Commission Junction
if(typeof $cookies["COMJUNCT"] !== 'undefined') {
$scope.models['affiliate_info'] = 'CJunctionCookie:' + $cookies['COMJUNCT'];
// Commission Junction's IA codes.
$scope.models['ia_affid'] = "1791";
$scope.models['ia_btag'] = "a_2222b_2035";
}
}
// Flag to say we are done with initialization process.
$scope.doneInitializing = true;
// Fire page load events (VPV)
$scope.firePageLoadEvents();
// Register page unload events (VPV)
$scope.registerUnloadEvents();
};
/**
* getBFGUDID
* Gets the BigFish user device id from core
*/
$scope.getBFGUDID = function(callback) {
if(window.hasOwnProperty('WebViewJavascriptBridge')) {
WebViewJavascriptBridge.callHandler("getBigFishUDID", {}, function(udidFromApp) {
console.log('ObjC JS Bridge responded with: ' + udidFromApp);
callback(udidFromApp);
});
} else {
// udid will be returned as undefined
callback();
}
}
/**
* getIDFA
* Gets the IDFA (Identication For Advertisers) from a device
*/
$scope.getIDFA = function(callback) {
if (window.hasOwnProperty('WebViewJavascriptBridge')) {
WebViewJavascriptBridge.callHandler("getIDFA", {}, function(idfa) {
console.log('ObjC JS Bridge responded with: ' + idfa);
callback(idfa);
});
} else {
// idfa will be returned as undefined
callback();
}
}
/**
* initializeDobDateInputs
* Init date select boxes with valid numeric ranges for a date.
*/
$scope.initializeDobDateInputs = function() {
// Populate the DOB months.
$scope.dobMonths = ['MM'];
var i;
for(i = 1; i < 13; i++) {
i = i < 10 ? '0' + i : i;
$scope.dobMonths.push(i);
}
// Based on the selected month, update the # of days in month.
var tmp = typeof $scope.models['dobMonth'] != 'undefined' && parseInt($scope.models['dobMonth']) > 0 ? $scope.models['dobMonth'] : 1;
$scope.updateDobDaysFromMonth(tmp);
// Populate years for the last 120 years.
$scope.dobYears = ['YYYY'];
var currentYear = new Date().getFullYear();
for(i = currentYear - 10; i >= currentYear - 120; i--) {
$scope.dobYears.push(i);
}
// Pre-select first dropdown option to prevent Angular from creating empty dropdown option.
$scope.models['dobYear'] = $scope.dobYears[0];
$scope.models['dobMonth'] = $scope.dobMonths[0];
$scope.models['dobDay'] = $scope.dobDays[0];
};
/**
* updateDobDaysFromMonth
* Update the dob select for 'day of month' and update it with the appropriate range of valid
* numeric values for the currently selected month (as far as we can gather)
* @param month {string} String representation of numeric month of year (1-12)
*/
$scope.updateDobDaysFromMonth = function(month) {
// Constrain month to valid 1-12 value.
month = parseInt(month) > 0 && parseInt(month) < 13 ? parseInt(month) : 1;
// Get max days in currently selected month.
$scope.dobDays = ['DD'];
var limit = $scope.monthDayMap[month];
// February handling.
if(month == 2) {
limit = 28;
var year = typeof $scope.models['dobYear'] != 'undefined' && $scope.models['dobYear'] > 0 ? $scope.models['dobYear'] : new Date().getFullYear();
// Cheap leap year check.
if(new Date(year, 1, 29).getMonth() == 1) {
limit = 29;
}
}
// Rebuild list of days.
for(var i = 1; i <= limit; i++) {
i = i < 10 ? '0' + i : i;
$scope.dobDays.push(i);
}
// Restore previously selected option after rebuild, if possible. Otherwise, select the 1st.
var tmp = $scope.dobDays[0];
if($scope.models.hasOwnProperty('dobDay') && $scope.inArray($scope.models['dobDay'], $scope.dobDays)) {
tmp = $scope.models['dobDay'];
}
$scope.models['dobDay'] = tmp;
};
/**
* firePageLoadEvents
* Fire events associated with page load.
*/
$scope.firePageLoadEvents = function() {
// Fire VPV
if($scope.config.hasOwnProperty('pageLoadVPV')) {
$scope.fireVirtualPageView($scope.config['pageLoadVPV']);
}
Cdi.Core.Events.trigger('terms:ready');
};
/**
* registerUnloadEvents
* Register page unload event to fire VPV unless form submission is occurring.
*/
$scope.registerUnloadEvents = function() {
window.onbeforeunload = function() {
if($scope.config.hasOwnProperty('pageUnloadVPV') && !$scope.isFinalValidation) {
$scope.fireVirtualPageView($scope.config['pageUnloadVPV']);
}
}
};
/**
* lookupFieldByName
* Look up a field description object from the config by the name of the field.
* @param fieldName {string} Name of the field we are looking up.
* @returns {boolean|object} The field object if found, false otherwise.
*/
$scope.lookupFieldByName = function(fieldName) {
var c, f;
for(c = 0; c < $scope.config.fields.length; c++) {
for(f = 0; f < $scope.config.fields[c].length; f++) {
if($scope.config.fields[c][f].name == fieldName) {
return $scope.config.fields[c][f];
}
}
}
return false;
};
/**
* updateSelectValue
* Update model value associated with a select element that has just been changed.
* @param field {string} Name of the field we are changing the value of.
* @param value {*} Value we are setting 'field' to.
*/
$scope.updateSelectValue = function(field, value) {
// Special handling for dates.
if(field == 'dobMonth') {
$scope.updateDobDaysFromMonth(value);
}
else if(field == 'dobYear') {
$scope.updateDobDaysFromMonth(typeof $scope.models['dobMonth'] != 'undefined' && $scope.models['dobMonth'] > 0 ? $scope.models['dobMonth'] : 1);
}
// Normal value handling - all fields.
if(value.length > 0) {
$scope.models[field] = value;
}
};
/**
* validateField
* Validate the specified field is ready to be submitted to the backend.
* @param field {string|object} Name of field to validate or field object itself from config.
* @param checkForWebserviceRequest {boolean} optional Whether or not to hit Registration webservice.
*/
$scope.validateField = function(field, checkForWebserviceRequest) {
// We accept string name or object, if string, look up object.
if(typeof field == 'string') {
field = $scope.lookupFieldByName(field);
if(field === false) {
return false;
}
}
// SSN and DOB fields have separate handlers.
if(field.name == 'ssn') {
return $scope.validateSSN(field);
}
else if(field.name == 'dob') {
return $scope.validateDOB(field);
}
// Whether or not to check if field should create/update RA record if valid?
checkForWebserviceRequest = (typeof checkForWebserviceRequest == 'undefined') ? false : !!checkForWebserviceRequest;
// List of errors.
var errors = [];
// Cache a few re-used basic checks.
var hasMask = field.hasOwnProperty('mask') && field.mask != "";
var hasInvalidMaskedValue = hasMask && (!$scope.models.hasOwnProperty(field.name) || $scope.models[field.name] == undefined);
var isRequired = field.hasOwnProperty('required') && field['required'] == true;
var hasValue = hasInvalidMaskedValue || ($scope.models.hasOwnProperty(field.name) && $scope.models[field.name].toString().length > 0 && $scope.models[field.name] != field['placeholder']);
var isFormerAddress = field.hasOwnProperty('formerAddress') && $scope.models.hasOwnProperty('needsFormerAddress');
var isRequiredForFormerAddress = $scope.models['needsFormerAddress'].toLowerCase() == 'yes' && field.hasOwnProperty('formerRequired') && field['formerRequired'];
var hasRegex = field.hasOwnProperty('pattern');
var matchesPattern = hasValue && hasRegex && !hasInvalidMaskedValue ? $scope.models[field.name].toString().match(field['pattern']) : false;
var hasRAHook = field.hasOwnProperty('raHook') && field['raHook'] == true;
var hasUpdateLocation = field.hasOwnProperty('updateLocation') && field['updateLocation'] == true;
// Check if field has value provided.
// This check is only on non-form submission validations and intended to clear validated styles if the box was emptied of a valid value.
if(!$scope.isFinalValidation && !hasValue) {
if($scope.validated.hasOwnProperty(field.name)) {
delete $scope.validated[field.name];
}
if($scope.errors.hasOwnProperty(field.name)) {
delete $scope.errors[field.name];
}
if(field.name == 'cam_username') {
$scope.availCheck.lastCheckedValue = '';
}
return true;
}
/**
* Required field check.
* Occurs when field is required, either under normal required:true or as a former address field.
**/
if(isRequired || (isFormerAddress && isRequiredForFormerAddress)) {
if(!hasValue) {
if(field.hasOwnProperty('requiredMessage')) {
//noinspection JSUnresolvedVariable
errors.push(field.requiredMessage);
}
else {
errors.push('You must provide a value for \'' + field.name + '\'');
}
}
}
/**
* Banned state list check.
*/
if(errors.length < 1 && field.name == 'state') {
if($scope.inArray($scope.models[field.name], $scope.knownRestrictedStates)) {
errors.push($scope.config.messages['restrictedState']);
}
}
/**
* Banned zip list check
*/
if(errors.length < 1 && field.name == 'zip') {
if($scope.inArray($scope.models[field.name], $scope.knownRestrictedZips)) {
errors.push($scope.config.messages['restrictedZip']);
}
}
/**
* Regex Pattern check.
**/
if(errors.length < 1 && hasRegex && hasValue && !matchesPattern) {
if(field.hasOwnProperty('patternMessage')) {
errors.push(field['patternMessage']);
}
else {
errors.push('You must provide a valid value for \'' + field.name + '\'');
}
}
/**
* Validate confirmation if it is available.
*/
$scope.validateConfirmation(field);
/**
* Username ONLY - Check if taken.
*/
if(errors.length < 1 && field.name == 'cam_username') {
//If we are in final validation and we have an error then persist it.
//Since the validation is done asynchronously, we do not want to make a call to the username available service
//when the user has clicked submit. Instead, we will want to disable to submit until the username check has
//returned.
if($scope.isFinalValidation) {
if ($scope.errors.hasOwnProperty('cam_username')) {
errors = errors.concat($scope.errors['cam_username']);
}
} else { //otherwise, we will need to make a username availability check
return $scope.usernameAvailabilityCheck();
}
}
/**
* Log errors, store to errors model, and return them if present.
**/
// Standard error handling.
if(errors.length > 0) {
// Delete validation record, add errors, return.
delete $scope.validated[field.name];
$scope.errors[field.name] = errors.join(',');
console.log(field.name + ' error: ' + $scope.errors[field.name]);
return errors;
}
else {
// Add validation record, delete any errors.
$scope.validated[field.name] = true;
delete $scope.errors[field.name];
// Fire virtual page view here.
if(!$scope.isFinalValidation) {
$scope.fireFieldVPV(field);
}
}
// If update location option detected, fire a request to the location endpoint
if(checkForWebserviceRequest && hasUpdateLocation) {
$scope.updateLocation();
}
// Otherwise no errors found, field validated.
// If RA Hook option detected, fire a request to the RA handler.
if(checkForWebserviceRequest && hasRAHook) {
$scope.reportRA();
}
return true;
};
$scope.validateConfirmation = function(field) {
// Generate name of confirmation field.
var confirmField = 'confirm' + field.name[0].toUpperCase() + field.name.substring(1);
// Booleans for validation results.
var confirmError = false;
var checkedForMatch = false;
var matches = false;
// Cache a few re-used basic checks.
var fieldType = field.hasOwnProperty('type') && field['type'] != 'text' ? field['type'] : 'text';
var doWePerformConfirm = field.hasOwnProperty('confirm') && field['confirm'] == true;
var hasValue = doWePerformConfirm && $scope.models.hasOwnProperty(field.name) && $scope.models[field.name].length > 0 && $scope.models[field.name] != field['placeholder'];
var hasConfirmValue = $scope.models.hasOwnProperty(confirmField) && $scope.models[confirmField].length > 0;
// Confirmation check - requires matching value to fieldName under confirmFieldName
if((fieldType == 'password') || (fieldType == 'text') && doWePerformConfirm) {
// Do we have a value for the confirmation field at all?
if(!hasConfirmValue) {
confirmError = true;
}
// Does the confirmation value match the original value?
if(!confirmError && hasValue && hasConfirmValue) {
checkedForMatch = true;
if(field.hasOwnProperty('confirmCaseSensitive') && field['confirmCaseSensitive']) {
matches = $scope.models[field.name] == $scope.models[confirmField];
}
else {
matches = $scope.models[field.name].toLowerCase() == $scope.models[confirmField].toLowerCase();
}
if(!matches) {
confirmError = true;
$scope.errors[confirmField] = field.hasOwnProperty('confirmErrorMessage') ? field['confirmErrorMessage'] : "Field and confirmation field do not match.";
}
}
// Show encountered error on final validation OR
// any time 'base' field has a value but 'confirm' does not.
if(confirmError && (hasValue || $scope.isFinalValidation)) {
if(field.hasOwnProperty('confirmMessage')) {
//noinspection JSUnresolvedVariable
$scope.errors[confirmField] = field.confirmMessage;
}
else if($scope.isFinalValidation) {
$scope.errors[confirmField] = field.hasOwnProperty('confirmErrorMessage') ? field['confirmErrorMessage'] : "Field and confirmation field do not match.";
}
}
// Fire confirm VPV if present.
if(!confirmError && field.hasOwnProperty('confirmSuccessVPV')) {
$scope.fireVirtualPageView(field['confirmSuccessVPV']);
}
}
// Confirmation error handling.
if(confirmError) {
// Remove validated record, return current confirmation error.
delete $scope.validated[confirmField];
return $scope.errors[confirmField];
}
else if(!checkedForMatch || matches) {
// Add validated record, remove any confirmation error.
$scope.validated[confirmField] = true;
delete $scope.errors[confirmField];
return true;
}
// Do nothing.
return "";
};
/**
* validateSSN
* Placeholder for validating social security number block of 3 text inputs.
* @param field {object} SSN field object from config.
*/
$scope.validateSSN = function(field) {
// Booleans for validation results.
var ssnValue = '';
var ssnError = false;
var ssnPartMissing = false;
// Store a few basic checks.
var hasGroup1 = $scope.models.hasOwnProperty('ssnGroup1') && $scope.models['ssnGroup1'].toString().length > 0;
var hasGroup2 = $scope.models.hasOwnProperty('ssnGroup2') && $scope.models['ssnGroup2'].toString().length > 0;
var hasGroup3 = $scope.models.hasOwnProperty('ssnGroup3') && $scope.models['ssnGroup3'].toString().length > 0;
// SSN Mode - Only 9-digit mode needs first 2 groups.
if($scope.ssnMode == 9) {
// First group - 3 digits
if(hasGroup1) {
if(/^\d{3}$/.test($scope.models['ssnGroup1'])) {
ssnValue += $scope.models['ssnGroup1'];
}
else {
ssnError = true;
}
}
else {
ssnPartMissing = true;
}
// Second group - 2 digits
if(hasGroup2) {
if(/^\d{2}$/.test($scope.models['ssnGroup2'])) {
ssnValue += $scope.models['ssnGroup2'];
}
else {
ssnError = true;
}
}
else {
ssnPartMissing = true;
}
}
// SSN Mode - 4-digit and 9-digit modes need last group of 4.
if($scope.ssnMode == 9 || $scope.ssnMode == 4) {
// Third group - 4 digits
if(hasGroup3) {
if(/^\d{4}$/.test($scope.models['ssnGroup3'])) {
ssnValue += $scope.models['ssnGroup3'];
}
else {
ssnError = true;
}
}
else {
ssnPartMissing = true;
}
}
// No errors on model detected.
if(!ssnError && !ssnPartMissing) {
// Add validation records, update value, delete errors.
$scope.models['ssn'] = ssnValue;
$scope.validated['ssn'] = true;
delete $scope.errors['ssn'];
// Fire virtual page view here.
if(!$scope.isFinalValidation) {
$scope.fireFieldVPV(field);
}
}
// If error was encountered, or a required # group is missing on final submission,
// set that up and remove any validation records.
if(ssnError || ($scope.isFinalValidation && ssnPartMissing)) {
if($scope.ssnMode == 9) {
$scope.errors['ssn'] = 'SSN must be provided in the format XXX XX XXXX, where X is a number.';
}
else if($scope.ssnMode == 4) {
$scope.errors['ssn'] = 'SSN must be provided in the format ####, where # is a number.';
}
delete $scope.validated['ssn'];
return false;
}
// Do not show error from missing numbers until final validation pass on form submission.
else if(!$scope.isFinalValidation && ssnPartMissing) {
if($scope.validated.hasOwnProperty('ssn')) {
delete $scope.validated['ssn'];
}
if($scope.errors.hasOwnProperty('ssn')) {
delete $scope.errors['ssn'];
}
return false;
}
// Passes validation.
return true;
};
/**
* validateDOB
* Placeholder for validating dob field as a valid date from 3 selects
* @param field {string} DOB field object from config.
*/
$scope.validateDOB = function(field) {
var dobValue = '';
var dobError = '';
var numericPattern = /^\d+$/;
// DOB Mode - All 3 values should be set, check them individually and concatenate them together.
// First input - Year
if($scope.models.hasOwnProperty('dobYear')) {
if(!$scope.isFinalValidation && $scope.models['dobYear'].toString().toLowerCase() == "yyyy") {
if($scope.validated.hasOwnProperty('dob')) {
delete $scope.validated['dob'];
}
return false;
}
if(numericPattern.test($scope.models['dobYear']) && $scope.models['dobYear'].toString().length == 4) {
dobValue += $scope.models['dobYear'];
}
else {
dobError = 'Please provide your full date of birth.';
}
}
// Second input - Month
if(dobError == '' && $scope.models.hasOwnProperty('dobMonth')) {
if(!$scope.isFinalValidation && $scope.models['dobMonth'].toString().toLowerCase() == "mm") {
if($scope.validated.hasOwnProperty('dob')) {
delete $scope.validated['dob'];
}
return false;
}
if(numericPattern.test($scope.models['dobMonth']) && $scope.models['dobMonth'].toString().length == 2) {
dobValue += '-' + $scope.models['dobMonth'];
}
else {
dobError = 'You must provide the month of your birth.';
}
}
// Third input - Day
if(dobError == '' && $scope.models.hasOwnProperty('dobDay')) {
if(!$scope.isFinalValidation && $scope.models['dobDay'].toString().toLowerCase() == "dd") {
if($scope.validated.hasOwnProperty('dob')) {
delete $scope.validated['dob'];
}
return false;
}
if(numericPattern.test($scope.models['dobDay']) && $scope.models['dobDay'].toString().length == 2) {
dobValue += '-' + $scope.models['dobDay'];
}
else {
dobError = 'You must provide the day of your birth.';
}
}
// Populate DOB field with concatenated parts from dropdown.
if(dobError == '') {
$scope.models['dob'] = dobValue;
}
// Check against validation pattern.
if(dobError == '') {
if(field.hasOwnProperty('pattern')) {
if(!dobValue.match(field['pattern'])) {
dobError = 'Invalid date format for DOB provided.';
}
}
}
// If no errors, and state is provided, check if minimum age req is met.
if(dobError == '' && $scope.models.hasOwnProperty('state') && $scope.models['state'].length > 0 && $scope.isUserOldEnough() == -1) {
dobError = 'You do not meet the minimum age requirement for your state of residence.';
}
// Store validated value in model.
if(dobError == '') {
$scope.validated['dob'] = true;
delete $scope.errors['dob'];
// Fire virtual page view here.
if(!$scope.isFinalValidation) {
$scope.fireFieldVPV(field);
}
}
else { // Set errors on model if detected and return.
$scope.errors['dob'] = dobError;
delete $scope.validated['dob'];
return dobError;
}
// Passes validation.
return true;
};
/**
* isUserOldEnough
*
* @return number 1 if old enough, -1 if not old enough, 0 if not enough data to determine.
*/
$scope.isUserOldEnough = function() {
var dob;
if($scope.models.hasOwnProperty('dob') && $scope.models['dob'].length > 0) {
dob = $scope.models['dob'].split(/[- :]/);
for(var i = 0; i < dob.length; i++) {
dob[i] = parseInt(dob[i]);
}
var oldEnough = new Date(dob[0]+parseInt($scope.minAge), dob[1] - 1, dob[2], 0, 0, 0);
var now = new Date();
if(oldEnough <= now) {
return 1; // Old enough
}
}
else {
return 0; // Not enough info to determine
}
return -1; // Not old enough
};
/**
* gatherLeadData
* Gather email data from form.
*/
$scope.gatherLeadData = function() {
// Loop through data model for email data.
var ret = {};
if(!$scope.hasNonEmptyValue($scope.errors, 'email') && $scope.hasNonEmptyValue($scope.models, 'email')) {
ret.email = $scope.models['email'];
}
// Add the register id if an RA record already exists.
if($scope.models.hasOwnProperty('register_id')) {
ret.register_id = $scope.models.register_id;
}
return ret;
};
/**
* gatherCleanFormData
* Gather all data in $models that passes validation.
*/
$scope.gatherCleanFormData = function() {
// Loop through data model for form.
// Store every field value with no associated error in 'ret'.
var c, i;
var ret = {};
for(c = 0; c < $scope.config.fields.length; c++) {
for(i = 0; i < $scope.config.fields[c].length; i++) {
if(!$scope.hasNonEmptyValue($scope.errors, $scope.config.fields[c][i].name)) {
if($scope.hasNonEmptyValue($scope.models, $scope.config.fields[c][i].name)) {
ret[$scope.config.fields[c][i].name] = $scope.models[$scope.config.fields[c][i].name];
}
}
}
}
// Add the register id if an RA record already exists.
if($scope.models.hasOwnProperty('register_id')) {
ret.register_id = $scope.models.register_id;
}
// Add promo code if exists.
if($scope.hasNonEmptyValue($scope.models, 'promo_code')) {
ret.promo_code = $scope.models.promo_code;
}
// Add origin if exists.
if($scope.hasNonEmptyValue($scope.models, 'origin')) {
ret.origin = $scope.models.origin;
}
// Add platform
if($scope.hasNonEmptyValue($scope.models, 'platform')) {
ret.platform = $scope.models.platform;
}
// Add udid
if($scope.hasNonEmptyValue($scope.models, 'udid')) {
ret.udid = $scope.models.udid;
}
// Add idfa
if($scope.hasNonEmptyValue($scope.models, 'idfa')) {
ret.idfa = $scope.models.idfa;
}
// Add Income Access data if exists.
if($scope.hasNonEmptyValue($scope.models, 'ia_affid')) {
ret.ia_affid = $scope.models.ia_affid;
}
if($scope.hasNonEmptyValue($scope.models, 'ia_btag')) {
ret.ia_btag = $scope.models.ia_btag;
}
// Add CJ data if exists.
if($scope.hasNonEmptyValue($scope.models, 'affiliate_info')) {
ret.affiliate_info = $scope.models.affiliate_info;
}
return ret;
};
/**
* fireFieldVPV
* @param field string|object Name of the field we are firing a virtual page view for or field object itself from
* config.
* @return boolean Whether or not the VPV was successfully fired.
*/
$scope.fireFieldVPV = function(field) {
// Make sure field is the object definition.
if(typeof field == 'string') {
field = $scope.lookupFieldByName(field);
}
// Make sure we have config data for this field.
if(field === false || !field.hasOwnProperty('vpv')) {
return false;
}
return $scope.fireVirtualPageView(field['vpv']);
};
/**
* fireVirtualPageView
* @param vpv string Virtual page string to be sent to google.
* @return boolean Whether or not the VPV was successfully fired.
*/
$scope.fireVirtualPageView = function(vpv) {
if($scope.config.hasOwnProperty('useAnalyticsApi') && $scope.config['useAnalyticsApi']) {
// Only fire virtual page views once.
if(!$scope.inArray(vpv, $scope.firedVPVs)) {
$scope.firedVPVs.push(vpv);
// Send page view event to Google.
//noinspection JSUnresolvedFunction
Cdi.Core.Analytics.action('navigate', {path: vpv});
return true;
}
else {
console.log("VPV already fired this page load: " + vpv);
}
return false;
} else {
// Make sure ga() function exists.
//noinspection JSUnresolvedVariable
if(typeof window.ga != 'function') {
return false;
}
// Only fire virtual page views once.
if(!$scope.inArray(vpv, $scope.firedVPVs)) {
console.log("Firing VPV: " + vpv);
$scope.firedVPVs.push(vpv);
// Send page view event to Google.
//noinspection JSUnresolvedFunction
window.ga('send', 'pageview', vpv);
return true;
}
else {
console.log("VPV already fired this page load: " + vpv);
}
return false;
}
};
$scope.updateLocation = function() {
$scope.disableSubmit = true;
var params = {};
if($scope.hasNonEmptyValue($scope.models, 'zip')) {
params.zip = $scope.models['zip'];
}
if($scope.hasNonEmptyValue($scope.models, 'state')) {
params.state = $scope.models['state'];
}
var promise = Cdi.Core.ServicesManager.reqresTS({
name: Cdi.Service.ADW_REGISTRATION_LOCATION,
params: params
});
promise.then(function(data) {
if(data.hasOwnProperty('Response')) {
// if the zip is restricted, cache the zip in a list of known restrictions
// to prevent unnecessary webservice request
if(data.Response.hasOwnProperty('zipAllowed') && !data.Response.zipAllowed) {
if(!$scope.inArray(data.Response.zip, $scope.knownRestrictedZips)) {
$scope.knownRestrictedZips.push(data.Response.zip);
}
}
// if the state is restricted, cache the state in a list of known restrictions
// to prevent unnecessary webservice request
if(data.Response.hasOwnProperty('stateAllowed') && !data.Response.stateAllowed) {
if(!$scope.inArray(data.Response.stateCode, $scope.knownRestrictedStates)) {
$scope.knownRestrictedStates.push(data.Response.stateCode);
}
}
// revalidate the dob to make sure our user's age is still ok
if(data.Response.hasOwnProperty('minAge')) {
$scope.minAge = data.Response.minAge;
}
// update city only if city is currently empty
if(data.Response.hasOwnProperty('city') && !$scope.hasNonEmptyValue($scope.models, 'city')) {
$scope.models['city'] = data.Response.city;
}
// update state
// the webservice sends us back the same state that the user has entered
// so we can confidently update it without overriding what the user has done
if(data.Response.hasOwnProperty('stateCode')) {
$scope.models['state'] = data.Response.stateCode;
}
// update the ssn requirement
if(data.Response.hasOwnProperty('ssn')) {
$scope.ssnMode = data.Response.ssn;
}
$scope.validateField('zip');
$scope.validateField('state');
$scope.validateField('city');
$scope.validateField('dob');
$scope.validateField('ssn');
}
$scope.disableSubmit = false;
$scope.$apply();
}, function(data) {
// failure
console.log('Unable to gather location information.');
console.log(data);
$scope.disableSubmit = false;
$scope.$apply();
});
};
/**
* reportRA
* Placeholder for pre-submission backend request.
*/
$scope.reportRA = function() {
if($scope.outstandingRARequest) {
$scope.queueRA = true;
return;
} else {
$scope.outstandingRARequest = true;
}
// Gather email information.
var params = $scope.gatherLeadData();
console.log('$scope.reportRA() request params:');
console.log(params);
if(!params.hasOwnProperty('email')) {
$scope.outstandingRARequest = false;
}
// NOTE - Email address is required to start an RA record.
//noinspection JSUnresolvedVariable
var promise = Cdi.Core.ServicesManager.reqresTS({
name: Cdi.Service.ADW_REGISTRATION_UPSERT,
params: params
});
promise.then(function(data) {
console.log('$scope.reportRA() response:');
console.log(data);
if(data.hasOwnProperty('Response')) {
// ^ We can't check data.status reliably since multiple actions are taking place.
// Geolookup may succeed but RA record creation may fail and the status will be failure.
// even though we might have actions to perform.
// Register ID options.
if(data.Response.hasOwnProperty('register_id')) {
$scope.models['register_id'] = data.Response.register_id;
}
}
$scope.raTimeout();
}, function(data) {
// Request failure callback.
console.log("WS request failure");
console.log(data);
$scope.raTimeout();
});
};
$scope.raTimeout = function() {
$scope.raTimeoutPromise = $timeout(function() {
$scope.outstandingRARequest = false;
$scope.raTimeoutPromise = null;
if($scope.queueRA) {
console.log('Trigger RA from timeout.');
$scope.queueRA = false;
$scope.reportRA();
}
}, 5000);
};
/**
* usernameAvailabilityCheck
* Checks if the last entered username is available.
*/
$scope.usernameAvailabilityCheck = function() {
// If we have a username and it was not the last checked username this page load:
if($scope.models.hasOwnProperty('cam_username') && $scope.models['cam_username'] != $scope.availCheck.lastCheckedValue) {
$scope.disableSubmit = true;
var params = {
cam_username : $scope.models['cam_username']
};
var promise = Cdi.Core.ServicesManager.reqresTS({
name: Cdi.Service['ADW_REGISTRATION_EXISTS'],
params: params
});
promise.then(function(data) {
if(data.hasOwnProperty('Response') && data.Response.hasOwnProperty('exists')) {
$scope.availCheck.lastCheckedValue = $scope.models['cam_username'];
if(!data.Response['exists']) {
// TODO - Change if WS gets updated.
// This is the safe case in spite of how it reads. Exists true means not already existing.
delete $scope.errors['cam_username'];
$scope.validated['cam_username'] = true;
$scope.fireFieldVPV('cam_username');
}
else {
$scope.errors['cam_username'] = 'This username is not available.';
if($scope.validated.hasOwnProperty('cam_username')) {
delete $scope.validated['cam_username'];
}
}
}
$scope.disableSubmit = false;
$scope.$digest();
}, function(data) {
console.log("WS request failure");
console.log(data);
$scope.disableSubmit = false;
});
}
};
/**
* signupClicked
* Form submission handler.
*/
$scope.signupClicked = function(e) {
$scope.disableSubmit = true;
var c, i, result;
var errors = [];
$scope.isFinalValidation = true;
//Show loading in the mobile.
if ($scope.isMobile) {
e.preventDefault();
e.stopPropagation();
//Disable submit button
if($scope.submitForm) {
return;
}
TSM.showLoading();
}
$scope.hideErrorMsgs();
// For each necessary field, re-validate before submitting.
for(c = 0; c < $scope.config.fields.length; c++) {
for(i = 0; i < $scope.config.fields[c].length; i++) {
// Required fields.
if( ($scope.config.fields[c][i].hasOwnProperty('required') && $scope.config.fields[c][i].required) ||
($scope.hasNonEmptyValue($scope.config.fields[c][i], 'pattern'))) {
result = $scope.validateField($scope.config.fields[c][i]);
if(result !== true) {
errors = errors.concat(result);
}
} // Former address - required fields.
else if($scope.config.fields[c][i].hasOwnProperty('formerAddress') && $scope.config.fields[c][i]['formerAddress']
&& $scope.models.hasOwnProperty('needsFormerAddress') && $scope.models['needsFormerAddress'].toLowerCase() == 'yes'
&& $scope.config.fields[c][i].hasOwnProperty('formerRequired') && $scope.config.fields[c][i]['formerRequired']) {
result = $scope.validateField($scope.config.fields[c][i]);
if(result !== true) {
errors = errors.concat(result);
}
}
// Confirmation fields.
if($scope.config.fields[c][i].hasOwnProperty('confirm') && $scope.config.fields[c][i].confirm) {
result = $scope.validateConfirmation($scope.config.fields[c][i]);
if(typeof result == 'string' && result.length > 0) {
errors = errors.concat(result);
}
}
}
}
// If no errors, attempt the actual signup.
if(errors.length < 1 || (errors.length == 1 && typeof errors[0] == 'undefined')) {
console.log("No pre-submission frontend validation errors detected. Firing signup request.");
if(!$scope.preventMulipleSubmits) {
console.log("Prevent multiple submits");
$scope.signup();
$scope.preventMulipleSubmits = true;
}
}
else {
$scope.isFinalValidation = false;
$scope.disableSubmit = false;
//hide loading layer.
if ($scope.isMobile) {
TSM.hideLoading();
e.preventDefault();
}
}
};
$scope.launchUrlInBrowser = function() {
var url = 'https://www.' + $scope.config.domain + '/account/password/request';
if($scope.isMobile) {
TSM.TopLevel.showPasswordReset();
} else {
window.location.href = url;
}
};
$scope.hideErrorMsgs = function () {
$scope.idCheckError = false;
$scope.ssnDuplicate = false;
$scope.userExists = false;
$scope.toteDown = false;
$scope.genericErrors = false;
$scope.showFormError = false;
$scope.replicationDeplay = false;
};
/**
* signup
* Submit registration request to backend and handle response.
*/
$scope.signup = function() {
if($scope.raTimeoutPromise !== null) {
$timeout.cancel($scope.raTimeoutPromise);
$scope.queueRA = false;
}
// Gather information currently available from form.
var params = $scope.gatherCleanFormData();
console.log('$scope.signup() request params:');
console.log(params);
// Form submission VPV
if($scope.config.hasOwnProperty('submitVPV')) {
$scope.fireVirtualPageView($scope.config['submitVPV']);
}
// Signup request
var promise = Cdi.Core.ServicesManager.reqresTS({
name: Cdi.Service['ADW_REGISTRATION_REGISTER'],
params: params
});
var postLoginCallback = function(accountNumber, registrationResponseData) {
// call the auto opt-in service function
bonusEngine.setUserAccountId(accountNumber);
// Check if promo_code is entered and send it as bonusCode if FT BNSVPRM is active.
var optIntoBonusViaPromo = {};
if ($scope.validatePromoAsBonus === true && bonusEngine.isSet(params) && bonusEngine.isSet(params.promo_code)) {
optIntoBonusViaPromo = {
bonusCode: params.promo_code
,validate: true
};
}
var autoOptinPromise = bonusEngine.autoOptUserIn(optIntoBonusViaPromo);
// no matter the result of the bonus opt-in
// we need to handle the successful signup
var handlePostOptIn = function() {
$scope.handleSignupSuccess(registrationResponseData);
};
if(autoOptinPromise.hasOwnProperty('finally')) {
// if we get the promise back from angular
// the promise function will be the finally function
autoOptinPromise.finally(handlePostOptIn);
} else if(autoOptinPromise.hasOwnProperty('always')) {
// if we get the promise back from jquery
// the promise function will be the always function
autoOptinPromise.always(handlePostOptIn);
} else {
// this should never occur, but we need to make sure
// we have a fallback if finally or always don't exist
handlePostOptIn();
}
};
promise.then(function(data) {
console.log('$scope.signup() response data:');
console.log(data);
// Success callback
if(data.hasOwnProperty('Response') && data.Response.hasOwnProperty('statusTag') && data.Response['statusTag'] == 'SUCCESS_REGISTER_CUSTOMER') {
if(data.Response.hasOwnProperty('account')) {
if($scope.config.hasOwnProperty('performBonusOptIn') && $scope.config.performBonusOptIn == true) {
// because of mobile requiring a tmsid to be passed along with the
// request made from a specific user, we have to call their login
// function instead of the Orb core login function
if($scope.isMobile) {
TSM.GlobalData.UserSession.login(params.cam_username, params.cam_password, function() {
postLoginCallback(TSM.GlobalData.UserSession.AccountNumber, data);
}, function() {
console.log('BONUS ENGINE OPT IN FAILURE: unable to retrieve user session.');
$scope.handleSignupSuccess(data);
});
} else {
Cdi.Core.SessionManager.login(params.cam_username, params.cam_password)
.then(function(session) {
postLoginCallback(session.accountNum, data);
}, function(err) {
console.log('BONUS ENGINE OPT IN FAILURE: unable to retrieve user session.');
console.log(err);
$scope.handleSignupSuccess(data);
});
}
} else {
$scope.handleSignupSuccess(data);
}
} else {
$scope.handleSignupSuccess(data);
}
}
else {
// Service responded OK but not happy.
$scope.handleSignupFailure(data);
}
$scope.isFinalValidation = false;
}, function(data) {
// Service was unhappy. Fail out.
$scope.handleSignupFailure(data);
$scope.isFinalValidation = false;
$scope.disableSubmit = false;
$scope.preventMulipleSubmits = false;
});
};
/**
* handleSignupSuccess
* @param data {object} Response object from signup request.
*/
$scope.handleSignupSuccess = function(data) {
$scope.resetAbandonedRegistrationFlag();
// Fire success VPV
if($scope.config.hasOwnProperty('successVPV')) {
$scope.fireVirtualPageView($scope.config['successVPV']);
}
// if we're using the Analytics API, register the succeess event
if($scope.config.hasOwnProperty('useAnalyticsApi') && $scope.config['useAnalyticsApi']) {
// give the customer transaction value a default value
var customerTransactionValue = 25.00;
if($scope.config.hasOwnProperty('customerTransactionValue') && $scope.config['customerTransactionValue']) {
// if set in the config, use that value
customerTransactionValue = $scope.config['customerTransactionValue'];
}
// create a data object to be used in the register action
var analyticsRegisterObj = {
'camid': data['Response']['register_id'],
'origin': $scope.models['origin'],
'platform': Cdi.Core.Analytics.currentPlatform,
'customerTotalValue': customerTransactionValue,
'customerValue': customerTransactionValue,
'incomeAccessPlayerID': data['Response']['incomeAccessCustomerId']
};
console.log(analyticsRegisterObj);
// call the register action of the Analytics API
Cdi.Core.Analytics.action('register', analyticsRegisterObj);
}
// Set registration success cookie.
var expires = new Date(new Date().getTime() + 2592000000).toGMTString(); // Now +30d
//noinspection JSValidateTypes
document.cookie = "REGISTRATION_SUCCESS=1; expires=" + expires + "; path=/";
// Cleanup origin
localStorage.removeItem('origin');
if($cookies.hasOwnProperty('origin')) {
document.cookie = "origin=; expires=Thu, 01 Jan 1970 00:00:01 GMT; domain="+$scope.config['cookieDomain']+"; path=/";
}
// Auto login
if($scope.hasNonEmptyValue($scope.register, 'autoLogin')) {
// Handled by callback: mobile site
if($scope.hasNonEmptyValue($scope.autoLogin, 'useCallbacks')
&& $scope.hasNonEmptyValue($scope.autoLogin, 'successCallback')) {
// Eval is evil, get function off window.
var callback = window[$scope.autoLogin['successCallback']];
// Fire callback.
if(typeof callback == 'function') {
callback($scope.models['cam_username'], $scope.models['cam_password']);
}
else {
console.log("Could not find a success callback function by the name of: " + $scope.autoLogin['successCallback']);
}
} // Handled by full page POST to login script: main site.
else if($scope.hasNonEmptyValue($scope.autoLogin, 'successRedirect') && document.forms.hasOwnProperty('registrationLoginForm')) {
console.log("Performing autologin.");
document.getElementById('registrationLoginUser').value = $scope.models['cam_username'];
document.getElementById('registrationLoginPass').value = $scope.models['cam_password'];
document.getElementById('registrationSID').value = Cdi.AppConfig.TMX_ID;
document.forms["registrationLoginForm"].submit();
}
} // Simple redirect
else if($scope.hasNonEmptyValue($scope.register, 'successRedirect')) {
window.location.href = $scope.register['successRedirect'];
}
};
/**
* handleSignupFailure
* @param data {object} Response object from signup request.
*/
$scope.handleSignupFailure = function(data) {
console.log("Submission error detected.");
// Do we have a more specific failure case?
var code = data.Response.hasOwnProperty('statusTag') ? data.Response['statusTag'] : '';
if(!$scope.isSoftFailure(code)) {
var retry = data.Response.hasOwnProperty('id_check_retry') ? data.Response['id_check_retry'] : false;
if($scope.isMobile) {
$scope.showFormError = true;
$scope.$evalAsync(function(scope){
//if we have an idcheck failure decide if we can retry
//if we can't retry, change the error case to be default
if(code == 'IDCHECK_FAILED' && ($scope.hasRetried || !retry)) {
//Allow only one ID check. Subsequent failure shows the default message.
$scope.resetAbandonedRegistrationFlag();
$scope.setFailedLocalStorageVar();
code = "DEFAULT_CASE";
}
switch(code) {
case 'IDCHECK_FAILED' :
$scope.hasRetried = true;
scope.idCheckError = true;
scope.disableSubmit = false;
scope.preventMulipleSubmits = false;
break;
case 'SSN_DUPLICATE' :
scope.ssnDuplicate = true;
break;
case 'ERROR_USERNAME_ALREADY_EXISTS' :
scope.userExists = true;
break;
case 'Tote is down.' :
scope.toteDown = true;
break;
case 'ERROR_NO_CUSTOMER_INFO_FOUND':
case 'ERROR_DELAYED_REPLICATION':
scope.replicationDelay = true;
scope.disableSubmit = false;
scope.preventMulipleSubmits = false;
break;
default :
scope.genericErrors = true;
$scope.submitForm = true;
$scope.submitClass = "registration-button-grey";
scope.showRegistration = false;
scope.showVerficationFailed = true;
break;
}
});
TSM.hideLoading();
return;
} else {
switch(code) {
case 'IDCHECK_FAILED':
if (retry && !$scope.hasRetried) {
$scope.hasRetried = true;
$scope.$evalAsync(function(scope) {
scope.showFormError = true;
scope.idCheckError = true;
scope.disableSubmit = false;
scope.preventMulipleSubmits = false;
});
return;
}
break;
case 'ERROR_NO_CUSTOMER_INFO_FOUND':
case 'ERROR_DELAYED_REPLICATION':
$scope.$evalAsync(function(scope) {
scope.showFormError = true;
scope.replicationDelay = true;
scope.disableSubmit = false;
scope.preventMulipleSubmits = false;
});
return;
}
}
$scope.resetAbandonedRegistrationFlag();
$scope.setFailedLocalStorageVar();
// Send error VPV
$scope.fireVirtualPageView($scope.lookupFailureVPVCode(code));
// If a special error exists, set a failed registration cookie.
if(code.length > 0) {
var expires = new Date(new Date().getTime() + 86400000).toGMTString(); // Now +1d
//noinspection JSValidateTypes
document.cookie = "REGISTRATION_FAILURE=" + code + "; path=/";
}
// Callback for failure
if($scope.hasNonEmptyValue($scope.autoLogin, 'useCallbacks')) {
if($scope.hasNonEmptyValue($scope.autoLogin, 'failureCallback')) {
// Eval is evil, get function off window.
var callback = window[$scope.autoLogin['failureCallback']];
// Fire callback.
if(typeof callback == 'function') {
callback(data);
}
else {
console.log("Could not find a failure callback function by the name of: " + $scope.autoLogin['failureCallback']);
}
}
} // Handled by full page POST to login script.
else if($scope.hasNonEmptyValue($scope.register, 'failureRedirect')) {
console.log("Redirect target: " + $scope.register['failureRedirect']);
window.location.href = $scope.register['failureRedirect'];
}
}
};
$scope.isSoftFailure = function(statusString) {
if($scope.isMobile) {
return false;
}
var softFailureErrors = {
"ERROR_USERNAME_ALREADY_EXISTS" : "ERROR_USERNAME_ALREADY_EXISTS"
};
return softFailureErrors.hasOwnProperty(statusString);
};
/**
* lookupFailureVPVCode
* @param errorType string Registration response error code.
* @returns string Failure VPV code mapping to response error code.
*/
$scope.lookupFailureVPVCode = function(errorType) {
console.log('Looking up error code: ' + errorType);
var knownErrors = {
"IDCHECK_FAILED" : "IdentityCheck",
"SSN_DUPLICATE" : "DuplicateAccount",
"SSN_BLACKLISTED" : "BlacklistedAccount"
};
if(knownErrors.hasOwnProperty(errorType)) {
return "/Virtual/Account/Signup/" + knownErrors[errorType];
}
return "/Virtual/Account/Signup/UnknownError";
};
/**
* inArray
* Array search helper method (sequential)
* @return {boolean} Whether or not the specified key was found in the array.
*/
$scope.inArray = function(needle, haystack) {
var length = haystack.length;
for(var i = 0; i < length; i++) {
if(haystack[i] == needle) return true;
}
return false;
};
/**
* getUrlVars
* Load any query string parameters.
* @returns {object} Key-value collection of any query string parameters.
*/
$scope.getUrlVars = function() {
var vars = {};
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
vars[key] = value;
});
return vars;
};
/**
* hasNonEmptyValue
* Checks array for a key.
* @return {boolean} Returns whether or not the specified key is set and resolves to true or a string of length > 0
*/
$scope.hasNonEmptyValue = function(object, key) {
var objValue = object[key];
if(typeof objValue === 'number') {
objValue = objValue.toString();
}
if(typeof objValue === 'undefined') {
objValue = '';
}
return (object.hasOwnProperty(key) && (objValue.length > 0 || objValue == true));
};
$scope.cancelSubmit = function() {
if ($scope.isMobile) {
TSM.Home.show();
}
};
/**
* Sets reg failed local storage storage var
* @return none
*/
$scope.setFailedLocalStorageVar = function(){
localStorage.setItem("tracking_registration_failed",true);
};
$scope.resetAbandonedRegistrationFlag = function() {
if(window.hasOwnProperty('localStorage')) {
localStorage.setItem(TRACKING_REGISTRATION_ABANDONED_KEY, false);
}
};
// Initialize form using configuration currently in $scope.config.
$scope.initializeForm();
}]);
angular.module('registration').directive('registration', function() {
return {
restrict: 'AE',
templateUrl: 'templates/registration.html',
controller: 'registrationController',
link: function(scope, element) {
scope.showRegistration = true;
scope.showVerficationFailed = false;
scope.showRegBack = false;
scope.showBackInfo = function() {
scope.$evalAsync(function() {
if (scope.showVerficationFailed === true) {
TSM.Home.show();
return;
}
scope.showRegistration = false;
scope.showVerficationFailed = false;
scope.showRegBack = true;
$('#registerBack').hide();
});
};
}
};
});
angular.module('registration').directive('textualInput', ['$compile', function($compile) {
return {
restrict: 'E',
link: function(scope, elem, attr) {
// compile the output of the template before adding to the DOM
// doing this because IE won't allow a change to the type attr
// of inputs so angular won't allow it to be dynamic
var inputHtml = "";
inputHtml += "";
var compiledHtml = $compile(inputHtml)(scope);
elem.replaceWith(compiledHtml);
if(scope.field.hasOwnProperty('mask') && scope.field.mask) {
elem.blur();
}
}
};
}]);
angular.module('registration').directive('placeholder', ['$timeout', function ($timeout) {
if (!angular.mock) {
var test = document.createElement('input');
if (test.placeholder !== void 0)
return {};
}
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
link: function (scope, elem, attrs, ngModel) {
var orig_val = getValue(), is_pwd = attrs.type === 'password', text = attrs.placeholder, emptyClassName = 'empty', domElem = elem[0], clone;
if (!text) {
return;
}
if (is_pwd) {
setupPasswordPlaceholder();
}
setValueWithModel(orig_val);
elem.bind('focus', function () {
if (elem.hasClass(emptyClassName)) {
elem.val('');
elem.removeClass(emptyClassName);
elem.removeClass('error');
}
});
elem.bind('blur', updateValue);
// because the ui-mask wants to blank the placeholder value,
// we have to watch for when the value changes
// and update the placeholder value accordingly
var maskWatcher = scope.$watch(attrs.ngModel, function() {
// $watch returns a function to disable it,
// we need to disable it in order for the mask to
// not mess up the placeholder functionality for IE9
// after it's already set its initial masking value
maskWatcher();
updateValue();
});
if (!ngModel) {
elem.bind('change', updateValue);
}
if (ngModel) {
ngModel.$render = function () {
setValue(ngModel.$viewValue);
};
}
function updateValue(e) {
var val = elem.val();
if (elem.hasClass(emptyClassName) && val === text) {
return;
}
if (document.documentMode <= 11) {
$timeout(function () {
setValueWithModel(val);
}, 0);
} else {
setValueWithModel(val);
}
}
function setValueWithModel(val) {
setValue(val);
if (ngModel) {
ngModel.$setViewValue(val);
}
}
function setValue(val) {
if (!val) {
elem.addClass(emptyClassName);
if (is_pwd) {
showPasswordPlaceholder();
} else {
elem.val(text);
}
} else {
elem.removeClass(emptyClassName);
if (is_pwd) {
hidePasswordPlaceholder();
}
elem.val(val);
}
}
function getValue() {
if (ngModel) {
return scope.$eval(attrs.ngModel) || '';
}
return getDomValue() || '';
}
function getDomValue() {
var val = elem.val();
if (val === attrs.placeholder) {
val = '';
}
return val;
}
function setupPasswordPlaceholder() {
clone = angular.element('').attr(angular.extend(extractAttributes(domElem), {
'type': undefined,
'value': text,
'placeholder': '',
'id': '',
'name': ''
})).addClass(emptyClassName).addClass('ng-hide').bind('focus', hidePasswordPlaceholderAndFocus);
domElem.parentNode.insertBefore(clone[0], domElem);
}
function showPasswordPlaceholder() {
clone.val(text);
elem.addClass('ng-hide');
clone.removeClass('ng-hide');
}
function hidePasswordPlaceholder() {
clone.addClass('ng-hide');
elem.removeClass('ng-hide');
}
function hidePasswordPlaceholderAndFocus() {
hidePasswordPlaceholder();
domElem.focus();
}
function extractAttributes(element) {
var attr = element.attributes, copy = {}, skip = /^jQuery\d+/;
for (var i = 0; i < attr.length; i++) {
if (attr[i].specified && !skip.test(attr[i].name)) {
copy[attr[i].name] = attr[i].value;
}
}
return copy;
}
}
};
}]);
angular.module('terms').directive('terms', ['$http', '$window',
function($http, $window) {
return {
restrict: 'A,E',
templateUrl: 'templates/terms.html',
link: function($scope, element, attrs) {
Cdi.Core.Events.once('terms:ready', function(userMessage) {
//Let if anything is needed just pass in
// the message and handle in the switch case below...
if(typeof userMessage != "undefined") {
switch(userMessage) {
case 'loginMessage':
$scope.$evalAsync(function() {
$scope.loginMessage = true;
});
break;
default :
break;
}
}
// Set up click event for "Close Terms of Service" button present inside ToS modal.
angular.element("#registration").on('click', '.registration-button', function() {
angular.element("#registration").scope().closeTerms();
});
if ($scope.termsSourceStandalone) {
// Set up click event for "Close Terms of Service" button present inside ToS modal.
angular.element("#terms").on('click', '.registration-button', function() {
angular.element("#terms").scope().closeTerms();
});
}
});
// Defaults for ToS config options.
$scope.termsSource = ''; // URL of terms of service content source of origin.
$scope.modalContent = 'By {{messageAction}} above, I agree to the Terms and Conditions
\n" + "By signing in, I agree to the Terms & Conditions
\n" + "Already registered? Login Now
\n" + "Modal content
'; // Modal content
$scope.showTermsModal = false; // Toggle for showing/hiding modal
$scope.messageAction = $scope.isMobile ? 'submitting' : 'clicking';
// Resizing fixes so the terms modal places nice on tablet/mobile resize.
angular.element($window).on('resize', $scope.resizeTerms);
angular.element($window).on('orientationchange', $scope.resizeTerms);
/**
* readTerms
* Open a modal with the terms of service for this registration event.
*/
$scope.readTerms = function() {
// GET request to endpoint with ToS data.
if (typeof Cdi != "undefined" &&
typeof Cdi.AppConfig != "undefined" &&
typeof Cdi.AppConfig.isNative != "undefined" &&
Cdi.AppConfig.isNative) {
$scope.termsSource = "resources/shared/extlibs/tsui/widgets/registration/terms.json";
} else if(!$scope.isMobile && $scope.termsSourceStandalone) {
$scope.termsSource = $scope.termsSourceStandalone;
}
$http.get($scope.termsSource).then(function(response) {
if (response.hasOwnProperty('data') && response.data.hasOwnProperty('content')) {
$scope.modalContent = response.data.content.replace(/(?:\r\n|\r|\n|\t)/g, "");
} else if (typeof response == 'string' && response.length > 0) {
$scope.modalContent = response;
} else {
$scope.modalContent = 'We were unable to load your terms of service.';
}
// Show modal.
$scope.showTermsModal = true;
// Apply the terms sizing fix html loads and animates.
angular.element(".app-modal-box")
.one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', $scope.resizeTerms);
}, function() {
$scope.modalContent = 'We were unable to load your terms of service.';
$scope.showTermsModal = true;
});
};
/**
* closeTerms
* Close the ToS modal.
*/
$scope.closeTerms = function() {
$scope.showTermsModal = false;
$scope.$apply();
};
/**
* resizeTerms
* Resize Terms and conditions modal to fit within screen.
*/
$scope.resizeTerms = function() {
var boxEl;
if (angular.element('.app-modal-box').length) {
if ($("#registration .app-modal-box").is(":visible")) {
boxEl = $("#registration");
} else {
boxEl = $("#terms");
}
var topOffset = boxEl.find('.app-modal-box').get(0).getBoundingClientRect()['top'];
var bottomOffset = boxEl.find('.app-modal-box').get(0).getBoundingClientRect()['bottom'];
var modalHeight = bottomOffset - topOffset;
var titleHeight = boxEl.find('.app-modal-box .title').outerHeight(true);
var buttonHeight = boxEl.find('.app-modal-box .registration-button-container').outerHeight(true);
var whiteSpace = (boxEl.find('.app-modal .inner').outerHeight(true) - boxEl.find('.app-modal .inner').innerHeight()) + 4;
console.log('modalHeight: ' + modalHeight + 'px');
console.log('titleHeight: ' + titleHeight + 'px');
console.log('buttonHeight: ' + buttonHeight + 'px');
console.log('whiteSpace: ' + whiteSpace + 'px');
var newHeight = modalHeight - titleHeight - buttonHeight - whiteSpace;
if (newHeight < 0) newHeight = 0;
console.log('newHeight: ' + newHeight + 'px');
boxEl.find('.app-modal .inner').height(newHeight + 'px');
}
};
}
};
}
]);