PageSourceSearch

https://be-fibre.co.uk/wp-content/plugins/anp-order-journey/resour…?ver=1.5.50.17353200%201790761963

js be-fibre.co.uk collected 2026-09-30 09:53:46 UTC 57,423 bytes, 2,517 lines download raw bytes

1(function( altnetOrderJourney, $, undefined ) {
2		
3/* Go!
4---------------------------------------*/
5
6var _checkoutForm;
7var _bookingCalendar;
8
9$(function () 
10{	
11	_checkoutForm = $('#anp-order-journey-form');
12	
13	if (_checkoutForm.length) {
14        initCheckout();
15    }
16	
17	_bookingCalendar = $('#anp-booking-calendar');
18	
19	if (_bookingCalendar.length) {
20        initCalendar();
21    }
22	
23	var productSelection = $('#anp-product-selection');
24
25	if (productSelection.length) {
26        initProductSelection();
27    }
28	
29	initAddressSearch();
30	initRegisterContactForm();
31	initProductCategorySwitchers();
32	initSurveys();
33	initInputs();
34	initTracking();
35	initOneTouchSwitch();
36	initPaymentDetails();
37	initCoupons();
38	initInputPlaceholders();
39	
40	// Focus effects
41	
42	$('.anp-parent-focus').each(function(){
43		var element = $(this);
44		var parent = element.closest('.anp-parent-focus-parent');
45		var activeClass = 'focused';
46		
47		element.focus(function(){
48			parent.addClass(activeClass);
49		});
50		
51		element.blur(function(){
52			parent.removeClass(activeClass);
53		});
54	});
55	
56	// Mouseover effects
57	
58	var mouseovers = $('.anp-mouseover');
59
60	mouseovers.bind('mouseenter', function () {
61		$(this).addClass('anp--over');
62	});
63	
64	mouseovers.bind('mouseleave', function () {
65		$(this).removeClass('anp--over');
66	});
67	
68	// Products
69	
70	$('.anp-showcase, .anp-new-showcase').bind('click', function(){
71		var link = $(this).data('link');
72		document.location.href= link;
73	});
74	
75	$('.anp-showcase__footnote').matchHeight();
76	
77	initProductShowcaseCarousels();
78	
79	// Payment
80	
81	$('.anp-change-billing').click(function(){
82		$('#anp-payment-billing-address').toggleClass('anp-toggleable--active');
83		return false;
84	});
85	
86	// Terms
87	
88	$('.anp-terms--embedded a').click(function(){
89		var href = $(this).attr('href');
90		window.open(href);
91		return false;
92	});
93	
94	$('.anp-checkout-terms-trigger').click(function (){
95		var wrapper = $('#anp-checkout-terms-wrapper');
96	
97		if (wrapper.length) {
98
99			if (wrapper.is(':visible')) {
100				wrapper.hide();
101			} else {
102				wrapper.show();
103				scrollToElement(wrapper);
104			}
105			
106		}
107		
108		return false;
109	});
110	
111	// Read more
112	
113	$('.anp-read-more').click(function(){
114		var element = $(this);
115		var target = element.parent().find('.anp-read-more-reveal');
116		
117		if (target) {
118			target.show();
119			element.hide();
120		}
121		
122		return false;
123	});
124	
125	//
126	
127	$('.anp-restart-confirm').click(function(){
128		return confirm('Are you sure? You\'ll have to restart your order from the beginning.');
129	});
130	
131	var sidebarHeight = $('#anp-sidebar').outerHeight();
132	
133	$('#anp-journey-wrapper').css('min-height', sidebarHeight + 'px');
134	
135	$('#anp-is-corporate').change(function(){
136		var section = $('#anp-corporate-fields');
137		
138		if ($(this).is(':checked')) {
139			section.show();
140		} else {
141			section.hide();
142		}
143	});
144	
145	$('.anp-button-bar--relocate').each(function(){
146		var bar = $(this);
147		var parent = bar.closest('.altnet');
148
149		if (parent) {
150			bar.appendTo(parent)
151		}
152	});
153	
154	scrollToError();
155});
156
157/* CookieBot
158---------------------------------------*/
159
160if (typeof Cookiebot != 'undefined') {
161	var doNotTrack = $('body').hasClass('anp-do-not-track');
162		
163	if (doNotTrack) {
164		Cookiebot.withdraw();
165		Cookiebot.hide();
166	}
167}
168
169/* Address Search
170---------------------------------------*/
171
172var _addressSelectElement;
173var _addressResultsWrapper;
174
175var _addressSearchResults;
176var _addressSearchPage;
177
178var _addressesLoading;
179var _addressProductLineSelect;
180var _addressProductLineInput;
181
182function initAddressSearch() {
183	
184	initFormAddressSearches();
185	initAddressAutoCompletes();
186	
187    if ($('#anp-postcoder-search').length) {
188        initAddressSearchForm();
189    }
190
191	$('#anp-postcoder-confirm').click(function() {
192		toggleSpinner(true);
193		var form = $('#anp-postcoder-form');
194		form.submit();
195        return false;
196    });
197
198	$('.anp-postcoder-help').click(function(){
199		if (typeof window.fcWidget != 'undefined') {
200			window.fcWidget.open();
201		} else {
202			window.open('/contact');
203		}
204	
205		return false;
206	});
207	
208	_addressProductLineSelect = $('#anp-postcoder-product-line');
209    _addressProductLineInput = $('#anp-postcoder-product-line-input');
210	
211	_addressProductLineSelect.change(function() {
212        _addressProductLineInput.val($(this).val());
213    });
214};
215
216const debouncedAddressLookup = debounce(submitAddressSearchForm, 400);
217
218function initAddressSearchForm() {
219   
220    _addressSelectElement = $('#anp-postcoder-address');
221    _addressResultsWrapper = $('#anp-postcoder-results');
222
223    $('#anp-postcoder-submit').click(function() {
224        submitAddressSearchForm();
225        return false;
226    });
227	
228	const input = document.getElementById('anp-postcoder-partial-address');
229	
230	if (!input) {
231		return false;
232	}
233	
234	let lastLookupValue = '';
235	
236	input.addEventListener('input', function (e) {
237		const el = e.target;
238		let cleaned = el.value.replace(/[^a-z0-9]/gi, '');
239
240		cleaned = cleaned.slice(0, 7);
241
242		if (cleaned !== el.value) {
243			el.value = cleaned;
244		}
245
246		if (cleaned !== lastLookupValue) {
247			if (cleaned.length > 5) {
248				debouncedAddressLookup();
249			}
250			
251			lastLookupValue = cleaned;
252		}
253	});
254	
255    $('#anp-postcoder-new-search').click(function() {
256		$('.anp-postcoder-hidden').hide();
257        $('.anp-postcoder-has-addresses').removeClass('anp-postcoder-has-addresses');
258        $('#anp-postcoder-partial-address').val('');
259        $('#anp-postcoder-partial-address').focus();
260        return false;
261    });
262	
263	$('#anp-postcoder-partial-address').keydown(function(e){
264		
265		if(e.which == 13){
266			submitAddressSearchForm();
267			return false;
268		}
269	})
270	
271    _addressSelectElement.change(onAddressSelectChange);
272    
273    var preloadedAddresses = anpPostcodeAddressData;
274	
275    if (preloadedAddresses && preloadedAddresses.data) {
276        _addressSearchResults = preloadedAddresses.data;
277        showAddressSearchResults();
278    } else {
279		$('.anp-postcoder-has-addresses').removeClass('anp-postcoder-has-addresses');
280	}
281}
282
283function submitAddressSearchForm()
284{
285	var address = getSearchAddress();
286	
287	if (address.length >= 3) {
288		_addressSearchPage = 0;
289		submitAddressSearch();
290	}
291}
292
293function submitAddressSearch() {
294	$('.anp-postcoder-hidden').hide();
295	
296    addressApiSearch( getSearchAddress(), _addressSearchPage );
297}
298
299function addressApiSearch( address, page, callback ) {
300
301    if (_addressesLoading) {
302        return false;
303    }
304
305    if (!address.length) {
306        return false
307    }
308
309    submitAddressSearchRequest(address, page, true, onAddressSearchComplete);
310}
311
312function submitAddressSearchRequest(address, page, cache, callback)
313{
314    _addressesLoading = true;
315
316    $.ajax({
317		headers: { 'X-WP-Nonce': window.wpRestNonce },
318        type: 'POST',
319        url: '/wp-json/altnet/v1/addresses/search',
320        dataType: 'json',
321        data: { address: address, page: page, path: window.location.pathname, cache: cache ? 1 : 0 },
322        complete: function(response){
323            _addressesLoading = false;
324            callback(response.responseJSON);
325        }
326    });
327	
328	toggleSpinner(true);
329}
330
331function onAddressSearchComplete(json)
332{
333    if (!json['success']) {
334        alert('An error occurred. Please try again shortly.');
335        return;
336    }
337	
338    _addressSearchResults = json.data;
339
340    showAddressSearchResults();
341	toggleSpinner(false);
342}
343
344function showAddressSearchResults() {
345    _addressSelectElement.empty();
346
347    addSearchSelectOption('', 'Select an address');
348
349    var addresses = _addressSearchResults;
350	var currentUPRN = $('#anp-postcoder-input-uprn').val();
351
352    if (addresses && addresses.length) {
353        for (var a in addresses) {
354            var address = addresses[a];
355			
356            addSearchSelectOption(a, address['summaryline'], address['uprn'] == currentUPRN);
357
358            if (address['morevalues'] == 'true') {
359                addSearchSelectOption('more', 'Load more addresses...');
360            }
361        }
362
363        _addressResultsWrapper.show();
364    } else {
365        $('#anp-postcoder-no-results').fadeIn();
366    }
367}
368
369function addSearchSelectOption(key, value, selected) {
370    _addressSelectElement.append('<option value="' + key + '"' + (selected ? ' selected' : '') + '>
370' + value + '</option>');
371}
372
373function onAddressSelectChange() {
374    var selected = _addressSelectElement.val();
375
376    if ( typeof _addressSearchResults[selected] != 'undefined' ) {
377        showAddressSearchConfirmation(_addressSearchResults[selected]);
378    } else if ('more' == selected){
379        _addressSearchPage++;
380        submitAddressSearch();
381    } else {
382		$('#anp-postcoder-footer').hide();
383	}
384}
385
386function showAddressSearchConfirmation(selectedAddress) {
387    var addressHTML = '';
388    var addressFields = ['address1', 'address2', 'posttown', 'postcode'];
389
390    for (var i in addressFields) {
391        var line = selectedAddress[addressFields[i]];
392
393        if (line != null && line.length > 1) {
394            addressHTML += line + '<br />';   
395        }
396    }
397	
398    for (var key in selectedAddress) {
399        var inputElement = $('#anp-postcoder-input-' + key);
400
401        if (inputElement.length) {
402            inputElement.val(selectedAddress[key]);
403        }
404    }
405    //$('#anp-selected-address').html(addressHTML);
406    $('#anp-postcoder-footer').show();
407}
408
409function getSearchAddress() {
410    return $('#anp-postcoder-partial-address').val();
411}
412
413function initFormAddressSearches() {
414
415	var postcodeFields = $('.anp-postcoder-adhoc-postcode');
416	
417	postcodeFields.each(function(){
418		var postcodeField = $(this);
419		var form = postcodeField.closest('form');
420
421		if (form.length) {
422			initFormAddressSearch(form);
423		}
424	});
425}
426
427function initFormAddressSearch(form)
428{
429	var fields = ['postcode', 'uprn', 'address1', 'address2'];
430	var inputs = [];
431	
432	for (var i in fields) {
433		var fieldName = fields[i];
434		var input = form.find('.anp-postcoder-adhoc-' + fieldName);
435	
436		if (input.length) {
437			inputs[fieldName] = input;
438		}
439	}
440	
441	if (!inputs.uprn || !inputs.postcode) {
442		return;
443	}
444	
445	inputs.postcode.change(function(){
446		var postcode = $(this).val();
447		
448		if (postcode.length > 3) {
449		
450			submitAddressSearchRequest(postcode, 0, false, function (json) {
451				var addresses = json.data;
452				var uprnInput = inputs.uprn;
453				
454				toggleSpinner(false);
455				
456				uprnInput.empty();
457				uprnInput.off();
458				
459				for (var i in addresses) {
460					var address = addresses[i];
461			
462					uprnInput.append($('<option>', { 
463						value: address.uprn,
464						text: address.summaryline 
465					}));
466				}
467				
468				uprnInput.change(function(){
469					var uprn = $(this).val();
470					
471					for (var i in addresses) {
472						var address = addresses[i];
473						
474						if (address.uprn == uprn) {
475							if (inputs.address1) {
476								inputs.address1.val(address.address1);
477							}
478							
479							if (inputs.address2) {
480								inputs.address2.val(address.address2);
481							}
482						}
483					}
484				})
485			});
486		}
487	});
488}
489
490// AUTOCOMPLETE
491
492const debouncedAutocompleteLookup = debounce(autocompleteLookup, 400);
493
494function initAddressAutoCompletes()
495{	
496	const forms = document.querySelectorAll('.anp-address-autocomplete-form');
497	
498	forms.forEach(form => {
499		
500		const input = form.querySelector('.anp-address-autocomplete');
501		const resultsContainer = form.querySelector('.anp-address-select-addresses');
502		const spinner = form.querySelector('.anp-address-spinner');
503		
504		const actionInput = form.querySelector('.anp-address-autocomplete-action');
505		const uprnInput = form.querySelector('.anp-address-autocomplete-uprn');
506
507		let lastLookupValue = '';
508	
509		input.addEventListener('input', function (e) {
510			const el = e.target;
511			let cleaned = el.value.replace(/[^a-z0-9]/gi, '');
512
513			cleaned = cleaned.slice(0, 7);
514
515			if (cleaned !== el.value) {
516				el.value = cleaned;
517			}
518
519			if (cleaned !== lastLookupValue) {
520				debouncedAutocompleteLookup(cleaned, resultsContainer, spinner);
521				lastLookupValue = cleaned;
522			}
523		});	
524		
525		form.addEventListener('keydown', function (e) {
526			if (e.key === 'Enter' && e.target.tagName.toLowerCase() !== 'textarea') {
527				e.preventDefault();
528			}
529		});
530		
531		resultsContainer.addEventListener('click', (e) => {
532			const option = e.target.closest('li');
533			const uprn = option.getAttribute('data-uprn');
534						
535			actionInput.value = 'after_autocomplete';
536			uprnInput.value = uprn;
537			
538			resultsContainer.style.display = 'none';
539			spinner.style.display = 'block';
540			
541			form.submit();
542		});
543	});
544}
545
546function autocompleteLookup(address, resultsContainer, spinner)
547{
548	if (address.length < 6) {
549		resultsContainer.style.display = 'none';
550		return;
551	}
552
553	spinner.style.display = 'block';
554	
555	submitAddressSearchRequest(address, 0, false, function(response) {
556		spinner.style.display = 'none';
557		onAutoCompleteLookupComplete(response, resultsContainer);
558	});
559}
560
561function onAutoCompleteLookupComplete(response, resultsContainer) {
562	resultsContainer.style.display = 'block';
563			
564	resultsContainer.innerHTML = '';
565
566	if (response.data && response.data.length > 0) {
567		resultsContainer.style.display = 'block';
568		response.data.forEach((suggestion, index) => {
569			const option = document.createElement('li');
570			option.textContent = suggestion.summaryline;
571			option.setAttribute('data-uprn', suggestion.uprn);
572			resultsContainer.appendChild(option);
573		});
574	
575	} else {
576		resultsContainer.style.display = 'none';
577	}
578}
579
580/* Checkout Form
581---------------------------------------*/
582
583function initCheckout()
584{
585	// Next button
586	
587	$('#anp-checkout-next-trigger').click(function () {
588		var step = $('#anp-order-journey-step').val();
589		var validates = true;
590			
591		if ('book_installation' == step) {
592			validates = bookInstallationValidation();
593		} else if ('purchase_products' == step) {
594			validates = productSelectionValidation();
595		} else {
596			validates = validateCheckoutForm();
597		}
598		
599		if (validates) {
600			
601			if ('contact_details' == step && $('#anp-data-layer-postcode').length && $('#anp-track-eligible-lead').length ) {
602				var dlPostcode = $('#anp-data-layer-postcode').val();
603				var dlAction = $('#anp-data-layer-action').val();
604				var dlType = $('#anp-data-layer-type').val();
605				var dlProductLine = $('#anp-data-layer-product_line').val();
606				var dlSplitTest = $('#anp-data-layer-journey_split').val();
607		
608				trackNewLeadEvent(dlPostcode, dlProductLine, dlType, dlAction, dlSplitTest, function() {
609					_checkoutForm.submit();
610				})
611			} else {
612				_checkoutForm.submit();
613			}
614			
615			toggleSpinner(true);
616		}
617		
618		return false;
619	});
620	
621	// Direct Debit
622
623	var sortCodeInput = $('#checkout_billing_sort_code');
624	
625	if (sortCodeInput.length) {
626		sortCodeInput.val(formatBillingSortCode(sortCodeInput.val()));
627
628		sortCodeInput.keyup(onBillingSortCodeChange);
629		sortCodeInput.blur(onBillingSortCodeChange);
630	}
631	
632	$('.anp-dd-guarantee-trigger').click(function(){
633		var guarantee = $('#anp-checkout-dd-guarantee');
634		
635		if (guarantee.is(':visible')) {
636			guarantee.hide();
637		} else {
638			guarantee.show();
639			scrollToElement(guarantee);
640		}
641		
642		return false;
643	});
644	
645	$('#anp-custom-billing-address').change(function(){
646		var element = $(this);
647		var checked = element.is(':checked');
648		var addressFields = $('#anp-billing-address')
649		
650		if (checked) {
651			addressFields.show();
652			/*
653			addressFields.find('input').each(function(){
654				var element = $(this);
655				element.val('');
656			});
657			*/
658		} else {
659			addressFields.hide();
660		}		
661	});
662	
663	// Restart/change
664	
665	$('.anp-cart-restart-button, .anp-order-submit-button, .anp-order-change-trigger').click(function(){
666		toggleSpinner(true);
667	});
668	
669	$('#anp-restart-trigger').click(function(){
670		toggleSpinner(true);
671		$('#anp-restart-form').submit();
672	});
673	
674	$('.anp-order-change-trigger').click(function(){
675		$('#anp-order-change-step').val($(this).data('step'));
676		$('#anp-order-change-form').submit();
677		
678		return false;
679	});
680	
681	// Terms
682	
683	$('.anp-checkout-terms-trigger').click(function(){
684		var terms = $('#anp-checkout-terms');
685		
686		if (terms.is(':visible')) {
687			terms.hide();
688		} else {
689			terms.show();
690		}
691		
692		return false;
693	});
694	
695	$('#anp-checkout-terms a').click(function() {
696		var href = $(this).attr('href');
697		window.open(href);
698		return false;
699	});
700}
701
702function onBillingSortCodeChange()
703{
704	var element = $(this);
705	element.val(formatBillingSortCode(element.val()));
706}
707
708/* Contact/Register for Updates Form
709---------------------------------------*/
710
711var _contactRegisterForm;
712var _ajaxLoading = false;
713var _contactRegisterFormDataLayerVars = {};
714
715function initRegisterContactForm()
716{
717    _contactRegisterForm = $('#anp-order-journey-form');
718    $('#anp-journey-form-submit-trigger').click(onRegisterContactFormSubmit);
719}
720
721function onRegisterContactFormSubmit()
722{
723    if (_ajaxLoading) {
724        return false;
725    }
726
727    var data = getFormData(_contactRegisterForm);
728	data['action'] = 'anp_form_submission';
729	
730	_contactRegisterFormDataLayerVars = { 
731		type: data['data_layer_type'], 
732		action: data['data_layer_action'], 
733		postcode: data['data_layer_postcode'],
734		product_line: data['data_layer_product_line'],
735	};
736	
737    _contactRegisterForm.find('.anp-error').removeClass('anp-error');
738	_ajaxLoading = true;
739
740    toggleSpinner(true);
741	clearFormErrors(_contactRegisterForm);
742	
743    $.ajax({
744        type: 'post',
745        url: anp_settings.ajax_url,
746        dataType: 'json',
747        data: data,
748        complete: function(response){
749            _ajaxLoading = false;
750
751            toggleSpinner(false);
752
753            var json = response.responseJSON;
754
755            if (json.success) {
756                showContactRegisterFormThanks(json);
757            } else if (json.errors) {
758                showFormErrors(json.errors);
759            }
760        }
761    });
762
763    return false;
764}
765
766function showContactRegisterFormThanks(response)
767{
768    _contactRegisterForm.find('input, select, textarea').val('');
769
770	$('#anp-journey-form-register').hide();
771    $('#anp-journey-form-thanks').show();
772	
773	$('.anp-button-bar').hide();
774	
775	trackNewLeadEvent(
776		_contactRegisterFormDataLayerVars['postcode'], 
777		_contactRegisterFormDataLayerVars['product_line'], 
778		_contactRegisterFormDataLayerVars['type'],
779		_contactRegisterFormDataLayerVars['action'],
780	);
781
782	scrollToElement($('#anp-journey-form-thanks'));
783
784    scrollToElement(_contactRegisterForm, -250);
785}
786
787/* Forms
788---------------------------------------*/
789
790function initInputs()
791{
792	initCheckboxes();
793	
794	$('.anp-true-false__label').click(function () {
795		var element = $(this);
796		var parent = element.closest('.anp-true-false');
797		var checkbox = parent.find('input');
798		
799		checkbox.prop('checked', !checkbox.prop('checked'));
800		checkbox.trigger('change');
801	});
802}
803
804function getFormData(form) {
805	var id = form.attr('id');
806	var data = new FormData(document.getElementById(id));
807	return Array.from(data.keys()).reduce((result, key) => {
808		result[key] = data.get(key);
809		return result;
810	}, {});
811}
812
813function showFormError(input, message)
814{
815	var field = input.closest('.anp-field');
816	var messageContainer = field.find('.anp-field__error');
817	
818	if (message && message.length) {
819		if (messageContainer.length) {
820			messageContainer.html(message);
821		}
822	}
823
824	field.addClass('anp-error');
825}
826
827function showFormErrors(errors)
828{
829    var focused = false;
830    var input;
831
832    for (var i in errors) {
833        input = $('#anp_' + i);
834        input.closest('.anp-field').addClass('anp-error');
835    }
836	
837	scrollToError();
838}
839
840function clearFormErrors(form)
841{
842	form.find('.anp-field__error').html('');
843	form.find('.anp-error').removeClass('anp-error');
844}
845
846function initCheckboxes()
847{
848	var boxes = $('.elementor-widget-form').find('input[type=checkbox]').not('.anp-tickbox');
849	
850	boxes.each(function(){
851		var box = $(this);
852		var name = box.attr('name');
853		var id = box.attr('id');
854		var value = box.val();
855		var parent = box.parent();
856		
857		var html = getCustomCheckboxHTML(name, id, value);
858		parent.addClass('anp-custom-tickbox');
859		box.replaceWith(html);
860	});
861}
862
863function getCustomCheckboxHTML(name, id, value)
864{
865	
866	return '<div class="altnet"><div class="anp-checkbox">'
867		+ '<input type="checkbox" name="' + name + '"  id="' + id + '" value="' + value + '" class="anp-tickbox" />'
868		+ '<label for="' + id + '"></label>'
869		+ '</div></div>';	
870}
871
872/* Validation
873---------------------------------------*/
874
875function validateCheckoutForm()
876{
877	var validates = true;
878	var form = _checkoutForm;
879
880	clearFormErrors(form);
881	
882	var mandatoryFields = form.find('.anp-field.mandatory:visible');
883	
884	mandatoryFields.each(function(){
885		var field = $(this);
886		var input = field.find('.anp-input');
887		var name = input.attr('name');
888		var type = input.attr('type');
889		var value = $.trim(input.val());
890		
891		if (name.match(/_confirm$/i)) {
892			var otherFieldName = name.replace(/_confirm$/i, '');
893			var otherInput = $('.anp-checkout-input[name="' + otherFieldName + '"]')
894			
895			if (otherInput.length) {
896				var otherValue = $.trim(otherInput.val());
897				
898				if (otherValue.toLowerCase() != value.toLowerCase()) {
899					showFormError(input, 'This needs to match the field above.');
900					validates = false;
901				}
902			}
903		}  else if ('telephone' == name && ! validateTelephone(value)) {
904			showFormError(input, 'Please enter a single, valid telephone number.');
905			validates = false;
906		} else if (!value.length) {
907			showFormError(input, 'This is a required field.');
908			validates = false;
909		}
910	});
911	
912	$('input[type="email"]').each(function(){
913		var field = $(this);
914		var input = field.closest('.anp-input');
915		var value = $.trim(input.val());
916		
917		if (value.length && !validateEmail(value)) {
918			showFormError(input, 'Please enter a valid email address.');
919			validates = false;
920		}
921	})
922	
923	if ( ! validates ) {
924		scrollToError();
925	}
926
927	return validates;
928}
929
930function scrollToError()
931{
932	var firstError = $('.anp-error:first');
933	
934	if (firstError.length) {
935		scrollToElement(firstError);
936	}
937}
938
939function bookInstallationValidation()
940{
941	var surveyCheckbox = $('#anp-timeslot-acknowledged');
942	
943	if (surveyCheckbox.is(':visible') && !surveyCheckbox.is(':checked')) {
944		showFormError(surveyCheckbox, 'Please confirm you are happy with a second appointment for installation.');
945		surveyCheckbox.focus();
946		return false;
947	}
948	
949	return true;
950}
951
952var _scrollToVoipConfirmation = true;
953
954function productSelectionValidation()
955{
956	if (_scrollToVoipConfirmation) {
957		var voipCheckbox = $('#anp-voip-confirmed');
958	
959		if (voipCheckbox.length && voipCheckbox.is(':visible') && !voipCheckbox.is(':checked')) {
960			if (!isInViewport(voipCheckbox[0])) {
961				scrollToElement($('#anp-voip-additional'));
962				
963				var fieldWrapper = voipCheckbox.closest('.anp-field');
964				
965				if (fieldWrapper.length) {
966					var errorWrapper = fieldWrapper.find('.anp-field__error');
967					
968					if (errorWrapper.length) {
969						errorWrapper.text('Please confirm you have an alternative method of contacting emergency services.');
970					}
971				}
972				
973				return false;
974			}
975		}
976		
977		_scrollToVoipConfirmation = false;
978	}
979	
980	return true;
981}
982
983function validateEmail(email)
984{
985	email = email.replace(/\+/, '', email);	
986	
987    var filter = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i
988    return filter.test(email);
989}
990
991function validateTelephone(telephone)
992{
993    var filter = /^[0+][0-9\-\s()]{5,15}$/i
994    return filter.test(telephone);
995}
996
997/* Product Selection
998---------------------------------------*/
999
1000function initProductSelection()
1001{
1002	const products = document.querySelectorAll('.anp-product');
1003	const noMatches = document.getElementById('anp-no-products');
1004	
1005	document.querySelectorAll('.anp-product-filter').forEach(input => {
1006		input.addEventListener('input', function (event) {
1007			applyProductFilters();
1008		}, false);
1009	});
1010	
1011	products.forEach(product => {
1012		const button = product.querySelector('.anp-product-button');
1013
1014		button.addEventListener('click', function (event) {
1015			selectProduct(product, products);
1016		});
1017	});
1018	
1019	applyProductFilters();
1020}
1021
1022function applyProductFilters() {
1023	const products = document.querySelectorAll('.anp-product');
1024	const noMatches = document.getElementById('anp-no-products');
1025	
1026	const categoryValue = document.getElementById('anp-contract-length')?.value ?? '';
1027	const productLineValue = document.getElementById('anp-product-line')?.value ?? '';
1028	const productSuffixValue = document.getElementById('anp-product-suffix')?.value ?? '';
1029			
1030	let selector = '';
1031
1032	if (categoryValue.length) {
1033		selector += '.category--' + categoryValue;
1034	}
1035			
1036	if (productLineValue.length) {
1037		selector += '.product-line--' + productLineValue;
1038	}
1039			
1040	if (productSuffixValue.length) {
1041		selector += '.product-suffix--' + productSuffixValue;
1042	}
1043			
1044	products.forEach(element => {
1045		element.classList.remove('visible');
1046		toggleProduct(element, false);
1047		onProductSelectionChange();
1048	});
1049			
1050	if (selector != '') {
1051		const matchingElements = document.querySelectorAll(selector);
1052				
1053		if (matchingElements && matchingElements.length) {
1054			noMatches.classList.remove('visible');
1055		} else {
1056			noMatches.classList.add('visible');
1057		}
1058				
1059		matchingElements.forEach(element => {
1060			element.classList.add('visible');
1061		});
1062	}
1063}
1064
1065function selectProduct(product, allProducts)
1066{
1067	const multiSelect = product.classList.contains('anp-addon');
1068	const selected = product.classList.contains('selected');
1069	
1070	const isVOIP = product.getAttribute('data-is-voip') == 1;
1071	const isInPorting = product.getAttribute('data-is-voip-porting') == 1;
1072
1073	if (!multiSelect && allProducts) {
1074		allProducts.forEach(product => {
1075			toggleProduct(product, false);
1076		});
1077	}
1078	
1079	
1080	if (isVOIP && !isInPorting) {
1081		const otherVOIPs = [...document.querySelectorAll('.anp-product.selected[data-is-voip="1"]')]
1082			.filter(element => element !== product);
1083					
1084		if (otherVOIPs.length) {
1085			alert('You can only have one VOIP product. The previously-selected option will be removed.');
1086		}
1087				
1088		otherVOIPs.forEach(element => {
1089			toggleProduct(element, false);
1090		});
1091	}
1092	
1093	const button = product.querySelector('.anp-product-button');
1094	
1095	if (selected) {
1096		toggleProduct(product, false);
1097	} else {
1098		 toggleProduct(product, true);
1099		
1100		if (!multiSelect) {
1101			scrollToFooter();
1102		}
1103	}
1104	
1105	onProductSelectionChange();
1106}
1107
1108function toggleProduct(product, selected)
1109{
1110	const button = product.querySelector('.anp-product-button');
1111	const selectedLabel = product.getAttribute('data-selected-label');
1112	const deselectedLabel = product.getAttribute('data-deselected-label');
1113	
1114	if (selected) {
1115		product.classList.add('selected');
1116		button.innerText = selectedLabel;
1117	} else {
1118		product.classList.remove('selected');
1119		button.innerText = deselectedLabel;
1120	}
1121}
1122
1123function onProductSelectionChange()
1124{
1125	const input = document.getElementById('anp-selected-products');
1126	const ids = [];
1127	let showVoipForm = false;
1128	let showInPortingForm = false;
1129
1130	document.querySelectorAll('.anp-product.selected').forEach(product => {
1131		const productID = product.getAttribute('data-product-id');
1132		const isVOIP = product.getAttribute('data-is-voip') == 1;
1133		const isInPorting = product.getAttribute('data-is-voip-porting') == 1;
1134		
1135		if (isVOIP) {
1136			showVoipForm = true;
1137		}
1138		
1139		if (isInPorting) {
1140			showInPortingForm = true;
1141		}
1142		
1143		ids.push(productID);
1144	});
1145	
1146	const unselectableClass = 'unselectable';
1147	const selectedClass = 'selected';
1148	
1149	document.querySelectorAll('.anp-product--child').forEach(product => {
1150		const parentID = product.getAttribute('data-parent-id');
1151		const childID = product.getAttribute('data-parent-id');
1152		const index = ids.indexOf(parentID);
1153		const childIndex = ids.indexOf(childID);
1154		
1155		if (index >= 0) {
1156			product.classList.remove(unselectableClass);
1157		} else {
1158			product.classList.add(unselectableClass);
1159			product.classList.remove(selectedClass);
1160			ids.splice(index, childIndex);
1161		}
1162	});
1163	
1164	input.value = ids.join(',');
1165	
1166	const wrapper = document.getElementById('anp-product-selection');
1167	const selectedLabel = wrapper.getAttribute('data-selected-next-label');
1168	const unselectedLabel = wrapper.getAttribute('data-unselected-next-label');
1169	
1170	let buttonLabel = ids.length ? selectedLabel : unselectedLabel;
1171	
1172	if (buttonLabel) {
1173		document.querySelectorAll('.anp-skip-button').forEach(element => {
1174			element.querySelector('.anp-button__label').innerText = buttonLabel;
1175		});
1176	}
1177	
1178	const voipForm = document.getElementById('anp-voip-form');
1179	
1180	if (voipForm) {
1181		if (showVoipForm) {
1182			voipForm.classList.add('visible');
1183		} else {
1184			voipForm.classList.remove('visible');
1185		}
1186	}
1187	
1188	const inPortingForm = document.getElementById('anp-in-porting-form');
1189	
1190	if (inPortingForm) {
1191		if (showInPortingForm) {
1192			inPortingForm.classList.add('visible');
1193		} else {
1194			inPortingForm.classList.remove('visible');
1195		}
1196	}
1197}
1198
1199/* Landing Page Products
1200---------------------------------------*/
1201
1202function initProductCategorySwitchers()
1203{
1204	var categoryLinks = $('.selection-nav__item');
1205	
1206	categoryLinks.click(function(){
1207		var activeClass = 'selection-nav__item--active';
1208		var element = $(this);
1209		
1210		categoryLinks.removeClass(activeClass);
1211		element.addClass(activeClass);
1212		
1213		var activeCategoryClass ='selection-category--active';
1214		var categoryID = element.data('category');
1215		$('.selection-category').removeClass(activeCategoryClass);
1216		$('.selection-category--id-' + categoryID).addClass(activeCategoryClass);
1217		
1218		
1219		return false;
1220	});
1221}
1222
1223function initProductShowcaseCarousels()
1224{
1225	 document.querySelectorAll('.anp-showcase-carousel').forEach(wrapperNode => {
1226		const viewportNode = wrapperNode.querySelector('.anp-showcase-carousel__viewport');
1227		const prevButtonNode = wrapperNode.querySelector('.anp-showcase-carousel__prev');
1228		const nextButtonNode = wrapperNode.querySelector('.anp-showcase-carousel__next');
1229		const dotNodes = wrapperNode.querySelectorAll('.anp-showcase-carousel__dot')
1230		
1231		if (prevButtonNode && nextButtonNode) {
1232			const emblaApi = EmblaCarousel(viewportNode, { loop: true });
1233				
1234			const nextSlide = () => {
1235				let nextIndex = parseInt(wrapperNode.getAttribute('data-index'));
1236				let maxIndex = parseInt(wrapperNode.getAttribute('data-max'));
1237				
1238				nextIndex++;
1239				
1240				if (nextIndex >= maxIndex) {
1241					nextIndex = 0;
1242				}
1243		
1244				gotoSlide(nextIndex);
1245			};
1246			
1247			const prevSlide = () => {
1248				let nextIndex = parseInt(wrapperNode.getAttribute('data-index'));
1249				let maxIndex = parseInt(wrapperNode.getAttribute('data-max'));
1250				nextIndex--;
1251				
1252				if (nextIndex < 0) {
1253					nextIndex = maxIndex - 1;
1254				}
1255		
1256				gotoSlide(nextIndex);
1257			};
1258			
1259			const gotoSlide = (slide) => {
1260				wrapperNode.setAttribute('data-index', slide);
1261				emblaApi.scrollTo(slide);
1262				
1263				dotNodes.forEach(el => {
1264					el.classList.remove('active');
1265				});
1266				
1267				const dot = wrapperNode.querySelector('.anp-showcase-carousel__dot--' + slide);
1268				
1269				if (dot) {
1270					dot.classList.add('active');
1271				}
1272			}
1273			
1274			prevButtonNode.addEventListener('click', () =>  prevSlide(), false);
1275			nextButtonNode.addEventListener('click', () =>  nextSlide(), false);
1276			
1277			dotNodes.forEach(dot => {
1278				dot.addEventListener('click', (e) => {
1279					const index = dot.getAttribute('data-slide');
1280					gotoSlide(index);
1281					e.preventDefault();
1282				}, false);
1283			});
1284		}
1285	});
1286}
1287
1288/* Booking Calendar
1289---------------------------------------*/
1290
1291var _bookingTimeslots = {};
1292var _bookingTimeslotsLoaded = false;
1293var _timeslotsView = 'selected';
1294var _bookingSurveyRequester;
1295var _bookingMonthSwitcher;
1296var _useEarliestSlot = true;
1297
1298function initCalendar()
1299{
1300	var date = new Date();
1301	var month = date.getMonth() + 1;
1302	var year = date.getFullYear();
1303	var calendar = _bookingCalendar;
1304	var container = $('#anp-book-timeslot');
1305			
1306	calendar.find('.anp-cal-reselect-trigger').click(function(){
1307		_useEarliestSlot = false;
1308		loadCalendarTimeslots();
1309		return false;
1310	});
1311	
1312	if (!container.hasClass('anp-cal-success')) {
1313		container.hide();
1314		loadCalendarTimeslots();
1315	} else {
1316		switchCalendarView('selected');
1317		container.show();
1318	}
1319	
1320	_bookingSurveyRequester = $('#anp-timeslot-survey-requested');
1321	
1322	_bookingSurveyRequester.change(function(){
1323		loadCalendarTimeslots(null, null, surveyRequested());
1324	});
1325	
1326	$('#anp-timeslot-reselect').click(function(){
1327		
1328		if (!_bookingTimeslotsLoaded) {
1329			loadCalendarTimeslots();
1330		}
1331		
1332		switchCalendarView('reselect');
1333		toggleBackNextNavigation(false);
1334		toggleBookingReselectControls(false);
1335		return false;
1336	});
1337	
1338	_bookingMonthSwitcher = $('#anp-booking-calendar-switch-month');
1339	
1340	_bookingMonthSwitcher.change(function(){
1341		var value = $(this).val();
1342		var tmp = value.split('-');
1343		var month = parseInt(tmp[1]) - 1;
1344		loadCalendarTimeslots(month, tmp[0], surveyRequested());
1345	});
1346}
1347
1348function surveyRequested()
1349{
1350	return _bookingSurveyRequester.is(':checked');
1351}
1352
1353function toggleBookingReselectControls(active)
1354{
1355	var controls = $('.anp-timeslot-reselect-hide');
1356
1357	if (active) {
1358		controls.show();
1359	} else {
1360		controls.hide();
1361	}
1362}
1363
1364function loadCalendarTimeslots(month, year, requestSurvey)
1365{
1366	toggleSpinner(true);
1367
1368	if (typeof month != 'undefined') {
1369		month = month + 1;
1370	}
1371	
1372	var data = { action: 'anp_get_timeslots', month: month, year: year };
1373	
1374	if (typeof requestSurvey != 'undefined') {
1375		data['request_survey'] = requestSurvey ? 1 : 0;
1376	}
1377		
1378	$.ajax({
1379        type: 'post',
1380        url: anp_settings.ajax_url,
1381        dataType: 'json',
1382        data: data,
1383        complete: function(response){
1384            toggleSpinner(false);
1385            var json = response.responseJSON;
1386			onCalendarTimeSlotsLoaded(json);
1387        }
1388    });
1389}
1390
1391function onCalendarTimeSlotsLoaded(response)
1392{
1393	var copydeck = response.copydeck;
1394	
1395	_bookingTimeslots = response ? response.slots : {};
1396	_bookingTimeslotsLoaded = true;
1397	
1398	
1399	
1400	$('.obj-intro__headline').text(copydeck.title);
1401	$('.obj-intro__strapline').html(copydeck.subheading);
1402	$('.obj-timeslot-instructions').html(copydeck.instructions);
1403	
1404	var readMore = copydeck.instructions_read_more;
1405	var hasReadMore = readMore && readMore.length > 10;
1406	
1407	if (hasReadMore) {
1408		$('.obj-timeslot-instructions-more').show();
1409		$('.obj-timeslot-instructions-more-copy').html(readMore);
1410	} else {
1411		$('.obj-timeslot-instructions-more').hide();
1412	}
1413	
1414	$('#anp-calendar-form').show();
1415	
1416	const dateString = response.start_date;
1417	const dateObject = new Date(dateString);
1418
1419	const formatted = dateObject.toLocaleDateString("en-GB", {
1420		month: "long",
1421		year: "numeric"
1422	});
1423
1424	document.querySelectorAll('.anp-calendar-title').forEach(element => {
1425		element.innerText = formatted;
1426	});
1427	
1428	var slots = response.slots;
1429	
1430	if (typeof slots == 'undefined') {
1431		$('#anp-book-timeslot').show();
1432		return;
1433	}
1434		
1435	var d = 0;
1436	var initialDateElement = $('#anp-calendar-initial-date');
1437	
1438	for (var date in slots) {
1439		var day = slots[date];
1440
1441		if (d == 0) {
1442			var index = 0;
1443			var firstSlot = day[index];
1444			populateSelectedDate(initialDateElement, 'Earliest date available', firstSlot.formatted_date_short, firstSlot.label);
1445			
1446			if (_useEarliestSlot) {
1447				populateBookingInputs(date, index);
1448			}
1449		}
1450		
1451		d++;
1452	}
1453	
1454	switchCalendarView(_timeslotsView);
1455	toggleTimeslotCheckboxes(response.type, response.survey_requested);
1456	$('#anp-book-timeslot').show();
1457	
1458	var hasSlots = slots && Object.keys(slots).length;
1459	
1460	if (!hasSlots) {
1461		$('#anp-timeslots-selected, #anp-timeslots-footer, #anp-timeslot-checkbox-survey').hide();
1462		//return;
1463	}
1464	
1465	var setTo = response.set_calendar_to;
1466	setCalendarTo(setTo.month - 1, setTo.year);
1467	
1468	var prevNav = _bookingCalendar.find('.anp-calendar__prev');
1469	var nextNav = _bookingCalendar.find('.anp-calendar__next');
1470	
1471	if (response.allow_nav_back) {
1472		prevNav.show();
1473	} else {
1474		prevNav.hide();
1475	}
1476	
1477	if (response.allow_nav_next) {
1478		nextNav.show();
1479	} else {
1480		nextNav.hide();
1481	}
1482}
1483
1484function populateSelectedDate(element, title, date, times)
1485{
1486	element.find('.anp-date-title').text(title);
1487	element.find('.anp-date-date').text(date);
1488	element.find('.anp-date-times').text(times);
1489}
1490
1491function switchCalendarView(id)
1492{
1493	$('.anp-timeslots__view').hide();
1494	$('#anp-timeslots-' + id).show();
1495	_timeslotsView = id;
1496}
1497
1498function toggleTimeslotCheckboxes(type, surveyRequested)
1499{
1500	$('.anp-timeslot-checkbox').hide();
1501	
1502	if (type) {
1503		$('#anp-timeslot-checkbox-' + type).show();
1504	}
1505
1506	if (surveyRequested == 1) {
1507		$('#anp-timeslot-checkbox-installation').show();
1508	}
1509}
1510
1511function setCalendarTo(month, year)
1512{
1513	var calendar = _bookingCalendar;
1514	var startDate = new Date(year, month, 1);
1515	var startDay = startDate.getDay();
1516	startDay = (startDay === 0) ? 7 : startDay;
1517	
1518	var cells = calendar.find('.anp-cal-td');
1519	cells.removeClass('inactive');
1520	cells.removeClass('active');
1521	cells.removeClass('preferred');
1522	
1523	var d = 1;
1524	var lastDayNumber = 0;
1525	
1526	cells.each(function () {
1527		var cell = $(this);
1528		var dayLabel = '';
1529
1530		var dayLink = cell.find('.dday');
1531		dayLink.removeClass('available');
1532		dayLink.removeClass('selected');
1533		
1534		var dateLabel = cell.find('.mlabel');
1535		dateLabel.text('');
1536				
1537		if (d >= startDay) {
1538			
1539			var dateString = startDate.toString();
1540			var tmp = dateString.split(' ');
1541			
1542			cell.addClass('active');
1543	
1544			if ( !(d > 28 && tmp[2] <= 5) ) {
1545				startDate.setDate(startDate.getDate() + 1);
1546			}
1547			
1548			var monthString = (month + 1).toString();
1549			
1550			if (monthString.length == 1) {
1551				monthString = '0' + monthString;
1552			}
1553			
1554			var dayNumber = parseInt(tmp[2]);
1555			
1556			if (dayNumber > lastDayNumber) {
1557				dayLabel = tmp[2];
1558				lastDayNumber = dayNumber;
1559			}
1560			
1561			var dateString = [year, monthString, tmp[2]].join('-');
1562			var config = _bookingTimeslots[dateString];
1563
1564			if (config && dayLabel != '') {
1565				
1566				dayLink.addClass('available');
1567				dateLabel.text(config[0].formatted_date_short);
1568				
1569				for (var _c in config) {
1570					if (config[_c].preferred) {
1571						cell.addClass('preferred');
1572					}
1573				}
1574			}
1575			
1576			cell.data('date', dateString);
1577		}
1578		
1579		dayLink.text(dayLabel);
1580		d++;
1581	});
1582
1583	
1584	var surveyRequestInput = $('#anp-timeslot-survey-requested');
1585	var surveyRequested = surveyRequestInput.is(':checked');
1586
1587	calendar.find('.anp-calendar__prev').off().click(function(){
1588		month--;
1589		
1590		if (month < 0) {
1591			month = 11;
1592			year--;
1593		}
1594		
1595		loadCalendarTimeslots(month, year, surveyRequested);
1596		
1597		return false;
1598	});
1599	
1600	calendar.find('.anp-calendar__next').off().click(function(){
1601		month++;
1602		
1603		if (month > 11) {
1604			month = 0;
1605			year++
1606		}
1607		
1608		loadCalendarTimeslots(month, year, surveyRequested);
1609		
1610		return false;
1611	});
1612	
1613	$('.anp-cal-a').off();
1614	$('.anp-cal-td').removeClass('selected');
1615	//onBookingDateSelected();
1616	
1617	calendar.find('.anp-cal-a').click(function () {
1618	
1619		var element = $(this);
1620		
1621		if (element.hasClass('available')) {
1622			var parent = element.parent();
1623			$('.anp-cal-td').removeClass('selected');
1624			parent.addClass('selected');
1625			onBookingDateSelected();
1626			
1627			$('#anp-timeslot-timepickers').show();
1628		}
1629		
1630		return false;
1631	});
1632	
1633	_bookingMonthSwitcher.val(year + '-' + (month + 1) );
1634}
1635
1636function onBookingDateSelected()
1637{
1638	var config = getSelectedBookingDateConfig();
1639	var timeInputs = $('.anp-time');
1640	
1641	timeInputs.removeClass('active');
1642	timeInputs.removeClass('selected');
1643	
1644	toggleBookingReselectControls(false);
1645	toggleBackNextNavigation(false);
1646	
1647	for (var i=0;i<config.length;i++) {
1648		var period = config[i]['period'];
1649		populateTimeSelector(i, period, config[i]);
1650	}
1651
1652	timeInputs.off().click(function () {
1653		$('.anp-time').removeClass('selected');
1654		$(this).addClass('selected');
1655		onBookingTimeSelected();
1656		return false;
1657	});
1658	
1659	populateBookingSummary();
1660}
1661
1662function onBookingTimeSelected()
1663{
1664	populateBookingSummary();
1665	toggleBookingReselectControls(true);
1666	toggleBackNextNavigation(true);
1667}
1668
1669function getSelectedBookingDateConfig()
1670{
1671	var date = getSelectedBookingDate();
1672	
1673	if (date) {
1674		return _bookingTimeslots[date];
1675	}
1676	
1677	return false;
1678}
1679
1680function getSelectedBookingDate()
1681{
1682	var selected = $('.anp-cal-td.selected');
1683	
1684	if (selected.length) {
1685		return selected.data('date');
1686	}
1687	
1688	return false;
1689}
1690
1691function populateTimeSelector(index, period, config)
1692{
1693	period = period.toLowerCase();
1694	var element = $('#anp-period-select-' + period);
1695	element.data('index', index);
1696	element.find('.anp-time__date').text(config['formatted_date']);
1697	element.find('.anp-time__time').text(config['label']);
1698	element.addClass('active');
1699}
1700
1701function populateBookingSummary()
1702{
1703	var config = getSelectedBookingDateConfig();
1704	
1705	if (!config) {
1706		return false;		
1707	}
1708	
1709	var date = getSelectedBookingDate();
1710	var displayElement = $('#anp-calendar-selected-date');	
1711	var timeSelector = $('.anp-time.selected');
1712	var timeLabel = 'Please select a time';
1713	
1714	var selectedDate = config[0].formatted_date;
1715
1716	if (timeSelector.length) {
1717		var index = timeSelector.data('index');
1718		var timeConfig = config[index];
1719		
1720		timeLabel = 'Arrival time: ' + timeConfig.label;
1721		populateBookingInputs(date, index);
1722	}
1723	
1724	populateSelectedDate(displayElement, '', selectedDate, timeLabel);
1725}
1726
1727function populateBookingInputs(date, index)
1728{
1729	var config = _bookingTimeslots[date];
1730	var timeConfig = config[index];
1731	var selectedTime = timeConfig.label.toUpperCase();
1732	var selectedPeriod = timeConfig.period.toUpperCase();
1733	var selectedDescription = timeConfig.description_short;
1734	
1735	$('#anp-timeslot-date').val(date);
1736	$('#anp-timeslot-times').val(selectedTime);
1737	$('#anp-timeslot-period').val(selectedPeriod);
1738	$('#anp-timeslot-text').val(selectedDescription);
1739}
1740
1741/* Survey
1742---------------------------------------*/
1743
1744function initSurveys()
1745{	
1746	$('.anp-survey').each(function () {
1747		var survey = $(this);
1748		var inputSection = survey.find('.anp-survey__other');
1749		var sessionUID = survey.data('session');
1750
1751		survey.find('.anp-tickbox').change(function(){
1752			var input = $(this);
1753			var checked = input.is(':checked');
1754			
1755			if (input.hasClass('anp-survey-user-input') && checked) {
1756				inputSection.show();
1757			} else {
1758				inputSection.find('input').val('');
1759				inputSection.hide();
1760			}
1761
1762		});
1763		
1764		survey.find('.anp-tickbox.anp-survey-standalone').change(function(){
1765			var value = $(this).val();
1766			
1767			var data = {
1768				action: 'anp_thanks_survey',
1769				value: value,
1770				session: sessionUID
1771			}
1772			
1773			toggleSpinner(true);
1774			
1775			$.ajax({
1776				type: 'post',
1777				url: anp_settings.ajax_url,
1778				dataType: 'json',
1779				data: data,
1780				complete: function () {
1781					survey.hide(200);
1782					toggleSpinner(false);
1783				}
1784			})
1785			
1786		});
1787	});
1788}
1789
1790/* Loading Spinner
1791---------------------------------------*/
1792
1793function toggleSpinner(active)
1794{
1795    var element = jQuery('#anp-spinner');
1796
1797    if (active) {
1798        element.fadeIn();
1799    } else {
1800        element.fadeOut();
1801    }
1802}
1803
1804/* Loading Spinner
1805---------------------------------------*/
1806
1807var _progressInterval;
1808var _progressBar;
1809
1810function showProgressBar(label, seconds, onComplete)
1811{
1812    var element = jQuery('#anp-progress');
1813	var labelElement = element.find('.anp-progress__label');
1814	
1815	_progressBar = element.find('.anp-progress__value');
1816	labelElement.text(label);
1817	element.fadeIn();
1818	
1819	var elapsedSeconds = 0;
1820	var percent = 0;
1821	
1822	_progressBar.width(0);
1823	
1824	clearInterval(_progressInterval);
1825	
1826	_progressInterval = setInterval(function(){
1827		
1828		elapsedSeconds += 0.1;
1829		
1830		percent = (elapsedSeconds / seconds) * 100;
1831		
1832		if (percent >= 100) {
1833			percent = 100;
1834			completeProgressBar();
1835			if (onComplete) {
1836				onComplete();
1837			}
1838		}
1839			
1840		_progressBar.width(percent + '%');
1841	}, 100);
1842}
1843
1844function completeProgressBar()
1845{
1846	
1847	if (typeof _progressBar == 'undefined') {
1848		return;
1849	}
1850	
1851	clearInterval(_progressInterval);
1852	_progressBar.width('100%');
1853	
1854	setTimeout(function (){
1855		jQuery('#anp-progress').fadeOut(function(){
1856			_progressBar.width(0);
1857			
1858		});
1859	}, 500);
1860}	
1861
1862/* Tracking
1863---------------------------------------*/
1864
1865function initTracking()
1866{
1867	//
1868}
1869
1870function trackNewLeadEvent(postcode, productLine, type, action, splitTest, callback)
1871{
1872	postcode = normalisePostcode(postcode);
1873	
1874	var data = {
1875		event: 'lead',
1876		postcode: postcode,
1877		product_line: productLine,
1878		type: type,
1879		action: action,
1880		journey_split: splitTest,
1881	};
1882	
1883	pushToDataLayer(data, callback);
1884}
1885
1886function pushToDataLayer(data, callback)
1887{
1888	window.dataLayer = window.dataLayer || [];
1889
1890	// Assisted journeys are kept out of the data layer so they aren't attributed as web orders.
1891	var suppress = (typeof anp_settings !== 'undefined' && '1' === anp_settings.suppress_tracking);
1892
1893	console.log(suppress ? 'Suppressing datalayer push (assisted journey)' : 'Pushing to datalayer');
1894	console.log(data);
1895
1896	if (callback) {
1897		setTimeout(function(){
1898			callback();
1899			console.log('Executing datalayer callback');
1900		}, 100);
1901	}
1902
1903	if (suppress) {
1904		return;
1905	}
1906
1907	dataLayer.push(data);
1908}
1909
1910/* One Touch Switch
1911---------------------------------------*/
1912
1913var _otsErrorBox;
1914var _otsErrorDefault;
1915var _otsErrorCustom;
1916var _otsCallToActionWrapper;
1917var _otsCallToActions;
1918var _otsPortingNumber;
1919var _otsOpIn;
1920
1921function initOneTouchSwitch()
1922{
1923	_otsErrorBox = $('#anp-ots-error-box');
1924	_otsErrorDefault = $('#anp-ots-error-default');
1925	_otsErrorCustom = $('#anp-ots-error-custom');
1926	_otsOpIn = $('#anp-ots-opt-in');
1927	_otsOpOut = $('#anp-ots-opt-out');
1928	_otsCallToActionWrapper = $('#anp-ots-error-ctas');
1929	_otsCallToActions = $('.anp-ots-next-action');
1930	_otsPortingNumber = $('#anp-ots-porting-number');
1931	
1932	$('#anp-ots-optin-wrapper').show();
1933	
1934	_otsOpIn.change(function(){
1935		var element = $(this);
1936		var toggleable = $('.anp-ots-hidden');
1937		
1938		if (element.is(':checked')) {
1939			toggleable.show();
1940			toggleOneTouchSwitchFooter(true);
1941		} else {
1942			toggleable.hide();
1943			hideOneTouchSwitchError();
1944		}
1945	});
1946	
1947	_otsOpOut.change(function(){
1948		var element = $(this);
1949		var toggleable = $('.anp-ots-hidden');
1950		
1951		if (!element.is(':checked') && _otsOpIn.is(':checked')) {
1952			toggleable.show();
1953			toggleOneTouchSwitchFooter(true);
1954		} else {
1955			toggleable.hide();
1956			hideOneTouchSwitchError();
1957		}
1958	});
1959	
1960	var validates = false;
1961	
1962	$('#anp-checkout-ots-trigger').click(function(){
1963		
1964		validates = validateCheckoutForm();
1965		
1966		if (validates) {
1967			submitOneTouchSwitch();
1968		}
1969		
1970		return false;
1971	});
1972	
1973	_otsCallToActions.click(function(){
1974		
1975		var action = $(this).data('action');
1976		var confirmed = true;
1977		
1978		if ('continue' == action) {
1979			confirmed = confirm("Are you sure?\n\nYou will need to contact your current provider to cancel the services you have with them.");
1980		} else if ('cancel' == action) {
1981			confirmed = confirm("Are you sure?\n\nThis will cancel the ordering process for now.");
1982		}
1983		
1984		if (confirmed) {
1985			submitOneTouchSwitchNextAction(action);
1986		}
1987
1988		return false;
1989	});
1990}
1991
1992function submitOneTouchSwitch()
1993{
1994	var data = getFormData(_checkoutForm);
1995	var task = data.ots_task;
1996	
1997	clearFormErrors(_checkoutForm);
1998	
1999	if ('match' == task) {
2000		submitOneTouchSwitchLookup();
2001	} else if ('options' == task) {
2002		submitOneTouchSwitchOptions();
2003	} else if ('voip_porting' == task) {
2004		submitOneTouchSwitchVoipPorting();
2005	} 
2006}
2007
2008function submitOneTouchSwitchLookup()
2009{
2010	var data = getFormData(_checkoutForm);
2011	var otsEligible = data.ots_eligible;
2012	var otsOptIn = _otsOpIn.is(':checked');
2013	
2014	if (1 != otsEligible ) {
2015		toggleSpinner(true);
2016		_checkoutForm.submit();
2017		return false;
2018	}
2019	
2020	hideOneTouchSwitchError();
2021	toggleOneTouchSwitchFooter(false);
2022
2023	data['ots_opt_in'] = otsOptIn ? 1 : 0;
2024	data['action'] = 'anp_ots_match';	
2025	delete data._wpnonce;
2026	
2027	$.ajax({
2028        type: 'POST',
2029		url: anp_settings.ajax_url,
2030        dataType: 'json',
2031        data: data,
2032        complete: function(response){
2033            completeProgressBar();
2034			onOneTouchSwitchMatchComplete(response.responseJSON);
2035        }
2036    });
2037	
2038	if (otsOptIn) {
2039		showProgressBar('Verifying your information - this may take a moment', 65, function(){
2040			showOneTouchSwitchError({ action: 'retry_or_continue' });
2041		});
2042	}
2043}
2044
2045function onOneTouchSwitchMatchComplete(response)
2046{
2047	var success = response.success;
2048	var action = response.action;
2049	
2050	if (!success) {
2051		showOneTouchSwitchError(response);
2052		$('#anp-ots-optin-wrapper').hide();
2053		scrollToOTSTop();
2054	} else {
2055		toggleSpinner(true);
2056		redirectToJourneyStart();
2057	}
2058}
2059
2060function showOneTouchSwitchError(response)
2061{
2062	var message = response.user_message;
2063	var customMessage = typeof message != 'undefined';
2064	
2065	if (customMessage) {
2066		_otsErrorDefault.hide();
2067		_otsErrorCustom.show();
2068		
2069		_otsErrorCustom.html(message);
2070	} else {
2071		_otsErrorDefault.show();
2072		_otsErrorCustom.hide();
2073	}
2074	
2075	var nextAction = response.action;
2076	
2077	_otsCallToActions.hide();
2078	_otsCallToActionWrapper.show();
2079	
2080	$('#anp-ots-form').hide();
2081	
2082	var ctas = _otsCallToActions.filter('.anp-ots-next-action--' + nextAction);
2083	
2084	ctas.show();
2085	
2086	_otsErrorBox.show();
2087	
2088	
2089	scrollToOTSTop();
2090}
2091
2092function submitOneTouchSwitchNextAction(nextAction)
2093{
2094	var data = {
2095		action: 'anp_ots_match_next_action',
2096		next_action: nextAction,
2097	};
2098	
2099	toggleSpinner(true);
2100	
2101	$.ajax({
2102        type: 'POST',
2103		url: anp_settings.ajax_url,
2104        dataType: 'json',
2105        data: data,
2106        complete: function(response){
2107			redirectToJourneyStart();
2108        }
2109    })
2110}
2111
2112function submitOneTouchSwitchVoipPorting()
2113{
2114	var formData = getFormData(_checkoutForm);
2115	
2116	toggleSpinner(true);
2117	
2118	$.ajax({
2119        type: 'POST',
2120		url: anp_settings.ajax_url,
2121        dataType: 'json',
2122        data: { 
2123			action: 'anp_ots_voip_porting', 
2124			ots_voip_port_provider_identity: formData.ots_voip_port_provider_identity, 
2125			ots_voip_port_number: formData.ots_voip_port_number
2126		},
2127        complete: function(response){
2128			var result = response.responseJSON;
2129	
2130			if (!result.success) {
2131				toggleSpinner(false);
2132				showFormErrors(result.errors);
2133			} else {
2134				redirectToJourneyStart();
2135			}
2136
2137        }
2138    });
2139}
2140
2141function hideOneTouchSwitchError()
2142{
2143	_otsErrorBox.hide(function(){
2144		_otsCallToActionWrapper.hide();
2145	});
2146}
2147
2148function submitOneTouchSwitchOptions()
2149{
2150	var formData = getFormData(_checkoutForm);
2151	var reference = formData.switch_order_reference;
2152	
2153	if (!reference) {
2154		scrollToElement($('#anp-ots-options'));
2155		return false;
2156	}
2157	
2158	toggleSpinner(true);
2159	
2160	$.ajax({
2161        type: 'POST',
2162		url: anp_settings.ajax_url,
2163        dataType: 'json',
2164        data: { 
2165			action: 'anp_ots_options', 
2166			order_reference: reference,
2167		},
2168        complete: function(response){
2169			onOneTouchSwitchOptionsComplete(response.responseJSON);
2170        }
2171    });
2172}
2173
2174function onOneTouchSwitchOptionsComplete(response)
2175{
2176	var success = response.success;
2177
2178	if (success) {
2179		redirectToJourneyStart();
2180	}
2181}
2182
2183function toggleOneTouchSwitchFooter(active)
2184{
2185	//active = true;
2186	
2187    var element = jQuery('#anp-ots-footer');
2188
2189    if (active) {
2190        element.show();
2191    } else {
2192        element.hide();
2193    }
2194}
2195
2196function retryOneTouchSwitch()
2197{
2198	hideOneTouchSwitchError();
2199	toggleOneTouchSwitchFooter(true);
2200}
2201
2202function scrollToOTSTop()
2203{
2204	scrollToElement($('#ots-top'));
2205}
2206
2207
2208/* Payment Details
2209---------------------------------------*/
2210
2211var _paymentBillingWrapper;
2212var _paymentChangeBillingTriggers;
2213
2214const debouncedBillingAddressLookup = debounce(submitBillingAddressSearch, 400);
2215
2216function initPaymentDetails()
2217{
2218	_paymentBillingWrapper = $('#anp-payment-billing-address');
2219	_paymentChangeBillingTriggers = $('.anp-change-billing');
2220	
2221	if (!_paymentBillingWrapper.length) {
2222		return;
2223	}
2224
2225	_paymentChangeBillingTriggers.click(function(){
2226		toggleBillingWrapper(true);
2227		setCustomBillingAddress(true);
2228		return false;
2229	});
2230	
2231	$('.anp-billing-address-reset-trigger').click(function(){
2232		toggleBillingWrapper(false);
2233		setCustomBillingAddress(false);
2234		
2235		$('#anp-billing-address-display').text(
2236			$('#anp-installation-address-display').text()
2237		);
2238		return false;
2239	});
2240
2241	_addressSelectElement = $('#anp-billing-addresses');
2242	
2243	_addressSelectElement.change(onBillingAddressSelectChange);
2244	
2245	const input = document.getElementById('anp-billing-partial-address');
2246	
2247	
2248	
2249	let lastLookupValue = '';
2250	
2251	input.addEventListener('input', function (e) {
2252		const el = e.target;
2253		let cleaned = el.value.replace(/[^a-z0-9]/gi, '');
2254
2255		cleaned = cleaned.slice(0, 7);
2256
2257		if (cleaned !== el.value) {
2258			el.value = cleaned;
2259		}
2260
2261		if (cleaned !== lastLookupValue) {
2262			if (cleaned.length > 5) {
2263				debouncedBillingAddressLookup();
2264			}
2265			
2266			lastLookupValue = cleaned;
2267		}
2268	});
2269}
2270
2271function setCustomBillingAddress(active)
2272{
2273	$('#anp-custom-billing').val(active ? 1 : 0);
2274}
2275
2276function submitBillingAddressSearch() {
2277
2278    const address = $('#anp-billing-partial-address').val();
2279	
2280    billingAddressApiSearch( address, _addressSearchPage );
2281}
2282
2283function billingAddressApiSearch( address, page, callback ) {
2284
2285    if (_addressesLoading) {
2286        return false;
2287    }
2288
2289    if (!address.length) {
2290        return false
2291    }
2292
2293    submitAddressSearchRequest(address, page, true, onBillingAddressSearchComplete);
2294}
2295
2296function onBillingAddressSearchComplete(json)
2297{
2298    if (!json['success']) {
2299        alert('An error occurred. Please try again shortly.');
2300        return;
2301    }
2302
2303    _addressSearchResults = json.data;
2304
2305    showBillingAddressSearchResults();
2306	toggleSpinner(false);
2307}
2308
2309function showBillingAddressSearchResults() {
2310    _addressSelectElement.empty();
2311
2312    addSearchSelectOption('', 'Select an address');
2313
2314    var addresses = _addressSearchResults;
2315
2316    if (addresses && addresses.length) {
2317        for (var a in addresses) {
2318            var address = addresses[a];
2319            addSearchSelectOption(a, address['summaryline'], false);
2320        }
2321    } else {
2322		_addressSelectElement.empty();
2323		addSearchSelectOption('null', 'No addresses found');
2324		$('.anp-billing-field').val('');
2325        toggleBillingManualAddress(true);
2326    }
2327}
2328
2329function onBillingAddressSelectChange() {
2330    var selected = _addressSelectElement.val();
2331
2332    if ( typeof _addressSearchResults[selected] != 'undefined' ) {
2333        populateBillingAddress(_addressSearchResults[selected]);
2334    } 
2335}
2336
2337function populateBillingAddress(address)
2338{
2339	toggleBillingManualAddress(true);
2340	
2341	$('#anp-billing-address-line-1').val(address.address1);
2342	$('#anp-billing-address-line-2').val(address.address2);
2343	$('#anp-billing-city').val(address.posttown);
2344	$('#anp-billing-postcode').val(address.postcode);
2345}
2346
2347function toggleBillingManualAddress(active)
2348{
2349	if (active) {
2350		$('#anp-payment-billing-fields').show();
2351	} else {
2352		$('#anp-payment-billing-fields').hide();
2353	}
2354}
2355
2356function toggleBillingWrapper(active)
2357{
2358	if (active) {
2359		_paymentBillingWrapper.show();
2360		_paymentChangeBillingTriggers.hide();
2361	} else {
2362		_paymentBillingWrapper.hide();
2363		_paymentChangeBillingTriggers.show();
2364	}
2365}
2366
2367/* Coupons
2368---------------------------------------*/
2369
2370function initCoupons()
2371{
2372	$('.anp-coupon-submit-trigger').click(function(){
2373		var couponInput = $(this).closest('.anp-coupons').find('.anp-coupon-code');
2374		var couponCode = couponInput.val();
2375		applyCoupon(couponCode);
2376		
2377		return false;
2378	});
2379	
2380	$('.anp-coupon-change-trigger').click(function(){
2381		applyCoupon('');
2382		return false;
2383	});
2384}
2385
2386function applyCoupon(couponCode) {
2387	toggleSpinner(true);
2388	
2389	$.ajax({
2390        type: 'post',
2391        url: anp_settings.ajax_url,
2392        dataType: 'json',
2393        data: { coupon_code: couponCode, action: 'anp_apply_coupon' },
2394        complete: function(response){
2395            toggleSpinner(false);
2396            var json = response.responseJSON;
2397			var code = json.coupon.code ?? '';
2398			
2399			if (json.errors['coupon_code']) {
2400				alert(json.errors['coupon_code'])
2401			}
2402			
2403			if (!code) {
2404				$('.anp-coupon-info').removeClass('active');
2405			} else {
2406				$('.anp-coupon-info').addClass('active');
2407			}
2408			
2409			var description = json.coupon.description ?? '';
2410			var terms = json.coupon.small_print ?? '';
2411
2412			
2413			$('.anp-input.anp-coupon-code').val(code);
2414			$('.anp-coupon-description').text(description)
2415			$('.anp-coupon-terms').text(terms);
2416        }
2417    });
2418}
2419
2420/* Kickoffs
2421---------------------------------------*/
2422
2423function initInputPlaceholders()
2424{
2425	const mq = window.matchMedia('(max-width: 500px)');
2426
2427    function updatePlaceholders(e) {
2428        $('[data-placeholder-mobile]').each(function () {
2429            const $el = $(this);
2430
2431            $el.attr(
2432                'placeholder',
2433                e.matches
2434                    ? $el.data('placeholder-mobile')
2435                    : $el.data('placeholder-desktop')
2436            );
2437        });
2438    }
2439
2440    updatePlaceholders(mq);
2441
2442    mq.addEventListener
2443        ? mq.addEventListener('change', updatePlaceholders)
2444        : mq.addListener(updatePlaceholders);
2445}
2446
2447/* Utilities
2448---------------------------------------*/
2449
2450function normalisePostcode(postcode)
2451{
2452	if (postcode == null) {
2453		return '';
2454	}
2455	
2456	return postcode.replace(/[^a-z|0-9]/ig, '').toUpperCase();
2457}
2458
2459function formatBillingSortCode(sortCode)
2460{
2461	sortCode = sortCode.replace(/[^0-9]/g, '');
2462	var formatted = (String(sortCode).match(/.{1,2}/g) || []).join('-');
2463	return formatted.substring(0, 8);
2464}
2465
2466function toggleBackNextNavigation(active)
2467{
2468	const element = document.getElementById('anp-obj-back-next');
2469	element.style.display = active ? 'block' : 'none';
2470}
2471
2472function scrollToElement(element, offset, speed)
2473{	
2474    if (!element.length) {
2475        return;
2476    }
2477	
2478    offset = offset ? offset : -200;
2479    speed = speed ? speed : .5;
2480
2481    var scrollTop = element.offset().top + offset;
2482
2483    $('html,body').animate({ scrollTop: scrollTop }, 1000 * speed);
2484}
2485
2486function scrollToFooter()
2487{	
2488	scrollToElement($('#obj-footer'));
2489}
2490
2491function redirectToJourneyStart()
2492{
2493	document.location.href = anp_settings.buy_url;
2494}
2495
2496function debounce(fn, delay)
2497{
2498	var timer;
2499	return function (...args) {
2500		clearTimeout(timer);
2501		timer = setTimeout(() => fn.apply(this, args), delay);
2502	};
2503}
2504
2505function isInViewport(el)
2506{
2507    const rect = el.getBoundingClientRect();
2508    return (
2509        rect.top >= 0 &&
2510        rect.left >= 0 &&
2511        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
2512        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
2513    );
2514}
2515
2516}( window.altnetOrderJourney = window.altnetOrderJourney || {}, jQuery ));
2517

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.