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" + " Loading...\n" + "
\n" + "
0\">\n" + "
\n" + "\n" + "
\n" + " \n" + "
\n" + " \n" + " \n" + "\n" + " \n" + "
\n" + " \n" + " \n" + "
\n" + "
\n" + "\n" + " \n" + "
0\"\n" + " ng-show=\"!(field.hasOwnProperty('formerAddress') && models['needsFormerAddress'].toLowerCase() != 'yes')\">\n" + " \n" + " \n" + " \n" + "
0\">{{errors[field.name]}}
\n" + "
\n" + "\n" + " \n" + "
\n" + "
\n" + "
\n" + "
\n" + " {{option.n}}\n" + "
\n" + "
\n" + "
\n" + "
\n" + " {{field.specialMessage}}\n" + "
\n" + "
\n" + "\n" + " \n" + "
\n" + " \n" + " \n" + "
{{errors[field.name]}}
\n" + "
\n" + "\n" + " \n" + "
\n" + " \n" + "
\n" + " \n" + " 0,\n" + " 'error' : hasNonEmptyValue(errors, 'confirm' + field.name[0].toUpperCase() + field.name.substring(1)),\n" + " 'success' : validated.hasOwnProperty(field.name) && validated.hasOwnProperty('confirm' + field.name[0].toUpperCase() + field.name.substring(1))}\"\n" + " id=\"{{'registrationConfirm' + field.name[0].toUpperCase() + field.name.substring(1)}}\"\n" + " ng-model=\"models['confirm' + field.name[0].toUpperCase() + field.name.substring(1)]\"\n" + " ng-if=\"!field.hasOwnProperty('type') || field.type == 'text'\"\n" + " maxlength=\"{{field.maxLength}}\"\n" + " placeholder=\"{{field.hasOwnProperty('placeholder') ? 'Confirm ' + field.placeholder : ''}}\"\n" + " type=\"text\"\n" + " ng-blur=\"validateConfirmation(field)\" />\n" + " \n" + " 0,\n" + " 'error' : hasNonEmptyValue(errors, 'confirm' + field.name[0].toUpperCase() + field.name.substring(1)),\n" + " 'success' : validated.hasOwnProperty(field.name) && validated.hasOwnProperty('confirm' + field.name[0].toUpperCase() + field.name.substring(1))}\"\n" + " id=\"{{'registrationConfirm' + field.name[0].toUpperCase() + field.name.substring(1)}}\"\n" + " ng-model=\"models['confirm' + field.name[0].toUpperCase() + field.name.substring(1)]\"\n" + " ng-if=\"field.hasOwnProperty('type') && field.type == 'password'\"\n" + " maxlength=\"{{field.maxLength}}\"\n" + " placeholder=\"{{field.hasOwnProperty('placeholder') ? 'Confirm ' + field.placeholder : ''}}\"\n" + " type=\"password\"\n" + " ng-blur=\"validateConfirmation(field)\" />\n" + "
\n" + " {{errors['confirm' + field.name[0].toUpperCase() + field.name.substring(1)]}}\n" + "
\n" + "
\n" + "
\n" + "
\n" + "\n" + " \n" + "
\n" + " \n" + "
\n" + "
\n" + " \n" + " 4\"\n" + " placeholder=\"XX\" type=\"tel\" size=\"2\" />\n" + " \n" + "
\n" + "
{{errors['ssn']}}
\n" + "
\n" + " Why Do We Need This?
\n" + " {{affiliateDisplayName}} is committed to responsible wagering and complying with state and federal regulations.\n" + " \n" + "
\n" + " 1. Identity Verification: We use your SSN to verify your age and state of residence.
\n" + " 2. IRS Requirement: The IRS requires all online wagering sites collect your SSN in order to report your taxable winnings.
\n" + "
\n" + " We protect your personal information with the latest website security technologies. We safeguard your information with SSL (Secure Sockets Layer).\n" + " SSL encrypts your personal information so that it cannot be read in transit by a third party. Your information is safe with us.\"\n" + " tooltip-title=\"Why {{affiliateDisplayName}} Requires Your SSN\"\n" + " tooltip-trigger=\"click\"\n" + " tooltip-position=\"bottom left\"\n" + " >
\n" + "
\n" + "
\n" + "
\n" + "\n" + " \n" + "
\n" + " \n" + "
\n" + "
\n" + " \n" + "\n" + " \n" + " \n" + "
\n" + "
\n" + " {{errors['dob']}}\n" + "
\n" + "
\n" + " You must be at least 18 years old to open a {{affiliateDisplayName}} account.\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + " \n" + "
\n" + "
Verification error: Please ensure all information is correct and try again.
\n" + "
You may already have an account. Go here to retrieve your login information.
\n" + "
This username is not available.
\n" + "
Sorry! Our systems may be down. Please try again later.
\n" + "
Temporary submission error. Please try again.
\n" + "
\n" + " We need to verify your identity. Please fax a copy of these to (859) 223-9141
\n" + " 1. Driver's License OR government issued ID
\n" + " 2. Social Security Card OR legal tax document
\n" + " 3. Utility Bill showing your address.

\n" + " For more help, please call (855) 845-5035.\n" + "
\n" + "
\n" + "\n" + " \n" + "\n" + " \n" + " \n" + "\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + " \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" + "

    You will need the following:

    \n" + "
  1. Driver's License OR Government Issued ID
  2. \n" + "
  3. Social Security Card OR Legal Tax Document
  4. \n" + "
  5. Utility Bill, showing a valid address
  6. \n" + "
\n" + "

You may fax these documents to expedite the process at 1-859-223-9141.

\n" + "
\n" + "
\n" + "

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('templates.terms', ['templates/terms.html']); angular.module("templates/terms.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("templates/terms.html", "
\n" + "

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" + "
"); }]); 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 = '
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'); } }; } }; } ]);