HEX
Server: Apache
System: Linux webm010.cluster103.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User: iestorre (46869)
PHP: 8.0.30
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/iestorre/www/modules/mod_smartcountdown/helpers/helper.php
<?php
/**
 * @package Module Smart Countdown for Joomla! 2.5 -3.0
 * @version 2.5: helper.php
 * @author Alex Polonski
 * @copyright (C) 2012-2013 - Alex Polonski
 * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
**/
// no direct access
defined('_JEXEC') or die;
jimport('joomla.event.dispatcher');

class modSmartCountdownHelper
{
	static function getCountdown($params)
	{
		// Explicitly disable caching for this module
		$params->set('cache', 0);
		
		// look for events import plugins
		$dispatcher = JDispatcher::getInstance();
		JPluginHelper::importPlugin('system');
		$result = $dispatcher->trigger('onCountdownGetEventsQueue', array('mod_smartcountdown', $params));
		if(in_array(false, $result, true)) {
			return false;
		}
		
		// get combined events queue from all plugins
		$groups = self::unionQueues($result);
		
		if($groups === false || $params->get('session_time_count', 0) || $params->get('submit_form', ''))
		{
			// all elements of the result array === true: means that no plugin
			// is enabled. This is not an error, just get internal countdown
			// Also if counter mode is set to "relative to session start" or
			// "submit form on countdown zero" is not empty, we ignore all plugin data
			return self::getInternal($params);
		}
		
		// we have event queue here, let's proceed
		
		$show_countdown = $params->get('show_countdown', -1); // auto
		
		$now_ts = JDate::getInstance()->getTimestamp();
		
		/**
		 * Queue format: 2-dimensional array
		 * 1-st dimension - events indexed by start time
		 * 2-nd dimension - events indexed by end time
		 */
		
		/*
		 * Re-process all events provided by events import plugins.
		 * *** current versions of plugins can be significantly simplified ***
		 * For new plugins - no need to calculate diff_js and up_limit in plugin
		 * code, as these values will be overwritten here. At the moment it is just
		 * a performance issue
		 */
		
		$past_slice = null;
		
		$start_times = array_keys($groups);
		foreach($start_times as $i => $time)
		{
			// we have to filter out all past groups except the most recent one
			if($time < $now_ts)
			{
				if(isset($past_slice))
				{
					// we can safely use $i - 1 index, as $past_slice will never be
					// set on the first iteration s_tyles
					
					// unset previous group
					unset($groups[$start_times[$i - 1]]);
				}
				// set flag: at least one past event found
				$past_slice = $time;
			}
			
			$next_start = @$start_times[$i+1];
			if(isset($next_start))
			{
				$events_hop = $next_start - $time;
			}
			else
			{
				$events_hop = PHP_INT_MAX - $time;
			}
		
			foreach($groups[$time] as $end => &$event)
			{
				$duration = $end - $time;
				
				if($show_countdown == 0) 
				{
					// do not show countdown - conrinue count up until the next event start
					$event['up_limit'] = $events_hop;
				}
				else if($show_countdown == -2)
				{
					// do not show count up - start countdown right after previous event start
					$event['up_limit'] = 0;
				}
				else if($show_countdown == -3)
				{
					// countdown_to_end mode
					$event['up_limit'] = 0;
					$countdown_to_end_mode = true;
				}
				else if($show_countdown == -1)
				{
					// auto - use event duration
					$event['up_limit'] = $events_hop == 0 || $events_hop > $duration ? $duration : $events_hop;
				}
				else if($show_countdown > 0)
				{
					// positive value means time to show countdown before the next event start
					// if greater than events hop, use event hop instead
					$event['up_limit'] = $events_hop > $show_countdown ? $events_hop - $show_countdown : $events_hop;
				}
				else
				{
					// negative value means time to show count up after the event start
					// if greater than events hop, use event hop instead
					$show_countup = $show_countdown * -1;
					$event['up_limit'] = $show_countup > $events_hop ? $events_hop : $show_countup;
				}
				
				// up_limit can never be greater than duration
				$event['up_limit'] = min($event['up_limit'], $duration);
			}
		}
		
		// Concatenate titles for duplicate-time events. We add support for different titles
		// in countdown and count up modes. Plugins can e.g. configure event links according
		// to their settings
		// We presume that if $tevent['title'] is set we have equal titles for both modes,
		// otherwise we look in $tevent['title_down'] and $tevent['title_up'] properties
		$titles_down = array();
		$titles_up = array();
		
		// store "countdown to end" groups
		$countdown_to_end = array();
		
		foreach($groups as $time => $tgroup)
		{
			// If at least one event in group is a countdown-to-end, we set
			// a flag, so that all other event titles are not included in
			// the resulting displayed title
			$countdown_to_end[$time] = false;
			foreach($tgroup as $tevent)
			{
				if(!empty($countdown_to_end_mode) && !empty($tevent['is_countdown_to_end']))
				{
					// all events with this deadline are "countdown to end"
					$countdown_to_end[$time] = true;
					break;
				}
			}
			foreach(array_reverse($tgroup, true) as $tevent)
			{
				if(!$countdown_to_end[$time] || !empty($tevent['is_countdown_to_end']))
				{
					// normal event or a "countdown to end" event in "countdown to end" mode -
					// store title (if any) for concatenation
					if(isset($tevent['title']))
					{
						// the same title for both countdown and count up modes
						if(!empty($tevent['title']))
						{
							if(!empty($titles_down[$time]['']))
							{
								$titles_down[$time][''] = $tevent['title'] . ', ' . $titles_down[$time][''];
								$titles_up[$time][''] = $tevent['title'] . ', ' . $titles_up[$time][''];
							}
							else
							{
								$titles_down[$time][''] = $tevent['title'];
								$titles_up[$time][''] = $tevent['title'];
							}	
						}
						
						$titles_down[$time][$tevent['up_limit']] = !empty($titles_down[$time]['']) ? $titles_down[$time][''] : '';
						$titles_up[$time][$tevent['up_limit']] = !empty($titles_up[$time]['']) ? $titles_up[$time][''] : '';
					}
					elseif(isset($tevent['title_down']) && isset($tevent['title_up']))
					{
						// different titles for countdown and count up modes
						
						// titles down
						if(!empty($tevent['title_down']))
						{
							if(!empty($titles_down[$time]['']))
							{
								$titles_down[$time][''] = $tevent['title_down'] . ', ' . $titles_down[$time][''];
							}
							else
							{
								$titles_down[$time][''] = $tevent['title_down'];
							}
						}
						
						$titles_down[$time][$tevent['up_limit']] = !empty($titles_down[$time]['']) ? $titles_down[$time][''] : '';
						
						// titles up
						if(!empty($tevent['title_up']))
						{
							if(!empty($titles_up[$time]['']))
							{
								$titles_up[$time][''] = $tevent['title_up'] . ', ' . $titles_up[$time][''];
							}
							else
							{
								$titles_up[$time][''] = $tevent['title_up'];
							}
						}
						
						$titles_up[$time][$tevent['up_limit']] = !empty($titles_up[$time]['']) ? $titles_up[$time][''] : '';
					}
					else 
					{
						// titles not set by import plugin
						$titles_down[$time][$tevent['up_limit']] = '';
						$titles_up[$time][$tevent['up_limit']] = '';
					}
				}
				else 
				{
					// titles must be ignored for all non-countdown-to-end events
					// in "countdown to end" mode
					$titles_down[$time][$tevent['up_limit']] = '';
					$titles_up[$time][$tevent['up_limit']] = '';
				}
			}
		}
		
		// flatten the groups array and get events timeline
		
		// start the output array
		$events = array();
		
		foreach($groups as $time => $group) {
			foreach($group as $e) {
				$events[] = $e;
			}
		}
		
		// filter out expired events
		
		foreach($events as $i => $event)
		{
			if($i == 0 && $show_countdown >= 0 && $events[$i]['start'] - $now_ts > $show_countdown) {
				// for the first event in the queue do not render counter at all
				// if show_countdown is set for a value smaller than event start - now
				return array();
			}
			
			if($show_countdown < -1 && $now_ts - $event['start'] > $show_countdown * -1)
			{
				// even if the event is current (not finished) but 'show countup' limit is
				// exeeded, remove event
				unset($events[$i]);
			}
				
			if($show_countdown > 0 && isset($events[$i + 1]) && $events[$i + 1]['start'] - $now_ts < $show_countdown)
			{
				// if next event 'show countdown' already wins, remove event - it will not be
				// displayed anyway (1-second flicker workaround)
				unset($events[$i]);
			}
		}
		
		$events = array_values($events);
		
		$lang = JFactory::getLanguage();
		$tag = strtolower($lang->getTag());

		// Final pass through events: calculate diff_js, diff_php for first event and
		// setup event texts
		foreach($events as $i => &$event)
		{
			if($i == 0)
			{
				$event['diff_js'] = $events[$i]['start'] - $now_ts;
				
				// only first event in php format
				$seconds = $event['diff_js'] > 0 ? $event['diff_js'] : $event['diff_js'] * -1;
				$days_diff = floor($seconds / 86400);
				$diff_time = gmdate('H:i:s', $seconds);
				list($h_diff, $i_diff, $s_diff) = explode(':', $diff_time);
				$event['diff_php'] = array('days' => $days_diff, 'hours' => $h_diff, 'minutes' => $i_diff, 'seconds' => $s_diff);
			}
			else
			{
				$prev = $events[$i - 1];
				$event['diff_js'] = $event['start'] - $prev['start'] - $prev['up_limit'];
				
				$event['diff_php'] = array();
			}
			
			// construct event texts
			$text_down = $params->get('text_down_'.$tag,
					$params->get('text_down_def', JText::_('MOD_SMARTCDPRO_TEXT_DOWN_DEFAULT')));
			$text_up = $params->get('text_up_'.$tag,
					$params->get('text_up_def', JText::_('MOD_SMARTCDPRO_TEXT_UP_DEFAULT')));
			
			$replace = array("\n", "\r\n", "\r");
			$text_down = str_replace($replace, '<br />', $text_down);
			$text_up = str_replace($replace, '<br />', $text_up);
				
			// plugins can define the order of the event text concatenation
			if(!empty($event['title_first'])) {
				// event title goes first
				$text_down = $titles_down[$event['start']][$event['up_limit']].' '.$text_down;
				$text_up = $titles_up[$event['start']][$event['up_limit']].' '.$text_up;
			} else {
				// generic text goes first, just append the title
				$text_down .= ' '.$titles_down[$event['start']][$event['up_limit']];
				$text_up .= ' '.$titles_up[$event['start']][$event['up_limit']];
			}

			// for countdown to end we use "text up" as countdown event title
			$event['text_down'] = !empty($countdown_to_end[$event['start']]) ? $text_up : $text_down;
			$event['text_up'] = $text_up;
			$event['text'] = $event['diff_js'] > 0 ? $event['text_down'] : $event['text_up'];
		}
		
		return $events;
	}
	
