PageSourceSearch

https://www.wherewisemenfish.com/scripts/cp_forms.js?m=1771842272

js wherewisemenfish.com collected 2026-09-26 10:06:17 UTC 49,249 bytes, 1,589 lines download raw bytes

1
2
3
4function cp_forms_init($form, form_templates, form_options) {
5
6	initFormStyles({$wrapper: $form});
7
8	$form.data('form_templates', form_templates);
9
10	$form.find('.removeBlockBtn').click(cp_forms_remove_block_click);
11	$form.find('[id^="addFieldsBtn"]').click(cp_forms_add_field_click);
12
13	$form.submit(cp_forms_submit);
14
15	if(form_options.warnBeforeUnload)
16	{
17		cp_forms_init_warn_before_unload($form);
18	}
19
20	add_client_referrer_data_to_form($form);
21
22	if($form.find('.btn.cpFormSubmit').length > 0)
23	{
24		var $cpFormSubmit = $form.find('.btn.cpFormSubmit').click(function(){$form.submit();});
25		$form.data("cpFormsSubmitButtons", $cpFormSubmit);
26	}
27}
28
29function add_client_referrer_data_to_form($form) {
30
31	if (cp.config.recruitment.client_referrer_collection_enabled && !cp.in_cp && $form.find('input[name=client_referrer]').length > 0)
32	{
33		var ccd_client_referrer = ccd_client_referrer_cookie();
34
35		if(ccd_client_referrer != null)
36		{
37			$form.find('input[name=client_referrer]').val(ccd_client_referrer);
38		}
39	}
40}
41
42function cp_forms_init_warn_before_unload($form) {
43	$form.change(cp_forms_on_change);
44	var formHash = $form.serialize().toHash();
45	$form.data("initial_hash", formHash);
46}
47
48function cp_forms_on_change($form) {
49	cp_forms_update_before_unload($(this).closest('form'));
50}
51
52function cp_forms_update_before_unload($form, turnOff) {
53	if(turnOff === true || $form.serialize().toHash() == $form.data("initial_hash"))
54	{
55		window.onbeforeunload = null;
56	}
57	else {
58		window.onbeforeunload = function(e) {
59			var dialogText = 'You have unsaved changes in your form.';
60			e.returnValue = dialogText;
61			return dialogText;
62		};
63	}
64}
65
66function cp_forms_remove_block_click() {
67
68	var $form = $(this).closest('form');
69	var use_table = $form.find('input[name="use_table"]').val() == 1;
70
71	var id_split = $(this).attr('id').replace("removeBlockBtn", "").split("_");
72	var block_id = id_split[0];
73	var grandparent_form_id = id_split[1];
74	var grandparent_form_seq = id_split[2];
75	var parent_form_id = id_split[3];
76	var parent_form_seq = id_split[4];
77	var block_seq = id_split[5];
78
79	var block_suffix = block_id+"_"+grandparent_form_id+"_"+grandparent_form_seq+"_"+parent_form_id+"_"+parent_form_seq;
80
81	var $blockWrapper = $('#blockWrapper'+block_suffix+"_"+block_seq);
82
83	var tr_or_div = use_table ? "tr" : "div";
84
85	numBlocksRemaining = $form.find(tr_or_div+'[id^="blockWrapper'+block_suffix+'_"]').length;
86
87	var animate = numBlocksRemaining > 1;
88
89	var $next = $blockWrapper.next();
90	var $prev = $blockWrapper.prev();
91
92	var $closestDivider = $next.is('.blockDivider') ? $blockWrapper.next() : $blockWrapper.prev();
93
94	var removeDivider = numBlocksRemaining > 1 && $closestDivider.length > 0;
95
96	if(animate)
97	{
98		if(removeDivider)
99		{
100			var animTo = {
101				  height: 0
102				, paddingTop: 0
103				, paddingBottom: 0
104				, marginTop: 0
105				, marginBottom: 0
106				, opacity: 0
107			};
108
109			$closestDivider.animate(animTo, 200, 'easeOutSine', function(){ $(this).remove(); });
110		}
111
112		$blockWrapper.addClass("removing").find('.blockAnimWrapper').animate(animTo, 200, 'easeOutSine');
113
114		setTimeout(function(){
115			$blockWrapper.remove();
116		}, 200);
117	}
118	else
119	{
120		if(removeDivider)
121		{
122			$closestDivider.remove();
123		}
124		$blockWrapper.remove();
125	}
126
127	if(numBlocksRemaining == 1)
128	{
129		cp_forms_add_field($form.find('#addFieldsBtn'+block_suffix), false);
130	}
131
132	cp_forms_update_before_unload($form);
133}
134
135function cp_forms_add_field_click() {
136	cp_forms_add_field($(this));
137}
138
139function cp_forms_add_field($btn, animate) {
140
141	animate = animate == null ? true : animate;
142
143	var $form = $btn.closest('form');
144
145	var id_split = $btn.attr('id').split("_");
146
147	var block_id = id_split[0].replace("addFieldsBtn", "");
148	var grandparent_form_id = parseInt(id_split[1]);
149	var grandparent_form_seq = parseInt(id_split[2]);
150	var parent_form_id = parseInt(id_split[3]);
151	var parent_form_seq = parseInt(id_split[4]);
152
153	var use_table = $form.find('input[name="use_table"]').val() == 1;
154
155	var dividerHTML = use_table ? '<tr class=\"blockDivider\"><td colspan=\"999\"></td></tr>' : '<div class="blockDivider"></div>';
156	var tr_or_div = use_table ? "tr" : "div";
157
158	var last_block_wrapper_selector = tr_or_div+'[id^="blockWrapper'+block_id+'_'+grandparent_form_id+'_'+grandparent_form_seq+'_'+parent_form_id+'_'+parent_form_seq+'_"]';
159	var $last_block_wrapper = $form.find(last_block_wrapper_selector).last();
160
161	var newSeq = 0;
162
163	if($last_block_wrapper.length > 0)
164	{
165		var last_block_wrapper_id = $last_block_wrapper.attr("id");
166		id_split = last_block_wrapper_id.split("_");
167		newSeq = parseInt(id_split[5]) + 1;
168	}
169
170	var form_html_and_javascript = cp_forms_process_form_templates($form, block_id, grandparent_form_seq, parent_form_seq, newSeq);
171
172	var $block_wrapper = $(form_html_and_javascript.html);
173
174	$block_wrapper.find('.removeBlockBtn').click(cp_forms_remove_block_click);
175	$block_wrapper.find('[id^="addFieldsBtn"]').click(cp_forms_add_field_click);
176
177	var $block_divider = animate ? $(dividerHTML) : "";
178
179	if($block_wrapper.first().hasClass('blockDivider'))
180	{//block already has a divider - we need this sometimes for subforms
181		$block_wrapper = $block_wrapper.last();
182	}
183
184	if(use_table)
185	{
186		$btn.closest('tr').before($block_divider, $block_wrapper);
187	}
188	else
189	{
190		$btn.before($block_divider, $block_wrapper);
191	}
192
193	eval(form_html_and_javascript.javascript);
194	initFormStyles({$wrapper: $block_wrapper});
195
196	if(animate)
197	{
198		var animFrom = {
199			  height: 0
200			, paddingTop: 0
201			, paddingBottom: 0
202			, marginTop: 0
203			, marginBottom: 0
204			, opacity: 0
205		};
206
207		var animTo = {
208			  height: $block_divider[0].scrollHeight
209			, paddingTop: $block_divider.css("padding-top")
210			, paddingBottom: $block_divider.css("padding-bottom")
211			, marginTop: $block_divider.css("margin-top")
212			, marginBottom: $block_divider.css("margin-bottom")
213			, opacity: 1
214		};
215
216		$block_divider.animate(animFrom, 0).animate(animTo, 200, 'easeOutSine', function(){ $block_divider.height(""); });
217
218		animTo = {
219			  height: $block_wrapper[0].scrollHeight
220			, paddingTop: $block_wrapper.css("padding-top")
221			, paddingBottom: $block_wrapper.css("padding-bottom")
222			, marginTop: $block_wrapper.css("margin-top")
223			, marginBottom: $block_wrapper.css("margin-bottom")
224			, opacity: 1
225		};
226
227		$block_wrapper.find('.blockAnimWrapper').first().animate(animFrom, 0).animate(animTo, 200, 'easeOutSine', function(){ $(this).height("").css('overflow', 'visible'); });
228	}
229
230	cp_forms_update_before_unload($form);
231}
232
233function cp_forms_process_form_templates($form, template_id, grandparent_form_seq, parent_form_seq, newSeq, html_only, javascript_only) {
234
235	html_only = html_only == null ? false : html_only == true;
236	javascript_only = javascript_only == null ? false : javascript_only == true;
237
238	var form_templates = $form.data('form_templates');
239	var form_template = form_templates[template_id];
240
241	var keys = ["{{grandparent_form_seq}}", "{{parent_form_seq}}", "{{seq}}"];
242	var replacements = [grandparent_form_seq, parent_form_seq, newSeq];
243
244	var newHTML = "";
245	var newScripts = "";
246
247	if(!javascript_only) //normally only used on subsequent recursions
248	{
249		/******************************************/
250		/******** replace HTML recursively ********/
251		/******************************************/
252
253		newHTML = str_replace(keys, replacements, form_template.html);
254
255		var html_regex = /{{subform_html_(\d+)}}/g;
256		var html_result;
257		while ((html_result = html_regex.exec(newHTML)) !== null) {
258
259			var subform = cp_forms_process_form_templates($form, parseInt(html_result[1]), parent_form_seq, newSeq, 0, true);
260
261			newHTML = newHTML.slice(0, html_result.index) + subform.html + newHTML.slice(html_result.index+html_result[0].length);
262
263			// regex is stateful, reset its index as we have modified newHTML
264			html_regex.lastIndex = 0;
265		}
266	}
267
268	if(!html_only) //normally only used on subsequent recursions
269	{
270		/******************************************/
271		/***** replace javascript recursively *****/
272		/******************************************/
273
274		newScripts = str_replace(keys, replacements, form_template.javascript);
275
276		var javascript_regex = new RegExp(/{{subform_javascript_(\d+)}}/g);
277		var javascript_result;
278		while ((javascript_result = javascript_regex.exec(newScripts)) !== null) {
279
280			var subform = cp_forms_process_form_templates($form, parseInt(javascript_result[1]), parent_form_seq, newSeq, 0, false, true);
281
282			newScripts = newScripts.slice(0, javascript_result.index) + subform.javascript + newScripts.slice(javascript_result.index+javascript_result[0].length);
283
284			// regex is stateful, reset its index as we have modified newScripts
285			javascript_regex.lastIndex = 0;
286		}
287	}
288
289	return({html: newHTML, javascript: newScripts});
290}
291
292function cp_forms_trigger_subform(e) {
293
294	var subforms = $(this).data('subforms');
295	var val = $(this).val();
296	var subform_cpda_id = subforms[val];
297
298	var id_split = $(this).attr('id').split("_");
299
300	var grandparent_form_id = parseInt(id_split[1]);
301	var grandparent_form_seq = parseInt(id_split[2]);
302	var parent_form_id = parseInt(id_split[3]);
303	var parent_form_seq = parseInt(id_split[4]);
304	var seq = parseInt(id_split[5]);
305
306	for(var i in subforms)
307	{
308		if(subforms.hasOwnProperty(i))
309		{
310			var subform_id = 'cpForm'+subforms[i]+'_'+grandparent_form_id+'_'+grandparent_form_seq+'_'+parent_form_id+'_'+parent_form_seq+'_'+seq;
311			var $subFormWrapper = $('#'+subform_id);
312
313			if($subFormWrapper.length > 0)
314			{
315				if(typeof $subFormWrapper.data("subformOn") == "undefined")
316				{
317					$subFormWrapper.data("subformOn", $subFormWrapper.hasClass('on'));
318				}
319
320				if(subforms[i] != subform_cpda_id)
321				{
322					if($subFormWrapper.data("subformOn") == true)
323					{
324						cp_forms_subform_anim_close($subFormWrapper);
325					}
326				}
327				else
328				{
329					if($subFormWrapper.data("subformOn") != true)
330					{
331						cp_forms_subform_anim_open($subFormWrapper);
332					}
333				}
334			}
335		}
336	}
337}
338
339function cp_forms_subform_anim_close($subFormWrapper) {
340
341	$subFormWrapper.data("subformOn", false).stop().removeClass('on');
342
343	var $subFormAnimWrapper = $subFormWrapper.find('.subFormAnimWrapper').first();
344
345	var animTo = {
346		  height: 0
347		, paddingTop: 0
348		, paddingBottom: 0
349		, marginTop: 0
350		, marginBottom: 0
351		, opacity: 0
352	};
353
354	$subFormAnimWrapper.stop().animate(animTo, 200, 'easeOutSine', function(){
355		$(this).height("");
356		$subFormWrapper.addClass('off');
357	});
358}
359
360function cp_forms_subform_anim_open($subFormWrapper) {
361
362	$subFormWrapper.data("subformOn", true).stop().removeClass('off').addClass('on');
363
364	var $subFormAnimWrapper = $subFormWrapper.find('.subFormAnimWrapper').first();
365
366	var animFrom = {
367		  height: 0
368		, paddingTop: 0
369		, paddingBottom: 0
370		, marginTop: 0
371		, marginBottom: 0
372		, opacity: 0
373	};
374
375	var animTo = {
376		  height: $subFormAnimWrapper[0].scrollHeight
377		, paddingTop: $subFormAnimWrapper.css("padding-top")
378		, paddingBottom: $subFormAnimWrapper.css("padding-bottom")
379		, marginTop: $subFormAnimWrapper.css("margin-top")
380		, marginBottom: $subFormAnimWrapper.css("margin-bottom")
381		, opacity: 1
382	};
383
384	$subFormAnimWrapper.stop().animate(animFrom, 0).animate(animTo, 200, 'easeOutSine', function(){
385		$(this).css({height: "", overflow: "visible"});
386	});
387}
388
389function cp_forms_sort_code_change(e) {
390	if($(this).val().toString().length == 2 && (mobile || this.selectionStart == 2))
391	{
392		$(this).nextAll("input").first().focus().select();
393	}
394}
395
396function cp_forms_document_upload_on_complete(id, fileName, responseJSON) {
397
398	var fieldName = this._element.id.replace('_upload_drop_zone', '');
399
400	var qtip_api = $('#'+fieldName+'_upload_button').removeClass("cpFormsInvalid").qtip('api');
401	if(typeof qtip_api != "undefined")
402	{
403		qtip_api.destroy();
404	}
405
406	if(responseJSON.error)
407	{
408		alert(responseJSON.error);
409	}
410	else
411	{
412		$(this._element).find('.qq-upload-list').empty();
413
414		if(typeof responseJSON.uploadName != "undefined")
415		{
416			cp_forms_document_set_file(fieldName, fileName, responseJSON.uploadName);
417		}
418	}
419}
420
421function cp_forms_document_set_file(fieldName, original_filename, filesystem_filename) {
422
423	original_filename = original_filename.replace(',', '');
424
425	$('#'+fieldName+'_upload_filename_new').val(original_filename + ',' + filesystem_filename);
426	$('#'+fieldName+'_upload_filename_text').html(original_filename);
427
428	$('#'+fieldName+'_new_file_wrapper').hide();
429	$('#'+fieldName+'_existing_file_wrapper').show();
430
431}
432
433function cp_forms_document_remove_click(e) {
434
435	var fieldName = $(this).attr('id').replace('_remove_button', '');
436	cp_forms_document_remove_file(fieldName);
437
438}
439
440function cp_forms_document_remove_file(fieldName) {
441
442	$('#'+fieldName+'_upload_filename_new').val('');
443	$('#'+fieldName+'_upload_filename_text').html('');
444
445	$('#'+fieldName+'_new_file_wrapper').removeClass('processing').show();
446	$('#'+fieldName+'_existing_file_wrapper').hide();
447
448}
449
450function cp_forms_document_upload_on_error(id, fileName, errorReason) {
451	alert("Error uploading file, please try again");
452	var fieldName = this._element.id.replace('_upload_drop_zone', '');
453	cp_forms_document_remove_file(fieldName);
454}
455
456function cp_forms_document_upload_on_upload() {
457	var fieldName = this._element.id.replace('_upload_drop_zone', '');
458	$('#'+fieldName+'_new_file_wrapper').addClass('processing');
459
460	var qtip_api = $('#'+fieldName+'_upload_button').removeClass("cpFormsInvalid").qtip('api');
461	if(typeof qtip_api != "undefined")
462	{
463		qtip_api.destroy();
464	}
465}
466
467//has some bug where it sometimes picks up hidden but mandatory fields?
468function cp_client_side_validation($form){
469
470
471	var error_count = 0;
472	var error_details = {
473		  errors : []
474		 ,form_cpda_id : $form.find('input[name=form_cpda_id]').val()
475		 ,success : false
476
477	};
478
479	$form.find('.mandatory').each(function(){
480		$input_field = $(this).find('input, textarea, select');
481		$users_input = $input_field.val();
482		if($users_input.length < 1){
483			error_count++;
484			error_item = {
485				 error_code : 0
486				,field_name : $input_field.attr("name")
487				,manditory : true
488				,field_title : $input_field.attr("placeholder")
489			};
490			error_details.errors.push(error_item);
491		}
492	});
493
494
495	$form.find('.set_fieldtype_form_consent_email, .set_fieldtype_form_email').each(function(){
496		$input_field = $(this).find('input');
497		$users_input = $input_field.val();
498		//the length check is to avoid empty optional email addresses failing validation
499		if($users_input.length > 0 && !validateEmail($users_input)){
500			error_count++;
501			error_item = {
502				 error_code : 2
503				,field_name : $input_field.attr("name")
504				,manditory : true
505				,field_title : $input_field.attr("placeholder")
506			};
507			error_details.errors.push(error_item);
508		}
509	});
510
511
512
513	if(error_count > 0){
514		cp_forms_submission_success(error_details, $form, true, null);
515		return false;
516	}
517
518	return true;
519}
520
521function cp_forms_submit(e) {
522
523	e.preventDefault();
524
525	var $form = $(this);
526
527	if(!navigator.onLine && !cp_client_side_validation($form)){
528		return false;
529	}
530
531	if(cp_forms_prepare_for_submission($form))
532	{
533
534		//check if offline. if offline, check for service worker. cache submission if so.
535		//I'm disabling this for now because it should probably be on a flag. we don't have a robust process for handling offline submissions which fail validation. addressible with complete client-side validation. we have partial client side navigation at the moment and it has proven to be buggy, so we can't rely on that yet.
536		if(!navigator.onLine && false){
537
538			$form_json = $form.serializeArray();
539
540			let offline_submitted_forms;
541
542			if (localStorage.getItem('offline_submitted_forms')) {
543				offline_submitted_forms = JSON.parse(localStorage.getItem('offline_submitted_forms'));
544			} else {
545				offline_submitted_forms = [];
546			}
547
548			offline_submitted_forms.push($form_json);
549
550			localStorage.setItem('offline_submitted_forms', JSON.stringify(offline_submitted_forms));
551			console.log("You are offline! We will hold a copy of your form in your local browser storage. We'll check periodically if you are online and then the form will submit.");
552
553			//normally the server responds with some stuff including the form_cpda_id which is then passed to the callbackfunction (if present). in this scenario lets grab it from the hidden form input
554			let form_cpda_id;
555			for (index = 0; index < $form_json.length; ++index) {
556				if($form_json[index]["name"] == "form_cpda_id"){
557			    	form_cpda_id = $form_json[index]["value"];
558				}
559			}
560
561			//and now call the success callback
562			if(typeof cp_forms_callback_success != "function" || cp_forms_callback_success($form, form_cpda_id) === false)
563			{
564				$form.find('.cpFormWrapper').slideUp(300);
565				$form.append('<p class="cpFormDefaultSuccessText">You appear to be offline, but we will submit your form as soon as you come back online.</p>');
566			}
567
568		}
569		else{
570			//lets submit
571			var submit_action = $form.attr('action') == null ? "cp_forms.php" : $form.attr('action');
572			$.ajax({
573				type : 'POST',
574				url : submit_action,
575				dataType : 'json',
576				data: $form.serializeArray(),
577				success : function(data){
578					cp_forms_processFormResponse(data, $form);
579				},
580				error:  function(XMLHttpRequest, textStatus, errorThrown) {
581					cp_forms_submission_error($form, XMLHttpRequest);
582				}
583			});
584		}
585
586
587	}
588
589	return false;
590}
591
592function cp_forms_clear_errors($form) {
593
594	$('.qtip').each(function(){
595		cp_forms_destroy_qtip($(this));
596	});
597
598	$form.find(".cpFormsInvalid").each(function(){
599		cp_forms_destroy_qtip($(this).removeClass("cpFormsInvalid"));
600	});
601
602}
603
604function cp_forms_destroy_qtip($elm) {
605	var qtip = $elm.qtip('api') ? $elm.qtip('api') : $elm.data('qtip');
606	if(qtip)
607	{
608		qtip.destroy(true);
609	}
610}
611
612function cp_forms_prepare_for_submission($form) {
613
614	if($form.data("cpFormsSubmitting") != true)
615	{
616		cp_forms_update_before_unload($form, true);
617
618		cp_forms_clear_errors($form);
619
620		$form.data("cpFormsSubmitting", true);
621		$form.data("cpFormsSubmitButtons").addClass('loading');
622
623		return true;
624	}
625
626	return false;
627}
628
629function cp_forms_submission_error($form, XMLHttpRequest) {
630	cp_forms_update_before_unload($form);
631	$form.data("cpFormsSubmitButtons").removeClass('loading');
632	$form.data("cpFormsSubmitting", false);
633	if(cp.in_cp)
634	{
635		cp.show_error('Form error', 'cp_forms_submission_error', null, XMLHttpRequest);
636	}
637	else
638	{
639		console.log("Form error: ", XMLHttpRequest.responseText);
640	}
641}
642
643//error codes
644/*
645-1 = this is a custom message from serverside
6460 = mandatory field left blank
6471 = confirm field left blank
6482 = invalid email address
6493 = email address confirm field does not match
6504 = invalid password
6515 = mandatory checkbox not checked
6526 = select menu option not selected
6537 = year range to is less than from
6548 = mandatory value not present
6559 = NMC or HPC PIN must be 8 characters long
65610 = NMC or HPC PIN must contain only letters and numbers
65711 = no data submitted
65812 = invalid captcha
65913 = mandatory yes/no not checked
66014 = multi select menu option not selected
66115 = value is not numeric
66216 = subform missing
66317 = bank account number invalid
66418 = sort code invalid
66519 = mandatory file not uploaded
66620 = date too far in future
66721 = salary lower than minimum wage
66822 = too many options selected
66923 = too many words
67024 = vat number invalid
67125 = email address already in use
67226 = app portal - mandatory data is missing from a subform
67327 = manage my data form too many requests
67428 = date of birth too far in future
67529 = number should be 10 digits
67630 = password confirm field does not match
67731 = National Insurance Number must be 9 characters long
67832 = National Insurance Number must be in the format AA999999A
67933 = must have a from and to date
680*/
681var cpFormResponseMessages = {
682	  "0": "This field cannot be left blank"
683	, "1": "This field cannot be left blank"
684	, "2": "Please type a valid email address"
685	, "3": "Email addresses do not match"
686	, "4": "Password must be at least 8 characters and include at least one letter and one number"
687	, "5": "This field must be checked"
688	, "6": "You must select an option"
689	, "7": "'From' date must not be more recent than 'To'"
690	, "8": "{{field_title}} is missing"
691	, "9": "NMC or HPC PIN must be 8 characters long"
692	, "10": "NMC or HPC PIN must be in the format 99A9999A"
693	, "11": "No data was submitted"
694	, "12": "Captcha check failed, please try again"
695	, "13": "You must answer yes"
696	, "14": "You must select at least one option"
697	, "15": "This field must contain only numbers"
698	, "16": "Some data is missing"
699	, "17": "{{field_title}} must be 8 digits"
700	, "18": "{{field_title}} must be 6 digits"
701	, "19": "No file was uploaded"
702	, "20": "Date too far in future"
703	, "21": "The salary you have specified is lower than minimum wage"
704	, "22": "You must select no more than {{max_options}} options"
705	, "23": "Please enter no more than {{max_words}} words"
706	, "24": "VAT number is invalid"
707	, "25": "Email address is already in use"
708	, "26": "" // app portal - mandatory data is missing from a subform
709	, "27": "You have submitted too many requests, please try again later"
710	, "28": "Invalid Date of Birth"
711	, "29": "{{field_title}} must be {{string_length}} {{string_type}}"
712	, "30": "Passwords do not match"
713	, "33": "You must choose both a 'from' and 'to' date"
714	, "34": "{{field_title}} must be in the format '{{string_format}}'"
715};
716
717var cpFormMobileResponseMessages = {
718	  "0": "{{field_title}} cannot be left blank"
719	, "1": "{{field_title}} cannot be left blank"
720	, "2": "Please type a valid email address"
721	, "3": "Email addresses do not match"
722	, "4": "Password must be at least 8 characters and include at least one letter and one number"
723	, "5": "This field must be checked: {{field_title}}"
724	, "6": "You must select an option: {{field_title}}"
725	, "7": "'From' date must not be more recent than 'To'"
726	, "8": "{{field_title}} is missing"
727	, "9": "NMC or HPC PIN must be 8 characters long"
728	, "10": "NMC or HPC PIN must be in the format 99A9999A"
729	, "11": "No data was submitted"
730	, "12": "Captcha check failed, please try again"
731	, "13": "{{field_title}}: You must answer yes"
732	, "14": "{{field_title}}: You must select at least one option"
733	, "15": "{{field_title}} must contain only numbers"
734	, "16": "Some data is missing"
735	, "17": "{{field_title}} must be 8 digits"
736	, "18": "{{field_title}} must be 6 digits"
737	, "19": "{{field_title}}: No file was uploaded"
738	, "20": "{{field_title}}: Date too far in future"
739	, "21": "The salary you have specified is lower than minimum wage"
740	, "22": "{{field_title}}: You must select no more than {{max_options}} options"
741	, "23": "{{field_title}}: Please enter no more than {{max_words}} words"
742	, "24": "VAT number is invalid"
743	, "25": "{{field_title}}: Email address is already in use"
744	, "26": "" // app portal - mandatory data is missing from a subform
745	, "27": "You have submitted too many requests, please try again later"
746	, "28": "{{field_title}}: Invalid Date of Birth"
747	, "29": "{{field_title}} must be {{string_length}} {{string_type}}"
748	, "30": "Passwords do not match"
749	, "33": "You must choose both a 'from' and 'to' date"
750	, "34": "{{field_title}} must be in the format '{{string_format}}'"
751};
752
753function cp_forms_processFormResponse(data, $form, divider_cpda_id, skipDefaultFormSubmittedAnimation) {
754
755	var keepLoadingSpinnersOnSuccess = false;
756	skipDefaultFormSubmittedAnimation = skipDefaultFormSubmittedAnimation === true;
757	var showDefaultFormSubmittedAnimation = !skipDefaultFormSubmittedAnimation;
758
759	if(data.success)
760	{
761		if (data.form_cpda_id == "-18") {
762			cp_googleTagManagerTrack('job_application');
763  			cp_facebookPixelTrack('SubmitApplication');
764		}
765		else
766		{
767			cp_googleTagManagerTrack('form_submission');
768  			cp_facebookPixelTrack('FormSubmission', {form_cpda_id: data.form_cpda_id});
769		}
770
771		if(typeof cp_forms_callback == "function")
772		{
773			cp_forms_callback(data);
774		}
775
776		if(typeof cp_forms_callback_success == "function")
777		{
778			var callback_success_response = cp_forms_callback_success($form, data.form_cpda_id);
779
780			if(callback_success_response === true || callback_success_response === 1) // return true or a 1 to skip the default submitted animation
781			{
782				showDefaultFormSubmittedAnimation = false;
783			}
784			else if(callback_success_response === 2) // return 2 to skip the default submitted animation AND keep the loading spinners on
785			{
786				showDefaultFormSubmittedAnimation = false;
787				keepLoadingSpinnersOnSuccess = true;
788			}
789		}
790
791		if(showDefaultFormSubmittedAnimation)
792		{
793			$form.find('.cpFormWrapper').slideUp(300);
794			$form.append('<p class="cpFormDefaultSuccessText">Form submitted successfully.</p>');
795		}
796	}
797
798	return cp_forms_submission_success(data, $form, keepLoadingSpinnersOnSuccess, divider_cpda_id);
799}
800
801function cp_forms_submission_success(data, $form, keepLoadingSpinnersOnSuccess, divider_cpda_id) {
802
803	if(typeof CPO_DEBUG != "undefined" && CPO_DEBUG == true)
804	{
805		console.log("data: ", data);
806	}
807
808	keepLoadingSpinnersOnSuccess = keepLoadingSpinnersOnSuccess == true;
809
810	if(!data.success || !keepLoadingSpinnersOnSuccess)
811	{
812		if(typeof $form.data("cpFormsSubmitButtons") !== "undefined")
813		{
814			$form.data("cpFormsSubmitButtons").removeClass('loading');
815		}
816	}
817
818	if(data.success)
819	{
820		return true;
821	}
822
823	var responseMessages = mobile ? cpFormMobileResponseMessages : cpFormResponseMessages;
824
825	var $wrapper = $form == null ? $('body') : $form;
826
827	var $container = $('body');
828	if($form.parents('.cpMain').length > 0)
829	{
830		$container = $('#cpMain');
831	}
832
833	if($form != null)
834	{
835		$form.data("cpFormsSubmitting", false);
836	}
837
838	if($form.find('.set_fieldtype_form_captcha').length > 0)
839	{
840		grecaptcha.reset();
841	}
842
843	for(var j in data.errors)
844	{
845		if(data.errors[j].error_code == 26)
846		{
847			$('#popupFormContent').find('form').submit();
848			return false;
849		}
850	}
851
852	if(typeof window.cpFormsDontFocusOnError == "undefined")
853	{
854		window.cpFormsDontFocusOnError = [];
855	}
856
857	for(var i in data.errors)
858	{
859		if(data.errors.hasOwnProperty(i) && (divider_cpda_id == null || data.errors[i].divider_id == divider_cpda_id))
860		{
861			var $input = null;
862
863			if(data.errors[i].field_name != null && data.errors[i].field_name != "")
864			{
865				var field_name = data.errors[i].field_name;
866
867				var lookup_name = field_name;
868				if(data.errors[i].subfield_name_suffix)
869				{
870					lookup_name += data.errors[i].subfield_name_suffix;
871				}
872
873				var tokenArray = ["{{field_title}}", "{{max_words}}", "{{max_options}}", "{{string_length}}", "{{string_type}}", "{{string_format}}"];
874				var tokenReplaceArray = [data.errors[i].field_title, data.errors[i].max_words, data.errors[i].max_options, data.errors[i].string_length, data.errors[i].string_type, data.errors[i].string_format];
875				var errorMessage =
876					data.errors[i].error_code == -1 ? data.errors[i].custom_error_message
877					: str_replace(tokenArray, tokenReplaceArray, responseMessages[data.errors[i].error_code]);
878
879				if(data.errors[i].error_code != 12 && data.errors[i].error_code != 19)
880				{
881					var fieldNameLastChar = field_name.substr(field_name.length - 1);
882					if(fieldNameLastChar == "*")
883					{
884						var fieldNameWithoutLastChar = field_name.substr(0, field_name.length - 1);
885						$input = $wrapper.find('input[name^="'+fieldNameWithoutLastChar+'"]').first();
886						if($input.length == 0)
887						{
888							$input = $wrapper.find('textarea[name^="'+fieldNameWithoutLastChar+'"]').first();
889
890							if($input.length == 0)
891							{
892								if(mobile)
893								{
894									$input = $wrapper.find('select[name^="'+fieldNameWithoutLastChar+'"]').first();
895								}
896
897								if($input.length == 0)
898								{
899									$input = null;
900								}
901							}
902						}
903					}
904					else
905					{
906						$input = $wrapper.find('input[name="'+lookup_name+'"]');
907						if($input.length == 0)
908						{
909							$input = $wrapper.find('textarea[name="'+lookup_name+'"]');
910
911							if($input.length == 0 && mobile)
912							{
913								$input = $wrapper.find('select[name="'+lookup_name+'"]');
914							}
915						}
916					}
917
918					//if we have found the culprit input
919					if($input != null)
920					{
921						var fieldTypeAndElm = fsGetFieldTypeAndElm($input);
922						if(fieldTypeAndElm != false)
923						{
924							var fieldType = fieldTypeAndElm.fieldType;
925							var $field = fieldTypeAndElm.$elm;
926							var $tinyMCE = null;
927
928							if($field.is("textarea") && typeof tinyMCE != "undefined" && tinyMCE.get(lookup_name))
929							{
930								fieldType = "html";
931								$tinyMCE = $(tinyMCE.get(lookup_name).getContainer());
932							}
933
934							if(!mobile)
935							{
936								var tooltipOptions = {
937									  content: errorMessage
938								    , position: {
939								          my: 'left center'
940								        , at: 'right center'
941								        , viewport: $(window)
942								        , effect: false
943								        , container: $container
944								    }
945								    , show: {
946								          event: 'focus mouseenter'
947								        , effect: function(offset) {
948								            $(this).fadeIn(50);
949								        }
950								       	, ready: i == 0 && window.cpFormsDontFocusOnError.indexOf(parseInt(data.form_cpda_id)) !== -1
951								    }
952								    , hide: {
953								          event: 'blur change mouseleave'
954								        , effect: function(offset) {
955								            $(this).fadeOut(50);
956								        }
957								          // event: false
958								    }
959								    , style: {
960								          classes: 'cpErrorTooltip qtip-shadow'
961								        , tip: {
962								            width: 8
963								        }
964								    }
965								};
966
967								$.fn.qtip.zindex = 99999;
968
969								switch(fieldType) {
970									case "select":
971									case "formStyledSelect":
972										break;
973									case "checkbox":
974									case "formStyledCheckbox":
975									case "formStyledPlaceholderCheckbox":
976										tooltipOptions.position.my = 'top left';
977										tooltipOptions.position.at = 'bottom center';
978										tooltipOptions.position.adjust = {y: 2};
979										tooltipOptions.show.ready = true;
980										tooltipOptions.show.event = false;
981										tooltipOptions.hide.event = 'change';
982										tooltipOptions.hide.target = $input;
983										break;
984									case "formStyledRadio":
985										tooltipOptions.position.my = 'center right';
986										tooltipOptions.position.at = 'center left';
987										tooltipOptions.position.adjust = {x: -2};
988										$field = $field.first().parent().parent();
989										break;
990									case "html":
991										// tinyMCEObj
992										tooltipOptions.position.my = 'bottom right';
993										tooltipOptions.position.at = 'top right';
994										tooltipOptions.position.target = $tinyMCE;
995										tooltipOptions.position.adjust = {x: 2};
996										tooltipOptions.show.ready = true;
997										tooltipOptions.show.event = false;
998										tooltipOptions.hide.event = false;
999										break;
1000								}
1001
1002								if($field.data("qtipOptions") != undefined)
1003								{
1004									$.extend(tooltipOptions, $field.data("qtipOptions"));
1005								}
1006								else if($field.hasClass("cpFormsTooltipLeft"))
1007								{
1008									tooltipOptions.position.my = 'right center';
1009									tooltipOptions.position.at = 'left center';
1010								}
1011								else if($field.hasClass("cpFormsTooltipBottom"))
1012								{
1013									tooltipOptions.position.my = 'top center';
1014									tooltipOptions.position.at = 'bottom center';
1015								}
1016								else if($field.hasClass("cpFormsTooltipTop"))
1017								{
1018									tooltipOptions.position.my = 'bottom center';
1019									tooltipOptions.position.at = 'top center';
1020								}
1021								else if($field.hasClass("cpFormsTooltipBottomRight"))
1022								{
1023									tooltipOptions.position.my = 'top left';
1024									tooltipOptions.position.at = 'bottom right';
1025								}
1026
1027								if(fieldType == "html")
1028								{
1029									$tinyMCE.addClass("cpFormsInvalid");
1030								}
1031								else if(fieldType == "cp_form_field_address_lookup")
1032								{
1033									CPFormFieldAddressLookup_toggle_manual_mode(true, field_name);
1034								}
1035
1036								$field.addClass("cpFormsInvalid").qtip(tooltipOptions);
1037							}
1038							else
1039							{
1040								alert(errorMessage);
1041								$field.addClass("cpFormsInvalid");
1042							}
1043
1044							$input.one("change", cp_forms_clearInvalidOnChange);
1045
1046							if(i == 0 && window.cpFormsDontFocusOnError.indexOf(parseInt(data.form_cpda_id)) === -1)
1047							{
1048								$field.focus();
1049							}
1050						}
1051						else
1052						{
1053							console.error("cp_forms_processFormResponse: couldn't find field '"+lookup_name+"'");
1054						}
1055					}
1056				}
1057				else if(data.errors[i].error_code == 19)
1058				{
1059					var $field = $('#'+lookup_name+'_upload_button');
1060					var $input = $('#'+lookup_name+'_upload_filename_new');
1061
1062					if(!mobile)
1063					{
1064						var tooltipOptions = {
1065							  content: errorMessage
1066						    , position: {
1067						          my: 'left center'
1068						        , at: 'right center'
1069						        , viewport: $(window)
1070						        , effect: false
1071						    }
1072						    , show: {
1073						          ready: true
1074						        , event: false
1075						    }
1076						    , hide: {
1077						          event: false
1078						    }
1079						    , style: {
1080						          classes: 'cpErrorTooltip qtip-shadow'
1081						        , tip: {
1082						            width: 8
1083						        }
1084						    }
1085						};
1086
1087						$.fn.qtip.zindex = 99999;
1088						$field.addClass("cpFormsInvalid").qtip(tooltipOptions);
1089					}
1090					else
1091					{
1092						alert(errorMessage);
1093						$field.addClass("cpFormsInvalid").qtip(tooltipOptions);
1094					}
1095
1096				}
1097				else if(data.errors[i].error_code == 12)
1098				{
1099					if(!mobile)
1100					{
1101						var tooltipOptions = {
1102							  content: errorMessage
1103						    , position: {
1104						          my: 'left center'
1105						        , at: 'right center'
1106						        , viewport: $(document)
1107						        , effect: false
1108						    }
1109						    , show: {
1110						          event: 'focus mouseenter'
1111						        , effect: function(offset) {
1112						            $(this).fadeIn(200);
1113						        }
1114						    }
1115						    , hide: {
1116						          event: 'blur change mouseleave'
1117						          // event: false
1118						    }
1119						    , style: {
1120						          classes: 'cpErrorTooltip qtip-shadow'
1121						        , tip: {
1122						            width: 8
1123						        }
1124						    }
1125						};
1126
1127						$.fn.qtip.zindex = 99999;
1128						$form.find('.g-recaptcha > div').addClass("cpFormsInvalid").qtip(tooltipOptions);
1129					}
1130					else
1131					{
1132						alert(errorMessage);
1133						$form.find('.g-recaptcha').addClass("cpFormsInvalid");
1134					}
1135
1136					$input = true;
1137
1138				}
1139			}
1140
1141			if($input == null)
1142			{//this is a general error not associated with a field
1143				//TODO make this prettier
1144
1145				var tokenArray = ["{{field_title}}"];
1146				var tokenReplaceArray = [data.errors[i].field_title];
1147				var errorMessage =
1148					data.errors[i].error_code == -1 ? data.errors[i].custom_error_message
1149					: str_replace(tokenArray, tokenReplaceArray, responseMessages[data.errors[i].error_code]);
1150
1151				if(typeof cp_toasts_add == "function" && data.errors[i].use_alert !== true)
1152				{
1153					cp_toasts_add({message: errorMessage, type: 2})
1154				}
1155				else
1156				{
1157					alert(errorMessage);
1158				}
1159
1160				if(data.errors[i].error_code == -1 && typeof CPO_DEBUG != undefined && CPO_DEBUG == true)
1161				{
1162					console.log("cp_forms_processFormResponse() debug_error_message: ", data.errors[i].debug_error_message);
1163				}
1164			}
1165		}
1166	}
1167
1168	cp_forms_update_before_unload($form);
1169
1170	if(typeof cp_forms_callback == "function")
1171	{
1172		cp_forms_callback(data);
1173	}
1174	return false;
1175}
1176
1177function cp_forms_datepicker_add_clear_button(input) {
1178    setTimeout(function() {
1179
1180    	if(mobile)
1181    	{
1182    		$(input).blur();
1183    	}
1184
1185    	var $widget = $(input).datepicker("widget");
1186
1187    	$widget.find('.ui-datepicker-header,.ui-datepicker-calendar').addClass('notranslate');
1188
1189        var $buttonPane = $widget.find(".ui-datepicker-buttonpane");
1190
1191        $( "<button>", {
1192            text: "Clear",
1193            click: function() {
1194                $(input).val('').datepicker("hide");
1195            }
1196        }).appendTo( $buttonPane ).addClass("ui-datepicker-clear ui-state-default ui-priority-primary ui-corner-all");
1197    }, 1 );
1198}
1199
1200function CPFormFieldSelectMultipleWithSearch_init(field_id) {
1201	setTimeout(function(){
1202		var $field_elm = document.getElementById(field_id+'_search_wrapper');
1203		var $field_elm_select_wrapper = document.getElementById('for'+field_id).querySelectorAll('.selectOptionWrapper');
1204		var $field_elm_sarch_input = document.getElementById(field_id+'_search_input')
1205		var field_elm_select_wrapper_orig_height = $field_elm_select_wrapper[0].clientHeight;
1206
1207		$field_elm_select_wrapper[0].style.maxHeight = field_elm_select_wrapper_orig_height+'px';
1208
1209		$field_elm_select_wrapper[0].style.height = $field_elm.clientHeight+'px';
1210		$field_elm_select_wrapper[0].style.overflow = 'hidden';
1211
1212		$field_elm_sarch_input.addEventListener('keyup', function() {
1213		 	if (this.value == '')
1214		 	{
1215		 		$field_elm_select_wrapper[0].style.height = $field_elm.clientHeight+'px';
1216		 		$field_elm_select_wrapper[0].style.overflow = 'hidden';
1217		 	}
1218		 	else
1219		 	{
1220		 		$field_elm_select_wrapper[0].style.height = 'auto';
1221		 		$field_elm_select_wrapper[0].style.overflowY = 'auto';
1222		 	}
1223		});
1224
1225        var clearSearchButton = $field_elm_select_wrapper[0].querySelectorAll('.clearSearch')[0];
1226        if (clearSearchButton) {
1227            clearSearchButton.addEventListener('click', function() {
1228                $field_elm_select_wrapper[0].style.height = $field_elm.clientHeight+'px';
1229                $field_elm_select_wrapper[0].style.overflow = 'hidden';
1230            });
1231        }
1232	}, 2);
1233}
1234
1235function CPFormFieldAddressLookup_init(field_id, restrictCountries) {
1236	if(cp.config.apis.ideal_postcodes_api_key)
1237	{
1238		IdealPostcodes.AddressFinder.setup({
1239			apiKey: cp.config.apis.ideal_postcodes_api_key,
1240			inputField: '#'+field_id+'_search',
1241			onUnhide: function() {
1242				if(!document.getElementById(field_id+'_manual_fields').classList.contains('manual_mode'))
1243				{
1244					CPFormFieldAddressLookup_toggle_manual_mode(true, field_id);
1245				}
1246			},
1247			outputFields: {
1248				line_1:    '#'+field_id+'_line_1',
1249				line_2:    '#'+field_id+'_line_2',
1250				line_3:    '#'+field_id+'_line_3',
1251				post_town: '#'+field_id+'_post_town',
1252				postcode:  '#'+field_id+'_postcode',
1253				latitude:  '#'+field_id+'_latitude',
1254				longitude: '#'+field_id+'_longitude'
1255			},
1256			restrictCountries: restrictCountries,
1257			onFailedCheck: function (error) {
1258				CPFormFieldAddressLookup_init_address_only(field_id);
1259				console.warn('CPFormFieldAddressLookup failed to initiate with supplied API key. This URL might not be whitelisted, or the number of lookups may have exceeded the daily rate. Error returned was: "'+error.message+'"');
1260			}
1261		});
1262
1263		document.getElementById(field_id+'_manual_entry').addEventListener('click', function()
1264		{
1265			CPFormFieldAddressLookup_manual_entry_click(field_id);
1266		});
1267
1268		// if started in manual mode
1269		if(document.getElementById(field_id+'_manual_fields').classList.contains('manual_mode'))
1270		{
1271			CPFormFieldAddressLookup_toggle_manual_mode(true, field_id, true);
1272		}
1273	}
1274	else
1275	{
1276		CPFormFieldAddressLookup_init_address_only(field_id);
1277	}
1278}
1279
1280function CPFormFieldAddressLookup_init_address_only(field_id) {
1281	document.getElementById(field_id+'_top_wrapper').remove();
1282	var $manual_field_wrapper = document.getElementById(field_id+'_manual_fields');
1283	$manual_field_wrapper.classList.add('open','manual_only');
1284	$manual_field_wrapper.style.height = null;
1285}
1286
1287function CPFormFieldAddressLookup_manual_entry_click(field_id) {
1288
1289
1290	if(document.getElementById(field_id+'_manual_fields').classList.contains('manual_mode'))
1291	{
1292		CPFormFieldAddressLookup_toggle_manual_mode(false, field_id);
1293	}
1294	else
1295	{
1296		CPFormFieldAddressLookup_toggle_manual_mode(true, field_id);
1297		setTimeout(function(){
1298			document.getElementById(field_id+'_line_1').focus();
1299		}, 200);
1300	}
1301}
1302
1303function CPFormFieldAddressLookup_toggle_manual_mode(onOff, field_id, from_init) {
1304
1305	var $manual_field_wrapper = document.getElementById(field_id+'_manual_fields');
1306
1307	// if field is in manual only mode we don't need to do anything
1308	if($manual_field_wrapper.classList.contains('manual_only'))
1309	{
1310		return;
1311	}
1312
1313	var anim_time = 200;
1314	var $manual_entry_p = document.getElementById(field_id+'_manual_entry');
1315	var $search_wrapper = document.getElementById(field_id+'_search_wrapper');
1316
1317	if(onOff === true)
1318	{
1319		$manual_field_wrapper.classList.add('manual_mode');
1320		$manual_entry_p.innerHTML = '&#8635; Search for address';
1321
1322		$search_wrapper.style.opacity = 0;
1323		$manual_field_wrapper.style.opacity = 1;
1324
1325		if(from_init !== true)
1326		{
1327			document.getElementById(field_id+'_search').blur();
1328
1329			$manual_field_wrapper.style.height = $manual_field_wrapper.scrollHeight+'px';
1330			$manual_field_wrapper.style.opacity = 1;
1331			setTimeout(function(){
1332				$manual_field_wrapper.classList.add('open');
1333				$manual_field_wrapper.style.height = '';
1334			}, anim_time+50);
1335
1336			$search_wrapper.style.height = $search_wrapper.scrollHeight+'px';
1337			$search_wrapper.classList.remove('open');
1338			setTimeout(function(){
1339				$search_wrapper.style.height = '0px';
1340			}, 2);
1341		}
1342	}
1343	else
1344	{
1345		$manual_field_wrapper.classList.remove('manual_mode');
1346		$manual_entry_p.innerHTML = 'Enter address manually';
1347
1348		$search_wrapper.style.opacity = 1;
1349		$manual_field_wrapper.style.opacity = 0;
1350
1351		$manual_field_wrapper.style.height = $manual_field_wrapper.scrollHeight+'px';
1352		$manual_field_wrapper.classList.remove('open');
1353		setTimeout(function(){
1354			$manual_field_wrapper.style.height = '0px';
1355		}, 2);
1356
1357		$search_wrapper.style.height = $search_wrapper.scrollHeight+'px';
1358		setTimeout(function(){
1359			$search_wrapper.classList.add('open');
1360			$search_wrapper.style.height = '';
1361		}, anim_time+50);
1362
1363		setTimeout(function(){
1364			document.getElementById(field_id+'_search').focus();
1365		}, anim_time+1);
1366	}
1367
1368
1369}
1370
1371function cp_forms_clearInvalidOnChange() {
1372	var fieldTypeAndElm = fsGetFieldTypeAndElm($(this));
1373	var $elm = fieldTypeAndElm.$elm;
1374	if(fieldTypeAndElm.fieldType == "formStyledRadio")
1375	{
1376		$elm = $elm.parent().parent();
1377	}
1378	$elm.removeClass("cpFormsInvalid").unbind("change");
1379	if($elm.qtip('api'))
1380	{
1381		$elm.qtip('api').destroy();
1382	}
1383}
1384
1385function CPFormFieldDateRange_init(elm_id,  date_format, date_range_divider, preset_ranges) {
1386	cp_date_range_init(elm_id,  date_format, date_range_divider, preset_ranges, false);
1387}
1388
1389function CPFormFieldDateTimeRange_init(elm_id,  date_format, date_range_divider, preset_ranges) {
1390	cp_date_range_init(elm_id,  date_format, date_range_divider, preset_ranges, true);
1391}
1392
1393function cp_date_range_init(elm_id, date_format, date_range_divider, preset_ranges, include_time) {
1394
1395	var $datepicker_btn     = $('#'+elm_id+'_btn');
1396	var $datepicker_summary = $('#'+elm_id);
1397	var $datepicker_from    = $('#'+elm_id+'_from');
1398	var $datepicker_to      = $('#'+elm_id+'_to');
1399
1400	var moment_from = $datepicker_from.val();
1401	var moment_to = $datepicker_to.val();
1402
1403	var internal_date_format = 'D MMM YYYY HH:mm';
1404
1405	var moment_today_midnight = moment().utcOffset(cp.default_utc_offset).startOf('day');
1406
1407	if(moment_from)
1408	{
1409		moment_from = moment(new Date(moment_from*1000)).utcOffset(cp.default_utc_offset);
1410	}
1411	else
1412	{
1413		moment_from = moment_today_midnight;
1414	}
1415
1416	if(moment_to)
1417	{
1418		moment_to = moment(new Date(moment_to*1000)).utcOffset(cp.default_utc_offset);
1419	}
1420	else
1421	{
1422		moment_to = moment().utcOffset(cp.default_utc_offset).endOf('day');
1423	}
1424
1425	var ranges = [];
1426	var label_lookup = [];
1427	for(var key in preset_ranges)
1428	{
1429		// convert unix timestampts to moment instances
1430		ranges[preset_ranges[key]['name']] = [
1431			moment(new Date(preset_ranges[key]['from']*1000)),
1432			moment(new Date(preset_ranges[key]['to']*1000)),
1433		];
1434
1435		label_lookup[preset_ranges[key]['name']] = key;
1436	}
1437
1438	var config = {
1439		autoApply: true, // does nothing in time picker mode
1440		alwaysShowCalendars: true,
1441		startDate: moment_from.format(internal_date_format),
1442		endDate: moment_to.format(internal_date_format),
1443		// maxDate: '11/25/2021',
1444		opens: 'center',
1445		drops: 'down',
1446		buttonClasses: 'none',
1447		applyButtonClasses: 'none',
1448		cancelClass: 'none',
1449		ranges: ranges,
1450		locale: {
1451			format: internal_date_format,
1452			separator: date_range_divider,
1453			applyLabel: 'Ok',
1454			cancelLabel: 'Cancel',
1455			fromLabel: 'From',
1456			toLabel: 'To',
1457			customRangeLabel: 'Custom',
1458			weekLabel: 'W',
1459			firstDay: 1,
1460			daysOfWeek: ['Su','Mo','Tu','We','Th','Fr','Sa'],
1461			monthNames: [
1462				'January',
1463				'February',
1464				'March',
1465				'April',
1466				'May',
1467				'June',
1468				'July',
1469				'August',
1470				'September',
1471				'October',
1472				'November',
1473				'December'
1474			]
1475		}
1476	};
1477
1478	if(include_time === true)
1479	{
1480		config.timePicker = true;
1481		config.showDropdowns = true;
1482		config.timePicker24Hour = true;
1483	}
1484
1485	$datepicker_btn.daterangepicker(config, function(start, end, label) {
1486
1487		// TODO: Asses if we do actually need utcOffset here, removed for now as it doesn't work correctly during BST
1488
1489		var moment_from = moment(start, internal_date_format)/*.utcOffset(cp.default_utc_offset)*/;
1490		if(include_time !== true)
1491		{
1492			moment_from.startOf('day');
1493		}
1494
1495		var moment_to = moment(end, internal_date_format)/*.utcOffset(cp.default_utc_offset)*/;
1496		if(include_time !== true)
1497		{
1498			moment_to.endOf("day");
1499		}
1500		else if(moment_to.format('m') === '59')
1501		{
1502			// when the 59th minute selected, also set seconds to 59
1503			moment_to.endOf("minute");
1504		}
1505
1506		if(label == 'Custom')
1507		{
1508			label =
1509				  moment(start, internal_date_format)/*.utcOffset(cp.default_utc_offset)*/.format(date_format)
1510				+ date_range_divider
1511				+ moment(end, internal_date_format)/*.utcOffset(cp.default_utc_offset)*/.format(date_format)
1512			;
1513		}
1514		else if(label == 'Last 24 hours')
1515		{
1516			// set here so that minutes are dynamic
1517			moment_from = moment()/*.utcOffset(cp.default_utc_offset)*/.subtract(1, 'days');
1518			moment_to = moment()/*.utcOffset(cp.default_utc_offset)*/;
1519
1520			$datepicker_btn.data('daterangepicker').setStartDate(moment_from.format(internal_date_format));
1521			$datepicker_btn.data('daterangepicker').setEndDate(moment_to.format(internal_date_format));
1522		}
1523		else if(label == 'Last hour')
1524		{
1525			// set here so that minutes are dynamic
1526			moment_from = moment()/*.utcOffset(cp.default_utc_offset)*/.subtract(1, 'hours');
1527			moment_to = moment()/*.utcOffset(cp.default_utc_offset)*/;
1528
1529			$datepicker_btn.data('daterangepicker').setStartDate(moment_from.format(internal_date_format));
1530			$datepicker_btn.data('daterangepicker').setEndDate(moment_to.format(internal_date_format));
1531		}
1532
1533	  	$datepicker_btn.find('p').html(label);
1534
1535		var val_from = moment_from.format('X');
1536	  	$datepicker_from.val(val_from);
1537
1538		var val_to = moment_to.format('X');
1539	  	$datepicker_to.val(val_to);
1540
1541		var val_summary = '';
1542
1543		if(label != 'Custom' && label_lookup.hasOwnProperty(label))
1544		{
1545			val_summary = label_lookup[label];
1546		}
1547		else
1548		{
1549			val_summary = moment_from.format('DD-MM-YYYY-HH:mm:ss') + ',' + moment_to.format('DD-MM-YYYY-HH:mm:ss');
1550		}
1551
1552	  	$datepicker_summary.val(val_summary);
1553	  	// Do this last as other items will likely bind their events to
1554	  	// the summary field as it uses the un-suffixed field ID
1555	  	$datepicker_summary[0].dispatchEvent(new Event('change'));
1556	})
1557	.on('show.daterangepicker', function(ev, picker) {
1558		if(include_time === true)
1559		{
1560			picker.container[0].classList.add('CPFormFieldDateTimeRange');
1561		}
1562		else
1563		{
1564			picker.container[0].classList.add('CPFormFieldDateRange');
1565		}
1566		$datepicker_btn.addClass('open');
1567	})
1568	.on('hide.daterangepicker', function(ev, picker) {
1569		$datepicker_btn.removeClass('open');
1570	})
1571	;
1572
1573	var daterangepicker_instance = $datepicker_btn.data('daterangepicker');
1574	daterangepicker_instance.clear_date_range = function(){ cp_date_range_clear(this, $datepicker_btn); };
1575}
1576
1577function cp_date_range_clear(daterangepicker, $datepicker_btn) {
1578
1579	$datepicker_btn.find('p').html($datepicker_btn.data('default-label'));
1580	daterangepicker.setStartDate(moment().startOf('day'));
1581	daterangepicker.setEndDate(moment().endOf('day'));
1582
1583	var elm_id = $datepicker_btn.data('base-id');
1584
1585	$('#'+elm_id).changeVal('');
1586	$('#'+elm_id+'_from').changeVal('');
1587	$('#'+elm_id+'_to').changeVal('');
1588
1589}

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.