/* Minification failed. Returning unminified contents.
(20,1): run-time error CSS1019: Unexpected token, found '!'
(20,2): run-time error CSS1019: Unexpected token, found 'function('
(20,12): run-time error CSS1019: Unexpected token, found '$'
(20,14): run-time error CSS1019: Unexpected token, found ')'
(20,16): run-time error CSS1019: Unexpected token, found '{'
(24,17): run-time error CSS1031: Expected selector, found '='
(24,17): run-time error CSS1025: Expected comma or open brace, found '='
(88,23): run-time error CSS1031: Expected selector, found '='
(88,23): run-time error CSS1025: Expected comma or open brace, found '='
(335,2): run-time error CSS1019: Unexpected token, found '$'
(335,18): run-time error CSS1031: Expected selector, found '='
(335,18): run-time error CSS1025: Expected comma or open brace, found '='
(347,2): run-time error CSS1019: Unexpected token, found '$'
(347,27): run-time error CSS1031: Expected selector, found '='
(347,27): run-time error CSS1025: Expected comma or open brace, found '='
(352,2): run-time error CSS1019: Unexpected token, found '$'
(352,30): run-time error CSS1031: Expected selector, found '='
(352,30): run-time error CSS1025: Expected comma or open brace, found '='
(354,15): run-time error CSS1031: Expected selector, found '='
(354,15): run-time error CSS1025: Expected comma or open brace, found '='
(453,20): run-time error CSS1031: Expected selector, found '='
(453,20): run-time error CSS1025: Expected comma or open brace, found '='
(474,1): run-time error CSS1019: Unexpected token, found '}'
(474,2): run-time error CSS1019: Unexpected token, found '('
(474,18): run-time error CSS1031: Expected selector, found ')'
(474,18): run-time error CSS1025: Expected comma or open brace, found ')'
(475,13): run-time error CSS1031: Expected selector, found '='
(475,13): run-time error CSS1025: Expected comma or open brace, found '='
(477,30): run-time error CSS1031: Expected selector, found '='
(477,30): run-time error CSS1025: Expected comma or open brace, found '='
(587,69): run-time error CSS1031: Expected selector, found '='
(587,69): run-time error CSS1025: Expected comma or open brace, found '='
(622,58): run-time error CSS1031: Expected selector, found '='
(622,58): run-time error CSS1025: Expected comma or open brace, found '='
(695,64): run-time error CSS1031: Expected selector, found '='
(695,64): run-time error CSS1025: Expected comma or open brace, found '='
(904,65): run-time error CSS1031: Expected selector, found '='
(904,65): run-time error CSS1025: Expected comma or open brace, found '='
(931,72): run-time error CSS1031: Expected selector, found '='
(931,72): run-time error CSS1025: Expected comma or open brace, found '='
(946,62): run-time error CSS1031: Expected selector, found '='
(946,62): run-time error CSS1025: Expected comma or open brace, found '='
(961,58): run-time error CSS1031: Expected selector, found '='
(961,58): run-time error CSS1025: Expected comma or open brace, found '='
(970,62): run-time error CSS1031: Expected selector, found '='
(970,62): run-time error CSS1025: Expected comma or open brace, found '='
(1026,55): run-time error CSS1031: Expected selector, found '='
(1026,55): run-time error CSS1025: Expected comma or open brace, found '='
(1040,60): run-time error CSS1031: Expected selector, found '='
(1040,60): run-time error CSS1025: Expected comma or open brace, found '='
(1066,72): run-time error CSS1031: Expected selector, found '='
(1066,72): run-time error CSS1025: Expected comma or open brace, found '='
(1086,64): run-time error CSS1031: Expected selector, found '='
(1086,64): run-time error CSS1025: Expected comma or open brace, found '='
(1097,72): run-time error CSS1031: Expected selector, found '='
(1097,72): run-time error CSS1025: Expected comma or open brace, found '='
(1109,68): run-time error CSS1031: Expected selector, found '='
(1109,68): run-time error CSS1025: Expected comma or open brace, found '='
(1125,56): run-time error CSS1031: Expected selector, found '='
(1125,56): run-time error CSS1025: Expected comma or open brace, found '='
(1165,59): run-time error CSS1031: Expected selector, found '='
(1165,59): run-time error CSS1025: Expected comma or open brace, found '='
(1206,1): run-time error CSS1019: Unexpected token, found '$'
(1206,2): run-time error CSS1019: Unexpected token, found '('
(1206,11): run-time error CSS1031: Expected selector, found ')'
(1206,11): run-time error CSS1025: Expected comma or open brace, found ')'
(1208,2): run-time error CSS1019: Unexpected token, found ')'
 */
/* =========================================================
 * bootstrap-datepicker.js 
 * http://www.eyecon.ro/bootstrap-datepicker
 * =========================================================
 * Copyright 2012 Stefan Petre
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================= */
 
