Index: admin_templates/browser/browser_header.tpl
===================================================================
--- admin_templates/browser/browser_header.tpl	(revision 13400)
+++ admin_templates/browser/browser_header.tpl	(working copy)
@@ -15,12 +15,14 @@
 
 <link rel="icon" href="img/favicon.ico" type="image/x-icon" />
 <link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon" />
-<link rel="stylesheet" rev="stylesheet" href="browser/browser.css" type="text/css" />
+<link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='browser/browser.css'/>" type="text/css" />
 
-<script type="text/javascript" src="js/is.js"></script>
-<script type="text/javascript" src="js/ajax.js"></script>
-<script type="text/javascript" src="js/application.js"></script>
-<script type="text/javascript" src="js/script.js"></script>
+<script type="text/javascript" src="<inp2:m_Compress files='
+	js/is.js|
+	js/ajax.js|
+	js/application.js|
+	js/script.js
+'/>"></script>
 
 <script type="text/javascript">
 var t = '<inp2:m_get param="t"/>';
Index: admin_templates/catalog/advanced_view.tpl
===================================================================
--- admin_templates/catalog/advanced_view.tpl	(revision 13400)
+++ admin_templates/catalog/advanced_view.tpl	(working copy)
@@ -11,10 +11,13 @@
 	<tr>
 	  	<td>
 	  		<input type="hidden" name="m_cat_id" value="<inp2:m_get name="m_cat_id"/>"/>
