summaryrefslogtreecommitdiff
path: root/lib/exe/css.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/exe/css.php')
-rw-r--r--lib/exe/css.php242
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;
}