!function( $ ) {
	
	// Picker object
	
	var Datepicker = function(element, options){
		this.element = $(element);
		this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
		this.picker = $(DPGlobal.template)
							.appendTo('body')
							.on({
								click: $.proxy(this.click, this)//,
								//mousedown: $.proxy(this.mousedown, this)
							});
		this.isInput = this.element.is('input');
		this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
		
		if (this.isInput) {
			this.element.on({
				focus: $.proxy(this.show, this),
				//blur: $.proxy(this.hide, this),
				keyup: $.proxy(this.update, this)
			});
		} else {
			if (this.component){
				this.component.on('click', $.proxy(this.show, this));
			} else {
				this.element.on('click', $.proxy(this.show, this));
			}
		}
	
		this.minViewMode = options.minViewMode||this.element.data('date-minviewmode')||0;
		if (typeof this.minViewMode === 'string') {
			switch (this.minViewMode) {
				case 'months':
					this.minViewMode = 1;
					break;
				case 'years':
					this.minViewMode = 2;
					break;
				default:
					this.minViewMode = 0;
					break;
			}
		}
		this.viewMode = options.viewMode||this.element.data('date-viewmode')||0;
		if (typeof this.viewMode === 'string') {
			switch (this.viewMode) {
				case 'months':
					this.viewMode = 1;
					break;
				case 'years':
					this.viewMode = 2;
					break;
				default:
					this.viewMode = 0;
					break;
			}
		}
		this.startViewMode = this.viewMode;
		this.weekStart = options.weekStart||this.element.data('date-weekstart')||0;
		this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
		this.onRender = options.onRender;
		this.fillDow();
		this.fillMonths();
		this.update();
		this.showMode();
	};
	
	Datepicker.prototype = {
		constructor: Datepicker,
		
		show: function(e) {
			this.picker.show();
			this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
			this.place();
			$(window).on('resize', $.proxy(this.place, this));
			if (e ) {
				e.stopPropagation();
				e.preventDefault();
			}
			if (!this.isInput) {
			}
			var that = this;
			$(document).on('mousedown', function(ev){
				if ($(ev.target).closest('.datepicker').length == 0) {
					that.hide();
				}
			});
			this.element.trigger({
				type: 'show',
				date: this.date
			});
		},
		
		hide: function(){
			this.picker.hide();
			$(window).off('resize', this.place);
			this.viewMode = this.startViewMode;
			this.showMode();
			if (!this.isInput) {
				$(document).off('mousedown', this.hide);
			}
			//this.set();
			this.element.trigger({
				type: 'hide',
				date: this.date
			});
		},
		
		set: function() {
			var formated = DPGlobal.formatDate(this.date, this.format);
			if (!this.isInput) {
				if (this.component){
					this.element.find('input').prop('value', formated);
				}
				this.element.data('date', formated);
			} else {
				this.element.prop('value', formated);
			}
		},
		
		setValue: function(newDate) {
			if (typeof newDate === 'string') {
				this.date = DPGlobal.parseDate(newDate, this.format);
			} else {
				this.date = new Date(newDate);
			}
			this.set();
			this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
			this.fill();
		},
		
		place: function(){
			var offset = this.component ? this.component.offset() : this.element.offset();
			this.picker.css({
				top: offset.top + this.height,
				left: offset.left
			});
		},
		
		update: function(newDate){
			this.date = DPGlobal.parseDate(
				typeof newDate === 'string' ? newDate : (this.isInput ? this.element.prop('value') : this.element.data('date')),
				this.format
			);
			this.viewDate = new Date(this.date.getFullYear(), this.date.getMonth(), 1, 0, 0, 0, 0);
			this.fill();
		},
		
		fillDow: function(){
			var dowCnt = this.weekStart;
			var html = '<tr>';
			while (dowCnt < this.weekStart + 7) {
				html += '<th class="dow">'+DPGlobal.dates.daysMin[(dowCnt++)%7]+'</th>';
			}
			html += '</tr>';
			this.picker.find('.datepicker-days thead').append(html);
		},
		
		fillMonths: function(){
			var html = '';
			var i = 0
			while (i < 12) {
				html += '<span class="month">'+DPGlobal.dates.monthsShort[i++]+'</span>';
			}
			this.picker.find('.datepicker-months td').append(html);
		},
		
		fill: function() {
			var d = new Date(this.viewDate),
				year = d.getFullYear(),
				month = d.getMonth(),
				currentDate = this.date.valueOf();
			this.picker.find('.datepicker-days th:eq(1)')
						.text(DPGlobal.dates.months[month]+' '+year);
			var prevMonth = new Date(year, month-1, 28,0,0,0,0),
				day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
			prevMonth.setDate(day);
			prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7);
			var nextMonth = new Date(prevMonth);
			nextMonth.setDate(nextMonth.getDate() + 42);
			nextMonth = nextMonth.valueOf();
			var html = [];
			var clsName,
				prevY,
				prevM;
			while(prevMonth.valueOf() < nextMonth) {
				if (prevMonth.getDay() === this.weekStart) {
					html.push('<tr>');
				}
				clsName = this.onRender(prevMonth);
				prevY = prevMonth.getFullYear();
				prevM = prevMonth.getMonth();
				if ((prevM < month &&  prevY === year) ||  prevY < year) {
					clsName += ' old';
				} else if ((prevM > month && prevY === year) || prevY > year) {
					clsName += ' new';
				}
				if (prevMonth.valueOf() === currentDate) {
					clsName += ' active';
				}
				html.push('<td class="day '+clsName+'">'+prevMonth.getDate() + '</td>');
				if (prevMonth.getDay() === this.weekEnd) {
					html.push('</tr>');
				}
				prevMonth.setDate(prevMonth.getDate()+1);
			}
			this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
			var currentYear = this.date.getFullYear();
			
			var months = this.picker.find('.datepicker-months')
						.find('th:eq(1)')
							.text(year)
							.end()
						.find('span').removeClass('active');
			if (currentYear === year) {
				months.eq(this.date.getMonth()).addClass('active');
			}
			
			html = '';
			year = parseInt(year/10, 10) * 10;
			var yearCont = this.picker.find('.datepicker-years')
								.find('th:eq(1)')
									.text(year + '-' + (year + 9))
									.end()
								.find('td');
			year -= 1;
			for (var i = -1; i < 11; i++) {
				html += '<span class="year'+(i === -1 || i === 10 ? ' old' : '')+(currentYear === year ? ' active' : '')+'">'+year+'</span>';
				year += 1;
			}
			yearCont.html(html);
		},
		
		click: function(e) {
			e.stopPropagation();
			e.preventDefault();
			var target = $(e.target).closest('span, td, th');
			if (target.length === 1) {
				switch(target[0].nodeName.toLowerCase()) {
					case 'th':
						switch(target[0].className) {
							case 'switch':
								this.showMode(1);
								break;
							case 'prev':
							case 'next':
								this.viewDate['set'+DPGlobal.modes[this.viewMode].navFnc].call(
									this.viewDate,
									this.viewDate['get'+DPGlobal.modes[this.viewMode].navFnc].call(this.viewDate) + 
									DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1)
								);
								this.fill();
								this.set();
								break;
						}
						break;
					case 'span':
						if (target.is('.month')) {
							var month = target.parent().find('span').index(target);
							this.viewDate.setMonth(month);
						} else {
							var year = parseInt(target.text(), 10)||0;
							this.viewDate.setFullYear(year);
						}
						if (this.viewMode !== 0) {
							this.date = new Date(this.viewDate);
							this.element.trigger({
								type: 'changeDate',
								date: this.date,
								viewMode: DPGlobal.modes[this.viewMode].clsName
							});
						}
						this.showMode(-1);
						this.fill();
						this.set();
						break;
					case 'td':
						if (target.is('.day') && !target.is('.disabled')){
							var day = parseInt(target.text(), 10)||1;
							var month = this.viewDate.getMonth();
							if (target.is('.old')) {
								month -= 1;
							} else if (target.is('.new')) {
								month += 1;
							}
							var year = this.viewDate.getFullYear();
							this.date = new Date(year, month, day,0,0,0,0);
							this.viewDate = new Date(year, month, Math.min(28, day),0,0,0,0);
							this.fill();
							this.set();
							this.element.trigger({
								type: 'changeDate',
								date: this.date,
								viewMode: DPGlobal.modes[this.viewMode].clsName
							});
						}
						break;
				}
			}
		},
		
		mousedown: function(e){
			e.stopPropagation();
			e.preventDefault();
		},
		
		showMode: function(dir) {
			if (dir) {
				this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
			}
			this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
		}
	};
	
	$.fn.datepicker = function ( option, val ) {
		return this.each(function () {
			var $this = $(this),
				data = $this.data('datepicker'),
				options = typeof option === 'object' && option;
			if (!data) {
				$this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
			}
			if (typeof option === 'string') data[option](val);
		});
	};

	$.fn.datepicker.defaults = {
		onRender: function(date) {
			return '';
		}
	};
	$.fn.datepicker.Constructor = Datepicker;
	
	var DPGlobal = {
		modes: [
			{
				clsName: 'days',
				navFnc: 'Month',
				navStep: 1
			},
			{
				clsName: 'months',
				navFnc: 'FullYear',
				navStep: 1
			},
			{
				clsName: 'years',
				navFnc: 'FullYear',
				navStep: 10
		}],
		dates:{
			days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
			daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
			daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
			months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
			monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
		},
		isLeapYear: function (year) {
			return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
		},
		getDaysInMonth: function (year, month) {
			return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
		},
		parseFormat: function(format){
			var separator = format.match(/[.\/\-\s].*?/),
				parts = format.split(/\W+/);
			if (!separator || !parts || parts.length === 0){
				throw new Error("Invalid date format.");
			}
			return {separator: separator, parts: parts};
		},
		parseDate: function(date, format) {
			var parts = date.split(format.separator),
				date = new Date(),
				val;
			date.setHours(0);
			date.setMinutes(0);
			date.setSeconds(0);
			date.setMilliseconds(0);
			if (parts.length === format.parts.length) {
				var year = date.getFullYear(), day = date.getDate(), month = date.getMonth();
				for (var i=0, cnt = format.parts.length; i < cnt; i++) {
					val = parseInt(parts[i], 10)||1;
					switch(format.parts[i]) {
						case 'dd':
						case 'd':
							day = val;
							date.setDate(val);
							break;
						case 'mm':
						case 'm':
							month = val - 1;
							date.setMonth(val - 1);
							break;
						case 'yy':
							year = 2000 + val;
							date.setFullYear(2000 + val);
							break;
						case 'yyyy':
							year = val;
							date.setFullYear(val);
							break;
					}
				}
				date = new Date(year, month, day, 0 ,0 ,0);
			}
			return date;
		},
		formatDate: function(date, format){
			var val = {
				d: date.getDate(),
				m: date.getMonth() + 1,
				yy: date.getFullYear().toString().substring(2),
				yyyy: date.getFullYear()
			};
			val.dd = (val.d < 10 ? '0' : '') + val.d;
			val.mm = (val.m < 10 ? '0' : '') + val.m;
			var date = [];
			for (var i=0, cnt = format.parts.length; i < cnt; i++) {
				date.push(val[format.parts[i]]);
			}
			return date.join(format.separator);
		},
		headTemplate: '<thead>'+
							'<tr>'+
								'<th class="prev">&lsaquo;</th>'+
								'<th colspan="5" class="switch"></th>'+
								'<th class="next">&rsaquo;</th>'+
							'</tr>'+
						'</thead>',
		contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>'
	};
	DPGlobal.template = '<div class="datepicker dropdown-menu">'+
							'<div class="datepicker-days">'+
								'<table class=" table-condensed">'+
									DPGlobal.headTemplate+
									'<tbody></tbody>'+
								'</table>'+
							'</div>'+
							'<div class="datepicker-months">'+
								'<table class="table-condensed">'+
									DPGlobal.headTemplate+
									DPGlobal.contTemplate+
								'</table>'+
							'</div>'+
							'<div class="datepicker-years">'+
								'<table class="table-condensed">'+
									DPGlobal.headTemplate+
									DPGlobal.contTemplate+
								'</table>'+
							'</div>'+
						'</div>';

}( window.jQuery );
var support = namespace("tl.support");