-			<link rel="stylesheet" rev="stylesheet" href="incs/nlsmenu.css" type="text/css" />
-			<script type="text/javascript" src="js/nlsmenu.js"></script>
-			<script type="text/javascript" src="js/nlsmenueffect_1_2_1.js"></script>
-	  		<script type="text/javascript" src="js/catalog.js"></script>
+			<link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/nlsmenu.css'/>" type="text/css" />
+			<script type="text/javascript" src="<inp2:m_Compress files='
+				js/nlsmenu.js|
+				js/nlsmenueffect_1_2_1.js|
+				js/catalog.js
+			'/>"></script>
+
 	  		<script type="text/javascript">
 	  			<inp2:m_if check="adm_CheckPermCache">
 	  				$(document).ready(
Index: admin_templates/catalog/catalog.tpl
===================================================================
--- admin_templates/catalog/catalog.tpl	(revision 13400)
+++ admin_templates/catalog/catalog.tpl	(working copy)
@@ -12,11 +12,13 @@
 	<tr>
 	  	<td>
 	  		<input type="hidden" name="m_cat_id" value="<inp2:m_get name="m_cat_id"/>"/>
-			<link rel="stylesheet" rev="stylesheet" href="incs/nlsmenu.css" type="text/css" />
-			<script type="text/javascript" src="js/nlsmenu.js"></script>
-			<script type="text/javascript" src="js/nlsmenueffect_1_2_1.js"></script>
+			<link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/nlsmenu.css'/>" type="text/css" />
+			<script type="text/javascript" src="<inp2:m_Compress files='
+				js/nlsmenu.js|
+				js/nlsmenueffect_1_2_1.js|
+				js/catalog.js
+			'/>"></script>
 
-	  		<script type="text/javascript" src="js/catalog.js"></script>
 	  		<script type="text/javascript">
 	  			<inp2:m_if check="adm_CheckPermCache">
 	  				$(document).ready(
Index: admin_templates/catalog/item_selector/item_selector_toolbar.tpl
===================================================================
--- admin_templates/catalog/item_selector/item_selector_toolbar.tpl	(revision 13400)
+++ admin_templates/catalog/item_selector/item_selector_toolbar.tpl	(working copy)
@@ -1,8 +1,10 @@
-<script type="text/javascript" src="js/ajax.js"></script>
-<link rel="stylesheet" rev="stylesheet" href="incs/nlsmenu.css" type="text/css" />
-<script type="text/javascript" src="js/nlsmenu.js"></script>
-<script type="text/javascript" src="js/nlsmenueffect_1_2_1.js"></script>
-<script type="text/javascript" src="js/catalog.js"></script>
+<link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/nlsmenu.css'/>" type="text/css" />
+<script type="text/javascript" src="<inp2:m_Compress files='
+	js/nlsmenu.js|
+	js/nlsmenueffect_1_2_1.js|
+	js/catalog.js
+'/>"></script>
+
 <script type="text/javascript">
 	var menuMgr = new NlsMenuManager("mgr");
 	menuMgr.timeout = 500;
Index: admin_templates/incs/grid_blocks.tpl
===================================================================
--- admin_templates/incs/grid_blocks.tpl	(revision 13400)
+++ admin_templates/incs/grid_blocks.tpl	(working copy)
@@ -635,9 +635,8 @@
 
 	<inp2:m_if check="m_ParamEquals" name="ajax" value="0">
 		<inp2:m_if check="m_GetEquals" name="fw_menu_included" value="">
-			<link rel="stylesheet" rev="stylesheet" href="incs/nlsmenu.css" type="text/css" />
-			<script type="text/javascript" src="js/nlsmenu.js"></script>
-			<script type="text/javascript" src="js/nlsmenueffect_1_2_1.js"></script>
+			<link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/nlsmenu.css'/>" type="text/css" />
+			<script type="text/javascript" src="<inp2:m_Compress files='js/nlsmenu.js|js/nlsmenueffect_1_2_1.js'/>"></script>
 
 			<script type="text/javascript">
 				var menuMgr = new NlsMenuManager("mgr");
@@ -841,12 +840,9 @@
 
 	<inp2:m_if check="m_ParamEquals" name="ajax" value="0">
 		<inp2:m_if check="m_GetEquals" name="fw_menu_included" value="">
-			<script type="text/javascript" src="incs/fw_menu.js"></script>
+			<link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/nlsmenu.css'/>" type="text/css" />
+			<script type="text/javascript" src="<inp2:m_Compress files='js/nlsmenu.js|js/nlsmenueffect_1_2_1.js'/>"></script>
 
-			<link rel="stylesheet" rev="stylesheet" href="incs/nlsmenu.css" type="text/css" />
-			<script type="text/javascript" src="js/nlsmenu.js"></script>
-			<script type="text/javascript" src="js/nlsmenueffect_1_2_1.js"></script>
-
 			<script type="text/javascript">
 				var menuMgr = new NlsMenuManager("mgr");
 				menuMgr.timeout = 500;
Index: admin_templates/incs/header.tpl
===================================================================
--- admin_templates/incs/header.tpl	(revision 13400)
+++ admin_templates/incs/header.tpl	(working copy)
@@ -18,34 +18,38 @@
 <link rel="icon" href="img/favicon.ico" type="image/x-icon" />
 <link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon" />
 
-<inp2:adm_AdminSkin/>
+<inp2:adm_AdminSkin file_only="1" result_to_var="skin_css"/>
 
-<link rel="stylesheet" href="js/jquery/thickbox/thickbox.css" type="text/css" media="screen" />
+<link rel="stylesheet" href="<inp2:m_Compress files='
+	js/jquery/thickbox/thickbox.css|
+	js/calendar/calendar-blue.css|
+	$skin_css
+'/>" type="text/css" media="screen" />
 
 <script type="text/javascript" src="js/jquery/jquery.pack.js"></script>
 <script type="text/javascript" src="js/jquery/jquery-ui.custom.min.js"></script>
 
-<script type="text/javascript" src="js/is.js"></script>
-<script type="text/javascript" src="js/ajax.js"></script>
-<script type="text/javascript" src="js/application.js"></script>
-<script type="text/javascript" src="js/script.js"></script>
-<script type="text/javascript" src="js/in-portal.js"></script>
-<script type="text/javascript" src="js/toolbar.js"></script>
-<script type="text/javascript" src="js/grid.js"></script>
-<script type="text/javascript" src="js/simple_grid.js"></script>
-<script type="text/javascript" src="js/grid_scroller.js"></script>
-<script type="text/javascript" src="js/forms.js"></script>
-<script type="text/javascript" src="js/drag.js"></script>
-<script type="text/javascript" src="js/ajax_dropdown.js"></script>
-<script type="text/javascript" src="js/form_controls.js"></script>
+<script type="text/javascript" src="<inp2:m_Compress files='
+	js/is.js|
+	js/ajax.js|
+	js/application.js|
+	js/script.js|
+	js/in-portal.js|
+	js/toolbar.js|
+	js/grid.js|
+	js/simple_grid.js|
+	js/grid_scroller.js|
+	js/forms.js|
+	js/drag.js|
+	js/ajax_dropdown.js|
+	js/form_controls.js|
+	js/jquery/thickbox/thickbox.js|
+	js/tab_scroller.js|
+	js/calendar/calendar.js|
+	js/calendar/calendar-setup.js|
+	js/calendar/calendar-en.js
+'/>"></script>
 
-<script type="text/javascript" src="js/jquery/thickbox/thickbox.js"></script>
-<script type="text/javascript" src="js/tab_scroller.js"></script>
-
-<link rel="stylesheet" rev="stylesheet" href="js/calendar/calendar-blue.css" type="text/css" />
-<script type="text/javascript" src="js/calendar/calendar.js"></script>
-<script type="text/javascript" src="js/calendar/calendar-setup.js"></script>
-<script type="text/javascript" src="js/calendar/calendar-en.js"></script>
 <script type="text/javascript">
 TB.pathToImage = 'js/jquery/thickbox/loadingAnimation.gif';
 var t = '<inp2:m_get param="t"/>';
Index: admin_templates/reviews/reviews.tpl
===================================================================
--- admin_templates/reviews/reviews.tpl	(revision 13400)
+++ admin_templates/reviews/reviews.tpl	(working copy)
@@ -11,11 +11,13 @@
 	<tr>
 	  	<td>
 	  		<input type="hidden" name="m_cat_id" value="<inp2:m_get name="m_cat_id"/>"/>
-			<link rel="stylesheet" rev="stylesheet" href="incs/nlsmenu.css" type="text/css" />
-			<script type="text/javascript" src="js/nlsmenu.js"></script>
-			<script type="text/javascript" src="js/nlsmenueffect_1_2_1.js"></script>
-	  		<script type="text/javascript" src="js/ajax.js"></script>
-	  		<script type="text/javascript" src="js/catalog.js"></script>
+			<link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/nlsmenu.css'/>" type="text/css" />
+			<script type="text/javascript" src="<inp2:m_Compress files='
+				js/nlsmenu.js|
+				js/nlsmenueffect_1_2_1.js|
+				js/catalog.js'
+			/>"></script>
+
 	  		<script type="text/javascript">
 	  			var menuMgr = new NlsMenuManager("mgr");
 					menuMgr.timeout = 500;
Index: admin_templates/sections_list.tpl
===================================================================
--- admin_templates/sections_list.tpl	(revision 13400)
+++ admin_templates/sections_list.tpl	(working copy)
@@ -1,6 +1,6 @@
 <inp2:m_include t="incs/header"/>
 
-<link rel="stylesheet" rev="stylesheet" href="incs/sections_list.css" type="text/css" />
+<link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/sections_list.css'/>" type="text/css" />
 
 <inp2:m_DefineElement name="section_list_header" icon_module="">
 	<inp2:m_RenderElement name="combined_header" prefix="$SectionPrefix" section="$section_name" title_preset="tree_#section#" parent="0"/>
Index: admin_templates/users/user_edit_items.tpl
===================================================================
--- admin_templates/users/user_edit_items.tpl	(revision 13400)
+++ admin_templates/users/user_edit_items.tpl	(working copy)
@@ -9,13 +9,14 @@
 <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0">
 <tbody>
 	<tr>
-  	<td>
-  		<link rel="stylesheet" rev="stylesheet" href="incs/nlsmenu.css" type="text/css" />
-			<script type="text/javascript" src="js/nlsmenu.js"></script>
-			<script type="text/javascript" src="js/nlsmenueffect_1_2_1.js"></script>
+  		<td>
+  			<link rel="stylesheet" rev="stylesheet" href="<inp2:m_Compress files='incs/nlsmenu.css'/>" type="text/css" />
+			<script type="text/javascript" src="<inp2:m_Compress files='
+				js/nlsmenu.js|
+				js/nlsmenueffect_1_2_1.js|
+				js/catalog.js
+			'/>"></script>
 
-	  		<script type="text/javascript" src="js/ajax.js"></script>
-	  		<script type="text/javascript" src="js/catalog.js"></script>
 	  		<script type="text/javascript">
   				var menuMgr = new NlsMenuManager("mgr");
 					menuMgr.timeout = 500;
Index: kernel/nparser/nparser.php
===================================================================
--- kernel/nparser/nparser.php	(revision 13400)
+++ kernel/nparser/nparser.php	(working copy)
@@ -1102,4 +1102,26 @@
 
 		echo $ret;
 	}
+
+	/**
+	 * Performs compression of given files or text
+	 *
+	 * @param mixed $data
+	 * @param bool $raw_script
+	 * @param string $file_extension
+	 * @return string
+	 */
+	function CompressScript($data, $raw_script = false, $file_extension = '')
+	{
+		$minify_helper =& $this->Application->recallObject('MinifyHelper');
+		/* @var $minify_helper MinifyHelper */
+
+		if ($raw_script) {
+			$minify_helper->compressString($data, $file_extension);
+
+			return $data;
+		}
+
+		return $minify_helper->CompressScriptTag($data);
+	}
 }
