function test() { global $test, $testcase, $shouldbe, $swap_ltr_rtl_in_url, $swap_left_right_in_url; $input = implode("\n", $testcase); $expect = implode("\n", $shouldbe); $output = JACSSJanus::transform($input, $swap_ltr_rtl_in_url, $swap_left_right_in_url); $pass = $output == $expect; $result = $pass ? '<span class="pass">pass</span>' : '<span class="fail">fail</span>'; ?> <h2 class="<?php echo $pass ? 'pass' : 'fail'; ?> "><?php echo $test; ?> </h2> <table class="result"> <tr><td>Input</td><td><?php echo str_replace("\n", "<br />\n", $input); ?> </td></tr> <tr><td>Expect</td><td><?php echo str_replace("\n", "<br />\n", $expect); ?> </td></tr> <tr><td>Output</td><td><?php echo str_replace("\n", "<br />\n", $output); ?> </td></tr> <tr><td>Result</td><td><?php echo $result; ?> </td></tr> </table> <br /><br /> <?php }
/** * @param string $path file path of less file to compile * @param string $topath file path of output css file * @return bool|mixed compile result or the css compiled content */ public static function compileCss($path, $topath = '', $split = false, $list = null) { $fromdir = dirname($path); $app = JFactory::getApplication(); $is_rtl = $app->getUserState('current_direction') == 'rtl'; if (empty($list)) { $list = self::parse($path); } if (empty($list)) { return false; } // join $list $content = ''; $importdirs = array(); $todir = $topath ? dirname($topath) : $fromdir; if (!is_dir(JPATH_ROOT . '/' . $todir)) { JFolder::create(JPATH_ROOT . '/' . $todir); } $importdirs[JPATH_ROOT . '/' . $fromdir] = self::relativePath($todir, $fromdir); foreach ($list as $f => $import) { if ($import) { $importdirs[JPATH_ROOT . '/' . dirname($f)] = self::relativePath($todir, dirname($f)); $content .= "\n#" . self::$kfilepath . "{content: \"{$f}\";}\n"; // $content .= "@import \"$import\";\n\n"; if (is_file(JPATH_ROOT . '/' . $f)) { $less_content = file_get_contents(JPATH_ROOT . '/' . $f); // remove vars/mixins for template & canvas less if (preg_match('@' . preg_quote(CANVAS_TEMPLATE_REL, '@') . '/less/@i', $f) || preg_match('@' . preg_quote(CANVAS_REL, '@') . '/less/@i', $f)) { $less_content = preg_replace(self::$rimportvars, '', $less_content); } self::$_path = CANVASPath::relativePath($fromdir, dirname($f)) . '/'; $less_content = preg_replace_callback(self::$rimport, array('CANVASLess', 'cb_import_path'), $less_content); $content .= $less_content; } } else { $content .= "\n#" . self::$kfilepath . "{content: \"{$path}\";}\n"; $content .= $f . "\n\n"; } } // get vars $vars_files = explode('|', self::getVars('urls')); // build source $source = ''; // build import vars foreach ($vars_files as $var) { $vars_path = CANVASPath::relativePath($fromdir, dirname($var)); if ($vars_path) { $vars_path .= "/"; } $var_file = $vars_path . basename($var); $source .= "@import \"" . $var_file . "\";\n"; } // less content $source .= "\n#" . self::$kvarsep . "{content: \"separator\";}\n" . $content; // call Less to compile $output = CANVASLessCompiler::compile($source, $path, $todir, $importdirs); // process content //use cssjanus to transform the content if ($is_rtl) { $output = preg_split(self::$rsplitbegin . self::$krtlsep . self::$rsplitend, $output, -1, PREG_SPLIT_DELIM_CAPTURE); $rtlcontent = isset($output[2]) ? $output[2] : false; $output = $output[0]; CANVAS::import('jacssjanus/ja.cssjanus'); $output = JACSSJanus::transform($output, true); // join with rtl content if ($rtlcontent) { $output = $output . "\n" . $rtlcontent; } } // skip duplicate clearfix $arr = preg_split(self::$rsplitbegin . self::$kvarsep . self::$rsplitend, $output, 2); if (preg_match('/bootstrap.less/', $path)) { $output = implode("\n", $arr); } else { $output = count($arr) > 1 ? $arr[1] : $arr[0]; } //remove comments and clean up $output = preg_replace(self::$rcomment, '', $output); $output = preg_replace(self::$rspace, "\n\n", $output); // split if needed if ($split) { //update path and store to files $split_contents = preg_split(self::$rsplitbegin . self::$kfilepath . self::$rsplitend, $output, -1, PREG_SPLIT_DELIM_CAPTURE); $file_contents = array(); $file = $path; $isfile = false; foreach ($split_contents as $chunk) { if ($isfile) { $isfile = false; $file = $chunk; } else { $file_contents[$file] = (isset($file_contents[$file]) ? $file_contents[$file] : '') . "\n" . $chunk . "\n\n"; $isfile = true; } } if (!empty($file_contents)) { //output the file to content and add to document foreach ($file_contents as $file => $content) { $content = trim($content); $filename = str_replace('/', '.', $file) . '.css'; JFile::write(JPATH_ROOT . '/' . $todir . '/' . $filename, $content); } } } else { $output = preg_replace(self::$rsplitbegin . self::$kfilepath . self::$rsplitend, '', $output); $output = trim($output); if ($topath) { JFile::write(JPATH_ROOT . '/' . $topath, $output); } else { return $output; } } // write to path return true; }
/** * 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; }