PageSourceSearch

https://www.tabaknee.nl/components/com_flexicontent/assets/js/flexi-lib.js?5afc0ec9b25d4913a51ad5d5e70da8bd

js tabaknee.nl collected 2026-10-02 10:57:03 UTC 83,588 bytes, 2,450 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]).attr("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
1169		var totals_tag = jQuery('#columnchoose_totals');
1170		if (totals_tag.length)
1171		{
1172			totals_tag.html(col_selectors.length == col_selected.length ? '' : ' '+(col_selected.length - col_selectors.length));
1173		}
1174	}
1175
1176
1177	// *** Create column choosers row for a table. NOTE must have <th> cells at row 0
1178	function create_column_choosers(container_div_id, data_tbl_id, firstload, start_text, end_text, toggle_on_init)
1179	{
1180		// 1. Get column-status array for the table with id: data_tbl_id
1181		var show_col = eval('show_col_'+data_tbl_id);
1182
1183		// 2. Get table and its first row and then the 'th' cells in it
1184		var firstrow  = jQuery("#"+data_tbl_id+" thead tr:first"); //document.getElementById(data_tbl_id);
1185		var thcells = firstrow.find('th');
1186
1187		// 3. Iterate through the 'th' cells and create column hiders for those having the class 'hideOnDemandClass'
1188		var str = (typeof start_text != "undefined") ? start_text : '';
1189
1190		for (var col = 0; col < thcells.length; col++)
1191		{
1192			// 4. Skip if not having 'hideOnDemandClass' class
1193			if (!jQuery(thcells[col]).hasClass('hideOnDemandClass')) continue;
1194
1195			// 5. Get column name
1196			var column_toggle_lbl = jQuery(thcells[col]).find('.column_toggle_lbl');
1197			var col_display_name = column_toggle_lbl.length ? column_toggle_lbl.html() : jQuery(thcells[col]).text();
1198			column_toggle_lbl.remove();
1199
1200			// 6. Show / Hide current column
1201			if ( ( !firstload && !js_isset(show_col[col]) ) || ( jQuery(thcells[col]).hasClass('initiallyHidden') && !js_isset(show_col[col]) ) )
1202			{
1203				var checked_str = '';
1204				var fontcolor_str = 'black';//var fontcolor_str = '#aaaaaa';
1205
1206				// *** It has value of 0 or not set, set it to 1 for fc_tbl_toggle_column() function to hide it
1207				show_col[col] = 1;
1208				if (toggle_on_init) fc_tbl_toggle_column(container_div_id, data_tbl_id, col, 1);
1209			}
1210			else
1211			{
1212				var checked_str = 'checked="checked"';
1213				var fontcolor_str = 'black'; //var fontcolor_str = 'black';
1214
1215				// *** It has value of 1 or not set, set it to 0 for fc_tbl_toggle_column() function to show it
1216				show_col[col] = 0;
1217				if (toggle_on_init) fc_tbl_toggle_column(container_div_id, data_tbl_id, col, 1);
1218			}
1219
1220			// 7. Create column checkbox and append it to str
1221			var ffieldid   = 'columnchoose_' + data_tbl_id + '_' + col;
1222			var ffieldname = 'columnchoose_' + data_tbl_id + '[' + col + ']';
1223			str = str + '<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);">'
1224			+ '<label id="' + ffieldid + '_label" style="color:'+fontcolor_str+';" for="' + ffieldid + '">' + col_display_name + '</label>';
1225		}
1226
1227		// 8. Fill in 'column choose box'
1228		str = '<span id="'+container_div_id+'_cols">' + str + '</span>' + end_text;
1229		document.getElementById(container_div_id).innerHTML=str;
1230
1231		// 9. Update cookie data and display data
1232		column_toggles_update_data(container_div_id, data_tbl_id);
1233	}
1234
1235
1236	/* Set a cookie */
1237	function fclib_setCookie(cookieName, cookieValue, nDays, samesite="lax")
1238	{
1239		var today = new Date();
1240		var expires = new Date();
1241		var path = typeof jclient_path_fc != 'undefined' ? jclient_path_fc : window.location.hostname;
1242		if (nDays==null || nDays<0) nDays=0;
1243
1244		if (nDays)
1245		{
1246			expires.setTime(today.getTime() + 3600000*24*nDays);
1247			document.cookie = cookieName+"="+encodeURIComponent(cookieValue) + ";samesite=" + samesite + ";path=" + path + ";expires=" + expires.toGMTString();
1248		} else {
1249			// nDays 0 , aka no expiration date, keep cookie till browser closes
1250			document.cookie = cookieName+"="+encodeURIComponent(cookieValue) + ";samesite=" + samesite + ";path=" + path;
1251		}
1252		//alert(cookieName+"="+encodeURIComponent(cookieValue) + ";path=" + path);
1253	}
1254
1255
1256	/* Get a cookie */
1257	function fclib_getCookie(cookieName)
1258	{
1259		let matched = document.cookie.match(new RegExp('(^|;\\s*)' + cookieName + '=([^;]+)'));
1260		return matched ? decodeURIComponent(matched[2]) : '';
1261	}
1262
1263
1264	/* Get a new list of 2-typles (key, element) living inside a cookie
1265	 * List is an object with 'key' being the property name and 'element' being the value of the property
1266	 */
1267	function fclib_createCookieList(cookieName, nDays)
1268	{
1269		var cookieValue = fclib_getCookie(cookieName);
1270		var items = jQuery.parseJSON(cookieValue ? cookieValue : '{}');
1271		nDays = nDays || 90;
1272
1273		return {
1274			// Add to the items
1275			"add": function(key, element)
1276			{
1277				items[key] = element;
1278				fclib_setCookie(cookieName, JSON.stringify(items), nDays);
1279			},
1280
1281			// Remove from items
1282			"remove": function (key)
1283			{
1284				if (typeof items[key] != 'undefined')
1285				{
1286					delete items[key];
1287					fclib_setCookie(cookieName, JSON.stringify(items), nDays);
1288				}
1289			},
1290
1291			// Clear items
1292			"clear": function()
1293			{
1294				items = {};
1295				fclib_setCookie(cookieName, JSON.stringify(items), nDays);
1296			},
1297
1298			// Get all the items
1299			"items": function()
1300			{
1301				return items;
1302			}
1303		}
1304	}
1305
1306
1307	/* Remove known textarea editors, optionally add a flag to restore editors laters */
1308	function fc_removeAreaEditors(txtareas, addRestoreClass)
1309	{
1310		addRestoreClass = typeof addRestoreClass != 'undefined' ? addRestoreClass : 0;
1311
1312		// Remove known JS editors
1313		txtareas.each(function(i, txtarea)
1314		{
1315			var areaid = jQuery(txtarea).attr('id');
1316
1317			var hasTinyMCE = !!(!areaid || typeof tinyMCE === 'undefined' ? false : tinyMCE.get(areaid));
1318			if (hasTinyMCE) {
1319				tinyMCE.majorVersion >= 4 ?
1320					tinymce.remove('#'+areaid) :
1321					tinymce.EditorManager.execCommand('mceRemoveEditor', false, areaid) ;
1322				if (addRestoreClass) jQuery(txtarea).addClass('fc_hadTinyMCE');
1323			}
1324
1325			var hasCodeMirror = typeof CodeMirror === 'undefined' ? false : jQuery(txtarea).first().next().hasClass('CodeMirror');
1326			if (hasCodeMirror) {
1327				jQuery(txtarea).first().next().get(0).CodeMirror.toTextArea();
1328				if (addRestoreClass) jQuery(txtarea).addClass('fc_hadCodeMirror');
1329
1330				// Remove codemirror container in case that removing it failed
1331				if (jQuery(txtarea).first().next('.CodeMirror').length) jQuery(txtarea).first().next('.CodeMirror').remove();
1332			}
1333		});
1334	}
1335
1336
1337	/* Restore known textarea editors to textareas with respective class */
1338	function fc_restoreAreaEditors(txtareas)
1339	{
1340		fc_attachTinyMCE( txtareas.filter('.fc_hadTinyMCE') );
1341		fc_attachCodeMirror( txtareas.filter('.fc_hadCodeMirror') );
1342	}
1343
1344
1345	/* Attach tinyMCE with default settings */
1346	function fc_attachTinyMCE(txtareas, MCEoptions, mceFieldname)
1347	{
1348		// Create a copy of the options,  so that we do not modify the original options objet
1349		var _MCEoptions = typeof MCEoptions!=='undefined' ? jQuery.extend({}, MCEoptions) : {};
1350		mceFieldname = typeof mceFieldname!=='undefined' ? mceFieldname : false;
1351
1352		var theArea, areaID;
1353		txtareas.each(function(i, txtarea)
1354		{
1355			var theArea = jQuery(txtarea);
1356			var areaID = theArea.attr('id');
1357
1358			// Remove tinymce classes from the textarea
1359			theArea.removeClass('mce_editable');
1360
1361			// J3.7.0+ (with integrated XTD buttons) is using new setup method
1362			if (typeof mceFieldname!=='undefined' && typeof Joomla.JoomlaTinyMCE != 'undefined' && typeof MCEoptions.joomlaExtButtons != 'undefined')
1363			{
1364				var tinyMCEOptions = {};
1365				tinyMCEOptions[mceFieldname] = _MCEoptions;
1366				Joomla.JoomlaTinyMCE.setupEditor(jQuery('#' + areaID).get(0), {tinyMCE: tinyMCEOptions});
1367			}
1368			else if (tinyMCE.majorVersion >= 4)
1369			{
1370				_MCEoptions.selector = '#' + areaID;
1371				tinyMCE.init(_MCEoptions);
1372				//tinyMCE.EditorManager.execCommand('mceAddEditor', true, areaID);  //var editor = tinyMCE.get(areaid);  //tinyMCE.editors[areaID];
1373			}
1374			else
1375			{
1376				_MCEoptions.mode = 'exact';
1377				_MCEoptions.elements = areaID;
1378				tinyMCE.init(_MCEoptions);
1379				//tinyMCE.EditorManager.execCommand('mceAddControl', true, areaID);  //var editor = tinymce.EditorManager.get(area_id);  // tinyMCE.editors[areaID];
1380			}
1381
1382			// Some cleanups for proper display
1383			theArea.addClass('mce_editable');
1384			theArea.parent().children('.mce-container').css('display', '');   // allow editor container to stretch to the full width of its external container
1385		});
1386
1387		return txtareas.length==1 ? tinyMCE.get(areaID) : true;
1388	}
1389
1390
1391	/* Attach CodeMirror with optional settings */
1392	function fc_attachCodeMirror(txtareas, CMoptions)
1393	{
1394		CMoptions = typeof CMoptions!=='undefined' ? CMoptions : {
1395			mode: 'application/x-httpd-php',
1396			indentUnit: 2,
1397			lineNumbers: true,
1398			matchBrackets: true,
1399			lineWrapping: true,
1400			onCursorActivity: function(CM)
1401			{
1402				CM.setLineClass(hlLine, null);
1403				hlLine = CM.setLineClass(CM.getCursor().line, 'activeline');
1404			}
1405		};
1406
1407		var editor, theArea;
1408		txtareas.each(function(i, txtarea)
1409		{
1410			theArea = jQuery(txtarea);
1411			theArea.removeClass(); // Remove all classes from the textarea
1412			editor = CodeMirror.fromTextArea(theArea.get(0), CMoptions);
1413			editor.refresh();
1414		});
1415
1416		return txtareas.length==1 ? editor : true;
1417	}
1418
1419
1420	/* Toggle box via a button and set CSS class to indicate that it is open  */
1421	function fc_toggle_box_via_btn(theBox, btn, btnClass, btnNew, mode, cookieName)
1422	{
1423		var box = typeof theBox=='string' ? jQuery('#'+theBox) : theBox;
1424
1425		if (btnNew)
1426		{
1427			// Show but remove 'display' property to allow CSS classes to take effect (possibly hiding it)
1428			btnNew.show().css('display', '');
1429			jQuery(btn).hide();
1430		}
1431		var cookieName = !!cookieName ? (typeof cookieName=='string' ? cookieName : theBox + '-disp') : false;
1432		//window.console.log(cookieName);
1433
1434		if (
1435			(typeof mode!=='undefined' && parseInt(mode)) ||  // use the mode provided
1436			(typeof mode==='undefined' && box.is(':hidden'))  // if any of the elements collection 'box' is hidden then open them all
1437		) {
1438			if (cookieName) fclib_setCookie(cookieName, 1, 0);  // nDays 0 , aka no expiration date, keep cookie till browser closes
1439			jQuery(btn).data('fc_noeffect') || jQuery(btn).hasClass('fc_noeffect') ?
1440				box.show() :
1441				box.slideDown(400) ;
1442			// Only remove 'data-fc_noeffect' but not the fc_noeffect class
1443			jQuery(btn).addClass(btnClass).data('fc_noeffect', null);
1444		}
1445		else
1446		{
1447			if (cookieName) fclib_setCookie(cookieName, 0, 0);  // nDays 0 , aka no expiration date, keep cookie till browser closes
1448			jQuery(btn).data('fc_noeffect') || jQuery(btn).hasClass('fc_noeffect') ?
1449				box.hide() :
1450				box.slideUp(400) ;
1451			// Only remove 'data-fc_noeffect' but not the fc_noeffect class
1452			jQuery(btn).removeClass(btnClass).data('fc_noeffect', null);
1453		}
1454	}
1455
1456
1457	/* Disable/enable a check box group e.g. when 'Use Global' option is toggled */
1458	function fc_toggle_checkbox_group(containerID, useGlobalElement)
1459	{
1460		var container = jQuery('#'+containerID);
1461		var inputs = container.find('input');
1462		el = jQuery(useGlobalElement);
1463		if ( el.is(':checked') ) {
1464			inputs.each(function(index){
1465				jQuery(this).attr('disabled', true);
1466			});
1467		} else {
1468			inputs.each(function(index){
1469				jQuery(this).attr('disabled', false);
1470			});
1471		}
1472		el.attr('disabled', false);
1473	}
1474
1475
1476	/* Auto-resize the currently open dialog vertically or horizontally */
1477	function fc_dialog_resize_now(boxWsel, boxHsel, contentSel)
1478	{
1479		boxWsel = typeof boxWsel != 'undefined' ? boxWsel : '.ui-dialog.fc-autow-dialog';
1480		boxHsel = typeof boxHsel != 'undefined' ? boxHsel : '.ui-dialog.fc-autoh-dialog';
1481		contentSel = typeof contentSel != 'undefined' ? contentSel : '.ui-dialog-content';
1482
1483		// Resize the modal(s)
1484		var dialogs = jQuery(boxWsel + ' , '+ boxHsel);
1485		dialogs.each(function( index )
1486		{
1487			var dialog_box = jQuery(this);
1488			var theDialog = dialog_box.data('fc_dialog');
1489			if (theDialog)
1490			{
1491				var params = theDialog.data('fc_dialog_params');
1492				var maxwidth  = theDialog.data('fc_dialog_maxwidth');
1493				var maxheight = theDialog.data('fc_dialog_maxheight');
1494				params = params || {};
1495				maxwidth  = maxwidth || 0;
1496				maxheight = maxheight || 0;
1497				params = fc_getAutoSizePos(maxwidth, maxheight, params);
1498
1499				dialog_box
1500					.css({ 'left': params.winleft+'px', 'width': params.winwidth+'px' })
1501					.css({ 'top': params.wintop+'px', 'height': params.winheight+'px' });
1502			}
1503		});
1504
1505		// Parameters for modal that should take full window
1506		var dialog_box_sbox = jQuery('#sbox-window');
1507		var dialog_box_jmedia = jQuery('div.field-media-wrapper > div.modal');
1508
1509		if (dialog_box_sbox.length || dialog_box_jmedia.length)
1510		{
1511			params = fc_getAutoSizePos(0, 0, {});
1512
1513			// Maximize sbox modal
1514			if (dialog_box_sbox.length)
1515			{
1516				dialog_box_sbox
1517					.css({ 'position': 'fixed' })
1518					.css({ 'left': params.winleft+'px', 'width': (params.winwidth-40)+'px' })
1519					.css({ 'top': params.wintop+'px', 'height': (params.winheight-20)+'px' })
1520					.find('#sbox-content').children('iframe')
1521						.removeAttr('width').removeAttr('height').css({ 'width': '100%', 'height': '100%' });
1522			}
1523
1524			// Maximize joomla media modal
1525			if (dialog_box_jmedia.length)
1526			{
1527				dialog_box_jmedia.css({ 'top': (params.wintop-30)+'px', 'height': (params.winheight-60)+'px' });
1528			}
1529		}
1530
1531		// Fix modal's inner container adding unneeded scrollbar
1532		var dialogs = jQuery(boxHsel);
1533		dialogs.each(function( index )
1534		{
1535			var dialog_box = jQuery(this);
1536			var content_box = dialog_box.find(contentSel);
1537			var h = dialog_box.height() - content_box.prev().outerHeight(true) - content_box.next().outerHeight(true);
1538			content_box.css({ 'height': h+'px', 'margin': '0', 'box-sizing': 'border-box' });
1539
1540			// Remove unneeded scroll bar inside iframe document, new browser security disallows this ?
1541			//var body = content_box.find('iframe').contents().find('body');
1542			//body.css({ 'height': 'unset' });
1543		});
1544	}
1545
1546
1547	/* Debounced function execution by the given 'wait' time, forcing single function execution within the given 'wait' time */
1548	function fc_debounce_exec(func, wait, immediate, func_context)
1549	{
1550		var timeout;
1551		return function() {
1552			var context = func_context || this, args = arguments;
1553			var later = function() {
1554				timeout = null;
1555				if (!immediate) func.apply(context, args);
1556			};
1557			var callNow = immediate && !timeout;
1558			clearTimeout(timeout);
1559			timeout = setTimeout(later, wait);
1560			if (callNow) func.apply(context, args);
1561		};
1562	}
1563
1564	var fc_dialog_resize = fc_debounce_exec(fc_dialog_resize_now, 200, false, null);
1565
1566	jQuery(window).resize(function() {
1567		fc_dialog_resize();
1568	});
1569
1570
1571	/* Apply select2 JS to targeted elements */
1572	function fc_attachSelect2(sel, s2_elems)
1573	{
1574		sel      = typeof sel !== 'undefined' && sel ? sel : 'body';
1575		var sbox = jQuery(sel);
1576
1577		s2_elems = s2_elems || sbox.find('select.use_select2_lib');
1578
1579		if (window.skip_select2_js)
1580		{
1581			s2_elems.removeClass('use_select2_lib').addClass('fc_isselect fc_ismobile fc_no_js_attach');
1582			//s2_elems.filter(function(){return !jQuery(this).attr('multiple');})
1583			sbox.find('.fc_mobile_label').show();
1584			return;
1585		}
1586
1587
1588		// Attach select2 to specific to select elements having specific CSS class, for select-multiple show values as togglable checkboxes
1589		s2_elems.each(function()
1590		{
1591			function fc_formatSel2Option(item)
1592			{
1593				var data_title = jQuery(item.element).attr('data-title');
1594				return !data_title ? item.text : "<div title='" + data_title + "'>" + item.text + "</div>";
1595			}
1596
1597			var sel_EL = jQuery(this);
1598
1599			var sel2ops = {
1600				minimumResultsForSearch: 10,
1601				formatResult: fc_formatSel2Option
1602			};
1603
1604			if ( sel_EL.hasClass('fc_select2_noselect') )
1605			{
1606				sel2ops.formatNoMatches = function() { return ''; };
1607				sel2ops.dropdownCssClass = 'select2-hidden';
1608				sel2ops.minimumResultsForSearch = -1;
1609			}
1610			if (sel_EL.attr('multiple') && !sel_EL.hasClass('fc_select2_no_check'))
1611			{
1612				sel2ops.closeOnSelect = false;
1613			}
1614
1615			if (sel_EL.attr('multiple') && !sel_EL.hasClass('fc_select2_no_check'))
1616				sel_EL.select2(sel2ops).addClass('select2_fc_checkboxes');
1617			else
1618				sel_EL.select2(sel2ops);
1619
1620			// Set initially selected data (this allows setting order too)
1621			if ( sel_EL.get(0).hasAttribute('data-select2-initdata') )
1622			{
1623				sel_EL.select2('data', sel_EL.data('select2-initdata'));
1624			}
1625
1626			// Make sortable if requested
1627			if ( sel_EL.hasClass('fc_select2_sortable') )
1628			{
1629				sel_EL.select2_sortable();
1630			}
1631		});
1632
1633
1634		// Customization of SELECT2 JS selectors
1635		sbox.find('div.use_select2_lib').each(function()
1636		{
1637			var el_container = jQuery(this);
1638			var sel_EL = el_container.next('select');
1639
1640
1641			// MULTI-SELECT2: Initialize internal labels, placing the label so that it overlaps the text filter box
1642			var fc_label_text = sel_EL.attr('data-fc_label_text');
1643			if (!fc_label_text) fc_label_text = sel_EL.attr('fc_label_text');
1644			if (fc_label_text)
1645			{
1646				var _label = (fc_label_text.length >= 30) ? fc_label_text.substring(0, 28) + '...' : fc_label_text;
1647
1648				jQuery('<span/>', {
1649					'class': 'fc_has_inner_label fc_has_inner_label_select2',
1650					'text': _label
1651				}).prependTo(el_container.find('.select2-search-field'));
1652			}
1653
1654
1655			// MULTI-SELECT2: Initialize internal prompts, placing the prompt so that it overlaps the text filter box
1656			var fc_prompt_text = sel_EL.attr('data-fc_prompt_text');
1657			if (!fc_prompt_text) fc_prompt_text = sel_EL.attr('fc_prompt_text');
1658			if (fc_prompt_text)
1659			{
1660				var _prompt = (fc_prompt_text.length >= 30) ? fc_prompt_text.substring(0, 28) + '...' : fc_prompt_text;
1661
1662				jQuery('<span/>', {
1663					'class': 'fc_has_inner_prompt fc_has_inner_prompt_select2',
1664					'text': _prompt
1665				}).prependTo(el_container.find('.select2-search-field')).hide();
1666			}
1667
1668
1669			// SINGLE-SELECT2: Highlight selects with an active value
1670			if ( ! sel_EL.attr('multiple') && sel_EL.hasClass('fc_add_highlight') )
1671			{
1672				var el = el_container.find('.select2-choice');
1673				var val = sel_EL.val();
1674				if (val === null) {
1675					//el.addClass('fc_highlight_disabled');
1676				} else if (!!val && val.length) {
1677					el.addClass('fc_highlight');
1678				} else {
1679					el.removeClass('fc_highlight');
1680				}
1681			}
1682		});
1683
1684
1685		// MULTI-SELECT2:
1686		s2_elems.on('select2-open', function()
1687		{
1688
1689			// Add events to handle focusing the text filter box (hide inner label)
1690			var sel_EL = jQuery(this);
1691			var el = sel_EL.parent().find('.select2-input');
1692			var el_label = el.prevAll('.fc_has_inner_label');
1693			if (el_label) el_label.hide();
1694			var el_prompt = el.prevAll('.fc_has_inner_prompt');
1695			if (el_prompt) el_prompt.show();
1696
1697
1698			// Allow listing already selected options WHEN having class 'select2_fc_checkboxes'
1699			if (sel_EL.hasClass('select2_fc_checkboxes')) {
1700				var els = jQuery('#select2-drop').find('.select2-selected');
1701				els.addClass('select2-selected-visible').removeClass('select2-selected');
1702
1703				// Suppress the onchange attribute while selector is open
1704				sel_EL.data('onchange', sel_EL.attr('onchange'));
1705				sel_EL.removeAttr('onchange');
1706			}
1707
1708
1709		}).on('select2-close', function(e)
1710		{
1711
1712			// Add events to handle bluring the text filter box (show inner label)
1713			var sel_EL = jQuery(this);
1714
1715			// Re-open if this was closed while selecting, FOR select2 v4, use 'select2:closing' instead
1716			if ( sel_EL.data('field_being_selected') )
1717			{
1718				sel_EL.select2('open');
1719				jQuery('#select2-drop').find('.select2-no-results').remove();
1720				return;
1721			}
1722
1723			var el = sel_EL.parent().find('.select2-input');
1724			var el_label = el.prevAll('.fc_has_inner_label');
1725			if (el_label) el_label.show();
1726			var el_prompt = el.prevAll('.fc_has_inner_prompt');
1727			if (el_prompt) el_prompt.hide();
1728
1729			// Restore already selected options state
1730			if (sel_EL.hasClass('select2_fc_checkboxes')) {
1731				var els = jQuery('#select2-drop').find('.select2-selected-visible');
1732				els.removeClass('select2-selected-visible').addClass('select2-selected');
1733			}
1734
1735
1736			// Restore the onchange attribute when selector closes, running the JS if changes were made
1737			if ( sel_EL.data('onchange') )
1738			{
1739				sel_EL.attr('onchange', sel_EL.data('onchange'));
1740				sel_EL.data('onchange', null);
1741			}
1742
1743			// Trigger change event on close if this was prevented
1744			if ( sel_EL.data('field_being_edited') )
1745			{
1746				sel_EL.data('field_being_edited', null);
1747				sel_EL.trigger('change');
1748				// LEGACY browsers ??
1749				/*if ( sel_EL.attr('onchange') )
1750				{
1751					eval(sel_EL.attr('onchange'));
1752				}*/
1753			}
1754
1755
1756		}).on('select2-selecting', function(e)
1757		{
1758
1759			// Handle toggling MULTI-SELECT2 as checkboxes
1760			var sel_EL = jQuery(this);
1761			if ( sel_EL.attr('multiple') && sel_EL.hasClass('select2_fc_checkboxes') )
1762			{
1763				var vals = sel_EL.val();
1764				var nVals = new Array();
1765				var i = 0;
1766				var found = 0;
1767				if (vals && vals.length)
1768				{
1769					for (var n = 0; n < vals.length; ++n)
1770					{
1771						if (vals[n] != e.val) nVals[i++] = vals[n];
1772						else found = 1;
1773					}
1774				}
1775
1776				if (!found) nVals[i++] = e.val;
1777				sel_EL.data('set_selected_values', nVals);
1778				sel_EL.data('field_being_edited', true);
1779				sel_EL.data('field_being_selected', true);
1780			}
1781
1782
1783		}).on('change', function(e)
1784		{
1785
1786			// SELECT2: Handle change event
1787			var sel_EL = jQuery(this);
1788
1789			// 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'
1790			sel_EL.data('field_being_selected', null);
1791			if (sel_EL.data('field_being_edited'))
1792			{
1793				e.preventDefault();
1794				e.stopPropagation();
1795				e.stopImmediatePropagation();
1796			}
1797
1798			var nVals = sel_EL.data('set_selected_values');
1799			if (nVals)
1800			{
1801				jQuery('#select2-drop').find('.select2-selected-visible, .select2-selected').removeClass('select2-selected-visible').removeClass('select2-selected');
1802				jQuery('#select2-drop').find('.select2-selected').removeClass('select2-selected-visible').removeClass('select2-selected');
1803
1804				// Specific values were requested to be set, but first clear data value, to prevent loop in the change event
1805				sel_EL.data('set_selected_values', null).select2('val', nVals).trigger('change');
1806			}
1807
1808
1809			// SINGLE-SELECT2:
1810			if ( ! sel_EL.attr('multiple') && sel_EL.hasClass('fc_add_highlight') )
1811			{
1812				// Handle highlighting selected value
1813				var el = jQuery(this).prev('div').find('.select2-choice');
1814				var val = sel_EL.val();
1815				if (!!val && val.length) {
1816					el.addClass('fc_highlight');
1817				} else {
1818					el.removeClass('fc_highlight');
1819				}
1820			}
1821
1822
1823			// MULTI-SELECT2:
1824			if ( sel_EL.attr('multiple') )
1825			{
1826				// Allow listing already selected options WHEN having class 'select2_fc_checkboxes'
1827				if (jQuery(this).hasClass('select2_fc_checkboxes')) {
1828					var els = jQuery('#select2-drop').find('.select2-selected');
1829					els.addClass('select2-selected-visible').removeClass('select2-selected');//.removeClass('select2-result-selectable');
1830				}
1831			}
1832
1833		});
1834
1835
1836		// MULTI-SELECT2: Handle highlighting selected value
1837		sbox.find('div.use_select2_lib.select2-container-multi input').on('keydown', function()
1838		{
1839			var el = jQuery(this);
1840			setTimeout(function() {
1841				var val = el.val();
1842				if (!!val && val.length) {
1843					var el_prompt = el.prevAll('.fc_has_inner_prompt');
1844					if (el_prompt) el_prompt.hide();
1845				} else {
1846					var el_prompt = el.prevAll('.fc_has_inner_prompt');
1847					if (el_prompt) el_prompt.show();
1848				}
1849			}, 0);
1850		});
1851
1852
1853		// SELECT2: scrollbar wrap problem
1854		s2_elems.on('loaded open', function()
1855		{
1856			var ul = jQuery('#select2-drop ul.select2-results');
1857			var needsScroll= ul.prop('scrollHeight') > ul.prop('clientHeight');
1858			if (needsScroll) ul.css('overflow-y', 'scroll');
1859			else  ul.css('overflow-y', 'auto');
1860		});
1861	}
1862
1863
1864	/* Valid HTML for legacy modals using rel, TODO: add more */
1865	jQuery(document).ready(function() {
1866		jQuery(".modal").each(function(i, el) {
1867			if (jQuery(el).get(0).hasAttribute("data-rel")) jQuery(el).attr("rel", jQuery(el).attr("data-rel"));
1868		});
1869	});
1870
1871
1872	/* Restore the given form fields values into the given form */
1873	function fc_restore_form_field_values(form, data)
1874	{
1875		if (!data)
1876		{
1877			form[0].reset();
1878
1879			var elements = Array.from(form[0].elements);
1880	 	 	for (var i = 0, l = elements.length; i < l; i++)
1881	 	 	{
1882				var el = jQuery(elements[i]);
1883				var tagName = elements[i].tagName;
1884				var inputType = tagName != 'INPUT' ? false : elements[i].type;
1885				if (tagName == 'SELECT' || (inputType && inputType!='BUTTON'))
1886				{
1887					el.trigger('change');
1888				}
1889			}
1890
1891			return;
1892		}
1893
1894		jQuery.each(data, function(name, val)
1895		{
1896			var el = form.find('[name="'+name+'"]'),
1897			type = el.attr('type');
1898
1899			switch(type)
1900			{
1901				case 'checkbox':  el.attr('checked', 'checked'); break;
1902				case 'radio':     el.filter('[value="'+val+'"]').attr('checked', 'checked'); break;
1903				default: el.val(val);
1904			}
1905			el.trigger('change');
1906		});
1907	}
1908
1909
1910	if (typeof jQuery.fn.serializeObject == "undefined")
1911	{
1912		jQuery.fn.serializeObject = function()
1913		{
1914			var o = {};
1915			var a = this.serializeArray();
1916			jQuery.each(a, function() {
1917				if (o[this.name]) {
1918					if (!o[this.name].push) {
1919						o[this.name] = [o[this.name]];
1920					}
1921					o[this.name].push(this.value || '');
1922				} else {
1923					o[this.name] = this.value || '';
1924				}
1925			});
1926			return o;
1927		}
1928	}
1929
1930
1931	if (typeof fc_isIE == "undefined")
1932	{
1933		window.fc_ie_version_number = null;
1934		fc_isIE = function()
1935		{
1936			if (window.fc_ie_version_number === null)
1937			{
1938				var userAgent = navigator.userAgent.toLowerCase();
1939				window.fc_ie_version_number = (userAgent.indexOf('msie') != -1) ? parseInt(userAgent.
1939split('msie')[1]) : false;
1940			}
1941			return window.fc_ie_version_number;
1942		}
1943	}
1944
1945
1946	window.fc_has_flash_addon_enabled = false;
1947	fc_has_flash_addon = function()
1948	{
1949		try {
1950			window.fc_has_flash_addon_enabled = Boolean(new ActiveXObject("ShockwaveFlash.ShockwaveFlash"));
1951		} catch(exception) {
1952			window.fc_has_flash_addon_enabled = ("undefined" != typeof navigator.mimeTypes["application/x-shockwave-flash"]);
1953		}
1954		return window.fc_has_flash_addon_enabled;
1955	}
1956
1957
1958	function fc_remove_non_alphanum_chars(text, allowed_chars)
1959	{
1960		var result = '';
1961		if (!text) return result;
1962		allowed_chars = typeof allowed == 'undefined' ? '' : allowed_chars;
1963
1964		var num_reg = new RegExp('[0-9]+$');
1965		var allowed_reg = new RegExp('['+allowed_chars+']+$');
1966
1967		var lower = text.toLowerCase();
1968		var upper = text.toUpperCase();
1969
1970		for (var i = 0; i < lower.length; ++i)
1971		{
1972			if (num_reg.test(text[i]) || (lower[i] != upper[i]) || (lower[i].trim() === ''))
1973			{
1974				result += text[i];
1975			}
1976			else if (allowed_chars.length && allowed_reg.test(text[i]))
1977			{
1978				result += text[i];
1979			}
1980		}
1981		return result;
1982	}
1983
1984
1985	function fc_attachSingleSlider(cfg, type)
1986	{
1987		var slider_tag_id = cfg.name + (cfg.sfx || '');
1988		var slider = document.getElementById(slider_tag_id + '_nouislider');
1989		//if (jQuery(slider).length) { window.console.log('not found slider ID: ' + slider_tag_id + '_nouislider'); return; }
1990
1991		var selectSL = jQuery('#' + slider_tag_id + '-sel');
1992		var inputSL = document.getElementById(slider_tag_id + '-val');
1993		var isSingle = 1;
1994		!!type || (type = 2);  // 1: drop-down select , 2: slider (default type)
1995
1996		var IEversion = fc_isIE();
1997		var is_IE8 = IEversion && IEversion < 9;
1998		var use_select = is_IE8 || type == 1;
1999		if (use_select)
2000		{
2001			if (selectSL.length)
2002			{
2003				var selected;
2004				jQuery.each(cfg.step_values, function(key, value)
2005				{
2006					selectSL.append(
2007						jQuery('<option></option>')
2008							.attr('value', cfg.step_values[key])
2009							.text(cfg.step_labels[key])
2010					);
2011				});
2012
2013				selectSL
2014					.val(cfg.step_values[cfg.initial_pos])
2015					.on('change', function(e, data) {
2016						var slider_input = jQuery('#' + slider_tag_id + '-val').get(0);
2017
2018						if (cfg.element_class_list != '')
2019						{
2020							jQuery(cfg.element_selector).removeClass(cfg.element_class_list).addClass(cfg.element_class_prefix + selectSL.val());
2021						}
2022						if (cfg.elem_container_class != '')
2023						{
2024							jQuery(cfg.elem_container_selector).removeClass(cfg.elem_container_class).addClass(cfg.elem_container_prefix + selectSL.val());
2025						}
2026
2027						fclib_setCookie(slider_input.name, selectSL.val(), 0);  // nDays 0 , aka no expiration date, keep cookie till browser closes
2028					})
2029					.css('display', '');  // avoid show() it adds display: block
2030			}
2031			return;
2032		}
2033		slider.style.display = '';
2034
2035		var step_values = cfg.step_values;
2036		var step_labels = cfg.step_labels;
2037
2038		noUiSlider.create(slider, {
2039			start: cfg.initial_pos,
2040			connect: false,
2041			step: 1,
2042			range: {'min': 0, 'max': (cfg.step_values.length - 1)},
2043		});
2044		if (!!cfg.start_hidden) jQuery(slider).hide();
2045
2046		var tipHandles = slider.getElementsByClassName('noUi-handle'),
2047		tooltips = [];
2048
2049		// Add divs to the slider handles.
2050		for ( var i = 0; i < tipHandles.length; i++ )
2051		{
2052			tooltips[i] = document.createElement('span');
2053			tipHandles[i].appendChild(tooltips[i]);
2054
2055			tooltips[i].className += 'fc-sliderTooltip fc-bottom'; // Add a class for styling
2056			tooltips[i].innerHTML = '<span></span>'; // Add additional markup
2057			tooltips[i] = tooltips[i].getElementsByTagName('span')[0];  // Replace the tooltip reference with the span we just added
2058		}
2059
2060		// When the slider changes, display the value in the tooltips and set it into the input form elements
2061		slider.noUiSlider.on('update', function( values, handle ) {
2062			var value = parseInt(values[handle]);
2063			var i = value;
2064
2065			if ( handle ) {
2066				alert('This slider does not have a right handler');
2067			} else {
2068				inputSL.value = typeof step_values[value] !== 'undefined' ? step_values[value] : value;
2069			}
2070			var tooltip_text = typeof step_labels[value] !== 'undefined' ? step_labels[value] : value;
2071			var max_len = 36;
2072			tooltips[handle].innerHTML = '<span class=\"icon-image\"></span> ' +
2073				(tooltip_text.length > max_len+4 ? tooltip_text.substring(0, max_len)+' ...' : tooltip_text);
2074			var left  = jQuery(tooltips[handle]).closest('.noUi-origin').position().left;
2075			var width = jQuery(tooltips[handle]).closest('.noUi-base').width();
2076
2077			//window.console.log ('handle: ' + handle + ', left : ' + left + ', width : ' + width);
2078			if (isSingle) {
2079				left<(50/100)*width ?
2080					jQuery(tooltips[handle]).parent().removeClass('fc-left').addClass('fc-right') :
2081					jQuery(tooltips[handle]).parent().removeClass('fc-right').addClass('fc-left');
2082			}
2083			else {
2084				alert('This slider only has 1 handle');
2085			}
2086		});
2087
2088		// Handle form autosubmit
2089		slider.noUiSlider.on('change', function() {
2090			var slider = jQuery('#' + slider_tag_id + '_nouislider');
2091			var slider_input = jQuery('#' + slider_tag_id + '-val').get(0);
2092
2093			if (cfg.element_class_list != '')
2094			{
2095				jQuery(cfg.element_selector).removeClass(cfg.element_class_list).addClass(cfg.element_class_prefix + slider_input.value);
2096			}
2097			if (cfg.elem_container_class != '')
2098			{
2099				jQuery(cfg.elem_container_selector).removeClass(cfg.elem_container_class).addClass(cfg.elem_container_prefix + slider_input.value);
2100			}
2101
2102			fclib_setCookie(slider_input.name, slider_input.value, 0);  // nDays 0 , aka no expiration date, keep cookie till browser closes
2103		});
2104
2105		inputSL.addEventListener('change', function(){
2106			var value = 0;  // default is first value = empty
2107			for(var i=1; i<step_values.length-1; i++)
2108			{
2109				if (step_values[i] == this.value) { value=i; break; }
2110			}
2111			slider.noUiSlider.set([value, null]);
2112		});
2113	}
2114
2115
2116
2117	// Toggle elements of all other views when switching between views (e.g. details / thumbnails views)
2118	function fc_toggle_view_mode(new_btn)
2119	{
2120		var btns = new_btn.parent().children();
2121
2122		btns.removeClass('active');
2123		btns.each(function(index, value)
2124		{
2125			var btn = jQuery(value);
2126			jQuery(btn.data('toggle_selector')).hide();
2127		});
2128
2129		new_btn.addClass('active');
2130		jQuery(new_btn.data('toggle_selector')).show().css('display', '').css('visibility', '');
2131	}
2132
2133
2134
2135	// Create a sanitized filename similar to what server-side code will accept
2136	fc_sanitize_filename = function(text)
2137	{
2138		if (!text) return '';
2139
2140		var validChars = new RegExp('[A-Za-z0-9\.\_\-]+');
2141		var result = '';
2142
2143		for (var i = 0; i < text.length; ++i)
2144		{
2145			if (validChars.test(text[i]))
2146			{
2147				result += text[i];
2148			}
2149		}
2150		return result;
2151	}
2152
2153
2154
2155	// Submit a form in parent window from a jQuery modal
2156	fc_parent_form_submit = function(container_id, form_id, fields, options)
2157	{
2158		var form = window.parent.document.getElementById(form_id);
2159		var task = typeof options.task !== 'undefined'  ?  options.task  :  false;
2160		var is_list = typeof options.is_list !== 'undefined'  ?  options.is_list  :  false;
2161		var is_jform = typeof options.is_jform !== 'undefined'  ?  options.is_jform  :  false;
2162
2163		for (var field_name in fields)
2164		{
2165			form.elements[field_name].value = fields[field_name];
2166		}
2167
2168		if (is_list && form.boxchecked.value==0)
2169		{
2170			alert(Joomla.JText._('FLEXI_NO_ITEMS_SELECTED'));
2171			return;
2172		}
2173
2174		// ***
2175		// *** Submit progress bar
2176		// ***
2177
2178		if (typeof form.progressbar_submit_handler_success === 'undefined' || !form.progressbar_submit_handler_success)
2179		{
2180			form.progressbar_submit_handler_success = true;
2181			// Default to doing animation
2182			form.doanimated_submit = typeof form.doanimated_submit === 'undefined' ? 1 : 0;
2183
2184			form.addEventListener('submit', function(e)
2185			{
2186				if ( !form.doanimated_submit )
2187				{
2188					return true;
2189				}
2190				//form.doanimated_submit = 0;  // Clear FLAG until next time Joomla.submitform() is called
2191
2192				var fc_filter_form_blocker = jQuery('#fc_filter_form_blocker');
2193
2194				if (!fc_filter_form_blocker.length)
2195				{
2196					fc_filter_form_blocker = jQuery(
2197						'<div id="fc_filter_form_blocker">' +
2198							'<div class="fc_blocker_overlay"></div>' +
2199							'<div class="fc_blocker_content">' +
2200								'<div class="fc_blocker_logo></div>' +
2201								'<div class="fc_blocker_mssg>' + Joomla.JText._('FLEXI_LOADING') + '</div>' +
2202								'<div class="fc_blocker_bar"><div></div></div>' +
2203							'</div>' +
2204						'</div>'
2205					).prependTo(jQuery('body'));
2206				}
2207
2208				if (fc_filter_form_blocker)
2209				{
2210					fc_filter_form_blocker.css("display", "block");
2211					fc_filter_form_blocker.find('.fc_blocker_mssg').html(Joomla.JText._('FLEXI_LOADING'));
2212					fc_admin_progress(95, jQuery('#fc_filter_form_blocker .fc_blocker_bar'));
2213				}
2214			});
2215		}
2216
2217		// Close modal to allow click events to work on the parent window 's form
2218		jQuery('#'+container_id).dialog('close');
2219
2220		// If it is a Joomla form
2221		if (is_jform)
2222		{
2223			window.parent.Joomla.submitbutton(task);
2224			return;
2225		}
2226
2227		// For proper submit, append a hidden submit button to the form and click it, then remove it if submit was prevented
2228		var button = document.createElement('input');
2229		button.style.display = 'none';
2230		button.type = 'submit';
2231		form.appendChild(button).click();
2232		form.removeChild(button);
2233	}
2234
2235
2236
2237	//*******************************
2238	//*** START OF fc_init*() methods
2239	//*******************************
2240
2241
2242	/*
2243	 * Initialize CSS styles and effects
2244	 */
2245	function fc_initDynamicLayoutJsCss(container_id, reAddOnEvents, html)
2246	{
2247		const container_sel = '#' + container_id;
2248		jQuery(container_sel).html(html);
2249
2250		tabberAutomatic(tabberOptions, container_id);
2251		fc_bindFormDependencies(container_sel, 0, '');
2252
2253		fc_initBootstrap(null, container_sel, reAddOnEvents);
2254		fc_initTooltips(null, container_sel, reAddOnEvents);
2255		fc_initCodeMirror(null, container_sel, reAddOnEvents);
2256		fc_initMinicolors(null, container_sel, reAddOnEvents);
2257		fc_initSubform(null, container_sel);
2258
2259		if (typeof(fcrecord_attach_sortable) == 'function') fcrecord_attach_sortable(container_sel);
2260		if (typeof(fcfield_attach_sortable) == 'function')  fcfield_attach_sortable(container_sel);
2261
2262		fc_initSelect2(null, container_sel, reAddOnEvents);
2263	}
2264
2265
2266	/*
2267	 * Initialize bootstrap styles and effects
2268	 */
2269	function fc_initBootstrap(event, container_sel, reAddOnEvents)
2270	{
2271		const container_el = jQuery(container_sel);
2272
2273		// Also run method on given "Add" events, e.g. 'subform-row-add'
2274		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initBootstrap));
2275
2276		// Add bootstap styless and effects
2277		fc_bootstrapAttach(container_el);
2278	}
2279
2280
2281	/*
2282	 * Initialize tooltips and popovers
2283	 */
2284	function fc_initTooltips(event, container_sel, reAddOnEvents)
2285	{
2286		const container_el = jQuery(container_sel);
2287		const elements_set_tooltip = container_el.find('.hasTooltip');
2288		const elements_set_popover = container_el.find('.hasPopover');
2289
2290		if (typeof jQuery === undefined || typeof jQuery.tooltip === undefined)
2291		{
2292			elements_set_tooltip.length && !!Joomla.fc_debug ? window.console.log('tooltip() not loaded. Skipping it') : false;
2293		}
2294		if (typeof jQuery === undefined || typeof jQuery.popover === undefined)
2295		{
2296			elements_set_popover.length && !!Joomla.fc_debug ? window.console.log('popover() not loaded. Skipping it') : false;
2297		}
2298		if (typeof jQuery === undefined || (typeof jQuery.tooltip === undefined && typeof jQuery.popover === undefined))
2299		{
2300			return;
2301		}
2302
2303		// Also run method on given "Add" events, e.g. 'subform-row-add'
2304		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initTooltips));
2305
2306		// Add tooltips and popovers
2307		typeof jQuery.tooltip !== undefined ? elements_set_tooltip.tooltip({html: true, container: container_el}) : false;
2308		typeof jQuery.popover !== undefined ? elements_set_popover.popover({html: true, container: container_el, trigger : 'hover focus'}) : false;
2309	}
2310
2311
2312	/*
2313	 * Initialize select2 behaviour
2314	 */
2315	function fc_initSelect2(event, container_sel, reAddOnEvents)
2316	{
2317		const container_el = jQuery(container_sel);
2318		const elements_set = container_el.find('select.use_select2_lib');
2319
2320		if (typeof jQuery === undefined || typeof jQuery.select2 === undefined)
2321		{
2322			elements_set.length && !!Joomla.fc_debug ? window.console.log('subformRepeatable() not loaded. Skipping it') : false;
2323			return;
2324		}
2325
2326		// Also run method on given "Add" events, e.g. 'subform-row-add'
2327		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initSelect2));
2328
2329		// Attach select2
2330		fc_attachSelect2(container_el, elements_set);
2331	}
2332
2333
2334	function fc_initCodeMirror(event, container_sel, reAddOnEvents)
2335	{
2336		const container_el = jQuery(container_sel);
2337		const elements_set = container_el.find('textarea.codemirror-source');
2338
2339		if (typeof Joomla.editors === undefined || typeof CodeMirror === undefined)
2340		{
2341			elements_set.length && !!Joomla.fc_debug ? window.console.log('CodeMirror not loaded. Skipping it') : false;
2342			return;
2343		}
2344
2345		// Also run method on given "Add" events, e.g. 'subform-row-add'
2346		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initCodeMirror));
2347
2348		elements_set.each(function () {
2349			var input = jQuery(this).removeClass('codemirror-source');
2350			var id = input.prop('id');
2351
2352			Joomla.editors.instances[id] = CodeMirror.fromTextArea(this, input.data('options'));
2353		});
2354	}
2355
2356
2357
2358	/*
2359	 * Initialize subform fields
2360	 */
2361	function fc_initSubform (event, container_sel)
2362	{
2363		const container_el = jQuery(container_sel);
2364		const elements_set = container_el.find('div.subform-repeatable');
2365
2366		if (typeof jQuery === undefined || typeof jQuery.subformRepeatable)
2367		{
2368			elements_set.length && !!Joomla.fc_debug ? window.console.log('subformRepeatable() not loaded. Skipping it') : false;
2369			return;
2370		}
2371		elements_set.subformRepeatable();
2372	}
2373
2374
2375	/*
2376	 * Initialize any minicolor fields
2377	 */
2378	function fc_initMinicolors(event, container_sel, reAddOnEvents)
2379	{
2380		const container_el = jQuery(container_sel);
2381		const elements_set = container_el.find('.minicolors');
2382
2383		if (typeof jQuery === undefined || typeof jQuery.minicolors === undefined)
2384		{
2385			elements_set.length && !!Joomla.fc_debug ? window.console.log('minicolors() not loaded. Skipping it') : false;
2386			return;
2387		}
2388
2389		// Also run method on given "Add" events, e.g. 'subform-row-add'
2390		(reAddOnEvents || []).forEach(element => container_el.on(element, fc_initMinicolors));
2391
2392		elements_set.each(function() {
2393			var $this = jQuery(this);
2394			var format = $this.data('validate') === 'color' ? 'hex' : $this.data('format') || 'hex';
2395
2396			$this.minicolors({
2397				control: $this.data('control') || 'hue',
2398				format: format === 'rgba' ? 'rgb' : format,
2399				keywords: $this.data('keywords') || '',
2400				opacity: format === 'rgba',
2401				position: $this.data('position') || 'default',
2402				theme: $this.data('theme') || 'bootstrap',
2403			});
2404		});
2405	}
2406
2407
2408	//*****************************
2409	//*** END OF fc_init*() methods
2410	//*****************************
2411
2412
2413
2414	/*
2415	 * Allow custom input in date fields having the custom text class
2416	 */
2417	document.addEventListener('DOMContentLoaded', function()
2418	{
2419		if (typeof JoomlaCalendar != 'undefined')
2420		{
2421			var JoomlaCalendar_setAltValue_fc = JoomlaCalendar.prototype.setAltValue;
2422			JoomlaCalendar.prototype.setAltValue = function()
2423			{
2424				if (this.inputField.classList.contains('fc_date_allow_text'))
2425				{
2426					return;
2427				}
2428				return JoomlaCalendar_setAltValue_fc.call(this, arguments);
2429			};
2430
2431			var JoomlaCalendar_init_fc = JoomlaCalendar.init;
2432			JoomlaCalendar.init = function()
2433			{
2434				var result = JoomlaCalendar_init_fc.apply(null, arguments);
2435
2436				var element = arguments[0];
2437				var input = element ? element.getElementsByTagName('input')[0] : false;
2438
2439				if (input)
2440				{
2441					if (!!input.id && input.classList.contains('fc_date_allow_text'))
2442					{
2443						input.value = document.getElementById(input.id + '_fc_value').getAttribute('data-fc-value');
2444					}
2445				}
2446
2447				return result;
2448			}
2449		}
2450	});

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.