\ No newline at end of file
Index: kernel/nparser/ntags.php
===================================================================
--- kernel/nparser/ntags.php	(revision 13400)
+++ kernel/nparser/ntags.php	(working copy)
@@ -617,4 +617,73 @@
 		return $o;
 	}
 
+}
+
+class _Tag_Compress extends _BlockTag {
+
+	function _Tag_Compress($tag)
+	{
+		parent::_BlockTag($tag);
+
+		if ($tag['is_closing']) {
+			if (!array_key_exists('files', $tag['NP'])) {
+				$this->_requiredParams = Array ('from');
+			}
+			else {
+				$this->_requiredParams = Array ('files');
+			}
+		}
+		else {
+			$this->_requiredParams = Array ('type'); // js/css
+		}
+	}
+
+	/**
+	 * When used as non-block tag, then compress given files and return url to result
+	 *
+	 * @param Array $tag
+	 * @return string
+	 */
+	function Open($tag)
+	{
+		$o = parent::Open($tag);
+
+		if ($o === false) {
+			// some required params not passed
+			return $o;
+		}
+
+		if ($tag['is_closing']) {
+			$to_pass = $this->Parser->CompileParamsArray($tag['NP']);
+			$this->AppendCode($o, "echo \$_parser->CompressScript($to_pass, false);");
+		}
+		else {
+			$this->AppendCode($o, "ob_start();");
+		}
+
+		return $o;
+	}
+
+	/**
+	 * When used as block tag, then compress contents between tags
+	 *
+	 * @param Array $tag
+	 * @return string
+	 */
+	function Close($tag)
+	{
+		if ($this->Tag['is_closing']) {
+			return parent::Close($tag);
+		}
+
+		$o = parent::Close($tag);
+
+		$code = Array ();
+		$code[] = '$res = ob_get_clean();';
+		$code[] = 'echo $_parser->CompressScript($res, true, \'' . $this->Tag['NP']['type'] . '\');';
+
+		$this->AppendCode($o, $code);
+
+		return $o;
+	}
 }
\ No newline at end of file
Index: units/categories/categories_tag_processor.php
===================================================================
--- units/categories/categories_tag_processor.php	(revision 13461)
+++ units/categories/categories_tag_processor.php	(working copy)
@@ -1337,25 +1337,41 @@
 		}
 
 		$this->Application->SetVar('admin_scripts_included', 1);
-
 		$js_url = $this->Application->BaseURL() . 'core/admin_templates/js';
 
-		$ret = '<link rel="stylesheet" href="' . $js_url . '/jquery/thickbox/thickbox.css" type="text/css" media="screen"/>' . "\n";
-		$ret .= '<link rel="stylesheet" href="' . $js_url . '/../incs/cms.css" type="text/css" media="screen"/>' . "\n";
+		$minify_helper =& $this->Application->recallObject('MinifyHelper');
+		/* @var $minify_helper MinifyHelper */
 