support.WarrantyRegistration = function(){
	var objSelf = this;

	this.validUploadFileTypes = ["text/csv", "text/plain", "application/vnd.ms-excel"];
	this.regexDate = /^((0?[13578]|10|12)(-|\/)(([1-9])|(0[1-9])|([12])([0-9]?)|(3[01]?))(-|\/)((19)([2-9])(\d{1})|(20)([012])(\d{1})|([8901])(\d{1}))|(0?[2469]|11)(-|\/)(([1-9])|(0[1-9])|([12])([0-9]?)|(3[0]?))(-|\/)((19)([2-9])(\d{1})|(20)([012])(\d{1})|([8901])(\d{1})))$/;

	this.DOMRefs = {
		warrantyRegistrationForm: $("#warrantyRegistrationForm"),

		// Modals
		modelNumberFinderModal: $("#model-number-finder-modal"),
		modelNumberFinderCallerId: $("#model-number-finder-caller-id"),
		bulkRegistrationModal: $("#bulk-registration-modal"),

		// Form
		emailType: $(".emailType"),
		companyRow: $("#companyRow"),
		companyName: $("#companyName"),
		title: $("#title"),

		country: $("#country"),
		city: $("#city"),
		stateColumn: $("#stateColumn"),
		state: $("#stateCode"),
		postalCode: $("#postalCode"),

		rowAddMoreProducts: $("#row-add-more-products"),
		divAddMoreProducts: $("#div-add-more-products"),
		addMoreProducts: $("#addMoreProducts"),
		ancillaryQuestions: $(".ancillary-questions"),
		duplicateSerialNumberMessage: $("#duplicateSerialNumberMessage"),
		hasExtendedWarranty: $(".hasExtendedWarrantyRadio"), //   Yes/No radio buttons for "Did you buy an extended warranty with this product?" question
		individualSerialNumberBoxes: $("#rowProductInfo .serialNumber"),
		rowProductInfo: $("#rowProductInfo"),
		submitButton: $("#btnSubmit"),

		modelNumberFields: $(".modelNumber"),
		purchaseDateFields: $(".purchaseDate"),
		removeItemLinks: $(".removeItem a"),
		
		userCsvFileOptions: $("#userCsvFileOptions"),
		divUploadCsvFile: $("#divUploadCsvFile"),
		uploadCsvFile: $("#uploadCsvFile"),
		userCsvFile: $("#userCsvFile"),
		userCsvFileUploadMessage: $("#userCsvFileUploadMessage")
	};

	this.DOMEls = {
		div: $(document.createElement("div")),
		h5: $(document.createElement("h5")),
		strong: $(document.createElement("strong")),
		input: $(document.createElement("input")),
		select: $(document.createElement("select")),
		ol: $(document.createElement("ol")),
		ul: $(document.createElement("ul")),
		li: $(document.createElement("li"))
	};

	this.rbStrings = {
		loadingFile: $("#rbLoadingFile").attr('data-rb-text'),
		stateListPlacHolder: $("#rbStateListPlacHolder").val(),
		postalCodePlacHolder: $("#rbPostalCodePlacHolder").val(),
		postalCodePlacHolderTenAlphaMax: $("#rbPostalCodePlacHolderTenAlphaMax").val(),
		rbDataErrorSerialNumber: $("#rbDataErrorSerialNumber").attr('data-rb-text'),
		rbRequiredFieldPurchaseDate: $("#rbRequiredFieldPurchaseDate").attr('data-rb-text'),
		rbDataErrorPurchaseDate: $("#rbDataErrorPurchaseDate").attr('data-rb-text')
	};

	this.messages = {};

	// Initial toggle of company fields
	this.toggleCompanyFields();

	// Toggle company fields based on product usage location
	this.DOMRefs.emailType.click(function(event){
		objSelf.toggleCompanyFields();
	});

	// Attach custom jQuery-Validation code (https://jqueryvalidation.org/documentation)
	this.attachCustomJQueryValidation();

	// Attach jquery plugin: date picker ( http://www.eyecon.ro/bootstrap-datepicker )
	this.attachDatePickers();

	// Handle Product to Register Rows
	this.attachProductInfoEvents();

	// Determine if we should hide the 'add more button'
	this.hideAddMoreButton();	

	// Update states & postal code placeholder based on country select list change
	objSelf.DOMRefs.country.change(function(){ 		
		objSelf.updateStateList(); // Only update for countries with states
		objSelf.updatePostalCodePlaceholder();
	});

	objSelf.DOMRefs.submitButton.on("click", function(event){
		event.preventDefault();
		var btnSubmit = $(this);
		
		// Do nothing on double clicks
		if(utilities.isDoubleClickSubmit(btnSubmit)){ return; }

		// Only submit when no duplicate serial numbers for same model number
		if(objSelf.validSerialNumbers()){
			objSelf.DOMRefs.warrantyRegistrationForm.submit();
		}
	});	
};