	private static function unionQueues($queues)
	{
		$events_queue = array();
		$plugins_enabled = false;
		
		foreach($queues as $queue)
		{
			if($queue === true)
			{
				continue;
			}
			$plugins_enabled = true;
			
			foreach($queue as $time => $group)
			{
				if(!isset($events_queue[$time]))
				{
					// empty start time slot - just add the whole group
					$events_queue[$time] = $group;
				}
				else 
				{
					// start time slot with existing group(s)
					// merge events
					foreach($group as $end_time => $event)
					{
						while(isset($events_queue[$time][$end_time]))
						{
							// make sure that end time slot is free, adding '0'
							// to the left of the array key will not affect
							// numeric ordering, but allows to avoid keys
							// duplicates
							$end_time = '0' . $end_time;
						}
						$events_queue[$time][$end_time] = $event;
					}
				}
			}
		}
		
		// apply ordering to all events in queue
		foreach($events_queue as &$group)
		{
			ksort($group, SORT_NUMERIC);
		}
		ksort($events_queue, SORT_NUMERIC);
		
		if($plugins_enabled)
		{
			return $events_queue;
		}
		else
		{
			return false;
		}
	}
	
	private static function getInternal($params)
	{
		$deadline = $params->get('deadline', '');
		
		$app = JFactory::getApplication();
		$session_time_count = $params->get('session_time_count', 0);
		$ids_suffix = '_'.$params->get('module_id', '');
		
		if($session_time_count)
		{
			$deadline = $app->getUserState('mod_smartcountdown' . $ids_suffix . '.session_deadline', null);
			
			if(empty($deadline))
			{
				$date = JFactory::getDate('now');
				if($session_time_count == -1)
				{
					$deadline_ts = $date->getTimestamp();
				}
				else
				{
					$deadline_ts = $date->getTimestamp() + $session_time_count;
				}
				$deadline = JFactory::getDate($deadline_ts)->toSql();
				$app->setUserState('mod_smartcountdown' . $ids_suffix . '.session_deadline', $deadline);
			}
		}
		else
		{
			$app->setUserState('mod_smartcountdown' . $ids_suffix . '.session_deadline', null);
		}

		if($deadline)
		{
			$deadline = JFactory::getDate($deadline);
			$date = JFactory::getDate('now');
			$diff_js = $deadline->format('U', false) - $date->format('U', false);
			
			// Check if an option is set for current mode (countdown/countup)
			
			$show_countdown = $params->get('show_countdown', -1);
			
			if($show_countdown == -3)
			{
				// for internal counter "countdown to end" has no sense, change it
				// to "auto"
				$show_countdown = -1;
			}
			if($show_countdown >= 0 && $diff_js > $show_countdown)
			{
				// too early to show countdown
				return false;
			} 
			if(($show_countdown <= -2 && $diff_js <= $show_countdown) || ($show_countdown == -2 && $diff_js <= 0) /* workaround from $diff_js = -1 */)
			{
				// too late to show count up
				return false;
			}
			
			// for auto-countdowns ($show_countdown = -1) always show
			
			if($show_countdown >= -1) 
			{
				$up_limit = PHP_INT_MAX;
			}
			elseif($show_countdown == -2)
			{
				$up_limit = 0;
			}
			else
			{
				$up_limit = $show_countdown * -1;
			}
			
			// Set event text (countdown or countup)
			$lang = JFactory::getLanguage();
			$tag = strtolower($lang->getTag());
			
			// First try to find language specific strings, then fall back to
			// uni-language string (if no localization plugin is installed) and
			// as last resource use default text from ini file
			$text_down = $params->get('text_down_'.$tag,
					$params->get('text_down_def', JText::_('MOD_SMARTCDPRO_TEXT_DOWN_DEFAULT')));
			$text_up = $params->get('text_up_'.$tag,
					$params->get('text_up_def', JText::_('MOD_SMARTCDPRO_TEXT_UP_DEFAULT')));

			// strip new lines from texts to make them valid js strings
			$replace = array("\n", "\r\n", "\r");
			$text_down = str_replace($replace, ' ', $text_down);
			$text_up = str_replace($replace, ' ', $text_up);
					
			$text = $diff_js > 0 ? $text_down : $text_up;
						
			// Calculate values for php output (for users without js and for first page load)
			$seconds = $diff_js > 0 ? $diff_js : $diff_js * -1;
			$days_diff = floor($seconds / 86400);
			$diff_time = gmdate('H:i:s', $seconds);
			list($h_diff, $i_diff, $s_diff) = explode(':', $diff_time);
			
			// Process on event goto option
			if($params->get('event_goto', 0) || $params->get('counter_clickable', 0))
			{
				$event_goto_url = $params->get('event_goto_url', '');
				$event_goto_menu = empty($event_goto_url) ? $params->get('event_goto_menu', 0) : 0;
				$event_submit_form = $params->get('submit_form', '');
			}
			else 
			{
				$event_goto_menu = 0;
				$event_goto_url = '';
				$event_goto_link = '';
				$event_submit_form = '';
			}
			if($event_goto_menu)
			{
				$menu = JFactory::getApplication()->getMenu();
				$item = $menu->getItem($event_goto_menu);
				
				// we have to check if we have a valid item here: missing item
				// can be caused by menu item unpublished state
				if(!empty($item))
				{
					$router = JSite::getRouter();
					if ($router->getMode() == JROUTER_MODE_SEF)
					{
						$event_goto_link = 'index.php?Itemid='.$event_goto_menu;
					}
					else
					{
						$event_goto_link = $item->link.'&Itemid='.$event_goto_menu;
					}
					$event_goto_link .= '&lang='.substr($item->language, 0, 2);
					if (strcasecmp(substr($event_goto_link, 0, 4), 'http') && (strpos($event_goto_link, 'index.php?') !== false))
					{
						$event_goto_link = JRoute::_($event_goto_link, true, $item->params->get('secure'));
					}
					else
					{
						$event_goto_link = JRoute::_($event_goto_link);
					}
					$event_goto_link = JURI::getInstance()->toString(array('scheme', 'host')).$event_goto_link;
				}
				else
				{
					$event_goto_link = '';
				}
			}
			if($event_goto_url)
			{
				// workaround for duplicated 'http://' prefix prepended to url
				// for relative urls (e.g. index.php)
				if(strripos($event_goto_url, 'http') !== 0)
				{
					$event_goto_url = substr($event_goto_url, stripos($event_goto_url, '://') + 3);
				}
				$event_goto_link = $event_goto_url;
				// ignore empty url
				if(substr($event_goto_link, -3) == '://')
				{
					$event_goto_link = '';
				}
			}
			if($event_submit_form)
			{
				// if submit form is not empty, make counter not clickable and do not allow redirect confirmation
				$event_goto_link = trim($event_submit_form);
				$params->set('counter_clickable', 0);
				$params->set('confirm_redirect', 0);
			}
			
			// build result
			$result = array();
			// construct events queue with one entry only
			$result[] = array(
					'diff_js' => $diff_js,
					'up_limit' => $up_limit,
					'text' => $text,
					'text_down' => $text_down,
					'text_up' => $text_up,
					'diff_php' => array('days' => $days_diff, 'hours' => $h_diff, 'minutes' => $i_diff, 'seconds' => $s_diff),
					'event_goto_link' => $event_goto_link
			);
			return $result;
		}
		return false;
	}
	
