diff options
Diffstat (limited to 'lib/exe/css.php')
-rw-r--r-- | lib/exe/css.php | 242 |
1 files changed, 170 insertions, 72 deletions
diff --git a/lib/exe/css.php b/lib/exe/css.php index 1e662c64a..6dfdf06e8 100644 --- a/lib/exe/css.php +++ b/lib/exe/css.php @@ -40,43 +40,32 @@ function css_out(){ $type = ''; } + // decide from where to get the template $tpl = trim(preg_replace('/[^\w-]+/','',$INPUT->str('t'))); - if($tpl){ - $tplinc = DOKU_INC.'lib/tpl/'.$tpl.'/'; - $tpldir = DOKU_BASE.'lib/tpl/'.$tpl.'/'; - }else{ - $tplinc = tpl_incdir(); - $tpldir = tpl_basedir(); - } - - // used style.ini file - $styleini = css_styleini($tplinc); + if(!$tpl) $tpl = $conf['template']; // The generated script depends on some dynamic options - $cache = new cache('styles'.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'].DOKU_BASE.$tplinc.$type,'.css'); + $cache = new cache('styles'.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'].DOKU_BASE.$tpl.$type,'.css'); - // load template styles - $tplstyles = array(); - if ($styleini) { - foreach($styleini['stylesheets'] as $file => $mode) { - $tplstyles[$mode][$tplinc.$file] = $tpldir; - } - } + // load styl.ini + $styleini = css_styleini($tpl); // if old 'default' userstyle setting exists, make it 'screen' userstyle for backwards compatibility if (isset($config_cascade['userstyle']['default'])) { $config_cascade['userstyle']['screen'] = $config_cascade['userstyle']['default']; } - // Array of needed files and their web locations, the latter ones - // are needed to fix relative paths in the stylesheets - $files = array(); - + // cache influencers + $tplinc = tpl_basedir($tpl); $cache_files = getConfigFiles('main'); $cache_files[] = $tplinc.'style.ini'; - $cache_files[] = $tplinc.'style.local.ini'; + $cache_files[] = $tplinc.'style.local.ini'; // @deprecated + $cache_files[] = DOKU_CONF."tpl/$tpl/style.ini"; $cache_files[] = __FILE__; + // Array of needed files and their web locations, the latter ones + // are needed to fix relative paths in the stylesheets + $files = array(); foreach($mediatypes as $mediatype) { $files[$mediatype] = array(); // load core styles @@ -88,8 +77,8 @@ function css_out(){ // load plugin styles $files[$mediatype] = array_merge($files[$mediatype], css_pluginstyles($mediatype)); // load template styles - if (isset($tplstyles[$mediatype])) { - $files[$mediatype] = array_merge($files[$mediatype], $tplstyles[$mediatype]); + if (isset($styleini['stylesheets'][$mediatype])) { + $files[$mediatype] = array_merge($files[$mediatype], $styleini['stylesheets'][$mediatype]); } // load user styles if(isset($config_cascade['userstyle'][$mediatype])){ @@ -101,7 +90,7 @@ function css_out(){ // please use "[dir=rtl]" in any css file in all, screen or print mode instead if ($mediatype=='screen') { if($lang['direction'] == 'rtl'){ - if (isset($tplstyles['rtl'])) $files[$mediatype] = array_merge($files[$mediatype], $tplstyles['rtl']); + if (isset($styleini['stylesheets']['rtl'])) $files[$mediatype] = array_merge($files[$mediatype], $styleini['stylesheets']['rtl']); if (isset($config_cascade['userstyle']['rtl'])) $files[$mediatype][$config_cascade['userstyle']['rtl']] = DOKU_BASE; } } @@ -131,6 +120,8 @@ function css_out(){ // load files $css_content = ''; foreach($files[$mediatype] as $file => $location){ + $display = str_replace(fullpath(DOKU_INC), '', fullpath($file)); + $css_content .= "\n/* XXXXXXXXX $display XXXXXXXXX */\n"; $css_content .= css_loadfile($file, $location); } switch ($mediatype) { @@ -152,10 +143,10 @@ function css_out(){ ob_end_clean(); // apply style replacements - $css = css_applystyle($css,$tplinc); + $css = css_applystyle($css, $styleini['replacements']); - // place all @import statements at the top of the file - $css = css_moveimports($css); + // parse less + $css = css_parseless($css); // compress whitespace and comments if($conf['compress']){ @@ -172,40 +163,157 @@ function css_out(){ } /** + * Uses phpless to parse LESS in our CSS + * + * most of this function is error handling to show a nice useful error when + * LESS compilation fails + * + * @param $css + * @return string + */ +function css_parseless($css) { + $less = new lessc(); + try { + return $less->compile($css); + } catch(Exception $e) { + // get exception message + $msg = str_replace(array("\n", "\r", "'"), array(), $e->getMessage()); + + // try to use line number to find affected file + if(preg_match('/line: (\d+)$/', $msg, $m)){ + $msg = substr($msg, 0, -1* strlen($m[0])); //remove useless linenumber + $lno = $m[1]; + + // walk upwards to last include + $lines = explode("\n", $css); + for($i=$lno-1; $i>=0; $i--){ + if(preg_match('/\/(\* XXXXXXXXX )(.*?)( XXXXXXXXX \*)\//', $lines[$i], $m)){ + // we found it, add info to message + $msg .= ' in '.$m[2].' at line '.($lno-$i); + break; + } + } + } + + // something went wrong + $error = 'A fatal error occured during compilation of the CSS files. '. + 'If you recently installed a new plugin or template it '. + 'might be broken and you should try disabling it again. ['.$msg.']'; + + echo ".dokuwiki:before { + content: '$error'; + background-color: red; + display: block; + background-color: #fcc; + border-color: #ebb; + color: #000; + padding: 0.5em; + }"; + + exit; + } +} + +/** * Does placeholder replacements in the style according to * the ones defined in a templates style.ini file * + * This also adds the ini defined placeholders as less variables + * (sans the surrounding __ and with a ini_ prefix) + * * @author Andreas Gohr <andi@splitbrain.org> */ -function css_applystyle($css,$tplinc){ - $styleini = css_styleini($tplinc); - - if($styleini){ - $css = strtr($css,$styleini['replacements']); +function css_applystyle($css, $replacements) { + // we convert ini replacements to LESS variable names + // and build a list of variable: value; pairs + $less = ''; + foreach((array) $replacements as $key => $value) { + $lkey = trim($key, '_'); + $lkey = '@ini_'.$lkey; + $less .= "$lkey: $value;\n"; + + $replacements[$key] = $lkey; } + + // we now replace all old ini replacements with LESS variables + $css = strtr($css, $replacements); + + // now prepend the list of LESS variables as the very first thing + $css = $less.$css; return $css; } /** - * Get contents of merged style.ini and style.local.ini as an array. + * Load style ini contents + * + * Loads and merges style.ini files from template and config and prepares + * the stylesheet modes * - * @author Anika Henke <anika@selfthinker.org> + * @author Andreas Gohr <andi@splitbrain.org> + * @param string $tpl the used template + * @return array with keys 'stylesheets' and 'replacements' */ -function css_styleini($tplinc) { - $styleini = array(); +function css_styleini($tpl) { + $stylesheets = array(); // mode, file => base + $replacements = array(); // placeholder => value + + // load template's style.ini + $incbase = tpl_incdir($tpl); + $webbase = tpl_basedir($tpl); + $ini = $incbase.'style.ini'; + if(file_exists($ini)){ + $data = parse_ini_file($ini, true); + + // stylesheets + if(is_array($data['stylesheets'])) foreach($data['stylesheets'] as $file => $mode){ + $stylesheets[$mode][$incbase.$file] = $webbase; + } - foreach (array($tplinc.'style.ini', $tplinc.'style.local.ini') as $ini) { - $tmp = (@file_exists($ini)) ? parse_ini_file($ini, true) : array(); + // replacements + if(is_array($data['replacements'])){ + $replacements = array_merge($replacements, $data['replacements']); + } + } - foreach($tmp as $key => $value) { - if(array_key_exists($key, $styleini) && is_array($value)) { - $styleini[$key] = array_merge($styleini[$key], $tmp[$key]); - } else { - $styleini[$key] = $value; - } + // load template's style.local.ini + // @deprecated 2013-08-03 + $ini = $incbase.'style.local.ini'; + if(file_exists($ini)){ + $data = parse_ini_file($ini, true); + + // stylesheets + if(is_array($data['stylesheets'])) foreach($data['stylesheets'] as $file => $mode){ + $stylesheets[$mode][$incbase.$file] = $webbase; + } + + // replacements + if(is_array($data['replacements'])){ + $replacements = array_merge($replacements, $data['replacements']); + } + } + + // load configs's style.ini + $webbase = DOKU_BASE; + $ini = DOKU_CONF."tpl/$tpl/style.ini"; + $incbase = dirname($ini).'/'; + if(file_exists($ini)){ + $data = parse_ini_file($ini, true); + + // stylesheets + if(is_array($data['stylesheets'])) foreach($data['stylesheets'] as $file => $mode){ + $stylesheets[$mode][$incbase.$file] = $webbase; + } + + // replacements + if(is_array($data['replacements'])){ + $replacements = array_merge($replacements, $data['replacements']); } } - return $styleini; + + return array( + 'stylesheets' => $stylesheets, + 'replacements' => $replacements + ); } /** @@ -314,7 +422,7 @@ function css_datauri($match){ $data = base64_encode(file_get_contents($local)); } if($data){ - $url = 'data:image/'.$ext.';base64,'.$data; + $url = '\'data:image/'.$ext.';base64,'.$data.'\''; }else{ $url = $base.$url; } @@ -333,9 +441,11 @@ function css_pluginstyles($mediatype='screen'){ $plugins = plugin_list(); foreach ($plugins as $p){ $list[DOKU_PLUGIN."$p/$mediatype.css"] = DOKU_BASE."lib/plugins/$p/"; + $list[DOKU_PLUGIN."$p/$mediatype.less"] = DOKU_BASE."lib/plugins/$p/"; // alternative for screen.css if ($mediatype=='screen') { $list[DOKU_PLUGIN."$p/style.css"] = DOKU_BASE."lib/plugins/$p/"; + $list[DOKU_PLUGIN."$p/style.less"] = DOKU_BASE."lib/plugins/$p/"; } // @deprecated 2012-04-09: rtl will cease to be a mode of its own, // please use "[dir=rtl]" in any css file in all, screen or print mode instead @@ -347,29 +457,6 @@ function css_pluginstyles($mediatype='screen'){ } /** - * Move all @import statements in a combined stylesheet to the top so they - * aren't ignored by the browser. - * - * @author Gabriel Birke <birke@d-scribe.de> - */ -function css_moveimports($css) -{ - if(!preg_match_all('/@import\s+(?:url\([^)]+\)|"[^"]+")\s*[^;]*;\s*/', $css, $matches, PREG_OFFSET_CAPTURE)) { - return $css; - } - $newCss = ""; - $imports = ""; - $offset = 0; - foreach($matches[0] as $match) { - $newCss .= substr($css, $offset, $match[1] - $offset); - $imports .= $match[0]; - $offset = $match[1] + strlen($match[0]); - } - $newCss .= substr($css, $offset); - return $imports.$newCss; -} - -/** * Very simple CSS optimizer * * @author Andreas Gohr <andi@splitbrain.org> @@ -386,8 +473,19 @@ function css_compress($css){ $css = preg_replace('/ ?([;,{}\/]) ?/','\\1',$css); $css = preg_replace('/ ?: /',':',$css); + // number compression + $css = preg_replace('/([: ])0+(\.\d+?)0*((?:pt|pc|in|mm|cm|em|ex|px)\b|%)(?=[^\{]*[;\}])/', '$1$2$3', $css); // "0.1em" to ".1em", "1.10em" to "1.1em" + $css = preg_replace('/([: ])\.(0)+((?:pt|pc|in|mm|cm|em|ex|px)\b|%)(?=[^\{]*[;\}])/', '$1$2', $css); // ".0em" to "0" + $css = preg_replace('/([: ]0)0*(\.0*)?((?:pt|pc|in|mm|cm|em|ex|px)(?=[^\{]*[;\}])\b|%)/', '$1', $css); // "0.0em" to "0" + $css = preg_replace('/([: ]\d+)(\.0*)((?:pt|pc|in|mm|cm|em|ex|px)(?=[^\{]*[;\}])\b|%)/', '$1$3', $css); // "1.0em" to "1em" + $css = preg_replace('/([: ])0+(\d+|\d*\.\d+)((?:pt|pc|in|mm|cm|em|ex|px)(?=[^\{]*[;\}])\b|%)/', '$1$2$3', $css); // "001em" to "1em" + + // shorten attributes (1em 1em 1em 1em -> 1em) + $css = preg_replace('/(?<![\w\-])((?:margin|padding|border|border-(?:width|radius)):)([\w\.]+)( \2)+(?=[;\}]| !)/', '$1$2', $css); // "1em 1em 1em 1em" to "1em" + $css = preg_replace('/(?<![\w\-])((?:margin|padding|border|border-(?:width)):)([\w\.]+) ([\w\.]+) \2 \3(?=[;\}]| !)/', '$1$2 $3', $css); // "1em 2em 1em 2em" to "1em 2em" + // shorten colors - $css = preg_replace("/#([0-9a-fA-F]{1})\\1([0-9a-fA-F]{1})\\2([0-9a-fA-F]{1})\\3/", "#\\1\\2\\3",$css); + $css = preg_replace("/#([0-9a-fA-F]{1})\\1([0-9a-fA-F]{1})\\2([0-9a-fA-F]{1})\\3(?=[^\{]*[;\}])/", "#\\1\\2\\3", $css); return $css; } |