PageSourceSearch

https://westminster-ma.gov/-1466400618.js

js westminster-ma.gov collected 2026-10-03 17:44:26 UTC 192,067 bytes, 6,312 lines download raw bytes

1/*
2The following errors were found when attempting to minify this file:
3- Line 37: Parse error. missing : after property id
4- Line 37: Parse error. illegal character
5- Line 37: Parse error. missing ; before statement
6- Line 37: Parse error. illegal character
7- Line 37: Parse error. syntax error
8- Line 38: Parse error. syntax error
9- Line 39: Parse error. syntax error
10- Line 40: Parse error. syntax error
11- Line 42: Parse error. syntax error
12- Line 43: Parse error. illegal character
13- Line 43: Parse error. missing ) after argument list
14- Line 43: Parse error. illegal character
15- Line 43: Parse error. syntax error
16- Line 44: Parse error. syntax error
17- Line 45: Parse error. syntax error
18- Line 46: Parse error. syntax error
19- Line 47: Parse error. syntax error
20- Line 49: Parse error. syntax error
21- Line 51: Parse error. missing ; before statement
22- Line 59: Parse error. syntax error
23- Line 61: Parse error. syntax error
24- Line 66: Parse error. invalid return
25- Line 67: Parse error. syntax error
26- Line 69: Parse error. missing ; before statement
27- Line 73: Parse error. syntax error
28- Line 74: Parse error. syntax error
29- Line 75: Parse error. syntax error
30- Line 77: Parse error. syntax error
31- Line 79: Parse error. missing ; before statement
32- Line 85: Parse error. illegal character
33- Line 85: Parse error. missing ; before statement
34- Line 85: Parse error. illegal character
35- Line 86: Parse error. syntax error
36- Line 87: Parse error. syntax error
37- Line 90: Parse error. syntax error
38- Line 93: Parse error. illegal character
39- Line 93: Parse error. missing ; before statement
40- Line 93: Parse error. illegal character
41- Line 97: Parse error. invalid return
42- Line 98: Parse error. syntax error
43- Line 100: Parse error. missing ; before statement
44- Line 101: Parse error. syntax error
45- Line 102: Parse error. syntax error
46- Line 103: Parse error. syntax error
47- Line 108: Parse error. invalid return
48- Line 113: Parse error. missing ; after for-loop initializer
49- Line 113: Parse error. missing ; before statement
50- Line 120: Parse error. syntax error
51- Line 124: Parse error. syntax error
52- Line 127: Parse error. syntax error
53- Line 129: Parse error. syntax error
54- Line 131: Parse error. syntax error
55- Line 132: Parse error. syntax error
56- Line 133: Parse error. syntax error
57- Line 398: Parse error. missing ; before statement
58*/
59// Gets Json object using a given url.
60function GetJson(url) {
61	var result;
62	$.ajaxSetup({ async: false });
63	$.post(url, function (data) {
64		result = data;
65	});
66	$.ajaxSetup({ async: true });
67	return result;
68}
69
70function maxLength(field, maxChars) {
71	if (field.value.length >= maxChars) {
72		event.returnValue = false;
73		return false;
74	}
75}
76
77function maxLengthPaste(field, maxChars) {
78	var copyText = field.value + window.clipboardData.getData("Text");
79	field.value = copyText.substr(0, maxChars);
80	if ((copyText.length) > maxChars) {
81		return false;
82	}
83	event.returnValue = true;
84}
85
86function appendFilesToInput(appendInput, mainFileInput) {
87	const valid = validateMultiFileUpload(appendInput, true);
88	if (!valid) {
89		alert("The file you are trying to upload is not an allowed file type or has an invalid file name. These are the accepted characters: A-z 0-9 ~ ! ( ) - + = [ ] { } , . _");
90		appendInput.value = null;
91		return;
92	}
93	const dt = new DataTransfer();
94	const originalFiles = Array.from(mainFileInput.files);
95	const existingSet = new Set(originalFiles.map(({ name, size, lastModified }) => `${name}-${size}-${lastModified}`));
96	originalFiles.forEach(file => {
97		dt.items.add(file);
98	})
99	const addedFiles = Array.from(appendInput.files);
100	addedFiles.forEach(file => {
101		if (!existingSet.has(`${file.name}-${file.size}-${file.lastModified}`)) {
102			dt.items.add(file);
103		}
104	})
105	mainFileInput.files = dt.files;
106	mainFileInput.dispatchEvent(new Event("change"));
107}
108
109function fileAppender(mainFileInput) {
110	const li = document.createElement("li");
111	const label = document.createElement("label");
112	label.innerHTML = '<span class="visuallyhidden">Add Files</span><svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" aria-hidden="true"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"/></svg>'
113	const appendInput = document.createElement("input");
114	appendInput.type = "file";
115	appendInput.multiple = true;
116	appendInput.className = "visuallyhidden";
117	appendInput.addEventListener("change", e => {
118		appendFilesToInput(appendInput, mainFileInput);
119	});
120	label.appendChild(appendInput);
121	const placeholder = document.createElement("span");
122	li.appendChild(placeholder);
123	li.appendChild(label);
124	return li;
125}
126
127function removeFileFromInput(input, idx) {
128	const dt = new DataTransfer();
129	const files = Array.from(input.files);
130	files.splice(idx, 1);
131	files.forEach(file => {
132		dt.items.add(file);
133	});
134	input.files = dt.files;
135}
136
137function listItemFile(input, file, idx) {
138	const li = document.createElement("li");
139	const fileNameSpan = document.createElement("span");
140	fileNameSpan.textContent = file.name;
141	li.appendChild(fileNameSpan);
142	const removeButton = document.createElement("button");
143	removeButton.title = `Remove ${file.name}`;
144	removeButton.addEventListener("click", e => {
145		e.preventDefault();
146		removeFileFromInput(input, idx);
147		input.dispatchEvent(new Event("change"));
148	});
149	removeButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="darkred" aria-hidden="true"><path d="m336-280 144-144 144 144 56-56-144-144 144-144-56-56-144 144-144-144-56 56 144 144-144 144 56 56ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"></path></svg>';
150	const removeButtonSpan = document.createElement("span");
151	removeButtonSpan.textContent = `Remove ${file.name}`;
152	removeButtonSpan.className = "visuallyhidden";
153	removeButton.appendChild(removeButtonSpan);
154	li.appendChild(removeButton);
155	return li;
156}
157
158function listFileInputFiles(fileInput) {
159	if (fileInput.nextElementSibling?.className === "file-list") {
160		fileInput.nextElementSibling.remove();
161	}
162	const files = fileInput.files || [];
163	const valid = files.length && validateMultiFileUpload(fileInput, true)
164	if (!valid) {
165		fileInput.value = null;
166		return;
167	}
168	if (files.length > 1) {
169		const ul = document.createElement("ul");
170		ul.className = "file-list";
171		for (let i = 0; i < files.length; i++) {			
172			const li = listItemFile(fileInput, files[i], i);
173			ul.appendChild(li);
174		}
175		const fileAppenderLi = fileAppender(fileInput);
176		ul.appendChild(fileAppenderLi);
177		fileInput.after(ul);
178	}
179}
180
181function initFileInputs() {
182	if (!((window?.FeatureToggles?.isActive?.("CMS.FormCenter.MultipleFileUploadEnhancement")) || (window?.FeatureToggles?.features?.["CMS.FormCenter.MultipleFileUploadEnhancement"]))) {
183		return;
184	}
185	document.querySelectorAll(".FileUpload input[type='file']").forEach(fileInput => {
186		listFileInputFiles(fileInput);
187		fileInput.addEventListener("change", e => {
188			listFileInputFiles(fileInput)
189		})
190	});
191}
192;
193/// <reference path="../../../../Scripts/json2.js">
194/// <reference path="../../../../Scripts/HtmlExtensionSupport.js">
195/// <reference path="../../../../Scripts/UrlLibrary.js">
196/// <reference path="../../../../Common/GlobalJSFunctionsDetail.js" />
197/// <reference path="Conditions.js" />
198/// <reference path="Validation.js" />
199/// <reference path="/Assets/Scripts/DynamicForm/PrintSubmission.js" />
200/// <reference path="../../../../Assets/Scripts/DateTimePicker/cp.datetimepicker.js" />
201
202//////// ENUMERATIONS ///////////
203var FormCenterHomeJS = (function ($) {
204	var self = this;
205
206	// This value is set on the ItemDetailFE.ascx page, using a value from the
207	// View Model.
208	self.submissionURL = '';
209
210	return self;
211}(jQuery));
212
213var FormCenterHomeScriptResources = GetJson("/FormCenter/Localization");
214
215// Field formats.
216var FieldValidation = {
217	Any: 0,
218	Letters: 1,
219	Numeric: 2,
220	PhoneNumber: 3,
221	PostalCode: 4,
222	EmailAddress: 5,
223	RegEx: 6,
224	Currency: 7,
225	Date: 8,
226	Time: 9,
227	DateTime: 10,
228	DateSpan: 11,
229	TimeSpan: 12,
230	DateTimeSpan: 13
231};
232
233// Supported field types.
234var FieldTypes = {
235	ShortAnswer: 1,
236	LongAnswer: 2,
237	Checkboxes: 3,
238	RadioButtons: 4,
239	Dropdown: 5,
240	FileUpload: 6,
241	ReplyEmail: 10,
242	Password: 11,
243	DateTime: 18,
244	EPayment: 23
245};
246
247//////// GLOBALS ////////////////
248
249// Stores instantiated form validator/condition instances for the current form.
250var formValidators = null;
251var formConditions = null;
252
253// Use with form submissions.
254var redirectNewWindow = null;
255
256//////// SUPPORT METHODS ////////f
257
258// Shows extra information for a form detail.
259function moreInfo(id) {
260	changeInfo(id, 'addClass', 'removeClass');
261}
262
263// Hides extra information for a form detail.
264function lessInfo(id) {
265	changeInfo(id, 'removeClass', 'addClass');
266}
267
268// Internal. Hides or shows extra information.
269function changeInfo(id, lessFn, moreFn) {
270	$([document.getElementById('spnLess' + id), document.getElementById('lnkMore' + id)])[lessFn]('hidden');
271	$(document.getElementById('spnMore' + id))[moreFn]('hidden');
272}
273
274// Assigns bulk action checkbox group to jQuery expression.
275function assignBulkCheckGroup($children, $parent) {
276	// Isolate children from parent in event of overlap ($children containing the $parent).
277	// Vastly simplifies the logic in the event handlers by doing this.
278	var $isolatedChildren = $children.filter(
279		function (index) {
280			return $parent.filter(this).length == 0;
281		}
282	);
283
284	// Cache current number of checked children.
285	$isolatedChildren.lengthChecked = $isolatedChildren.filter(':checked').length;
286
287	// Hook childen change.
288	$isolatedChildren.change(function (event) {
289		$isolatedChildren.lengthChecked += (this.checked ? 1 : -1);
290
291		var allChildrenChecked = ($isolatedChildren.lengthChecked == $isolatedChildren.length);
292
293		$parent.each(function () {
294			this.checked = allChildrenChecked;
295		});
296	});
297
298	// Hook parent change.
299	$parent.change(function (event) {
300		var parentChecked = this.checked;
301
302		$isolatedChildren.lengthChecked = this.checked ? $isolatedChildren.length : 0;
303
304		$isolatedChildren.each(function () {
305			this.checked = parentChecked;
306		});
307	});
308}
309
310function initCommon() {
311	// Hack: Internet Explorer 7 mode bizarre display bugs.
312	$('.sidebar .search.noStyles').css('zoom', '1');
313
314	// Hook search category bulk checks.
315	var $bulkCheck = $('#categoryFilter_0');
316	var $bulkGroup = $('.categoryList input[type="checkbox"]');
317
318	assignBulkCheckGroup($bulkGroup, $bulkCheck);
319
320	// Get input jQuery refs.
321	var $inputFCTerm = $('#inputFCTerm');
322	var $inputFCSearch = $('#inputFCSearch');
323
324	// Hook search button and textbox to capture enter key.
325	$inputFCTerm.keydown(function (event) {
326		// If user presses enter, submit for search.
327		switch (event.which) {
328			case 10: // LF
329			case 13: // CR
330				event.preventDefault();
331				$inputFCSearch.click();
332				break;
333		}
334	});
335
336	$inputFCSearch.click(function (event) {
337		event.preventDefault();
338
339		// Build search querystring.
340		var qs = {};
341		var formID = extractFormIDFromURL(location.href);
342		var categoryID = extractCategoryIDFromURL(location.href);
343
344		var searchTerm = $.trim(isNull($inputFCTerm.val(), ''));
345
346		if (formID)
347			qs.formID = formID;
348
349		if (categoryID)
350			qs.categoryID = categoryID;
351
352		if (searchTerm != '')
353			qs.term = searchTerm;
354
355		if (!$bulkCheck[0].checked) {
356			var values = [];
357
358			$bulkGroup.filter(':checked').each(function () {
359				values.push(this.value);
360			});
361
362			if (values.length > 0)
363				qs.categoryFilter = values.join(',');
364		}
365
366		// Set search URL and go.
367		window.location = '/FormCenter/Search' + (new QueryStringBuilder(qs)).toString();
368	});
369
370	// Hook search category dropdown.
371	var $catList = $('#categoryList');
372	var $catListToggle = $('a.mega');
373
374	$catListToggle.click(function (e) {
375		e.preventDefault();
376
377		if ($catList.hasClass('open'))
378			$catList.slideUp(300);
379		else
380			$catList.slideDown(300);
381
382		$catListToggle.toggleClass('active');
383		$catList.toggleClass('open');
384	});
385
386	// Show print dialog if URL contains standard print query string.
387	if (containsPrint(location.href))
388		window.print();
389}
390
391// Initialization for search (Search.aspx view).
392function initSearch() {
393	if (this.wasInit)
394		return;
395
396	this.wasInit = true;
397
398	attachCategoryVisibilityTogglers();
399
400	initCommon();
401}
402
403// Initialization for category/form list (Index.aspx view).
404function initCategoryList() {
405	if (this.wasInit)
406		return;
407
408	this.wasInit = true;
409
410	attachCategoryVisibilityTogglers();
411
412	initCommon();
413}
414
415// Initialization for confirmation (Confirmation.aspx view).
416function initConfirmation() {
417	if (this.wasInit)
418		return;
419
420	this.wasInit = true;
421
422	initCommon();
423}
424
425function initPostSubmissionSpam() {
426	initCommon();
427}
428
429
430// Causes current form to be submitted for printing.
431function printForm(saveData) {
432	if (formValidate()) { //check for valid responses before creating print window
433		var frm = document.aspnetForm;
434		var $form = $(document.aspnetForm);
435		var printPrevType = (getPrintPreviewType() == 1 ? 'Print' : 'Preview');
436		var formID = extractFormIDFromURL(location.href);
437
438		$form.attr('target', 'PrintWindow');
439		// Change target/action to print data.
440		frm.action = '/FormCenter/Print?formID=' + formID + '&' + printPrevType + '=YES&Save=' + (saveData ? 'True' : 'False');
441
442		var savedProgressID = parseInt($('#hdnSavedProgressID').val(), 10);
443
444		if (!isNaN(savedProgressID)) {
445			frm.action = frm.action + '&savedProgressID=' + savedProgressID;
446		}
447
448		submitForm($form, true, saveData, formID);
449	} else {
450		enableDisableSubmit(true);
451	}
452}
453
454function submitFormHandler(e) {
455	try {
456		let json = JSON.parse(this.response);
457
458		if (this.status !== 200 || json.Success !== true) {
459			$('.submissionConfirmationMessage').html("Something went wrong with the submission. Please try again.");
460			$('.submissionConfirmation').show();
461			enableDisableSubmit(true);
462			return;
463		}
464
465		const responseUrl = this.responseURL && new URL(this.responseURL);
466		const saveSubmission = responseUrl && responseUrl.searchParams.get('save') && responseUrl.searchParams.get('save').toLowerCase() === 'true';
467		const doPrint = responseUrl && responseUrl.searchParams.get('print') && responseUrl.searchParams.get('print').toLowerCase() === 'true';
468		const redirectAfterSubmissionSaved = json.Redirect && json.Redirect === true && saveSubmission === true;
469
470		if (redirectNewWindow) {
471			// There are two possible redirects in use:
472			// (1) The regular "submit" redirect for the form vs. category confirmation page.
473			// (2) The "conditional" redirect that is applied when a form condition is met.
474			// redirectNewWindow is the latter; redirectAfterSubmissionSaved below deals with the former.
475			window.open(redirectNewWindow);
476			redirectNewWindow = '';
477		}
478
479		if (redirectAfterSubmissionSaved)
480		{
481			if (json.IsHttps === false) {
482				json.Message = json.Message.replace("https://", "http://");
483			}
484
485			location.href = json.Message;
486		}
487		else {
488			if (saveSubmission === true) {
489				$('.submissionConfirmationMessage').html(json.Message);
490				typeof displayStep === "function" && displayStep(1);
491				$('.submitForm').hide();
492				$('.submissionConfirmation').show();
493			}
494
495			enableDisableSubmit(true);
496		}
497
498		if (doPrint === true) {
499			window.open("/FormCenter/Print?formId=" + json.FormId.toString() + '&save=' + (saveSubmission ? 'True' : 'False'));
500		}
501
502		if (json.Success === true) {
503			document.aspnetForm.reset();
504
505			if (typeof (grecaptcha) !== "undefined")
506			{
507				grecaptcha.reset();
508			}
509		}
510
511		!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
512	} catch (ex) {
513		console.log(ex);
514	}
515}
516
517function submitForm($form, print, saveSubmission, formID) {
518	var submittedViaWidget = $form.parents(".widgetBody").length > 0;
519
520	serializeSpecialFields();
521
522	if (submittedViaWidget !== true) {
523		var printWindow;
524
525		if (print === true)
526		{
527			printWindow = window.open("", "PrintWindow");
528
529			// Deferred object at the window level, this becomes available on the opened window via window.opener
530			// Whichever view gets rendered will resolve it and pass the appropriate status code.
531			window.deferredStatus = $.Deferred();
532		}
533
534		if (printWindow) {
535			window.deferredStatus.done(function (status) {
536				if (status === CP_DynamicForm_PrintSubmission.StatusCodes.Success) {
537					if (saveSubmission && saveSubmission === true) {
538						window.location = '/FormCenter/Confirmation?formID=' + formID;
539					}
540				} else {
541					printWindow.close();
542
543					if (saveSubmission) {
544						window.location = '/FormCenter/ErrorSubmittingForm?formID=' + formID;
545					}
546				}
547			});
548		}
549
550		$form.submit();
551	}
552	else {
553		var submissionUrl = FormCenterHomeJS.submissionURL + (saveSubmission ? '&save=True' : "");
554		submissionUrl = submissionUrl + (print ? '&print=True' : '');
555
556		var formData = new FormData($form[0]);
557		var xhr = new XMLHttpRequest();
558		xhr.onload = submitFormHandler;
559		xhr.onloadend = ajaxPostBackEnd;
560		xhr.onerror = function() { console.log("A network error occurred with the transaction."); };
561		xhr.open("POST", submissionUrl);
562		xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
563		ajaxPostBackStart('Loading');
564		xhr.send(formData);
565	}
566}
567
568function serializeSpecialFields()
569{
570	submitCheckboxHandler();
571
572	submitRadioHandler();
573
574	submitDateTimeHandler();
575}
576
577// Initialization for form item printing (Print.aspx view).
578function initFormPrint() {
579	if (this.wasInit)
580		return;
581
582	this.wasInit = true;
583
584	initCommon();
585
586	initValidationAndConditions();
587
588	registerFormPrintEvents();
589}
590
591function initValidationAndConditions() {
592	// Register validation.
593	if (typeof getFormValidatorData !== 'undefined') {
594		formValidators = FieldValidator.createMulti(getFormValidatorData());
595	} else {
596		formValidators = [];
597	}
598
599	// Register conditions.
600	if (getFormConditionData) {
601		formConditions = FieldConditions.createMulti(getFormConditionData(), formValidators);
602	} else {
603		formConditions = FieldConditions.createMulti([], formValidators);
604	}
605
606	// Register condition runner and make initial condition check.
607	formConditionsRunner = new FieldConditionsRunner(formConditions, $.noop, true, false); // TODO: Finish up, put debugCondRunnerLogResults for $.noop.
608	formConditionsRunner.run();
609}
610
611// Initialization for form item (Item.aspx view).
612function initForm() {
613
614	if (this.wasInit)
615		return;
616
617	this.wasInit = true;
618
619	initCommon();
620	initValidationAndConditions();
621	initDatePickers();
622	initTimePickers();
623	initFileInputs();
624	registerFormEvents(); // Hook up buttons (submit/cancel/etc).
625}
626
627function initDatePickers() {
628	if ($('.telerikDatePicker').length > 0) {
629		$('.telerikDatePicker').each(function () {
630          if (typeof (cp) !== 'undefined') {
631				var datePickerSelector = this.id ? '#' + this.id : this;
632				cp.datetimepicker && cp.datetimepicker.createDatePicker(datePickerSelector);
633			}
634		});
635	}
636}
637
638function initTimePickers() {
639	if ($('.formCenterTimePicker').length > 0) {
640		$('.formCenterTimePicker').focusout(function () {
641			var text = $(this).val().toLowerCase();
642			if (text != '' && (text.indexOf('am') < 0 && text.indexOf('pm') < 0)) {
643				text = $(this).val().trim() + ' AM';
644				$(this).val(text);
645			}
646		});
647	}
648	else {
649		$('.telerikTimePicker').each(function () {
650			if (typeof (cp) !== 'undefined')
651				cp.datetimepicker && cp.datetimepicker.createTimePicker(this,
652					{
653						format: 'h:i A'
654					});
655		});
656	}
657}
658
659// Registers event handlers for form.
660function registerFormPrintEvents() {
661	$('#btnFormContinue').click(function (event) {
662		event.preventDefault();
663
664		var frm = document.aspnetForm;
665		var formID = extractFormIDFromURL(location.href);
666		var $form = $(document.aspnetForm);
667
668		// Change target/action to confirmation page.
669		frm.action = '/FormCenter/Confirmation?formID=' + formID;
670
671		$form.trigger("submit");
672	});
673}
674
675// Registers event handlers for form.
676function registerFormEvents() {
677	// Remove troublemakers (prevent the jQuery submit handler from ever firing, and not needed for this screen anyways).
678	document.aspnetForm.removeAttribute('onsubmit');
679	document.aspnetForm.onsubmit = null;
680
681	var $form = $(document.aspnetForm);
682
683	// TODO: Plug this into other validation.
684	var wantCopyAddr, $wantCopyAddrLI;
685	var wantCopy = document.getElementById('wantCopy');
686
687	if (wantCopy) {
688		wantCopyAddr = document.getElementById('wantCopyAddress');
689		$wantCopyAddrLI = $(wantCopyAddr.parentNode.parentNode);
690
691		$(wantCopy).change(function (event) {
692			if (this.checked)
693				$wantCopyAddrLI.show();
694			else
695				$wantCopyAddrLI.hide();
696		});
697	}
698
699	/* #2378*/
700	$(document).keydown(function (event) {
701		if (event.keyCode === 13 &&
702			event.target.tagName === 'INPUT' &&
703			event.target.id !== 'btnFormSubmit' &&
704			event.target.tagName !== 'TEXTAREA' &&
705			event.target.tagName !== 'SELECT' &&
706			event.target.getAttribute('data-enable-enter-keypress') !== 'true') {
707			event.preventDefault();
708			return false;
709		}
710		if (event.keyCode === 32 &&
711			event.target.tagName === 'A' &&
712			event.target.getAttribute('role') === 'button') {
713			$(event.target).trigger('click');
714			event.preventDefault();
715			return false;
716		}
717	});
718
719	$form.submit(function () {
720		// Run validation, stop data submission if necessary.
721		if (!formValidate()) {
722			enableDisableSubmit(true);
723			return false;
724		}
725
726		if (isNull(redirectNewWindow, '').trimEnd() != '')
727			window.open(redirectNewWindow);
728
729		// Field types requiring special serialization code (radio/check/file).
730		// The deserialization of dictionaries from forms in MVC 2 is somewhat sensitive.
731		serializeSpecialFields();
732		return true;
733	});
734
735	$('#btnFormSubmit').click(function (event) {
736		event.preventDefault();
737		if (formValidate()) {
738			processSubmit(handleSubmitClick);
739		}
740	});
741
742	$('#btnFormPrint').click(function (event) {
743		event.preventDefault();
744		printForm(false);
745	});
746
747	$('#btnFormSubmitPrint').click(function (event) {
748		event.preventDefault();
749		if (formValidate()) {
750			processSubmit(handleSubmitPrintClick);
751		}
752	});
753
754	$('#btnCalculateTotals').click(function (e) {
755		e.preventDefault();
756
757		submitCheckboxHandler();
758		submitRadioHandler();
759
760		if (formValidate()) {
761			var $form = $(document.aspnetForm);
762
763			$.ajax({
764				url: '/FormCenter/Home/CalculateTotal?formID=' + extractFormIDFromURL(location.href),
765				type: 'POST',
766				data: $(document.aspnetForm).serializeArray(),
767				success: function (response) {
768					openCpModal({
769						title: FormCenterHomeScriptResources.CalculateTotals,
770						className: 'modalCalculateTotal',
771						isFrontEnd: window.location.href.indexOf('/Admin/FormCenter') == -1,
772						htmlContent: response
773					});
774				},
775				error: function (xhr, textStatus, exception) {
776					alert('Error: ' + xhr.statusText + '\nStatus: ' + xhr.status);
777				}
778			});
779		}
780	});
781
782	$('#btnProceedToCheckOut').click(function (e) {
783		proceedToCheckOut(e);
784	});
785
786	if ($('.submissionConfirmationOk').length > 0)
787	{
788		$('.submissionConfirmationOk').unbind('click').click(function () {
789			$('.submitForm').show();
790			$('.submissionConfirmation').hide();
791			!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
792		})
793	}
794}
795
796function onRecaptchaLoadCallback() {
797	window.gRecaptchaClientId = grecaptcha.render('inline-recaptcha', {
798		'sitekey': document.querySelector('#inline-recaptcha').dataset.sitekey,
799		'badge': 'inline',
800		'size': 'invisible'
801	});
802}
803
804function setRecaptchaResponse(token) {
805	if (!token) {
806		return;
807	}
808
809	var responseFields = document.querySelectorAll('[name="g-recaptcha-response"]');
810	for (var i = 0; i < responseFields.length; i++) {
811		responseFields[i].value = token;
812	}
813}
814
815function processSubmit(callback) {
816	const recaptcha = document.querySelector('.recaptcha');
817	if (!recaptcha) {
818		callback();
819		return;
820	}
821
822	grecaptcha.ready(function() {
823		grecaptcha.execute(window.gRecaptchaClientId, { action: 'submit' }).then(function(token) {
824			setRecaptchaResponse(token);
825			callback();
826		});
827	});
828}
829
830function enableDisableSubmit(enable) {
831	if (enable) {
832		if ($('#btnFormSubmitPrint').length > 0) {
833			$('#btnFormSubmitPrint').removeClass('inactive');
834			$('#btnFormSubmitPrint').click(function (event) {
835				handleSubmitPrintClick();
836			});
837		}
838		if ($('#btnFormSubmit').length > 0) {
839			$('#btnFormSubmit').removeClass('inactive');
840			$('#btnFormSubmit').click(function (event) {
841				event.preventDefault();
842				if (formValidate()) {
843					closeModalDialog('editItemBehavior');
844					processSubmit(handleSubmitClick);
845				}
846			});
847		}
848	} else {
849		if ($('#btnFormSubmitPrint').length > 0) {
850			$('#btnFormSubmitPrint').addClass("inactive");
851			$('#btnFormSubmitPrint').unbind();
852		}
853		if ($('#btnFormSubmit').length > 0) {
854			$('#btnFormSubmit').addClass("inactive");
855			$('#btnFormSubmit').unbind();
856		}
857	}
858}
859
860function handleSubmitClick() {
861	enableDisableSubmit(false);
862
863	var frm = document.aspnetForm;
864
865	frm.action = FormCenterHomeJS.submissionURL;
866
867	var $form = $(frm);
868
869	var formID = extractFormIDFromURL(location.href);
870
871	submitForm($form, false, true, formID);
872}
873
874function handleSubmitPrintClick() {
875	enableDisableSubmit(false);
876	printForm(true);
877}
878
879function proceedToCheckOut(e) {
880	e.preventDefault();
881
882	serializeSpecialFields();
883
884	if (formValidate()) {
885		var savedProgressID = parseInt($('#hdnSavedProgressID').val(), 10);
886
887		var submitUrl = '/FormCenter/Home/ProceedToCheckOut?formID=' + extractFormIDFromURL(location.href);
888
889		if (!isNaN(savedProgressID)) {
890			submitUrl = submitUrl + '&savedProgressID=' + savedProgressID;
891		}
892
893		var $form = $(document.aspnetForm);
894		$form.attr('action', submitUrl);
895		$form.submit();
896	}
897}
898
899function getModalDialogObjects() {
900	return {
901		iframe: document.getElementById('liveEditDialog'),
902		windowElement: $('.modalContainerCP')[0],
903		behavior: $find('editItemBehavior'),
904		titleElement: $('.modalTitle')[0]
905	};
906}
907
908function getBooleanValue(value) {
909	switch (value.toLowerCase()) {
910		case "true": case "yes": case "1": return true;
911		case "false": case "no": case "0": case null: return false;
912		default: return Boolean(string);
913	}
914}
915
916// Validates current form.
917function formValidate() {
918	var assignFocus = true;
919	var ERROR_MSG = FormCenterHomeScriptResources.InvalidEmailAddressFormat2;
920	var wantCopy = document.getElementById('wantCopy');
921
922	if (wantCopy) {
923		wantCopyAddr = document.getElementById('wantCopyAddress');
924		$wantCopyAddrLI = $(wantCopyAddr.parentNode.parentNode);
925
926		if ($wantCopyAddrLI.is(":visible") && wantCopyAddr.value != "") {
927			if (!emailValidate(wantCopyAddr.value)) {
928				errorShow(ERROR_MSG);
929				return false;
930			}
931		}
932		if (wantCopy.checked == false)//if the checkbox is not checked then empty the emailaddress textbox
933			document.getElementById('wantCopyAddress').value = "";
934	}
935	else {
936		//if the checkbox is not checked then empty the emailaddress textbox
937		var wantCopyAddress = document.getElementById('wantCopyAddress');
938		if (wantCopyAddress)
939			document.getElementById('wantCopyAddress').value = "";
940	}
941
942	if (typeof formValidators.keys !== 'undefined') {
943		var liCurrent = $('li.current');
944		var step = "1";
945		if (liCurrent.length > 0) {
946			step = liCurrent.attr('id').replace('liStep', '');
947		}
948
949		var wizardStep = $('#wizard' + step);
950
951		if (wizardStep.length > 0) {
952			wizardStep = wizardStep[0];
953		}
954
955		var checkForInappropriateWords = new Array();
956
957		for (var i = 0, len = formValidators.keys.length; i < len; i++) {
958			var key = formValidators.keys[i];
959
960			if (wizardStep == null || wizardStep.length == 0 || (wizardStep != null && wizardStep.contains(formValidators[key].elem
960Container))) {
961				if (!formValidators[key].validate(assignFocus, checkForInappropriateWords))
962					assignFocus = false;
963			}
964		}
965
966		if (validateInappropriateWords(checkForInappropriateWords)) {
967			assignFocus = false;
968		}
969	}
970
971	return assignFocus;
972}
973
974function validateInappropriateWords(checkForInappropriateWords) {
975	var data = {};
976	var j = 0;
977	var inappropriateWordsFound = false;
978
979	for (j = 0; j < checkForInappropriateWords.length; j++) {
980		data[checkForInappropriateWords[j].id.toString()] = checkForInappropriateWords[j].elemInput.value.trim();
981	}
982
983	var results = HasInappropriateWordsMultiple(data);
984
985	for (j = 0; j < checkForInappropriateWords.length; j++) {
986		if (results[checkForInappropriateWords[j].id.toString()] == true) {
987			checkForInappropriateWords[j].handleInappropriateWords(inappropriateWordsFound == false);
988
989			inappropriateWordsFound = true;
990		}
991	}
992
993	return inappropriateWordsFound;
994}
995
996// Displays error message if email validation failed
997function errorShow(message) {
998	var elemContainer = $('.anonEmail');
999	elemContainer.addClass('error');
1000
1001	if (elemContainer.find('.explanation').length == 0) {
1002		var errorElemMsg = document.createElement('p');
1003		errorElemMsg.className = 'explanation';
1004		errorElemMsg.innerHTML = message;
1005		elemContainer.append(errorElemMsg);
1006	}
1007}
1008
1009// Determines if URL contains print querystring.
1010function containsPrint(url) {
1011	return url.search(/(&|\?)print\=yes(&.*|)$/i) > -1;
1012}
1013
1014
1015// Extracts CategoryID identifier from URL specified.
1016function extractCategoryIDFromURL(url) {
1017
1018	url = url.toLowerCase();
1019	var indexOfForm = url.lastIndexOf("/formcenter/");
1020	if (indexOfForm > -1) {
1021		//extract the categoryurl
1022		var formURL = url.substr(indexOfForm + 12);
1023
1024
1025		//check whether u have formid appended
1026
1027		if (formURL.indexOf("/") > -1)
1028			formURL = formURL.substr(0, formURL.indexOf("/"));
1029		//check wheter the url has categoryName
1030		var indexOfHypen = formURL.lastIndexOf("-");
1031		var categoryID = 0;
1032		if (indexOfHypen > -1)
1033			categoryID = formURL.substr(indexOfHypen + 1);
1034		else
1035			categoryID = formURL;
1036		return categoryID;
1037	}
1038	else
1039		return null;
1040}
1041
1042
1043// Extracts form identifier from URL specified.
1044function extractFormIDFromURL(url) {
1045	return parseInt($('#hdnFormID').val(), 10);
1046
1047	//there is some query string appended if so remove those
1048	if (url.indexOf("/?") > -1) {
1049		url = url.substr(0, url.indexOf("/?"));
1050	}
1051
1052	url = url.toLowerCase();
1053	var indexOfForm = url.lastIndexOf("/formcenter/");
1054	if (indexOfForm > -1) {
1055		//extract the url
1056		var formURL = url.substr(indexOfForm + 12);
1057
1058		if (formURL.indexOf("/") > -1) {
1059			formURL = formURL.substr(formURL.indexOf("/") + 1);
1060			//check wheter the url has categoryName
1061			var indexOfHypen = formURL.lastIndexOf("-");
1062			var formID = 0;
1063			if (indexOfHypen > -1)
1064				formID = formURL.substr(indexOfHypen + 1);
1065			else if (formURL == "")
1066				return null;
1067			else
1068				formID = formURL;
1069
1070			return formID;
1071		}
1072		else
1073			return null;
1074	}
1075	else
1076		return null;
1077}
1078
1079// Attaches expand/collapse handlers to section headers.
1080function attachCategoryVisibilityTogglers() {
1081	$('#FormCenterContent .contentMain').click(function (event) {
1082		var target = event.target;
1083
1084		if (/^lnkLess[0-9]*$/.test(target.id)) {
1085			// Form details should shrink.
1086			lessInfo(parseInt(target.id.substr(7), 10));
1087			event.preventDefault();
1088		}
1089		else if (/^lnkMore[0-9]*$/.test(target.id)) {
1090			// Form details should expand.
1091			moreInfo(parseInt(target.id.substr(7), 10));
1092			event.preventDefault();
1093		}
1094	});
1095}
1096
1097// Called by form submittal to handle radio buttons for serialization.
1098function submitRadioHandler() {
1099	var names = [];
1100	var values = {};
1101	$checkBoxSelector = null;
1102	$webCheckBox = $('div.formWrap ol.cpForm input[type=radio]');
1103	$mobileCheckBox = $('.mobileGuts input[type=radio]');
1104
1105	if ($webCheckBox.length != 0 && $mobileCheckBox.length == 0)
1106		$checkBoxSelector = $webCheckBox;
1107
1108	if ($webCheckBox.length == 0 && $mobileCheckBox.length != 0)
1109		$checkBoxSelector = $mobileCheckBox;
1110	if ($checkBoxSelector != null) {
1111		// Get list of radio inputs and their values.
1112		$checkBoxSelector.each(function () {
1113			if (!names.contains(this.name)) {
1114				names.push(this.name);
1115				values[this.name] = '';
1116			}
1117
1118			if (this.checked)
1119				values[this.name] = this.value;
1120		})
1121	}
1122
1123	// Map collected data to hidden inputs submitted to server.
1124	for (var i = 0, len = names.length; i < len; i++) {
1125		var inputName = names[i];
1126		var id = (inputName.match(/_/g) || []).length == 2 ? inputName.substring(inputName.indexOf('_') + 1) : inputName;
1127		document.getElementById(id).value = values[inputName];
1128	}
1129}
1130
1131// Called by form submittal to handle checkboxes for serialization.
1132function submitCheckboxHandler() {
1133	var names = [];
1134	var values = {};
1135	$checkBoxSelector = null;
1136	$webCheckBox = $('div.formWrap ol.cpForm input[type=checkbox]');
1137	$mobileCheckBox = $('.mobileGuts input[type=checkbox]');
1138
1139	if ($webCheckBox.length != 0 && $mobileCheckBox.length == 0)
1140		$checkBoxSelector = $webCheckBox;
1141
1142	if ($webCheckBox.length == 0 && $mobileCheckBox.length != 0)
1143		$checkBoxSelector = $mobileCheckBox;
1144
1145	// Get list of checkbox inputs and their values.
1146	if ($checkBoxSelector != null) {
1147		$checkBoxSelector.each(function () {
1148			if (!names.contains(this.name)) {
1149				names.push(this.name);
1150				values[this.name] = [];
1151			}
1152
1153            if (this.checked) {
1154                var val = this.value.replace(/target="__blank"/g, 'target="_blank"');
1155				val = val.replace(/\,/g, '_comma_');
1156				if (val.trim() == '')
1157					val = '_empty_value_';
1158				values[this.name].push(val);
1159
1160			}
1161		});
1162	}
1163
1164	// Map collected data to hidden inputs submitted to server.
1165	for (var i = 0, len = names.length; i < len; i++)
1166		document.getElementById(names[i]).value = values[names[i]].join(',');
1167}
1168
1169function submitDateTimeHandler() {
1170	var names = [];
1171	var values = [];
1172
1173	var $datePickers, $timePickers;
1174    if ($('.formCenterDatePicker').length > 0 || $('.formCenterTimePicker').length > 0) {
1175        $datePickers = $('.formCenterDatePicker');
1176        $timePickers = $('.formCenterTimePicker');
1177    }
1178    else {
1179        $datePickers = $('.telerikDatePicker');
1180        $timePickers = $('.telerikTimePicker');
1181    }
1182
1183	$datePickers.each(function () {
1184		if (!names.contains(this.name)) {
1185			names.push(this.name);
1186			values[this.name] = [this.value];
1187		}
1188		else {
1189			values[this.name].push(this.value);
1190		}
1191	});
1192
1193	$timePickers.each(function () {
1194		if (!names.contains(this.name)) {
1195			names.push(this.name);
1196			values[this.name] = ['', '', this.value];
1197		}
1198		else {
1199			if (values[this.name].length == 1) {
1200				values[this.name].push('');
1201			}
1202			values[this.name].push(this.value);
1203		}
1204	});
1205
1206	for (var i = 0, len = names.length; i < len; i++)
1207		document.getElementById(names[i]).value = values[names[i]].join();
1208}
1209
1210$(function () {
1211	$('#aspnetForm,form[name=aspnetForm]').attr('enctype', "multipart/form-data");
1212	$('#aspnetForm,form[name=aspnetForm]').addClass('submitForm');
1213});
1214;
1215
1216/// <reference path="../../../../Scripts/json2.js" />
1217/// <reference path="../../../../Scripts/HtmlExtensionSupport.js" />
1218/// <reference path="../../../../Common/GlobalJSFunctionsDetail.js" />
1219/// <reference path="../../../../Assets/Scripts/InappropriateWords.js" />
1220/// <reference path="Home.js" />
1221
1222// Overridden on a per-form basis. Retrieves validator information for submission validation.
1223//function getFormValidatorData() {
1224//	return [];
1225//}
1226
1227var FormCenterHomeScriptResources = GetJson("/FormCenter/Localization");
1228var originalTextColorRadio;
1229var originalTextColorCheckBox;
1230
1231$.getScript("/Assets/Scripts/InappropriateWords.js");
1232
1233// Used to properly localize strings.
1234String.prototype.format = function () {
1235	var args = arguments;
1236	return this.replace(/{(\d+)}/g, function (match, number) {
1237		return typeof args[number] != 'undefined'
1238			? args[number]
1239			: match
1240			;
1241	});
1242};
1243
1244
1245// Performs validation for a form field.
1246function FieldValidator(elemInput, id, fieldType, isInternal, isRequired, formatValidation, formatValidationCustom, minA
1246nswer, maxAnswer, maxLength) {
1247	var self = this;
1248	this.elemInputIsArray = $.isArray(elemInput);
1249
1250	if (this.elemInputIsArray) {
1251		for (var i = 0, len = elemInput.length; i < len; i++) {
1252			if (typeof (elemInput[i]) == "string")
1253				elemInput[i] = document.getElementById(elemInput[i]);
1254		}
1255
1256		if (elemInput.length > 0 && elemInput[0]) {
1257			this.elemContainer = elemInput[0].parentNode.parentNode.parentNode.parentNode;
1258			this.elemInput = elemInput;
1259		}
1260	}
1261	else {
1262		if (typeof (elemInput) == "string")
1263			elemInput = document.getElementById(elemInput);
1264
1265		if (elemInput) {
1266			if (fieldType === FieldTypes.DatePicker)
1267				this.elemContainer = elemInput.parentNode.parentNode.parentNode.parentNode;
1268			else
1269				this.elemContainer = elemInput.parentNode.parentNode;
1270			this.elemInput = elemInput;
1271		}
1272	}
1273
1274	this.enabled = true;
1275	this.$elemContainer = $(this.elemContainer);
1276	this.fieldType = fieldType;
1277	if (this.fieldType === FieldTypes.FileUpload) {
1278		//When validating <input type="file" />, we need to update the elemInput reference since it is created a new one when failing inside validateFileUpload method - LC
1279		var validateInputFileUpload = function () {
1280			var $this = $(this);
1281			if (!validateMultiFileUpload($this[0], true)) {
1282				alert("The file you are trying to upload is not an allowed file type or has an invalid file name. These are the accepted characters: A-z 0-9 ~ ! ( ) - + = [ ] { } , . _");
1283				self.elemInput.value = null;
1284				self.elemInput.focus();
1285			}
1286		};
1287		$(this.elemInput).attr("change", "").removeAttr("onChange").unbind("change").change(validateInputFileUpload);
1288	}
1289	this.id = id;
1290	this.isInternal = isInternal;
1291	this.isRequired = isRequired;
1292	this.formatValidation = formatValidation;
1293	this.formatValidationCustom = formatValidationCustom;
1294	this.elemErrorNote = null;
1295	this.minAnswer = minAnswer;
1296	this.maxAnswer = maxAnswer;
1297	this.maxLength = maxLength;
1298	this.focusElementIndex = 0;
1299	this.alreadySetFocus = false;
1300	var isRequiredInfoLableDisplayed = false;
1301	if (this.isRequired && !isRequiredInfoLableDisplayed) {
1302		$('#liRequiredFieldInfo').show();
1303		isRequiredInfoLableDisplayed = true;
1304	}
1305}
1306
1307// Static. Creates a single validation object from an options object.
1308FieldValidator.createSingle = function (options) {
1309	return new FieldValidator(
1310		options.elemInput,
1311		options.id,
1312		options.fieldType,
1313		options.isInternal,
1314		options.isRequired,
1315		options.formatValidation,
1316		options.formatValidationCustom,
1317		options.minAnswer,
1318		options.maxAnswer,
1319		options.maxLength);
1320};
1321
1322// Static. Creates array of validation objects from array of option objects.
1323FieldValidator.createMulti = function (optionsArray) {
1324	var key = null;
1325
1326	var ret = {
1327		keys: [],
1328		add: function (validator) {
1329			var key = validator.id;
1330			this[key] = validator;
1331			this.keys.push(key);
1332		}
1333	};
1334
1335	for (var i = 0, len = optionsArray.length; i < len; i++)
1336		ret.add(FieldValidator.createSingle(optionsArray[i]));
1337
1338	return ret;
1339};
1340
1341// Internal.  Used by validation() to ensure the input does not exceed the max length.
1342FieldValidator.prototype.validateMaxLength = function (errorMsg) {
1343	var ERROR_MSG = FormCenterHomeScriptResources.MaxLengthErrorMessage.format(this.maxLength);
1344	if (this.maxLength == null)
1345		return true;
1346	var cleanValue = this.elemInput.value.trim();
1347	if (cleanValue != null && cleanValue.length > this.maxLength) {
1348		errorMsg.push(ERROR_MSG);
1349		return false;
1350	}
1351	return true;
1352};
1353
1354// Internal. Used by validate() to ensure user-enterable fields are phone numbers (of NANP format).
1355FieldValidator.prototype.validatePhone = function (errorMsg) {
1356	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidPhoneNumberFormat);
1357	var origLen = errorMsg.length;
1358
1359	switch (this.fieldType) {
1360		case FieldTypes.ShortAnswer:
1361		case FieldTypes.LongAnswer:
1362		case FieldTypes.Password:
1363			var cleanValue = this.elemInput.value.trim();
1364			if (cleanValue != '' && !phoneValidate(cleanValue, intCountryCode))
1365				errorMsg.push(ERROR_MSG);
1366			break;
1367	}
1368
1369	// Return false if validation check failed (number of errors changed).
1370	return origLen == errorMsg.length;
1371}
1372
1373// Internal. Used by validate() to ensure user-enterable fields are postal codes.
1374FieldValidator.prototype.validatePostal = function (errorMsg) {
1375	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidPostalCode);
1376	var origLen = errorMsg.length;
1377
1378	switch (this.fieldType) {
1379		case FieldTypes.ShortAnswer:
1380		case FieldTypes.LongAnswer:
1381		case FieldTypes.Password:
1382			var cleanValue = this.elemInput.value.trim();
1383
1384			if (cleanValue != '' && !isZipCode(cleanValue, intCountryCode))
1385				errorMsg.push(ERROR_MSG);
1386
1387			break;
1388	}
1389
1390	// Return false if validation check failed (number of errors changed).
1391	return origLen == errorMsg.length;
1392}
1393
1394// Internal. Used by validate() to ensure user-enterable fields are email addresses.
1395FieldValidator.prototype.validateEmail = function (errorMsg) {
1396	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidEmailAddressFormat2);
1397	var origLen = errorMsg.length;
1398
1399	switch (this.fieldType) {
1400		case FieldTypes.ShortAnswer:
1401		case FieldTypes.LongAnswer:
1402		case FieldTypes.Password:
1403		case FieldTypes.ReplyEmail:
1404			var cleanValue = this.elemInput.value.trim();
1405
1406			if (cleanValue != '' && !emailValidate(cleanValue))
1407				errorMsg.push(ERROR_MSG);
1408
1409			break;
1410	}
1411
1412	// Return false if validation check failed (number of errors changed).
1413	return origLen == errorMsg.length;
1414}
1415function getNumericThousandsSeparator() {
1416	var dummy = new Number(11111111);
1417	var thousandSep = dummy.toLocaleString().replace(/1/g, '');
1418	return (thousandSep.length > 0 ? thousandSep.charAt(0) : ',');
1419}
1420// Internal. Used by validate() to ensure user-enterable fields match custom expression.
1421FieldValidator.prototype.validateCustom = function (expr, errorMsg) {
1422	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidGenericField);
1423	var origLen = errorMsg.length;
1424
1425	var customRegExp = new RegExp(expr);
1426
1427	switch (this.fieldType) {
1428		case FieldTypes.ShortAnswer:
1429		case FieldTypes.LongAnswer:
1430		case FieldTypes.Password:
1431			var cleanValue = this.elemInput.value.trim();
1432
1433			if (cleanValue != '' && (!customRegExp.test(cleanValue)))
1434				errorMsg.push(ERROR_MSG);
1435
1436			break;
1437	}
1438
1439	// Return false if validation check failed (number of errors changed).
1440	return origLen == errorMsg.length;
1441}
1442
1443// Internal. Used by validate() to ensure user-enterable fields are letters only.
1444FieldValidator.prototype.validateLetters = function (errorMsg) {
1445	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.LettersOnlyField);
1446	var origLen = errorMsg.length;
1447
1448	var letterRegExp = /^[a-zA-Z ]+$/;
1449
1450	switch (this.fieldType) {
1451		case FieldTypes.ShortAnswer:
1452		case FieldTypes.LongAnswer:
1453		case FieldTypes.Password:
1454			var cleanValue = this.elemInput.value.trim();
1455
1456			if (cleanValue != '' && (!letterRegExp.test(cleanValue)))
1457				errorMsg.push(ERROR_MSG);
1458
1459			break;
1460	}
1461
1462	// Return false if validation check failed (number of errors changed).
1463	return origLen == errorMsg.length;
1464}
1465
1466// Internal. Used by validate() to ensure user-enterable fields are numeric (numbers, decimal separators, and group separators allowed).
1467FieldValidator.prototype.validateNumeric = function (errorMsg) {
1468	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.NumericOnlyField);
1469	var origLen = errorMsg.length;
1470
1471	var numericRegExp = new RegExp("^(([0-9]+?[0-9%G]+?[0-9](|%D[0-9]+))|([0-9]*(|%D[0-9]+)))$"
1472		.replace(/%G/g, RegExp.metaEscape(getNumericThousandsSeparator()))
1473		.replace(/%D/g, RegExp.metaEscape(getNumericDecimalSeparator())));
1474
1475	switch (this.fieldType) {
1476		case FieldTypes.ShortAnswer:
1477		case FieldTypes.LongAnswer:
1478		case FieldTypes.Password:
1479			var cleanValue = this.elemInput.value.trim();
1480
1481			if (cleanValue != '' && (!numericRegExp.test(cleanValue)))
1482				errorMsg.push(ERROR_MSG);
1483
1484			break;
1485	}
1486
1487	// Return false if validation check failed (number of errors changed).
1488	return origLen == errorMsg.length;
1489}
1490
1491FieldValidator.prototype.validateCurrency = function (errorMsg) {
1492	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.CurrencyOnlyField);
1493	var origLen = errorMsg.length;
1494
1495	var value = this.elemInput.value.trim();
1496
1497	if (!this.isValidMoney(value)) {
1498		errorMsg.push(ERROR_MSG);;
1499	}
1500
1501	// Return false if validation check failed (nubmer of errors changed).
1502	return origLen == errorMsg.length;
1503}
1504
1505FieldValidator.prototype.isValidMoney = function (value) {
1506	if (value && value != '')
1507		return /^\$?\s*\d+(,\d{3})*(\.\d{1,2})?$/.test(value);
1508	else
1509		return true;
1510};
1511
1512//#region Date Validation
1513
1514FieldValidator.prototype.validateDate = function (errorMsg) {
1515	var origLen = errorMsg.length;
1516
1517	var date = $(this.elemInput[0]).val();
1518	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidDate);
1519
1520	if (!this.isValidDate(date, this.isRequired)) {
1521		errorMsg.push(ERROR_MSG);
1522	}
1523
1524	// Return false if validation check failed (nubmer of errors changed).
1525	return origLen == errorMsg.length;
1526}
1527
1528FieldValidator.prototype.validateTime = function (errorMsg) {
1529	var origLen = errorMsg.length;
1530
1531	var time = $(this.elemInput[0]).val();
1532	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidTime);
1533
1534	if (!this.isValidTime(time, this.isRequired)) {
1535		errorMsg.push(ERROR_MSG);
1536	}
1537
1538	// Return false if validation check failed (nubmer of errors changed).
1539	return origLen == errorMsg.length;
1540}
1541
1542FieldValidator.prototype.validateDateTime = function (errorMsg) {
1543	var origLen = errorMsg.length;
1544
1545	var date = $(this.elemInput[0]).val();
1546
1547	var time = $(this.elemInput[1]).val();
1548
1549	var validDate = this.isValidDate(date, this.isRequired);
1550
1551	var validTime = this.isValidTime(time, this.isRequired);
1552	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidDateTime);
1553
1554	if (!validDate && !validTime) {
1555		if (!this.alreadySetFocus) {
1556			this.focusElementIndex = 0;
1557			this.alreadySetFocus = true;
1558		}
1559		errorMsg.push(ERROR_MSG);
1560	}
1561	else if (!validDate) {
1562		if (!this.alreadySetFocus) {
1563			this.focusElementIndex = 0;
1564			this.alreadySetFocus = true;
1565		}
1566		errorMsg.push(ERROR_MSG);
1567	}
1568	else if (!validTime) {
1569		if (!this.alreadySetFocus) {
1570			this.focusElementIndex = 1;
1571			this.alreadySetFocus = true;
1572		}
1573		errorMsg.push(ERROR_MSG);
1574	}
1575
1576	// Return false if validation check failed (nubmer of errors changed).
1577	return origLen == errorMsg.length;
1578}
1579
1580FieldValidator.prototype.validateDateSpan = function (errorMsg) {
1581	var origLen = errorMsg.length;
1582
1583	var date = $(this.elemInput[0]).val();
1584
1585	var dateSpan = $(this.elemInput[1]).val();
1586
1587	if (!this.isValidDate(date, this.isRequired)) {
1588		if (!this.alreadySetFocus) {
1589			this.focusElementIndex = 0;
1590			this.alreadySetFocus = true;
1591		}
1592		errorMsg.push(FormCenterHomeScriptResources.InvalidStartDate);
1593	}
1594	if (!this.isValidDate(dateSpan, this.isRequired)) {
1595		if (!this.alreadySetFocus) {
1596			this.focusElementIndex = 1;
1597			this.alreadySetFocus = true;
1598		}
1599		errorMsg.push(FormCenterHomeScriptResources.InvalidEndDate);
1600	}
1601	if (!this.isValidDateRange(date, dateSpan, this.isRequired, this.isRequired)) {
1602		if (!this.alreadySetFocus) {
1603			this.focusElementIndex = 0;
1604			this.alreadySetFocus = true;
1605		}
1606		errorMsg.push(FormCenterHomeScriptResources.InvalidDateSpan);
1607	}
1608
1609	// Return false if validation check failed (nubmer of errors changed).
1610	return origLen == errorMsg.length;
1611}
1612
1613FieldValidator.prototype.validateTimeSpan = function (errorMsg) {
1614	var origLen = errorMsg.length;
1615
1616	var time = $(this.elemInput[0]).val();
1617
1618	var timeSpan = $(this.elemInput[1]).val();
1619
1620	if (!this.isValidTime(time, this.isRequired)) {
1621		if (!this.alreadySetFocus) {
1622			this.focusElementIndex = 0;
1623			this.alreadySetFocus = true;
1624		}
1625		errorMsg.push(FormCenterHomeScriptResources.InvalidStartTime);
1626	}
1627
1628	if (!this.isValidTime(timeSpan, this.isRequired)) {
1629		if (!this.alreadySetFocus) {
1630			this.focusElementIndex = 1;
1631			this.alreadySetFocus = true;
1632		}
1633		errorMsg.push(FormCenterHomeScriptResources.InvalidEndTime);
1634	}
1635
1636	if (!this.isValidTimeRange(time, timeSpan, this.isRequired, this.isRequired)) {
1637		if (!this.alreadySetFocus) {
1638			this.focusElementIndex = 0;
1639			this.alreadySetFocus = true;
1640		}
1641		errorMsg.push(FormCenterHomeScriptResources.InvalidTimeSpan);
1642	}
1643
1644	// Return false if validation check failed (nubmer of errors changed).
1645	return origLen == errorMsg.length;
1646}
1647
1648FieldValidator.prototype.validateDateTimeSpan = function (errorMsg) {
1649	var origLen = errorMsg.length;
1650
1651	var date = $(this.elemInput[0]).val();
1652
1653	var dateSpan = $(this.elemInput[1]).val();
1654
1655	var time = $(this.elemInput[2]).val();
1656
1657	var timeSpan = $(this.elemInput[3]).val();
1658
1659	var validDate = this.isValidDate(date, this.isRequired);
1660
1661	var validTime = this.isValidTime(time, this.isRequired);
1662
1663	if (!validDate && !validTime) {
1664		if (!this.alreadySetFocus) {
1665			this.focusElementIndex = 0;
1666			this.alreadySetFocus = true;
1667		}
1668		errorMsg.push(FormCenterHomeScriptResources.InvalidStartDateTime);
1669	}
1670	else if (!validDate) {
1671		if (!this.alreadySetFocus) {
1672			this.focusElementIndex = 0;
1673			this.alreadySetFocus = true;
1674		}
1675		errorMsg.push(FormCenterHomeScriptResources.InvalidStartDate);
1676	}
1677	else if (!validTime) {
1678		if (!this.alreadySetFocus) {
1679			this.focusElementIndex = 2;
1680			this.alreadySetFocus = true;
1681		}
1682		errorMsg.push(FormCenterHomeScriptResources.InvalidStartTime);
1683	}
1684
1685	var validSpanDate = this.isValidDate(dateSpan, this.isRequired);
1686
1687	var validSpanTime = this.isValidTime(timeSpan, this.isRequired);
1688
1689	if (!validSpanDate && !validSpanTime) {
1690		if (!this.alreadySetFocus) {
1691			this.focusElementIndex = 1;
1692			this.alreadySetFocus = true;
1693		}
1694		errorMsg.push(FormCenterHomeScriptResources.InvalidEndDateTime);
1695	}
1696	else if (!validSpanDate) {
1697		if (!this.alreadySetFocus) {
1698			this.focusElementIndex = 1;
1699			this.alreadySetFocus = true;
1700		}
1701		errorMsg.push(FormCenterHomeScriptResources.InvalidEndDate);
1702	}
1703	else if (!validSpanTime) {
1704		if (!this.alreadySetFocus) {
1705			this.focusElementIndex = 3;
1706			this.alreadySetFocus = true;
1707		}
1708		errorMsg.push(FormCenterHomeScriptResources.InvalidEndTime);
1709	}
1710	var isDayBeforeMonth = getDateFormat().toLocaleLowerCase() == "dd/mm/yyyy" ? true : false;
1711	if (!this.isValidDateTimeRange(getDateFromInput(this.elemInput[0], isDayBeforeMonth), getDateFromInput(this.elemInput[1], isDayBeforeMonth), this.isRequired, this.isRequired, time, timeSpan, this.isRequired, this.isRequired)) {
1712		if (!this.alreadySetFocus) {
1713			this.focusElementIndex = 1;
1714			this.alreadySetFocus = true;
1715		}
1716		errorMsg.push(FormCenterHomeScriptResources.InvalidDateTimeSpan);
1717	}
1718
1719	// Return false if validation check failed (nubmer of errors changed).
1720	return origLen == errorMsg.length;
1721}
1722
1723FieldValidator.prototype.isValidDate = function (date, dateRequired) {
1724	var objDate = new dateValidator();
1725
1726	return objDate.dateValidateNew(date, dateRequired, '');
1727}
1728
1729FieldValidator.prototype.isValidTime = function (time, timeRequired) {
1730	var objDate = new dateValidator();
1731
1732	var amIndex = time.toLowerCase().indexOf('am');
1733	var pmIndex = time.toLowerCase().indexOf('pm');
1734
1735	if (amIndex != -1) {
1736		time = time.substring(0, amIndex).replace(" ", "");
1737
1738		if (time.trim() === "") {
1739			return false;
1740		}
1741	}
1742	else if (pmIndex != -1) {
1743		time = time.substring(0, pmIndex).replace(" ", "");
1744
1745		if (time.trim() === "") {
1746			return false;
1747		}
1748	}
1749
1750	return objDate.timeValidate(time, timeRequired, '');
1751}
1752
1753FieldValidator.prototype.isValidTimeRange = function (startTime, endTime, startTimeRequired, endTimeRequired) {
1754	var objDate = new dateValidator();
1755
1756	objDate.ysnAllowTimeOnly = true;
1757	objDate.dtiStartTime = this.stripAMPM(startTime);
1758	objDate.ysnStartTimeRequired = startTimeRequired;
1759	objDate.dtiEndTime = this.stripAMPM(endTime);
1760	objDate.ysnEndTimeRequired = endTimeRequired;
1761	objDate.ysnDatesAreEqual = true;
1762	objDate.strStartAMPM = this.getAMPMString(startTime);
1763	objDate.strEndAMPM = this.getAMPMString(endTime);
1764	objDate.timesAlreadyValidated = true;
1765
1766	return objDate.timeOrderValidate();
1767
1768}
1769
1770FieldValidator.prototype.isValidDateRange = function (startDate, endDate, startDateRequired, endDateRequired) {
1771	var objDate = new dateValidator();
1772	objDate.setStartDate(checkDateFormatReturnUSString(startDate, false));
1773	objDate.setEndDate(checkDateFormatReturnUSString(endDate, false));
1774	objDate.setStartDateRequired(startDateRequired);
1775	objDate.setEndDateRequired(endDateRequired);
1776	objDate.datesAlreadyValidated = true;
1777
1778	return objDate.dateOrderValidateNew();
1779}
1780
1781FieldValidator.prototype.isValidDateTimeRange = function (startDate, endDate, startDateRequired, endDateRequired, startTime, endTime, startTimeRequired, endTimeRequired) {
1782	var objDate = new dateValidator();
1783
1784
1785	objDate.setStartDate(checkDateFormatReturnUSString(startDate, false));
1786	objDate.setEndDate(checkDateFormatReturnUSString(endDate, false));
1787	objDate.setStartDateRequired(startDateRequired);
1788	objDate.setEndDateRequired(endDateRequired);
1789	objDate.ysnAllowEqualDates = true;
1790	objDate.datesAlreadyValidated = true;
1791
1792	var validDateOrder = objDate.dateOrderValidateNew();
1793
1794	objDate.strStartAMPM = this.getAMPMString(startTime);
1795	objDate.strEndAMPM = this.getAMPMString(endTime);
1796	objDate.ysnAllowTimeOnly = true;
1797	objDate.ysnAllowEqualTimes = !objDate.ysnDatesAreEqual;
1798	objDate.dtiStartTime = this.stripAMPM(startTime);
1799	objDate.ysnStartTimeRequired = startTimeRequired;
1800	objDate.dtiEndTime = this.stripAMPM(endTime);
1801	objDate.ysnEndTimeRequired = endTimeRequired;
1802	objDate.timesAlreadyValidated = true;
1803
1804	var validTimeOrder = objDate.timeOrderValidate();
1805
1806	return validDateOrder && validTimeOrder;
1807}
1808
1809FieldValidator.prototype.stripAMPM = function (time) {
1810	var amIndex = time.toLowerCase().indexOf('am');
1811	var pmIndex = time.toLowerCase().indexOf('pm');
1812
1813	if (amIndex != -1) {
1814		time = time.substring(0, amIndex).replace(" ", "");
1815	}
1816	else if (pmIndex != -1) {
1817		time = time.substring(0, pmIndex).replace(" ", "");
1818	}
1819
1820	return time;
1821}
1822
1823FieldValidator.prototype.getAMPMString = function (time) {
1824	var amIndex = time.toLowerCase().indexOf('am');
1825	var pmIndex = time.toLowerCase().indexOf('pm');
1826
1827	var AMPM = '';
1828
1829	if (amIndex != -1) {
1830		AMPM = time.substring(amIndex).replace(" ", "");
1831	}
1832	else if (pmIndex != -1) {
1833		AMPM = time.substring(pmIndex).replace(" ", "");
1834	}
1835	return AMPM;
1836}
1837
1838//#endregion Date Validation
1839
1840//checkbox validation done for min and max answer selcted
1841FieldValidator.prototype.checkBoxValidation = function (errorMsg) {
1842	var origLen = errorMsg.length;
1843	if (this.fieldType == FieldTypes.Checkboxes && ((this.minAnswer != "" && this.minAnswer != undefined) || (this.maxAnswer != "" && this.maxAnswer != undefined))) {
1844		var i = 0, len = this.elemInput.length;
1845		var checkedCount = 0;
1846		for (; i < len; i++) {
1847			if (this.elemInput[i].checked)
1848				checkedCount++;
1849		}
1850		//validation for checkbox min max fields
1851		if (this.minAnswer != "" && this.minAnswer != undefined) {
1852			if (checkedCount < parseInt(this.minAnswer, 10)) // the message is not localized on purpose as this has text and dynamic value
1853				errorMsg.push("This field must have at least " + parseInt(this.minAnswer, 10) + " options selected");
1854		}
1855
1856		if (origLen == errorMsg.length && this.maxAnswer != "" && this.maxAnswer != undefined) {
1857			if (checkedCount > parseInt(this.maxAnswer, 10)) // the message is not localized on purpose as this has text and dynamic value
1858				errorMsg.push("This field can have at most " + parseInt(this.maxAnswer, 10) + " options selected");
1859		}
1860	}
1861
1862	// Return false if validation check failed (number of errors changed).
1863	return origLen == errorMsg.length;
1864};
1865
1866// Internal. Used by validate() to ensure required fields are set.
1867FieldValidator.prototype.validateRequired = function (errorMsg) {
1868	var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.RequiredField);//FormCenterHomeScriptResources.RequiredField;
1869	var origLen = errorMsg.length;
1870	switch (this.fieldType) {
1871		case FieldTypes.ShortAnswer:
1872		case FieldTypes.LongAnswer:
1873		case FieldTypes.ReplyEmail:
1874		case FieldTypes.Password:
1875		case FieldTypes.FileUpload:
1876		case FieldTypes.CodeSnippet:
1877		case FieldTypes.InputLink:
1878		case FieldTypes.DatePicker:
1879		case FieldTypes.InputImage:
1880			if (this.elemInput != 'undefined' && this.elemInput.value != undefined) {
1881				if (this.elemInput.value.trim() == '')
1882					errorMsg.push(ERROR_MSG);
1883			}
1884			break;
1885		case FieldTypes.Checkboxes:
1886		case FieldTypes.RadioButtons:
1887			var i = 0, len = this.elemInput.length;
1888
1889			for (; i < len; i++) {
1890				if (this.elemInput[i].checked)
1891					break;
1892			}
1893
1894			if (i == len)
1895				errorMsg.push(ERROR_MSG);
1896
1897			break;
1898		case FieldTypes.Dropdown:
1899		case FieldTypes.FormLink:
1900			if (this.elemInput.selectedIndex <= 0)
1901				errorMsg.push(ERROR_MSG);
1902
1903			break;
1904		case FieldTypes.DateTime:
1905			var i = 0, len = this.elemInput.length;
1906
1907			for (; i < len; i++) {
1908				if (this.elemInput[i].value.trim() == '') {
1909					errorMsg.push(ERROR_MSG);
1910					this.focusElementIndex = i;
1911					break;
1912				}
1913			}
1914	}
1915	// Return false if validation check failed (number of errors changed).
1916	return origLen == errorMsg.length;
1917};
1918
1919// Performs validation for field and updates format if necessary.
1920FieldValidator.prototype.validate = function (assignFocusOnFailure, checkForInappropriateWords) {
1921	var errorMsg = [];
1922
1923	// Element not present OR disabled, pass (skipped).
1924	if (this.elemInput == null || !this.enabled)
1925		return true;
1926
1927	if (!this.isRequired || (this.isRequired && this.validateRequired(errorMsg))) {
1928		//checkbox validation done for min and max answer selected
1929		if (this.checkBoxValidation(errorMsg)) {
1930			switch (this.formatValidation) {
1931				case FieldValidation.Letters:
1932					this.validateLetters(errorMsg);
1933					break;
1934				case FieldValidation.Numeric:
1935					this.validateNumeric(errorMsg);
1936					break;
1937				case FieldValidation.Currency:
1938					this.validateCurrency(errorMsg);
1939					break;
1940				case FieldValidation.PhoneNumber:
1941					this.validatePhone(errorMsg);
1942					break;
1943				case FieldValidation.PostalCode:
1944					this.validatePostal(errorMsg);
1945					break;
1946				case FieldValidation.EmailAddress:
1947					this.validateEmail(errorMsg);
1948					break;
1949				case FieldValidation.RegEx:
1950					this.validateCustom(this.formatValidationCustom, errorMsg);
1951					break;
1952				case FieldValidation.Date:
1953					this.validateDate(errorMsg);
1954					break;
1955				case FieldValidation.Time:
1956					this.validateTime(errorMsg);
1957					break;
1958				case FieldValidation.DateTime:
1959					this.validateDateTime(errorMsg);
1960					break;
1961				case FieldValidation.DateSpan:
1962					this.validateDateSpan(errorMsg);
1963					break;
1964				case FieldValidation.TimeSpan:
1965					this.validateTimeSpan(errorMsg);
1966					break;
1967				case FieldValidation.DateTimeSpan:
1968					this.validateDateTimeSpan(errorMsg);
1969					break;
1970			}
1971		}
1972	}
1973	if (this.fieldType === FieldTypes.DatePicker && this.elemInput.value.trim() != '') {
1974		//validate date picker value (based on telerik mvc datePicker)
1975		var datepicker = $(this.elemInput).data("tDatePicker");
1976		if ($(this.elemInput).hasClass('t-state-error') || datepicker.parse(this.elemInput.value.trim()) == null)
1977			errorMsg.push("Invalid date format");
1978	}
1979	if (this.fieldType === FieldTypes.ShortAnswer || this.fieldType === FieldTypes.LongAnswer) {
1980		var validator = this;
1981
1982		if (checkForInappropriateWords != null && typeof (checkForInappropriateWords.push) == 'function') {
1983			checkForInappropriateWords.push(validator);
1984		} else {
1985			if (HasInappropriateWords(this.elemInput.value.trim())) {
1986				$(this.elemInput).addClass('t-state-error');
1987				errorMsg.push(FormCenterHomeScriptResources.InappropriateWords);
1988			}
1989		}
1990	}
1991
1992	if (FeatureToggles.isActive("CMS.FormCenter.EnableServerSideValidationsForSubmissions")
1993		&& this.fieldType === FieldTypes.FileUpload
1994		&& this.elemInput.value.trim() != '') {
1995		if (Math.round((this.elemInput.files[0].size / 1024)) > 20480)
1996			errorMsg.push("Files cannot be larger than 20 MB.");
1997	}
1998
1999	this.validateMaxLength(errorMsg);
2000
2001	if (errorMsg.length > 0 && $(this.elemInput).is(":visible")) {
2002		this.errorShow(errorMsg);
2003
2004		if (assignFocusOnFailure) {
2005			this.focusErrorElement();
2006		}
2007
2008		return false;
2009	} else {
2010		this.errorClear();
2011		return true;
2012	}
2013
2014};
2015
2016// Performs validation for admin edit internal only fields and updates format if necessary.
2017FieldValidator.prototype.validateInternal = function (assignFocusOnFailure, checkForInappropriateWords) {
2018	if (this.isInternal) {
2019		var errorMsg = [];
2020
2021		// Element not present OR disabled, pass (skipped).
2022		if (this.elemInput == null || !this.enabled)
2023			return true;
2024
2025		if (!this.isRequired || (this.isRequired && this.validateRequired(errorMsg))) {
2026			//checkbox validation done for min and max answer selected
2027			if (this.checkBoxValidation(errorMsg)) {
2028				switch (this.formatValidation) {
2029					case FieldValidation.Letters:
2030						this.validateLetters(errorMsg);
2031						break;
2032					case FieldValidation.Numeric:
2033						this.validateNumeric(errorMsg);
2034						break;
2035					case FieldValidation.Currency:
2036						this.validateCurrency(errorMsg);
2037						break;
2038					case FieldValidation.PhoneNumber:
2039						this.validatePhone(errorMsg);
2040						break;
2041					case FieldValidation.PostalCode:
2042						this.validatePostal(errorMsg);
2043						break;
2044					case FieldValidation.EmailAddress:
2045						this.validateEmail(errorMsg);
2046						break;
2047					case FieldValidation.RegEx:
2048						this.validateCustom(this.formatValidationCustom, errorMsg);
2049						break;
2050					case FieldValidation.Date:
2051						this.validateDate(errorMsg);
2052						break;
2053					case FieldValidation.Time:
2054						this.validateTime(errorMsg);
2055						break;
2056					case FieldValidation.DateTime:
2057						this.validateDateTime(errorMsg);
2058						break;
2059					case FieldValidation.DateSpan:
2060						this.validateDateSpan(errorMsg);
2061						break;
2062					case FieldValidation.TimeSpan:
2063						this.validateTimeSpan(errorMsg);
2064						break;
2065					case FieldValidation.DateTimeSpan:
2066						this.validateDateTimeSpan(errorMsg);
2067						break;
2068				}
2069			}
2070		}
2071		if (this.fieldType === FieldTypes.DatePicker && this.elemInput.value.trim() != '') {
2072			//validate date picker value (based on telerik mvc datePicker)
2073			var datepicker = $(this.elemInput).data("tDatePicker");
2074			if ($(this.elemInput).hasClass('t-state-error') || datepicker.parse(this.elemInput.value.trim()) == null)
2075				errorMsg.push("Invalid date format");
2076		}
2077		if (this.fieldType === FieldTypes.ShortAnswer || this.fieldType === FieldTypes.LongAnswer) {
2078			var validator = this;
2079
2080			if (checkForInappropriateWords != null && typeof (checkForInappropriateWords.push) == 'function') {
2081				checkForInappropriateWords.push(validator);
2082			} else {
2083				if (HasInappropriateWords(this.elemInput.value.trim())) {
2084					$(this.elemInput).addClass('t-state-error');
2085					errorMsg.push(FormCenterHomeScriptResources.InappropriateWords);
2086				}
2087			}
2088		}
2089
2090		this.validateMaxLength(errorMsg);
2091
2092		if (errorMsg.length > 0 && $(this.elemInput).is(":visible")) {
2093			this.errorShow(errorMsg);
2094
2095			if (assignFocusOnFailure) {
2096				this.focusErrorElement();
2097			}
2098
2099			return false;
2100		} else {
2101			this.errorClear();
2102			return true;
2103		}
2104	}
2105	else {
2106		return true;
2107	}
2108};
2109
2110FieldValidator.prototype.handleInappropriateWords = function (focus) {
2111	var errorMsg = [];
2112	$(this.elemInput).addClass('t-state-error');
2113	errorMsg.push(FormCenterHomeScriptResources.InappropriateWords);
2114	this.errorShow(errorMsg);
2115	if (focus) {
2116		this.focusErrorElement();
2117	}
2118};
2119
2120FieldValidator.prototype.focusErrorElement = function () {
2121	var scrollIntoView = function (element) {
2122		var offset = $(element).offset();
2123		var elementTop = offset.top;
2124		// Add a 50px padding to the top of the element, just for aesthetics
2125		$(document).scrollTop(elementTop - 150);
2126	};
2127
2128	if (this.elemInputIsArray) {
2129		if ($(this.elemInput).is(":visible")) {
2130			if (this.elemInput.length > this.focusElementIndex) {
2131				this.elemInput[this.focusElementIndex].focus();
2132			}
2133			else {
2134				this.elemInput[0].focus();
2135			}
2136			scrollIntoView(this.elemInput[0]);
2137		}
2138		this.alreadySetFocus = false;
2139		this.focusElementIndex = 0;
2140	}
2141	else {
2142		if ($(this.elemInput).is(":visible")) {
2143			this.elemInput.focus();
2144			scrollIntoView(this.elemInput);
2145		}
2146	}
2147}
2148
2149// Shows error message for field. Called by validate() if field value fails validation.
2150FieldValidator.prototype.errorShow = function (messages) {
2151	if (this.elemErrorNote)
2152		this.errorClear();
2153
2154	// Element not defined, cannot perform operation.
2155	if (this.elemInput == null)
2156		return;
2157
2158	this.$elemContainer.addClass('error');
2159	this.elemErrorNote = [];
2160
2161	$('ol li.error div').addClass('selfClear');
2162
2163	if (this.elemInput != undefined) {
2164		if (this.elemInput["0"] != undefined) {
2165			//Check current element is radio
2166			if (this.elemInput["0"].type == "radio" & !(this.$elemContainer.closest('li').hasClass('error'))) {
2167				this.$elemContainer.closest('li').addClass('error');
2168				originalTextColorRadio = this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color;
2169				this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = "#800000";
2170			}
2171			else //Check current element is checkbox
2172				if (this.elemInput["0"].type == "checkbox" & !(this.$elemContainer.closest('li').hasClass('error'))) {
2173					this.$elemContainer.closest('li').addClass('error');
2174					originalTextColorCheckBox = this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color;
2175					this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = "#800000";
2176				}
2177		}
2178	}
2179
2180	for (var i = 0, len = messages.length; i < len; i++) {
2181		var errorElemMsg = document.createElement('p');
2182
2183		errorElemMsg.className = 'explanation';
2184
2185		var multiSelectable = this.elemInput[0] !== undefined && this.elemInput.tagName !== "SELECT";
2186		if (multiSelectable) {
2187			errorElemMsg.id = 'errorMsg' + this.elemInput[0].id;
2188		}
2189		else {
2190			errorElemMsg.id = 'errorMsg' + this.elemInput.id;
2191		}
2192
2193		errorElemMsg.innerHTML = messages[i];
2194
2195		if (this.elemInput != undefined) {
2196			if (this.elemInput["0"] != undefined) {
2197				if (this.elemInput["0"].type == "radio" || this.elemInput["0"].type == "checkbox") {
2198					errorElemMsg.style.backgroundColor = "#940E0D"
2199					errorElemMsg.style.color = "#FFFFFF";
2200					errorElemMsg.style.fontWeight = "bold"
2201					errorElemMsg.style.lineHeight = "1.2"
2202					errorElemMsg.style.width = "94%"
2203					errorElemMsg.style.margin = ".5em 0 0"
2204					errorElemMsg.style.padding = ".5em"
2205				}
2206			}
2207		}
2208
2209		this.elemContainer.appendChild(errorElemMsg);
2210
2211		this.elemErrorNote.push(errorElemMsg);
2212	}
2213	!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
2214};
2215
2216// Clears error message for field. Called by validate() if field value passes validation.
2217FieldValidator.prototype.errorClear = function () {
2218	if (this.elemInput != undefined) {
2219		if (this.elemInput["0"] != undefined) {
2220			var i = 0, len = this.elemInput.length;
2221			for (; i < len; i++) {
2222				if (this.elemInput[i].checked)
2223					break;
2224			}
2225
2226			if (i != len) {
2227				if (this.elemInput["0"].type == "radio") {
2228					this.$elemContainer.closest('li').removeClass('error');
2229					this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = originalTextColorRadio;
2230				}
2231				else if (this.elemInput["0"].type == "checkbox") {
2232					this.$elemContainer.closest('li').removeClass('error');
2233					this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = originalTextColorCheckBox;
2234				}
2235			}
2236		}
2237	}
2238	if (this.elemErrorNote == null)
2239		return;
2240	else if (this.elemInput == null)
2241		return;
2242
2243	if (this.elemErrorNote.length > 0) {
2244		var errorParent = this.elemErrorNote[0].parentNode;
2245
2246		for (var i = 0, len = this.elemErrorNote.length; i < len; i++)
2247			errorParent.removeChild(this.elemErrorNote[i]);
2248
2249		this.elemErrorNote = null;
2250	}
2251
2252	this.$elemContainer.removeClass('error');
2253	!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
2254};
2255
2256FieldValidator.prototype.getErrorMessage = function (errorTemplateMsg) {
2257	var $fieldElemContainer = $(this.elemContainer);
2258
2259	if (!$fieldElemContainer.hasClass('formFieldContainer'))
2260	{
2261		$fieldElemContainer = $fieldElemContainer.parents('.formFieldContainer');
2262	}
2263
2264	var $fieldLabelElem = $fieldElemContainer.find('.labelForFieldElement');
2265	var fieldLabel = '';
2266
2267	if ($fieldLabelElem.length > 0)
2268	{
2269		fieldLabel = $fieldLabelElem.text().replace(/\*/g, '');
2270	}
2271	else
2272	{
2273		$fieldLabelElem = $fieldElemContainer.find('legend');
2274		if ($fieldLabelElem.length > 0)
2275		{
2276			fieldLabel = $fieldLabelElem.text().replace(/\*/g, '');
2277		}
2278	}
2279
2280	var errorMsg = errorTemplateMsg.format(fieldLabel);
2281
2282	return errorMsg;
2283}
2284;
2285/// <reference path="../../../../Scripts/json2.js"/>
2286/// <reference path="../../../../Scripts/HtmlExtensionSupport.js"/>
2287/// <reference path="../../../../Common/GlobalJSFunctionsDetail.js" />
2288/// <reference path="Home.js" />
2289
2290// Configurable in debug console. Controls behavior of FieldConditionsRunner.log().
2291var condRunnerHideLogDupes = true;
2292
2293// Missing methods for IE
2294if (!Element.prototype.matches) {
2295	Element.prototype.matches =
2296		Element.prototype.msMatchesSelector ||
2297		Element.prototype.webkitMatchesSelector;
2298}
2299
2300if (!Element.prototype.closest) {
2301	Element.prototype.closest = function (s) {
2302		var el = this;
2303
2304		do {
2305			if (Element.prototype.matches.call(el, s)) return el;
2306			el = el.parentElement || el.parentNode;
2307		} while (el !== null && el.nodeType === 1);
2308		return null;
2309	};
2310}
2311
2312////////// Helper Functions ///////////////
2313
2314$("*").focus(function () {
2315	var inputs = $("input[type=text][cp5ph=true], textarea[cp5ph=true]");
2316	if (inputs != null) {
2317		for (var i = 0; i < inputs.length; i++) {
2318			var elementName = inputs[i].getAttribute('id');
2319			if (elementName != null) {
2320				var top = $('#' + elementName).offset().top;
2321				var left = $('#' + elementName).offset().left;
2322				if (top != -2 && left != -2)
2323					$('#ph_' + elementName).offset({ left: (left + 5), top: (top + 5) });
2324			}
2325		}
2326	}
2327});
2328
2329// Helper that handles replacement of special characters.
2330// * value - variable to handle characters of.
2331function HandleCharacters(value) {
2332	return value.replace(/,/g, '_d');
2333}
2334
2335if (Array.prototype.any == null && !$.isFunction(Array.prototype.any)) {
2336	// Returns true if callback condition returns true for any items in array.
2337	// * callback - Callback function with single argument for current index. The 'this' reference points to the array.
2338	Array.prototype.any = function (callback) {
2339		if (this == null)
2340			return false;
2341
2342		for (var i = 0, len = this.length; i < len; i++) {
2343			if (callback.call(this, i))
2344				return true;
2345		}
2346
2347		return false;
2348	}
2349}
2350
2351if (Array.prototype.all == null && !$.isFunction(Array.prototype.all)) {
2352	// Returns true if callback condition returns true for all items in array.
2353	// * callback - Callback function with single argument for current index. The 'this' reference points to the array.
2354	Array.prototype.all = function (callback) {
2355		if (this == null)
2356			return false;
2357
2358		for (var i = 0, len = this.length; i < len; i++) {
2359			if (!callback.call(this, i))
2360				return false;
2361		}
2362
2363		return true;
2364	};
2365}
2366
2367if (Error.argNull == null || !$.isFunction(Error.argNull)) {
2368	// Static helper that creates error when null argument that should not be null is encountered.
2369	// * argName - Name of problematic null argument.
2370	Error.argNull = function (argName) {
2371		return new ReferenceError(argName + " cannot be null");
2372	}
2373}
2374
2375// Overridden on a per-form basis. Retrieves validator information for submission validation.
2376// * validators - Validator dictionary.
2377function getFormConditionData() {
2378	return [];
2379}
2380
2381// Prints useful debugging information for FieldConditionsRunner.run() results. Use as done callback.
2382// Note: When minifiying the JavaScript, this function can/should be removed.
2383// * logs - Log generated by run().
2384function debugCondRunnerLogResults(logs) {
2385	if (logs == null)
2386		throw new Error.argNull("logs");
2387
2388	console.log("## run() started at " + (new Date()) + " ##");
2389
2390	for (var i = 0, iLen = logs.length; i < iLen; i++) {
2391		var log = logs[i];
2392		var resultEx = log.isMet ? " (met)" : " (not met)";
2393
2394		console.log(
2395			"field: " + log.id +
2396			", condIndex: " + log.condIndex +
2397			", action: " + ConditionActions.getName(log.actType) +
2398			", result: " + ConditionRunnerActResult.getName(log.result) + resultEx);
2399
2400		if (log.needRestart)
2401			console.log("-- restart --");
2402	}
2403
2404	console.log("## run() ended ##");
2405	console.log("\t");
2406}
2407
2408// Helper that repositions line siblings by re-assigning left/middle/right classes.
2409// * liSiblingList - Array of LI elements for a form line.
2410function repositionLineSiblings(liSiblingList) {
2411	if (liSiblingList == null)
2412		throw new Error.argNull("liSiblingList");
2413
2414	var sibling, $sibling;
2415	var len = liSiblingList.length;
2416
2417	if (len > 0) {
2418		var operableNodes = [];
2419
2420		for (var i = 0; i < len; i++) {
2421			sibling = liSiblingList[i];
2422			$sibling = $(sibling);
2423
2424			if (!$sibling.hasClass('hidden'))
2425				operableNodes.push($sibling);
2426		}
2427
2428		len = operableNodes.length;
2429
2430		if (len > 0) {
2431			operableNodes[0].removeClass('middle right').addClass('left');
2432
2433			var j = 1;
2434
2435			for (; j < len - 1; j++)
2436				operableNodes[j].removeClass('left right').addClass('middle');
2437
2438			if (j < len)
2439				operableNodes[j].removeClass('left middle').addClass('right');
2440		}
2441	}
2442}
2443
2444// Helper that gets sibling field LI elements for a line field LI element.
2445// * liElem - The LI element for a form field to get line siblings LIs for.
2446function getLineSiblings(liElem) {
2447
2448	if (liElem == null && (window.location.href).toString().toLowerCase().indexOf("print") != -1)
2449		return "";
2450
2451	if (liElem == null)
2452		throw new Error.argNull("liElem");
2453
2454	var nodeList = [];
2455	var startNode = liElem;
2456
2457	// Get start of line.
2458	while (startNode && !$(startNode).hasClass('lineStart'))
2459		startNode = previousElementSibling(startNode);
2460
2461	// Get end of line.
2462	if (startNode) {
2463		var endNode = startNode;
2464		var $endNode = null;
2465
2466		while (endNode != null) {
2467			$endNode = $(endNode);
2468
2469			if ($endNode.hasClass('lineEnd'))
2470				break;
2471
2472			endNode = nextElementSibling(endNode);
2473		}
2474	}
2475
2476	if (startNode && endNode) {
2477		var nodeEnum = startNode;
2478
2479		while (true) {
2480			nodeList.push(nodeEnum);
2481
2482			if (nodeEnum == endNode)
2483				break;
2484
2485			nodeEnum = nextElementSibling(nodeEnum);
2486		}
2487	}
2488
2489	return nodeList;
2490}
2491
2492////////// Enumerations ///////////////////
2493
2494// Condition actions.
2495var ConditionActions = {
2496	None: 0,
2497	Show: 1,
2498	Hide: 2,
2499	Require: 3,
2500	RedirectTo: 4,
2501	getName: function (value) {
2502		for (var k in ConditionActions) {
2503			if (ConditionActions[k] == value)
2504				return k;
2505		}
2506
2507		return null;
2508	}
2509};
2510
2511// Condition comparison types.
2512var ConditionComparison = {
2513	None: 0,
2514	GreaterThan: 1,
2515	LessThan: 2,
2516	EqualTo: 3,
2517	NotEqualTo: 4,
2518	Contains: 5,
2519	Available: 6,
2520	Unavailable: 7,
2521	Between: 8,
2522	getName: function (value) {
2523		for (var k in ConditionComparison) {
2524			if (ConditionComparison[k] == value)
2525				return k;
2526		}
2527
2528		return null;
2529	}
2530};
2531
2532// Condition runner action results.
2533var ConditionRunnerActResult = {
2534	None: 0,
2535	SuccessImplicit: 1,
2536	SuccessExplicit: 2,
2537	SkipDuplicate: 3,
2538	SkipContradiction: 4,
2539	getName: function (value) {
2540		for (var k in ConditionRunnerActResult) {
2541			if (ConditionRunnerActResult[k] == value)
2542				return k;
2543		}
2544
2545		return null;
2546	}
2547};
2548
2549////////// FieldCondition Class ///////////
2550
2551// Internal. Creates instance of FieldCondition class.
2552// * parent - Parent FieldConditions instance.
2553// * condInfo - Object containing properties {compType, compValue, actElemInput, actID, actType, actFieldType, actURL, actURLNewWindow}.
2554function FieldCondition(parent, condInfo) {
2555	if (parent == null)
2556		throw new Error.argNull("parent");
2557	else if (condInfo == null)
2558		throw new Error.argNull("condInfo");
2559
2560	$.extend(this, condInfo);
2561
2562	// Set instance parent.
2563	this.parent = parent;
2564
2565	// Process actElemInput argument.
2566	var actElemInput = this.actElemInput;
2567	this.actElemInputIsArray = $.isArray(actElemInput);
2568
2569	if (this.actElemInputIsArray) {
2570		for (var i = 0, len = actElemInput.length; i < len; i++) {
2571			if (typeof (actElemInput[i]) == "string")
2572				actElemInput[i] = document.getElementById(actElemInput[i]);
2573		}
2574
2575		if (actElemInput.length > 0 && actElemInput[0]) {
2576			this.actElemContainer = actElemInput[0].closest('.formFieldContainer');
2577			this.actElemInput = actElemInput;
2578		}
2579	}
2580	else {
2581		if (typeof (actElemInput) == "string")
2582			actElemInput = document.getElementById(actElemInput);
2583
2584		if (actElemInput) {
2585			this.actElemContainer = actElemInput.closest('.formFieldContainer');
2586			this.actElemInput = actElemInput;
2587		}
2588	}
2589
2590	// Get shared container.
2591	this.$actElemContainer = $(this.actElemContainer);
2592}
2593
2594// Static. Creates multiple instances from list of condition information.
2595FieldCondition.createMulti = function (parent, condList) {
2596	if (parent == null)
2597		throw new Error.argNull("parent");
2598	else if (condList == null)
2599		throw new Error.argNull("condList");
2600
2601	var ret = [];
2602
2603	for (var i = 0, len = condList.length; i < len; i++)
2604		ret.push(new FieldCondition(parent, condList[i]));
2605
2606	return ret;
2607}
2608
2609// Checks if condition is met and performs any necessary action.
2610// * updateUI - Determines if UI is updated after check. Treated as true if null/unspecified.
2611FieldCondition.prototype.check = function (updateUI) {
2612	// TODO: Handle checkboxes/radios/dropdowns when Akila gets that done on back-end.
2613	var thisValue = [];
2614	var compValue = this.compValue;
2615	var isMet = false;
2616
2617	switch (this.actFieldType) {
2618		case FieldTypes.Checkboxes:
2619			for (var i = 0, len = this.actElemInput.length; i < len; i++) {
2620				if (this.actElemInput[i].checked)
2621					thisValue.push(this.actElemInput[i].value.replace(/\,/g, '_comma_'));
2622			}
2623			break;
2624		case FieldTypes.RadioButtons:
2625			for (var i = 0, len = this.actElemInput.length; i < len; i++) {
2626				if (this.actElemInput[i].checked) {
2627					thisValue = [this.actElemInput[i].value];
2628					break;
2629				}
2630			}
2631			break;
2632		case FieldTypes.DateTime:
2633			var tempArray = new Array();
2634			for (var i = 0, len = this.actElemInput.length; i < len; i++) {
2635				tempArray.push([this.actElemInput[i].value])
2636			}
2637			thisValue = [tempArray.join(',')];
2638			break;
2639		default:
2640			thisValue = [this.actElemInput.value];
2641	}
2642
2643	var extractMoneyValue = function (v) {
2644		var stringBeginsWith = function (val, beginString) {
2645			return val.substring(0, beginString.length) === beginString
2646		}
2647		if (v && v != '') {
2648			if (stringBeginsWith(v, '$')) {
2649				return v.substring(1).replace(',', '');
2650			}
2651		}
2652		return v.replace(',', '');
2653	};
2654
2655
2656	var isNum = function (val) {
2657		var extracted = extractMoneyValue(val);
2658		return parseFloat(extracted) === +extracted;
2659	};
2660
2661	var checkAgainst = function (compValue) {
2662		switch (this.compType) {
2663			case ConditionComparison.GreaterThan:
2664				return thisValue.any(function (index) {
2665					if (isNum(this[index]) && isNum(HandleCharacters(compValue))) {
2666						return parseFloat(extractMoneyValue(this[index])) > parseFloat(extractMoneyValue(HandleCharacters(compValue)));
2667					}
2668					return false;
2669				});
2670			case ConditionComparison.LessThan:
2671				return thisValue.any(function (index) {
2672					if (isNum(this[index]) && isNum(HandleCharacters(compValue))) {
2673						return parseFloat(extractMoneyValue(this[index])) < parseFloat(extractMoneyValue(HandleCharacters(compValue)));
2674					}
2675					return false;
2676				});
2677			case ConditionComparison.EqualTo:
2678				if (this.actFieldType == FieldTypes.DateTime) {
2679					if (this.actIsDateTimeSpan) {
2680						return this.checkDateEqualSpan(thisValue[0], compValue);
2681					}
2682					else {
2683						return this.checkDateEqualTo(thisValue[0], compValue);
2684					}
2685				}
2686				else {
2687					return thisValue.any(function (index) {
2688						return (isNull(HandleCharacters(this[index]), '') == isNull(HandleCharacters(compValue), ''));
2689					});
2690				}
2691			case ConditionComparison.NotEqualTo:
2692				return thisValue.any(function (index) { return (isNull(this[index], '').toUpperCase() != isNull(HandleCharacters(compValue), '').toUpperCase()); });
2693				break;
2694			case ConditionComparison.Contains:
2695				var containsExp = new RegExp(RegExp.metaEscape(HandleCharacters(compValue)), 'i');
2696
2697				return thisValue.any(function (index) {
2698					return containsExp.test(this[index]);
2699				});
2700			case ConditionComparison.Unavailable:
2701				return this.$actElemContainer.hasClass('unavailable');
2702			case ConditionComparison.Available:
2703				return !this.$actElemContainer.hasClass('unavailable');
2704			case ConditionComparison.Between:
2705				return this.checkDateBetween(thisValue[0], compValue);
2706				return;
2707			case ConditionComparison.None:
2708			default:
2709				return true;
2710		}
2711	};
2712
2713	switch (this.actFieldType) {
2714		case FieldTypes.Checkboxes:
2715			var compArr = compValue.split(',');
2716
2717			isMet = true;
2718
2719			for (var i = 0, len = compArr.length; i < len; i++) {
2720
2721				if (!checkAgainst.call(this, compArr[i])) {
2722					isMet = false;
2723					break;
2724				}
2725			}
2726			break;
2727		default:
2728			isMet = checkAgainst.call(this, compValue);
2729	}
2730
2731	if (updateUI)
2732		this.doAction(isMet);
2733
2734	return isMet;
2735};
2736
2737// Performs changes to applying field based on condition action and comparison result.
2738FieldCondition.prototype.doAction = function (isMet) {
2739	var validator = (this.parent.validators ? this.parent.validators[this.parent.id] : null);
2740
2741	switch (this.actType) {
2742		case ConditionActions.Show:
2743			this.doActionShow(isMet, validator);
2744			break;
2745		case ConditionActions.Hide:
2746			this.doActionHide(isMet, validator);
2747			break;
2748		case ConditionActions.Require:
2749			this.doActionRequire(isMet, validator);
2750			break;
2751		case ConditionActions.RedirectTo:
2752			this.doActionRedirect(isMet, validator);
2753			break;
2754	}
2755};
2756
2757FieldCondition.prototype.doActionShow = function (isMet, validator) {
2758	if (isMet) {
2759		this.parent.$elemContainer.removeClass('hidden');
2760		this.parent.$elemContainer.removeClass('unavailable');
2761
2762		// Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed).
2763		repositionLineSiblings(getLineSiblings(this.parent.elemContainer));
2764
2765		if (this.parent.$elemContainer.find('label > span') != null && this.parent.$elemContainer.find('label > span').length > 0)
2766			$('#liRequiredFieldInfo').show();
2767
2768		if (validator)
2769			validator.enabled = true;
2770	}
2771	else {
2772		this.parent.$elemContainer.addClass('hidden');
2773		this.parent.$elemContainer.addClass('unavailable');
2774
2775		// Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed).
2776		repositionLineSiblings(getLineSiblings(this.parent.elemContainer));
2777
2778		if (validator)
2779			validator.enabled = false;
2780	}
2781};
2782
2783FieldCondition.prototype.doActionHide = function (isMet, validator) {
2784	if (isMet) {
2785		this.parent.$elemContainer.addClass('hidden');
2786		this.parent.$elemContainer.addClass('unavailable');
2787
2788		// Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed).
2789		repositionLineSiblings(getLineSiblings(this.parent.elemContainer));
2790
2791		if (validator)
2792			validator.enabled = false;
2793	}
2794	else {
2795		this.parent.$elemContainer.removeClass('hidden');
2796		this.parent.$elemContainer.removeClass('unavailable');
2797
2798		// Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed).
2799		repositionLineSiblings(getLineSiblings(this.parent.elemContainer));
2800
2801		if (validator)
2802			validator.enabled = true;
2803	}
2804};
2805
2806FieldCondition.prototype.doActionRequire = function (isMet, validator) {
2807	if (isMet) {
2808		this.parent.$elemReq.removeClass('hidden');
2809
2810		if (validator == null) {
2811			validator = new FieldValidator(this.parent.elemInput, this.parent.id, this.parent.fieldType, this.parent.isInternal, true, FieldValidation.Any);
2812			this.parent.validators.add(validator);
2813		}
2814
2815		this.parent.$elemContainer.find('label.labelForFieldElement > span').show();
2816		$('#liRequiredFieldInfo').show();
2817		if (validator)
2818			validator.isRequired = true;
2819	}
2820	else {
2821		this.parent.$elemReq.addClass('hidden');
2822		this.parent.$elemContainer.find('label.labelForFieldElement > span').hide();
2823
2824		if (validator)
2825			validator.isRequired = false;
2826	}
2827};
2828
2829FieldCondition.prototype.doActionRedirect = function (isMet, validator) {
2830	// TODO: Some point alter this to pass in references for submitRedirect/redirectNewWindow. Not ideal at moment to ref. global vars.
2831	var submitRedirect = document.getElementById('submitRedirect');
2832
2833	redirectNewWindow = null;
2834	submitRedirect.value = '';
2835
2836	if (isMet) {
2837		if (this.actURLNewWindow) {
2838			redirectNewWindow = this.actURL;
2839			submitRedirect.value = '';
2840		}
2841		else
2842			submitRedirect.value = this.actURL;
2843	}
2844};
2845
2846// Attachs event handler to field to monitor changes.
2847// * condRunner - FieldConditionsRunner instance to attach.
2848// Note: Calling more than once will have no effect.
2849FieldCondition.prototype.attachRunner = function (condRunner) {
2850	if (condRunner == null)
2851		throw new Error.argNull("condRunner");
2852
2853	// Do not try to bind this if it is an internal field condition and we are not running internal field
2854	// conditions.
2855	if (this.parent.isInternal && !condRunner.runInternal)
2856		return;
2857
2858	if ($.data(this.actElemInput, "runner_chghook") == null) {
2859		var changeHook = function (event) { 
2860			condRunner.run(true); 
2861			!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
2862		};
2863		$.data(this.actElemInput, "runner_chghook", changeHook);
2864		$(this.actElemInput).change(changeHook);
2865	}
2866};
2867
2868// Removes event handler that monitors changes.
2869// Note: Calling more than once will have no effect.
2870FieldCondition.prototype.detachRunner = function () {
2871	var changeHook = $.data(this.actElemInput, "runner_chghook");
2872
2873	if (changeHook) {
2874		$(this.actElemInput).unbind('change', changeHook);
2875		$.removeData(this.actElemInput, "runner_chghook");
2876	}
2877};
2878
2879FieldCondition.prototype.checkDateCommon = function (thisValue, compValue) {
2880	var defaultDate = '1/1/70';
2881	var defualtDateObj = new Date(defaultDate);
2882
2883	var compArr = compValue.split(',');
2884	var valueArr = thisValue.split(',');
2885
2886	var hasTimeOfDayStart = false;
2887	var hasTimeOfDayEnd = false;
2888
2889	var compareStartDate;
2890	var compareEndDate;
2891
2892	if (compArr[0] == '') {
2893		compareStartDate = new Date(defaultDate + ' ' + compArr[2]);
2894	}
2895	else {
2896		compareStartDate = new Date(compArr[0] + ' ' + compArr[2]);
2897	}
2898
2899	if (compArr[1] == '') {
2900		compareEndDate = new Date(defaultDate + ' ' + compArr[3]);
2901	}
2902	else {
2903		compareEndDate = new Date(compArr[1] + ' ' + compArr[3]);
2904	}
2905
2906	var validStartDate = new Date(defaultDate);
2907	var validEndDate = new Date(defaultDate);
2908	var timeOfDayStart = '';
2909	var timeOfDayEnd = '';
2910
2911	if (valueArr.length == 4) {
2912		if (valueArr[0] == '') {
2913			validStartDate = new Date(defaultDate + ' ' + valueArr[2]);
2914		}
2915		else {
2916			validStartDate = new Date(valueArr[0] + ' ' + valueArr[2]);
2917		}
2918
2919		timeOfDayStart = this.getAMPMString(valueArr[2]);
2920
2921		if (timeOfDayStart != '')
2922			hasTimeOfDayStart = true;
2923
2924		if (valueArr[1] == '') {
2925			validEndDate = new Date(defaultDate + ' ' + valueArr[3]);
2926		}
2927		else {
2928			validEndDate = new Date(valueArr[1] + ' ' + valueArr[3]);
2929		}
2930
2931		timeOfDayEnd = this.getAMPMString(valueArr[3]);
2932
2933		if (timeOfDayEnd != '')
2934			hasTimeOfDayEnd = true;
2935	}
2936	else if (valueArr.length == 2) {
2937		timeOfDayStart = this.getAMPMString(valueArr[0]);
2938
2939		if (timeOfDayStart == '') {
2940			if (valueArr[0] != '') {
2941				validStartDate = new Date(valueArr[0]);
2942				hasTimeOfDayStart = true;
2943			}
2944		}
2945		else {
2946			validStartDate = new Date(defaultDate + ' ' + valueArr[0]);
2947			hasTimeOfDayStart = true;
2948		}
2949
2950		timeOfDayEnd = this.getAMPMString(valueArr[1]);
2951
2952		if (timeOfDayEnd == '') {
2953			if (valueArr[1] != '') {
2954				validEndDate = new Date(valueArr[1]);
2955				hasTimeOfDayEnd = true;
2956			}
2957		}
2958		else {
2959			validEndDate = new Date(defaultDate + ' ' + valueArr[1]);
2960			hasTimeOfDayEnd = true;
2961		}
2962	}
2963
2964	return {
2965		validStartDate: validStartDate,
2966		compareStartDate: compareStartDate,
2967		compareEndDate: compareEndDate,
2968		hasTimeOfDayStart: hasTimeOfDayStart,
2969		validEndDate: validEndDate,
2970		hasTimeOfDayEnd: hasTimeOfDayEnd
2971	};
2972};
2973
2974FieldCondition.prototype.checkDateBetween = function (thisValue, compValue) {
2975	var checkDateCommonData = this.checkDateCommon(thisValue, compValue);
2976
2977	return (checkDateCommonData.validStartDate.getTime() >= checkDateCommonData.compareStartDate.getTime() && checkDateCommonData.validStartDate.getTime() <= checkDateCommonData.compareEndDate.getTime() && checkDateCommonData.hasTimeOfDayStart)
2978		&& (checkDateCommonData.validEndDate.getTime() <= checkDateCommonData.compareEndDate.getTime() && checkDateCommonData.validEndDate.getTime() >= checkDateCommonData.compareStartDate.getTime() && checkDateCommonData.hasTimeOfDayEnd);
2979};
2980
2981FieldCondition.prototype.checkDateEqualSpan = function (thisValue, compValue) {
2982	var checkDateCommonData = this.checkDateCommon(thisValue, compValue);
2983
2984	return (checkDateCommonData.validStartDate.getTime() == checkDateCommonData.compareStartDate.getTime() && checkDateCommonData.hasTimeOfDayStart)
2985		&& (checkDateCommonData.validEndDate.getTime() == checkDateCommonData.compareEndDate.getTime() && checkDateCommonData.hasTimeOfDayEnd);
2986};
2987
2988FieldCondition.prototype.checkDateEqualTo = function (thisValue, compValue) {
2989	var defaultDate = '1/1/70';
2990	var defualtDateObj = new Date(defaultDate);
2991
2992	var compArr = compValue.split(',');
2993	var valueArr = thisValue.split(',');
2994
2995	var compareDate;
2996
2997	var hasTimeOfDay = false;
2998
2999	if (compArr[0] == '') {
3000		compareDate = new Date(defaultDate + ' ' + compArr[2]);
3001	}
3002	else {
3003		compareDate = new Date(compArr[0] + ' ' + compArr[2]);
3004	}
3005
3006	var validDate = new Date(defaultDate);
3007	var timeOfDay = '';
3008
3009	if (valueArr.length == 1) {
3010		timeOfDay = this.getAMPMString(valueArr[0]);
3011
3012		if (timeOfDay == '') {
3013			if (valueArr[0] != '') {
3014				validDate = new Date(valueArr[0]);
3015				hasTimeOfDay = true;
3016			}
3017		}
3018		else {
3019			hasTimeOfDay = true;
3020			validDate = new Date(valueArr[0]);
3021		}
3022	}
3023	else if (valueArr.length == 2) {
3024		timeOfDay = this.getAMPMString(valueArr[1]);
3025
3026		if (timeOfDay != '')
3027			hasTimeOfDay = true;
3028
3029		if (valueArr[0] != '') {
3030			validDate = new Date((valueArr[0] + ' ' + valueArr[1] + ' ').replace(/\-/g, '/'));
3031		}
3032		else {
3033			validDate = new Date((defaultDate + ' ' + valueArr[0] + ' ').replace(/\-/g, '/'));
3034		}
3035	}
3036
3037	return validDate.getTime() == compareDate.getTime() && hasTimeOfDay;
3038};
3039
3040FieldCondition.prototype.getAMPMString = function (time) {
3041	var amIndex = time.indexOf('AM');
3042	var pmIndex = time.indexOf('PM');
3043
3044	var timeOfDay = '';
3045
3046	// First see if the time is military time... if so we need to convert it to AM/PM so the rest
3047	// of this function will return the proper value.
3048
3049	if (amIndex === -1 && pmIndex === -1) {
3050		time = militaryTimeToStandard(time);
3051		amIndex = time.indexOf('AM');
3052		pmIndex = time.indexOf('PM');
3053	}
3054
3055	if (amIndex != -1) {
3056		timeOfDay = time.substring(amIndex).replace(" ", "");
3057	}
3058	else if (pmIndex != -1) {
3059		timeOfDay = time.substring(pmIndex).replace(" ", "");
3060	}
3061
3062	return timeOfDay;
3063};
3064
3065function militaryTimeToStandard(militaryTime) {
3066	var colonIndex = militaryTime.indexOf(':');
3067	var hours = '';
3068	var min = '';
3069
3070	if (colonIndex === -1) {
3071		hours = militaryTime.substring(0, 2);
3072		min = militaryTime.substring(2, 4);
3073	} else {
3074		var tokens = militaryTime.split(':');
3075		hours = tokens[0];
3076		min = tokens[1];
3077	}
3078
3079	if (hours < 12) {
3080		ampm = 'AM';
3081	} else {
3082		hours = hours - 12;
3083		ampm = 'PM';
3084	}
3085
3086	return parseInt(hours) + ':' + ('00' + min).slice('-2') + ' ' + ampm; ß
3087}
3088
3089
3090////////// FieldConditions Class //////////
3091
3092// Creates instance of FieldConditions class.
3093// * elemInput - Reference to DOM element of field with condition. May be a DOM element or string containing ID. May also be array of DOM elements or IDs. Multiple elements must share same parent.
3094// * id - The ID of the field with the condition.
3095// * fieldType - The field type of the field with the condition.
3096// * isInternal - Whether or not this condition is for internal-only fields.
3097// * condList - List (array) of zero or more condition clauses for the field.
3098// * validators - Validator dictionary.
3099function FieldConditions(options, validators) {
3100	if (options.elemInput == null)
3101		throw new Error.argNull("options.elemInput");
3102	else if (options.id == null)
3103		throw new Error.argNull("id");
3104	else if (options.fieldType == null)
3105		throw new Error.argNull("options.fieldType");
3106	else if (options.condList == null)
3107		throw new Error.argNull("options.condList");
3108	else if (validators == null)
3109		throw new Error.argNull("validators");
3110
3111	this.elemInputIsArray = $.isArray(options.elemInput);
3112
3113	// Process elemInput argument.
3114	if (this.elemInputIsArray) {
3115		for (var i = 0, len = options.elemInput.length; i < len; i++) {
3116			if (typeof (options.elemInput[i]) == "string")
3117				options.elemInput[i] = document.getElementById(options.elemInput[i]);
3118		}
3119
3120		if (options.elemInput.length > 0 && options.elemInput[0]) {
3121			this.elemContainer = options.elemInput[0].closest('.formFieldContainer');
3122			this.elemInput = options.elemInput;
3123		}
3124	}
3125	else {
3126		if (typeof (options.elemInput) == "string")
3127			options.elemInput = document.getElementById(options.elemInput);
3128
3129		if (options.elemInput) {
3130			this.elemContainer = options.elemInput.closest('.formFieldContainer');
3131			this.elemInput = options.elemInput;
3132		}
3133	}
3134
3135	this.validators = validators;
3136	this.elemReq = document.getElementById('r_' + options.id);
3137	this.$elemContainer = $(this.elemContainer);
3138	this.$elemReq = $(this.elemReq);
3139	this.fieldType = options.fieldType;
3140	this.id = options.id;
3141	this.isInternal = options.isInternal;
3142	this.conditions = FieldCondition.createMulti(this, options.condList);
3143}
3144
3145// Static. Creates a single conditions object from an options object.
3146FieldConditions.createSingle = function (options, validators) {
3147	if (options == null)
3148		throw new Error.argNull("options");
3149	else if (validators == null)
3150		throw new Error.argNull("validators");
3151
3152	return new FieldConditions(
3153		options,
3154		validators);
3155};
3156
3157// Static. Creates dictionary of conditions objects from array of option objects.
3158FieldConditions.createMulti = function (optionsArray, validators) {
3159	if (optionsArray == null)
3160		throw new Error.argNull("optionsArray");
3161	else if (validators == null)
3162		throw new Error.argNull("validators");
3163
3164	var ret = { keys: [] };
3165	var key = null;
3166
3167	for (var i = 0, len = optionsArray.length; i < len; i++) {
3168		if (optionsArray[i].id != null) {
3169			key = optionsArray[i].id;
3170			ret[key] = FieldConditions.createSingle(optionsArray[i], validators);
3171			ret.keys.push(key);
3172		}
3173	}
3174
3175	return ret;
3176};
3177
3178// Attachs event handler to field to monitor changes.
3179// * condRunner - FieldConditionsRunner instance to attach.
3180// Note: Calling more than once will have no effect.
3181FieldConditions.prototype.attachRunner = function (condRunner) {
3182	if (condRunner == null)
3183		throw new Error.argNull("condRunner");
3184
3185	for (var i = 0, len = this.conditions.length; i < len; i++)
3186		this.conditions[i].attachRunner(condRunner);
3187};
3188
3189// Removes event handler that monitors changes.
3190// Note: Calling more than once will have no effect.
3191FieldConditions.prototype.detachRunner = function () {
3192	for (var i = 0, len = this.conditions.length; i < len; i++)
3193		this.conditions[i].detachRunner();
3194};
3195
3196////////// FieldConditionsRunner Class ////
3197
3198// Creates new instance of conditions runners for the field conditions objects provided.
3199// * allFieldConditions - Array of FieldConditions objects to execute checks for.
3200// * doneCallback - Method executed when run() completes. Recieves log information as argument.
3201// * autoAttach - Determines if allFieldConditions are automatically attached to this instance by the constructor. If null/unspecified, treated as true.
3202// * runInternal - Set to true to execute internal-only fields.
3203function FieldConditionsRunner(allFieldConditions, doneCallback, autoAttach, runInternal) {
3204	if (allFieldConditions == null)
3205		throw new Error.argNull("allFieldConditions");
3206	else if (doneCallback == null)
3207		throw new Error.argNull("doneCallback");
3208
3209	this.fieldConditions = allFieldConditions;
3210	this.doneCallback = doneCallback;
3211	this.runInternal = runInternal;
3212
3213	if (autoAttach == null || autoAttach)
3214		this.attachConditions();
3215}
3216
3217// Static. Internal. Creates static information object for state tracking during execution.
3218FieldConditionsRunner.createFieldStateInfo = function () {
3219	var ret = {};
3220
3221	ret[ConditionActions.None] = ConditionRunnerActResult.None;
3222	ret[ConditionActions.Show] = ConditionRunnerActResult.None;
3223	ret[ConditionActions.Hide] = ConditionRunnerActResult.None;
3224	ret[ConditionActions.Require] = ConditionRunnerActResult.None;
3225	ret[ConditionActions.RedirectTo] = ConditionRunnerActResult.None;
3226
3227	return ret;
3228};
3229
3230// Attaches to FieldConditions/FieldCondition objects that runner will use.
3231FieldConditionsRunner.prototype.attachConditions = function () {
3232	for (var i = 0, iLen = this.fieldConditions.keys.length; i < iLen; i++)
3233		this.fieldConditions[this.fieldConditions.keys[i]].attachRunner(this);
3234};
3235
3236// Detaches to FieldConditions/FieldCondition objects that runner may have used.
3237FieldConditionsRunner.prototype.detachConditions = function () {
3238	for (var i = 0, iLen = this.fieldConditions.keys.length; i < iLen; i++)
3239		this.fieldConditions[this.fieldConditions.keys[i]].detachRunner(this);
3240};
3241
3242// Internal. Cleans up runner for use again.
3243FieldConditionsRunner.prototype.cleanup = function () {
3244	if (this.fieldConditionResults != null) {
3245		delete this.fieldConditionResults;
3246		delete this.fieldConditionLog;
3247	}
3248};
3249
3250// Internal. Called by run() to log result for each condition.
3251FieldConditionsRunner.prototype.log = function (id, condIndex, actType, result, isMet, needRestart) {
3252	if (condRunnerHideLogDupes && result == ConditionRunnerActResult.SkipDuplicate)
3253		return;
3254
3255	this.fieldConditionLog.push({
3256		id: id,
3257		condIndex: condIndex,
3258		actType: actType,
3259		result: result,
3260		isMet: isMet,
3261		needRestart: needRestart
3262	});
3263};
3264
3265// Internal. Sets whether or not the condition engine is executing.
3266FieldConditionsRunner.prototype.setRunning = function (value) {
3267	if (arguments.length == 0)
3268		this.isRunning = true;
3269	else
3270		this.isRunning = value;
3271};
3272
3273// Gets whether or not the condition engine is executing.
3274FieldConditionsRunner.prototype.getRunning = function () {
3275	return this.isRunning;
3276};
3277
3278// Internal. Tells condition execution task to reset cycle (done whenever something changes).
3279FieldConditionsRunner.prototype.setRestart = function (value) {
3280	if (arguments == null || arguments.length == 0)
3281		this.needRestart = true;
3282	else
3283		this.needRestart = value;
3284};
3285
3286// Internal. Gets whether or not the runner needs to restart.
3287FieldConditionsRunner.prototype.getRestart = function () {
3288	return this.needRestart;
3289};
3290
3291// Launches execution of conditions.
3292// * fromEvent - Should be set to true when triggerd from DOM events.
3293// NOTE: fromEvent may be a race condition but will be left for now while debugging (to simplify logs).
3294FieldConditionsRunner.prototype.run = function (fromEvent) {
3295	if (this.getRunning() && !fromEvent)
3296		this.setRestart();
3297	else {
3298		this.setRunning();
3299
3300		this.cleanup();
3301
3302		this.runTask();
3303
3304		this.doneCallback(this.fieldConditionLog);
3305
3306		this.setRunning(false);
3307	}
3308};
3309
3310// Internal. Executes conditions. Should not be called while already running, so run() has sync mechanism for this.
3311// * runInternal - If true, conditions for internal conditions will be ran.
3312FieldConditionsRunner.prototype.runTask = function (runInternal) {
3313	var needRestart = true;
3314
3315	// Prepare objects for execution.
3316	this.fieldConditionLog = [];
3317	this.fieldConditionResults = [];
3318	this.setRedirectUrl = false;
3319
3320	// Populate state information.
3321	var condCondList = this.fieldConditions;
3322	var condListInfo = null;
3323	var condList = null;
3324
3325	for (var i = 0, iLen = condCondList.keys.length; i < iLen; i++)
3326		this.fieldConditionResults[condCondList[condCondList.keys[i]].id] = FieldConditionsRunner.createFieldStateInfo();
3327
3328	// Number of restart actions (tracked to eventually kill infinite loops that should not happen).
3329	var numRestarts = 0;
3330
3331	// Start/restart actions.
3332	while (needRestart && (++numRestarts < 65535)) {
3333		needRestart = false;
3334		this.setRestart(needRestart);
3335
3336		for (var i = 0, iLen = condCondList.keys.length; i < iLen && !needRestart; i++) {
3337			condListInfo = condCondList[condCondList.keys[i]];
3338
3339			// If we are on the front-end, we don't want to have anything to do with back-end (internal only)
3340			// conditions, they will cause javascript errors.
3341			if (!this.runInternal && condListInfo.isInternal == true)
3342				continue;
3343
3344			if (condListInfo.isInternal == false && document.URL.indexOf("Admin") > -1 && document.URL.indexOf("Submissions") > -1)
3345				continue;
3346
3347			condList = condListInfo.conditions;
3348
3349			for (var j = 0, jLen = condList.length; j < jLen && !needRestart; j++) {
3350				var cond = condList[j];
3351				var results = this.fieldConditionResults[condListInfo.id];
3352				var logState = ConditionRunnerActResult.None;
3353
3354				// Determine whether or not condition is met.
3355				var isMet = cond.check(false);
3356
3357				// This is because the effective action for a failed explicit Show is an implicit Hide (and vice versa).
3358				var effectiveActType = cond.actType;
3359
3360				// Determine if action is contradictory or duplicate and skip UI changes if so.
3361				switch (effectiveActType) {
3362					case ConditionActions.Show:
3363						// Show can collide with hide.
3364						var resultsHide = results[ConditionActions.Hide];
3365
3366						if (resultsHide == ConditionRunnerActResult.SuccessExplicit || (resultsHide == ConditionRunnerActResult.SuccessImplicit && !isMet))
3367							logState = ConditionRunnerActResult.SkipContradiction;
3368
3369						break;
3370					case ConditionActions.Hide:
3371						// Hide can collide with show.
3372						var resultsShow = results[ConditionActions.Show];
3373
3374						if (resultsShow == ConditionRunnerActResult.SuccessExplicit || (resultsShow == ConditionRunnerActResult.SuccessImplicit && !isMet))
3375							logState = ConditionRunnerActResult.SkipContradiction;
3376
3377						break;
3378					case ConditionActions.RedirectTo:
3379						// Redirects work differently since they span all fields. Extra contradiction detection needed.
3380						if (this.setRedirectUrl)
3381							logState = ConditionRunnerActResult.SkipContradiction;
3382						else if (isMet)
3383							this.setRedirectUrl = true;
3384						break;
3385				}
3386
3387				if (isMet && results[effectiveActType] == ConditionRunnerActResult.SuccessExplicit)
3388					logState = ConditionRunnerActResult.SkipDuplicate;
3389				else if (!isMet && results[effectiveActType] == ConditionRunnerActResult.SuccessExplicit)
3390					logState = ConditionRunnerActResult.SkipContradiction;
3391				else if (!isMet && results[effectiveActType] == ConditionRunnerActResult.SuccessImplicit)
3392					logState = ConditionRunnerActResult.SkipDuplicate;
3393
3394				if (logState == ConditionRunnerActResult.None) {
3395					// Determine log result.
3396					logState = isMet ? ConditionRunnerActResult.SuccessExplicit : ConditionRunnerActResult.SuccessImplicit;
3397
3398					// Update running state info.
3399					results[effectiveActType] = logState;
3400
3401					// Update form UI.
3402					cond.doAction(isMet);
3403
3404					// Restart task.
3405					this.setRestart();
3406					needRestart = true;
3407				}
3408
3409				// Log result.
3410				this.log(condListInfo.id, j, cond.actType, logState, isMet, needRestart);
3411
3412				// Some other action could cause this to get set.
3413				if (this.getRestart())
3414					needRestart = true;
3415			}
3416		}
3417	}
3418
3419	if (numRestarts >= 65535)
3420		console[console.warn ? "warn" : "log"]("Infinite Loop (heuristic-detected) in FieldConditionsRunner.runTask()");
3421};
3422;
3423// This file contains javascript functions for the "External Integration" field type.
3424// REQUIREMENTS:
3425//		FieldSetDesigner.js
3426
3427// These values must match the values provided in CivicPlus.Entities.Modules.FormCenter.Enums.ExternalIntegrationCommandType
3428var ExternalIntegrationCommandType = {
3429	XMLCommand: 1,
3430	EchoCommand: 2
3431};
3432
3433// Closure containing the client-side functionality for the External Integration field type.
3434// * externalIntegrationClass - The name of the CSS class that is used to identify external integration buttons.
3435function ExternalIntegration(externalIntegrationClassname, fieldSetDesigner) {
3436	this.cssClassname = externalIntegrationClassname;
3437	this.fieldSetDesigner = fieldSetDesigner;
3438
3439	this.hookEvents = function () {
3440		var my = this;
3441		if (my.cssClassname == null)
3442			throw 'No external integration button CSS class name has been specified.'
3443
3444		$('.' + this.cssClassname).click(function (event) {
3445			var fieldID = null;
3446			fieldID = event.target.id;
3447			my.sendRequest(fieldID);
3448		});
3449	};
3450
3451	this.sendRequest = function (fieldID) {
3452		var params = this.getFormData();
3453        var formID = $('#Item_FormID').val();
3454        var intFieldID = fieldID.replace('e_', '');
3455
3456		var my = this;
3457		var sendData = {
3458		    id: formID,
3459		    fieldID: intFieldID,
3460			parameters: JSON.stringify(params)
3461		};
3462
3463		$.ajax({
3464			type: 'post',
3465			url: '/FormCenter/ExecuteExternalIntegrationCommand',
3466			data: sendData,
3467			success: function (data) { my.updateFormData(data, my); },
3468			error: function (xhr, status, error) { alert('An error occurred while sending the request.'); },
3469		    beforeSend: function () {
3470		        ajaxPostBackStart('Loading');
3471		    },
3472	        complete: function () {
3473		        ajaxPostBackEnd();
3474		    }
3475		});
3476	};
3477
3478	// This function takes the values from the JSON result and sets the appropriate
3479	// form field values.
3480	this.updateFormData = function (responseFromController, externalIntegrationObject) {
3481
3482		if (responseFromController.error) {
3483			alert('An error occurred: ' + responseFromController.errorMessage);
3484			return;
3485		}
3486
3487		for (var i = 0, len = responseFromController.length; i < len; i++) {
3488			var param = responseFromController[i];
3489			switch (responseFromController[i].Type) {
3490				case FieldTypes.ShortAnswer:
3491					externalIntegrationObject.updateShortAnswerField(param);
3492					break;
3493				case FieldTypes.LongAnswer:
3494					externalIntegrationObject.updateLongAnswerField(param);
3495					break;
3496				case FieldTypes.Checkboxes:
3497					externalIntegrationObject.updateCheckboxField(param);
3498					break;
3499				case FieldTypes.Dropdown:
3500					externalIntegrationObject.updateDropdownField(param);
3501					break;
3502				case FieldTypes.Password:
3503					externalIntegrationObject.updatePasswordField(param);
3504					break;
3505				case FieldTypes.RadioButtons:
3506					externalIntegrationObject.updateRadioField(param);
3507					break;
3508			    case FieldTypes.DateTime:
3509			        externalIntegrationObject.updateDateField(param);
3510			        break;
3511			}
3512		}
3513	}
3514
3515	this.getFormData = function () {
3516		var params = [];
3517
3518		this.appendShortAnswerFields(params);
3519		this.appendCheckboxFields(params);
3520		this.appendRadioFields(params);
3521		this.appendDropdowns(params);
3522		this.appendTextAreaFields(params);
3523		this.appendPasswordFields(params);
3524
3525		return params;
3526	};
3527
3528	this.createFieldValue = function (fieldID, type, value, externalName) {
3529		return { ID: fieldID, Type: type, Value: value, ExternalName: externalName};
3530	};
3531
3532	this.appendShortAnswerFields = function (params) {
3533		var my = this;
3534		$('input[id^="e_"][type="text"]').each(function (i) {
3535			var element = $(this);
3536			params.push(my.createFieldValue(element.attr('id'), FieldTypes.ShortAnswer, element.val(), my.externalSubmissionName(element)));
3537		});
3538	};
3539
3540	this.externalSubmissionName = function($element) {
3541	    return $element.parents('*[data-external-submission-name]').attr('data-external-submission-name');
3542	};
3543
3544	this.updateShortAnswerField = function (param) {
3545		var id = param.ID;
3546		$('#' + param.ID).val(param.Value);
3547	}
3548
3549	this.appendCheckboxFields = function (params) {
3550		var my = this;
3551		$('input[id^="e_"][type="checkbox"]').each(function (index) {
3552			var item = $(this);
3553			var value = item.attr('value');
3554			var checked = false;
3555			if (item.is(':checked')) {
3556			    checked = true;
3557			}
3558			var fieldValue = my.createFieldValue(item.attr('id'), FieldTypes.Checkboxes, value, my.externalSubmissionName(item));
3559			fieldValue.Checked = checked;
3560			params.push(fieldValue);
3561		});
3562	};
3563
3564	this.updateCheckboxField = function (param) {
3565		var id = param.ID;
3566		if (param.Checked == true)
3567			$('#' + param.ID).prop('checked', true);
3568		else
3569			$('#' + param.ID).prop('checked', false);
3570	};
3571
3572	this.appendRadioFields = function (params) {
3573		var my = this;
3574		$('input[id^="e_"][type="radio"]').each(function (index) {
3575			var item = $(this);
3576			var value = item.attr('value');
3577			var checked = false;
3578			if (item.is(':checked')) {
3579			    checked = true;
3580			}
3581			var fieldValue = my.createFieldValue(item.attr('id'), FieldTypes.RadioButtons, value, my.externalSubmissionName(item));
3582			fieldValue.Checked = checked;
3583
3584			params.push(fieldValue);
3585		});
3586	};
3587
3588	this.updateRadioField = function (param) {
3589	    var id = param.ID;
3590	    var value = param.Value;
3591	    var button = $('input[id^="' + id + '"][value="' + value + '"]');
3592	    button.prop('checked', true);
3593	};
3594
3595	this.appendDropdowns = function (params) {
3596		var my = this;
3597		$('select[id^="e_"]').each(function (index) {
3598			var item = $(this);
3599			params.push(my.createFieldValue(item.attr('id'), FieldTypes.Dropdown, item.val(), my.externalSubmissionName(item)));
3600		});
3601	};
3602
3603	this.updateDropdownField = function (param) {
3604		$('#' + param.ID).val(param.Value);
3605	}
3606
3607	this.appendTextAreaFields = function (params) {
3608		var my = this;
3609		$('textarea[id^="e_"]').each(function (index) {
3610			var element = $(this);
3611
3612			if (element.closest('li.LongAnswer').length > 0) {
3613				params.push(my.createFieldValue(element.attr('id'), FieldTypes.LongAnswer, element.text(), my.externalSubmissionName(element)));
3614			}
3615		});
3616	};
3617
3618	this.updateLongAnswerField = function (param) {
3619		$('#' + param.ID).text(param.Value);
3620	}
3621
3622	this.appendPasswordFields = function (params) {
3623		var my = this;
3624		$('input[id^="e_"][type="password"').each(function (i) {
3625			var element = $(this);
3626			params.push(my.createFieldValue(element.attr('id'), FieldTypes.Password, element.val(), my.externalSubmissionName(element)));
3627		});
3628	}
3629
3630	this.updatePasswordField = function (param) {
3631		// We don't want to update password information with values returned from the remote service.
3632		return;
3633	}
3634
3635	this.updateDateField = function (param) {
3636	    var my = this;
3637	    var isSpan = (param.Value.indexOf(',') >= 0);
3638
3639	    var dateID = param.ID + '_date';
3640	    var timeID = param.ID + '_time';
3641	    var endDateID = param.ID + '_dateSpan';
3642	    var endTimeID = param.ID + '_timeSpan';
3643        
3644	    var updateDate = function (selector, val) {
3645	        var datePicker = $('#' + selector);
3646	        if (datePicker.length == 0)
3647	            return;
3648
3649	        datePicker.data('tDatePicker').value(val);
3650	    };
3651
3652	    var updateTime = function (selector, val) {
3653	        var timePicker = $('#' + selector);
3654	        if (timePicker.length == 0)
3655	            return;
3656	        timePicker.data('tTimePicker').value(val);
3657	    };
3658
3659	    if (isSpan) {
3660	        var dateArray = param.Value.split(',');
3661	        var startDate = new Date(dateArray[0]);
3662	        var endDate = new Date(dateArray[1]);
3663            
3664	        updateDate(dateID, startDate);
3665	        updateTime(timeID, startDate);
3666
3667	        updateDate(endDateID, endDate);
3668	        updateTime(endTimeID, endDate);
3669	    } else {
3670	        var startDate = new Date(param.Value);
3671	        updateDate(dateID, startDate);
3672	        updateTime(timeID, startDate);
3673	    }
3674	}
3675};
3676
3677// Calls subroutines used to initialize the ExternalIntegration javascript object.
3678ExternalIntegration.prototype.initialize = function () {
3679	this.hookEvents();
3680};
3681
3682var CP_DynamicForm_ExternalSubmissionFE = function($) {
3683
3684	var self = this;
3685
3686
3687	self.getExternalSubmissionData = function() {
3688		var $fieldContainers = $('.formFieldContainer');
3689		var data = {};
3690		$fieldContainers.each(function (index) {
3691			var $container = $(this);
3692			var externalSubmissionName = self.getExternalSubmissionName($container);
3693			var singleFieldData = self.getSingleFieldData($container);
3694			if (singleFieldData != null) {
3695				data[externalSubmissionName] = self.getSingleFieldData($container);
3696			}
3697		});
3698		return data;
3699	};
3700
3701	self.getExternalSubmissionName = function($container) {
3702		return $container.attr('data-external-submission-name');
3703	};
3704
3705	self.getSingleFieldData = function($container) {
3706		var self = this;
3707		var result = null;
3708		if ($container.hasClass('ShortAnswer')) {
3709			result = self.submissionDataShortAnswer($container);
3710		} else if ($container.hasClass('LongAnswer')) {
3711			result = self.submissionDataLongAnswer($container);
3712		} else if ($container.hasClass('Checkboxes')) {
3713			result = self.submissionDataCheckboxes($container);
3714		} else if ($container.hasClass('Dropdown')) {
3715			result = self.submissionDataDropdown($container);
3716		} else if ($container.hasClass('RadioButtons')) {
3717			result = self.submissionDataRadioButtons($container);
3718		} else if ($container.hasClass('ReplyEmail')) {
3719			result = self.submissionDataReplyEmail($container);
3720		} else if ($container.hasClass('DateTime')) {
3721			result = self.submissionDataDateTime($container);
3722		} else if ($container.hasClass('DateTimeSpan')) {
3723			result = self.submissionDataDateTimeSpan($container);
3724		} else if ($container.hasClass('Password')) {
3725			result = self.submissionDataPassword($container);
3726		}
3727
3728		return result;
3729	};
3730
3731	//
3732	// There needs to be one function for each type of field that will be passed to 
3733	// an external submission service.
3734	//
3735
3736	self.submissionDataShortAnswer = function($container) {
3737		return $('input[id^=e_]', $container).val();
3738	};
3739
3740	self.submissionDataLongAnswer = function($container) {
3741		return $('textarea', $container).html();
3742	};
3743
3744	self.submissionDataPassword = function($container) {
3745		return $('input[id^=e_]', $container).val();
3746	};
3747
3748	self.submissionDataCheckboxes = function($container) {
3749		var result = [];
3750		var $checkedItems = $('input[type=checkbox]:checked', $container);
3751		$checkedItems.each(function (index) {
3752			result.push($(this).attr('Value'));
3753		});
3754		return result.join();
3755	};
3756
3757	self.submissionDataDropdown = function($container) {
3758		return $('select option:selected').val();
3759	};
3760
3761	self.submissionDataRadioButtons = function($container) {
3762		return $('input:radio:checked', $container).val();
3763	};
3764
3765	self.submissionDataReplyEmail = function($container) {
3766		return $('input[id^=e_]', $container).val();
3767	};
3768
3769	self.isDateTimeValueEmpty = function(val) {
3770		// The telerik controls sometimes have just "0" if the user does not
3771		// enter a value, so this function checks for both an empty value and for "0"
3772		if (val == null || val == '' || val == '0')
3773			return true;
3774		else
3775			return false;
3776	};
3777
3778	self.submissionDataDateTime = function($container) {
3779		var $dateDiv = $('div.date', $container).not('.time')[0];
3780		var $timeDiv = $('div.time', $container)[0];
3781
3782		var date = $('input', $dateDiv).val();
3783		var time = $('input', $timeDiv).val();
3784
3785		return formatDateTime(date, time);
3786	};
3787
3788	self.formatDateTime = function(date, time) {
3789		var dateEmpty = isDateTimeValueEmpty(date);
3790		var timeEmpty = isDateTimeValueEmpty(time);
3791
3792		if (dateEmpty && timeEmpty)
3793			return '';
3794		else if (!dateEmpty && timeEmpty)
3795			return date;
3796		else if (dateEmpty && !timeEmpty)
3797			return time;
3798		else
3799			return date + ' ' + time;
3800	}
3801
3802	self.submissionDataDateTimeSpan = function($container) {
3803		var $startDateDiv = $('div.date', $container).not('.time')[0];
3804		var $startTimeDiv = $('div.date.time', $container)[0];
3805		var startDate = $('input', $startDateDiv).val();
3806		var startTime = $('input', $startTimeDiv).val();
3807
3808		var $endDateDiv = $('div.date', $container).not('.time')[1];
3809		var $endTimeDiv = $('div.date.time', $container)[1];
3810		var endDate = $('input', $endDateDiv).val();
3811		var endTime = $('input', $endTimeDiv).val();
3812
3813		var startDateTime = formatDateTime(startDate, startTime);
3814		var endDateTime = formatDateTime(endDate, endTime);
3815
3816		if (startDateTime == '' && endDateTime == '')
3817			return '';
3818
3819		return startDateTime + ',' + endDateTime;
3820	};
3821
3822
3823	return self;
3824}(jQuery);
3825
3826
3827var CP_DynamicForm_PrintSubmission = (function () {
3828	self.StatusCodes = {
3829		Loading: 1,
3830		Success: 2,
3831		Spam: 3,
3832		Timeout: 4
3833	};	
3834
3835	return self;
3836})();
3837/*
3838 * ----------------------------- JSTORAGE -------------------------------------
3839 * Simple local storage wrapper to save data on the browser side, supporting
3840 * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
3841 *
3842 * Copyright (c) 2010 - 2012 Andris Reinman, [email protected]
3843 * Project homepage: www.jstorage.info
3844 *
3845 * Licensed under MIT-style license:
3846 *
3847 * Permission is hereby granted, free of charge, to any person obtaining a copy
3848 * of this software and associated documentation files (the "Software"), to deal
3849 * in the Software without restriction, including without limitation the rights
3850 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3851 * copies of the Software, and to permit persons to whom the Software is
3852 * furnished to do so, subject to the following conditions:
3853 *
3854 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3855 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3856 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3857 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3858 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3859 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3860 * SOFTWARE.
3861 */
3862
3863(function () {
3864    var
3865        /* jStorage version */
3866        JSTORAGE_VERSION = "0.4.3",
3867
3868        /* detect a dollar object or create one if not found */
3869        $ = window.jQuery || window.$ || (window.$ = {}),
3870
3871        /* check for a JSON handling support */
3872        JSON = {
3873            parse:
3874                window.JSON && (window.JSON.parse || window.JSON.decode) ||
3875                String.prototype.evalJSON && function (str) { return String(str).evalJSON(); } ||
3876                $.parseJSON ||
3877                $.evalJSON,
3878            stringify:
3879                Object.toJSON ||
3880                window.JSON && (window.JSON.stringify || window.JSON.encode) ||
3881                $.toJSON
3882        };
3883
3884    // Break if no JSON support was found
3885    if (!JSON.parse || !JSON.stringify) {
3886        throw new Error("No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page");
3887    }
3888
3889    var
3890        /* This is the object, that holds the cached values */
3891        _storage = { __jstorage_meta: { CRC32: {} } },
3892
3893        /* Actual browser storage (localStorage or globalStorage['domain']) */
3894        _storage_service = { jStorage: "{}" },
3895
3896        /* DOM element for older IE versions, holds userData behavior */
3897        _storage_elm = null,
3898
3899        /* How much space does the storage take */
3900        _storage_size = 0,
3901
3902        /* which backend is currently used */
3903        _backend = false,
3904
3905        /* onchange observers */
3906        _observers = {},
3907
3908        /* timeout to wait after onchange event */
3909        _observer_timeout = false,
3910
3911        /* last update time */
3912        _observer_update = 0,
3913
3914        /* pubsub observers */
3915        _pubsub_observers = {},
3916
3917        /* skip published items older than current timestamp */
3918        _pubsub_last = +new Date(),
3919
3920        /* Next check for TTL */
3921        _ttl_timeout,
3922
3923        /**
3924         * XML encoding and decoding as XML nodes can't be JSON'ized
3925         * XML nodes are encoded and decoded if the node is the value to be saved
3926         * but not if it's as a property of another object
3927         * Eg. -
3928         *   $.jStorage.set("key", xmlNode);        // IS OK
3929         *   $.jStorage.set("key", {xml: xmlNode}); // NOT OK
3930         */
3931        _XMLService = {
3932
3933            /**
3934             * Validates a XML node to be XML
3935             * based on jQuery.isXML function
3936             */
3937            isXML: function (elm) {
3938                var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
3939                return documentElement ? documentElement.nodeName !== "HTML" : false;
3940            },
3941
3942            /**
3943             * Encodes a XML node to string
3944             * based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
3945             */
3946            encode: function (xmlNode) {
3947                if (!this.isXML(xmlNode)) {
3948                    return false;
3949                }
3950                try { // Mozilla, Webkit, Opera
3951                    return new XMLSerializer().serializeToString(xmlNode);
3952                } catch (E1) {
3953                    try {  // IE
3954                        return xmlNode.xml;
3955                    } catch (E2) { }
3956                }
3957                return false;
3958            },
3959
3960            /**
3961             * Decodes a XML node from string
3962             * loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
3963             */
3964            decode: function (xmlString) {
3965                var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
3966                        (window.ActiveXObject && function (_xmlString) {
3967                            var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
vendor: 26,289 bytes, lines 3968-4773
3968                            xml_doc.async = 'false';
3969                            xml_doc.loadXML(_xmlString);
3970                            return xml_doc;
3971                        }),
3972                resultXML;
3973                if (!dom_parser) {
3974                    return false;
3975                }
3976                resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
3977                return this.isXML(resultXML) ? resultXML : false;
3978            }
3979        };
3980
3981
3982    ////////////////////////// PRIVATE METHODS ////////////////////////
3983
3984    /**
3985     * Initialization function. Detects if the browser supports DOM Storage
3986     * or userData behavior and behaves accordingly.
3987     */
3988    function _init() {
3989        /* Check if browser supports localStorage */
3990        var localStorageReallyWorks = false;
3991        if ("localStorage" in window) {
3992            try {
3993                window.localStorage.setItem('_tmptest', 'tmpval');
3994                localStorageReallyWorks = true;
3995                window.localStorage.removeItem('_tmptest');
3996            } catch (BogusQuotaExceededErrorOnIos5) {
3997                // Thanks be to iOS5 Private Browsing mode which throws
3998                // QUOTA_EXCEEDED_ERRROR DOM Exception 22.
3999            }
4000        }
4001
4002        if (localStorageReallyWorks) {
4003            try {
4004                if (window.localStorage) {
4005                    _storage_service = window.localStorage;
4006                    _backend = "localStorage";
4007                    _observer_update = _storage_service.jStorage_update;
4008                }
4009            } catch (E3) {/* Firefox fails when touching localStorage and cookies are disabled */ }
4010        }
4011            /* Check if browser supports globalStorage */
4012        else if ("globalStorage" in window) {
4013            try {
4014                if (window.globalStorage) {
4015                    _storage_service = window.globalStorage[window.location.hostname];
4016                    _backend = "globalStorage";
4017                    _observer_update = _storage_service.jStorage_update;
4018                }
4019            } catch (E4) {/* Firefox fails when touching localStorage and cookies are disabled */ }
4020        }
4021            /* Check if browser supports userData behavior */
4022        else {
4023            _storage_elm = document.createElement('link');
4024            if (_storage_elm.addBehavior) {
4025
4026                /* Use a DOM element to act as userData storage */
4027                _storage_elm.style.behavior = 'url(#default#userData)';
4028
4029                /* userData element needs to be inserted into the DOM! */
4030                document.getElementsByTagName('head')[0].appendChild(_storage_elm);
4031
4032                try {
4033                    _storage_elm.load("jStorage");
4034                } catch (E) {
4035                    // try to reset cache
4036                    _storage_elm.setAttribute("jStorage", "{}");
4037                    _storage_elm.save("jStorage");
4038                    _storage_elm.load("jStorage");
4039                }
4040
4041                var data = "{}";
4042                try {
4043                    data = _storage_elm.getAttribute("jStorage");
4044                } catch (E5) { }
4045
4046                try {
4047                    _observer_update = _storage_elm.getAttribute("jStorage_update");
4048                } catch (E6) { }
4049
4050                _storage_service.jStorage = data;
4051                _backend = "userDataBehavior";
4052            } else {
4053                _storage_elm = null;
4054                return;
4055            }
4056        }
4057
4058        // Load data from storage
4059        _load_storage();
4060
4061        // remove dead keys
4062        _handleTTL();
4063
4064        // start listening for changes
4065        _setupObserver();
4066
4067        // initialize publish-subscribe service
4068        _handlePubSub();
4069
4070        // handle cached navigation
4071        if ("addEventListener" in window) {
4072            window.addEventListener("pageshow", function (event) {
4073                if (event.persisted) {
4074                    _storageObserver();
4075                }
4076            }, false);
4077        }
4078    }
4079
4080    /**
4081     * Reload data from storage when needed
4082     */
4083    function _reloadData() {
4084        var data = "{}";
4085
4086        if (_backend == "userDataBehavior") {
4087            _storage_elm.load("jStorage");
4088
4089            try {
4090                data = _storage_elm.getAttribute("jStorage");
4091            } catch (E5) { }
4092
4093            try {
4094                _observer_update = _storage_elm.getAttribute("jStorage_update");
4095            } catch (E6) { }
4096
4097            _storage_service.jStorage = data;
4098        }
4099
4100        _load_storage();
4101
4102        // remove dead keys
4103        _handleTTL();
4104
4105        _handlePubSub();
4106    }
4107
4108    /**
4109     * Sets up a storage change observer
4110     */
4111    function _setupObserver() {
4112        if (_backend == "localStorage" || _backend == "globalStorage") {
4113            if ("addEventListener" in window) {
4114                window.addEventListener("storage", _storageObserver, false);
4115            } else {
4116                document.attachEvent("onstorage", _storageObserver);
4117            }
4118        } else if (_backend == "userDataBehavior") {
4119            setInterval(_storageObserver, 1000);
4120        }
4121    }
4122
4123    /**
4124     * Fired on any kind of data change, needs to check if anything has
4125     * really been changed
4126     */
4127    function _storageObserver() {
4128        var updateTime;
4129        // cumulate change notifications with timeout
4130        clearTimeout(_observer_timeout);
4131        _observer_timeout = setTimeout(function () {
4132
4133            if (_backend == "localStorage" || _backend == "globalStorage") {
4134                updateTime = _storage_service.jStorage_update;
4135            } else if (_backend == "userDataBehavior") {
4136                _storage_elm.load("jStorage");
4137                try {
4138                    updateTime = _storage_elm.getAttribute("jStorage_update");
4139                } catch (E5) { }
4140            }
4141
4142            if (updateTime && updateTime != _observer_update) {
4143                _observer_update = updateTime;
4144                _checkUpdatedKeys();
4145            }
4146
4147        }, 25);
4148    }
4149
4150    /**
4151     * Reloads the data and checks if any keys are changed
4152     */
4153    function _checkUpdatedKeys() {
4154        var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)),
4155            newCrc32List;
4156
4157        _reloadData();
4158        newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32));
4159
4160        var key,
4161            updated = [],
4162            removed = [];
4163
4164        for (key in oldCrc32List) {
4165            if (oldCrc32List.hasOwnProperty(key)) {
4166                if (!newCrc32List[key]) {
4167                    removed.push(key);
4168                    continue;
4169                }
4170                if (oldCrc32List[key] != newCrc32List[key] && String(oldCrc32List[key]).substr(0, 2) == "2.") {
4171                    updated.push(key);
4172                }
4173            }
4174        }
4175
4176        for (key in newCrc32List) {
4177            if (newCrc32List.hasOwnProperty(key)) {
4178                if (!oldCrc32List[key]) {
4179                    updated.push(key);
4180                }
4181            }
4182        }
4183
4184        _fireObservers(updated, "updated");
4185        _fireObservers(removed, "deleted");
4186    }
4187
4188    /**
4189     * Fires observers for updated keys
4190     *
4191     * @param {Array|String} keys Array of key names or a key
4192     * @param {String} action What happened with the value (updated, deleted, flushed)
4193     */
4194    function _fireObservers(keys, action) {
4195        keys = [].concat(keys || []);
4196        if (action == "flushed") {
4197            keys = [];
4198            for (var key in _observers) {
4199                if (_observers.hasOwnProperty(key)) {
4200                    keys.push(key);
4201                }
4202            }
4203            action = "deleted";
4204        }
4205        for (var i = 0, len = keys.length; i < len; i++) {
4206            if (_observers[keys[i]]) {
4207                for (var j = 0, jlen = _observers[keys[i]].length; j < jlen; j++) {
4208                    _observers[keys[i]][j](keys[i], action);
4209                }
4210            }
4211            if (_observers["*"]) {
4212                for (var j = 0, jlen = _observers["*"].length; j < jlen; j++) {
4213                    _observers["*"][j](keys[i], action);
4214                }
4215            }
4216        }
4217    }
4218
4219    /**
4220     * Publishes key change to listeners
4221     */
4222    function _publishChange() {
4223        var updateTime = (+new Date()).toString();
4224
4225        if (_backend == "localStorage" || _backend == "globalStorage") {
4226            _storage_service.jStorage_update = updateTime;
4227        } else if (_backend == "userDataBehavior") {
4228            _storage_elm.setAttribute("jStorage_update", updateTime);
4229            _storage_elm.save("jStorage");
4230        }
4231
4232        _storageObserver();
4233    }
4234
4235    /**
4236     * Loads the data from the storage based on the supported mechanism
4237     */
4238    function _load_storage() {
4239        /* if jStorage string is retrieved, then decode it */
4240        if (_storage_service.jStorage) {
4241            try {
4242                _storage = JSON.parse(String(_storage_service.jStorage));
4243            } catch (E6) { _storage_service.jStorage = "{}"; }
4244        } else {
4245            _storage_service.jStorage = "{}";
4246        }
4247        _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
4248
4249        if (!_storage.__jstorage_meta) {
4250            _storage.__jstorage_meta = {};
4251        }
4252        if (!_storage.__jstorage_meta.CRC32) {
4253            _storage.__jstorage_meta.CRC32 = {};
4254        }
4255    }
4256
4257    /**
4258     * This functions provides the "save" mechanism to store the jStorage object
4259     */
4260    function _save() {
4261        _dropOldEvents(); // remove expired events
4262        try {
4263            _storage_service.jStorage = JSON.stringify(_storage);
4264            // If userData is used as the storage engine, additional
4265            if (_storage_elm) {
4266                _storage_elm.setAttribute("jStorage", _storage_service.jStorage);
4267                _storage_elm.save("jStorage");
4268            }
4269            _storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
4270        } catch (E7) {/* probably cache is full, nothing is saved this way*/ }
4271    }
4272
4273    /**
4274     * Function checks if a key is set and is string or numberic
4275     *
4276     * @param {String} key Key name
4277     */
4278    function _checkKey(key) {
4279        if (!key || (typeof key != "string" && typeof key != "number")) {
4280            throw new TypeError('Key name must be string or numeric');
4281        }
4282        if (key == "__jstorage_meta") {
4283            throw new TypeError('Reserved key name');
4284        }
4285        return true;
4286    }
4287
4288    /**
4289     * Removes expired keys
4290     */
4291    function _handleTTL() {
4292        var curtime, i, TTL, CRC32, nextExpire = Infinity, changed = false, deleted = [];
4293
4294        clearTimeout(_ttl_timeout);
4295
4296        if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != "object") {
4297            // nothing to do here
4298            return;
4299        }
4300
4301        curtime = +new Date();
4302        TTL = _storage.__jstorage_meta.TTL;
4303
4304        CRC32 = _storage.__jstorage_meta.CRC32;
4305        for (i in TTL) {
4306            if (TTL.hasOwnProperty(i)) {
4307                if (TTL[i] <= curtime) {
4308                    delete TTL[i];
4309                    delete CRC32[i];
4310                    delete _storage[i];
4311                    changed = true;
4312                    deleted.push(i);
4313                } else if (TTL[i] < nextExpire) {
4314                    nextExpire = TTL[i];
4315                }
4316            }
4317        }
4318
4319        // set next check
4320        if (nextExpire != Infinity) {
4321            _ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime);
4322        }
4323
4324        // save changes
4325        if (changed) {
4326            _save();
4327            _publishChange();
4328            _fireObservers(deleted, "deleted");
4329        }
4330    }
4331
4332    /**
4333     * Checks if there's any events on hold to be fired to listeners
4334     */
4335    function _handlePubSub() {
4336        var i, len;
4337        if (!_storage.__jstorage_meta.PubSub) {
4338            return;
4339        }
4340        var pubelm,
4341            _pubsubCurrent = _pubsub_last;
4342
4343        for (i = len = _storage.__jstorage_meta.PubSub.length - 1; i >= 0; i--) {
4344            pubelm = _storage.__jstorage_meta.PubSub[i];
4345            if (pubelm[0] > _pubsub_last) {
4346                _pubsubCurrent = pubelm[0];
4347                _fireSubscribers(pubelm[1], pubelm[2]);
4348            }
4349        }
4350
4351        _pubsub_last = _pubsubCurrent;
4352    }
4353
4354    /**
4355     * Fires all subscriber listeners for a pubsub channel
4356     *
4357     * @param {String} channel Channel name
4358     * @param {Mixed} payload Payload data to deliver
4359     */
4360    function _fireSubscribers(channel, payload) {
4361        if (_pubsub_observers[channel]) {
4362            for (var i = 0, len = _pubsub_observers[channel].length; i < len; i++) {
4363                // send immutable data that can't be modified by listeners
4364                _pubsub_observers[channel][i](channel, JSON.parse(JSON.stringify(payload)));
4365            }
4366        }
4367    }
4368
4369    /**
4370     * Remove old events from the publish stream (at least 2sec old)
4371     */
4372    function _dropOldEvents() {
4373        if (!_storage.__jstorage_meta.PubSub) {
4374            return;
4375        }
4376
4377        var retire = +new Date() - 2000;
4378
4379        for (var i = 0, len = _storage.__jstorage_meta.PubSub.length; i < len; i++) {
4380            if (_storage.__jstorage_meta.PubSub[i][0] <= retire) {
4381                // deleteCount is needed for IE6
4382                _storage.__jstorage_meta.PubSub.splice(i, _storage.__jstorage_meta.PubSub.length - i);
4383                break;
4384            }
4385        }
4386
4387        if (!_storage.__jstorage_meta.PubSub.length) {
4388            delete _storage.__jstorage_meta.PubSub;
4389        }
4390
4391    }
4392
4393    /**
4394     * Publish payload to a channel
4395     *
4396     * @param {String} channel Channel name
4397     * @param {Mixed} payload Payload to send to the subscribers
4398     */
4399    function _publish(channel, payload) {
4400        if (!_storage.__jstorage_meta) {
4401            _storage.__jstorage_meta = {};
4402        }
4403        if (!_storage.__jstorage_meta.PubSub) {
4404            _storage.__jstorage_meta.PubSub = [];
4405        }
4406
4407        _storage.__jstorage_meta.PubSub.unshift([+new Date, channel, payload]);
4408
4409        _save();
4410        _publishChange();
4411    }
4412
4413
4414    /**
4415     * JS Implementation of MurmurHash2
4416     *
4417     *  SOURCE: https://github.com/garycourt/murmurhash-js (MIT licensed)
4418     *
4419     * @author <a href="mailto:[email protected]">Gary Court</a>
4420     * @see http://github.com/garycourt/murmurhash-js
4421     * @author <a href="mailto:[email protected]">Austin Appleby</a>
4422     * @see http://sites.google.com/site/murmurhash/
4423     *
4424     * @param {string} str ASCII only
4425     * @param {number} seed Positive integer only
4426     * @return {number} 32-bit positive integer hash
4427     */
4428
4429    function murmurhash2_32_gc(str, seed) {
4430        var
4431            l = str.length,
4432            h = seed ^ l,
4433            i = 0,
4434            k;
4435
4436        while (l >= 4) {
4437            k =
4438                ((str.charCodeAt(i) & 0xff)) |
4439                ((str.charCodeAt(++i) & 0xff) << 8) |
4440                ((str.charCodeAt(++i) & 0xff) << 16) |
4441                ((str.charCodeAt(++i) & 0xff) << 24);
4442
4443            k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
4444            k ^= k >>> 24;
4445            k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
4446
4447            h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
4448
4449            l -= 4;
4450            ++i;
4451        }
4452
4453        switch (l) {
4454            case 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
4455            case 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
4456            case 1: h ^= (str.charCodeAt(i) & 0xff);
4457                h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
4458        }
4459
4460        h ^= h >>> 13;
4461        h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
4462        h ^= h >>> 15;
4463
4464        return h >>> 0;
4465    }
4466
4467    ////////////////////////// PUBLIC INTERFACE /////////////////////////
4468
4469    $.jStorage = {
4470        /* Version number */
4471        version: JSTORAGE_VERSION,
4472
4473        /**
4474         * Sets a key's value.
4475         *
4476         * @param {String} key Key to set. If this value is not set or not
4477         *              a string an exception is raised.
4478         * @param {Mixed} value Value to set. This can be any value that is JSON
4479         *              compatible (Numbers, Strings, Objects etc.).
4480         * @param {Object} [options] - possible options to use
4481         * @param {Number} [options.TTL] - optional TTL value
4482         * @return {Mixed} the used value
4483         */
4484        set: function (key, value, options) {
4485            _checkKey(key);
4486
4487            options = options || {};
4488
4489            // undefined values are deleted automatically
4490            if (typeof value == "undefined") {
4491                this.deleteKey(key);
4492                return value;
4493            }
4494
4495            if (_XMLService.isXML(value)) {
4496                value = { _is_xml: true, xml: _XMLService.encode(value) };
4497            } else if (typeof value == "function") {
4498                return undefined; // functions can't be saved!
4499            } else if (value && typeof value == "object") {
4500                // clone the object before saving to _storage tree
4501                value = JSON.parse(JSON.stringify(value));
4502            }
4503
4504            _storage[key] = value;
4505
4506            _storage.__jstorage_meta.CRC32[key] = "2." + murmurhash2_32_gc(JSON.stringify(value), 0x9747b28c);
4507
4508            this.setTTL(key, options.TTL || 0); // also handles saving and _publishChange
4509
4510            _fireObservers(key, "updated");
4511            return value;
4512        },
4513
4514        /**
4515         * Looks up a key in cache
4516         *
4517         * @param {String} key - Key to look up.
4518         * @param {mixed} def - Default value to return, if key didn't exist.
4519         * @return {Mixed} the key value, default value or null
4520         */
4521        get: function (key, def) {
4522            _checkKey(key);
4523            if (key in _storage) {
4524                if (_storage[key] && typeof _storage[key] == "object" && _storage[key]._is_xml) {
4525                    return _XMLService.decode(_storage[key].xml);
4526                } else {
4527                    return _storage[key];
4528                }
4529            }
4530            return typeof (def) == 'undefined' ? null : def;
4531        },
4532
4533        /**
4534         * Deletes a key from cache.
4535         *
4536         * @param {String} key - Key to delete.
4537         * @return {Boolean} true if key existed or false if it didn't
4538         */
4539        deleteKey: function (key) {
4540            _checkKey(key);
4541            if (key in _storage) {
4542                delete _storage[key];
4543                // remove from TTL list
4544                if (typeof _storage.__jstorage_meta.TTL == "object" &&
4545                  key in _storage.__jstorage_meta.TTL) {
4546                    delete _storage.__jstorage_meta.TTL[key];
4547                }
4548
4549                delete _storage.__jstorage_meta.CRC32[key];
4550
4551                _save();
4552                _publishChange();
4553                _fireObservers(key, "deleted");
4554                return true;
4555            }
4556            return false;
4557        },
4558
4559        /**
4560         * Sets a TTL for a key, or remove it if ttl value is 0 or below
4561         *
4562         * @param {String} key - key to set the TTL for
4563         * @param {Number} ttl - TTL timeout in milliseconds
4564         * @return {Boolean} true if key existed or false if it didn't
4565         */
4566        setTTL: function (key, ttl) {
4567            var curtime = +new Date();
4568            _checkKey(key);
4569            ttl = Number(ttl) || 0;
4570            if (key in _storage) {
4571
4572                if (!_storage.__jstorage_meta.TTL) {
4573                    _storage.__jstorage_meta.TTL = {};
4574                }
4575
4576                // Set TTL value for the key
4577                if (ttl > 0) {
4578                    _storage.__jstorage_meta.TTL[key] = curtime + ttl;
4579                } else {
4580                    delete _storage.__jstorage_meta.TTL[key];
4581                }
4582
4583                _save();
4584
4585                _handleTTL();
4586
4587                _publishChange();
4588                return true;
4589            }
4590            return false;
4591        },
4592
4593        /**
4594         * Gets remaining TTL (in milliseconds) for a key or 0 when no TTL has been set
4595         *
4596         * @param {String} key Key to check
4597         * @return {Number} Remaining TTL in milliseconds
4598         */
4599        getTTL: function (key) {
4600            var curtime = +new Date(), ttl;
4601            _checkKey(key);
4602            if (key in _storage && _storage.__jstorage_meta.TTL && _storage.__jstorage_meta.TTL[key]) {
4603                ttl = _storage.__jstorage_meta.TTL[key] - curtime;
4604                return ttl || 0;
4605            }
4606            return 0;
4607        },
4608
4609        /**
4610         * Deletes everything in cache.
4611         *
4612         * @return {Boolean} Always true
4613         */
4614        flush: function () {
4615            _storage = { __jstorage_meta: { CRC32: {} } };
4616            _save();
4617            _publishChange();
4618            _fireObservers(null, "flushed");
4619            return true;
4620        },
4621
4622        /**
4623         * Returns a read-only copy of _storage
4624         *
4625         * @return {Object} Read-only copy of _storage
4626        */
4627        storageObj: function () {
4628            function F() { }
4629            F.prototype = _storage;
4630            return new F();
4631        },
4632
4633        /**
4634         * Returns an index of all used keys as an array
4635         * ['key1', 'key2',..'keyN']
4636         *
4637         * @return {Array} Used keys
4638        */
4639        index: function () {
4640            var index = [], i;
4641            for (i in _storage) {
4642                if (_storage.hasOwnProperty(i) && i != "__jstorage_meta") {
4643                    index.push(i);
4644                }
4645            }
4646            return index;
4647        },
4648
4649        /**
4650         * How much space in bytes does the storage take?
4651         *
4652         * @return {Number} Storage size in chars (not the same as in bytes,
4653         *                  since some chars may take several bytes)
4654         */
4655        storageSize: function () {
4656            return _storage_size;
4657        },
4658
4659        /**
4660         * Which backend is currently in use?
4661         *
4662         * @return {String} Backend name
4663         */
4664        currentBackend: function () {
4665            return _backend;
4666        },
4667
4668        /**
4669         * Test if storage is available
4670         *
4671         * @return {Boolean} True if storage can be used
4672         */
4673        storageAvailable: function () {
4674            return !!_backend;
4675        },
4676
4677        /**
4678         * Register change listeners
4679         *
4680         * @param {String} key Key name
4681         * @param {Function} callback Function to run when the key changes
4682         */
4683        listenKeyChange: function (key, callback) {
4684            _checkKey(key);
4685            if (!_observers[key]) {
4686                _observers[key] = [];
4687            }
4688            _observers[key].push(callback);
4689        },
4690
4691        /**
4692         * Remove change listeners
4693         *
4694         * @param {String} key Key name to unregister listeners against
4695         * @param {Function} [callback] If set, unregister the callback, if not - unregister all
4696         */
4697        stopListening: function (key, callback) {
4698            _checkKey(key);
4699
4700            if (!_observers[key]) {
4701                return;
4702            }
4703
4704            if (!callback) {
4705                delete _observers[key];
4706                return;
4707            }
4708
4709            for (var i = _observers[key].length - 1; i >= 0; i--) {
4710                if (_observers[key][i] == callback) {
4711                    _observers[key].splice(i, 1);
4712                }
4713            }
4714        },
4715
4716        /**
4717         * Subscribe to a Publish/Subscribe event stream
4718         *
4719         * @param {String} channel Channel name
4720         * @param {Function} callback Function to run when the something is published to the channel
4721         */
4722        subscribe: function (channel, callback) {
4723            channel = (channel || "").toString();
4724            if (!channel) {
4725                throw new TypeError('Channel not defined');
4726            }
4727            if (!_pubsub_observers[channel]) {
4728                _pubsub_observers[channel] = [];
4729            }
4730            _pubsub_observers[channel].push(callback);
4731        },
4732
4733        /**
4734         * Publish data to an event stream
4735         *
4736         * @param {String} channel Channel name
4737         * @param {Mixed} payload Payload to deliver
4738         */
4739        publish: function (channel, payload) {
4740            channel = (channel || "").toString();
4741            if (!channel) {
4742                throw new TypeError('Channel not defined');
4743            }
4744
4745            _publish(channel, payload);
4746        },
4747
4748        /**
4749         * Reloads the data from browser storage
4750         */
4751        reInit: function () {
4752            _reloadData();
4753        }
4754    };
4755
4756    // Initialize jStorage
4757    _init();
4758
4759})();
4760/*!
4761 * jQuery Form Plugin
4762 * version: 4.3.0
4763 * Requires jQuery v1.7.2 or later
4764 * Project repository: https://github.com/jquery-form/form
4765
4766 * Copyright 2017 Kevin Morris
4767 * Copyright 2006 M. Alsup
4768
4769 * Dual licensed under the LGPL-2.1+ or MIT licenses
4770 * https://github.com/jquery-form/form#license
4771
4772 * This library is free software; you can redistribute it and/or
4773 * modify it under the terms of the GNU Lesser General Public
4774 * License as published by the Free Software Foundation; either
4775 * version 2.1 of the License, or (at your option) any later version.
4776 * This library is distributed in the hope that it will be useful,
4777 * but WITHOUT ANY WARRANTY; without even the implied warranty of
4778 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
4779 * Lesser General Public License for more details.
4780 */
4781/* global ActiveXObject */
4782
4783/* eslint-disable */
4784(function (factory) {
4785	if (typeof define === 'function' && define.amd) {
4786		// AMD. Register as an anonymous module.
4787		define(['jquery'], factory);
4788	} else if (typeof module === 'object' && module.exports) {
4789		// Node/CommonJS
4790		module.exports = function( root, jQuery ) {
4791			if (typeof jQuery === 'undefined') {
4792				// require('jQuery') returns a factory that requires window to build a jQuery instance, we normalize how we use modules
4793				// that require this pattern but the window provided is a noop if it's defined (how jquery works)
4794				if (typeof window !== 'undefined') {
4795					jQuery = require('jquery');
4796				}
4797				else {
4798					jQuery = require('jquery')(root);
4799				}
4800			}
4801			factory(jQuery);
4802			return jQuery;
4803		};
4804	} else {
4805		// Browser globals
4806		factory(jQuery);
4807	}
4808
4809}(function ($) {
4810/* eslint-enable */
4811	'use strict';
4812
4813	/*
4814		Usage Note:
4815		-----------
4816		Do not use both ajaxSubmit and ajaxForm on the same form. These
4817		functions are mutually exclusive. Use ajaxSubmit if you want
4818		to bind your own submit handler to the form. For example,
4819
4820		$(document).ready(function() {
4821			$('#myForm').on('submit', function(e) {
4822				e.preventDefault(); // <-- important
4823				$(this).ajaxSubmit({
4824					target: '#output'
4825				});
4826			});
4827		});
4828
4829		Use ajaxForm when you want the plugin to manage all the event binding
4830		for you. For example,
4831
4832		$(document).ready(function() {
4833			$('#myForm').ajaxForm({
4834				target: '#output'
4835			});
4836		});
4837
4838		You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
4839		form does not have to exist when you invoke ajaxForm:
4840
4841		$('#myForm').ajaxForm({
4842			delegation: true,
4843			target: '#output'
4844		});
4845
4846		When using ajaxForm, the ajaxSubmit function will be invoked for you
4847		at the appropriate time.
4848	*/
4849
4850	var rCRLF = /\r?\n/g;
4851
4852	/**
4853	 * Feature detection
4854	 */
4855	var feature = {};
4856
4857	feature.fileapi = $('<input type="file">').get(0).files !== undefined;
4858	feature.formdata = (typeof window.FormData !== 'undefined');
4859
4860	var hasProp = !!$.fn.prop;
4861
4862	// attr2 uses prop when it can but checks the return type for
4863	// an expected string. This accounts for the case where a form
4864	// contains inputs with names like "action" or "method"; in those
4865	// cases "prop" returns the element
4866	$.fn.attr2 = function() {
4867		if (!hasProp) {
4868			return this.attr.apply(this, arguments);
4869		}
4870
4871		var val = this.prop.apply(this, arguments);
4872
4873		if ((val && val.jquery) || typeof val === 'string') {
4874			return val;
4875		}
4876
4877		return this.attr.apply(this, arguments);
4878	};
4879
4880	/**
4881	 * ajaxSubmit() provides a mechanism for immediately submitting
4882	 * an HTML form using AJAX.
4883	 *
4884	 * @param	{object|string}	options		jquery.form.js parameters or custom url for submission
4885	 * @param	{object}		data		extraData
4886	 * @param	{string}		dataType	ajax dataType
4887	 * @param	{function}		onSuccess	ajax success callback function
4888	 */
4889	$.fn.ajaxSubmit = function(options, data, dataType, onSuccess) {
4890		// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
4891		if (!this.length) {
4892			log('ajaxSubmit: skipping submit process - no element selected');
4893
4894			return this;
4895		}
4896
4897		/* eslint consistent-this: ["error", "$form"] */
4898		var method, action, url, isMsie, iframeSrc, $form = this;
4899
4900		if (typeof options === 'function') {
4901			options = {success: options};
4902
4903		} else if (typeof options === 'string' || (options === false && arguments.length > 0)) {
4904			options = {
4905				'url'      : options,
4906				'data'     : data,
4907				'dataType' : dataType
4908			};
4909
4910			if (typeof onSuccess === 'function') {
4911				options.success = onSuccess;
4912			}
4913
4914		} else if (typeof options === 'undefined') {
4915			options = {};
4916		}
4917
4918		method = options.method || options.type || this.attr2('method');
4919		action = options.url || this.attr2('action');
4920
4921		url = (typeof action === 'string') ? $.trim(action) : '';
4922		url = url || window.location.href || '';
4923		if (url) {
4924			// clean url (don't include hash vaue)
4925			url = (url.match(/^([^#]+)/) || [])[1];
4926		}
4927		// IE requires javascript:false in https, but this breaks chrome >
492783 and goes against spec.
4928		// Instead of using javascript:false always, let's only apply it for IE.
4929		isMsie = /(MSIE|Trident)/.test(navigator.userAgent || '');
4930		iframeSrc = (isMsie && /^https/i.test(window.location.href || '')) ? 'javascript:false' : 'about:blank'; // eslint-disable-line no-script-url
4931
4932		options = $.extend(true, {
4933			url       : url,
4934			success   : $.ajaxSettings.success,
4935			type      : method || $.ajaxSettings.type,
4936			iframeSrc : iframeSrc
4937		}, options);
4938
4939		// hook for manipulating the form data before it is extracted;
4940		// convenient for use with rich editors like tinyMCE or FCKEditor
4941		var veto = {};
4942
4943		this.trigger('form-pre-serialize', [this, options, veto]);
4944
4945		if (veto.veto) {
4946			log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
4947
4948			return this;
4949		}
4950
4951		// provide opportunity to alter form data before it is serialized
4952		if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
4953			log('ajaxSubmit: submit aborted via beforeSerialize callback');
4954
4955			return this;
4956		}
4957
4958		var traditional = options.traditional;
4959
4960		if (typeof traditional === 'undefined') {
4961			traditional = $.ajaxSettings.traditional;
4962		}
4963
4964		var elements = [];
4965		var qx, a = this.formToArray(options.semantic, elements, options.filtering);
4966
4967		if (options.data) {
4968			var optionsData = $.isFunction(options.data) ? options.data(a) : options.data;
4969
4970			options.extraData = optionsData;
4971			qx = $.param(optionsData, traditional);
4972		}
4973
4974		// give pre-submit callback an opportunity to abort the submit
4975		if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
4976			log('ajaxSubmit: submit aborted via beforeSubmit callback');
4977
4978			return this;
4979		}
4980
4981		// fire vetoable 'validate' event
4982		this.trigger('form-submit-validate', [a, this, options, veto]);
4983		if (veto.veto) {
4984			log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
4985
4986			return this;
4987		}
4988
4989		var q = $.param(a, traditional);
4990
4991		if (qx) {
4992			q = (q ? (q + '&' + qx) : qx);
4993		}
4994
4995		if (options.type.toUpperCase() === 'GET') {
4996			options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
4997			options.data = null;	// data is null for 'get'
4998		} else {
4999			options.data = q;		// data is the query string for 'post'
5000		}
5001
5002		var callbacks = [];
5003
5004		if (options.resetForm) {
5005			callbacks.push(function() {
5006				$form.resetForm();
5007			});
5008		}
5009
5010		if (options.clearForm) {
5011			callbacks.push(function() {
5012				$form.clearForm(options.includeHidden);
5013			});
5014		}
5015
5016		// perform a load on the target only if dataType is not provided
5017		if (!options.dataType && options.target) {
5018			var oldSuccess = options.success || function(){};
5019
5020			callbacks.push(function(data, textStatus, jqXHR) {
5021				var successArguments = arguments,
5022					fn = options.replaceTarget ? 'replaceWith' : 'html';
5023
5024				$(options.target)[fn](data).each(function(){
5025					oldSuccess.apply(this, successArguments);
5026				});
5027			});
5028
5029		} else if (options.success) {
5030			if ($.isArray(options.success)) {
5031				$.merge(callbacks, options.success);
5032			} else {
5033				callbacks.push(options.success);
5034			}
5035		}
5036
5037		options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
5038			var context = options.context || this;		// jQuery 1.4+ supports scope context
5039
5040			for (var i = 0, max = callbacks.length; i < max; i++) {
5041				callbacks[i].apply(context, [data, status, xhr || $form, $form]);
5042			}
5043		};
5044
5045		if (options.error) {
5046			var oldError = options.error;
5047
5048			options.error = function(xhr, status, error) {
5049				var context = options.context || this;
5050
5051				oldError.apply(context, [xhr, status, error, $form]);
5052			};
5053		}
5054
5055		if (options.complete) {
5056			var oldComplete = options.complete;
5057
5058			options.complete = function(xhr, status) {
5059				var context = options.context || this;
5060
5061				oldComplete.apply(context, [xhr, status, $form]);
5062			};
5063		}
5064
5065		// are there files to upload?
5066
5067		// [value] (issue #113), also see comment:
5068		// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
5069		var fileInputs = $('input[type=file]:enabled', this).filter(function() {
5070			return $(this).val() !== '';
5071		});
5072		var hasFileInputs = fileInputs.length > 0;
5073		var mp = 'multipart/form-data';
5074		var multipart = ($form.attr('enctype') === mp || $form.attr('encoding') === mp);
5075		var fileAPI = feature.fileapi && feature.formdata;
5076
5077		log('fileAPI :' + fileAPI);
5078
5079		var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
5080		var jqxhr;
5081
5082		// options.iframe allows user to force iframe mode
5083		// 06-NOV-09: now defaulting to iframe mode if file input is detected
5084		if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
5085			// hack to fix Safari hang (thanks to Tim Molendijk for this)
5086			// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
5087			if (options.closeKeepAlive) {
5088				$.get(options.closeKeepAlive, function() {
5089					jqxhr = fileUploadIframe(a);
5090				});
5091
5092			} else {
5093				jqxhr = fileUploadIframe(a);
5094			}
5095
5096		} else if ((hasFileInputs || multipart) && fileAPI) {
5097			jqxhr = fileUploadXhr(a);
5098
5099		} else {
5100			jqxhr = $.ajax(options);
5101		}
5102
5103		$form.removeData('jqxhr').data('jqxhr', jqxhr);
5104
5105		// clear element array
5106		for (var k = 0; k < elements.length; k++) {
5107			elements[k] = null;
5108		}
5109
5110		// fire 'notify' event
5111		this.trigger('form-submit-notify', [this, options]);
5112
5113		return this;
5114
5115		// utility fn for deep serialization
5116		function deepSerialize(extraData) {
5117			var serialized = $.param(extraData, options.traditional).split('&');
5118			var len = serialized.length;
5119			var result = [];
5120			var i, part;
5121
5122			for (i = 0; i < len; i++) {
5123				// #252; undo param space replacement
5124				serialized[i] = serialized[i].replace(/\+/g, ' ');
5125				part = serialized[i].split('=');
5126				// #278; use array instead of object storage, favoring array serializations
5127				result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
5128			}
5129
5130			return result;
5131		}
5132
5133		// XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
5134		function fileUploadXhr(a) {
5135			var formdata = new FormData();
5136
5137			for (var i = 0; i < a.length; i++) {
5138				formdata.append(a[i].name, a[i].value);
5139			}
5140
5141			if (options.extraData) {
5142				var serializedData = deepSerialize(options.extraData);
5143
5144				for (i = 0; i < serializedData.length; i++) {
5145					if (serializedData[i]) {
5146						formdata.append(serializedData[i][0], serializedData[i][1]);
5147					}
5148				}
5149			}
5150
5151			options.data = null;
5152
5153			var s = $.extend(true, {}, $.ajaxSettings, options, {
5154				contentType : false,
5155				processData : false,
5156				cache       : false,
5157				type        : method || 'POST'
5158			});
5159
5160			if (options.uploadProgress) {
5161				// workaround because jqXHR does not expose upload property
5162				s.xhr = function() {
5163					var xhr = $.ajaxSettings.xhr();
5164
5165					if (xhr.upload) {
5166						xhr.upload.addEventListener('progress', function(event) {
5167							var percent = 0;
5168							var position = event.loaded || event.position;			/* event.position is deprecated */
5169							var total = event.total;
5170
5171							if (event.lengthComputable) {
5172								percent = Math.ceil(position / total * 100);
5173							}
5174
5175							options.uploadProgress(event, position, total, percent);
5176						}, false);
5177					}
5178
5179					return xhr;
5180				};
5181			}
5182
5183			s.data = null;
5184
5185			var beforeSend = s.beforeSend;
5186
5187			s.beforeSend = function(xhr, o) {
5188				// Send FormData() provided by user
5189				if (options.formData) {
5190					o.data = options.formData;
5191				} else {
5192					o.data = formdata;
5193				}
5194
5195				if (beforeSend) {
5196					beforeSend.call(this, xhr, o);
5197				}
5198			};
5199
5200			return $.ajax(s);
5201		}
5202
5203		// private function for handling file uploads (hat tip to YAHOO!)
5204		function fileUploadIframe(a) {
5205			var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
5206			var deferred = $.Deferred();
5207
5208			// #341
5209			deferred.abort = function(status) {
5210				xhr.abort(status);
5211			};
5212
5213			if (a) {
5214				// ensure that every serialized input is still enabled
5215				for (i = 0; i < elements.length; i++) {
5216					el = $(elements[i]);
5217					if (hasProp) {
5218						el.prop('disabled', false);
5219					} else {
5220						el.removeAttr('disabled');
5221					}
5222				}
5223			}
5224
5225			s = $.extend(true, {}, $.ajaxSettings, options);
5226			s.context = s.context || s;
5227			id = 'jqFormIO' + new Date().getTime();
5228			var ownerDocument = form.ownerDocument;
5229			var $body = $form.closest('body');
5230
5231			if (s.iframeTarget) {
5232				$io = $(s.iframeTarget, ownerDocument);
5233				n = $io.attr2('name');
5234				if (!n) {
5235					$io.attr2('name', id);
5236				} else {
5237					id = n;
5238				}
5239
5240			} else {
5241				$io = $('<iframe name="' + id + '" src="' + s.iframeSrc + '" />', ownerDocument);
5242				$io.css({position: 'absolute', top: '-1000px', left: '-1000px'});
5243			}
5244			io = $io[0];
5245
5246
5247			xhr = { // mock object
5248				aborted               : 0,
5249				responseText          : null,
5250				responseXML           : null,
5251				status                : 0,
5252				statusText            : 'n/a',
5253				getAllResponseHeaders : function() {},
5254				getResponseHeader     : function() {},
5255				setRequestHeader      : function() {},
5256				abort                 : function(status) {
5257					var e = (status === 'timeout' ? 'timeout' : 'aborted');
5258
5259					log('aborting upload... ' + e);
5260					this.aborted = 1;
5261
5262					try { // #214, #257
5263						if (io.contentWindow.document.execCommand) {
5264							io.contentWindow.document.execCommand('Stop');
5265						}
5266					} catch (ignore) {}
5267
5268					$io.attr('src', s.iframeSrc); // abort op in progress
5269					xhr.error = e;
5270					if (s.error) {
5271						s.error.call(s.context, xhr, e, status);
5272					}
5273
5274					if (g) {
5275						$.event.trigger('ajaxError', [xhr, s, e]);
5276					}
5277
5278					if (s.complete) {
5279						s.complete.call(s.context, xhr, e);
5280					}
5281				}
5282			};
5283
5284			g = s.global;
5285			// trigger ajax global events so that activity/block indicators work like normal
5286			if (g && $.active++ === 0) {
5287				$.event.trigger('ajaxStart');
5288			}
5289			if (g) {
5290				$.event.trigger('ajaxSend', [xhr, s]);
5291			}
5292
5293			if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
5294				if (s.global) {
5295					$.active--;
5296				}
5297				deferred.reject();
5298
5299				return deferred;
5300			}
5301
5302			if (xhr.aborted) {
5303				deferred.reject();
5304
5305				return deferred;
5306			}
5307
5308			// add submitting element to data if we know it
5309			sub = form.clk;
5310			if (sub) {
5311				n = sub.name;
5312				if (n && !sub.disabled) {
5313					s.extraData = s.extraData || {};
5314					s.extraData[n] = sub.value;
5315					if (sub.type === 'image') {
5316						s.extraData[n + '.x'] = form.clk_x;
5317						s.extraData[n + '.y'] = form.clk_y;
5318					}
5319				}
5320			}
5321
5322			var CLIENT_TIMEOUT_ABORT = 1;
5323			var SERVER_ABORT = 2;
5324
5325			function getDoc(frame) {
5326				/* it looks like contentWindow or contentDocument do not
5327				 * carry the protocol property in ie8, when running under ssl
5328				 * frame.document is the only valid response document, since
5329				 * the protocol is know but not on the other two objects. strange?
5330				 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
5331				 */
5332
5333				var doc = null;
5334
5335				// IE8 cascading access check
5336				try {
5337					if (frame.contentWindow) {
5338						doc = frame.contentWindow.document;
5339					}
5340				} catch (err) {
5341					// IE8 access denied under ssl & missing protocol
5342					log('cannot get iframe.contentWindow document: ' + err);
5343				}
5344
5345				if (doc) { // successful getting content
5346					return doc;
5347				}
5348
5349				try { // simply checking may throw in ie8 under ssl or mismatched protocol
5350					doc = frame.contentDocument ? frame.contentDocument : frame.document;
5351				} catch (err) {
5352					// last attempt
5353					log('cannot get iframe.contentDocument: ' + err);
5354					doc = frame.document;
5355				}
5356
5357				return doc;
5358			}
5359
5360			// Rails CSRF hack (thanks to Yvan Barthelemy)
5361			var csrf_token = $('meta[name=csrf-token]').attr('content');
5362			var csrf_param = $('meta[name=csrf-param]').attr('content');
5363
5364			if (csrf_param && csrf_token) {
5365				s.extraData = s.extraData || {};
5366				s.extraData[csrf_param] = csrf_token;
5367			}
5368
5369			// take a breath so that pending repaints get some cpu time before the upload starts
5370			function doSubmit() {
5371				// make sure form attrs are set
5372				var t = $form.attr2('target'),
5373					a = $form.attr2('action'),
5374					mp = 'multipart/form-data',
5375					et = $form.attr('enctype') || $form.attr('encoding') || mp;
5376
5377				// update form attrs in IE friendly way
5378				form.setAttribute('target', id);
5379				if (!method || /post/i.test(method)) {
5380					form.setAttribute('method', 'POST');
5381				}
5382				if (a !== s.url) {
5383					form.setAttribute('action', s.url);
5384				}
5385
5386				// ie borks in some cases when setting encoding
5387				if (!s.skipEncodingOverride && (!method || /post/i.test(method))) {
5388					$form.attr({
5389						encoding : 'multipart/form-data',
5390						enctype  : 'multipart/form-data'
5391					});
5392				}
5393
5394				// support timout
5395				if (s.timeout) {
5396					timeoutHandle = setTimeout(function() {
5397						timedOut = true; cb(CLIENT_TIMEOUT_ABORT);
5398					}, s.timeout);
5399				}
5400
5401				// look for server aborts
5402				function checkState() {
5403					try {
5404						var state = getDoc(io).readyState;
5405
5406						log('state = ' + state);
5407						if (state && state.toLowerCase() === 'uninitialized') {
5408							setTimeout(checkState, 50);
5409						}
5410
5411					} catch (e) {
5412						log('Server abort: ', e, ' (', e.name, ')');
5413						cb(SERVER_ABORT);				// eslint-disable-line callback-return
5414						if (timeoutHandle) {
5415							clearTimeout(timeoutHandle);
5416						}
5417						timeoutHandle = undefined;
5418					}
5419				}
5420
5421				// add "extra" data to form if provided in options
5422				var extraInputs = [];
5423
5424				try {
5425					if (s.extraData) {
5426						for (var n in s.extraData) {
5427							if (s.extraData.hasOwnProperty(n)) {
5428								// if using the $.param format that allows for multiple values with the same name
5429								if ($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
5430									extraInputs.push(
5431										$('<input type="hidden" name="' + s.extraData[n].name + '">', ownerDocument).val(s.extraData[n].value)
5432											.appendTo(form)[0]);
5433								} else {
5434									extraInputs.push(
5435										$('<input type="hidden" name="' + n + '">', ownerDocument).val(s.extraData[n])
5436											.appendTo(form)[0]);
5437								}
5438							}
5439						}
5440					}
5441
5442					if (!s.iframeTarget) {
5443						// add iframe to doc and submit the form
5444						$io.appendTo($body);
5445					}
5446
5447					if (io.attachEvent) {
5448						io.attachEvent('onload', cb);
5449					} else {
5450						io.addEventListener('load', cb, false);
5451					}
5452
5453					setTimeout(checkState, 15);
5454
5455					try {
5456						form.submit();
5457
5458					} catch (err) {
5459						// just in case form has element with name/id of 'submit'
5460						var submitFn = document.createElement('form').submit;
5461
5462						submitFn.apply(form);
5463					}
5464
5465				} finally {
5466					// reset attrs and remove "extra" input elements
5467					form.setAttribute('action', a);
5468					form.setAttribute('enctype', et); // #380
5469					if (t) {
5470						form.setAttribute('target', t);
5471					} else {
5472						$form.removeAttr('target');
5473					}
5474					$(extraInputs).remove();
5475				}
5476			}
5477
5478			if (s.forceSync) {
5479				doSubmit();
5480			} else {
5481				setTimeout(doSubmit, 10); // this lets dom updates render
5482			}
5483
5484			var data, doc, domCheckCount = 50, callbackProcessed;
5485
5486			function cb(e) {
5487				if (xhr.aborted || callbackProcessed) {
5488					return;
5489				}
5490
5491				doc = getDoc(io);
5492				if (!doc) {
5493					log('cannot access response document');
5494					e = SERVER_ABORT;
5495				}
5496				if (e === CLIENT_TIMEOUT_ABORT && xhr) {
5497					xhr.abort('timeout');
5498					deferred.reject(xhr, 'timeout');
5499
5500					return;
5501
5502				}
5503				if (e === SERVER_ABORT && xhr) {
5504					xhr.abort('server abort');
5505					deferred.reject(xhr, 'error', 'server abort');
5506
5507					return;
5508				}
5509
5510				if (!doc || doc.location.href === s.iframeSrc) {
5511					// response not received yet
5512					if (!timedOut) {
5513						return;
5514					}
5515				}
5516
5517				if (io.detachEvent) {
5518					io.detachEvent('onload', cb);
5519				} else {
5520					io.removeEventListener('load', cb, false);
5521				}
5522
5523				var status = 'success', errMsg;
5524
5525				try {
5526					if (timedOut) {
5527						throw 'timeout';
5528					}
5529
5530					var isXml = s.dataType === 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
5531
5532					log('isXml=' + isXml);
5533
5534					if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
5535						if (--domCheckCount) {
5536							// in some browsers (Opera) the iframe DOM is not always traversable when
5537							// the onload callback fires, so we loop a bit to accommodate
5538							log('requeing onLoad callback, DOM not available');
5539							setTimeout(cb, 250);
5540
5541							return;
5542						}
5543						// let this fall through because server response could be an empty document
5544						// log('Could not access iframe DOM after mutiple tries.');
5545						// throw 'DOMException: not available';
5546					}
5547
5548					// log('response detected');
5549					var docRoot = doc.body ? doc.body : doc.documentElement;
5550
5551					xhr.responseText = docRoot ? docRoot.innerHTML : null;
5552					xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
5553					if (isXml) {
5554						s.dataType = 'xml';
5555					}
5556					xhr.getResponseHeader = function(header){
5557						var headers = {'content-type': s.dataType};
5558
5559						return headers[header.toLowerCase()];
5560					};
5561					// support for XHR 'status' & 'statusText' emulation :
5562					if (docRoot) {
5563						xhr.status = Number(docRoot.getAttribute('status')) || xhr.status;
5564						xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
5565					}
5566
5567					var dt = (s.dataType || '').toLowerCase();
5568					var scr = /(json|script|text)/.test(dt);
5569
5570					if (scr || s.textarea) {
5571						// see if user embedded response in textarea
5572						var ta = doc.getElementsByTagName('textarea')[0];
5573
5574						if (ta) {
5575							xhr.responseText = ta.value;
5576							// support for XHR 'status' & 'statusText' emulation :
5577							xhr.status = Number(ta.getAttribute('status')) || xhr.status;
5578							xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
5579
5580						} else if (scr) {
5581							// account for browsers injecting pre around json response
5582							var pre = doc.getElementsByTagName('pre')[0];
5583							var b = doc.getElementsByTagName('body')[0];
5584
5585							if (pre) {
5586								xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
5587							} else if (b) {
5588								xhr.responseText = b.textContent ? b.textContent : b.innerText;
5589							}
5590						}
5591
5592					} else if (dt === 'xml' && !xhr.responseXML && xhr.responseText) {
5593						xhr.responseXML = toXml(xhr.responseText);			// eslint-disable-line no-use-before-define
5594					}
5595
5596					try {
5597						data = httpData(xhr, dt, s);						// eslint-disable-line no-use-before-define
5598
5599					} catch (err) {
5600						status = 'parsererror';
5601						xhr.error = errMsg = (err || status);
5602					}
5603
5604				} catch (err) {
5605					log('error caught: ', err);
5606					status = 'error';
5607					xhr.error = errMsg = (err || status);
5608				}
5609
5610				if (xhr.aborted) {
5611					log('upload aborted');
5612					status = null;
5613				}
5614
5615				if (xhr.status) { // we've set xhr.status
5616					status = ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ? 'success' : 'error';
5617				}
5618
5619				// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
5620				if (status === 'success') {
5621					if (s.success) {
5622						s.success.call(s.context, data, 'success', xhr);
5623					}
5624
5625					deferred.resolve(xhr.responseText, 'success', xhr);
5626
5627					if (g) {
5628						$.event.trigger('ajaxSuccess', [xhr, s]);
5629					}
5630
5631				} else if (status) {
5632					if (typeof errMsg === 'undefined') {
5633						errMsg = xhr.statusText;
5634					}
5635					if (s.error) {
5636						s.error.call(s.context, xhr, status, errMsg);
5637					}
5638					deferred.reject(xhr, 'error', errMsg);
5639					if (g) {
5640						$.event.trigger('ajaxError', [xhr, s, errMsg]);
5641					}
5642				}
5643
5644				if (g) {
5645					$.event.trigger('ajaxComplete', [xhr, s]);
5646				}
5647
5648				if (g && !--$.active) {
5649					$.event.trigger('ajaxStop');
5650				}
5651
5652				if (s.complete) {
5653					s.complete.call(s.context, xhr, status);
5654				}
5655
5656				callbackProcessed = true;
5657				if (s.timeout) {
5658					clearTimeout(timeoutHandle);
5659				}
5660
5661				// clean up
5662				setTimeout(function() {
5663					if (!s.iframeTarget) {
5664						$io.remove();
5665					} else { // adding else to clean up existing iframe response.
5666						$io.attr('src', s.iframeSrc);
5667					}
5668					xhr.responseXML = null;
5669				}, 100);
5670			}
5671
5672			var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
5673				if (window.ActiveXObject) {
5674					doc = new ActiveXObject('Microsoft.XMLDOM');
5675					doc.async = 'false';
5676					doc.loadXML(s);
5677
5678				} else {
5679					doc = (new DOMParser()).parseFromString(s, 'text/xml');
5680				}
5681
5682				return (doc && doc.documentElement && doc.documentElement.nodeName !== 'parsererror') ? doc : null;
5683			};
5684			var parseJSON = $.parseJSON || function(s) {
5685				/* jslint evil:true */
5686				return window['eval']('(' + s + ')');			// eslint-disable-line dot-notation
5687			};
5688
5689			var httpData = function(xhr, type, s) { // mostly lifted from jq1.4.4
5690
5691				var ct = xhr.getResponseHeader('content-type') || '',
5692					xml = ((type === 'xml' || !type) && ct.indexOf('xml') >= 0),
5693					data = xml ? xhr.responseXML : xhr.responseText;
5694
5695				if (xml && data.documentElement.nodeName === 'parsererror') {
5696					if ($.error) {
5697						$.error('parsererror');
5698					}
5699				}
5700				if (s && s.dataFilter) {
5701					data = s.dataFilter(data, type);
5702				}
5703				if (typeof data === 'string') {
5704					if ((type === 'json' || !type) && ct.indexOf('json') >= 0) {
5705						data = parseJSON(data);
5706					} else if ((type === 'script' || !type) && ct.indexOf('javascript') >= 0) {
5707						$.globalEval(data);
5708					}
5709				}
5710
5711				return data;
5712			};
5713
5714			return deferred;
5715		}
5716	};
5717
5718	/**
5719	 * ajaxForm() provides a mechanism for fully automating form submission.
5720	 *
5721	 * The advantages of using this method instead of ajaxSubmit() are:
5722	 *
5723	 * 1: This method will include coordinates for <input type="image"> elements (if the element
5724	 *	is used to submit the form).
5725	 * 2. This method will include the submit element's name/value data (for the element that was
5726	 *	used to submit the form).
5727	 * 3. This method binds the submit() method to the form for you.
5728	 *
5729	 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
5730	 * passes the options argument along after properly binding events for submit elements and
5731	 * the form itself.
5732	 */
5733	$.fn.ajaxForm = function(options, data, dataType, onSuccess) {
5734		if (typeof options === 'string' || (options === false && arguments.length > 0)) {
5735			options = {
5736				'url'      : options,
5737				'data'     : data,
5738				'dataType' : dataType
5739			};
5740
5741			if (typeof onSuccess === 'function') {
5742				options.success = onSuccess;
5743			}
5744		}
5745
5746		options = options || {};
5747		options.delegation = options.delegation && $.isFunction($.fn.on);
5748
5749		// in jQuery 1.3+ we can fix mistakes with the ready state
5750		if (!options.delegation && this.length === 0) {
5751			var o = {s: this.selector, c: this.context};
5752
5753			if (!$.isReady && o.s) {
5754				log('DOM not ready, queuing ajaxForm');
5755				$(function() {
5756					$(o.s, o.c).ajaxForm(options);
5757				});
5758
5759				return this;
5760			}
5761
5762			// is your DOM ready?  http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
5763			log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
5764
5765			return this;
5766		}
5767
5768		if (options.delegation) {
5769			$(document)
5770				.off('submit.form-plugin', this.selector, doAjaxSubmit)
5771				.off('click.form-plugin', this.selector, captureSubmittingElement)
5772				.on('submit.form-plugin', this.selector, options, doAjaxSubmit)
5773				.on('click.form-plugin', this.selector, options, captureSubmittingElement);
5774
5775			return this;
5776		}
5777
5778		if (options.beforeFormUnbind) {
5779			options.beforeFormUnbind(this, options);
5780		}
5781
5782		return this.ajaxFormUnbind()
5783			.on('submit.form-plugin', options, doAjaxSubmit)
5784			.on('click.form-plugin', options, captureSubmittingElement);
5785	};
5786
5787	// private event handlers
5788	function doAjaxSubmit(e) {
5789		/* jshint validthis:true */
5790		var options = e.data;
5791
5792		if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
5793			e.preventDefault();
5794			$(e.target).closest('form').ajaxSubmit(options); // #365
5795		}
5796	}
5797
5798	function captureSubmittingElement(e) {
5799		/* jshint validthis:true */
5800		var target = e.target;
5801		var $el = $(target);
5802
5803		if (!$el.is('[type=submit],[type=image]')) {
5804			// is this a child element of the submit el?  (ex: a span within a button)
5805			var t = $el.closest('[type=submit]');
5806
5807			if (t.length === 0) {
5808				return;
5809			}
5810			target = t[0];
5811		}
5812
5813		var form = target.form;
5814
5815		form.clk = target;
5816
5817		if (target.type === 'image') {
5818			if (typeof e.offsetX !== 'undefined') {
5819				form.clk_x = e.offsetX;
5820				form.clk_y = e.offsetY;
5821
5822			} else if (typeof $.fn.offset === 'function') {
5823				var offset = $el.offset();
5824
5825				form.clk_x = e.pageX - offset.left;
5826				form.clk_y = e.pageY - offset.top;
5827
5828			} else {
5829				form.clk_x = e.pageX - target.offsetLeft;
5830				form.clk_y = e.pageY - target.offsetTop;
5831			}
5832		}
5833		// clear form vars
5834		setTimeout(function() {
5835			form.clk = form.clk_x = form.clk_y = null;
5836		}, 100);
5837	}
5838
5839
5840	// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
5841	$.fn.ajaxFormUnbind = function() {
5842		return this.off('submit.form-plugin click.form-plugin');
5843	};
5844
5845	/**
5846	 * formToArray() gathers form element data into an array of objects that can
5847	 * be passed to any of the following ajax functions: $.get, $.post, or load.
5848	 * Each object in the array has both a 'name' and 'value' property. An example of
5849	 * an array for a simple login form might be:
5850	 *
5851	 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
5852	 *
5853	 * It is this array that is passed to pre-submit callback functions provided to the
5854	 * ajaxSubmit() and ajaxForm() methods.
5855	 */
5856	$.fn.formToArray = function(semantic, elements, filtering) {
5857		var a = [];
5858
5859		if (this.length === 0) {
5860			return a;
5861		}
5862
5863		var form = this[0];
5864		var formId = this.attr('id');
5865		var els = (semantic || typeof form.elements === 'undefined') ? form.getElementsByTagName('*') : form.elements;
5866		var els2;
5867
5868		if (els) {
5869			els = $.makeArray(els); // convert to standard array
5870		}
5871
5872		// #386; account for inputs outside the form which use the 'form' attribute
5873		// FinesseRus: in non-IE browsers outside fields are already included in form.elements.
5874		if (formId && (semantic || /(Edge|Trident)\//.test(navigator.userAgent))) {
5875			els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
5876			if (els2.length) {
5877				els = (els || []).concat(els2);
5878			}
5879		}
5880
5881		if (!els || !els.length) {
5882			return a;
5883		}
5884
5885		if ($.isFunction(filtering)) {
5886			els = $.map(els, filtering);
5887		}
5888
5889		var i, j, n, v, el, max, jmax;
5890
5891		for (i = 0, max = els.length; i < max; i++) {
5892			el = els[i];
5893			n = el.name;
5894			if (!n || el.disabled) {
5895				continue;
5896			}
5897
5898			if (semantic && form.clk && el.type === 'image') {
5899				// handle image inputs on the fly when semantic == true
5900				if (form.clk === el) {
5901					a.push({name: n, value: $(el).val(), type: el.type});
5902					a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y});
5903				}
5904				continue;
5905			}
5906
5907			v = $.fieldValue(el, true);
5908			if (v && v.constructor === Array) {
5909				if (elements) {
5910					elements.push(el);
5911				}
5912				for (j = 0, jmax = v.length; j < jmax; j++) {
5913					a.push({name: n, value: v[j]});
5914				}
5915
5916			} else if (feature.fileapi && el.type === 'file') {
5917				if (elements) {
5918					elements.push(el);
5919				}
5920
5921				var files = el.files;
5922
5923				if (files.length) {
5924					for (j = 0; j < files.length; j++) {
5925						a.push({name: n, value: files[j], type: el.type});
5926					}
5927				} else {
5928					// #180
5929					a.push({name: n, value: '', type: el.type});
5930				}
5931
5932			} else if (v !== null && typeof v !== 'undefined') {
5933				if (elements) {
5934					elements.push(el);
5935				}
5936				a.push({name: n, value: v, type: el.type, required: el.required});
5937			}
5938		}
5939
5940		if (!semantic && form.clk) {
5941			// input type=='image' are not found in elements array! handle it here
5942			var $input = $(form.clk), input = $input[0];
5943
5944			n = input.name;
5945
5946			if (n && !input.disabled && input.type === 'image') {
5947				a.push({name: n, value: $input.val()});
5948				a.push({name: n + '.x', value: form.clk_x}, {name: n + '.y', value: form.clk_y});
5949			}
5950		}
5951
5952		return a;
5953	};
5954
5955	/**
5956	 * Serializes form data into a 'submittable' string. This method will return a string
5957	 * in the format: name1=value1&amp;name2=value2
5958	 */
5959	$.fn.formSerialize = function(semantic) {
5960		// hand off to jQuery.param for proper encoding
5961		return $.param(this.formToArray(semantic));
5962	};
5963
5964	/**
5965	 * Serializes all field elements in the jQuery object into a query string.
5966	 * This method will return a string in the format: name1=value1&amp;name2=value2
5967	 */
5968	$.fn.fieldSerialize = function(successful) {
5969		var a = [];
5970
5971		this.each(function() {
5972			var n = this.name;
5973
5974			if (!n) {
5975				return;
5976			}
5977
5978			var v = $.fieldValue(this, successful);
5979
5980			if (v && v.constructor === Array) {
5981				for (var i = 0, max = v.length; i < max; i++) {
5982					a.push({name: n, value: v[i]});
5983				}
5984
5985			} else if (v !== null && typeof v !== 'undefined') {
5986				a.push({name: this.name, value: v});
5987			}
5988		});
5989
5990		// hand off to jQuery.param for proper encoding
5991		return $.param(a);
5992	};
5993
5994	/**
5995	 * Returns the value(s) of the element in the matched set. For example, consider the following form:
5996	 *
5997	 *	<form><fieldset>
5998	 *		<input name="A" type="text">
5999	 *		<input name="A" type="text">
6000	 *		<input name="B" type="checkbox" value="B1">
6001	 *		<input name="B" type="checkbox" value="B2">
6002	 *		<input name="C" type="radio" value="C1">
6003	 *		<input name="C" type="radio" value="C2">
6004	 *	</fieldset></form>
6005	 *
6006	 *	var v = $('input[type=text]').fieldValue();
6007	 *	// if no values are entered into the text inputs
6008	 *	v === ['','']
6009	 *	// if values entered into the text inputs are 'foo' and 'bar'
6010	 *	v === ['foo','bar']
6011	 *
6012	 *	var v = $('input[type=checkbox]').fieldValue();
6013	 *	// if neither checkbox is checked
6014	 *	v === undefined
6015	 *	// if both checkboxes are checked
6016	 *	v === ['B1', 'B2']
6017	 *
6018	 *	var v = $('input[type=radio]').fieldValue();
6019	 *	// if neither radio is checked
6020	 *	v === undefined
6021	 *	// if first radio is checked
6022	 *	v === ['C1']
6023	 *
6024	 * The successful argument controls whether or not the field element must be 'successful'
6025	 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
6026	 * The default value of the successful argument is true. If this value is false the value(s)
6027	 * for each element is returned.
6028	 *
6029	 * Note: This method *always* returns an array. If no valid value can be determined the
6030	 *	array will be empty, otherwise it will contain one or more values.
6031	 */
6032	$.fn.fieldValue = function(successful) {
6033		for (var val = [], i = 0, max = this.length; i < max; i++) {
6034			var el = this[i];
6035			var v = $.fieldValue(el, successful);
6036
6037			if (v === null || typeof v === 'undefined' || (v.constructor === Array && !v.length)) {
6038				continue;
6039			}
6040
6041			if (v.constructor === Array) {
6042				$.merge(val, v);
6043			} else {
6044				val.push(v);
6045			}
6046		}
6047
6048		return val;
6049	};
6050
6051	/**
6052	 * Returns the value of the field element.
6053	 */
6054	$.fieldValue = function(el, successful) {
6055		var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
6056
6057		if (typeof successful === 'undefined') {
6058			successful = true;
6059		}
6060
6061		/* eslint-disable no-mixed-operators */
6062		if (successful && (!n || el.disabled || t === 'reset' || t === 'button' ||
6063			(t === 'checkbox' || t === 'radio') && !el.checked ||
6064			(t === 'submit' || t === 'image') && el.form && el.form.clk !== el ||
6065			tag === 'select' && el.selectedIndex === -1)) {
6066		/* eslint-enable no-mixed-operators */
6067			return null;
6068		}
6069
6070		if (tag === 'select') {
6071			var index = el.selectedIndex;
6072
6073			if (index < 0) {
6074				return null;
6075			}
6076
6077			var a = [], ops = el.options;
6078			var one = (t === 'select-one');
6079			var max = (one ? index + 1 : ops.length);
6080
6081			for (var i = (one ? index : 0); i < max; i++) {
6082				var op = ops[i];
6083
6084				if (op.selected && !op.disabled) {
6085					var v = op.value;
6086
6087					if (!v) { // extra pain for IE...
6088						v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
6089					}
6090
6091					if (one) {
6092						return v;
6093					}
6094
6095					a.push(v);
6096				}
6097			}
6098
6099			return a;
6100		}
6101
6102		return $(el).val().replace(rCRLF, '\r\n');
6103	};
6104
6105	/**
6106	 * Clears the form data. Takes the following actions on the form's input fields:
6107	 *  - input text fields will have their 'value' property set to the empty string
6108	 *  - select elements will have their 'selectedIndex' property set to -1
6109	 *  - checkbox and radio inputs will have their 'checked' property set to false
6110	 *  - inputs of type submit, button, reset, and hidden will *not* be effected
6111	 *  - button elements will *not* be effected
6112	 */
6113	$.fn.clearForm = function(includeHidden) {
6114		return this.each(function() {
6115			$('input,select,textarea', this).clearFields(includeHidden);
6116		});
6117	};
6118
6119	/**
6120	 * Clears the selected form elements.
6121	 */
6122	$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
6123		var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
6124
6125		return this.each(function() {
6126			var t = this.type, tag = this.tagName.toLowerCase();
6127
6128			if (re.test(t) || tag === 'textarea') {
6129				this.value = '';
6130
6131			} else if (t === 'checkbox' || t === 'radio') {
6132				this.checked = false;
6133
6134			} else if (tag === 'select') {
6135				this.selectedIndex = -1;
6136
6137			} else if (t === 'file') {
6138				if (/MSIE/.test(navigator.userAgent)) {
6139					$(this).replaceWith($(this).clone(true));
6140				} else {
6141					$(this).val('');
6142				}
6143
6144			} else if (includeHidden) {
6145				// includeHidden can be the value true, or it can be a selector string
6146				// indicating a special test; for example:
6147				// $('#myForm').clearForm('.special:hidden')
6148				// the above would clean hidden inputs that have the class of 'special'
6149				if ((includeHidden === true && /hidden/.test(t)) ||
6150					(typeof includeHidden === 'string' && $(this).is(includeHidden))) {
6151					this.value = '';
6152				}
6153			}
6154		});
6155	};
6156
6157
6158	/**
6159	 * Resets the form data or individual elements. Takes the following actions
6160	 * on the selected tags:
6161	 * - all fields within form elements will be reset to their original value
6162	 * - input / textarea / select fields will be reset to their original value
6163	 * - option / optgroup fields (for multi-selects) will defaulted individually
6164	 * - non-multiple options will find the right select to default
6165	 * - label elements will be searched against its 'for' attribute
6166	 * - all others will be searched for appropriate children to default
6167	 */
6168	$.fn.resetForm = function() {
6169		return this.each(function() {
6170			var el = $(this);
6171			var tag = this.tagName.toLowerCase();
6172
6173			switch (tag) {
6174			case 'input':
6175				this.checked = this.defaultChecked;
6176				// fall through
6177
6178			case 'textarea':
6179				this.value = this.defaultValue;
6180
6181				return true;
6182
6183			case 'option':
6184			case 'optgroup':
6185				var select = el.parents('select');
6186
6187				if (select.length && select[0].multiple) {
6188					if (tag === 'option') {
6189						this.selected = this.defaultSelected;
6190					} else {
6191						el.find('option').resetForm();
6192					}
6193				} else {
6194					select.resetForm();
6195				}
6196
6197				return true;
6198
6199			case 'select':
6200				el.find('option').each(function(i) {				// eslint-disable-line consistent-return
6201					this.selected = this.defaultSelected;
6202					if (this.defaultSelected && !el[0].multiple) {
6203						el[0].selectedIndex = i;
6204
6205						return false;
6206					}
6207				});
6208
6209				return true;
6210
6211			case 'label':
6212				var forEl = $(el.attr('for'));
6213				var list = el.find('input,select,textarea');
6214
6215				if (forEl[0]) {
6216					list.unshift(forEl[0]);
6217				}
6218
6219				list.resetForm();
6220
6221				return true;
6222
6223			case 'form':
6224				// guard against an input with the name of 'reset'
6225				// note that IE reports the reset function as an 'object'
6226				if (typeof this.reset === 'function' || (typeof this.reset === 'object' && !this.reset.nodeType)) {
6227					this.reset();
6228				}
6229
6230				return true;
6231
6232			default:
6233				el.find('form,input,label,select,textarea').resetForm();
6234
6235				return true;
6236			}
6237		});
6238	};
6239
6240	/**
6241	 * Enables or disables any matching elements.
6242	 */
6243	$.fn.enable = function(b) {
6244		if (typeof b === 'undefined') {
6245			b = true;
6246		}
6247
6248		return this.each(function() {
6249			this.disabled = !b;
6250		});
6251	};
6252
6253	/**
6254	 * Checks/unchecks any matching checkboxes or radio buttons and
6255	 * selects/deselects and matching option elements.
6256	 */
6257	$.fn.selected = function(select) {
6258		if (typeof select === 'undefined') {
6259			select = true;
6260		}
6261
6262		return this.each(function() {
6263			var t = this.type;
6264
6265			if (t === 'checkbox' || t === 'radio') {
6266				this.checked = select;
6267
6268			} else if (this.tagName.toLowerCase() === 'option') {
6269				var $sel = $(this).parent('select');
6270
6271				if (select && $sel[0] && $sel[0].type === 'select-one') {
6272					// deselect all other options
6273					$sel.find('option').selected(false);
6274				}
6275
6276				this.selected = select;
6277			}
6278		});
6279	};
6280
6281	// expose debug var
6282	$.fn.ajaxSubmit.debug = false;
6283
6284	// helper fn for console logging
6285	function log() {
6286		if (!$.fn.ajaxSubmit.debug) {
6287			return;
6288		}
6289
6290		var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, '');
6291
6292		if (window.console && window.console.log) {
6293			window.console.log(msg);
6294
6295		} else if (window.opera && window.opera.postError) {
6296			window.opera.postError(msg);
6297		}
6298	}
6299}));
6300;
6301window.pageHandleResponsive = true;
6302
6303$.when(window.Pages.rwdReady).done(function () {
6304
6305	toggleClassMedia("maxWidth600px", ".cpForm:media(this-max-width: 600px)");
6306	toggleClassMedia("maxWidth515px", "#FormCenterContent:media(this-max-width: 515px)");
6307	toggleClassMedia("maxWidth485px", "#FormCenterContent:media(this-max-width: 485px)");
6308	toggleClassMedia("maxWidth440px", ".cpForm:media(this-max-width: 440px)");
6309	toggleClassMedia("maxWidth395px", "#FormCenterContent:media(this-max-width: 395px)");
6310	toggleClassMedia("maxWidth380px", ".cpForm:media(this-max-width: 380px)");
6311});
6312

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.