<?php
/** 
 * App runner
 * @package JCHAT::plugins::system
 * @author Joomla! Extensions Store
 * @Copyright (C) 2015 - Joomla! Extensions Store
 * @license GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html  
 */
// no direct access
defined ( '_JEXEC' ) or die ( 'Restricted access' );
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Joomla\Event\Event;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Registry\Registry;
use Joomla\CMS\Factory;
use Joomla\CMS\Date\Date;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Filter\OutputFilter;
use JExtstore\Component\JChat\Administrator\Framework\Helpers\Language as JChatHelpersLanguage;

class PlgSystemJChat extends CMSPlugin implements SubscriberInterface {
	/**
	 * @access private
	 * @var boolean
	 */
	private $isPluginStopped;
	
	/**
	 * App reference
	 *
	 * @access protected
	 * @var Object
	 */
	protected $appInstance;
	
	/**
	 * DB reference
	 *
	 * @access protected
	 * @var Object
	 */
	protected $dbInstance;
	
	/**
	 * Component configuration
	 *
	 * @access protected
	 * @var Object
	 */
	protected $componentConfig;
	
	/**
	 * User object
	 *
	 * @access protected
	 * @var Object
	 */
	protected $myUser;
	
	/**
	 * JS App Inject
	 *
	 * @access	private
	 * @param Object $cParams
	 * @return void
	 */
	private function injectApp ($cParams) {
		// Ottenimento document
		$doc = $this->appInstance->getDocument ();
		$option = $this->appInstance->input->get('option', null);
		$viewName = $this->appInstance->input->get('view', null);
		
		// Output JS APP nel Document
		if($doc->getType() !== 'html' || $this->appInstance->input->getCmd ( 'tmpl' ) === 'component') {
			return false;
		}
		
		$user = $this->appInstance->getIdentity();
		if(!$user->id && !$cParams->get('guestenabled', false)) {
			return;
		}
		
		// Special exclusion for Akeeba LoginGuard 2SV page and J4 Captive Login
		if (($option == 'com_users' && $this->appInstance->input->get ( 'view' ) == 'captive') || $option == 'com_loginguard') {
			return false;
		}
		
		// Special exclusion for Kunena reply post
		if ($option == 'com_kunena' && $this->appInstance->input->get ( 'view' ) == 'topic' && $this->appInstance->input->get ( 'layout' ) == 'reply') {
			return false;
		}
		
		// Check access levels intersection to ensure that users has access usage permission for chat
		// Get users access levels based on user groups belonging
		$userAccessLevels = $user->getAuthorisedViewLevels();
		
		// Get chat access level from configuration, if set AKA param != array(0) go on with intersection
		$chatAccessLevels = $cParams->get('chat_accesslevels', array(0));
		if(is_array($chatAccessLevels) && !in_array(0, $chatAccessLevels, false)) {
			$intersectResult = array_intersect($userAccessLevels, $chatAccessLevels);
			$hasChatAccess = (bool)(count($intersectResult));
			// Return if user has no access
			if(!$hasChatAccess) {
				return;
			}
		}
		
		// Check for menu exclusion
		$menu = $this->appInstance->getMenu()->getActive();
		if(is_object($menu)) {
			$menuItemid = $menu->id;
			$menuExcluded = $cParams->get('chat_exclusions');
			if(is_array($menuExcluded) && !in_array(0, $menuExcluded, false) && in_array($menuItemid, $menuExcluded)) {
				return;
			}
			$doc->getWebAssetManager()->addInlineScript("var jchat_itemid='$menuItemid';");
		}
		
		// Check for IP multiple ranges exclusions
		if($cParams->get ( 'ipbanning', false)) {
			$ipAddressRegex = '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/i';
			$clientIP = $_SERVER ['REMOTE_ADDR'];
			$clientIpDec = ( float ) sprintf ( "%u", ip2long ( $clientIP ) );
			$ipRanges = $cParams->get ( 'iprange_multiple', null);
			// Check if data are not null
			if($ipRanges) {
				// Try to load every range, one per row
				$explodeRows = explode(PHP_EOL, $ipRanges);
				if(!empty($explodeRows)) {
					foreach ($explodeRows as $singleRange) {
						// Try to detect single range
						$explodeRange = explode('-', $singleRange);
						if(!empty($explodeRange) && count($explodeRange) == 2) {
							$ipStart = trim($explodeRange[0]);
							$ipEnd = trim($explodeRange[1]);
							$validIpRangeStart = preg_match ( $ipAddressRegex, $ipStart );
							$validIpRangeEnd = preg_match ( $ipAddressRegex, $ipEnd );
							if ($validIpRangeStart && $validIpRangeEnd) {
								$lowerIpDec = ( float ) sprintf ( "%u", ip2long ( $ipStart ) );
								$upperIpDec = ( float ) sprintf ( "%u", ip2long ( $ipEnd ) );
								if (($clientIpDec >= $lowerIpDec) && ($clientIpDec <= $upperIpDec)) {
									return false;
								}
							}
						}
					}
				}
			}
		}
		
		// Check for hours activation
		$startHour = $cParams->get('start_at_hour', null);
		$stopHour = $cParams->get('stop_at_hour', null);
		if($startHour && $stopHour) {
			$jTimeZone = $this->appInstance->getConfig ()->get ( 'offset' );
			$dateObject = Factory::getDate();
			$dateObject->setTimezone(new \DateTimeZone($jTimeZone));
			$currentHour = $dateObject->format('G', true);
			if($currentHour < $startHour || $currentHour >= $stopHour) {
				return;
			}
		}
		
		// Check for day of the week activation
		$daysOfTheWeek = $cParams->get('days_of_the_week', null);
		if(is_array($daysOfTheWeek) && count($daysOfTheWeek) && !in_array('', $daysOfTheWeek, true))  {
			$jTimeZone = $this->appInstance->getConfig ()->get ( 'offset' );
			$dateObject = Factory::getDate();
			$dateObject->setTimezone(new \DateTimeZone($jTimeZone));
			$currentDay = $dateObject->format('w', true);
			if(!in_array($currentDay, $daysOfTheWeek)) {
				return;
			}
		}
		
		// Ensure that no 'commas' are used for the session_id
		if($cParams->get ( 'clean_sessionid', false)) {
			$session = $this->appInstance->getSession();
			$sessionID = $session->getId();
			if(strpos($sessionID, ',') !== false) {
				$config = $this->appInstance->getConfig ();
				if($config->get ( 'session_handler' ) == 'database') {
					try {
						// Load and temporary store the current session data
						$selectQuery = $this->dbInstance->getQuery(true);
						$selectQuery->select('*');
						$selectQuery->from($this->dbInstance->quoteName('#__session'));
						$selectQuery->where($this->dbInstance->quoteName('session_id') . ' = ' . $this->dbInstance->quote($sessionID));
						$sessionObject = $this->dbInstance->setQuery($selectQuery)->loadObject();

						// Fork now the session
						$session->fork();
							
						// Restore the previous session storage state
						$newSessionID = $session->getId();
						$insertQuery = $this->dbInstance->getQuery(true);
						$insertQuery->insert($this->dbInstance->quoteName('#__session'))->columns($this->dbInstance->quoteName('session_id'))->values($this->dbInstance->quote($newSessionID));
						$this->dbInstance->setQuery($insertQuery)->execute();
							
						$updateQuery = $this->dbInstance->getQuery(true);
						$updateQuery->update($this->dbInstance->quoteName('#__session'));
						$updateQuery->set('client_id = ' .  $this->dbInstance->quote($sessionObject->client_id));
						$updateQuery->set('guest = ' .  $this->dbInstance->quote($sessionObject->guest));
						$updateQuery->set('time = ' .  time());
						$updateQuery->set('data = ' .  $this->dbInstance->quote($sessionObject->data));
						$updateQuery->set('userid = ' .  $this->dbInstance->quote($sessionObject->userid));
						$updateQuery->set('username = ' .  $this->dbInstance->quote($sessionObject->username));
						$updateQuery->where($this->dbInstance->quoteName('session_id') . ' = ' . $this->dbInstance->quote($newSessionID));
						$this->dbInstance->setQuery($updateQuery)->execute();

						// Delete query
						$deleteQuery = $this->dbInstance->getQuery(true);
						$deleteQuery->delete($this->dbInstance->quoteName('#__session'));
						$deleteQuery->where($this->dbInstance->quoteName('session_id') . ' = ' . $this->dbInstance->quote($sessionID));
						$this->dbInstance->setQuery($deleteQuery)->execute();
							
						// Final redirect, if again character match start a loop ending when the session ID is cleaned
						$this->appInstance->redirect(Uri::current());
					} catch (\Exception $e) {
						// No errors managed, leave the process go on anyway
					}
				}
			}
		}
		
		//load the translation
		require_once JPATH_BASE . '/administrator/components/com_jchat/Framework/Helpers/Language.php';
		$base = Uri::base();
		
		// Manage partial language translations
		$jLang = $this->appInstance->getLanguage();
		$jLang->load('com_jchat', JPATH_SITE . '/components/com_jchat', 'en-GB', true, true);
		if($jLang->getTag() != 'en-GB') {
			$jLang->load('com_jchat', JPATH_SITE, null, true, false);
			$jLang->load('com_jchat', JPATH_SITE . '/components/com_jchat', null, true, false);
		}
		
		$chatLanguage = JChatHelpersLanguage::getInstance();
		
		// Inject js translations
		$translations = array(	'chat',
								'privatechat',
								'nousers',
								'nousers_filter',
								'gooffline',
								'available',
								'busy',
								'statooffline',
								'statodonotdisturb',
								'reset_chatboxes',
								'minimize_chatboxes',
								'defaultstatus',
								'sent_file',
								'received_file',
								'sent_file_waiting',
								'sent_file_downloaded',
								'sent_file_downloaded_realtime',
								'sent_file_download',
								'error_deleted_file',
								'error_notfound_file',
								'groupchat_filter',
								'addfriend',
								'optionsbutton',
								'maximizebutton_maximized',
								'maximizebutton_minimized',
								'closesidebarbutton',
								'loginbutton',
								'logindesc',
								'already_logged',
								'spacer',  
								'scegliemoticons',
								'wall_msgs',
								'wall_msgs_refresh',
								'manage_avatars',
								'seconds',
								'minutes',
								'hours',
								'days',
								'years',
								'groupchat_request_sent',
								'groupchat_request_received',
								'groupchat_request_accepted',
								'groupchat_request_removed',
								'groupchat_request_received',
								'groupchat_request_accepted_owner',
								'groupchat_nousers',
								'groupchat_allusers',
								'audio_onoff',
								'public_audio_onoff',
								'vibrate_onoff',
								'notification_onoff',
								'wall_notification',
								'privatemsg_notification',
								'sentfile_notification',
								'trigger_emoticon',
								'trigger_fileupload',
								'trigger_export',
								'trigger_delete',
								'trigger_refresh',
								'trigger_skypesave',
								'trigger_skypedelete',
								'trigger_infoguest',
								'trigger_room',
								'trigger_history',
								'trigger_history_wall',
								'trigger_webrtc',
								'trigger_webrtc_ringing',
								'trigger_send',
								'trigger_geolocation',
								'trigger_blackboard',
								'search',
								'invite',
								'pending',
								'remove',
								'userprofile_link',
								'you',
								'me',
								'seen',
								'banning',
								'banneduser',
								'ban_moderator',
								'startskypecall',
								'startskypedownload',
								'insert_skypeid',
								'skypeidsaved',
								'skypeid_deleted',
								'roomname',
								'roomcount',
								'available_rooms',
								'chatroom_users',
								'chatroom_join',
								'chatroom_joined',
								'users_inchatroom',
								'noavailable_rooms',
								'chatroom',
								'addnew_chatroom',
								'chatroomform_name',
								'chatroomform_description',
								'chatroomform_accesslevel',
								'chatroomform_submit',
								'success_storing_chatroom',
								'success_deleting_chatroom',
								'insert_override_name',
								'trigger_override_name',
								'override_name_saved',
								'override_name_deleted',
								'select_period',
								'nomessages_available',
								'period_1d',
								'period_1w',
								'period_1m',
								'period_3m',
								'period_6m',
								'period_1y',
								'skype',
								'newmessage_tab',
								'start_call',
								'accept_call',
								'decline_call',
								'end_call',
								'call_starting',
								'call_started',
								'call_disconnected',
								'incoming_started',
								'connecting',
								'missing_local_stream',
								'closing_connection',
								'connection_closed',
								'connection_active',
								'error_creating_connection',
								'session_error',
								'connection_close_error',
								'connection_failed',
								'webrtc_videochat',
								'webcam',
								'webcam_quality',
								'webrtc_bandwidth',
								'webrtc_recorder_local',
								'webrtc_recorder_remote',
								'webrtc_recorder_both',
								'mediastream_error',
								'requires_https',
								'noanswer',
								'webrtc_nosupport',
								'chrome_webrtc_support',
								'firefox_webrtc_support',
								'opera_webrtc_support',
								'ie_webrtc_support',
								'safari_webrtc_support',
								'webrtc_caniuse',
								'nowebcam_detected',
								'grant_cam_access',
								'quality_cam',
								'onoffswitch',
								'endcall_before_close',
								'endcall_totoggle_popups',
								'hardware_unavailable',
								'webrtc_notification_ringing',
								'newvideocall_tab',
								'fallback_suggestion',
								'expired_msg',
								'lamform_name',
								'lamform_email',
								'lamform_phonenumber',
								'lamform_message',
								'lamform_submit',
								'lamform_required',
								'success_send_lamessage',
								'error_send_lamessage',
								'sendus_aticket',
								'select_user_receiver',
								'trigger_messaging_emoticon',
								'trigger_messaging_fileupload',
								'trigger_messaging_export',
								'trigger_messaging_delete',
								'trigger_messaging_openbox',
								'trigger_conference_fileupload',
								'trigger_localvideo_toggle',
								'remote_pin_video',
								'open_privatemess',
								'agentbox_defaultmessage',
								'source_language',
								'target_language',
								'lang_switcher',
								'translate_arrow',
								'start_accept_call',
								'end_decline_call',
								'start_recording',
								'stop_recording',
								'pause_recording',
								'view_recording',
								'download_recording',
								'upload_recording',
								'send_recording',
								'upload_complete',
								'upload_error',
								'start_sharing',
								'end_sharing',
								'accept_sharing',
								'decline_sharing',
								'blackboard',
								'local_blackboard',
								'remote_blackboard',
								'blackboard_request',
								'trigger_blackboard_ringing',
								'blackboard_not_supported',
								'webrtc_blackboard_nosupport',
								'chrome_webrtc_blackboard_support',
								'firefox_webrtc_blackboard_support',
								'opera_webrtc_blackboard_support',
								'session_started',
								'session_starting',
								'missing_local_blackboard_stream',
								'blackboard_noanswer',
								'blackboard_image_upload',
								'blackboard_screen_share',
								'blackboard_screen_end',
								'toggle_sharing',
								'chatroom_menuitems',
								'privacy_policy_label',
								'predefined_answers',
								'recaptcha_label',
								'delete_message_confirmation',
								'delete_message_second_confirmation',
								'delete_message_yes',
								'delete_message_no',
								'whatsapp_forward',
								'copy_addon',
								'copy_addon_success'
		);
		$chatLanguage->injectJsTranslations($translations, $doc);
				
		// Output JS APP nel Document
		$defaultWidth = '';
		$baseTemplate = $cParams->get('chat_template', 'default.css');
		switch ($baseTemplate) {
			case 'custom.css':
				HTMLHelper::stylesheet('com_jchat/css/templates/default.css', array('relative' => true, 'pathOnly' => false, 'detectBrowser' => false, 'detectDebug' => false));
			break;

			case 'livesupportchatbot.css':
				$doc->getWebAssetManager()->registerAndUseStyle ( 'jchat.defaultcss', 'components/com_jchat/css/templates/default.css');
				$myAvatarImage = $cParams->get('chatbot_avatar', null);
				if($myAvatarImage) {
					$myAvatarImage = Uri::root() . $myAvatarImage;
					$doc->getWebAssetManager()->addInlineStyle("#jchat_userstab_popup div.jchat_userstabtitle:after{background-image: url('$myAvatarImage')}");
				}
				if($cParams->get('rendering_mode', 'auto') == 'auto') {
					$defaultWidth = '340';
				}
			break;
			
			case 'livesupport.css':
				$doc->getWebAssetManager()->registerAndUseStyle ( 'jchat.defaultcss', 'components/com_jchat/css/templates/default.css');
				if($cParams->get('rendering_mode', 'auto') == 'auto') {
					$defaultWidth = '340';
				}
			break;

			case 'mobile.css':
			case 'animated.css':
				$doc->getWebAssetManager()->registerAndUseStyle ( 'jchat.defaultcss', 'components/com_jchat/css/templates/default.css');
				$doc->getWebAssetManager()->registerAndUseStyle ( 'jchat.font-awesome', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css');
				break;

			case 'default.css';
			default:
				$doc->getWebAssetManager()->registerAndUseStyle ( 'jchat.defaultcss', 'components/com_jchat/css/templates/default.css');
			break;
		}
		
		$directTemplates = array('default.css', 'custom.css');
		if(!in_array($baseTemplate, $directTemplates)) {
			$doc->getWebAssetManager()->registerAndUseStyle ( 'jchat.templatecss', 'components/com_jchat/css/templates/' . $baseTemplate, [], [], ['jchat.defaultcss']);
		}
		
		// Scripts loading
		$defer = $cParams->get('scripts_loading', null) == 'defer' ? true : false;
		$async = $cParams->get('scripts_loading', null) == 'async' ? true : false;

		if($cParams->get('includejquery', 1)) {
			$doc->getWebAssetManager()->useScript('jquery');
		}
		if($cParams->get('noconflict', 0)) {
			$doc->getWebAssetManager()->useScript('jquery-noconflict');
		}
		
		$doc->getWebAssetManager()->addInlineScript("var jchat_livesite='$base';" .
													"var jchat_excludeonmobile=" . $cParams->get('exclude_onmobile', 0) . ";" .
													"var jchat_guestenabled='" . $cParams->get('guestenabled', 1) . "';" .
													"var jchat_userid='" . $user->id . "';" .
													"var jchat_usersessionid='" . session_id() . "';" .
													"var jchat_notifications_time='" . $cParams->get('notifications_time', 10) . "';" .
													"var jchat_notifications_public_time='" . $cParams->get('notifications_public_time', 5) . "';" .
													"var jchat_wall_sendbutton=" . $cParams->get('show_send_button', 2) . ";" .
													"var jchat_sidebar_default_width_override='" . $cParams->get('sidebar_default_width_override', $defaultWidth) . "';" .
													"var jchat_privatemess_uri='" . @Route::_('index.php?option=com_jchat&view=messaging') . "';" .
													"var jchat_privacy_policy=" . $cParams->get('privacy_policy', 0) . ";" .
													"var jchat_privacy_policy_link='" . Text::_($cParams->get('privacy_policy_link', 'javascript:void(0)'), true) . "';" .
													"var jchat_tickets_form_include_recaptcha=" . $cParams->get('tickets_form_include_recaptcha', 0) . ";" .
													"var jchat_tickets_form_include_phonenumber=" . $cParams->get('tickets_form_include_phonenumber', 0) . ";" .
													"var jchat_manage_unstable_network=" . $cParams->get('manage_unstable_network', 0) . ";" .
													"var jchat_download_msgs_multitabs_mode=" . $cParams->get('download_msgs_multitabs_mode', 0) . ";" .
													"var jchat_messenger_sounds_variation='" . $cParams->get('messenger_sounds_variation', 'variation1') . "';" .
													"var jchat_messenger_typing_sound=" . $cParams->get('messenger_typing_sound', 1) . ";");
		
		// Manage by plugin append the chat target element based on rendering mode and related overridden styles
		$renderingMode = $cParams->get('rendering_mode', 'auto');
		$targetElement = $renderingMode == 'auto' ? 'body' : '#jchat_target';
		$offlineMessage = str_replace(array( "\n",  "\r", "\t", PHP_EOL), ' ', Text::_($cParams->get('offline_message', Text::_('COM_JCHAT_DEFAULT_OFFLINE_MESSAGE')), true));
		$doc->getWebAssetManager()->addInlineScript("var jchatTargetElement='$targetElement';" .
													"var jchatOfflineMessage='$offlineMessage';" );
		
		
		// Load and inject emoticons into the JS domain
		$query = $this->dbInstance->getQuery(true);
		$query->select($this->dbInstance->quoteName('linkurl') . ' AS ' . $this->dbInstance->quoteName('path') . ',' . $this->dbInstance->quoteName('keycode'));
		$query->from($this->dbInstance->quoteName('#__jchat_emoticons'));
		$query->where($this->dbInstance->quoteName('published') . '=1');
		$query->order($this->dbInstance->quoteName('ordering') . 'ASC');
		$emoticons = $this->dbInstance->setQuery($query)->loadObjectList();
		$doc->getWebAssetManager()->addInlineScript("window.jchat_emoticons=" . json_encode($emoticons) . ";");
		
		// Add styles for module displacement
		if($renderingMode == 'module') {
			$doc->getWebAssetManager()->addInlineStyle('
				#jchat_base, #jchat_wall_popup, #jchat_userstab_popup {
						position: relative;
					}
				#jchat_base, #jchat_wall_popup, #jchat_userstab_popup {
						width: ' . ($cParams->get('sidebar_width', 250)) . 'px;
					}
				#jchat_userstab, #jchat_userstab.jchat_userstabclick {
						width: ' . ($cParams->get('sidebar_width', 250) - 2) . 'px;
					}
				#jchat_target {
						width: ' . $cParams->get('sidebar_width', 250) . 'px;
						height: ' . $cParams->get('sidebar_height', 600) . 'px;
					}
				#jchat_users_search {
						width: ' . $cParams->get('search_width', 100) . 'px;
					}
				#jchat_roomstooltip {
						width: ' . $cParams->get('chatroom_width', 400) . 'px;
					}
				#jchat_roomsdragger {
						width: ' . (int)($cParams->get('chatroom_width', 400) + 2) . 'px;
					}
				#jchat_users_search {
						padding: 2px 6px 0 16px;
					}
				#jchat_wall_popup.jchat_wall_minimized {
						top: 0;
					}
			');
		}
		
		if($fontsizeOverride = $cParams->get('fontsize_override', null)) {
			$doc->getWebAssetManager()->addInlineStyle("div.jchat_chatboxmessage,
									   					div.jchat_textarea{font-size:$fontsizeOverride}");
		}
		
		if($fontsizeTitlesOverride = $cParams->get('fontsize_titles_override', null)) {
			$doc->getWebAssetManager()->addInlineStyle("span.jchat_publicchattitle,
													   span.jchat_privatechattitle,
													   #jchat_userstab:not(.jchat_tabclick) #jchat_userstab_text,
													   span.jchat_lamform_title{font-size:$fontsizeTitlesOverride}");
			if((int)$fontsizeTitlesOverride >= 17 && $cParams->get('chat_template', 'default.css') != 'alternative.css') {
				$doc->getWebAssetManager()->addInlineStyle("#jchat_userstab:not(.jchat_tabclick) #jchat_userstab_text{margin-top:0}");
			}
			$doc->getWebAssetManager()->addInlineStyle("div.jchat_userlist span.jchat_userscontentname{font-size:" . ((int)$fontsizeTitlesOverride - 1) . "px}");
		}
		
		if(!$cParams->get('show_users_count', 1)) {
			$doc->getWebAssetManager()->addInlineStyle("span.jchat_userscount{display:none}");
		}
		
		if($cParams->get('messages_deletion', 0)) {
			$doc->getWebAssetManager()->addInlineStyle("div.jchat_tabpopup div.jchat_chatboxmessage span.jchat_chatboxmessagecontent.selfmessage{max-width:65%}#jchat_private_messaging div.jchat_chatboxmessageinfo span.jchat_chatboxmessagecontent.selfmessage{max-width:85%}");
		}
		
		$renderRoundedBaloon = $cParams->get('render_rounded_baloon', 1);
		if($renderRoundedBaloon || $baseTemplate == 'animated.css') {
			$marginLeftRoundedBaloon = '';
			$bottomRoundedOffset = "-80px";
			switch ($baseTemplate) {
				case 'mobile.css':
					$marginLeftRoundedBaloon = '#jchat_userstab:not(.jchat_userstabclick) #jchat_userstab_icon{margin-left:12px}#jchat_userstab:not(.jchat_userstabclick) #jchat_userstab_text{margin-top:10px;margin-left:10px}';
					$paddingRoundedBaloon = 'padding: 15px 0 !important';
					break;

				case 'animated.css':
					$pathToCustomLiveSupportIcon = $cParams->get('chatbot_avatar', null);
					$chatbotTopInnerIconImage = $cParams->get('chatbot_top_inner_icon_image', null);
					$pathToLiveSupportIcon =  $pathToCustomLiveSupportIcon ? Uri::root() . $pathToCustomLiveSupportIcon : Uri::root() . 'components/com_jchat/images/default/chatbot.svg';
					if($cParams->get('chatbot_top_inner_icon', 1)) {
						$pathToLiveSupportInnerIcon =  $chatbotTopInnerIconImage ? Uri::root() . $chatbotTopInnerIconImage : Uri::root() . 'components/com_jchat/images/default/chatbot.png';
					} else {
						$pathToLiveSupportInnerIcon = Uri::root() . 'components/com_jchat/images/default/blank.gif';
					}
					$backgroundCustomSize = $pathToCustomLiveSupportIcon ? 'background-size: 100px 100px' : 'background-size: 50% 50%';
					$marginLeftRoundedBaloon = '#jchat_userstab_popup .jchat_userstabtitle{background: #FFF url("' . $pathToLiveSupportInnerIcon . '") no-repeat !important;background-position: right 10px top 42px !important;background-size: auto 50% !important}#jchat_userstab.jchat_tab:not(.jchat_tabclick){background: url("' . $pathToLiveSupportIcon . '") no-repeat #fff;background-position: center center !important;' . $backgroundCustomSize . '}#jchat_userstab.jchat_tab:not(.jchat_tabclick) *{display:none}#jchat_userstab:not(.jchat_userstabclick) #jchat_userstab_icon{margin-left:12px}#jchat_userstab:not(.jchat_userstabclick) #jchat_userstab_text{margin-top:10px;margin-left:10px}';
					$paddingRoundedBaloon = 'padding: 15px 0 !important';
					$bottomRoundedOffset = "-60px";
					break;
					
				case 'alternative.css':
					$marginLeftRoundedBaloon = '#jchat_userstab:not(.jchat_userstabclick) #jchat_userstab_icon{margin-left:4px;padding-right:2px}';
					$paddingRoundedBaloon = 'padding: 35px 0 !important';
					break;

				case 'livesupportchatbot.css':
					$pathToCustomLiveSupportIcon = $cParams->get('chatbot_avatar', null);
					$pathToLiveSupportIcon =  $pathToCustomLiveSupportIcon ? Uri::root() . $pathToCustomLiveSupportIcon : Uri::root() . 'components/com_jchat/images/default/livesupport_placeholder_white.png';
					$backgroundCustomSize = $pathToCustomLiveSupportIcon ? 'background-size: 100px 100px' : '';
					$marginLeftRoundedBaloon = '#jchat_userstab.jchat_tab:not(.jchat_tabclick){background: url("' . $pathToLiveSupportIcon . '") no-repeat #fff;background-position: center center !important;' . $backgroundCustomSize . '}#jchat_userstab.jchat_tab:not(.jchat_tabclick) *{display:none}';
					$paddingRoundedBaloon = 'padding: 35px 0 !important';
					break;
					
				default:
					$marginLeftRoundedBaloon = '#jchat_userstab:not(.jchat_userstabclick) #jchat_userstab_icon{margin-left:4px;padding-right:2px}';
					$paddingRoundedBaloon = 'padding: 35px 0 !important';
					break;
			}
			if($renderRoundedBaloon) {
				$doc->getWebAssetManager()->addInlineStyle("#jchat_userstab:not(.jchat_userstabclick){box-sizing:border-box;width:100px !important;height:100px !important;border-radius:50px !important;margin-top:$bottomRoundedOffset !important;margin-right:20px;$paddingRoundedBaloon}$marginLeftRoundedBaloon");
			} else {
				$doc->getWebAssetManager()->addInlineStyle('#jchat_userstab_popup .jchat_userstabtitle{background: #FFF url("' . $pathToLiveSupportInnerIcon . '") no-repeat !important;background-position: right 10px top 42px !important;background-size: auto 50% !important}');
			}
		}
		
		// Add override color styles
		if($colorOverride = $cParams->get('chat_color_override', null)) {
			$doc->getWebAssetManager()->addInlineStyle("#jchat_userstab.jchat_tab,
													    div.jchat_userstabtitle,
													    #jchat_roomsdragger.jchat_tab,
													    div#jchat_select_period,
													    div.jchat_tooltip_header,
													    div.jchat_roomstooltip div.jchat_roomright .jchat_roomjoin,
													    div.jchat_userstabsubtitle,
													    #jchat_confirm_delete,
									   				    #jchat_confirm_message_delete,
														.jchat_chatboxmessage.selfmessage #jchat_confirm_message_delete,
													    div.jchat_infoguest_title{background:$colorOverride!important;background-color:$colorOverride}
													    div.jchat_single_period,
									   				    div.jchat_tabpopup div[class^=jchat_trigger_].toggle_on,
														div.jchat_blackboardtooltip label.image_uploader,
														div[class^=jchat_trigger].jchat_trigger_conference_fileupload.toggle_on,
 														#activation_form input.jchat_button,
														input.jchat_button_exit_chat,
													    #jchat_private_messaging div[class^=jchat_trigger_].toggle_on{background-color:$colorOverride!important}
														div.jchat_tabpopup span.jchat_tab{background:$colorOverride url('" . Uri::root()  . "components/com_jchat/images/default/jchat.png') no-repeat top left;background-position: -20px}
													    div.jchat_tabpopup span.jchat_tab,
													    #jchat_roomstooltip,
													    #jchat_confirm_delete,
													    #jchat_select_period,
														#chatroom_adder,
													    div.jchat_emoticonstooltip div.jchat_tooltip_content,
													    div.jchat_fileuploadtooltip iframe,
													    div.jchat_messaging_fileuploadtooltip iframe,
													    div.jchat_historytooltip,
													    div.jchat_webrtctooltip,
													    div.jchat_messaging_delete_conversation_tooltip,
													    div.jchat_userslist_ctrls div.jchat_userslist_reply,
													    div.jchat_tooltip_header,
													    div.jchat_infoguesttooltip,
													    div.jchat_geolocationtooltip,
									   				    #jchat_confirm_message_delete,
									   				    div.jchat_delete_message_tooltip,
									   				    div.jchat_emoticonstooltip,
									   				    div.jchat_blackboardtooltip,
									   				    div.jchat_chatroom_usersinfo_tooltip,
														div.jchat_conference_fileuploadtooltip iframe,
									   				    div.jchat_messaging_emoticonstooltip{border-color:$colorOverride!important}");
			
			if(in_array($baseTemplate, array('mobile.css','animated.css'))) {
				$doc->getWebAssetManager()->addInlineStyle("#jchat_loginbutton{background:$colorOverride!important}
														   div.jchat_tabcontent.messagelist > div[class^=jchat_trigger],div.jchat_userslist_ctrls div[class^=jchat_trigger],div.jchat_closebox_bottom,div.jchat_closebox_bottom::before,div.jchat_tooltip_avatar_upload_header::before,.jchat_tooltip_chatroom_adder_header::before{border-color:$colorOverride!important;color:$colorOverride!important}
														   .jchat_chatboxmessage.selfmessage span.jchat_messagesdeletion g {fill: $colorOverride}
														   .jchat_chatboxmessage.selfmessage span.jchat_messagesdeletion {border-color: $colorOverride}
														   div.jchat_tabcontentinput::before,
														   #jchat_privatemessaging_textarea::before {
																mask: url('" . Uri::root() . "components/com_jchat/images/default/chatsend.svg') no-repeat;
																-webkit-mask: url('" . Uri::root() . "components/com_jchat/images/default/chatsend.svg') no-repeat;
																background: $colorOverride !important;
																-webkit-mask-repeat: no-repeat;
																mask-repeat: no-repeat;
														   }");
				if($baseTemplate == 'animated.css' && $renderRoundedBaloon) {
					$pathToCustomLiveSupportIcon = $cParams->get('chatbot_avatar', null);
					$pathToLiveSupportIcon =  $pathToCustomLiveSupportIcon ? Uri::root() . $pathToCustomLiveSupportIcon : Uri::root() . 'components/com_jchat/images/default/chatbot.svg';
					$backgroundCustomSize = $pathToCustomLiveSupportIcon ? '100px 100px' : '50% 50%';
					$doc->getWebAssetManager()->addInlineStyle("#jchat_userstab.jchat_tab:not(.jchat_tabclick){background: url('" . $pathToLiveSupportIcon . "') center center/" . $backgroundCustomSize . " no-repeat $colorOverride !important}");
				}
			}
		}
		
		// Submit button color override
		if($submitButtonColorOverride = $cParams->get('submitlamform_color_override', null)) {
			$pathImages = Uri::root(true) . '/components/com_jchat/images/default/icon_ticket.png';
			$doc->getWebAssetManager()->addInlineStyle("div.jchat_submit_lam_form {border-color:$submitButtonColorOverride;background:$submitButtonColorOverride url($pathImages) no-repeat 6px 3px}
									   					div.jchat_submit_chatroom_form {border-color:$submitButtonColorOverride;background:$submitButtonColorOverride}");
		}
		
		// Tooltip border color override
		if($tooltipBorderColorOverride = $cParams->get('tooltip_bordercolor_override', null)) {
			$doc->getWebAssetManager()->addInlineStyle("#jchat_default_suggestion_tooltip div.jchat_tooltip_content {border-color: $tooltipBorderColorOverride}" .
													   "#jchat_default_suggestion_tooltip:not(.jchat_arm)::before {background-color: $tooltipBorderColorOverride}");
		}
		
		// Tooltip custom background image
		if($cParams->get('chat_template_tooltip', 'std') == 'custom' && $chatTemplateTooltipImage = $cParams->get('chat_template_tooltip_image', null)) {
			$doc->getWebAssetManager()->addInlineStyle("#jchat_default_suggestion_tooltip::before {background: url('" . Uri::root()  . $chatTemplateTooltipImage . "') no-repeat;content: '';border-radius: 10px;position: absolute;bottom: -31px;right: 20px;width: 150px;height: 200px;border: none;box-shadow: none;cursor: pointer;z-index: 1;}#jchat_default_suggestion_tooltip .jchat_tooltip_content {display:none}#jchat_tooltip_close{margin-right:0}");
		}
		
		// Tooltip background color override
		if($tooltipBckcolorOverride = $cParams->get('tooltip_bckcolor_override', null)) {
			$doc->getWebAssetManager()->addInlineStyle("#jchat_default_suggestion_tooltip div.jchat_tooltip_content {background-color: $tooltipBckcolorOverride !important}");
		}
		
		// Add override height
		if($cParams->get('public_chat_height_override', null)) {
			$publicChatHeightOverride = (int)trim($cParams->get('public_chat_height_override'), '%');
			$doc->getWebAssetManager()->addInlineStyle("#jchat_wall_popup{height:$publicChatHeightOverride%}");
		}
		if($cParams->get('private_chat_height_override', null)) {
			$privateChatHeightOverride = (int)trim($cParams->get('private_chat_height_override'), '%');
			$doc->getWebAssetManager()->addInlineStyle("#jchat_userstab_popup.jchat_tabopen{height:$privateChatHeightOverride%}" .
													   "#jchat_wall_popup.jchat_wall_minimized{bottom:$privateChatHeightOverride%}");
		}
		
		// Add override top margin for public chat
		if($cParams->get('public_chat_top_override', null)) {
			$publicChatTopOverride = trim($cParams->get('public_chat_top_override'));
			if(stripos($publicChatTopOverride, 'px') === false && stripos($publicChatTopOverride, '%') === false) {
				$publicChatTopOverride .= 'px';
			}
			$doc->getWebAssetManager()->addInlineStyle("#jchat_wall_popup:not(.maximized):not(.jchat_wall_minimized ){top:$publicChatTopOverride}");
		}
		
		// Disable emoticons if needed
		if(!$cParams->get('emoticons_enabled', 1)) {
			$doc->getWebAssetManager()->addInlineStyle("*.jchat_trigger_emoticon,*.jchat_trigger_messaging_emoticon{display:none;}");
		}
		
		// Override emoticons width
		if($cParams->get('emoticons_original_size', 0)) {
			$doc->getWebAssetManager()->addInlineStyle("div.jchat_tabcontenttext span.jchat_chatboxmessagecontent img.jchat_emoticons," .
													   " #jchat_usersmessages span.jchat_chatboxmessagecontent img.jchat_emoticons{max-width:inherit!important;max-height:inherit!important;}" .
													   " span.jchat_chatboxmessagecontent{min-width:26px;}");
		}
		
		// Custom videoconference chat overrides
		if($option == 'com_jchat' && ($viewName == 'conference' || $viewName == 'livestreaming')) {
			$videoconferenceLocalvideoMaxwidth = trim($cParams->get('videoconference_localvideo_maxwidth', '300px'));
			if(stripos($videoconferenceLocalvideoMaxwidth, 'px') === false && stripos($videoconferenceLocalvideoMaxwidth, '%') === false) {
				$videoconferenceLocalvideoMaxwidth .= 'px';
			}
			$videoconferencePinnedvideoMaxwidth = trim($cParams->get('videoconference_pinnedvideo_maxwidth', '260px'));
			if(stripos($videoconferencePinnedvideoMaxwidth, 'px') === false && stripos($videoconferencePinnedvideoMaxwidth, '%') === false) {
				$videoconferencePinnedvideoMaxwidth .= 'px';
			}
			$videoconferenceRemotevideoMaxwidth = trim($cParams->get('videoconference_remotevideo_maxwidth', '200px'));
			if(stripos($videoconferenceRemotevideoMaxwidth, 'px') === false && stripos($videoconferenceRemotevideoMaxwidth, '%') === false) {
				$videoconferenceRemotevideoMaxwidth .= 'px';
			}
			$doc->getWebAssetManager()->addInlineStyle("#jchat_wrapper_localvideo{max-width:$videoconferenceLocalvideoMaxwidth}div.jchat_wrapper_remotevideo.jchat_conference_pinnedvideo{max-width:$videoconferencePinnedvideoMaxwidth}div.jchat_wrapper_remotevideo:not(.jchat_conference_pinnedvideo){max-width:$videoconferenceRemotevideoMaxwidth}");
			
			if($customConferencebackground = $this->appInstance->getParams('com_jchat')->get('conference_custom_color', '')) {
				$doc->getWebAssetManager()->addInlineStyle("#jchat_conference_container,#jchat_left_userscolumn,#jchat_right_userscolumn,#jchat_conference_container #jchat_conference_userslist li:hover,div.jchat_conference_container_blacktheme #mic_vumeter{background-color:$customConferencebackground !important}");
			}
		}
		
		// Add custom styles if any
		if($customCssStyles = trim($cParams->get('custom_css_styles', ''))) {
			$doc->getWebAssetManager()->addInlineStyle($customCssStyles);
		}
		
		$wa = $doc->getWebAssetManager();
		$wa->registerAndUseScript ( 'jchat.utility', 'components/com_jchat/js/utility.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery']);
		$wa->registerAndUseScript ( 'jchat.jstorage', 'components/com_jchat/js/jstorage.min.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery']);
		$wa->registerAndUseScript ( 'jchat.soundmanager', 'components/com_jchat/sounds/soundmanager2.js', [], ['defer'=>$defer, 'async'=>$async]);
		$wa->registerAndUseScript ( 'jchat.notifications', 'components/com_jchat/js/notifications.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery', 'jchat.soundmanager']);
		
		if($option == 'com_jchat' && $viewName == 'conference') {} else {
			$wa->registerAndUseScript ( 'jchat.webrtc', 'components/com_jchat/js/webrtc.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery']);
		}
		if($cParams->get('enable_recording', 0) && $viewName != 'livestreaming') {
			$doc->getWebAssetManager()->addInlineScript("var jchat_recorder_max_videos_per_row=" . $cParams->get('recorder_max_videos_per_row', 4) . ";" .
														"var jchat_recorder_video_width=" . $cParams->get('recorder_video_width', 640) . ";" .
														"var jchat_recorder_video_height=" . $cParams->get('recorder_video_height', 480) . ";" .
														"var jchat_recorder_video_miniature_width=" . $cParams->get('recorder_video_miniature_width', 160) . ";");
			$wa->registerAndUseScript ( 'jchat.merger', 'components/com_jchat/js/merger.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery']);
			$wa->registerAndUseScript ( 'jchat.recorder', 'components/com_jchat/js/recorder.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery']);
		}
		if($cParams->get('enable_blackboard', 0)) {
			$wa->registerAndUseScript ( 'jchat.blackboard', 'components/com_jchat/js/blackboard.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery']);
		}

		$wa->registerAndUseScript ( 'jchat.emoticons', 'components/com_jchat/js/emoticons.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery']);
		$wa->registerAndUseScript ( 'jchat.main', 'components/com_jchat/js/main.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery', 'jchat.utility', 'jchat.jstorage', 'jchat.soundmanager', 'jchat.notifications', 'jchat.emoticons']);
		
		// Check for Geolocation feature and related scripts
		if($cParams->get('geolocation_enabled', 0)) {
			$wa->registerAndUseScript ( 'jchat.maps.google', 'https://maps.google.com/maps/api/js?key=AIzaSyCHiHkF9krK7xfDsqX_37a9SjqKJ80wgYM');
			$wa->registerAndUseScript ( 'jchat.gmap', 'components/com_jchat/js/gmap.js', [], ['defer'=>$defer, 'async'=>$async], ['jquery']);
		}
		
		// Kill CSP headers
		$facebookLogin = $this->componentConfig->get('fblogin_active', 0);
		$googleLogin = $this->componentConfig->get('gpluslogin_active', 0);
		$twitterLogin = $this->componentConfig->get('twitterlogin_active', 0);
		if($facebookLogin || $googleLogin || $twitterLogin) {
			// Kill CSP headers
			$httpHeadersPlugin = PluginHelper::getPlugin('system', 'httpheaders');
			if(is_object($httpHeadersPlugin)) {
				$httpHeadersPluginParams = new Registry($httpHeadersPlugin->params);
				if($httpHeadersPluginParams->get('contentsecuritypolicy', 0) && $this->componentConfig->get('auto_manage_csp', 1)) {
					$this->appInstance->setHeader('content-security-policy', null, true);
					$this->appInstance->setHeader('content-security-policy-report-only', null, true);
				}
			}
		}
		
		// Remove expired sessions from the database.
		if($cParams->get('force_joomla_session_cleanup', 0)) {
			$config = $this->appInstance->getConfig ();
			if($config->get ( 'session_handler' ) == 'database') {
				try {
					$time = time();
					$session = $this->appInstance->getSession();
					$sessionLifetime = $session->getExpire();
					if ($time % 2) {
						// The modulus introduces a little entropy, making the flushing less accurate but fires the query less than half the time.
						$query = $this->dbInstance->getQuery(true)
								->delete($this->dbInstance->quoteName('#__session'))
								->where($this->dbInstance->quoteName('time') . ' < ' . $this->dbInstance->quote((int) ($time - $sessionLifetime)));
							
						$this->dbInstance->setQuery($query);
						$this->dbInstance->execute();
					}
				} catch (\Exception $e) {
					// No errors managed, leave the process go on anyway
				}
			}
		}
	}
	
	/**
	 * onBeforeCompileHead handler
	 *
	 * @param Event $event
	 * @access	public
	 * @return null
	 */
	public function injectAppBeforeCompileHead(Event $event) {
		$component = ComponentHelper::getComponent('com_jchat');
		$cParams = $component->params;
		if($this->appInstance->isClient ('site') && $cParams->get('jchat_include_event', 'afterdispatch') == 'beforecompilehead') {
			$this->injectApp($cParams);
		}
	}

	/**
	 * onAfterDispatch handler
	 *
	 * @param Event $event
	 * @access	public
	 * @return null
	 */
	public function injectAppAfterDispatch(Event $event) {
		// Redirect to the component configuration if the Joomla global configuration is requested instead
		if($this->appInstance->isClient('administrator')) {
			$dispatchedComponent = $this->appInstance->input->get ( 'option' );
			$dispatchedView = $this->appInstance->input->get ( 'view' );
			$componentConfig = $this->appInstance->input->get ( 'component' );
			if($dispatchedComponent == 'com_config' && $dispatchedView == 'component' && $componentConfig == 'com_jchat') {
				$this->appInstance->redirect(Route::_('index.php?option=com_jchat&task=config.display', false));
				return;
			}
		}
		
		$component = ComponentHelper::getComponent('com_jchat');
		$cParams = $component->params;
		if($this->appInstance->isClient ('site') && $cParams->get('jchat_include_event', 'afterdispatch') == 'afterdispatch') {
			$this->injectApp($cParams);
		}
		
		// Refresh always the session start time on Joomla 4
		if($this->appInstance->isClient ('site')) {
			$session = $this->appInstance->getSession();
			$session->set('session.timer.start', time());
		}
	}
	
	/** Manage the Joomla session metadata in all cases even if disabled by the global configuration
	 *
	 * @param Event $event
	 * @access public
	 * @return void
	 */
	public function manageSessionMetadata(Event $event) {
		// Always force session metadata tracking
		if(!$this->appInstance->getConfig ()->get ( 'session_metadata', 1 ) || !$this->appInstance->getConfig ()->get ( 'session_metadata_guest', 0 )) {
			$container = Factory::getContainer(); 
			$session = $this->appInstance->getSession();
			$user = $this->appInstance->getIdentity();
			$container->get(\Joomla\CMS\Session\MetadataManager::class)->createOrUpdateRecord($session, $user);
		}
	}
	
	/**
	 * Event to manipulate the menu item dashboard in backend
	 *
	 * @param Event $event
	 * @subparam   array  &$policy  The privacy policy status data, passed by reference, with keys "published" and "editLink"
	 *
	 * @return  void
	 */
	public function processMenuItemsDashboard(Event $event) {
		// subparams: $policy
		$arguments = $event->getArguments();
		$context = &$arguments[0];
		$items = &$arguments[1];
		
		if(!empty($items) && $context == 'administrator.module.mod_submenu') {
			foreach ($items as &$item) {
				if($item->element == 'com_jchat') {
					$item->img = Uri::base() . 'components/com_jchat/images/jchat-16x16.png';
					$item->title = 'COM_JCHAT_DASHBOARD_TITLE';
				}
			}
		}
		
		// Kill com_joomlaupdate informations about extensions missing updater info, leave only main one
		$document = $this->appInstance->getDocument();
		if(!$this->appInstance->get('jextstore_joomlaupdate_script') && $this->appInstance->input->get('option') == 'com_joomlaupdate' && !$this->appInstance->input->get('view') && !$this->appInstance->input->get('task')) {
			$document->getWebAssetManager()->addInlineScript ("
				window.addEventListener('DOMContentLoaded', function(e) {
					if(document.querySelector('#preupdatecheck')) {
						var jextensionsIntervalCount = 0;
						var jextensionsIntervalTimer = setInterval(function() {
						    [].slice.call(document.querySelectorAll('#compatibilityTable1 tbody tr th.exname')).forEach(function(th) {
						        let txt = th.innerText;
						        if (txt && txt.toLowerCase().match(/jsitemap|gdpr|responsivizer|jchatsocial|jcomment|jrealtime|jspeed|jredirects|vsutility|visualstyles|visual\sstyles|instant\sfacebook\slogin|instantpaypal|screen\sreader|jspeed|jamp/i)) {
						            th.parentElement.style.display = 'none';
						            th.parentElement.classList.remove('error');
									th.parentElement.classList.add('jextcompatible');
						        }
						    });
							[].slice.call(document.querySelectorAll('#compatibilityTable2 tbody tr th.exname')).forEach(function(th) {
						        let txt = th.innerText;
						        if (txt && txt.toLowerCase().match(/jsitemap|gdpr|responsivizer|jchatsocial|jcomment|jrealtime|jspeed|jredirects|vsutility|visualstyles|visual\sstyles|instant\sfacebook\slogin|instantpaypal|screen\sreader|jspeed|jamp/i)) {
									th.parentElement.classList.remove('error');
									th.parentElement.classList.add('jextcompatible');
						            let smallDiv = th.querySelector(':scope div.small');
									if(smallDiv) {
										smallDiv.style.display = 'none';
									}
						        }
						    });
							if (document.querySelectorAll('#compatibilityTable0 tbody tr').length == 0 &&
								document.querySelectorAll('#compatibilityTable1 tbody tr:not(.jextcompatible)').length == 0 &&
								document.querySelectorAll('#compatibilityTable2 tbody tr:not(.jextcompatible)').length == 0) {
						        [].slice.call(document.querySelectorAll('#preupdatecheckbox, #preupdateCheckCompleteProblems')).forEach(function(element) {
						            element.style.display = 'none';
						        });
								if(document.querySelector('#noncoreplugins')) {
									document.querySelector('#noncoreplugins').checked = true;
								}
								if(document.querySelector('button.submitupdate')) {
							        document.querySelector('button.submitupdate').disabled = false;
							        document.querySelector('button.submitupdate').classList.remove('disabled');
								}
								if(document.querySelector('#joomlaupdate-precheck-extensions-tab span.fa')) {
									let tabIcon = document.querySelector('#joomlaupdate-precheck-extensions-tab span.fa');
									tabIcon.classList.remove('fa-times');
									tabIcon.classList.remove('text-danger');
									tabIcon.classList.remove('fa-exclamation-triangle');
									tabIcon.classList.remove('text-warning');
									tabIcon.classList.add('fa-check');
									tabIcon.classList.add('text-success');
								}
						    };
					
							if (document.querySelectorAll('#compatibilityTable0 tbody tr').length == 0) {
								if(document.querySelectorAll('#compatibilityTable1 tbody tr:not(.jextcompatible)').length == 0) {
									let compatibilityTable1 = document.querySelector('#compatibilityTable1');
									if(compatibilityTable1) {
										compatibilityTable1.style.display = 'none';
									}
								}
								clearInterval(jextensionsIntervalTimer);
							}
					
						    jextensionsIntervalCount++;
						}, 1000);
					};
				});");
			$this->appInstance->set('jextstore_joomlaupdate_script', true);
		}
	}
	
	/** Manage the Joomla updater based on the user license
	 *
	 * @param Event $event
	 * @subparam   string  The $url for the package update download
	 * @subparam   array  The headers array.
	 * @access public
	 * @return void
	 */
	public function jchatUpdateInstall(Event $event) {
		// subparams: &$url, &$headers
		$arguments = $event->getArguments();
		$url = &$arguments[0];
		$headers = &$arguments[1];
		
		$uri 	= Uri::getInstance($url);
		$parts 	= explode('/', $uri->getPath());
		if ($uri->getHost() == 'storejextensions.org' && in_array('com_jchat.zip', $parts)) {
			// Init as false unless the license is valid
			$validUpdate = false;
				
			// Manage partial language translations
			$jLang = $this->appInstance->getLanguage();
			$jLang->load('com_jchat', JPATH_BASE . '/components/com_jchat', 'en-GB', true, true);
			if($jLang->getTag() != 'en-GB') {
				$jLang->load('com_jchat', JPATH_BASE, null, true, false);
				$jLang->load('com_jchat', JPATH_BASE . '/components/com_jchat', null, true, false);
			}
				
			// Email license validation API call and &$url building construction override
			$cParams = ComponentHelper::getParams('com_jchat');
			$registrationEmail = $cParams->get('registration_email', null);
				
			// License
			if($registrationEmail) {
				$prodCode = 'jchatent';
				$cdFuncUsed = 'str_' . 'ro' . 't' . '13';
	
				// Retrieve license informations from the remote REST API
				$apiResponse = null;
				$apiEndpoint = $cdFuncUsed('uggc' . '://' . 'fgberwrkgrafvbaf' . '.bet') . "/option,com_easycommerce/action,licenseCode/email,$registrationEmail/productcode,$prodCode";
				if (function_exists('curl_init')){
					$ch = curl_init();
					curl_setopt($ch, CURLOPT_URL, $apiEndpoint);
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
					$apiResponse = curl_exec($ch);
					curl_close($ch);
				}
				$objectApiResponse = json_decode($apiResponse);
	
				if(!is_object($objectApiResponse)) {
					// Message user about error retrieving license informations
					$this->appInstance->enqueueMessage(Text::_('COM_JCHAT_ERROR_RETRIEVING_LICENSE_INFO'));
				} else {
					if(!$objectApiResponse->success) {
						switch ($objectApiResponse->reason) {
							// Message user about the reason the license is not valid
							case 'nomatchingcode':
								$this->appInstance->enqueueMessage(Text::_('COM_JCHAT_LICENSE_NOMATCHING'));
								break;
	
							case 'expired':
								// Message user about license expired on $objectApiResponse->expireon
								$this->appInstance->enqueueMessage(Text::sprintf('COM_JCHAT_LICENSE_EXPIRED', $objectApiResponse->expireon));
								break;
						}
							
					}
						
					// Valid license found, builds the URL update link and message user about the license expiration validity
					if($objectApiResponse->success) {
						$url = $cdFuncUsed('uggc' . '://' . 'fgberwrkgrafvbaf' . '.bet' . '/XZY1405SSUOnifs3243564864kfunex35tdrntq5895q.ugzy');
	
						$validUpdate = true;
						$this->appInstance->enqueueMessage(Text::sprintf('COM_JCHAT_EXTENSION_UPDATED_SUCCESS', $objectApiResponse->expireon));
					}
				}
			} else {
				// Message user about missing email license code
				$this->appInstance->enqueueMessage(Text::sprintf('COM_JCHAT_MISSING_REGISTRATION_EMAIL_ADDRESS', OutputFilter::ampReplace('index.php?option=com_jchat&task=config.display#_licensepreferences')));
			}
				
			if(!$validUpdate) {
				$this->appInstance->enqueueMessage(Text::_('COM_JCHAT_UPDATER_STANDARD_ADVISE'), 'notice');
			}
		}
	}
	
	/**
	 * Returns an array of events this subscriber will listen to.
	 *
	 * @return  array
	 *
	 * @since 4.0.0
	 */
	public static function getSubscribedEvents(): array {
		return [
				'onAfterInitialise' => 'manageSessionMetadata',
				'onAfterDispatch' => 'injectAppAfterDispatch',
				'onBeforeCompileHead' => 'injectAppBeforeCompileHead',
				'onPreprocessMenuItems' => 'processMenuItemsDashboard',
				'onInstallerBeforePackageDownload' => 'jchatUpdateInstall'
		];
	}
	
	/**
	 * Override registers Listeners to the Dispatcher
	 * It allows to stop a plugin execution based on the return value of its constructor
	 *
	 * @override
	 * @return  void
	 */
	public function registerListeners() {
		// Check if the plugin has not been stopped by the constructor
		if(!$this->isPluginStopped) {
			parent::registerListeners();
		}
	}
	
	/**
	 * Constructor
	 *
	 * @param
	 *        	object &$subject The object to observe
	 * @param array $config
	 *        	An optional associative array of configuration settings.
	 *        	Recognized key values include 'name', 'group', 'params', 'language'
	 *        	(this list is not meant to be comprehensive).
	 *        	
	 * @since 1.5
	 */
	public function __construct(&$subject, $config = array()) {
		parent::__construct ( $subject, $config );
		
		// Init application
		$this->appInstance = Factory::getApplication();
		
		// Init database
		$this->dbInstance = Factory::getContainer()->get('DatabaseDriver');
		
		// Exclude always the api client
		if ($this->appInstance->isClient ('api')) {
			$this->isPluginStopped = true;
			return;
		}
		
		$this->myUser = $this->appInstance->getIdentity ();
		$this->componentConfig = ComponentHelper::getParams ( 'com_jchat' );
	}
}