public static function optimizecss($tpl) { $outputpath = JPATH_ROOT . '/' . $tpl->getParam('t3-assets', 't3-assets') . '/css'; $outputurl = JURI::root(true) . '/' . $tpl->getParam('t3-assets', 't3-assets') . '/css'; if (!JFile::exists($outputpath)){ JFolder::create($outputpath); @chmod($outputpath, 0755); } if (!is_writeable($outputpath)) { return false; } $doc = JFactory::getDocument(); //======================= Group css ================= // $cssgroups = array(); $stylesheets = array(); $ielimit = 4095; $selcounts = 0; $regex = '/\{.+?\}|,/s'; //selector counter $csspath = ''; foreach ($doc->_styleSheets as $url => $stylesheet) { $url = self::fixUrl($url); if ($stylesheet['mime'] == 'text/css' && ($csspath = self::cssPath($url))) { $stylesheet['path'] = $csspath; $stylesheet['data'] = JFile::read($csspath); $selcount = preg_match_all($regex, $stylesheet['data'], $matched); if(!$selcount) { $selcount = 1; //just for sure } //if we found an @import rule or reach IE limit css selector count, break into the new group if (preg_match('#@import\s+.+#', $stylesheet['data']) || $selcounts + $selcount >= $ielimit) { if(count($stylesheets)){ $cssgroup = array(); $groupname = array(); foreach ( $stylesheets as $gurl => $gsheet ) { $cssgroup[$gurl] = $gsheet; $groupname[] = $gurl; } $cssgroup['groupname'] = implode('', $groupname); $cssgroups[] = $cssgroup; } $stylesheets = array($url => $stylesheet); // empty - begin a new group $selcounts = $selcount; } else { $stylesheets[$url] = $stylesheet; $selcounts += $selcount; } } else { // first get all the stylsheets up to this point, and get them into // the items array if(count($stylesheets)){ $cssgroup = array(); $groupname = array(); foreach ( $stylesheets as $gurl => $gsheet ) { $cssgroup[$gurl] = $gsheet; $groupname[] = $gurl; } $cssgroup['groupname'] = implode('', $groupname); $cssgroups[] = $cssgroup; } //mark ignore current stylesheet $cssgroup = array($url => $stylesheet, 'ignore' => true); $cssgroups[] = $cssgroup; $stylesheets = array(); // empty - begin a new group } } if(count($stylesheets)){ $cssgroup = array(); $groupname = array(); foreach ( $stylesheets as $gurl => $gsheet ) { $cssgroup[$gurl] = $gsheet; $groupname[] = $gurl; } $cssgroup['groupname'] = implode('', $groupname); $cssgroups[] = $cssgroup; } //======================= Group css ================= // $output = array(); foreach ($cssgroups as $cssgroup) { if(isset($cssgroup['ignore'])){ unset($cssgroup['ignore']); foreach ($cssgroup as $furl => $fsheet) { $output[$furl] = $fsheet; } } else { $groupname = 'css-' . substr(md5($cssgroup['groupname']), 0, 5) . '.css'; $groupfile = $outputpath . '/' . $groupname; $grouptime = JFile::exists($groupfile) ? @filemtime($groupfile) : -1; $rebuild = $grouptime < 0; //filemtime == -1 => rebuild unset($cssgroup['groupname']); foreach ($cssgroup as $furl => $fsheet) { if(!$rebuild && @filemtime($fsheet['path']) > $grouptime){ $rebuild = true; } } if($rebuild){ $cssdata = array(); foreach ($cssgroup as $furl => $fsheet) { $cssdata[] = "\n\n/*==============================="; $cssdata[] = $furl; $cssdata[] = "================================================================================*/"; $cssmin = Minify_CSS_Compressor::process($fsheet['data']); $cssmin = T3Path::updateUrl($cssmin, T3Path::relativePath($outputurl, dirname($furl))); $cssdata[] = $cssmin; } $cssdata = implode("\n", $cssdata); JFile::write($groupfile, $cssdata); @chmod($groupfile, 0644); } $output[$outputurl . '/' . $groupname] = array( 'mime' => 'text/css', 'media' => null, 'attribs' => array() ); } } //apply the change make change $doc->_styleSheets = $output; }
function compileCss($path, $topath = '') { $app = JFactory::getApplication(); $theme = $app->getUserState('vars_theme'); $realpath = realpath(JPATH_ROOT . '/' . $path); // check path if (!is_file($realpath)) { //if (!JPath::check ($realpath)){ return; } // Get file content $content = JFile::read($realpath); // remove comments $content = preg_replace('!/\\*[^*]*\\*+([^/][^*]*\\*+)*/!', '', $content); // split into array, separated by the import $arr = preg_split('#^\\s*@import\\s+"([^"]*)"\\s*;#im', $content, -1, PREG_SPLIT_DELIM_CAPTURE); // check and add theme less if not is theme less if ($theme && !preg_match('#themes/#', $path)) { $themepath = 'themes/' . $theme . '/' . basename($path); if (is_file(T3_TEMPLATE_PATH . '/less/' . $themepath)) { $arr[] = $themepath; $arr[] = ''; } } // variables & mixin $vars = $this->getVars(); // add vars //$this->setImportDir (array(dirname($realpath), T3_TEMPLATE_PATH.'/less/')); $importdirs = array(); // compile chuck $import = false; $output = ''; foreach ($arr as $s) { if ($import) { $import = false; if ($s == 'vars.less') { continue; } // process import file $url = T3Path::cleanPath(dirname($path) . '/' . $s); $importcontent = JFile::read(JPATH_ROOT . '/' . $url); if (preg_match('#^\\s*@import\\s+"([^"]*)"\\s*;#im', $importcontent)) { $importdirs[] = dirname(JPATH_ROOT . '/' . $url); } $output .= "#less-file-path{content: \"{$url}\";}\n" . $importcontent . "\n\n"; } else { $import = true; $s = trim($s); if ($s) { $output .= "#less-file-path{content: \"{$path}\";}\n" . $s . "\n\n"; } } } $importdirs[] = dirname($realpath); $importdirs[] = T3_TEMPLATE_PATH . '/less/'; $this->setImportDir($importdirs); $output = $this->compile($vars . "\n" . $output); $arr = preg_split('#^\\s*\\#less-file-path\\s*{\\s*[\\r\\n]*\\s*content:\\s*"([^"]*)";\\s*[\\r\\n]*\\s*}#im', $output, -1, PREG_SPLIT_DELIM_CAPTURE); $output = ''; $file = ''; $isfile = false; foreach ($arr as $s) { if ($isfile) { $isfile = false; $file = $s; $relpath = $topath ? T3Path::relativePath(dirname($topath), dirname($file)) : JURI::base(true) . '/' . dirname($file); } else { $output .= ($file ? T3Path::updateUrl($s, $relpath) : $s) . "\n\n"; $isfile = true; } } // remove the dupliate clearfix at the beggining if not bootstrap.css file if (!preg_match('#bootstrap.less#', $path)) { $arr = preg_split('/[\\r?\\n]{2,}/', $output); // ignore first one, it's clearfix array_shift($arr); $output = implode("\n", $arr); } if ($topath) { $tofile = JPATH_ROOT . '/' . $topath; if (!is_dir(dirname($tofile))) { JFolder::create(dirname($tofile)); } $ret = JFile::write($tofile, $output); @chmod($tofile, 0644); return $ret; } return $output; }
/** * Parse a less file to get all its overrides before compile * @param string $path the less file */ public static function parse($path) { $rtpl_check = '@' . preg_quote(T3_TEMPLATE_REL, '@') . '/@i'; $rtpl_less_check = '@' . preg_quote(T3_TEMPLATE_REL, '@') . '/less/@i'; $app = JFactory::getApplication(); $theme = $app->getUserState('current_theme'); $dir = $app->getUserState('current_direction'); $is_rtl = $dir == 'rtl'; $rel_path = preg_replace($rtpl_check, '', $path); $rel_dir = dirname($rel_path); // check path $realpath = realpath(JPATH_ROOT . '/' . $path); if (!is_file($realpath)) { return false; } // get file content $content = file_get_contents($realpath); //remove vars.less $content = preg_replace(self::$rimportvars, '', $content); // split into array, separated by the import $arr = preg_split(self::$rimport, $content, -1, PREG_SPLIT_DELIM_CAPTURE); $arr[] = basename($rel_path); $arr[] = ''; $list = array(); $rtl_list = array(); $list[$path] = ''; $import = false; foreach ($arr as $chunk) { if ($import) { $import = false; $import_url = T3Path::cleanPath(T3_TEMPLATE_REL . '/' . $rel_dir . '/' . $chunk); // if $url in less folder, get all its overrides if (preg_match($rtpl_less_check, $import_url)) { $less_rel_url = preg_replace($rtpl_less_check, '', $import_url); $array = T3Path::getAllPath('less/' . $less_rel_url, true); if ($theme) { $array = array_merge($array, T3Path::getAllPath('less/themes/' . $theme . '/' . $less_rel_url, true)); } foreach ($array as $f) { // add file in template only if (preg_match($rtpl_check, $f)) { $list[$f] = T3Path::relativePath(dirname($path), $f); } } // rtl overrides if ($is_rtl) { $array = T3Path::getAllPath('less/rtl/' . $less_rel_url, true); if ($theme) { $array = array_merge($array, T3Path::getAllPath('less/rtl/themes/' . $theme . '/' . $less_rel_url, true)); } foreach ($array as $f) { // add file in template only if (preg_match($rtpl_check, $f)) { $rtl_list[$f] = T3Path::relativePath(dirname($path), $f); } } } } else { $list[$import_url] = T3Path::cleanPath($chunk); // rtl override if ($is_rtl) { $rtl_url = preg_replace('/\\/less\\//', '/less/rtl/', $import_url); if (is_file(JPATH_ROOT . '/' . $rtl_url)) { $rtl_list[$rtl_url] = T3Path::relativePath(dirname($path), $rtl_url); } } } } else { $import = true; $list[$chunk] = false; } } // remove itself unset($list[$path]); // join rtl if ($is_rtl) { $list["\n\n#" . self::$krtlsep . "{content: \"separator\";}\n\n"] = false; $list = array_merge($list, $rtl_list); } return $list; }
/** * * Show thememagic form */ public static function thememagic($path) { $app = JFactory::getApplication(); $input = $app->input; $isadmin = $app->isAdmin(); if ($isadmin) { $tplparams = T3::getTplParams(); } else { $tplparams = $app->getTemplate(true)->params; } $url = $isadmin ? JUri::root(true) . '/index.php' : JUri::current(); $url .= (preg_match('/\\?/', $url) ? '&' : '?') . 'themer=1'; $url .= $tplparams->get('theme', -1) != -1 ? '&t3style=' . $tplparams->get('theme') : ''; if ($isadmin) { $url .= '&t3tmid=' . $input->getCmd('id'); } $assetspath = T3_TEMPLATE_PATH; $themepath = $assetspath . '/less/themes'; if (!class_exists('JRegistryFormatLESS')) { include_once T3_ADMIN_PATH . '/includes/format/less.php'; } $themes = array(); $jsondata = array(); //push a default theme $tobj = new stdClass(); $tobj->id = 'base'; $tobj->title = JText::_('JDEFAULT'); $themes['base'] = $tobj; $varfile = $assetspath . '/less/variables.less'; if (file_exists($varfile)) { $params = new JRegistry(); $params->loadString(JFile::read($varfile), 'LESS'); $jsondata['base'] = $params->toArray(); } // if (JFolder::exists($themepath)) { foreach (T3Path::getAllPath('/less/themes') as $themepath) { $listthemes = JFolder::folders($themepath); if (count($listthemes)) { foreach ($listthemes as $theme) { //$varsfile = $themepath . '/' . $theme . '/variables-custom.less'; //if(file_exists($varsfile)){ $tobj = new stdClass(); $tobj->id = $theme; $tobj->title = $theme; //check for all less file in theme folder $params = false; $others = JFolder::files($themepath . '/' . $theme, '.less', false, true); foreach ($others as $other) { $otherrel = T3Path::relativePath('less/', str_replace(T3_TEMPLATE_PATH . '/', '', $other)); //get those developer custom values if ($other == 'variables.less') { $params = new JRegistry(); $params->loadString(JFile::read($themepath . '/' . $theme . '/variables.less'), 'LESS'); } if ($other != 'variables-custom.less') { $tobj->{$other} = $otherrel; } } $cparams = new JRegistry(); $varsfile = $themepath . '/' . $theme . '/variables.less'; if (file_exists($varsfile)) { $cparams->loadString(JFile::read($varsfile), 'LESS'); } $varsfile = $themepath . '/' . $theme . '/variables-custom.less'; if (file_exists($varsfile)) { $cparams->loadString(JFile::read($varsfile), 'LESS'); } if ($params) { foreach ($cparams->toArray() as $key => $value) { $params->set($key, $value); } } else { $params = $cparams; } $themes[$theme] = $tobj; $jsondata[$theme] = $params->toArray(); //} } } } // get active theme $active_theme = $tplparams->get('theme', 'base'); if (!isset($themes[$active_theme])) { $active_theme = 'base'; } $langs = array('addTheme' => JText::_('T3_TM_ASK_ADD_THEME'), 'delTheme' => JText::_('T3_TM_ASK_DEL_THEME'), 'overwriteTheme' => JText::_('T3_TM_ASK_OVERWRITE_THEME'), 'correctName' => JText::_('T3_TM_ASK_CORRECT_NAME'), 'themeExist' => JText::_('T3_TM_EXISTED'), 'saveChange' => JText::_('T3_TM_ASK_SAVE_CHANGED'), 'previewError' => JText::_('T3_TM_PREVIEW_ERROR'), 'unknownError' => JText::_('T3_MSG_UNKNOWN_ERROR'), 'lblCancel' => JText::_('JCANCEL'), 'lblOk' => JText::_('T3_TM_LABEL_OK'), 'lblNo' => JText::_('JNO'), 'lblYes' => JText::_('JYES'), 'lblDefault' => JText::_('JDEFAULT')); //Keepalive $config = JFactory::getConfig(); $lifetime = $config->get('lifetime') * 60000; $refreshTime = $lifetime <= 60000 ? 30000 : $lifetime - 60000; // Refresh time is 1 minute less than the liftime assined in the configuration.php file. // The longest refresh period is one hour to prevent integer overflow. if ($refreshTime > 3600000 || $refreshTime <= 0) { $refreshTime = 3600000; } $backurl = JFactory::getURI(); $backurl->delVar('t3action'); $backurl->delVar('t3task'); if (!$isadmin) { $backurl->delVar('tm'); $backurl->delVar('themer'); } T3::import('depend/t3form'); $form = new T3Form('thememagic.themer', array('control' => 't3form')); $form->load(JFile::read(JFile::exists(T3_TEMPLATE_PATH . '/thememagic.xml') ? T3_TEMPLATE_PATH . '/thememagic.xml' : T3_PATH . '/params/thememagic.xml')); $form->loadFile(T3_TEMPLATE_PATH . '/templateDetails.xml', true, '//config'); $tplform = new T3Form('thememagic.overwrite', array('control' => 't3form')); $tplform->loadFile(T3_TEMPLATE_PATH . '/templateDetails.xml', true, '//config'); $fieldSets = $form->getFieldsets('thememagic'); $tplFieldSets = $tplform->getFieldsets('thememagic'); $disabledFieldSets = array(); foreach ($tplFieldSets as $name => $fieldSet) { if (isset($fieldSet->disabled)) { $disabledFieldSets[] = $name; } } include T3_ADMIN_PATH . '/admin/thememagic/thememagic.tpl.php'; exit; }
/** * Special compilation for template.css file when development mode is on * @param $path * @return bool */ public static function divide($path) { //check system self::requirement(); $parser = new Less_Parser(); $app = JFactory::getApplication(); $doc = JFactory::getDocument(); $tpl = T3_TEMPLATE; $theme = $app->getTemplate(true)->params->get('theme'); $is_rtl = $doc->direction == 'rtl' && strpos($path, 'rtl/') === false; $subdir = ($is_rtl ? 'rtl/' : '') . ($theme ? $theme . '/' : ''); $topath = T3_DEV_FOLDER . '/' . $subdir; $tofile = null; $root = JUri::root(true); //pattern $rimport = '@^\\s*\\@import\\s+"([^"]*)"\\s*;@im'; $rvarscheck = '@(base|base-bs3|bootstrap|' . preg_quote($tpl) . ')/less/(vars|variables|mixins)\\.less@'; $rexcludepath = '@(base|base-bs3|bootstrap|' . preg_quote($tpl) . ')/less/@'; $rimportvars = '@^\\s*\\@import\\s+".*(variables-custom|variables|vars|mixins)\\.less"\\s*;@im'; $rsplitbegin = '@^\\s*\\#'; $rsplitend = '[^\\s]*?\\s*{\\s*[\\r\\n]*\\s*content:\\s*"([^"]*)";\\s*[\\r\\n]*\\s*}@im'; $rswitchrtl = '@/less/(themes/[^/]*/)?@'; $kfilepath = 'less-file-path'; $kvarsep = 'less-content-separator'; $krtlsep = 'rtl-less-content'; if ($topath) { if (!is_dir(JPATH_ROOT . '/' . $topath)) { JFolder::create(JPATH_ROOT . '/' . $topath); } } // check path $realpath = realpath(JPATH_ROOT . '/' . $path); if (!is_file($realpath)) { return; } // get file content $content = JFile::read($realpath); //remove vars.less if (preg_match($rexcludepath, $path)) { $content = preg_replace($rimportvars, '', $content); } // check and add theme less if not is theme less if ($theme && strpos($path, 'themes/') === false) { $themepath = 'themes/' . $theme . '/' . basename($path); if (is_file(T3_TEMPLATE_PATH . '/less/' . $themepath)) { $content = $content . "\n@import \"{$themepath}\"; \n\n"; } } // split into array, separated by the import $split_contents = preg_split($rimport, $content, -1, PREG_SPLIT_DELIM_CAPTURE); //check if we need to rebuild $rebuild = false; $vars_lm = $app->getUserState('vars_last_modified', 0); $file_lm = @filemtime(JPATH_ROOT . '/' . $path); //check for this file and rtl $cssfile = $topath . str_replace('/', '.', $path) . '.css'; $css_lm = is_file(JPATH_ROOT . '/' . $cssfile) ? @filemtime(JPATH_ROOT . '/' . $cssfile) : -1; //rebuild? if ($css_lm < $vars_lm || $css_lm < $file_lm) { $rebuild = true; } else { $doc->addStylesheet($root . '/' . $cssfile); } //check for rebuild if this rtl overwrite file has just modified if (!$rebuild && $is_rtl) { $rtl_url = preg_replace('@/less/(themes/)?@', '/less/rtl/', $path); $rtl_lm = is_file(JPATH_ROOT . '/' . $rtl_url) ? @filemtime(JPATH_ROOT . '/' . $rtl_url) : 0; $rebuild = $css_lm < $rtl_lm; } if (!$rebuild) { $import = false; foreach ($split_contents as $chunk) { if ($import) { $import = false; $url = T3Path::cleanPath(dirname($path) . '/' . $chunk); if (is_file(JPATH_ROOT . '/' . $url)) { //$css_lm should be the same as templates.css $file_lm = @filemtime(JPATH_ROOT . '/' . $url); $theme_lm = -1; $rtl_lm = -1; $theme_rtl_lm = -1; if ($theme && strpos($url, 'themes/') === false) { $themepath = 'themes/' . $theme . '/' . basename($path); if (is_file(T3_TEMPLATE_PATH . '/less/' . $themepath)) { $theme_lm = @filemtime(T3_TEMPLATE_PATH . '/less/' . $themepath); } if ($is_rtl) { $rtlthemepath = preg_replace($rswitchrtl, '/less/rtl/' . $theme . '/', $url); if (is_file(JPATH_ROOT . '/' . $rtlthemepath)) { $theme_rtl_lm = @filemtime(JPATH_ROOT . '/' . $rtlthemepath); } } } if ($is_rtl) { $rtl_url = preg_replace('@/less/(themes/)?@', '/less/rtl/', $url); if (is_file(JPATH_ROOT . '/' . $rtl_url)) { $rtl_lm = @filemtime(JPATH_ROOT . '/' . $rtl_url); } } if (!is_file(JPATH_ROOT . '/' . $cssfile) || $css_lm < $vars_lm || $css_lm < $file_lm || $css_lm < $theme_lm || $css_lm < $rtl_lm || $css_lm < $theme_rtl_lm) { $rebuild = true; // rebuild for sure break; //no need further check } else { $doc->addStylesheet($root . '/' . $topath . str_replace('/', '.', $url) . '.css'); } } } else { $import = true; } } } // so, no need to rebuild? if (!$rebuild) { // add RTL css if needed if ($is_rtl) { $cssfile = $topath . str_replace('/', '.', str_replace('.less', '-rtl.less', $path)) . '.css'; if (is_file(JPATH_ROOT . '/' . $cssfile)) { $doc->addStylesheet($root . '/' . $cssfile); } } return false; } // variables & mixin $vars = self::getVars(); $output = ''; $importdirs = array(); // iterate to each chunk and add separator mark $import = false; foreach ($split_contents as $chunk) { if ($import) { $import = false; $url = T3Path::cleanPath(dirname($path) . '/' . $chunk); // ignore vars.less and variables.less if they are in template folder // cause there may have other css less file with the same name (eg. font awesome) if (preg_match($rvarscheck, $url)) { continue; } // remember this path when lookup for import $importdirs[dirname(JPATH_ROOT . '/' . $url)] = $root . '/' . dirname($url) . '/'; $output .= "#{$kfilepath}{content: \"{$url}\";}\n@import \"{$chunk}\";\n\n"; // check and add theme less if ($theme && strpos($url, 'themes/') === false) { $theme_rel = 'themes/' . $theme . '/' . basename($url); $theme_path = T3_TEMPLATE_PATH . '/less/' . $theme_rel; if (is_file($theme_path)) { $importdirs[dirname($theme_path)] = T3_TEMPLATE_URL . dirname($theme_rel) . '/'; $output .= "#{$kfilepath}{content: \"" . ('templates/' . T3_TEMPLATE . '/less/' . $theme_rel) . "\";}\n@import \"{$theme_rel}\";\n\n"; } } } else { $import = true; $chunk = trim($chunk); if ($chunk) { $output .= "#{$kfilepath}{content: \"{$path}\";}\n{$chunk}\n\n"; } } } // compile RTL overwrite when in RTL mode if ($is_rtl) { $rtlcontent = ''; // import rtl override $import = false; foreach ($split_contents as $chunk) { if ($import) { $import = false; $url = T3Path::cleanPath(dirname($path) . '/' . $chunk); // ignore vars.less and variables.less if they are in template folder // cause there may have other css less file with the same name (eg. font awesome) if (preg_match($rvarscheck, $url)) { continue; } // process import file $rtl_url = preg_replace('@/less/(themes/)?@', '/less/rtl/', $url); // is there overwrite file? if (!is_file(JPATH_ROOT . '/' . $rtl_url)) { continue; } // process import file $importcontent = JFile::read(JPATH_ROOT . '/' . $rtl_url); if (preg_match($rexcludepath, $rtl_url)) { $importcontent = preg_replace($rimportvars, '', $importcontent); } // remember this path when lookup for import if (preg_match($rimport, $importcontent)) { $importdirs[dirname(JPATH_ROOT . '/' . $rtl_url)] = $root . '/' . dirname($rtl_url) . '/'; } $rtlcontent .= "\n{$importcontent}\n\n"; // rtl theme overwrite if ($theme && strpos($url, 'themes/') === false) { $rtlthemepath = preg_replace($rswitchrtl, '/less/rtl/' . $theme . '/', $url); if (is_file(JPATH_ROOT . '/' . $rtlthemepath)) { // process import file $importcontent = JFile::read(JPATH_ROOT . '/' . $rtlthemepath); $rtlcontent .= "\n{$importcontent}\n\n"; $importdirs[dirname(JPATH_ROOT . '/' . $rtlthemepath)] = $root . '/' . dirname($rtlthemepath) . '/'; } } } else { $import = true; } } // override in template for this file $rtlpath = preg_replace($rswitchrtl, '/less/rtl/', $path); if (is_file(JPATH_ROOT . '/' . $rtlpath)) { // process import file $importcontent = JFile::read(JPATH_ROOT . '/' . $rtlpath); $rtlcontent .= "\n{$importcontent}\n\n"; $importdirs[dirname(JPATH_ROOT . '/' . $rtlpath)] = $root . '/' . dirname($rtlpath) . '/'; } // rtl theme if ($theme) { $rtlthemepath = preg_replace($rswitchrtl, '/less/rtl/' . $theme . '/', $path); if (is_file(JPATH_ROOT . '/' . $rtlthemepath)) { // process import file $importcontent = JFile::read(JPATH_ROOT . '/' . $rtlthemepath); $rtlcontent .= "\n{$importcontent}\n\n"; $importdirs[dirname(JPATH_ROOT . '/' . $rtlthemepath)] = $root . '/' . dirname($rtlthemepath) . '/'; } } if ($rtlcontent) { //rtl content will be treat as a new file $rtlfile = str_replace('.less', '-rtl.less', $path); $output = $output . "\n#{$kfilepath}{content: \"{$rtlfile}\";}\n\n#{$krtlsep}{content: \"separator\";}\n\n{$rtlcontent}\n\n"; } } // common place $importdirs[T3_TEMPLATE_PATH . '/less'] = T3_TEMPLATE_URL . '/less/'; // myself $importdirs[dirname(JPATH_ROOT . '/' . $path)] = $root . '/' . dirname($path) . '/'; // compile less to css using lessphp $parser->SetImportDirs($importdirs); $parser->SetFileInfo(JPATH_ROOT . '/' . $path, $root . '/' . dirname($path) . '/'); $source = $vars . "\n#{$kvarsep}{content: \"separator\";}\n" . $output; $parser->parse($source); $output = $parser->getCss(); //use cssjanus to transform the content if ($is_rtl) { if ($rtlcontent) { $output = preg_split($rsplitbegin . $krtlsep . $rsplitend, $output, -1, PREG_SPLIT_DELIM_CAPTURE); $rtlcontent = isset($output[2]) ? $output[2] : false; $output = $output[0]; } T3::import('jacssjanus/ja.cssjanus'); $output = JACSSJanus::transform($output, true); if ($rtlcontent) { $output = $output . "\n" . $rtlcontent; } } //update path and store to files $split_contents = preg_split($rsplitbegin . $kfilepath . $rsplitend, $output, -1, PREG_SPLIT_DELIM_CAPTURE); $file_contents = array(); $file = $path; //default $relpath = JURI::base(true) . '/' . dirname($file); $isfile = false; foreach ($split_contents as $chunk) { if ($isfile) { $isfile = false; $file = $chunk; $relpath = $topath ? T3Path::relativePath($topath, dirname($file)) : JURI::base(true) . '/' . dirname($file); } else { $file_contents[$file] = (isset($file_contents[$file]) ? $file_contents[$file] : '') . "\n" . ($file ? T3Path::updateUrl($chunk, $relpath) : $chunk) . "\n\n"; $isfile = true; } } if (!empty($file_contents)) { // remove the duplicate clearfix at the beginning $split_contents = preg_split($rsplitbegin . $kvarsep . $rsplitend, reset($file_contents)); // ignore first one, it's clearfix if (is_array($split_contents)) { array_shift($split_contents); } $file_contents[key($file_contents)] = implode("\n", $split_contents); //output the file to content and add to document foreach ($file_contents as $file => $content) { $cssfile = $topath . str_replace('/', '.', $file) . '.css'; JFile::write(JPATH_ROOT . '/' . $cssfile, $content); $doc->addStylesheet($root . '/' . $cssfile); } } }
function compileCss($path, $topath = '') { $app = JFactory::getApplication(); $tpl = T3_TEMPLATE; $theme = $app->getUserState('vars_theme'); $tofile = null; //pattern $rcomment = '@/\\*[^*]*\\*+([^/][^*]*\\*+)*/@'; $rspace = '@[\\r?\\n]{2,}@'; $rimport = '@^\\s*\\@import\\s+"([^"]*)"\\s*;@im'; $rvarscheck = '@(base|bootstrap|' . preg_quote($tpl) . ')/less/(vars|variables|mixins)\\.less@'; $rexcludepath = '@(base|bootstrap|' . preg_quote($tpl) . ')/less/@'; $rimportvars = '@^\\s*\\@import\\s+".*(variables-custom|variables|vars|mixins)\\.less"\\s*;@im'; $rsplitbegin = '@^\\s*\\#'; $rsplitend = '[^\\s]*?\\s*{\\s*[\\r\\n]*\\s*content:\\s*"([^"]*)";\\s*[\\r\\n]*\\s*}@im'; $kfilepath = 'less-file-path'; $kvarsep = 'less-content-separator'; $krtlsep = 'rtl-less-content'; if ($topath) { $tofile = JPATH_ROOT . '/' . $topath; if (!is_dir(dirname($tofile))) { JFolder::create(dirname($tofile)); } } // check path $realpath = realpath(JPATH_ROOT . '/' . $path); if (!is_file($realpath)) { return; } // get file content $content = JFile::read($realpath); // remove comments? - we should keep comment for rtl flip //$content = preg_replace($rcomment, '', $content); // split into array, separated by the import $arr = preg_split($rimport, $content, -1, PREG_SPLIT_DELIM_CAPTURE); // check and add theme less if not is theme less if ($theme && strpos($path, 'themes/') === false) { $themepath = 'themes/' . $theme . '/' . basename($path); if (is_file(T3_TEMPLATE_PATH . '/less/' . $themepath)) { $arr[] = $themepath; $arr[] = ''; } } // variables & mixin $vars = $this->getVars(); $importdirs = array(); // compile chunk $import = false; $output = ''; foreach ($arr as $s) { if ($import) { $import = false; $url = T3Path::cleanPath(dirname($path) . '/' . $s); // ignore vars.less and variables.less if they are in template folder // cause there may have other css less file with the same name (eg. font awesome) if (preg_match($rvarscheck, $url)) { continue; } // process import file $importcontent = JFile::read(JPATH_ROOT . '/' . $url); if (preg_match($rexcludepath, $url)) { $importcontent = preg_replace($rimportvars, '', $importcontent); } // remember this path when lookup for import if (preg_match($rimport, $importcontent)) { $importdirs[] = dirname(JPATH_ROOT . '/' . $url); } $output .= "#{$kfilepath}{content: \"{$url}\";}\n{$importcontent}\n\n"; } else { $import = true; $s = trim($s); if ($s) { $output .= "#{$kfilepath}{content: \"{$path}\";}\n{$s}\n\n"; } } } $rtlcontent = ''; $is_rtl = $app->getUserState('DIRECTION') == 'rtl' && strpos($path, 'rtl/') === false; // convert to RTL if using RTL if ($is_rtl) { // import rtl override // check override for import $import = false; foreach ($arr as $s) { if ($import) { $import = false; $url = T3Path::cleanPath(dirname($path) . '/' . $s); // ignore vars.less and variables.less if they are in template folder // cause there may have other css less file with the same name (eg. font awesome) if (preg_match($rvarscheck, $url)) { continue; } // process import file $url = preg_replace('/\\/less\\/(themes\\/)?/', '/less/rtl/', $url); if (!is_file(JPATH_ROOT . '/' . $url)) { continue; } // process import file $importcontent = JFile::read(JPATH_ROOT . '/' . $url); if (preg_match($rexcludepath, $url)) { $importcontent = preg_replace($rimportvars, '', $importcontent); } // remember this path when lookup for import if (preg_match($rimport, $importcontent)) { $importdirs[] = dirname(JPATH_ROOT . '/' . $url); } $rtlcontent .= "#{$kfilepath}-rtl{content: \"{$url}\";}\n{$importcontent}\n\n"; } else { $import = true; } } // override in template for this file $rtlpath = preg_replace('/\\/less\\/(themes\\/[^\\/]*\\/)?/', '/less/rtl/', $path); if (is_file(JPATH_ROOT . '/' . $rtlpath)) { // process import file $importcontent = JFile::read(JPATH_ROOT . '/' . $rtlpath); $rtlcontent .= "#{$kfilepath}-rtl{content: \"{$rtlpath}\";}\n{$importcontent}\n\n"; } // rtl theme if ($theme) { $rtlthemepath = preg_replace('/\\/less\\/(themes\\/[^\\/]*\\/)?/', '/less/rtl/' . $theme . '/', $path); if (is_file(JPATH_ROOT . '/' . $rtlthemepath)) { // process import file $importcontent = JFile::read(JPATH_ROOT . '/' . $rtlthemepath); $rtlcontent .= "#{$kfilepath}-rtl{content: \"{$rtlthemepath}\";}\n{$importcontent}\n\n"; } } if ($rtlcontent) { $output = $output . "#{$krtlsep}{content: \"separator\";}\n\n{$rtlcontent}\n\n"; } } $importdirs[] = dirname($realpath); $importdirs[] = T3_TEMPLATE_PATH . '/less/'; $this->setImportDir($importdirs); $this->setPreserveComments(true); $source = $vars . "\n#{$kvarsep}{content: \"separator\";}\n" . $output; // compile less to css using lessphp $output = $this->compile($source); $arr = preg_split($rsplitbegin . $kfilepath . $rsplitend, $output, -1, PREG_SPLIT_DELIM_CAPTURE); $output = ''; $file = ''; $isfile = false; foreach ($arr as $s) { if ($isfile) { $isfile = false; $file = $s; $relpath = $topath ? T3Path::relativePath(dirname($topath), dirname($file)) : JURI::base(true) . '/' . dirname($file); } else { $output .= ($file ? T3Path::updateUrl($s, $relpath) : $s) . "\n\n"; $isfile = true; } } // remove the dupliate clearfix at the beggining if not bootstrap.css file if (strpos($path, $tpl . '/less/bootstrap.less') === false) { $arr = preg_split($rsplitbegin . $kvarsep . $rsplitend, $output); // ignore first one, it's clearfix if (is_array($arr)) { array_shift($arr); } $output = implode("\n", $arr); } else { $output = preg_replace($rsplitbegin . $kvarsep . $rsplitend, '', $output); } if ($is_rtl) { if ($rtlcontent) { $output = preg_split($rsplitbegin . $krtlsep . $rsplitend, $output, -1, PREG_SPLIT_DELIM_CAPTURE); $rtlcontent = isset($output[2]) ? $output[2] : false; $output = $output[0]; } T3::import('jacssjanus/ja.cssjanus'); $output = JACSSJanus::transform($output, true); if ($rtlcontent) { $output = $output . "\n" . $rtlcontent; } } //remove comments and clean up $output = preg_replace($rcomment, '', $output); $output = preg_replace($rspace, "\n\n", $output); if ($tofile) { $ret = JFile::write($tofile, $output); @chmod($tofile, 0644); return $ret; } return $output; }
/** * @param $tpl template object * @return bool optimize success or not */ public static function optimizecss($tpl) { $outputpath = JPATH_ROOT . '/' . $tpl->getParam('t3-assets', 't3-assets') . '/css'; $outputurl = JURI::root(true) . '/' . $tpl->getParam('t3-assets', 't3-assets') . '/css'; if (!JFile::exists($outputpath)) { JFolder::create($outputpath); @chmod($outputpath, 0755); } if (!is_writeable($outputpath)) { return false; } //prepare config self::prepare($tpl); $doc = JFactory::getDocument(); //======================= Group css ================= // $mediagroup = array(); $cssgroups = array(); $stylesheets = array(); $ielimit = 4095; $selcounts = 0; $regex = '/\\{.+?\\}|,/s'; //selector counter $csspath = ''; // group css into media $mediagroup['all'] = array(); $mediagroup['screen'] = array(); foreach ($doc->_styleSheets as $url => $stylesheet) { $media = $stylesheet['media'] ? $stylesheet['media'] : 'all'; if (empty($mediagroup[$media])) { $mediagroup[$media] = array(); } $mediagroup[$media][$url] = $stylesheet; } foreach ($mediagroup as $media => $group) { $stylesheets = array(); // empty - begin a new group foreach ($group as $url => $stylesheet) { $url = self::fixUrl($url); if ($stylesheet['mime'] == 'text/css' && ($csspath = self::cssPath($url))) { $stylesheet['path'] = $csspath; $stylesheet['data'] = file_get_contents($csspath); $selcount = preg_match_all($regex, $stylesheet['data'], $matched); if (!$selcount) { $selcount = 1; //just for sure } //if we found an @import rule or reach IE limit css selector count, break into the new group if (preg_match('#@import\\s+.+#', $stylesheet['data']) || $selcounts + $selcount >= $ielimit) { if (count($stylesheets)) { $cssgroup = array(); $groupname = array(); $grouptime = 0; foreach ($stylesheets as $gurl => $gsheet) { $cssgroup[$gurl] = $gsheet; $groupname[] = $gurl; $ftime = @filemtime($gsheet['path']); if ($ftime > $grouptime) { $grouptime = $ftime; } } $cssgroup['groupname'] = implode('', $groupname); $cssgroup['grouptime'] = $grouptime; $cssgroup['media'] = $media; $cssgroups[] = $cssgroup; } $stylesheets = array($url => $stylesheet); // empty - begin a new group $selcounts = $selcount; } else { $stylesheets[$url] = $stylesheet; $selcounts += $selcount; } } else { // first get all the stylsheets up to this point, and get them into // the items array if (count($stylesheets)) { $cssgroup = array(); $groupname = array(); $grouptime = 0; foreach ($stylesheets as $gurl => $gsheet) { $cssgroup[$gurl] = $gsheet; $groupname[] = $gurl; $ftime = @filemtime($gsheet['path']); if ($ftime > $grouptime) { $grouptime = $ftime; } } $cssgroup['groupname'] = implode('', $groupname); $cssgroup['grouptime'] = $grouptime; $cssgroup['media'] = $media; $cssgroups[] = $cssgroup; } //mark ignore current stylesheet $cssgroup = array($url => $stylesheet, 'ignore' => true); $cssgroups[] = $cssgroup; $stylesheets = array(); // empty - begin a new group } } if (count($stylesheets)) { $cssgroup = array(); $groupname = array(); $grouptime = 0; foreach ($stylesheets as $gurl => $gsheet) { $cssgroup[$gurl] = $gsheet; $groupname[] = $gurl; $ftime = @filemtime($gsheet['path']); if ($ftime > $grouptime) { $grouptime = $ftime; } } $cssgroup['groupname'] = implode('', $groupname); $cssgroup['grouptime'] = $grouptime; $cssgroup['media'] = $media; $cssgroups[] = $cssgroup; } } //======================= Group css ================= // $output = array(); foreach ($cssgroups as $cssgroup) { if (isset($cssgroup['ignore'])) { unset($cssgroup['ignore']); unset($cssgroup['groupname']); unset($cssgroup['media']); foreach ($cssgroup as $furl => $fsheet) { $output[$furl] = $fsheet; } } else { $rebuildCheck = self::checkRebuild($cssgroup, 'css', $outputpath); $media = $cssgroup['media']; unset($cssgroup['groupname']); unset($cssgroup['grouptime']); unset($cssgroup['media']); $groupname = $rebuildCheck['filename']; if ($rebuildCheck['rebuild']) { $groupfile = $outputpath . '/' . $groupname; $cssdata = array(); foreach ($cssgroup as $furl => $fsheet) { $cssdata[] = "\n\n/*==============================="; $cssdata[] = $furl; $cssdata[] = "================================================================================*/"; $cssmin = self::minifyCss($fsheet['data']); $cssmin = T3Path::updateUrl($cssmin, T3Path::relativePath($outputurl, dirname($furl))); $cssdata[] = $cssmin; } $cssdata = implode("\n", $cssdata); if (!JFile::write($groupfile, $cssdata)) { // cannot write file, ignore minify return false; } $grouptime = @filemtime($groupfile); @chmod($groupfile, 0644); } $output[$outputurl . '/' . $groupname] = array('mime' => 'text/css', 'media' => $media == 'all' ? NULL : $media, 'attribs' => array()); } } //apply the change make change $doc->_styleSheets = $output; }