support.WarrantyRegistration.prototype.attachCustomJQueryValidation = function(){
	var objSelf = this;

	// EXAMPLE: FluidValidation - jQuery Validation Plugin - Custom Validation for Front End
	$.validator.addMethod(
		"SerialNumberLength",
		function(value,element){
			var isValidLength = [0, 6, 8, 10, 12, 13, 18].indexOf(value.length) >= 0;
			return isValidLength; // order is important to allow empty values as valid
		},
		objSelf.rbStrings.rbDataErrorSerialNumber
	);

	$(".serialNumber").each(function(index, el){
		var currSerialNumberBox = $(el);
		var spanError = currSerialNumberBox.siblings("span[data-valmsg-for]");
		
		currSerialNumberBox.rules("add",{SerialNumberLength: "Valid serial number lengths"});
		currSerialNumberBox.on("focusin", function(event){
			var serialNumberBox = $(this);			
			
			serialNumberBox.removeClass("input-validation-error");
			serialNumberBox.addClass("valid");
			serialNumberBox.attr("aria-invalid", false);
			
			spanError.removeClass("field-validation-error");
			spanError.addClass("field-validation-valid");
			spanError.empty();
			
			// Delete dup error span b/c of custom attaching jquery-validation & manual error html inserted via bulk upload ajax call
			if(spanError.length > 1){ spanError[1].remove(); } 
		});
	});
};