+		$to_compress = Array (
+			$js_url . '/jquery/thickbox/thickbox.css',
+			$js_url . '/../incs/cms.css',
+		);
+
+		$css_compressed = $minify_helper->CompressScriptTag( Array ('files' => implode('|', $to_compress)) );
+
+		$ret = '<link rel="stylesheet" href="' . $css_compressed . '" type="text/css" media="screen"/>' . "\n";
+
 		if (EDITING_MODE == EDITING_MODE_DESIGN) {
 			$ret .= '	<style type="text/css" media="all">
 							div.movable-element .movable-header { cursor: move; }
 						</style>';
 		}
 
+
 		$ret .= '<script type="text/javascript" src="' . $js_url . '/jquery/jquery.pack.js"></script>' . "\n";
 		$ret .= '<script type="text/javascript" src="' . $js_url . '/jquery/jquery-ui.custom.min.js"></script>' . "\n";
-		$ret .= '<script type="text/javascript" src="' . $js_url . '/is.js"></script>' . "\n";
-		$ret .= '<script type="text/javascript" src="' . $js_url . '/application.js"></script>' . "\n";
-		$ret .= '<script type="text/javascript" src="' . $js_url . '/script.js"></script>' . "\n";
-		$ret .= '<script type="text/javascript" src="' . $js_url . '/jquery/thickbox/thickbox.js"></script>' . "\n";
-		$ret .= '<script type="text/javascript" src="' . $js_url . '/template_manager.js"></script>' . "\n";
+
+		$to_compress = Array (
+			$js_url . '/is.js',
+			$js_url . '/application.js',
+			$js_url . '/script.js',
+			$js_url . '/jquery/thickbox/thickbox.js',
+			$js_url . '/template_manager.js',
+		);
+
+		$js_compressed = $minify_helper->CompressScriptTag( Array ('files' => implode('|', $to_compress)) );
+
+		$ret .= '<script type="text/javascript" src="' . $js_compressed . '"></script>' . "\n";
 		$ret .= '<script language="javascript">' . "\n";
 		$ret .= "TB.pathToImage = '" . $js_url . "/jquery/thickbox/loadingAnimation.gif';" . "\n";
 
