PageSourceSearch

https://venuesin.pt/wp-content/themes/MIUDO/assets/js/script.js?ver=1791262316

js venuesin.pt collected 2026-10-06 04:52:33 UTC 75,626 bytes, 2,311 lines download raw bytes

1(function( $ ) {
2
3	if (jQuery(window).scrollTop() >= 50) {
4
5		jQuery('.navigation-top').removeClass("transparent");
6
7	} else {
8		jQuery('.navigation-top').addClass("transparent");
9	}
10
11	$(window).scroll(function () {
12
13		if (jQuery(window).scrollTop() >= 50) {
14
15			jQuery('.navigation-top').removeClass("transparent");
16
17		} else {
18			jQuery('.navigation-top').addClass("transparent");
19		}
20	});
21
22
23	$('.wpmm-mobile-menu li a').on('click', function(){
24		setTimeout(function(){
25			$('.wpmm_mobile_menu_btn').click();
26		}, 500);
27	});
28
29
30	// Activate select2 on element
31	function activate_select2_element( select_element ) {
32		let select_options = {};
33
34		// Caso não deva haver search
35		if( $(select_element).attr('data-searchable') != 'true' ) {
36			select_options.minimumResultsForSearch = Infinity;
37		}
38
39		// Caso deva permitir escolher opção vazia
40		if( $(select_element).data('allow-empty-value') === 'true' ) {
41			select_options.allowClear = true;
42		}
43
44		$(select_element).select2( select_options );
45	}
46
47
48	// Activate all select2 elements with selector
49	function activate_all_select2_elements_by_selector( selector ) {
50		// Obter todos os elementos select2
51		var select2_elements = $( selector );
52
53		select2_elements.each( function() {
54			activate_select2_element( this );
55		});
56	}
57
58
59	// Select2
60	if( $('.select2').length > 0 ) {
61		// Ativar todos os elementos select2
62		activate_all_select2_elements_by_selector( '.select2' );
63	}
64
65
66
67	var max_file_text = $('.max-file-size').text();
68
69	$('input[type=file]').on('change', function(){
70		var filename = $(this).val().split('\\').pop();
71		$('.max-file-size').html(filename);
72		$('.formulario-mais-detalhes .cross-html').css('display','inline-block');
73	});
74
75	$('.cross-html').on('click', function(){
76		$('input[type=file]').replaceWith($('input[type=file]').val('').clone(true));
77		$('.max-file-size').text( max_file_text );
78		$('.formulario-mais-detalhes .cross-html').css('display','none');
79	});
80
81	var file_anexed = $('.info-anexarCV').text();
82
83	$('input[name="AnexarCV"]').on('change', function(){
84		var filename = $(this).val().split('\\').pop();
85
86		if(filename == '') {
87			$('.info-anexarCV').html(file_anexed);	
88		}else {
89			$('.info-anexarCV').html(filename);			
90		}
91	});
92
93	$(".update-current-year").text(new Date().getFullYear());
94
95	// Função que traduz a janela hovercard do plugin de chat
96	function wplc_translate_hovercard() {
97		var path = window.location.pathname;
98
99		if(path.includes("/en/") || path.includes("/es/")) {
100			// Tradução Inglês
101			if(path.includes("/en/")) {
102				// Textos
103				var hovercard_content = "What can we help you with?";
104				var speeching_button = "Submit";
105
106				// Modificar textos
107				$("#wplc_hovercard_content").text(hovercard_content);
108				$("#speeching_button").text(speeching_button);
109			}
110
111			// Tradução Espanhol
112			else if(path.includes("/es/")) {
113				// Textos
114				var hovercard_content = "¿Como podemos ayudar?";
115				var speeching_button = "Someter";
116
117				// Modificar textos
118				$("#wplc_hovercard_content").text(hovercard_content);
119				$("#speeching_button").text(speeching_button);
120			}
121		}
122	}
123
124
125	// Função que traduz a janela de live chat do plugin de chat
126	function wplc_translate_live_chat() {
127		var path = window.location.pathname;
128
129		$("#wplc_name").val("");
130
131		if(path.includes("/en/") || path.includes("/es/")) {
132			// Tradução Inglês
133			if(path.includes("/en/")) {
134				// Textos
135				var wplc_first_message_offline = "Chat offline. Leave a message.";
136				var wp_live_chat_2_info = "We will contact you as soon as possible.";
137				var send_message_button = "Send Message";
138
139				// Modificar textos
140				$("#wplc_first_message .wplc_offline").text(wplc_first_message_offline);
141				$("#wp-live-chat-2-info").text(wp_live_chat_2_info);
142				$("#wplc_na_msg_btn").val(send_message_button);
143			}
144
145			// Tradução Espanhol
146			else if(path.includes("/es/")) {
147				// Textos
148				var wplc_first_message_offline = "Chat sin conexión. Deja un mensaje.";
149				var wp_live_chat_2_info = "Nos pondremos en contacto con usted tan pronto como sea posible.";
150				var send_message_button = "Enviar Mensaje";
151
152				// Modificar textos
153				$("#wplc_first_message .wplc_offline").text(wplc_first_message_offline);
154				$("#wp-live-chat-2-info").text(wp_live_chat_2_info);
155				$("#wplc_na_msg_btn").val(send_message_button);
156			}
157		}
158	}
159	setTimeout(function() {
160		wplc_translate_hovercard();
161		wplc_translate_live_chat()
162	}, 3000);
163	
164	
165	
166	// ##########  Notícias  ##########
167	
168	// Pagination
169	$('#noticias-wrapper').on('click', '#noticias-pagination .page-link', function() {
170		// Get current page
171		let page = parseInt( $('#noticias-page-filter').val() );
172		
173		// Previous page
174		if( $(this).hasClass('page-link-prev') ) {
175			page = page - 1;
176		}
177		// Next page
178		else if( $(this).hasClass('page-link-next') ) {
179			page = page + 1;
180		}
181		// Specific page
182		else {
183			page = $(this).data('page');
184		}
185		
186		// Update page
187		$('#noticias-page-filter').val( page );
188		$(this).trigger('noticias-update-list');
189	});
190	
191	
192	// Update noticias list
193	var search_noticias_queue = 0;
194	$('#noticias-wrapper').on('noticias-update-list', '.update-noticias-list', function() {
195		search_noticias_queue += 1;
196		
197		let noticias_container = $('#noticias-wrapper');
198		
199		// Get filter values
200		let page = $('#noticias-page-filter').val();
201		
202		// Show loader
203		let loader_container = $('#noticias-wrapper');
204// 		loader_container.block( default_loader );
205		
206		// AJAX request
207		$.ajax({
208			type: 'POST',
209			dataType: 'html',
210			url: ajax_var.url,
211			data: {
212				action: 'get_noticias_paginated',
213				nonce: ajax_var.nonce,
214				page: page,
215				search_noticias_queue: search_noticias_queue
216			},
217			success:function( data ) {
218				let queue_number_response = '';
219				
220				$(data).each(function(){
221					if( $(this).find("[name='search_noticias_queue']").length > 0 ) {
222						queue_number_response = $(this).find("[name='search_noticias_queue']").val();
223					}
224				});
225				
226				
227				if( parseInt(queue_number_response) === search_noticias_queue ) {
228					$(noticias_container).html( data );
229					
230					// Hide loader
231				// 	loader_container.unblock();
232					
233					// Reset number queue
234					search_noticias_queue = 0;
235				}
236			},
237			error: function( errorThrown ) {
238				console.log( "Error: ", errorThrown );
239				
240				// Hide loader
241				// loader_container.unblock();
242			}
243		});
244	});
245	
246	jQuery(document).ready(function($) {
247		$(document).on('click', '.update-artigos-list', function(e) {
248			e.preventDefault();
249
250			var page = $(this).data('page');
251			if (!page) return; // Proteção para botões disabled
252
253			var data = {
254				'action': 'get_artigos_paginated',
255				'page': page
256			};
257
258			// Adiciona um efeito visual de loading (opcional)
259			$('#artigos-wrapper').css('opacity', '0.5');
260
261			$.post(ajaxurl, data, function(response) {
262				$('#artigos-wrapper').html(response);
263				$('#artigos-wrapper').css('opacity', '1');
264
265				// Faz scroll suave de volta para o topo da grelha
266				$('html, body').animate({
267					scrollTop: $("#artigos-wrapper").offset().top - 100
268				}, 500);
269			});
270		});
271	});
272	
273	
274	
275	
276	
277	
278	
279	
280	// ##########  Form AI validation  ##########
281
282	$('#form-ai-validate').on('blur', 'input[type="number"][min], input[type="number"][max]', function() {
283		let min = $(this).attr('min');
284		let max = $(this).attr('max');
285		let value = $(this).val();
286
287		// Adiciona uma verificação para campos vazios
288		if (value === '') {
289			// Se o campo for deixado vazio, força-o a ter o valor mínimo
290			if (min !== undefined) {
291				$(this).val(min);
292			}
293			return;
294		}
295
296		value = parseInt(value); // Converte o valor para número para comparações seguras
297
298		if ( min !== undefined ) {
299			min = parseInt( min );
300			if ( value < min ) {
301				value = min;
302			}
303		}
304
305		if ( max !== undefined ) {
306			max = parseInt( max );
307			if ( value > max ) {
308				value = max;
309			}
310		}
311
312		$(this).val(value);
313	});
314
315
316	var tooltip_options = {
317		container: 'body',
318		boundary: 'window',
319		html: true 
320	};
321
322	$('#form-ai-content').on('click', 'a.tooltip-icon', function(e) {
323		e.preventDefault();
324	});
325
326	$('#form-ai-content [data-toggle="tooltip"]').tooltip(tooltip_options);
327
328	// Mostra/esconde o campo de texto "Outro" para a Tipologia de Evento
329	$('#form-ai-tipologia-evento').on('change', function() {
330		if ($(this).val() === 'Outro') {
331			$('#form-ai-tipologia-evento-outro').slideDown();
332		} else {
333			$('#form-ai-tipologia-evento-outro').slideUp();
334		}
335	});
336
337	// ##########  Limitar dígitos de campos numéricos  ##########
338	$('#coffee-breaks').on('input', function() {
339		if (this.value.length > 2) this.value = this.value.slice(0, 2); 
340	});
341	$('#qts_noites, #qts_single, #qts_twin').on('input', function() {
342		if (this.value.length > 4) this.value = this.value.slice(0, 4); 
343	});
344
345	// Sincroniza o número de participantes principal com os campos dos espaços de evento
346	$('#form-ai-participants').on('input', function() {
347		let mainParticipants = $(this).val();
348		$('input[id^="nr_participantes_sala"]').val(mainParticipants);
349	});
350
351	// ##########  Adicionar salas secundarias  ##########
352	const layoutOptionsHtml = $('#sala-principal-disposicao').html();
353
354	// 	$('#qts-salas-adicionais').on('input', function() {
355	// 		const numSalas = parseInt($(this).val()) || 0;
356	// 		const container = $('#dynamic-salas-adicionais-container');
357
358	// 		container.html(''); 
359
360	// 		if (numSalas > 0) {
361	// 			const headerHtml = `
362	// <div class="row dynamic-salas-header mb-2">
363	// <div class="col-6"><label class="form-question-small">Nº de participantes em cada sala</label></div>
364	// <div class="col-6"><label class="form-question-small">Disposição das salas</label></div>
365	// </div>`;
366	// 			container.append(headerHtml);
367	// 		}
368
369	// 		for (let i = 1; i <= numSalas; i++) {
370	// 			const salaHtml = `
371	// <div class="row dynamic-sala-row align-items-center mb-2">
372	// <div class="col-2"><label class="sala-number">${i}º</label></div>
373	// <div class="col-4"><input type="number" name="sala_adicional_participantes[]" class="form-control form-control-sm" min="1" placeholder="Nº"></div>
374	// <div class="col-6"><select name="sala_adicional_disposicao[]" class="form-control form-control-sm">${layoutOptionsHtml}</select></div>
375	// </div>`;
376	// 			container.append(salaHtml);
377	// 		}
378	// 	});
379	
380	
381	var dynamic_salas_adicionais_num_participants_str = $('#salas-adicionais-num-participants-string').val();
382	var dynamic_salas_adicionais_layout_str = $('#salas-adicionais-layout-string').val();
383	var dynamic_salas_adicionais_number_abrev_str = $('#salas-adicionais-number-abrev-string').val();
384
385	$('#dynamic-event-space-days-container').on('input', 'input[id^="qts-salas-adicionais"]', function() {
386		const numSalas = parseInt($(this).val()) || 0;
387		const container = $(this).closest('.event-space-day').find('[id^="dynamic-salas-adicionais-container"]');
388		const layoutOptionsHtml = $('#sala-principal-disposicao').html();
389
390		container.html(''); 
391
392		if (numSalas > 0) {
393			const headerHtml = `
394            <div class="row dynamic-salas-header mb-2">
395            <div class="col-6"><label class="form-question-small">${dynamic_salas_adicionais_num_participants_str}</label></div>
396            <div class="col-6"><label class="form-question-small">${dynamic_salas_adicionais_layout_str}</label></div>
397            </div>`;
398			container.append(headerHtml);
399		}
400
401		for (let i = 1; i <= numSalas; i++) {
402			const salaHtml = `
403            <div class="row dynamic-sala-row align-items-center mb-2">
404            <div class="col-2"><label class="sala-number">${i}º</label></div>
405            <div class="col-4"><input type="number" name="sala_adicional_participantes[]" class="form-control form-control-sm" min="1" placeholder="${dynamic_salas_adicionais_number_abrev_str}"></div>
406            <div class="col-6"><select name="sala_adicional_disposicao[]" class="form-control form-control-sm">${layoutOptionsHtml}</select></div>
407            </div>`;
408			container.append(salaHtml);
409		}
410	});
411
412	// ##########  Adicionar dias de Espaços de Evento  ##########
413	function updateEventSpaceDays() {
414		const container = $('#dynamic-event-space-days-container');
415		const template = $('#event-space-day-template');
416		const desired_days = parseInt($('#form-ai-duration').val()) || 0;
417		const current_days = container.find('.dynamic-event-space-day').length;
418		const mainParticipantsValue = $('#form-ai-participants').val();
419
420		if (desired_days > current_days) {
421			for (let d = current_days + 1; d <= desired_days; d++) {
422				const new_day = template.clone();
423				new_day.addClass('dynamic-event-space-day').removeAttr('id').show();
424				new_day.find('.event-space-day-text').text(d);
425				new_day.find('[id^="nr_participantes_sala"]').val(mainParticipantsValue);
426
427				new_day.find('input, select').each(function() {
428					const old_id = $(this).attr('id');
429					if (old_id) {
430						$(this).attr('id', old_id + '_' + d);
431						new_day.find(`label[for="${old_id}"]`).attr('for', old_id + '_' + d);
432					}
433					const old_name = $(this).attr('name');
434					if(old_name) $(this).attr('name', old_name + '_day[' + d + ']');
435				});
436				new_day.find('[id^="dynamic-salas-adicionais-container"]').attr('id', 'dynamic-salas-adicionais-container_' + d);
437				container.append(new_day);
438				new_day.find('[id^="sala-principal-tipo_"]').prop('required', true);
439				new_day.find('[id^="nr_participantes_sala_"]').prop('required', true);
440				new_day.find('[id^="sala-principal-disposicao_"]').prop('required', true);
441			}
442		} else if (desired_days < current_days) {
443			container.find('.dynamic-event-space-day:gt(' + (desired_days - 1) + ')').remove();
444		}
445	}
446
447	// ##########  Adicionar dias de catering  ##########
448
449	function updateCateringDays() {
450		const container = $('#dynamic-catering-days-container');
451		const template = $('#catering-day-template');
452		const desired_days = parseInt($('#form-ai-duration').val()) || 0;
453		const current_days = container.find('.dynamic-catering-day').length;
454
455		// Cenário 1: ADICIONAR DIAS
456		if (desired_days > current_days) {
457			for (let d = current_days + 1; d <= desired_days; d++) {
458				const new_day = template.clone();
459				new_day.addClass('dynamic-catering-day').removeAttr('id').show();
460				new_day.find('.catering-day-text').text(d);
461
462				new_day.find('input').each(function() {
463					const old_id = $(this).attr('id');
464					if (old_id) {
465						const new_id = old_id + '_' + d;
466						$(this).attr('id', new_id);
467						new_day.find(`label[for="${old_id}"]`).attr('for', new_id);
468					}
469
470					const old_name = $(this).attr('name');
471					if (old_name) {
472						// Lógica de renomear para garantir unicidade
473						const new_name_base = old_name.replace(/_day\[\d+\]/g, '').replace('[]', '');
474						const new_name = new_name_base + '_day[' + d + ']' + (old_name.endsWith('[]') ? '[]' : '');
475						$(this).attr('name', new_name);
476					}
477				});
478				container.append(new_day);
479				new_day.find('[data-toggle="tooltip"]').tooltip(tooltip_options);
480			}
481		} 
482		// Cenário 2: REMOVER DIAS
483		else if (desired_days < current_days) {
484			container.find('.dynamic-catering-day:gt(' + (desired_days - 1) + ')').remove();
485		}
486
487	}
488
489	$('#dynamic-catering-days-container').on('change', 'input[id^="coffee_breaks_toggle"]', function() {
490		const dropdown = $(this).closest('.coffee-break-group').find('.coffee-breaks-input');
491
492		if ($(this).is(':checked')) {
493			dropdown.show();
494			dropdown.val('1'); 
495		} 
496		else {
497			dropdown.hide();
498		}
499	});
500	$('#catering-day-template .coffee-breaks-input').hide();
501
502	$('#form-ai-duration').on('input', function() {
503		updateCateringDays();
504		updateEventSpaceDays();
505	});
506
507	$('#form-ai-duration').on('blur', function() {
508		if (!$(this).val() || parseInt($(this).val()) < 1) {
509			$(this).val(1);
510			$(this).trigger('input');
511		}
512	});
513	updateCateringDays();
514	updateEventSpaceDays();
515
516	// ##########  Lógica de Submissão do Formulário  ##########
517	$('#form-ai-validate').on('submit', function(e) {
518		e.preventDefault();
519
520		let url_params = new URLSearchParams( window.location.search );
521
522		let debug_mode = false;
523		if( url_params.has( 'debug' ) && url_params.get( 'debug' ) === 'true' ) {
524			debug_mode = true;
525		}
526
527		$('.is-invalid').removeClass('is-invalid');
528		$('.invalid-feedback').removeClass('d-block').text('');
529		$('#form-ai-feedback-container').empty();
530		$('#label-inclui-catering').removeClass('has-error'); 
531
532		if (this.checkValidity() === false) {
533			$(this).addClass('was-validated'); 
534			$(this).find(':invalid').first().focus(); 
535
536			$(this).find('input:invalid, select:invalid').each(function() {
537				const field = $(this);
538				const message = field.prop('validationMessage') || 'Este campo é obrigatório'; // Mensagem do browser ou genérica
539
540				if (field.closest('.custom-number-input').length > 0) {
541					field.closest('.custom-number-input').addClass('is-invalid');
542				} else {
543					field.addClass('is-invalid');
544					if (field.hasClass('select2-hidden-accessible')) {
545						field.next('.select2-container').find('.select2-selection').addClass('is-invalid');
546					}
547				}
548				field.closest('.form-group, .custom-number-input').next('.invalid-feedback').text(message).addClass('d-block');
549			});
550
551			return;
552		}
553
554		let submit_button = $('#form-ai-submit');
555		let button_text = submit_button.children('span');
556		let button_loader = submit_button.children('.button-loader-container'); 
557
558		submit_button.attr('disabled', true);
559		button_text.hide();
560		button_loader.show();
561
562		// Obter dados do form
563		let form_data = new FormData( $(this)[0] );
564
565		// Obter salas adicionais por dia
566		let event_spaces_additional_rooms_days = {};
567		$('#dynamic-event-space-days-container .dynamic-event-space-day').each(function(index) {
568			let day_index = index + 1;
569			let daily_rooms = [];
570
571			$(this).find('.dynamic-sala-row').each(function() {
572				let room_info = {
573					"num_participants": $(this).find('input[name="sala_adicional_participantes[]"]').val() || 0,
574					"layout": $(this).find('select[name="sala_adicional_disposicao[]"]').val()
575				};
576				daily_rooms.push(room_info);
577			});
578
579			if (daily_rooms.length > 0) {
580				event_spaces_additional_rooms_days[day_index] = daily_rooms;
581			}
582		});
583
584		if (Object.keys(event_spaces_additional_rooms_days).length > 0) {
585			form_data.append('event_spaces_additional_rooms_days', JSON.stringify(event_spaces_additional_rooms_days));
586		}
587
588
589		// Get catering
590		if( $('#inclui_catering').is(':checked') ) {
591			let catering_details = [];
592			$('#dynamic-catering-days-container .dynamic-catering-day').each( function( index, day_element ) {
593				let day_info = {
594					"day": (index + 1),
595					"welcome_coffee": $(day_element).find('input[id^="welcome_coffee"]').is(':checked'),
596					"coffee_breaks": $(day_element).find('input[id^="coffee_breaks_toggle"]').is(':checked') ? (parseInt($(day_element).find('[id^="coffee_breaks_count"]').val()) || false) : false,
597					"welcome_drink": $(day_element).find('input[id^="welcome_drink"]').is(':checked'),
598					"lunch": $(day_element).find('input[id^="inclui_almoco"]').is(':checked'),
599					"canapes": $(day_element).find('input[id^="canapes"]').is(':checked'),
600					"dinner": $(day_element).find('input[id^="inclui_jantar"]').is(':checked'),
601					"open_bar": $(day_element).find('input[id^="bar_aberto"]').is(':checked'),
602					"supper": $(day_element).find('input[id^="ceia"]').is(':checked')
603				};
604
605				catering_details.push(day_info);
606			});
607
608			form_data.append( 'catering_details', JSON.stringify(catering_details) );
609		}
610		
611		
612		// Check URL params
613		// let total_visible = true;
614		let total_visible = false;
615		if( url_params.has( 'ver-hv' ) ) {
616		    total_visible = false;
617		    
618		    console.log( 'total_visible', total_visible );
619		}
620		
621		form_data.append( 'total_visible', total_visible );
622
623
624		$('#form-ai-validate .error_msg').html("");
625
626		$.ajax({
627			type: 'POST',
628			dataType: 'json',
629			contentType: false,
630			processData: false,
631			url: ajax_var.url,
632			data: form_data,
633			success: function( data ) {
634				console.log(data);
635
636				if( data.success === 'true' ) {
637					// --- Preenchimento do Cabeçalho e Título ---
638					let dataEvento = $('#form-ai-data-evento').val();
639					let location = $('#form-ai-location').val();
640					let tipologiasVenue = $('#form-ai-tipo-venue').val();
641					
642					let tipologiaEvento = $('#form-ai-tipologia-evento').val();
643					let tipologiaEventoTexto = $('#form-ai-tipologia-evento option:selected').map(function() {
644						return $(this).text();
645					}).get().join(', ');
646					
647					if (tipologiaEvento === 'Outro') {
648						tipologiaEventoTexto = $('#form-ai-tipologia-evento-outro').val();
649					}
650					
651					let participants = $('#form-ai-participants').val();
652					let duration = $('#form-ai-duration').val();
653
654					const localizacoesTexto = $('#form-ai-location option:selected').map(function() {
655						return $(this).text();
656					}).get().join(', ');
657
658					const tipologiasVenueTexto = $('#form-ai-tipo-venue option:selected').map(function() {
659						return $(this).text();
660					}).get().join(', ');
661
662					const participantsLabel = $('label[for="form-ai-participants"]').data('value');
663					const durationLabel = $('label[for="form-ai-duration"]').data('value');
664					
665					// Get current language
666					let language_formatted = document.getElementsByTagName('html')[0].getAttribute('lang');
667					language_formatted = language_formatted ?? 'pt-PT';
668
669					const dateObj = new Date(dataEvento + 'T00:00:00');
670					const formattedDate = dateObj.toLocaleDateString(language_formatted, { day: 'numeric', month: 'long', year: 'numeric' });
671
672					// --- PREENCHIMENTO DO CABEÇALHO DO RESULTADO COM OS TEXTOS VISÍVEIS ---
673					$('#form-ai-result .result-data').text(formattedDate);
674					$('#form-ai-result .result-location').text(localizacoesTexto);
675					$('#form-ai-result .result-venue-type').text(tipologiasVenueTexto);
676					$('#form-ai-result .result-event-type').text(tipologiaEventoTexto);
677					$('#form-ai-result .result-participants').text(participants + " " + participantsLabel);
678					$('#form-ai-result .result-duration').text(duration + " " + durationLabel);
679
680
681					// 					// --- LÓGICA PARA ESPAÇOS E SALAS ADICIONAIS ---
682					// 					const paxSalaPrincipal = $('#nr_participantes_sala').val() || 'N/A';
683					// 					const layoutSalaPrincipal = $('#sala-principal-disposicao option:selected').text();
684					// 					let salasDetailText = `Sala Principal - ${layoutSalaPrincipal} - ${paxSalaPrincipal} participantes`;
685
686					// 					$('.dynamic-sala-row').each(function(index) {
687					// 						const paxAdicional = $(this).find('input[name="sala_adicional_participantes[]"]').val() || 'N/A';
688					// 						const layoutAdicional = $(this).find('select[name="sala_adicional_disposicao[]"] option:selected').text();
689					// 						salasDetailText += ` + Sala Adicional - ${layoutAdicional} - ${paxAdicional} participantes`;
690					// 					});
691
692					// 					let salasPeriodoMontagem = $('#montagem_dia').val();
693					// 					salasDetailText += ` + Montagem - ${salasPeriodoMontagem}`;
694
695					// 					$('#form-ai-result .salas-line .detail').text(salasDetailText);
696					// 					$('#form-ai-result .salas-line').show();
697
698
699					// 					//----- LOGICA PARA CATERING ------
700					// 					if ($('#inclui_catering').is(':checked')) {
701					// 						const all_days_summary = [];
702
703					// 						// Itera sobre cada dia de catering que foi gerado
704					// 						$('#dynamic-catering-days-container .dynamic-catering-day').each(function(index) {
705					// 							const day_summary_parts = [];
706
707					// 							// Para cada input marcado, adiciona o texto da sua label ao resumo do dia
708					// 							$(this).find('input[type="checkbox"]:checked').each(function() {
709					// 								const label_text = $(`label[for="${$(this).attr('id')}"]`).text().trim();
710					// 								day_summary_parts.push(label_text);
711					// 							});
712
713					// 							// Adiciona a contagem de coffee breaks, se for maior que zero
714					// 							const coffeeBreaks = parseInt($(this).find('input[name^="coffee_breaks"]').val()) || 0;
715					// 							if (coffeeBreaks > 0) {
716					// 								day_summary_parts.push(`${coffeeBreaks} Coffee Break(s)`);
717					// 							}
718
719					// 							// Se houver algum item para este dia, formata a string
720					// 							if (day_summary_parts.length > 0) {
721					// 								all_days_summary.push(`Dia ${index + 1}: ${day_summary_parts.join(' + ')}`);
722					// 							}
723					// 						});
724
725					// 						// Junta os resumos de todos os dias e mostra no ecrã de resultados
726					// 						$('#form-ai-result .catering-line .detail').text(all_days_summary.join(' | '));
727					// 						$('#form-ai-result .catering-line').show();
728
729					// 					} else {
730					// 						$('#form-ai-result .catering-line').hide();
731					// 					}
732
733					// 					// --- LÓGICA PARA ALOJAMENTO ---
734					// 					if ($('#inclui_alojamento').is(':checked')) {
735					// 						const noites = $('#qts_noites').val();
736					// 						const single = $('#qts_single').val();
737					// 						const twin = $('#qts_twin').val();
738
739					// 						const noitesLabel = $('label[for="qts_noites"]').data('value');
740					// 						const singleLabel = $('label[for="qts_single"]').data('value');
741					// 						const twinLabel = $('label[for="qts_twin"]').data('value');
742
743					// 						const detailText = `${noites} ${noitesLabel}: ${single} ${singleLabel} e ${twin} ${twinLabel}`;
744
745					// 						$('#form-ai-result .accomodation-line .detail').text(detailText);
746					// 						$('#form-ai-result .accomodation-line').show();
747					// 					} else {
748					// 						$('#form-ai-result .accomodation-line').hide();
749					// 					}
750
751
752					// 					// --- LÓGICA PARA AUDIOVISUAIS ---
753					// 					if ($('#inclui_audiovisuais').is(':checked')) {
754					// 						const pacoteSelecionado = $('input[name="audio_option"]:checked').val();
755					// 						let detalhesDoPacote = [];
756
757					// 						$('input[name="audio_option"]:checked').closest('.package-radio').find('.package-item').each(function() {
758					// 							const detalheTexto = $(this).clone().children().remove().end().text().trim();
759					// 							detalhesDoPacote.push(detalheTexto);
760					// 						});
761
762					// 						let resultadoFinal = `${pacoteSelecionado}`;
763					// 						if (detalhesDoPacote.length > 0) {
764					// 							resultadoFinal += ` (${detalhesDoPacote.join(' + ')})`;
765					// 						}
766
767					// 						$('#form-ai-result .audiovisuals-line .detail').text(resultadoFinal);
768					// 						$('#form-ai-result .audiovisuals-line').show();
769					// 					} else {
770					// 						$('#form-ai-result .audiovisuals-line').hide();
771					// 					}
772
773
774					// If response is returned
775					if( 'response_endpoint' in data && data.response_endpoint ) {
776						// Simulation saved
777						if( 'save_simulation_result' in data && data.save_simulation_result ) {
778							let save_simulation_result = data.save_simulation_result;
779
780							$('#pid').val( save_simulation_result.id );
781							$('#gid').val( save_simulation_result.guid );
782							
783							$('#form-ai-simulation-ref-text').text( save_simulation_result.id );
784
785
786							// Check if there are charts to create
787							if( 'charts_info' in data.response_endpoint && data.response_endpoint.charts_info ) {
788								// Generate and save simulation charts
789								generate_and_save_simulation_charts( save_simulation_result.id, save_simulation_result.guid, data.response_endpoint.charts_info );
790							}
791						}
792
793
794						// Mostrar debug
795						let show_debug = debug_mode;
796						if( show_debug ) {
797							let debug_html = `<h4 class="mb-4">Debug</h4>`;
798
799							if( 'calculated_event_prices' in data.response_endpoint && data.response_endpoint.calculated_event_prices ) {
800								let calculated_event_prices = data.response_endpoint.calculated_event_prices;
801
802								debug_html += `<h6 class="mb-4 p-0">Total evento: ${ calculated_event_prices.event_total }</h4>`;
803
804								// Event spaces
805								if ('event_spaces_prices' in calculated_event_prices && calculated_event_prices.event_spaces_prices.daily_setup_prices) {
806									let event_spaces_prices = calculated_event_prices.event_spaces_prices;
807									let event_space_services = ``;
808
809									// Percorre cada dia no array de preços
810									event_spaces_prices.daily_setup_prices.forEach((day_price, index) => {
811										event_space_services += `<li><b>Dia ${index + 1}</b></li>`;
812
813										// Adiciona uma lista dentro da lista para organizar
814										event_space_services += `<ul>`;
815
816										// Main event space para este dia
817										if (day_price.main_event_space) {
818											event_space_services += `<li>Sala principal - unit: ${day_price.main_event_space.price_unit} | total: ${day_price.main_event_space.price_total}</li>`;
819										}
820
821										// Additional rooms para este dia
822										if (day_price.additional_rooms && day_price.additional_rooms.length > 0) {
823											day_price.additional_rooms.forEach((room) => {
824												event_space_services += `<li>Sala adicional - unit: ${room.price_unit} | total: ${room.price_total}</li>`;
825											});
826										}
827
828										event_space_services += `</ul>`;
829									});
830
831									debug_html += `
832                                        <div class="mb-4">
833                                        <h5 class="m-0">Espaços de Evento</h5>
834                                        <p>Total: ${event_spaces_prices.event_spaces_total}</p>
835                                        <ul>${event_space_services}</ul>
836                                        </div>
837                                        `;
838								}
839
840								// Catering
841								if( 'catering_prices' in calculated_event_prices ) {
842									let catering_prices = calculated_event_prices.catering_prices;
843
844									let catering_services = ``;
845									catering_prices.catering_days.forEach( (catering_day, index) => {
846										let catering_day_services = ``;
847
848										if( 'welcome_coffee' in catering_day ) {
849											catering_day_services += `<li>Welcome coffee - unit: ${ catering_day.welcome_coffee.price_unit } | total: ${ catering_day.welcome_coffee.price_total }</li>`;
850										}
851
852										if( 'coffee_breaks' in catering_day ) {
853											catering_day_services += `<li>Coffee break - unit: ${ catering_day.coffee_breaks.price_unit } | total: ${ catering_day.coffee_breaks.price_total }</li>`;
854										}
855
856										if( 'welcome_drink' in catering_day ) {
857											catering_day_services += `<li>Welcome drink - unit: ${ catering_day.welcome_drink.price_unit } | total: ${ catering_day.welcome_drink.price_total }</li>`;
858										}
859
860										if( 'lunch' in catering_day ) {
861											catering_day_services += `<li>Almoço - unit: ${ catering_day.lunch.price_unit } | total: ${ catering_day.lunch.price_total }</li>`;
862										}
863
864										if( 'canapes' in catering_day ) {
865											catering_day_services += `<li>Canapés - unit: ${ catering_day.canapes.price_unit } | total: ${ catering_day.canapes.price_total }</li>`;
866										}
867
868										if( 'dinner' in catering_day ) {
869											catering_day_services += `<li>Jantar - unit: ${ catering_day.dinner.price_unit } | total: ${ catering_day.dinner.price_total }</li>`;
870										}
871
872										if( 'open_bar' in catering_day ) {
873											catering_day_services += `<li>Bar aberto - unit: ${ catering_day.open_bar.price_unit } | total: ${ catering_day.open_bar.price_total }</li>`;
874										}
875
876										if( 'supper' in catering_day ) {
877											catering_day_services += `<li>Ceia - unit: ${ catering_day.supper.price_unit } | total: ${ catering_day.supper.price_total }</li>`;
878										}
879
880
881										if( catering_day_services.length > 0 ) {
882											catering_services += `
883<p>Dia ${ (index + 1) }</p>
884<ul>${ catering_day_services }</ul>
885`;
886										}
887									});
888
889
890									debug_html += `
891<div class="mb-4">
892<h5 class="m-0">Catering</h5>
893<p>Total: ${ catering_prices.catering_total }</p>
894
895<div>${ catering_services }</div>
896</div>
897`;
898								}
899
900								// Accommodation
901								if( 'accommodation_prices' in calculated_event_prices ) {
902									let accommodation_prices = calculated_event_prices.accommodation_prices;
903									let number_of_nights = accommodation_prices.number_of_nights;
904
905									let accommodation_services = ``;
906									if( 'single_rooms' in accommodation_prices ) {
907										accommodation_services += `<li>Quartos single - unit: ${ accommodation_prices.single_rooms.price_unit } | total: ${ accommodation_prices.single_rooms.price_total }</li>`;
908									}
909
910									if( 'twin_rooms' in accommodation_prices ) {
911										accommodation_services += `<li>Quartos twin - unit: ${ accommodation_prices.twin_rooms.price_unit } | total: ${ accommodation_prices.twin_rooms.price_total }</li>`;
912									}
913
914
915									debug_html += `
916<div class="mb-4">
917<h5 class="m-0">Alojamento</h5>
918<p>Total: ${ accommodation_prices.accommodation_total }</p>
919<p>Noites: ${ number_of_nights }</p>
920
921<ul>${ accommodation_services }</ul>
922</div>
923`;
924								}
925
926								// Audiovisuals
927								if( 'audiovisuals_prices' in calculated_event_prices ) {
928									let audiovisuals_prices = calculated_event_prices.audiovisuals_prices;
929
930									let audiovisual_service = `<li>Serviço - unit: ${ audiovisuals_prices.audiovisual_service.price_unit } | total: ${ audiovisuals_prices.audiovisual_service.price_total }</li>`;
931
932
933									debug_html += `
934<div class="mb-4">
935<h5 class="m-0">Audiovisuais</h5>
936<p>Total: ${ audiovisuals_prices.audiovisuals_total }</p>
937
938<ul>${ audiovisual_service }</ul>
939</div>
940`;
941								}
942							}
943							else {
944								debug_html += `<p>Sem informação</p>`;
945							}
946
947							$('#debug-result-container').html( debug_html );
948						}
949
950
951					}
952
953
954
955					// --- Mostrar o resultado ---
956				// 	$('#form-ai-result .para-testes').html(JSON.stringify(data, null, 2));
957
958					let eventTotal = 0;
959					if( ( 'response_endpoint' in data && data.response_endpoint ) && ( 'calculated_event_prices' in data.response_endpoint && data.response_endpoint.calculated_event_prices ) && ( 'event_total' in data.response_endpoint.calculated_event_prices && data.response_endpoint.calculated_event_prices.event_total ) ) {
960						eventTotal = data.response_endpoint.calculated_event_prices.event_total;
961					}
962					
963					
964					
965					// Check URL params
966					if( total_visible ) {
967					    eventTotal = parseFloat( eventTotal ).toLocaleString( 'pt-PT', { minimumFractionDigits: 2, maximumFractionDigits: 2 } );
968					    $('#form-ai-result .price-value').html( eventTotal );
969					}
970					else {
971					    $('#form-ai-result .price').html('');
972					    $('#form-ai-result .price').addClass('blured-total-value');
973					}
974					
975
976					if( ! debug_mode ) {
977						$('#form-ai-content').css("display", "none");
978						$('#form-ai-content').hide();
979					}
980
981					$('#form-ai-result').css("display", "block");
982					$('#form-ai-result').show();
983					
984					// Scroll to show the simulation values
985					let scroll_to_form_ai_offset = $('#masthead > nav').outerHeight() + 50;
986					if( $('#wpadminbar').length > 0 ) {
987					    scroll_to_form_ai_offset += $('#wpadminbar').outerHeight();
988					}
989					
990					$(document).scrollTop( $("#form-ai-result").offset().top - scroll_to_form_ai_offset );
991				}
992				// Mostrar erros do registo
993				else {					
994					// --- 2. Mostrar novos erros ---
995					if (data.errors && data.errors.input_errors) {
996						const errors = data.errors.input_errors;
997
998						for (const fieldKey in errors) {
999							const message = errors[fieldKey];
1000
1001							if (fieldKey === 'catering') {
1002								$('#label-inclui-catering').addClass('has-error');
1003								$('#form-ai-feedback-container').html(`<div class="alert alert-danger" style="color: #dc3545; border: 1px solid #dc3545; padding: 10px; margin-bottom: 15px;">${message}</div>`);
1004								continue; 
1005							}
1006
1007							const elementId = fieldKey.replace('_day_', '_');
1008							const field = $('#' + elementId);
1009
1010							if (field.length > 0) {
1011								field.addClass('is-invalid');
1012
1013								if (field.hasClass('select2-hidden-accessible')) {
1014									field.next('.select2-container').find('.select2-selection').addClass('is-invalid');
1015								}
1016
1017								field.next('.invalid-feedback').text(message).addClass('d-block');
1018							}
1019						}
1020
1021						const firstErrorKey = Object.keys(errors)[0];
1022						if (firstErrorKey) {
1023							const firstElementId = firstErrorKey.replace('_day_', '_');
1024						}
1025					} else {
1026						$('#form-ai-feedback-container').html('<div class="alert alert-danger">Ocorreu um erro. Por favor, tente novamente.</div>');
1027					}
1028				}
1029			},		
1030			error: function( errorThrown ) {
1031				console.log( "Error: ", errorThrown );
1032				$('#form-ai-validate .error_msg').html("Erro ao processar pedido. Tente novamente");
1033			},
1034			complete: function() {
1035				// Reativar o botão, mostrar o texto e esconder o loader
1036				if ($('#form-ai-content').is(':visible')) {
1037					button_text.show();
1038					button_loader.hide();
1039					submit_button.attr('disabled', false);
1040				}
1041			}
1042		});
1043	});
1044
1045	// ########## Validação de E-mail para o Formulário AI ##########
1046	// 
1047	const emailFieldAI = $('#form-ai-result .submit-form input[name="your-email"]');
1048
1049	// Executa apenas se o campo de email existir na página
1050	if (emailFieldAI.length) {
1051		emailFieldAI.after('<span class="wpcf7-not-valid-tip email-provider-error" style="display:none;"></span>');
1052		const errorContainer = emailFieldAI.next('.email-provider-error');
1053
1054		// Função para validar o email
1055		function validateEmailProvider() {
1056			const blocked_providers = $('#form-ai-blocked-email-providers').val().split(',');
1057			const emailValue = emailFieldAI.val();
1058
1059			if (emailValue) {
1060				let email_parts = emailValue.split('@');
1061				if (email_parts.length > 1) {
1062					let domain = email_parts[1].toLowerCase();
1063
1064					if (blocked_providers.includes(domain)) {
1065						errorContainer.text( $('#form-ai-blocked-email-providers-str').val() );
1066						errorContainer.show();
1067						return false; 
1068					}
1069				}
1070			}
1071
1072			errorContainer.hide();
1073			return true; 
1074		}
1075
1076		emailFieldAI.on('blur', validateEmailProvider);
1077	}
1078
1079	// Substituir resultado por mensagem de sucesso 
1080	document.addEventListener( 'wpcf7mailsent', function( event ) {
1081		var targetFormIds = [ 12507, 13537, 13538 ];
1082
1083		if ( targetFormIds.includes( event.detail.contactFormId ) ) {
1084			setTimeout(function() {
1085				var resultContainer = jQuery('#form-ai-result');
1086				if (resultContainer.length) {
1087					jQuery('#form-ai-content').hide();
1088					
1089					
1090					let msg_title_str = $('#form-ai-client-details-success-title-str').val();
1091					let msg_text1_str = $('#form-ai-client-details-success-text1-str').val();
1092					let msg_text2_str = $('#form-ai-client-details-success-text2-str').val();
1093					
1094					let msg_btn_new_simulation_str = $('#form-ai-client-details-success-btn-new-simulation-str').val();
1095					let msg_btn_back_str = $('#form-ai-client-details-success-btn-back-str').val();
1096					
1097					var successMessageHtml = `
1098						<div class="form-ai-success-message">
1099							<h2>${msg_title_str}</h2>
1100							<p>${msg_text1_str}</p>
1101							<p>${msg_text2_str}</p>
1102
1103							<div class="success-buttons-container">
1104								<a href="${window.location.pathname}" class="btn-success-action primary">${msg_btn_new_simulation_str}</a>
1105								<a href="https://venuesin.pt/" class="btn-success-action secondary">${msg_btn_back_str}</a>
1106							</div>
1107						</div>
1108						`;
1109					resultContainer.html(successMessageHtml).show();
1110					jQuery('html, body').animate({
1111						scrollTop: resultContainer.offset().top - 60
1112					}, 500);
1113				}
1114			}, 10);
1115		}
1116	}, false );
1117
1118	var chart_api_endpoint_url = 'https://quickchart.io/chart';
1119	
1120	var chart_strings;
1121	if( $('#chart-strings').length > 0 ) {
1122	    chart_strings = JSON.parse( $('#chart-strings').val() );
1123	}
1124	
1125	var charts_colors = [
1126	    '#9DC5FB',
1127        '#523ABD',
1128        '#7A99EB',
1129        '#5B72DE',
1130        '#575BD0',
1131        '#3E31B2',
1132        '#121D9A',
1133        '#0B1366'
1134	];
1135	
1136	async function venuesin_generate_and_save_events_price_benchmark_chart( pdata, gdata, chart_data, test=false ) {
1137	    let chart_type = 'events_price_benchmark';
1138	    
1139	    let event_price_per_person = chart_data.event_price_per_person;
1140	    let event_min_price_per_person = chart_data.event_min_price_per_person;
1141	    let event_max_price_per_person = chart_data.event_max_price_per_person;
1142	    
1143	    let event_price_per_person_label = event_price_per_person.toLocaleString( 'pt-PT', { minimumFractionDigits: 2, maximumFractionDigits: 2 } ) + '€';
1144	    let event_min_price_per_person_label = event_min_price_per_person.toLocaleString( 'pt-PT', { minimumFractionDigits: 2, maximumFractionDigits: 2 } ) + '€';
1145	    let event_max_price_per_person_label = event_max_price_per_person.toLocaleString( 'pt-PT', { minimumFractionDigits: 2, maximumFractionDigits: 2 } ) + '€';
1146	    
1147	    let min_max_dist = event_max_price_per_person - event_min_price_per_person;
1148	    let event_dist = event_price_per_person - event_min_price_per_person;
1149	    let event_price_per_person_label_pos_x = Math.round( (event_dist * 100) / min_max_dist );
1150	    
1151	    let chart_str_price_min = 'Preço Mínimo';
1152	    let chart_str_price_max = 'Preço Máximo';
1153	    if( chart_strings ) {
1154	        chart_str_price_min = 'price_min' in chart_strings && chart_strings.price_min ? chart_strings.price_min : chart_str_price_min;
1155	        chart_str_price_max = 'price_max' in chart_strings && chart_strings.price_max ? chart_strings.price_max : chart_str_price_max;
1156	    }
1157	    
1158	    
1159	    let chart_config = {
1160			type: 'bar',
1161			data: {
1162				labels: [''],
1163				datasets: [{
1164				    data: [ 100 ],
1165				    backgroundColor: '#9DC5FB',
1166				    borderColor: '#000000'
1167				}]
1168			},
1169			options: {
1170			    indexAxis: 'y',
1171				scales: {
1172					x: {
1173					    display: false
1174					},
1175					y: {
1176					    display: false
1177					}
1178				},
1179				plugins: {
1180				    legend: {
1181				        display: false
1182				    },
1183				    annotation: {
1184				        clip: false,
1185                        common: {
1186                            drawTime: 'afterDraw'
1187                        },
1188				        annotations: {
1189    				        low: {
1190    				            type: 'label',
1191                                xValue: 0,
1192                                yAdjust: 35,
1193                                content: [ event_min_price_per_person_label, chart_str_price_min ],
1194                                font: {
1195                                    family: '"Poppins", Sans-serif',
1196                                    size: 14,
1197                                    weight: '500'
1198                                },
1199                                color: '#A0A3BD'
1200    				        },
1201    				        high: {
1202    				            type: 'label',
1203                                xValue: 100,
1204                                yAdjust: 35,
1205                                content: [ event_max_price_per_person_label, chart_str_price_max ],
1206                                font: {
1207                                    family: '"Poppins", Sans-serif',
1208                                    size: 14,
1209                                    weight: '500',
1210                                },
1211                                color: '#A0A3BD'
1212    				        },
1213    				        arrow: {
1214                                type: 'point',
1215                                pointStyle: 'circle',
1216                                xValue: event_price_per_person_label_pos_x,
1217                                yAdjust: 0,
1218                                backgroundColor: '#523ABD',
1219                                borderColor: '#000000',
1220                                borderWidth: 0,
1221                                radius: 15
1222                            },
1223                            label1: {
1224                                type: 'label',
1225                                content: [ event_price_per_person_label ],
1226                                xValue: event_price_per_person_label_pos_x,
1227                                yAdjust: -30,
1228                                font: {
1229                                    family: '"Poppins", Sans-serif',
1230                                    size: 14,
1231                                    weight: '500'
1232                                },
1233                                color: '#170F49'
1234                            }
1235    				    }
1236				    }
1237				},
1238				layout: {
1239                    padding: {
1240                        top: 30,
1241                        bottom: 40,
1242                        left: 50,
1243                        right: 50
1244                    }
1245                },
1246                borderWidth: 0,
1247                borderRadius: 20,
1248                borderSkipped: false
1249			}
1250		};
1251
1252		// Generate chart URL params and final URL
1253		let url_params = encodeURIComponent( JSON.stringify( chart_config ) );
1254		let chart_url = chart_api_endpoint_url + '?v=4&h=100&c=' + url_params;
1255		
1256		if( test === false ) {
1257		    let chart_base64_img = await venuesin_get_chart_base64_img( chart_url );
1258    		
1259    		if( chart_base64_img ) {
1260    		    venuesin_save_simulation_chart( pdata, gdata, chart_type, chart_data, chart_base64_img );
1261    		}
1262		}
1263		else {
1264		    console.log( 'chart_url', chart_url );
1265		}
1266	}
1267	
1268
1269	async function venuesin_generate_and_save_events_price_evolution_by_service_category_chart( pdata, gdata, chart_data, test=false ) {
1270	    let chart_type = 'events_price_evolution_by_service_category';
1271	    
1272		let labels = [ '2022', '2023', '2024', '2025' ];
1273		
1274		let line_fill = false;
1275		let line_width = 4;
1276		let line_tension = 0;
1277		let point_radius = 6;
1278	    
1279	    let chart_str_audiovisuals = 'Audiovisuais';
1280	    let chart_str_event_spaces = 'Espaços de Evento';
1281	    let chart_str_catering = 'Catering';
1282	    let chart_str_accommodation = 'Alojamento';
1283	    let chart_str_year = 'Ano';
1284	    let chart_str_euros_per_participant = '€ por Participante';
1285	    if( chart_strings ) {
1286	        chart_str_audiovisuals = 'audiovisuals' in chart_strings && chart_strings.audiovisuals ? chart_strings.audiovisuals : chart_str_audiovisuals;
1287	        chart_str_event_spaces = 'event_spaces' in chart_strings && chart_strings.event_spaces ? chart_strings.event_spaces : chart_str_event_spaces;
1288	        chart_str_catering = 'catering' in chart_strings && chart_strings.catering ? chart_strings.catering : chart_str_catering;
1289	        chart_str_accommodation = 'accommodation' in chart_strings && chart_strings.accommodation ? chart_strings.accommodation : chart_str_accommodation;
1290	        
1291	        chart_str_year = 'year' in chart_strings && chart_strings.year ? chart_strings.year : chart_str_year;
1292	        chart_str_euros_per_participant = 'euros_per_participant' in chart_strings && chart_strings.euros_per_participant ? chart_strings.euros_per_participant : chart_str_euros_per_participant;
1293	    }
1294	    
1295
1296		let values = [];
1297		if( 'audiovisuals' in chart_data && chart_data.audiovisuals ) {
1298			values.push({
1299				label: chart_str_audiovisuals,
1300				data: chart_data.audiovisuals,
1301                borderColor: '#9DC5FB',
1302                backgroundColor: '#9DC5FB',
1303				fill: line_fill,
1304				borderWidth: line_width,
1305				lineTension: line_tension,
1306				pointRadius: point_radius
1307			});
1308		}
1309		
1310		if( 'event_spaces' in chart_data && chart_data.event_spaces ) {
1311			values.push({
1312				label: chart_str_event_spaces,
1313				data: chart_data.event_spaces,
1314                borderColor: '#523ABD',
1315                backgroundColor: '#523ABD',
1316				fill: line_fill,
1317				borderWidth: line_width,
1318				lineTension: line_tension,
1319				pointRadius: point_radius
1320			});
1321		}
1322
1323		if( 'catering' in chart_data && chart_data.catering ) {
1324			values.push({
1325				label: chart_str_catering,
1326				data: chart_data.catering,
1327                borderColor: '#5B72DE',
1328                backgroundColor: '#5B72DE',
1329				fill: line_fill,
1330				borderWidth: line_width,
1331				lineTension: line_tension,
1332				pointRadius: point_radius
1333			});
1334		}
1335
1336		if( 'accommodation' in chart_data && chart_data.accommodation ) {
1337			values.push({
1338				label: chart_str_accommodation,
1339				data: chart_data.accommodation,
1340                borderColor: '#121D9A',
1341                backgroundColor: '#121D9A',
1342				fill: line_fill,
1343				borderWidth: line_width,
1344				lineTension: line_tension,
1345				pointRadius: point_radius
1346			});
1347		}
1348
1349
1350		let chart_config = {
1351			type: 'line',
1352			data: {
1353				labels: labels,
1354				datasets: values
1355			},
1356			options: {
1357				scales: {
1358					x: {
1359						display: true,
1360						title: {
1361							display: true,
1362							text: chart_str_year,
1363							font: {
1364							    family: '"Poppins", Sans-serif',
1365                                size: 14,
1366                                weight: 500
1367                            },
1368                            color: '#A0A3BD'
1369						},
1370						ticks: {
1371						    font: {
1372						        family: '"Poppins", Sans-serif',
1373                                size: 14,
1374                                weight: 500
1375                            },
1376                            color: '#A0A3BD'
1377                        },
1378						grid: {
1379						    display: false,
1380                        },
1381                        border: {
1382                            display: false
1383                        }
1384					},
1385					y: {
1386						display: true,
1387						beginAtZero: false,
1388						suggestedMin: 70,
1389						title: {
1390							display: true,
1391							text: chart_str_euros_per_participant,
1392							font: {
1393							    family: '"Poppins", Sans-serif',
1394                                size: 14,
1395                                weight: 500
1396                            },
1397                            color: '#A0A3BD'
1398						},
1399						ticks: {
1400						    maxTicksLimit: 10,
1401						    font: {
1402						        family: '"Poppins", Sans-serif',
1403                                size: 14,
1404                                weight: 500
1405                            },
1406                            color: '#A0A3BD'
1407                        },
1408						grid: {
1409						    display: false,
1410                        },
1411                        border: {
1412                            display: false
1413                        }
1414					}
1415				},
1416				plugins: {
1417				    legend: {
1418    					display: true,
1419    					position: 'bottom',
1420    					labels: {
1421    					    usePointStyle: true,
1422    					    pointStyle: 'rectRounded',
1423    					    padding: 20,
1424    					    boxWidth: 6,
1425    					    boxHeight: 6,
1426    					    font: {
1427            			        family: '"Poppins", Sans-serif',
1428                                size: 14,
1429                                weight: 500
1430                            },
1431                            color: '#A0A3BD'
1432                        }
1433    				}
1434				}
1435			}
1436		};
1437
1438		// Generate chart URL params and final URL
1439		let url_params = encodeURIComponent( JSON.stringify( chart_config ) );
1440		let chart_url = chart_api_endpoint_url + '?v=4&c=' + url_params;
1441
1442		if( test === false ) {
1443		    let chart_base64_img = await venuesin_get_chart_base64_img( chart_url );
1444    		
1445    		if( chart_base64_img ) {
1446    		    venuesin_save_simulation_chart( pdata, gdata, chart_type, chart_data, chart_base64_img );
1447    		}
1448		}
1449		else {
1450		    console.log( 'chart_url', chart_url );
1451		}
1452	}
1453
1454
1455    function stringifyWithFunctions(obj) {
1456      const json = JSON.stringify(obj, (key, value) => {
1457        if (typeof value === 'function') return value.toString();
1458        return value;
1459      }, 0);
1460    
1461      // Remove as aspas que envolviam as funções (ficaram como strings)
1462      return json.replace(/"function\s*\([^)]*\)\s*\{[\s\S]*?\}"/g, match =>
1463        match.slice(1, -1) // tira as aspas do início e do fim
1464      );
1465    }
1466
1467
1468	async function venuesin_generate_and_save_events_by_venue_typology_chart( pdata, gdata, chart_data, test=false ) {
1469	    let chart_type = 'events_by_venue_typology';
1470	    
1471		let labels = Object.keys( chart_data );
1472		let values = Object.values( chart_data );
1473		
1474		// Translate labels
1475		if( chart_strings ) {
1476		    let translated_labels = [];
1477		    
1478		    labels.forEach( ( label, index ) => {
1479		        if( label in chart_strings ) {
1480		            labels[index] = chart_strings[label];
1481		        }
1482		    });
1483		}
1484		
1485		
1486		let chart_config = {
1487			type: 'doughnut',
1488			data: {
1489				labels: labels,
1490				datasets: [
1491					{
1492						label: 'Dataset 1',
1493						data: values,
1494						backgroundColor: charts_colors
1495					}
1496				]
1497			},
1498			options: {
1499			    cutout: '80%',
1500			    elements: {
1501			        arc: {
1502			            borderWidth: 0
1503                    }
1504                },
1505				plugins: {
1506				    legend: {
1507    					display: true,
1508    					position: 'bottom',
1509    					labels: {
1510    					    usePointStyle: true,
1511    					    pointStyle: 'rectRounded',
1512    					    padding: 20,
1513            			    boxWidth: 8,
1514            			    boxHeight: 8,
1515            			    font: {
1516            			        family: '"Poppins", Sans-serif',
1517                                size: 14,
1518                                weight: 500
1519                            },
1520                            color: '#A0A3BD'
1521                        }
1522    				},
1523				    datalabels: {
1524				        font: {
1525				            family: '"Poppins", Sans-serif',
1526				            size: 14,
1527				            weight: 'bold'
1528				        },
1529				        color: '#ffffff',
1530				        formatter: function(value) { return value + '%'; }
1531				    }
1532                }
1533			}
1534		};
1535
1536		// Generate chart URL params and final URL
1537		let url_params = encodeURIComponent( stringifyWithFunctions( chart_config ) );
1538		let chart_url = chart_api_endpoint_url + '?v=4&c=' + url_params;
1539
1540		if( test === false ) {
1541		    let chart_base64_img = await venuesin_get_chart_base64_img( chart_url );
1542    		
1543    		if( chart_base64_img ) {
1544    		    venuesin_save_simulation_chart( pdata, gdata, chart_type, chart_data, chart_base64_img );
1545    		}
1546		}
1547		else {
1548		    console.log( 'chart_url', chart_url );
1549		}
1550	}
1551
1552
1553	async function venuesin_generate_and_save_events_by_venue_region_chart_bar( pdata, gdata, chart_data, test=false ) {
1554	    let chart_type = 'events_by_venue_region';
1555	    
1556		let labels = Object.keys( chart_data );
1557		let values = Object.values( chart_data );
1558		
1559		// Translate labels
1560		if( chart_strings ) {
1561		    let translated_labels = [];
1562		    
1563		    labels.forEach( ( label, index ) => {
1564		        if( label in chart_strings ) {
1565		            labels[index] = chart_strings[label];
1566		        }
1567		    });
1568		}
1569		
1570		
1571		let chart_str_event_percentage = '% de Eventos';
1572	    if( chart_strings ) {
1573	        chart_str_event_percentage = 'event_percentage' in chart_strings && chart_strings.event_percentage ? chart_strings.event_percentage : chart_str_event_percentage;
1574	    }
1575		
1576
1577		let chart_config = {
1578			type: 'bar',
1579			data: {
1580				labels: labels,
1581				datasets: [
1582					{
1583						label: 'Dataset 1',
1584						data: values,
1585						backgroundColor: '#501EA6'
1586					}
1587				]
1588			},
1589			options: {
1590				scales: {
1591					x: {
1592					    display: true,
1593                        border: {
1594                            display: false
1595                        },
1596					    grid: {
1597					        display: false,
1598                        }
1599					},
1600                    y: {
1601                        display: true,
1602                        border: {
1603                            display: false
1604                        },
1605					    grid: {
1606					        display: false,
1607                        },
1608                        title: {
1609                            display: true,
1610                            text: chart_str_event_percentage
1611                        },
1612                        suggestedMin: 0,
1613                        ticks: {
1614                            maxTicksLimit: 10
1615                        }
1616                    }
1617				},
1618				plugins: {
1619				    legend: {
1620				        display: false
1621				    }
1622                },
1623                font: {
1624                    family: '"Poppins", Sans-serif'
1625                },
1626                borderRadius: 14
1627			}
1628		};
1629
1630		// Generate chart URL params and final URL
1631		let url_params = encodeURIComponent( JSON.stringify( chart_config ) );
1632		let chart_url = chart_api_endpoint_url + '?v=4&c=' + url_params;
1633
1634		if( test === false ) {
1635		    let chart_base64_img = await venuesin_get_chart_base64_img( chart_url );
1636    		
1637    		if( chart_base64_img ) {
1638    		    venuesin_save_simulation_chart( pdata, gdata, chart_type, chart_data, chart_base64_img );
1639    		}
1640		}
1641		else {
1642		    console.log( 'chart_url', chart_url );
1643		}
1644	}
1645	
1646	
1647	async function venuesin_generate_and_save_events_by_venue_region_chart_polar( pdata, gdata, chart_data, test=false ) {
1648	    let chart_type = 'events_by_venue_region';
1649	    
1650		let labels = Object.keys( chart_data );
1651		let values = Object.values( chart_data );
1652		
1653		// Translate labels
1654		if( chart_strings ) {
1655		    let translated_labels = [];
1656		    
1657		    labels.forEach( ( label, index ) => {
1658		        if( label in chart_strings ) {
1659		            labels[index] = chart_strings[label];
1660		        }
1661		    });
1662		}
1663		
1664		
1665		// Opacity - see https://stackoverflow.com/questions/69683716/transparent-css-hex-value
1666		let hex_opacity = '80';
1667		
1668		let background_colors = [];
1669		charts_colors.forEach( (color) => {
1670		    background_colors.push( color + hex_opacity );
1671		});
1672		
1673		
1674		let chart_config = {
1675			type: 'polarArea',
1676			data: {
1677				labels: labels,
1678				datasets: [
1679					{
1680						label: 'Dataset 1',
1681						data: values,
1682						backgroundColor: background_colors
1683					}
1684				]
1685			},
1686			options: {
1687				plugins: {
1688				    legend: {
1689				        display: true,
1690				        position: 'bottom',
1691				        labels: {
1692				            usePointStyle: true,
1693				            pointStyle: 'rectRounded',
1694                            padding: 20,
1695                            boxWidth: 8,
1696                            boxHeight: 8,
1697                            font: {
1698                            family: '"Poppins", Sans-serif',
1699                                size: 14,
1700                                weight: 500
1701                            },
1702                            color: '#A0A3BD'
1703                        }
1704				    },
1705				    datalabels: {
1706                        font: {
1707                            family: '"Poppins", Sans-serif',
1708                            size: 14,
1709                            weight: 'bold',
1710                        },
1711                        color: '#ffffff',
1712                        borderWidth: 0,
1713				        formatter: function(value) { return value + '%'; }
1714                    }
1715                },
1716                borderWidth: 2,
1717                borderRadius: 0
1718			}
1719		};
1720
1721		// Generate chart URL params and final URL
1722		let url_params = encodeURIComponent( stringifyWithFunctions( chart_config ) );
1723		let chart_url = chart_api_endpoint_url + '?v=4&c=' + url_params;
1724
1725		if( test === false ) {
1726		    let chart_base64_img = await venuesin_get_chart_base64_img( chart_url );
1727    		
1728    		if( chart_base64_img ) {
1729    		    venuesin_save_simulation_chart( pdata, gdata, chart_type, chart_data, chart_base64_img );
1730    		}
1731		}
1732		else {
1733		    console.log( 'chart_url', chart_url );
1734		}
1735	}
1736
1737
1738	function generate_and_save_simulation_charts( pdata, gdata, charts_info, test=false ) {
1739		/* Create charts */
1740		
1741		// Events price benchmark
1742		if( 'events_price_benchmark' in charts_info && charts_info.events_price_benchmark ) {
1743		    venuesin_generate_and_save_events_price_benchmark_chart( pdata, gdata, charts_info.events_price_benchmark, test );
1744		}
1745
1746		// Events price evolution by service category
1747		if( 'events_price_evolution_by_category' in charts_info && charts_info.events_price_evolution_by_category ) {
1748			venuesin_generate_and_save_events_price_evolution_by_service_category_chart( pdata, gdata, charts_info.events_price_evolution_by_category, test );
1749		}
1750
1751		//  Events by Venue Typology
1752		if( 'events_by_venue_typology' in charts_info && charts_info.events_by_venue_typology ) {
1753			venuesin_generate_and_save_events_by_venue_typology_chart( pdata, gdata, charts_info.events_by_venue_typology, test );
1754		}
1755
1756		// Events by Venue Region
1757		if( 'events_by_venue_region' in charts_info && charts_info.events_by_venue_region ) {
1758// 			venuesin_generate_and_save_events_by_venue_region_chart_bar( pdata, gdata, charts_info.events_by_venue_region, test );
1759			venuesin_generate_and_save_events_by_venue_region_chart_polar( pdata, gdata, charts_info.events_by_venue_region, test );
1760		}
1761	}
1762
1763
1764	async function venuesin_get_chart_base64_img( chart_url ) {
1765		let chart_request_result = await fetch( chart_url );
1766
1767		if( chart_request_result.ok ) {
1768			let data_blob = await chart_request_result.blob();
1769
1770			// Convert blob to base64
1771			if( data_blob.size > 0 ) {
1772				return new Promise( (resolve, reject) => {
1773					let file_reader = new FileReader();
1774					file_reader.onloadend = function() {
1775						resolve( file_reader.result );
1776					}
1777					file_reader.readAsDataURL( data_blob );
1778				});
1779			}
1780		}
1781
1782		return false;
1783	}
1784	
1785	
1786	function venuesin_save_simulation_chart( pdata, gdata, chart_type, chart_data, chart_base64_img ) {
1787	    let form_data = new FormData();
1788		form_data.append( 'action', 'update_v_ai_simulation_chart' );
1789		form_data.append( 'nonce', ajax_var.nonce );
1790		form_data.append( 'pdata', pdata );
1791		form_data.append( 'gdata', gdata );
1792		form_data.append( 'chart_type', chart_type );
1793		form_data.append( 'chart_data', JSON.stringify(chart_data) );
1794		form_data.append( 'chart_base64_img', chart_base64_img );
1795		
1796		$.ajax({
1797			type: 'POST',
1798			dataType: 'json',
1799			contentType: false,
1800			processData: false,
1801			url: ajax_var.url,
1802			data: form_data,
1803			success: function( data ) {
1804			    console.log( 'venuesin_save_simulation_chart_AJAX', data );
1805			},
1806			error: function( errorThrown ) {
1807				console.log( "Error: ", errorThrown );
1808			}
1809		});
1810	}
1811
1812
1813	$('#test-ai-charts').on( 'click', function() {
1814	    let test = true;
1815	    
1816	    let pdata = '12972';
1817	    let gdata = 'A92B3098-AA97-4F3B-A181-5E2E3D1B4794';
1818	    
1819	    let test_events_price_benchmark_chart = true;
1820		let test_events_price_evolution_by_category_chart = false;
1821		let test_events_by_venue_typology_chart = false;
1822		let test_events_by_venue_region_chart = false;
1823
1824		let charts_info = {};
1825		
1826		if( test_events_price_benchmark_chart ) {
1827		  //  charts_info.events_price_benchmark = {
1828		  //      'event_price_per_person': 347.6,
1829		  //      'event_min_price_per_person': 275.4,
1830		  //      'event_max_price_per_person': 495.8
1831		  //  };
1832		    
1833		    charts_info.events_price_benchmark = {
1834		        'event_price_per_person': 489.3,
1835		        'event_min_price_per_person': 165,
1836		        'event_max_price_per_person': 552.91
1837		    };
1838		}
1839
1840		if( test_events_price_evolution_by_category_chart ) {
1841			charts_info.events_price_evolution_by_category = {
1842				'event_spaces': [ 102, 112, 122, 133 ],
1843				'catering': [ 120, 130, 137, 143 ],
1844				'accommodation': [ 145, 159, 163, 171 ],
1845				'audiovisuals': [ 80, 97, 100, 103 ]
1846			};
1847		}
1848
1849		if( test_events_by_venue_typology_chart ) {
1850			charts_info.events_by_venue_typology = {
1851				'Hotel': 98,
1852				'Congress Centre': 2
1853			};
1854		}
1855
1856		if( test_events_by_venue_region_chart ) {
1857			charts_info.events_by_venue_region = {
1858				'Centre': 48,
1859				'Greater Lisbon': 42,
1860				'West': 10
1861			};
1862		}
1863
1864
1865		generate_and_save_simulation_charts( pdata, gdata, charts_info, test );
1866	});
1867
1868
1869
1870	// ##########  Chefs validation  ##########
1871
1872	$('#form-chefs-validate').on('submit', function(e) {
1873		e.preventDefault();
1874
1875		// Obter dados do form
1876		let form_data = new FormData( $(this)[0] );
1877		form_data.append( 'nonce', ajax_var.nonce );
1878
1879		let submit_button = $('#form-chefs-validate input[type="submit"]');
1880		$(submit_button).attr('disabled', true);
1881
1882		$('#form-chefs-validate .error_msg').html("");
1883
1884
1885		$.ajax({
1886			type: 'POST',
1887			dataType: 'json',
1888			contentType: false,
1889			processData: false,
1890			url: ajax_var.url,
1891			data: form_data,
1892			success: function( data ) {
1893
1894				// Caso o registo seja bem sucedido
1895				if( data.success === 'true' ) {					
1896					$('#chefs-form-content').html(data.content);	
1897					$('.chefs-grow-bg').hide();
1898					wpcf7.init($('#chefs-form-content form')[0]);
1899
1900				}
1901				// Mostrar erros do registo
1902				else {					
1903					if ("message" in data) {
1904						$('#form-chefs-validate .error_msg').html(data.message);						
1905					}					
1906				}
1907
1908				$(submit_button).attr('disabled', false);
1909			},
1910			error: function( errorThrown ) {
1911
1912				$(submit_button).attr('disabled', false);
1913
1914				console.log( "Error: ", errorThrown );
1915				$('#form-chefs-validate .error_msg').html("Erro ao processar pedido. Tente novamente");
1916
1917			}
1918		});
1919	});
1920
1921
1922	// ##########  Premio Exclusivo  ##########
1923
1924	function get_success_form_alert_html( message ) {
1925		let alert_html = '<div class="alert alert-success">';
1926		alert_html 	  += 	feather.icons['check'].toSvg({ class: 'mr-2' }) + '<span>' + message + '</span>';
1927		alert_html 	  += '</div>';
1928
1929		return alert_html;
1930	}
1931
1932
1933	function get_error_form_alert_html( message ) {
1934		let alert_html = '<div class="alert alert-danger">';
1935		alert_html 	  += 	feather.icons['alert-circle'].toSvg({ class: 'mr-2' }) + '<span>' + message + '</span>';
1936		alert_html 	  += '</div>';
1937
1938		return alert_html;
1939	}
1940
1941
1942	// Capture the wheel spin animation end
1943	$('.ruleta').on("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function( e ) {
1944		// Check if it's the spinning animation
1945		if( 'originalEvent' in e && 'animationName' in e.originalEvent && e.originalEvent.animationName === 'animacion' ) {
1946			// 			$('#roleta-container').fadeTo( 1000, 0, function() {
1947			// 				$('#oferta').fadeIn(1000);
1948			// 			});
1949
1950			$('#premio-exclusivo-title').fadeOut(1000);
1951			$('#roleta-container').fadeOut( 1000, function() {
1952				$('#oferta').fadeIn(1000);
1953			});
1954		}
1955	});
1956
1957
1958	// Validar participação no Prémio Exclusivo
1959	if( $('#form-premio-exclusivo').length ) {
1960		var blocked_email_providers = $('#form-premio-exclusivo-blocked-email-providers').val().split(',');
1961		var blocked_email_providers_aux = blocked_email_providers.slice();
1962
1963		var last_blocked_email_provider = '';
1964		if( blocked_email_providers_aux.length > 1 ) {
1965			last_blocked_email_provider = blocked_email_providers_aux.pop();
1966			last_blocked_email_provider = last_blocked_email_provider ? ' e ' + last_blocked_email_provider : last_blocked_email_provider;
1967		}
1968		var blocked_email_providers_string = blocked_email_providers_aux.join(', ') + last_blocked_email_provider;
1969
1970		// Add custom email validation rule
1971		$.validator.addMethod('valid_email', function( value, element ) {
1972			let user_email_provider = value.split('@');
1973			user_email_provider = user_email_provider[1].split('.');
1974			user_email_provider = user_email_provider[0];
1975
1976			if( blocked_email_providers.includes( user_email_provider ) ) {
1977				return false;
1978			}
1979			else {
1980				return true;
1981			}
1982		});
1983
1984
1985		// Validate form submition
1986		$('#form-premio-exclusivo').validate( {
1987			rules: {
1988				'nome': {
1989					required: true
1990				},
1991				'empresa': {
1992					required: true
1993				},
1994				'email': {
1995					required: true,
1996					email: true,
1997					valid_email: true
1998				},
1999				'telefone': {
2000					required: true
2001				},
2002				'aceito-contato-venuesin': {
2003					required: true
2004				},
2005				'aceito-termos-condicoes': {
2006					required: true
2007				}
2008			},
2009			messages: {
2010				'nome': {
2011					required: 'Este campo é obrigatório'
2012				},
2013				'empresa': {
2014					required: 'Este campo é obrigatório'
2015				},
2016				'email': {
2017					required: 'Este campo é obrigatório',
2018					email: 'Por favor introduza um email válido',
2019					valid_email: 'Não são permitidos emails ' + blocked_email_providers_string
2020				},
2021				'telefone': {
2022					required: 'Este campo é obrigatório'
2023				},
2024				'aceito-contato-venuesin': {
2025					required: 'Este campo é obrigatório'
2026				},
2027				'aceito-termos-condicoes': {
2028					required: 'Este campo é obrigatório'
2029				}
2030			},
2031			errorPlacement: function( error, element ) {
2032				// Telephone input with the International Telephone Input JS library
2033				if( element.hasClass('show-phone-country-code') ) {
2034					$(element).parents('.form-group').get(0).append( error.get(0) );
2035				}
2036				// Checkboxes
2037				else if( element.attr('type') === 'checkbox' ) {
2038					$(element).parents('.form-check').get(0).append( error.get(0) );
2039				}
2040				// Other inputs
2041				else {
2042					error.insertAfter( element );
2043				}
2044			},
2045			submitHandler: function( form, event ) {
2046				// Impedir submit default (caso haja um erro, o "return false" não previne o submit)
2047				event.preventDefault();
2048
2049				// 
2050				let container_form = $('#formulario');
2051				let container_roleta = $('#roleta-container');
2052
2053				// Prevent additional submits
2054				let submit_button = $('#form-premio-exclusivo-submit');
2055				$(submit_button).attr('disabled', true);
2056				$(submit_button).addClass('loading');
2057
2058				let validator = this;
2059				let feedback_container = $('#form-premio-exclusivo-feedback-container');
2060
2061				let form_data = new FormData( $(form)[0] );
2062
2063				$.ajax({
2064					type: 'POST',
2065					dataType: 'json',
2066					contentType: false,
2067					processData: false,
2068					url: ajax_var.url,
2069					data: form_data,
2070					success:function( data ) {
2071						console.log( 'form submit: ', data );
2072
2073						// Submition succeeded
2074						if( data.result === 'success' ) {
2075							// feedback_container.html( get_success_form_alert_html( data.success_message.message ) );
2076
2077							// Trigger wheel spin
2078							// rotateRolate();
2079
2080							// Hide form
2081							container_form.fadeOut( 1000, function() {
2082								container_roleta.fadeIn( 1000, function() {
2083									// rotateRolate();
2084								}).css( 'display', 'flex' );
2085							});
2086						}
2087						// Show errors
2088						else {
2089							// General error
2090							if( data.errors.general_error.message ) {
2091								feedback_container.html( get_error_form_alert_html( data.errors.general_error.message ) );
2092							}
2093
2094							// Input errors
2095							if( data.errors.input_errors ) {
2096								validator.showErrors( data.errors.input_errors );
2097							}
2098
2099							// Permit new submitions
2100							$(submit_button).removeClass('loading');
2101							$(submit_button).attr('disabled', false);
2102						}
2103					},
2104					error: function( errorThrown ) {
2105						feedback_container.html( get_error_form_alert_html( 'Ocorreu um erro ao validar a sua participação. Por favor tente novamente' ) );
2106
2107						// Permit new submitions
2108						$(submit_button).removeClass('loading');
2109						$(submit_button).attr('disabled', false);
2110
2111						// console.log( "Error: ", errorThrown );
2112					}
2113				});
2114
2115				// Retornar false uma vez que o submit default foi alterado
2116				return false;
2117			}
2118		} );
2119	}
2120
2121
2122	// 
2123	$('#btn-premio-exclusivo-view-gift').on( 'click', function(e) {
2124		e.preventDefault();
2125
2126		rotateRolate();
2127	});
2128
2129
2130	// Initialize phone country codes
2131	function initialize_phone_country_codes( country_code_phone_inputs ) {
2132		// For each input
2133		country_code_phone_inputs.forEach( (country_code_phone_input) => {
2134			let disabled = $(country_code_phone_input).is(":disabled");
2135			let preferred_countries = ['pt', 'es', 'us', 'uk'];
2136
2137			let intl_tel_input = window.intlTelInput(country_code_phone_input, {
2138				utilsScript: 'https://cdn.jsdelivr.net/npm/[email protected]/build/js/utils.js',
2139				containerClass: 'show-phone-country-code-container',
2140				allowDropdown: disabled === false ? true : false,
2141				countrySearch: false,
2142				initialCountry: 'pt',
2143				preferredCountries: preferred_countries,
2144				showSelectedDialCode: true,
2145				nationalMode: false
2146			});
2147
2148			// Register events
2149			if( disabled === false ) {
2150				$(country_code_phone_input).on('countrychange input', function() {
2151					let full_phone_number = intl_tel_input.getNumber();
2152					$( '#' + $(this).attr('id') + '-full' ).val( full_phone_number );
2153				});
2154			}
2155		});
2156	}
2157
2158	// Initialize phone country codes
2159	if( $('.show-phone-country-code').length > 0 ) {
2160		let country_code_phone_inputs = document.querySelectorAll('.show-phone-country-code');
2161		initialize_phone_country_codes( country_code_phone_inputs );
2162	}
2163
2164
2165	var multiplicador = 7;
2166
2167	/*$('.ruleta').click(function(){
2168		rotateRolate();
2169	})*/
2170
2171
2172	function rotateRolate() {
2173		//$('body').css('background-color', 'var(--bg-cont-b');
2174		$('.ruleta').css('pointer-events', 'none');
2175		$('.colores').removeClass('animacion');
2176		var color = colorAleatorio(1, 360);
2177
2178		$('#animacion').empty().append(
2179			'@keyframes animacion{'+
2180			'0%{transform: rotate(0deg)}'+
2181			'100%{transform: rotate('+color+'deg)}'+
2182			'}'
2183		);
2184
2185
2186
2187		setTimeout(function(){
2188			$('.colores').addClass('animacion')
2189			//$('.colores').css('animation', 'animacion 4s ease-out forwards');
2190		},1)
2191
2192		setTimeout(function(){
2193			if(color > 0 && color <= (90 + (360 * multiplicador))){
2194				$('#nha-body').css('background-color', 'var(--bg-b)')
2195
2196			}else if(color > (90 + (360 * multiplicador)) && color <= (180 + (360 * multiplicador))){
2197				$('#nha-body').css('background-color', 'var(--bg-y)')
2198
2199			}else if(color > (180 + (360 * multiplicador)) && color <= (270 + (360 * multiplicador))){
2200				$('#nha-body').css('background-color', 'var(--bg-g)')
2201
2202			}else if(color > (270 + (360 * multiplicador))){
2203				$('#nha-body').css('background-color', 'var(--bg-r)')
2204			}
2205
2206			$('.ruleta').css('pointer-events', 'initial');
2207		},4000);
2208	}
2209
2210
2211	function colorAleatorio(min, max) {
2212		var colorAleatorio = ((Math.random() * (max - min) + min).toFixed(0))*1;
2213		colorAleatorio = colorAleatorio + (360 * multiplicador);
2214		return colorAleatorio;
2215	}
2216
2217
2218	/* Envelope */
2219
2220	openEnvelope();
2221
2222	function openEnvelope() {
2223		$('#envelope').addClass("open").removeClass("close");
2224	}
2225
2226
2227	// Show Feather Icons
2228	feather.replace();
2229
2230	// 
2231	const swiper = new Swiper('.swiper', {
2232		loop: true,
2233		slidesPerView: 1,
2234		spaceBetween: 10,
2235		pagination: {
2236			el: '.swiper-pagination',
2237		},
2238		navigation: {
2239			nextEl: '.swiper-button-next',
2240			prevEl: '.swiper-button-prev',
2241		}
2242	});
2243
2244})( jQuery );
2245
2246// Anexar Ficheiros
2247document.addEventListener('DOMContentLoaded', function() {
2248    
2249    // Seleção de elementos
2250    var fileInput = document.getElementById('anexar-ficheiro');
2251    var customBtn = document.querySelector('.add-file-btn'); 
2252    var fileListDisplay = document.getElementById('lista-anexos');
2253    var clearBtn = document.getElementById('limpar-anexos');
2254    var maxSizeMsg = document.querySelector('.max-file-size');
2255
2256    if(fileInput && customBtn && fileListDisplay) {
2257
2258        // Forçar multiple
2259        fileInput.setAttribute('multiple', 'multiple');
2260
2261        customBtn.addEventListener('click', function() {
2262            fileInput.click();
2263        });
2264
2265        fileInput.addEventListener('change', function() {
2266            fileListDisplay.innerHTML = ''; 
2267            
2268            if (this.files.length > 0) {
2269                if(maxSizeMsg) maxSizeMsg.style.display = 'none';
2270
2271                var ul = document.createElement('ul');
2272                ul.style.listStyle = 'none';
2273                ul.style.padding = '0';
2274                ul.style.fontSize = '12px';
2275                ul.style.marginTop = '10px';
2276                
2277                // Contador de ficheiros
2278                var info = document.createElement('p');
2279                info.style.fontWeight = 'bold';
2280                info.textContent = this.files.length + ' ficheiros selecionados:';
2281                fileListDisplay.appendChild(info);
2282
2283                // Listar nomes
2284                for (var i = 0; i < this.files.length; i++) {
2285                    var li = document.createElement('li');
2286                    li.innerHTML = '<span style="margin-right:5px">📎</span>' + this.files[i].name;
2287                    li.style.marginBottom = '3px';
2288                    ul.appendChild(li);
2289                }
2290                
2291                fileListDisplay.appendChild(ul);
2292                if(clearBtn) clearBtn.style.display = 'block'; 
2293            } else {
2294                // Se o utilizador cancelar a seleção na janela
2295                if(clearBtn) clearBtn.style.display = 'none';
2296                if(maxSizeMsg) maxSizeMsg.style.display = 'block'; 
2297            }
2298        });
2299
2300        // Botão de limpar tudo
2301        if(clearBtn) {
2302            clearBtn.addEventListener('click', function() {
2303                fileInput.value = ''; 
2304                fileListDisplay.innerHTML = ''; 
2305                clearBtn.style.display = 'none';
2306                
2307                if(maxSizeMsg) maxSizeMsg.style.display = 'block';
2308            });
2309        }
2310    }
2311});

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.