support.WarrantyRegistration.prototype.attachDatePickers = function(){
	var objSelf = this;
	var nowTemp = new Date();
	var now = new Date(nowTemp.getFullYear(), nowTemp.getMonth(), nowTemp.getDate(), 0, 0, 0, 0);
		
	// EXAMPLE: FluidValidation - jQuery Validation Plugin - Custom Validation for Front End
	$.validator.addMethod(
		"PurchaseDateRequired",
		function(value,element){ return value.length > 0; },
		objSelf.rbStrings.rbRequiredFieldPurchaseDate
	);
	$.validator.addMethod(
		"PurchaseDateInvalid",
		function(value,element){ return objSelf.regexDate.test(value); },
		objSelf.rbStrings.rbDataErrorPurchaseDate
	);
	
	$(".purchaseDate").each(function(index, el) {
		var purchaseDate = $(el).datepicker({
			onRender: function(date){ return date.valueOf() > now.valueOf() ? 'disabled' : ''; }
		})
		.on('changeDate', function(ev){ $(this).focus(); purchaseDate.hide(); })
		.on('focusout', function(ev){ 
			if(ev.relatedTarget != null){ purchaseDate.hide(); } // Hide calender when tabbing through fields
		}) 
		.data('datepicker');
		
		// FluidValidation - Attach custom validation
		$(el).rules("add",{PurchaseDateRequired: "Required Purchase Date"});
		$(el).rules("add",{PurchaseDateInvalid: "Invalid Purchase Date"});
	});

	// Disallow manual entering of dates, force datepicker calendar usage
	// objSelf.DOMRefs.rowProductInfo.on("keypress", ".purchaseDate", function(event) {
	// 	$(this).focus(); 
	// 	return false; 
	// });

	// Limit number of chars to allow for mm/dd/yyyy dates
	objSelf.DOMRefs.rowProductInfo.on("keypress", ".purchaseDate", function(event) {
		var dateValue = $(this).val(); 
		return dateValue.length < 10;
	});

	// Allow only numbers and slashes when entering dates
	objSelf.DOMRefs.rowProductInfo.on("keydown", ".purchaseDate", function(event) {
		var value = event.key;
		var regexDateChars = /^[0-9/]$/;

		switch (event.keyCode) {
            case 8:  // Backspace
            case 9:  // Tab
            case 13: // Enter
            case 16: // Shift
            case 17: // Ctrl
            case 35: // End
            case 36: // Home
            case 37: // Left
            case 38: // Up
            case 39: // Right
            case 40: // Down
            case 46: // Delete
            break;
            default:			    
				if( !regexDateChars.test(value) ){
			       event.preventDefault();
			       return false;
			    }
            break;
        }
	});
};