	static function getDigitsConfig($xml, $scale = 1.0, $baseFontSize = 12)
	{
		// compatibility workaround: in some environments XML document is
		// saved with "<styles>" element opening tags replaced with
		// "<s-tyles>". We allow this replace (as it can be DB security
		// concerned: looks like an HTML element <style...), but we need
		// to recover original XML syntax
		$xml = str_replace('<s-tyles>', '<styles>', $xml);
		
		// now XML document should be valid
		libxml_use_internal_errors(true);
				
		$xml = simplexml_load_string($xml);
		
		foreach (libxml_get_errors() as $error)
		{
			JLog::add(JText::_('MOD_SMARTCOUNTDOWN').': ' . $error->message, JLog::WARNING);
		}
		if(empty($xml))
		{
			return false;
		}
		
		$digitsConfig = array();
		
		// global settings
		$digitsConfig['name'] = $xml['name'] ? (string)$xml['name'] : 'Custom';
		$digitsConfig['description'] = $xml['description'] ? (string)$xml['description'] : '';
		
		$digitsConfig['images_folder'] = JURI::root().($xml['images_folder'] ? (string)$xml['images_folder'] : '');
		
		// get all digit scopes configurations
		foreach($xml->digit as $digit) {
			
			// scope attribute may contain more than one value (comma-separated list)
			$scopes = explode(',', (string)$digit['scope']);
			
			foreach($scopes as $scope) {
				// init config for all scopes in list
				$digitsConfig['digits'][$scope] = array();
			}
			
			// construct digit style
			$styles = '';
			foreach($digit->styles->style as $value) {
				$attrs = array();
				foreach($value->attributes() as $k => $v) {
					$attrs[$k] = (string)$v;
				}
				
				// If attribut unit is "px" we translate it to "em" using global base font size
				// setting
				if($attrs['unit'] == 'px')
				{
					$attrs['unit'] = 'em';
					$attrs['value'] = $attrs['value'] / $baseFontSize;
				}
				
				// Scale the value if it has 'scalable' attribute set
				$result = !empty($attrs['scalable']) ? $scale * $attrs['value'] : $attrs['value'];
				$styles .= $attrs['name'].':'.$result.(!empty($attrs['unit']) ? $attrs['unit'] : '').';';
			}
			
			// for digit style - if background set, prepend images_folder
			$styles = preg_replace('#url\((\S+)\)#', 'url('.$digitsConfig['images_folder'].'$1)', $styles);
			
			foreach($scopes as $scope) {
				// set styles for all scopes in list
				$digitsConfig['digits'][$scope]['style'] = $styles;
			}
			
			// get modes (down and up)
			foreach($digit->modes->mode as $groups) {
				
				$attrs = $groups->attributes();
				$mode = (string)$attrs['name'];
				
				foreach($groups as $group) {
					
					$grConfig = array();
					
					$grAttrs = $group->attributes();
					foreach($grAttrs as $k => $v) {
						$grConfig[$k] = (string)$v;
					}
					
					$grConfig['elements'] = array();
					
					// get all elements for the group
					foreach($group as $element) {
						// default values to use if attribute is missing
						$elConfig = array('filename_base' => '', 'filename_ext' => '', 'value_type' => '');
						
						$elAttrs = $element->attributes();
						foreach($elAttrs as $k => $v) {
							$elConfig[$k] = (string)$v;
						}
						
						$elConfig['styles'] = self::getElementStyles($element->styles, $digitsConfig['images_folder']);
						$elConfig['tweens'] = self::getElementTweens($element->tweens);
						
						$grConfig['elements'][] = $elConfig;
					}
					
					foreach($scopes as $scope) {
						// set fx configuration for all scopes in list
						$digitsConfig['digits'][$scope][$mode][] = $grConfig;
					}
				}
			}
		}
		
		return $digitsConfig;
	}
	
