├── .npmignore ├── test ├── css │ ├── charsets.css │ ├── import-once.css │ ├── lazy-eval.css │ ├── import-interpolation.css │ ├── mixins-closure.css │ ├── legacy │ │ └── legacy.css │ ├── compression │ │ └── compression.css │ ├── mixins-nested.css │ ├── extend-clearfix.css │ ├── javascript.css │ ├── extend-media.css │ ├── ie-filters.css │ ├── mixins-named-args.css │ ├── css-escapes.css │ ├── extend-exact.css │ ├── scope.css │ ├── mixins-important.css │ ├── mixins-pattern.css │ ├── whitespace.css │ ├── debug │ │ ├── linenumbers-comments.css │ │ ├── linenumbers-mediaquery.css │ │ └── linenumbers-all.css │ ├── import.css │ ├── parens.css │ ├── extend-chaining.css │ ├── operations.css │ ├── strings.css │ ├── variables.css │ ├── rulesets.css │ ├── extend.css │ ├── comments.css │ ├── colors.css │ ├── extend-selector.css │ ├── mixins-guards.css │ ├── static-urls │ │ └── urls.css │ ├── css.css │ ├── mixins-args.css │ ├── urls.css │ ├── mixins.css │ ├── selectors.css │ ├── css-3.css │ ├── extend-nest.css │ ├── functions.css │ └── media.css ├── data │ ├── image.jpg │ ├── page.html │ └── data-uri-fail.png ├── less │ ├── errors │ │ ├── parse-error-curly-bracket.less │ │ ├── bad-variable-declaration1.less │ │ ├── property-in-root2.less │ │ ├── add-mixed-units.less │ │ ├── color-operation-error.less │ │ ├── divide-mixed-units.less │ │ ├── import-no-semi.less │ │ ├── property-in-root.less │ │ ├── property-in-root3.less │ │ ├── recursive-variable.less │ │ ├── comment-in-selector.less │ │ ├── import-subfolder1.less │ │ ├── import-subfolder2.less │ │ ├── extend-no-selector.less │ │ ├── extend-not-at-end.less │ │ ├── parens-error-1.less │ │ ├── parens-error-2.less │ │ ├── parens-error-3.less │ │ ├── add-mixed-units2.less │ │ ├── imports │ │ │ ├── import-subfolder1.less │ │ │ ├── import-test.less │ │ │ ├── import-subfolder2.less │ │ │ └── subfolder │ │ │ │ ├── mixin-not-defined.less │ │ │ │ └── parse-error-curly-bracket.less │ │ ├── javascript-error.less │ │ ├── parse-error-missing-bracket.less │ │ ├── property-ie5-hack.less │ │ ├── mixin-not-matched.less │ │ ├── mixin-not-matched2.less │ │ ├── multiply-mixed-units.less │ │ ├── import-subfolder2.txt │ │ ├── parse-error-curly-bracket.txt │ │ ├── bad-variable-declaration1.txt │ │ ├── import-no-semi.txt │ │ ├── parse-error-with-import.txt │ │ ├── import-missing.less │ │ ├── mixed-mixin-definition-args-1.less │ │ ├── comment-in-selector.txt │ │ ├── import-subfolder1.txt │ │ ├── mixed-mixin-definition-args-2.less │ │ ├── mixin-not-defined.txt │ │ ├── parens-error-2.txt │ │ ├── parens-error-1.txt │ │ ├── parens-error-3.txt │ │ ├── parse-error-missing-bracket.txt │ │ ├── property-ie5-hack.txt │ │ ├── color-operation-error.txt │ │ ├── mixin-not-matched.txt │ │ ├── recursive-variable.txt │ │ ├── extend-not-at-end.txt │ │ ├── mixin-not-matched2.txt │ │ ├── property-in-root3.txt │ │ ├── import-missing.txt │ │ ├── mixin-not-defined.less │ │ ├── property-in-root.txt │ │ ├── property-in-root2.txt │ │ ├── add-mixed-units.txt │ │ ├── extend-no-selector.txt │ │ ├── mixed-mixin-definition-args-1.txt │ │ ├── mixed-mixin-definition-args-2.txt │ │ ├── parse-error-with-import.less │ │ ├── add-mixed-units2.txt │ │ ├── divide-mixed-units.txt │ │ ├── javascript-error.txt │ │ └── multiply-mixed-units.txt │ ├── import │ │ ├── import-charset-test.less │ │ ├── import-test-d.css │ │ ├── import-test-e.less │ │ ├── deeper │ │ │ └── import-once-test-a.less │ │ ├── import-interpolation.less │ │ ├── import-test-c.less │ │ ├── urls.less │ │ ├── import-once-test-c.less │ │ ├── import-test-a.less │ │ ├── import-interpolation2.less │ │ ├── imports │ │ │ ├── logo.less │ │ │ └── font.less │ │ ├── import-test-b.less │ │ └── import-and-relative-paths-test.less │ ├── charsets.less │ ├── lazy-eval.less │ ├── import-once.less │ ├── legacy │ │ └── legacy.less │ ├── import-interpolation.less │ ├── debug │ │ ├── linenumbers.less │ │ └── import │ │ │ └── test.less │ ├── extend-clearfix.less │ ├── compression │ │ └── compression.less │ ├── mixins-nested.less │ ├── mixins-important.less │ ├── extend-media.less │ ├── mixins-closure.less │ ├── ie-filters.less │ ├── css-escapes.less │ ├── rulesets.less │ ├── mixins-named-args.less │ ├── javascript.less │ ├── import.less │ ├── extend-exact.less │ ├── whitespace.less │ ├── extend-nest.less │ ├── static-urls │ │ └── urls.less │ ├── parens.less │ ├── strings.less │ ├── extend.less │ ├── extend-chaining.less │ ├── operations.less │ ├── extend-selector.less │ ├── mixins-pattern.less │ ├── comments.less │ ├── variables.less │ ├── scope.less │ ├── colors.less │ ├── urls.less │ ├── css.less │ ├── mixins.less │ ├── selectors.less │ ├── css-3.less │ ├── mixins-args.less │ ├── media.less │ └── mixins-guards.less ├── browser │ ├── less │ │ ├── imports │ │ │ ├── urls.less │ │ │ └── urls2.less │ │ ├── rootpath │ │ │ └── urls.less │ │ ├── relative-urls │ │ │ └── urls.less │ │ ├── rootpath-relative │ │ │ └── urls.less │ │ └── urls.less │ ├── runner-errors.js │ ├── runner-relative-urls.js │ ├── runner-legacy.js │ ├── runner-rootpath.js │ ├── runner-production.js │ ├── runner-browser.js │ ├── runner-rootpath-relative.js │ ├── runner-main.js │ ├── template.htm │ ├── css │ │ ├── rootpath │ │ │ └── urls.css │ │ ├── rootpath-relative │ │ │ └── urls.css │ │ ├── relative-urls │ │ │ └── urls.css │ │ └── urls.css │ └── common.js └── browser-test-prepare.js ├── .gitignore ├── .gitattributes ├── lib └── less │ ├── tree │ ├── unicode-descriptor.js │ ├── comment.js │ ├── paren.js │ ├── alpha.js │ ├── keyword.js │ ├── negative.js │ ├── assignment.js │ ├── anonymous.js │ ├── value.js │ ├── url.js │ ├── extend.js │ ├── variable.js │ ├── quoted.js │ ├── directive.js │ ├── expression.js │ ├── condition.js │ ├── operation.js │ ├── javascript.js │ ├── selector.js │ ├── rule.js │ ├── call.js │ ├── element.js │ └── import.js │ ├── join-selector-visitor.js │ ├── tree.js │ ├── visitor.js │ ├── rhino.js │ ├── lessc_helper.js │ ├── env.js │ └── import-visitor.js ├── README.md ├── benchmark └── less-benchmark.js ├── package.json ├── Makefile └── CONTRIBUTING.md /.npmignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/css/charsets.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | -------------------------------------------------------------------------------- /test/data/image.jpg: -------------------------------------------------------------------------------- 1 | not actually a jpeg file 2 | -------------------------------------------------------------------------------- /test/less/errors/parse-error-curly-bracket.less: -------------------------------------------------------------------------------- 1 | }} -------------------------------------------------------------------------------- /test/data/page.html: -------------------------------------------------------------------------------- 1 |

This page is 100% Awesome.