support.WarrantyRegistration.prototype.attachProductInfoEvents = function(){
	var objSelf = this;

	// BULK REGISTRATION: Upload CSV File
	objSelf.DOMRefs.userCsvFile.change(function(event) {
		var inpFile = $(this)[0];
		var file = inpFile.files[0];
		var errors = [];

		event.preventDefault();

		// Clear out any existing messages
		objSelf.DOMRefs.userCsvFileUploadMessage.empty();

		// No files selected
		if(inpFile.files.length == 0) { return; }

		// Enforce correct file type
		if(objSelf.validUploadFileTypes.indexOf(file.type) < 0){
			errors.push( 
				objSelf.DOMEls.li.clone()
					.append( objSelf.DOMEls.strong.clone().html("Invalid file type") )
					.append( ". Update your selection.")
			);
		}

		// Enforce file size limit
		if(file.size > 1200000){ // 1200000 1.2MB
			errors.push( 
				objSelf.DOMEls.li.clone()
					.append( "File size " + objSelf.returnFileSize(file.size) + ", " )
					.append( objSelf.DOMEls.strong.clone().html("exceeds file size limit") )
					.append( " of 1MB" )
			);
		}

		// Display any errors we may generated
		if(errors.length){
			var ul = objSelf.DOMEls.ul.clone();

			for (var i = 0; i < errors.length; i++){ ul.append(errors[i]); }

			objSelf.DOMRefs.userCsvFileUploadMessage
				.append( objSelf.DOMEls.h5.clone().html("Problems with upload file: ").append( objSelf.DOMEls.strong.html(file.name) ) )
				.append( ul );
		}
		// Process csv remotely
		else{ 
			// Create FormData object  
            var fileData = new FormData(); 
            	fileData.append(file.name, file);

            // Hide row containg csv file options & show processing message
            objSelf.DOMRefs.userCsvFileOptions.hide();
            objSelf.DOMRefs.userCsvFileUploadMessage.append('<span class="m-Left20 m-Right20">'+objSelf.rbStrings.loadingFile+'</span>').append(tl.ajxLoadingSmall);

            $.ajax({
            	url: "/api/support/processBulkWarrantyRegistration",
                type: "POST",
                contentType: false, // Not to set any content header  
                processData: false, // Not to process data  
                data: fileData,  
                success: function (response) { 
    				objSelf.DOMRefs.userCsvFileUploadMessage.empty();

                	if(response.Success){
                		objSelf.DOMRefs.rowProductInfo.empty().prepend(response.Data);
                		objSelf.DOMRefs.rowAddMoreProducts.hide(); // Hide entire section of add more options
                		objSelf.DOMRefs.bulkRegistrationModal.modal("hide");
						objSelf.attachCustomJQueryValidation(); // Attach custom jQuery-Validation code (https://jqueryvalidation.org/documentation)
						objSelf.attachDatePickers();            // Attach jquery plugin: date picker ( http://www.eyecon.ro/bootstrap-datepicker )
						objSelf.reinitModelsTypeahead();        // Attach twitter typeahead to model number input box
                	}
                	else{
                		objSelf.DOMRefs.userCsvFileOptions.show("slow");
                		objSelf.DOMRefs.userCsvFileUploadMessage
							.append( objSelf.DOMEls.h5.clone().html("Problems with upload file: ").append( objSelf.DOMEls.strong.html(file.name) ) )
							.append( 
								objSelf.DOMEls.ul.clone().append(
									objSelf.DOMEls.li.clone().append( objSelf.DOMEls.strong.clone().html(response.Message) )
								)
							);
                	}
                },
                error: function (err){ console.log(err.statusText); },
            })
            .always(function(){
            	objSelf.DOMRefs.userCsvFileOptions.show('slow');
            });
		}
	});

	// INDIVIDUAL REGISTRATION: Hide all rows except for first.
	$(".horizontal-group", objSelf.DOMRefs.rowProductInfo).each(function(index, row){
		if (index > 0){
			var currentRow = $(row);
			var currentModelNumber  = currentRow.find(".modelNumber");
			var currentPurchaseDate = currentRow.find(".purchaseDate");
			var currentSerialNumber = currentRow.find(".serialNumber");

			if (currentModelNumber.val() === "" && currentPurchaseDate.val() === ""){
				currentRow.addClass("hide");
				currentModelNumber.attr("disabled", "disabled");
				currentPurchaseDate.attr("disabled", "disabled");
				currentSerialNumber.attr("disabled", "disabled");
			} else {
				currentRow.removeClass("hide");
				currentModelNumber.removeAttr("disabled");
				currentPurchaseDate.removeAttr("disabled");
				currentSerialNumber.removeAttr("disabled");
			}
		}
	});

	// Reveal additional hidden row - INDIVIDUAL
	objSelf.DOMRefs.addMoreProducts.click(function(event){
		var hiddenRows = $(".horizontal-group.hide", objSelf.DOMRefs.rowProductInfo);
		var nextHiddenRow = $(hiddenRows[0]);
		var currentModelNumber  = nextHiddenRow.find(".modelNumber");
		var currentPurchaseDate = nextHiddenRow.find(".purchaseDate");
		var currentSerialNumber = nextHiddenRow.find(".serialNumber");

		nextHiddenRow.removeClass("hide");

		// Enable fields for revealed Row
		currentModelNumber.removeAttr("disabled");
		currentPurchaseDate.removeAttr("disabled");
		currentSerialNumber.removeAttr("disabled");

		if (hiddenRows.length === 1){
			objSelf.DOMRefs.divAddMoreProducts.addClass("hide");
		}
	});

	// Remove (Hide) row - INDIVIDUAL
	objSelf.DOMRefs.removeItemLinks.click(function(){
		var currentRow = $(this).parents(".horizontal-group");
		var currentModelNumber  = currentRow.find(".modelNumber");
		var currentPurchaseDate = currentRow.find(".purchaseDate");
		var currentSerialNumber = currentRow.find(".serialNumber");
		var currentDidYouBuy    = currentRow.find(".didYouBuy");
		
		// Only hide visible rows. Don't think we'll not be in a visible state when link is clicked, but just in case
		if(!currentRow.hasClass("hide")){			
			currentRow.addClass("hide");
			
			currentModelNumber.attr("disabled", "disabled").val("");
			currentPurchaseDate.attr("disabled", "disabled").val("");
			
			objSelf.toggleSerialNumberField(false, currentSerialNumber);            // Reset to hidden serial number area
			objSelf.toggleDidYouBuyExtendedWarranty(false, currentDidYouBuy, null); // Reset to hidden extended warr area
			
			// Enable "Add More Button"
			objSelf.DOMRefs.divAddMoreProducts.removeClass("hide");
		}

	});

	// Model Number Finder Modal - Mark which model number drop down to update
	objSelf.DOMRefs.modelNumberFinderModal.on("show.bs.modal", function (event){
		var button = $(event.relatedTarget);
		var targetId = button.parents(".horizontal-item").find("input.modelNumber.tt-input").attr("id");		
		
		// For those that don't have twitter typead attached
		if(targetId == undefined){ targetId = button.parents(".horizontal-item").find("input.modelNumber").attr("id"); }

		objSelf.DOMRefs.modelNumberFinderCallerId.val( targetId );
	});

	//  Yes/No radio buttons for "Did you buy an extended warranty with this product?" question
	objSelf.DOMRefs.rowProductInfo.on("change", ".hasExtendedWarrantyRadio", function(event) {	
		event.preventDefault();
		var elRadio = $(this);

		if (elRadio.val() == "yes"){
			elRadio.closest(".didYouBuy").find(".extendedWarranty").show();
			elRadio.closest(".didYouBuy").find(".ancillary-questions").hide();
		} 
		else {
			elRadio.closest(".didYouBuy").find(".extendedWarranty").hide();
			elRadio.closest(".didYouBuy").find(".ancillary-questions").show();
		}
	});

	// Attach handlers for toggling serial number field and extended warranty question
	objSelf.DOMRefs.rowProductInfo.on("blur", ".modelNumber", function(event) {	
		// event.preventDefault();
		var elModelNumber  = $(this);
		var elSerialNumber = elModelNumber.parents(".horizontal-group").children(".serialNumberItem").children(".serialNumber");
		var elDidYouBuy    = elModelNumber.parents(".horizontal-group").children(".didYouBuy");

		objSelf.toggleSerialAndExtendedWarranty(elModelNumber.val(), elSerialNumber, elDidYouBuy);
	});

	// On page reload, make sure serial number field & extended warranty section are showing where necessary
	for (var i = 0; i < objSelf.DOMRefs.modelNumberFields.length; i++) {
		var elModelNumber  = $(objSelf.DOMRefs.modelNumberFields[i]);
		var elSerialNumber = elModelNumber.parents(".horizontal-group").children(".serialNumberItem").children(".serialNumber");
		var elDidYouBuy    = elModelNumber.parents(".horizontal-group").children(".didYouBuy");

		objSelf.toggleSerialAndExtendedWarranty(elModelNumber.val(), elSerialNumber, elDidYouBuy);

		if (elDidYouBuy.find('.hasExtendedWarrantyRadio:checked').val() == "yes"){ 
			elDidYouBuy.find('.extendedWarranty').show();
			elDidYouBuy.find('.ancillary-questions').hide();
		}
	}
};

support.WarrantyRegistration.prototype.fillExtendedWarrantyList = function(cleanProductNumber, elDidYouBuy){
	var objSelf = this;
	var extendedWarrantyList = elDidYouBuy.find(".extendedWarranty").find("select"); // SELECT to be dynamically populated
		extendedWarrantyList.empty();

	$.ajax({
		url: "/api/products/getExtendedWarrantiesAndServices",
		type: "GET",
		data: { cleanProductNumber: cleanProductNumber },
		dataType: "json",
		contentType: "application/json",
		success: function(response){
			var extendedWarranties = response.Data.warranties;
			var extendedWarranty = "";

			for (var i = 0; i < extendedWarranties.length; i++){
				extendedWarranty = "<option>" + extendedWarranties[i].RelatedPartNumber + "</option>";
				extendedWarrantyList.append(extendedWarranty);
			}
		},
		error: function(response){
			console.log(response);
		},
		cache: false
	});
};

support.WarrantyRegistration.prototype.getHasExtendedWarrantyAndToggle = function(cleanProductNumber, elDidYouBuy){
	var objSelf = this;
	$.ajax({
		url: "/api/products/ajxHasExtendedWarranty",
		data: { cleanProductNumber: cleanProductNumber },
		success: function(hasExtWarrantyAvailable){ // Returns 'true' or 'false'
			objSelf.toggleDidYouBuyExtendedWarranty(hasExtWarrantyAvailable, elDidYouBuy, cleanProductNumber);
		},
		error: function(response){
			console.log(response);
		},
		cache: false
	});
};

support.WarrantyRegistration.prototype.getHasSerialAndToggle = function(cleanProductNumber, elSerialNumber){
	var objSelf = this;
	$.ajax({
		url: "/api/products/ajxHasSerial",
		data: { cleanProductNumber: cleanProductNumber },
		success: function(hasSerial){ // Returns 'true' or 'false'
			objSelf.toggleSerialNumberField(hasSerial, elSerialNumber);
		},
		error: function(response){
			console.log(response);
		},
		cache: false
	});
};

support.WarrantyRegistration.prototype.hideAddMoreButton = function(){
	var objSelf = this;
	var hiddenIndividualRows = $(".horizontal-group.hide", objSelf.DOMRefs.rowProductInfo);

	if (hiddenIndividualRows.length === 0){
		objSelf.DOMRefs.divAddMoreProducts.addClass("hide");
	}
};

support.WarrantyRegistration.prototype.reinitModelsTypeahead = function(){
	var objSelf = this;

	// instantiate the bloodhound suggestion engine
	var models = new Bloodhound({
		datumTokenizer: Bloodhound.tokenizers.obj.whitespace("value"),
		queryTokenizer: Bloodhound.tokenizers.whitespace,
    	limit: 20,
		remote: {
			url: "/api/products/getTypeAheadProductsMatch?q=%QUERY",
			filter: function (models) {
				return $.map(models, function (model) { return { value: model.modelNumber }; });
			}
		}
	});

	// initialize the bloodhound suggestion engine
	models.initialize();

	for (var i = 1; i < 11; i++) {
		var inputId = "#modelID" + i;
		var names = "models" + i;
		
		// Destroy typeahead for model number in order to re-create
		$(inputId).typeahead('destroy','NoCached'); 

		$(inputId)
			.bind("keypress", function(e) {
				var code = (e.keyCode ? e.keyCode : e.which);
				if (code === 13) {
					e.preventDefault();
					$(inputId).val( $(this).val() );
				}
				e.stopPropagation();
			})
			.typeahead(
				{
					hint: true,
					highlight: true,
					minLength: 2
				},
				{
					name: names,
					valueKey: "modelNumber",
					source: models.ttAdapter()
				}
			)
			.on("typeahead:selected", function (obj, datum) {
				if (inputId !== "#modelID10")
				{
					$(inputId).val( $(this).val() );
				}
			});
	}
};

support.WarrantyRegistration.prototype.returnFileSize = function(number){
	objSelf = this;

	if(number < 1024) {
		return number + 'bytes';
	} 
	else if(number >= 1024 && number < 1048576) {
		return (number/1024).toFixed(1) + 'KB';
	} 
	else if(number >= 1048576) {
		return (number/1048576).toFixed(1) + 'MB';
	}
};

support.WarrantyRegistration.prototype.toggleCompanyFields = function(){
	var objSelf = this;

	if (objSelf.DOMRefs.emailType.is(":checked")){
		var location = $(".emailType:checked");

		// Only show company fields for work selection
		if (location.val() === "w"){
			// Show company name
			objSelf.DOMRefs.companyRow.removeClass("hide");
			objSelf.DOMRefs.companyRow.fadeIn("slow");

			// Turn ON required company fields
			objSelf.DOMRefs.companyName.removeAttr("disabled");
			objSelf.DOMRefs.title.removeAttr("disabled");
		} else {
			// Hide company fields (rows) when user selects "At home"
			objSelf.DOMRefs.companyRow.fadeOut("slow");

			// Turn OFF required company fields
			objSelf.DOMRefs.companyName.attr("disabled", "disabled");
			objSelf.DOMRefs.title.attr("disabled", "disabled");
		}
	}
};

