1
0
mirror of https://github.com/mrclay/minify.git synced 2025-08-12 17:14:24 +02:00

Minify_CSS : remove charset at-rules by default & options cleanup

Minify_HTML : speed patch (Issue 192)
Minify_Controller_MinApp : better error logging (Issue 193)
index.php : allow easier custom controller hacking
This commit is contained in:
Steve Clay
2010-09-30 04:31:58 +00:00
parent e9cd5d4b9b
commit c6a2f87641
9 changed files with 306 additions and 284 deletions

View File

@@ -14,14 +14,12 @@
* *
* If you want to use a custom error logger, set this to your logger * If you want to use a custom error logger, set this to your logger
* instance. Your object should have a method log(string $message). * instance. Your object should have a method log(string $message).
*
* @todo cache system does not have error logging yet.
*/ */
$min_errorLogger = false; $min_errorLogger = false;
/** /**
* To allow debugging, you must set this option to true. * To allow debug mode output, you must set this option to true.
* *
* Once true, you can send the cookie minDebug to request debug mode output. The * Once true, you can send the cookie minDebug to request debug mode output. The
* cookie value should match the URIs you'd like to debug. E.g. to debug * cookie value should match the URIs you'd like to debug. E.g. to debug

View File

@@ -54,10 +54,9 @@ if ($min_errorLogger) {
require_once 'Minify/Logger.php'; require_once 'Minify/Logger.php';
if (true === $min_errorLogger) { if (true === $min_errorLogger) {
require_once 'FirePHP.php'; require_once 'FirePHP.php';
Minify_Logger::setLogger(FirePHP::getInstance(true)); $min_errorLogger = FirePHP::getInstance(true);
} else {
Minify_Logger::setLogger($min_errorLogger);
} }
Minify_Logger::setLogger($min_errorLogger);
} }
// check for URI versioning // check for URI versioning
@@ -70,7 +69,12 @@ if (isset($_GET['g'])) {
} }
if (isset($_GET['f']) || isset($_GET['g'])) { if (isset($_GET['f']) || isset($_GET['g'])) {
// serve! // serve!
Minify::serve('MinApp', $min_serveOptions);
if (! isset($min_serveController)) {
require 'Minify/Controller/MinApp.php';
$min_serveController = new Minify_Controller_MinApp();
}
Minify::serve($min_serveController, $min_serveOptions);
} elseif ($min_enableBuilder) { } elseif ($min_enableBuilder) {
header('Location: builder/'); header('Location: builder/');

View File

@@ -26,6 +26,8 @@ class Minify_CSS {
* 'preserveComments': (default true) multi-line comments that begin * 'preserveComments': (default true) multi-line comments that begin
* with "/*!" will be preserved with newlines before and after to * with "/*!" will be preserved with newlines before and after to
* enhance readability. * enhance readability.
*
* 'removeCharsets': (default true) remove all @charset at-rules
* *
* 'prependRelativePath': (default null) if given, this string will be * 'prependRelativePath': (default null) if given, this string will be
* prepended to all relative URIs in import/url declarations * prepended to all relative URIs in import/url declarations
@@ -36,23 +38,37 @@ class Minify_CSS {
* the desired files. For this to work, the files *must* exist and be * the desired files. For this to work, the files *must* exist and be
* visible by the PHP process. * visible by the PHP process.
* *
* 'symlinks': (default = array()) If the CSS file is stored in * 'symlinks': (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to * a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because * target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute * paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.: * the doc root in the link paths (the array keys). E.g.:
* <code> * <code>
* array('//symlink' => '/real/target/path') // unix * array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows * array('//static' => 'D:\\staticStorage') // Windows
* </code> * </code>
*
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
* see Minify_CSS_UriRewriter::rewrite
* *
* @return string * @return string
*/ */
public static function minify($css, $options = array()) public static function minify($css, $options = array())
{ {
$options = array_merge(array(
'removeCharsets' => true,
'preserveComments' => true,
'currentDir' => null,
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
'prependRelativePath' => null,
'symlinks' => array(),
), $options);
if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
}
require_once 'Minify/CSS/Compressor.php'; require_once 'Minify/CSS/Compressor.php';
if (isset($options['preserveComments']) if (! $options['preserveComments']) {
&& !$options['preserveComments']) {
$css = Minify_CSS_Compressor::process($css, $options); $css = Minify_CSS_Compressor::process($css, $options);
} else { } else {
require_once 'Minify/CommentPreserver.php'; require_once 'Minify/CommentPreserver.php';
@@ -62,16 +78,16 @@ class Minify_CSS {
,array($options) ,array($options)
); );
} }
if (! isset($options['currentDir']) && ! isset($options['prependRelativePath'])) { if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css; return $css;
} }
require_once 'Minify/CSS/UriRewriter.php'; require_once 'Minify/CSS/UriRewriter.php';
if (isset($options['currentDir'])) { if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite( return Minify_CSS_UriRewriter::rewrite(
$css $css
,$options['currentDir'] ,$options['currentDir']
,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'] ,$options['docRoot']
,isset($options['symlinks']) ? $options['symlinks'] : array() ,$options['symlinks']
); );
} else { } else {
return Minify_CSS_UriRewriter::prepend( return Minify_CSS_UriRewriter::prepend(

View File

@@ -65,11 +65,11 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
if (0 === strpos($file, '//')) { if (0 === strpos($file, '//')) {
$file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1);
} }
$file = realpath($file); $realpath = realpath($file);
if ($file && is_file($file)) { if ($realpath && is_file($realpath)) {
$sources[] = $this->_getFileSource($file, $cOptions); $sources[] = $this->_getFileSource($realpath, $cOptions);
} else { } else {
$this->log("The path \"{$file}\" could not be found (or was not a file)"); $this->log("The path \"{$file}\" (realpath \"{$realpath}\") could not be found (or was not a file)");
return $options; return $options;
} }
} }
@@ -97,7 +97,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
// no "./" // no "./"
|| preg_match('/(?:^|[^\\.])\\.\\//', $_GET['f']) || preg_match('/(?:^|[^\\.])\\.\\//', $_GET['f'])
) { ) {
$this->log("GET param 'f' invalid (see MinApp.php line 63)"); $this->log("GET param 'f' was invalid");
return $options; return $options;
} }
$ext = ".{$m[1]}"; $ext = ".{$m[1]}";
@@ -109,7 +109,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
} }
$files = explode(',', $_GET['f']); $files = explode(',', $_GET['f']);
if ($files != array_unique($files)) { if ($files != array_unique($files)) {
$this->log("Duplicate files specified"); $this->log("Duplicate files were specified");
return $options; return $options;
} }
if (isset($_GET['b'])) { if (isset($_GET['b'])) {
@@ -120,7 +120,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
// valid base // valid base
$base = "/{$_GET['b']}/"; $base = "/{$_GET['b']}/";
} else { } else {
$this->log("GET param 'b' invalid (see MinApp.php line 84)"); $this->log("GET param 'b' was invalid");
return $options; return $options;
} }
} else { } else {
@@ -134,25 +134,26 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
foreach ($files as $file) { foreach ($files as $file) {
$uri = $base . $file; $uri = $base . $file;
$path = $_SERVER['DOCUMENT_ROOT'] . $uri; $path = $_SERVER['DOCUMENT_ROOT'] . $uri;
$file = realpath($path); $realpath = realpath($path);
if (false === $file || ! is_file($file)) { if (false === $realpath || ! is_file($realpath)) {
$this->log("The path \"{$path}\" (realpath \"{$realpath}\") could not be found (or was not a file)");
if (! $missingUri) { if (! $missingUri) {
$missingUri = $uri; $missingUri = $uri;
continue; continue;
} else { } else {
$this->log("At least two files missing: '$missingUri', '$uri'"); $this->log("More than one file was missing: '$missingUri', '$uri'");
return $options; return $options;
} }
} }
try { try {
parent::checkNotHidden($file); parent::checkNotHidden($realpath);
parent::checkAllowDirs($file, $allowDirs, $uri); parent::checkAllowDirs($realpath, $allowDirs, $uri);
} catch (Exception $e) { } catch (Exception $e) {
$this->log($e->getMessage()); $this->log($e->getMessage());
return $options; return $options;
} }
$sources[] = $this->_getFileSource($file, $cOptions); $sources[] = $this->_getFileSource($realpath, $cOptions);
$basenames[] = basename($file, $ext); $basenames[] = basename($realpath, $ext);
} }
if ($this->selectionId) { if ($this->selectionId) {
$this->selectionId .= '_f='; $this->selectionId .= '_f=';

View File

@@ -1,245 +1,240 @@
<?php <?php
/** /**
* Class Minify_HTML * Class Minify_HTML
* @package Minify * @package Minify
*/ */
/** /**
* Compress HTML * Compress HTML
* *
* This is a heavy regex-based removal of whitespace, unnecessary comments and * This is a heavy regex-based removal of whitespace, unnecessary comments and
* tokens. IE conditional comments are preserved. There are also options to have * tokens. IE conditional comments are preserved. There are also options to have
* STYLE and SCRIPT blocks compressed by callback functions. * STYLE and SCRIPT blocks compressed by callback functions.
* *
* A test suite is available. * A test suite is available.
* *
* @package Minify * @package Minify
* @author Stephen Clay <steve@mrclay.org> * @author Stephen Clay <steve@mrclay.org>
*/ */
class Minify_HTML { class Minify_HTML {
/** /**
* "Minify" an HTML page * "Minify" an HTML page
* *
* @param string $html * @param string $html
* *
* @param array $options * @param array $options
* *
* 'cssMinifier' : (optional) callback function to process content of STYLE * 'cssMinifier' : (optional) callback function to process content of STYLE
* elements. * elements.
* *
* 'jsMinifier' : (optional) callback function to process content of SCRIPT * 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored. * elements. Note: the type attribute is ignored.
* *
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype. * unset, minify will sniff for an XHTML doctype.
* *
* @return string * @return string
*/ */
public static function minify($html, $options = array()) { public static function minify($html, $options = array()) {
$min = new Minify_HTML($html, $options); $min = new Minify_HTML($html, $options);
return $min->process(); return $min->process();
} }
/** /**
* Create a minifier object * Create a minifier object
* *
* @param string $html * @param string $html
* *
* @param array $options * @param array $options
* *
* 'cssMinifier' : (optional) callback function to process content of STYLE * 'cssMinifier' : (optional) callback function to process content of STYLE
* elements. * elements.
* *
* 'jsMinifier' : (optional) callback function to process content of SCRIPT * 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored. * elements. Note: the type attribute is ignored.
* *
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype. * unset, minify will sniff for an XHTML doctype.
* *
* @return null * @return null
*/ */
public function __construct($html, $options = array()) public function __construct($html, $options = array())
{ {
$this->_html = str_replace("\r\n", "\n", trim($html)); $this->_html = str_replace("\r\n", "\n", trim($html));
if (isset($options['xhtml'])) { if (isset($options['xhtml'])) {
$this->_isXhtml = (bool)$options['xhtml']; $this->_isXhtml = (bool)$options['xhtml'];
} }
if (isset($options['cssMinifier'])) { if (isset($options['cssMinifier'])) {
$this->_cssMinifier = $options['cssMinifier']; $this->_cssMinifier = $options['cssMinifier'];
} }
if (isset($options['jsMinifier'])) { if (isset($options['jsMinifier'])) {
$this->_jsMinifier = $options['jsMinifier']; $this->_jsMinifier = $options['jsMinifier'];
} }
} }
/** /**
* Minify the markeup given in the constructor * Minify the markeup given in the constructor
* *
* @return string * @return string
*/ */
public function process() public function process()
{ {
if ($this->_isXhtml === null) { if ($this->_isXhtml === null) {
$this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML')); $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
} }
$this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
$this->_placeholders = array(); $this->_placeholders = array();
// replace SCRIPTs (and minify) with placeholders // replace SCRIPTs (and minify) with placeholders
$this->_html = preg_replace_callback( $this->_html = preg_replace_callback(
'/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
,array($this, '_removeScriptCB') ,array($this, '_removeScriptCB')
,$this->_html); ,$this->_html);
// replace STYLEs (and minify) with placeholders // replace STYLEs (and minify) with placeholders
$this->_html = preg_replace_callback( $this->_html = preg_replace_callback(
'/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i' '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
,array($this, '_removeStyleCB') ,array($this, '_removeStyleCB')
,$this->_html); ,$this->_html);
// remove HTML comments (not containing IE conditional comments). // remove HTML comments (not containing IE conditional comments).
$this->_html = preg_replace_callback( $this->_html = preg_replace_callback(
'/<!--([\\s\\S]*?)-->/' '/<!--([\\s\\S]*?)-->/'
,array($this, '_commentCB') ,array($this, '_commentCB')
,$this->_html); ,$this->_html);
// replace PREs with placeholders // replace PREs with placeholders
$this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i' $this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
,array($this, '_removePreCB') ,array($this, '_removePreCB')
,$this->_html); ,$this->_html);
// replace TEXTAREAs with placeholders // replace TEXTAREAs with placeholders
$this->_html = preg_replace_callback( $this->_html = preg_replace_callback(
'/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
,array($this, '_removeTextareaCB') ,array($this, '_removeTextareaCB')
,$this->_html); ,$this->_html);
// trim each line. // trim each line.
// @todo take into account attribute values that span multiple lines. // @todo take into account attribute values that span multiple lines.
$this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html); $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
// remove ws around block/undisplayed elements // remove ws around block/undisplayed elements
$this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
.'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
.'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
.'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
.'|ul)\\b[^>]*>)/i', '$1', $this->_html); .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
// remove ws outside of all elements // remove ws outside of all elements
$this->_html = preg_replace_callback( $this->_html = preg_replace(
'/>([^<]+)</' '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
,array($this, '_outsideTagCB') ,'>$1$2$3<'
,$this->_html); ,$this->_html);
// use newlines before 1st attribute in open tags (to limit line lengths) // use newlines before 1st attribute in open tags (to limit line lengths)
$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html); $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
// fill placeholders // fill placeholders
$this->_html = str_replace( $this->_html = str_replace(
array_keys($this->_placeholders) array_keys($this->_placeholders)
,array_values($this->_placeholders) ,array_values($this->_placeholders)
,$this->_html ,$this->_html
); );
return $this->_html; return $this->_html;
} }
protected function _commentCB($m) protected function _commentCB($m)
{ {
return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<![')) return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
? $m[0] ? $m[0]
: ''; : '';
} }
protected function _reservePlace($content) protected function _reservePlace($content)
{ {
$placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%'; $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
$this->_placeholders[$placeholder] = $content; $this->_placeholders[$placeholder] = $content;
return $placeholder; return $placeholder;
} }
protected $_isXhtml = null; protected $_isXhtml = null;
protected $_replacementHash = null; protected $_replacementHash = null;
protected $_placeholders = array(); protected $_placeholders = array();
protected $_cssMinifier = null; protected $_cssMinifier = null;
protected $_jsMinifier = null; protected $_jsMinifier = null;
protected function _outsideTagCB($m) protected function _removePreCB($m)
{ {
return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<'; return $this->_reservePlace("<pre{$m[1]}");
} }
protected function _removePreCB($m) protected function _removeTextareaCB($m)
{ {
return $this->_reservePlace($m[1]); return $this->_reservePlace("<textarea{$m[1]}");
} }
protected function _removeTextareaCB($m) protected function _removeStyleCB($m)
{ {
return $this->_reservePlace($m[1]); $openStyle = "<style{$m[1]}";
} $css = $m[2];
// remove HTML comments
protected function _removeStyleCB($m) $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
{
$openStyle = $m[1]; // remove CDATA section markers
$css = $m[2]; $css = $this->_removeCdata($css);
// remove HTML comments
$css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css); // minify
$minifier = $this->_cssMinifier
// remove CDATA section markers ? $this->_cssMinifier
$css = $this->_removeCdata($css); : 'trim';
$css = call_user_func($minifier, $css);
// minify
$minifier = $this->_cssMinifier return $this->_reservePlace($this->_needsCdata($css)
? $this->_cssMinifier ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
: 'trim'; : "{$openStyle}{$css}</style>"
$css = call_user_func($minifier, $css); );
}
return $this->_reservePlace($this->_needsCdata($css)
? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>" protected function _removeScriptCB($m)
: "{$openStyle}{$css}</style>" {
); $openScript = "<script{$m[2]}";
} $js = $m[3];
protected function _removeScriptCB($m) // whitespace surrounding? preserve at least one space
{ $ws1 = ($m[1] === '') ? '' : ' ';
$openScript = $m[2]; $ws2 = ($m[4] === '') ? '' : ' ';
$js = $m[3];
// remove HTML comments (and ending "//" if present)
// whitespace surrounding? preserve at least one space $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
$ws1 = ($m[1] === '') ? '' : ' ';
$ws2 = ($m[4] === '') ? '' : ' '; // remove CDATA section markers
$js = $this->_removeCdata($js);
// remove HTML comments (and ending "//" if present)
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js); // minify
$minifier = $this->_jsMinifier
// remove CDATA section markers ? $this->_jsMinifier
$js = $this->_removeCdata($js); : 'trim';
$js = call_user_func($minifier, $js);
// minify
$minifier = $this->_jsMinifier return $this->_reservePlace($this->_needsCdata($js)
? $this->_jsMinifier ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
: 'trim'; : "{$ws1}{$openScript}{$js}</script>{$ws2}"
$js = call_user_func($minifier, $js); );
}
return $this->_reservePlace($this->_needsCdata($js)
? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}" protected function _removeCdata($str)
: "{$ws1}{$openScript}{$js}</script>{$ws2}" {
); return (false !== strpos($str, '<![CDATA['))
} ? str_replace(array('<![CDATA[', ']]>'), '', $str)
: $str;
protected function _removeCdata($str) }
{
return (false !== strpos($str, '<![CDATA[')) protected function _needsCdata($str)
? str_replace(array('<![CDATA[', ']]>'), '', $str) {
: $str; return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
} }
}
protected function _needsCdata($str)
{
return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
}
}