2 | -------------------------------------------------------------------------------- /test/less/errors/bad-variable-declaration1.less: -------------------------------------------------------------------------------- 1 | @@demo: "hi"; -------------------------------------------------------------------------------- /test/less/import/import-charset-test.less: -------------------------------------------------------------------------------- 1 | @charset "ISO-8859-1"; -------------------------------------------------------------------------------- /test/less/import/import-test-d.css: -------------------------------------------------------------------------------- 1 | #css { color: yellow; } 2 | -------------------------------------------------------------------------------- /test/css/import-once.css: -------------------------------------------------------------------------------- 1 | #import { 2 | color: #ff0000; 3 | } 4 | -------------------------------------------------------------------------------- /test/css/lazy-eval.css: -------------------------------------------------------------------------------- 1 | .lazy-eval { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /test/less/errors/property-in-root2.less: -------------------------------------------------------------------------------- 1 | @import "property-in-root"; -------------------------------------------------------------------------------- /test/less/import/import-test-e.less: -------------------------------------------------------------------------------- 1 | 2 | body { width: 100% } 3 | -------------------------------------------------------------------------------- /test/less/errors/add-mixed-units.less: -------------------------------------------------------------------------------- 1 | .a { 2 | error: (1px + 3em); 3 | } -------------------------------------------------------------------------------- /test/less/errors/color-operation-error.less: -------------------------------------------------------------------------------- 1 | .a { 2 | prop: (3 / #fff); 3 | } -------------------------------------------------------------------------------- /test/less/errors/divide-mixed-units.less: -------------------------------------------------------------------------------- 1 | .a { 2 | error: (1px / 3em); 3 | } -------------------------------------------------------------------------------- /test/less/errors/import-no-semi.less: -------------------------------------------------------------------------------- 1 | @import "this-statement-is-invalid.less" -------------------------------------------------------------------------------- /test/less/errors/property-in-root.less: -------------------------------------------------------------------------------- 1 | .a() { 2 | prop:1; 3 | } 4 | .a(); -------------------------------------------------------------------------------- /test/less/errors/property-in-root3.less: -------------------------------------------------------------------------------- 1 | prop:1; 2 | .a { 3 | prop:1; 4 | } -------------------------------------------------------------------------------- /test/less/errors/recursive-variable.less: -------------------------------------------------------------------------------- 1 | @bodyColor: darken(@bodyColor, 30%); -------------------------------------------------------------------------------- /test/less/errors/comment-in-selector.less: -------------------------------------------------------------------------------- 1 | #gaga /* Comment */ span { color: red } -------------------------------------------------------------------------------- /test/less/errors/import-subfolder1.less: -------------------------------------------------------------------------------- 1 | @import "imports/import-subfolder1.less"; -------------------------------------------------------------------------------- /test/less/errors/import-subfolder2.less: -------------------------------------------------------------------------------- 1 | @import "imports/import-subfolder2.less"; -------------------------------------------------------------------------------- /test/less/import/deeper/import-once-test-a.less: -------------------------------------------------------------------------------- 1 | @import "../import-once-test-c"; -------------------------------------------------------------------------------- /test/less/charsets.less: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @import "import/import-charset-test"; -------------------------------------------------------------------------------- /test/less/errors/extend-no-selector.less: -------------------------------------------------------------------------------- 1 | :extend(.a all) { 2 | property: red; 3 | } -------------------------------------------------------------------------------- /test/less/errors/extend-not-at-end.less: -------------------------------------------------------------------------------- 1 | .a:extend(.b all).c { 2 | property: red; 3 | } -------------------------------------------------------------------------------- /test/less/errors/parens-error-1.less: -------------------------------------------------------------------------------- 1 | .a { 2 | something: (12 (13 + 5 -23) + 5); 3 | } -------------------------------------------------------------------------------- /test/less/errors/parens-error-2.less: -------------------------------------------------------------------------------- 1 | .a { 2 | something: (12 * (13 + 5 -23)); 3 | } -------------------------------------------------------------------------------- /test/less/errors/parens-error-3.less: -------------------------------------------------------------------------------- 1 | .a { 2 | something: (12 + (13 + 10 -23)); 3 | } -------------------------------------------------------------------------------- /test/less/import/import-interpolation.less: -------------------------------------------------------------------------------- 1 | @import "import-@{in}@{terpolation}2.less"; -------------------------------------------------------------------------------- /test/less/errors/add-mixed-units2.less: -------------------------------------------------------------------------------- 1 | .a { 2 | error: ((1px * 2px) + (3em * 3px)); 3 | } -------------------------------------------------------------------------------- /test/less/errors/imports/import-subfolder1.less: -------------------------------------------------------------------------------- 1 | @import "subfolder/mixin-not-defined.less"; -------------------------------------------------------------------------------- /test/less/errors/imports/import-test.less: -------------------------------------------------------------------------------- 1 | .someclass 2 | { 3 | font-weight: bold; 4 | } -------------------------------------------------------------------------------- /test/less/errors/javascript-error.less: -------------------------------------------------------------------------------- 1 | .scope { 2 | var: `this.foo.toJS()`; 3 | } 4 | -------------------------------------------------------------------------------- /test/less/errors/parse-error-missing-bracket.less: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #fff; 3 | -------------------------------------------------------------------------------- /test/less/errors/imports/import-subfolder2.less: -------------------------------------------------------------------------------- 1 | @import "subfolder/parse-error-curly-bracket.less"; -------------------------------------------------------------------------------- /test/less/errors/imports/subfolder/mixin-not-defined.less: -------------------------------------------------------------------------------- 1 | @import "../../mixin-not-defined.less"; -------------------------------------------------------------------------------- /test/less/errors/property-ie5-hack.less: -------------------------------------------------------------------------------- 1 | .test { 2 | display/*/: block; /*sorry for IE5*/ 3 | } -------------------------------------------------------------------------------- /test/less/import/import-test-c.less: -------------------------------------------------------------------------------- 1 | 2 | @c: red; 3 | 4 | #import { 5 | color: @c; 6 | } 7 | -------------------------------------------------------------------------------- /test/less/import/urls.less: -------------------------------------------------------------------------------- 1 | // empty file showing that it loads from the relative path first 2 | -------------------------------------------------------------------------------- /test/css/import-interpolation.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 100%; 3 | } 4 | .a { 5 | var: test; 6 | } 7 | -------------------------------------------------------------------------------- /test/less/import/import-once-test-c.less: -------------------------------------------------------------------------------- 1 | 2 | @c: red; 3 | 4 | #import { 5 | color: @c; 6 | } 7 | -------------------------------------------------------------------------------- /test/less/import/import-test-a.less: -------------------------------------------------------------------------------- 1 | @import "import-test-b.less"; 2 | @a: 20%; 3 | @import "urls.less"; -------------------------------------------------------------------------------- /test/less/lazy-eval.less: -------------------------------------------------------------------------------- 1 | @var: @a; 2 | @a: 100%; 3 | 4 | .lazy-eval { 5 | width: @var; 6 | } 7 | -------------------------------------------------------------------------------- /test/data/data-uri-fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddii/less.js/master/test/data/data-uri-fail.png -------------------------------------------------------------------------------- /test/browser/less/imports/urls.less: -------------------------------------------------------------------------------- 1 | @import "modify-this.css"; 2 | .modify { 3 | my-url: url("a.png"); 4 | } -------------------------------------------------------------------------------- /test/browser/less/imports/urls2.less: -------------------------------------------------------------------------------- 1 | @import "modify-again.css"; 2 | .modify { 3 | my-url: url("b.png"); 4 | } -------------------------------------------------------------------------------- /test/less/errors/imports/subfolder/parse-error-curly-bracket.less: -------------------------------------------------------------------------------- 1 | @import "../../parse-error-curly-bracket.less"; -------------------------------------------------------------------------------- /test/less/import/import-interpolation2.less: -------------------------------------------------------------------------------- 1 | .a { 2 | var: test; 3 | } 4 | 5 | @in: "redefined-does-nothing"; -------------------------------------------------------------------------------- /test/less/errors/mixin-not-matched.less: -------------------------------------------------------------------------------- 1 | @saxofon:trumpete; 2 | 3 | .mixin(saxofon) { 4 | } 5 | 6 | .mixin(@saxofon); -------------------------------------------------------------------------------- /test/less/errors/mixin-not-matched2.less: -------------------------------------------------------------------------------- 1 | @saxofon:trumpete; 2 | 3 | .mixin(@a, @b) { 4 | } 5 | 6 | .mixin(@a: @saxofon); -------------------------------------------------------------------------------- /test/less/errors/multiply-mixed-units.less: -------------------------------------------------------------------------------- 1 | /* Test */ 2 | #blah { 3 | // blah 4 | } 5 | .a { 6 | error: (1px * 1em); 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .emacs* 3 | *.flymake 4 | *~ 5 | .#* 6 | .idea 7 | test/browser/less.js 8 | test/browser/test-runner-*.htm 9 | -------------------------------------------------------------------------------- /test/less/import/imports/logo.less: -------------------------------------------------------------------------------- 1 | #logo { 2 | width: 100px; 3 | height: 100px; 4 | background: url('../assets/logo.png'); 5 | } 6 | -------------------------------------------------------------------------------- /test/less/errors/import-subfolder2.txt: -------------------------------------------------------------------------------- 1 | ParseError: missing opening `{` in {path}parse-error-curly-bracket.less on line 1, column 2: 2 | 1 }} 3 | -------------------------------------------------------------------------------- /test/less/import/import-test-b.less: -------------------------------------------------------------------------------- 1 | @import "import-test-c"; 2 | 3 | @b: 100%; 4 | 5 | .mixin { 6 | height: 10px; 7 | color: @c; 8 | } 9 | -------------------------------------------------------------------------------- /test/browser/runner-errors.js: -------------------------------------------------------------------------------- 1 | less.strictUnits = true; 2 | 3 | describe("less.js error tests", function() { 4 | testLessErrorsInDocument(); 5 | }); -------------------------------------------------------------------------------- /test/less/errors/parse-error-curly-bracket.txt: -------------------------------------------------------------------------------- 1 | ParseError: missing opening `{` in {path}parse-error-curly-bracket.less on line 1, column 2: 2 | 1 }} 3 | -------------------------------------------------------------------------------- /test/css/mixins-closure.css: -------------------------------------------------------------------------------- 1 | .class { 2 | width: 99px; 3 | } 4 | .overwrite { 5 | width: 99px; 6 | } 7 | .nested .class { 8 | width: 5px; 9 | } 10 | -------------------------------------------------------------------------------- /test/less/errors/bad-variable-declaration1.txt: -------------------------------------------------------------------------------- 1 | ParseError: Unrecognised input in {path}bad-variable-declaration1.less on line 1, column 1: 2 | 1 @@demo: "hi"; 3 | -------------------------------------------------------------------------------- /test/less/errors/import-no-semi.txt: -------------------------------------------------------------------------------- 1 | ParseError: Unrecognised input in {path}import-no-semi.less on line 1, column 1: 2 | 1 @import "this-statement-is-invalid.less" 3 | -------------------------------------------------------------------------------- /test/less/errors/parse-error-with-import.txt: -------------------------------------------------------------------------------- 1 | ParseError: Unrecognised input in {path}parse-error-with-import.less on line 8, column 9: 2 | 7 3 | 8 nonsense; 4 | 9 5 | -------------------------------------------------------------------------------- /test/browser/runner-relative-urls.js: -------------------------------------------------------------------------------- 1 | less.relativeUrls = true; 2 | describe("less.js browser test - relative url's", function() { 3 | testLessEqualsInDocument(); 4 | }); -------------------------------------------------------------------------------- /test/less/errors/import-missing.less: -------------------------------------------------------------------------------- 1 | .a { 2 | color: green; 3 | // tests line number for import reference is correct 4 | } 5 | 6 | @import "file-does-not-exist.less"; -------------------------------------------------------------------------------- /test/less/errors/mixed-mixin-definition-args-1.less: -------------------------------------------------------------------------------- 1 | .mixin(@a : 4, @b : 3, @c: 2) { 2 | will: fail; 3 | } 4 | .mixin-test { 5 | .mixin(@a: 5; @b: 6, @c: 7); 6 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js text eol=lf 2 | lessc text eol=lf 3 | *.less text eol=lf 4 | *.css text eol=lf 5 | *.htm text eol=lf 6 | 7 | *.jpg binary 8 | *.png binary 9 | *.jpeg binary -------------------------------------------------------------------------------- /test/less/errors/comment-in-selector.txt: -------------------------------------------------------------------------------- 1 | ParseError: Unrecognised input in {path}comment-in-selector.less on line 1, column 21: 2 | 1 #gaga /* Comment */ span { color: red } 3 | -------------------------------------------------------------------------------- /test/less/errors/import-subfolder1.txt: -------------------------------------------------------------------------------- 1 | NameError: .mixin-not-defined is undefined in {path}mixin-not-defined.less on line 11, column 1: 2 | 10 3 | 11 .mixin-not-defined(); 4 | -------------------------------------------------------------------------------- /test/less/errors/mixed-mixin-definition-args-2.less: -------------------------------------------------------------------------------- 1 | .mixin(@a : 4, @b : 3, @c: 2) { 2 | will: fail; 3 | } 4 | .mixin-test { 5 | .mixin(@a: 5, @b: 6; @c: 7); 6 | } 7 | -------------------------------------------------------------------------------- /test/less/errors/mixin-not-defined.txt: -------------------------------------------------------------------------------- 1 | NameError: .mixin-not-defined is undefined in {path}mixin-not-defined.less on line 11, column 1: 2 | 10 3 | 11 .mixin-not-defined(); 4 | -------------------------------------------------------------------------------- /test/browser/runner-legacy.js: -------------------------------------------------------------------------------- 1 | less.strictMath = false; 2 | less.strictUnits = false; 3 | 4 | describe("less.js legacy tests", function() { 5 | testLessEqualsInDocument(); 6 | }); -------------------------------------------------------------------------------- /test/less/errors/parens-error-2.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: expected ')' got '-' in {path}parens-error-2.less on line 2, column 28: 2 | 1 .a { 3 | 2 something: (12 * (13 + 5 -23)); 4 | 3 } 5 | -------------------------------------------------------------------------------- /test/browser/runner-rootpath.js: -------------------------------------------------------------------------------- 1 | less.rootpath = "https://www.github.com/"; 2 | describe("less.js browser test - rootpath url's", function() { 3 | testLessEqualsInDocument(); 4 | }); -------------------------------------------------------------------------------- /test/less/errors/parens-error-1.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: expected ')' got '(' in {path}parens-error-1.less on line 2, column 18: 2 | 1 .a { 3 | 2 something: (12 (13 + 5 -23) + 5); 4 | 3 } 5 | -------------------------------------------------------------------------------- /test/less/errors/parens-error-3.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: expected ')' got '-' in {path}parens-error-3.less on line 2, column 29: 2 | 1 .a { 3 | 2 something: (12 + (13 + 10 -23)); 4 | 3 } 5 | -------------------------------------------------------------------------------- /test/less/errors/parse-error-missing-bracket.txt: -------------------------------------------------------------------------------- 1 | ParseError: missing closing `}` in {path}parse-error-missing-bracket.less on line 3, column 1: 2 | 2 background-color: #fff; 3 | 3 4 | -------------------------------------------------------------------------------- /test/less/import/import-and-relative-paths-test.less: -------------------------------------------------------------------------------- 1 | @import "../css/background.css"; 2 | @import "import-test-d.css"; 3 | 4 | @import "imports/logo"; 5 | @import "imports/font"; 6 | 7 | -------------------------------------------------------------------------------- /test/less/import/imports/font.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: xecret; 3 | src: url('../assets/xecret.ttf'); 4 | } 5 | 6 | #secret { 7 | font-family: xecret, sans-serif; 8 | } 9 | -------------------------------------------------------------------------------- /test/less/errors/property-ie5-hack.txt: -------------------------------------------------------------------------------- 1 | ParseError: Unrecognised input in {path}property-ie5-hack.less on line 2, column 3: 2 | 1 .test { 3 | 2 display/*/: block; /*sorry for IE5*/ 4 | 3 } 5 | -------------------------------------------------------------------------------- /test/css/legacy/legacy.css: -------------------------------------------------------------------------------- 1 | @media (-o-min-device-pixel-ratio: 2/1) { 2 | .test-math-and-units { 3 | font: ignores 0/0 rules; 4 | test-division: 7em; 5 | simple: 2px; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/less/errors/color-operation-error.txt: -------------------------------------------------------------------------------- 1 | OperationError: Can't substract or divide a color from a number in {path}color-operation-error.less on line null, column 0: 2 | 1 prop: (3 / #fff); 3 | -------------------------------------------------------------------------------- /test/less/errors/mixin-not-matched.txt: -------------------------------------------------------------------------------- 1 | RuntimeError: No matching definition was found for `.mixin(trumpete)` in {path}mixin-not-matched.less on line 6, column 1: 2 | 5 3 | 6 .mixin(@saxofon); 4 | -------------------------------------------------------------------------------- /test/less/errors/recursive-variable.txt: -------------------------------------------------------------------------------- 1 | NameError: Recursive variable definition for @bodyColor in {path}recursive-variable.less on line 1, column 20: 2 | 1 @bodyColor: darken(@bodyColor, 30%); 3 | -------------------------------------------------------------------------------- /test/less/import-once.less: -------------------------------------------------------------------------------- 1 | @import "import/import-once-test-c"; 2 | @import "import/import-once-test-c"; 3 | @import "import/import-once-test-c.less"; 4 | @import "import/deeper/import-once-test-a"; 5 | -------------------------------------------------------------------------------- /test/less/errors/extend-not-at-end.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Extend can only be used at the end of selector in {path}extend-not-at-end.less on line 1, column 21: 2 | 1 .a:extend(.b all).c { 3 | 2 property: red; 4 | -------------------------------------------------------------------------------- /test/less/errors/mixin-not-matched2.txt: -------------------------------------------------------------------------------- 1 | RuntimeError: No matching definition was found for `.mixin(@a:trumpete)` in {path}mixin-not-matched2.less on line 6, column 1: 2 | 5 3 | 6 .mixin(@a: @saxofon); 4 | -------------------------------------------------------------------------------- /test/less/errors/property-in-root3.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}property-in-root3.less on line 1, column 1: 2 | 1 prop:1; 3 | 2 .a { 4 | -------------------------------------------------------------------------------- /test/less/legacy/legacy.less: -------------------------------------------------------------------------------- 1 | @media (-o-min-device-pixel-ratio: 2/1) { 2 | .test-math-and-units { 3 | font: ignores 0/0 rules; 4 | test-division: 4 / 2 + 5em; 5 | simple: 1px + 1px; 6 | } 7 | } -------------------------------------------------------------------------------- /test/css/compression/compression.css: -------------------------------------------------------------------------------- 1 | #colours{color1:#fea;color2:#fea;color3:rgba(255,238,170,0.1);string:"#ffeeaa"} 2 | dimensions{val:.1px;val:0;val:4cm;val:.2;val:5;angles-must-have-unit:0deg;width:auto\9} 3 | -------------------------------------------------------------------------------- /test/less/errors/import-missing.txt: -------------------------------------------------------------------------------- 1 | FileError: '{pathhref}file-does-not-exist.less' wasn't found{404status} in {path}import-missing.less on line 6, column 1: 2 | 5 3 | 6 @import "file-does-not-exist.less"; 4 | -------------------------------------------------------------------------------- /test/less/errors/mixin-not-defined.less: -------------------------------------------------------------------------------- 1 | 2 | .error-is-further-on() { 3 | } 4 | 5 | .pad-here-to-reproduce-error-in() { 6 | } 7 | 8 | .the-import-subfolder-test() { 9 | } 10 | 11 | .mixin-not-defined(); -------------------------------------------------------------------------------- /test/less/errors/property-in-root.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}property-in-root.less on line 2, column 3: 2 | 1 .a() { 3 | 2 prop:1; 4 | 3 } 5 | -------------------------------------------------------------------------------- /test/less/errors/property-in-root2.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: properties must be inside selector blocks, they cannot be in the root. in {path}property-in-root.less on line 2, column 3: 2 | 1 .a() { 3 | 2 prop:1; 4 | 3 } 5 | -------------------------------------------------------------------------------- /test/less/import-interpolation.less: -------------------------------------------------------------------------------- 1 | @my_theme: "test"; 2 | 3 | @import "import/import-@{my_theme}-e.less"; 4 | 5 | @import "import/import-@{in}@{terpolation}.less"; 6 | 7 | @in: "in"; 8 | @terpolation: "terpolation"; -------------------------------------------------------------------------------- /test/less/errors/add-mixed-units.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Incompatible units. Change the units or use the unit function. Bad units: 'px' and 'em'. in {path}add-mixed-units.less on line null, column 0: 2 | 1 error: (1px + 3em); 3 | -------------------------------------------------------------------------------- /test/less/errors/extend-no-selector.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Extend must be used to extend a selector, it cannot be used on its own in {path}extend-no-selector.less on line 1, column 17: 2 | 1 :extend(.a all) { 3 | 2 property: red; 4 | -------------------------------------------------------------------------------- /test/less/errors/mixed-mixin-definition-args-1.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Cannot mix ; and , as delimiter types in {path}mixed-mixin-definition-args-1.less on line 5, column 30: 2 | 4 .mixin-test { 3 | 5 .mixin(@a: 5; @b: 6, @c: 7); 4 | 6 } 5 | -------------------------------------------------------------------------------- /test/less/errors/mixed-mixin-definition-args-2.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Cannot mix ; and , as delimiter types in {path}mixed-mixin-definition-args-2.less on line 5, column 26: 2 | 4 .mixin-test { 3 | 5 .mixin(@a: 5, @b: 6; @c: 7); 4 | 6 } 5 | -------------------------------------------------------------------------------- /test/less/errors/parse-error-with-import.less: -------------------------------------------------------------------------------- 1 | @import 'import/import-test.less'; 2 | 3 | body 4 | { 5 | font-family: arial, sans-serif; 6 | } 7 | 8 | nonsense; 9 | 10 | .clickable 11 | { 12 | cursor: pointer; 13 | } -------------------------------------------------------------------------------- /test/browser/runner-production.js: -------------------------------------------------------------------------------- 1 | less.env = "production"; 2 | 3 | describe("less.js production behaviour", function() { 4 | it("doesn't log any messages", function() { 5 | expect(logMessages.length).toEqual(0); 6 | }); 7 | }); -------------------------------------------------------------------------------- /test/less/errors/add-mixed-units2.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Incompatible units. Change the units or use the unit function. Bad units: 'px*px' and 'em*px'. in {path}add-mixed-units2.less on line null, column 0: 2 | 1 error: ((1px * 2px) + (3em * 3px)); 3 | -------------------------------------------------------------------------------- /test/less/errors/divide-mixed-units.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Multiple units in dimension. Correct the units or use the unit function. Bad unit: px/em in {path}divide-mixed-units.less on line 2, column 3: 2 | 1 .a { 3 | 2 error: (1px / 3em); 4 | 3 } 5 | -------------------------------------------------------------------------------- /test/less/errors/javascript-error.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: JavaScript evaluation error: 'TypeError: Cannot call method 'toJS' of undefined' in {path}javascript-error.less on line 2, column 27: 2 | 1 .scope { 3 | 2 var: `this.foo.toJS()`; 4 | 3 } 5 | -------------------------------------------------------------------------------- /test/browser/runner-browser.js: -------------------------------------------------------------------------------- 1 | describe("less.js browser behaviour", function() { 2 | testLessEqualsInDocument(); 3 | 4 | it("has some log messages", function() { 5 | expect(logMessages.length).toBeGreaterThan(0); 6 | }); 7 | }); -------------------------------------------------------------------------------- /test/less/errors/multiply-mixed-units.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Multiple units in dimension. Correct the units or use the unit function. Bad unit: em*px in {path}multiply-mixed-units.less on line 6, column 3: 2 | 5 .a { 3 | 6 error: (1px * 1em); 4 | 7 } 5 | -------------------------------------------------------------------------------- /test/browser/runner-rootpath-relative.js: -------------------------------------------------------------------------------- 1 | less.rootpath = "https://www.github.com/cloudhead/less.js/"; 2 | less.relativeUrls = true; 3 | describe("less.js browser test - rootpath and relative url's", function() { 4 | testLessEqualsInDocument(); 5 | }); -------------------------------------------------------------------------------- /test/css/mixins-nested.css: -------------------------------------------------------------------------------- 1 | .class .inner { 2 | height: 300; 3 | } 4 | .class .inner .innest { 5 | width: 30; 6 | border-width: 60; 7 | } 8 | .class2 .inner { 9 | height: 600; 10 | } 11 | .class2 .inner .innest { 12 | width: 60; 13 | border-width: 120; 14 | } 15 | -------------------------------------------------------------------------------- /test/css/extend-clearfix.css: -------------------------------------------------------------------------------- 1 | .clearfix, 2 | .foo, 3 | .bar { 4 | *zoom: 1; 5 | } 6 | .clearfix:after, 7 | .foo:after, 8 | .bar:after { 9 | content: ''; 10 | display: block; 11 | clear: both; 12 | height: 0; 13 | } 14 | .foo { 15 | color: red; 16 | } 17 | .bar { 18 | color: blue; 19 | } 20 | -------------------------------------------------------------------------------- /test/less/debug/linenumbers.less: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @import "import/test.less"; 4 | 5 | .start() { 6 | .test2 { 7 | color: red; 8 | } 9 | } 10 | 11 | .mix() { 12 | color: black; 13 | } 14 | 15 | .test1 { 16 | .mix(); 17 | } 18 | 19 | .start(); 20 | 21 | .mixin_import1(); 22 | 23 | .mixin_import2(); -------------------------------------------------------------------------------- /test/less/extend-clearfix.less: -------------------------------------------------------------------------------- 1 | .clearfix { 2 | *zoom: 1; 3 | &:after { 4 | content: ''; 5 | display: block; 6 | clear: both; 7 | height: 0; 8 | } 9 | } 10 | 11 | .foo { 12 | &:extend(.clearfix all); 13 | color: red; 14 | } 15 | 16 | .bar { 17 | &:extend(.clearfix all); 18 | color: blue; 19 | } 20 | -------------------------------------------------------------------------------- /test/less/compression/compression.less: -------------------------------------------------------------------------------- 1 | #colours { 2 | color1: #fea; 3 | color2: #ffeeaa; 4 | color3: rgba(255, 238, 170, 0.1); 5 | @color1: #fea; 6 | string: "@{color1}"; 7 | } 8 | dimensions { 9 | val: 0.1px; 10 | val: 0em; 11 | val: 4cm; 12 | val: 0.2; 13 | val: 5; 14 | angles-must-have-unit: 0deg; 15 | width: auto\9; 16 | } -------------------------------------------------------------------------------- /test/less/mixins-nested.less: -------------------------------------------------------------------------------- 1 | .mix-inner (@var) { 2 | border-width: @var; 3 | } 4 | 5 | .mix (@a: 10) { 6 | .inner { 7 | height: (@a * 10); 8 | 9 | .innest { 10 | width: @a; 11 | .mix-inner((@a * 2)); 12 | } 13 | } 14 | } 15 | 16 | .class { 17 | .mix(30); 18 | } 19 | 20 | .class2 { 21 | .mix(60); 22 | } 23 | -------------------------------------------------------------------------------- /lib/less/tree/unicode-descriptor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.UnicodeDescriptor = function (value) { 4 | this.value = value; 5 | }; 6 | tree.UnicodeDescriptor.prototype = { 7 | type: "UnicodeDescriptor", 8 | toCSS: function (env) { 9 | return this.value; 10 | }, 11 | eval: function () { return this } 12 | }; 13 | 14 | })(require('../tree')); 15 | -------------------------------------------------------------------------------- /test/css/javascript.css: -------------------------------------------------------------------------------- 1 | .eval { 2 | js: 42; 3 | js: 2; 4 | js: "hello world"; 5 | js: 1, 2, 3; 6 | title: "string"; 7 | ternary: true; 8 | multiline: 2; 9 | } 10 | .scope { 11 | var: 42; 12 | escaped: 7px; 13 | } 14 | .vars { 15 | width: 8; 16 | } 17 | .escape-interpol { 18 | width: hello world; 19 | } 20 | .arrays { 21 | ary: "1, 2, 3"; 22 | ary1: "1, 2, 3"; 23 | } 24 | -------------------------------------------------------------------------------- /test/less/mixins-important.less: -------------------------------------------------------------------------------- 1 | 2 | .mixin (9) { 3 | border: 9 !important; 4 | } 5 | .mixin (@a: 0) { 6 | border: @a; 7 | boxer: @a; 8 | .inner { 9 | test: @a; 10 | } 11 | // comment 12 | } 13 | 14 | .class { 15 | .mixin(1); 16 | .mixin(2) !important; 17 | .mixin(3); 18 | .mixin(4) !important; 19 | .mixin(5); 20 | .mixin !important; 21 | .mixin(9); 22 | } 23 | -------------------------------------------------------------------------------- /lib/less/tree/comment.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Comment = function (value, silent) { 4 | this.value = value; 5 | this.silent = !!silent; 6 | }; 7 | tree.Comment.prototype = { 8 | type: "Comment", 9 | toCSS: function (env) { 10 | return env.compress ? '' : this.value; 11 | }, 12 | eval: function () { return this } 13 | }; 14 | 15 | })(require('../tree')); 16 | -------------------------------------------------------------------------------- /test/less/extend-media.less: -------------------------------------------------------------------------------- 1 | .ext1 .ext2 { 2 | background: black; 3 | } 4 | 5 | @media tv { 6 | .ext1 .ext3 { 7 | color: white; 8 | } 9 | .tv-lowres :extend(.ext1 all) { 10 | background: blue; 11 | } 12 | @media hires { 13 | .ext1 .ext4 { 14 | color: green; 15 | } 16 | .tv-hires :extend(.ext1 all) { 17 | background: red; 18 | } 19 | } 20 | } 21 | 22 | .all:extend(.ext1 all) { 23 | 24 | } -------------------------------------------------------------------------------- /test/less/mixins-closure.less: -------------------------------------------------------------------------------- 1 | .scope { 2 | @var: 99px; 3 | .mixin () { 4 | width: @var; 5 | } 6 | } 7 | 8 | .class { 9 | .scope > .mixin; 10 | } 11 | 12 | .overwrite { 13 | @var: 0px; 14 | .scope > .mixin; 15 | } 16 | 17 | .nested { 18 | @var: 5px; 19 | .mixin () { 20 | width: @var; 21 | } 22 | .class { 23 | @var: 10px; 24 | .mixin; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/css/extend-media.css: -------------------------------------------------------------------------------- 1 | .ext1 .ext2, 2 | .all .ext2 { 3 | background: black; 4 | } 5 | @media tv { 6 | .ext1 .ext3, 7 | .tv-lowres .ext3, 8 | .all .ext3 { 9 | color: white; 10 | } 11 | .tv-lowres { 12 | background: blue; 13 | } 14 | } 15 | @media tv and hires { 16 | .ext1 .ext4, 17 | .tv-hires .ext4, 18 | .all .ext4 { 19 | color: green; 20 | } 21 | .tv-hires { 22 | background: red; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/css/ie-filters.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=20); 3 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0); 4 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr="#000000", GradientType=0); 5 | } 6 | .evalTest1 { 7 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=30); 8 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=5); 9 | } 10 | -------------------------------------------------------------------------------- /test/browser/runner-main.js: -------------------------------------------------------------------------------- 1 | less.functions = { 2 | add: function (a, b) { 3 | return new(less.tree.Dimension)(a.value + b.value); 4 | }, 5 | increment: function (a) { 6 | return new(less.tree.Dimension)(a.value + 1); 7 | }, 8 | _color: function (str) { 9 | if (str.value === "evil red") { return new(less.tree.Color)("600") } 10 | } 11 | }; 12 | 13 | describe("less.js main tests", function() { 14 | testLessEqualsInDocument(); 15 | }); -------------------------------------------------------------------------------- /test/less/debug/import/test.less: -------------------------------------------------------------------------------- 1 | @charset "ISO-8859-1"; 2 | 3 | .mixin_import1() { 4 | @media all { 5 | .tst { 6 | color: black; 7 | @media screen { 8 | color: red; 9 | .tst3 { 10 | color: white; 11 | } 12 | } 13 | } 14 | } 15 | } 16 | 17 | .mixin_import2() { 18 | .tst2 { 19 | color: white; 20 | } 21 | } 22 | 23 | .tst3 { 24 | color: grey; 25 | } -------------------------------------------------------------------------------- /test/css/mixins-named-args.css: -------------------------------------------------------------------------------- 1 | .named-arg { 2 | color: blue; 3 | width: 5px; 4 | height: 99%; 5 | args: 1px 100%; 6 | text-align: center; 7 | } 8 | .class { 9 | width: 5px; 10 | height: 19%; 11 | args: 1px 20%; 12 | } 13 | .all-args-wrong-args { 14 | width: 10px; 15 | height: 9%; 16 | args: 2px 10%; 17 | } 18 | .named-args2 { 19 | width: 15px; 20 | height: 49%; 21 | color: #646464; 22 | } 23 | .named-args3 { 24 | width: 5px; 25 | height: 29%; 26 | color: #123456; 27 | } 28 | -------------------------------------------------------------------------------- /test/less/ie-filters.less: -------------------------------------------------------------------------------- 1 | @fat: 0; 2 | @cloudhead: "#000000"; 3 | 4 | .nav { 5 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity = 20); 6 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=@fat); 7 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr=@cloudhead, GradientType=@fat); 8 | } 9 | .evalTest(@arg) { 10 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=@arg); 11 | } 12 | .evalTest1 { 13 | .evalTest(30); 14 | .evalTest(5); 15 | } -------------------------------------------------------------------------------- /test/browser/template.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/less/tree/paren.js: -------------------------------------------------------------------------------- 1 | 2 | (function (tree) { 3 | 4 | tree.Paren = function (node) { 5 | this.value = node; 6 | }; 7 | tree.Paren.prototype = { 8 | type: "Paren", 9 | accept: function (visitor) { 10 | this.value = visitor.visit(this.value); 11 | }, 12 | toCSS: function (env) { 13 | return '(' + this.value.toCSS(env).trim() + ')'; 14 | }, 15 | eval: function (env) { 16 | return new(tree.Paren)(this.value.eval(env)); 17 | } 18 | }; 19 | 20 | })(require('../tree')); 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | less.js 2 | ======= 3 | 4 | The **dynamic** stylesheet language. 5 | 6 | 7 | 8 | about 9 | ----- 10 | 11 | This is the JavaScript, and now official, stable version of LESS. 12 | 13 | For more information on the language and usage visit [lesscss.org](http://lesscss.org). More information also available [in our wiki](https://github.com/cloudhead/less.js/wiki) 14 | 15 | license 16 | ------- 17 | 18 | See `LICENSE` file. 19 | 20 | > Copyright (c) 2009-2013 Alexis Sellier & The Core Less Team 21 | -------------------------------------------------------------------------------- /test/css/css-escapes.css: -------------------------------------------------------------------------------- 1 | .escape\|random\|char { 2 | color: red; 3 | } 4 | .mixin\!tUp { 5 | font-weight: bold; 6 | } 7 | .\34 04 { 8 | background: red; 9 | } 10 | .\34 04 strong { 11 | color: #ff00ff; 12 | font-weight: bold; 13 | } 14 | .trailingTest\+ { 15 | color: red; 16 | } 17 | /* This hideous test of hideousness checks for the selector "blockquote" with various permutations of hex escapes */ 18 | \62\6c\6f \63 \6B \0071 \000075o\74 e { 19 | color: silver; 20 | } 21 | [ng\:cloak], 22 | ng\:form { 23 | display: none; 24 | } 25 | -------------------------------------------------------------------------------- /lib/less/tree/alpha.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Alpha = function (val) { 4 | this.value = val; 5 | }; 6 | tree.Alpha.prototype = { 7 | type: "Alpha", 8 | accept: function (visitor) { 9 | this.value = visitor.visit(this.value); 10 | }, 11 | eval: function (env) { 12 | if (this.value.eval) { this.value = this.value.eval(env) } 13 | return this; 14 | }, 15 | toCSS: function () { 16 | return "alpha(opacity=" + 17 | (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; 18 | } 19 | }; 20 | 21 | })(require('../tree')); 22 | -------------------------------------------------------------------------------- /lib/less/tree/keyword.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Keyword = function (value) { this.value = value }; 4 | tree.Keyword.prototype = { 5 | type: "Keyword", 6 | eval: function () { return this; }, 7 | toCSS: function () { return this.value; }, 8 | compare: function (other) { 9 | if (other instanceof tree.Keyword) { 10 | return other.value === this.value ? 0 : 1; 11 | } else { 12 | return -1; 13 | } 14 | } 15 | }; 16 | 17 | tree.True = new(tree.Keyword)('true'); 18 | tree.False = new(tree.Keyword)('false'); 19 | 20 | })(require('../tree')); 21 | -------------------------------------------------------------------------------- /test/less/css-escapes.less: -------------------------------------------------------------------------------- 1 | @ugly: fuchsia; 2 | 3 | .escape\|random\|char { 4 | color: red; 5 | } 6 | 7 | .mixin\!tUp { 8 | font-weight: bold; 9 | } 10 | 11 | // class="404" 12 | .\34 04 { 13 | background: red; 14 | 15 | strong { 16 | color: @ugly; 17 | .mixin\!tUp; 18 | } 19 | } 20 | 21 | .trailingTest\+ { 22 | color: red; 23 | } 24 | 25 | /* This hideous test of hideousness checks for the selector "blockquote" with various permutations of hex escapes */ 26 | \62\6c\6f \63 \6B \0071 \000075o\74 e { 27 | color: silver; 28 | } 29 | 30 | [ng\:cloak], 31 | ng\:form { 32 | display: none; 33 | } 34 | -------------------------------------------------------------------------------- /test/css/extend-exact.css: -------------------------------------------------------------------------------- 1 | .replace.replace .replace, 2 | .c.replace + .replace .replace, 3 | .replace.replace .c, 4 | .c.replace + .replace .c, 5 | .rep_ace { 6 | prop: copy-paste-replace; 7 | } 8 | .a .b .c { 9 | prop: not_effected; 10 | } 11 | .a, 12 | .effected { 13 | prop: is_effected; 14 | } 15 | .a .b { 16 | prop: not_effected; 17 | } 18 | .a .b.c { 19 | prop: not_effected; 20 | } 21 | .c .b .a, 22 | .a .b .a, 23 | .c .a .a, 24 | .a .a .a, 25 | .c .b .c, 26 | .a .b .c, 27 | .c .a .c, 28 | .a .a .c { 29 | prop: not_effected; 30 | } 31 | .e.e, 32 | .dbl { 33 | prop: extend-double; 34 | } 35 | .e.e:hover { 36 | hover: not-extended; 37 | } 38 | -------------------------------------------------------------------------------- /lib/less/tree/negative.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Negative = function (node) { 4 | this.value = node; 5 | }; 6 | tree.Negative.prototype = { 7 | type: "Negative", 8 | accept: function (visitor) { 9 | this.value = visitor.visit(this.value); 10 | }, 11 | toCSS: function (env) { 12 | return '-' + this.value.toCSS(env); 13 | }, 14 | eval: function (env) { 15 | if (env.isMathOn()) { 16 | return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); 17 | } 18 | return new(tree.Negative)(this.value.eval(env)); 19 | } 20 | }; 21 | 22 | })(require('../tree')); 23 | -------------------------------------------------------------------------------- /test/less/rulesets.less: -------------------------------------------------------------------------------- 1 | #first > .one { 2 | > #second .two > #deux { 3 | width: 50%; 4 | #third { 5 | &:focus { 6 | color: black; 7 | #fifth { 8 | > #sixth { 9 | .seventh #eighth { 10 | + #ninth { 11 | color: purple; 12 | } 13 | } 14 | } 15 | } 16 | } 17 | height: 100%; 18 | } 19 | #fourth, #five, #six { 20 | color: #110000; 21 | .seven, .eight > #nine { 22 | border: 1px solid black; 23 | } 24 | #ten { 25 | color: red; 26 | } 27 | } 28 | } 29 | font-size: 2em; 30 | } 31 | -------------------------------------------------------------------------------- /lib/less/tree/assignment.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Assignment = function (key, val) { 4 | this.key = key; 5 | this.value = val; 6 | }; 7 | tree.Assignment.prototype = { 8 | type: "Assignment", 9 | accept: function (visitor) { 10 | this.value = visitor.visit(this.value); 11 | }, 12 | toCSS: function () { 13 | return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); 14 | }, 15 | eval: function (env) { 16 | if (this.value.eval) { 17 | return new(tree.Assignment)(this.key, this.value.eval(env)); 18 | } 19 | return this; 20 | } 21 | }; 22 | 23 | })(require('../tree')); -------------------------------------------------------------------------------- /test/css/scope.css: -------------------------------------------------------------------------------- 1 | .tiny-scope { 2 | color: #998899; 3 | } 4 | .scope1 { 5 | color: #0000ff; 6 | border-color: #000000; 7 | } 8 | .scope1 .scope2 { 9 | color: #0000ff; 10 | } 11 | .scope1 .scope2 .scope3 { 12 | color: #ff0000; 13 | border-color: #000000; 14 | background-color: #ffffff; 15 | } 16 | .scope { 17 | scoped-val: #008000; 18 | } 19 | .heightIsSet { 20 | height: 1024px; 21 | } 22 | .useHeightInMixinCall { 23 | mixin-height: 1024px; 24 | } 25 | .imported { 26 | exists: true; 27 | } 28 | .testImported { 29 | exists: true; 30 | } 31 | #allAreUsedHere { 32 | default: 'top level'; 33 | scope: 'top level'; 34 | sub-scope-only: 'inside'; 35 | } 36 | -------------------------------------------------------------------------------- /test/less/mixins-named-args.less: -------------------------------------------------------------------------------- 1 | .mixin (@a: 1px, @b: 50%) { 2 | width: (@a * 5); 3 | height: (@b - 1%); 4 | args: @arguments; 5 | } 6 | .mixin (@a: 1px, @b: 50%) when (@b > 75%){ 7 | text-align: center; 8 | } 9 | 10 | .named-arg { 11 | color: blue; 12 | .mixin(@b: 100%); 13 | } 14 | 15 | .class { 16 | @var: 20%; 17 | .mixin(@b: @var); 18 | } 19 | 20 | .all-args-wrong-args { 21 | .mixin(@b: 10%, @a: 2px); 22 | } 23 | 24 | .mixin2 (@a: 1px, @b: 50%, @c: 50) { 25 | width: (@a * 5); 26 | height: (@b - 1%); 27 | color: (#000000 + @c); 28 | } 29 | 30 | .named-args2 { 31 | .mixin2(3px, @c: 100); 32 | } 33 | 34 | .named-args3 { 35 | .mixin2(@b: 30%, @c: #123456); 36 | } -------------------------------------------------------------------------------- /lib/less/tree/anonymous.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Anonymous = function (string) { 4 | this.value = string.value || string; 5 | }; 6 | tree.Anonymous.prototype = { 7 | type: "Anonymous", 8 | toCSS: function () { 9 | return this.value; 10 | }, 11 | eval: function () { return this }, 12 | compare: function (x) { 13 | if (!x.toCSS) { 14 | return -1; 15 | } 16 | 17 | var left = this.toCSS(), 18 | right = x.toCSS(); 19 | 20 | if (left === right) { 21 | return 0; 22 | } 23 | 24 | return left < right ? -1 : 1; 25 | } 26 | }; 27 | 28 | })(require('../tree')); 29 | -------------------------------------------------------------------------------- /test/css/mixins-important.css: -------------------------------------------------------------------------------- 1 | .class { 2 | border: 1; 3 | boxer: 1; 4 | border: 2 !important; 5 | boxer: 2 !important; 6 | border: 3; 7 | boxer: 3; 8 | border: 4 !important; 9 | boxer: 4 !important; 10 | border: 5; 11 | boxer: 5; 12 | border: 0 !important; 13 | boxer: 0 !important; 14 | border: 9 !important; 15 | border: 9; 16 | boxer: 9; 17 | } 18 | .class .inner { 19 | test: 1; 20 | } 21 | .class .inner { 22 | test: 2 !important; 23 | } 24 | .class .inner { 25 | test: 3; 26 | } 27 | .class .inner { 28 | test: 4 !important; 29 | } 30 | .class .inner { 31 | test: 5; 32 | } 33 | .class .inner { 34 | test: 0 !important; 35 | } 36 | .class .inner { 37 | test: 9; 38 | } 39 | -------------------------------------------------------------------------------- /test/less/javascript.less: -------------------------------------------------------------------------------- 1 | .eval { 2 | js: `42`; 3 | js: `1 + 1`; 4 | js: `"hello world"`; 5 | js: `[1, 2, 3]`; 6 | title: `typeof process.title`; 7 | ternary: `(1 + 1 == 2 ? true : false)`; 8 | multiline: `(function(){var x = 1 + 1; 9 | return x})()`; 10 | } 11 | .scope { 12 | @foo: 42; 13 | var: `parseInt(this.foo.toJS())`; 14 | escaped: ~`2 + 5 + 'px'`; 15 | } 16 | .vars { 17 | @var: `4 + 4`; 18 | width: @var; 19 | } 20 | .escape-interpol { 21 | @world: "world"; 22 | width: ~`"hello" + " " + @{world}`; 23 | } 24 | .arrays { 25 | @ary: 1, 2, 3; 26 | @ary2: 1 2 3; 27 | ary: `@{ary}.join(', ')`; 28 | ary1: `@{ary2}.join(', ')`; 29 | } 30 | -------------------------------------------------------------------------------- /test/less/import.less: -------------------------------------------------------------------------------- 1 | @import url(http://fonts.googleapis.com/css?family=Open+Sans); 2 | 3 | @import url(/absolute/something.css) screen and (color) and (max-width: 600px); 4 | 5 | @var: 100px; 6 | @import url("//ha.com/file.css") (min-width:@var); 7 | 8 | #import-test { 9 | .mixin; 10 | width: 10px; 11 | height: (@a + 10%); 12 | } 13 | @import "import/import-test-e" screen and (max-width: 600px); 14 | 15 | @import url("import/import-test-a.less"); 16 | 17 | @import (less, multiple) "import/import-test-d.css" screen and (max-width: 601px); 18 | 19 | @import (multiple) "import/import-test-e" screen and (max-width: 602px); 20 | 21 | @import (less, multiple) url("import/import-test-d.css") screen and (max-width: 603px); -------------------------------------------------------------------------------- /test/css/mixins-pattern.css: -------------------------------------------------------------------------------- 1 | .zero { 2 | variadic: true; 3 | zero: 0; 4 | one: 1; 5 | two: 2; 6 | three: 3; 7 | } 8 | .one { 9 | variadic: true; 10 | one: 1; 11 | one-req: 1; 12 | two: 2; 13 | three: 3; 14 | } 15 | .two { 16 | variadic: true; 17 | two: 2; 18 | three: 3; 19 | } 20 | .three { 21 | variadic: true; 22 | three-req: 3; 23 | three: 3; 24 | } 25 | .left { 26 | left: 1; 27 | } 28 | .right { 29 | right: 1; 30 | } 31 | .border-right { 32 | color: black; 33 | border-right: 4px; 34 | } 35 | .border-left { 36 | color: black; 37 | border-left: 4px; 38 | } 39 | .only-right { 40 | right: 33; 41 | } 42 | .only-left { 43 | left: 33; 44 | } 45 | .left-right { 46 | both: 330; 47 | } 48 | -------------------------------------------------------------------------------- /test/css/whitespace.css: -------------------------------------------------------------------------------- 1 | .whitespace { 2 | color: white; 3 | } 4 | .whitespace { 5 | color: white; 6 | } 7 | .whitespace { 8 | color: white; 9 | } 10 | .whitespace { 11 | color: white; 12 | } 13 | .whitespace { 14 | color: white ; 15 | } 16 | .white, 17 | .space, 18 | .mania { 19 | color: white; 20 | } 21 | .no-semi-column { 22 | color: #ffffff; 23 | } 24 | .no-semi-column { 25 | color: white; 26 | white-space: pre; 27 | } 28 | .no-semi-column { 29 | border: 2px solid #ffffff; 30 | } 31 | .newlines { 32 | background: the, 33 | great, 34 | wall; 35 | border: 2px 36 | solid 37 | black; 38 | } 39 | .sel .newline_ws .tab_ws { 40 | color: white; 41 | background-position: 45 -23; 42 | } 43 | -------------------------------------------------------------------------------- /test/less/extend-exact.less: -------------------------------------------------------------------------------- 1 | .replace.replace, 2 | .c.replace + .replace { 3 | .replace, 4 | .c { 5 | prop: copy-paste-replace; 6 | } 7 | } 8 | .rep_ace:extend(.replace.replace .replace) {} 9 | 10 | .a .b .c { 11 | prop: not_effected; 12 | } 13 | 14 | .a { 15 | prop: is_effected; 16 | .b { 17 | prop: not_effected; 18 | } 19 | .b.c { 20 | prop: not_effected; 21 | } 22 | } 23 | 24 | .c, .a { 25 | .b, .a { 26 | .a, .c { 27 | prop: not_effected; 28 | } 29 | } 30 | } 31 | 32 | .effected { 33 | &:extend(.a); 34 | &:extend(.b); 35 | &:extend(.c); 36 | } 37 | 38 | .e { 39 | && { 40 | prop: extend-double; 41 | &:hover { 42 | hover: not-extended; 43 | } 44 | } 45 | } 46 | .dbl:extend(.e.e) {} 47 | -------------------------------------------------------------------------------- /test/less/whitespace.less: -------------------------------------------------------------------------------- 1 | 2 | 3 | .whitespace 4 | { color: white; } 5 | 6 | .whitespace 7 | { 8 | color: white; 9 | } 10 | .whitespace 11 | { color: white; } 12 | 13 | .whitespace{color:white;} 14 | .whitespace { color : white ; } 15 | 16 | .white, 17 | .space, 18 | .mania 19 | { color: white; } 20 | 21 | .no-semi-column { color: white } 22 | .no-semi-column { 23 | color: white; 24 | white-space: pre 25 | } 26 | .no-semi-column {border: 2px solid white} 27 | .newlines { 28 | background: the, 29 | great, 30 | wall; 31 | border: 2px 32 | solid 33 | black; 34 | } 35 | .empty { 36 | 37 | } 38 | .sel 39 | .newline_ws .tab_ws { 40 | color: 41 | white; 42 | background-position: 45 43 | -23; 44 | } 45 | -------------------------------------------------------------------------------- /test/css/debug/linenumbers-comments.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | /* line 3, {pathimport}test.less */ 3 | /* @charset "ISO-8859-1"; */ 4 | /* line 23, {pathimport}test.less */ 5 | .tst3 { 6 | color: grey; 7 | } 8 | /* line 15, {path}linenumbers.less */ 9 | .test1 { 10 | color: black; 11 | } 12 | /* line 6, {path}linenumbers.less */ 13 | .test2 { 14 | color: red; 15 | } 16 | @media all { 17 | /* line 5, {pathimport}test.less */ 18 | .tst { 19 | color: black; 20 | } 21 | } 22 | @media all and screen { 23 | /* line 7, {pathimport}test.less */ 24 | .tst { 25 | color: red; 26 | } 27 | /* line 9, {pathimport}test.less */ 28 | .tst .tst3 { 29 | color: white; 30 | } 31 | } 32 | /* line 18, {pathimport}test.less */ 33 | .tst2 { 34 | color: white; 35 | } 36 | -------------------------------------------------------------------------------- /lib/less/tree/value.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Value = function (value) { 4 | this.value = value; 5 | }; 6 | tree.Value.prototype = { 7 | type: "Value", 8 | accept: function (visitor) { 9 | this.value = visitor.visit(this.value); 10 | }, 11 | eval: function (env) { 12 | if (this.value.length === 1) { 13 | return this.value[0].eval(env); 14 | } else { 15 | return new(tree.Value)(this.value.map(function (v) { 16 | return v.eval(env); 17 | })); 18 | } 19 | }, 20 | toCSS: function (env) { 21 | return this.value.map(function (e) { 22 | return e.toCSS(env); 23 | }).join(env.compress ? ',' : ', '); 24 | } 25 | }; 26 | 27 | })(require('../tree')); 28 | -------------------------------------------------------------------------------- /test/css/import.css: -------------------------------------------------------------------------------- 1 | @import url(http://fonts.googleapis.com/css?family=Open+Sans); 2 | 3 | @import url(/absolute/something.css) screen and (color) and (max-width: 600px); 4 | 5 | @import url("//ha.com/file.css") (min-width: 100px); 6 | #import-test { 7 | height: 10px; 8 | color: #ff0000; 9 | width: 10px; 10 | height: 30%; 11 | } 12 | @media screen and (max-width: 600px) { 13 | body { 14 | width: 100%; 15 | } 16 | } 17 | #import { 18 | color: #ff0000; 19 | } 20 | .mixin { 21 | height: 10px; 22 | color: #ff0000; 23 | } 24 | @media screen and (max-width: 601px) { 25 | #css { 26 | color: yellow; 27 | } 28 | } 29 | @media screen and (max-width: 602px) { 30 | body { 31 | width: 100%; 32 | } 33 | } 34 | @media screen and (max-width: 603px) { 35 | #css { 36 | color: yellow; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/css/parens.css: -------------------------------------------------------------------------------- 1 | .parens { 2 | border: 2px solid #000000; 3 | margin: 1px 3px 16 3; 4 | width: 36; 5 | padding: 2px 36px; 6 | } 7 | .more-parens { 8 | padding: 8 4 4 4px; 9 | width-all: 96; 10 | width-first: 16 * 6; 11 | width-keep: (4 * 4) * 6; 12 | height-keep: (7 * 7) + (8 * 8); 13 | height-all: 113; 14 | height-parts: 49 + 64; 15 | margin-keep: (4 * (5 + 5) / 2) - (4 * 2); 16 | margin-parts: 20 - 8; 17 | margin-all: 12; 18 | border-radius-keep: 4px * (1 + 1) / 4 + 3px; 19 | border-radius-parts: 8px / 7px; 20 | border-radius-all: 5px; 21 | } 22 | .negative { 23 | neg-var: -1; 24 | neg-var-paren: -(1); 25 | } 26 | .nested-parens { 27 | width: 2 * (4 * (2 + (1 + 6))) - 1; 28 | height: ((2 + 3) * (2 + 3) / (9 - 4)) + 1; 29 | } 30 | .mixed-units { 31 | margin: 2px 4em 1 5pc; 32 | padding: 6px 1em 2px 2; 33 | } 34 | -------------------------------------------------------------------------------- /test/css/extend-chaining.css: -------------------------------------------------------------------------------- 1 | .a, 2 | .b, 3 | .c { 4 | color: black; 5 | } 6 | .f, 7 | .e, 8 | .d { 9 | color: black; 10 | } 11 | .g.h, 12 | .i.j.h, 13 | .k.j.h { 14 | color: black; 15 | } 16 | .i.j, 17 | .k.j { 18 | color: white; 19 | } 20 | .l, 21 | .m, 22 | .n, 23 | .o, 24 | .p, 25 | .q, 26 | .r, 27 | .s, 28 | .t { 29 | color: black; 30 | } 31 | .u, 32 | .v.u.v { 33 | color: black; 34 | } 35 | .w, 36 | .v.w.v { 37 | color: black; 38 | } 39 | .x, 40 | .y, 41 | .z { 42 | color: x; 43 | } 44 | .y, 45 | .z, 46 | .x { 47 | color: y; 48 | } 49 | .z, 50 | .x, 51 | .y { 52 | color: z; 53 | } 54 | @media tv { 55 | .ma, 56 | .mb, 57 | .mc { 58 | color: black; 59 | } 60 | .md, 61 | .ma, 62 | .mb, 63 | .mc { 64 | color: white; 65 | } 66 | } 67 | @media tv and plasma { 68 | .me, 69 | .mf { 70 | background: red; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/css/operations.css: -------------------------------------------------------------------------------- 1 | #operations { 2 | color: #111111; 3 | height: 9px; 4 | width: 3em; 5 | substraction: 0; 6 | division: 1; 7 | } 8 | #operations .spacing { 9 | height: 9px; 10 | width: 3em; 11 | } 12 | .with-variables { 13 | height: 16em; 14 | width: 24em; 15 | size: 1cm; 16 | } 17 | .with-functions { 18 | color: #646464; 19 | color: #ff8080; 20 | color: #c94a4a; 21 | } 22 | .negative { 23 | height: 0px; 24 | width: 4px; 25 | } 26 | .shorthands { 27 | padding: -1px 2px 0 -4px; 28 | } 29 | .rem-dimensions { 30 | font-size: 5.5rem; 31 | } 32 | .colors { 33 | color: #123; 34 | border-color: #334455; 35 | background-color: #000000; 36 | } 37 | .colors .other { 38 | color: #222222; 39 | border-color: #222222; 40 | } 41 | .negations { 42 | variable: -4px; 43 | variable1: 0px; 44 | variable2: 0px; 45 | variable3: 8px; 46 | variable4: 0px; 47 | paren: -4px; 48 | paren2: 16px; 49 | } 50 | -------------------------------------------------------------------------------- /test/css/strings.css: -------------------------------------------------------------------------------- 1 | #strings { 2 | background-image: url("http://son-of-a-banana.com"); 3 | quotes: "~" "~"; 4 | content: "#*%:&^,)!.(~*})"; 5 | empty: ""; 6 | brackets: "{" "}"; 7 | escapes: "\"hello\" \\world"; 8 | escapes2: "\"llo"; 9 | } 10 | #comments { 11 | content: "/* hello */ // not-so-secret"; 12 | } 13 | #single-quote { 14 | quotes: "'" "'"; 15 | content: '""#!&""'; 16 | empty: ''; 17 | semi-colon: ';'; 18 | } 19 | #escaped { 20 | filter: DX.Transform.MS.BS.filter(opacity=50); 21 | } 22 | #one-line { 23 | image: url(http://tooks.com); 24 | } 25 | #crazy { 26 | image: url(http://), "}", url("http://}"); 27 | } 28 | #interpolation { 29 | url: "http://lesscss.org/dev/image.jpg"; 30 | url2: "http://lesscss.org/image-256.jpg"; 31 | url3: "http://lesscss.org#445566"; 32 | url4: "http://lesscss.org/hello"; 33 | url5: "http://lesscss.org/54.4px"; 34 | } 35 | .mix-mul-class { 36 | color: #0000ff; 37 | color: #ff0000; 38 | color: #000000; 39 | color: #ffa500; 40 | } 41 | -------------------------------------------------------------------------------- /test/css/variables.css: -------------------------------------------------------------------------------- 1 | .variables { 2 | width: 14cm; 3 | } 4 | .variables { 5 | height: 24px; 6 | color: #888888; 7 | font-family: "Trebuchet MS", Verdana, sans-serif; 8 | quotes: "~" "~"; 9 | } 10 | .redef { 11 | zero: 0; 12 | } 13 | .redef .inition { 14 | three: 3; 15 | } 16 | .values { 17 | minus-one: -1; 18 | font-family: 'Trebuchet', 'Trebuchet', 'Trebuchet'; 19 | color: #888888 !important; 20 | multi: something 'A', B, C, 'Trebuchet'; 21 | } 22 | .variable-names { 23 | name: 'hello'; 24 | } 25 | .alpha { 26 | filter: alpha(opacity=42); 27 | } 28 | .testPollution { 29 | a: 'no-pollution'; 30 | } 31 | .units { 32 | width: 1px; 33 | same-unit-as-previously: 1px; 34 | square-pixel-divided: 1px; 35 | odd-unit: 2; 36 | percentage: 500%; 37 | pixels: 500px; 38 | conversion-metric-a: 30mm; 39 | conversion-metric-b: 3cm; 40 | conversion-imperial: 3in; 41 | custom-unit: 420octocats; 42 | custom-unit-cancelling: 18dogs; 43 | mix-units: 2px; 44 | invalid-units: 1px; 45 | } 46 | -------------------------------------------------------------------------------- /lib/less/tree/url.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.URL = function (val, currentFileInfo) { 4 | this.value = val; 5 | this.currentFileInfo = currentFileInfo; 6 | }; 7 | tree.URL.prototype = { 8 | type: "Url", 9 | accept: function (visitor) { 10 | this.value = visitor.visit(this.value); 11 | }, 12 | toCSS: function () { 13 | return "url(" + this.value.toCSS() + ")"; 14 | }, 15 | eval: function (ctx) { 16 | var val = this.value.eval(ctx), rootpath; 17 | 18 | // Add the base path if the URL is relative 19 | rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; 20 | if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { 21 | if (!val.quote) { 22 | rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); 23 | } 24 | val.value = rootpath + val.value; 25 | } 26 | 27 | return new(tree.URL)(val, null); 28 | } 29 | }; 30 | 31 | })(require('../tree')); 32 | -------------------------------------------------------------------------------- /test/less/extend-nest.less: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | width: 300px; 3 | background: red; 4 | 5 | .box { 6 | background: #FFF; 7 | border: 1px solid #000; 8 | margin: 10px 0; 9 | } 10 | } 11 | 12 | .sidebar2 { 13 | &:extend(.sidebar all); 14 | background: blue; 15 | } 16 | 17 | .type1 { 18 | .sidebar3 { 19 | &:extend(.sidebar all); 20 | background: green; 21 | } 22 | } 23 | 24 | .type2 { 25 | &.sidebar4 { 26 | &:extend(.sidebar all); 27 | background: red; 28 | } 29 | } 30 | 31 | .button { 32 | color: black; 33 | &:hover { 34 | color: white; 35 | } 36 | } 37 | .submit { 38 | &:extend(.button); 39 | &:hover:extend(.button:hover) {} 40 | } 41 | 42 | .nomatch { 43 | &:hover:extend(.button :hover) {} 44 | } 45 | 46 | .button2 { 47 | :hover { 48 | nested: white; 49 | } 50 | } 51 | .button2 :hover { 52 | notnested: black; 53 | } 54 | 55 | .nomatch :extend(.button2:hover) {} 56 | 57 | .amp-test-a, 58 | .amp-test-b { 59 | .amp-test-c &.amp-test-d&.amp-test-e { 60 | .amp-test-f&+&.amp-test-g:extend(.amp-test-h) {} 61 | } 62 | } 63 | .amp-test-h { 64 | test: extended by masses of selectors; 65 | } -------------------------------------------------------------------------------- /test/css/rulesets.css: -------------------------------------------------------------------------------- 1 | #first > .one { 2 | font-size: 2em; 3 | } 4 | #first > .one > #second .two > #deux { 5 | width: 50%; 6 | } 7 | #first > .one > #second .two > #deux #third { 8 | height: 100%; 9 | } 10 | #first > .one > #second .two > #deux #third:focus { 11 | color: black; 12 | } 13 | #first > .one > #second .two > #deux #third:focus #fifth > #sixth .seventh #eighth + #ninth { 14 | color: purple; 15 | } 16 | #first > .one > #second .two > #deux #fourth, 17 | #first > .one > #second .two > #deux #five, 18 | #first > .one > #second .two > #deux #six { 19 | color: #110000; 20 | } 21 | #first > .one > #second .two > #deux #fourth .seven, 22 | #first > .one > #second .two > #deux #five .seven, 23 | #first > .one > #second .two > #deux #six .seven, 24 | #first > .one > #second .two > #deux #fourth .eight > #nine, 25 | #first > .one > #second .two > #deux #five .eight > #nine, 26 | #first > .one > #second .two > #deux #six .eight > #nine { 27 | border: 1px solid black; 28 | } 29 | #first > .one > #second .two > #deux #fourth #ten, 30 | #first > .one > #second .two > #deux #five #ten, 31 | #first > .one > #second .two > #deux #six #ten { 32 | color: red; 33 | } 34 | -------------------------------------------------------------------------------- /test/less/static-urls/urls.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | src: url("/fonts/garamond-pro.ttf"); 3 | src: local(Futura-Medium), 4 | url(fonts.svg#MyGeometricModern) format("svg"); 5 | } 6 | #shorthands { 7 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 8 | } 9 | #misc { 10 | background-image: url(images/image.jpg); 11 | } 12 | #data-uri { 13 | background: url(data:image/png;charset=utf-8;base64, 14 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 15 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 16 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 17 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 18 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 19 | } 20 | 21 | #svg-data-uri { 22 | background: transparent url('data:image/svg+xml, '); 23 | } 24 | 25 | .comma-delimited { 26 | background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg); 27 | } 28 | .values { 29 | @a: 'Trebuchet'; 30 | url: url(@a); 31 | } 32 | 33 | @import "../import/import-and-relative-paths-test"; 34 | -------------------------------------------------------------------------------- /test/browser/less/rootpath/urls.less: -------------------------------------------------------------------------------- 1 | @import "../imports/urls.less"; 2 | @import "http://localhost:8081/browser/less/imports/urls2.less"; 3 | @font-face { 4 | src: url("/fonts/garamond-pro.ttf"); 5 | src: local(Futura-Medium), 6 | url(fonts.svg#MyGeometricModern) format("svg"); 7 | } 8 | #shorthands { 9 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 10 | } 11 | #misc { 12 | background-image: url(images/image.jpg); 13 | } 14 | #data-uri { 15 | background: url(data:image/png;charset=utf-8;base64, 16 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 17 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 18 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 19 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 20 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 21 | } 22 | 23 | #svg-data-uri { 24 | background: transparent url('data:image/svg+xml, '); 25 | } 26 | 27 | .comma-delimited { 28 | background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg); 29 | } 30 | .values { 31 | @a: 'Trebuchet'; 32 | url: url(@a); 33 | } 34 | -------------------------------------------------------------------------------- /test/browser/less/relative-urls/urls.less: -------------------------------------------------------------------------------- 1 | @import ".././imports/urls.less"; 2 | @import "http://localhost:8081/browser/less/imports/urls2.less"; 3 | @font-face { 4 | src: url("/fonts/garamond-pro.ttf"); 5 | src: local(Futura-Medium), 6 | url(fonts.svg#MyGeometricModern) format("svg"); 7 | } 8 | #shorthands { 9 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 10 | } 11 | #misc { 12 | background-image: url(images/image.jpg); 13 | } 14 | #data-uri { 15 | background: url(data:image/png;charset=utf-8;base64, 16 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 17 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 18 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 19 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 20 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 21 | } 22 | 23 | #svg-data-uri { 24 | background: transparent url('data:image/svg+xml, '); 25 | } 26 | 27 | .comma-delimited { 28 | background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg); 29 | } 30 | .values { 31 | @a: 'Trebuchet'; 32 | url: url(@a); 33 | } 34 | -------------------------------------------------------------------------------- /test/browser/less/rootpath-relative/urls.less: -------------------------------------------------------------------------------- 1 | @import "../imports/urls.less"; 2 | @import "http://localhost:8081/browser/less/imports/urls2.less"; 3 | @font-face { 4 | src: url("/fonts/garamond-pro.ttf"); 5 | src: local(Futura-Medium), 6 | url(fonts.svg#MyGeometricModern) format("svg"); 7 | } 8 | #shorthands { 9 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 10 | } 11 | #misc { 12 | background-image: url(images/image.jpg); 13 | } 14 | #data-uri { 15 | background: url(data:image/png;charset=utf-8;base64, 16 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 17 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 18 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 19 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 20 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 21 | } 22 | 23 | #svg-data-uri { 24 | background: transparent url('data:image/svg+xml, '); 25 | } 26 | 27 | .comma-delimited { 28 | background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg); 29 | } 30 | .values { 31 | @a: 'Trebuchet'; 32 | url: url(@a); 33 | } 34 | -------------------------------------------------------------------------------- /test/less/parens.less: -------------------------------------------------------------------------------- 1 | .parens { 2 | @var: 1px; 3 | border: (@var * 2) solid black; 4 | margin: (@var * 1) (@var + 2) (4 * 4) 3; 5 | width: (6 * 6); 6 | padding: 2px (6 * 6px); 7 | } 8 | 9 | .more-parens { 10 | @var: (2 * 2); 11 | padding: (2 * @var) 4 4 (@var * 1px); 12 | width-all: ((@var * @var) * 6); 13 | width-first: ((@var * @var)) * 6; 14 | width-keep: (@var * @var) * 6; 15 | height-keep: (7 * 7) + (8 * 8); 16 | height-all: ((7 * 7) + (8 * 8)); 17 | height-parts: ((7 * 7)) + ((8 * 8)); 18 | margin-keep: (4 * (5 + 5) / 2) - (@var * 2); 19 | margin-parts: ((4 * (5 + 5) / 2)) - ((@var * 2)); 20 | margin-all: ((4 * (5 + 5) / 2) + (-(@var * 2))); 21 | border-radius-keep: 4px * (1 + 1) / @var + 3px; 22 | border-radius-parts: ((4px * (1 + 1))) / ((@var + 3px)); 23 | border-radius-all: (4px * (1 + 1) / @var + 3px); 24 | //margin: (6 * 6)px; 25 | } 26 | 27 | .negative { 28 | @var: 1; 29 | neg-var: -@var; // -1 ? 30 | neg-var-paren: -(@var); // -(1) ? 31 | } 32 | 33 | .nested-parens { 34 | width: 2 * (4 * (2 + (1 + 6))) - 1; 35 | height: ((2 + 3) * (2 + 3) / (9 - 4)) + 1; 36 | } 37 | 38 | .mixed-units { 39 | margin: 2px 4em 1 5pc; 40 | padding: (2px + 4px) 1em 2px 2; 41 | } 42 | -------------------------------------------------------------------------------- /test/css/extend.css: -------------------------------------------------------------------------------- 1 | .error, 2 | .badError { 3 | border: 1px #f00; 4 | background: #fdd; 5 | } 6 | .error.intrusion, 7 | .badError.intrusion { 8 | font-size: 1.3em; 9 | font-weight: bold; 10 | } 11 | .intrusion .error, 12 | .intrusion .badError { 13 | display: none; 14 | } 15 | .badError { 16 | border-width: 3px; 17 | } 18 | .foo .bar, 19 | .foo .baz, 20 | .ext1 .ext2 .bar, 21 | .ext1 .ext2 .baz, 22 | .ext3 .bar, 23 | .ext3 .baz, 24 | .foo .ext3, 25 | .ext4 .bar, 26 | .ext4 .baz, 27 | .foo .ext4 { 28 | display: none; 29 | } 30 | div.ext5, 31 | .ext6 > .ext5, 32 | div.ext7, 33 | .ext6 > .ext7 { 34 | width: 100px; 35 | } 36 | .ext8.ext9, 37 | .fuu { 38 | result: add-foo; 39 | } 40 | .ext8 .ext9, 41 | .ext8 + .ext9, 42 | .ext8 > .ext9, 43 | .buu, 44 | .zap, 45 | .zoo { 46 | result: bar-matched; 47 | } 48 | .ext8.nomatch { 49 | result: none; 50 | } 51 | .ext8 .ext9, 52 | .buu { 53 | result: match-nested-bar; 54 | } 55 | .ext8.ext9, 56 | .fuu { 57 | result: match-nested-foo; 58 | } 59 | .aa, 60 | .cc { 61 | color: black; 62 | } 63 | .aa .dd, 64 | .aa .ee { 65 | background: red; 66 | } 67 | .bb, 68 | .cc, 69 | .ee, 70 | .ff { 71 | background: red; 72 | } 73 | .bb .bb, 74 | .ff .ff { 75 | color: black; 76 | } 77 | -------------------------------------------------------------------------------- /test/less/strings.less: -------------------------------------------------------------------------------- 1 | #strings { 2 | background-image: url("http://son-of-a-banana.com"); 3 | quotes: "~" "~"; 4 | content: "#*%:&^,)!.(~*})"; 5 | empty: ""; 6 | brackets: "{" "}"; 7 | escapes: "\"hello\" \\world"; 8 | escapes2: "\"llo"; 9 | } 10 | #comments { 11 | content: "/* hello */ // not-so-secret"; 12 | } 13 | #single-quote { 14 | quotes: "'" "'"; 15 | content: '""#!&""'; 16 | empty: ''; 17 | semi-colon: ';'; 18 | } 19 | #escaped { 20 | filter: ~"DX.Transform.MS.BS.filter(opacity=50)"; 21 | } 22 | #one-line { image: url(http://tooks.com) } 23 | #crazy { image: url(http://), "}", url("http://}") } 24 | #interpolation { 25 | @var: '/dev'; 26 | url: "http://lesscss.org@{var}/image.jpg"; 27 | 28 | @var2: 256; 29 | url2: "http://lesscss.org/image-@{var2}.jpg"; 30 | 31 | @var3: #456; 32 | url3: "http://lesscss.org@{var3}"; 33 | 34 | @var4: hello; 35 | url4: "http://lesscss.org/@{var4}"; 36 | 37 | @var5: 54.4px; 38 | url5: "http://lesscss.org/@{var5}"; 39 | } 40 | 41 | // multiple calls with string interpolation 42 | 43 | .mix-mul (@a: green) { 44 | color: ~"@{a}"; 45 | } 46 | .mix-mul-class { 47 | .mix-mul(blue); 48 | .mix-mul(red); 49 | .mix-mul(black); 50 | .mix-mul(orange); 51 | } 52 | -------------------------------------------------------------------------------- /lib/less/tree/extend.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Extend = function Extend(selector, option, index) { 4 | this.selector = selector; 5 | this.option = option; 6 | this.index = index; 7 | 8 | switch(option) { 9 | case "all": 10 | this.allowBefore = true; 11 | this.allowAfter = true; 12 | break; 13 | default: 14 | this.allowBefore = false; 15 | this.allowAfter = false; 16 | break; 17 | } 18 | }; 19 | 20 | tree.Extend.prototype = { 21 | type: "Extend", 22 | accept: function (visitor) { 23 | this.selector = visitor.visit(this.selector); 24 | }, 25 | eval: function (env) { 26 | return new(tree.Extend)(this.selector.eval(env), this.option, this.index); 27 | }, 28 | clone: function (env) { 29 | return new(tree.Extend)(this.selector, this.option, this.index); 30 | }, 31 | findSelfSelectors: function (selectors) { 32 | var selfElements = []; 33 | 34 | for(i = 0; i < selectors.length; i++) { 35 | selfElements = selfElements.concat(selectors[i].elements); 36 | } 37 | 38 | this.selfSelectors = [{ elements: selfElements }]; 39 | } 40 | }; 41 | 42 | })(require('../tree')); 43 | -------------------------------------------------------------------------------- /test/css/comments.css: -------------------------------------------------------------------------------- 1 | /******************\ 2 | * * 3 | * Comment Header * 4 | * * 5 | \******************/ 6 | /* 7 | 8 | Comment 9 | 10 | */ 11 | /* 12 | * Comment Test 13 | * 14 | * - cloudhead (http://cloudhead.net) 15 | * 16 | */ 17 | /* Colors 18 | * ------ 19 | * #EDF8FC (background blue) 20 | * #166C89 (darkest blue) 21 | * 22 | * Text: 23 | * #333 (standard text) // A comment within a comment! 24 | * #1F9EC9 (standard link) 25 | * 26 | */ 27 | /* @group Variables 28 | ------------------- */ 29 | #comments { 30 | /**/ 31 | color: red; 32 | /* A C-style comment */ 33 | /* A C-style comment */ 34 | 35 | background-color: orange; 36 | font-size: 12px; 37 | /* lost comment */ 38 | content: "content"; 39 | border: 1px solid black; 40 | padding: 0; 41 | margin: 2em; 42 | } 43 | /* commented out 44 | #more-comments { 45 | color: grey; 46 | } 47 | */ 48 | .selector, 49 | .lots, 50 | .comments { 51 | color: #808080, /* blue */ #ffa500; 52 | -webkit-border-radius: 2px /* webkit only */; 53 | -moz-border-radius: 8px /* moz only with operation */; 54 | } 55 | .test { 56 | color: 1px //put in @b - causes problems! --->; 57 | } 58 | #last { 59 | color: #0000ff; 60 | } 61 | /* *//* { *//* *//* *//* */#div { 62 | color: #A33; 63 | } 64 | /* } */ 65 | -------------------------------------------------------------------------------- /test/css/debug/linenumbers-mediaquery.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000033}} 3 | /* @charset "ISO-8859-1"; */ 4 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000323}} 5 | .tst3 { 6 | color: grey; 7 | } 8 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\0000315}} 9 | .test1 { 10 | color: black; 11 | } 12 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\000036}} 13 | .test2 { 14 | color: red; 15 | } 16 | @media all { 17 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000035}} 18 | .tst { 19 | color: black; 20 | } 21 | } 22 | @media all and screen { 23 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000037}} 24 | .tst { 25 | color: red; 26 | } 27 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000039}} 28 | .tst .tst3 { 29 | color: white; 30 | } 31 | } 32 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000318}} 33 | .tst2 { 34 | color: white; 35 | } 36 | -------------------------------------------------------------------------------- /test/css/colors.css: -------------------------------------------------------------------------------- 1 | #yelow #short { 2 | color: #fea; 3 | } 4 | #yelow #long { 5 | color: #ffeeaa; 6 | } 7 | #yelow #rgba { 8 | color: rgba(255, 238, 170, 0.1); 9 | } 10 | #yelow #argb { 11 | color: #1affeeaa; 12 | } 13 | #blue #short { 14 | color: #00f; 15 | } 16 | #blue #long { 17 | color: #0000ff; 18 | } 19 | #blue #rgba { 20 | color: rgba(0, 0, 255, 0.1); 21 | } 22 | #blue #argb { 23 | color: #1a0000ff; 24 | } 25 | #alpha #hsla { 26 | color: rgba(61, 45, 41, 0.6); 27 | } 28 | #overflow .a { 29 | color: #000000; 30 | } 31 | #overflow .b { 32 | color: #ffffff; 33 | } 34 | #overflow .c { 35 | color: #ffffff; 36 | } 37 | #overflow .d { 38 | color: #00ff00; 39 | } 40 | #grey { 41 | color: #c8c8c8; 42 | } 43 | #333333 { 44 | color: #333333; 45 | } 46 | #808080 { 47 | color: #808080; 48 | } 49 | #00ff00 { 50 | color: #00ff00; 51 | } 52 | .lightenblue { 53 | color: #3333ff; 54 | } 55 | .darkenblue { 56 | color: #0000cc; 57 | } 58 | .unknowncolors { 59 | color: blue2; 60 | border: 2px solid superred; 61 | } 62 | .transparent { 63 | color: transparent; 64 | background-color: rgba(0, 0, 0, 0); 65 | } 66 | #alpha #fromvar { 67 | opacity: 0.7; 68 | } 69 | #alpha #short { 70 | opacity: 1; 71 | } 72 | #alpha #long { 73 | opacity: 1; 74 | } 75 | #alpha #rgba { 76 | opacity: 0.2; 77 | } 78 | #alpha #hsl { 79 | opacity: 1; 80 | } 81 | -------------------------------------------------------------------------------- /test/less/extend.less: -------------------------------------------------------------------------------- 1 | .error { 2 | border: 1px #f00; 3 | background: #fdd; 4 | } 5 | .error.intrusion { 6 | font-size: 1.3em; 7 | font-weight: bold; 8 | } 9 | .intrusion .error { 10 | display: none; 11 | } 12 | .badError { 13 | &:extend(.error all); 14 | border-width: 3px; 15 | } 16 | 17 | .foo .bar, .foo .baz { 18 | display: none; 19 | } 20 | 21 | .ext1 .ext2 { 22 | &:extend(.foo all); 23 | } 24 | 25 | .ext3, 26 | .ext4 { 27 | &:extend(.foo all); 28 | &:extend(.bar all); 29 | } 30 | 31 | div.ext5, 32 | .ext6 > .ext5 { 33 | width: 100px; 34 | } 35 | 36 | .ext7 { 37 | &:extend(.ext5 all); 38 | } 39 | 40 | .ext8.ext9 { 41 | result: add-foo; 42 | } 43 | .ext8 .ext9, 44 | .ext8 + .ext9, 45 | .ext8 > .ext9 { 46 | result: bar-matched; 47 | } 48 | .ext8.nomatch { 49 | result: none; 50 | } 51 | .ext8 { 52 | .ext9 { 53 | result: match-nested-bar; 54 | } 55 | } 56 | .ext8 { 57 | &.ext9 { 58 | result: match-nested-foo; 59 | } 60 | } 61 | 62 | .fuu:extend(.ext8.ext9 all) {} 63 | .buu:extend(.ext8 .ext9 all) {} 64 | .zap:extend(.ext8 + .ext9 all) {} 65 | .zoo:extend(.ext8 > .ext9 all) {} 66 | 67 | .aa { 68 | color: black; 69 | .dd { 70 | background: red; 71 | } 72 | } 73 | .bb { 74 | background: red; 75 | .bb { 76 | color: black; 77 | } 78 | } 79 | .cc:extend(.aa,.bb) {} 80 | .ee:extend(.dd all,.bb) {} 81 | .ff:extend(.dd,.bb all) {} -------------------------------------------------------------------------------- /lib/less/tree/variable.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Variable = function (name, index, currentFileInfo) { this.name = name, this.index = index, this.currentFileInfo = currentFileInfo }; 4 | tree.Variable.prototype = { 5 | type: "Variable", 6 | eval: function (env) { 7 | var variable, v, name = this.name; 8 | 9 | if (name.indexOf('@@') == 0) { 10 | name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; 11 | } 12 | 13 | if (this.evaluating) { 14 | throw { type: 'Name', 15 | message: "Recursive variable definition for " + name, 16 | filename: this.currentFileInfo.file, 17 | index: this.index }; 18 | } 19 | 20 | this.evaluating = true; 21 | 22 | if (variable = tree.find(env.frames, function (frame) { 23 | if (v = frame.variable(name)) { 24 | return v.value.eval(env); 25 | } 26 | })) { 27 | this.evaluating = false; 28 | return variable; 29 | } 30 | else { 31 | throw { type: 'Name', 32 | message: "variable " + name + " is undefined", 33 | filename: this.currentFileInfo.filename, 34 | index: this.index }; 35 | } 36 | } 37 | }; 38 | 39 | })(require('../tree')); 40 | -------------------------------------------------------------------------------- /test/css/extend-selector.css: -------------------------------------------------------------------------------- 1 | .error, 2 | .badError { 3 | border: 1px #f00; 4 | background: #fdd; 5 | } 6 | .error.intrusion, 7 | .badError.intrusion { 8 | font-size: 1.3em; 9 | font-weight: bold; 10 | } 11 | .intrusion .error, 12 | .intrusion .badError { 13 | display: none; 14 | } 15 | .badError { 16 | border-width: 3px; 17 | } 18 | .foo .bar, 19 | .foo .baz, 20 | .ext1 .ext2 .bar, 21 | .ext1 .ext2 .baz, 22 | .ext3 .bar, 23 | .ext3 .baz, 24 | .ext4 .bar, 25 | .ext4 .baz { 26 | display: none; 27 | } 28 | div.ext5, 29 | .ext6 > .ext5, 30 | div.ext7, 31 | .ext6 > .ext7 { 32 | width: 100px; 33 | } 34 | .ext, 35 | .a .c, 36 | .b .c { 37 | test: 1; 38 | } 39 | .a, 40 | .b { 41 | test: 2; 42 | } 43 | .a .c, 44 | .b .c { 45 | test: 3; 46 | } 47 | .a .c .d, 48 | .b .c .d { 49 | test: 4; 50 | } 51 | .replace.replace .replace, 52 | .c.replace + .replace .replace, 53 | .replace.replace .c, 54 | .c.replace + .replace .c, 55 | .rep_ace .rep_ace .rep_ace, 56 | .c.rep_ace + .rep_ace .rep_ace, 57 | .rep_ace .rep_ace .c, 58 | .c.rep_ace + .rep_ace .c { 59 | prop: copy-paste-replace; 60 | } 61 | .attributes [data="test"], 62 | .attributes .attributes .attribute-test { 63 | extend: attributes; 64 | } 65 | .attributes [data], 66 | .attributes .attributes .attribute-test2 { 67 | extend: attributes2; 68 | } 69 | .attributes [data="test3"], 70 | .attributes .attributes .attribute-test { 71 | extend: attributes2; 72 | } 73 | -------------------------------------------------------------------------------- /lib/less/join-selector-visitor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | tree.joinSelectorVisitor = function() { 3 | this.contexts = [[]]; 4 | this._visitor = new tree.visitor(this); 5 | }; 6 | 7 | tree.joinSelectorVisitor.prototype = { 8 | run: function (root) { 9 | return this._visitor.visit(root); 10 | }, 11 | visitRule: function (ruleNode, visitArgs) { 12 | visitArgs.visitDeeper = false; 13 | }, 14 | visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { 15 | visitArgs.visitDeeper = false; 16 | }, 17 | 18 | visitRuleset: function (rulesetNode, visitArgs) { 19 | var context = this.contexts[this.contexts.length - 1]; 20 | var paths = []; 21 | this.contexts.push(paths); 22 | 23 | if (! rulesetNode.root) { 24 | rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); 25 | rulesetNode.paths = paths; 26 | } 27 | }, 28 | visitRulesetOut: function (rulesetNode) { 29 | this.contexts.length = this.contexts.length - 1; 30 | }, 31 | visitMedia: function (mediaNode, visitArgs) { 32 | var context = this.contexts[this.contexts.length - 1]; 33 | mediaNode.ruleset.root = (context.length === 0 || context[0].multiMedia); 34 | } 35 | }; 36 | 37 | })(require('./tree')); -------------------------------------------------------------------------------- /test/browser/css/rootpath/urls.css: -------------------------------------------------------------------------------- 1 | @import "https://www.github.com/modify-this.css"; 2 | 3 | @import "https://www.github.com/modify-again.css"; 4 | .modify { 5 | my-url: url("https://www.github.com/a.png"); 6 | } 7 | .modify { 8 | my-url: url("https://www.github.com/b.png"); 9 | } 10 | @font-face { 11 | src: url("/fonts/garamond-pro.ttf"); 12 | src: local(Futura-Medium), url(https://www.github.com/fonts.svg#MyGeometricModern) format("svg"); 13 | } 14 | #shorthands { 15 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 16 | } 17 | #misc { 18 | background-image: url(https://www.github.com/images/image.jpg); 19 | } 20 | #data-uri { 21 | background: url(data:image/png;charset=utf-8;base64, 22 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 23 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 24 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 25 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 26 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 27 | } 28 | #svg-data-uri { 29 | background: transparent url('data:image/svg+xml, '); 30 | } 31 | .comma-delimited { 32 | background: url(https://www.github.com/bg.jpg) no-repeat, url(https://www.github.com/bg.png) repeat-x top left, url(https://www.github.com/bg); 33 | } 34 | .values { 35 | url: url('https://www.github.com/Trebuchet'); 36 | } 37 | -------------------------------------------------------------------------------- /test/css/mixins-guards.css: -------------------------------------------------------------------------------- 1 | .light1 { 2 | color: white; 3 | margin: 1px; 4 | } 5 | .light2 { 6 | color: black; 7 | margin: 1px; 8 | } 9 | .max1 { 10 | width: 6; 11 | } 12 | .max2 { 13 | width: 8; 14 | } 15 | .glob1 { 16 | margin: auto auto; 17 | } 18 | .ops1 { 19 | height: gt-or-eq; 20 | height: lt-or-eq; 21 | } 22 | .ops2 { 23 | height: gt-or-eq; 24 | height: not-eq; 25 | } 26 | .ops3 { 27 | height: lt-or-eq; 28 | height: not-eq; 29 | } 30 | .default1 { 31 | content: default; 32 | } 33 | .test1 { 34 | content: "true."; 35 | } 36 | .test2 { 37 | content: "false."; 38 | } 39 | .test3 { 40 | content: "false."; 41 | } 42 | .test4 { 43 | content: "false."; 44 | } 45 | .test5 { 46 | content: "false."; 47 | } 48 | .bool1 { 49 | content: true and true; 50 | content: true; 51 | content: false, true; 52 | content: false and true and true, true; 53 | content: false, true and true; 54 | content: false, false, true; 55 | content: false, true and true and true, false; 56 | content: not false; 57 | content: not false and false, not false; 58 | } 59 | .equality-units { 60 | test: pass; 61 | } 62 | .colorguardtest { 63 | content: is #ff0000; 64 | content: is not #0000ff its #ff0000; 65 | content: is not #0000ff its #800080; 66 | } 67 | .stringguardtest { 68 | content: is theme1; 69 | content: is not theme2; 70 | content: is theme1 no quotes; 71 | } 72 | #tryNumberPx { 73 | catch: all; 74 | declare: 4; 75 | declare: 4px; 76 | } 77 | -------------------------------------------------------------------------------- /benchmark/less-benchmark.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | fs = require('fs'), 3 | sys = require('util'); 4 | 5 | var less = require('../lib/less'); 6 | var file = path.join(__dirname, 'benchmark.less'); 7 | 8 | if (process.argv[2]) { file = path.join(process.cwd(), process.argv[2]) } 9 | 10 | fs.readFile(file, 'utf8', function (e, data) { 11 | var tree, css, start, end, total; 12 | 13 | sys.puts("Benchmarking...\n", path.basename(file) + " (" + 14 | parseInt(data.length / 1024) + " KB)", ""); 15 | 16 | start = new(Date); 17 | 18 | new(less.Parser)({ optimization: 2 }).parse(data, function (err, tree) { 19 | end = new(Date); 20 | 21 | total = end - start; 22 | 23 | sys.puts("Parsing: " + 24 | total + " ms (" + 25 | parseInt(1000 / total * 26 | data.length / 1024) + " KB\/s)"); 27 | 28 | start = new(Date); 29 | css = tree.toCSS(); 30 | end = new(Date); 31 | 32 | sys.puts("Generation: " + (end - start) + " ms (" + 33 | parseInt(1000 / (end - start) * 34 | data.length / 1024) + " KB\/s)"); 35 | 36 | total += end - start; 37 | 38 | sys.puts("Total: " + total + "ms (" + 39 | parseInt(1000 / total * data.length / 1024) + " KB/s)"); 40 | 41 | if (err) { 42 | less.writeError(err); 43 | process.exit(3); 44 | } 45 | }); 46 | }); 47 | 48 | -------------------------------------------------------------------------------- /test/css/static-urls/urls.css: -------------------------------------------------------------------------------- 1 | @import "folder (1)/../css/background.css"; 2 | 3 | @import "folder (1)/import-test-d.css"; 4 | @font-face { 5 | src: url("/fonts/garamond-pro.ttf"); 6 | src: local(Futura-Medium), url(folder\ \(1\)/fonts.svg#MyGeometricModern) format("svg"); 7 | } 8 | #shorthands { 9 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 10 | } 11 | #misc { 12 | background-image: url(folder\ \(1\)/images/image.jpg); 13 | } 14 | #data-uri { 15 | background: url(data:image/png;charset=utf-8;base64, 16 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 17 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 18 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 19 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 20 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 21 | } 22 | #svg-data-uri { 23 | background: transparent url('data:image/svg+xml, '); 24 | } 25 | .comma-delimited { 26 | background: url(folder\ \(1\)/bg.jpg) no-repeat, url(folder\ \(1\)/bg.png) repeat-x top left, url(folder\ \(1\)/bg); 27 | } 28 | .values { 29 | url: url('folder (1)/Trebuchet'); 30 | } 31 | #logo { 32 | width: 100px; 33 | height: 100px; 34 | background: url('folder (1)/../assets/logo.png'); 35 | } 36 | @font-face { 37 | font-family: xecret; 38 | src: url('folder (1)/../assets/xecret.ttf'); 39 | } 40 | #secret { 41 | font-family: xecret, sans-serif; 42 | } 43 | -------------------------------------------------------------------------------- /test/less/extend-chaining.less: -------------------------------------------------------------------------------- 1 | //very simple chaining 2 | .a { 3 | color: black; 4 | } 5 | .b:extend(.a) {} 6 | .c:extend(.b) {} 7 | 8 | //very simple chaining, ordering not important 9 | 10 | .d:extend(.e) {} 11 | .e:extend(.f) {} 12 | .f { 13 | color: black; 14 | } 15 | 16 | //extend with all 17 | 18 | .g.h { 19 | color: black; 20 | } 21 | .i.j:extend(.g all) { 22 | color: white; 23 | } 24 | .k:extend(.i all) {} 25 | 26 | //extend multi-chaining 27 | 28 | .l { 29 | color: black; 30 | } 31 | .m:extend(.l){} 32 | .n:extend(.m){} 33 | .o:extend(.n){} 34 | .p:extend(.o){} 35 | .q:extend(.p){} 36 | .r:extend(.q){} 37 | .s:extend(.r){} 38 | .t:extend(.s){} 39 | 40 | // self referencing is ignored 41 | 42 | .u {color: black;} 43 | .v.u.v:extend(.u all){} 44 | 45 | // circular reference because the new extend product will match the existing extend 46 | 47 | .w:extend(.w) {color: black;} 48 | .v.w.v:extend(.w all){} 49 | 50 | // classic circular references 51 | 52 | .x:extend(.z) { 53 | color: x; 54 | } 55 | .y:extend(.x) { 56 | color: y; 57 | } 58 | .z:extend(.y) { 59 | color: z; 60 | } 61 | 62 | // media queries - dont extend outside, do extend inside 63 | 64 | @media tv { 65 | .ma:extend(.a,.b,.c,.d,.e,.f,.g,.h,.i,.j,.k,.l,.m,.n,.o,.p,.q,.r,.s,.t,.u,.v,.w,.x,.y,.z,.md) { 66 | color: black; 67 | } 68 | .md { 69 | color: white; 70 | } 71 | @media plasma { 72 | .me, .mf { 73 | &:extend(.mb,.md); 74 | background: red; 75 | } 76 | } 77 | } 78 | .mb:extend(.ma) {}; 79 | .mc:extend(.mb) {}; -------------------------------------------------------------------------------- /lib/less/tree/quoted.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Quoted = function (str, content, escaped, index, currentFileInfo) { 4 | this.escaped = escaped; 5 | this.value = content || ''; 6 | this.quote = str.charAt(0); 7 | this.index = index; 8 | this.currentFileInfo = currentFileInfo; 9 | }; 10 | tree.Quoted.prototype = { 11 | type: "Quoted", 12 | toCSS: function () { 13 | if (this.escaped) { 14 | return this.value; 15 | } else { 16 | return this.quote + this.value + this.quote; 17 | } 18 | }, 19 | eval: function (env) { 20 | var that = this; 21 | var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { 22 | return new(tree.JavaScript)(exp, that.index, true).eval(env).value; 23 | }).replace(/@\{([\w-]+)\}/g, function (_, name) { 24 | var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); 25 | return (v instanceof tree.Quoted) ? v.value : v.toCSS(); 26 | }); 27 | return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); 28 | }, 29 | compare: function (x) { 30 | if (!x.toCSS) { 31 | return -1; 32 | } 33 | 34 | var left = this.toCSS(), 35 | right = x.toCSS(); 36 | 37 | if (left === right) { 38 | return 0; 39 | } 40 | 41 | return left < right ? -1 : 1; 42 | } 43 | }; 44 | 45 | })(require('../tree')); 46 | -------------------------------------------------------------------------------- /test/less/operations.less: -------------------------------------------------------------------------------- 1 | #operations { 2 | color: (#110000 + #000011 + #001100); // #111111 3 | height: (10px / 2px + 6px - 1px * 2); // 9px 4 | width: (2 * 4 - 5em); // 3em 5 | .spacing { 6 | height: (10px / 2px+6px-1px*2); 7 | width: (2 * 4-5em); 8 | } 9 | substraction: (20 - 10 - 5 - 5); // 0 10 | division: (20 / 5 / 4); // 1 11 | } 12 | 13 | @x: 4; 14 | @y: 12em; 15 | 16 | .with-variables { 17 | height: (@x + @y); // 16em 18 | width: (12 + @y); // 24em 19 | size: (5cm - @x); // 1cm 20 | } 21 | 22 | .with-functions { 23 | color: (rgb(200, 200, 200) / 2); 24 | color: (2 * hsl(0, 50%, 50%)); 25 | color: (rgb(10, 10, 10) + hsl(0, 50%, 50%)); 26 | } 27 | 28 | @z: -2; 29 | 30 | .negative { 31 | height: (2px + @z); // 0px 32 | width: (2px - @z); // 4px 33 | } 34 | 35 | .shorthands { 36 | padding: -1px 2px 0 -4px; // 37 | } 38 | 39 | .rem-dimensions { 40 | font-size: (20rem / 5 + 1.5rem); // 5.5rem 41 | } 42 | 43 | .colors { 44 | color: #123; // #112233 45 | border-color: (#234 + #111111); // #334455 46 | background-color: (#222222 - #fff); // #000000 47 | .other { 48 | color: (2 * #111); // #222222 49 | border-color: (#333333 / 3 + #111); // #222222 50 | } 51 | } 52 | 53 | .negations { 54 | @var: 4px; 55 | variable: (-@var); // 4 56 | variable1: (-@var + @var); // 0 57 | variable2: (@var + -@var); // 0 58 | variable3: (@var - -@var); // 8 59 | variable4: (-@var - -@var); // 0 60 | paren: (-(@var)); // -4px 61 | paren2: (-(2 + 2) * -@var); // 16 62 | } 63 | -------------------------------------------------------------------------------- /test/less/extend-selector.less: -------------------------------------------------------------------------------- 1 | .error { 2 | border: 1px #f00; 3 | background: #fdd; 4 | } 5 | .error.intrusion { 6 | font-size: 1.3em; 7 | font-weight: bold; 8 | } 9 | .intrusion .error { 10 | display: none; 11 | } 12 | .badError:extend(.error all) { 13 | border-width: 3px; 14 | } 15 | 16 | .foo .bar, .foo .baz { 17 | display: none; 18 | } 19 | 20 | .ext1 .ext2 21 | :extend(.foo all) { 22 | } 23 | 24 | .ext3:extend(.foo all), 25 | .ext4:extend(.foo all) { 26 | } 27 | 28 | div.ext5, 29 | .ext6 > .ext5 { 30 | width: 100px; 31 | } 32 | 33 | .should-not-exist-in-output, 34 | .ext7:extend(.ext5 all) { 35 | } 36 | 37 | .ext { 38 | test: 1; 39 | } 40 | // same as 41 | // .a .c:extend(.ext all) 42 | // .b .c:extend(.ext all) 43 | // .a .c .d 44 | // .b .c .d 45 | .a, .b { 46 | test: 2; 47 | .c:extend(.ext all) { 48 | test: 3; 49 | .d { 50 | test: 4; 51 | } 52 | } 53 | } 54 | 55 | .replace.replace, 56 | .c.replace + .replace { 57 | .replace, 58 | .c { 59 | prop: copy-paste-replace; 60 | } 61 | } 62 | .rep_ace:extend(.replace all) {} 63 | 64 | .attributes { 65 | [data="test"] { 66 | extend: attributes; 67 | } 68 | .attribute-test { 69 | &:extend([data="test"] all); 70 | } 71 | [data] { 72 | extend: attributes2; 73 | } 74 | .attribute-test2 { 75 | &:extend([data] all); //you could argue it should match [data="test"]... not for now though... 76 | } 77 | @attr-data: "test3"; 78 | [data=@{attr-data}] { 79 | extend: attributes2; 80 | } 81 | .attribute-test { 82 | &:extend([data="test3"] all); 83 | } 84 | } -------------------------------------------------------------------------------- /test/browser/less/urls.less: -------------------------------------------------------------------------------- 1 | @import "imports/urls.less"; 2 | @import "http://localhost:8081/browser/less/imports/urls2.less"; 3 | @font-face { 4 | src: url("/fonts/garamond-pro.ttf"); 5 | src: local(Futura-Medium), 6 | url(fonts.svg#MyGeometricModern) format("svg"); 7 | } 8 | #shorthands { 9 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 10 | } 11 | #misc { 12 | background-image: url(images/image.jpg); 13 | } 14 | #data-uri { 15 | background: url(data:image/png;charset=utf-8;base64, 16 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 17 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 18 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 19 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 20 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 21 | } 22 | 23 | #svg-data-uri { 24 | background: transparent url('data:image/svg+xml, '); 25 | } 26 | 27 | .comma-delimited { 28 | background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg); 29 | } 30 | .values { 31 | @a: 'Trebuchet'; 32 | url: url(@a); 33 | } 34 | #data-uri { 35 | uri: data-uri('image/jpeg;base64', '../../data/image.jpg'); 36 | } 37 | 38 | #data-uri-guess { 39 | uri: data-uri('../../data/image.jpg'); 40 | } 41 | 42 | #data-uri-ascii { 43 | uri-1: data-uri('text/html', '../../data/page.html'); 44 | uri-2: data-uri('../../data/page.html'); 45 | } 46 | 47 | #data-uri-toobig { 48 | uri: data-uri('../../data/data-uri-fail.png'); 49 | } 50 | -------------------------------------------------------------------------------- /lib/less/tree.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.debugInfo = function(env, ctx) { 4 | var result=""; 5 | if (env.dumpLineNumbers && !env.compress) { 6 | switch(env.dumpLineNumbers) { 7 | case 'comments': 8 | result = tree.debugInfo.asComment(ctx); 9 | break; 10 | case 'mediaquery': 11 | result = tree.debugInfo.asMediaQuery(ctx); 12 | break; 13 | case 'all': 14 | result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); 15 | break; 16 | } 17 | } 18 | return result; 19 | }; 20 | 21 | tree.debugInfo.asComment = function(ctx) { 22 | return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; 23 | }; 24 | 25 | tree.debugInfo.asMediaQuery = function(ctx) { 26 | return '@media -sass-debug-info{filename{font-family:' + 27 | ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function(a){if(a=='\\') a = '\/'; return '\\' + a}) + 28 | '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; 29 | }; 30 | 31 | tree.find = function (obj, fun) { 32 | for (var i = 0, r; i < obj.length; i++) { 33 | if (r = fun.call(obj, obj[i])) { return r } 34 | } 35 | return null; 36 | }; 37 | tree.jsify = function (obj) { 38 | if (Array.isArray(obj.value) && (obj.value.length > 1)) { 39 | return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; 40 | } else { 41 | return obj.toCSS(false); 42 | } 43 | }; 44 | 45 | })(require('./tree')); 46 | -------------------------------------------------------------------------------- /test/less/mixins-pattern.less: -------------------------------------------------------------------------------- 1 | .mixin (...) { 2 | variadic: true; 3 | } 4 | .mixin () { 5 | zero: 0; 6 | } 7 | .mixin (@a: 1px) { 8 | one: 1; 9 | } 10 | .mixin (@a) { 11 | one-req: 1; 12 | } 13 | .mixin (@a: 1px, @b: 2px) { 14 | two: 2; 15 | } 16 | 17 | .mixin (@a, @b, @c) { 18 | three-req: 3; 19 | } 20 | 21 | .mixin (@a: 1px, @b: 2px, @c: 3px) { 22 | three: 3; 23 | } 24 | 25 | .zero { 26 | .mixin(); 27 | } 28 | 29 | .one { 30 | .mixin(1); 31 | } 32 | 33 | .two { 34 | .mixin(1, 2); 35 | } 36 | 37 | .three { 38 | .mixin(1, 2, 3); 39 | } 40 | 41 | // 42 | 43 | .mixout ('left') { 44 | left: 1; 45 | } 46 | 47 | .mixout ('right') { 48 | right: 1; 49 | } 50 | 51 | .left { 52 | .mixout('left'); 53 | } 54 | .right { 55 | .mixout('right'); 56 | } 57 | 58 | // 59 | 60 | .border (@side, @width) { 61 | color: black; 62 | .border-side(@side, @width); 63 | } 64 | .border-side (left, @w) { 65 | border-left: @w; 66 | } 67 | .border-side (right, @w) { 68 | border-right: @w; 69 | } 70 | 71 | .border-right { 72 | .border(right, 4px); 73 | } 74 | .border-left { 75 | .border(left, 4px); 76 | } 77 | 78 | // 79 | 80 | 81 | .border-radius (@r) { 82 | both: (@r * 10); 83 | } 84 | .border-radius (@r, left) { 85 | left: @r; 86 | } 87 | .border-radius (@r, right) { 88 | right: @r; 89 | } 90 | 91 | .only-right { 92 | .border-radius(33, right); 93 | } 94 | .only-left { 95 | .border-radius(33, left); 96 | } 97 | .left-right { 98 | .border-radius(33); 99 | } 100 | -------------------------------------------------------------------------------- /test/browser/css/rootpath-relative/urls.css: -------------------------------------------------------------------------------- 1 | @import "https://www.github.com/cloudhead/imports/modify-this.css"; 2 | 3 | @import "https://www.github.com/cloudhead/imports/modify-again.css"; 4 | .modify { 5 | my-url: url("https://www.github.com/cloudhead/imports/a.png"); 6 | } 7 | .modify { 8 | my-url: url("https://www.github.com/cloudhead/imports/b.png"); 9 | } 10 | @font-face { 11 | src: url("/fonts/garamond-pro.ttf"); 12 | src: local(Futura-Medium), url(https://www.github.com/cloudhead/less.js/fonts.svg#MyGeometricModern) format("svg"); 13 | } 14 | #shorthands { 15 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 16 | } 17 | #misc { 18 | background-image: url(https://www.github.com/cloudhead/less.js/images/image.jpg); 19 | } 20 | #data-uri { 21 | background: url(data:image/png;charset=utf-8;base64, 22 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 23 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 24 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 25 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 26 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 27 | } 28 | #svg-data-uri { 29 | background: transparent url('data:image/svg+xml, '); 30 | } 31 | .comma-delimited { 32 | background: url(https://www.github.com/cloudhead/less.js/bg.jpg) no-repeat, url(https://www.github.com/cloudhead/less.js/bg.png) repeat-x top left, url(https://www.github.com/cloudhead/less.js/bg); 33 | } 34 | .values { 35 | url: url('https://www.github.com/cloudhead/less.js/Trebuchet'); 36 | } 37 | -------------------------------------------------------------------------------- /test/css/debug/linenumbers-all.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | /* line 3, {pathimport}test.less */ 3 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000033}} 4 | /* @charset "ISO-8859-1"; */ 5 | /* line 23, {pathimport}test.less */ 6 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000323}} 7 | .tst3 { 8 | color: grey; 9 | } 10 | /* line 15, {path}linenumbers.less */ 11 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\0000315}} 12 | .test1 { 13 | color: black; 14 | } 15 | /* line 6, {path}linenumbers.less */ 16 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathesc}linenumbers\.less}line{font-family:\000036}} 17 | .test2 { 18 | color: red; 19 | } 20 | @media all { 21 | /* line 5, {pathimport}test.less */ 22 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000035}} 23 | .tst { 24 | color: black; 25 | } 26 | } 27 | @media all and screen { 28 | /* line 7, {pathimport}test.less */ 29 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000037}} 30 | .tst { 31 | color: red; 32 | } 33 | /* line 9, {pathimport}test.less */ 34 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000039}} 35 | .tst .tst3 { 36 | color: white; 37 | } 38 | } 39 | /* line 18, {pathimport}test.less */ 40 | @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000318}} 41 | .tst2 { 42 | color: white; 43 | } 44 | -------------------------------------------------------------------------------- /test/less/comments.less: -------------------------------------------------------------------------------- 1 | /******************\ 2 | * * 3 | * Comment Header * 4 | * * 5 | \******************/ 6 | 7 | /* 8 | 9 | Comment 10 | 11 | */ 12 | 13 | /* 14 | * Comment Test 15 | * 16 | * - cloudhead (http://cloudhead.net) 17 | * 18 | */ 19 | 20 | //////////////// 21 | @var: "content"; 22 | //////////////// 23 | 24 | /* Colors 25 | * ------ 26 | * #EDF8FC (background blue) 27 | * #166C89 (darkest blue) 28 | * 29 | * Text: 30 | * #333 (standard text) // A comment within a comment! 31 | * #1F9EC9 (standard link) 32 | * 33 | */ 34 | 35 | /* @group Variables 36 | ------------------- */ 37 | #comments /* boo */ { 38 | /**/ // An empty comment 39 | color: red; /* A C-style comment */ /* A C-style comment */ 40 | background-color: orange; // A little comment 41 | font-size: 12px; 42 | 43 | /* lost comment */ content: @var; 44 | 45 | border: 1px solid black; 46 | 47 | // padding & margin // 48 | padding: 0; // }{ '" 49 | margin: 2em; 50 | } // 51 | 52 | /* commented out 53 | #more-comments { 54 | color: grey; 55 | } 56 | */ 57 | 58 | .selector /* .with */, .lots, /* of */ .comments { 59 | color: grey, /* blue */ orange; 60 | -webkit-border-radius: 2px /* webkit only */; 61 | -moz-border-radius: (2px * 4) /* moz only with operation */; 62 | } 63 | 64 | .mixin_def_with_colors(@a: white, // in 65 | @b: 1px //put in @b - causes problems! ---> 66 | ) // the 67 | when (@a = white) { 68 | .test { 69 | color: @b; 70 | } 71 | } 72 | .mixin_def_with_colors(); 73 | 74 | #last { color: blue } 75 | // 76 | 77 | /* *//* { *//* *//* *//* */#div { color:#A33; }/* } */ 78 | -------------------------------------------------------------------------------- /lib/less/tree/directive.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Directive = function (name, value) { 4 | this.name = name; 5 | 6 | if (Array.isArray(value)) { 7 | this.ruleset = new(tree.Ruleset)([], value); 8 | this.ruleset.allowImports = true; 9 | } else { 10 | this.value = value; 11 | } 12 | }; 13 | tree.Directive.prototype = { 14 | type: "Directive", 15 | accept: function (visitor) { 16 | this.ruleset = visitor.visit(this.ruleset); 17 | this.value = visitor.visit(this.value); 18 | }, 19 | toCSS: function (env) { 20 | if (this.ruleset) { 21 | this.ruleset.root = true; 22 | return this.name + (env.compress ? '{' : ' {\n ') + 23 | this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') + 24 | (env.compress ? '}': '\n}\n'); 25 | } else { 26 | return this.name + ' ' + this.value.toCSS() + ';\n'; 27 | } 28 | }, 29 | eval: function (env) { 30 | var evaldDirective = this; 31 | if (this.ruleset) { 32 | env.frames.unshift(this); 33 | evaldDirective = new(tree.Directive)(this.name); 34 | evaldDirective.ruleset = this.ruleset.eval(env); 35 | env.frames.shift(); 36 | } 37 | return evaldDirective; 38 | }, 39 | variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, 40 | find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, 41 | rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } 42 | }; 43 | 44 | })(require('../tree')); 45 | -------------------------------------------------------------------------------- /lib/less/tree/expression.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Expression = function (value) { this.value = value; }; 4 | tree.Expression.prototype = { 5 | type: "Expression", 6 | accept: function (visitor) { 7 | this.value = visitor.visit(this.value); 8 | }, 9 | eval: function (env) { 10 | var returnValue, 11 | inParenthesis = this.parens && !this.parensInOp, 12 | doubleParen = false; 13 | if (inParenthesis) { 14 | env.inParenthesis(); 15 | } 16 | if (this.value.length > 1) { 17 | returnValue = new(tree.Expression)(this.value.map(function (e) { 18 | return e.eval(env); 19 | })); 20 | } else if (this.value.length === 1) { 21 | if (this.value[0].parens && !this.value[0].parensInOp) { 22 | doubleParen = true; 23 | } 24 | returnValue = this.value[0].eval(env); 25 | } else { 26 | returnValue = this; 27 | } 28 | if (inParenthesis) { 29 | env.outOfParenthesis(); 30 | } 31 | if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { 32 | returnValue = new(tree.Paren)(returnValue); 33 | } 34 | return returnValue; 35 | }, 36 | toCSS: function (env) { 37 | return this.value.map(function (e) { 38 | return e.toCSS ? e.toCSS(env) : ''; 39 | }).join(' '); 40 | }, 41 | throwAwayComments: function () { 42 | this.value = this.value.filter(function(v) { 43 | return !(v instanceof tree.Comment); 44 | }); 45 | } 46 | }; 47 | 48 | })(require('../tree')); 49 | -------------------------------------------------------------------------------- /test/browser/css/relative-urls/urls.css: -------------------------------------------------------------------------------- 1 | @import "http://localhost:8081/browser/less/imports/modify-this.css"; 2 | 3 | @import "http://localhost:8081/browser/less/imports/modify-again.css"; 4 | .modify { 5 | my-url: url("http://localhost:8081/browser/less/imports/a.png"); 6 | } 7 | .modify { 8 | my-url: url("http://localhost:8081/browser/less/imports/b.png"); 9 | } 10 | @font-face { 11 | src: url("/fonts/garamond-pro.ttf"); 12 | src: local(Futura-Medium), url(http://localhost:8081/browser/less/relative-urls/fonts.svg#MyGeometricModern) format("svg"); 13 | } 14 | #shorthands { 15 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 16 | } 17 | #misc { 18 | background-image: url(http://localhost:8081/browser/less/relative-urls/images/image.jpg); 19 | } 20 | #data-uri { 21 | background: url(data:image/png;charset=utf-8;base64, 22 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 23 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 24 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 25 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 26 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 27 | } 28 | #svg-data-uri { 29 | background: transparent url('data:image/svg+xml, '); 30 | } 31 | .comma-delimited { 32 | background: url(http://localhost:8081/browser/less/relative-urls/bg.jpg) no-repeat, url(http://localhost:8081/browser/less/relative-urls/bg.png) repeat-x top left, url(http://localhost:8081/browser/less/relative-urls/bg); 33 | } 34 | .values { 35 | url: url('http://localhost:8081/browser/less/relative-urls/Trebuchet'); 36 | } 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "less", 3 | "version": "1.4.0-b4", 4 | "description": "Leaner CSS", 5 | "homepage": "http://lesscss.org", 6 | "author": "Alexis Sellier ", 7 | "contributors": [ 8 | "The Core Less Team" 9 | ], 10 | "bugs": { 11 | "url": "https://github.com/cloudhead/less.js/issues" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/cloudhead/less.js.git" 16 | }, 17 | "bin": { 18 | "lessc": "./bin/lessc" 19 | }, 20 | "main": "./lib/less/index", 21 | "directories": { 22 | "test": "./test" 23 | }, 24 | "jam": { 25 | "main": "./dist/less-1.4.0-beta.js" 26 | }, 27 | "engines": { 28 | "node": ">=0.4.2" 29 | }, 30 | "scripts": { 31 | "test": "make test" 32 | }, 33 | "optionalDependencies": { 34 | "mime": "1.2.x", 35 | "request": ">=2.12.0", 36 | "mkdirp": "~0.3.4", 37 | "ycssmin": ">=1.0.1" 38 | }, 39 | "devDependencies": { 40 | "diff": "~1.0" 41 | }, 42 | "keywords": [ 43 | "compile less", 44 | "css nesting", 45 | "css variable", 46 | "css", 47 | "gradients css", 48 | "gradients css3", 49 | "less compiler", 50 | "less css", 51 | "less mixins", 52 | "less", 53 | "less.js", 54 | "lesscss", 55 | "mixins", 56 | "nested css", 57 | "parser", 58 | "preprocessor", 59 | "bootstrap css", 60 | "bootstrap less", 61 | "style", 62 | "styles", 63 | "stylesheet", 64 | "variables in css", 65 | "css less" 66 | ], 67 | "licenses": [ 68 | { 69 | "type": "Apache v2", 70 | "url": "https://github.com/cloudhead/less.js/blob/master/LICENSE" 71 | } 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /lib/less/tree/condition.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Condition = function (op, l, r, i, negate) { 4 | this.op = op.trim(); 5 | this.lvalue = l; 6 | this.rvalue = r; 7 | this.index = i; 8 | this.negate = negate; 9 | }; 10 | tree.Condition.prototype = { 11 | type: "Condition", 12 | accept: function (visitor) { 13 | this.lvalue = visitor.visit(this.lvalue); 14 | this.rvalue = visitor.visit(this.rvalue); 15 | }, 16 | eval: function (env) { 17 | var a = this.lvalue.eval(env), 18 | b = this.rvalue.eval(env); 19 | 20 | var i = this.index, result; 21 | 22 | var result = (function (op) { 23 | switch (op) { 24 | case 'and': 25 | return a && b; 26 | case 'or': 27 | return a || b; 28 | default: 29 | if (a.compare) { 30 | result = a.compare(b); 31 | } else if (b.compare) { 32 | result = b.compare(a); 33 | } else { 34 | throw { type: "Type", 35 | message: "Unable to perform comparison", 36 | index: i }; 37 | } 38 | switch (result) { 39 | case -1: return op === '<' || op === '=<'; 40 | case 0: return op === '=' || op === '>=' || op === '=<'; 41 | case 1: return op === '>' || op === '>='; 42 | } 43 | } 44 | })(this.op); 45 | return this.negate ? !result : result; 46 | } 47 | }; 48 | 49 | })(require('../tree')); 50 | -------------------------------------------------------------------------------- /test/less/variables.less: -------------------------------------------------------------------------------- 1 | @a: 2; 2 | @x: (@a * @a); 3 | @y: (@x + 1); 4 | @z: (@x * 2 + @y); 5 | @var: -1; 6 | 7 | .variables { 8 | width: (@z + 1cm); // 14cm 9 | } 10 | 11 | @b: @a * 10; 12 | @c: #888; 13 | 14 | @fonts: "Trebuchet MS", Verdana, sans-serif; 15 | @f: @fonts; 16 | 17 | @quotes: "~" "~"; 18 | @q: @quotes; 19 | @onePixel: 1px; 20 | 21 | .variables { 22 | height: (@b + @x + 0px); // 24px 23 | color: @c; 24 | font-family: @f; 25 | quotes: @q; 26 | } 27 | 28 | .redef { 29 | @var: 0; 30 | .inition { 31 | @var: 4; 32 | @var: 2; 33 | three: @var; 34 | @var: 3; 35 | } 36 | zero: @var; 37 | } 38 | 39 | .values { 40 | minus-one: @var; 41 | @a: 'Trebuchet'; 42 | @multi: 'A', B, C; 43 | font-family: @a, @a, @a; 44 | color: @c !important; 45 | multi: something @multi, @a; 46 | } 47 | 48 | .variable-names { 49 | @var: 'hello'; 50 | @name: 'var'; 51 | name: @@name; 52 | } 53 | 54 | .alpha { 55 | @var: 42; 56 | filter: alpha(opacity=@var); 57 | } 58 | 59 | .polluteMixin() { 60 | @a: 'pollution'; 61 | } 62 | .testPollution { 63 | @a: 'no-pollution'; 64 | a: @a; 65 | .polluteMixin(); 66 | a: @a; 67 | } 68 | 69 | .units { 70 | width: @onePixel; 71 | same-unit-as-previously: (@onePixel / @onePixel); 72 | square-pixel-divided: (@onePixel * @onePixel / @onePixel); 73 | odd-unit: unit((@onePixel * 4em / 2cm)); 74 | percentage: (10 * 50%); 75 | pixels: (50px * 10); 76 | conversion-metric-a: (20mm + 1cm); 77 | conversion-metric-b: (1cm + 20mm); 78 | conversion-imperial: (1in + 72pt + 6pc); 79 | custom-unit: (42octocats * 10); 80 | custom-unit-cancelling: (8cats * 9dogs / 4cats); 81 | mix-units: (1px + 1em); 82 | invalid-units: (1px * 1px); 83 | } 84 | -------------------------------------------------------------------------------- /test/less/scope.less: -------------------------------------------------------------------------------- 1 | @x: red; 2 | @x: blue; 3 | @z: transparent; 4 | @mix: none; 5 | 6 | .mixin { 7 | @mix: #989; 8 | } 9 | @mix: blue; 10 | .tiny-scope { 11 | color: @mix; // #989 12 | .mixin; 13 | } 14 | 15 | .scope1 { 16 | @y: orange; 17 | @z: black; 18 | color: @x; // blue 19 | border-color: @z; // black 20 | .hidden { 21 | @x: #131313; 22 | } 23 | .scope2 { 24 | @y: red; 25 | color: @x; // blue 26 | .scope3 { 27 | @local: white; 28 | color: @y; // red 29 | border-color: @z; // black 30 | background-color: @local; // white 31 | } 32 | } 33 | } 34 | 35 | #namespace { 36 | .scoped_mixin() { 37 | @local-will-be-made-global: green; 38 | .scope { 39 | scoped-val: @local-will-be-made-global; 40 | } 41 | } 42 | } 43 | 44 | #namespace > .scoped_mixin(); 45 | 46 | .setHeight(@h) { @height: 1024px; } 47 | .useHeightInMixinCall(@h) { .useHeightInMixinCall { mixin-height: @h; } } 48 | @mainHeight: 50%; 49 | .setHeight(@mainHeight); 50 | .heightIsSet { height: @height; } 51 | .useHeightInMixinCall(@height); 52 | 53 | .importRuleset() { 54 | .imported { 55 | exists: true; 56 | } 57 | } 58 | .importRuleset(); 59 | .testImported { 60 | .imported; 61 | } 62 | 63 | @parameterDefault: 'top level'; 64 | @anotherVariable: 'top level'; 65 | //mixin uses top-level variables 66 | .mixinNoParam(@parameter: @parameterDefault) when (@parameter = 'top level') { 67 | default: @parameter; 68 | scope: @anotherVariable; 69 | sub-scope-only: @subScopeOnly; 70 | } 71 | 72 | #allAreUsedHere { 73 | //redefine top-level variables in different scope 74 | @parameterDefault: 'inside'; 75 | @anotherVariable: 'inside'; 76 | @subScopeOnly: 'inside'; 77 | //use the mixin 78 | .mixinNoParam(); 79 | } -------------------------------------------------------------------------------- /test/css/css.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | div { 3 | color: black; 4 | } 5 | div { 6 | width: 99%; 7 | } 8 | * { 9 | min-width: 45em; 10 | } 11 | h1, 12 | h2 > a > p, 13 | h3 { 14 | color: none; 15 | } 16 | div.class { 17 | color: blue; 18 | } 19 | div#id { 20 | color: green; 21 | } 22 | .class#id { 23 | color: purple; 24 | } 25 | .one.two.three { 26 | color: grey; 27 | } 28 | @media print { 29 | * { 30 | font-size: 3em; 31 | } 32 | } 33 | @media screen { 34 | * { 35 | font-size: 10px; 36 | } 37 | } 38 | @font-face { 39 | font-family: 'Garamond Pro'; 40 | } 41 | a:hover, 42 | a:link { 43 | color: #999; 44 | } 45 | p, 46 | p:first-child { 47 | text-transform: none; 48 | } 49 | q:lang(no) { 50 | quotes: none; 51 | } 52 | p + h1 { 53 | font-size: 2.2em; 54 | } 55 | #shorthands { 56 | border: 1px solid #000; 57 | font: 12px/16px Arial; 58 | font: 100%/16px Arial; 59 | margin: 1px 0; 60 | padding: 0 auto; 61 | } 62 | #more-shorthands { 63 | margin: 0; 64 | padding: 1px 0 2px 0; 65 | font: normal small / 20px 'Trebuchet MS', Verdana, sans-serif; 66 | font: 0/0 a; 67 | border-radius: 5px / 10px; 68 | } 69 | .misc { 70 | -moz-border-radius: 2px; 71 | display: -moz-inline-stack; 72 | width: .1em; 73 | background-color: #009998; 74 | background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), to(#0000ff)); 75 | margin: ; 76 | filter: alpha(opacity=100); 77 | width: auto\9; 78 | } 79 | .misc .nested-multiple { 80 | multiple-semi-colons: yes; 81 | } 82 | #important { 83 | color: red !important; 84 | width: 100%!important; 85 | height: 20px ! important; 86 | } 87 | @font-face { 88 | font-family: font-a; 89 | } 90 | @font-face { 91 | font-family: font-b; 92 | } 93 | .æøå { 94 | margin: 0; 95 | } 96 | -------------------------------------------------------------------------------- /test/less/colors.less: -------------------------------------------------------------------------------- 1 | #yelow { 2 | #short { 3 | color: #fea; 4 | } 5 | #long { 6 | color: #ffeeaa; 7 | } 8 | #rgba { 9 | color: rgba(255, 238, 170, 0.1); 10 | } 11 | #argb { 12 | color: argb(rgba(255, 238, 170, 0.1)); 13 | } 14 | } 15 | 16 | #blue { 17 | #short { 18 | color: #00f; 19 | } 20 | #long { 21 | color: #0000ff; 22 | } 23 | #rgba { 24 | color: rgba(0, 0, 255, 0.1); 25 | } 26 | #argb { 27 | color: argb(rgba(0, 0, 255, 0.1)); 28 | } 29 | } 30 | 31 | #alpha #hsla { 32 | color: hsla(11, 20%, 20%, 0.6); 33 | } 34 | 35 | #overflow { 36 | .a { color: (#111111 - #444444); } // #000000 37 | .b { color: (#eee + #fff); } // #ffffff 38 | .c { color: (#aaa * 3); } // #ffffff 39 | .d { color: (#00ee00 + #009900); } // #00ff00 40 | } 41 | 42 | #grey { 43 | color: rgb(200, 200, 200); 44 | } 45 | 46 | #333333 { 47 | color: rgb(20%, 20%, 20%); 48 | } 49 | 50 | #808080 { 51 | color: hsl(50, 0%, 50%); 52 | } 53 | 54 | #00ff00 { 55 | color: hsl(120, 100%, 50%); 56 | } 57 | 58 | .lightenblue { 59 | color: lighten(blue, 10%); 60 | } 61 | 62 | .darkenblue { 63 | color: darken(blue, 10%); 64 | } 65 | 66 | .unknowncolors { 67 | color: blue2; 68 | border: 2px solid superred; 69 | } 70 | 71 | .transparent { 72 | color: transparent; 73 | background-color: rgba(0, 0, 0, 0); 74 | } 75 | #alpha { 76 | @colorvar: rgba(150, 200, 150, 0.7); 77 | #fromvar { 78 | opacity: alpha(@colorvar); 79 | } 80 | #short { 81 | opacity: alpha(#aaa); 82 | } 83 | #long { 84 | opacity: alpha(#bababa); 85 | } 86 | #rgba { 87 | opacity: alpha(rgba(50, 120, 95, 0.2)); 88 | } 89 | #hsl { 90 | opacity: alpha(hsl(120, 100%, 50%)); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/less/tree/operation.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Operation = function (op, operands, isSpaced) { 4 | this.op = op.trim(); 5 | this.operands = operands; 6 | this.isSpaced = isSpaced; 7 | }; 8 | tree.Operation.prototype = { 9 | type: "Operation", 10 | accept: function (visitor) { 11 | this.operands = visitor.visit(this.operands); 12 | }, 13 | eval: function (env) { 14 | var a = this.operands[0].eval(env), 15 | b = this.operands[1].eval(env), 16 | temp; 17 | 18 | if (env.isMathOn()) { 19 | if (a instanceof tree.Dimension && b instanceof tree.Color) { 20 | if (this.op === '*' || this.op === '+') { 21 | temp = b, b = a, a = temp; 22 | } else { 23 | throw { type: "Operation", 24 | message: "Can't substract or divide a color from a number" }; 25 | } 26 | } 27 | if (!a.operate) { 28 | throw { type: "Operation", 29 | message: "Operation on an invalid type" }; 30 | } 31 | 32 | return a.operate(env, this.op, b); 33 | } else { 34 | return new(tree.Operation)(this.op, [a, b], this.isSpaced); 35 | } 36 | }, 37 | toCSS: function (env) { 38 | var separator = this.isSpaced ? " " : ""; 39 | return this.operands[0].toCSS() + separator + this.op + separator + this.operands[1].toCSS(); 40 | } 41 | }; 42 | 43 | tree.operate = function (env, op, a, b) { 44 | switch (op) { 45 | case '+': return a + b; 46 | case '-': return a - b; 47 | case '*': return a * b; 48 | case '/': return a / b; 49 | } 50 | }; 51 | 52 | })(require('../tree')); 53 | -------------------------------------------------------------------------------- /test/less/urls.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | src: url("/fonts/garamond-pro.ttf"); 3 | src: local(Futura-Medium), 4 | url(fonts.svg#MyGeometricModern) format("svg"); 5 | } 6 | #shorthands { 7 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 8 | background: url("img.jpg") center / 100px; 9 | background: #fff url(image.png) center / 1px 100px repeat-x scroll content-box padding-box; 10 | } 11 | #misc { 12 | background-image: url(images/image.jpg); 13 | } 14 | #data-uri { 15 | background: url(data:image/png;charset=utf-8;base64, 16 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 17 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 18 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 19 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 20 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 21 | } 22 | 23 | #svg-data-uri { 24 | background: transparent url('data:image/svg+xml, '); 25 | } 26 | 27 | .comma-delimited { 28 | background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg); 29 | } 30 | .values { 31 | @a: 'Trebuchet'; 32 | url: url(@a); 33 | } 34 | 35 | @import "import/import-and-relative-paths-test"; 36 | 37 | #data-uri { 38 | uri: data-uri('image/jpeg;base64', '../data/image.jpg'); 39 | } 40 | 41 | #data-uri-guess { 42 | uri: data-uri('../data/image.jpg'); 43 | } 44 | 45 | #data-uri-ascii { 46 | uri-1: data-uri('text/html', '../data/page.html'); 47 | uri-2: data-uri('../data/page.html'); 48 | } 49 | 50 | #data-uri-toobig { 51 | uri: data-uri('../data/data-uri-fail.png'); 52 | } 53 | .add_an_import(@file_to_import) { 54 | @import "@{file_to_import}"; 55 | } 56 | 57 | .add_an_import("file.css"); 58 | -------------------------------------------------------------------------------- /lib/less/tree/javascript.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.JavaScript = function (string, index, escaped) { 4 | this.escaped = escaped; 5 | this.expression = string; 6 | this.index = index; 7 | }; 8 | tree.JavaScript.prototype = { 9 | type: "JavaScript", 10 | eval: function (env) { 11 | var result, 12 | that = this, 13 | context = {}; 14 | 15 | var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { 16 | return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); 17 | }); 18 | 19 | try { 20 | expression = new(Function)('return (' + expression + ')'); 21 | } catch (e) { 22 | throw { message: "JavaScript evaluation error: `" + expression + "`" , 23 | index: this.index }; 24 | } 25 | 26 | for (var k in env.frames[0].variables()) { 27 | context[k.slice(1)] = { 28 | value: env.frames[0].variables()[k].value, 29 | toJS: function () { 30 | return this.value.eval(env).toCSS(); 31 | } 32 | }; 33 | } 34 | 35 | try { 36 | result = expression.call(context); 37 | } catch (e) { 38 | throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , 39 | index: this.index }; 40 | } 41 | if (typeof(result) === 'string') { 42 | return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); 43 | } else if (Array.isArray(result)) { 44 | return new(tree.Anonymous)(result.join(', ')); 45 | } else { 46 | return new(tree.Anonymous)(result); 47 | } 48 | } 49 | }; 50 | 51 | })(require('../tree')); 52 | 53 | -------------------------------------------------------------------------------- /lib/less/visitor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.visitor = function(implementation) { 4 | this._implementation = implementation; 5 | }; 6 | 7 | tree.visitor.prototype = { 8 | visit: function(node) { 9 | 10 | if (node instanceof Array) { 11 | return this.visitArray(node); 12 | } 13 | 14 | if (!node || !node.type) { 15 | return node; 16 | } 17 | 18 | var funcName = "visit" + node.type, 19 | func = this._implementation[funcName], 20 | visitArgs, newNode; 21 | if (func) { 22 | visitArgs = {visitDeeper: true}; 23 | newNode = func.call(this._implementation, node, visitArgs); 24 | if (this._implementation.isReplacing) { 25 | node = newNode; 26 | } 27 | } 28 | if ((!visitArgs || visitArgs.visitDeeper) && node && node.accept) { 29 | node.accept(this); 30 | } 31 | funcName = funcName + "Out"; 32 | if (this._implementation[funcName]) { 33 | this._implementation[funcName](node); 34 | } 35 | return node; 36 | }, 37 | visitArray: function(nodes) { 38 | var i, newNodes = []; 39 | for(i = 0; i < nodes.length; i++) { 40 | var evald = this.visit(nodes[i]); 41 | if (evald instanceof Array) { 42 | newNodes = newNodes.concat(evald); 43 | } else { 44 | newNodes.push(evald); 45 | } 46 | } 47 | if (this._implementation.isReplacing) { 48 | return newNodes; 49 | } 50 | return nodes; 51 | } 52 | }; 53 | 54 | })(require('./tree')); -------------------------------------------------------------------------------- /lib/less/tree/selector.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Selector = function (elements, extendList) { 4 | this.elements = elements; 5 | this.extendList = extendList || []; 6 | }; 7 | tree.Selector.prototype = { 8 | type: "Selector", 9 | accept: function (visitor) { 10 | this.elements = visitor.visit(this.elements); 11 | this.extendList = visitor.visit(this.extendList) 12 | }, 13 | match: function (other) { 14 | var elements = this.elements, 15 | len = elements.length, 16 | oelements, olen, max, i; 17 | 18 | oelements = other.elements.slice( 19 | (other.elements.length && other.elements[0].value === "&") ? 1 : 0); 20 | olen = oelements.length; 21 | max = Math.min(len, olen); 22 | 23 | if (olen === 0 || len < olen) { 24 | return false; 25 | } else { 26 | for (i = 0; i < max; i++) { 27 | if (elements[i].value !== oelements[i].value) { 28 | return false; 29 | } 30 | } 31 | } 32 | return true; 33 | }, 34 | eval: function (env) { 35 | return new(tree.Selector)(this.elements.map(function (e) { 36 | return e.eval(env); 37 | }), this.extendList.map(function(extend) { 38 | return extend.eval(env); 39 | })); 40 | }, 41 | toCSS: function (env) { 42 | if (this._css) { return this._css } 43 | 44 | if (this.elements[0].combinator.value === "") { 45 | this._css = ' '; 46 | } else { 47 | this._css = ''; 48 | } 49 | 50 | this._css += this.elements.map(function (e) { 51 | if (typeof(e) === 'string') { 52 | return ' ' + e.trim(); 53 | } else { 54 | return e.toCSS(env); 55 | } 56 | }).join(''); 57 | 58 | return this._css; 59 | } 60 | }; 61 | 62 | })(require('../tree')); 63 | -------------------------------------------------------------------------------- /test/less/css.less: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | div { color: black; } 3 | div { width: 99%; } 4 | 5 | * { 6 | min-width: 45em; 7 | } 8 | 9 | h1, h2 > a > p, h3 { 10 | color: none; 11 | } 12 | 13 | div.class { 14 | color: blue; 15 | } 16 | 17 | div#id { 18 | color: green; 19 | } 20 | 21 | .class#id { 22 | color: purple; 23 | } 24 | 25 | .one.two.three { 26 | color: grey; 27 | } 28 | 29 | @media print { 30 | * { 31 | font-size: 3em; 32 | } 33 | } 34 | 35 | @media screen { 36 | * { 37 | font-size: 10px; 38 | } 39 | } 40 | 41 | @font-face { 42 | font-family: 'Garamond Pro'; 43 | } 44 | 45 | a:hover, a:link { 46 | color: #999; 47 | } 48 | 49 | p, p:first-child { 50 | text-transform: none; 51 | } 52 | 53 | q:lang(no) { 54 | quotes: none; 55 | } 56 | 57 | p + h1 { 58 | font-size: +2.2em; 59 | } 60 | 61 | #shorthands { 62 | border: 1px solid #000; 63 | font: 12px/16px Arial; 64 | font: 100%/16px Arial; 65 | margin: 1px 0; 66 | padding: 0 auto; 67 | } 68 | 69 | #more-shorthands { 70 | margin: 0; 71 | padding: 1px 0 2px 0; 72 | font: normal small/20px 'Trebuchet MS', Verdana, sans-serif; 73 | font: 0/0 a; 74 | border-radius: 5px / 10px; 75 | } 76 | 77 | .misc { 78 | -moz-border-radius: 2px; 79 | display: -moz-inline-stack; 80 | width: .1em; 81 | background-color: #009998; 82 | background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue)); 83 | margin: ; 84 | .nested-multiple { 85 | multiple-semi-colons: yes;;;;;; 86 | }; 87 | filter: alpha(opacity=100); 88 | width: auto\9; 89 | } 90 | 91 | #important { 92 | color: red !important; 93 | width: 100%!important; 94 | height: 20px ! important; 95 | } 96 | 97 | .def-font(@name) { 98 | @font-face { 99 | font-family: @name 100 | } 101 | } 102 | 103 | .def-font(font-a); 104 | .def-font(font-b); 105 | 106 | .æøå { 107 | margin: 0; 108 | } 109 | -------------------------------------------------------------------------------- /test/css/mixins-args.css: -------------------------------------------------------------------------------- 1 | #hidden { 2 | color: transparent; 3 | } 4 | #hidden1 { 5 | color: transparent; 6 | } 7 | .two-args { 8 | color: blue; 9 | width: 10px; 10 | height: 99%; 11 | border: 2px dotted #000000; 12 | } 13 | .one-arg { 14 | width: 15px; 15 | height: 49%; 16 | } 17 | .no-parens { 18 | width: 5px; 19 | height: 49%; 20 | } 21 | .no-args { 22 | width: 5px; 23 | height: 49%; 24 | } 25 | .var-args { 26 | width: 45; 27 | height: 17%; 28 | } 29 | .multi-mix { 30 | width: 10px; 31 | height: 29%; 32 | margin: 4; 33 | padding: 5; 34 | } 35 | body { 36 | padding: 30px; 37 | color: #ff0000; 38 | } 39 | .scope-mix { 40 | width: 8; 41 | } 42 | .content { 43 | width: 600px; 44 | } 45 | .content .column { 46 | margin: 600px; 47 | } 48 | #same-var-name { 49 | radius: 5px; 50 | } 51 | #var-inside { 52 | width: 10px; 53 | } 54 | .arguments { 55 | border: 1px solid #000000; 56 | width: 1px; 57 | } 58 | .arguments2 { 59 | border: 0px; 60 | width: 0px; 61 | } 62 | .arguments3 { 63 | border: 0px; 64 | width: 0px; 65 | } 66 | .arguments4 { 67 | border: 0 1 2 3 4; 68 | rest: 1 2 3 4; 69 | width: 0; 70 | } 71 | .edge-case { 72 | border: "{"; 73 | width: "{"; 74 | } 75 | .slash-vs-math { 76 | border-radius: 2px/5px; 77 | border-radius: 5px/10px; 78 | border-radius: 6px; 79 | } 80 | .comma-vs-semi-colon { 81 | one: a; 82 | two: b, c; 83 | one: d, e; 84 | two: f; 85 | one: g; 86 | one: h; 87 | one: i; 88 | one: j; 89 | one: k; 90 | two: l; 91 | one: m, n; 92 | one: o, p; 93 | two: q; 94 | one: r, s; 95 | two: t; 96 | } 97 | #named-conflict { 98 | four: a, 11, 12, 13; 99 | four: a, 21, 22, 23; 100 | } 101 | .test-mixin-default-arg { 102 | defaults: 1px 1px 1px; 103 | defaults: 2px 2px 2px; 104 | } 105 | .selector { 106 | margin: 2, 2, 2, 2; 107 | } 108 | .selector2 { 109 | margin: 2, 2, 2, 2; 110 | } 111 | .selector3 { 112 | margin: 4; 113 | } 114 | -------------------------------------------------------------------------------- /test/browser/css/urls.css: -------------------------------------------------------------------------------- 1 | @import "http://localhost:8081/browser/less/modify-this.css"; 2 | 3 | @import "http://localhost:8081/browser/less/modify-again.css"; 4 | .modify { 5 | my-url: url("http://localhost:8081/browser/less/a.png"); 6 | } 7 | .modify { 8 | my-url: url("http://localhost:8081/browser/less/b.png"); 9 | } 10 | @font-face { 11 | src: url("/fonts/garamond-pro.ttf"); 12 | src: local(Futura-Medium), url(http://localhost:8081/browser/less/fonts.svg#MyGeometricModern) format("svg"); 13 | } 14 | #shorthands { 15 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 16 | } 17 | #misc { 18 | background-image: url(http://localhost:8081/browser/less/images/image.jpg); 19 | } 20 | #data-uri { 21 | background: url(data:image/png;charset=utf-8;base64, 22 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 23 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 24 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 25 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 26 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 27 | } 28 | #svg-data-uri { 29 | background: transparent url('data:image/svg+xml, '); 30 | } 31 | .comma-delimited { 32 | background: url(http://localhost:8081/browser/less/bg.jpg) no-repeat, url(http://localhost:8081/browser/less/bg.png) repeat-x top left, url(http://localhost:8081/browser/less/bg); 33 | } 34 | .values { 35 | url: url('http://localhost:8081/browser/less/Trebuchet'); 36 | } 37 | #data-uri { 38 | uri: url('http://localhost:8081/browser/less/../../data/image.jpg'); 39 | } 40 | #data-uri-guess { 41 | uri: url('http://localhost:8081/browser/less/../../data/image.jpg'); 42 | } 43 | #data-uri-ascii { 44 | uri-1: url('http://localhost:8081/browser/less/../../data/page.html'); 45 | uri-2: url('http://localhost:8081/browser/less/../../data/page.html'); 46 | } 47 | #data-uri-toobig { 48 | uri: url('http://localhost:8081/browser/less/../../data/data-uri-fail.png'); 49 | } 50 | -------------------------------------------------------------------------------- /test/css/urls.css: -------------------------------------------------------------------------------- 1 | @import "import/../css/background.css"; 2 | 3 | @import "import/import-test-d.css"; 4 | 5 | @import "file.css"; 6 | @font-face { 7 | src: url("/fonts/garamond-pro.ttf"); 8 | src: local(Futura-Medium), url(fonts.svg#MyGeometricModern) format("svg"); 9 | } 10 | #shorthands { 11 | background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; 12 | background: url("img.jpg") center / 100px; 13 | background: #ffffff url(image.png) center / 1px 100px repeat-x scroll content-box padding-box; 14 | } 15 | #misc { 16 | background-image: url(images/image.jpg); 17 | } 18 | #data-uri { 19 | background: url(data:image/png;charset=utf-8;base64, 20 | kiVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/ 21 | k//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U 22 | kg9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC); 23 | background-image: url(data:image/x-png,f9difSSFIIGFIFJD1f982FSDKAA9==); 24 | background-image: url(http://fonts.googleapis.com/css?family=\"Rokkitt\":\(400\),700); 25 | } 26 | #svg-data-uri { 27 | background: transparent url('data:image/svg+xml, '); 28 | } 29 | .comma-delimited { 30 | background: url(bg.jpg) no-repeat, url(bg.png) repeat-x top left, url(bg); 31 | } 32 | .values { 33 | url: url('Trebuchet'); 34 | } 35 | #logo { 36 | width: 100px; 37 | height: 100px; 38 | background: url('import/imports/../assets/logo.png'); 39 | } 40 | @font-face { 41 | font-family: xecret; 42 | src: url('import/imports/../assets/xecret.ttf'); 43 | } 44 | #secret { 45 | font-family: xecret, sans-serif; 46 | } 47 | #data-uri { 48 | uri: url('data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg=='); 49 | } 50 | #data-uri-guess { 51 | uri: url('data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg=='); 52 | } 53 | #data-uri-ascii { 54 | uri-1: url('data:text/html,%3Ch1%3EThis%20page%20is%20100%25%20Awesome.%3C%2Fh1%3E%0A'); 55 | uri-2: url('data:text/html,%3Ch1%3EThis%20page%20is%20100%25%20Awesome.%3C%2Fh1%3E%0A'); 56 | } 57 | #data-uri-toobig { 58 | uri: url('../data/data-uri-fail.png'); 59 | } 60 | -------------------------------------------------------------------------------- /test/browser-test-prepare.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | fs = require('fs'), 3 | sys = require('util'); 4 | 5 | var readDirFilesSync = function(dir, regex, callback) { 6 | fs.readdirSync(dir).forEach(function (file) { 7 | if (! regex.test(file)) { return; } 8 | callback(file); 9 | }); 10 | } 11 | 12 | var createTestRunnerPage = function(dir, exclude, testSuiteName, dir2) { 13 | var output = '\n'; 14 | 15 | readDirFilesSync(path.join("test", dir, 'less', dir2 || ""), /\.less$/, function (file) { 16 | var name = path.basename(file, '.less'), 17 | id = (dir ? dir + '-' : "") + 'less-' + (dir2 ? dir2 + "-" : "") + name; 18 | 19 | if (exclude && name.match(exclude)) { return; } 20 | 21 | output += '\n'; 22 | output += '\n'; 23 | }); 24 | 25 | output += String(fs.readFileSync(path.join('test/browser', 'template.htm'))).replace("{runner-name}", testSuiteName); 26 | 27 | fs.writeFileSync(path.join('test/browser', 'test-runner-'+testSuiteName+'.htm'), output); 28 | }; 29 | 30 | var removeFiles = function(dir, regex) { 31 | readDirFilesSync(dir, regex, function(file) { 32 | fs.unlinkSync(path.join(dir, file), function() { 33 | console.log("Failed to delete " + file); 34 | }); 35 | }); 36 | } 37 | 38 | removeFiles("test/browser", /test-runner-[a-zA-Z-]*\.htm$/); 39 | createTestRunnerPage("", /javascript|urls/, "main"); 40 | createTestRunnerPage("", null, "legacy", "legacy"); 41 | createTestRunnerPage("", /javascript/, "errors", "errors"); 42 | createTestRunnerPage("browser", null, "browser"); 43 | createTestRunnerPage("browser", null, "relative-urls", "relative-urls"); 44 | createTestRunnerPage("browser", null, "rootpath", "rootpath"); 45 | createTestRunnerPage("browser", null, "rootpath-relative", "rootpath-relative"); 46 | createTestRunnerPage("browser", null, "production"); -------------------------------------------------------------------------------- /lib/less/tree/rule.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Rule = function (name, value, important, index, currentFileInfo, inline) { 4 | this.name = name; 5 | this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); 6 | this.important = important ? ' ' + important.trim() : ''; 7 | this.index = index; 8 | this.currentFileInfo = currentFileInfo; 9 | this.inline = inline || false; 10 | 11 | if (name.charAt(0) === '@') { 12 | this.variable = true; 13 | } else { this.variable = false } 14 | }; 15 | 16 | tree.Rule.prototype = { 17 | type: "Rule", 18 | accept: function (visitor) { 19 | this.value = visitor.visit(this.value); 20 | }, 21 | toCSS: function (env) { 22 | if (this.variable) { return "" } 23 | else { 24 | try { 25 | return this.name + (env.compress ? ':' : ': ') + 26 | this.value.toCSS(env) + 27 | this.important + (this.inline ? "" : ";"); 28 | } 29 | catch(e) { 30 | e.index = this.index; 31 | e.filename = this.currentFileInfo.filename; 32 | throw e; 33 | } 34 | } 35 | }, 36 | eval: function (env) { 37 | var strictMathBypass = false; 38 | if (this.name === "font" && env.strictMath === false) { 39 | strictMathBypass = true; 40 | env.strictMath = true; 41 | } 42 | try { 43 | return new(tree.Rule)(this.name, 44 | this.value.eval(env), 45 | this.important, 46 | this.index, this.currentFileInfo, this.inline); 47 | } 48 | finally { 49 | if (strictMathBypass) { 50 | env.strictMath = false; 51 | } 52 | } 53 | }, 54 | makeImportant: function () { 55 | return new(tree.Rule)(this.name, 56 | this.value, 57 | "!important", 58 | this.index, this.currentFileInfo, this.inline); 59 | } 60 | }; 61 | 62 | })(require('../tree')); 63 | -------------------------------------------------------------------------------- /lib/less/tree/call.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | // 4 | // A function call node. 5 | // 6 | tree.Call = function (name, args, index, currentFileInfo) { 7 | this.name = name; 8 | this.args = args; 9 | this.index = index; 10 | this.currentFileInfo = currentFileInfo; 11 | }; 12 | tree.Call.prototype = { 13 | type: "Call", 14 | accept: function (visitor) { 15 | this.args = visitor.visit(this.args); 16 | }, 17 | // 18 | // When evaluating a function call, 19 | // we either find the function in `tree.functions` [1], 20 | // in which case we call it, passing the evaluated arguments, 21 | // if this returns null or we cannot find the function, we 22 | // simply print it out as it appeared originally [2]. 23 | // 24 | // The *functions.js* file contains the built-in functions. 25 | // 26 | // The reason why we evaluate the arguments, is in the case where 27 | // we try to pass a variable to a function, like: `saturate(@color)`. 28 | // The function should receive the value, not the variable. 29 | // 30 | eval: function (env) { 31 | var args = this.args.map(function (a) { return a.eval(env); }), 32 | nameLC = this.name.toLowerCase(), 33 | result, func; 34 | 35 | if (nameLC in tree.functions) { // 1. 36 | try { 37 | func = new tree.functionCall(env, this.currentFileInfo); 38 | result = func[nameLC].apply(func, args); 39 | if (result != null) { 40 | return result; 41 | } 42 | } catch (e) { 43 | throw { type: e.type || "Runtime", 44 | message: "error evaluating function `" + this.name + "`" + 45 | (e.message ? ': ' + e.message : ''), 46 | index: this.index, filename: this.currentFileInfo.filename }; 47 | } 48 | } 49 | 50 | // 2. 51 | return new(tree.Anonymous)(this.name + 52 | "(" + args.map(function (a) { return a.toCSS(env); }).join(', ') + ")"); 53 | }, 54 | 55 | toCSS: function (env) { 56 | return this.eval(env).toCSS(); 57 | } 58 | }; 59 | 60 | })(require('../tree')); 61 | -------------------------------------------------------------------------------- /test/css/mixins.css: -------------------------------------------------------------------------------- 1 | .mixin { 2 | border: 1px solid black; 3 | } 4 | .mixout { 5 | border-color: orange; 6 | } 7 | .borders { 8 | border-style: dashed; 9 | } 10 | #namespace .borders { 11 | border-style: dotted; 12 | } 13 | #namespace .biohazard { 14 | content: "death"; 15 | } 16 | #namespace .biohazard .man { 17 | color: transparent; 18 | } 19 | #theme > .mixin { 20 | background-color: grey; 21 | } 22 | #container { 23 | color: black; 24 | border: 1px solid black; 25 | border-color: orange; 26 | background-color: grey; 27 | } 28 | #header .milk { 29 | color: white; 30 | border: 1px solid black; 31 | background-color: grey; 32 | } 33 | #header #cookie { 34 | border-style: dashed; 35 | } 36 | #header #cookie .chips { 37 | border-style: dotted; 38 | } 39 | #header #cookie .chips .calories { 40 | color: black; 41 | border: 1px solid black; 42 | border-color: orange; 43 | background-color: grey; 44 | } 45 | .secure-zone { 46 | color: transparent; 47 | } 48 | .direct { 49 | border-style: dotted; 50 | } 51 | .bo, 52 | .bar { 53 | width: 100%; 54 | } 55 | .bo { 56 | border: 1px; 57 | } 58 | .ar.bo.ca { 59 | color: black; 60 | } 61 | .jo.ki { 62 | background: none; 63 | } 64 | .amp.support { 65 | color: orange; 66 | } 67 | .extended { 68 | width: 100%; 69 | border: 1px; 70 | background: none; 71 | color: orange; 72 | } 73 | .foo .bar { 74 | width: 100%; 75 | } 76 | .underParents { 77 | color: red; 78 | } 79 | .parent .underParents { 80 | color: red; 81 | } 82 | * + h1 { 83 | margin-top: 25px; 84 | } 85 | legend + h1 { 86 | margin-top: 0; 87 | } 88 | h1 + * { 89 | margin-top: 10px; 90 | } 91 | * + h2 { 92 | margin-top: 20px; 93 | } 94 | legend + h2 { 95 | margin-top: 0; 96 | } 97 | h2 + * { 98 | margin-top: 8px; 99 | } 100 | * + h3 { 101 | margin-top: 15px; 102 | } 103 | legend + h3 { 104 | margin-top: 0; 105 | } 106 | h3 + * { 107 | margin-top: 5px; 108 | } 109 | .error { 110 | background-image: "/a.png"; 111 | background-position: center center; 112 | } 113 | .test-rec .recursion { 114 | color: black; 115 | } 116 | .button { 117 | padding-left: 44px; 118 | } 119 | .button.large { 120 | padding-left: 40em; 121 | } 122 | -------------------------------------------------------------------------------- /test/less/mixins.less: -------------------------------------------------------------------------------- 1 | .mixin { border: 1px solid black; } 2 | .mixout { border-color: orange; } 3 | .borders { border-style: dashed; } 4 | 5 | #namespace { 6 | .borders { 7 | border-style: dotted; 8 | } 9 | .biohazard { 10 | content: "death"; 11 | .man { 12 | color: transparent; 13 | } 14 | } 15 | } 16 | #theme { 17 | > .mixin { 18 | background-color: grey; 19 | } 20 | } 21 | #container { 22 | color: black; 23 | .mixin; 24 | .mixout; 25 | #theme > .mixin; 26 | } 27 | 28 | #header { 29 | .milk { 30 | color: white; 31 | .mixin; 32 | #theme > .mixin; 33 | } 34 | #cookie { 35 | .chips { 36 | #namespace .borders; 37 | .calories { 38 | #container; 39 | } 40 | } 41 | .borders; 42 | } 43 | } 44 | .secure-zone { #namespace .biohazard .man; } 45 | .direct { 46 | #namespace > .borders; 47 | } 48 | 49 | .bo, .bar { 50 | width: 100%; 51 | } 52 | .bo { 53 | border: 1px; 54 | } 55 | .ar.bo.ca { 56 | color: black; 57 | } 58 | .jo.ki { 59 | background: none; 60 | } 61 | .amp { 62 | &.support { 63 | color: orange; 64 | } 65 | } 66 | .extended { 67 | .bo; 68 | .jo.ki; 69 | .amp.support; 70 | } 71 | .foo .bar { 72 | .bar; 73 | } 74 | .has_parents() { 75 | & .underParents { 76 | color: red; 77 | } 78 | } 79 | .has_parents(); 80 | .parent { 81 | .has_parents(); 82 | } 83 | .margin_between(@above, @below) { 84 | * + & { margin-top: @above; } 85 | legend + & { margin-top: 0; } 86 | & + * { margin-top: @below; } 87 | } 88 | h1 { .margin_between(25px, 10px); } 89 | h2 { .margin_between(20px, 8px); } 90 | h3 { .margin_between(15px, 5px); } 91 | 92 | .mixin_def(@url, @position){ 93 | background-image: @url; 94 | background-position: @position; 95 | } 96 | .error{ 97 | @s: "/"; 98 | .mixin_def( "@{s}a.png", center center); 99 | } 100 | .recursion() { 101 | color: black; 102 | } 103 | .test-rec { 104 | .recursion { 105 | .recursion(); 106 | } 107 | } 108 | .paddingFloat(@padding) { padding-left: @padding; } 109 | 110 | .button { 111 | .paddingFloat(((10px + 12) * 2)); 112 | 113 | &.large { .paddingFloat(((10em * 2) * 2)); } 114 | } 115 | -------------------------------------------------------------------------------- /test/less/selectors.less: -------------------------------------------------------------------------------- 1 | h1, h2, h3 { 2 | a, p { 3 | &:hover { 4 | color: red; 5 | } 6 | } 7 | } 8 | 9 | #all { color: blue; } 10 | #the { color: blue; } 11 | #same { color: blue; } 12 | 13 | ul, li, div, q, blockquote, textarea { 14 | margin: 0; 15 | } 16 | 17 | td { 18 | margin: 0; 19 | padding: 0; 20 | } 21 | 22 | td, input { 23 | line-height: 1em; 24 | } 25 | 26 | a { 27 | color: red; 28 | 29 | &:hover { color: blue; } 30 | 31 | div & { color: green; } 32 | 33 | p & span { color: yellow; } 34 | } 35 | 36 | .foo { 37 | .bar, .baz { 38 | & .qux { 39 | display: block; 40 | } 41 | .qux & { 42 | display: inline; 43 | } 44 | .qux& { 45 | display: inline-block; 46 | } 47 | .qux & .biz { 48 | display: none; 49 | } 50 | } 51 | } 52 | 53 | .b { 54 | &.c { 55 | .a& { 56 | color: red; 57 | } 58 | } 59 | } 60 | 61 | .b { 62 | .c & { 63 | &.a { 64 | color: red; 65 | } 66 | } 67 | } 68 | 69 | .p { 70 | .foo &.bar { 71 | color: red; 72 | } 73 | } 74 | 75 | .p { 76 | .foo&.bar { 77 | color: red; 78 | } 79 | } 80 | 81 | .foo { 82 | .foo + & { 83 | background: amber; 84 | } 85 | & + & { 86 | background: amber; 87 | } 88 | } 89 | 90 | .foo, .bar { 91 | & + & { 92 | background: amber; 93 | } 94 | } 95 | 96 | .foo, .bar { 97 | a, b { 98 | & > & { 99 | background: amber; 100 | } 101 | } 102 | } 103 | 104 | .other ::fnord { color: red } 105 | .other::fnord { color: red } 106 | .other { 107 | ::bnord {color: red } 108 | &::bnord {color: red } 109 | } 110 | // selector interpolation 111 | @theme: blood; 112 | @selector: ~".@{theme}"; 113 | @{selector} { 114 | color:red; 115 | } 116 | @{selector}red { 117 | color: green; 118 | } 119 | .red { 120 | #@{theme}.@{theme}&.black { 121 | color:black; 122 | } 123 | } 124 | @num: 3; 125 | :nth-child(@{num}) { 126 | selector: interpolated; 127 | } 128 | .test { 129 | &:nth-child(odd):not(:nth-child(3)) { 130 | color: #ff0000; 131 | } 132 | } 133 | [prop], 134 | [prop="value@{num}"], 135 | [prop*="val@{num}"], 136 | [|prop~="val@{num}"], 137 | [*|prop$="val@{num}"], 138 | [ns|prop^="val@{num}"], 139 | [@{num}^="val@{num}"], 140 | [@{num}=@{num}], 141 | [@{num}] { 142 | attributes: yes; 143 | } -------------------------------------------------------------------------------- /test/css/selectors.css: -------------------------------------------------------------------------------- 1 | h1 a:hover, 2 | h2 a:hover, 3 | h3 a:hover, 4 | h1 p:hover, 5 | h2 p:hover, 6 | h3 p:hover { 7 | color: red; 8 | } 9 | #all { 10 | color: blue; 11 | } 12 | #the { 13 | color: blue; 14 | } 15 | #same { 16 | color: blue; 17 | } 18 | ul, 19 | li, 20 | div, 21 | q, 22 | blockquote, 23 | textarea { 24 | margin: 0; 25 | } 26 | td { 27 | margin: 0; 28 | padding: 0; 29 | } 30 | td, 31 | input { 32 | line-height: 1em; 33 | } 34 | a { 35 | color: red; 36 | } 37 | a:hover { 38 | color: blue; 39 | } 40 | div a { 41 | color: green; 42 | } 43 | p a span { 44 | color: yellow; 45 | } 46 | .foo .bar .qux, 47 | .foo .baz .qux { 48 | display: block; 49 | } 50 | .qux .foo .bar, 51 | .qux .foo .baz { 52 | display: inline; 53 | } 54 | .qux.foo .bar, 55 | .qux.foo .baz { 56 | display: inline-block; 57 | } 58 | .qux .foo .bar .biz, 59 | .qux .foo .baz .biz { 60 | display: none; 61 | } 62 | .a.b.c { 63 | color: red; 64 | } 65 | .c .b.a { 66 | color: red; 67 | } 68 | .foo .p.bar { 69 | color: red; 70 | } 71 | .foo.p.bar { 72 | color: red; 73 | } 74 | .foo + .foo { 75 | background: amber; 76 | } 77 | .foo + .foo { 78 | background: amber; 79 | } 80 | .foo + .foo, 81 | .foo + .bar, 82 | .bar + .foo, 83 | .bar + .bar { 84 | background: amber; 85 | } 86 | .foo a > .foo a, 87 | .foo a > .bar a, 88 | .foo a > .foo b, 89 | .foo a > .bar b, 90 | .bar a > .foo a, 91 | .bar a > .bar a, 92 | .bar a > .foo b, 93 | .bar a > .bar b, 94 | .foo b > .foo a, 95 | .foo b > .bar a, 96 | .foo b > .foo b, 97 | .foo b > .bar b, 98 | .bar b > .foo a, 99 | .bar b > .bar a, 100 | .bar b > .foo b, 101 | .bar b > .bar b { 102 | background: amber; 103 | } 104 | .other ::fnord { 105 | color: #ff0000; 106 | } 107 | .other::fnord { 108 | color: #ff0000; 109 | } 110 | .other ::bnord { 111 | color: #ff0000; 112 | } 113 | .other::bnord { 114 | color: #ff0000; 115 | } 116 | .blood { 117 | color: red; 118 | } 119 | .bloodred { 120 | color: green; 121 | } 122 | #blood.blood.red.black { 123 | color: black; 124 | } 125 | :nth-child(3) { 126 | selector: interpolated; 127 | } 128 | .test:nth-child(odd):not(:nth-child(3)) { 129 | color: #ff0000; 130 | } 131 | [prop], 132 | [prop="value3"], 133 | [prop*="val3"], 134 | [|prop~="val3"], 135 | [*|prop$="val3"], 136 | [ns|prop^="val3"], 137 | [3^="val3"], 138 | [3=3], 139 | [3] { 140 | attributes: yes; 141 | } 142 | -------------------------------------------------------------------------------- /test/less/css-3.less: -------------------------------------------------------------------------------- 1 | .comma-delimited { 2 | text-shadow: -1px -1px 1px red, 6px 5px 5px yellow; 3 | -moz-box-shadow: 0pt 0pt 2px rgba(255, 255, 255, 0.4) inset, 4 | 0pt 4px 6px rgba(255, 255, 255, 0.4) inset; 5 | -webkit-transform: rotate(-0.0000000001deg); 6 | } 7 | @font-face { 8 | font-family: Headline; 9 | unicode-range: U+??????, U+0???, U+0-7F, U+A5; 10 | } 11 | .other { 12 | -moz-transform: translate(0, 11em) rotate(-90deg); 13 | transform: rotateX(45deg); 14 | } 15 | .item[data-cra_zy-attr1b-ut3=bold] { 16 | font-weight: bold; 17 | } 18 | p:not([class*="lead"]) { 19 | color: black; 20 | } 21 | 22 | input[type="text"].class#id[attr=32]:not(1) { 23 | color: white; 24 | } 25 | 26 | div#id.class[a=1][b=2].class:not(1) { 27 | color: white; 28 | } 29 | 30 | ul.comma > li:not(:only-child)::after { 31 | color: white; 32 | } 33 | 34 | ol.comma > li:nth-last-child(2)::after { 35 | color: white; 36 | } 37 | 38 | li:nth-child(4n+1), 39 | li:nth-child(-5n), 40 | li:nth-child(-n+2) { 41 | color: white; 42 | } 43 | 44 | a[href^="http://"] { 45 | color: black; 46 | } 47 | 48 | a[href$="http://"] { 49 | color: black; 50 | } 51 | 52 | form[data-disabled] { 53 | color: black; 54 | } 55 | 56 | p::before { 57 | color: black; 58 | } 59 | 60 | #issue322 { 61 | -webkit-animation: anim2 7s infinite ease-in-out; 62 | } 63 | 64 | @-webkit-keyframes frames { 65 | 0% { border: 1px } 66 | 5.5% { border: 2px } 67 | 100% { border: 3px } 68 | } 69 | 70 | @keyframes fontbulger1 { 71 | to { 72 | font-size: 15px; 73 | } 74 | from,to { 75 | font-size: 12px; 76 | } 77 | 0%,100% { 78 | font-size: 12px; 79 | } 80 | } 81 | 82 | .units { 83 | font: 1.2rem/2rem; 84 | font: 8vw/9vw; 85 | font: 10vh/12vh; 86 | font: 12vm/15vm; 87 | font: 12vmin/15vmin; 88 | font: 1.2ch/1.5ch; 89 | } 90 | 91 | @supports ( box-shadow: 2px 2px 2px black ) or 92 | ( -moz-box-shadow: 2px 2px 2px black ) { 93 | .outline { 94 | box-shadow: 2px 2px 2px black; 95 | -moz-box-shadow: 2px 2px 2px black; 96 | } 97 | } 98 | 99 | @-x-document url-prefix(""github.com"") { 100 | h1 { 101 | color: red; 102 | } 103 | } 104 | 105 | @viewport { 106 | font-size: 10px; 107 | } 108 | @namespace foo url(http://www.example.com); 109 | 110 | foo|h1 { color: blue; } 111 | foo|* { color: yellow; } 112 | |h1 { color: red; } 113 | *|h1 { color: green; } 114 | h1 { color: green; } -------------------------------------------------------------------------------- /test/css/css-3.css: -------------------------------------------------------------------------------- 1 | .comma-delimited { 2 | text-shadow: -1px -1px 1px #ff0000, 6px 5px 5px #ffff00; 3 | -moz-box-shadow: 0pt 0pt 2px rgba(255, 255, 255, 0.4) inset, 0pt 4px 6px rgba(255, 255, 255, 0.4) inset; 4 | -webkit-transform: rotate(-0.0000000001deg); 5 | } 6 | @font-face { 7 | font-family: Headline; 8 | unicode-range: U+??????, U+0???, U+0-7F, U+A5; 9 | } 10 | .other { 11 | -moz-transform: translate(0, 11em) rotate(-90deg); 12 | transform: rotateX(45deg); 13 | } 14 | .item[data-cra_zy-attr1b-ut3=bold] { 15 | font-weight: bold; 16 | } 17 | p:not([class*="lead"]) { 18 | color: black; 19 | } 20 | input[type="text"].class#id[attr=32]:not(1) { 21 | color: white; 22 | } 23 | div#id.class[a=1][b=2].class:not(1) { 24 | color: white; 25 | } 26 | ul.comma > li:not(:only-child)::after { 27 | color: white; 28 | } 29 | ol.comma > li:nth-last-child(2)::after { 30 | color: white; 31 | } 32 | li:nth-child(4n+1), 33 | li:nth-child(-5n), 34 | li:nth-child(-n+2) { 35 | color: white; 36 | } 37 | a[href^="http://"] { 38 | color: black; 39 | } 40 | a[href$="http://"] { 41 | color: black; 42 | } 43 | form[data-disabled] { 44 | color: black; 45 | } 46 | p::before { 47 | color: black; 48 | } 49 | #issue322 { 50 | -webkit-animation: anim2 7s infinite ease-in-out; 51 | } 52 | @-webkit-keyframes frames { 53 | 0% { 54 | border: 1px; 55 | } 56 | 5.5% { 57 | border: 2px; 58 | } 59 | 100% { 60 | border: 3px; 61 | } 62 | } 63 | @keyframes fontbulger1 { 64 | to { 65 | font-size: 15px; 66 | } 67 | from, 68 | to { 69 | font-size: 12px; 70 | } 71 | 0%, 72 | 100% { 73 | font-size: 12px; 74 | } 75 | } 76 | .units { 77 | font: 1.2rem/2rem; 78 | font: 8vw/9vw; 79 | font: 10vh/12vh; 80 | font: 12vm/15vm; 81 | font: 12vmin/15vmin; 82 | font: 1.2ch/1.5ch; 83 | } 84 | @supports ( box-shadow: 2px 2px 2px black ) or 85 | ( -moz-box-shadow: 2px 2px 2px black ) { 86 | .outline { 87 | box-shadow: 2px 2px 2px black; 88 | -moz-box-shadow: 2px 2px 2px black; 89 | } 90 | } 91 | @-x-document url-prefix(""github.com"") { 92 | h1 { 93 | color: red; 94 | } 95 | } 96 | @viewport { 97 | font-size: 10px; 98 | } 99 | @namespace foo url(http://www.example.com); 100 | foo | h1 { 101 | color: blue; 102 | } 103 | foo | * { 104 | color: yellow; 105 | } 106 | | h1 { 107 | color: red; 108 | } 109 | * | h1 { 110 | color: green; 111 | } 112 | h1 { 113 | color: green; 114 | } 115 | -------------------------------------------------------------------------------- /lib/less/tree/element.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Element = function (combinator, value, index) { 4 | this.combinator = combinator instanceof tree.Combinator ? 5 | combinator : new(tree.Combinator)(combinator); 6 | 7 | if (typeof(value) === 'string') { 8 | this.value = value.trim(); 9 | } else if (value) { 10 | this.value = value; 11 | } else { 12 | this.value = ""; 13 | } 14 | this.index = index; 15 | }; 16 | tree.Element.prototype = { 17 | type: "Element", 18 | accept: function (visitor) { 19 | this.combinator = visitor.visit(this.combinator); 20 | this.value = visitor.visit(this.value); 21 | }, 22 | eval: function (env) { 23 | return new(tree.Element)(this.combinator, 24 | this.value.eval ? this.value.eval(env) : this.value, 25 | this.index); 26 | }, 27 | toCSS: function (env) { 28 | var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); 29 | if (value == '' && this.combinator.value.charAt(0) == '&') { 30 | return ''; 31 | } else { 32 | return this.combinator.toCSS(env || {}) + value; 33 | } 34 | } 35 | }; 36 | 37 | tree.Attribute = function (key, op, value) { 38 | this.key = key; 39 | this.op = op; 40 | this.value = value; 41 | }; 42 | tree.Attribute.prototype = { 43 | type: "Attribute", 44 | accept: function (visitor) { 45 | this.value = visitor.visit(this.value); 46 | }, 47 | eval: function (env) { 48 | return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, 49 | this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); 50 | }, 51 | toCSS: function (env) { 52 | var value = this.key.toCSS ? this.key.toCSS(env) : this.key; 53 | 54 | if (this.op) { 55 | value += this.op; 56 | value += (this.value.toCSS ? this.value.toCSS(env) : this.value); 57 | } 58 | 59 | return '[' + value + ']'; 60 | } 61 | }; 62 | 63 | tree.Combinator = function (value) { 64 | if (value === ' ') { 65 | this.value = ' '; 66 | } else { 67 | this.value = value ? value.trim() : ""; 68 | } 69 | }; 70 | tree.Combinator.prototype = { 71 | type: "Combinator", 72 | toCSS: function (env) { 73 | return { 74 | '' : '', 75 | ' ' : ' ', 76 | ':' : ' :', 77 | '+' : env.compress ? '+' : ' + ', 78 | '~' : env.compress ? '~' : ' ~ ', 79 | '>' : env.compress ? '>' : ' > ', 80 | '|' : env.compress ? '|' : ' | ' 81 | }[this.value]; 82 | } 83 | }; 84 | 85 | })(require('../tree')); 86 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Run all tests 3 | # 4 | test: 5 | node test/less-test.js 6 | 7 | # 8 | # Run benchmark 9 | # 10 | benchmark: 11 | node benchmark/less-benchmark.js 12 | 13 | # 14 | # Build less.js 15 | # 16 | SRC = lib/less 17 | HEADER = build/header.js 18 | VERSION = `cat package.json | grep version \ 19 | | grep -o '[0-9]\.[0-9]\.[0-9]\+'` 20 | DIST = dist/less-${VERSION}.js 21 | RHINO = dist/less-rhino-${VERSION}.js 22 | DIST_MIN = dist/less-${VERSION}.min.js 23 | 24 | browser-prepare: DIST := test/browser/less.js 25 | 26 | alpha: DIST := dist/less-${VERSION}-alpha.js 27 | alpha: DIST_MIN := dist/less-${VERSION}-alpha.min.js 28 | 29 | beta: DIST := dist/less-${VERSION}-beta.js 30 | beta: DIST_MIN := dist/less-${VERSION}-beta.min.js 31 | 32 | less: 33 | @@mkdir -p dist 34 | @@touch ${DIST} 35 | @@cat ${HEADER} | sed s/@VERSION/${VERSION}/ > ${DIST} 36 | @@echo "(function (window, undefined) {" >> ${DIST} 37 | @@cat build/require.js\ 38 | ${SRC}/parser.js\ 39 | ${SRC}/functions.js\ 40 | ${SRC}/colors.js\ 41 | ${SRC}/tree/*.js\ 42 | ${SRC}/tree.js\ 43 | ${SRC}/env.js\ 44 | ${SRC}/visitor.js\ 45 | ${SRC}/import-visitor.js\ 46 | ${SRC}/join-selector-visitor.js\ 47 | ${SRC}/extend-visitor.js\ 48 | ${SRC}/browser.js\ 49 | build/amd.js >> ${DIST} 50 | @@echo "})(window);" >> ${DIST} 51 | @@echo ${DIST} built. 52 | 53 | browser-prepare: less 54 | node test/browser-test-prepare.js 55 | 56 | browser-test: browser-prepare 57 | phantomjs test/browser/phantom-runner.js 58 | 59 | browser-test-server: browser-prepare 60 | phantomjs test/browser/phantom-runner.js --no-tests 61 | 62 | rhino: 63 | @@mkdir -p dist 64 | @@touch ${RHINO} 65 | @@cat build/require-rhino.js\ 66 | ${SRC}/parser.js\ 67 | ${SRC}/env.js\ 68 | ${SRC}/visitor.js\ 69 | ${SRC}/import-visitor.js\ 70 | ${SRC}/join-selector-visitor.js\ 71 | ${SRC}/extend-visitor.js\ 72 | ${SRC}/functions.js\ 73 | ${SRC}/colors.js\ 74 | ${SRC}/tree/*.js\ 75 | ${SRC}/tree.js\ 76 | ${SRC}/rhino.js > ${RHINO} 77 | @@echo ${RHINO} built. 78 | 79 | min: less 80 | @@echo minifying... 81 | @@uglifyjs ${DIST} > ${DIST_MIN} 82 | @@echo ${DIST_MIN} built. 83 | 84 | alpha: min 85 | 86 | beta: min 87 | 88 | alpha-release: alpha 89 | git add dist/*.js 90 | git commit -m "Update alpha ${VERSION}" 91 | 92 | dist: min rhino 93 | git add dist/* 94 | git commit -a -m "(dist) build ${VERSION}" 95 | git archive master --prefix=less/ -o less-${VERSION}.tar.gz 96 | npm publish less-${VERSION}.tar.gz 97 | 98 | stable: 99 | npm tag less@${VERSION} stable 100 | 101 | 102 | .PHONY: test benchmark 103 | -------------------------------------------------------------------------------- /test/css/extend-nest.css: -------------------------------------------------------------------------------- 1 | .sidebar, 2 | .sidebar2, 3 | .type1 .sidebar3, 4 | .type2.sidebar4 { 5 | width: 300px; 6 | background: red; 7 | } 8 | .sidebar .box, 9 | .sidebar2 .box, 10 | .type1 .sidebar3 .box, 11 | .type2.sidebar4 .box { 12 | background: #FFF; 13 | border: 1px solid #000; 14 | margin: 10px 0; 15 | } 16 | .sidebar2 { 17 | background: blue; 18 | } 19 | .type1 .sidebar3 { 20 | background: green; 21 | } 22 | .type2.sidebar4 { 23 | background: red; 24 | } 25 | .button, 26 | .submit { 27 | color: black; 28 | } 29 | .button:hover, 30 | .submit:hover { 31 | color: white; 32 | } 33 | .button2 :hover { 34 | nested: white; 35 | } 36 | .button2 :hover { 37 | notnested: black; 38 | } 39 | .amp-test-h, 40 | .amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e.amp-test-g, 41 | .amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e.amp-test-g, 42 | .amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e.amp-test-g, 43 | .amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e.amp-test-g, 44 | .amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e.amp-test-g, 45 | .amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e.amp-test-g, 46 | .amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e.amp-test-g, 47 | .amp-test-f.amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e.amp-test-g, 48 | .amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e.amp-test-g, 49 | .amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e.amp-test-g, 50 | .amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e.amp-test-g, 51 | .amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e.amp-test-g, 52 | .amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-a.amp-test-e.amp-test-g, 53 | .amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-a.amp-test-d.amp-test-b.amp-test-e.amp-test-g, 54 | .amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-a.amp-test-e.amp-test-g, 55 | .amp-test-f.amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e + .amp-test-c .amp-test-b.amp-test-d.amp-test-b.amp-test-e.amp-test-g { 56 | test: extended by masses of selectors; 57 | } 58 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Less.js 2 | 3 | > We welcome feature requests and bug reports. Please read these guidelines before submitting one. 4 | 5 | 6 | **Words that begin with the at sign (`@`) must be wrapped in backticks!** . as a courtesy to avoid sending notifications to any user that might have the `@username` being referenced. Remember, usernames start with the at sign. 7 | 8 | GitHub has other great markdown features as well, [go here to learn more about them](https://help.github.com/articles/github-flavored-markdown). 9 | 10 | 11 | ## Reporting Issues 12 | 13 | We only accept issues that are bug reports or feature requests. Bugs must be isolated and reproducible problems that we can fix within the Less.js core. Please read the following guidelines before opening any issue. 14 | 15 | 1. **Search for existing issues.** We get a lot of duplicate issues, and you'd help us out a lot by first checking if someone else has reported the same issue. Moreover, the issue may have already been resolved with a fix available. 16 | 2. **Create an isolated and reproducible test case.** Be sure the problem exists in Less.js's code with [reduced test cases](http://css-tricks.com/reduced-test-cases/) that should be included in each bug report. 17 | 3. **Test with the latest version**. We get a lot of issues that could be resolved by updating your version of Less.js. 18 | 3. **Include a live example.** Please use [less2css.org](http://less2css.org/) for sharing your isolated test cases. 19 | 4. **Share as much information as possible.** Include operating system and version. Describe how you use Less. If you use it in the browser, please include browser and version, and the version of Less.js you're using. Let us know if you're using the command line (`lessc`) or an external tool. And try to include steps to reproduce the bug. 20 | 21 | 22 | ## Feature Requests 23 | 24 | * Please search for existing feature requests first to see if something similar already exists. 25 | * Include a clear and specific use-case. We love new ideas, but we do not add language features without a reason. 26 | * Consider whether or not your language feature would be better as a function or implemented in a 3rd-party build system such as [assemble-less](http://github.com/assemble/assemble-less). 27 | 28 | 29 | ## Pull Requests 30 | 31 | _Pull requests are encouraged!_ 32 | 33 | * Start by adding a feature request to get feedback and see how your idea is received. 34 | * If your pull request solves an existing issue, but it's different in some way, _please create a new issue_ and make sure to discuss it with the core contributors. Otherwise you risk your hard work being rejected. 35 | * Do not change the **./dist/** folder, we do this when releasing 36 | * _Please add tests_ for your work. Use `make test` to see if they pass node.js tests and `make browser-test` to see the browser ([PhantomJS](http://phantomjs.org/)) tests pass. 37 | 38 | 39 | ### Coding Standards 40 | 41 | * Always use spaces, never tabs 42 | * End lines in semi-colons. 43 | * Loosely aim towards jsHint standards 44 | 45 | 46 | ## Developing 47 | If you want to take an issue just add a small comment saying you are having a go at something, so we don't get duplication. 48 | 49 | Learn more about [developing Less.js](https://github.com/cloudhead/less.js/wiki/Developing-less.js). 50 | -------------------------------------------------------------------------------- /test/css/functions.css: -------------------------------------------------------------------------------- 1 | #functions { 2 | color: #660000; 3 | width: 16; 4 | height: undefined("self"); 5 | border-width: 5; 6 | variable: 11; 7 | background: linear-gradient(#000000, #ffffff); 8 | } 9 | #built-in { 10 | escaped: -Some::weird(#thing, y); 11 | lighten: #ffcccc; 12 | darken: #330000; 13 | saturate: #203c31; 14 | desaturate: #29332f; 15 | greyscale: #2e2e2e; 16 | hsl-clamp: #ffffff; 17 | spin-p: #bf6a40; 18 | spin-n: #bf4055; 19 | luma-white: 100%; 20 | luma-black: 0%; 21 | luma-black-alpha: 0%; 22 | luma-red: 21%; 23 | luma-green: 72%; 24 | luma-blue: 7%; 25 | luma-yellow: 93%; 26 | luma-cyan: 79%; 27 | luma-white-alpha: 50%; 28 | contrast-filter: contrast(30%); 29 | contrast-white: #000000; 30 | contrast-black: #ffffff; 31 | contrast-red: #ffffff; 32 | contrast-green: #000000; 33 | contrast-blue: #ffffff; 34 | contrast-yellow: #000000; 35 | contrast-cyan: #000000; 36 | contrast-light: #111111; 37 | contrast-dark: #eeeeee; 38 | contrast-wrongorder: #111111; 39 | contrast-light-thresh: #111111; 40 | contrast-dark-thresh: #eeeeee; 41 | contrast-high-thresh: #eeeeee; 42 | contrast-low-thresh: #111111; 43 | contrast-light-thresh-per: #111111; 44 | contrast-dark-thresh-per: #eeeeee; 45 | contrast-high-thresh-per: #eeeeee; 46 | contrast-low-thresh-per: #111111; 47 | format: "rgb(32, 128, 64)"; 48 | format-string: "hello world"; 49 | format-multiple: "hello earth 2"; 50 | format-url-encode: "red is %23ff0000"; 51 | eformat: rgb(32, 128, 64); 52 | unitless: 12; 53 | unit: 14em; 54 | hue: 98; 55 | saturation: 12%; 56 | lightness: 95%; 57 | hsvhue: 98; 58 | hsvsaturation: 12%; 59 | hsvvalue: 95%; 60 | red: 255; 61 | green: 255; 62 | blue: 255; 63 | rounded: 11; 64 | rounded-two: 10.67; 65 | roundedpx: 3px; 66 | roundedpx-three: 3.333px; 67 | rounded-percentage: 10%; 68 | ceil: 11px; 69 | floor: 12px; 70 | sqrt: 5px; 71 | pi: 3.141592653589793; 72 | mod: 2m; 73 | abs: 4%; 74 | tan: 0.9004040442978399; 75 | sin: 0.17364817766693033; 76 | cos: 0.8438539587324921; 77 | atan: 0.1rad; 78 | atan: 34.00000000000001deg; 79 | atan: 45.00000000000001deg; 80 | pow: 64px; 81 | pow: 64; 82 | pow: 27; 83 | percentage: 20%; 84 | color: #ff0011; 85 | tint: #898989; 86 | tint-full: #ffffff; 87 | tint-percent: #898989; 88 | shade: #686868; 89 | shade-full: #000000; 90 | shade-percent: #686868; 91 | fade-out: rgba(255, 0, 0, 0.95); 92 | fade-in: rgba(255, 0, 0, 0.9500000000000001); 93 | hsv: #4d2926; 94 | hsva: rgba(77, 40, 38, 0.2); 95 | mix: #ff3300; 96 | mix-0: #ffff00; 97 | mix-100: #ff0000; 98 | mix-weightless: #ff8000; 99 | } 100 | #built-in .is-a { 101 | color: true; 102 | color1: true; 103 | color2: true; 104 | keyword: true; 105 | number: true; 106 | string: true; 107 | pixel: true; 108 | percent: true; 109 | em: true; 110 | cat: true; 111 | } 112 | #alpha { 113 | alpha: rgba(153, 94, 51, 0.6); 114 | } 115 | #blendmodes { 116 | multiply: #ed0000; 117 | screen: #f600f6; 118 | overlay: #ed0000; 119 | softlight: #ff0000; 120 | hardlight: #0000ed; 121 | difference: #f600f6; 122 | exclusion: #f600f6; 123 | average: #7b007b; 124 | negation: #d73131; 125 | } 126 | #extract { 127 | result: 3 2 1 C B A; 128 | } 129 | -------------------------------------------------------------------------------- /lib/less/rhino.js: -------------------------------------------------------------------------------- 1 | var name; 2 | 3 | function loadStyleSheet(sheet, callback, reload, remaining) { 4 | var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), 5 | sheetName = name.slice(0, endOfPath + 1) + sheet.href, 6 | contents = sheet.contents || {}, 7 | input = readFile(sheetName); 8 | 9 | input = input.replace(/^\xEF\xBB\xBF/, ''); 10 | 11 | contents[sheetName] = input; 12 | 13 | var parser = new less.Parser({ 14 | paths: [sheet.href.replace(/[\w\.-]+$/, '')], 15 | contents: contents 16 | }); 17 | parser.parse(input, function (e, root) { 18 | if (e) { 19 | return error(e, sheetName); 20 | } 21 | try { 22 | callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); 23 | } catch(e) { 24 | error(e, sheetName); 25 | } 26 | }); 27 | } 28 | 29 | function writeFile(filename, content) { 30 | var fstream = new java.io.FileWriter(filename); 31 | var out = new java.io.BufferedWriter(fstream); 32 | out.write(content); 33 | out.close(); 34 | } 35 | 36 | // Command line integration via Rhino 37 | (function (args) { 38 | var output, 39 | compress = false, 40 | i; 41 | 42 | for(i = 0; i < args.length; i++) { 43 | switch(args[i]) { 44 | case "-x": 45 | compress = true; 46 | break; 47 | default: 48 | if (!name) { 49 | name = args[i]; 50 | } else if (!output) { 51 | output = args[i]; 52 | } else { 53 | print("unrecognised parameters"); 54 | print("input_file [output_file] [-x]"); 55 | } 56 | } 57 | } 58 | 59 | if (!name) { 60 | print('No files present in the fileset; Check your pattern match in build.xml'); 61 | quit(1); 62 | } 63 | path = name.split("/");path.pop();path=path.join("/") 64 | 65 | var input = readFile(name); 66 | 67 | if (!input) { 68 | print('lesscss: couldn\'t open file ' + name); 69 | quit(1); 70 | } 71 | 72 | var result; 73 | try { 74 | var parser = new less.Parser(); 75 | parser.parse(input, function (e, root) { 76 | if (e) { 77 | error(e, name); 78 | quit(1); 79 | } else { 80 | result = root.toCSS({compress: compress || false}); 81 | if (output) { 82 | writeFile(output, result); 83 | print("Written to " + output); 84 | } else { 85 | print(result); 86 | } 87 | quit(0); 88 | } 89 | }); 90 | } 91 | catch(e) { 92 | error(e, name); 93 | quit(1); 94 | } 95 | print("done"); 96 | }(arguments)); 97 | 98 | function error(e, filename) { 99 | 100 | var content = "Error : " + filename + "\n"; 101 | 102 | filename = e.filename || filename; 103 | 104 | if (e.message) { 105 | content += e.message + "\n"; 106 | } 107 | 108 | var errorline = function (e, i, classname) { 109 | if (e.extract[i]) { 110 | content += 111 | String(parseInt(e.line) + (i - 1)) + 112 | ":" + e.extract[i] + "\n"; 113 | } 114 | }; 115 | 116 | if (e.stack) { 117 | content += e.stack; 118 | } else if (e.extract) { 119 | content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n'; 120 | errorline(e, 0); 121 | errorline(e, 1); 122 | errorline(e, 2); 123 | } 124 | print(content); 125 | } -------------------------------------------------------------------------------- /lib/less/tree/import.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | // 3 | // CSS @import node 4 | // 5 | // The general strategy here is that we don't want to wait 6 | // for the parsing to be completed, before we start importing 7 | // the file. That's because in the context of a browser, 8 | // most of the time will be spent waiting for the server to respond. 9 | // 10 | // On creation, we push the import path to our import queue, though 11 | // `import,push`, we also pass it a callback, which it'll call once 12 | // the file has been fetched, and parsed. 13 | // 14 | tree.Import = function (path, features, options, index, currentFileInfo) { 15 | var that = this; 16 | 17 | this.options = options; 18 | this.index = index; 19 | this.path = path; 20 | this.features = features; 21 | this.currentFileInfo = currentFileInfo; 22 | 23 | if (this.options.less !== undefined) { 24 | this.css = !this.options.less; 25 | } else { 26 | var pathValue = this.getPath(); 27 | if (pathValue && /css([\?;].*)?$/.test(pathValue)) { 28 | this.css = true; 29 | } 30 | } 31 | }; 32 | 33 | // 34 | // The actual import node doesn't return anything, when converted to CSS. 35 | // The reason is that it's used at the evaluation stage, so that the rules 36 | // it imports can be treated like any other rules. 37 | // 38 | // In `eval`, we make sure all Import nodes get evaluated, recursively, so 39 | // we end up with a flat structure, which can easily be imported in the parent 40 | // ruleset. 41 | // 42 | tree.Import.prototype = { 43 | type: "Import", 44 | accept: function (visitor) { 45 | this.features = visitor.visit(this.features); 46 | this.path = visitor.visit(this.path); 47 | this.root = visitor.visit(this.root); 48 | }, 49 | toCSS: function (env) { 50 | var features = this.features ? ' ' + this.features.toCSS(env) : ''; 51 | 52 | if (this.css) { 53 | return "@import " + this.path.toCSS() + features + ';\n'; 54 | } else { 55 | return ""; 56 | } 57 | }, 58 | getPath: function () { 59 | if (this.path instanceof tree.Quoted) { 60 | var path = this.path.value; 61 | return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; 62 | } else if (this.path instanceof tree.URL) { 63 | return this.path.value.value; 64 | } 65 | return null; 66 | }, 67 | evalForImport: function (env) { 68 | return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); 69 | }, 70 | evalPath: function (env) { 71 | var path = this.path.eval(env); 72 | var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; 73 | if (rootpath && !(path instanceof tree.URL)) { 74 | var pathValue = path.value; 75 | // Add the base path if the import is relative 76 | if (pathValue && env.isPathRelative(pathValue)) { 77 | path.value = rootpath + pathValue; 78 | } 79 | } 80 | return path; 81 | }, 82 | eval: function (env) { 83 | var ruleset, features = this.features && this.features.eval(env); 84 | 85 | if (this.skip) { return []; } 86 | 87 | if (this.css) { 88 | var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); 89 | if (!newImport.css && this.error) { 90 | throw this.error; 91 | } 92 | return newImport; 93 | } else { 94 | ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); 95 | 96 | ruleset.evalImports(env); 97 | 98 | return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; 99 | } 100 | } 101 | }; 102 | 103 | })(require('../tree')); 104 | -------------------------------------------------------------------------------- /test/css/media.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | .class { 3 | color: blue; 4 | } 5 | .class .sub { 6 | width: 42; 7 | } 8 | .top, 9 | header > h1 { 10 | color: #444444; 11 | } 12 | } 13 | @media screen { 14 | body { 15 | max-width: 480; 16 | } 17 | } 18 | @media all and (device-aspect-ratio: 16 / 9) { 19 | body { 20 | max-width: 800px; 21 | } 22 | } 23 | @media all and (orientation: portrait) { 24 | aside { 25 | float: none; 26 | } 27 | } 28 | @media handheld and (min-width: 42), screen and (min-width: 20em) { 29 | body { 30 | max-width: 480px; 31 | } 32 | } 33 | @media print { 34 | body { 35 | padding: 20px; 36 | } 37 | body header { 38 | background-color: red; 39 | } 40 | } 41 | @media print and (orientation: landscape) { 42 | body { 43 | margin-left: 20px; 44 | } 45 | } 46 | @media screen { 47 | .sidebar { 48 | width: 300px; 49 | } 50 | } 51 | @media screen and (orientation: landscape) { 52 | .sidebar { 53 | width: 500px; 54 | } 55 | } 56 | @media a { 57 | 58 | } 59 | @media a and b { 60 | .first .second .third { 61 | width: 300px; 62 | } 63 | .first .second .fourth { 64 | width: 3; 65 | } 66 | } 67 | @media a and b and c { 68 | .first .second .third { 69 | width: 500px; 70 | } 71 | } 72 | @media a, b and c { 73 | body { 74 | width: 95%; 75 | } 76 | } 77 | @media a and x, b and c and x, a and y, b and c and y { 78 | body { 79 | width: 100%; 80 | } 81 | } 82 | .a { 83 | background: black; 84 | } 85 | @media handheld { 86 | .a { 87 | background: white; 88 | } 89 | } 90 | @media handheld and (max-width: 100px) { 91 | .a { 92 | background: red; 93 | } 94 | } 95 | .b { 96 | background: black; 97 | } 98 | @media handheld { 99 | .b { 100 | background: white; 101 | } 102 | } 103 | @media handheld and (max-width: 200px) { 104 | .b { 105 | background: red; 106 | } 107 | } 108 | @media only screen and (max-width: 200px) { 109 | width: 480px; 110 | } 111 | @media print { 112 | @page :left { 113 | margin: 0.5cm; 114 | } 115 | @page :right { 116 | margin: 0.5cm; 117 | } 118 | @page Test:first { 119 | margin: 1cm; 120 | } 121 | @page :first { 122 | size: 8.5in 11in;@top-left { 123 | margin: 1cm; 124 | } 125 | @top-left-corner { 126 | margin: 1cm; 127 | } 128 | @top-center { 129 | margin: 1cm; 130 | } 131 | @top-right { 132 | margin: 1cm; 133 | } 134 | @top-right-corner { 135 | margin: 1cm; 136 | } 137 | @bottom-left { 138 | margin: 1cm; 139 | } 140 | @bottom-left-corner { 141 | margin: 1cm; 142 | } 143 | @bottom-center { 144 | margin: 1cm; 145 | } 146 | @bottom-right { 147 | margin: 1cm; 148 | } 149 | @bottom-right-corner { 150 | margin: 1cm; 151 | } 152 | @left-top { 153 | margin: 1cm; 154 | } 155 | @left-middle { 156 | margin: 1cm; 157 | } 158 | @left-bottom { 159 | margin: 1cm; 160 | } 161 | @right-top { 162 | margin: 1cm; 163 | } 164 | @right-middle { 165 | content: "Page " counter(page); 166 | } 167 | @right-bottom { 168 | margin: 1cm; 169 | } 170 | } 171 | } 172 | @media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx), (min-resolution: 128dpcm) { 173 | .b { 174 | background: red; 175 | } 176 | } 177 | body { 178 | background: red; 179 | } 180 | @media (max-width: 500px) { 181 | body { 182 | background: green; 183 | } 184 | } 185 | @media (max-width: 1000px) { 186 | body { 187 | background: red; 188 | background: blue; 189 | } 190 | } 191 | @media (max-width: 1000px) and (max-width: 500px) { 192 | body { 193 | background: green; 194 | } 195 | } 196 | @media (max-width: 1200px) { 197 | /* a comment */ 198 | } 199 | @media (max-width: 1200px) and (max-width: 900px) { 200 | body { 201 | font-size: 11px; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /test/less/mixins-args.less: -------------------------------------------------------------------------------- 1 | .mixin (@a: 1px, @b: 50%) { 2 | width: (@a * 5); 3 | height: (@b - 1%); 4 | } 5 | 6 | .mixina (@style, @width, @color: black) { 7 | border: @width @style @color; 8 | } 9 | 10 | .mixiny 11 | (@a: 0, @b: 0) { 12 | margin: @a; 13 | padding: @b; 14 | } 15 | 16 | .hidden() { 17 | color: transparent; // asd 18 | } 19 | 20 | #hidden { 21 | .hidden; 22 | } 23 | 24 | #hidden1 { 25 | .hidden(); 26 | } 27 | 28 | .two-args { 29 | color: blue; 30 | .mixin(2px, 100%); 31 | .mixina(dotted, 2px); 32 | } 33 | 34 | .one-arg { 35 | .mixin(3px); 36 | } 37 | 38 | .no-parens { 39 | .mixin; 40 | } 41 | 42 | .no-args { 43 | .mixin(); 44 | } 45 | 46 | .var-args { 47 | @var: 9; 48 | .mixin(@var, (@var * 2)); 49 | } 50 | 51 | .multi-mix { 52 | .mixin(2px, 30%); 53 | .mixiny(4, 5); 54 | } 55 | 56 | .maxa(@arg1: 10, @arg2: #f00) { 57 | padding: (@arg1 * 2px); 58 | color: @arg2; 59 | } 60 | 61 | body { 62 | .maxa(15); 63 | } 64 | 65 | @glob: 5; 66 | .global-mixin(@a:2) { 67 | width: (@glob + @a); 68 | } 69 | 70 | .scope-mix { 71 | .global-mixin(3); 72 | } 73 | 74 | .nested-ruleset (@width: 200px) { 75 | width: @width; 76 | .column { margin: @width; } 77 | } 78 | .content { 79 | .nested-ruleset(600px); 80 | } 81 | 82 | // 83 | 84 | .same-var-name2(@radius) { 85 | radius: @radius; 86 | } 87 | .same-var-name(@radius) { 88 | .same-var-name2(@radius); 89 | } 90 | #same-var-name { 91 | .same-var-name(5px); 92 | } 93 | 94 | // 95 | 96 | .var-inside () { 97 | @var: 10px; 98 | width: @var; 99 | } 100 | #var-inside { .var-inside; } 101 | 102 | .mixin-arguments (@width: 0px, ...) { 103 | border: @arguments; 104 | width: @width; 105 | } 106 | 107 | .arguments { 108 | .mixin-arguments(1px, solid, black); 109 | } 110 | .arguments2 { 111 | .mixin-arguments(); 112 | } 113 | .arguments3 { 114 | .mixin-arguments; 115 | } 116 | 117 | .mixin-arguments2 (@width, @rest...) { 118 | border: @arguments; 119 | rest: @rest; 120 | width: @width; 121 | } 122 | .arguments4 { 123 | .mixin-arguments2(0, 1, 2, 3, 4); 124 | } 125 | 126 | // Edge cases 127 | 128 | .edge-case { 129 | .mixin-arguments("{"); 130 | } 131 | 132 | // Division vs. Literal Slash 133 | .border-radius(@r: 2px/5px) { 134 | border-radius: @r; 135 | } 136 | .slash-vs-math { 137 | .border-radius(); 138 | .border-radius(5px/10px); 139 | .border-radius((3px * 2)); 140 | } 141 | // semi-colon vs comma for delimiting 142 | 143 | .mixin-takes-one(@a) { 144 | one: @a; 145 | } 146 | 147 | .mixin-takes-two(@a; @b) { 148 | one: @a; 149 | two: @b; 150 | } 151 | 152 | .comma-vs-semi-colon { 153 | .mixin-takes-two(@a : a; @b : b, c); 154 | .mixin-takes-two(@a : d, e; @b : f); 155 | .mixin-takes-one(@a: g); 156 | .mixin-takes-one(@a : h;); 157 | .mixin-takes-one(i); 158 | .mixin-takes-one(j;); 159 | .mixin-takes-two(k, l); 160 | .mixin-takes-one(m, n;); 161 | .mixin-takes-two(o, p; q); 162 | .mixin-takes-two(r, s; t;); 163 | } 164 | 165 | .mixin-conflict(@a:defA, @b:defB, @c:defC) { 166 | three: @a, @b, @c; 167 | } 168 | 169 | .mixin-conflict(@a:defA, @b:defB, @c:defC, @d:defD) { 170 | four: @a, @b, @c, @d; 171 | } 172 | 173 | #named-conflict { 174 | .mixin-conflict(11, 12, 13, @a:a); 175 | .mixin-conflict(@a:a, 21, 22, 23); 176 | } 177 | @a: 3px; 178 | .mixin-default-arg(@a: 1px, @b: @a, @c: @b) { 179 | defaults: 1px 1px 1px; 180 | defaults: 2px 2px 2px; 181 | } 182 | 183 | .test-mixin-default-arg { 184 | .mixin-default-arg(); 185 | .mixin-default-arg(2px); 186 | } 187 | 188 | .mixin-comma-default1(@color; @padding; @margin: 2, 2, 2, 2) { 189 | margin: @margin; 190 | } 191 | .selector { 192 | .mixin-comma-default1(#33acfe; 4); 193 | } 194 | .mixin-comma-default2(@margin: 2, 2, 2, 2;) { 195 | margin: @margin; 196 | } 197 | .selector2 { 198 | .mixin-comma-default2(); 199 | } 200 | .mixin-comma-default3(@margin: 2, 2, 2, 2) { 201 | margin: @margin; 202 | } 203 | .selector3 { 204 | .mixin-comma-default3(4,2,2,2); 205 | } -------------------------------------------------------------------------------- /lib/less/lessc_helper.js: -------------------------------------------------------------------------------- 1 | // lessc_helper.js 2 | // 3 | // helper functions for lessc 4 | sys = require('util'); 5 | 6 | var lessc_helper = { 7 | 8 | //Stylize a string 9 | stylize : function(str, style) { 10 | var styles = { 11 | 'reset' : [0, 0], 12 | 'bold' : [1, 22], 13 | 'inverse' : [7, 27], 14 | 'underline' : [4, 24], 15 | 'yellow' : [33, 39], 16 | 'green' : [32, 39], 17 | 'red' : [31, 39], 18 | 'grey' : [90, 39] 19 | }; 20 | return '\033[' + styles[style][0] + 'm' + str + 21 | '\033[' + styles[style][1] + 'm'; 22 | }, 23 | 24 | //Print command line options 25 | printUsage: function() { 26 | sys.puts("usage: lessc [option option=parameter ...] [destination]"); 27 | sys.puts(""); 28 | sys.puts("If source is set to `-' (dash or hyphen-minus), input is read from stdin."); 29 | sys.puts(""); 30 | sys.puts("options:"); 31 | sys.puts(" -h, --help Print help (this message) and exit."); 32 | sys.puts(" --include-path=PATHS Set include paths. Separated by `:'. Use `;' on Windows."); 33 | sys.puts(" -M, --depends Output a makefile import dependency list to stdout"); 34 | sys.puts(" --no-color Disable colorized output."); 35 | sys.puts(" --no-ie-compat Disable IE compatibility checks."); 36 | sys.puts(" -l, --lint Syntax check only (lint)."); 37 | sys.puts(" -s, --silent Suppress output of error messages."); 38 | sys.puts(" --strict-imports Force evaluation of imports."); 39 | sys.puts(" --verbose Be verbose."); 40 | sys.puts(" -v, --version Print version number and exit."); 41 | sys.puts(" -x, --compress Compress output by removing some whitespaces."); 42 | sys.puts(" --yui-compress Compress output using ycssmin"); 43 | sys.puts(" --max-line-len=LINELEN Max line length used by ycssmin"); 44 | sys.puts(" -O0, -O1, -O2 Set the parser's optimization level. The lower"); 45 | sys.puts(" the number, the less nodes it will create in the"); 46 | sys.puts(" tree. This could matter for debugging, or if you"); 47 | sys.puts(" want to access the individual nodes in the tree."); 48 | sys.puts(" --line-numbers=TYPE Outputs filename and line numbers."); 49 | sys.puts(" TYPE can be either 'comments', which will output"); 50 | sys.puts(" the debug info within comments, 'mediaquery'"); 51 | sys.puts(" that will output the information within a fake"); 52 | sys.puts(" media query which is compatible with the SASS"); 53 | sys.puts(" format, and 'all' which will do both."); 54 | sys.puts(" -rp, --rootpath=URL Set rootpath for url rewriting in relative imports and urls."); 55 | sys.puts(" Works with or without the relative-urls option."); 56 | sys.puts(" -ru, --relative-urls re-write relative urls to the base less file."); 57 | sys.puts(" -sm=on|off Turn on or off strict math, where in strict mode, math"); 58 | sys.puts(" --strict-math=on|off requires brackets. This option may default to on and then"); 59 | sys.puts(" be removed in the future."); 60 | sys.puts(" -su=on|off Allow mixed units, e.g. 1px+1em or 1px*1px which have units"); 61 | sys.puts(" --strict-units=on|off that cannot be represented."); 62 | sys.puts(""); 63 | sys.puts("Report bugs to: http://github.com/cloudhead/less.js/issues"); 64 | sys.puts("Home page: "); 65 | } 66 | 67 | 68 | } 69 | 70 | // Exports helper functions 71 | for (var h in lessc_helper) { exports[h] = lessc_helper[h] } 72 | -------------------------------------------------------------------------------- /test/less/media.less: -------------------------------------------------------------------------------- 1 | 2 | // For now, variables can't be declared inside @media blocks. 3 | 4 | @var: 42; 5 | 6 | @media print { 7 | .class { 8 | color: blue; 9 | .sub { 10 | width: @var; 11 | } 12 | } 13 | .top, header > h1 { 14 | color: (#222 * 2); 15 | } 16 | } 17 | 18 | @media screen { 19 | @base: 8; 20 | body { max-width: (@base * 60); } 21 | } 22 | 23 | @ratio_large: 16; 24 | @ratio_small: 9; 25 | 26 | @media all and (device-aspect-ratio: @ratio_large / @ratio_small) { 27 | body { max-width: 800px; } 28 | } 29 | 30 | @media all and (orientation:portrait) { 31 | aside { float: none; } 32 | } 33 | 34 | @media handheld and (min-width: @var), screen and (min-width: 20em) { 35 | body { 36 | max-width: 480px; 37 | } 38 | } 39 | 40 | body { 41 | @media print { 42 | padding: 20px; 43 | 44 | header { 45 | background-color: red; 46 | } 47 | 48 | @media (orientation:landscape) { 49 | margin-left: 20px; 50 | } 51 | } 52 | } 53 | 54 | @media screen { 55 | .sidebar { 56 | width: 300px; 57 | @media (orientation: landscape) { 58 | width: 500px; 59 | } 60 | } 61 | } 62 | 63 | @media a { 64 | .first { 65 | @media b { 66 | .second { 67 | .third { 68 | width: 300px; 69 | @media c { 70 | width: 500px; 71 | } 72 | } 73 | .fourth { 74 | width: 3; 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | body { 82 | @media a, b and c { 83 | width: 95%; 84 | 85 | @media x, y { 86 | width: 100%; 87 | } 88 | } 89 | } 90 | 91 | .mediaMixin(@fallback: 200px) { 92 | background: black; 93 | 94 | @media handheld { 95 | background: white; 96 | 97 | @media (max-width: @fallback) { 98 | background: red; 99 | } 100 | } 101 | } 102 | 103 | .a { 104 | .mediaMixin(100px); 105 | } 106 | 107 | .b { 108 | .mediaMixin(); 109 | } 110 | @smartphone: ~"only screen and (max-width: 200px)"; 111 | @media @smartphone { 112 | width: 480px; 113 | } 114 | 115 | @media print { 116 | @page :left { 117 | margin: 0.5cm; 118 | } 119 | @page :right { 120 | margin: 0.5cm; 121 | } 122 | @page Test:first { 123 | margin: 1cm; 124 | } 125 | @page :first { 126 | size: 8.5in 11in; 127 | @top-left { 128 | margin: 1cm; 129 | } 130 | @top-left-corner { 131 | margin: 1cm; 132 | } 133 | @top-center { 134 | margin: 1cm; 135 | } 136 | @top-right { 137 | margin: 1cm; 138 | } 139 | @top-right-corner { 140 | margin: 1cm; 141 | } 142 | @bottom-left { 143 | margin: 1cm; 144 | } 145 | @bottom-left-corner { 146 | margin: 1cm; 147 | } 148 | @bottom-center { 149 | margin: 1cm; 150 | } 151 | @bottom-right { 152 | margin: 1cm; 153 | } 154 | @bottom-right-corner { 155 | margin: 1cm; 156 | } 157 | @left-top { 158 | margin: 1cm; 159 | } 160 | @left-middle { 161 | margin: 1cm; 162 | } 163 | @left-bottom { 164 | margin: 1cm; 165 | } 166 | @right-top { 167 | margin: 1cm; 168 | } 169 | @right-middle { 170 | content: "Page " counter(page); 171 | } 172 | @right-bottom { 173 | margin: 1cm; 174 | } 175 | } 176 | } 177 | 178 | @media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx), (min-resolution: 128dpcm) { 179 | .b { 180 | background: red; 181 | } 182 | } 183 | 184 | .bg() { 185 | background: red; 186 | 187 | @media (max-width: 500px) { 188 | background: green; 189 | } 190 | } 191 | 192 | body { 193 | .bg(); 194 | } 195 | 196 | @bpMedium: 1000px; 197 | @media (max-width: @bpMedium) { 198 | body { 199 | .bg(); 200 | background: blue; 201 | } 202 | } 203 | 204 | @media (max-width: 1200px) { 205 | /* a comment */ 206 | 207 | @media (max-width: 900px) { 208 | body { font-size: 11px; } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /test/less/mixins-guards.less: -------------------------------------------------------------------------------- 1 | 2 | // Stacking, functions.. 3 | 4 | .light (@a) when (lightness(@a) > 50%) { 5 | color: white; 6 | } 7 | .light (@a) when (lightness(@a) < 50%) { 8 | color: black; 9 | } 10 | .light (@a) { 11 | margin: 1px; 12 | } 13 | 14 | .light1 { .light(#ddd) } 15 | .light2 { .light(#444) } 16 | 17 | // Arguments against each other 18 | 19 | .max (@a, @b) when (@a > @b) { 20 | width: @a; 21 | } 22 | .max (@a, @b) when (@a < @b) { 23 | width: @b; 24 | } 25 | 26 | .max1 { .max(3, 6) } 27 | .max2 { .max(8, 1) } 28 | 29 | // Globals inside guards 30 | 31 | @g: auto; 32 | 33 | .glob (@a) when (@a = @g) { 34 | margin: @a @g; 35 | } 36 | .glob1 { .glob(auto) } 37 | 38 | // Other operators 39 | 40 | .ops (@a) when (@a >= 0) { 41 | height: gt-or-eq; 42 | } 43 | .ops (@a) when (@a =< 0) { 44 | height: lt-or-eq; 45 | } 46 | .ops (@a) when not(@a = 0) { 47 | height: not-eq; 48 | } 49 | .ops1 { .ops(0) } 50 | .ops2 { .ops(1) } 51 | .ops3 { .ops(-1) } 52 | 53 | // Scope and default values 54 | 55 | @a: auto; 56 | 57 | .default (@a: inherit) when (@a = inherit) { 58 | content: default; 59 | } 60 | .default1 { .default } 61 | 62 | // true & false keywords 63 | .test (@a) when (@a) { 64 | content: "true."; 65 | } 66 | .test (@a) when not (@a) { 67 | content: "false."; 68 | } 69 | 70 | .test1 { .test(true) } 71 | .test2 { .test(false) } 72 | .test3 { .test(1) } 73 | .test4 { .test(boo) } 74 | .test5 { .test("true") } 75 | 76 | // Boolean expressions 77 | 78 | .bool () when (true) and (false) { content: true and false } // FALSE 79 | .bool () when (true) and (true) { content: true and true } // TRUE 80 | .bool () when (true) { content: true } // TRUE 81 | .bool () when (false) and (false) { content: true } // FALSE 82 | .bool () when (false), (true) { content: false, true } // TRUE 83 | .bool () when (false) and (true) and (true), (true) { content: false and true and true, true } // TRUE 84 | .bool () when (true) and (true) and (false), (false) { content: true and true and false, false } // FALSE 85 | .bool () when (false), (true) and (true) { content: false, true and true } // TRUE 86 | .bool () when (false), (false), (true) { content: false, false, true } // TRUE 87 | .bool () when (false), (false) and (true), (false) { content: false, false and true, false } // FALSE 88 | .bool () when (false), (true) and (true) and (true), (false) { content: false, true and true and true, false } // TRUE 89 | .bool () when not (false) { content: not false } 90 | .bool () when not (true) and not (false) { content: not true and not false } 91 | .bool () when not (true) and not (true) { content: not true and not true } 92 | .bool () when not (false) and (false), not (false) { content: not false and false, not false } 93 | 94 | .bool1 { .bool } 95 | 96 | .equality-unit-test(@num) when (@num = 1%) { 97 | test: fail; 98 | } 99 | .equality-unit-test(@num) when (@num = 2) { 100 | test: pass; 101 | } 102 | .equality-units { 103 | .equality-unit-test(1px); 104 | .equality-unit-test(2px); 105 | } 106 | 107 | .colorguard(@col) when (@col = red) { content: is @col; } 108 | .colorguard(@col) when not (blue = @col) { content: is not blue its @col; } 109 | .colorguard(@col) {} 110 | .colorguardtest { 111 | .colorguard(red); 112 | .colorguard(blue); 113 | .colorguard(purple); 114 | } 115 | 116 | .stringguard(@str) when (@str = "theme1") { content: is theme1; } 117 | .stringguard(@str) when not ("theme2" = @str) { content: is not theme2; } 118 | .stringguard(@str) when (~"theme1" = @str) { content: is theme1 no quotes; } 119 | .stringguard(@str) {} 120 | .stringguardtest { 121 | .stringguard("theme1"); 122 | .stringguard("theme2"); 123 | .stringguard(theme1); 124 | } 125 | 126 | .mixin(...) { 127 | catch:all; 128 | } 129 | .mixin(@var) when (@var=4) { 130 | declare: 4; 131 | } 132 | .mixin(@var) when (@var=4px) { 133 | declare: 4px; 134 | } 135 | #tryNumberPx { 136 | .mixin(4px); 137 | } -------------------------------------------------------------------------------- /lib/less/env.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | var parseCopyProperties = [ 4 | 'paths', // option - unmodified - paths to search for imports on 5 | 'optimization', // option - optimization level (for the chunker) 6 | 'files', // list of files that have been imported, used for import-once 7 | 'contents', // browser-only, contents of all the files 8 | 'relativeUrls', // option - whether to adjust URL's to be relative 9 | 'strictImports', // option - 10 | 'dumpLineNumbers', // option - whether to dump line numbers 11 | 'compress', // option - whether to compress 12 | 'processImports', // option - whether to process imports. if false then imports will not be imported 13 | 'mime', // browser only - mime type for sheet import 14 | 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. 15 | ]; 16 | 17 | //currentFileInfo = { 18 | // 'relativeUrls' - option - whether to adjust URL's to be relative 19 | // 'filename' - full resolved filename of current file 20 | // 'rootpath' - path to append to normal URLs for this node 21 | // 'currentDirectory' - path to the current file, absolute 22 | // 'rootFilename' - filename of the base file 23 | // 'entryPath' = absolute path to the entry file 24 | 25 | tree.parseEnv = function(options) { 26 | copyFromOriginal(options, this, parseCopyProperties); 27 | 28 | if (!this.contents) { this.contents = {}; } 29 | if (!this.files) { this.files = {}; } 30 | 31 | if (!this.currentFileInfo) { 32 | var filename = options.filename || "input"; 33 | options.filename = null; 34 | var entryPath = filename.replace(/[^\/\\]*$/, ""); 35 | this.currentFileInfo = { 36 | filename: filename, 37 | relativeUrls: this.relativeUrls, 38 | rootpath: options.rootpath || "", 39 | currentDirectory: entryPath, 40 | entryPath: entryPath, 41 | rootFilename: filename 42 | }; 43 | } 44 | }; 45 | 46 | tree.parseEnv.prototype.toSheet = function (path) { 47 | var env = new tree.parseEnv(this); 48 | env.href = path; 49 | //env.title = path; 50 | env.type = this.mime; 51 | return env; 52 | }; 53 | 54 | var evalCopyProperties = [ 55 | 'silent', // whether to swallow errors and warnings 56 | 'verbose', // whether to log more activity 57 | 'compress', // whether to compress 58 | 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) 59 | 'strictMath', // whether math has to be within parenthesis 60 | 'strictUnits' // whether units need to evaluate correctly 61 | ]; 62 | 63 | tree.evalEnv = function(options, frames) { 64 | copyFromOriginal(options, this, evalCopyProperties); 65 | 66 | this.frames = frames || []; 67 | }; 68 | 69 | tree.evalEnv.prototype.inParenthesis = function () { 70 | if (!this.parensStack) { 71 | this.parensStack = []; 72 | } 73 | this.parensStack.push(true); 74 | }; 75 | 76 | tree.evalEnv.prototype.outOfParenthesis = function () { 77 | this.parensStack.pop(); 78 | }; 79 | 80 | tree.evalEnv.prototype.isMathOn = function () { 81 | return this.strictMath ? (this.parensStack && this.parensStack.length) : true; 82 | }; 83 | 84 | tree.evalEnv.prototype.isPathRelative = function (path) { 85 | return !/^(?:[a-z-]+:|\/)/.test(path); 86 | }; 87 | 88 | //todo - do the same for the toCSS env 89 | //tree.toCSSEnv = function (options) { 90 | //}; 91 | 92 | var copyFromOriginal = function(original, destination, propertiesToCopy) { 93 | if (!original) { return; } 94 | 95 | for(var i = 0; i < propertiesToCopy.length; i++) { 96 | if (original.hasOwnProperty(propertiesToCopy[i])) { 97 | destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; 98 | } 99 | } 100 | } 101 | })(require('./tree')); -------------------------------------------------------------------------------- /test/browser/common.js: -------------------------------------------------------------------------------- 1 | /*if not async then phantomjs fails to run the webserver and the test concurrently*/ 2 | var less = { async: true, strictMath: true }; 3 | 4 | /* record log messages for testing */ 5 | var logMessages = [], 6 | realConsoleLog = console.log; 7 | console.log = function(msg) { 8 | logMessages.push(msg); 9 | realConsoleLog.call(console, msg); 10 | }; 11 | 12 | var testLessEqualsInDocument = function() { 13 | testLessInDocument(testSheet); 14 | }; 15 | 16 | var testLessErrorsInDocument = function() { 17 | testLessInDocument(testErrorSheet); 18 | }; 19 | 20 | var testLessInDocument = function(testFunc) { 21 | var links = document.getElementsByTagName('link'), 22 | typePattern = /^text\/(x-)?less$/; 23 | 24 | for (var i = 0; i < links.length; i++) { 25 | if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && 26 | (links[i].type.match(typePattern)))) { 27 | testFunc(links[i]); 28 | } 29 | } 30 | }; 31 | 32 | var testSheet = function(sheet) { 33 | it(sheet.id + " should match the expected output", function() { 34 | var lessOutputId = sheet.id.replace("original-", ""), 35 | expectedOutputId = "expected-" + lessOutputId, 36 | lessOutput = document.getElementById(lessOutputId).innerText, 37 | expectedOutputHref = document.getElementById(expectedOutputId).href, 38 | expectedOutput = loadFile(expectedOutputHref); 39 | 40 | waitsFor(function() { 41 | return expectedOutput.loaded; 42 | }, "failed to load expected outout", 10000); 43 | 44 | runs(function() { 45 | // use sheet to do testing 46 | expect(lessOutput).toEqual(expectedOutput.text); 47 | }); 48 | }); 49 | }; 50 | 51 | var testErrorSheet = function(sheet) { 52 | it(sheet.id + " should match an error", function() { 53 | var lessHref = sheet.href, 54 | id = sheet.id.replace(/^original-less:/, "less-error-message:"), 55 | errorHref = lessHref.replace(/.less$/, ".txt"), 56 | errorFile = loadFile(errorHref), 57 | actualErrorElement = document.getElementById(id), 58 | actualErrorMsg; 59 | 60 | describe("the error", function() { 61 | expect(actualErrorElement).not.toBe(null); 62 | }); 63 | 64 | actualErrorMsg = actualErrorElement.innerText 65 | .replace(/\n\d+/g, function(lineNo) { return lineNo + " "; }) 66 | .replace(/\n\s*in /g, " in ") 67 | .replace("\n\n", "\n"); 68 | 69 | waitsFor(function() { 70 | return errorFile.loaded; 71 | }, "failed to load expected outout", 10000); 72 | 73 | runs(function() { 74 | var errorTxt = errorFile.text 75 | .replace("{path}", "") 76 | .replace("{pathrel}", "") 77 | .replace("{pathhref}", "http://localhost:8081/less/errors/") 78 | .replace("{404status}", " (404)"); 79 | expect(actualErrorMsg).toEqual(errorTxt); 80 | if (errorTxt == actualErrorMsg) { 81 | actualErrorElement.style.display = "none"; 82 | } 83 | }); 84 | }); 85 | }; 86 | 87 | var loadFile = function(href) { 88 | var request = new XMLHttpRequest(), 89 | response = { loaded: false, text: ""}; 90 | request.open('GET', href, true); 91 | request.onload = function(e) { 92 | response.text = request.response.replace(/\r/g, ""); 93 | response.loaded = true; 94 | } 95 | request.send(); 96 | return response; 97 | }; 98 | 99 | (function() { 100 | var jasmineEnv = jasmine.getEnv(); 101 | jasmineEnv.updateInterval = 1000; 102 | 103 | var htmlReporter = new jasmine.HtmlReporter(); 104 | 105 | jasmineEnv.addReporter(htmlReporter); 106 | 107 | jasmineEnv.specFilter = function(spec) { 108 | return htmlReporter.specFilter(spec); 109 | }; 110 | 111 | var currentWindowOnload = window.onload; 112 | 113 | window.onload = function() { 114 | if (currentWindowOnload) { 115 | currentWindowOnload(); 116 | } 117 | execJasmine(); 118 | }; 119 | 120 | function execJasmine() { 121 | setTimeout(function() { 122 | jasmineEnv.execute(); 123 | }, 3000); 124 | } 125 | 126 | })(); -------------------------------------------------------------------------------- /lib/less/import-visitor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | tree.importVisitor = function(importer, finish, evalEnv) { 3 | this._visitor = new tree.visitor(this); 4 | this._importer = importer; 5 | this._finish = finish; 6 | this.env = evalEnv || new tree.evalEnv(); 7 | this.importCount = 0; 8 | }; 9 | 10 | tree.importVisitor.prototype = { 11 | isReplacing: true, 12 | run: function (root) { 13 | var error; 14 | try { 15 | // process the contents 16 | this._visitor.visit(root); 17 | } 18 | catch(e) { 19 | error = e; 20 | } 21 | 22 | this.isFinished = true; 23 | 24 | if (this.importCount === 0) { 25 | this._finish(error); 26 | } 27 | }, 28 | visitImport: function (importNode, visitArgs) { 29 | var importVisitor = this, 30 | evaldImportNode; 31 | 32 | if (!importNode.css) { 33 | 34 | try { 35 | evaldImportNode = importNode.evalForImport(this.env); 36 | } catch(e){ 37 | if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } 38 | // attempt to eval properly and treat as css 39 | importNode.css = true; 40 | // if that fails, this error will be thrown 41 | importNode.error = e; 42 | } 43 | 44 | if (evaldImportNode && !evaldImportNode.css) { 45 | importNode = evaldImportNode; 46 | this.importCount++; 47 | var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); 48 | this._importer.push(importNode.getPath(), importNode.currentFileInfo, function (e, root, imported) { 49 | if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } 50 | if (imported && !importNode.options.multiple) { importNode.skip = imported; } 51 | 52 | var subFinish = function(e) { 53 | importVisitor.importCount--; 54 | 55 | if (importVisitor.importCount === 0 && importVisitor.isFinished) { 56 | importVisitor._finish(e); 57 | } 58 | }; 59 | 60 | if (root) { 61 | importNode.root = root; 62 | new(tree.importVisitor)(importVisitor._importer, subFinish, env) 63 | .run(root); 64 | } else { 65 | subFinish(); 66 | } 67 | }); 68 | } 69 | } 70 | visitArgs.visitDeeper = false; 71 | return importNode; 72 | }, 73 | visitRule: function (ruleNode, visitArgs) { 74 | visitArgs.visitDeeper = false; 75 | return ruleNode; 76 | }, 77 | visitDirective: function (directiveNode, visitArgs) { 78 | this.env.frames.unshift(directiveNode); 79 | return directiveNode; 80 | }, 81 | visitDirectiveOut: function (directiveNode) { 82 | this.env.frames.shift(); 83 | }, 84 | visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { 85 | this.env.frames.unshift(mixinDefinitionNode); 86 | return mixinDefinitionNode; 87 | }, 88 | visitMixinDefinitionOut: function (mixinDefinitionNode) { 89 | this.env.frames.shift(); 90 | }, 91 | visitRuleset: function (rulesetNode, visitArgs) { 92 | this.env.frames.unshift(rulesetNode); 93 | return rulesetNode; 94 | }, 95 | visitRulesetOut: function (rulesetNode) { 96 | this.env.frames.shift(); 97 | }, 98 | visitMedia: function (mediaNode, visitArgs) { 99 | this.env.frames.unshift(mediaNode.ruleset); 100 | return mediaNode; 101 | }, 102 | visitMediaOut: function (mediaNode) { 103 | this.env.frames.shift(); 104 | } 105 | }; 106 | 107 | })(require('./tree')); --------------------------------------------------------------------------------