Fix MSIE conditionals regex in minify output func

Allows IE conditionals like the following to remain unmodified.

<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->

Credit to joebert regex from
http://www.sitepoint.com/forums/showthread.php?696559-Regex-pattern-to-strip-HTML-comments-but-leave-conditonals&s=3eef4ceb0a59b2fdb946fa56220fb6fd&p=4678083&viewfull=1#post4678083
diff --git a/system/core/Output.php b/system/core/Output.php
index ce0500e..b452639 100644
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -1,922 +1,922 @@
-<?php
-/**
- * CodeIgniter
- *
- * An open source application development framework for PHP 5.2.4 or newer
- *
- * NOTICE OF LICENSE
- *
- * Licensed under the Open Software License version 3.0
- *
- * This source file is subject to the Open Software License (OSL 3.0) that is
- * bundled with this package in the files license.txt / license.rst.  It is
- * also available through the world wide web at this URL:
- * http://opensource.org/licenses/OSL-3.0
- * If you did not receive a copy of the license and are unable to obtain it
- * through the world wide web, please send an email to
- * licensing@ellislab.com so we can send you a copy immediately.
- *
- * @package		CodeIgniter
- * @author		EllisLab Dev Team
- * @copyright	Copyright (c) 2008 - 2013, EllisLab, Inc. (http://ellislab.com/)
- * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
- * @link		http://codeigniter.com
- * @since		Version 1.0
- * @filesource
- */
-defined('BASEPATH') OR exit('No direct script access allowed');
-
-/**
- * Output Class
- *
- * Responsible for sending final output to the browser.
- *
- * @package		CodeIgniter
- * @subpackage	Libraries
- * @category	Output
- * @author		EllisLab Dev Team
- * @link		http://codeigniter.com/user_guide/libraries/output.html
- */
-class CI_Output {
-
-	/**
-	 * Final output string
-	 *
-	 * @var	string
-	 */
-	public $final_output;
-
-	/**
-	 * Cache expiration time
-	 *
-	 * @var	int
-	 */
-	public $cache_expiration = 0;
-
-	/**
-	 * List of server headers
-	 *
-	 * @var	array
-	 */
-	public $headers =	array();
-
-	/**
-	 * List of mime types
-	 *
-	 * @var	array
-	 */
-	public $mimes =		array();
-
-	/**
-	 * Mime-type for the current page
-	 *
-	 * @var	string
-	 */
-	protected $mime_type	= 'text/html';
-
-	/**
-	 * Enable Profiler flag
-	 *
-	 * @var	bool
-	 */
-	public $enable_profiler = FALSE;
-
-	/**
-	 * zLib output compression flag
-	 *
-	 * @var	bool
-	 */
-	protected $_zlib_oc =		FALSE;
-
-	/**
-	 * List of profiler sections
-	 *
-	 * @var	array
-	 */
-	protected $_profiler_sections =	array();
-
-	/**
-	 * Parse markers flag
-	 *
-	 * Whether or not to parse variables like {elapsed_time} and {memory_usage}.
-	 *
-	 * @var	bool
-	 */
-	public $parse_exec_vars =	TRUE;
-
-	/**
-	 * Class constructor
-	 *
-	 * Determines whether zLib output compression will be used.
-	 *
-	 * @return	void
-	 */
-	public function __construct()
-	{
-		$this->_zlib_oc = (bool) @ini_get('zlib.output_compression');
-
-		// Get mime types for later
-		$this->mimes =& get_mimes();
-
-		log_message('debug', 'Output Class Initialized');
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Get Output
-	 *
-	 * Returns the current output string.
-	 *
-	 * @return	string
-	 */
-	public function get_output()
-	{
-		return $this->final_output;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Set Output
-	 *
-	 * Sets the output string.
-	 *
-	 * @param	string	$output	Output data
-	 * @return	CI_Output
-	 */
-	public function set_output($output)
-	{
-		$this->final_output = $output;
-		return $this;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Append Output
-	 *
-	 * Appends data onto the output string.
-	 *
-	 * @param	string	$output	Data to append
-	 * @return	CI_Output
-	 */
-	public function append_output($output)
-	{
-		if (empty($this->final_output))
-		{
-			$this->final_output = $output;
-		}
-		else
-		{
-			$this->final_output .= $output;
-		}
-
-		return $this;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Set Header
-	 *
-	 * Lets you set a server header which will be sent with the final output.
-	 *
-	 * Note: If a file is cached, headers will not be sent.
-	 * @todo	We need to figure out how to permit headers to be cached.
-	 *
-	 * @param	string	$header		Header
-	 * @param	bool	$replace	Whether to replace the old header value, if already set
-	 * @return	CI_Output
-	 */
-	public function set_header($header, $replace = TRUE)
-	{
-		// If zlib.output_compression is enabled it will compress the output,
-		// but it will not modify the content-length header to compensate for
-		// the reduction, causing the browser to hang waiting for more data.
-		// We'll just skip content-length in those cases.
-		if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)
-		{
-			return $this;
-		}
-
-		$this->headers[] = array($header, $replace);
-		return $this;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Set Content-Type Header
-	 *
-	 * @param	string	$mime_type	Extension of the file we're outputting
-	 * @param	string	$charset	Character set (default: NULL)
-	 * @return	CI_Output
-	 */
-	public function set_content_type($mime_type, $charset = NULL)
-	{
-		if (strpos($mime_type, '/') === FALSE)
-		{
-			$extension = ltrim($mime_type, '.');
-
-			// Is this extension supported?
-			if (isset($this->mimes[$extension]))
-			{
-				$mime_type =& $this->mimes[$extension];
-
-				if (is_array($mime_type))
-				{
-					$mime_type = current($mime_type);
-				}
-			}
-		}
-
-		$this->mime_type = $mime_type;
-
-		if (empty($charset))
-		{
-			$charset = config_item('charset');
-		}
-
-		$header = 'Content-Type: '.$mime_type
-			.(empty($charset) ? NULL : '; charset='.$charset);
-
-		$this->headers[] = array($header, TRUE);
-		return $this;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Get Current Content-Type Header
-	 *
-	 * @return	string	'text/html', if not already set
-	 */
-	public function get_content_type()
-	{
-		for ($i = 0, $c = count($this->headers); $i < $c; $i++)
-		{
-			if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1)
-			{
-				return $content_type;
-			}
-		}
-
-		return 'text/html';
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Get Header
-	 *
-	 * @param	string	$header_name
-	 * @return	string
-	 */
-	public function get_header($header)
-	{
-		// Combine headers already sent with our batched headers
-		$headers = array_merge(
-			// We only need [x][0] from our multi-dimensional array
-			array_map('array_shift', $this->headers),
-			headers_list()
-		);
-
-		if (empty($headers) OR empty($header))
-		{
-			return NULL;
-		}
-
-		for ($i = 0, $c = count($headers); $i < $c; $i++)
-		{
-			if (strncasecmp($header, $headers[$i], $l = strlen($header)) === 0)
-			{
-				return trim(substr($headers[$i], $l+1));
-			}
-		}
-
-		return NULL;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Set HTTP Status Header
-	 *
-	 * As of version 1.7.2, this is an alias for common function
-	 * set_status_header().
-	 *
-	 * @param	int	$code	Status code (default: 200)
-	 * @param	string	$text	Optional message
-	 * @return	CI_Output
-	 */
-	public function set_status_header($code = 200, $text = '')
-	{
-		set_status_header($code, $text);
-		return $this;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Enable/disable Profiler
-	 *
-	 * @param	bool	$val	TRUE to enable or FALSE to disable
-	 * @return	CI_Output
-	 */
-	public function enable_profiler($val = TRUE)
-	{
-		$this->enable_profiler = is_bool($val) ? $val : TRUE;
-		return $this;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Set Profiler Sections
-	 *
-	 * Allows override of default/config settings for
-	 * Profiler section display.
-	 *
-	 * @param	array	$sections	Profiler sections
-	 * @return	CI_Output
-	 */
-	public function set_profiler_sections($sections)
-	{
-		if (isset($sections['query_toggle_count']))
-		{
-			$this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];
-			unset($sections['query_toggle_count']);
-		}
-
-		foreach ($sections as $section => $enable)
-		{
-			$this->_profiler_sections[$section] = ($enable !== FALSE);
-		}
-
-		return $this;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Set Cache
-	 *
-	 * @param	int	$time	Cache expiration time in seconds
-	 * @return	CI_Output
-	 */
-	public function cache($time)
-	{
-		$this->cache_expiration = is_numeric($time) ? $time : 0;
-		return $this;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Display Output
-	 *
-	 * Processes sends the sends finalized output data to the browser along
-	 * with any server headers and profile data. It also stops benchmark
-	 * timers so the page rendering speed and memory usage can be shown.
-	 *
-	 * Note: All "view" data is automatically put into $this->final_output
-	 *	 by controller class.
-	 *
-	 * @uses	CI_Output::$final_output
-	 * @param	string	$output	Output data override
-	 * @return	void
-	 */
-	public function _display($output = '')
-	{
-		// Note:  We use globals because we can't use $CI =& get_instance()
-		// since this function is sometimes called by the caching mechanism,
-		// which happens before the CI super object is available.
-		global $BM, $CFG;
-
-		// Grab the super object if we can.
-		if (class_exists('CI_Controller'))
-		{
-			$CI =& get_instance();
-		}
-
-		// --------------------------------------------------------------------
-
-		// Set the output data
-		if ($output === '')
-		{
-			$output =& $this->final_output;
-		}
-
-		// --------------------------------------------------------------------
-
-		// Is minify requested?
-		if ($CFG->item('minify_output') === TRUE)
-		{
-			$output = $this->minify($output, $this->mime_type);
-		}
-
-		// --------------------------------------------------------------------
-
-		// Do we need to write a cache file? Only if the controller does not have its
-		// own _output() method and we are not dealing with a cache file, which we
-		// can determine by the existence of the $CI object above
-		if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
-		{
-			$this->_write_cache($output);
-		}
-
-		// --------------------------------------------------------------------
-
-		// Parse out the elapsed time and memory usage,
-		// then swap the pseudo-variables with the data
-
-		$elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
-
-		if ($this->parse_exec_vars === TRUE)
-		{
-			$memory	= round(memory_get_usage() / 1024 / 1024, 2).'MB';
-
-			$output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
-		}
-
-		// --------------------------------------------------------------------
-
-		// Is compression requested?
-		if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE
-			&& extension_loaded('zlib')
-			&& isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
-		{
-			ob_start('ob_gzhandler');
-		}
-
-		// --------------------------------------------------------------------
-
-		// Are there any server headers to send?
-		if (count($this->headers) > 0)
-		{
-			foreach ($this->headers as $header)
-			{
-				@header($header[0], $header[1]);
-			}
-		}
-
-		// --------------------------------------------------------------------
-
-		// Does the $CI object exist?
-		// If not we know we are dealing with a cache file so we'll
-		// simply echo out the data and exit.
-		if ( ! isset($CI))
-		{
-			echo $output;
-			log_message('debug', 'Final output sent to browser');
-			log_message('debug', 'Total execution time: '.$elapsed);
-			return;
-		}
-
-		// --------------------------------------------------------------------
-
-		// Do we need to generate profile data?
-		// If so, load the Profile class and run it.
-		if ($this->enable_profiler === TRUE)
-		{
-			$CI->load->library('profiler');
-			if ( ! empty($this->_profiler_sections))
-			{
-				$CI->profiler->set_sections($this->_profiler_sections);
-			}
-
-			// If the output data contains closing </body> and </html> tags
-			// we will remove them and add them back after we insert the profile data
-			$output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();
-			if ($count > 0)
-			{
-				$output .= '</body></html>';
-			}
-		}
-
-		// Does the controller contain a function named _output()?
-		// If so send the output there.  Otherwise, echo it.
-		if (method_exists($CI, '_output'))
-		{
-			$CI->_output($output);
-		}
-		else
-		{
-			echo $output; // Send it to the browser!
-		}
-
-		log_message('debug', 'Final output sent to browser');
-		log_message('debug', 'Total execution time: '.$elapsed);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Write Cache
-	 *
-	 * @param	string	$output	Output data to cache
-	 * @return	void
-	 */
-	public function _write_cache($output)
-	{
-		$CI =& get_instance();
-		$path = $CI->config->item('cache_path');
-		$cache_path = ($path === '') ? APPPATH.'cache/' : $path;
-
-		if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
-		{
-			log_message('error', 'Unable to write cache file: '.$cache_path);
-			return;
-		}
-
-		$uri =	$CI->config->item('base_url').
-				$CI->config->item('index_page').
-				$CI->uri->uri_string();
-
-		$cache_path .= md5($uri);
-
-		if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
-		{
-			log_message('error', 'Unable to write cache file: '.$cache_path);
-			return;
-		}
-
-		$expire = time() + ($this->cache_expiration * 60);
-
-		if (flock($fp, LOCK_EX))
-		{
-			fwrite($fp, $expire.'TS--->'.$output);
-			flock($fp, LOCK_UN);
-		}
-		else
-		{
-			log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
-			return;
-		}
-		fclose($fp);
-		@chmod($cache_path, FILE_WRITE_MODE);
-
-		log_message('debug', 'Cache file written: '.$cache_path);
-
-		// Send HTTP cache-control headers to browser to match file cache settings.
-		$this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Update/serve cached output
-	 *
-	 * @uses	CI_Config
-	 * @uses	CI_URI
-	 *
-	 * @param	object	&$CFG	CI_Config class instance
-	 * @param	object	&$URI	CI_URI class instance
-	 * @return	bool	TRUE on success or FALSE on failure
-	 */
-	public function _display_cache(&$CFG, &$URI)
-	{
-		$cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');
-
-		// Build the file path. The file name is an MD5 hash of the full URI
-		$uri =	$CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;
-		$filepath = $cache_path.md5($uri);
-
-		if ( ! @file_exists($filepath) OR ! $fp = @fopen($filepath, FOPEN_READ))
-		{
-			return FALSE;
-		}
-
-		flock($fp, LOCK_SH);
-
-		$cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';
-
-		flock($fp, LOCK_UN);
-		fclose($fp);
-
-		// Strip out the embedded timestamp
-		if ( ! preg_match('/^(\d+)TS--->/', $cache, $match))
-		{
-			return FALSE;
-		}
-
-		$last_modified = filemtime($cache_path);
-		$expire = $match[1];
-
-		// Has the file expired?
-		if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
-		{
-			// If so we'll delete it.
-			@unlink($filepath);
-			log_message('debug', 'Cache file has expired. File deleted.');
-			return FALSE;
-		}
-		else
-		{
-			// Or else send the HTTP cache control headers.
-			$this->set_cache_header($last_modified, $expire);
-		}
-
-		// Display the cache
-		$this->_display(substr($cache, strlen($match[0])));
-		log_message('debug', 'Cache file is current. Sending it to browser.');
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Delete cache
-	 *
-	 * @param	string	$uri	URI string
-	 * @return	bool
-	 */
-	public function delete_cache($uri = '')
-	{
-		$CI =& get_instance();
-		$cache_path = $CI->config->item('cache_path');
-		if ($cache_path === '')
-		{
-			$cache_path = APPPATH.'cache/';
-		}
-
-		if ( ! is_dir($cache_path))
-		{
-			log_message('error', 'Unable to find cache path: '.$cache_path);
-			return FALSE;
-		}
-
-		if (empty($uri))
-		{
-			$uri = $CI->uri->uri_string();
-		}
-
-		$cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').$uri);
-
-		if ( ! @unlink($cache_path))
-		{
-			log_message('error', 'Unable to delete cache file for '.$uri);
-			return FALSE;
-		}
-
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Set Cache Header
-	 *
-	 * Set the HTTP headers to match the server-side file cache settings
-	 * in order to reduce bandwidth.
-	 *
-	 * @param	int	$last_modified	Timestamp of when the page was last modified
-	 * @param	int	$expiration	Timestamp of when should the requested page expire from cache
-	 * @return	void
-	 */
-	public function set_cache_header($last_modified, $expiration)
-	{
-		$max_age = $expiration - $_SERVER['REQUEST_TIME'];
-
-		if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']))
-		{
-			$this->set_status_header(304);
-			exit;
-		}
-		else
-		{
-			header('Pragma: public');
-			header('Cache-Control: max-age=' . $max_age . ', public');
-			header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
-			header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
-		}
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Minify
-	 *
-	 * Reduce excessive size of HTML/CSS/JavaScript content.
-	 *
-	 * @param	string	$output	Output to minify
-	 * @param	string	$type	Output content MIME type
-	 * @return	string	Minified output
-	 */
-	public function minify($output, $type = 'text/html')
-	{
-		switch ($type)
-		{
-			case 'text/html':
-
-				if (($size_before = strlen($output)) === 0)
-				{
-					return '';
-				}
-
-				// Find all the <pre>,<code>,<textarea>, and <javascript> tags
-				// We'll want to return them to this unprocessed state later.
-				preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);
-				preg_match_all('{<code.+</code>}msU', $output, $codes_clean);
-				preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);
-				preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);
-
-				// Minify the CSS in all the <style> tags.
-				preg_match_all('{<style.+</style>}msU', $output, $style_clean);
-				foreach ($style_clean[0] as $s)
-				{
-					$output = str_replace($s, $this->_minify_script_style($s, TRUE), $output);
-				}
-
-				// Minify the javascript in <script> tags.
-				foreach ($javascript_clean[0] as $s)
-				{
-					$javascript_mini[] = $this->_minify_script_style($s, TRUE);
-				}
-
-				// Replace multiple spaces with a single space.
-				$output = preg_replace('!\s{2,}!', ' ', $output);
-
-				// Remove comments (non-MSIE conditionals)
-				$output = preg_replace('{\s*<!--[^\[].*-->\s*}msU', '', $output);
-
-				// Remove spaces around block-level elements.
-				$output = preg_replace('/\s*(<\/?(html|head|title|meta|script|link|style|body|h[1-6]|div|p|br)[^>]*>)\s*/is', '$1', $output);
-
-				// Replace mangled <pre> etc. tags with unprocessed ones.
-
-				if ( ! empty($pres_clean))
-				{
-					preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);
-					$output = str_replace($pres_messed[0], $pres_clean[0], $output);
-				}
-
-				if ( ! empty($codes_clean))
-				{
-					preg_match_all('{<code.+</code>}msU', $output, $codes_messed);
-					$output = str_replace($codes_messed[0], $codes_clean[0], $output);
-				}
-
-				if ( ! empty($codes_clean))
-				{
-					preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);
-					$output = str_replace($textareas_messed[0], $textareas_clean[0], $output);
-				}
-
-				if (isset($javascript_mini))
-				{
-					preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);
-					$output = str_replace($javascript_messed[0], $javascript_mini, $output);
-				}
-
-				$size_removed = $size_before - strlen($output);
-				$savings_percent = round(($size_removed / $size_before * 100));
-
-				log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');
-
-			break;
-
-			case 'text/css':
-			case 'text/javascript':
-
-				$output = $this->_minify_script_style($output);
-
-			break;
-
-			default: break;
-		}
-
-		return $output;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
-	 * Minify Style and Script
-	 *
-	 * Reduce excessive size of CSS/JavaScript content.  To remove spaces this
-	 * script walks the string as an array and determines if the pointer is inside
-	 * a string created by single quotes or double quotes.  spaces inside those
-	 * strings are not stripped.  Opening and closing tags are severed from
-	 * the string initially and saved without stripping whitespace to preserve
-	 * the tags and any associated properties if tags are present
-	 *
-	 * Minification logic/workflow is similar to methods used by Douglas Crockford
-	 * in JSMIN. http://www.crockford.com/javascript/jsmin.html
-	 *
-	 * KNOWN ISSUE: ending a line with a closing parenthesis ')' and no semicolon
-	 * where there should be one will break the Javascript. New lines after a
-	 * closing parenthesis are not recognized by the script. For best results
-	 * be sure to terminate lines with a semicolon when appropriate.
-	 *
-	 * @param	string	$output		Output to minify
-	 * @param	bool	$has_tags	Specify if the output has style or script tags
-	 * @return	string	Minified output
-	 */
-	protected function _minify_script_style($output, $has_tags = FALSE)
-	{
-		// We only need this if there are tags in the file
-		if ($has_tags === TRUE)
-		{
-			// Remove opening tag and save for later
-			$pos = strpos($output, '>') + 1;
-			$open_tag = substr($output, 0, $pos);
-			$output = substr_replace($output, '', 0, $pos);
-
-			// Remove closing tag and save it for later
-			$end_pos = strlen($output);
-			$pos = strpos($output, '</');
-			$closing_tag = substr($output, $pos, $end_pos);
-			$output = substr_replace($output, '', $pos);
-		}
-
-		// Remove CSS comments
-		$output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!i', '', $output);
-
-		// Remove spaces around curly brackets, colons,
-		// semi-colons, parenthesis, commas
-		$output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!i', '$1', $output);
-
-		// Replace tabs with spaces
-		// Replace carriage returns & multiple new lines with single new line
-		// and trim any leading or trailing whitespace
-		$output = trim(preg_replace(array('/\t+/', '/\r/', '/\n+/'), array(' ', "\n", "\n"), $output));
-
-		// Remove spaces when safe to do so.
-		$in_string = $in_dstring = $prev = FALSE;
-		$array_output = str_split($output);
-		foreach ($array_output as $key => $value)
-		{
-			if ($in_string === FALSE && $in_dstring === FALSE)
-			{
-				if ($value === ' ')
-				{
-					// Get the next element in the array for comparisons
-					$next = $array_output[$key + 1];
-
-					// Strip spaces preceded/followed by a non-ASCII character
-					// or not preceded/followed by an alphanumeric
-					// or not preceded/followed \ $ and _
-					if ((preg_match('/^[\x20-\x7f]*$/D', $next) OR preg_match('/^[\x20-\x7f]*$/D', $prev))
-						&& ( ! ctype_alnum($next) OR ! ctype_alnum($prev))
-						&& ! in_array($next, array('\\', '_', '$'), TRUE)
-						&& ! in_array($prev, array('\\', '_', '$'), TRUE)
-					)
-					{
-						unset($array_output[$key]);
-					}
-				}
-				else
-				{
-					// Save this value as previous for the next iteration
-					// if it is not a blank space
-					$prev = $value;
-				}
-			}
-
-			if ($value === "'")
-			{
-				$in_string = ! $in_string;
-			}
-			elseif ($value === '"')
-			{
-				$in_dstring = ! $in_dstring;
-			}
-		}
-
-		// Put the string back together after spaces have been stripped
-		$output = implode($array_output);
-
-		// Remove new line characters unless previous or next character is
-		// printable or Non-ASCII
-		preg_match_all('/[\n]/', $output, $lf, PREG_OFFSET_CAPTURE);
-		$removed_lf = 0;
-		foreach ($lf as $feed_position)
-		{
-			foreach ($feed_position as $position)
-			{
-				$position = $position[1] - $removed_lf;
-				$next = $output[$position + 1];
-				$prev = $output[$position - 1];
-				if ( ! ctype_print($next) && ! ctype_print($prev)
-					&& ! preg_match('/^[\x20-\x7f]*$/D', $next)
-					&& ! preg_match('/^[\x20-\x7f]*$/D', $prev)
-				)
-				{
-					$output = substr_replace($output, '', $position, 1);
-					$removed_lf++;
-				}
-			}
-		}
-
-		// Put the opening and closing tags back if applicable
-		return isset($open_tag)
-			? $open_tag.$output.$closing_tag
-			: $output;
-	}
-
-}
-
-/* End of file Output.php */
+<?php

+/**

+ * CodeIgniter

+ *

+ * An open source application development framework for PHP 5.2.4 or newer

+ *

+ * NOTICE OF LICENSE

+ *

+ * Licensed under the Open Software License version 3.0

+ *

+ * This source file is subject to the Open Software License (OSL 3.0) that is

+ * bundled with this package in the files license.txt / license.rst.  It is

+ * also available through the world wide web at this URL:

+ * http://opensource.org/licenses/OSL-3.0

+ * If you did not receive a copy of the license and are unable to obtain it

+ * through the world wide web, please send an email to

+ * licensing@ellislab.com so we can send you a copy immediately.

+ *

+ * @package		CodeIgniter

+ * @author		EllisLab Dev Team

+ * @copyright	Copyright (c) 2008 - 2013, EllisLab, Inc. (http://ellislab.com/)

+ * @license		http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)

+ * @link		http://codeigniter.com

+ * @since		Version 1.0

+ * @filesource

+ */

+defined('BASEPATH') OR exit('No direct script access allowed');

+

+/**

+ * Output Class

+ *

+ * Responsible for sending final output to the browser.

+ *

+ * @package		CodeIgniter

+ * @subpackage	Libraries

+ * @category	Output

+ * @author		EllisLab Dev Team

+ * @link		http://codeigniter.com/user_guide/libraries/output.html

+ */

+class CI_Output {

+

+	/**

+	 * Final output string

+	 *

+	 * @var	string

+	 */

+	public $final_output;

+

+	/**

+	 * Cache expiration time

+	 *

+	 * @var	int

+	 */

+	public $cache_expiration = 0;

+

+	/**

+	 * List of server headers

+	 *

+	 * @var	array

+	 */

+	public $headers =	array();

+

+	/**

+	 * List of mime types

+	 *

+	 * @var	array

+	 */

+	public $mimes =		array();

+

+	/**

+	 * Mime-type for the current page

+	 *

+	 * @var	string

+	 */

+	protected $mime_type	= 'text/html';

+

+	/**

+	 * Enable Profiler flag

+	 *

+	 * @var	bool

+	 */

+	public $enable_profiler = FALSE;

+

+	/**

+	 * zLib output compression flag

+	 *

+	 * @var	bool

+	 */

+	protected $_zlib_oc =		FALSE;

+

+	/**

+	 * List of profiler sections

+	 *

+	 * @var	array

+	 */

+	protected $_profiler_sections =	array();

+

+	/**

+	 * Parse markers flag

+	 *

+	 * Whether or not to parse variables like {elapsed_time} and {memory_usage}.

+	 *

+	 * @var	bool

+	 */

+	public $parse_exec_vars =	TRUE;

+

+	/**

+	 * Class constructor

+	 *

+	 * Determines whether zLib output compression will be used.

+	 *

+	 * @return	void

+	 */

+	public function __construct()

+	{

+		$this->_zlib_oc = (bool) @ini_get('zlib.output_compression');

+

+		// Get mime types for later

+		$this->mimes =& get_mimes();

+

+		log_message('debug', 'Output Class Initialized');

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Get Output

+	 *

+	 * Returns the current output string.

+	 *

+	 * @return	string

+	 */

+	public function get_output()

+	{

+		return $this->final_output;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Set Output

+	 *

+	 * Sets the output string.

+	 *

+	 * @param	string	$output	Output data

+	 * @return	CI_Output

+	 */

+	public function set_output($output)

+	{

+		$this->final_output = $output;

+		return $this;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Append Output

+	 *

+	 * Appends data onto the output string.

+	 *

+	 * @param	string	$output	Data to append

+	 * @return	CI_Output

+	 */

+	public function append_output($output)

+	{

+		if (empty($this->final_output))

+		{

+			$this->final_output = $output;

+		}

+		else

+		{

+			$this->final_output .= $output;

+		}

+

+		return $this;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Set Header

+	 *

+	 * Lets you set a server header which will be sent with the final output.

+	 *

+	 * Note: If a file is cached, headers will not be sent.

+	 * @todo	We need to figure out how to permit headers to be cached.

+	 *

+	 * @param	string	$header		Header

+	 * @param	bool	$replace	Whether to replace the old header value, if already set

+	 * @return	CI_Output

+	 */

+	public function set_header($header, $replace = TRUE)

+	{

+		// If zlib.output_compression is enabled it will compress the output,

+		// but it will not modify the content-length header to compensate for

+		// the reduction, causing the browser to hang waiting for more data.

+		// We'll just skip content-length in those cases.

+		if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)

+		{

+			return $this;

+		}

+

+		$this->headers[] = array($header, $replace);

+		return $this;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Set Content-Type Header

+	 *

+	 * @param	string	$mime_type	Extension of the file we're outputting

+	 * @param	string	$charset	Character set (default: NULL)

+	 * @return	CI_Output

+	 */

+	public function set_content_type($mime_type, $charset = NULL)

+	{

+		if (strpos($mime_type, '/') === FALSE)

+		{

+			$extension = ltrim($mime_type, '.');

+

+			// Is this extension supported?

+			if (isset($this->mimes[$extension]))

+			{

+				$mime_type =& $this->mimes[$extension];

+

+				if (is_array($mime_type))

+				{

+					$mime_type = current($mime_type);

+				}

+			}

+		}

+

+		$this->mime_type = $mime_type;

+

+		if (empty($charset))

+		{

+			$charset = config_item('charset');

+		}

+

+		$header = 'Content-Type: '.$mime_type

+			.(empty($charset) ? NULL : '; charset='.$charset);

+

+		$this->headers[] = array($header, TRUE);

+		return $this;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Get Current Content-Type Header

+	 *

+	 * @return	string	'text/html', if not already set

+	 */

+	public function get_content_type()

+	{

+		for ($i = 0, $c = count($this->headers); $i < $c; $i++)

+		{

+			if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1)

+			{

+				return $content_type;

+			}

+		}

+

+		return 'text/html';

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Get Header

+	 *

+	 * @param	string	$header_name

+	 * @return	string

+	 */

+	public function get_header($header)

+	{

+		// Combine headers already sent with our batched headers

+		$headers = array_merge(

+			// We only need [x][0] from our multi-dimensional array

+			array_map('array_shift', $this->headers),

+			headers_list()

+		);

+

+		if (empty($headers) OR empty($header))

+		{

+			return NULL;

+		}

+

+		for ($i = 0, $c = count($headers); $i < $c; $i++)

+		{

+			if (strncasecmp($header, $headers[$i], $l = strlen($header)) === 0)

+			{

+				return trim(substr($headers[$i], $l+1));

+			}

+		}

+

+		return NULL;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Set HTTP Status Header

+	 *

+	 * As of version 1.7.2, this is an alias for common function

+	 * set_status_header().

+	 *

+	 * @param	int	$code	Status code (default: 200)

+	 * @param	string	$text	Optional message

+	 * @return	CI_Output

+	 */

+	public function set_status_header($code = 200, $text = '')

+	{

+		set_status_header($code, $text);

+		return $this;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Enable/disable Profiler

+	 *

+	 * @param	bool	$val	TRUE to enable or FALSE to disable

+	 * @return	CI_Output

+	 */

+	public function enable_profiler($val = TRUE)

+	{

+		$this->enable_profiler = is_bool($val) ? $val : TRUE;

+		return $this;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Set Profiler Sections

+	 *

+	 * Allows override of default/config settings for

+	 * Profiler section display.

+	 *

+	 * @param	array	$sections	Profiler sections

+	 * @return	CI_Output

+	 */

+	public function set_profiler_sections($sections)

+	{

+		if (isset($sections['query_toggle_count']))

+		{

+			$this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];

+			unset($sections['query_toggle_count']);

+		}

+

+		foreach ($sections as $section => $enable)

+		{

+			$this->_profiler_sections[$section] = ($enable !== FALSE);

+		}

+

+		return $this;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Set Cache

+	 *

+	 * @param	int	$time	Cache expiration time in seconds

+	 * @return	CI_Output

+	 */

+	public function cache($time)

+	{

+		$this->cache_expiration = is_numeric($time) ? $time : 0;

+		return $this;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Display Output

+	 *

+	 * Processes sends the sends finalized output data to the browser along

+	 * with any server headers and profile data. It also stops benchmark

+	 * timers so the page rendering speed and memory usage can be shown.

+	 *

+	 * Note: All "view" data is automatically put into $this->final_output

+	 *	 by controller class.

+	 *

+	 * @uses	CI_Output::$final_output

+	 * @param	string	$output	Output data override

+	 * @return	void

+	 */

+	public function _display($output = '')

+	{

+		// Note:  We use globals because we can't use $CI =& get_instance()

+		// since this function is sometimes called by the caching mechanism,

+		// which happens before the CI super object is available.

+		global $BM, $CFG;

+

+		// Grab the super object if we can.

+		if (class_exists('CI_Controller'))

+		{

+			$CI =& get_instance();

+		}

+

+		// --------------------------------------------------------------------

+

+		// Set the output data

+		if ($output === '')

+		{

+			$output =& $this->final_output;

+		}

+

+		// --------------------------------------------------------------------

+

+		// Is minify requested?

+		if ($CFG->item('minify_output') === TRUE)

+		{

+			$output = $this->minify($output, $this->mime_type);

+		}

+

+		// --------------------------------------------------------------------

+

+		// Do we need to write a cache file? Only if the controller does not have its

+		// own _output() method and we are not dealing with a cache file, which we

+		// can determine by the existence of the $CI object above

+		if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))

+		{

+			$this->_write_cache($output);

+		}

+

+		// --------------------------------------------------------------------

+

+		// Parse out the elapsed time and memory usage,

+		// then swap the pseudo-variables with the data

+

+		$elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');

+

+		if ($this->parse_exec_vars === TRUE)

+		{

+			$memory	= round(memory_get_usage() / 1024 / 1024, 2).'MB';

+

+			$output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);

+		}

+

+		// --------------------------------------------------------------------

+

+		// Is compression requested?

+		if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE

+			&& extension_loaded('zlib')

+			&& isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)

+		{

+			ob_start('ob_gzhandler');

+		}

+

+		// --------------------------------------------------------------------

+

+		// Are there any server headers to send?

+		if (count($this->headers) > 0)

+		{

+			foreach ($this->headers as $header)

+			{

+				@header($header[0], $header[1]);

+			}

+		}

+

+		// --------------------------------------------------------------------

+

+		// Does the $CI object exist?

+		// If not we know we are dealing with a cache file so we'll

+		// simply echo out the data and exit.

+		if ( ! isset($CI))

+		{

+			echo $output;

+			log_message('debug', 'Final output sent to browser');

+			log_message('debug', 'Total execution time: '.$elapsed);

+			return;

+		}

+

+		// --------------------------------------------------------------------

+

+		// Do we need to generate profile data?

+		// If so, load the Profile class and run it.

+		if ($this->enable_profiler === TRUE)

+		{

+			$CI->load->library('profiler');

+			if ( ! empty($this->_profiler_sections))

+			{

+				$CI->profiler->set_sections($this->_profiler_sections);

+			}

+

+			// If the output data contains closing </body> and </html> tags

+			// we will remove them and add them back after we insert the profile data

+			$output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();

+			if ($count > 0)

+			{

+				$output .= '</body></html>';

+			}

+		}

+

+		// Does the controller contain a function named _output()?

+		// If so send the output there.  Otherwise, echo it.

+		if (method_exists($CI, '_output'))

+		{

+			$CI->_output($output);

+		}

+		else

+		{

+			echo $output; // Send it to the browser!

+		}

+

+		log_message('debug', 'Final output sent to browser');

+		log_message('debug', 'Total execution time: '.$elapsed);

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Write Cache

+	 *

+	 * @param	string	$output	Output data to cache

+	 * @return	void

+	 */

+	public function _write_cache($output)

+	{

+		$CI =& get_instance();

+		$path = $CI->config->item('cache_path');

+		$cache_path = ($path === '') ? APPPATH.'cache/' : $path;

+

+		if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))

+		{

+			log_message('error', 'Unable to write cache file: '.$cache_path);

+			return;

+		}

+

+		$uri =	$CI->config->item('base_url').

+				$CI->config->item('index_page').

+				$CI->uri->uri_string();

+

+		$cache_path .= md5($uri);

+

+		if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))

+		{

+			log_message('error', 'Unable to write cache file: '.$cache_path);

+			return;

+		}

+

+		$expire = time() + ($this->cache_expiration * 60);

+

+		if (flock($fp, LOCK_EX))

+		{

+			fwrite($fp, $expire.'TS--->'.$output);

+			flock($fp, LOCK_UN);

+		}

+		else

+		{

+			log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);

+			return;

+		}

+		fclose($fp);

+		@chmod($cache_path, FILE_WRITE_MODE);

+

+		log_message('debug', 'Cache file written: '.$cache_path);

+

+		// Send HTTP cache-control headers to browser to match file cache settings.

+		$this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Update/serve cached output

+	 *

+	 * @uses	CI_Config

+	 * @uses	CI_URI

+	 *

+	 * @param	object	&$CFG	CI_Config class instance

+	 * @param	object	&$URI	CI_URI class instance

+	 * @return	bool	TRUE on success or FALSE on failure

+	 */

+	public function _display_cache(&$CFG, &$URI)

+	{

+		$cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');

+

+		// Build the file path. The file name is an MD5 hash of the full URI

+		$uri =	$CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;

+		$filepath = $cache_path.md5($uri);

+

+		if ( ! @file_exists($filepath) OR ! $fp = @fopen($filepath, FOPEN_READ))

+		{

+			return FALSE;

+		}

+

+		flock($fp, LOCK_SH);

+

+		$cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';

+

+		flock($fp, LOCK_UN);

+		fclose($fp);

+

+		// Strip out the embedded timestamp

+		if ( ! preg_match('/^(\d+)TS--->/', $cache, $match))

+		{

+			return FALSE;

+		}

+

+		$last_modified = filemtime($cache_path);

+		$expire = $match[1];

+

+		// Has the file expired?

+		if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))

+		{

+			// If so we'll delete it.

+			@unlink($filepath);

+			log_message('debug', 'Cache file has expired. File deleted.');

+			return FALSE;

+		}

+		else

+		{

+			// Or else send the HTTP cache control headers.

+			$this->set_cache_header($last_modified, $expire);

+		}

+

+		// Display the cache

+		$this->_display(substr($cache, strlen($match[0])));

+		log_message('debug', 'Cache file is current. Sending it to browser.');

+		return TRUE;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Delete cache

+	 *

+	 * @param	string	$uri	URI string

+	 * @return	bool

+	 */

+	public function delete_cache($uri = '')

+	{

+		$CI =& get_instance();

+		$cache_path = $CI->config->item('cache_path');

+		if ($cache_path === '')

+		{

+			$cache_path = APPPATH.'cache/';

+		}

+

+		if ( ! is_dir($cache_path))

+		{

+			log_message('error', 'Unable to find cache path: '.$cache_path);

+			return FALSE;

+		}

+

+		if (empty($uri))

+		{

+			$uri = $CI->uri->uri_string();

+		}

+

+		$cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').$uri);

+

+		if ( ! @unlink($cache_path))

+		{

+			log_message('error', 'Unable to delete cache file for '.$uri);

+			return FALSE;

+		}

+

+		return TRUE;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Set Cache Header

+	 *

+	 * Set the HTTP headers to match the server-side file cache settings

+	 * in order to reduce bandwidth.

+	 *

+	 * @param	int	$last_modified	Timestamp of when the page was last modified

+	 * @param	int	$expiration	Timestamp of when should the requested page expire from cache

+	 * @return	void

+	 */

+	public function set_cache_header($last_modified, $expiration)

+	{

+		$max_age = $expiration - $_SERVER['REQUEST_TIME'];

+

+		if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']))

+		{

+			$this->set_status_header(304);

+			exit;

+		}

+		else

+		{

+			header('Pragma: public');

+			header('Cache-Control: max-age=' . $max_age . ', public');

+			header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');

+			header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');

+		}

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Minify

+	 *

+	 * Reduce excessive size of HTML/CSS/JavaScript content.

+	 *

+	 * @param	string	$output	Output to minify

+	 * @param	string	$type	Output content MIME type

+	 * @return	string	Minified output

+	 */

+	public function minify($output, $type = 'text/html')

+	{

+		switch ($type)

+		{

+			case 'text/html':

+

+				if (($size_before = strlen($output)) === 0)

+				{

+					return '';

+				}

+

+				// Find all the <pre>,<code>,<textarea>, and <javascript> tags

+				// We'll want to return them to this unprocessed state later.

+				preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);

+				preg_match_all('{<code.+</code>}msU', $output, $codes_clean);

+				preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);

+				preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);

+

+				// Minify the CSS in all the <style> tags.

+				preg_match_all('{<style.+</style>}msU', $output, $style_clean);

+				foreach ($style_clean[0] as $s)

+				{

+					$output = str_replace($s, $this->_minify_script_style($s, TRUE), $output);

+				}

+

+				// Minify the javascript in <script> tags.

+				foreach ($javascript_clean[0] as $s)

+				{

+					$javascript_mini[] = $this->_minify_script_style($s, TRUE);

+				}

+

+				// Replace multiple spaces with a single space.

+				$output = preg_replace('!\s{2,}!', ' ', $output);

+

+				// Remove comments (non-MSIE conditionals)

+				$output = preg_replace('{\s*<!--[^\[<>].*(?<!!)-->\s*}msU', '', $output);

+

+				// Remove spaces around block-level elements.

+				$output = preg_replace('/\s*(<\/?(html|head|title|meta|script|link|style|body|h[1-6]|div|p|br)[^>]*>)\s*/is', '$1', $output);

+

+				// Replace mangled <pre> etc. tags with unprocessed ones.

+

+				if ( ! empty($pres_clean))

+				{

+					preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);

+					$output = str_replace($pres_messed[0], $pres_clean[0], $output);

+				}

+

+				if ( ! empty($codes_clean))

+				{

+					preg_match_all('{<code.+</code>}msU', $output, $codes_messed);

+					$output = str_replace($codes_messed[0], $codes_clean[0], $output);

+				}

+

+				if ( ! empty($codes_clean))

+				{

+					preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);

+					$output = str_replace($textareas_messed[0], $textareas_clean[0], $output);

+				}

+

+				if (isset($javascript_mini))

+				{

+					preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);

+					$output = str_replace($javascript_messed[0], $javascript_mini, $output);

+				}

+

+				$size_removed = $size_before - strlen($output);

+				$savings_percent = round(($size_removed / $size_before * 100));

+

+				log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');

+

+			break;

+

+			case 'text/css':

+			case 'text/javascript':

+

+				$output = $this->_minify_script_style($output);

+

+			break;

+

+			default: break;

+		}

+

+		return $output;

+	}

+

+	// --------------------------------------------------------------------

+

+	/**

+	 * Minify Style and Script

+	 *

+	 * Reduce excessive size of CSS/JavaScript content.  To remove spaces this

+	 * script walks the string as an array and determines if the pointer is inside

+	 * a string created by single quotes or double quotes.  spaces inside those

+	 * strings are not stripped.  Opening and closing tags are severed from

+	 * the string initially and saved without stripping whitespace to preserve

+	 * the tags and any associated properties if tags are present

+	 *

+	 * Minification logic/workflow is similar to methods used by Douglas Crockford

+	 * in JSMIN. http://www.crockford.com/javascript/jsmin.html

+	 *

+	 * KNOWN ISSUE: ending a line with a closing parenthesis ')' and no semicolon

+	 * where there should be one will break the Javascript. New lines after a

+	 * closing parenthesis are not recognized by the script. For best results

+	 * be sure to terminate lines with a semicolon when appropriate.

+	 *

+	 * @param	string	$output		Output to minify

+	 * @param	bool	$has_tags	Specify if the output has style or script tags

+	 * @return	string	Minified output

+	 */

+	protected function _minify_script_style($output, $has_tags = FALSE)

+	{

+		// We only need this if there are tags in the file

+		if ($has_tags === TRUE)

+		{

+			// Remove opening tag and save for later

+			$pos = strpos($output, '>') + 1;

+			$open_tag = substr($output, 0, $pos);

+			$output = substr_replace($output, '', 0, $pos);

+

+			// Remove closing tag and save it for later

+			$end_pos = strlen($output);

+			$pos = strpos($output, '</');

+			$closing_tag = substr($output, $pos, $end_pos);

+			$output = substr_replace($output, '', $pos);

+		}

+

+		// Remove CSS comments

+		$output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!i', '', $output);

+

+		// Remove spaces around curly brackets, colons,

+		// semi-colons, parenthesis, commas

+		$output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!i', '$1', $output);

+

+		// Replace tabs with spaces

+		// Replace carriage returns & multiple new lines with single new line

+		// and trim any leading or trailing whitespace

+		$output = trim(preg_replace(array('/\t+/', '/\r/', '/\n+/'), array(' ', "\n", "\n"), $output));

+

+		// Remove spaces when safe to do so.

+		$in_string = $in_dstring = $prev = FALSE;

+		$array_output = str_split($output);

+		foreach ($array_output as $key => $value)

+		{

+			if ($in_string === FALSE && $in_dstring === FALSE)

+			{

+				if ($value === ' ')

+				{

+					// Get the next element in the array for comparisons

+					$next = $array_output[$key + 1];

+

+					// Strip spaces preceded/followed by a non-ASCII character

+					// or not preceded/followed by an alphanumeric

+					// or not preceded/followed \ $ and _

+					if ((preg_match('/^[\x20-\x7f]*$/D', $next) OR preg_match('/^[\x20-\x7f]*$/D', $prev))

+						&& ( ! ctype_alnum($next) OR ! ctype_alnum($prev))

+						&& ! in_array($next, array('\\', '_', '$'), TRUE)

+						&& ! in_array($prev, array('\\', '_', '$'), TRUE)

+					)

+					{

+						unset($array_output[$key]);

+					}

+				}

+				else

+				{

+					// Save this value as previous for the next iteration

+					// if it is not a blank space

+					$prev = $value;

+				}

+			}

+

+			if ($value === "'")

+			{

+				$in_string = ! $in_string;

+			}

+			elseif ($value === '"')

+			{

+				$in_dstring = ! $in_dstring;

+			}

+		}

+

+		// Put the string back together after spaces have been stripped

+		$output = implode($array_output);

+

+		// Remove new line characters unless previous or next character is

+		// printable or Non-ASCII

+		preg_match_all('/[\n]/', $output, $lf, PREG_OFFSET_CAPTURE);

+		$removed_lf = 0;

+		foreach ($lf as $feed_position)

+		{

+			foreach ($feed_position as $position)

+			{

+				$position = $position[1] - $removed_lf;

+				$next = $output[$position + 1];

+				$prev = $output[$position - 1];

+				if ( ! ctype_print($next) && ! ctype_print($prev)

+					&& ! preg_match('/^[\x20-\x7f]*$/D', $next)

+					&& ! preg_match('/^[\x20-\x7f]*$/D', $prev)

+				)

+				{

+					$output = substr_replace($output, '', $position, 1);

+					$removed_lf++;

+				}

+			}

+		}

+

+		// Put the opening and closing tags back if applicable

+		return isset($open_tag)

+			? $open_tag.$output.$closing_tag

+			: $output;

+	}

+

+}

+

+/* End of file Output.php */

 /* Location: ./system/core/Output.php */
\ No newline at end of file