	private static function getElementStyles($styles, $images_folder)
	{
		$result = '';
		
		if(empty($styles)) {
			return $result;
		}
		
		$tpl = '%s:%s;';
		$styles = $styles->children();
		
		for($i = 0; $count = count($styles), $i < $count; $i++) {
			$result .= sprintf($tpl, $styles[$i]->getName(), (string)$styles[$i]);
		}
		
		// if styles contain background - prepend images_folder
		$result = preg_replace('#url\((\S+)\)#', 'url('.$images_folder.'$1)', $result);

		return $result;
	}
	
	private static function getElementTweens($tweens)
	{
		$result = new stdClass();
		
		if(empty($tweens)) {
			return $result;
		}
		
		$tweens = $tweens->children();
		
		for($i = 0; $count = count($tweens), $i < $count; $i++) {
			$name = $tweens[$i]->getName();
			$result->$name = explode(',', (string)$tweens[$i]);
		}
		
		return $result;
	}
	
	public static function getLayoutConfig($params)
	{
		$config = array();
		$base_font_size = $params->get('base_font_size', 12);
		
		$config['hide_zero_fields'] = $params->get('hide_zero_fields', 1);
		$config['display_seconds'] = $params->get('display_seconds', 1);

		$module_width = $params->get('module_width', '');
		$module_padding = $params->get('module_padding', 0);
		$module_background = $params->get('module_background', '');
		$module_style = ($module_width ? 'width:'.$module_width.'px;' : '');
		$module_style .= ($module_padding ? 'padding:'.$module_padding.'px;' : '');
		$module_style .= ($module_background ? 'background:'.str_replace('"', "'", $module_background).';' : '');
		if($params->get('global_center', 1)) {
			// add global horizontal centering feature
			$module_style .= ($params->get('global_center', 1) ? 'text-align:center;' : '');
			$config['wrapper_class'] = ' class="scdp-wrapper-center"';
		} else {
			$config['wrapper_class'] = '';
		}
		// Add global font-size style to the module wrapper
		// we also override line-height:normal, so that actual line-height will be calculated 
		// for all elements basing on current font size
		$config['module_style'] = $module_style.'font-size:'.$base_font_size.'px;line-height:normal;';
		
		// new feature ***
		$hide_events_text = $params->get('countdown_mode', 'down') == 'up' && $params->get('hidden_countup', 0) ? 'display:none;' : '';
		$are_texts_responsive = $params->get('are_texts_responsive', 1);
		
		$event_text_style = $params->get('event_text_style', '');
		if($are_texts_responsive)
		{
			$event_text_size = $params->get('event_text_size', 20) / $base_font_size;
			$event_text_style = 'font-size:'.$event_text_size.'em;'.$event_text_style;
		}
		else 
		{
			$event_text_size = $params->get('event_text_size', 20);
			$event_text_style = 'font-size:'.$event_text_size.'px;'.$event_text_style;
		}
		$config['event_text_style'] = $event_text_style . $hide_events_text;
		
		$text_after_style = $params->get('text_after_style', '');
		if($are_texts_responsive)
		{
			$text_after_size = $params->get('text_after_size', 20) / $base_font_size;
			$text_after_style = 'font-size:'.$text_after_size.'em;'.$text_after_style;
		}
		else
		{
			$text_after_size = $params->get('text_after_size', 20);
			$text_after_style = 'font-size:'.$text_after_size.'px;'.$text_after_style;
		}
		$config['text_after_style'] = $text_after_style . $hide_events_text;
		
		$numbers_style = $params->get('digits_style', '');
		$config['numbers_style'] = $numbers_style;
		
		$labels_size = $params->get('labels_size', 20) / $base_font_size;
		$labels_style = $params->get('labels_style', '');
		$labels_style = 'font-size:'.$labels_size.'em;'.$labels_style;
		$config['labels_style'] = $labels_style;
		
		if($params->get('overload_labels', 0)) {
			$tpl = $params->get('overload_labels_tpl', '');
		}
		else {
			$tpl = '%|%|%|%';
		}
		$tpl = explode('|', $tpl);
		$tpl = array_slice(array_pad($tpl, 4, ''), 0, 4);
		
		// *** new feature - deadline message
		$deadline_message_style = $params->get('deadline_message_style', '');
		if($are_texts_responsive)
		{
			$deadline_message_text_size = $params->get('deadline_message_text_size', 20) / $base_font_size;
			$deadline_message_style = 'font-size:'.$deadline_message_text_size.'em;'.$deadline_message_style;
		}
		else 
		{
			$deadline_message_text_size = $params->get('deadline_message_text_size', 20);
			$deadline_message_style = 'font-size:'.$deadline_message_text_size.'px;'.$deadline_message_style;
		}
		$config['deadline_message_style'] = $deadline_message_style;
		
		$config['label_types'] = array_combine(array('days', 'hours', 'minutes', 'seconds'), $tpl);
		$config['separator_image'] = $params->get('separator_image', '');
		
		$textPosition = $params->get('text_position', 'top');
		$labelsPosition = $params->get('labels_position', 'right');
		
		$textBeforeCounter = $textPosition == 'left' || $textPosition == 'top';
		$labelsBeforeNumbers = $labelsPosition == 'left' || $labelsPosition == 'top';
		
		$config['text_layout_class'] = $textPosition == 'left' || $textPosition == 'right' ? 'horz' : 'vert';
		$config['counter_layout_class'] = $params->get('counter_layout', 'column');
		$config['units_layout_class'] = $labelsPosition == 'left' || $labelsPosition == 'right' ? 'horz' : 'vert';
		
		$config['text_before_counter'] = $textBeforeCounter;
		$config['labels_before_numbers'] = $labelsBeforeNumbers;
		
		$config['counter_style'] = $params->get('counter_style', '');
		if(strpos($config['counter_style'], 'margin') === false) {
			// use 'text_spacing' setting if no margins are set in 'counter_style'
			$text_spacing = $params->get('text_spacing', 6) / $base_font_size;
			$config['counter_style'] = $config['counter_style'].'margin-'.$textPosition.':'.$text_spacing.'em;';
		}
		if($params->get('global_center', 1)) {
			// add global horizontal centering feature
			$config['counter_style'] .= 'display:inline-block;';
		}
		
		$units_spacing = $params->get('units_spacing', 4) / $base_font_size;
		$units_style = $config['counter_layout_class'] == 'column' ?
			'padding:'.($units_spacing / 2).'em 0;' :
			'padding:0 '.($units_spacing / 2).'em;' ;
		$config['units_style'] = $units_style;
		
		$labels_spacing = $params->get('numbers_lables_spacing', 8) / $base_font_size;
		$labels_margin = 'margin-'.$labelsPosition.':'.$labels_spacing.'em;';
		$config['numbers_style'] = $labels_margin.$config['numbers_style'];
		
		$config['module_style'] = $config['module_style'] ? ' style="'.$config['module_style'].'"' : '';
		$config['event_text_style'] = $config['event_text_style'] ? ' style="'.$config['event_text_style'].'"' : '';
		$config['text_after_style'] = $config['text_after_style'] ? ' style="'.$config['text_after_style'].'"' : '';
		$config['counter_style'] = $config['counter_style'] ? ' style="'.$config['counter_style'].'"' : '';
		
		// special for reserved space setting for day digits
		$min_days_width = $params->get('min_days_width', 0) == '' ? 0 : $params->get('min_days_width', 0)  / $base_font_size;
		$config['days_number_style'] = ' style="'.$config['numbers_style'].'min-width:'.$min_days_width.'em;"';
		
		$config['numbers_style'] = $config['numbers_style'] ? ' style="'.$config['numbers_style'].'"' : '';
		$config['labels_style'] = $config['labels_style'] ? ' style="'.$config['labels_style'].'"' : '';
		
		// *** new feature - deadline message
		$config['deadline_message_style'] = $config['deadline_message_style'] ? ' style="'.$config['deadline_message_style'].'"' : '';
		
		return $config;
	}
	
	public static function getNumber($name, $values)
	{
		$assets = array(
			'days' => array(6, null), 
			'hours' => array(4, 2),
			'minutes' => array(2, 2),
			'seconds' => array(0, 2)
		);

		// digits floating left, so we start with the highest digit here...
		$digits = array_reverse(array_slice($values, $assets[$name][0], $assets[$name][1], true), true);
		$value = implode('', $digits);
		
		return array('digits' => $digits, 'value' => $value);
	}
}