support.WarrantyRegistration.prototype.toggleDidYouBuyExtendedWarranty = function(hasExtWarrantyAvailable, elDidYouBuy, cleanProductNumber){
	var objSelf = this;
	
	if (hasExtWarrantyAvailable){
		// Display "Did you buy an extended warranty with this product?" section
		elDidYouBuy.fadeIn("slow");
		elDidYouBuy.find(".ancillary-questions").show();

		objSelf.fillExtendedWarrantyList(cleanProductNumber, elDidYouBuy);

		// Populate "Available Extended Warranties" link
		var warrantiesLink = "/products/extended-warranty-service-finder?mid=" + cleanProductNumber;
		elDidYouBuy.find(".availableExtWarr").attr("href", warrantiesLink);
	} else {
		$(".warrantyOrderNumber", elDidYouBuy).val(""); // Empty out warranty order number value
		$(":radio[value=no]", elDidYouBuy).click();     // Trigger clicking on the "No" options
		elDidYouBuy.hide();
	}
};

support.WarrantyRegistration.prototype.toggleSerialNumberField = function(hasSerial, elSerialNumber){
	if (hasSerial){
		elSerialNumber.removeAttr("disabled");
		elSerialNumber.parent().removeClass("hide").fadeIn("slow");
	}
	else {
		elSerialNumber.attr("disabled", "disabled").val("");
		elSerialNumber.parent().addClass("hide").fadeOut("slow");
	}
};

support.WarrantyRegistration.prototype.toggleSerialAndExtendedWarranty = function (productNumber, elSerialNumber, elDidYouBuy){
	var objSelf = this;
	var uppercaseProductNumber = productNumber.toUpperCase();

	tl.utilities.getCleanProductNumber(uppercaseProductNumber, function(returnValue){
		var cleanProductNumber = (returnValue == null) ? uppercaseProductNumber.replace(/[^A-Za-z0-9]/g, "") : returnValue;

		objSelf.getHasSerialAndToggle(cleanProductNumber, elSerialNumber);        // Make ajax call to determine if product has serial number
		objSelf.getHasExtendedWarrantyAndToggle(cleanProductNumber, elDidYouBuy); // Make ajax call to determine if product has extended warranty
	});
};

support.WarrantyRegistration.prototype.updatePostalCodePlaceholder = function(){
	var objSelf = this;
	var countryCode = objSelf.DOMRefs.country.val();
    var zipField = objSelf.DOMRefs.postalCode;
    var zipReqCountries = ["CA", "PR", "US"];
	var fiveDigitZipCountries = ["PR", "US"];
    var zipReqSpan = zipField.siblings(".required");
	var txtPlaceholder = (fiveDigitZipCountries.indexOf(countryCode) >= 0) ? objSelf.rbStrings.postalCodePlacHolder : objSelf.rbStrings.postalCodePlacHolderTenAlphaMax;

    objSelf.DOMRefs.postalCode.attr("placeholder", txtPlaceholder);

    // Require zip for certain countries
    if(zipReqCountries.indexOf(countryCode) >= 0){ zipReqSpan.removeClass("hide"); }
    else                                         { zipReqSpan.addClass("hide");    }
};

support.WarrantyRegistration.prototype.updateStateList = function(){
	var objSelf = this;
    var statesList = objSelf.DOMRefs.state;
	var stateReqSpan = statesList.siblings('.required');
	var countryCode = objSelf.DOMRefs.country.val();
	var sysStateCountries = ["CA", "CO", "MX", "US"];
	var useStatesList = sysStateCountries.indexOf(countryCode) >= 0;

    statesList.empty();
    statesList.prop('disabled', true);

    $("<option />", { val: "", text: objSelf.rbStrings.stateListPlacHolder }).appendTo(statesList);

    if (!countryCode) { return; }

    // Get states only for certain countries
    if (useStatesList){
	    $.ajax("/api/getStates/" + countryCode,
	        {
	            url: 'api/getStates/' + countryCode,
	            accept: 'application/json',
	            success: function (response) {
	                if (response.Success) {
	                    statesList.prop('disabled', (response.Data && response.Data.length < 1));

	                    $(response.Data).each(function () {
	                        $("<option />", { val: this.Key, text: this.Value }).appendTo(statesList);
	                    });
	                }
	            }
	        }
	    );
	    
	    stateReqSpan.removeClass("hide");
	}
	else{
	    stateReqSpan.addClass("hide");
	}
};

support.WarrantyRegistration.prototype.validSerialNumbers = function(){
	var objSelf = this;
	var serialNumbersOk = true;

	// Remove any current error highlighting
	$(".horizontal-item.error").removeClass("error");

	objSelf.DOMRefs.individualSerialNumberBoxes.each(function(index, serialNumberBox){
		var currSerialNumberBox = $(serialNumberBox);

		for (var i = index+1; i < objSelf.DOMRefs.individualSerialNumberBoxes.length; i++){
			var nextSerialNumberBox = $(objSelf.DOMRefs.individualSerialNumberBoxes[i]);

			// Find matching serial number values
			if (currSerialNumberBox.val() !== "" && currSerialNumberBox.val() === nextSerialNumberBox.val()){
				var currSerialNumberBoxDiv = currSerialNumberBox.parent();
				var nextSerialNumberBoxDiv = nextSerialNumberBox.parent();
				var currSerialNumberBoxModelNumber = currSerialNumberBox.parents(".horizontal-group").find(".modelNumber");
				var nextSerialNumberBoxModelNumber = nextSerialNumberBox.parents(".horizontal-group").find(".modelNumber");

				// Find matching Model Numbers
				if (currSerialNumberBoxModelNumber.val() === nextSerialNumberBoxModelNumber.val()){
					// Duplicate found
					serialNumbersOk = false;

					// Highlight both serial number boxes that match
					currSerialNumberBoxDiv.addClass("error");
					nextSerialNumberBoxDiv.addClass("error");

					// Tell user that
					alert( objSelf.DOMRefs.duplicateSerialNumberMessage.data("rb-text") );

					break; // Look no further in loop
				}
			}
		}
	});

	return serialNumbersOk;
};

$(document).ready(function (){
	registrationForm = new support.WarrantyRegistration();	
});
