PageSourceSearch

https://www.carotenoidspalma2026.org/js/common.js

js carotenoidspalma2026.org collected 2026-10-08 18:54:30 UTC 98,205 bytes, 3,248 lines download raw bytes

1/**
2 * COMMON JAVASCRIPT
3 * Behaviour as expected
4 */
5function _msg(key, alt, replacements) {
6	let string = '';
7	string =
8		(typeof msg == 'undefined' || typeof msg[key] == 'undefined')
9			? string = alt
10			: msg[key];
11
12	if (!replacements) return string;
13
14	if ('object' === typeof replacements && !Array.isArray(replacements)) {
15		Object.keys(replacements).forEach(key => {
16			string = string.replaceAll(key, replacements[key]);
17		});
18	}
19
20	return string;
21}
22
23function numberPadLeft(number, n, str) {
24	return Array(n - String(number).length + 1).join(str || '0') + number;
25}
26
27const PRISTINE_JS = {
28	BASIC_CONFIG: {
29		classTo: 'input-group', // class of the parent element where the error/success class is added
30		errorClass: 'input-group__input--error',
31		successClass: 'has-success',
32		errorTextParent: 'input-group', // class of the parent element where error text element is appended
33		errorTextTag: 'p', // type of element to create for the error text
34		errorTextClass: 'input-group__validation input-group__input--error'	// class of the error text element
35	},
36	CUSTOM_VALIDATORS: {
37		valid_url : (value, element) => {
38			// only for non required elements
39			if (!value) {
40				return true;
41			}
42
43			const urlPattern = new RegExp('^((ft|htt)ps?:\\/\\/)?'+ // protocol
44				'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name and extension
45				'((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
46				'(\\:\\d+)?'+ // port
47				'(\\/[-a-z\\d%@_.~+&:]*)*'+ // path
48				'(\\?[;&a-z\\d%@_.,~+&:=-]*)?'+ // query string
49				'(\\#[-a-z\\d_]*)?$','i'); // fragment locator
50
51			return urlPattern.test(value.trim());
52
53		}
54
55	}
56}
57
58
59/**
60 	get url param
61	optional url
62	example getAllUrlParams().eventId will return data or undefined if not found
63*/
64function getAllUrlParams(url) {
65	// get query string from url (optional) or window
66	var queryString = url ? url.split("?")[1] : window.location.search.slice(1);
67
68	// we'll store the parameters here
69	var obj = {};
70
71	// if query string exists
72	if (queryString) {
73		// stuff after # is not part of query string, so get rid of it
74		queryString = queryString.split("#")[0];
75
76		// split our query string into its component parts
77		var arr = queryString.split("&");
78
79		for (var i = 0; i < arr.length; i++) {
80			// separate the keys and the values
81			var a = arr[i].split("=");
82
83			// set parameter name and value (use 'true' if empty)
84			var paramName = a[0];
85			var paramValue = typeof a[1] === "undefined" ? true : a[1];
86
87			// (optional) keep case consistent
88			paramName = paramName.toLowerCase();
89			if (typeof paramValue === "string") paramValue = paramValue.toLowerCase();
90
91			// if the paramName ends with square brackets, e.g. colors[] or colors[2]
92			if (paramName.match(/\[(\d+)?\]$/)) {
93				// create key if it doesn't exist
94				var key = paramName.replace(/\[(\d+)?\]/, "");
95				if (!obj[key]) obj[key] = [];
96
97				// if it's an indexed array e.g. colors[2]
98				if (paramName.match(/\[\d+\]$/)) {
99					// get the index value and add the entry at the appropriate position
100					var index = /\[(\d+)\]/.exec(paramName)[1];
101					obj[key][index] = paramValue;
102				} else {
103					// otherwise add the value to the end of the array
104					obj[key].push(paramValue);
105				}
106			} else {
107				// we're dealing with a string
108				if (!obj[paramName]) {
109					// if it doesn't exist, create property
110					obj[paramName] = paramValue;
111				} else if (obj[paramName] && typeof obj[paramName] === "string") {
112					// if property does exist and it's a string, convert it to an array
113					obj[paramName] = [obj[paramName]];
114					obj[paramName].push(paramValue);
115				} else {
116					// otherwise add the property
117					obj[paramName].push(paramValue);
118				}
119			}
120		}
121	}
122
123	return obj;
124}
125
126/**
127 * Method to remove params from current URL and updates navigator bar
128 *
129 * @param {array | string } params Params to be removed from URL
130 * @param {boolean} removeAllParams remove all parameters in url
131 */
132function removeUrlParams(params = [], removeAllParams = false) {
133
134	const url =  new URL(window.location.href);
135	const currentParams = new URLSearchParams(url.searchParams.toString());
136
137	// lets remove all params
138	if (removeAllParams) {
139		const newUrl = url.origin + url.pathname;
140		history.replaceState('', '', newUrl);
141		return;
142	}
143	// if no params or empty array
144	if (!params || !params.length) {
145		return;
146	}
147
148	// manages a string
149	if (typeof(params) === 'string') {
150		currentParams.delete(params);
151	}
152
153	// manages an array
154	if (Array.isArray(params)) {
155		params.forEach(param => {
156			currentParams.delete(param);
157		});
158	}
159
160	// update current URL in navigator
161	const newParams = Array.from(currentParams).length ? '?' + currentParams.toString() : '';
162	const newUrl = url.origin + url.pathname + newParams;
163	history.replaceState('', '', newUrl);
164
165}
166
167/**
168 * Set default width to 100% to ckeditor table
169 * @param CKEDITOR
170 */
171function setDefaultCkEditorTableWidth(CKEDITOR) {
172	CKEDITOR.on('dialogDefinition', function( ev ) {
173
174		const diagName = ev.data.name;
175		const diagDefn = ev.data.definition;
176
177		if(diagName === 'table') {
178			const infoTab = diagDefn.getContents('info');
179
180			const width = infoTab.get('txtWidth');
181			width['default'] = "100%";
182		}
183	});
184}
185
186// method to check if url has param to call google analytics
187function ga_analytics_from_component(eventTracker, data = null) {
188
189	console.log("llego", eventTracker, data);
190	if (data && eventTracker) {
191		SYM.TRACKER.trackEventObj(eventTracker, data);
192	}
193
194  }
195
196var JSON = JSON || {}; // make JSON object available if no native support
197
198var SYM = window.SYM || {}; // initialize namespace: SYM
199
200SYM = $.extend(SYM, function() { // declare namespace SYM
201
202	var ajax_load = false;
203	var ajax_load_offset = -6;
204
205	const plupload_input_file_default_selector = '.input_file';
206	const plupload_pick_file_default_selector = '.btn_pickfiles';
207	const plupload_default_options = {
208		runtimes : 'html5,flash,silverlight,html4,gears,browserplus',
209		browse_button : 'pickfiles',
210		container : 'container',
211		max_file_size : '100mb',
212		chunk_size: '2mb',
213		url : '/file_manager/plupload_receiver',
214		flash_swf_url : '/js/plupload/js/plupload.flash.swf',
215		silverlight_xap_url : '/js/plupload/js/plupload.silverlight.xap'
216	};
217
218	return {
219
220		uploader: null, // plupload
221		uploader_autostart: true, // plupload
222		uploader_id: null, // plupload
223
224		uploader_alt: null,
225		uploader_alt_autostart: true,
226		uploader_alt_id: null,
227
228		uploader_multi: null,
229		uploader_multi_autostart: true,
230		uploader_multi_id: null,
231
232		timer_session: null, // keep the session alive
233		debugc: (typeof _IS_LOCAL !== 'undefined' && _IS_LOCAL) ? true : false, //enable or disable debugging
234
235		// AJAX progress bar
236		bind_ajax_progress_bar: function() {
237			if (typeof SYMJS != 'undefined' && typeof SYMJS.blocks_init != 'undefined') return;
238			if($('#template').hasClass('emagister')) {
239				ajax_load_offset = 0;
240			}
241			$(document).ajaxSend(function() {
242				if(!$('#ajax_load').length) {
243					$('<div></div>').attr('id', 'ajax_load').append(
244						$('<img>').attr('src', '/imgs/common/ajax_load.gif')
245					).appendTo($('body').css('cursor', 'progress')).animate({
246						top: ajax_load_offset
247					}, 900);
248				}
249				ajax_load = $('#ajax_load');
250				//Hide element at 10 seconds without ajax response
251				setTimeout(function() {
252					if(ajax_load.length) {
253						$('body').trigger('ajaxStop');
254					}
255				}, 10000);
256			}).ajaxStop(function() {
257				setTimeout(function() {
258					$('body').css('cursor', 'default');
259					if(ajax_load.length) {
260						ajax_load.animate({
261							top: -40
262						}, 700, function() {
263							ajax_load.remove();
264						});
265					}
266				}, 900);
267			});
268		},
269
270		// do not show progress bar while async requesting
271		unbind_ajax_progress_bar: function() {
272			$('body').unbind('ajaxSend ajaxStop');
273		},
274
275		// maintain session alive #WATCHDOG DEPRECATED?
276		keep_session: function() {
277			if (_IS_LOGGED) {
278				clearInterval(this.timer_session);
279				this.timer_session = setInterval(function() {
280					SYM.unbind_ajax_progress_bar();
281					$.get(noCache('/login/renew_session'), function() {
282						SYM.bind_ajax_progress_bar();
283					});
284				}, 1800000); // 30 min.
285			}
286		},
287
288		// go to proper partner login page
289		go_to_partner_login: function(partner_code, encoded_redirect) {
290			switch (partner_code) {
291				case 'um': case 'ull':
292				case 'demoupf': case 'upf':
293				case 'demoupc': case 'upc': case 'upc-pruebas' :
294					window.open(_SERVERURI + 'login/cas_' + partner_code + '/login?uri=' + _SERVERURI);
295					break;
296				case 'unizar': case 'demo-unizar':
297					window.open(_SERVERURI + 'login/cas_' + partner_code + '/login');
298					break;
299				case 'usal': case 'uma':
300					window.open(_SERVERURI + 'login/papipoa');
301					break;
302				case 'demo-unex':
303					break;
304				case 'unex': case 'urjc': case 'urjc-verano':
305					window.open(_SERVERURI + 'login/ldap');
306					break;
307				case 'madrimasd':
308					window.open(_SERVERURI + 'login/sym_sso/madrimasd/' + encoded_redirect);
309					break;
310				case 'demomadrimasd':
311					window.open(_SERVERURI + 'login/sym_sso/demomadrimasd/' + encoded_redirect);
312					break;
313				default:
314					msgbox_close('all');
315					msgbox_infotop(msg.COMING_SOON);
316			}
317		},
318
319		get_partner_login: function(partner_code) {
320			switch (partner_code) {
321				case 'um': case 'ull':
322				case 'unizar': case 'demo-unizar':
323				case 'demoupf': case 'upf':
324				case 'demoupc': case 'upc': case 'upc-pruebas' :
325					url = _SERVERURI + 'login/cas_' + partner_code + '/login?uri=' + _SERVERURI;
326					break;
327				case 'usal': case 'uma':
328					url = _SERVERURI + 'login/papipoa';
329					break;
330				case 'demo-unex':
331					url = false;
332					break;
333				case 'unex': case 'urjc': case 'urjc-verano':
334					url = _SERVERURI + 'login/ldap';
335					break;
336				case 'madrimasd':
337					url = _SERVERURI + 'login/sym_sso/madrimasd';
338					break;
339				case 'demomadrimasd':
340					url = _SERVERURI + 'login/sym_sso/demomadrimasd';
341					break;
342				default:
343					url = false;
344			}
345			return url;
346		},
347
348		// fill integer with zeros on the left
349		zero_fill: function(number, width) {
350			width -= number.toString().length;
351			if(width > 0) {
352				return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number;
353			}
354			return number;
355		},
356
357		// NOTE: it requires script "http://www.google.com/recaptcha/api/js/recaptcha_ajax.js?1" in order to work!
358		create_captcha: function() {
359			Recaptcha.create(_CAPTCHA_KEY, 'captcha',
360			{
361				theme : 'white',
362				lang : _LANGUAGE
363			});
364		},
365
366		// automatically set tooltips if any
367		activate_tooltips: function(selector) {
368			if ($(selector+'img.tooltip_trigger').length) { // user
369				if (!$('#tooltip_div').length) {
370					//create tooltip div
371					$('<div></div>').attr('id', 'tooltip_div').appendTo($('body'));
372				}
373				//activate tooltip
374				$(selector+'img.tooltip_trigger').tooltip({
375					tip: '#tooltip_div',
376					direction: 'down',
377					bounce: true,
378					position: 'bottom center',
379					onBeforeShow: function (event) {
380						// Transform # symbol into line breaks in tooltip texts
381						if ($(event.srcElement).hasClass('line-break-tooltip')) {
382							$('#tooltip_div').html($('#tooltip_div').html().replace(/#/g, '<br/>'));
383						}
384					}
385				});
386			} else if ($('a.tooltip_trigger').length) { // event_detail
387				if (!$('#tooltip_div').length) {
388					//create tooltip div
389					$('<div></div>').attr('id', 'tooltip_div').appendTo($('body'));
390				}
391				//activate tooltip
392				$('a.tooltip_trigger').tooltip({
393					tip: '#tooltip_div',
394					direction: 'down',
395					bounce: true,
396					position: 'bottom center'
397				}).dynamic({
398					bottom: {
399						direction: 'down'
400					},
401					top: {
402						direction: 'up'
403					}
404				});
405			}
406		},
407
408		/**
409		 * Generate a plupload uploader
410		 *
411		 * Include the following scripts AFTER jquery
412		 * - /js/plupload/js/plupload.browserplus.js
413		 * - /js/plupload/js/plupload.full.js
414		 *
415		 * Copy the same HTML markup from one of the example uploaders like event_detail/upload
416		 *
417		 * You will get an extra INPUT called UPLOAD which will have the path to the uploaded file (obfuscated, see Utils Helper)
418		 * It uses YAV validation to make this INPUT REQUIRED
419		 * Use SYM.reset_plupload_input() to reset the input after a succesful post or any other case you may need to
420		 */
421		pluploader_init: function(properties, callback) {
422
423			const debug = false;
424			const file_selector = plupload_input_file_default_selector;
425			const pick_files_selector = plupload_pick_file_default_selector;
426
427			if (!$(file_selector).length) {
428				if ('function' === typeof callback) callback('No files found');
429				return null;
430			}
431
432			if (SYM.uploader != null) {
433				try {
434					SYM.uploader.destroy();
435					if (debug) console.log('uploader destroyed', SYM.uploader);
436					SYM.uploader = null;
437					if (debug) console.log('uploader should be null by now', SYM.uploader);
438				} catch (e) {
439					console.error(e);
440					if ('function' === typeof callback) callback(e);
441				}
442				if (debug) console.log('pluploader_init_multi destroyed');
443				$(file_selector).off();
444				if ($(pick_files_selector).length) {
445					$(pick_files_selector).each((i, o) => {
446						const clone = $(o).clone();
447						$(o).replaceWith(clone);
448					});
449				}
450				if (debug) console.log('input files and button pickfiles events unbinded', file_selector, pick_files_selector);
451			}
452
453			$(file_selector).focus(function(){
454				$('#pickfiles').focus();
455			});
456
457			if(typeof properties.autostart != 'undefined') {
458				SYM.uploader_autostart = properties.autostart;
459				delete properties.autostart;
460			}
461
462			properties = $.extend({}, plupload_default_options, properties);
463
464			try {
465				SYM.uploader = new plupload.Uploader(properties);
466				SYM.uploader.init();
467			} catch (err) {
468				console.error(err);
469				if ('function' === typeof callback) callback(err);
470				return;
471			}
472
473			SYM.uploader.bind('FilesAdded', function(up, files) {
474				// clean previous errors
475				SYM.reset_plupload_input();
476				$('#filelist .error').remove();
477				// Limit to 1 file, the last added
478				var file_ext = '';
479				if (SYM.uploader.files.length > 1) {
480					// keep in the array just the last file
481					SYM.uploader.files.splice(0,1);
482					SYM.uploader.stop();
483				}
484				$(file_selector).val(files[0].name + ' (' + plupload.formatSize(files[0].size)+ ') ');
485				// Catch the file extension. If there is none, file_ext will be null. If there is one, it will be on the position 1 of an array.
486				file_ext = files[0].name.match(/\.([\w\d]+)$/);
487				// Forbidden extensions
488				var extensions = ['', 'exe', 'com', 'pif', 'src', 'dot', 'vbs', 'php', 'sh'];
489				// Tweak the value of the variable for easier comparison
490				if (file_ext == null) {
491					file_ext = '';
492				} else file_ext = file_ext[1];
493				// If the extension is not found, upload
494				if (extensions.indexOf(file_ext) == -1) {
495					if(SYM.uploader_autostart) {
496						SYM.uploader.start();
497					}
498				} else {
499					$('#filelist').append('<p class="error">'+msg['INVALID_EXTENSION']+'</p>');
500				}
501				up.refresh();
501 // Reposition Flash/Silverlight
502
503				SYM.uploader.trigger('FilesQueued');
504			});
505
506			SYM.uploader.bind('UploadProgress', function(up, file) {
507				$("#bar").css("display", "block");
508				$("#bar").css("width", file.percent + "%");
509				$("#percent").html(file.percent + "%");
510			});
511
512			SYM.uploader.bind('Error', function(up, err) {
513				var code_to_msg = '';
514				switch(err.code) {
515					case -600:
516						code_to_msg  = 'FILE_TOO_BIG';
517						break;
518					case -601:
519						code_to_msg  = 'INVALID_EXTENSION';
520						break;
521					default:
522						code_to_msg = 'UPLOAD_BASE_ERROR';
523						break;
524				}
525				SYM.reset_plupload_input();
526				$('#filelist .error').remove();
527				$('#filelist').append('<p class="error">'+msg[code_to_msg]+'</p>');
528
529				up.refresh(); // Reposition Flash/Silverlight
530			});
531
532			SYM.uploader.bind('FileUploaded', function(up, file, response) {
533				var data = JSON.parse(response.response);
534				$('#upload').val(data.MSG).trigger('change');
535			});
536
537			if ('function' === typeof callback) callback(null, SYM.uploader);
538		},
539
540		pluploader_init_alt: function(properties, callback) {
541			const debug = false;
542
543			let suffix =
544				'undefined' != typeof properties.alternative
545					? properties.alternative
546					: 'alt';
547
548			const file_selector = plupload_input_file_default_selector + '_' + suffix;
549			const pick_files_selector = '#pickfiles_' + suffix;
550			const uploader_identifier = 'uploader_' + suffix;
551
552			if(!$(file_selector).length){
553				if ('function' === typeof callback) callback('No files found');
554				return null;
555			}
556
557			if (!SYM.uploader_alt) {
558				SYM.uploader_alt = {};
559			}
560
561			if (SYM.uploader_alt[uploader_identifier]) {
562				try {
563					SYM.uploader_alt[uploader_identifier].destroy();
564					if (debug) console.log('uploader destroyed', SYM.uploader_alt[uploader_identifier]);
565					SYM.uploader_alt[uploader_identifier] = null;
566					if (debug) console.log('uploader should be null by now', SYM.uploader_alt[uploader_identifier]);
567				} catch (e) {
568					console.error(e);
569					if ('function' === typeof callback) callback(e);
570				}
571				if (debug) console.log('pluploader_init_multi destroyed');
572				$(file_selector).off();
573				if ($(pick_files_selector).length) {
574					$(pick_files_selector).each((i, o) => {
575						const clone = $(o).clone();
576						$(o).replaceWith(clone);
577					});
578				}
579				if (debug) console.log('input files and button pickfiles events unbinded', file_selector, pick_files_selector);
580			}
581
582			$(file_selector).focus(function(){
583				$(pick_files_selector).focus();
584			});
585
586			if(typeof properties.autostart != 'undefined') {
587				SYM.uploader_alt_autostart = properties.autostart;
588				delete properties.autostart;
589			}
590
591			properties = $.extend({}, plupload_default_options, {
592				browse_button : 'pickfiles_'+suffix,
593				container : 'container_'+suffix,
594			}, properties);
595
596			try {
597				SYM.uploader_alt[uploader_identifier] = new plupload.Uploader(properties);
598				SYM.uploader_alt[uploader_identifier].init();
599			} catch (err) {
600				console.error(err);
601				if ('function' === typeof callback) callback(err);
602				return;
603			}
604
605			SYM.uploader_alt[uploader_identifier].bind('FilesAdded', function(up, files) {
606				// clean previous errors
607				SYM.reset_plupload_alt_input({alternative: suffix});
608				$('#filelist_'+suffix+' .error').remove();
609				// Limit to 1 file, the last added
610				var file_ext = '';
611				if (SYM.uploader_alt[uploader_identifier].files.length > 1) {
612					// keep in the array just the last file
613					SYM.uploader_alt[uploader_identifier].files.splice(0,1);
614					SYM.uploader_alt[uploader_identifier].stop();
615				}
616				$(file_selector).val(files[0].name + ' (' + plupload.formatSize(files[0].size)+ ') ');
617				// Catch the file extension. If there is none, file_ext will be null. If there is one, it will be on the position 1 of an array.
618				file_ext = files[0].name.match(/\.([\w\d]+)$/);
619				// Forbidden extensions
620				var extensions = ['', 'exe', 'com', 'pif', 'src', 'dot', 'vbs', 'php', 'sh'];
621				// Tweak the value of the variable for easier comparison
622				if (file_ext == null) {
623					file_ext = '';
624				} else file_ext = file_ext[1];
625				// If the extension is not found, upload
626				if (extensions.indexOf(file_ext) == -1) {
627					if(SYM.uploader_alt_autostart) {
628						SYM.uploader_alt[uploader_identifier].start();
629					}
630				} else {
631					$('#filelist_'+suffix).append('<p class="error">'+msg['INVALID_EXTENSION']+'</p>');
632				}
633				up.refresh();
633 // Reposition Flash/Silverlight
634
635				SYM.uploader_alt[uploader_identifier].trigger('FilesQueued');
636			});
637
638			SYM.uploader_alt[uploader_identifier].bind('UploadProgress', function(up, file) {
639				$("#bar_"+suffix).css("display", "block");
640				$("#bar_"+suffix).css("width", file.percent + "%");
641				$("#percent_"+suffix).html(file.percent + "%");
642			});
643
644			SYM.uploader_alt[uploader_identifier].bind('Error', function(up, err) {
645				var code_to_msg = '';
646				switch(err.code) {
647					case -600:
648						code_to_msg  = 'FILE_TOO_BIG';
649						break;
650					case -601:
651						code_to_msg  = 'INVALID_EXTENSION';
652						break;
653					default:
654						code_to_msg = 'UPLOAD_BASE_ERROR';
655						break;
656				}
657				SYM.reset_plupload_alt_input({alternative: suffix});
658				$('#filelist_'+suffix+' .error').remove();
659				$('#filelist_'+suffix).append('<p class="error">'+msg[code_to_msg]+'</p>');
660
661				up.refresh(); // Reposition Flash/Silverlight
662			});
663
664			SYM.uploader_alt[uploader_identifier].bind('FileUploaded', function(up, file, response) {
665				var data = JSON.parse(response.response);
666				$('#filelist_' + suffix + ' .error').remove();
667				if (data.STAT == 'OK') {
668					$('#upload_' + suffix).val(data.MSG);
669					$('#field_' + suffix + '_file').val(data.MSG);
670				} else {
671					$('#upload_' + suffix).val('');
672					$('#field_' + suffix + '_file').val('');
673
674					$("#bar_" + suffix).css('width', '0%');
675					$("#percent_" + suffix).html('');
676					$('#filelist_' + suffix).append('<p class="error">' + data.MSG + '</p>'); // plain English
677				}
678			});
679
680			if ('function' === typeof callback) {
681				callback(null, SYM.uploader_alt[uploader_identifier]);
682			}
683		},
684
685		pluploader_init_multi: function(properties, callback) {
686			const debug = false;
687			const file_selector = '.input_file';
688			const pick_files_selector = plupload_pick_file_default_selector;
689
690			if (!$(file_selector).length) {
691				// Nothing to do.
692				return;
693			}
694
695			const first_uploader_id = $(file_selector).eq(0).data('field');
696			if (debug) console.log('Initialize pluploader_init_multi = ', first_uploader_id);
697
698			if (SYM.uploader_multi != null) {
699				try {
700					SYM.uploader_multi.destroy();
701					if (debug) console.log('uploader destroyed', SYM.uploader_multi);
702					SYM.uploader_multi = null;
703					if (debug) console.log('uploader should be null by now', SYM.uploader_multi);
704				} catch (e) { console.log(e); }
705				if (debug) console.log('pluploader_init_multi destroyed');
706				$(file_selector).off();
707				if ($(pick_files_selector).length) {
708					$(pick_files_selector).each((i, o) => {
709						const clone = $(o).clone();
710						$(o).replaceWith(clone);
711					});
712				}
713				if (debug) console.log('input files and button pickfiles events unbinded', file_selector, pick_files_selector);
714			}
715
716			// prepare buttons for picking up files
717			$(file_selector)
718				.off('focus')
719				.on('focus', function(){
720					if (debug) console.log('Focus = ', $(this));
721					$('#pickfiles_'+$(this).data('field')).focus();
722				});
723
724			$(pick_files_selector)
725				.off('mouseenter')
726				.on('mouseenter', function(){
727					SYM.uploader_multi_id = $(this).data('field');
728					if (debug) console.log('Set uploader = ' + SYM.uploader_multi_id);
729				})
730				.off('click')
731				.on('click', function(ev){
732					ev.preventDefault();
733					ev.stopPropagation();
734
735					const current_uploader_id = $(this).data('field');
736
737					if(current_uploader_id != first_uploader_id){
738						const first_pick_file_selector = '#pickfiles_' + first_uploader_id;
739						$(first_pick_file_selector)[0].click();
740						if (debug) console.log('Click to (' + current_uploader_id + ') = ' + '#pickfiles_' + first_uploader_id + ' / ' + first_uploader_id);
741						return false;
742					}
743
744					return false;
745				});
746
747			// IE Flash fix
748			if ($('.reg_form_table td.uploader').length) {
749				$('.reg_form_table td.uploader')
750					.off('hover')
751					.hover(function(){
752						SYM.uploader_multi_id = $(this).find(pick_files_selector).attr('data-field');
753						if (debug) console.log('Set uploader flashfix = ' + SYM.uploader_multi_id);
754					});
755			}
756
757			// prepare uploader instance
758			properties = $.extend({}, plupload_default_options, {
759				browse_button : 'pickfiles_'+first_uploader_id,
760				container : 'container_'+first_uploader_id,
761				multi_selection: false
762			}, properties);
763
764			try {
765				SYM.uploader_multi = new plupload.Uploader(properties);
766				SYM.uploader_multi.init();
767			} catch (e) {
768				console.error(e);
769			}
770
771			SYM.uploader_multi.bind('Error', function(up, err) {
772				var code_to_msg = '';
773				switch(err.code) {
774					case -600:
775						code_to_msg  = 'FILE_TOO_BIG';
776						break;
777					case -601:
778						code_to_msg  = 'INVALID_EXTENSION';
779						break;
780					default:
781						code_to_msg = 'UPLOAD_BASE_ERROR';
782						break;
783				}
784				$('#container_' + SYM.uploader_multi_id).find('.error').remove();
785				$('#container_' + SYM.uploader_multi_id).append('<p class="error">'+msg[code_to_msg]+'</p>');
786
787				up.refresh();
787 // Reposition Flash/Silverlight
788			});
789
790			SYM.uploader_multi.bind('FilesAdded', function(up, files) {
791				// IE fails to set up
792				SYM.uploader_multi_id = SYM.uploader_multi_id || $(file_selector).eq(0).data('field');
793				if (debug) console.log('FilesAdded to = ' + SYM.uploader_multi_id);
794				// reset previous error message
795				$('#container_' + SYM.uploader_multi_id).find('.error').remove();
796				// keep this file
797				var last_file = files[files.length-1];
798				last_file.field_id = SYM.uploader_multi_id;
799				// check for forbidden extensions
800				var extensions = ['', 'exe', 'com', 'pif', 'src', 'dot', 'vbs', 'php', 'sh'];
801				var file_ext = last_file.name.match(/\.([\w\d]+)$/);
802				if (file_ext == null) {
803					file_ext = '';
804				} else {
805					file_ext = file_ext[1];
806				}
807				if (extensions.indexOf(file_ext) == -1) {
808					// remove previous file (omitting last one)
809					var indexes_to_remove = [];
810					$.each(SYM.uploader_multi.files, function(i, file) {
811						if(last_file.id != file.id) {
812							if(file.field_id == SYM.uploader_multi_id) {
813								indexes_to_remove.push(i);
814							}
815						}
816					});
817					$.each(indexes_to_remove, function(i, the_index) {
818						SYM.uploader_multi.files.splice(the_index, 1);
819					});
820					$('#field_' + SYM.uploader_multi_id).val(last_file.name);
821					// reset progress bar and uploaded file for this new file
822					$('#container_' + SYM.uploader_multi_id).find('.bar').css('width','0%');
823					$('#field_' + SYM.uploader_multi_id + '_file').val('');
824				}else{
825					$('#container_' + SYM.uploader_multi_id).append('<p class="error">'+msg['INVALID_EXTENSION']+'</p>');
826					up.files.pop();
827				}
828
829				up.refresh();
829 // Reposition Flash/Silverlight
830
831				SYM.uploader_multi.trigger('FilesQueued');
832			});
833
834			SYM.uploader_multi.bind('UploadProgress', function(up, file) {
835				if (debug) console.log('UploadProgress to = ' + '#container_' + file.field_id, up, file);
836				$('#container_' + file.field_id).find('.bar').css('width', file.percent + '%');
837			});
838
839			SYM.uploader_multi.bind('FileUploaded', function(up, file, response) {
840				// /_files/plupload/deobfuscateString(data.MSG)
841				var data = JSON.parse(response.response);
842
843				if (debug) console.log('FileUploaded to = ', up, file, response);
844
845				if(data.STAT == 'OK') {
846					$('#field_' + file.field_id + '_file').val(data.MSG);
847				} else {
848					$('#field_' + file.field_id).val('');
849					$('#container_' + file.field_id).find('.bar').css('width','0%');
850					$('#container_' + file.field_id).append('<p class="error">'+data.MSG+'</p>'); // plain English
851				}
852			});
853
854			if (debug) console.log('Uploader queue must exist by now', SYM.uploader_multi._queue);
855
856			if (typeof callback == 'function') {
857				callback(null, SYM.uploader_multi);
858			}
859		},
860
861		reset_plupload_input: function() {
862			$(plupload_input_file_default_selector).val('');
863			$("#bar").css("width", "0%");
864			$("#percent").html("");
865			$('#upload').val('');
866		},
867
868		reset_plupload_alt_input: function(properties) {
869			let suffix =
870				'undefined' !== typeof properties.alternative
871					? properties.alternative
872					: "alt";
873
874			const file_selector = plupload_input_file_default_selector + '_' + suffix;
875
876			$(file_selector).val('');
877			$("#bar_"+suffix).css("width", "0%");
878			$("#percent_"+suffix).html("");
879			$('#upload_'+suffix).val('');
880		},
881
882		multilang_warning_effects: function() {
883			if ($('#multilang_warning_overlay').length) {
884				var availableWidth = parseInt(($(window).width() - $('#user_content').width()) / 2)-45;
885				var maxWidth = $('#multilang_warning_overlay').width();
886				var minWidth = 160;
887				while (maxWidth > availableWidth) maxWidth--;
888				if (maxWidth < minWidth) maxWidth = minWidth;
889				$('#multilang_warning_overlay').css('width', maxWidth+'px');
890
891				var min = 10;
892				var war = $('#multilang_warning_overlay').height();
893				var max = $('#right_column').height()-(war+min+20);
894
895				$(window).scroll(function(e){
896					var scrollto = window.pageYOffset + min;
897					max = $('#right_column').height()-(war+min+20); // refresh max because right_column is mutable
898					if (scrollto > max) scrollto = max;
899					if (scrollto < min) scrollto = min;
900					$('#multilang_warning_overlay').animate({marginTop: scrollto+'px'}, {duration: 500, queue: false});
901				});
902				$('#multilang_overlay_close').click(function(){
903					$(window).unbind('scroll'); // unbind
904					$('#multilang_warning_overlay').remove();
905				});
906			}
907		},
908
909		// Enable multilang button "Copy Original Content"
910		i18n_reset: function(table){
911			msgbox_confirm(msg.YOU_SURE, msg.LOOSE_TRANSLATION, function(result){
912				if (result == true){
913					var ref_id = 0;
914					var refresh_function = '';
915					// delete translation
916					switch(table){
917						case 'events.id.welcome_msg':
918						case 'events.id.sponsors':
919						case 'events.id.speakers':
920						case 'events.id.program':
921						case 'events.id.place_detail':
922						case 'events.id.title,description':
923							ref_id = $('#event_id').val();
924							refresh_function = 'location.reload();';
925							break;
926						case 'event_schedule_i18n':
927							ref_id = $('#dateid').val();
928							refresh_function = 'create_agenda();$("button.cancel").click();';
929							break;
930						case 'event_videos_i18n':
931							ref_id = $('#video_id').val();
932							refresh_function = 'show_videos(1);$("#form_add_video li.edit_buttons button.cancel").click();';
933							break;
934						case 'event_sponsors_i18n':
935							ref_id = $('#patro_id').val();
936							refresh_function = 'reload_patros();$("#cancel_update_patro").click();';
937							break;
938						case 'event_speakers_i18n':
939							ref_id = $('#speaker_id').val();
940							refresh_function = 'reload_speakers();$("#cancel_update_speaker").click();';
941							break;
942						case 'event_sections_i18n':
943							ref_id = section_id;
944							refresh_function = 'get_sections();cancel_edition();';
945							break;
946						case 'event_news_i18n':
947							ref_id = SYM.I18N.new_id;
948							refresh_function = 'reload_news();';
949							break;
950						case 'event_metadata_i18n':
951							ref_id = $('#eventid').val();
952							refresh_function = 'location.reload();';
953							break;
954						case 'event_dates_i18n':
955							ref_id = $('#date_id').val();
956							refresh_function = 'get_dates(1);$(".button_wrapper button.cancel").click();';
957							break;
958						case 'papers_categories_i18n':
959							ref_id = $('#inp_identifier').val();
960							refresh_function = 'SYM.PAPERS_CATEGORIES.load_categories(1);$("#frm_papers_categories button.btn_cancel").click();';
961							break;
962						case 'events.id.lopd_template':
963							ref_id = $('#event_id').val();
964							refresh_function = 'SYM.EVENT_ATTENDING_CONFIG.load_lopd();';
965							break;
966					}
967					if(ref_id){
968						$.post(noCache('/user_events/reset_translation'), {
969							table: table,
970							lang_key: _LANGUAGE,
971							ref_id: ref_id
972						}, function(response){
973							if(response.stat == 'OK'){
974								// refresh contents
975								eval(refresh_function);
976							}
977						});
978					}
979				}
980			});
981    	},
982
983		check_dirty_form: function () {
984			if (typeof CKEDITOR != "undefined") {
985				var dirty = false;
986				$.each(CKEDITOR.instances, function(){dirty = (dirty || this.checkDirty())});
987			}
988			if (($('#drtyfrm').length && $('#drtyfrm').val() == 'true') || dirty) {
989				return msg['DIRTY_FORM'];
990			}
991			return false;
992		},
993
994		// Add this method on document ready in order to show a warning if any form input is dirty before unload
995		dirty_forms_warning_setup: function(parent) {
996			// console.log('setup');
997			if (typeof parent == "undefined") {
998				parent = 'body';
999			}
1000
1001			$(window).bind("beforeunload",function(event){
1002				SYM.check_dirty_form();
1003			});
1004
1005			$('select, input', parent).change(function(){
1006				// console.log('change');
1007				if (!$('#drtyfrm').length) {
1008					$(parent).append('<input type="hidden" id="drtyfrm" value="true" />');
1009				}
1010			});
1011
1012			$('input, textarea', parent).keyup(function(){
1013				// console.log('keyup');
1014				if (!$('#drtyfrm').length) {
1015					$(parent).append('<input type="hidden" id="drtyfrm" value="true" />');
1016				}
1017			});
1018
1019		},
1020
1021		// Manually mark a form as dirty
1022		dirty_forms_manual_marker: function(selector) {
1023			if (!$('#drtyfrm').length) {
1024				$(selector).append('<input type="hidden" id="drtyfrm" value="true" />');
1025			}
1026		},
1027
1028		// Add this method on every form callback to clean the previously set flag
1029		dirty_forms_cleanup: function() {
1030			$('#drtyfrm').remove();
1031			if (typeof CKEDITOR != "undefined") {
1032				$.each(CKEDITOR.instances, function(){this.resetDirty()});
1033			}
1034		},
1035
1036		/**
1037		 * Formats a JavaScript Date object into a string to use with our datepicker
1038		 * @param  {JavaScript.Date} date       The date object to be processed
1039		 * @param  {string} 		 dateformat The format of the date string, for example: yyyy-MM-dd or MM/dd/yyyy-MM-dd
1040		 * @return {string}            			The string to use as the value of the input, for example: 2013-10-18
1041		 */
1042		format_date: function(date, dateformat) {
1043			if (typeof dateformat == 'undefined') {
1044				dateformat = (typeof msg.yavdateformat != 'undefined')?msg.yavdateformat:'';
1045			}
1046
1047			dateformat = dateformat.replace('dd', ('00'+date.getDate()).slice(-2));
1048			dateformat = dateformat.replace('MM', ('00'+(date.getMonth()+1)).slice(-2));
1049			dateformat = dateformat.replace('yyyy', date.getFullYear());
1050			dateformat = dateformat.replace('HH', ('00'+date.getHours()).slice(-2));
1051			dateformat = dateformat.replace('ii', ('00'+date.getMinutes()).slice(-2));
1052			dateformat = dateformat.replace('ss', ('00'+date.getSeconds()).slice(-2));
1053
1054			return dateformat;
1055		},
1056
1057		/**
1058		 * Builds a JavaScript Date object parsing a string
1059		 * @param  {string} date       		The date to be parsed
1060		 * @param  {string} dateformat 		A template to parse the date, for example: yyyy-MM-dd HH:ii:ss
1061		 * @param  {string} gmt        		The timezone the date is at, only numeric. For example: +0000 (GMT+0) or +0200 (GMT+2)
1062		 * @return {JavaScript.Date}   		The date object translated to the browser timezone
1063		 */
1064		build_date: function(date, dateformat, gmt) {
1065			if (typeof dateformat == 'undefined') {
1066				dateformat = (typeof msg.yavdateformat != 'undefined')?msg.yavdateformat:'';
1067			}
1068
1069			if (date == null || date.length == 0) return new Date(0);
1070
1071			var seconds, minutes, hours, day, month, year;
1072			day = date.substr(dateformat.indexOf('dd'),2);
1073			month = date.substr(dateformat.indexOf('MM'),2);
1074			year = date.substr(dateformat.indexOf('yyyy'),4);
1075
1076			if (dateformat.indexOf('H')>=0) {
1077				hours = date.substr(dateformat.indexOf('HH'),2);
1078			} else hours = 0;
1079			if (dateformat.indexOf('i')>=0) {
1080				minutes = date.substr(dateformat.indexOf('ii'),2);
1081			} else minutes = 0;
1082			if (dateformat.indexOf('s')>=0) {
1083				seconds = date.substr(dateformat.indexOf('ss'),2);
1084			} else seconds = 0;
1085
1086			month = month-1;
1087			if (month < 10) month = '0'+month;
1088
1089			if (typeof gmt == 'undefined') {
1090				gmt = '+0000';
1091			}
1092			var new_date = new Date(year, month, day, hours, minutes, seconds),
1093				date_str = new_date.toDateString()+' '+new_date.toTimeString().substr(0,8);
1094			return new Date(date_str+' '+gmt);
1095		},
1096
1097		download_file: function(url, filename) {
1098			if (typeof filename == 'undefined') {
1099				filename = url.substr(url.lastIndexOf('/')+1);
1100			}
1101			var anchor = document.createElement('a');
1102		    anchor.setAttribute('href', url);
1103		    anchor.setAttribute('download', filename);
1104
1105			var ev = document.createEvent("MouseEvents");
1106			ev.initMouseEvent("click", true, false, self, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
1107			anchor.dispatchEvent(ev);
1108		},
1109
1110		compare_timezone_offsets: function() {
1111			if (typeof _TZ_OFFSET != 'undefined' && _TZ_OFFSET != -1) {
1112				var d = new Date();
1113				return (_TZ_OFFSET != (d.getTimezoneOffset()*60));
1114			}
1115			return false;
1116		},
1117
1118		printpdf_task: function(local_url, local_params, print_folder, print_file, success, failure) {
1119			var request_params = local_params || {};
1120			$.post(noCache(local_url), request_params,
1121					function(response) {
1122						if (response.stat == 'OK') {
1123							var request_folder = print_folder || response.path;
1124							var request_file = print_file || response.filename;
1125							/* var remainingIntervalRetries = 15; */
1126							var retryTimeout = 2000;
1127							var interval = setInterval(function(){
1128								console.log({file_path:request_folder+request_file});
1129								$.post(noCache('/user_events/pdfprinter_file_status'),
1130									{
1131										file_path:request_folder+request_file
1132									},
1133									function(response){
1134										console.log(response);
1135										if (response.status) {
1136											clearInterval(interval);
1137											SYM.download_file('/user_events/pdfprinter_file_download/'+request_folder.replace(/\//g,'@')+request_file, request_file);
1138											if (typeof success == 'function') success();
1139										}
1140									}
1141								);
1142								/* // Stop the retries on timeout.
1143								if (--remainingIntervalRetries <= 0) {
1144									clearInterval(interval);
1145									msgbox_alert(_msg('SERVER_ERROR', 'Error en el servidor'));
1146								} */
1147							}, retryTimeout);
1148						} else {
1149							if (typeof failure == 'function') failure();
1150						}
1151					}
1152				);
1153		},
1154
1155		remove_errors: function(){
1156			$('p.error').remove();
1157		},
1158
1159		add_errors: function(msg, dom_element){
1160			$('<p></p>').addClass('error').text(msg).insertAfter(dom_element);
1161		},
1162
1163		validateEmail(email) {
1164			var re = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
1165			return re.test(String(email).toLowerCase());
1166		},
1167
1168		validateURL(url) {
1169			var re = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/i;
1170			return re.test(url);
1171		},
1172
1173		fetchFile(url, data, filename, onError, onSuccess) {
1174			const p = Object.assign({}, data);
1175			const formData = new FormData();
1176			const noCacheUrl =  noCache(url);
1177			for (let i in p) {
1178				if (p.hasOwnProperty(i)) {
1179					formData.append(i, p[i]);
1180				}
1181			}
1182			const params = {
1183				method: 'POST',
1184				body: formData
1185			};
1186			console.log('fetchFile', params, JSON.stringify(Object.fromEntries(formData)));
1187			fetch(noCacheUrl, params).then(function (response) {
1188				console.log('fetchFileResponse', response);
1189				if (response.ok) {
1190					// Check response type
1191					// if json, check controlled error response
1192					if (response.headers.get('Content-Type') == 'application/json') {
1193						response.json().then(json => {
1194							if (json.stat !== 'OK') {
1195								if (typeof onError == 'function') {
1196									onError(json.msg)
1197								}
1198							}
1199						}).catch(function (err) {
1200							console.error('Reading json error', err);
1201							// Check errors on request
1202							if (typeof onError == 'function') {
1203								onError(err.message);
1204							}
1205						});
1206					} else {
1207						// if blob, check file download
1208						response.blob().then((blob) => {
1209							const headerContentDisposition = response.headers.get('Content-Disposition');
1210							const serverFilename = headerContentDisposition.match(/filename="(.+)"/)[1];
1211							const theFilename = filename || serverFilename;
1212							SYM.onDownloadBlob(blob, theFilename);
1213							if (typeof onSuccess == 'function') {
1214								onSuccess(blob, theFilename);
1215							}
1216						}).catch(function (err) {
1217							console.error('Getting blob error', err);
1218							// Check errors on request
1219							if (typeof onError == 'function') {
1220								onError(err.message);
1221							}
1222						});
1223					}
1224				} else {
1225					if (typeof onError == 'function') {
1226						onError(response.statusText);
1227					}
1228				}
1229			}).catch(function (err) {
1230				console.error('fetchFileError', err);
1231				// Check errors on request
1232				if (typeof onError == 'function') {
1233					onError(err.message);
1234				}
1235			});
1236		},
1237
1238		fetchFileGet(url, filename, onError, onSuccess) {
1239			const noCacheUrl =  noCache(url);
1240			console.log('fetchFileGet', noCacheUrl);
1241			fetch(noCacheUrl).then(function (response) {
1242				console.log('fetchFileResponse', response);
1243				if (response.ok) {
1244					// Check response type
1245					// if json, check controlled error response
1246					if (response.headers.get('Content-Type') == 'application/json') {
1247						response.json().then(json => {
1248							if (json.stat !== 'OK') {
1249								if (typeof onError == 'function') {
1250									onError(json.msg)
1251								}
1252							}
1253						}).catch(function (err) {
1254							console.error('Reading json error', err);
1255							// Check errors on request
1256							if (typeof onError == 'function') {
1257								onError(err.message);
1258							}
1259						});
1260					} else {
1261						// if blob, check file download
1262						response.blob().then((blob) => {
1263							const headerContentDisposition = response.headers.get('Content-Disposition');
1264							const serverFilename = headerContentDisposition.match(/filename="(.+)"/)[1];
1265							const theFilename = filename || serverFilename;
1266							SYM.onDownloadBlob(blob, theFilename);
1267							if (typeof onSuccess == 'function') {
1268								onSuccess(blob, theFilename);
1269							}
1270						}).catch(function (err) {
1271							console.error('Getting blob error', err);
1272							// Check errors on request
1273							if (typeof onError == 'function') {
1274								onError(err.message);
1275							}
1276						});
1277					}
1278				} else {
1279					if (typeof onError == 'function') {
1280						onError(response.statusText);
1281					}
1282				}
1283			}).catch(function (err) {
1284				console.error('fetchFileError', err);
1285				// Check errors on request
1286				if (typeof onError == 'function') {
1287					onError(err.message);
1288				}
1289			});
1290		},
1291
1292		onDownloadBlob: function (blob, filename) {
1293			// Create blob to link and download.
1294			const finalFilename = filename;
1295			const objURL = window.URL.createObjectURL(blob);
1296			let link = document.createElement('a');
1297			link.href = objURL;
1298			link.download = finalFilename;
1299			link.click();
1300		}
1301
1302	};
1303
1304}());
1305
1306SYM.FORM = window.SYM.FORM || {};
1307SYM.FORM = $.extend(SYM.FORM, function ($) {
1308	var METHODS = {
1309		private: {
1310			/**
1311			 * initInputCounter
1312			 * - Requires the $dom object to have the following data attributes:
1313			 *   - data-text-count: Type of element to be counted [chars|words] (words by default)
1314			 *   - data-text-count-for: Input to check the length of
1315			 *   - data-max: Maximum number of 'type' to count
1316			 *   - data-text: Some text to add after the number of remaining items
1317			 * @param {Object} $dom - The jQuery object of the dom holding the counter
1318			 */
1319			initInputCounter: function ($dom) {
1320				var type = $dom.attr('data-text-count');
1321				var input = '#' + $dom.attr('data-text-count-for');
1322				var max = $dom.attr('data-max');
1323				var text = $dom.attr('data-text');
1324				$(input).on('keyup', function (ev) {
1325					var count = type == 'chars' ? $(this).val().length :
1326							 /* type == 'words' */$(this).val().split(' ')
1327							.filter(function (n) { return n != '' })
1328							.length;
1329					var remaining = (max - count);
1330					$dom.html(remaining + ' ' + text);
1331					$dom.toggleClass('input-group__subtext--warning', remaining < 0);
1332				});
1333				$(input).trigger('keyup');
1334			}
1335		},
1336		public: {
1337			initInputCounter: function ($selector) {
1338				var $selection = $selector || '.text-count-input';
1339				$($selection).each(function (index, object) { METHODS.private.initInputCounter($(object)); });
1340			}
1341		}
1342	};
1343	return METHODS.public;
1344}(jQuery));
1345
1346// Solves the Cache problem using AJAX calls
1347
1348function noCache(uri) {
1349	return uri.concat(/\?/.test(uri) ? '&' : '?', 'noCache=', (new Date).getTime(), '.', Math.random() * 1234567);
1350}
1351
1352// String to JSON object
1353
1354function evalJson(src) {
1355	var pattern = /^\(.+\)$/gi; // Add parentheses if needed
1356	if (pattern.test(src) == false) {
1357		src = '(' + src + ')';
1358	}
1359
1360	var data = '';
1361	if (typeof JSON == 'object' && JSON.parse) {
1362		try {
1363			data = JSON.parse(src);
1364		}
1365		catch (e) {
1366			try {
1367				data = eval(src);
1368			}
1369			catch (e) {
1370				data = src;
1371			}
1372		}
1373
1374	}
1375	else {
1376		data = eval(src);
1377	}
1378
1379	if (typeof data == 'object') {
1380		return data
1381	}
1382	else {
1383		return {
1384			stat: 'ERROR',
1385			msg: 'ERROR'
1386		};
1387	}
1388}
1389
1390// implement JSON.stringify serialization
1391JSON.stringify = JSON.stringify || function (obj) {
1392	var t = typeof (obj);
1393	if (t != "object" || obj === null) {
1394		// simple data type
1395		if (t == "string") obj = '"'+obj+'"';
1396		return String(obj);
1397	} else {
1398		// recurse array or object
1399		var n, v, json = [], arr = (obj && obj.constructor == Array);
1400		for (n in obj) {
1401			v = obj[n]; t = typeof(v);
1402			if (t == "string") v = '"'+v+'"';
1403			else if (t == "object" && v !== null) v = JSON.stringify(v);
1404			json.push((arr ? "" : '"' + n + '":') + String(v));
1405		}
1406		return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
1407	}
1408};
1409// implement JSON.parse de-serialization
1410JSON.parse = JSON.parse || function (str) {
1411	if (str === "") str = '""';
1412	eval("var p=" + str + ";");
1413	return p;
1414};
1415
1416// Function to delay keyup event, to wait for the last one
1417var delay = (function() {
1418	var timer = 0;
1419	return function(callback, ms) {
1420		clearTimeout (timer);
1421		timer = setTimeout(callback, ms);
1422	};
1423})();
1424
1425function htmlentities(string, quote_style) {
1426	// Convert all applicable characters to HTML entities
1427	//
1428	// version: 1004.2314
1429	// discuss at: http://phpjs.org/functions/htmlentities    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1430	// +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1431	// +   improved by: nobbler
1432	// +    tweaked by: Jack
1433	// +   bugfixed by: Onno Marsman    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1434	// +    bugfixed by: Brett Zamir (http://brett-zamir.me)
1435	// +      input by: Ratheous
1436	// -    depends on: get_html_translation_table
1437	// *     example 1: htmlentities('Kevin & van Zonneveld');    // *     returns 1: 'Kevin &amp; van Zonneveld'
1438	// *     example 2: htmlentities("foo'bar","ENT_QUOTES");
1439	// *     returns 2: 'foo&#039;bar'
1440	var hash_map = {},
1441		symbol = '',
1442		tmp_str = '',
1443		entity = '';
1444	tmp_str = string.toString();
1445	if (false === (hash_map = this.get_html_translation_table('HTML_ENTITIES', quote_style))) {
1446		return false;
1447	}
1448	hash_map["'"] = '&#039;';
1449	for (symbol in hash_map) {
1450		entity = hash_map[symbol];
1451		tmp_str = tmp_str.split(symbol).join(entity);
1452	}
1453	return tmp_str;
1454}
1455
1456function base64_encode(data) {
1457	var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
1457+/=";
1458	var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
1459		ac = 0,
1460		enc = "",
1461		tmp_arr = [];
1462	if (!data) {
1463		return data
1464	}
1465	do {
1466		o1 = data.charCodeAt(i++);
1467		o2 = data.charCodeAt(i++);
1468		o3 = data.charCodeAt(i++);
1469		bits = o1 << 16 | o2 << 8 | o3;
1470		h1 = bits >> 18 & 0x3f;
1471		h2 = bits >> 12 & 0x3f;
1472		h3 = bits >> 6 & 0x3f;
1473		h4 = bits & 0x3f;
1474		tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4)
1475	} while (i < data.length);
1476	enc = tmp_arr.join('');
1477	switch (data.length % 3) {
1478	case 1:
1479		enc = enc.slice(0, -2) + '==';
1480		break;
1481	case 2:
1482		enc = enc.slice(0, -1) + '=';
1483		break
1484	}
1485	return enc
1486}
1487
1488function base64_decode(input) {
1489	var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
1490
1491	var output = "";
1492	var chr1, chr2, chr3 = "";
1493	var enc1, enc2, enc3, enc4 = "";
1494	var i = 0;
1495
1496	// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
1497	var base64test = /[^A-Za-z0-9\+\/\=]/g;
1498	if (base64test.exec(input)) {
1499	alert("There were invalid base64 characters in the input text.\n" +
1500	"Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
1501	"Expect errors in decoding.");
1502	}
1503	input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
1504
1505	do {
1506	enc1 = b64.indexOf(input.charAt(i++));
1507	enc2 = b64.indexOf(input.charAt(i++));
1508	enc3 = b64.indexOf(input.charAt(i++));
1509	enc4 = b64.indexOf(input.charAt(i++));
1510
1511	chr1 = (enc1 << 2) | (enc2 >> 4);
1512	chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
1513	chr3 = ((enc3 & 3) << 6) | enc4;
1514
1515	output = output + String.fromCharCode(chr1);
1516
1517	if (enc3 != 64) {
1518	output = output + String.fromCharCode(chr2);
1519	}
1520	if (enc4 != 64) {
1521	output = output + String.fromCharCode(chr3);
1522	}
1523
1524	chr1 = chr2 = chr3 = "";
1525	enc1 = enc2 = enc3 = enc4 = "";
1526
1527	} while (i < input.length);
1528
1529	return unescape(output);
1530}
1531
1532function get_html_translation_table(table, quote_style) {
1533	// http://kevin.vanzonneveld.net
1534	// +   original by: Philip Peterson
1535	// +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1536	// +   bugfixed by: noname
1537	// +   bugfixed by: Alex
1538	// +   bugfixed by: Marco
1539	// +   bugfixed by: madipta
1540	// +   improved by: KELAN
1541	// +   improved by: Brett Zamir (http://brett-zamir.me)
1542	// +   bugfixed by: Brett Zamir (http://brett-zamir.me)
1543	// +      input by: Frank Forte
1544	// +   bugfixed by: T.Wild
1545	// +      input by: Ratheous
1546	// %          note: It has been decided that we're not going to add global
1547	// %          note: dependencies to php.js, meaning the constants are not
1548	// %          note: real constants, but strings instead. Integers are also supported if someone
1549	// %          note: chooses to create the constants themselves.
1550	// *     example 1: get_html_translation_table('HTML_SPECIALCHARS');
1551	// *     returns 1: {'"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;'}
1552	var entities = {},
1553		hash_map = {},
1554		decimal = 0,
1555		symbol = '';
1556	var constMappingTable = {},
1557		constMappingQuoteStyle = {};
1558	var useTable = {},
1559		useQuoteStyle = {};
1560
1561	// Translate arguments
1562	constMappingTable[0] = 'HTML_SPECIALCHARS';
1563	constMappingTable[1] = 'HTML_ENTITIES';
1564	constMappingQuoteStyle[0] = 'ENT_NOQUOTES';
1565	constMappingQuoteStyle[2] = 'ENT_COMPAT';
1566	constMappingQuoteStyle[3] = 'ENT_QUOTES';
1567
1568	useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS';
1569	useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT';
1570
1571	if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') {
1572		throw new Error("Table: " + useTable + ' not supported');
1573		// return false;
1574	}
1575
1576	entities['38'] = '&amp;';
1577	if (useTable === 'HTML_ENTITIES') {
1578		entities['160'] = '&nbsp;';
1579		entities['161'] = '&iexcl;';
1580		entities['162'] = '&cent;';
1581		entities['163'] = '&pound;';
1582		entities['164'] = '&curren;';
1583		entities['165'] = '&yen;';
1584		entities['166'] = '&brvbar;';
1585		entities['167'] = '&sect;';
1586		entities['168'] = '&uml;';
1587		entities['169'] = '&copy;';
1588		entities['170'] = '&ordf;';
1589		entities['171'] = '&laquo;';
1590		entities['172'] = '&not;';
1591		entities['173'] = '&shy;';
1592		entities['174'] = '&reg;';
1593		entities['175'] = '&macr;';
1594		entities['176'] = '&deg;';
1595		entities['177'] = '&plusmn;';
1596		entities['178'] = '&sup2;';
1597		entities['179'] = '&sup3;';
1598		entities['180'] = '&acute;';
1599		entities['181'] = '&micro;';
1600		entities['182'] = '&para;';
1601		entities['183'] = '&middot;';
1602		entities['184'] = '&cedil;';
1603		entities['185'] = '&sup1;';
1604		entities['186'] = '&ordm;';
1605		entities['187'] = '&raquo;';
1606		entities['188'] = '&frac14;';
1607		entities['189'] = '&frac12;';
1608		entities['190'] = '&frac34;';
1609		entities['191'] = '&iquest;';
1610		entities['192'] = '&Agrave;';
1611		entities['193'] = '&Aacute;';
1612		entities['194'] = '&Acirc;';
1613		entities['195'] = '&Atilde;';
1614		entities['196'] = '&Auml;';
1615		entities['197'] = '&Aring;';
1616		entities['198'] = '&AElig;';
1617		entities['199'] = '&Ccedil;';
1618		entities['200'] = '&Egrave;';
1619		entities['201'] = '&Eacute;';
1620		entities['202'] = '&Ecirc;';
1621		entities['203'] = '&Euml;';
1622		entities['204'] = '&Igrave;';
1623		entities['205'] = '&Iacute;';
1624		entities['206'] = '&Icirc;';
1625		entities['207'] = '&Iuml;';
1626		entities['208'] = '&ETH;';
1627		entities['209'] = '&Ntilde;';
1628		entities['210'] = '&Ograve;';
1629		entities['211'] = '&Oacute;';
1630		entities['212'] = '&Ocirc;';
1631		entities['213'] = '&Otilde;';
1632		entities['214'] = '&Ouml;';
1633		entities['215'] = '&times;';
1634		entities['216'] = '&Oslash;';
1635		entities['217'] = '&Ugrave;';
1636		entities['218'] = '&Uacute;';
1637		entities['219'] = '&Ucirc;';
1638		entities['220'] = '&Uuml;';
1639		entities['221'] = '&Yacute;';
1640		entities['222'] = '&THORN;';
1641		entities['223'] = '&szlig;';
1642		entities['224'] = '&agrave;';
1643		entities['225'] = '&aacute;';
1644		entities['226'] = '&acirc;';
1645		entities['227'] = '&atilde;';
1646		entities['228'] = '&auml;';
1647		entities['229'] = '&aring;';
1648		entities['230'] = '&aelig;';
1649		entities['231'] = '&ccedil;';
1650		entities['232'] = '&egrave;';
1651		entities['233'] = '&eacute;';
1652		entities['234'] = '&ecirc;';
1653		entities['235'] = '&euml;';
1654		entities['236'] = '&igrave;';
1655		entities['237'] = '&iacute;';
1656		entities['238'] = '&icirc;';
1657		entities['239'] = '&iuml;';
1658		entities['240'] = '&eth;';
1659		entities['241'] = '&ntilde;';
1660		entities['242'] = '&ograve;';
1661		entities['243'] = '&oacute;';
1662		entities['244'] = '&ocirc;';
1663		entities['245'] = '&otilde;';
1664		entities['246'] = '&ouml;';
1665		entities['247'] = '&divide;';
1666		entities['248'] = '&oslash;';
1667		entities['249'] = '&ugrave;';
1668		entities['250'] = '&uacute;';
1669		entities['251'] = '&ucirc;';
1670		entities['252'] = '&uuml;';
1671		entities['253'] = '&yacute;';
1672		entities['254'] = '&thorn;';
1673		entities['255'] = '&yuml;';
1674	}
1675
1676	if (useQuoteStyle !== 'ENT_NOQUOTES') {
1677		entities['34'] = '&quot;';
1678	}
1679	if (useQuoteStyle === 'ENT_QUOTES') {
1680		entities['39'] = '&#39;';
1681	}
1682	entities['60'] = '&lt;';
1683	entities['62'] = '&gt;';
1684
1685
1686	// ascii decimals to real symbols
1687	for (decimal in entities) {
1688		symbol = String.fromCharCode(decimal);
1689		hash_map[symbol] = entities[decimal];
1690	}
1691
1692	return hash_map;
1693}
1694
1695empty = function(mixed_var) {
1696	// !No description available for empty. @php.js developers: Please update the function summary text file.
1697	//
1698	// version: 1006.1915
1699	// discuss at: http://phpjs.org/functions/empty    // +   original by: Philippe Baumann
1700	// +      input by: Onno Marsman
1701	// +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
1702	// +      input by: LH
1703	// +   improved by: Onno Marsman    // +   improved by: Francesco
1704	// +   improved by: Marc Jansen
1705	// +   input by: Stoyan Kyosev (http://www.svest.org/)
1706	// *     example 1: empty(null);
1707	// *     returns 1: true    // *     example 2: empty(undefined);
1708	// *     returns 2: true
1709	// *     example 3: empty([]);
1710	// *     returns 3: true
1711	// *     example 4: empty({});    // *     returns 4: true
1712	// *     example 5: empty({'aFunc' : function () { alert('humpty'); } });
1713	// *     returns 5: false
1714	var key;
1715	if (mixed_var === "" || mixed_var === 0 || mixed_var === "0" || mixed_var === null || mixed_var === false || typeof mixed_var === 'undefined') {
1716		return true;
1717	}
1718	if (typeof mixed_var == 'object') {
1719		for (key in mixed_var) {
1720			return false;
1721		}
1722		return true;
1723	}
1724
1725	return false;
1726}
1727
1728// Encode UTF8
1729
1730function encode_utf8(s) {
1731	return unescape(encodeURIComponent(s));
1732}
1733// Decode UTF8
1734
1735function decode_utf8(s) {
1736	return decodeURIComponent(escape(s));
1737}
1738
1739
1740function enable_tab_height() {
1741	if ($('.tabs', '#right_column').length) {
1742		$('.tabs a', '#right_column').hover(function() {
1743			$('.tabs a.current', '#right_column').css('height', 23);
1744			$(this).css('height', 25);
1745		}, function() {
1746			$(this).css('height', 23);
1747			$('.tabs a.current', '#right_column').css('height', 25);
1748		});
1749
1750	}
1751}
1752
1753// eliminar duplicados en un array
1754
1755function array_unique(vals) {
1756	var uniques = [];
1757	for (var i = vals.length; i--;) {
1758		var val = vals[i];
1759		if ($.inArray(val, uniques) === -1) {
1760			uniques.unshift(val);
1761		}
1762	}
1763	return uniques;
1764}
1765
1766//PASS ALL OPTIONS OF A SELECT TO AN ARRAY
1767
1768function optionsToArray(id) {
1769	var result = new Array();
1770	var i = 0;
1771	$.each($('#' + id + ' option'), function() {
1772		result[i] = $(this).val();
1773		i++;
1774	});
1775	return result;
1776}
1777
1778// validate an email using RFC
1779/**
1780 *  Validate email applies compliance RFC 5322
1781 *  Permits some especial chars
1782 *  refers to documentation for more info https://datatracker.ietf.org/doc/html/rfc5322#section-3.4
1783 * @param string email to check
1784 * @returns bool
1785 */
1786 function validate_email_rfc(email) {
1787	var regexp = /^(?=.{1,64}@)(\w{1,}[!#\$\%\&\'\*\+\-\/=\?\.\^_`\{\|\}~]{0,1})+\w+@((?=.{1,255}\.)((?:[A-Za-z0-9]+(?:(?:[A-Za-z0-9\-]*[A-Za-z0-9])?)\.)+[A-Za-z]{2,})|(((0|[1-9A-Fa-f][0-9A-Fa-f]{0,3}):){0,6}(0|)]))$/i;
1788	return regexp.test(email);
1789};
1790
1791/*************** YAV DEFAULT CUSTOM RULES **************/
1792//comprobar que un ckeditor no está vacío
1793//elimina espacios, saltos de línea, etc, antes de validar
1794
1795function ckeditor_required(return_value) {
1796	var ckeditor_value = CKEDITOR.instances[return_value.id].getData();
1797	if (!$('#ckeditor_aux_div').length) {
1798		$('<div></div>').attr('id', 'ckeditor_aux_div').css('display', 'hidden').appendTo('body');
1799	}
1800	var aux_div = $('#ckeditor_aux_div');
1801	aux_div.hide().html(ckeditor_value);
1802	if ($.trim(aux_div.text()).replace(/^\s+/g, '').replace(/\s+$/g, '').length || aux_div.find('img').length) {
1803		return null;
1804	} else {
1805		return return_value;
1806	}
1807}
1808
1809function ckeditor_max_word_count(return_value) {
1810	var ckeditor_value = CKEDITOR.instances[return_value.id].getData();
1811	var word_count = parseInt($('#'+return_value.id).attr('data-word-limit'));
1812
1813	// Word count is disabled
1814	if (word_count <= 0) return null;
1815
1816	ckeditor_value = ckeditor_value.replace(/<(?:.|\n)*?>/gm, '').replace(/&nbsp;/gm, ' ').replace(/\s+/gm, ' ').trim();
1817	word_count = word_count - ckeditor_value.split(' ').length;
1818
1819	if (word_count >= 0) {
1820		return null;
1821	} else {
1822		return return_value;
1823	}
1824}
1825
1826
1827//Validate dates + times
1828
1829function time_travel(return_value) {
1830	var timeTravel = true;
1831	var date_ini_val = $('#date_ini').val();
1832	var time_ini_val = ($('#init_hour').val() < 10 ? '0' + $('#init_hour').val():$('#init_hour').val()) + $('#
1832init_minute').val();
1833	var date_end_val = $('#date_end').val();
1834	var time_end_val = ($('#end_hour').val() < 10 ? '0' + $('#end_hour').val():$('#end_hour').val()) + $('#end_minute').val();
1835	if(date_ini_val.length >= 10 && date_end_val.length >= 10) {
1836		timeTravel = false;
1837		var date_ini_eng = date_ini_val.match(/(\d{1,2})\/(\d{2})\/(\d{4})/);
1838		var date_ini_esp = date_ini_val.match(/(\d{1,2})\-(\d{2})\-(\d{4})/);
1839		var date_end_eng = date_end_val.match(/(\d{1,2})\/(\d{2})\/(\d{4})/);
1840		var date_end_esp = date_end_val.match(/(\d{1,2})\-(\d{2})\-(\d{4})/);
1841			if(date_ini_eng && date_end_eng) {
1842				timeTravel = parseInt(date_ini_eng[3] + date_ini_eng[1] + date_ini_eng[2] + time_ini_val, 10) > parseInt(date_end_eng[3] + date_end_eng[1] + date_end_eng[2] + time_end_val, 10);
1843			} else if(date_ini_esp && date_end_esp) {
1844				timeTravel = parseInt(date_ini_esp[3] + date_ini_esp[2] + date_ini_esp[1] + time_ini_val, 10) > parseInt(date_end_esp[3] + date_end_esp[2] + date_end_esp[1] + time_end_val, 10);
1845			} else {
1846				timeTravel = true;
1847			}
1848	}
1849	return timeTravel == true ? return_value : null;
1850}
1851
1852
1853//Validate dates
1854
1855function date_travel(return_value) {
1856	var timeTravel = true;
1857	var date_ini_val = $('#date_ini').val();
1858	var date_end_val = $('#date_end').val();
1859	var valid_dates = true;
1860	if((!date_ini_val.length || !date_end_val.length) || (date_ini_val.length > 9 && date_end_val.length > 9)) {
1861		timeTravel = false;
1862		//date ini validation
1863		if(date_ini_val.length) {
1864			var date_ini_eng = date_ini_val.match(/(\d{1,2})\/(\d{2})\/(\d{4})/); //english
1865			var date_ini_esp = date_ini_val.match(/(\d{1,2})\-(\d{2})\-(\d{4})/); //spanish
1866			if(!date_ini_eng && !date_ini_esp) {
1867				timeTravel = true;
1868				valid_dates = false;
1869			}
1870
1871		}
1872		//date end validation
1873		if(date_end_val.length && valid_dates) {
1874			var date_end_eng = date_end_val.match(/(\d{1,2})\/(\d{2})\/(\d{4})/); //english
1875			var date_end_esp = date_end_val.match(/(\d{1,2})\-(\d{2})\-(\d{4})/); //spanish
1876			if(!date_end_eng && !date_end_esp) {
1877				timeTravel = true;
1878				valid_dates = false;
1879			}
1880		}
1881		//date ini & date end validation
1882		if(date_ini_val.length && date_end_val.length && valid_dates) {
1883			if(date_ini_eng && date_end_eng) {
1884				timeTravel = parseInt(date_ini_eng[3] + date_ini_eng[1] + date_ini_eng[2], 10) > parseInt(date_end_eng[3] + date_end_eng[1] + date_end_eng[2], 10);
1885			} else if(date_ini_esp && date_end_esp) {
1886				timeTravel = parseInt(date_ini_esp[3] + date_ini_esp[2] + date_ini_esp[1], 10) > parseInt(date_end_esp[3] + date_end_esp[2] + date_end_esp[1], 10);
1887			} else {
1888				timeTravel = true;
1889				valid_dates = false;
1890			}
1891		}
1892		if(!valid_dates) {
1893			return_value.msg = msg.WHEN_TIP;
1894		}
1895	}
1896	return timeTravel == true ? return_value : null;
1897}
1898
1899var valid_url = function(return_value){
1900	if($("#" + return_value.id).val() == '') { // permitir no requerido
1901		return null;
1902	}
1903	else if (/([-a-zA-Z0-9@:%_\+.~#?&//=]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))/gi.test( $("#" + return_value.id).val() )){
1904		return null;
1905	}else{
1906		return return_value;
1907	}
1908}
1909
1910var integer_no_zero = function(return_value){
1911	if($("#" + return_value.id).val() == '') { // permitir no requerido
1912		return null;
1913	}
1914	else if (/[1-9][0-9]*/.test( $("#" + return_value.id).val() )){
1915		return null;
1916	}else{
1917		return return_value;
1918	}
1919}
1920
1921var required_one = function(return_value) {
1922	var parent = $('#'+return_value.id).parent(),
1923	radios = $('input[type="radio"],input[type="checkbox"]', parent),
1924	one_checked = false,
1925	find_attempts = 3;
1926	while (radios.length == 1 && find_attempts > 0) {
1927		find_attempts--;
1928		parent = $(parent).parent();
1929		radios = $('input[type="radio"],input[type="checkbox"]', parent);
1930	}
1931	for (var i = 0; i < radios.length; i++) {
1932		one_checked = (one_checked || $(radios[i]).is(':checked'));
1933		if(i > 0){
1934			$(radios[i]).removeClass('required_one'); // avoid multiple error messages
1935		}
1936	}
1937	if (one_checked || ! $('#'+return_value.id).hasClass('required_one')) {
1938		return null;
1939	} else {
1940		return return_value;
1941	}
1942}
1943
1944/**
1945 * The field is required if the author base form required fields are filled.
1946 * If everything goes right the method will return null (no error).
1947 * @version 1.0
1948 * @param return_value string This string defines the error.
1949 * @author <[email protected]>
1950 * @return string|null
1951 */
1952var required_if_valid_author_insert = function (return_value) {
1953	var field = $('#' + return_value.id);
1954	var authorBlock = field.parents('.author_form').first();
1955	var authorName = authorBlock.find('.author_name_input');
1956	var authorEmail = authorBlock.find('.author_email_input');
1957	var validAuthorInsert = (
1958		(authorName && authorName.val() && authorName.val() !== '') ||
1959		(authorEmail && authorEmail.val() && authorEmail.val() !== '')
1960	);
1961
1962	if ( ! field || ! field.val() || field.val() == '') {
1963		if (validAuthorInsert) {
1964			return return_value;
1965		}
1966	}
1967
1968	return null;
1969}
1970
1971/**
1972 * The field is required if the author base form required fields are filled.
1973 * If everything goes right the method will return null (no error).
1974 * @version 1.0
1975 * @param return_value string This string defines the error.
1976 * @author <[email protected]>
1977 * @return string|null
1978 */
1979var required_one_if_valid_author_insert = function (return_value) {
1980	var field = $('#' + return_value.id);
1981	var authorBlock = field.parents('.author_form').first();
1982	var authorName = authorBlock.find('.author_name_input');
1983	var authorEmail = authorBlock.find('.author_email_input');
1984	var validAuthorInsert = (
1985		(authorName && authorName.val() && authorName.val() !== '') ||
1986		(authorEmail && authorEmail.val() && authorEmail.val() !== '')
1987	);
1988
1989	var parent = field.parent(),
1990		radios = $('input[type="radio"],input[type="checkbox"]', parent),
1991		one_checked = false;
1992	for (var i = 0; i < radios.length; i++) {
1993		one_checked = (one_checked || $(radios[i]).is(':checked'));
1994		if (i > 0) {
1995			$(radios[i]).removeClass('required_one_if_valid_author_insert'); // avoid multiple error messages
1996		}
1997	}
1998
1999	if (one_checked || ! field.hasClass('required_one_if_valid_author_insert')) {
2000		return null;
2001	} else {
2002		return (validAuthorInsert) ? return_value : null;
2003	}
2004}
2005
2006/**
2007 *  Validate email applies compliance RFC 5322
2008 *  Permits some especial chars
2009 *  refers to documentation for more info https://datatracker.ietf.org/doc/html/rfc5322#section-3.4
2010 * @param {*} return_value
2011 * @returns
2012 */
2013var email_rfc = function (return_value) {
2014	var field = $("#" + return_value.id);
2015
2016	if (field.val() == "") {
2017		return null;
2018	}
2019
2020	return validate_email_rfc(field.val()) ? null : return_value;
2021};
2022
2023/**
2024 * Rule for YAV to validate only alphanumeric characters
2025 * @param return_value
2026 * @returns {*|null}
2027 */
2028const only_alphanumeric = function (return_value) {
2029	var field = $("#" + return_value.id);
2030	if (field.val() == "") {
2031		return null;
2032	}
2033	const currentValue = field.val();
2034	// pattern to validate
2035	var wl_pattern = /(<[^>]+>|=|"|<|>)+/i;
2036	var result = currentValue.match(wl_pattern);
2037	return result ? return_value : null;
2038};
2039
2040const valid_uri = (returnValue) => {
2041	const urlPattern = new RegExp('^((ft|htt)ps?:\\/\\/)?'+ // protocol
2042		'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name and extension
2043		'((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
2044		'(\\:\\d+)?'+ // port
2045		'(\\/[-a-z\\d%@_.~+&:]*)*'+ // path
2046		'(\\?[;&a-z\\d%@_.,~+&:=-]*)?'+ // query string
2047		'(\\#[-a-z\\d_]*)?$','i'); // fragment locator
2048	const currentElement = document.getElementById(returnValue.id);
2049	const currentValue = currentElement.value || '';
2050	if (currentValue.trim() === '') { // permitir no requerido
2051		return null;
2052	}
2053
2054	return !urlPattern.test(currentValue.trim()) ? returnValue : null;
2055}
2056
2057const yav_valid_cif_nif_nie = function (returnValue) {
2058	const $field = $('#' + returnValue.id);
2059	if (!$field.length || $field.val() == "") return null;
2060
2061	return valida_nif_cif_nie($field.val()) < 0 ? returnValue : null;
2062}
2063
2064// YAV CUSTOM RULES //
2065$.yav_customfunctions = {
2066	"ckeditor_required": ckeditor_required,
2067	"time_travel": time_travel,
2068	"date_travel": date_travel,
2069	"valid_url": valid_url,
2070	"integer_no_zero": integer_no_zero,
2071	"required_one": required_one,
2072	"email_rfc": email_rfc,
2073	"valid_cif_nif_nie": yav_valid_cif_nif_nie ,
2074	"only_alphanumeric": only_alphanumeric,
2075};
2076$.yav_default = {
2077	hideTitle: false,
2078	rulesOnSubmit: true
2079};
2080$.yav_default.library = {
2081	ISFOCUSSET: true
2082};
2083// END YAV CUSTOM RULES //
2084
2085//js log configuration
2086if(/[#,&,\?]debug=true/.test(document.location.href)){
2087		document.writeln("<script type='text/javascript' src='https://getfirebug.com/firebug-lite.js#enableTrace=true'></script>");
2088}else if (SYM.debugc || _ENVIRONMENT == 'testing') {
2089	if (typeof console == 'undefined') {
2090		document.writeln("<script type='text/javascript' src='https://getfirebug.com/firebug-lite.js#enableTrace=true'></script>");
2091	}
2092}else {
2093	try {
2094		console_disable = function(){
2095			return false;
2096		}
2097		window['console'] = {
2098				log: console_disable,
2099				info: console_disable,
2100				debug: console_disable,
2101				error: console_disable,
2102				warn: console_disable
2103			}
2104	}
2105	catch (e) {}
2106}
2107//end js log configuration
2108
2109//Make unique space cleaned tags
2110function generate_tags(tag_aux_arr)
2111{
2112	tag_arr = [];
2113	$.each(array_unique(tag_aux_arr), function() {
2114		var str_val = $.trim(this.toString());
2115		if(str_val.length) {
2116			tag_arr.push(str_val)
2117		}
2118	});
2119	return tag_arr;
2120}
2121
2122/**
2123 * Links two datepickers, forcing the second to have at least the same day as the first
2124 * @param String class_identifier The class identifying both datepickers
2125 * @param String date_ini_id The id of the date ini datepicker
2126 */
2127function datepicker_range_pair(class_identifier, date_ini_id) {
2128	$.datepicker.setDefaults(msg.datepicker);
2129	var date_ini_full_id = '#'+date_ini_id,
2130		dates = $(class_identifier).datepicker({
2131		onSelect: function( selectedDate ) {
2132			if (this.id == date_ini_id) { 	// If Im date ini and have been changed
2133				var option = "minDate",
2134					instance = $( this ).data( "datepicker" ),
2135					date = $.datepicker.parseDate(
2136						instance.settings.dateFormat ||
2137						$.datepicker._defaults.dateFormat,
2138						selectedDate, instance.settings );
2139
2140				dates.not( this ).datepicker( "option", option, date ); // Set the minDate of my pair
2141				if ($.datepicker.parseDate( msg.datepicker.dateFormat , selectedDate) > $.datepicker.parseDate( msg.datepicker.dateFormat ,dates.not( this ).val()))
2142					// If date_ini > date_end, set the new date_end at date_ini, as it's its minimum
2143					dates.not( this ).val(selectedDate);
2144			}
2145		},
2146		beforeShow: function( input, instance ) {
2147			// hack for calendar position in modals jQuery.ui.1.13.2 with jQuery 3.6.2 requires ui.position
2148			// Dirty hack, only works this way
2149			var calendar = instance.dpDiv;
2150			setTimeout(function() {
2151				calendar.position({
2152					my: 'left top',
2153					at: 'left bottom',
2154					collision: 'none',
2155					of: input
2156				});
2157			}, 1);
2158
2159			if (this.id != date_ini_id && $(date_ini_full_id).val().length > 0) { 	// If Im date end, and date ini has a value
2160				$(this).datepicker("option", "minDate", $.datepicker.parseDate( msg.datepicker.dateFormat ,dates.not( this ).val()));
2161			}
2162		}
2163	});
2164}
2165
2166/**
2167 * Scrolls to an element. Useful to show errors.
2168 * @param String identifier Valid jQuery selector.
2169 */
2170function scroll_to_el(identifier, speed, margin) {
2171	if (typeof speed == 'undefined') speed = 750;
2172	if (typeof margin == 'undefined') margin = 0;
2173	$('html,body').animate({
2174		scrollTop: $(identifier).offset().top + margin
2175	}, speed);
2176}
2177
2178/*
2179FUNCIONES AUXILIARES
2180str_replace -> Emula el str_replace de PHP
2181valida_nif_cif_nie -> Valida un documento NIF, CIF o NIE
2182*/
2183function str_replace(search, replace, subject) {
2184    var f = search, r = replace, s = subject;
2185    var ra = r instanceof Array, sa = s instanceof Array, f = [].concat(f), r = [].concat(r), i = (s = [].concat(s)).length;
2186    while (j = 0, i--) {
2187        if (s[i]) {
2188            while (s[i] = s[i].split(f[j]).join(ra ? r[j] || "" : r[0]), ++j in f){};
2189        }
2190    };
2191    return sa ? s : s[0];
2192}
2193//Retorna: 1 = NIF ok, 2 = CIF ok, 3 = NIE ok, -1 = NIF error, -2 = CIF error, -3 = NIE error, 0 = ??? error
2194function valida_nif_cif_nie(a)
2195{
2196	var temp=a.toUpperCase();
2197	var cadenadni="TRWAGMYFPDXBNJZSQVHLCKE";
2198
2199	if (temp!==''){
2200		//si no tiene un formato valido devuelve error
2201		if ((!/^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$/.test(temp) && !/^[T]{1}[A-Z0-9]{8}$/.test(temp)) && !/^[0-9]{8}[A-Z]{1}$/.test(temp))
2202		{
2203			return 0;
2204		}
2205
2206		//comprobacion de NIFs estandar
2207		if (/^[0-9]{8}[A-Z]{1}$/.test(temp))
2208		{
2209			posicion = a.substring(8,0) % 23;
2210			letra = cadenadni.charAt(posicion);
2211			var letradni=temp.charAt(8);
2212			if (letra == letradni)
2213			{
2214			   	return 1;
2215			}
2216			else
2217			{
2218				return -1;
2219			}
2220		}
2221
2222		//algoritmo para comprobacion de codigos tipo CIF
2223		suma = parseInt(a[2])+parseInt(a[4])+parseInt(a[6]);
2224		for (i = 1; i < 8; i += 2)
2225		{
2226			temp1 = 2 * parseInt(a[i]);
2227			temp1 += '';
2228			temp1 = temp1.substring(0,1);
2229			temp2 = 2 * parseInt(a[i]);
2230			temp2 += '';
2231			temp2 = temp2.substring(1,2);
2232			if (temp2 == '')
2233			{
2234				temp2 = '0';
2235			}
2236
2237			suma += (parseInt(temp1) + parseInt(temp2));
2238		}
2239		suma += '';
2240		n = 10 - parseInt(suma.substring(suma.length-1, suma.length));
2241
2242		//comprobacion de NIFs especiales (se calculan como CIFs)
2243		if (/^[KLM]{1}/.test(temp))
2244		{
2245			if (a[8] == String.fromCharCode(64 + n))
2246			{
2247				return 1;
2248			}
2249			else
2250			{
2251				return -1;
2252			}
2253		}
2254
2255		//comprobacion de CIFs
2256		if (/^[ABCDEFGHJNPQRSUVW]{1}/.test(temp))
2257		{
2258			temp = n + '';
2259			if (a[8] == String.fromCharCode(64 + n) || a[8] == parseInt(temp.substring(temp.length-1, temp.length)))
2260			{
2261				return 2;
2262			}
2263			else
2264			{
2265				return -2;
2266			}
2267		}
2268
2269		//comprobacion de NIEs
2270		//T
2271		if (/^[T]{1}/.test(temp))
2272		{
2273			if (a[8] == /^[T]{1}[A-Z0-9]{8}$/.test(temp))
2274			{
2275				return 3;
2276			}
2277			else
2278			{
2279				return -3;
2280			}
2281		}
2282
2283		//XYZ
2284		if (/^[XYZ]{1}/.test(temp))
2285		{
2286			pos = str_replace(['X', 'Y', 'Z'], ['0','1','2'], temp).substring(0, 8) % 23;
2287			if (a[8] == cadenadni.substring(pos, pos + 1))
2288			{
2289				return 3;
2290			}
2291			else
2292			{
2293				return -3;
2294			}
2295		}
2296	}
2297	return 0;
2298}
2299
2300// indexOf for IE
2301if (!Array.prototype.indexOf)
2302{
2303  Array.prototype.indexOf = function(elt /*, from*/)
2304  {
2305    var len = this.length >>> 0;
2306
2307    var from = Number(arguments[1]) || 0;
2308    from = (from < 0)
2309         ? Math.ceil(from)
2310         : Math.floor(from);
2311    if (from < 0)
2312      from += len;
2313
2314    for (; from < len; from++)
2315    {
2316      if (from in this &&
2317          this[from] === elt)
2318        return from;
2319    }
2320    return -1;
2321  };
2322}
2323
2324jQuery.fn.whenPresent = function(successCallback, failedCallback) {
2325	var selector = this.selector,
2326		interval = 100,
2327		timeout = 20,
2328		checkExist = setInterval(function() {
2329			if ($(selector).length || timeout < 0) {
2330				var callback = (timeout > 0) ? successCallback : failedCallback;
2331				if (typeof callback == 'function') {
2332					callback();
2333				}
2334				clearInterval(checkExist);
2335			} else {
2336				timeout--;
2337			}
2338		}, interval);
2339	return this;
2340};
2341
2342jQuery.fn.scrollTo = function(speed) {
2343	var selector = this.selector;
2344	if (typeof speed == 'undefined') speed = 750;
2345	$('html,body').animate({
2346		scrollTop: $(selector).offset().top
2347	}, speed);
2348};
2349
2350
2351SYM.TRACKER = $.extend({}, (($) => {
2352	const CONST = {
2353		ga4Events: {
2354			symposium_webinars_banner_show: {
2355				event: "Campaings",
2356				name: "Webinars Banner",
2357				parameters: { title: "Show" }
2358			},
2359
2360			symposium_webinars_banner_more_info: {
2361				event: "Campaings",
2362				name: "Webinars Banner",
2363				parameters: { title: "More Info" }
2364			},
2365
2366			symposium_webinars_banner_close: {
2367				event: "Campaings",
2368				name: "Webinars Banner",
2369				parameters: { title: "Close" }
2370			},
2371
2372			symposium_webinars_overlay_show: {
2373				event: "Campaings",
2374				name: "Webinars Overlay",
2375				parameters: { title: "Show" }
2376			},
2377
2378			symposium_webinars_overlay_close: {
2379				event: "Campaings",
2380				name: "Webinars Overlay",
2381				parameters: { title: "Close" }
2382			},
2383
2384			symposium_webinars_overlay_more_info: {
2385				event: "Campaings",
2386				name: "Webinars Overlay",
2387				parameters: { title: "More Info" }
2388			},
2389
2390			symposium_webinars_overlay_close_forever: {
2391				event: "AB_Testing",
2392				name: "MyEvents",
2393				parameters: { title: "Close Forever" }
2394			},
2395
2396			symposium_AB_Testing_myevents_click_on_arrow: {
2397				event: "AB_Testing",
2398				name: "MyEvents",
2399				parameters: { title: "Toggle by click on arrow" }
2400			},
2401
2402			symposium_AB_Testing_myevents_click_on_title: {
2403				event: "AB_Testing",
2404				name: "MyEvents",
2405				parameters: { title: "Toggle by click on title" }
2406			},
2407
2408			symposium_from_vue_widget_event_click: {
2409				event: "VUE_event_click",
2410				name: "MyEvents",
2411				parameters: { title: "Click on vue widget event" }
2412			},
2413
2414			symposium_from_vue_widget_news_click: {
2415				event: "VUE_news_click",
2416				name: "MyEvents",
2417				parameters: { title: "Click on vue widget news" }
2418			},
2419
2420			symposium_maps_display: {
2421				event: "Maps",
2422				name: "Display",
2423				parameters: { title: "New event step 1" }
2424			},
2425
2426			symposium_maps_display_directory_map: {
2427				event: "Maps",
2428				name: "Display",
2429				parameters: { title: "Directory Map" }
2430			},
2431
2432			symposium_maps_display_event_places: {
2433				event: "Maps",
2434				name: "Display",
2435				parameters: { title: "Event_places" }
2436			},
2437
2438			symposium_maps_display_event_map: {
2439				event: "Maps",
2440				name: "Display",
2441				parameters: { title: "Event map" }
2442			},
2443
2444			symposium_maps_preload_preload_map: {
2445				event: "Maps",
2446				name: "Preload",
2447				parameters: { title: "Map preloaded" }
2448			},
2449
2450			symposium_widget_maps_display_event: {
2451				event: "Maps",
2452				name: "Preload",
2453				parameters: { title: "widget_map_event" }
2454			},
2455
2456		}
2457
2458	};
2459
2460	const METHODS = {
2461
2462		private: {
2463			trackGoogleAnalytics4Event: (event, extraParams = null) => {
2464				const addCID = ['VUE_event_click', 'VUE_news_click'];
2465				const currentEvent = CONST.ga4Events[event];
2466				// remove cid param
2467				if (addCID.includes(currentEvent.event)) {
2468					currentEvent.parameters.id = getAllUrlParams().cid;
2469					currentEvent.event = "SymWidgetComponent_eventClicked"
2470					removeUrlParams('cid');
2471				}
2472
2473				// gtag not found due cookieconsent
2474				if (typeof gtag === 'undefined') {
2475					return;
2476				}
2477
2478				if (extraParams) {
2479					currentEvent.params = { ...currentEvent.params, ...extraParams };
2480				}
2481
2482				gtag('event', currentEvent.event, currentEvent.parameters);
2483
2484			},
2485		},
2486		public: {
2487			trackEvent: (event, extraParams = null) => {
2488				console.log('track', event);
2489				if (CONST.ga4Events[event]) {
2490					METHODS.private.trackGoogleAnalytics4Event(event, extraParams);
2491				} else {
2492					console.log('cant track event');
2493				}
2494			}
2495		}
2496	}
2497
2498	return METHODS.public;
2499
2500})(jQuery));
2501
2502
2503// Once document is loaded...
2504$.fn.ready(function() {
2505	//Configuration for AJAX calls
2506	SYM.bind_ajax_progress_bar();
2507
2508	// get rid of those system borders being generated for A tags //
2509	if (typeof $.browser != 'undefined' && $.browser.msie) {
2510		$('a').focus(function() {
2511			$(this).attr('hideFocus', 'true');
2512		});
2513	}
2514
2515	// prevent session lost
2516	if(typeof _IS_PARTNER_SITE != 'undefined') { // EE no ofrece gestión de usuarios
2517		if(_IS_PARTNER_SITE){
2518			SYM.keep_session();
2519		}
2520	}
2521
2522	//ELLIPSIS
2523	if ($('.ellipsis').length) {
2524		if ($.fn.ellipsis) {
2525			$('.ellipsis').ellipsis(true);
2526		}
2527	}
2528
2529	enable_tab_height();
2530
2531	// Always stick the footer to the bottom of the page
2532	if ($("div.sticky").length > 0) {
2533		setTimeout(function() {
2534			if (typeof $.fn.stickyFooter == 'function') {
2535				$("div.sticky").stickyFooter();
2536			} else {
2537				console.error('stickyFooter was not found');
2538			}
2539		}, 1500);
2540	}
2541
2542	//Symposium by emagister link
2543	$('.sym_info', '#body_footer').css('cursor', 'pointer').click(function() {
2544		document.location.href = $(this).children('a').attr('href');
2545	});
2546
2547	// show tooltips
2548	SYM.activate_tooltips('');
2549
2550	// activate multilang warning movement
2551	SYM.multilang_warning_effects();
2552
2553	// extend jQuery load
2554	if (typeof $.fn._load == 'undefined') { // Don't do this twice
2555		(function($) {
2556			$.fn._load = $.fn.load;
2557			$.fn.extend({
2558				load: function(url, args, callback) {
2559					if (typeof SYMJS != 'undefined' && typeof SYMJS.blocks_init != 'undefined') return $(this)._load(url, args, callback);
2560					// Backwards compatibility for the jquery load function
2561					if ($.isFunction(url)) return $(this)._load(url, args, callback);
2562					if (!this.length) return this;
2563
2564					// append ajax loader
2565					this.html('<div class="ajax_load_points"></div>');
2566
2567					return $(this)._load(url, args, callback);
2568				}
2569			});
2570		})(jQuery);
2571	}
2572
2573	// Migration warning
2574	var migrationwarning = false;
2575	if (migrationwarning && typeof msgbox_infotop != 'undefined') {
2576		if (typeof localStorage != 'undefined') {
2577			var lastShow = new Date(localStorage.getItem('_MaintenanceWarning')),
2578				now = new Date(),
2579				diff = now.getTime() - lastShow.getTime(),
2580				hours = diff / (1000*60*60);
2581
2582			if (hours < 1) {
2583				console.log('hours = '+hours);
2584				return;
2585			}
2586
2587			localStorage.setItem('_MaintenanceWarning', now.toISOString());
2588		}
2589		var text = {
2590				'es' : 'La plataforma de eventos estará en mantenimiento el miércoles 19 de Octubre de 5:00 am a 9:00 am. Disculpen las molestias.',
2591				'en' : 'The events platform will be in maintenance mode on wednesday 19th of October from 5:00 am to 9:00 am. We apologize for any inconvenience.',
2592				'ca' : "La plataforma d'esdeveniments estarà en manteniment el dimecres 19 d'octubre de 05:00 a.m. a 09:00 a.m. Disculpeu les molèsteis."
2593			},
2594			lang = _LANGUAGE;
2595		if (typeof text[_LANGUAGE] == 'undefined') lang = 'es';
2596		msgbox_infotop(text[lang], 'migration-warning');
2597		setTimeout(function(){
2598			msgbox_close($('#migration-warning'));
2599		}, 10000);
2600	}
2601
2602	// Include js-dropdown functionality
2603	$('body').delegate('.js-dropdown .js-dropdown-trigger', 'mouseenter', function (ev) {
2604		ev.preventDefault();
2605		var $body = $('body');
2606		var $trigger = $(this);
2607		var $parent = $trigger.parents('.js-dropdown');
2608		var $drop = $parent.find('.js-dropdown-drop');
2609		var direction = null;
2610		if ($drop.length) {
2611			$drop.detach();
2612			$drop.hide();
2613			$body.append($drop);
2614			direction = $drop.data('direction') || 'bottom';
2615			$drop.addClass(direction);
2616			$trigger[0]._js_drop = $drop;
2617			$trigger[0]._js_drop_direction = direction;
2618		} else {
2619			$drop = $trigger[0]._js_drop;
2620		}
2621		direction = $trigger[0]._js_drop_direction;
2622		$drop.css({'position': 'absolute'});
2623		$drop.show();
2624		var left = 0;
2625		var top = 0;
2626		switch (direction) {
2627			case 'left':
2628				left = $trigger.offset().left
2629							- $drop.width()
2630							- parseInt($drop.css('padding-left'))
2631							- parseInt($drop.css('padding-right'))
2632							- parseInt($drop.css('border-left'))
2633							- parseInt($drop.css('border-right'))
2634							- 18;
2635				top = $trigger.offset().top - ($drop.height() / 2);
2636				break;
2637			case 'right':
2638				left = $trigger.offset().left
2639							+ $trigger.width()
2640							+ parseInt($trigger.css('padding-right'))
2641							+ parseInt($trigger.css('padding-left'))
2642							+ 18;
2643				top = $trigger.offset().top - ($drop.height() / 2);
2644				break;
2645			case 'bottom':
2646				left = $trigger.offset().left - ($drop[0].clientWidth / 2) + ($trigger[0].clientWidth / 2);
2647				top = $trigger.offset().top + $trigger[0].clientHeight + 18;
2648				break;
2649			case 'top':
2650				left = $trigger.offset().left - ($drop[0].clientWidth / 2) + ($trigger[0].clientWidth / 2);
2651				top = $trigger.offset().top - $drop.height() - parseInt($drop.css('padding-top')) - parseInt($drop.css('padding-bottom')) - 18;
2652				break;
2653		}
2654		$drop.css({ 'left': left, 'top': top });
2655		return false;
2656	});
2657	$('body').delegate('.js-dropdown .js-dropdown-trigger', 'mouseleave', function (ev) {
2658		ev.preventDefault();
2659		var $trigger = $(this);
2660		if ($trigger[0]._js_drop) {
2661			$trigger[0]._js_drop.hide();
2662		}
2663		return false;
2664	});
2665
2666	// Closes sidebar menu when clicks out
2667	var $sideMenu = $('#event-menu-collapse');
2668	if ($sideMenu.length) {
2669		var $closeSideMenu = $sideMenu.find('.close-menu');
2670		$sideMenu.find('a:not(.dropdown-toggle):not(.close-menu)').click(() => {
2671			$closeSideMenu.click();
2672		});
2673	}
2674
2675	// Symposium Banner fadeout
2676	$('.symposiumBanner__close').on('click', function (ev) {
2677		ev.preventDefault();
2678		ev.stopPropagation();
2679		var $trigger = $(this);
2680		var $banner = $trigger.parents('.symposiumBanner');
2681		var $bannerIdentifier = $banner.data('banner-identifier');
2682		var $bannerReopen = $banner.data('banner-reopen');
2683		if ($bannerIdentifier) {
2684			$banner.slideUp('slow');
2685			// Check if the banner is ment to open every time (we dont need to keep track of it)
2686			if (!$bannerReopen || $bannerReopen != 'reopen') {
2687				var now = new Date();
2688				localStorage.setItem('banner-'+$bannerIdentifier, now);
2689				// Add statistics
2690				SYM.TRACKER.trackEvent("symposium_webinars_banner_close");
2691			}
2692		}
2693	});
2694	// Symposium Banner more information click
2695	$(".symposiumBanner__action-link").on('click', 'a', (ev) => {
2696		// Add statistics
2697		SYM.TRACKER.trackEvent("symposium_webinars_banner_more_info");
2698	});
2699	// Symposium Banner fade in
2700	$('.symposiumBanner').each(function (i, o) {
2701		var $banner = $(o);
2702		var $bannerIdentifier = $banner.data('banner-identifier');
2703		if ($bannerIdentifier) {
2704			var now = new Date();
2705			// Search banner in localstorage
2706			// If no data or the date is older than 8 hours, open the banner
2707			var date = localStorage.getItem('banner-'+$bannerIdentifier);
2708			var show = false;
2709			if (date) {
2710				date = new Date(date);
2711				var diff = (now.getTime() - date.getTime()) / 1000 / 3600;
2712				if (diff > 8) {
2713					show = true;
2714				}
2715			} else {
2716				show = true;
2717			}
2718			if (show) {
2719				$banner.slideDown('slow');
2720				if ($bannerIdentifier == "zoom-support-banner") {
2721					SYM.TRACKER.trackEvent("symposium_webinars_banner_show");
2722				}
2723			}
2724		}
2725	});
2726
2727	// Symposium login modal
2728	if (SYM.LOGIN) {
2729		SYM.LOGIN.tryLoginModal();
2730	} else {
2731		let loginTryies = 0;
2732		let loadLoginWidgetScript = function () {
2733			$.getScript("/js/widgets/login_panel.js")
2734			.done(function () {
2735				SYM.LOGIN.tryLoginModal();
2736			})
2737			.fail(function () {
2738				loginTryies++;
2739				if (loginTryies < 5) loadLoginWidgetScript();
2740			});
2741		}
2742		loadLoginWidgetScript();
2743	}
2744
2745});
2746
2747// Splash screen
2748/*$(window).load(function() {
2749	$('.loading_splash').fadeOut('slow', function() {
2750		$(this).remove();
2751	});
2752});
2753*/
2754
2755if ( !jQuery.browser ) {
2756  var uaMatch = function( ua ) {
2757    ua = ua.toLowerCase();
2758
2759    var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
2760      /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
2761      /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
2762      /(msie) ([\w.]+)/.exec( ua ) ||
2763      ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
2764      [];
2765
2766    return {
2767      browser: match[ 1 ] || "",
2768      version: match[ 2 ] || "0"
2769    };
2770  };
2771
2772  matched = uaMatch( navigator.userAgent );
2773  browser = {};
2774
2775  if ( matched.browser ) {
2776    browser[ matched.browser ] = true;
2777    browser.version = matched.version;
2778  }
2779
2780  // Chrome is Webkit, but Webkit is also Safari.
2781  if ( browser.chrome ) {
2782    browser.webkit = true;
2783  } else if ( browser.webkit ) {
2784    browser.safari = true;
2785  }
2786
2787  jQuery.browser = browser;
2788}
2789
2790SYM.VAL = {
2791	current: null,
2792	validate: function(selector, elementsSelector) {
2793		SYM.VAL.current = $(selector);
2794		var valid = true;
2795		$('.symValError', selector).remove();
2796		if (typeof elementsSelector == 'undefined') elementsSelector = '.symValInp:visible';
2797		$(elementsSelector, selector).each(function(c,el){
2798			if (valid) {
2799				var fn = $(el).attr('data-val-fn'),
2800					params = $(el).attr('data-val-params');
2801				if (params != null && params.length) params = JSON.parse(params);
2802				switch ($(el).attr('type')) {
2803					case 'checkbox':
2804					case 'radio':
2805						var result = SYM.VAL.fn[fn]($(el).attr('data-val-group'), params);
2806						break;
2807					default:
2808						var result = SYM.VAL.fn[fn]($(el).val(), params);
2809						break;
2810				}
2811				if (!result) {
2812					var error = $(el).attr('data-val-error');
2813					if (error == null || error.length == 0) {
2814						error = msg.REQUIRED_FIELD;
2815					}
2816					var error_container = $(el).attr('data-val-error-container');
2817					if (error_container == null || error_container.length == 0) {
2818						$('<p class="error symValError"> ' + error + ' </p>').insertAfter(el);
2819					} else {
2820						$(error_container).append($('<p class="error symValError"> ' + error + ' </p>'));
2821					}
2822				}
2823				valid = valid && result;
2824			}
2825		});
2826		return valid;
2827	},
2828	fn: {
2829		email: function(value) {
2830			var reg = new RegExp(/^([a-z0-9_\.\-\+]+\@[\da-z\.-]+\.[a-z\.]{2,})$/i);
2831			console.log('email', reg.test(value), value);
2832			return reg.test(value);
2833		},
2834		email_rfc: function(value) {
2835			return validate_email_rfc(value);
2836		},
2837		required: function(value) {
2838			console.log('required', value);
2839			return value != null && (typeof value != 'undefined') && value.length > 0;
2840		},
2841		regex: function(value, params) {
2842			var reg = new RegExp(params.reg);
2843			console.log('regex', reg.test(value), value);
2844			return reg.test(value);
2845		},
2846		require_at_least: function(group, params) {
2847			var selected = $('input[data-val-group=' + group + ']:checked', SYM.VAL.current).length,
2848				min = (typeof params == 'object' && typeof params.min != 'undefined') ? params.min : 0,
2849				max = (typeof params == 'object' && typeof params.max != 'undefined') ? params.max : 99999;
2850			console.log('require_at_least', min, selected, max);
2851			return (selected >= min && selected <= max);
2852		}
2853	}
2854};
2855
2856jQuery.fn.fadeThenSlideToggle = function (speed, easing, callback) {
2857	if (this.is(":hidden")) {
2858		return this.slideDown(speed, easing).fadeTo(speed, 1, easing, callback);
2859	} else {
2860		return this.fadeTo(speed, 0, easing).slideUp(speed, easing, callback);
2861	}
2862};
2863
2864jQuery.fn.toClipboard = function () {
2865	var element = this[0];
2866	var selection = window.getSelection();
2867	var range = document.createRange();
2868	range.selectNodeContents(element);
2869	selection.removeAllRanges();
2870	selection.addRange(range);
2871	try {
2872		var successful = document.execCommand('copy');
2873		selection.removeAllRanges();
2874		return successful;
2875	} catch (err) {
2876		return false;
2877	}
2878}
2879
2880SYM.CLIPBOARD = {
2881	// copy text from DOM element via unsecure mode (non https)
2882	unsecuredCopyToClipboard: (text) => {
2883		const textArea = document.createElement("textarea");
2884		textArea.value = text;
2885		document.body.appendChild(textArea);
2886		textArea.select();
2887		try {
2888			document.execCommand('copy');
2889		} catch (err) {
2890			console.error('Unable to copy to clipboard', err);
2891		}
2892		document.body.removeChild(textArea);
2893	},
2894
2895	copyTextToClipboard: async (text) => {
2896		// call right method depending on secure context
2897		(window.isSecureContext && navigator.clipboard) ?
2898			await navigator.clipboard.writeText(text) :
2899			SYM.CLIPBOARD.unsecuredCopyToClipboard(text);
2900	},
2901
2902	// copy code to clipboard
2903	copyToClipboard: async (htmlElement) => {
2904		const content = htmlElement.value || '';
2905
2906		SYM.CLIPBOARD.copyTextToClipboard(text);
2907
2908		htmlElement.value = htmlElement.dataset.copyText;
2909		setTimeout(() => (htmlElement.value = content), 2000);
2910	},
2911}
2912
2913SYM.SERVICES = $.extend({}, (function ($) {
2914	var BASIC_SERVICES = {
2915		get: function (url, params, cb) {
2916			$.getJSON(url, params, function (response) {
2917				var data = null;
2918				var error = null;
2919				if (response && response.stat == 'OK') {
2920					data = response.data;
2921				} else if (response && response.stat == 'WARNING') {
2922					data = response;
2923				} else if (typeof response.msg !== 'undefined') {
2924					if (typeof msg[response.msg] !== 'undefined') {
2925						error = msg[response.msg];
2926					} else {
2927						error = response.msg;
2928					}
2929				} else if (typeof _msg == 'function' && _msg(response.msg)) {
2930					error = _msg(response.msg);
2931				} else {
2932					error = msg.SERVER_ERROR;
2933				}
2934				if (error && typeof response.errors !== 'undefined') {
2935					data = response.errors;
2936				}
2937				if (typeof cb == 'function') {
2938					return cb(error, data);
2939				}
2940			}).fail(function (jqXHR, textStatus, errorThrown) {
2941				if (typeof cb == 'function') {
2942					return cb(
2943                        errorThrown,
2944                        {
2945                            jqXHR: jqXHR,
2946                            textStatus: textStatus,
2947                            errorThrown: errorThrown,
2948                            requestId: jqXHR.getResponseHeader('x-symposium-unique-request-id')
2949                        }
2950                    );
2951				}
2952			});
2953		},
2954		post: function (url, params, cb) {
2955			$.post(url, params, function (response) {
2956				var data = null;
2957				var error = null;
2958				if (response && response.stat == 'OK') {
2959					data = response.data;
2960				} else if (response && response.stat == 'WARNING') {
2961					data = response;
2962				} else if (typeof response.msg !== 'undefined') {
2963					if (typeof msg[response.msg] !== 'undefined') {
2964						error = msg[response.msg];
2965					} else {
2966						error = response.msg;
2967					}
2968				} else if (typeof _msg == 'function' && _msg(response.msg)) {
2969					error = _msg(response.msg);
2970				} else {
2971					error = msg.SERVER_ERROR;
2972				}
2973				if (error && typeof response.errors !== "undefined") {
2974					data = response.errors;
2975				}
2976				if (typeof cb == 'function') {
2977					return cb(error, data);
2978				}
2979			}, 'json').fail(function (jqXHR, textStatus, errorThrown) {
2980				if (typeof cb == 'function') {
2981					return cb(
2982                        errorThrown,
2983                        {
2984                            jqXHR: jqXHR,
2985                            textStatus: textStatus,
2986                            errorThrown: errorThrown,
2987                            requestId: jqXHR.getResponseHeader('x-symposium-unique-request-id')
2988                        }
2989                    );
2990				}
2991			});
2992		},
2993		put: function(url, data, callback, type) {
2994
2995			if ( $.isFunction(data) ){
2996				type = type || callback,
2997					callback = data,
2998					data = {}
2999			}
3000
3001			return $.ajax({
3002				url: url,
3003				type: 'PUT',
3004				success: callback,
3005				data: data,
3006				contentType: type
3007			});
3008		},
3009		delete: function(url, data, callback, type) {
3010
3011			if ( $.isFunction(data) ){
3012				type = type || callback,
3013					callback = data,
3014					data = {}
3015			}
3016			return $.ajax({
3017				url: url,
3018				type: 'DELETE',
3019				success: callback,
3020				data: data,
3021				contentType: type
3022			});
3023		},
3024
3025		// fetch implementation
3026		fetch: {
3027
3028			_common: {
3029				isJson: (res) => res.headers.get('Content-Type').match(/^application\/json/),
3030				getJsonResponse: (res, success, reject) => {
3031					res.json().then(success).catch(err => {
3032						console.error('Reading json error', err);
3033						reject(err.message);
3034					});
3035				},
3036				downloadFile: (res, success, reject) => {
3037					res.blob().then(blob => {
3038						const headerContentDisposition = res.headers.get('Content-Disposition');
3039						if (!headerContentDisposition) return reject('SERVER_ERROR');
3040						const serverFilename = headerContentDisposition.match(/filename="(.+)"/)[1];
3041						const theFilename = serverFilename || 'file';
3042						SYM.onDownloadBlob(blob, theFilename);
3043						success({
3044							blob: blob,
3045							filename: theFilename
3046						});
3047					}).catch(err => {
3048						console.error('Error in blob', err);
3049						return reject(err);
3050					});
3051				},
3052				getUrlObject: function (url, base) {
3053					try {
3054						return new URL(url, base);
3055					} catch (err) {
3056						if (!base) {
3057							return this.getUrlObject(url, window.location.origin);
3058						}
3059					}
3060				},
3061				default: (url, config, success, reject) => {
3062					fetch(url, config)
3063					.then((res) => {
3064						if (!res.ok) {
3065							return reject({
3066								status: res.status,
3067								statusText: res.statusText,
3068								supportHeader: res.headers.get('x-symposium-server-error')
3069							});
3070						}
3071						if (SYM.SERVICES.fetch._common.isJson(res)) {
3072							SYM.SERVICES.fetch._common.getJsonResponse(res, success, reject);
3073						} else {
3074							SYM.SERVICES.fetch._common.downloadFile(res, success, reject);
3075						}
3076					})
3077					.catch(err => {
3078						console.error(err);
3079						reject(err.message)
3080					});
3081				},
3082				defaultError: function (res, reject) {
3083					console.error('default error', res);
3084					const baseError = {
3085						status: res.status,
3086						statusText: res.statusText,
3087						supportHeader: res.headers.get('x-symposium-server-error')
3088					};
3089					res.json().then((json) => {
3090						let statusText = json.scalar || res.statusText;
3091						const error = json?.error || '';
3092						const msg = json?.msg || '';
3093						return reject(Object.assign({}, baseError, { statusText: statusText, error, msg }));
3094					}).catch(err => {
3095						console.error(err);
3096						return reject(baseError);
3097					});
3098				},
3099				defaultSuccess: function (res, success, reject) {
3100					if (SYM.SERVICES.fetch._common.isJson(res)) {
3101						SYM.SERVICES.fetch._common.getJsonResponse(res, success, reject);
3102					} else {
3103						SYM.SERVICES.fetch._common.downloadFile(res, success, reject);
3104					}
3105				},
3106				default: (url, config, success, reject) => {
3107					fetch(url, config)
3108						.then((res) => {
3109							if (!res.ok) {
3110								SYM.SERVICES.fetch._common.defaultError(res, reject);
3111							} else {
3112								SYM.SERVICES.fetch._common.defaultSuccess(res, success, reject);
3113							}
3114						})
3115						.catch(err => {
3116							console.error(err);
3117							reject(err.message)
3118						});
3119				},
3120			},
3121
3122			get: function (url, qs_params) {
3123				const urlObject = this._common.getUrlObject(url);
3124				if (qs_params) urlObject.search = new URLSearchParams(qs_params).toString();
3125				const config = {method: 'GET'};
3126				const defaultRun = this._common.default;
3127				return new Promise((success, reject) => {
3128					defaultRun(urlObject, config, success, reject);
3129				});
3130			},
3131
3132			delete: function (url, qs_params) {
3133				const urlObject = this._common.getUrlObject(url);
3134				if (qs_params) urlObject.search = new URLSearchParams(qs_params).toString();
3135				const config = {method: 'DELETE'};
3136				const defaultRun = this._common.default;
3137				return new Promise((success, reject) => {
3138					defaultRun(urlObject, config, success, reject);
3139				});
3140			},
3141
3142			post: function (url, params, qs_params, json='json') {
3143				const urlObject = this._common.getUrlObject(url);
3144				let config = { method: 'POST' };
3145				if (qs_params) urlObject.search = new URLSearchParams(qs_params).toString();
3146				if (params && json) {
3147					config = Object.assign({}, config, {
3148						headers: { "Content-Type": "application/json" },
3149						body: JSON.stringify(params)
3150					});
3151				} else if (params) {
3152					config = Object.assign({}, config, {
3153						headers: { "Content-Type": "application/x-www-form-urlencoded" },
3154						body: new URLSearchParams(params)
3155					});
3156				}
3157				const defaultRun = this._common.default;
3158				return new Promise((success, reject) => {
3159					defaultRun(urlObject, config, success, reject);
3160				});
3161			},
3162
3163			put: function (url, params, qs_params, json='json') {
3164				const urlObject = this._common.getUrlObject(url);
3165				let config = { method: 'PUT' };
3166				if (qs_params) urlObject.search = new URLSearchParams(qs_params).toString();
3167				if (params && json) {
3168					config = Object.assign({}, config, {
3169						headers: { "Content-Type": "application/json" },
3170						body: JSON.stringify(params)
3171					});
3172				} else if (params) {
3173					config = Object.assign({}, config, { body: params });
3174				}
3175				const defaultRun = this._common.default;
3176				return new Promise((success, reject) => {
3177					defaultRun(urlObject, config, success, reject);
3178				});
3179			},
3180
3181		}
3182
3183	}
3184	return BASIC_SERVICES;
3185})(jQuery));
3186
3187SYM.EXPORT_FORMAT = $.extend({}, (function ($) {
3188
3189	const DOM = {
3190		MODAL: {
3191			SELECTOR: '.div_export_formats'
3192		},
3193	};
3194
3195	const METHODS = {
3196		private: {
3197			openModal: function (selector, type = '', modalOptions = null, success, reject) {
3198				const $modalTemplate = $(selector);
3199				if ($modalTemplate.length) {
3200					// This can be done without detaching the modal and using $modal.html() as parameter to msgbox.
3201					// Is done this way to keep the styles of the main class, using the same object as is done in other views.
3202					$('body').append($modalTemplate.detach());
3203					$modal = msgbox($modalTemplate, type, modalOptions);
3204					if ($modal) {
3205						METHODS.private.initModalHandlers($modal, success, reject);
3206					} else {
3207						reject();
3208					}
3209				}
3210			},
3211			initModalHandlers: function ($modal, success, reject) {
3212
3213				$('a', $modal).off('click').on('click', function(ev){
3214					ev.preventDefault();
3215					ev.stopPropagation();
3216
3217					const $target = $(ev.target);
3218					const data = $target.data();
3219					const format = data?.format || 'excel';
3220
3221					success(format);
3222					msgbox_close($modal);
3223
3224					return false;
3225				});
3226
3227				$('.close', $modal).off('click').on('click', function (ev) {
3228					ev.preventDefault();
3229					ev.stopPropagation();
3230					reject();
3231					msgbox_close($modal);
3232					return false;
3233				});
3234
3235			}
3236		},
3237		public: {
3238			select: function (modalType='', modalOptions=null) {
3239				return new Promise((success, reject) => {
3240					METHODS.private.openModal(DOM.MODAL.SELECTOR, modalType, modalOptions, success, reject);
3241				});
3242			}
3243		}
3244	};
3245
3246	return METHODS.public;
3247
3248})(jQuery));

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.