Index: units/helpers/minifiers/css_minify_helper.php
===================================================================
--- units/helpers/minifiers/css_minify_helper.php	(revision 0)
+++ units/helpers/minifiers/css_minify_helper.php	(revision 0)
@@ -0,0 +1,218 @@
+<?php
+/**
+ * Class Minify_CSS_Compressor
+ * @package Minify
+ */
+
+/**
+ * Compress CSS
+ *
+ * This is a heavy regex-based removal of whitespace, unnecessary
+ * comments and tokens, and some CSS value minimization, where practical.
+ * Many steps have been taken to avoid breaking comment-based hacks,
+ * including the ie5/mac filter (and its inversion), but expect tricky
+ * hacks involving comment tokens in 'content' value strings to break
+ * minimization badly. A test suite is available.
+ *
+ * @package Minify
+ * @author Stephen Clay <steve@mrclay.org>
+ * @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
+ */
+class CssMinifyHelper extends kHelper {
+
+    /**
+     * @var bool Are we "in" a hack?
+     *
+     * I.e. are some browsers targetted until the next comment?
+     */
+    var $_inHack = false;
+
+    /**
+     * Minify a CSS string
+     *
+     * @param string $css
+     *
+     * @return string
+     */
+    function minify($css)
+    {
+        $css = str_replace("\r\n", "\n", $css);
+
+        // preserve empty comment after '>'
+        // http://www.webdevout.net/css-hacks#in_css-selectors
+        $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
+
+        // preserve empty comment between property and value
+        // http://css-discuss.incutio.com/?page=BoxModelHack
+        $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
+        $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
+
+        // apply callback to all valid comments (and strip out surrounding ws
+        $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
+            ,array($this, '_commentCB'), $css);
+
+        // remove ws around { } and last semicolon in declaration block
+        $css = preg_replace('/\\s*{\\s*/', '{', $css);
+        $css = preg_replace('/;?\\s*}\\s*/', '}', $css);
+
+        // remove ws surrounding semicolons
+        $css = preg_replace('/\\s*;\\s*/', ';', $css);
+
+        // remove ws around urls
+        $css = preg_replace('/
+                url\\(      # url(
+                \\s*
+                ([^\\)]+?)  # 1 = the URL (really just a bunch of non right parenthesis)
+                \\s*
+                \\)         # )
+            /x', 'url($1)', $css);
+
+        // remove ws between rules and colons
+        $css = preg_replace('/
+                \\s*
+                ([{;])              # 1 = beginning of block or rule separator
+                \\s*
+                ([\\*_]?[\\w\\-]+)  # 2 = property (and maybe IE filter)
+                \\s*
+                :
+                \\s*
+                (\\b|[#\'"])        # 3 = first character of a value
+            /x', '$1$2:$3', $css);
+
+        // remove ws in selectors
+        $css = preg_replace_callback('/
+                (?:              # non-capture
+                    \\s*
+                    [^~>+,\\s]+  # selector part
+                    \\s*
+                    [,>+~]       # combinators
+                )+
+                \\s*
+                [^~>+,\\s]+      # selector part
+                {                # open declaration block
+            /x'
+            ,array($this, '_selectorsCB'), $css);
+
+        // minimize hex colors
+        $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
+            , '$1#$2$3$4$5', $css);
+
+        // remove spaces between font families
+        $css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
+            ,array($this, '_fontFamilyCB'), $css);
+
+        $css = preg_replace('/@import\\s+url/', '@import url', $css);
+
+        // replace any ws involving newlines with a single newline
+        $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
+
+        // separate common descendent selectors w/ newlines (to limit line lengths)
+        $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
+
+        // Use newline after 1st numeric value (to limit line lengths).
+        $css = preg_replace('/
+            ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
+            \\s+
+            /x'
+            ,"$1\n", $css);
+
+        // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
+        $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
+
+        return trim($css);
+    }
+
+    /**
+     * Replace what looks like a set of selectors
+     *
+     * @param array $m regex matches
+     *
+     * @return string
+     */
+    function _selectorsCB($m)
+    {
+        // remove ws around the combinators
+        return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
+    }
+
+    /**
+     * Process a comment and return a replacement
+     *
+     * @param array $m regex matches
+     *
+     * @return string
+     */
+    function _commentCB($m)
+    {
+        $hasSurroundingWs = (trim($m[0]) !== $m[1]);
+        $m = $m[1];
+        // $m is the comment content w/o the surrounding tokens,
+        // but the return value will replace the entire comment.
+        if ($m === 'keep') {
+            return '/**/';
+        }
+        if ($m === '" "') {
+            // component of http://tantek.com/CSS/Examples/midpass.html
+            return '/*" "*/';
+        }
+        if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
+            // component of http://tantek.com/CSS/Examples/midpass.html
+            return '/*";}}/* */';
+        }
+        if ($this->_inHack) {
+            // inversion: feeding only to one browser
+            if (preg_match('@
+                    ^/               # comment started like /*/
+                    \\s*
+                    (\\S[\\s\\S]+?)  # has at least some non-ws content
+                    \\s*
+                    /\\*             # ends like /*/ or /**/
+                @x', $m, $n)) {
+                // end hack mode after this comment, but preserve the hack and comment content
+                $this->_inHack = false;
+                return "/*/{$n[1]}/**/";
+            }
+        }
+        if (substr($m, -1) === '\\') { // comment ends like \*/
+            // begin hack mode and preserve hack
+            $this->_inHack = true;
+            return '/*\\*/';
+        }
+        if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
+            // begin hack mode and preserve hack
+            $this->_inHack = true;
+            return '/*/*/';
+        }
+        if ($this->_inHack) {
+            // a regular comment ends hack mode but should be preserved
+            $this->_inHack = false;
+            return '/**/';
+        }
+        // Issue 107: if there's any surrounding whitespace, it may be important, so
+        // replace the comment with a single space
+        return $hasSurroundingWs // remove all other comments
+            ? ' '
+            : '';
+    }
+
+    /**
+     * Process a font-family listing and return a replacement
+     *
+     * @param array $m regex matches
+     *
+     * @return string
+     */
+    function _fontFamilyCB($m)
+    {
+        $m[1] = preg_replace('/
+                \\s*
+                (
+                    "[^"]+"      # 1 = family in double qutoes
+                    |\'[^\']+\'  # or 1 = family in single quotes
+                    |[\\w\\-]+   # or 1 = unquoted family
+                )
+                \\s*
+            /x', '$1', $m[1]);
+        return 'font-family:' . $m[1] . $m[2];
+    }
+}
Index: units/helpers/minifiers/js_minify_helper.php
===================================================================
--- units/helpers/minifiers/js_minify_helper.php	(revision 0)
+++ units/helpers/minifiers/js_minify_helper.php	(revision 0)
@@ -0,0 +1,296 @@
+<?php
+/**
+ * jsmin.php - PHP implementation of Douglas Crockford's JSMin.
+ *
+ * This is a direct port of jsmin.c to PHP with a few PHP performance tweaks and
+ * modifications to preserve some comments (see below). Also, rather than using
+ * stdin/stdout, JSMin::minify() accepts a string as input and returns another
+ * string as output.
+ *
+ * Comments containing IE conditional compilation are preserved, as are multi-line
+ * comments that begin with "/*!" (for documentation purposes). In the latter case
+ * newlines are inserted around the comment to enhance readability.
+ *
+ * PHP 5 or higher is required.
+ *
+ * Permission is hereby granted to use this version of the library under the
+ * same terms as jsmin.c, which has the following license:
+ *
+ * --
+ * Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * The Software shall be used for Good, not Evil.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * --
+ *
+ * @package JSMin
+ * @author Ryan Grove <ryan@wonko.com> (PHP port)
+ * @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
+ * @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
+ * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
+ * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
+ * @license http://opensource.org/licenses/mit-license.php MIT License
+ * @link http://code.google.com/p/jsmin-php/
+ */
+
+define('JSM_ORD_LF', 10);
+define('JSM_ORD_SPACE', 32);
+define('JSM_ACTION_KEEP_A', 1);
+define('JSM_ACTION_DELETE_A', 2);
+define('JSM_ACTION_DELETE_A_B', 3);
+
+class JsMinifyHelper extends kHelper {
+    /*const ORD_LF            = 10;
+    const ORD_SPACE         = 32;
+    const ACTION_KEEP_A     = 1;
+    const ACTION_DELETE_A   = 2;
+    const ACTION_DELETE_A_B = 3;*/
+
+    /* protected */ var $a           = "\n";
+    /* protected */ var $b           = '';
+    /* protected */ var $input       = '';
+    /* protected */ var $inputIndex  = 0;
+    /* protected */ var $inputLength = 0;
+    /* protected */ var $lookAhead   = null;
+    /* protected */ var $output      = '';
+
+    /**
+     * Perform minification, return result
+     */
+    /* public*/ function minify($input)
+    {
+		$this->input       = str_replace("\r\n", "\n", $input);
+        $this->inputLength = strlen($this->input);
+
+        if ($this->output !== '') { // min already run
+            return $this->output;
+        }
+        $this->action(JSM_ACTION_DELETE_A_B/*self::ACTION_DELETE_A_B*/);
+
+        while ($this->a !== null) {
+            // determine next command
+            $command = JSM_ACTION_KEEP_A/*self::ACTION_KEEP_A*/; // default
+            if ($this->a === ' ') {
+                if (! $this->isAlphaNum($this->b)) {
+                    $command = JSM_ACTION_DELETE_A/*self::ACTION_DELETE_A*/;
+                }
+            } elseif ($this->a === "\n") {
+                if ($this->b === ' ') {
+                    $command = JSM_ACTION_DELETE_A_B/*self::ACTION_DELETE_A_B*/;
+                } elseif (false === strpos('{[(+-', $this->b)
+                          && ! $this->isAlphaNum($this->b)) {
+                    $command = JSM_ACTION_DELETE_A/*self::ACTION_DELETE_A*/;
+                }
+            } elseif (! $this->isAlphaNum($this->a)) {
+                if ($this->b === ' '
+                    || ($this->b === "\n"
+                        && (false === strpos('}])+-"\'', $this->a)))) {
+                    $command = JSM_ACTION_DELETE_A_B/*self::ACTION_DELETE_A_B*/;
+                }
+            }
+            $this->action($command);
+        }
+        $this->output = trim($this->output);
+        return $this->output;
+    }
+
+    /**
+     * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
+     * ACTION_DELETE_A = Copy B to A. Get the next B.
+     * ACTION_DELETE_A_B = Get the next B.
+     */
+    /* protected */ function action($command)
+    {
+        switch ($command) {
+            case JSM_ACTION_KEEP_A/*self::ACTION_KEEP_A*/:
+                $this->output .= $this->a;
+                // fallthrough
+            case JSM_ACTION_DELETE_A/*self::ACTION_DELETE_A*/:
+                $this->a = $this->b;
+                if ($this->a === "'" || $this->a === '"') { // string literal
+                    $str = $this->a; // in case needed for exception
+                    while (true) {
+                        $this->output .= $this->a;
+                        $this->a       = $this->get();
+                        if ($this->a === $this->b) { // end quote
+                            break;
+                        }
+                        if (ord($this->a) <= JSM_ORD_LF/*self::ORD_LF*/) {
+                        	trigger_error('Unterminated String: ' . var_export($str, true), E_USER_ERROR);
+                        }
+                        $str .= $this->a;
+                        if ($this->a === '\\') {
+                            $this->output .= $this->a;
+                            $this->a       = $this->get();
+                            $str .= $this->a;
+                        }
+                    }
+                }
+                // fallthrough
+            case JSM_ACTION_DELETE_A_B/*self::ACTION_DELETE_A_B*/:
+                $this->b = $this->next();
+                if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal
+                    $this->output .= $this->a . $this->b;
+                    $pattern = '/'; // in case needed for exception
+                    while (true) {
+                        $this->a = $this->get();
+                        $pattern .= $this->a;
+                        if ($this->a === '/') { // end pattern
+                            break; // while (true)
+                        } elseif ($this->a === '\\') {
+                            $this->output .= $this->a;
+                            $this->a       = $this->get();
+                            $pattern      .= $this->a;
+                        } elseif (ord($this->a) <= JSM_ORD_LF/*self::ORD_LF*/) {
+                            trigger_error('Unterminated RegExp: '. var_export($pattern, true), E_USER_ERROR);
+                        }
+                        $this->output .= $this->a;
+                    }
+                    $this->b = $this->next();
+                }
+            // end case ACTION_DELETE_A_B
+        }
+    }
+
+    /* protected */ function isRegexpLiteral()
+    {
+        if (false !== strpos("\n{;(,=:[!&|?", $this->a)) { // we aren't dividing
+            return true;
+        }
+        if (' ' === $this->a) {
+            $length = strlen($this->output);
+            if ($length < 2) { // weird edge case
+                return true;
+            }
+            // you can't divide a keyword
+            if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
+                if ($this->output === $m[0]) { // odd but could happen
+                    return true;
+                }
+                // make sure it's a keyword, not end of an identifier
+                $charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
+                if (! $this->isAlphaNum($charBeforeKeyword)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get next char. Convert ctrl char to space.
+     */
+    /* protected */ function get()
+    {
+        $c = $this->lookAhead;
+        $this->lookAhead = null;
+        if ($c === null) {
+            if ($this->inputIndex < $this->inputLength) {
+                $c = $this->input[$this->inputIndex];
+                $this->inputIndex += 1;
+            } else {
+                return null;
+            }
+        }
+        if ($c === "\r" || $c === "\n") {
+            return "\n";
+        }
+        if (ord($c) < JSM_ORD_SPACE/*self::ORD_SPACE*/) { // control char
+            return ' ';
+        }
+        return $c;
+    }
+
+    /**
+     * Get next char. If is ctrl character, translate to a space or newline.
+     */
+    /* protected */ function peek()
+    {
+        $this->lookAhead = $this->get();
+        return $this->lookAhead;
+    }
+
+    /**
+     * Is $c a letter, digit, underscore, dollar sign, escape, or non-ASCII?
+     */
+    /* protected */ function isAlphaNum($c)
+    {
+        return (preg_match('/^[0-9a-zA-Z_\\$\\\\]$/', $c) || ord($c) > 126);
+    }
+
+    /* protected */ function singleLineComment()
+    {
+        $comment = '';
+        while (true) {
+            $get = $this->get();
+            $comment .= $get;
+            if (ord($get) <= JSM_ORD_LF/*self::ORD_LF*/) { // EOL reached
+                // if IE conditional comment
+                if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
+                    return "/{$comment}";
+                }
+                return $get;
+            }
+        }
+    }
+
+    /* protected */ function multipleLineComment()
+    {
+        $this->get();
+        $comment = '';
+        while (true) {
+            $get = $this->get();
+            if ($get === '*') {
+                if ($this->peek() === '/') { // end of comment reached
+                    $this->get();
+                    // if comment preserved by YUI Compressor
+                    if (0 === strpos($comment, '!')) {
+                        return "\n/*" . substr($comment, 1) . "*/\n";
+                    }
+                    // if IE conditional comment
+                    if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
+                        return "/*{$comment}*/";
+                    }
+                    return ' ';
+                }
+            } elseif ($get === null) {
+                trigger_error('Unterminated Comment: ' . var_export('/*' . $comment, true), E_USER_ERROR);
+            }
+            $comment .= $get;
+        }
+    }
+
+    /**
+     * Get the next character, skipping over comments.
+     * Some comments may be preserved.
+     */
+    /* protected */ function next()
+    {
+        $get = $this->get();
+        if ($get !== '/') {
+            return $get;
+        }
+        switch ($this->peek()) {
+            case '/': return $this->singleLineComment();
+            case '*': return $this->multipleLineComment();
+            default: return $get;
+        }
+    }
+}
\ No newline at end of file
Index: units/helpers/minifiers/minifiers_config.php
===================================================================
--- units/helpers/minifiers/minifiers_config.php	(revision 0)
+++ units/helpers/minifiers/minifiers_config.php	(revision 0)
@@ -0,0 +1,26 @@
+<?php
+/**
+* @version	$Id: controls_config.php 13086 2010-01-12 19:47:40Z alex $
+* @package	In-Portal
+* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
+* @license      GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+	$config = Array (
+		'Prefix' => 'minify-helpers',
+		'EventHandlerClass' => Array ('class' => 'kEventHandler', 'file' => '', 'build_event' => 'OnBuild'),
+
+		'RegisterClasses' => Array (
+			Array ('pseudo' => 'CssMinifyHelper', 'class' => 'CssMinifyHelper', 'file' => 'css_minify_helper.php', 'build_event' => '', 'require_classes' => 'kHelper'),
+			Array ('pseudo' => 'JsMinifyHelper', 'class' => 'JsMinifyHelper', 'file' => 'js_minify_helper.php', 'build_event' => '', 'require_classes' => Array ('kHelper')),
+			Array ('pseudo' => 'MinifyHelper', 'class' => 'MinifyHelper', 'file' => 'minify_helper.php', 'build_event' => '', 'require_classes' => Array ('kHelper')),
+		),
+	);
\ No newline at end of file
Index: units/helpers/minifiers/minify_helper.php
===================================================================
--- units/helpers/minifiers/minify_helper.php	(revision 0)
+++ units/helpers/minifiers/minify_helper.php	(revision 0)
@@ -0,0 +1,231 @@
+<?php
+
+	class MinifyHelper extends kHelper {
+
+		/**
+		 * Debug mode mark
+		 *
+		 * @var bool
+		 */
+		var $debugMode = false;
+
+		/**
+		 * Path to compress information file
+		 *
+		 * @var string
+		 */
+		var $infoFile = '';
+
+		/**
+		 * Compress information
+		 *
+		 * @var Array
+		 */
+		var $compressInfo = Array ();
+
+		function MinifyHelper()
+		{
+			parent::kHelper();
+
+			$this->debugMode = $this->Application->isDebugMode();
+			$this->infoFile = WRITEABLE . '/cache/compress_info.txt';
+
+			if ( file_exists($this->infoFile) ) {
+				$this->compressInfo = unserialize( file_get_contents($this->infoFile) );
+			}
+		}
+
+		/**
+		 * When used as non-block tag, then compress given files and return url to result
+		 *
+		 * @param Array $files
+		 * @return string
+		 */
+		function CompressScriptTag($params)
+		{
+			// put to queue
+			if (array_key_exists('to', $params)) {
+				$files = $this->Application->GetVar($params['to'], '');
+				$this->Application->SetVar($params['to'], $files . '|' . $params['files']);
+
+				return '';
+			}
+
+			if (array_key_exists('from', $params)) {
+				// get from queue
+				$files = $this->Application->GetVar($params['from']);
+			}
+			else {
+				// get from tag
+				$files = $params['files'];
+			}
+
+			$files = $this->_getTemplatePaths( array_map('trim', explode('|', $files)) );
+			$extension = pathinfo($files[0], PATHINFO_EXTENSION);
+
+			$hash = $this->_getHash($files);
+			$file_mask = 'cache/' . ($this->debugMode ? 'd' : 'c') . '_' . $hash . '_%s.' . $extension;
+
+			$was_compressed = array_key_exists($hash, $this->compressInfo);
+
+			if ($was_compressed) {
+				$current_file = WRITEABLE . '/' . sprintf($file_mask, $this->compressInfo[$hash]);
+
+				if (!file_exists($current_file)) {
+					// when info exists, but file doesn't -> re-compress
+					$was_compressed = false;
+				}
+
+				if ($this->debugMode) {
+					// check if any of listed files was changed since compressed file was created (only, when in debug mode)
+					foreach ($files as $file) {
+						if (filemtime($file) > $this->compressInfo[$hash]) {
+							$was_compressed = false;
+							break;
+						}
+					}
+				}
+			}
+
+			if (!$was_compressed) {
+				$string = '';
+				$path_length = strlen(FULL_PATH) + 1;
+
+				foreach ($files as $file) {
+					// add filename before for easier debugging
+					if ($this->debugMode) {
+						$string .= '/* === File: ' . substr($file, $path_length) . ' === */' . "\n";
+						$string .= '/* ' . str_repeat('=', strlen(substr($file, $path_length)) + 14) . ' */' . "\n\n";
+					}
+
+					// add file content
+					$string .= file_get_contents($file) . "\n\n";
+				}
+
+				// remove previous version of compressed file
+				if (isset($current_file)) {
+					if (file_exists($current_file)) {
+						unlink($current_file);
+					}
+				}
+
+				// replace templates base
+				$templates_base = $this->Application->ProcessParsedTag('m', 'TemplatesBase', Array ());
+				$string = str_replace('@templates_base@', rtrim($templates_base, '/'), $string);
+
+				// compress collected data
+				$this->compressInfo[$hash] = adodb_mktime();
+
+				if (!$this->debugMode) {
+					// don't compress merged js/css file in debug mode to allow js/css debugging
+					$this->compressString($string, $extension);
+				}
+
+				// save compressed file
+				$fp = fopen(WRITEABLE . '/' . sprintf($file_mask, $this->compressInfo[$hash]), 'w');
+				fwrite($fp, $string);
+				fclose($fp);
+
+				$this->_saveInfo();
+			}
+
+			// got compressed file -> use it
+			return $this->Application->BaseURL(WRITEBALE_BASE) . sprintf($file_mask, $this->compressInfo[$hash]);
+		}
+
+		/**
+		 * Returns hash string based on given files
+		 *
+		 * @param Array $files
+		 * @return int
+		 */
+		function _getHash($files)
+		{
+			$hash = $files;
+
+			if ($this->Application->isAdmin) {
+				array_unshift($hash, 'A:1');
+			}
+			else {
+				array_unshift($hash, 'A:0;T:' . $this->Application->GetVar('m_theme'));
+			}
+
+			return crc32( implode('|', $hash) );
+		}
+
+		/**
+		 * Saves file with compress information
+		 *
+		 */
+		function _saveInfo()
+		{
+			$fp = fopen($this->infoFile, 'w');
+			fwrite($fp, serialize($this->compressInfo));
+			fclose($fp);
+		}
+
+		/**
+		 * Deletes compression info file
+		 *
+		 * @todo also delete all listed there files
+		 */
+		function delete()
+		{
+			if (file_exists($this->infoFile)) {
+				unlink($this->infoFile);
+			}
+		}
+
+		/**
+		 * Compress $string based on $extension
+		 *
+		 * @param string $string
+		 * @param string $extension
+		 */
+		function compressString(&$string, $extension)
+		{
+			if ($extension == 'js') {
+				$minifier =& $this->Application->makeClass('JsMinifyHelper');
+				/* @var $minifier JsMinifyHelper */
+
+				$string = $minifier->minify($string);
+			}
+			elseif ($extension == 'css') {
+				$minifier =& $this->Application->makeClass('CssMinifyHelper');
+				/* @var $minifier CssMinifyHelper */
+
+				$string = $minifier->minify($string);
+			}
+		}
+
+		/**
+		 * Get full paths on disk for each of given templates
+		 *
+		 * @param Array $templates
+		 * @return Array
+		 */
+		function _getTemplatePaths($templates)
+		{
+			$ret = Array ();
+			$reg_exp = '/^' . preg_quote($this->Application->BaseURL(), '/') . '(.*)/';
+
+			foreach ($templates as $template) {
+				if (!$template) {
+					continue;
+				}
+
+				if (preg_match($reg_exp, $template, $regs)) {
+					// full url (from current domain) to a file
+					$path = FULL_PATH . '/' . $regs[1];
+				}
+				else {
+					list ($path, $module_filename) = $this->Application->TemplatesCache->GetTemplatePaths($template);
+					$path .= DIRECTORY_SEPARATOR . $module_filename;
+				}
+
+				$ret[] = $path;
+			}
+
+			return $ret;
+		}
+	}
\ No newline at end of file
Index: units/helpers/themes_helper.php
===================================================================
--- units/helpers/themes_helper.php	(revision 13400)
+++ units/helpers/themes_helper.php	(working copy)
@@ -390,6 +390,11 @@
 
 			$this->Application->incrementCacheSerial('theme');
 			$this->Application->incrementCacheSerial('theme-file');
+
+			$minify_helper =& $this->Application->recallObject('MinifyHelper');
+			/* @var $minify_helper MinifyHelper */
+
+			$minify_helper->delete();
 		}
 
 		/**
