PageSourceSearch

https://www.gsscloud.com/components/com_flexicontent/assets/js/flexi-lib.js?aa478795d00eb0921a32bca1a7c863b5

js gsscloud.com collected 2026-09-30 22:00:06 UTC 83,677 bytes, 2,454 lines download raw bytes

1
2	window.fc_init_hide_dependent = 1;
3	window.fc_refreshing_dependent = 0;
4	window.fc_dependent_params = {};
5	window.fc_cascade_field_funcs = {};
6	window.fc_Dialogs = {};
7
8	function fc_getAutoSizePos(winwidth, winheight, params)
9	{
10		var maxWidth  = typeof winwidth === 'undefined'  ? 0 : winwidth;
11		var maxHeight = typeof winheight === 'undefined' ? 0 : winheight;
12
13		var paddingW = (!!params && !!params.paddingW ? params.paddingW : 70);
14		var paddingH = (!!params && !!params.paddingH ? params.paddingH : 120);
15
16		var visibleWidth  = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
17		var visibleHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
18
19		var w = maxWidth  ? maxWidth  : visibleWidth - paddingW;
20		var h = maxHeight ? maxHeight : visibleHeight - paddingH;
21
22		// Add fixed scrolling class and ... also add auto-width / auto-height classes so dimensions are treated as MAX values
23		params.dialogClass = typeof params.dialogClass !== 'undefined'  ?  params.dialogClass  :  'fc-fixed-dialog';
24		params.dialogClass += ' fc-autow-dialog';
25		params.dialogClass += ' fc-autoh-dialog';
26
27		if (visibleWidth < 600 || visibleHeight < 600)
28		{
29			params.winwidth  = w  > (visibleWidth - 10)  ? (visibleWidth - 10)  : w;
30			params.winheight = h  > (visibleHeight - 10) ? (visibleHeight - 10) : h;
31		}
32		else
33		{
34			params.winwidth  = w  > (visibleWidth - paddingW)  ? (visibleWidth - paddingW)  : w;
35			params.winheight = h  > (visibleHeight - paddingH) ? (visibleHeight - paddingH) : h;
36		}
37		//window.console.log ('winwidth  : ' + params.winwidth  + ', winheight : ' + params.winheight );
38
39		params.winleft = (visibleWidth  - params.winwidth)  / 2 - 5;
40		params.wintop  = (visibleHeight - params.winheight) / 2 - 5;
41		//window.console.log ('winleft : ' + params.winleft + ', wintop : ' + params.wintop);
42
43		return params;
44	}
45
46
47	function fc_loadImagePreview(input_id, img_id, msg_id, thumb_w, thumb_h, nonimg_mssg)
48	{
49		var nonimg_mssg = typeof nonimg_mssg !== 'undefined' ? nonimg_mssg : '';
50		var input = document.getElementById(input_id);
51		var msg_box = document.getElementById(msg_id);
52		var _msg = '';
53
54		var input_files = input.files;
55
56		if (input_files && input_files[0])
57		{
58			var imageType = /image.*/;
59
60			if (!input_files[0].type.match(imageType))
61			{
62		  	if (nonimg_mssg=='-1') _msg = '';
63		  	else if (nonimg_mssg=='-2') _msg = input.value;
64		  	else if (nonimg_mssg=='0' || nonimg_mssg=='') _msg = (nonimg_mssg!='' ? nonimg_mssg : input.value+' <br/> '+Joomla.JText._('FLEXI_NOT_AN_IMAGE_FILE'));
65		  	jQuery('#'+img_id).hide();
66		  }
67			else
68			{
69		  	if (nonimg_mssg=='-2' || nonimg_mssg=='0') _msg = input.value;
70		  	else _msg = '';
71				var reader = new FileReader();
72				reader.onload = function (e) {
73					var img = jQuery('#'+img_id);
74					img.attr('src', e.target.result);
75					if (thumb_w)  parseInt(thumb_w) ? img.width(thumb_w)  : img.css('width',  thumb_w);
76					if (thumb_h)  parseInt(thumb_h) ? img.height(thumb_h) : img.css('height', thumb_h);
77					img.show();
78				};
79				reader.readAsDataURL(input_files[0]);
80			}
81		}
82		msg_box.nodeName == 'INPUT' ? msg_box.value = input.value : msg_box.innerHTML = input.value;
83	}
84
85
86	// Display content in modal popup
87	function fc_showAsDialog(obj, winwidth, winheight, closeFunc, params)
88	{
89		params = typeof params !== 'undefined' ? params : {};
90		params = fc_getAutoSizePos(winwidth, winheight, params);
91
92		// Make it jQuery object if it is not already
93		obj = jQuery(obj);
94
95		// Get close function
96		closeFunc = typeof closeFunc !== 'undefined' && closeFunc ? closeFunc : 0;
97		var keepPlace = !!params.keepPlace;
98		var visibleOnClose = !!params.visibleOnClose;
99
100		// Because allowing moving modal to be moved out of form (under body), we need to set form ATTRIBUTE to form, for any form elements inside it
101		if (!keepPlace)
102		{
103			var form_fields = obj.find('textarea, select, input, button').toArray();
104			for ( var i = 0, l = form_fields.length; i < l; i++ )
105			{
106				// Get field type / tag name
107				var $el = jQuery(form_fields[i]);
108				var form = form_fields[i].form;
109				if (form && form.id && !$el.attr('form'))
110				{
111					$el.attr('form', form.id);
112				}
113			}
114		}
115
116		var parent = obj.parent();
117		var theDialog = obj.dialog({
118			title: params.title,
119			closeOnEscape: (typeof params.closeOnEscape !== 'undefined'  ?  params.closeOnEscape  :  true),
120			resize: false,
121			autoOpen: false,
122			width: params.winwidth,
123			height: params.winheight,
124			//position: { at: 'center' },
125			modal: (typeof params.modal !== 'undefined'  ?  params.modal  :  true),
126			dialogClass: params.dialogClass,
127			icons: {
128				primary: "ui-icon-heart"
129			},
130			buttons: {
131				/*'Close': function() { jQuery(this).dialog('close'); }*/
132			},
133			open: function(ev, ui){
134				// Fix for Pagebuilder CK modifying not-owned modals
135				jQuery('.ui-widgetck .ui-dialog-titlebar-close > .ui-icon').removeClass('ui-widgetck-overlay').css('top', '0').css('left', '0');
136				jQuery('.ui-widgetck').removeClass('ui-widgetck').addClass('ui-widget fc-no-scroll');
137				jQuery('.ui-widgetck-content').removeClass('ui-widgetck-content').addClass('ui-widget-content');
138				jQuery('.ui-widgetck-header').removeClass('ui-widgetck-header').addClass('ui-widget-header');
139				jQuery('.ui-widgetck-overlay').removeClass('ui-widgetck-overlay').addClass('ui-widget-overlay').css('z-index', '2049').css('position', 'fixed');
140
141				// Close on click outside of modal popup
142				if (typeof params.closeOnOutClick !== 'undefined'  ?  params.closeOnOutClick  :  true) {
143					jQuery('.ui-widget-overlay, .pac-container').bind('click', function () {
144						obj.dialog('close');
145					});
146				}
147			},
148			// Clear contents after dialog closes
149			close: function(ev, ui)
150			{
151				// Allow parent document to scroll again
152				var bodyTag = jQuery(this).closest('body');
153				var fc_modal_count = parseInt(bodyTag.data('fc_modal_count'));
154				bodyTag.data('fc_modal_count', --fc_modal_count);
155
156				if (fc_modal_count == 0)
157				{
158					bodyTag.removeClass('fc-no-scroll');
159				}
160
161				// Destroy the dialog completely, this is needed because we have moved the content back into its proper position
162				jQuery(this).dialog('destroy');
163
164				// Allow CSS class to apply
165				if (visibleOnClose) obj.css({'height': '', 'width': '', 'display': '', 'visibility': ''});
166
167				// Finalize by doing the closing operation
168				if (typeof closeFunc === 'function') closeFunc(obj);
169				else if (closeFunc == 1) window.location.reload(false);
170				else if (closeFunc == 0) ;
171				else alert('Unknown action'+closeFunc);
172			}
173		})
174		.dialog('widget').next('.ui-widget-overlay').css('background', 'gray');  // Add an overlay
175
176		// Manually move the dialog content back into its proper position, (the extra dialog container will be destroyed on dialog close)
177		if (keepPlace) obj.parent().appendTo(parent);
178
179		// Force manual position of dialog
180		obj.parent().css({ 'left': params.winleft+'px', 'width': params.winwidth+'px', 'top': params.wintop+'px'});
181
182		// Open the dialog manually
183		theDialog = obj.dialog('open');
184
185		// Add modal's data to the data of the parent of the HTML container: max-width, max-height, params, dialog reference
186		theDialog.data('fc_dialog_params', params);
187		theDialog.data('fc_dialog_maxwidth', winwidth);
188		theDialog.data('fc_dialog_maxheight', winheight);
189		obj.parent().data('fc_dialog', theDialog);
190
191		// Stop scrolling of parent document
192		var bodyTag = obj.closest('body');
193		var fc_modal_count = !! bodyTag.data('fc_modal_count') ? parseInt(bodyTag.data('fc_modal_count')) : 0;
194		bodyTag.data('fc_modal_count', ++fc_modal_count);
195		bodyTag.addClass('fc-no-scroll');
196
197		// Return a reference of the dialog to the caller
198		return theDialog;
199	}
200
201
202	// Load given URL in an open dialog
203	function fc_showDialog(url, tagid, no_iframe, winwidth, winheight, closeFunc, params)
204	{
205		params = typeof params !== 'undefined' ? params : {};
206		params = fc_getAutoSizePos(winwidth, winheight, params);
207
208		var loadFunc = typeof params.loadFunc !== 'undefined' && params.loadFunc ? params.loadFunc : false;
209
210		// Get close function
211		var closeFunc = typeof closeFunc !== 'undefined' && closeFunc ? closeFunc : 0;
212
213		// Get container creating it if it does not exist
214		var container = jQuery('#'+tagid);
215		if (!container.length)
216		{
217			container = jQuery('<div id="'+tagid+'"></div>').appendTo(document.body);
218		}
219
220		// Get loading animation tag, creating it if it does not exist, (re)showing if it is exists
221		var loading = jQuery('#'+tagid+'_loading');
222		if (!loading.length)
223		{
224			loading = jQuery('<div id="'+tagid+'_loading" class="fc_loading_msg" style="position:absolute; background-color:transparent;">loading...</div>');
225			container.prepend(loading);
226		}
227		else loading.show();
228
229		// Add the iframe
230		var iframe;
231		if (!no_iframe)
232		{
233			iframe = jQuery('<iframe id="'+tagid+'_frame" style="visibility:hidden; width:100%; height:100%; border:0; margin:0; padding:0;" src=""></iframe>');
234			container.append(iframe);
235			iframe.on('load', function() {
236				// Show retrieved contents and hide the loading animation
237				iframe.show().css('visibility', 'visible');
238				loading.hide();
239
240				// Remove unneeded scroll bar inside iframe document, new browser security disallows this ?
241				//iframe.contents().find('body').css({ 'height': 'unset' });
242			});
243		}
244
245		// Contaner CSS depending on usage of iframe
246		no_iframe ?
247			container.css('overflow', 'scroll') :
248			container.css('overflow', 'hidden').css('padding', '0') ;
249
250		// Initialize popup container
251		container.dialog({
252			title: params.title,
253			closeOnEscape: (typeof params.closeOnEscape !== 'undefined'  ?  params.closeOnEscape  :  false),
254			autoOpen: false,
255			width: params.winwidth,
256			height: params.winheight,
257			modal: (typeof params.modal !== 'undefined'  ?  params.modal  :  true),
258			//position: { at: 'center' },
259			dialogClass: params.dialogClass,
260			// Load contents (url) when dialog opens
261			open: function(ev, ui){
262				no_iframe ?
263					container.load(url) :
264					jQuery('#'+tagid+'_frame').attr('src', url);
265
266				// Fix for Pagebuilder CK modifying not-owned modals
267				jQuery('.ui-widgetck .ui-dialog-titlebar-close > .ui-icon').removeClass('ui-widgetck-overlay').css('top', '0').css('left', '0');
268				jQuery('.ui-widgetck').removeClass('ui-widgetck').addClass('ui-widget fc-no-scroll');
269				jQuery('.ui-widgetck-content').removeClass('ui-widgetck-content').addClass('ui-widget-content');
270				jQuery('.ui-widgetck-header').removeClass('ui-widgetck-header').addClass('ui-widget-header');
271				jQuery('.ui-widgetck-overlay').removeClass('ui-widgetck-overlay').addClass('ui-widget-overlay').css('z-index', '2049').css('position', 'fixed');
272
273				// Close on click outside of modal popup
274				jQuery('.ui-widget-overlay, .pac-container').bind('click', function(){
275					container.dialog('close');
276				});
277			},
278			// Clear contents after dialog closes
279			close: function(ev, ui)
280			{
281				// Allow parent document to scroll again
282				var bodyTag = jQuery(this).closest('body');
283				var fc_modal_count = parseInt(bodyTag.data('fc_modal_count'));
284				bodyTag.data('fc_modal_count', --fc_modal_count);
285
286				if (fc_modal_count == 0)
287				{
288					bodyTag.removeClass('fc-no-scroll');
289				}
290
291				// Remove container / iframe contents (we re-use it)
292				no_iframe  ?  container.html('')  :  jQuery('#'+tagid+'_frame').remove();
293
294				// Finalize by doing the closing operation
295				if (typeof closeFunc === 'function') closeFunc();
296				else if (closeFunc == 1) window.location.reload(false);
297				else if (closeFunc == 0) ;
298				else alert('Unknown action'+closeFunc);
299			}
300		});
301
302		// Force manual position of dialog
303		container.parent().css({ 'left': params.winleft+'px', 'width': params.winwidth+'px', 'top': params.wintop+'px'});
304
305		// Open the dialog manually
306		var theDialog = container.dialog('open');
307		fc_Dialogs[tagid] = theDialog;
308
309		// Add modal's data to the data of the parent of the HTML container: max-width, max-height, params, dialog reference
310		theDialog.data('fc_dialog_params', params);
311		theDialog.data('fc_dialog_maxwidth', winwidth);
312		theDialog.data('fc_dialog_maxheight', winheight);
313		container.parent().data('fc_dialog', theDialog);
314
315		// Attach on-load function to the iframe
316		if (!no_iframe && loadFunc)
317		{
318			jQuery('#'+tagid).find('iframe').first().on('load', function(e, data) {
319				loadFunc( container );
320			});
321		}
322
323		// Stop scrolling of parent document
324		var bodyTag = container.closest('body');
325		var fc_modal_count = !! bodyTag.data('fc_modal_count') ? parseInt(bodyTag.data('fc_modal_count')) : 0;
326		bodyTag.data('fc_modal_count', ++fc_modal_count);
327		bodyTag.addClass('fc-no-scroll');
328
329		// Return a reference of the dialog to the caller
330		return theDialog;
331	}
332
333
334	// Close given dialog
335	function fc_closeDialog(name)
336	{
337		if (!!fc_Dialogs[name])
338		{
339			fc_Dialogs[name].dialog('close');
340		}
341		return false;
342	}
343
344
345	// Scroll into view
346	function fc_scrollIntoView (elem, smooth)
347	{
348		var cTop = elem.offset().top;
349		var cHeight = elem.outerHeight(true);
350		var windowTop = jQuery(window).scrollTop();
351		var visibleHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
352		var top_extra = 90;
353		var bottom_extra = 80;
354
355		//window.console.log ('cTop: ' + cTop + ' , windowTop: ' + windowTop);
356		//window.console.log ('cHeight: ' + cHeight + ' , visibleHeight: ' + visibleHeight);
357		if (cTop - top_extra < windowTop) {
358			if (smooth) {
359				jQuery('html, body').animate({scrollTop: cTop - top_extra}, 400);
360			} else {
361				jQuery('html, body').scrollTop(cTop - top_extra);
362			}
363		} else if (cTop + cHeight + bottom_extra > windowTop + visibleHeight) {
364			if (smooth) {
365				jQuery('html, body').animate({scrollTop: cTop - visibleHeight + cHeight + bottom_extra}, 400);
366			} else {
367				jQuery('html, body').scrollTop(cTop - visibleHeight + cHeight + bottom_extra);
368			}
369		}
370	}
371
372
373	function fc_findFormDependencies(el, toggleParent, toggleParentSelector, field)
374	{
375		var seton_list = el.data('_seton_list');   // (selector comma list) show elements by selector
376		var setoff_list= el.data('_setoff_list');  // (selector comma list) hide elements by selector
377		var refsh_list = el.data('_refsh_list');   // (selector comma list) trigger change on elements
378
379		var show_list  = el.data('_show_list');    // (classnames comma list) grant  display dependency to elements (container shown  when dependencies are zero)
380		var hide_list  = el.data('_hide_list');    // (classnames comma list) revoke display dependency to elements (container hidden when depen
380dencies non-zero)
381		var force_list = el.data('_force_list');   // (classnames comma list) show regardless of display dependencies (this is non-persistent -and- element's dependencies are not cleared)
382
383		var fcreadonly = el.data('_fcreadonly');   // (JSON format, element name : value) set readonly property of elements, 1:ON , 2:OFF
384		var fcconfigs  = el.data('_fcconfigs');    // (JSON format, element name : value) set value of elements
385
386		var _d;
387		if (!seton_list) {
388			seton_list = {};
389			seton_list[0] = el.attr('data-seton_list')  ? el.attr('data-seton_list')  : null;
390			el.data('_seton_list', seton_list);
391		}
392		if (!setoff_list) {
393			setoff_list = {};
394			setoff_list[0] = el.attr('data-setoff_list')  ? el.attr('data-setoff_list')  : null;
395			el.data('_setoff_list', setoff_list);
396		}
397		if (!refsh_list) {
398			refsh_list = {};
399			refsh_list[0] = el.attr('data-refsh_list')  ? el.attr('data-refsh_list')  : null;
400			el.data('_refsh_list', refsh_list);
401		}
402
403		if (!fcreadonly) {
404			var fcreadonly = el.attr('data-fcreadonly');
405			if (fcreadonly) {
406				fcreadonly = fcreadonly.replace(/\'/g, '"');
407				fcreadonly = jQuery.parseJSON(fcreadonly);
408			}
409			el.data('_fcconfigs', fcconfigs);
410		}
411		if (!fcconfigs) {
412			var fcconfigs = el.attr('data-fcconfigs');
413			if (fcconfigs) {
414				fcconfigs = fcconfigs.replace(/\'/g, '"');
415				fcconfigs = jQuery.parseJSON(fcconfigs);
416			}
417			el.data('_fcconfigs', fcconfigs);
418		}
419
420		if (!show_list) {
421			show_list = {};
422			_d  = el.attr('data-show_list')  ? el.attr('data-show_list').split(',')  : Array();
423			for (var i = 0; i<_d.length; i++) show_list[_d[i].trim()] = 1;
424			el.data('_show_list', show_list);
425		}
426		if (!hide_list) {
427			hide_list = {};
428			_d = el.attr('data-hide_list')  ? el.attr('data-hide_list').split(',')  : Array();
429			for (var i = 0; i<_d.length; i++) hide_list[_d[i].trim()] = 1;
430			el.data('_hide_list', hide_list);
431		}
432		if (!force_list) {
433			force_list = {};
434			_d = el.attr('data-force_list') ? el.attr('data-force_list').split(',') : Array();
435			for (var i = 0; i<_d.length; i++) force_list[_d[i].trim()] = 1;
436			el.data('_force_list', force_list);
437		}
438
439		var toBeUpdated = Array();
440		var u = 0;
441		jQuery.each( force_list, function( cname, val ) {
442			if (!fc_dependent_params.hasOwnProperty(cname))  fc_dependent_params[cname] = Array();
443			fc_dependent_params[cname][fc_dependent_params[cname].length] = el;
444
445			if (val) {
446				jQuery('.'+cname).each(function( index ) {
447					var c = jQuery(this);
448					c.attr('data-fc_forced_display', '1');
449					toBeUpdated[u++] = c;
450				});
451			}
452		});
453		jQuery.each( hide_list, function( cname, val ) {
454			if (!fc_dependent_params.hasOwnProperty(cname))  fc_dependent_params[cname] = Array();
455			fc_dependent_params[cname][fc_dependent_params[cname].length] = el;
456
457			if (val) {
458				jQuery('.'+cname).each(function( index ) {
459					var c = jQuery(this);
460					var dlist = c.data('fc_depend_list');
461					if (!dlist) dlist = {};
462					if (dlist.hasOwnProperty(cname)) dlist[cname]++; else dlist[cname]= 1;
463					c.data('fc_depend_list', dlist);
464					if (c.attr('data-fc_forced_display')!='1') {
465						toBeUpdated[u++] = c;
466					}
467				});
468			}
469		});
470
471		// Display fields / enable input on them ( removeAttr + css ),  force them to update display ( trigger:click ), and to validate new value ( trigger:blur )
472		if (fcconfigs) for (var fieldname in fcconfigs) {
473			if (fcconfigs.hasOwnProperty(fieldname)) {
474				var jf_field = jQuery('#'+'jform_attribs_'+fieldname); // first try 'jform_attribs'
475				if (!jf_field.length) {
476					jf_field = jQuery('#'+'jform_params_'+fieldname);  // then try 'jform_params'
477					if (!jf_field.length) {
478						jf_field = jQuery('#'+'jform_'+fieldname);  // then try just 'jform_'
479						if (!jf_field.length) continue;
480					}
481				}
482				jf_field = jf_field.first();
483
484				if (jf_field.is('fieldset')) {
485					jf_field.find('input').removeAttr('disabled').removeAttr('readonly');
486					jf_field.find('label').removeAttr('disabled').css('pointer-events', 'auto').css('opacity', '1');
487					jf_field.find(':input[value="'+fcconfigs[fieldname]+'"]').next().trigger('click').trigger('blur');
488				} else if (jf_field.is('select')) {
489
490					jf_field.removeAttr('disabled').removeAttr('readonly');
491					if (jf_field.prop('multiple')) {
492						jf_field.val('').trigger('change');
493						jQuery.each(fcconfigs[fieldname].split(','), function(i, val){
494							jf_field.find('option[value=\"' + val.trim() + '\"]').attr('selected', 'selected');
495						});
496					} else {
497						jf_field.val(fcconfigs[fieldname]);
498					}
499					jf_field.trigger('change');
500
501				} else {
502					jf_field.removeAttr('disabled').removeAttr('readonly').val(fcconfigs[fieldname]).trigger('click').trigger('blur');
503				}
504			}
505		}
506
507		if (fcreadonly) for (var fieldname in fcreadonly) {
508			if (fcreadonly.hasOwnProperty(fieldname)) {
509				var jf_field = jQuery('#'+'jform_attribs_'+fieldname); // first try 'jform_attribs'
510				if (!jf_field.length) {
511					jf_field = jQuery('#'+'jform_params_'+fieldname);  // then try 'jform_params'
512					if (!jf_field.length) {
513						jf_field = jQuery('#'+'jform_'+fieldname);  // then try just 'jform_'
514						if (!jf_field.length) continue;
515					}
516				}
517				jf_field = jf_field.first();
518
519				if (fcreadonly[fieldname] && !field.hasClass('fccustom_revert')) {
520					if (jf_field.is('fieldset')) {
521						jf_field.find('input').attr('readonly', 'readonly');
522						jf_field.find('label').attr('disabled', true).css('pointer-events', 'n
522one').css('opacity', '0.65');
523					} else if (jf_field.is('select')) {
524						jf_field.attr('readonly', 'readonly').css('pointer-events', 'none').css('opacity', '0.85');
525					} else {
526						jf_field.attr('readonly', 'readonly').css('opacity', '0.85')
527					}
528				} else {
529					if (jf_field.is('fieldset')) {
530						jf_field.find('input').removeAttr('disabled');
531						jf_field.find('label').removeAttr('disabled').css('pointer-events', 'auto').css('opacity', '1');
532					}
533					else {
534						jf_field.removeAttr('readonly').css('pointer-events', 'auto').css('opacity', '1');
535					}
536				}
537			}
538		}
539
540		jQuery.each( show_list, function( cname, val ) {
541			if (!fc_dependent_params.hasOwnProperty(cname))  fc_dependent_params[cname] = Array();
542			fc_dependent_params[cname][fc_dependent_params[cname].length] = el;
543
544			if (val) {
545				jQuery('.'+cname).each(function( index ) {
546					var c = jQuery(this);
547					var dlist = c.data('fc_depend_list');
548					if (!dlist) dlist = {};
549					if (dlist.hasOwnProperty(cname)) dlist[cname]--; else dlist[cname]= -1;
550					c.data('fc_depend_list', dlist);
551					if (c.attr('data-fc_forced_display')!='1') {
552						toBeUpdated[u++] = c;
553					}
554				});
555			}
556		});
557
558		setTimeout(function(){
559			jQuery.each( setoff_list, function( i, selector ) {
560				if (selector) {
561					jQuery(selector).each(function( index ) {
562						var c = jQuery(this);
563						c.slideUp('fast');
564					});
565				}
566			});
567			jQuery.each( seton_list, function( i, selector ) {
568				if (selector) {
569					jQuery(selector).each(function( index ) {
570						var c = jQuery(this);
571						c.slideDown('fast');
572					});
573				}
574			});
575		}, !fc_init_hide_dependent ? 0 : 50);
576
577
578		if (!fc_init_hide_dependent) {
579			fc_applyFormDependencies(toBeUpdated, toggleParent, toggleParentSelector, 0);
580
581			if (!fc_refreshing_dependent) {
582				fc_refreshing_dependent = 1;
583
584				// Refresh needed dependencies
585				if (typeof refsh_list != 'string') jQuery.each( refsh_list, function( i, selector ) {
586					if (selector) {
587						jQuery(selector).each(function( index ) {
588							var c = jQuery(this);
589							if (c.is('select'))
590								c.trigger('fcform_run_deps');  // instead of change
591							else if (c.is('input[type="radio"]'))
592								c.closest('.fcform_toggler_element').find('input[type="radio"]:checked').trigger('fcform_run_deps');  // instead of click
593							else if (c.is('fieldset') && c.hasClass('radio'))
594								c.find('input[type="radio"]:checked').trigger('fcform_run_deps');  // instead of click
595						});
596					}
597				});
598			}
599			fc_refreshing_dependent = 0;
600		}
601
602		// Restore the form to select the element with value ''
603		if ( field.hasClass('fccustom_revert') && el.value!='' )
604		{
605			var currVal;
606			if ( field.is('fieldset') ) {
607				currVal = field.find('input[type="radio"]:checked').val();
608				if (currVal!='') {
609					field.find('input').attr('disabled', 'disabled');
610					field.find('label').attr('disabled', true).css('pointer-events', 'none').css('opacity', '0.65');
611				}
612			} else {
613				currVal = field.val();
614				if (currVal!='') field.attr('disabled', 'disabled').css('pointer-events', 'none').css('opacity', '0.85');
615			}
616
617			if (currVal!='') setTimeout(function(){
618				if (field.is('fieldset')) {
619					field.find('input').removeAttr('disabled').removeAttr('readonly');
620					field.find('label').removeAttr('disabled').css('pointer-events', 'auto').css('opacity', '1');
621					field.find(':input[value=""]').next().trigger('click');
622				} else {
623					field.removeAttr('disabled').removeAttr('readonly').css('pointer-events', 'auto').css('opacity', '1');
624					field.val('').trigger('click');
625				}
626			}, 200);
627		}
628
629		return toBeUpdated;
630	}
631
632
633	// Add toggling of dependent form elements
634	function fc_bindFormDependencies(container, toggleParent, toggleParentSelector)
635	{
636		var vTimeStart = new Date().getTime();
637
638		var toBeUpdated_ALL = Array();
639		var k = 0;
640
641		// Performance concern: use custom event
642
643		// Bind dependencies of select elements
644		jQuery(container+' select.fcform_toggler_element').on('change fcform_run_deps', function(e, data) {
645			var toBeUpdated = fc_findFormDependencies( jQuery('option:selected', this), toggleParent, toggleParentSelector, jQuery(this) );
646			for (var i = 0; i < toBeUpdated.length; i++) {
647				toBeUpdated_ALL[k++] = toBeUpdated[i];
648			}
649		});
650
651		// Bind dependencies of radio elements
652		jQuery(container+' .fcform_toggler_element input:radio').on('click fcform_run_deps', function(e, data) {
653			var toBeUpdated = fc_findFormDependencies( jQuery(this), toggleParent, toggleParentSelector, jQuery(this).parent('.fcform_toggler_element') );
654			for (var i = 0; i < toBeUpdated.length; i++) {
655				toBeUpdated_ALL[k++] = toBeUpdated[i];
656			}
657		});
658
659		// *** Update the form
660		jQuery('form').data('skip_validation' ,1);
661
662		// Enqueue dependencies by triggering change on select elements
663		jQuery('form select.fcform_toggler_element').trigger('fcform_run_deps');
664		// Enqueue dependencies by triggering change on radio elements
665		jQuery('form .fcform_toggler_element input[type="radio"]:checked').trigger('fcform_run_deps');
666
667		//alert(toBeUpdated_ALL.length);
668		// Apply form changes according to dependencies
669		fc_applyFormDependencies(toBeUpdated_ALL, toggleParent, toggleParentSelector, 1);
670
671		// Clear this flag to indicate that form initialization is done
672		fc_init_hide_dependent = 0;
673
674		jQuery('form').data('skip_validation', null);
675
676		/*setTimeout(function(){ }, 20);*/
677		if (!!Joomla.fc_debug) window.console.log( 'fc_bindFormDependencies() time: ' + ((new Date())  - vTimeStart) + ' ms');
678	}
679
680
681	/* Attach form validation to inner elements of the given selector */
682	function fc_validationAttach(sel)
683	{
684		document.formvalidator.attachToForm(null, sel);
685	}
686
687
688	/* Set children to have same minimum height e.g. floated elements that need to wrap properly */
689	function fc_setEqualHeights(box, property, delay)
690	{
691		property = !!property ? property : 0;  // 0: both height + min-height, 1: height, 2: min-height
692		delay = !!delay ? delay : 20;
693		var elements = box.children();
694		elements.css({'height': '', 'min-height': ''});
695		setTimeout(function() {
696			var max_h  = Number.NEGATIVE_INFINITY;
697			var max_mh = Number.NEGATIVE_INFINITY;
698
699			jQuery.each(elements, function(index, item) {
700				if (property==1 || property==0)
701				{
702					if (jQuery(item).height() > max_h) max_h = jQuery(item).height();
703				}
704				if (property==2 || property==0)
705				{
706					if (jQuery(item).outerHeight() > max_mh) max_mh = jQuery(item).outerHeight();
707				}
708			});
709
710			var val_h  = '' + max_h + 'px';
711			var val_mh = '' + max_mh + 'px';
712			elements.each(function(i, v) {
713				if (property==1 || property==0)
714				{
715					if (v.style.height != val_h) v.style.height = val_h;
716				}
717				if (property==2 || property==0)
718				{
719					if (v.style.minHeight != val_mh) v.style.minHeight = val_mh;
720				}
721			});
722		}, delay);
723	}
724
725
726	function fc_toggleCompactValuesView(el, field_values_box, btn_class)
727	{
728		btn_class = btn_class || 'btn-info';
729		// Get field value containers
730		var rows = field_values_box.find('.fcfieldval_container');
731		var el = jQuery(el);
732
733		// Set expanding flag and expand or compact properties
734		if (el.prop('tagName')=='INPUT')
735		{
736			var expand_field = el.prop('type')=='checkbox' ? el.is(':checked') : el.val()=='1';
737		}
738		else
739		{
740			var expand_field = !el.data('expandedFieldState');
741			el.data('expandedFieldState', expand_field);
742			expand_field ? el.addClass('active ' + btn_class) : el.removeClass('active ' + btn_class);
743		}
744		expand_field ? rows.addClass('fc-expanded'): rows.removeClass('fc-expanded');
745	}
746
747
748	/* Attach boostrap styling / behaviour to the inner contents of the given selector */
749	function fc_bootstrapAttach(sel)
750	{
751		var sbox = jQuery(sel);
752
753		// Turn radios into btn-group
754		sbox.find('.radio.btn-group label').addClass('btn');
755
756		sbox.find('fieldset.btn-group').each(function() {
757			// Handle disabled, prevent clicks on the container, and add disabled style to each button
758			if (jQuery(this).prop('disabled')) {
759				jQuery(this).css('pointer-events', 'none').off('click');
760				jQuery(this).find('.btn').addClass('disabled');
761			}
762		});
763
764		sbox.find('.btn-group label:not(.active)').click(function()
765		{
766			var label = jQuery(this);
767			var input = jQuery('#' + label.attr('for'));
768
769			if (!input.prop('checked')) {
770				label.closest('.btn-group').find('label').removeClass('active btn-success btn-danger btn-primary');
771				if (input.val() == '') {
772					label.addClass('active btn-primary');
773				} else if (input.val() == 0) {
774					label.addClass('active btn-danger');
775				} else {
776					label.addClass('active btn-success');
777				}
778				input.prop('checked', true);
779				input.trigger('change');
780			}
781		});
782
783		sbox.find('.btn-group input[checked="checked"]').each(function()
784		{
785			var input = jQuery(this);
786			if (input.val() == '') {
787				input.parent().find('label[for="' + input.attr('id') + '"]').addClass('active btn-primary');
788			} else if (input.val() == 0) {
789				input.parent().find('label[for="' + input.attr('id') + '"]').addClass('active btn-danger');
790			} else {
791				input.parent().find('label[for="' + input.attr('id') + '"]').addClass('active btn-success');
792			}
793		});
794	}
795
796
797	function fc_applyFormDependencies(toBeUpdated, toggleParent, toggleParentSelector, noFX)
798	{
799		var emptySet = jQuery();
800		jQuery.each( toBeUpdated, function( i, val ) {
801			var c = jQuery(this);
802			var dlist = c.data('fc_depend_list');
803			if (!dlist) dlist = {};
804			var forced = c.attr('data-fc_forced_display');
805
806			jQuery.each(dlist, function( i, val ) { if(val<=0) delete dlist[i]; if(val>=1) dlist[i]=1 ;});
807			c.data('fc_depend_list', dlist);
808
809			var toggledBox = !toggleParent || c.hasClass('fc_toggle_current') ? emptySet : (toggleParentSelector ? c.parents(toggleParentSelector) : c.parents().eq(toggleParent));
810
811			if ( jQuery.isEmptyObject(dlist) || forced=='1' )
812			{
813				toggledBox.length ? toggledBox.slideDown(noFX ? 0 : 500) : c.slideDown(noFX ? 0 : 500);
814			}
815			else
816			{
817				toggledBox.length ? toggledBox.slideUp(noFX ? 0 : 500) : c.slideUp(noFX ? 0 : 500);
818			}
819		});
820
821		jQuery.each( toBeUpdated, function( i, val ) {
822			var c = jQuery(this);
823			c.attr('data-fc_forced_display', '0');
824		});
825	}
826
827	function fcCascadedField_update(elVal, trgID, field_id, item_id, field_type, cascade_prompt, prompt_enabled, valindex)
828	{
829    // Joomla Base URL
830		var base_url = !!jbase_url_fc ? jbase_url_fc : '';;
831
832		var trgEL = jQuery('#'+trgID);
833		trgEL.parent().find('.field_cascade_loading').html('<img src="'+base_url+'components/com_flexicontent/assets/images/ajax-loader.gif" align="center" /> ... Loading');
834
835		var loading_prompt = '';  // Joomla.JText._('FLEXI_PLEASE_WAIT')
836		fcCascadedField_clear(trgEL, loading_prompt, prompt_enabled);
837		trgEL.removeAttr('data-defvals');  // Remove default values of select2 JS
838
839		jQuery.ajax({
840			type: 'POST',
841			url: base_url + 'index.php?option=com_flexicontent&tmpl=component&format=raw',
842			data: {
843				lang: (typeof fc_sef_lang != 'undefined' ? fc_sef_lang : ''),
844				task: 'call_extfunc',
845				omethod: 'html', /* unused */
846				exttype: 'plugins',
847				extfolder: 'flexicontent_fields',
848				extname: field_type,
849				extfunc: 'getCascadedField',
850				field_id: field_id,
851				item_id: item_id,
852				valgrps: elVal,
853				valindex: valindex
854			}
855		}).done( function(data) {
856			//window.console.log ('Got data for:' + trgEL.attr('id'));
857			trgEL.parent().find('.field_cascade_loading').html('');
858			data = data.trim();
859
860			if (data!='')
861			{
862				trgEL.empty().append(data).val('');
863				var trgTagName = trgEL.prop("tagName");
864
865				// Special case 0: Re-attach cascade function to the updated HTML of radio/checkbox set
866				if (fc_cascade_field_funcs.hasOwnProperty(trgID) && trgTagName!='SELECT')
867				{
868					//window.console.log ('Re-attach cascade function for new HTML source for tag ID:' + trgID);
869					fc_cascade_field_funcs[trgID]();
870				}
871
872				// Special case 1: Re-apply prettyCheckable to the updated HTML of radio/checkbox set
873				trgEL.find('.use_prettycheckable').each(function() {
874					var elem = jQuery(this);
875					var lbl = elem.next('label');
876					var lbl_html = elem.next('label').html();
877					lbl.remove();
878					elem.prettyCheckable({
879						color: 'blue',
880						label: lbl_html
881					});
882				});
883			}
884			else
885			{
886				trgEL.empty().append('<option value="" '+(!prompt_enabled ? 'disabled="disabled"' : '')+'>'+cascade_prompt+'</option>');
887			}
888			trgEL.trigger('change');  // Retrigger change event to update select2 display
889		});
890	}
891
892	function fcCascadedField_clear(el, prompt, prompt_enabled)
893	{
894		var trgTagName = el.prop("tagName");
895		if (trgTagName=='SELECT')
896		{
897			el.empty();
898			if (prompt) el.append('<option value="" '+(!prompt_enabled ? 'disabled="disabled"' : '')+'>' + prompt + '</option>');
899			el.trigger('change', [{elementClear:1}]);
900		}
901		else
902		{
903			el.find('input').first().trigger('change', [{elementClear:1}]);
904			el.empty();
905			if (prompt) el.append('<span class="fcpadded alert alert-info">'+prompt+'</span>');
906		}
907	}
908
909	function fcCascadedField(field_id, item_id, field_type, srcSelector, trgID, cascade_prompt, prompt_enabled, valindex)
910	{
911		var onEL  = jQuery(srcSelector);
912		var onEL2 = onEL.parent().find('select.use_select2_lib');
913		var isSel2 = onEL2.length != 0;
914
915		var srcEL = isSel2 ? onEL2 : onEL;
916		var trgEL = jQuery('#'+trgID);
917		//window.console.log ('ATTACHING fcCascadedField() FOR source SELECTOR: ' + srcSelector + ' to UPDATE element with ID: ' + trgID + ' , valindex: ' + valindex);
918
919		var elementClear = (typeof data!== 'undefined' && typeof data.elementClear !== 'undefined') ? data.elementClear : 0;  // workaround for radio, checkbox causing unneeded server call
920		var elType = srcEL.attr('type');
921		if (elType=='radio' || elType=='checkbox') {
922			var elVal = Array();
923			srcEL.parent().parent().find('input:checked').each(function( index ) {
924				elVal[elVal.length] = jQuery(this).val();
925			});
926			elVal = elVal.join(',');
927		} else {
928			var elVal = srcEL.val();
929		}
930
931		if ( !elementClear && !! elVal )
932		{
933			trgEL.closest('.fcfieldval_container_outer, .control-group').show();
934		} else {
935			trgEL.closest('.fcfieldval_container_outer, .control-group').hide();
936		}
937
938		srcEL.on('change', function(e, data){
939			var elementClear = (typeof data!== 'undefined' && typeof data.elementClear !== 'undefined') ? data.elementClear : 0;  // workaround for radio, checkbox causing unneeded server call
940			var elType = srcEL.attr('type');
941			if (elType=='radio' || elType=='checkbox') {
942				var elVal = Array();
943				srcEL.parent().parent().find('input:checked').each(function( index ) {
944					elVal[elVal.length] = jQuery(this).val();
945				});
946				elVal = elVal.join(',');
947			} else {
948				var elVal = srcEL.val();
949			}
950
951			//window.console.log ('CHANGED element ID: ' + srcEL.attr('id') + ' , isCHECKED: ' + srcEL.is(':checked') + ' type: '+srcEL.attr('type'));
952			if ( !elementClear && !! elVal )
953			{
954				//window.console.log ('CHANGED element ID: ' + srcEL.attr('id') + ' --> Updating:' + trgEL.attr('id') + ' for VALGRP: -' + elVal + '-  value of 1st element: ' + srcEL.val() + ' type: '+srcEL.attr('type'));
955				trgEL.closest('.fcfieldval_container_outer, .control-group').show();
956				fcCascadedField_update(elVal, trgID, field_id, item_id, field_type, cascade_prompt, prompt_enabled, valindex);
957			} else {
958				//window.console.log ('CHANGED element ID: ' + srcEL.attr('id') + ' --> Clearing:' + trgEL.attr('id') + ' TAG type: ' + trgEL.prop("tagName"));
959				trgEL.closest('.fcfieldval_container_outer, .control-group').hide();
960				fcCascadedField_clear(trgEL, cascade_prompt, prompt_enabled);
961			}
962		});
963	}
964
965
966
967	// *** Check that a variable is not null and is defined (has a value)
968	function js_isset (variable)
969	{
970		if (variable==null) return false;
971		if (typeof variable == "undefined") return false;
972		return true;
973	}
974
975
976	// Store a configuration cookie (like current user choices)
977	function fc_config_store(conf, prop_name, prop_value)
978	{
979		var cookieName  = conf.fc_man_config_cookie;
980		var managerName = conf.fc_man_manager_name;
981		var cookieValue = fclib_getCookie(cookieName);
982
983		//window.console.log('Using cookieName: ' + cookieName + ', managerName: ' + managerName);
984		//window.console.log(cookieName + ', Old value: ' +cookieValue);
985
986		cookieValue = jQuery.parseJSON(cookieValue ? cookieValue : '{}');
987
988		if (typeof cookieValue[managerName] == 'undefined')
989		{
990			cookieValue[managerName] = {};
991		}
992		cookieValue[managerName][prop_name] = prop_value;
993		window.console.log(cookieName + ', New value: ' + JSON.stringify(cookieValue));
994
995		var nDays = 30;
996		fclib_setCookie(cookieName, JSON.stringify(cookieValue), nDays);
997	}
998
999
1000	//******************************************
1001	//*** Column hide/show related functions ***
1002	//******************************************
1003
1004	// *** Hide a column of a table
1005	function fc_tbl_toggle_column(container_div_id, data_tbl_id, col_no, firstrun)
1006	{
1007		// 1. Get column-status array for the table with id: data_tbl_id
1008		var show_col = eval('show_col_'+data_tbl_id);
1009
1010		// 2. Decide show or hide action and update the displayed number of the hidden columns (if any must be in the element with id: data_tbl_id+'_hidecolnum_box')
1011		if ( !js_isset(show_col[col_no]) || show_col[col_no] ) {
1012			var action_func = 'hide';
1013			show_col[col_no] = 0;
1014			var hidecol_box = document.getElementById(data_tbl_id+'_hidecolnum_box');
1015			if ( js_isset(hidecol_box) ) {
1016				hidecol_box.innerHTML = parseInt(hidecol_box.innerHTML) + 1;
1017			}
1018		} else {
1019			var action_func = 'show';
1020			show_col[col_no] = 1;
1021			var hidecol_box = document.getElementById(data_tbl_id+'_hidecolnum_box');
1022			if ( js_isset(hidecol_box) ) {
1023				hidecol_box.innerHTML = parseInt(hidecol_box.innerHTML) - 1;
1024			}
1025		}
1026
1027
1028		if (firstrun)
1029		{
1030			if (action_func === 'hide' && !document.getElementById('fc_' + container_div_id + '_btn').classList.contains('btn-inverse'))
1031			{
1032				document.getElementById('fc_' + container_div_id + '_btn').classList += ' btn-dark btn-inverse text-white';
1033			}
1034		}
1035
1036		//var ffieldname_label = 'columnchoose_' + data_tbl_id + '_' + col_no + '_label';
1037		//var ffieldname_label = document.getElementById(ffieldname_label);
1038		//if ( js_isset(ffieldname_label) ) if (action_func == 'show')  ffieldname_label.style.color='black';  else ffieldname_label.style.color='#00aa00';
1039
1040		// 3. Get table and all its rows
1041		var tbl  = document.getElementById(data_tbl_id);
1042		var rows = jQuery(tbl).find('> thead > tr, > tbody > tr');
1043		var toggle_amount = 1;
1044
1045		// Find 'colspan' start of the column given the -index- 'col_no' of the head cell
1046		var _col_no = 0;
1047		var tcells = jQuery(rows[0]).children('td, th');
1048		for (cell=0; cell<col_no; cell++) {
1049			if (cell>=tcells.length) break;
1050			colspan = parseInt(jQuery(tcells[cell]).attr('colspan'));
1051			if (colspan) {
1052				_col_no = _col_no + colspan;
1053			} else {
1054				_col_no++;
1055			}
1056		}
1057
1058		// 4. Iterate through rows toggling the particular column
1059		for (var row=0; row<rows.length; row++) {
1060			var cell_cnt, cell;
1061
1062			// Get cell(s) of the current row
1063			var tcells = jQuery(rows[row]).children('td, th');
1064
1065			// First row is header, we will get 'colspan' of the HEAD CELL, which indicates how many 'toggle_amount' single-colspan columns should be toggled
1066			if (row==0) {
1067				toggle_amount = parseInt(jQuery(tcells[col_no]).attr('colspan'));
1068				if (!toggle_amount) toggle_amount = 1;
1069			}
1070
1071			// Find where the cell's index in current row, we need this loop since previous cell of row maybe using colspan
1072			cell_cnt = 0;
1073			for (cell=0; cell<tcells.length; cell++) {
1074				var colspan;
1075				var data_colspan = jQuery(tcells[cell]).attr('data-colspan');
1076				if (firstrun && !data_colspan) {
1077					colspan = parseInt(jQuery(tcells[cell]).attr('colspan'));
1078					if (colspan) {
1079						jQuery(tcells[cell]).attr('data-colspan', colspan);
1080					}
1081				} else {
1082					colspan = parseInt(jQuery(tcells[cell]).attr('data-colspan'));
1083				}
1084
1085				if (cell_cnt==_col_no+toggle_amount-1) break;
1086				var next_cnt = colspan ? cell_cnt + colspan : cell_cnt + 1;
1087				if (next_cnt > _col_no) break;
1088				cell_cnt = next_cnt;
1089			}
1090
1091			// Finally TOGGLE the found cell, we take into account that the given cell may have colspan,
1092			// if it does have colspan we increase / decrease, a ZERO remaining colspan, will make us hide the cell, and a non-zero makes us display the cell
1093			var _cell = cell;
1094			var colspan_remaining = toggle_amount;
1095			for (cell=_cell; cell<_cell+toggle_amount; cell++) {
1096				if (cell<tcells.length) {
1097					var colspan = parseInt(jQuery(tcells[cell]).attr('colspan'));
1098
1099					jQuery(tcells[cell]).removeClass('initiallyHidden');
1100					if ( colspan )
1101					{
1102						if ( action_func == 'hide' )
1103						{
1104							if ( colspan > colspan_remaining ) {
1105								jQuery(tcells[cell]).attr('colspan', colspan - colspan_remaining);
1106							} else {
1107								firstrun ?
1108								eval('jQuery(tcells[cell]).'+action_func+'(0)') :
1109								eval('jQuery(tcells[cell]).'+action_func+'(0)');
1110								jQuery(tcells[cell]).addClass('isHidden');
1111							}
1112						}
1113						else if (!firstrun)
1114						{
1115							if ( !jQuery(tcells[cell]).hasClass('isHidden') ) {
1116								jQuery(tcells[cell]).attr('colspan', colspan + colspan_remaining);
1117							} else {
1118								eval('jQuery(tcells[cell]).'+action_func+'(0)');
1119								jQuery(tcells[cell]).removeClass('isHidden');
1120							}
1121						}
1122						colspan_remaining = colspan_remaining - colspan;
1123					}
1124					else if (action_func == 'hide' || !firstrun)
1125					{
1126						firstrun ?
1127						eval('jQuery(tcells[cell]).'+action_func+'(0)') :
1128						eval('jQuery(tcells[cell]).'+action_func+'(0)');
1129						colspan_remaining--;
1130					}
1131				}
1132				if (colspan_remaining<1) break; // no more colspan to toggle columns
1133			}
1134		}
1135
1136		// Update cookie data and display data
1137		if (!firstrun && container_div_id)
1138		{
1139			column_toggles_update_data(container_div_id, data_tbl_id);
1140		}
1141	}
1142
1143
1144	// *** Update cookie data and display data
1145	function column_toggles_update_data(container_div_id, data_tbl_id)
1146	{
1147		var col_selectors = jQuery('#'+container_div_id+'_cols input');
1148		var col_selected = new Array();
1149		var i = 0;
1150		for (var cnt=0; cnt<col_selectors.length; cnt++)
1151		{
1152			if ( jQuery(col_selectors[cnt]).prop("checked") )
1153			{
1154				col_selected[i++] = jQuery(col_selectors[cnt]).attr("data-colno");
1155			}
1156		}
1157
1158		var cookieName = 'fc_columnchooser';
1159		var cookieValue  = fclib_getCookie(cookieName);
1160		//window.console.log(cookieName + ' - old value: ' +cookieValue);
1161
1162		cookieValue = jQuery.parseJSON(cookieValue ? cookieValue : '{}');
1163		cookieValue[data_tbl_id] = col_selected.join(',');
1164		//window.console.log(cookieName + ' - new value: ' + JSON.stringify(cookieValue));
1165
1166		var nDays = 30;
1167		fclib_setCookie(cookieName, JSON.stringify(cookieValue), nDays);
1168    //console.log('Updated columns selector cookie');
1169
1170		var totals_tag = jQuery('#columnchoose_totals');
1171		if (totals_tag.length)
1172		{
1173			totals_tag.html(col_selectors.length == col_selected.length ? '' : ' '+(col_selected.length - col_selectors.length));
1174		}
1175	}
1176
1177
1178	// *** Create column choosers row for a table. NOTE must have <th> cells at row 0
1179	function create_column_choosers(container_div_id, data_tbl_id, firstload, start_text, end_text, toggle_on_init)
1180	{
1181		// 1. Get column-status array for the table with id: data_tbl_id
1182		var show_col = eval('show_col_'+data_tbl_id);
1183
1184		// 2. Get table and its first row and then the 'th' cells in it
1185		var firstrow  = jQuery("#"+data_tbl_id+" thead tr:first"); //document.getElementById(data_tbl_id);
1186		var thcells = firstrow.find('th');
1187
1188		// 3. Iterate through the 'th' cells and create column hiders for those having the class 'hideOnDemandClass'
1189		var str = (typeof start_text != "undefined") ? start_text : '';
1190
1191		for (var col = 0; col < thcells.length; col++)
1192		{
1193			// 4. Skip if not having 'hideOnDemandClass' class
1194			if (!jQuery(thcells[col]).hasClass('hideOnDemandClass')) continue;
1195
1196			// 5. Get column name
1197			var column_toggle_lbl = jQuery(thcells[col]).find('.column_toggle_lbl');
1198			var col_display_name = column_toggle_lbl.length ? column_toggle_lbl.html() : jQuery(thcells[col]).text();
1199			column_toggle_lbl.remove();
1200
1201			// 6. Show / Hide current column
1202			if ( ( !firstload && !js_isset(show_col[col]) ) || ( jQuery(thcells[col]).hasClass('initiallyHidden') && !js_isset(show_col[col]) ) )
1203			{
1204				var checked_str = '';
1205				var fontcolor_str = 'black';//var fontcolor_str = '#aaaaaa';
1206
1207				// *** It has value of 0 or not set, set it to 1 for fc_tbl_toggle_column() function to hide it
1208				show_col[col] = 1;
1209				if (toggle_on_init) fc_tbl_toggle_column(container_div_id, data_tbl_id, col, 1);
1210			}
1211			else
1212			{
1213				var checked_str = 'checked="checked"';
1214				var fontcolor_str = 'black'; //var fontcolor_str = 'black';
1215
1216				// *** It has value of 1 or not set, set it to 0 for fc_tbl_toggle_column() function to show it
1217				show_col[col] = 0;
1218				if (toggle_on_init) fc_tbl_toggle_column(container_div_id, data_tbl_id, col, 1);
1219			}
1220
1221			// 7. Create column checkbox and append it to str
1222			var ffieldid   = 'columnchoose_' + data_tbl_id + '_' + col;
1223			var ffieldname = 'columnchoose_' + data_tbl_id + '[' + col + ']';
1224			str = str + '<li><input id="' + ffieldid + '" name="' + ffieldname + '" type="checkbox" data-colno="' + col + '" ' + checked_str + ' onclick="fc_tbl_toggle_column(\''+container_div_id+'\', \''+data_tbl_id+'\', '+col+', 0);">'
1225			+ '<label id="' + ffieldid + '_label" style="color:'+fontcolor_str+';" for="' + ffieldid + '">' + col_display_name + '</label></li>';
1226		}
1227
1228		// 8. Fill in 'column choose box'
1229		str = '<span id="'+container_div_id+'_cols"><ul>' + str + '</ul></span>' + end_text;
1230		document.getElementById(container_div_id).innerHTML=str;
1231
1232		// 9. Update cookie data and display data
1233		column_toggles_update_data(container_div_id, data_tbl_id);
1234	}
1235
1236
1237	/* Set a cookie */
1238	function fclib_setCookie(cookieName, cookieValue, nDays, samesite="lax")
1239	{
1240		var today = new Date();
1241		var expires = new Date();
1242		var path = typeof jclient_path_fc != 'undefined' ? jclient_path_fc : window.location.hostname;
1243		if (nDays==null || nDays<0) nDays=0;
1244
1245		if (nDays)
1246		{
1247			expires.setTime(today.getTime() + 3600000*24*nDays);
1248			document.cookie = cookieName+"="+encodeURIComponent(cookieValue) + ";samesite=" + samesite + ";path=" + path + ";expires=" + expires.toGMTString();
1249		} else {
1250			// nDays 0 , aka no expiration date, keep cookie till browser closes
1251			document.cookie = cookieName+"="+encodeURIComponent(cookieValue) + ";samesite=" + samesite + ";path=" + path;
1252		}
1253		//alert(cookieName+"="+encodeURIComponent(cookieValue) + ";path=" + path);
1254	}
1255
1256
1257	/* Get a cookie */
1258	function fclib_getCookie(cookieName)
1259	{
1260		let matched = document.cookie.match(new RegExp('(^|;\\s*)' + cookieName + '=([^;]+)'));
1261		return matched ? decodeURIComponent(matched[2]) : '';
1262	}
1263
1264
1265	/* Get a new list of 2-typles (key, element) living inside a cookie
1266	 * List is an object with 'key' being the property name and 'element' being the value of the property
1267	 */
1268	function fclib_createCookieList(cookieName, nDays)
1269	{
1270		var cookieValue = fclib_getCookie(cookieName);
1271		var items = jQuery.parseJSON(cookieValue ? cookieValue : '{}');
1272		nDays = nDays || 90;
1273
1274		return {
1275			// Add to the items
1276			"add": function(key, element)
1277			{
1278				items[key] = element;
1279				fclib_setCookie(cookieName, JSON.stringify(items), nDays);
1280			},
1281
1282			// Remove from items
1283			"remove": function (key)
1284			{
1285				if (typeof items[key] != 'undefined')
1286				{
1287					delete items[key];
1288					fclib_setCookie(cookieName, JSON.stringify(items), nDays);
1289				}
1290			},
1291
1292			// Clear items
1293			"clear": function()
1294			{
1295				items = {};
1296				fclib_setCookie(cookieName, JSON.stringify(items), nDays);
1297			},
1298
1299			// Get all the items
1300			"items": function()
1301			{
1302				return items;
1303			}
1304		}
1305	}
1306
1307
1308	/* Remove known textarea editors, optionally add a flag to restore editors laters */
1309	function fc_removeAreaEditors(txtareas, addRestoreClass)
1310	{
1311		addRestoreClass = typeof addRestoreClass != 'undefined' ? addRestoreClass : 0;
1312
1313		// Remove known JS editors
1314		txtareas.each(function(i, txtarea)
1315		{
1316			var areaid = jQuery(txtarea).attr('id');
1317
1318			var hasTinyMCE = !!(!areaid || typeof tinyMCE === 'undefined' ? false : tinyMCE.get(areaid));
1319			if (hasTinyMCE) {
1320				tinyMCE.majorVersion >= 4 ?
1321					tinymce.remove('#'+areaid) :
1322					tinymce.EditorManager.execCommand('mceRemoveEditor', false, areaid) ;
1323				if (addRestoreClass) jQuery(txtarea).addClass('fc_hadTinyMCE');
1324			}
1325
1326			var hasCodeMirror = typeof CodeMirror === 'undefined' ? false : jQuery(txtarea).first().next().hasClass('CodeMirror');
1327			if (hasCodeMirror) {
1328				jQuery(txtarea).first().next().get(0).CodeMirror.toTextArea();
1329				if (addRestoreClass) jQuery(txtarea).addClass('fc_hadCodeMirror');
1330
1331				// Remove codemirror container in case that removing it failed
1332				if (jQuery(txtarea).first().next('.CodeMirror').length) jQuery(txtarea).first().next('.CodeMirror').remove();
1333			}
1334		});
1335	}
1336
1337
1338	/* Restore known textarea editors to textareas with respective class */
1339	function fc_restoreAreaEditors(txtareas)
1340	{
1341		fc_attachTinyMCE( txtareas.filter('.fc_hadTinyMCE') );
1342		fc_attachCodeMirror( txtareas.filter('.fc_hadCodeMirror') );
1343	}
1344
1345
1346	/* Attach tinyMCE with default settings */
1347	function fc_attachTinyMCE(txtareas, MCEoptions, mceFieldname)
1348	{
1349		// Create a copy of the options,  so that we do not modify the original options objet
1350		var _MCEoptions = typeof MCEoptions!=='undefined' ? jQuery.extend({}, MCEoptions) : {};
1351		mceFieldname = typeof mceFieldname!=='undefined' ? mceFieldname : false;
1352
1353		var theArea, areaID;
1354		txtareas.each(function(i, txtarea)
1355		{
1356			var theArea = jQuery(txtarea);
1357			var areaID = theArea.attr('id');
1358
1359			// Remove tinymce classes from the textarea
1360			theArea.removeClass('mce_editable');
1361
1362			// J3.7.0+ (with integrated XTD buttons) is using new setup method
1363			if (typeof mceFieldname!=='undefined' && typeof Joomla.JoomlaTinyMCE != 'undefined' && typeof MCEoptions.joomlaExtButtons != 'undefined')
1364			{
1365				var tinyMCEOptions = {};
1366				tinyMCEOptions[mceFieldname] = _MCEoptions;
1367				Joomla.JoomlaTinyMCE.setupEditor(jQuery('#' + areaID).get(0), {tinyMCE: tinyMCEOptions});
1368			}
1369			else if (tinyMCE.majorVersion >= 4)
1370			{
1371				_MCEoptions.selector = '#' + areaID;
1372				tinyMCE.init(_MCEoptions);
1373				//tinyMCE.EditorManager.execCommand('mceAddEditor', true, areaID);  //var editor = tinyMCE.get(areaid);  //tinyMCE.editors[areaID];
1374			}
1375			else
1376			{
1377				_MCEoptions.mode = 'exact';
1378				_MCEoptions.elements = areaID;
1379				tinyMCE.init(_MCEoptions);
1380				//tinyMCE.EditorManager.execCommand('mceAddControl', true, areaID);  //var editor = tinymce.EditorManager.get(area_id);  // tinyMCE.editors[areaID];
1381			}
1382
1383			// Some cleanups for proper display
1384			theArea.addClass('mce_editable');
1385			theArea.parent().children('.mce-container').css('display', '');   // allow editor container to stretch to the full width of its external container
1386		});
1387
1388		return txtareas.length==1 ? tinyMCE.get(areaID) : true;
1389	}
1390
1391
1392	/* Attach CodeMirror with optional settings */
1393	function fc_attachCodeMirror(txtareas, CMoptions)
1394	{
1395		CMoptions = typeof CMoptions!=='undefined' ? CMoptions : {
1396			mode: 'application/x-httpd-php',
1397			indentUnit: 2,
1398			lineNumbers: true,
1399			matchBrackets: true,
1400			lineWrapping: true,
1401			onCursorActivity: function(CM)
1402			{
1403				CM.setLineClass(hlLine, null);
1404				hlLine = CM.setLineClass(CM.getCursor().line, 'activeline');
1405			}
1406		};
1407
1408		var editor, theArea;
1409		txtareas.each(function(i, txtarea)
1410		{
1411			theArea = jQuery(txtarea);
1412			theArea.removeClass(); // Remove all classes from the textarea
1413			editor = CodeMirror.fromTextArea(theArea.get(0), CMoptions);
1414			editor.refresh();
1415		});
1416
1417		return txtareas.length==1 ? editor : true;
1418	}
1419
1420
1421	/* Toggle box via a button and set CSS class to indicate that it is open  */
1422	function fc_toggle_box_via_btn(theBox, btn, btnClass, btnNew, mode, cookieName)
1423	{
1424		var box = typeof theBox=='string' ? jQuery('#'+theBox) : theBox;
1425
1426		if (btnNew)
1427		{
1428			// Show but remove 'display' property to allow CSS classes to take effect (possibly hiding it)
1429			btnNew.show().css('display', '');
1430			jQuery(btn).hide();
1431		}
1432		var cookieName = !!cookieName ? (typeof cookieName=='string' ? cookieName : theBox + '-disp') : false;
1433		//window.console.log(cookieName);
1434
1435		if (
1436			(typeof mode!=='undefined' && parseInt(mode)) ||  // use the mode provided
1437			(typeof mode==='undefined' && box.is(':hidden'))  // if any of the elements collection 'box' is hidden then open them all
1438		) {
1439			if (cookieName) fclib_setCookie(cookieName, 1, 0);  // nDays 0 , aka no expiration date, keep cookie till browser closes
1440			jQuery(btn).data('fc_noeffect') || jQuery(btn).hasClass('fc_noeffect') ?
1441				box.show() :
1442				box.slideDown(400) ;
1443			// Only remove 'data-fc_noeffect' but not the fc_noeffect class
1444			jQuery(btn).addClass(btnClass).data('fc_noeffect', null);
1445		}
1446		else
1447		{
1448			if (cookieName) fclib_setCookie(cookieName, 0, 0);  // nDays 0 , aka no expiration date, keep cookie till browser closes
1449			jQuery(btn).data('fc_noeffect') || jQuery(btn).hasClass('fc_noeffect') ?
1450				box.hide() :
1451				box.slideUp(400) ;
1452			// Only remove 'data-fc_noeffect' but not the fc_noeffect class
1453			jQuery(btn).removeClass(btnClass).data('fc_noeffect', null);
1454		}
1455	}
1456
1457
1458	/* Disable/enable a check box group e.g. when 'Use Global' option is toggled */
1459	function fc_toggle_checkbox_group(containerID, useGlobalElement)
1460	{
1461		var container = jQuery('#'+containerID);
1462		var inputs = container.find('input');
1463		el = jQuery(useGlobalElement);
1464		if ( el.is(':checked') ) {
1465			inputs.each(function(index){
1466				jQuery(this).attr('disabled', true);
1467			});
1468		} else {
1469			inputs.each(function(index){
1470				jQuery(this).attr('disabled', false);
1471			});
1472		}
1473		el.attr('disabled', false);
1474	}
1475
1476
1477	/* Auto-resize the currently open dialog vertically or horizontally */
1478	function fc_dialog_resize_now(boxWsel, boxHsel, contentSel)
1479	{
1480		boxWsel = typeof boxWsel != 'undefined' ? boxWsel : '.ui-dialog.fc-autow-dialog';
1481		boxHsel = typeof boxHsel != 'undefined' ? boxHsel : '.ui-dialog.fc-autoh-dialog';
1482		contentSel = typeof contentSel != 'undefined' ? contentSel : '.ui-dialog-content';
1483
1484		// Resize the modal(s)
1485		var dialogs = jQuery(boxWsel + ' , '+ boxHsel);
1486		dialogs.each(function( index )
1487		{
1488			var dialog_box = jQuery(this);
1489			var theDialog = dialog_box.data('fc_dialog');
1490			if (theDialog)
1491			{
1492				var params = theDialog.data('fc_dialog_params');
1493				var maxwidth  = theDialog.data('fc_dialog_maxwidth');
1494				var maxheight = theDialog.data('fc_dialog_maxheight');
1495				params = params || {};
1496				maxwidth  = maxwidth || 0;
1497				maxheight = maxheight || 0;
1498				params = fc_getAutoSizePos(maxwidth, maxheight, params);
1499
1500				dialog_box
1501					.css({ 'left': params.winleft+'px', 'width': params.winwidth+'px' })
1502					.css({ 'top': params.wintop+'px', 'height': params.winheight+'px' });
1503			}
1504		});
1505
1506		// Parameters for modal that should take full window
1507		var dialog_box_sbox = jQuery('#sbox-window');
1508		var dialog_box_jmedia = jQuery('div.field-media-wrapper > div.modal');
1509
1510		if (dialog_box_sbox.length || dialog_box_jmedia.length)
1511		{
1512			params = fc_getAutoSizePos(0, 0, {});
1513
1514			// Maximize sbox modal
1515			if (dialog_box_sbox.length)
1516			{
1517				dialog_box_sbox
1518					.css({ 'position': 'fixed' })
1519					.css({ 'left': params.winleft+'px', 'width': (params.winwidth-40)+'px' })
1520					.css({ 'top': params.wintop+'px', 'height': (params.winheight-20)+'px' })
1521					.find('#sbox-content').children('iframe')
1522						.removeAttr('width').removeAttr('height').css({ 'width': '100%', 'height': '100%' });
1523			}
1524
1525			// Maximize joomla media modal
1526			if (dialog_box_jmedia.length)
1527			{
1528				dialog_box_jmedia.css({ 'top': (params.wintop-30)+'px', 'height': (params.winheight-60)+'px' });
1529			}
1530		}
1531
1532		// Fix modal's inner container adding unneeded scrollbar
1533		var dialogs = jQuery(boxHsel);
1534		dialogs.each(function( index )
1535		{
1536			var dialog_box = jQuery(this);
1537			var content_box = dialog_box.find(contentSel);
1538			var h = dialog_box.height() - content_box.prev().outerHeight(true) - content_box.next().outerHeight(true);
1539			content_box.css({ 'height': h+'px', 'margin': '0', 'box-sizing': 'border-box' });
1540
1541			// Remove unneeded scroll bar inside iframe document, new browser security disallows this ?
1542			//var body = content_box.find('iframe').contents().find('body');
1543			//body.css({ 'height': 'unset' });
1544		});
1545	}
1546
1547
1548	/* Debounced function execution by the given 'wait' time, forcing single function execution within the given 'wait' time */
1549	function fc_debounce_exec(func, wait, immediate, func_context)
1550	{
1551		var timeout;
1552		return function() {
1553			var context = func_context || this, args = arguments;
1554			var later = function() {
1555				timeout = null;
1556				if (!immediate) func.apply(context, args);
1557			};
1558			var callNow = immediate && !timeout;
1559			clearTimeout(timeout);
1560			timeout = setTimeout(later, wait);
1561			if (callNow) func.apply(context, args);
1562		};
1563	}
1564
1565	var fc_dialog_resize = fc_debounce_exec(fc_dialog_resize_now, 200, false, null);
1566
1567	jQuery(window).resize(function() {
1568		fc_dialog_resize();
1569	});
1570
1571
1572	/* Apply select2 JS to targeted elements */
1573	function fc_attachSelect2(sel, s2_elems)
1574	{
1575		sel      = typeof sel !== 'undefined' && sel ? sel : 'body';
1576		var sbox = jQuery(sel);
1577
1578		s2_elems = s2_elems || sbox.find('select.use_select2_lib');
1579
1580		if (window.skip_select2_js)
1581		{
1582			s2_elems.removeClass('use_select2_lib').addClass('fc_isselect fc_ismobile fc_no_js_attach');
1583			//s2_elems.filter(function(){return !jQuery(this).attr('multiple');})
1584			sbox.find('.fc_mobile_label').show();
1585			return;
1586		}
1587
1588
1589		// Attach select2 to specific to select elements having specific CSS class, for select-multiple show values as togglable checkboxes
1590		s2_elems.each(function()
1591		{
1592			function fc_formatSel2Option(item)
1593			{
1594				var data_title = jQuery(item.element).attr('data-title');
1595				return !data_title ? item.text : "<div title='" + data_title + "'>" + item.text + "</div>";
1596			}
1597
1598			var sel_EL = jQuery(this);
1599
1600			var sel2ops = {
1601				minimumResultsForSearch: 10,
1602				formatResult: fc_formatSel2Option
1603			};
1604
1605			if ( sel_EL.hasClass('fc_select2_noselect') )
1606			{
1607				sel2ops.formatNoMatches = function() { return ''; };
1608				sel2ops.dropdownCssClass = 'select2-hidden';
1609				sel2ops.minimumResultsForSearch = -1;
1610			}
1611			if (sel_EL.attr('multiple') && !sel_EL.hasClass('fc_select2_no_check'))
1612			{
1613				sel2ops.closeOnSelect = false;
1614			}
1615
1616			if (sel_EL.attr('multiple') && !sel_EL.hasClass('fc_select2_no_check'))
1617			{
1618				sel_EL.select2(sel2ops).addClass('select2_fc_checkboxes');
1619			} else
1620			{
1621				sel_EL.select2(sel2ops);
1622			}
1623
1624			// Set initially selected data (this allows setting order too)
1625			if ( sel_EL.get(0).hasAttribute('data-select2-initdata') )
1626			{
1627				sel_EL.select2('data', sel_EL.data('select2-initdata'));
1628			}
1629
1630			// Make sortable if requested
1631			if ( sel_EL.hasClass('fc_select2_sortable') )
1632			{
1633				sel_EL.select2_sortable();
1634			}
1635		});
1636
1637
1638		// Customization of SELECT2 JS selectors
1639		sbox.find('div.use_select2_lib').each(function()
1640		{
1641			var el_container = jQuery(this);
1642			var sel_EL = el_container.next('select');
1643
1644
1645			// MULTI-SELECT2: Initialize internal labels, placing the label so that it overlaps the text filter box
1646			var fc_label_text = sel_EL.attr('data-fc_label_text');
1647			if (!fc_label_text) fc_label_text = sel_EL.attr('fc_label_text');
1648			if (fc_label_text)
1649			{
1650				var _label = (fc_label_text.length >= 30) ? fc_label_text.substring(0, 28) + '...' : fc_label_text;
1651
1652				jQuery('<span/>', {
1653					'class': 'fc_has_inner_label fc_has_inner_label_select2',
1654					'text': _label
1655				}).prependTo(el_container.find('.select2-search-field'));
1656			}
1657
1658
1659			// MULTI-SELECT2: Initialize internal prompts, placing the prompt so that it overlaps the text filter box
1660			var fc_prompt_text = sel_EL.attr('data-fc_prompt_text');
1661			if (!fc_prompt_text) fc_prompt_text = sel_EL.attr('fc_prompt_text');
1662			if (fc_prompt_text)
1663			{
1664				var _prompt = (fc_prompt_text.length >= 30) ? fc_prompt_text.substring(0, 28) + '...' : fc_prompt_text;
1665
1666				jQuery('<span/>', {
1667					'class': 'fc_has_inner_prompt fc_has_inner_prompt_select2',
1668					'text': _prompt
1669				}).prependTo(el_container.find('.select2-search-field')).hide();
1670			}
1671
1672
1673			// SINGLE-SELECT2: Highlight selects with an active value
1674			if ( ! sel_EL.attr('multiple') && sel_EL.hasClass('fc_add_highlight') )
1675			{
1676				var el = el_container.find('.select2-choice');
1677				var val = sel_EL.val();
1678				if (val === null) {
1679					//el.addClass('fc_highlight_disabled');
1680				} else if (!!val && val.length) {
1681					el.addClass('fc_highlight');
1682				} else {
1683					el.removeClass('fc_highlight');
1684				}
1685			}
1686		});
1687
1688
1689		// MULTI-SELECT2:
1690		s2_elems.on('select2-open', function()
1691		{
1692
1693			// Add events to handle focusing the text filter box (hide inner label)
1694			var sel_EL = jQuery(this);
1695			var el = sel_EL.parent().find('.select2-input');
1696			var el_label = el.prevAll('.fc_has_inner_label');
1697			if (el_label) el_label.hide();
1698			var el_prompt = el.prevAll('.fc_has_inner_prompt');
1699			if (el_prompt) el_prompt.show();
1700
1701
1702			// Allow listing already selected options WHEN having class 'select2_fc_checkboxes'
1703			if (sel_EL.hasClass('select2_fc_checkboxes')) {
1704				var els = jQuery('#select2-drop').find('.select2-selected');
1705				els.addClass('select2-selected-visible').removeClass('select2-selected');
1706
1707				// Suppress the onchange attribute while selector is open
1708				sel_EL.data('onchange', sel_EL.attr('onchange'));
1709				sel_EL.removeAttr('onchange');
1710			}
1711
1712
1713		}).on('select2-close', function(e)
1714		{
1715
1716			// Add events to handle bluring the text filter box (show inner label)
1717			var sel_EL = jQuery(this);
1718
1719			// Re-open if this was closed while selecting, FOR select2 v4, use 'select2:closing' instead
1720			if ( sel_EL.data('field_being_selected') )
1721			{
1722				sel_EL.select2('open');
1723				jQuery('#select2-drop').find('.select2-no-results').remove();
1724				return;
1725			}
1726
1727			var el = sel_EL.parent().find('.select2-input');
1728			var el_label = el.prevAll('.fc_has_inner_label');
1729			if (el_label) el_label.show();
1730			var el_prompt = el.prevAll('.fc_has_inner_prompt');
1731			if (el_prompt) el_prompt.hide();
1732
1733			// Restore already selected options state
1734			if (sel_EL.hasClass('select2_fc_checkboxes')) {
1735				var els = jQuery('#select2-drop').find('.select2-selected-visible');
1736				els.removeClass('select2-selected-visible').addClass('select2-selected');
1737			}
1738
1739
1740			// Restore the onchange attribute when selector closes, running the JS if changes were made
1741			if ( sel_EL.data('onchange') )
1742			{
1743				sel_EL.attr('onchange', sel_EL.data('onchange'));
1744				sel_EL.data('onchange', null);
1745			}
1746
1747			// Trigger change event on close if this was prevented
1748			if ( sel_EL.data('field_being_edited') )
1749			{
1750				sel_EL.data('field_being_edited', null);
1751				sel_EL.trigger('change');
1752				// LEGACY browsers ??
1753				/*if ( sel_EL.attr('onchange') )
1754				{
1755					eval(sel_EL.attr('onchange'));
1756				}*/
1757			}
1758
1759
1760		}).on('select2-selecting', function(e)
1761		{
1762
1763			// Handle toggling MULTI-SELECT2 as checkboxes
1764			var sel_EL = jQuery(this);
1765			if ( sel_EL.attr('multiple') && sel_EL.hasClass('select2_fc_checkboxes') )
1766			{
1767				var vals = sel_EL.val();
1768				var nVals = new Array();
1769				var i = 0;
1770				var found = 0;
1771				if (vals && vals.length)
1772				{
1773					for (var n = 0; n < vals.length; ++n)
1774					{
1775						if (vals[n] != e.val) nVals[i++] = vals[n];
1776						else found = 1;
1777					}
1778				}
1779
1780				if (!found) nVals[i++] = e.val;
1781				sel_EL.data('set_selected_values', nVals);
1782				sel_EL.data('field_being_edited', true);
1783				sel_EL.data('field_being_selected', true);
1784			}
1785
1786
1787		}).on('change', function(e)
1788		{
1789
1790			// SELECT2: Handle change event
1791			var sel_EL = jQuery(this);
1792
1793			// Clear 'being_selected' FLAG but not the 'being_edited', as this will be done by the 'close' event, also stop event propagation if 'being_edited'
1794			sel_EL.data('field_being_selected', null);
1795			if (sel_EL.data('field_being_edited'))
1796			{
1797				e.preventDefault();
1798				e.stopPropagation();
1799				e.stopImmediatePropagation();
1800			}
1801
1802			var nVals = sel_EL.data('set_selected_values');
1803			if (nVals)
1804			{
1805				jQuery('#select2-drop').find('.select2-selected-visible, .select2-selected').removeClass('select2-selected-visible').removeClass('select2-selected');
1806				jQuery('#select2-drop').find('.select2-selected').removeClass('select2-selected-visible').removeClass('select2-selected');
1807
1808				// Specific values were requested to be set, but first clear data value, to prevent loop in the change event
1809				sel_EL.data('set_selected_values', null).select2('val', nVals).trigger('change');
1810			}
1811
1812
1813			// SINGLE-SELECT2:
1814			if ( ! sel_EL.attr('multiple') && sel_EL.hasClass('fc_add_highlight') )
1815			{
1816				// Handle highlighting selected value
1817				var el = jQuery(this).prev('div').find('.select2-choice');
1818				var val = sel_EL.val();
1819				if (!!val && val.length) {
1820					el.addClass('fc_highlight');
1821				} else {
1822					el.removeClass('fc_highlight');
1823				}
1824			}
1825
1826
1827			// MULTI-SELECT2:
1828			if ( sel_EL.attr('multiple') )
1829			{
1830				// Allow listing already selected options WHEN having class 'select2_fc_checkboxes'
1831				if (jQuery(this).hasClass('select2_fc_checkboxes')) {
1832					var els = jQuery('#select2-drop').find('.select2-selected');
1833					els.addClass('select2-selected-visible').removeClass('select2-selected');//.removeClass('select2-result-selectable');
1834				}
1835			}
1836
1837		});
1838
1839
1840		// MULTI-SELECT2: Handle highlighting selected value
1841		sbox.find('div.use_select2_lib.select2-container-multi input').on('keydown', function()
1842		{
1843			var el = jQuery(this);
1844			setTimeout(function() {
1845				var val = el.val();
1846				if (!!val && val.length) {
1847					var el_prompt = el.prevAll('.fc_has_inner_prompt');
1848					if (el_prompt) el_prompt.hide();
1849				} else {
1850					var el_prompt = el.prevAll('.fc_has_inner_prompt');
1851					if (el_prompt) el_prompt.show();
1852				}
1853			}, 0);
1854		});
1855
1856
1857		// SELECT2: scrollbar wrap problem
1858		s2_elems.on('loaded open', function()
1859		{
1860			var ul = jQuery('#select2-drop ul.select2-results');
1861			var needsScroll= ul.prop('scrollHeight') > ul.prop('clientHeight');
1862			if (needsScroll) ul.css('overflow-y', 'scroll');
1863			else  ul.css('overflow-y', 'auto');
1864		});
1865	}
1866
1867
1868	/* Valid HTML for legacy modals using rel, TODO: add more */
1869	jQuery(document).ready(function() {
1870		jQuery(".modal").each(function(i, el) {
1871			if (jQuery(el).get(0).hasAttribute("data-rel")) jQuery(el).attr("rel", jQuery(el).attr("data-rel"));
1872		});
1873	});
1874
1875
1876	/* Restore the given form fields values into the given form */
1877	function fc_restore_form_field_values(form, data)
1878	{
1879		if (!data)
1880		{
1881			form[0].reset();
1882
1883			var elements = Array.from(form[0].elements);
1884	 	 	for (var i = 0, l = elements.length; i < l; i++)
1885	 	 	{
1886				var el = jQuery(elements[i]);
1887				var tagName = elements[i].tagName;
1888				var inputType = tagName != 'INPUT' ? false : elements[i].type;
1889				if (tagName == 'SELECT' || (inputType && inputType!='BUTTON'))
1890				{
1891					el.trigger('change');
1892				}
1893			}
1894
1895			return;
1896		}
1897
1898		jQuery.each(data, function(name, val)
1899		{
1900			var el = form.find('[name="'+name+'"]'),
1901			type = el.attr('type');
1902
1903			switch(type)
1904			{
1905				case 'checkbox':  el.attr('checked', 'checked'); break;
1906				case 'radio':     el.filter('[value="'+val+'"]').attr('checked', 'checked'); break;
1907				default: el.val(val);
1908			}
1909			el.trigger('change');
1910		});
1911	}
1912
1913
1914	if (typeof jQuery.fn.serializeObject == "undefined")
1915	{
1916		jQuery.fn.serializeObject = function()
1917		{
1918			var o = {};
1919			var a = this.serializeArray();
1920			jQuery.each(a, function() {
1921				if (o[this.name]) {
1922					if (!o[this.name].push) {
1923						o[this.name] = [o[this.name]];
1924					}
1925					o[this.name].push(this.value || '');
1926				} else {
1927					o[this.name] = this.value || '';
1928				}
1929			});
1930			return o;
1931		}
1932	}
1933
1934
1935	if (typeof fc_isIE == "undefined")
1936	{
1937		window.fc_ie_version_number = null;
1938		fc_isIE = function()
1939		{
1940			if (window.fc_ie_version_number === null)
1941			{
1942				var userAgent = navigator.userAgent.toLowerCase();
1943				window.fc_ie_version_number = (userAgent.indexOf('msie') != -1) ? parseInt(userAgent.
1943split('msie')[1]) : false;
1944			}
1945			return window.fc_ie_version_number;
1946		}
1947	}
1948
1949
1950	window.fc_has_flash_addon_enabled = false;
1951	fc_has_flash_addon = function()
1952	{
1953		try {
1954			window.fc_has_flash_addon_enabled = Boolean(new ActiveXObject("ShockwaveFlash.ShockwaveFlash"));
1955		} catch(exception) {
1956			window.fc_has_flash_addon_enabled = ("undefined" != typeof navigator.mimeTypes["application/x-shockwave-flash"]);
1957		}
1958		return window.fc_has_flash_addon_enabled;
1959	}
1960
1961
1962	function fc_remove_non_alphanum_chars(text, allowed_chars)
1963	{
1964		var result = '';
1965		if (!text) return result;
1966		allowed_chars = typeof allowed == 'undefined' ? '' : allowed_chars;
1967
1968		var num_reg = new RegExp('[0-9]+$');
1969		var allowed_reg = new RegExp('['+allowed_chars+']+$');
1970
1971		var lower = text.toLowerCase();
1972		var upper = text.toUpperCase();
1973
1974		for (var i = 0; i < lower.length; ++i)
1975		{
1976			if (num_reg.test(text[i]) || (lower[i] != upper[i]) || (lower[i].trim() === ''))
1977			{
1978				result += text[i];
1979			}
1980			else if (allowed_chars.length && allowed_reg.test(text[i]))
1981			{
1982				result += text[i];
1983			}
1984		}
1985		return result;
1986	}
1987
1988
1989	function fc_attachSingleSlider(cfg, type)
1990	{
1991		var slider_tag_id = cfg.name + (cfg.sfx || '');
1992		var slider = document.getElementById(slider_tag_id + '_nouislider');
1993		//if (jQuery(slider).length) { window.console.log('not found slider ID: ' + slider_tag_id + '_nouislider'); return; }
1994
1995		var selectSL = jQuery('#' + slider_tag_id + '-sel');
1996		var inputSL = document.getElementById(slider_tag_id + '-val');
1997		var isSingle = 1;
1998		!!type || (type = 2);  // 1: drop-down select , 2: slider (default type)
1999
2000		var IEversion = fc_isIE();
2001		var is_IE8 = IEversion && IEversion < 9;
2002		var use_select = is_IE8 || type == 1;
2003		if (use_select)
2004		{
2005			if (selectSL.length)
2006			{
2007				var selected;
2008				jQuery.each(cfg.step_values, function(key, value)
2009				{
2010					selectSL.append(
2011						jQuery('<option></option>')
2012							.attr('value', cfg.step_values[key])
2013							.text(cfg.step_labels[key])
2014					);
2015				});
2016
2017				selectSL
2018					.val(cfg.step_values[cfg.initial_pos])
2019					.on('change', function(e, data) {
2020						var slider_input = jQuery('#' + slider_tag_id + '-val').get(0);
2021
2022						if (cfg.element_class_list != '')
2023						{
2024							jQuery(cfg.element_selector).removeClass(cfg.element_class_list).addClass(cfg.element_class_prefix + selectSL.val());
2025						}
2026						if (cfg.elem_container_class != '')
2027						{
2028							jQuery(cfg.elem_container_selector).removeClass(cfg.elem_container_class).addClass(cfg.elem_container_prefix + selectSL.val());
2029						}
2030
2031						fclib_setCookie(slider_input.name, selectSL.val(), 0);  // nDays 0 , aka no expiration date, keep cookie till browser closes
2032					})
2033					.css('display', '');  // avoid show() it adds display: block
2034			}
2035			return;
2036		}
2037		slider.style.display = '';
2038
2039		var step_values = cfg.step_values;
2040		var step_labels = cfg.step_labels;
2041
2042		noUiSlider.create(slider, {
2043			start: cfg.initial_pos,
2044			connect: false,
2045			step: 1,
2046			range: {'min': 0, 'max': (cfg.step_values.length - 1)},
2047		});
2048		if (!!cfg.start_hidden) jQuery(slider).hide();
2049
2050		var tipHandles = slider.getElementsByClassName('noUi-handle'),
2051		tooltips = [];
2052
2053		// Add divs to the slider handles.
2054		for ( var i = 0; i < tipHandles.length; i++ )
2055		{
2056			tooltips[i] = document.createElement('span');
2057			tipHandles[i].appendChild(tooltips[i]);
2058
2059			tooltips[i].className += 'fc-sliderTooltip fc-bottom'; // Add a class for styling
2060			tooltips[i].innerHTML = '<span></span>'; // Add additional markup
2061			tooltips[i] = tooltips[i].getElementsByTagName('span')[0];  // Replace the tooltip reference with the span we just added
2062		}
2063
2064		// When the slider changes, display the value in the tooltips and set it into the input form elements
2065		slider.noUiSlider.on('update', function( values, handle ) {
2066			var value = parseInt(values[handle]);
2067			var i = value;
2068
2069			if ( handle ) {
2070				alert('This slider does not have a right handler');
2071			} else {
2072				inputSL.value = typeof step_values[value] !== 'undefined' ? step_values[value] : value;
2073			}
2074			var tooltip_text = typeof step_labels[value] !== 'undefined' ? step_labels[value] : value;
2075			var max_len = 36;
2076			tooltips[handle].innerHTML = '<span class=\"icon-image\"></span> ' +
2077				(tooltip_text.length > max_len+4 ? tooltip_text.substring(0, max_len)+' ...' : tooltip_text);
2078			var left  = jQuery(tooltips[handle]).closest('.noUi-origin').position().left;
2079			var width = jQuery(tooltips[handle]).closest('.noUi-base').width();
2080
2081			//window.console.log ('handle: ' + handle + ', left : ' + left + ', width : ' + width);
2082			if (isSingle) {
2083				left<(50/100)*width ?
2084					jQuery(tooltips[handle]).parent().removeClass('fc-left').addClass('fc-right') :
2085					jQuery(tooltips[handle]).parent().removeClass('fc-right').addClass('fc-left');
2086			}
2087			else {
2088				alert('This slider only has 1 handle');
2089			}
2090		});
2091
2092		// Handle form autosubmit
2093		slider.noUiSlider.on('change', function() {
2094			var slider = jQuery('#' + slider_tag_id + '_nouislider');
2095			var slider_input = jQuery('#' + slider_tag_id + '-val').get(0);
2096
2097			if (cfg.element_class_list != '')
2098			{
2099				jQuery(cfg.element_selector).removeClass(cfg.element_class_list).addClass(cfg.element_class_prefix + slider_input.value);
2100			}
2101			if (cfg.elem_container_class != '')
2102			{
2103				jQuery(cfg.elem_container_selector).removeClass(cfg.elem_container_class).addClass(cfg.elem_container_prefix + slider_input.value);
2104			}
2105
2106			fclib_setCookie(slider_input.name, slider_input.value, 0);  // nDays 0 , aka no expiration date, keep cookie till browser closes
2107		});
2108
2109		inputSL.addEventListener('change', function(){
2110			var value = 0;  // default is first value = empty
2111			for(var i=1; i<step_values.length-1; i++)
2112			{
2113				if (step_values[i] == this.value) { value=i; break; }
2114			}
2115			slider.noUiSlider.set([value, null]);
2116		});
2117	}
2118
2119
2120
2121	// Toggle elements of all other views when switching between views (e.g. details / thumbnails views)
2122	function fc_toggle_view_mode(new_btn)
2123	{
2124		var btns = new_btn.parent().children();
2125
2126		btns.removeClass('active');
2127		btns.each(function(index, value)
2128		{
2129			var btn = jQuery(value);
2130			jQuery(btn.data('toggle_selector')).hide();
2131		});
2132
2133		new_btn.addClass('active');
2134		jQuery(new_btn.data('toggle_selector')).show().css('display', '').css('visibility', '');
2135	}
2136
2137
2138
2139	// Create a sanitized filename similar to what server-side code will accept
2140	fc_sanitize_filename = function(text)
2141	{
2142		if (!text) return '';
2143
2144		var validChars = new RegExp('[A-Za-z0-9\.\_\-]+');
2145		var result = '';
2146
2147		for (var i = 0; i < text.length; ++i)
2148		{
2149			if (validChars.test(text[i]))
2150			{
2151				result += text[i];
2152			}
2153		}
2154		return result;
2155	}
2156
2157
2158
2159	// Submit a form in parent window from a jQuery modal
2160	fc_parent_form_submit = function(container_id, form_id, fields, options)
2161	{
2162		var form = window.parent.document.getElementById(form_id);
2163		var task = typeof options.task !== 'undefined'  ?  options.task  :  false;
2164		var is_list = typeof options.is_list !== 'undefined'  ?  options.is_list  :  false;
2165		var is_jform = typeof options.is_jform !== 'undefined'  ?  options.is_jform  :  false;
2166
2167		for (var field_name in fields)
2168		{
2169			form.elements[field_name].value = fields[field_name];
2170		}
2171
2172		if (is_list && form.boxchecked.value==0)
2173		{
2174			alert(Joomla.JText._('FLEXI_NO_ITEMS_SELECTED'));
2175			return;
2176		}
2177
2178		// ***
2179		// *** Submit progress bar
2180		// ***
2181
2182		if (typeof form.progressbar_submit_handler_success === 'undefined' || !form.progressbar_submit_handler_success)
2183		{
2184			form.progressbar_submit_handler_success = true;
2185			// Default to doing animation
2186			form.doanimated_submit = typeof form.doanimated_submit === 'undefined' ? 1 : 0;
2187
2188			form.addEventListener('submit', function(e)
2189			{
2190				if ( !form.doanimated_submit )
2191				{
2192					return true;
2193				}
2194				//form.doanimated_submit = 0;  // Clear FLAG until next time Joomla.submitform() is called
2195
2196				var fc_filter_form_blocker = jQuery('#fc_filter_form_blocker');
2197
2198				if (!fc_filter_form_blocker.length)
2199				{
2200					fc_filter_form_blocker = jQuery(
2201						'<div id="fc_filter_form_blocker">' +
2202							'<div class="fc_blocker_overlay"></div>' +
2203							'<div class="fc_blocker_content">' +
2204								'<div class="fc_blocker_logo></div>' +
2205								'<div class="fc_blocker_mssg>' + Joomla.JText._('FLEXI_LOADING') + '</div>' +
2206								'<div class="fc_blocker_bar"><div></div></div>' +
2207							'</div>' +
2208						'</div>'
2209					).prependTo(jQuery('body'));
2210				}
2211
2212				if (fc_filter_form_blocker)
2213				{
2214					fc_filter_form_blocker.css("display", "block");
2215					fc_filter_form_blocker.find('.fc_blocker_mssg').html(Joomla.JText._('FLEXI_LOADING'));
2216					fc_admin_progress(95, jQuery('#fc_filter_form_blocker .fc_blocker_bar'));
2217				}
2218			});
2219		}
2220
2221		// Close modal to allow click events to work on the parent window 's form
2222		jQuery('#'+container_id).dialog('close');
2223
2224		// If it is a Joomla form
2225		if (is_jform)
2226		{
2227			window.parent.Joomla.submitbutton(task);
2228			return;
2229		}
2230
2231		// For proper submit, append a hidden submit button to the form and click it, then remove it if submit was prevented
2232		var button = document.createElement('input');
2233		button.style.display = 'none';
2234		button.type = 'submit';
2235		form.appendChild(button).click();
2236		form.removeChild(button);
2237	}
2238
2239
2240
2241	//*******************************
2242	//*** START OF fc_init*() methods
2243	//*******************************
2244
2245
2246	/*
2247	 * Initialize CSS styles and effects
2248	 */
2249	function fc_initDynamicLayoutJsCss(container_id, reAddOnEvents, html)
2250	{
2251		const container_sel = '#' + container_id;
2252		jQuery(container_sel).html(html);
2253
2254		tabberAutomatic(tabberOptions, container_id);
2255		fc_bindFormDependencies(container_sel, 0, '');
2256
2257		fc_initBootstrap(null, container_sel, reAddOnEvents);
2258		fc_initTooltips(null, container_sel, reAddOnEvents);
2259		fc_initCodeMirror(null, container_sel, reAddOnEvents);
2260		fc_initMinicolors(null, container_sel, reAddOnEvents);
2261		fc_initSubform(null, container_sel);
2262
2263		if (typeof(fcrecord_attach_sortable) == 'function') fcrecord_attach_sortable(container_sel);
2264		if (typeof(fcfield_attach_sortable) == 'function')  fcfield_attach_sortable(container_sel);
2265
2266		fc_initSelect2(null, container_sel, reAddOnEvents);
2267	}
2268
2269
2270	/*
2271	 * Initialize bootstrap styles and effects
2272	 */
2273	function fc_initBootstrap(event, container_sel, reAddOnEvents)
2274	{
2275		const container_el = jQuery(container_sel);
2276
2277		// Also run method on given "Add" events, e.g. 'subform-row-add'
2278		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initBootstrap));
2279
2280		// Add bootstap styless and effects
2281		fc_bootstrapAttach(container_el);
2282	}
2283
2284
2285	/*
2286	 * Initialize tooltips and popovers
2287	 */
2288	function fc_initTooltips(event, container_sel, reAddOnEvents)
2289	{
2290		const container_el = jQuery(container_sel);
2291		const elements_set_tooltip = container_el.find('.hasTooltip');
2292		const elements_set_popover = container_el.find('.hasPopover');
2293
2294		if (typeof jQuery === undefined || typeof jQuery.tooltip === undefined)
2295		{
2296			elements_set_tooltip.length && !!Joomla.fc_debug ? window.console.log('tooltip() not loaded. Skipping it') : false;
2297		}
2298		if (typeof jQuery === undefined || typeof jQuery.popover === undefined)
2299		{
2300			elements_set_popover.length && !!Joomla.fc_debug ? window.console.log('popover() not loaded. Skipping it') : false;
2301		}
2302		if (typeof jQuery === undefined || (typeof jQuery.tooltip === undefined && typeof jQuery.popover === undefined))
2303		{
2304			return;
2305		}
2306
2307		// Also run method on given "Add" events, e.g. 'subform-row-add'
2308		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initTooltips));
2309
2310		// Add tooltips and popovers
2311		typeof jQuery.tooltip !== undefined ? elements_set_tooltip.tooltip({html: true, container: container_el}) : false;
2312		typeof jQuery.popover !== undefined ? elements_set_popover.popover({html: true, container: container_el, trigger : 'hover focus'}) : false;
2313	}
2314
2315
2316	/*
2317	 * Initialize select2 behaviour
2318	 */
2319	function fc_initSelect2(event, container_sel, reAddOnEvents)
2320	{
2321		const container_el = jQuery(container_sel);
2322		const elements_set = container_el.find('select.use_select2_lib');
2323
2324		if (typeof jQuery === undefined || typeof jQuery.select2 === undefined)
2325		{
2326			elements_set.length && !!Joomla.fc_debug ? window.console.log('subformRepeatable() not loaded. Skipping it') : false;
2327			return;
2328		}
2329
2330		// Also run method on given "Add" events, e.g. 'subform-row-add'
2331		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initSelect2));
2332
2333		// Attach select2
2334		fc_attachSelect2(container_el, elements_set);
2335	}
2336
2337
2338	function fc_initCodeMirror(event, container_sel, reAddOnEvents)
2339	{
2340		const container_el = jQuery(container_sel);
2341		const elements_set = container_el.find('textarea.codemirror-source');
2342
2343		if (typeof Joomla.editors === undefined || typeof CodeMirror === undefined)
2344		{
2345			elements_set.length && !!Joomla.fc_debug ? window.console.log('CodeMirror not loaded. Skipping it') : false;
2346			return;
2347		}
2348
2349		// Also run method on given "Add" events, e.g. 'subform-row-add'
2350		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initCodeMirror));
2351
2352		elements_set.each(function () {
2353			var input = jQuery(this).removeClass('codemirror-source');
2354			var id = input.prop('id');
2355
2356			Joomla.editors.instances[id] = CodeMirror.fromTextArea(this, input.data('options'));
2357		});
2358	}
2359
2360
2361
2362	/*
2363	 * Initialize subform fields
2364	 */
2365	function fc_initSubform (event, container_sel)
2366	{
2367		const container_el = jQuery(container_sel);
2368		const elements_set = container_el.find('div.subform-repeatable');
2369
2370		if (typeof jQuery === undefined || typeof jQuery.subformRepeatable)
2371		{
2372			elements_set.length && !!Joomla.fc_debug ? window.console.log('subformRepeatable() not loaded. Skipping it') : false;
2373			return;
2374		}
2375		elements_set.subformRepeatable();
2376	}
2377
2378
2379	/*
2380	 * Initialize any minicolor fields
2381	 */
2382	function fc_initMinicolors(event, container_sel, reAddOnEvents)
2383	{
2384		const container_el = jQuery(container_sel);
2385		const elements_set = container_el.find('.minicolors');
2386
2387		if (typeof jQuery === undefined || typeof jQuery.minicolors === undefined)
2388		{
2389			elements_set.length && !!Joomla.fc_debug ? window.console.log('minicolors() not loaded. Skipping it') : false;
2390			return;
2391		}
2392
2393		// Also run method on given "Add" events, e.g. 'subform-row-add'
2394		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initMinicolors));
2395
2396		elements_set.each(function() {
2397			var $this = jQuery(this);
2398			var format = $this.data('validate') === 'color' ? 'hex' : $this.data('format') || 'hex';
2399
2400			$this.minicolors({
2401				control: $this.data('control') || 'hue',
2402				format: format === 'rgba' ? 'rgb' : format,
2403				keywords: $this.data('keywords') || '',
2404				opacity: format === 'rgba',
2405				position: $this.data('position') || 'default',
2406				theme: $this.data('theme') || 'bootstrap',
2407			});
2408		});
2409	}
2410
2411
2412	//*****************************
2413	//*** END OF fc_init*() methods
2414	//*****************************
2415
2416
2417
2418	/*
2419	 * Allow custom input in date fields having the custom text class
2420	 */
2421	document.addEventListener('DOMContentLoaded', function()
2422	{
2423		if (typeof JoomlaCalendar != 'undefined')
2424		{
2425			var JoomlaCalendar_setAltValue_fc = JoomlaCalendar.prototype.setAltValue;
2426			JoomlaCalendar.prototype.setAltValue = function()
2427			{
2428				if (this.inputField.classList.contains('fc_date_allow_text'))
2429				{
2430					return;
2431				}
2432				return JoomlaCalendar_setAltValue_fc.call(this, arguments);
2433			};
2434
2435			var JoomlaCalendar_init_fc = JoomlaCalendar.init;
2436			JoomlaCalendar.init = function()
2437			{
2438				var result = JoomlaCalendar_init_fc.apply(null, arguments);
2439
2440				var element = arguments[0];
2441				var input = element ? element.getElementsByTagName('input')[0] : false;
2442
2443				if (input)
2444				{
2445					if (!!input.id && input.classList.contains('fc_date_allow_text'))
2446					{
2447						input.value = document.getElementById(input.id + '_fc_value').getAttribute('data-fc-value');
2448					}
2449				}
2450
2451				return result;
2452			}
2453		}
2454	});

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.