View File

@@ -1,3 +1,5 @@
@charset "utf-8";
/* some CSS to try to exercise things in general */ /* some CSS to try to exercise things in general */
@import url( /more.css ); @import url( /more.css );

View File

@@ -20,10 +20,12 @@ rel="alternate"
type="application/rss+xml" type="application/rss+xml"
title="RSS" title="RSS"
href="http://www.csszengarden.com/zengarden.xml" /></head><body href="http://www.csszengarden.com/zengarden.xml" /></head><body
id="css-zen-garden"> <!--[if !IE]>--><p>Browser != IE</p><!--<![endif]--><div id="css-zen-garden">
<!--[if !IE]>--><p>Browser != IE</p><!--<![endif]--><div
id="container"><div id="container"><div
id="pageHeader"><h1><span>css Zen Garden</span></h1><h2><span>The Beauty of <acronym id="pageHeader"><h1><span>css Zen Garden</span></h1><h2><span>The Beauty of <acronym
title="Cascading Style Sheets">CSS</acronym> Design</span></h2></div><pre> title="Cascading Style Sheets">CSS</acronym>
Design</span></h2></div><pre>
White space is important here! White space is important here!
</pre><div </pre><div
id="quickSummary"><p id="quickSummary"><p

View File

@@ -20,10 +20,12 @@ rel="alternate"
type="application/rss+xml" type="application/rss+xml"
title="RSS" title="RSS"
href="http://www.csszengarden.com/zengarden.xml"></head><body href="http://www.csszengarden.com/zengarden.xml"></head><body
id="css-zen-garden"> <!--[if !IE]>--><p>Browser != IE</p><!--<![endif]--><div id="css-zen-garden">
<!--[if !IE]>--><p>Browser != IE</p><!--<![endif]--><div
id="container"><div id="container"><div
id="pageHeader"><h1><span>css Zen Garden</span></h1><h2><span>The Beauty of <acronym id="pageHeader"><h1><span>css Zen Garden</span></h1><h2><span>The Beauty of <acronym
title="Cascading Style Sheets">CSS</acronym> Design</span></h2></div><pre> title="Cascading Style Sheets">CSS</acronym>
Design</span></h2></div><pre>
White space is important here! White space is important here!
</pre><div </pre><div
id="quickSummary"><p id="quickSummary"><p

View File

@@ -1,4 +1,6 @@
@media screen { @media screen {
@charset "utf-8";
/* some CSS to try to exercise things in general */ /* some CSS to try to exercise things in general */
@import url(/more.css); @import url(/more.css);