Commit 56610a1c by Crisu83

updated the less extension

parent d8948072
......@@ -4,114 +4,102 @@
* @author Christoffer Niska <ChristofferNiska@gmail.com>
* @copyright Copyright &copy; Christoffer Niska 2011-
* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
* @version 0.10.0
*/
Yii::setPathOfAlias('Less', dirname(__FILE__).'/../lib/lessphp/lib/Less');
require(dirname(__FILE__).'/../lib/lessphp/lessc.inc.php');
/**
* Less compiler application component.
* Preload the component to enable auto compiling.
*/
class LessCompiler extends CApplicationComponent
{
/**
* @var string the base path.
* @var string base path.
*/
public $basePath;
/**
* @var array the paths for the files to parse.
* @var array paths to process.
*/
public $paths = array();
/**
* @var boolean indicates whether to force compiling.
* @var boolean indicates whether to always compile all files.
*/
public $forceCompile = false;
/**
* @var boolean compiler debug mode.
* @var lessc LESS compiler instance.
*/
public $debug = false;
/**
* @var boolean whether to compress css or not.
*/
public $compress = false;
protected $_parser;
/**
* Initializes the component.
* @throws CException if the base path does not exist.
*/
public function init()
{
if ($this->basePath === null)
if (!isset($this->basePath))
$this->basePath = Yii::getPathOfAlias('webroot');
if (!file_exists($this->basePath))
throw new CException(__CLASS__.': Failed to initialize compiler. Base path does not exist.');
throw new CException(__CLASS__.': Failed to initialize compiler. Base path does not exist!');
$env = new \Less\Environment();
$env->setDebug($this->debug);
$env->setCompress($this->compress);
$this->_parser = new \Less\Parser($env);
if (!is_dir($this->basePath))
throw new CException(__CLASS__.': Failed to initialize compiler. Base path is not a directory!');
$this->_parser = new lessc();
if ($this->forceCompile || $this->hasChanges())
$this->compile();
$this->compileAll();
}
/**
* Compiles the less files.
* @throws CException if the source path does not exist
* Compiles a LESS file to a CSS file.
* @param string $from the path to the source LESS file.
* @param string $to the path to the target CSS file.
* @throws CException if the compilation fails or the source path does not exist.
*/
public function compile()
public function compile($from, $to)
{
foreach ($this->paths as $lessPath => $cssPath)
if (file_exists($from))
{
$fromPath = $this->basePath.'/'.$lessPath;
$toPath = $this->basePath.'/'.$cssPath;
if (file_exists($fromPath))
file_put_contents($toPath, $this->parse($fromPath));
else
throw new CException(__CLASS__.': Failed to compile less file. Source path does not exist.');
$this->_parser->clearCss();
try
{
$this->_parser->importDir = dirname($from); // Set the correct context.
file_put_contents($to, $this->_parser->parse(file_get_contents($from)));
}
catch (exception $e)
{
throw new CException(__CLASS__.': Failed to compile less file with message: '.$e->getMessage().'.');
}
}
else
throw new CException(__CLASS__.': Failed to compile less file. Source path does not exist!');
}
/**
* Parses the less code to CSS.
* @param string $filename the file path to the less file
* @return string the CSS
* Compiles all LESS files.
*/
public function parse($filename)
protected function compileAll()
{
try
{
$css = $this->_parser->parseFile($filename);
}
catch (\Less\Exception\ParserException $e)
foreach ($this->paths as $lessPath => $cssPath)
{
throw new CException(__CLASS__.': Failed to parse less file. "'.$e->getMessage().'".');
$from = $this->basePath.'/'.$lessPath;
$to = $this->basePath.'/'.$cssPath;
$this->compile($from, $to);
}
return $css;
}
/**
* Returns whether any of files configured to be compiled has changed.
* @return boolean the result
* @return boolean the result.
*/
protected function hasChanges()
{
$dirs = array();
foreach ($this->paths as $source => $destination)
{
$destination = realpath($destination);
$compiled = $this->getLastModified($destination);
if (!isset($lastCompiled) || $compiled < $lastCompiled )
if (!isset($lastCompiled) || $compiled < $lastCompiled)
$lastCompiled = $compiled;
$source = realpath($source);
if (!in_array($source, $dirs))
if (!in_array(dirname($source), $dirs))
$dirs[] = $source;
}
......@@ -126,8 +114,8 @@ class LessCompiler extends CApplicationComponent
/**
* Returns the last modified for a specific path.
* @param string $path the path
* @return integer the last modified (as a timestamp)
* @param string $path the path.
* @return integer the last modified (as a timestamp).
*/
protected function getLastModified($path)
{
......@@ -144,23 +132,24 @@ class LessCompiler extends CApplicationComponent
{
$lastModified = null;
/** @var Directory $dir */
$dir = dir($path);
while ($entry = $dir->read())
{
if (strpos($entry, '.') === 0)
continue;
$entry = $path.'/'.$entry;
$path .= '/'.$entry;
if( is_dir($entry) )
$modified = $this->getLastModified($entry);
if (is_dir($path))
$modified = $this->getLastModified($path);
else
{
$stat = stat($entry);
$stat = stat($path);
$modified = $stat['mtime'];
}
if( isset($lastModified) || $modified > $lastModified )
if (isset($lastModified) || $modified > $lastModified)
$lastModified = $modified;
}
......
less.php
========
# lessphp v0.3.4-2
### <http://leafo.net/lessphp>
The **dynamic** stylesheet language.
`lessphp` is a compiler for LESS written in PHP. The documentation is great,
so check it out: <http://leafo.net/lessphp/docs/>.
<http://lesscss.org>
Here's a quick tutorial:
about
-----
### How to use in your PHP project
This is a PHP port of the official JavaScript version of LESS <http://lesscss.org>.
Copy `lessc.inc.php` to your include directory and include it into your project.
Most of the code structure remains the same, which should allow for fairly easy updates in the future. That does
mean this library requires PHP5.3 as it makes heavy use of namespaces, anonymous functions and the shorthand ternary
operator - `?:` (to replicate the way Javascript will return the value of the first valid operand when using `||`).
There are a few ways to interface with the compiler. The easiest is to have it
compile a LESS file when the page is requested. The static function
`lessc::ccompile`, checked compile, will compile the input LESS file only when it
is newer than the output file.
A couple of things have been omitted from this initial version:
try {
lessc::ccompile('input.less', 'output.css');
} catch (exception $ex) {
exit($ex->getMessage());
}
- Evaluation of JavaScript expressions within back-ticks (for obvious reasons).
- Definition of custom functions - will be added to the `\Less\Environment` class.
- A tidy up of the API is needed.
`lessc::ccompile` is not aware of imported files that change. Read [about
`lessc::cexecute`](http://leafo.net/lessphp/docs/#compiling_automatically).
use
---
Note that all failures with lessc are reported through exceptions.
If you need more control you can make your own instance of lessc.
### The parser
$input = 'mystyle.less';
```php
<?php
$lc = new lessc($input);
$parser = new \Less\Parser();
$parser->getEnvironment()->setCompress(true);
try {
file_put_contents('mystyle.css', $lc->parse());
} catch (exception $ex) { ... }
// parse css from a less source file or directly from a string
$css = $parser
->parseFile($path)
->parse("@color: #4D926F; #header { color: @color; } h2 { color: @color; }")
->getCss();
```
In addition to loading from file, you can also parse from a string like so:
### The command line tool
$lc = new lessc();
$lesscode = 'body { ... }';
$out = $lc->parse($lesscode);
The `bin/lessc` command line tool will accept an input (and optionally an output) file name to process.
### How to use from the command line
```bash
$ ./bin/lessc input.less output.css
```
An additional script has been included to use the compiler from the command
line. In the simplest invocation, you specify an input file and the compiled
css is written to standard out:
### In your website
$ plessc input.less > output.css
The `bin/less.php` file can be moved to the directory containing your less source files. Including a links as follows
will compile and cache the css.
Using the -r flag, you can specify LESS code directly as an argument or, if
the argument is left off, from standard in:
```html
<link rel="stylesheet" type="text/css" href="/static/less/css.php?bootstrap.less" />
```
$ plessc -r "my less code here"
NB: You'll need to update this file to point to the `lib` directory, and also make sure the `./cache` directory is
writable by the web server.
Finally, by using the -w flag you can watch a specified input file and have it
compile as needed to the output file
$ plessc -w input-file output-file
Errors from watch mode are written to standard out.
license
-------
See `LICENSE` file.
#!/usr/bin/php -q
<?php
//
// command line utility to compile less to stdout
// leaf corcoran <leafo.net>
error_reporting(E_ALL);
$path = realpath(dirname(__FILE__)).'/';
require $path."lessc.inc.php";
$VERSION = lessc::$VERSION;
$fa = "Fatal Error: ";
function err($msg) {
fwrite(STDERR, $msg."\n");
}
if (php_sapi_name() != "cli") {
err($fa.$argv[0]." must be run in the command line.");
exit(1);
}
$exe = array_shift($argv); // remove filename
function process($data, $import = null) {
global $fa;
$l = new lessc();
if ($import) $l->importDir = $import;
try {
echo $l->parse($data);
exit(0);
} catch (exception $ex) {
err($fa."\n".str_repeat('=', 20)."\n".
$ex->getMessage());
exit(1);
}
}
// process args
$opts = array();
foreach ($argv as $loc => $a) {
if (preg_match("/^-([a-zA-Z]+)$/", $a, $m)) {
$m = $m[1];
for ($i = 0; $i < strlen($m); $i++)
$opts[$m{$i}] = $loc;
unset($argv[$loc]);
}
}
function has($o, &$loc = null) {
global $opts;
if (!isset($opts[$o])) return false;
$loc = $opts[$o];
return true;
}
function hasValue($o, &$value = null) {
global $argv;
if (!has($o,$loc)) return false;
if (!isset($argv[$loc+1])) return false;
$value = $argv[$loc+1];
return true;
}
if (has("v")) {
exit($VERSION."\n");
}
if (has("r", $loc)) {
if (!hasValue("r", $data)) {
while (!feof(STDIN)) {
$data .= fread(STDIN, 8192);
}
}
return process($data);
}
if (has("w")) {
// need two files
if (!is_file($in = array_shift($argv)) ||
null == $out = array_shift($argv))
{
err($fa.$exe." -w infile outfile");
exit(1);
}
echo "Watching ".$in.
(has("n") ? ' with notifications' : '').
", press Ctrl + c to exit.\n";
$cache = $in;
$last_action = 0;
while (1) {
clearstatcache();
// check if anything has changed since last fail
$updated = false;
if (is_array($cache)) {
foreach ($cache['files'] as $fname=>$_) {
if (filemtime($fname) > $last_action) {
$updated = true;
break;
}
}
} else $updated = true;
// try to compile it
if ($updated) {
$last_action = time();
try {
$cache = lessc::cexecute($cache);
echo "Writing updated file: ".$out."\n";
if (!file_put_contents($out, $cache['compiled'])) {
err($fa."Could not write to file ".$out);
exit(1);
}
} catch (exception $ex) {
echo "\nFatal Error:\n".str_repeat('=', 20)."\n".$ex->getMessage()."\n\n";
if (has("n")) {
`notify-send -u critical "compile failed" "{$ex->getMessage()}"`;
}
}
}
sleep(1);
}
exit(0);
}
if (!$fname = array_shift($argv)) {
echo "Usage: ".$exe." input-file [output-file]\n";
exit(1);
}
function dumpValue($node, $depth = 0) {
if (is_object($node)) {
$indent = str_repeat(" ", $depth);
$out = array();
foreach ($node->props as $prop) {
$out[] = $indent . dumpValue($prop, $depth + 1);
}
$out = implode("\n", $out);
if (!empty($node->tags)) {
$out = "+ ".implode(", ", $node->tags)."\n".$out;
}
return $out;
} elseif (is_array($node)) {
if (empty($node)) return "[]";
$type = $node[0];
if ($type == "block")
return dumpValue($node[1], $depth);
$out = array();
foreach ($node as $value) {
$out[] = dumpValue($value, $depth);
}
return "{ ".implode(", ", $out)." }";
} else {
if (is_string($node) && preg_match("/[\s,]/", $node)) {
return '"'.$node.'"';
}
return $node; // normal value
}
}
try {
$l = new lessc($fname);
if (has("T") || has("X")) {
$t = $l->parseTree();
if (has("X"))
$out = print_r($t, 1);
else
$out = dumpValue($t)."\n";
} else {
$out = $l->parse();
}
if (!$fout = array_shift($argv)) {
echo $out;
} else {
file_put_contents($fout, $out);
}
} catch (exception $ex) {
err($fa.$ex->getMessage());
exit(1);
}
?>
## test.php
To run:
php test.php [flags] [test-name-glob]
Runs through all files in `inputs`, compiles them, then compares to respective
file in `outputs`. If there are any differences then the test will fail.
Add the `-d` flag to show the differences of failed tests. Defaults to showing
differences with `diff` but you can set the tool by doing `-d=toolname`.
Pass the `-C` flag to save the output of the inputs to the appropriate file. This
will overwrite any existing outputs. Use this when you want to save verified
test results. Combine with a *test-name-glob* to selectively compile.
You can also run specific tests by passing in an argument that contains any
part of the test name.
## bootstrap.sh
It's a syntetic test comparing lessc and lessphp output compiling twitter bootstrap;
see bootstrap.sh for details.
\ No newline at end of file
echo "This script clones twitter bootsrap, compiles it with lessc and lessphp,"
echo "cleans up results with csstidy, and outputs diff. To run it, you need to"
echo "have git, csstidy and lessc installed."
echo ""
csstidy_params="--allow_html_in_templates=false --compress_colors=false
--compress_font-weight=false --discard_invalid_properties=false
--lowercase_s=false --preserve_css=true --remove_bslash=false
--remove_last_;=false --sort-properties=true --sort-selectors=true
--timestamp=false --silent=true --merge_selectors=0 --case-properties=0
--optimize-shorthands=0 --template=high"
if [ -z "$@" ]; then
diff_tool="diff -b -u -t -B"
else
diff_tool=$@
fi
mkdir -p tmp
if [ ! -d 'bootstrap/' ]; then
echo ">> Cloning bootstrap to bootstrap/"
git clone https://github.com/twitter/bootstrap
fi
echo ">> Lessc compilation"
lessc bootstrap/less/bootstrap.less tmp/bootstrap.lessc.css
echo ">> Lessphp compilation"
../plessc bootstrap/less/bootstrap.less tmp/bootstrap.lessphp.css
echo ">> Cleanup and convert"
# csstidy tmp/bootstrap.lessc.css $csstidy_params tmp/bootstrap.lessc.clean.css
# csstidy tmp/bootstrap.lessphp.css $csstidy_params tmp/bootstrap.lessphp.clean.css
#
# # put a newline after { and :
# function split() {
# sed 's/\(;\|{\)/\1\n/g'
# }
#
# # csstidy is messed up and wont output to stdout when there are a bunch of options
# cat tmp/bootstrap.lessc.clean.css | split | tee tmp/bootstrap.lessc.clean.css
# cat tmp/bootstrap.lessphp.clean.css | split | tee tmp/bootstrap.lessphp.clean.css
php sort.php tmp/bootstrap.lessc.css > tmp/bootstrap.lessc.clean.css
php sort.php tmp/bootstrap.lessphp.css > tmp/bootstrap.lessphp.clean.css
echo ">> Doing diff"
$diff_tool tmp/bootstrap.lessc.clean.css tmp/bootstrap.lessphp.clean.css
/* accessors */
#defaults {
@width: 960px;
@color: black;
.something {
@space: 10px;
@hello {
color: green;
}
}
}
.article { color: #294366; }
.comment {
width: #defaults[@width];
color: .article['color'];
padding: #defaults > .something[@space];
}
.wow {
height: .comment['width'];
background-color: .comment['color'];
color: #defaults > .something > @hello['color'];
padding: #defaults > non-existant['padding'];
margin: #defaults > .something['non-existant'];
}
.mix {
#defaults;
font-size: .something[@space];
}
// simple arity
.hello(@a) {
color: one;
}
.hello(@a, @b) {
color: two;
}
.hello(@a, @b, @c) {
color: three;
}
.world(@a, @b, @c) {
color: three;
}
.world(@a, @b) {
color: two;
}
.world(@a) {
color: one;
}
.one {
.hello(1);
.world(1);
}
.two {
.hello(1, 1);
.world(1, 1);
}
.three {
.hello(1, 1, 1);
.world(1, 1, 1);
}
// arity with default values
.foo(@a, @b: cool) {
color: two;
}
.foo(@a, @b: cool, @c: yeah) {
color: three;
}
.baz(@a, @b, @c: yeah) {
color: three;
}
.baz(@a, @b: cool) {
color: two;
}
.multi-foo {
.foo(1);
.foo(1, 1);
.foo(1,1,1);
}
.multi-baz {
.baz(1);
.baz(1, 1);
.baz(1,1,1);
}
* { color: blue; }
E { color: blue; }
E[foo] { color: blue; }
[foo] { color: blue; }
[foo] .helloWorld { color: blue; }
[foo].helloWorld { color: blue; }
E[foo="barbar"] { color: blue; }
E[foo~="hello#$@%@$#^"] { color: blue; }
E[foo^="color: green;"] { color: blue; }
E[foo$="239023"] { color: blue; }
E[foo*="29302"] { color: blue; }
E[foo|="239032"] { color: blue; }
E:root { color: blue; }
E:nth-child(odd) { color: blue; }
E:nth-child(2n+1) { color: blue; }
E:nth-child(5) { color: blue; }
E:nth-last-child(-n+2) { color: blue; }
E:nth-of-type(2n) { color: blue; }
E:nth-last-of-type(n) { color: blue; }
E:first-child { color: blue; }
E:last-child { color: blue; }
E:first-of-type { color: blue; }
E:last-of-type { color: blue; }
E:only-child { color: blue; }
E:only-of-type { color: blue; }
E:empty { color: blue; }
E:lang(en) { color: blue; }
E::first-line { color: blue; }
E::before { color: blue; }
E#id { color: blue; }
E:not(:link) { color: blue; }
E F { color: blue; }
E > F { color: blue; }
E + F { color: blue; }
E ~ F { color: blue; }
// builtin
@something: "hello world";
@color: #112233;
@color2: rgba(44,55,66, .6);
body {
color: @something;
@num: 7 / 6;
height: @num + 4;
height: floor(@num) + 4px;
height: ceil(@num) + 4px;
@num2: 2 / 3;
width: @num2;
width: round(@num2);
width: floor(@num2);
width: ceil(@num2);
width: round(10px / 3);
color: rgbahex(@color);
color: rgbahex(@color2);
}
format {
@r: 32;
format: %("rgb(%d, %d, %d)", @r, 128, 64);
format-string: %("hello %s", "world");
format-multiple: %("hello %s %d", "earth", 2);
format-url-encode: %('red is %A', #ff0000);
eformat: e(%("rgb(%d, %d, %d)", @r, 128, 64));
}
body {
color: hsl(34, 50%, 40%);
color: hsla(34, 50%, 40%, 0.3);
lighten: lighten(#efefef, 10%);
lighten: lighten(rgb(23, 53, 231), 22%);
lighten: lighten(rgba(212, 103, 88, 0.5), 10%);
darken: darken(#efefef, 10%);
darken: darken(rgb(23, 53, 231), 22%);
darken: darken(rgba(23, 53, 231, 0.5), 10%);
saturate: saturate(#efefef, 10%);
saturate: saturate(rgb(23, 53, 231), 22%);
saturate: saturate(rgba(23, 53, 231, 0.5), 10%);
desaturate: desaturate(#efefef, 10%);
desaturate: desaturate(rgb(23, 53, 231), 22%);
desaturate: desaturate(rgba(23, 53, 231, 0.5), 10%);
spin: spin(#efefef, 12);
spin: spin(rgb(23, 53, 231), 15);
spin: spin(rgba(23, 53, 231, 0.5), 19);
spin: spin(#efefef, -12);
spin: spin(rgb(23, 53, 231), -15);
spin: spin(rgba(23, 53, 231, 0.5), -19);
one: fadein(#abcdef, 10%);
one: fadeout(#abcdef, -10%);
two: fadeout(#029f23, 10%);
two: fadein(#029f23, -10%);
three: fadein(rgba(1,2,3, 0.5), 10%);
three: fadeout(rgba(1,2,3, 0.5), -10%);
four: fadeout(rgba(1,2,3, 0), 10%);
four: fadein(rgba(1,2,3, 0), -10%);
hue: hue(rgb(34,20,40));
sat: saturation(rgb(34,20,40));
lit: lightness(rgb(34,20,40));
@old: #34fa03;
@new: hsl(hue(@old), 45%, 90%);
what: @new;
zero: saturate(#123456, -100%);
zero: saturate(#123456, 100%);
zero: saturate(#000000, 100%);
zero: saturate(#ffffff, 100%);
zero: lighten(#123456, -100%);
zero: lighten(#123456, 100%);
zero: lighten(#000000, 100%);
zero: lighten(#ffffff, 100%);
zero: spin(#123456, -100);
zero: spin(#123456, 100);
zero: spin(#000000, 100);
zero: spin(#ffffff, 100);
}
alpha {
// g: alpha(red);
g: alpha(rgba(0,0,0,0));
g: alpha(rgb(155,55,0));
}
fade {
f: fade(red, 50%);
f: fade(#fff, 20%);
f: fade(rgba(34,23,64,0.4), 50%);
}
@a: rgb(255,255,255);
@b: rgb(0,0,0);
.mix {
color: mix(@a, @b, 50%);
}
.percent {
per: percentage(0.5);
}
// color keywords
.colorz {
color: whitesmoke - 10;
color: spin(red, 34);
}
// purposfuly whacky to match less.js
@color: #fcf8e3;
body {
start: @color;
spin: spin(@color, -10); // #fcf4e3
chained: darken(spin(@color, -10), 3%); // gives #fbeed5, should be #fbefd5
direct: darken(#fcf4e3, 3%); // #fbefd5
}
// spin around
pre {
@errorBackground: #f2dede;
spin: spin(@errorBackground, -10);
}
dd {
@white: #fff;
background-color: mix(@white, darken(@white, 10%), 60%);
}
@mixin {
height: 22px;
ul {
height: 20px;
li {
@color: red;
height: 10px;
div span, link {
margin: 10px;
color: @color;
}
}
div, p {
border: 1px;
&.hello {
color: green;
}
:what {
color: blue;
}
}
a {
b {
color: blue;
}
}
}
}
body {
@mixin;
}
body {
@hello: "world";
border: e("this is simple");
border: e(this is simple); // bug in lessjs
border: e("this is simple", "cool lad");
border: e(1232);
border: e(@hello);
border: e("one" + 'more'); // no string addition lessjs
border: e(); // syntax error lessjs
line-height: ~"eating rice";
line-height: ~"string cheese";
line-height: a b c ~"string me" d e f;
line-height: ~"string @{hello}";
}
.class {
filter: ~"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image.png')";
}
@font-directory: 'fonts/';
@some-family: Gentium;
@font-face: maroon; // won't collide with @font-face { }
@font-face {
font-family: Graublau Sans Web;
src: url(@{font-directory}GraublauWeb.otf) format("opentype");
}
@font-face {
font-family: @some-family;
src: url('@{font-directory}Gentium.ttf');
}
@font-face {
font-family: @some-family;
src: url("@{font-directory}GentiumItalic.ttf");
font-style: italic;
}
h2 {
font-family: @some-family;
crazy: @font-face;
}
.simple(@hi) when (@hi) {
color: yellow;
}
.something(@hi) when (@hi = cool) {
color: red;
}
.another(@x) when (@x > 10) {
color: green;
}
.flipped(@x) when (@x =< 10) {
color: teal;
}
.yeah(@arg) when (isnumber(@arg)) {
color: purple;
}
.yeah(@arg) when (ispixel(@arg)) {
color: silver;
}
.hello(@arg) when not (@arg) {
color: orange;
}
dd {
.simple(true);
.simple(2344px);
}
b {
.something(cool);
.something(birthday);
}
img {
.another(12);
.another(2);
.flipped(12);
.flipped(2);
}
body {
.yeah("world");
.yeah(232px);
.yeah(232);
.hello(true);
}
.something(@x) when (@x) and (@y), not (@x = what) {
color: blue;
}
div {
@y: true;
.something(true);
}
pre {
.something(what);
}
.coloras(@g) when (iscolor(@g)) {
color: true @g;
}
link {
.coloras(red);
.coloras(10px);
.coloras(ffjref);
.coloras(#fff);
.coloras(#fffddd);
.coloras(rgb(0,0,0));
.coloras(rgba(0,0,0, .34));
}
// css hacks
:root .alert-message, :root .btn {
border-radius: 0 \0;
}
@import 'file1.less'; // file found and imported
@import "something.css" media;
@import url("something.css") media;
@import url(something.css) media, screen, print;
@color: maroon;
@import url(file2); // found and imported
body {
line-height: 10em;
@colors;
}
div {
@color: fuchsia;
@import "file2";
}
@keyframes 'bounce' {
from {
top: 100px;
animation-timing-function: ease-out;
}
25% {
top: 50px;
animation-timing-function: ease-in;
}
50% {
top: 100px;
animation-timing-function: ease-out;
}
75% {
top: 75px;
animation-timing-function: ease-in;
}
to {
top: 100px;
}
}
div {
animation-name: 'diagonal-slide';
animation-duration: 5s;
animation-iteration-count: 10;
}
@keyframes 'diagonal-slide' {
from {
left: 0;
top: 0;
}
to {
left: 100px;
top: 100px;
}
}
.unary {
// all operators are parsable as unary operators, anything
// but - throws an error right now though,
// this gives two numbers
sub: 10 -5;
// add: 10 +5; // error
// div: 10 /5; // error
// mul: 10 *5; // error
}
.spaces {
// we can make the parser do math by leaving out the
// space after the first value, or putting spaces on both sides
sub: 10-5;
sub: 10 - 5;
add: 10+5;
add: 10 + 5;
// div: 10/5; // this wont work, read on
div: 10 / 5;
mul: 10*5;
mul: 10 * 5;
}
// these properties have divison not in parenthases
.supress-division {
border-radius: 10px / 10px;
border-radius: 10px/10px;
border-radius: hello (10px/10px) world;
@x: 10px;
font: @x/30 sans-serif;
font: 10px / 20px sans-serif;
font: 10px/20px sans-serif;
border-radius:0 15px 15px 15px / 0 50% 50% 50%;
}
.parens {
// if you are unsure, then just wrap the expression in parentheses and it will
// always evaluate.
// notice we no longer have unary operators, and these will evaluate
sub: (10 -5);
add: (10 +5);
div: (10 /5);
div: (10/5); // no longer interpreted as the shorthand
mul: (10 *5);
}
.keyword-names {
// watch out when doing math with keywords, - is a valid keyword character
@a: 100;
@b: 25;
@a-: "hello";
height: @a-@b; // here we get "hello" 25, not 75
}
.negation {
hello: -(1px);
hello: 0-(1px);
@something: 10;
hello: -@something;
}
// and now here are the tests
.test {
single: (5);
single: 5+(5);
single: (5)+((5));
parens: (5 +(5)) -2;
// parens: ((5 +(5)) -2); // FAILS - fixme
math: (5 + 5)*(2 / 1);
math: (5+5)*(2/1);
width: 2 * (4 * (2 + (1 + 6))) - 1;
height: ((2+3)*(2+3) / (9-4)) + 1;
padding: (2px + 4px) 1em 2px 2;
@var: (2 * 2);
padding: (2 * @var) 4 4 (@var * 1px);
width: (@var * @var) * 6;
height: (7 * 7) + (8 * 8);
margin: 4 * (5 + 5) / 2 - (@var * 2);
}
.percents {
color: 100 * 10%;
color: 10% * 100;
color: 10% * 10%;
color: 100px * 10%; // lessjs makes this px
color: 10% * 100px; // lessjs makes this %
color: 20% + 10%;
color: 20% - 10%;
color: 20% / 10%;
}
.misc {
x: 10px * 4em;
y: 10 * 4em;
}
@media screen, 3D {
P { color: green; }
}
@media print {
body { font-size: 10pt }
}
@media screen {
body { font-size: 13px }
}
@media screen, print {
body { line-height: 1.2 }
}
@media all and (min-width: 0px) {
body { line-height: 1.2 }
}
@media all and (min-width: 0) {
body { line-height: 1.2 }
}
@media
screen and (min-width: 102.5em) and (max-width: 117.9375em),
screen and (min-width: 150em) {
body { color: blue }
}
@media screen and (min-height: 100px + 10px) {
body { color: red; }
}
@cool: 100px;
@media screen and (height: @cool) and (width: @cool + 10), (size: @cool + 20) {
body { color: red; }
}
@hello: "utf-8";
@charset @hello;
@color: #fff;
@base_path: "/assets/images/";
@images: @base_path + "test/";
.topbar { background: url(@{images}topbar.png); }
.hello { test: empty-function(@images, 40%, to(@color)); }
.css3 {
background-image: -webkit-gradient(linear, 0% 0%, 0% 90%,
from(#E9A000), to(#A37000));
}
/**
Here is a block comment
**/
// this is a comment
.test, /*hello*/.world {
border: 1px solid red; // world
/* another property */
color: url(http://mage-page.com);
string: "hello /* this is not a comment */";
world: "// neither is this";
string: 'hello /* this is not a comment */' /*what if this is a comment */;
world: '// neither is this' // hell world;
;
what-/*something?*/ever: 100px;
background: url(/*no comment here*/);
}
.urls {
@var: "http://google.com";
background: url(@var);
background: url(@{var});
background: url("@{var}");
}
.mix(@arg) { color: @arg; }
@aaa: aaa;
@bbb: bbb;
// make sure the opening selector isn't too greedy
.cool {.mix("@{aaa}, @{bbb}")}
.cool();
.cool("{hello");
.cool('{hello');
// merging of mixins
.span-17 { float: left; }
.span-17 { width: 660px; }
.x {.span-17;}
.hi {
pre {
color: red;
}
}
.hi {
pre {
color: blue;
}
}
.rad {
.hi;
}
@outer: 10px;
@class(@var:22px, @car: 400px + @outer) {
margin: @var;
height: @car;
}
@group {
@f(@color) {
color: @color;
}
.cool {
border-bottom: 1px solid green;
}
}
.class(@width:200px) {
padding: @width;
}
body {
.class(2.0em);
@group > @f(red);
@class(10px, 10px + 2);
@group > .cool;
}
@lots(@a: 10px, @b: 20px, @c: 30px, @d: 40px, @e: 4px, @f:3px, @g:2px, @h: 1px) {
padding: @a @b @c @d;
margin: @e @f @g @h;
}
.skip_args {
@class(,12px);
@lots(,,,88px,,12px);
@group > @f(red,,,,);
@group > @f(red);
}
@tester {
p, div { height: 10px; }
}
#test1 {
div { color: red; }
@tester;
}
@cool {
a,b,i { width: 1px; }
}
#test2 {
b { color: red; }
@cool;
}
#test3 {
@cool;
b { color: red; }
}
@cooler {
a { margin: 1px; }
}
#test4 {
a, div, html { color: blue; }
@cooler;
}
@hi {
img, strong { float: right; }
}
#test5 {
img, strong { padding: 2px; }
@hi;
}
@nested {
div, span {
a {
color: red;
}
}
}
#test6 {
div, span {
a {
line-height: 10px;
}
}
@nested;
}
@broken-nesting {
div, span {
strong, b {
color: red;
}
}
}
#test7 {
div {
strong {
margin: 1px;
}
}
@broken-nesting;
}
@another-nest {
a,b {
i {
color: red;
}
s {
color: blue;
}
}
}
#test8 {
a, b {
i,s {
background: red;
}
}
@another-nest;
}
@rounded-corners {
border-radius: 10px;
}
.bold {
@font-size: 20px;
font-size: @font-size;
font-weight: bold;
}
body #window {
@rounded-corners;
.bold;
line-height: @font-size * 1.5;
}
#bundle {
.button {
display: block;
border: 1px solid black;
background-color: grey;
&:hover { background-color: white }
}
}
#header a {
color: orange;
#bundle > .button; // mixin the button class
}
div {
@abstract {
hello: world;
b {
color: blue;
}
}
@abstract > b;
@abstract;
}
@poop {
big: baby;
}
body {
div;
}
// not using > to list mixins
.hello {
.world {
color: blue;
}
}
.foobar {
.hello .world;
}
.butter {
.this .one .isnt .found;
}
// arguments
.spam(@something: 100, @dad: land) {
@wow: 23434;
foo: @arguments;
bar: @arguments;
}
.eggs {
.spam(1px, 2px);
.spam();
}
.first(@one, @two, @three, @four: cool) {
cool: @arguments;
}
#hello {
.first(one, two, three);
}
.rad(@name) {
cool: @arguments;
}
#world {
@hello: "world";
.rad("@{hello}");
}
.second(@x, @y:skip, @z: me) {
things: @arguments;
}
#another {
.second(red, blue, green);
.second(red blue green);
}
.another(@x, @y:skip, @z:me) {
.cool {
color: @arguments;
}
}
#day {
.another(one,two, three);
.another(one two three);
}
#header {
color: black;
.navigation {
font-size: 12px;
.border {
.outside {
color: blue;
}
}
}
.logo {
width: 300px;
&:hover { text-decoration: none }
}
}
a { b { ul { li { color: green; } } } }
this { will { not { show { } } } }
.cool {
div & { color: green; }
p & span { color: yellow; }
}
another {
.cool;
}
b {
& .something {
color: blue;
}
&.something {
color: blue;
}
}
.foo {
.bar, .baz {
& .qux {
display: block;
}
.qux & {
display: inline;
}
.qux & .biz {
display: none;
}
}
}
b {
hello [x="&yeah"] {
color: red;
}
}
.demo (light, @color) {
color: lighten(@color, 10%);
}
.demo (@_, @color) {
display: block;
}
@switch: light;
.class {
.demo(@switch, #888);
}
// by arity
.mixin () {
zero: 0;
}
.mixin (@a: 1px) {
one: 1;
}
.mixin (@a) {
one-req: 1;
}
.mixin (@a: 1px, @b: 2px) {
two: 2;
}
.mixin (@a, @b, @c) {
three-req: 3;
}
.mixin (@a: 1px, @b: 2px, @c: 3px) {
three: 3;
}
.zero {
.mixin();
}
.one {
.mixin(1);
}
.two {
.mixin(1, 2);
}
.three {
.mixin(1, 2, 3);
}
//
.mixout ('left') {
left: 1;
}
.mixout ('right') {
right: 1;
}
.left {
.mixout('left');
}
.right {
.mixout('right');
}
//
.border (@side, @width) {
color: black;
.border-side(@side, @width);
}
.border-side (left, @w) {
border-left: @w;
}
.border-side (right, @w) {
border-right: @w;
}
.border-right {
.border(right, 4px);
}
.border-left {
.border(left, 4px);
}
//
.border-radius (@r) {
both: @r * 10;
}
.border-radius (@r, left) {
left: @r;
}
.border-radius (@r, right) {
right: @r;
}
.only-right {
.border-radius(33, right);
}
.only-left {
.border-radius(33, left);
}
.left-right {
.border-radius(33);
}
.hola(hello, @hello...) {
color: blue;
}
#hola {
.hola(hello, world);
.hola(jello, world);
}
.resty(@hello, @world, @the_rest...) {
padding: @hello @world;
rest: @the_rest;
}
#nnn {
.body(10, 10, 10, 10, 10);
.body(10, 10, 10);
.body(10, 10);
.body(10);
.body();
}
.defaults(@aa, @bb:e343, @cc: "heah", ...) {
height: @aa;
}
#defaults_1 {
.defaults();
.defaults(one);
.defaults(two, one);
.defaults(three, two, one);
.defaults(four, three, two, one);
}
.thing() { color: green; }
.thing(...) { color: blue; }
.thing { color: red; }
#aa {
.thing();
}
#bb {
.thing;
}
#cc {
.thing(1);
}
@a: 10;
@some {
@b: @a + 10;
div {
@c: @b + 10;
other {
@d: @c + 10;
world {
@e: @d + 10;
height: @e;
}
}
}
}
body {
@some;
}
@some;
.test(@x: 10) {
height: @x;
.test(@y: 11) {
height: @y;
.test(@z: 12) {
height: @z;
}
.test;
}
.test;
}
pre {
.test;
}
@color: blue;
(~"something @{color}"), world {
color: blue;
}
.div {
@color: red;
(3434) {
height: 100px;
}
(~"cool @{color}") {
height: 4000px;
}
}
.heck(@a) { color: @a+10 }
.spanX (@index) when (@index > 0) {
(~".span@{index}") { .heck(@index) }
.spanX(@index - 1);
}
.spanX (0) {}
.spanX (5);
// these are the demos from the lessphp homepage
default {
@base: 24px;
@border-color: #B2B;
.underline { border-bottom: 1px solid green }
#header {
color: black;
border: 1px solid @border-color + #222222;
.navigation {
font-size: @base / 2;
a {
.underline;
}
}
.logo {
width: 300px;
&:hover { text-decoration: none }
}
}
}
variables {
@a: 2;
@x: @a * @a;
@y: @x + 1;
@z: @x * 2 + @y;
@nice-blue: #5B83AD;
@light-blue: @nice-blue + #111;
@b: @a * 10;
@c: #888;
@fonts: "Trebuchet MS", Verdana, sans-serif;
.variables {
width: @z + 1cm; // 14cm
height: @b + @x + 0px; // 24px
color: @c;
background: @light-blue;
font-family: @fonts;
}
}
mixins {
.bordered {
border-top: dotted 1px black;
border-bottom: solid 2px black;
}
#menu a {
color: #111;
.bordered;
}
.post a {
color: red;
.bordered;
}
}
nested-rules {
#header {
color: black;
.navigation {
font-size: 12px;
}
.logo {
width: 300px;
&:hover { text-decoration: none }
}
}
}
namespaces {
#bundle {
.button {
display: block;
border: 1px solid black;
background-color: grey;
&:hover { background-color: white }
}
}
#header a {
color: orange;
#bundle > .button; // mixin the button class
}
}
mixin-functions {
@outer: 10px;
@class(@var:22px, @car: 400px + @outer) {
margin: @var;
height: @car;
}
@group {
@f(@color) {
color: @color;
}
.cool {
border-bottom: 1px solid green;
}
}
.class(@width:200px) {
padding: @width;
}
body {
.class(2.0em);
@group > @f(red);
@class(10px, 10px + 2);
@group > .cool;
}
}
/**
* This is a test import file
*/
@colors {
div.bright {
color: red;
}
div.sad {
color: blue;
}
}
@a: 2;
@x: @a * @a;
@y: @x + 1;
@z: @y + @x * 2;
@m: @z % @y;
@nice-blue: #5B83AD;
@light-blue: @nice-blue + #111;
@rgb-color: rgb(20%, 15%, 80%);
@rgba-color: rgba(23,68,149,0.5);
@b: @a * 10px;
@c: #888;
@fonts: "Trebuchet MS", Verdana, sans-serif;
.variables {
width: @z + 1cm; // 14cm
height: @b + @x + 0px; // 24px
margin-top: -@b; // -20px
margin-bottom: 10 - -@b; // 30px
@d: @c + #001;
color: @d;
background: @light-blue;
font-family: @fonts;
margin: @m + 0px; // 3px
font: 10px/12px serif;
font: 120%/120% serif;
}
.external {
color: @c;
border: 1px solid @rgb-color;
background: @rgba-color;
padding: @nonexistant + 4px;
}
@hello: 44px;
@something: "hello";
@cool: something;
color: @@something;
color: @@@cool;
.article { color:#294366; }
.comment {
width:960px;
color:#294366;
padding:10px;
}
.wow {
height:960px;
background-color:#294366;
color:green;
padding:;
margin:;
}
.mix { font-size:10px; }
\ No newline at end of file
.one {
color:one;
color:one;
}
.two {
color:two;
color:two;
}
.three {
color:three;
color:three;
}
.multi-foo {
color:two;
color:three;
color:two;
color:three;
color:three;
}
.multi-baz {
color:two;
color:three;
color:two;
color:three;
}
\ No newline at end of file
* { color:blue; }
E { color:blue; }
E[foo] { color:blue; }
[foo] { color:blue; }
[foo] .helloWorld { color:blue; }
[foo].helloWorld { color:blue; }
E[foo="barbar"] { color:blue; }
E[foo~="hello#$@%@$#^"] { color:blue; }
E[foo^="color: green;"] { color:blue; }
E[foo$="239023"] { color:blue; }
E[foo*="29302"] { color:blue; }
E[foo|="239032"] { color:blue; }
E:root { color:blue; }
E:nth-child(odd) { color:blue; }
E:nth-child(2n+1) { color:blue; }
E:nth-child(5) { color:blue; }
E:nth-last-child(-n+2) { color:blue; }
E:nth-of-type(2n) { color:blue; }
E:nth-last-of-type(n) { color:blue; }
E:first-child { color:blue; }
E:last-child { color:blue; }
E:first-of-type { color:blue; }
E:last-of-type { color:blue; }
E:only-child { color:blue; }
E:only-of-type { color:blue; }
E:empty { color:blue; }
E:lang(en) { color:blue; }
E::first-line { color:blue; }
E::before { color:blue; }
E#id { color:blue; }
E:not(:link) { color:blue; }
E F { color:blue; }
E > F { color:blue; }
E + F { color:blue; }
E ~ F { color:blue; }
\ No newline at end of file
body {
color:"hello world";
height:5.1666666666667;
height:5px;
height:6px;
width:0.66666666666667;
width:1;
width:0;
width:1;
width:3px;
color:#00112233;
color:#992c3742;
}
format {
format:"rgb(32, 128, 64)";
format-string:"hello world";
format-multiple:"hello earth 2";
format-url-encode:'red is %A';
eformat:rgb(32, 128, 64);
}
\ No newline at end of file
body {
color:#996d33;
color:rgba(153,109,51,0.3);
lighten:#ffffff;
lighten:#7c8df2;
lighten:rgba(222,140,129,0.5);
darken:#d6d6d6;
darken:#0d1e81;
darken:rgba(18,42,185,0.5);
saturate:#f1eded;
saturate:#0025fe;
saturate:rgba(10,44,244,0.5);
desaturate:#efefef;
desaturate:#3349cb;
desaturate:rgba(36,62,218,0.5);
spin:#efefef;
spin:#2d17e7;
spin:rgba(59,23,231,0.5);
spin:#efefef;
spin:#1769e7;
spin:rgba(23,119,231,0.5);
one:#abcdef;
one:#abcdef;
two:rgba(2,159,35,0.9);
two:rgba(2,159,35,0.9);
three:rgba(1,2,3,0.6);
three:rgba(1,2,3,0.6);
four:rgba(1,2,3,0);
four:rgba(1,2,3,0);
hue:282;
sat:33;
lit:12;
what:#dff1da;
zero:#343434;
zero:#003468;
zero:#000000;
zero:#ffffff;
zero:#000000;
zero:#ffffff;
zero:#ffffff;
zero:#ffffff;
zero:#1d5612;
zero:#56124b;
zero:#000000;
zero:#ffffff;
}
alpha {
g:0;
g:1;
}
fade {
f:rgba(255,0,0,0.5);
f:rgba(255,255,255,0.2);
f:rgba(34,23,64,0.5);
}
.mix { color:#808080; }
.percent { per:50%; }
.colorz {
color:#ebebeb;
color:#ff9100;
}
body {
start:#fcf8e3;
spin:#fcf4e3;
chained:#fbeed5;
direct:#fbefd5;
}
pre { spin:#f2dee1; }
dd { background-color:#f5f5f5; }
\ No newline at end of file
body { height:22px; }
body ul { height:20px; }
body ul li { height:10px; }
body ul li div span, body ul li link {
margin:10px;
color:red;
}
body ul div, body ul p { border:1px; }
body ul div.hello, body ul p.hello { color:green; }
body ul div :what, body ul p :what { color:blue; }
body ul a b { color:blue; }
\ No newline at end of file
body {
border:this is simple;
border:this;
border:this is simple;
border:1232;
border:world;
border:onemore;
border:;
line-height:eating rice;
line-height:string cheese;
line-height:a b c string me d e f;
line-height:string world;
}
.class { filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image.png'); }
\ No newline at end of file
@font-face {
font-family:Graublau Sans Web;
src:url(fonts/GraublauWeb.otf) format("opentype");
}
@font-face {
font-family:Gentium;
src:url('fonts/Gentium.ttf');
}
@font-face {
font-family:Gentium;
src:url("fonts/GentiumItalic.ttf");
font-style:italic;
}
h2 {
font-family:Gentium;
crazy:maroon;
}
\ No newline at end of file
dd { color:yellow; }
b {
color:red;
color:blue;
color:blue;
}
img {
color:green;
color:teal;
}
body {
color:purple;
color:silver;
color:purple;
}
div { color:blue; }
link {
color:true red;
color:true #ffffff;
color:true #fffddd;
color:true #000000;
color:true rgba(0,0,0,0.34);
}
\ No newline at end of file
:root .alert-message, :root .btn { border-radius:0 \0; }
\ No newline at end of file
@import url("something.css") media;
@import url("something.css") media;
@import url("something.css") media, screen, print;
b {
color:maroon;
padding:16px;
}
body { line-height:10em; }
body div.bright { color:red; }
body div.sad { color:blue; }
div b {
color:fuchsia;
padding:16px;
}
\ No newline at end of file
@keyframes 'bounce' {
from {
top:100px;
animation-timing-function:ease-out;
}
25% {
top:50px;
animation-timing-function:ease-in;
}
50% {
top:100px;
animation-timing-function:ease-out;
}
75% {
top:75px;
animation-timing-function:ease-in;
}
to {
top:100px;
}
}
div {
animation-name:'diagonal-slide';
animation-duration:5s;
animation-iteration-count:10;
}
@keyframes 'diagonal-slide' {
from {
left:0;
top:0;
}
to {
left:100px;
top:100px;
}
}
\ No newline at end of file
.unary { sub:10 -5; }
.spaces {
sub:5;
sub:5;
add:15;
add:15;
div:2;
mul:50;
mul:50;
}
.supress-division {
border-radius:10px / 10px;
border-radius:10px / 10px;
border-radius:hello(10px / 10px) world;
font:10px / 30 sans-serif;
font:10px / 20px sans-serif;
font:10px / 20px sans-serif;
border-radius:0 15px 15px 15px / 0 50% 50% 50%;
}
.parens {
sub:5;
add:15;
div:2;
div:2;
mul:50;
}
.keyword-names { height:"hello" 25; }
.negation {
hello:-1px;
hello:-1px;
hello:-10;
}
.test {
single:5;
single:10;
single:10;
parens:10 -2;
math:20;
math:20;
width:71;
height:6;
padding:6px 1em 2px 2;
padding:8 4 4 4px;
width:96;
height:113;
margin:12;
}
.percents {
color:1000%;
color:1000%;
color:100%;
color:1000px;
color:1000%;
color:30%;
color:10%;
color:2%;
}
.misc {
x:40px;
y:40em;
}
\ No newline at end of file
@media screen, 3D {
P { color:green; }
}
@media print {
body { font-size:10pt; }
}
@media screen {
body { font-size:13px; }
}
@media screen, print {
body { line-height:1.2; }
}
@media all and (min-width: 0px) {
body { line-height:1.2; }
}
@media all and (min-width: 0) {
body { line-height:1.2; }
}
@media screen and (min-width: 102.5em) and (max-width: 117.9375em),
screen and (min-width: 150em) {
body { color:blue; }
}
@media screen and (min-height: 110px) {
body { color:red; }
}
@media screen and (height: 100px) and (width: 110px), (size: 120px) {
body { color:red; }
}
\ No newline at end of file
@charset "utf-8";
color:"aaa, bbb";
.topbar { background:url(/assets/images/test/topbar.png); }
.hello { test:empty-function("/assets/images/test/",40%,to(#ffffff)); }
.css3 { background-image:-webkit-gradient(linear,0% 0%,0% 90%,from(#e9a000),to(#a37000)); }
.test, .world {
border:1px solid red;
color:url(http://mage-page.com);
string:"hello /* this is not a comment */";
world:"// neither is this";
string:'hello /* this is not a comment */';
world:'// neither is this';
what-ever:100px;
background:url(/*no comment here*/);
}
.urls {
background:url("http://google.com");
background:url(http://google.com);
background:url("http://google.com");
}
.cool { color:"aaa, bbb"; }
.span-17 { float:left; }
.span-17 { width:660px; }
.x {
float:left;
width:660px;
}
.hi pre { color:red; }
.hi pre { color:blue; }
.rad pre { color:red; }
.rad pre { color:blue; }
\ No newline at end of file
body {
padding:2.0em;
color:red;
margin:10px;
height:12px;
border-bottom:1px solid green;
}
.skip_args {
margin:22px;
height:12px;
padding:10px 20px 30px 88px;
margin:4px 12px 2px 1px;
color:red;
}
\ No newline at end of file
#test1 div {
color:red;
height:10px;
}
#test1 p { height:10px; }
#test2 b {
color:red;
width:1px;
}
#test2 a, #test2 i { width:1px; }
#test3 a, #test3 i { width:1px; }
#test3 b {
width:1px;
color:red;
}
#test4 a {
color:blue;
margin:1px;
}
#test4 div, #test4 html { color:blue; }
#test5 img, #test5 strong {
padding:2px;
float:right;
}
#test6 div a, #test6 span a {
line-height:10px;
color:red;
}
#test7 div strong {
margin:1px;
color:red;
}
#test7 div b { color:red; }
#test7 span strong, #test7 span b { color:red; }
#test8 a i, #test8 b i {
background:red;
color:red;
}
#test8 a s, #test8 b s {
background:red;
color:blue;
}
\ No newline at end of file
.bold {
font-size:20px;
font-weight:bold;
}
body #window {
border-radius:10px;
font-size:20px;
font-weight:bold;
line-height:30px;
}
#bundle .button {
display:block;
border:1px solid black;
background-color:grey;
}
#bundle .button:hover { background-color:white; }
#header a {
color:orange;
display:block;
border:1px solid black;
background-color:grey;
}
#header a:hover { background-color:white; }
div {
color:blue;
hello:world;
}
div b { color:blue; }
body {
color:blue;
hello:world;
}
body b { color:blue; }
.hello .world { color:blue; }
.foobar { color:blue; }
.eggs {
foo:1px 2px;
bar:1px 2px;
foo:100 land;
bar:100 land;
}
#hello { cool:one two three cool; }
#world { cool:"world"; }
#another {
things:red blue green;
things:red blue green skip me;
}
#day .cool { color:one two three; }
#day .cool { color:one two three skip me; }
\ No newline at end of file
#header { color:black; }
#header .navigation { font-size:12px; }
#header .navigation .border .outside { color:blue; }
#header .logo { width:300px; }
#header .logo:hover { text-decoration:none; }
a b ul li { color:green; }
div .cool { color:green; }
p .cool span { color:yellow; }
div another { color:green; }
p another span { color:yellow; }
b .something { color:blue; }
b.something { color:blue; }
.foo .bar .qux, .foo .baz .qux { display:block; }
.qux .foo .bar, .qux .foo .baz { display:inline; }
.qux .foo .bar .biz, .qux .foo .baz .biz { display:none; }
b hello [x="&yeah"] { color:red; }
\ No newline at end of file
#header .navigation .border .outside { color:blue; }
#header .navigation { font-size:12px; }
#header .logo:hover { text-decoration:none; }
#header .logo { width:300px; }
#header { color:black; }
a b ul li { color:green; }
\ No newline at end of file
.class {
color:#a2a2a2;
display:block;
}
.zero {
zero:0;
one:1;
two:2;
three:3;
}
.one {
one:1;
one-req:1;
two:2;
three:3;
}
.two {
two:2;
three:3;
}
.three {
three-req:3;
three:3;
}
.left { left:1; }
.right { right:1; }
.border-right {
color:black;
border-right:4px;
}
.border-left {
color:black;
border-left:4px;
}
.only-right { right:33; }
.only-left { left:33; }
.left-right { both:330; }
#hola { color:blue; }
#defaults_1 {
height:one;
height:two;
height:three;
height:four;
}
.thing { color:red; }
#aa {
color:green;
color:blue;
color:red;
}
#bb {
color:green;
color:blue;
color:red;
}
#cc { color:blue; }
\ No newline at end of file
body div other world { height:50; }
div other world { height:50; }
pre {
height:10;
height:11;
height:12;
}
\ No newline at end of file
something blue, world { color:blue; }
.div 3434 { height:100px; }
.div cool red { height:4000px; }
.span5 { color:15; }
.span4 { color:14; }
.span3 { color:13; }
.span2 { color:12; }
.span1 { color:11; }
\ No newline at end of file
default .underline { border-bottom:1px solid green; }
default #header {
color:black;
border:1px solid #dd44dd;
}
default #header .navigation { font-size:12px; }
default #header .navigation a { border-bottom:1px solid green; }
default #header .logo { width:300px; }
default #header .logo:hover { text-decoration:none; }
variables .variables {
width:14cm;
height:24px;
color:#888888;
background:#6c94be;
font-family:"Trebuchet MS", Verdana, sans-serif;
}
mixins .bordered {
border-top:dotted 1px black;
border-bottom:solid 2px black;
}
mixins #menu a {
color:#111111;
border-top:dotted 1px black;
border-bottom:solid 2px black;
}
mixins .post a {
color:red;
border-top:dotted 1px black;
border-bottom:solid 2px black;
}
nested-rules #header { color:black; }
nested-rules #header .navigation { font-size:12px; }
nested-rules #header .logo { width:300px; }
nested-rules #header .logo:hover { text-decoration:none; }
namespaces #bundle .button {
display:block;
border:1px solid black;
background-color:grey;
}
namespaces #bundle .button:hover { background-color:white; }
namespaces #header a {
color:orange;
display:block;
border:1px solid black;
background-color:grey;
}
namespaces #header a:hover { background-color:white; }
mixin-functions body {
padding:2.0em;
color:red;
margin:10px;
height:12px;
border-bottom:1px solid green;
}
\ No newline at end of file
color:44px;
color:44px;
.variables {
width:14cm;
height:24px;
margin-top:-20px;
margin-bottom:30px;
color:#888899;
background:#6c94be;
font-family:"Trebuchet MS", Verdana, sans-serif;
margin:3px;
font:10px / 12px serif;
font:120% / 120% serif;
}
.external {
color:#888888;
border:1px solid #3326cc;
background:rgba(23,68,149,0.5);
padding:4px;
}
\ No newline at end of file
<?php
error_reporting(E_ALL);
require realpath(dirname(__FILE__)).'/../lessc.inc.php';
// sorts the selectors in stylesheet in order to normalize it for comparison
$exe = array_shift($argv); // remove filename
if (!$fname = array_shift($argv)) {
$fname = "php://stdin";
}
// also sorts the tags in the block
function sort_key($block) {
if (!isset($block->sort_key)) {
sort($block->tags, SORT_STRING);
$block->sort_key = implode(",", $block->tags);
}
return $block->sort_key;
}
class sort_css extends lessc {
function __construct() {
parent::__construct();
}
// normalize numbers
function compileValue($value) {
$ignore = array('list', 'keyword', 'string', 'color', 'function');
if ($value[0] == 'number' || !in_array($value[0], $ignore)) {
$value[1] = $value[1] + 0; // convert to either double or int
}
return parent::compileValue($value);
}
function parse_and_sort($str) {
$root = $this->parseTree($str);
$less = $this;
usort($root->props, function($a, $b) use ($less) {
$sort = strcmp(sort_key($a[1]), sort_key($b[1]));
if ($sort == 0)
return strcmp($less->compileBlock($a[1]), $less->compileBlock($b[1]));
return $sort;
});
return $this->compileBlock($root);
}
}
$sorter = new sort_css;
echo $sorter->parse_and_sort(file_get_contents($fname));
#!/usr/bin/env php
<?php
error_reporting(E_ALL);
/**
* Go through all files matching pattern in input directory
* and compile them, then compare them to paired file in
* output directory.
*/
$difftool = 'diff -b -B -t -u';
$input = array(
'dir' => 'inputs',
'glob' => '*.less',
);
$output = array(
'dir' => 'outputs',
'filename' => '%s.css',
);
$prefix = strtr(realpath(dirname(__FILE__)), '\\', '/');
require $prefix.'/../lessc.inc.php';
$compiler = new lessc();
$compiler->importDir = array($input['dir'].'/test-imports');
$fa = 'Fatal Error: ';
if (php_sapi_name() != 'cli') {
exit($fa.$argv[0].' must be run in the command line.');
}
$opts = getopt('hCd::g');
if ($opts === false || isset($opts['h'])) {
echo <<<EOT
Usage: ./test.php [options] [searchstring]
where [options] can be a mix of these:
-h Show this help message and exit.
-d=[difftool] Show the diff of the actual output vs. the reference when a
test fails; uses 'diff -b -B -t -u' by default.
The test is aborted after the first failure report, unless
you also specify the '-g' option ('go on').
-g Continue executing the other tests when a test fails and
option '-d' is active.
-C Regenerate ('compile') the reference output files from the
given inputs.
WARNING: ONLY USE THIS OPTION WHEN YOU HAVE ASCERTAINED
THAT lessphp PROCESSES ALL TESTS CORRECTLY!
The optional [searchstring] is used to filter the input files: only tests
which have filename(s) containing the specified searchstring will be
executed. I.e. the corresponding glob pattern is '*[searchstring]*.less'.
The script EXIT CODE is the number of failed tests (with a maximum of 255),
0 on success and 1 when this help message is shown. This aids in integrating
this script in larger (user defined) shell test scripts.
Examples of use:
- Test the full test set:
./test.php
- Run only the mixin tests:
./test.php mixin
- Use a custom diff tool to show diffs for failing tests
./test.php -d=meld
EOT;
exit(1);
}
$input['dir'] = $prefix.'/'.$input['dir'];
$output['dir'] = $prefix.'/'.$output['dir'];
if (!is_dir($input['dir']) || !is_dir($output['dir']))
exit($fa." both input and output directories must exist\n");
$exe = array_shift($argv); // remove filename
// get the first non flag as search string
$searchString = null;
foreach ($argv as $a) {
if (strlen($a) > 0 && $a{0} != '-') {
$searchString = $a;
break;
}
}
$tests = array();
$matches = glob($input['dir'].'/'.(!is_null($searchString) ? '*'.$searchString : '' ).$input['glob']);
if ($matches) {
foreach ($matches as $fname) {
extract(pathinfo($fname)); // for $filename, from php 5.2
$tests[] = array(
'in' => $fname,
'out' => $output['dir'].'/'.sprintf($output['filename'], $filename),
);
}
}
$count = count($tests);
$compiling = isset($opts["C"]);
$continue_when_test_fails = isset($opts["g"]);
$showDiff = isset($opts["d"]);
if ($showDiff && !empty($opts["d"])) {
$difftool = $opts["d"];
}
echo ($compiling ? "Compiling" : "Running")." $count test".($count == 1 ? '' : 's').":\n";
function dump($msgs, $depth = 1, $prefix=" ") {
if (!is_array($msgs)) $msgs = array($msgs);
foreach ($msgs as $m) {
echo str_repeat($prefix, $depth).' - '.$m."\n";
}
}
$fail_prefix = " ** ";
$fail_count = 0;
$i = 1;
foreach ($tests as $test) {
printf(" [Test %04d/%04d] %s -> %s\n", $i, $count, basename($test['in']), basename($test['out']));
try {
ob_start();
$parsed = trim($compiler->parse(file_get_contents($test['in'])));
ob_end_clean();
} catch (exception $e) {
dump(array(
"Failed to compile input, reason:",
$e->getMessage(),
"Aborting"
), 1, $fail_prefix);
break;
}
if ($compiling) {
file_put_contents($test['out'], $parsed);
} else {
if (!is_file($test['out'])) {
dump(array(
"Failed to find output file: $test[out]",
"Maybe you forgot to compile tests?",
"Aborting"
), 1, $fail_prefix);
break;
}
$expected = trim(file_get_contents($test['out']));
// don't care about CRLF vs LF change (DOS/Win vs. UNIX):
$expected = trim(str_replace("\r\n", "\n", $expected));
$parsed = trim(str_replace("\r\n", "\n", $parsed));
if ($expected != $parsed) {
$fail_count++;
if ($showDiff) {
dump("Failed:", 1, $fail_prefix);
$tmp = $test['out'].".tmp";
file_put_contents($tmp, $parsed);
system($difftool.' '.$test['out'].' '.$tmp);
unlink($tmp);
if (!$continue_when_test_fails) {
dump("Aborting");
break;
} else {
echo "===========================================================================\n";
}
} else {
dump("Failed, run with -d flag to view diff", 1, $fail_prefix);
}
} else {
dump("Passed");
}
}
$i++;
}
exit($fail_count > 255 ? 255 : $fail_count);
?>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment