From be788a05d4b050d4dff4e425a95b214f1c3505ed Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Tue, 23 Jul 2013 23:41:28 -0400 Subject: [PATCH 01/36] overhaul README. adds information about installing, testing and development --- README.md | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 324 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index af7a5689..a2aa53d4 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,331 @@ -less.js -======= +# [Less.js v1.4.1](http://lesscss.org) -The **dynamic** stylesheet language. - - - -about ------ +> The **dynamic** stylesheet language. [http://lesscss.org](http://lesscss.org). This is the JavaScript, and now official, stable version of LESS. -For more information on the language and usage visit [lesscss.org](http://lesscss.org). More information also available [in our wiki](https://github.com/less/less.js/wiki) -license -------- +## Getting Started -See `LICENSE` file. +Options for adding Less.js to your project: -> Copyright (c) 2009-2013 Alexis Sellier & The Core Less Team +* Install with [NPM](https://npmjs.org/): `npm install less` +* [Download the latest release][download] +* Clone the repo: `git clone git://github.com/less/less.js.git` + + + +## Feature Highlights +LESS extends CSS with dynamic features such as: + +* [nesting](#nesting) +* [variables](#variables) +* [operations](#operations) +* [mixins](#mixins) +* [extend](#extend) (selector inheritance) + +To learn about the many other features Less.js has to offer please visit [http://lesscss.org](http://lesscss.org) and [the Less.js wiki][wiki] + + +### Examples +#### nesting +Take advantage of nesting to make code more readable and maintainable. This: + +``` less +.nav > li > a { + border: 1px solid #f5f5f5; + &:hover { + border-color: #ddd; + } +} +``` + +renders to: + +``` css +.nav > li > a { + border: 1px solid #f5f5f5; +} +.nav > li > a:hover { + border-color: #ddd; +} +``` + + +#### variables +Updated commonly used values from a single location. + +``` less +// Variables ("inline" comments like this can be used) +@link-color: #428bca; // appears as "sea blue" + +/* Or "block comments" that span + multiple lines, like this */ +a { + color: @link-color; // use the variable in styles +} +``` + +Variables can also be used in `@import` statements, URLs, selector names, and more. + + + +#### operations +Continuing with the same example above, we can use our variables even easier to maintain with _operations_, which enables the use of addition, subraction, multiplication and division in your styles: + +``` less +// Variables +@link-color: #428bca; +@link-color-hover: darken(@link-color, 10%); + +// Styles +a { + color: @link-color; +} +a:hover { + color: @link-color-hover; +} +``` +renders to: + +``` css +a { + color: #428bca; +} +a:hover { + color: #3071a9; +} +``` + +#### mixins +##### "implicit" mixins +Mixins enable you to apply the styles of one selector inside another selector like this: + +``` less +// Any "regular" class... +.link { + color: @link-color; +} +a { + font-weight: bold; + .link; // ...can be used as an "implicit" mixin +} +``` + +renders to: + +``` css +.link { + color: #428bca; +} +a { + font-weight: bold; + color: #428bca; +} +``` + +So any selector can be an "implicit mixin". We'll show you a DRYer way to do this below. + + + +##### parametric mixins +Mixins can also accept parameters: + +``` less +// Transition mixin +.transition(@transition) { + -webkit-transition: @transition; + -moz-transition: @transition; + -o-transition: @transition; + transition: @transition; +} +``` + +used like this: + +``` less +a { + font-weight: bold; + color: @link-color; + .transition(color .2s ease-in-out); + // Hover state + &:hover { + color: @link-color-hover; + } +} +``` + +renders to: + +``` css +a { + font-weight: bold; + color: #428bca; + -webkit-transition: color 0.2s ease-in-out; + -moz-transition: color 0.2s ease-in-out; + -o-transition: color 0.2s ease-in-out; + transition: color 0.2s ease-in-out; +} +a:hover { + color: #3071a9; +} +``` + + +#### extend +The `extend` feature can be thought of as the _inverse_ of mixins. It accomplishes the goal of "borrowing styles", but rather than copying all the rules of _Selector A_ over to _Selector B_, `extend` copies the name of the _inheriting selector_ (_Selector B_) over to the _extending selector_ (_Selector A_). So continuing with the example used for [mixins](#mixins) above, extend works like this: + +``` less +.link { + color: @link-color; +} +a:extend(.link) { + font-weight: bold; +} +// Can also be written as +a { + &:extend(.link); + font-weight: bold; +} +``` + +renders to: + +``` css +.link, a { + color: #428bca; +} +``` + +## Usage + +### Compiling and Parsing +Invoke the compiler from node: + +``` javascript +var less = require('less'); + +less.render('.class { width: (1 + 1) }', function (e, css) { + console.log(css); +}); +``` + +Outputs: + +``` css +.class { + width: 2; +} +``` + +You may also manually invoke the parser and compiler: + +``` javascript +var parser = new(less.Parser); + +parser.parse('.class { width: (1 + 1) }', function (err, tree) { + if (err) { return console.error(err) } + console.log(tree.toCSS()); +}); +``` + + +### Configuration +You may also pass options to the compiler: + +``` javascript +var parser = new(less.Parser)({ + paths: ['.', './src/less'], // Specify search paths for @import directives + filename: 'style.less' // Specify a filename, for better error messages +}); + +parser.parse('.class { width: (1 + 1) }', function (e, tree) { + tree.toCSS({ compress: true }); // Minify CSS output +}); +``` + +## Contributing +Please read [CONTRIBUTING.md](./CONTRIBUTING.md). Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/). + +### Reporting Issues + +Before opening any issue, please search for existing issues and read the [Issue Guidelines](https://github.com/necolas/issue-guidelines), written by [Nicolas Gallagher](https://github.com/necolas/). After that if you find a bug or would like to make feature request, [please open a new issue][issues]. + + + +### Development + +#### Install Less.js + +Start by either [downloading this project][download] manually, or in the command line: + +```shell +git clone https://github.com/less/less.js.git "less" +``` +and then `cd less`. + + +#### Install dependencies + +Tests and benchmarking require Grunt `~0.4.1`. If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to install and use Grunt plugins, which are necessary for development with Less.js. + +To install Grunt and other necessary dependencies run: + +```shell +npm install +``` + +You should now be able to build Less.js, run tests, benchmarking, and other tasks listed in the Gruntfile. + + +## More information + +For general information on the language, configuration options or usage visit [lesscss.org](http://lesscss.org) or [the less wiki][wiki]. + +Here are other resources for using Less.js: + +* [stackoverflow.com][so] is a great place to get answers about Less. +* [node.js tools](https://github.com/less/less.js/wiki/Converting-LESS-to-CSS) for converting Less to CSS +* [GUI compilers for Less](https://github.com/less/less.js/wiki/GUI-compilers-that-use-LESS.js) +* [Less.js Issues][issues] for reporting bugs + + + + +## Building Less.js + +The Less.js [Gruntfile](Gruntfile.js) is configured with the following "convenience tasks" (you must first install [the necessary local dependencies](package.json)): + +``` +$ npm install +``` + +When all of the necessary dependencies are installed you may run the various Grunt.js commands provided: + +#### build - `grunt` +Build Less.js from from the `/lib/less` source files. + +#### test - `grunt test` +Runs jshint, nodeunit and headless jasmine tests using [phantomjs](http://code.google.com/p/phantomjs/). You must have phantomjs installed for the jasmine tests to run. + +#### readme - `grunt readme` +Build the README file from [a template](build/README.md) to ensure that metadata is up-to-date and (more likely to be) correct. + +Please review the [Gruntfile](Gruntfile.js) to become acquainted with the other available tasks. + +**Please note** that if you have any issues installing dependencies or running any of the Gruntfile commands, please make sure to uninstall any previous versions, both in the local node_modules directory, and clear your global npm cache, and then try running `npm install` again. After that if you still have issues, please let us know about it so we can help. + + +## Release History +See the [changelog](./CHANGELOG) + +## [License](./LICENSE) + +Copyright (c) 2009-2013 Alexis Sellier & The Core Less Team +Licensed under the Apache license. + + +[so]: http://stackoverflow.com/questions/tagged/twitter-bootstrap+less "StackOverflow.com" +[issues]: https://github.com/less/less.js/issues "GitHub Issues for Less.js" +[wiki]: https://github.com/less/less.js/wiki "The official wiki for Less.js" +[download]: https://github.com/less/less.js/zipball/master "Download Less.js" From 04e183e66d7ae1989aff8d1abeaba2fe36858d63 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Tue, 23 Jul 2013 23:43:39 -0400 Subject: [PATCH 02/36] Add README.md template to ./build directory. All changes to the README should be made here. Pull requests can still be made against the template, and anyone who pulls down the repo will be able to build the readme with `grunt readme`. So dates, versions, changelog or any other data that gets updated over time (in other files in the repo) will be generated into the readme automatically. --- build/README.md | 330 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 build/README.md diff --git a/build/README.md b/build/README.md new file mode 100644 index 00000000..fd5fde92 --- /dev/null +++ b/build/README.md @@ -0,0 +1,330 @@ +# [Less.js v<%= pkg.version %>](http://lesscss.org) + +> The **dynamic** stylesheet language. [http://lesscss.org](http://lesscss.org). + +This is the JavaScript, and now official, stable version of LESS. + + +## Getting Started + +Options for adding Less.js to your project: + +* Install with [NPM](https://npmjs.org/): `npm install less` +* [Download the latest release][download] +* Clone the repo: `git clone git://github.com/less/less.js.git` + + + +## Feature Highlights +LESS extends CSS with dynamic features such as: + +* [nesting](#nesting) +* [variables](#variables) +* [operations](#operations) +* [mixins](#mixins) +* [extend](#extend) (selector inheritance) + +To learn about the many other features Less.js has to offer please visit [http://lesscss.org](http://lesscss.org) and [the Less.js wiki][wiki] + + +### Examples +#### nesting +Take advantage of nesting to make code more readable and maintainable. This: + +```less +.nav > li > a { + border: 1px solid #f5f5f5; + &:hover { + border-color: #ddd; + } +} +``` + +renders to: + +```css +.nav > li > a { + border: 1px solid #f5f5f5; +} +.nav > li > a:hover { + border-color: #ddd; +} +``` + + +#### variables +Updated commonly used values from a single location. + +```less +// Variables ("inline" comments like this can be used) +@link-color: #428bca; // appears as "sea blue" + +/* Or "block comments" that span + multiple lines, like this */ +a { + color: @link-color; // use the variable in styles +} +``` + +Variables can also be used in `@import` statements, URLs, selector names, and more. + + + +#### operations +Continuing with the same example above, we can use our variables even easier to maintain with _operations_, which enables the use of addition, subraction, multiplication and division in your styles: + +```less +// Variables +@link-color: #428bca; +@link-color-hover: darken(@link-color, 10%); + +// Styles +a { + color: @link-color; +} +a:hover { + color: @link-color-hover; +} +``` +renders to: + +```css +a { + color: #428bca; +} +a:hover { + color: #3071a9; +} +``` + +#### mixins +##### "implicit" mixins +Mixins enable you to apply the styles of one selector inside another selector like this: + +```less +// Any "regular" class... +.link { + color: @link-color; +} +a { + font-weight: bold; + .link; // ...can be used as an "implicit" mixin +} +``` + +renders to: + +```css +.link { + color: #428bca; +} +a { + font-weight: bold; + color: #428bca; +} +``` + +So any selector can be an "implicit mixin". We'll show you a DRYer way to do this below. + + + +##### parametric mixins +Mixins can also accept parameters: + +```less +// Transition mixin +.transition(@transition) { + -webkit-transition: @transition; + -moz-transition: @transition; + -o-transition: @transition; + transition: @transition; +} +``` + +used like this: + +```less +a { + font-weight: bold; + color: @link-color; + .transition(color .2s ease-in-out); + // Hover state + &:hover { + color: @link-color-hover; + } +} +``` + +renders to: + +```css +a { + font-weight: bold; + color: #428bca; + -webkit-transition: color 0.2s ease-in-out; + -moz-transition: color 0.2s ease-in-out; + -o-transition: color 0.2s ease-in-out; + transition: color 0.2s ease-in-out; +} +a:hover { + color: #3071a9; +} +``` + + +#### extend +The `extend` feature can be thought of as the _inverse_ of mixins. It accomplishes the goal of "borrowing styles", but rather than copying all the rules of _Selector A_ over to _Selector B_, `extend` copies the name of the _inheriting selector_ (_Selector B_) over to the _extending selector_ (_Selector A_). So continuing with the example used for [mixins](#mixins) above, extend works like this: + +```less +.link { + color: @link-color; +} +a:extend(.link) { + font-weight: bold; +} +// Can also be written as +a { + &:extend(.link); + font-weight: bold; +} +``` + +renders to: + +```css +.link, a { + color: #428bca; +} +``` + +## Usage + +### Compiling and Parsing +Invoke the compiler from node: + +```javascript +var less = require('less'); + +less.render('.class { width: (1 + 1) }', function (e, css) { + console.log(css); +}); +``` + +Outputs: + +```css +.class { + width: 2; +} +``` + +You may also manually invoke the parser and compiler: + +```javascript +var parser = new(less.Parser); + +parser.parse('.class { width: (1 + 1) }', function (err, tree) { + if (err) { return console.error(err) } + console.log(tree.toCSS()); +}); +``` + + +### Configuration +You may also pass options to the compiler: + +```javascript +var parser = new(less.Parser)({ + paths: ['.', './src/less'], // Specify search paths for @import directives + filename: 'style.less' // Specify a filename, for better error messages +}); + +parser.parse('.class { width: (1 + 1) }', function (e, tree) { + tree.toCSS({ compress: true }); // Minify CSS output +}); +``` + +## More information + +For general information on the language, configuration options or usage visit [lesscss.org](http://lesscss.org) or [the less wiki][wiki]. + +Here are other resources for using Less.js: + +* [stackoverflow.com][so] is a great place to get answers about Less. +* [node.js tools](https://github.com/less/less.js/wiki/Converting-LESS-to-CSS) for converting Less to CSS +* [GUI compilers for Less](https://github.com/less/less.js/wiki/GUI-compilers-that-use-LESS.js) +* [Less.js Issues][issues] for reporting bugs + + + +## Contributing +Please read [CONTRIBUTING.md](./CONTRIBUTING.md). Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/). + +### Reporting Issues + +Before opening any issue, please search for existing issues and read the [Issue Guidelines](https://github.com/necolas/issue-guidelines), written by [Nicolas Gallagher](https://github.com/necolas/). After that if you find a bug or would like to make feature request, [please open a new issue][issues]. + + + +### Development + +#### Install Less.js + +Start by either [downloading this project][download] manually, or in the command line: + +```shell +git clone https://github.com/less/less.js.git "less" +``` +and then `cd less`. + + +#### Install dependencies + +Tests and benchmarking require Grunt `<%= pkg.devDependencies.grunt %>`. If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to install and use Grunt plugins, which are necessary for development with Less.js. + +To install Grunt and other necessary dependencies run: + +```shell +npm install +``` + +You should now be able to build Less.js, run tests, benchmarking, and other tasks listed in the Gruntfile. + + +## Building Less.js + +The Less.js [Gruntfile](Gruntfile.js) is configured with the following "convenience tasks" (you must first install [the necessary local dependencies](package.json)): + +```shell +$ npm install +``` + +When all of the necessary dependencies are installed you may run the various Grunt.js commands provided: + +#### build - `grunt` +Build Less.js from from the `/lib/less` source files. + +#### test - `grunt test` +Runs jshint, nodeunit and headless jasmine tests using [phantomjs](http://code.google.com/p/phantomjs/). You must have phantomjs installed for the jasmine tests to run. + +#### readme - `grunt readme` +Build the README file from [a template](build/README.md) to ensure that metadata is up-to-date and (more likely to be) correct. + +Please review the [Gruntfile](Gruntfile.js) to become acquainted with the other available tasks. + +**Please note** that if you have any issues installing dependencies or running any of the Gruntfile commands, please make sure to uninstall any previous versions, both in the local node_modules directory, and clear your global npm cache, and then try running `npm install` again. After that if you still have issues, please let us know about it so we can help. + + +## Release History +See the [changelog](CHANGELOG) + +## [License](LICENSE) + +Copyright (c) 2009-<%= grunt.template.today("yyyy") %> [{%= authors[0].name %}]({%= authors[0].url %}) & The Core Less Team +Licensed under the [Apache License](LICENSE). + + +[so]: http://stackoverflow.com/questions/tagged/twitter-bootstrap+less "StackOverflow.com" +[issues]: https://github.com/less/less.js/issues "GitHub Issues for Less.js" +[wiki]: https://github.com/less/less.js/wiki "The official wiki for Less.js" +[download]: https://github.com/less/less.js/zipball/master "Download Less.js" \ No newline at end of file From 3513889683b9e164c6d3b2e7bb60ad809c46a526 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Tue, 23 Jul 2013 23:44:05 -0400 Subject: [PATCH 03/36] adds grunt dependencies to package.json, .jshintrc for jshint task, updates gitignore to account for tmp dir while testing --- .gitignore | 10 +++++++++- .jshintrc | 15 +++++++++++++++ package.json | 24 ++++++++++++++++-------- 3 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 .jshintrc diff --git a/.gitignore b/.gitignore index a0db3828..bb4e736a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,16 @@ -node_modules +# OS and IDE .emacs* *.flymake *~ .#* .idea +*.sublime-* + +# npm +node_modules +npm-debug.log + +# project-specific +tmp test/browser/less.js test/browser/test-runner-*.htm diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..c25d1ab4 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,15 @@ +{ + "curly": true, + "eqeqeq": true, + "evil": true, + "immed": true, + "latedef": true, + "newcap": true, + "noarg": true, + "sub": true, + "undef": true, + "unused": true, + "boss": true, + "eqnull": true, + "node": true +} diff --git a/package.json b/package.json index b20dba56..f6e1bd42 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,12 @@ "type": "git", "url": "https://github.com/less/less.js.git" }, + "licenses": [ + { + "type": "Apache v2", + "url": "https://github.com/less/less.js/blob/master/LICENSE" + } + ], "bin": { "lessc": "./bin/lessc" }, @@ -28,7 +34,7 @@ "node": ">=0.4.2" }, "scripts": { - "test": "make test" + "test": "grunt test" }, "optionalDependencies": { "mime": "1.2.x", @@ -37,7 +43,15 @@ "ycssmin": ">=1.0.1" }, "devDependencies": { - "diff": "~1.0" + "diff": "~1.0.5", + "grunt": "~0.4.1", + "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-concat": "~0.3.0", + "grunt-contrib-jshint": "~0.6.0", + "grunt-contrib-uglify": "~0.2.2", + "grunt-contrib-watch": "~0.4.4", + "grunt-shell": "~0.3.1", + "matchdep": "~0.1.2" }, "keywords": [ "compile less", @@ -63,11 +77,5 @@ "stylesheet", "variables in css", "css less" - ], - "licenses": [ - { - "type": "Apache v2", - "url": "https://github.com/less/less.js/blob/master/LICENSE" - } ] } From 2ec34489ca7000d2a7d629f5d0f705b9c9c129cb Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Tue, 23 Jul 2013 23:56:47 -0400 Subject: [PATCH 04/36] add build.yml for controlling paths and order of source files only. Paths for anything besides source files, such as testing or benchmarking, should be controlled in the Gruntfile. this file must be updated when files in the project change. --- build/build.yml | 146 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 build/build.yml diff --git a/build/build.yml b/build/build.yml new file mode 100644 index 00000000..40c6dea9 --- /dev/null +++ b/build/build.yml @@ -0,0 +1,146 @@ +### +# NOTICE: +# this file is specifically for controlling +# paths for Less.js source files, as well as +# the order in which source files are +# concatenated. +# +# Please do not add paths for anything else +# to this file. All other paths for testing, +# benchmarking and so on should be controlled +# in the Gruntfile. +### + +# Less.js Lib +lib: lib/less + + +# ================================= +# General +# ================================= +prepend: + browser: build/require.js + rhino: build/require-rhino.js + +append: + amd: build/amd.js + browser: <%= build.lib %>/browser.js + rhino: <%= build.lib %>/rhino.js + + +# ================================= +# Core less files +# ================================= + +# <%= build.less.* %> +less: + parser : <%= build.lib %>/parser.js + functions: <%= build.lib %>/functions.js + colors : <%= build.lib %>/colors.js + tree : <%= build.lib %>/tree.js + env : <%= build.lib %>/env.js + visitor : <%= build.lib %>/visitor.js + import : <%= build.lib %>/import-visitor.js + join : <%= build.lib %>/join-selector-visitor.js + to_css : <%= build.lib %>/to-css-visitor.js + extend : <%= build.lib %>/extend-visitor.js + browser : <%= build.lib %>/browser.js + + # glob all files in ./lib/less/tree directory + treedir : <%= build.lib %>/tree/*.js + + +# ================================= +# Browser build +# ================================= + +# <%= build.browser %> +browser: + + # prepend utils + - <%= build.prepend.browser %> + + # core + - <%= build.less.parser %> + - <%= build.less.functions %> + - <%= build.less.colors %> + - <%= build.less.treedir %> # glob all files + - <%= build.less.tree %> + - <%= build.less.env %> + - <%= build.less.visitor %> + - <%= build.less.import %> + - <%= build.less.join %> + - <%= build.less.to_css %> + - <%= build.less.extend %> + + # append browser-specific code + - <%= build.append.browser %> + - <%= build.append.amd %> + + +# ================================= +# Rhino build +# ================================= + +# <%= build.rhino %> +rhino: + # prepend utils + - <%= build.prepend.rhino %> + + # core + - <%= build.less.parser %> + - <%= build.less.env %> + - <%= build.less.visitor %> + - <%= build.less.import %> + - <%= build.less.join %> + - <%= build.less.to_css %> + - <%= build.less.extend %> + - <%= build.less.functions %> + - <%= build.less.colors %> + - <%= build.less.treedir %> # glob all files + - <%= build.less.tree %> + + # append rhino-specific code + - <%= build.append.rhino %> + + +# ================================= +# Tree files +# ================================= + +# <%= build.tree %> +# Technically listing the array out this way isn't +# necessary since we can glob the files in alphabetical +# order anyway. But this gives you control over the order +# the files are used, and allows targeting of individual +# files directly in the Gruntfile. But be we can just +# remove this if files can be concatenated in any order. +tree: + - <%= build.lib %>/tree/alpha.js + - <%= build.lib %>/tree/anonymous.js + - <%= build.lib %>/tree/assignment.js + - <%= build.lib %>/tree/call.js + - <%= build.lib %>/tree/color.js + - <%= build.lib %>/tree/comment.js + - <%= build.lib %>/tree/condition.js + - <%= build.lib %>/tree/dimension.js + - <%= build.lib %>/tree/directive.js + - <%= build.lib %>/tree/element.js + - <%= build.lib %>/tree/expression.js + - <%= build.lib %>/tree/extend.js + - <%= build.lib %>/tree/import.js + - <%= build.lib %>/tree/javascript.js + - <%= build.lib %>/tree/keyword.js + - <%= build.lib %>/tree/media.js + - <%= build.lib %>/tree/mixin.js + - <%= build.lib %>/tree/negative.js + - <%= build.lib %>/tree/operation.js + - <%= build.lib %>/tree/paren.js + - <%= build.lib %>/tree/quoted.js + - <%= build.lib %>/tree/rule.js + - <%= build.lib %>/tree/ruleset.js + - <%= build.lib %>/tree/selector.js + - <%= build.lib %>/tree/unicode-descriptor.js + - <%= build.lib %>/tree/url.js + - <%= build.lib %>/tree/value.js + - <%= build.lib %>/tree/variable.js \ No newline at end of file From a3c0049d4c89af4160b19274c6940249d1b42e2f Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Tue, 23 Jul 2013 23:59:20 -0400 Subject: [PATCH 05/36] Add Gruntfile with tasks/target configurations, and commands for running tasks. --- Gruntfile.js | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 Gruntfile.js diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..e60c57f0 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,184 @@ +'use strict'; + +module.exports = function(grunt) { + + // Project configuration. + grunt.initConfig({ + build: grunt.file.readYAML('build/build.yml'), + pkg: grunt.file.readJSON('package.json'), + + // Metadata + meta: { + license: '<%= _.pluck(pkg.licenses, "type").join(", ") %>', + copyright: 'Copyright (c) 2009-<%= grunt.template.today("yyyy") %>', + banner: + '/* \n' + + ' * LESS - <%= pkg.description %> v<%= pkg.version %> \n' + + ' * http://lesscss.org \n' + + ' * \n' + + ' * <%= meta.copyright %>, <%= pkg.author %> \n' + + ' * Licensed under the <%= meta.license %> License. \n' + + ' * \n' + + ' * @licence \n' + + ' */ \n\n' + }, + + shell: { + options: {stdout: 'log'}, + test: { + command: 'node test/less-test.js' + }, + browser: { + command: 'node test/browser-test-prepare.js' + }, + phantom: { + command: 'phantomjs test/browser/phantom-runner.js' + }, + benchmark: { + command: 'node benchmark/less-benchmark.js' + } + }, + + concat: { + options: { + stripBanner: true, + banner: '<%= meta.banner %>\n\n(function (window, undefined) {', + footer: '\n})(window);' + }, + // Browser versions + browser: { + src: ['<%= build.browser %>'], + dest: 'test/browser/less.js' + }, + alpha: { + src: ['<%= build.browser %>'], + dest: 'tmp/less-<%= pkg.version %>-alpha.js' + }, + beta: { + src: ['<%= build.browser %>'], + dest: 'tmp/less-<%= pkg.version %>-beta.js' + }, + // Rhino + rhino: { + options: { + banner: '/* LESS.js v<%= pkg.version %> RHINO | <%= meta.copyright %>, <%= pkg.author %> */\n\n', + footer: '' // override task-level footer + }, + src: ['<%= build.rhino %>'], + dest: 'tmp/less-rhino-<%= pkg.version %>.js' + }, + // Generate readme + readme: { + options: { + process: true, + banner: '' // override task-level banner + }, + src: ['build/README.md'], + dest: 'README.md' + } + }, + + uglify: { + options: { + banner: '<%= meta.banner %>', + mangle: true + }, + browser: { + src: ['<%= concat.browser.src %>'], + dest: 'tmp/less-<%= pkg.version %>.min.js' + }, + alpha: { + src: ['<%= concat.alpha.src %>'], + dest: 'tmp/less-<%= pkg.version %>-alpha.min.js' + }, + beta: { + src: ['<%= concat.beta.src %>'], + dest: 'tmp/less-<%= pkg.version %>-beta.min.js' + } + }, + + jshint: { + options: { + jshintrc: '.jshintrc' + }, + benchmark: { + src: ['benchmark/**/*.js'] + }, + gruntfile: { + src: 'Gruntfile.js' + }, + lib: { + src: ['lib/**/*.js'] + }, + test: { + src: ['test/*.js', 'test/browser/runner-*.js'] + } + }, + + // Before running tests, clean out the results + // of any previous tests. (this will need to be + // setup based on configuration of browser tests. + clean: { + test: ['test/browser/test-runner-*.htm'] + } + }); + + // Load these plugins to provide the necessary tasks + require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); + + // Actually load this plugin's task(s). + grunt.loadTasks('build/tasks'); + + // Default task to build Less.js + grunt.registerTask('default', [ + 'browser', + 'rhino', + 'min' + ]); + + // Browser + grunt.registerTask('browser', [ + 'concat:browser', + 'uglify:browser' + ]); + + // Rhino + grunt.registerTask('rhino', [ + 'concat:rhino' + // 'uglify:rhino' + ]); + // Minify + grunt.registerTask('min', [ + 'uglify' + ]); + + // Alpha + grunt.registerTask('alpha', [ + 'concat:alpha', + 'uglify:alpha' + ]); + // Beta + grunt.registerTask('beta', [ + 'concat:beta', + 'uglify:beta' + ]); + + // Run all tests + grunt.registerTask('test', [ + 'jshint:lib', + 'clean', + 'shell:test' + // 'shell:browser', + // 'shell:phantom' + ]); + + // Run benchmark + grunt.registerTask('benchmark', [ + 'shell:benchmark' + ]); + + // Readme. + grunt.registerTask('readme', [ + 'concat:readme' + ]); +}; From ce6e4ede997c5ced90418fd7aeb6cf66735a95d6 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Wed, 24 Jul 2013 16:02:34 -0400 Subject: [PATCH 06/36] remove header.js since this is accomplished with meta.banner in the gruntfile. remove makefile. --- Makefile | 48 +++++++++++++++--------------------------------- build/header.js | 9 --------- 2 files changed, 15 insertions(+), 42 deletions(-) delete mode 100644 build/header.js diff --git a/Makefile b/Makefile index db27cf04..9e29bb6f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # # Run all tests # -test: +test: node test/less-test.js # @@ -15,20 +15,11 @@ benchmark: # SRC = lib/less HEADER = build/header.js -VERSION = `cat package.json | grep version \ - | grep -o '[0-9]\.[0-9]\.[0-9]\+'` +VERSION = `cat package.json | grep version | grep -o '[0-9]\.[0-9]\.[0-9]\+'` DIST = dist/less-${VERSION}.js RHINO = dist/less-rhino-${VERSION}.js DIST_MIN = dist/less-${VERSION}.min.js -browser-prepare: DIST := test/browser/less.js - -alpha: DIST := dist/less-${VERSION}-alpha.js -alpha: DIST_MIN := dist/less-${VERSION}-alpha.min.js - -beta: DIST := dist/less-${VERSION}-beta.js -beta: DIST_MIN := dist/less-${VERSION}-beta.min.js - less: @@mkdir -p dist @@touch ${DIST} @@ -50,10 +41,16 @@ less: build/amd.js >> ${DIST} @@echo "})(window);" >> ${DIST} @@echo ${DIST} built. - +min: less + @@echo minifying... + @@uglifyjs ${DIST} > ${DIST_MIN} + @@echo ${DIST_MIN} built. + + +browser-prepare: DIST := test/browser/less.js browser-prepare: less node test/browser-test-prepare.js - + browser-test: browser-prepare phantomjs test/browser/phantom-runner.js @@ -78,27 +75,12 @@ rhino: ${SRC}/rhino.js > ${RHINO} @@echo ${RHINO} built. -min: less - @@echo minifying... - @@uglifyjs ${DIST} > ${DIST_MIN} - @@echo ${DIST_MIN} built. + +alpha: DIST := dist/less-${VERSION}-alpha.js +alpha: DIST_MIN := dist/less-${VERSION}-alpha.min.js alpha: min +beta: DIST := dist/less-${VERSION}-beta.js +beta: DIST_MIN := dist/less-${VERSION}-beta.min.js beta: min - -alpha-release: alpha - git add dist/*.js - git commit -m "Update alpha ${VERSION}" - -dist: min rhino - git add dist/* - git commit -a -m "(dist) build ${VERSION}" - git archive master --prefix=less/ -o less-${VERSION}.tar.gz - npm publish less-${VERSION}.tar.gz - -stable: - npm tag less@${VERSION} stable - - -.PHONY: test benchmark diff --git a/build/header.js b/build/header.js deleted file mode 100644 index 791b5514..00000000 --- a/build/header.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - * LESS - Leaner CSS v@VERSION - * http://lesscss.org - * - * Copyright (c) 2009-2013, Alexis Sellier - * Licensed under the Apache 2.0 License. - * - * @licence - */ From 1fcf190ed7030d40a97b8eacc9bdd755162f8434 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Wed, 24 Jul 2013 16:03:52 -0400 Subject: [PATCH 07/36] fix code fences and some metadata, remove watch task from package.json --- CONTRIBUTING.md | 2 +- README.md | 72 ++++++++++++++++++++++++------------------------- build/README.md | 2 +- package.json | 1 - 4 files changed, 38 insertions(+), 39 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 52f86662..0d89c29a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ > We welcome feature requests and bug reports. Please read these guidelines before submitting one. -**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. +**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, please remember that GitHub usernames also start with the at sign. If you don't wrap them in backticks, users will get unintended notifications from you. GitHub has other great markdown features as well, [go here to learn more about them](https://help.github.com/articles/github-flavored-markdown). diff --git a/README.md b/README.md index a2aa53d4..286b8fd1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [Less.js v1.4.1](http://lesscss.org) +# [Less.js v1.4.2](http://lesscss.org) > The **dynamic** stylesheet language. [http://lesscss.org](http://lesscss.org). @@ -31,7 +31,7 @@ To learn about the many other features Less.js has to offer please visit [http:/ #### nesting Take advantage of nesting to make code more readable and maintainable. This: -``` less +```less .nav > li > a { border: 1px solid #f5f5f5; &:hover { @@ -42,7 +42,7 @@ Take advantage of nesting to make code more readable and maintainable. This: renders to: -``` css +```css .nav > li > a { border: 1px solid #f5f5f5; } @@ -55,7 +55,7 @@ renders to: #### variables Updated commonly used values from a single location. -``` less +```less // Variables ("inline" comments like this can be used) @link-color: #428bca; // appears as "sea blue" @@ -73,7 +73,7 @@ Variables can also be used in `@import` statements, URLs, selector names, and mo #### operations Continuing with the same example above, we can use our variables even easier to maintain with _operations_, which enables the use of addition, subraction, multiplication and division in your styles: -``` less +```less // Variables @link-color: #428bca; @link-color-hover: darken(@link-color, 10%); @@ -88,7 +88,7 @@ a:hover { ``` renders to: -``` css +```css a { color: #428bca; } @@ -101,7 +101,7 @@ a:hover { ##### "implicit" mixins Mixins enable you to apply the styles of one selector inside another selector like this: -``` less +```less // Any "regular" class... .link { color: @link-color; @@ -114,7 +114,7 @@ a { renders to: -``` css +```css .link { color: #428bca; } @@ -131,7 +131,7 @@ So any selector can be an "implicit mixin". We'll show you a DRYer way to do thi ##### parametric mixins Mixins can also accept parameters: -``` less +```less // Transition mixin .transition(@transition) { -webkit-transition: @transition; @@ -143,7 +143,7 @@ Mixins can also accept parameters: used like this: -``` less +```less a { font-weight: bold; color: @link-color; @@ -157,7 +157,7 @@ a { renders to: -``` css +```css a { font-weight: bold; color: #428bca; @@ -175,7 +175,7 @@ a:hover { #### extend The `extend` feature can be thought of as the _inverse_ of mixins. It accomplishes the goal of "borrowing styles", but rather than copying all the rules of _Selector A_ over to _Selector B_, `extend` copies the name of the _inheriting selector_ (_Selector B_) over to the _extending selector_ (_Selector A_). So continuing with the example used for [mixins](#mixins) above, extend works like this: -``` less +```less .link { color: @link-color; } @@ -191,7 +191,7 @@ a { renders to: -``` css +```css .link, a { color: #428bca; } @@ -202,7 +202,7 @@ renders to: ### Compiling and Parsing Invoke the compiler from node: -``` javascript +```javascript var less = require('less'); less.render('.class { width: (1 + 1) }', function (e, css) { @@ -212,7 +212,7 @@ less.render('.class { width: (1 + 1) }', function (e, css) { Outputs: -``` css +```css .class { width: 2; } @@ -220,7 +220,7 @@ Outputs: You may also manually invoke the parser and compiler: -``` javascript +```javascript var parser = new(less.Parser); parser.parse('.class { width: (1 + 1) }', function (err, tree) { @@ -233,7 +233,7 @@ parser.parse('.class { width: (1 + 1) }', function (err, tree) { ### Configuration You may also pass options to the compiler: -``` javascript +```javascript var parser = new(less.Parser)({ paths: ['.', './src/less'], // Specify search paths for @import directives filename: 'style.less' // Specify a filename, for better error messages @@ -244,6 +244,19 @@ parser.parse('.class { width: (1 + 1) }', function (e, tree) { }); ``` +## More information + +For general information on the language, configuration options or usage visit [lesscss.org](http://lesscss.org) or [the less wiki][wiki]. + +Here are other resources for using Less.js: + +* [stackoverflow.com][so] is a great place to get answers about Less. +* [node.js tools](https://github.com/less/less.js/wiki/Converting-LESS-to-CSS) for converting Less to CSS +* [GUI compilers for Less](https://github.com/less/less.js/wiki/GUI-compilers-that-use-LESS.js) +* [Less.js Issues][issues] for reporting bugs + + + ## Contributing Please read [CONTRIBUTING.md](./CONTRIBUTING.md). Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/). @@ -278,25 +291,11 @@ npm install You should now be able to build Less.js, run tests, benchmarking, and other tasks listed in the Gruntfile. -## More information - -For general information on the language, configuration options or usage visit [lesscss.org](http://lesscss.org) or [the less wiki][wiki]. - -Here are other resources for using Less.js: - -* [stackoverflow.com][so] is a great place to get answers about Less. -* [node.js tools](https://github.com/less/less.js/wiki/Converting-LESS-to-CSS) for converting Less to CSS -* [GUI compilers for Less](https://github.com/less/less.js/wiki/GUI-compilers-that-use-LESS.js) -* [Less.js Issues][issues] for reporting bugs - - - - ## Building Less.js The Less.js [Gruntfile](Gruntfile.js) is configured with the following "convenience tasks" (you must first install [the necessary local dependencies](package.json)): -``` +```shell $ npm install ``` @@ -317,15 +316,16 @@ Please review the [Gruntfile](Gruntfile.js) to become acquainted with the other ## Release History -See the [changelog](./CHANGELOG) +See the [changelog](CHANGELOG) -## [License](./LICENSE) +## [License](LICENSE) -Copyright (c) 2009-2013 Alexis Sellier & The Core Less Team -Licensed under the Apache license. +Copyright (c) 2009-2013 [Alexis Sellier](http://cloudhead.io/) & The Core Less Team +Licensed under the [Apache License](LICENSE). [so]: http://stackoverflow.com/questions/tagged/twitter-bootstrap+less "StackOverflow.com" [issues]: https://github.com/less/less.js/issues "GitHub Issues for Less.js" [wiki]: https://github.com/less/less.js/wiki "The official wiki for Less.js" [download]: https://github.com/less/less.js/zipball/master "Download Less.js" +})(window); \ No newline at end of file diff --git a/build/README.md b/build/README.md index fd5fde92..e3d4e49c 100644 --- a/build/README.md +++ b/build/README.md @@ -320,7 +320,7 @@ See the [changelog](CHANGELOG) ## [License](LICENSE) -Copyright (c) 2009-<%= grunt.template.today("yyyy") %> [{%= authors[0].name %}]({%= authors[0].url %}) & The Core Less Team +Copyright (c) 2009-<%= grunt.template.today("yyyy") %> [Alexis Sellier](http://cloudhead.io/) & The Core Less Team Licensed under the [Apache License](LICENSE). diff --git a/package.json b/package.json index f6e1bd42..96030300 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "grunt-contrib-concat": "~0.3.0", "grunt-contrib-jshint": "~0.6.0", "grunt-contrib-uglify": "~0.2.2", - "grunt-contrib-watch": "~0.4.4", "grunt-shell": "~0.3.1", "matchdep": "~0.1.2" }, From 4f3da9554e223c73f555c5af97793f84a4c160c0 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Wed, 24 Jul 2013 16:07:00 -0400 Subject: [PATCH 08/36] remove makefile --- Makefile | 86 -------------------------------------------------------- 1 file changed, 86 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index 9e29bb6f..00000000 --- a/Makefile +++ /dev/null @@ -1,86 +0,0 @@ -# -# Run all tests -# -test: - node test/less-test.js - -# -# Run benchmark -# -benchmark: - node benchmark/less-benchmark.js - -# -# Build less.js -# -SRC = lib/less -HEADER = build/header.js -VERSION = `cat package.json | grep version | grep -o '[0-9]\.[0-9]\.[0-9]\+'` -DIST = dist/less-${VERSION}.js -RHINO = dist/less-rhino-${VERSION}.js -DIST_MIN = dist/less-${VERSION}.min.js - -less: - @@mkdir -p dist - @@touch ${DIST} - @@cat ${HEADER} | sed s/@VERSION/${VERSION}/ > ${DIST} - @@echo "(function (window, undefined) {" >> ${DIST} - @@cat build/require.js\ - ${SRC}/parser.js\ - ${SRC}/functions.js\ - ${SRC}/colors.js\ - ${SRC}/tree/*.js\ - ${SRC}/tree.js\ - ${SRC}/env.js\ - ${SRC}/visitor.js\ - ${SRC}/import-visitor.js\ - ${SRC}/join-selector-visitor.js\ - ${SRC}/to-css-visitor.js\ - ${SRC}/extend-visitor.js\ - ${SRC}/browser.js\ - build/amd.js >> ${DIST} - @@echo "})(window);" >> ${DIST} - @@echo ${DIST} built. -min: less - @@echo minifying... - @@uglifyjs ${DIST} > ${DIST_MIN} - @@echo ${DIST_MIN} built. - - -browser-prepare: DIST := test/browser/less.js -browser-prepare: less - node test/browser-test-prepare.js - -browser-test: browser-prepare - phantomjs test/browser/phantom-runner.js - -browser-test-server: browser-prepare - phantomjs test/browser/phantom-runner.js --no-tests - -rhino: - @@mkdir -p dist - @@touch ${RHINO} - @@cat build/require-rhino.js\ - ${SRC}/parser.js\ - ${SRC}/env.js\ - ${SRC}/visitor.js\ - ${SRC}/import-visitor.js\ - ${SRC}/join-selector-visitor.js\ - ${SRC}/to-css-visitor.js\ - ${SRC}/extend-visitor.js\ - ${SRC}/functions.js\ - ${SRC}/colors.js\ - ${SRC}/tree/*.js\ - ${SRC}/tree.js\ - ${SRC}/rhino.js > ${RHINO} - @@echo ${RHINO} built. - - - -alpha: DIST := dist/less-${VERSION}-alpha.js -alpha: DIST_MIN := dist/less-${VERSION}-alpha.min.js -alpha: min - -beta: DIST := dist/less-${VERSION}-beta.js -beta: DIST_MIN := dist/less-${VERSION}-beta.min.js -beta: min From aa7e3cc0157114b442825e1678a2da82f7868bfb Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Tue, 30 Jul 2013 02:17:54 -0400 Subject: [PATCH 09/36] fixed `stripBanners` type in concat task. reformatted author name/email in package.json. --- Gruntfile.js | 21 ++++++++++----------- package.json | 5 ++++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index e60c57f0..12b242e5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -16,7 +16,7 @@ module.exports = function(grunt) { ' * LESS - <%= pkg.description %> v<%= pkg.version %> \n' + ' * http://lesscss.org \n' + ' * \n' + - ' * <%= meta.copyright %>, <%= pkg.author %> \n' + + ' * <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>> \n' + ' * Licensed under the <%= meta.license %> License. \n' + ' * \n' + ' * @licence \n' + @@ -41,7 +41,7 @@ module.exports = function(grunt) { concat: { options: { - stripBanner: true, + stripBanners: true, banner: '<%= meta.banner %>\n\n(function (window, undefined) {', footer: '\n})(window);' }, @@ -52,20 +52,20 @@ module.exports = function(grunt) { }, alpha: { src: ['<%= build.browser %>'], - dest: 'tmp/less-<%= pkg.version %>-alpha.js' + dest: 'dist/less-<%= pkg.version %>-alpha.js' }, beta: { src: ['<%= build.browser %>'], - dest: 'tmp/less-<%= pkg.version %>-beta.js' + dest: 'dist/less-<%= pkg.version %>-beta.js' }, // Rhino rhino: { options: { - banner: '/* LESS.js v<%= pkg.version %> RHINO | <%= meta.copyright %>, <%= pkg.author %> */\n\n', + banner: '/* LESS.js v<%= pkg.version %> RHINO | <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>> */\n\n', footer: '' // override task-level footer }, src: ['<%= build.rhino %>'], - dest: 'tmp/less-rhino-<%= pkg.version %>.js' + dest: 'dist/less-rhino-<%= pkg.version %>.js' }, // Generate readme readme: { @@ -85,15 +85,15 @@ module.exports = function(grunt) { }, browser: { src: ['<%= concat.browser.src %>'], - dest: 'tmp/less-<%= pkg.version %>.min.js' + dest: 'dist/less-<%= pkg.version %>.min.js' }, alpha: { src: ['<%= concat.alpha.src %>'], - dest: 'tmp/less-<%= pkg.version %>-alpha.min.js' + dest: 'dist/less-<%= pkg.version %>-alpha.min.js' }, beta: { src: ['<%= concat.beta.src %>'], - dest: 'tmp/less-<%= pkg.version %>-beta.min.js' + dest: 'dist/less-<%= pkg.version %>-beta.min.js' } }, @@ -132,8 +132,7 @@ module.exports = function(grunt) { // Default task to build Less.js grunt.registerTask('default', [ 'browser', - 'rhino', - 'min' + 'rhino' ]); // Browser diff --git a/package.json b/package.json index 96030300..550ec8d7 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,10 @@ "version": "1.4.2", "description": "Leaner CSS", "homepage": "http://lesscss.org", - "author": "Alexis Sellier ", + "author": { + "name": "Alexis Sellier", + "email": "self@cloudhead.net" + }, "contributors": [ "The Core Less Team" ], From 38a8664013df8281ea30b3fa6373437985790f1b Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Thu, 1 Aug 2013 06:51:59 -0400 Subject: [PATCH 10/36] Merge branch 'less.js/1.5.0-wip' Conflicts: .jshintrc Makefile package.json --- .gitignore | 2 + .jshintignore | 5 + .jshintrc | 20 +- CHANGELOG.md | 3 +- Makefile | 114 +++++++ bin/lessc | 55 +++- build/browser-header.js | 4 + build/rhino-header.js | 4 + lib/less/browser.js | 41 +-- lib/less/env.js | 9 +- lib/less/extend-visitor.js | 11 +- lib/less/functions.js | 25 +- lib/less/index.js | 18 +- lib/less/join-selector-visitor.js | 4 + lib/less/lessc_helper.js | 8 +- lib/less/parser.js | 244 +++++++++------ lib/less/rhino.js | 8 +- lib/less/source-map-output.js | 84 ++++++ lib/less/to-css-visitor.js | 98 +++++- lib/less/tree.js | 40 ++- lib/less/tree/alpha.js | 18 +- lib/less/tree/anonymous.js | 11 +- lib/less/tree/assignment.js | 16 +- lib/less/tree/call.js | 24 +- lib/less/tree/color.js | 40 +-- lib/less/tree/comment.js | 8 +- lib/less/tree/condition.js | 2 +- lib/less/tree/dimension.js | 318 ++++++++++---------- lib/less/tree/directive.js | 22 +- lib/less/tree/element.js | 47 ++- lib/less/tree/expression.js | 12 +- lib/less/tree/import.js | 17 +- lib/less/tree/javascript.js | 1 + lib/less/tree/keyword.js | 7 +- lib/less/tree/media.js | 26 +- lib/less/tree/mixin.js | 20 +- lib/less/tree/negative.js | 6 +- lib/less/tree/operation.js | 16 +- lib/less/tree/paren.js | 7 +- lib/less/tree/quoted.js | 13 +- lib/less/tree/rule.js | 24 +- lib/less/tree/ruleset.js | 229 +++++++------- lib/less/tree/selector.js | 28 +- lib/less/tree/unicode-descriptor.js | 7 +- lib/less/tree/url.js | 7 +- lib/less/tree/value.js | 15 +- lib/less/tree/variable.js | 8 +- lib/less/visitor.js | 3 +- package.json | 34 +-- test/browser-test-prepare.js | 7 +- test/browser/css/rootpath-relative/urls.css | 1 - test/browser/css/urls.css | 1 - test/css/comments.css | 8 +- test/css/compression/compression.css | 3 +- test/css/css-guards.css | 6 + test/css/extend-selector.css | 4 +- test/css/import-inline.css | 4 +- test/css/import.css | 2 - test/css/media.css | 3 +- test/css/static-urls/urls.css | 1 - test/css/urls.css | 2 - test/less-test.js | 57 +++- test/less/css-guards.less | 35 ++- test/less/errors/svg-gradient1.txt | 2 +- test/less/import/invalid-css.less | 2 +- test/less/sourcemaps/basic.less | 26 ++ test/sourcemaps/basic.json | 1 + test/sourcemaps/index.html | 16 + 68 files changed, 1285 insertions(+), 679 deletions(-) create mode 100644 .jshintignore create mode 100644 Makefile create mode 100644 build/browser-header.js create mode 100644 build/rhino-header.js create mode 100644 lib/less/source-map-output.js create mode 100644 test/less/sourcemaps/basic.less create mode 100644 test/sourcemaps/basic.json create mode 100644 test/sourcemaps/index.html diff --git a/.gitignore b/.gitignore index bb4e736a..3fa214b2 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ npm-debug.log tmp test/browser/less.js test/browser/test-runner-*.htm +test/sourcemaps/*.map +test/sourcemaps/*.css diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 00000000..ccc70d75 --- /dev/null +++ b/.jshintignore @@ -0,0 +1,5 @@ +benchmark/ +build/ +dist/ +node_modules/ +test/browser/ diff --git a/.jshintrc b/.jshintrc index c25d1ab4..2f830954 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,15 +1,9 @@ { - "curly": true, - "eqeqeq": true, - "evil": true, - "immed": true, - "latedef": true, - "newcap": true, - "noarg": true, - "sub": true, - "undef": true, - "unused": true, - "boss": true, - "eqnull": true, - "node": true + "evil": true, + "boss": true, + "expr": true, + "laxbreak": true, + "node": true, + "unused": "vars", + "noarg": true } diff --git a/CHANGELOG.md b/CHANGELOG.md index 0809b5d0..c7fe2a55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # 1.5.0 WIP - - support for import inline option to include css that you do not want less to parse e.g. `@import (inline) "file.css";` + - support for import inline option to include css that you do NOT want less to parse e.g. `@import (inline) "file.css";` - better support for modifyVars (refresh styles with new variables, using a file cache), is now more resiliant - support for import reference option to reference external css, but not output it. Any mixin calls or extend's will be output. - support for guards on selectors (currently only if you have a single selector) @@ -12,6 +12,7 @@ - Fix the saturate function to pass through when using the CSS syntax - Added svg-gradient function - Added no-js option to lessc (in browser, use javascriptEnabled: false) which disallows JavaScript in less files + - switched from the little supported and buggy cssmin (previously ycssmin) to clean-css # 1.4.2 diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..c9920c5e --- /dev/null +++ b/Makefile @@ -0,0 +1,114 @@ +# +# Run all tests +# +test: + node test/less-test.js + +# +# Run benchmark +# +benchmark: + node benchmark/less-benchmark.js + +# +# Build less.js +# +SRC = lib/less +HEADER = build/header.js +VERSION = `cat package.json | grep version \ + | grep -o '[0-9]\.[0-9]\.[0-9]\+'` +DIST = dist/less-${VERSION}.js +RHINO = dist/less-rhino-${VERSION}.js +DIST_MIN = dist/less-${VERSION}.min.js + +browser-prepare: DIST := test/browser/less.js + +alpha: DIST := dist/less-${VERSION}-alpha.js +alpha: DIST_MIN := dist/less-${VERSION}-alpha.min.js + +beta: DIST := dist/less-${VERSION}-beta.js +beta: DIST_MIN := dist/less-${VERSION}-beta.min.js + +less: + @@mkdir -p dist + @@touch ${DIST} + @@cat ${HEADER} | sed s/@VERSION/${VERSION}/ > ${DIST} + @@echo "(function (window, undefined) {" >> ${DIST} + @@cat build/require.js\ + build/browser-header.js\ + ${SRC}/parser.js\ + ${SRC}/functions.js\ + ${SRC}/colors.js\ + ${SRC}/tree/*.js\ + ${SRC}/tree.js\ + ${SRC}/env.js\ + ${SRC}/visitor.js\ + ${SRC}/import-visitor.js\ + ${SRC}/join-selector-visitor.js\ + ${SRC}/to-css-visitor.js\ + ${SRC}/extend-visitor.js\ + ${SRC}/browser.js\ + build/amd.js >> ${DIST} + @@echo "})(window);" >> ${DIST} + @@echo ${DIST} built. + +browser-prepare: less + node test/browser-test-prepare.js + +browser-test: browser-prepare + phantomjs test/browser/phantom-runner.js + +browser-test-server: browser-prepare + phantomjs test/browser/phantom-runner.js --no-tests + +jshint: + node_modules/.bin/jshint --config ./.jshintrc . + +test-sourcemaps: + node bin/lessc --source-map --source-map-inline test/less/import.less test/sourcemaps/import.css + node bin/lessc --source-map --source-map-inline test/less/sourcemaps/basic.less test/sourcemaps/basic.css + node node_modules/http-server/bin/http-server test/sourcemaps -p 8083 + +rhino: + @@mkdir -p dist + @@touch ${RHINO} + @@cat build/require-rhino.js\ + build/rhino-header.js\ + ${SRC}/parser.js\ + ${SRC}/env.js\ + ${SRC}/visitor.js\ + ${SRC}/import-visitor.js\ + ${SRC}/join-selector-visitor.js\ + ${SRC}/to-css-visitor.js\ + ${SRC}/extend-visitor.js\ + ${SRC}/functions.js\ + ${SRC}/colors.js\ + ${SRC}/tree/*.js\ + ${SRC}/tree.js\ + ${SRC}/rhino.js > ${RHINO} + @@echo ${RHINO} built. + +min: less + @@echo minifying... + @@uglifyjs ${DIST} > ${DIST_MIN} + @@echo ${DIST_MIN} built. + +alpha: min + +beta: min + +alpha-release: alpha + git add dist/*.js + git commit -m "Update alpha ${VERSION}" + +dist: min rhino + git add dist/* + git commit -a -m "(dist) build ${VERSION}" + git archive master --prefix=less/ -o less-${VERSION}.tar.gz + npm publish less-${VERSION}.tar.gz + +stable: + npm tag less@${VERSION} stable + + +.PHONY: test benchmark diff --git a/bin/lessc b/bin/lessc index 91114875..c830488b 100755 --- a/bin/lessc +++ b/bin/lessc @@ -11,7 +11,7 @@ var args = process.argv.slice(1); var options = { depends: false, compress: false, - yuicompress: false, + cleancss: false, max_line_len: -1, optimization: 1, silent: false, @@ -52,6 +52,8 @@ var checkBooleanArg = function(arg) { return Boolean(onOff[2]); }; +var warningMessages = ""; + args = args.filter(function (arg) { var match; @@ -95,7 +97,11 @@ args = args.filter(function (arg) { options.depends = true; break; case 'yui-compress': - options.yuicompress = true; + warningMessages += "yui-compress option has been removed. assuming clean-css."; + options.cleancss = true; + break; + case 'clean-css': + options.cleancss = true; break; case 'max-line-len': if (checkArgFunc(arg, match[2])) { @@ -132,6 +138,21 @@ args = args.filter(function (arg) { options.dumpLineNumbers = match[2]; } break; + case 'source-map': + if (!match[2]) { + options.sourceMap = true; + } else { + options.sourceMap = match[2]; + } + break; + case 'source-map-rootpath': + if (checkArgFunc(arg, match[2])) { + options.sourceMapRootpath = match[2]; + } + break; + case 'source-map-inline': + options.outputSourceFiles = true; + break; case 'rp': case 'rootpath': if (checkArgFunc(arg, match[2])) { @@ -169,7 +190,22 @@ if (input && input != '-') { var output = args[2]; var outputbase = args[2]; if (output) { + options.sourceMapOutputFilename = output; output = path.resolve(process.cwd(), output); + if (warningMessages) { + sys.puts(warningMessages); + } +} + +options.sourceMapBasepath = process.cwd(); + +if (options.sourceMap === true) { + if (!output) { + sys.puts("the sourcemap option only has an optional filename if the css filename is given"); + return; + } + options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; + options.sourceMap = path.basename(options.sourceMapFullFilename); } if (! input) { @@ -202,6 +238,12 @@ if (options.depends) { sys.print(outputbase + ": "); } +var writeSourceMap = function(output) { + var filename = options.sourceMapFullFilename || options.sourceMap; + ensureDirectory(filename); + fs.writeFileSync(filename, output, 'utf8'); +}; + var parseLessFile = function (e, data) { if (e) { sys.puts("lessc: " + e.message); @@ -230,7 +272,14 @@ var parseLessFile = function (e, data) { verbose: options.verbose, ieCompat: options.ieCompat, compress: options.compress, - yuicompress: options.yuicompress, + cleancss: options.cleancss, + sourceMap: Boolean(options.sourceMap), + sourceMapFilename: options.sourceMap, + sourceMapOutputFilename: options.sourceMapOutputFilename, + sourceMapBasepath: options.sourceMapBasepath, + sourceMapRootpath: options.sourceMapRootpath || "", + outputSourceFiles: options.outputSourceFiles, + writeSourceMap: writeSourceMap, maxLineLen: options.maxLineLen, strictMath: options.strictMath, strictUnits: options.strictUnits diff --git a/build/browser-header.js b/build/browser-header.js new file mode 100644 index 00000000..e0022b71 --- /dev/null +++ b/build/browser-header.js @@ -0,0 +1,4 @@ +if (typeof(window.less) === 'undefined') { window.less = {}; } +less = window.less; +tree = window.less.tree = {}; +less.mode = 'browser'; diff --git a/build/rhino-header.js b/build/rhino-header.js new file mode 100644 index 00000000..891f0943 --- /dev/null +++ b/build/rhino-header.js @@ -0,0 +1,4 @@ +if (typeof(window) === 'undefined') { less = {} } +else { less = window.less = {} } +tree = less.tree = {}; +less.mode = 'rhino'; \ No newline at end of file diff --git a/lib/less/browser.js b/lib/less/browser.js index bb800f5d..844a4af1 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -1,6 +1,7 @@ // // browser.js - client-side engine // +/*global less */ var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); @@ -43,7 +44,7 @@ less.watch = function () { less.env = 'development'; initRunningMode(); } - return this.watchMode = true + return this.watchMode = true; }; less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; @@ -105,12 +106,12 @@ less.modifyVars = function(record) { newVars += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); } - less.refresh(false, newVars) + less.refresh(false, newVars); }; less.refresh = function (reload, newVars) { var startTime, endTime; - startTime = endTime = new(Date); + startTime = endTime = new Date(); loadStyleSheets(function (e, root, _, sheet, env) { if (e) { @@ -122,9 +123,9 @@ less.refresh = function (reload, newVars) { log("parsed " + sheet.href + " successfully."); createCSS(root.toCSS(less), sheet, env.lastModified); } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); + log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); + (env.remaining === 0) && log("css generated in " + (new Date() - startTime) + 'ms'); + endTime = new Date(); }, reload, newVars); loadStyles(newVars); @@ -145,6 +146,7 @@ function loadStyles(newVars) { lessText += "\n" + newVars; } + /*jshint loopfunc:true */ // use closure to store current value of i var callback = (function(style) { return function (e, cssAST) { @@ -158,7 +160,7 @@ function loadStyles(newVars) { } else { style.innerHTML = css; } - } + }; })(style); new(less.Parser)(env).parse(lessText, callback); } @@ -337,20 +339,20 @@ function loadFile(originalHref, currentFileInfo, callback, env, newVars) { if (newVars) { lessText += "\n" + newVars; } - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }) + callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); } catch (e) { callback(e, null, href); } return; } - xhr(href, env.mime, function (data, lastModified) { + doXHR(href, env.mime, function (data, lastModified) { // per file cache fileCache[href] = data; // Use remote copy (re-parse) try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }) + callback(null, data, href, newFileInfo, { lastModified: lastModified }); } catch (e) { callback(e, null, href); } @@ -406,7 +408,7 @@ function createCSS(styles, sheet, lastModified) { // If there is no oldCss, just append; otherwise, only append if we need // to replace oldCss with an updated stylesheet - if (oldCss == null || keepOldCss === false) { + if (oldCss === null || keepOldCss === false) { var nextEl = sheet && sheet.nextSibling || null; (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl); } @@ -427,7 +429,7 @@ function createCSS(styles, sheet, lastModified) { } } -function xhr(url, type, callback, errback) { +function doXHR(url, type, callback, errback) { var xhr = getXMLHttpRequest(); var async = isFileProtocol ? less.fileAsync : less.async; @@ -467,10 +469,11 @@ function xhr(url, type, callback, errback) { function getXMLHttpRequest() { if (window.XMLHttpRequest) { - return new(XMLHttpRequest); + return new XMLHttpRequest(); } else { try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); + /*global ActiveXObject */ + return new ActiveXObject("MSXML2.XMLHTTP.3.0"); } catch (e) { log("browser doesn't support AJAX."); return null; @@ -483,13 +486,13 @@ function removeNode(node) { } function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } + if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str); } } function error(e, rootHref) { var id = 'less-error-message:' + extractId(rootHref || ""); var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; + var elem = document.createElement('div'), timer, content, errors = []; var filename = e.filename || rootHref; var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; @@ -500,8 +503,8 @@ function error(e, rootHref) { '' + '

    in ' + filenameNoPath + " "; var errorline = function (e, i, classname) { - if (e.extract[i] != undefined) { - error.push(template.replace(/\{line\}/, (parseInt(e.line) || 0) + (i - 1)) + if (e.extract[i] !== undefined) { + errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) .replace(/\{class\}/, classname) .replace(/\{content\}/, e.extract[i])); } @@ -512,7 +515,7 @@ function error(e, rootHref) { errorline(e, 1, 'line'); errorline(e, 2, ''); content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; + '
      ' + errors.join('') + '
    '; } else if (e.stack) { content += '
    ' + e.stack.split('\n').slice(1).join('
    '); } diff --git a/lib/less/env.js b/lib/less/env.js index 203830e0..dd4d5a5f 100644 --- a/lib/less/env.js +++ b/lib/less/env.js @@ -57,7 +57,9 @@ 'yuicompress', // whether to compress with the outside tool yui compressor 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) 'strictMath', // whether math has to be within parenthesis - 'strictUnits' // whether units need to evaluate correctly + 'strictUnits', // whether units need to evaluate correctly + 'cleancss', // whether to compress with clean-css + 'sourceMap' // whether to output a source map ]; tree.evalEnv = function(options, frames) { @@ -97,5 +99,6 @@ destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; } } - } -})(require('./tree')); \ No newline at end of file + }; + +})(require('./tree')); diff --git a/lib/less/extend-visitor.js b/lib/less/extend-visitor.js index cd9d9458..aead49f2 100644 --- a/lib/less/extend-visitor.js +++ b/lib/less/extend-visitor.js @@ -1,4 +1,6 @@ (function (tree) { + /*jshint loopfunc:true */ + tree.extendFinderVisitor = function() { this._visitor = new tree.visitor(this); this.contexts = []; @@ -246,7 +248,7 @@ haystackElement = hackstackSelector.elements[hackstackElementIndex]; // if we allow elements before our match we can add a potential match every time. otherwise only at the first element. - if (extend.allowBefore || (haystackSelectorIndex == 0 && hackstackElementIndex == 0)) { + if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) { potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); } @@ -257,7 +259,7 @@ // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out // what the resulting combinator will be targetCombinator = haystackElement.combinator.value; - if (targetCombinator == '' && hackstackElementIndex === 0) { + if (targetCombinator === '' && hackstackElementIndex === 0) { targetCombinator = ' '; } @@ -333,7 +335,8 @@ firstElement = new tree.Element( match.initialCombinator, replacementSelector.elements[0].value, - replacementSelector.elements[0].index + replacementSelector.elements[0].index, + replacementSelector.elements[0].currentFileInfo ); if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { @@ -388,4 +391,4 @@ } }; -})(require('./tree')); \ No newline at end of file +})(require('./tree')); diff --git a/lib/less/functions.js b/lib/less/functions.js index 93b13fdf..2806bfde 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -26,10 +26,10 @@ tree.functions = { function hue(h) { h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h); - if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; - else if (h * 2 < 1) return m2; - else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6; - else return m1; + if (h * 6 < 1) { return m1 + (m2 - m1) * h * 6; } + else if (h * 2 < 1) { return m2; } + else if (h * 3 < 2) { return m1 + (m2 - m1) * (2/3 - h) * 6; } + else { return m1; } } }, @@ -223,6 +223,7 @@ tree.functions = { str = quoted.value; for (var i = 0; i < args.length; i++) { + /*jshint loopfunc:true */ str = str.replace(/%[sda]/i, function(token) { var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; @@ -259,6 +260,7 @@ tree.functions = { }, _math: function (fn, unit, n) { if (n instanceof tree.Dimension) { + /*jshint eqnull:true */ return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit); } else if (typeof(n) === 'number') { return fn(n); @@ -463,10 +465,10 @@ tree.functions = { // use base 64 unless it's an ASCII or UTF-8 format var charset = mime.charsets.lookup(mimetype); useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; - if (useBase64) mimetype += ';base64'; + if (useBase64) { mimetype += ';base64'; } } else { - useBase64 = /;base64$/.test(mimetype) + useBase64 = /;base64$/.test(mimetype); } var buf = fs.readFileSync(filePath); @@ -528,13 +530,14 @@ tree.functions = { case "to top right": gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"'; break; - case "radial": + case "ellipse": + case "ellipse at center": gradientType = "radial"; gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"'; rectangleDimension = 'x="-50" y="-50" width="101" height="101"'; - break + break; default: - throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'radial'" }; + throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" }; } returner = '' + '' + @@ -554,8 +557,7 @@ tree.functions = { } positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; alpha = color.alpha; - color.alpha = 1; - returner += ''; + returner += ''; } returner += '' + ''; @@ -607,6 +609,7 @@ var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"} {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}], createMathFunction = function(name, unit) { return function(n) { + /*jshint eqnull:true */ if (unit != null) { n = n.unify(); } diff --git a/lib/less/index.js b/lib/less/index.js index 5c6a81ac..cb95ca88 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -24,7 +24,7 @@ var less = { catch (err) { callback(err); } }); } else { - ee = new(require('events').EventEmitter); + ee = new (require('events').EventEmitter)(); process.nextTick(function () { parser.parse(input, function (e, root) { @@ -42,10 +42,10 @@ var less = { var message = ""; var extract = ctx.extract; var error = []; - var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str }; + var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red') } + if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } if (!ctx.hasOwnProperty('index') || !extract) { return ctx.stack || ctx.message; @@ -132,7 +132,7 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { newFileInfo.filename = pathname; callback(null, data, pathname, newFileInfo); - }; + } var isUrl = isUrlRe.test( file ); if (isUrl || isUrlRe.test(currentFileInfo.currentDirectory)) { @@ -146,12 +146,7 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { } var urlStr = isUrl ? file : url.resolve(currentFileInfo.currentDirectory, file), - urlObj = url.parse(urlStr), - req = { - host: urlObj.hostname, - port: urlObj.port || 80, - path: urlObj.pathname + (urlObj.search||'') - }; + urlObj = url.parse(urlStr); request.get(urlStr, function (error, res, body) { if (res.statusCode === 404) { @@ -205,7 +200,7 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { }); } } -} +}; require('./env'); require('./functions'); @@ -215,5 +210,6 @@ require('./import-visitor.js'); require('./extend-visitor.js'); require('./join-selector-visitor.js'); require('./to-css-visitor.js'); +require('./source-map-output.js'); for (var k in less) { exports[k] = less[k]; } diff --git a/lib/less/join-selector-visitor.js b/lib/less/join-selector-visitor.js index 3a4c464b..7c9a1af8 100644 --- a/lib/less/join-selector-visitor.js +++ b/lib/less/join-selector-visitor.js @@ -21,6 +21,10 @@ this.contexts.push(paths); if (! rulesetNode.root) { + rulesetNode.selectors = rulesetNode.selectors.filter(function(selector) { return selector.getIsOutput(); }); + if (rulesetNode.selectors.length === 0) { + rulesetNode.rules.length = 0; + } rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); rulesetNode.paths = paths; } diff --git a/lib/less/lessc_helper.js b/lib/less/lessc_helper.js index 08d29992..1a2bba15 100644 --- a/lib/less/lessc_helper.js +++ b/lib/less/lessc_helper.js @@ -40,8 +40,7 @@ var lessc_helper = { sys.puts(" --verbose Be verbose."); sys.puts(" -v, --version Print version number and exit."); sys.puts(" -x, --compress Compress output by removing some whitespaces."); - sys.puts(" --yui-compress Compress output using ycssmin"); - sys.puts(" --max-line-len=LINELEN Max line length used by ycssmin"); + sys.puts(" --clean-css Compress output using clean-css"); sys.puts(" -O0, -O1, -O2 Set the parser's optimization level. The lower"); sys.puts(" the number, the less nodes it will create in the"); sys.puts(" tree. This could matter for debugging, or if you"); @@ -52,6 +51,9 @@ var lessc_helper = { sys.puts(" that will output the information within a fake"); sys.puts(" media query which is compatible with the SASS"); sys.puts(" format, and 'all' which will do both."); + sys.puts(" --source-map[=FILENAME] Outputs a v3 sourcemap to the filename (or output filename.map)"); + sys.puts(" --source-map-rootpath=X adds this path onto the sourcemap filename and less file paths"); + sys.puts(" --source-map-inline puts the less files into the map instead of referencing them"); sys.puts(" -rp, --rootpath=URL Set rootpath for url rewriting in relative imports and urls."); sys.puts(" Works with or without the relative-urls option."); sys.puts(" -ru, --relative-urls re-write relative urls to the base less file."); @@ -67,4 +69,4 @@ var lessc_helper = { }; // Exports helper functions -for (var h in lessc_helper) { exports[h] = lessc_helper[h] } +for (var h in lessc_helper) { exports[h] = lessc_helper[h]; } diff --git a/lib/less/parser.js b/lib/less/parser.js index d32b15b1..1dd9a26e 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -1,23 +1,10 @@ -var less, tree, charset; +var less, tree; -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, +// Node.js does not have a header file added which defines less +if (less === undefined) { + less = exports; tree = require('./tree'); less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; } // // less.js - parser @@ -63,8 +50,6 @@ less.Parser = function Parser(env) { current, // index of current chunk, in `input` parser; - var that = this; - // Top parser on an import tree must be sure there is one "env" // which will then be passed around by reference. if (!(env instanceof tree.parseEnv)) { @@ -117,7 +102,7 @@ less.Parser = function Parser(env) { fileParsedFunc(e, root, fullPath); }); } - }, env) + }, env); } } }; @@ -140,7 +125,7 @@ less.Parser = function Parser(env) { // Parse from a token, regexp or string, and move forward if match // function $(tok) { - var match, args, length, index, k; + var match, length; // // Non-terminal @@ -189,13 +174,13 @@ less.Parser = function Parser(env) { mem = i += length; while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } + if (! isWhitespace(input.charAt(i))) { break; } i++; } chunks[j] = chunks[j].slice(length + (i - mem)); current = i; - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } + if (chunks[j].length === 0 && j < chunks.length - 1) { j++; } return oldi !== i || oldj !== j; } @@ -235,13 +220,23 @@ less.Parser = function Parser(env) { } } - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } + function getLocation(index, inputStream) { + var n = index + 1, + line = null, + column = -1; - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; + while (--n >= 0 && inputStream.charAt(n) !== '\n') { + column++; + } + + if (typeof index === 'number') { + line = (inputStream.slice(0, index).match(/\n/g) || "").length; + } + + return { + line: line, + column: column + }; } function getDebugInfo(index, inputStream, env) { @@ -261,6 +256,7 @@ less.Parser = function Parser(env) { loc = getLocation(e.index, input), line = loc.line, col = loc.column, + callLine = e.call && getLocation(e.call, input).line, lines = input.split('\n'); this.type = e.type || 'Syntax'; @@ -268,8 +264,8 @@ less.Parser = function Parser(env) { this.filename = e.filename || env.currentFileInfo.filename; this.index = e.index; this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; + this.callLine = callLine + 1; + this.callExtract = lines[callLine]; this.stack = e.stack; this.column = col; this.extract = [ @@ -301,7 +297,7 @@ less.Parser = function Parser(env) { // call `callback` when done. // parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; + var root, line, lines, error = null; i = j = current = furthest = 0; input = str.replace(/\r\n/g, '\n'); @@ -309,6 +305,8 @@ less.Parser = function Parser(env) { // Remove potential UTF Byte Order Mark input = input.replace(/^\uFEFF/, ''); + parser.imports.contents[env.currentFileInfo.filename] = input; + // Split the input into chunks. chunks = (function (chunks) { var j = 0, @@ -353,16 +351,42 @@ less.Parser = function Parser(env) { } switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); + case '{': + if (!inParam) { + level++; + chunk.push(c); + break; + } + /* falls through */ + case '}': + if (!inParam) { + level--; + chunk.push(c); + chunks[++j] = chunk = []; + break; + } + /* falls through */ + case '(': + if (!inParam) { + inParam = true; + chunk.push(c); + break; + } + /* falls through */ + case ')': + if (inParam) { + inParam = false; + chunk.push(c); + break; + } + /* falls through */ + default: + chunk.push(c); } i++; } - if (level != 0) { + if (level !== 0) { error = new(LessError)({ index: i-1, type: 'Parse', @@ -371,7 +395,7 @@ less.Parser = function Parser(env) { }, env); } - return chunks.map(function (c) { return c.join('') }); + return chunks.map(function (c) { return c.join(''); }); })([[]]); if (error) { @@ -391,11 +415,10 @@ less.Parser = function Parser(env) { } root.toCSS = (function (evaluate) { - var line, lines, column; - return function (options, variables) { options = options || {}; - var importError, + var evaldRoot, + css, evalEnv = new tree.evalEnv(options); // @@ -427,7 +450,7 @@ less.Parser = function Parser(env) { } try { - var evaldRoot = evaluate.call(this, evalEnv); + evaldRoot = evaluate.call(this, evalEnv); new(tree.joinSelectorVisitor)() .run(evaldRoot); @@ -438,7 +461,21 @@ less.Parser = function Parser(env) { new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) .run(evaldRoot); - var css = evaldRoot.toCSS({ + if (options.sourceMap) { + evaldRoot = new tree.sourceMapOutput( + { + writeSourceMap: options.writeSourceMap, + rootNode: evaldRoot, + contentsMap: parser.imports.contents, + sourceMapFilename: options.sourceMapFilename, + outputFilename: options.sourceMapOutputFilename, + sourceMapBasepath: options.sourceMapBasepath, + sourceMapRootpath: options.sourceMapRootpath, + outputSourceFiles: options.outputSourceFiles + }); + } + + css = evaldRoot.toCSS({ compress: Boolean(options.compress), dumpLineNumbers: env.dumpLineNumbers, strictUnits: Boolean(options.strictUnits)}); @@ -446,8 +483,8 @@ less.Parser = function Parser(env) { throw new(LessError)(e, env); } - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css, options.maxLineLen); + if (options.cleancss && less.mode === 'node') { + return require('clean-css').process(css); } else if (options.compress) { return css.replace(/(^(\s)+)|((\s)+$)/g, ""); } else { @@ -466,10 +503,9 @@ less.Parser = function Parser(env) { // and the part which didn't), so we can color them differently. if (i < input.length - 1) { i = furthest; + var loc = getLocation(i, input); lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } + line = loc.line + 1; error = { type: "Parse", @@ -477,7 +513,7 @@ less.Parser = function Parser(env) { index: i, filename: env.currentFileInfo.filename, line: line, - column: column, + column: loc.column, extract: [ lines[line - 2], lines[line - 1], @@ -571,7 +607,7 @@ less.Parser = function Parser(env) { comment: function () { var comment; - if (input.charAt(i) !== '/') return; + if (input.charAt(i) !== '/') { return; } if (input.charAt(i + 1) === '/') { return new(tree.Comment)($(/^\/\/.*/), true, i, env.currentFileInfo); @@ -602,8 +638,8 @@ less.Parser = function Parser(env) { quoted: function () { var str, j = i, e, index = i; - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; + if (input.charAt(j) === '~') { j++, e = true; } // Escaped strings + if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } e && $('~'); @@ -643,13 +679,13 @@ less.Parser = function Parser(env) { call: function () { var name, nameLC, args, alpha_ret, index = i; - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; + if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) { return; } name = name[1]; nameLC = name.toLowerCase(); - if (nameLC === 'url') { return null } - else { i += name.length } + if (nameLC === 'url') { return null; } + else { i += name.length; } if (nameLC === 'alpha') { alpha_ret = $(this.alpha); @@ -673,7 +709,9 @@ less.Parser = function Parser(env) { while (arg = $(this.entities.assignment) || $(this.expression)) { args.push(arg); - if (! $(',')) { break } + if (! $(',')) { + break; + } } return args; }, @@ -707,12 +745,16 @@ less.Parser = function Parser(env) { url: function () { var value; - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; + if (input.charAt(i) !== 'u' || !$(/^url\(/)) { + return; + } + value = $(this.entities.quoted) || $(this.entities.variable) || $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; expect(')'); + /*jshint eqnull:true */ return new(tree.URL)((value.value != null || value instanceof tree.Variable) ? value : new(tree.Anonymous)(value), env.currentFileInfo); }, @@ -735,7 +777,7 @@ less.Parser = function Parser(env) { // A variable entity useing the protective {} e.g. @{var} variableCurly: function () { - var name, curly, index = i; + var curly, index = i; if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); @@ -765,7 +807,9 @@ less.Parser = function Parser(env) { dimension: function () { var value, c = input.charCodeAt(i); //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; + if ((c > 57 || c < 43) || c === 47 || c == 44) { + return; + } if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { return new(tree.Dimension)(value[1], value[2]); @@ -815,7 +859,7 @@ less.Parser = function Parser(env) { variable: function () { var name; - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } + if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1]; } }, // @@ -841,7 +885,7 @@ less.Parser = function Parser(env) { extendList.push(new(tree.Extend)(new(tree.Selector)(elements), option, index)); - } while($(",")) + } while($(",")); expect(/^\)/); @@ -875,14 +919,14 @@ less.Parser = function Parser(env) { // selector for now. // call: function () { - var elements = [], e, c, args, delim, arg, index = i, s = input.charAt(i), important = false; + var elements = [], e, c, args, index = i, s = input.charAt(i), important = false; - if (s !== '.' && s !== '#') { return } + if (s !== '.' && s !== '#') { return; } save(); // stop us absorbing part of an invalid selector while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); + elements.push(new(tree.Element)(c, e, i, env.currentFileInfo)); c = $('>'); } if ($('(')) { @@ -937,7 +981,7 @@ less.Parser = function Parser(env) { if (isCall) { // Variable if (arg.value.length == 1) { - var val = arg.value[0]; + val = arg.value[0]; } } else { val = arg; @@ -986,7 +1030,7 @@ less.Parser = function Parser(env) { isSemiColonSeperated = true; if (expressions.length > 1) { - value = new (tree.Value)(expressions); + value = new(tree.Value)(expressions); } argsSemiColon.push({ name:name, value:value }); @@ -1019,9 +1063,11 @@ less.Parser = function Parser(env) { // the `{...}` block. // definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; + var name, params = [], match, ruleset, cond, variadic = false; if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; + peek(/^[^{]*\}/)) { + return; + } save(); @@ -1083,7 +1129,7 @@ less.Parser = function Parser(env) { alpha: function () { var value; - if (! $(/^\(opacity=/i)) return; + if (! $(/^\(opacity=/i)) { return; } if (value = $(/^\d+/) || $(this.entities.variable)) { expect(')'); return new(tree.Alpha)(value); @@ -1103,7 +1149,7 @@ less.Parser = function Parser(env) { // and an element name, such as a tag a class, or `*`. // element: function () { - var e, t, c, v; + var e, c, v; c = $(this.combinator); @@ -1119,7 +1165,7 @@ less.Parser = function Parser(env) { } } - if (e) { return new(tree.Element)(c, e, i) } + if (e) { return new(tree.Element)(c, e, i, env.currentFileInfo); } }, // @@ -1136,7 +1182,7 @@ less.Parser = function Parser(env) { if (c === '>' || c === '+' || c === '~' || c === '|') { i++; - while (input.charAt(i).match(/\s/)) { i++ } + while (input.charAt(i).match(/\s/)) { i++; } return new(tree.Combinator)(c); } else if (input.charAt(i - 1).match(/\s/)) { return new(tree.Combinator)(" "); @@ -1160,7 +1206,7 @@ less.Parser = function Parser(env) { // Selectors are made out of one or more Elements, see above. // selector: function (isLess) { - var sel, e, elements = [], c, extend, extendList = [], when, condition; + var e, elements = [], c, extend, extendList = [], when, condition; while ((isLess && (extend = $(this.extend))) || (isLess && (when = $(/^when/))) || (e = $(this.element))) { if (when) { @@ -1174,19 +1220,21 @@ less.Parser = function Parser(env) { error("Extend can only be used at the end of selector"); } c = input.charAt(i); - elements.push(e) + elements.push(e); e = null; } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } + if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { + break; + } } if (elements.length > 0) { return new(tree.Selector)(elements, extendList, condition, i, env.currentFileInfo); } if (extendList.length) { error("Extend must be used to extend a selector, it cannot be used on its own"); } }, attribute: function () { - var attr = '', key, val, op; + var key, val, op; - if (! $('[')) return; + if (! $('[')) { return; } if (!(key = $(this.entities.variableCurly))) { key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); @@ -1220,8 +1268,9 @@ less.Parser = function Parser(env) { save(); - if (env.dumpLineNumbers) + if (env.dumpLineNumbers) { debugInfo = getDebugInfo(i, input, env); + } while (s = $(this.lessSelector)) { selectors.push(s); @@ -1235,8 +1284,9 @@ less.Parser = function Parser(env) { if (selectors.length > 0 && (rules = $(this.block))) { var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) + if (env.dumpLineNumbers) { ruleset.debugInfo = debugInfo; + } return ruleset; } else { // Backtrack @@ -1248,7 +1298,7 @@ less.Parser = function Parser(env) { var name, value, c = input.charAt(i), important, merge = false; save(); - if (c === '.' || c === '#' || c === '&') { return } + if (c === '.' || c === '#' || c === '&') { return; } if (name = $(this.variable) || $(this.ruleProperty)) { // prefer to try to parse first if its a variable or we are compressing @@ -1333,7 +1383,7 @@ less.Parser = function Parser(env) { break; } options[optionName] = value; - if (! $(',')) { break } + if (! $(',')) { break; } } } while (o); expect(')'); @@ -1364,7 +1414,7 @@ less.Parser = function Parser(env) { } else { return null; } - } else { return null } + } else { return null; } } } while (e); @@ -1379,10 +1429,10 @@ less.Parser = function Parser(env) { do { if (e = $(this.mediaFeature)) { features.push(e); - if (! $(',')) { break } + if (! $(',')) { break; } } else if (e = $(this.entities.variable)) { features.push(e); - if (! $(',')) { break } + if (! $(',')) { break; } } } while (e); @@ -1392,16 +1442,18 @@ less.Parser = function Parser(env) { media: function () { var features, rules, media, debugInfo; - if (env.dumpLineNumbers) + if (env.dumpLineNumbers) { debugInfo = getDebugInfo(i, input, env); + } if ($(/^@media/)) { features = $(this.mediaFeatures); if (rules = $(this.block)) { media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if(env.dumpLineNumbers) + if (env.dumpLineNumbers) { media.debugInfo = debugInfo; + } return media; } } @@ -1413,10 +1465,10 @@ less.Parser = function Parser(env) { // @charset "utf-8"; // directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, + var name, value, rules, nonVendorSpecificName, hasBlock, hasIdentifier, hasExpression; - if (input.charAt(i) !== '@') return; + if (input.charAt(i) !== '@') { return; } if (value = $(this['import']) || $(this.media)) { return value; @@ -1426,7 +1478,7 @@ less.Parser = function Parser(env) { name = $(/^@[a-z-]+/); - if (!name) return; + if (!name) { return; } nonVendorSpecificName = name; if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { @@ -1499,11 +1551,11 @@ less.Parser = function Parser(env) { // and before the `;`. // value: function () { - var e, expressions = [], important; + var e, expressions = []; while (e = $(this.expression)) { expressions.push(e); - if (! $(',')) { break } + if (! $(',')) { break; } } if (expressions.length > 0) { @@ -1528,7 +1580,7 @@ less.Parser = function Parser(env) { } }, multiplication: function () { - var m, a, op, operation, isSpaced, expression = []; + var m, a, op, operation, isSpaced; if (m = $(this.operand)) { isSpaced = isWhitespace(input.charAt(i - 1)); while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*')))) { @@ -1571,7 +1623,7 @@ less.Parser = function Parser(env) { condition: function () { var a, b, c, op, index = i, negate = false; - if ($(/^not/)) { negate = true } + if ($(/^not/)) { negate = true; } expect('('); if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { if (op = $(/^(?:>=|=<|[<=>])/)) { @@ -1595,7 +1647,7 @@ less.Parser = function Parser(env) { operand: function () { var negate, p = input.charAt(i + 1); - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } + if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-'); } var o = $(this.sub) || $(this.entities.dimension) || $(this.entities.color) || $(this.entities.variable) || $(this.entities.call); @@ -1616,7 +1668,7 @@ less.Parser = function Parser(env) { // @var * 2 // expression: function () { - var e, delim, entities = [], d; + var e, delim, entities = []; while (e = $(this.addition) || $(this.entity)) { entities.push(e); diff --git a/lib/less/rhino.js b/lib/less/rhino.js index fe917a2b..5feb2e74 100644 --- a/lib/less/rhino.js +++ b/lib/less/rhino.js @@ -1,3 +1,5 @@ +/*jshint rhino:true, unused: false */ +/*global name:true, less, loadStyleSheet */ var name; function loadStyleSheet(sheet, callback, reload, remaining) { @@ -61,7 +63,7 @@ function writeFile(filename, content) { print('No files present in the fileset; Check your pattern match in build.xml'); quit(1); } - path = name.split("/");path.pop();path=path.join("/") + path = name.split("/");path.pop();path=path.join("/"); var input = readFile(name); @@ -109,7 +111,7 @@ function error(e, filename) { var errorline = function (e, i, classname) { if (e.extract[i]) { content += - String(parseInt(e.line) + (i - 1)) + + String(parseInt(e.line, 10) + (i - 1)) + ":" + e.extract[i] + "\n"; } }; @@ -123,4 +125,4 @@ function error(e, filename) { errorline(e, 2); } print(content); -} \ No newline at end of file +} diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js new file mode 100644 index 00000000..8988eb23 --- /dev/null +++ b/lib/less/source-map-output.js @@ -0,0 +1,84 @@ +(function (tree) { + var sourceMap = require("source-map"); + + tree.sourceMapOutput = function (options) { + this._css = []; + this._rootNode = options.rootNode; + this._writeSourceMap = options.writeSourceMap; + this._contentsMap = options.contentsMap; + this._sourceMapFilename = options.sourceMapFilename; + this._outputFilename = options.outputFilename; + this._sourceMapBasepath = options.sourceMapBasepath; + this._sourceMapRootpath = options.sourceMapRootpath; + this._outputSourceFiles = options.outputSourceFiles; + + if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { + this._sourceMapRootpath += '/'; + } + + this._lineNumber = 0; + this._column = 0; + }; + + tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { + if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { + filename = filename.substring(this._sourceMapBasepath.length); + if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { + filename = filename.substring(1); + } + } + return this._sourceMapRootpath + filename.replace(/\\/g, '/'); + }; + + tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index) { + + if (!chunk) { + //TODO what is calling this with undefined? + return; + } + + var lines, + columns; + + if (fileInfo) { + var inputSource = this._contentsMap[fileInfo.filename].substring(0, index); + lines = inputSource.split("\n"); + columns = lines[lines.length-1]; + this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, + original: { line: lines.length, column: columns.length}, + source: this.normalizeFilename(fileInfo.filename)}); + } + lines = chunk.split("\n"); + columns = lines[lines.length-1]; + + if (lines.length === 1) { + this._column += columns.length; + } else { + this._lineNumber += lines.length - 1; + this._column = columns.length; + } + + this._css.push(chunk); + }; + + tree.sourceMapOutput.prototype.toCSS = function(env) { + this._sourceMapGenerator = new sourceMap.SourceMapGenerator({ file: this._outputFilename, sourceRoot: null }); + + if (this._outputSourceFiles) { + for(var filename in this._contentsMap) { + this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), this._contentsMap[filename]); + } + } + + this._rootNode.genCSS(env, this); + + this._writeSourceMap(JSON.stringify(this._sourceMapGenerator.toJSON())); + + if (this._sourceMapFilename) { + this._css.push("/*# sourceMappingURL=" + this._sourceMapRootpath + this._sourceMapFilename + " */"); + } + + return this._css.join(''); + }; + +})(require('./tree')); \ No newline at end of file diff --git a/lib/less/to-css-visitor.js b/lib/less/to-css-visitor.js index 0af0f157..a9988c6f 100644 --- a/lib/less/to-css-visitor.js +++ b/lib/less/to-css-visitor.js @@ -21,6 +21,10 @@ return []; }, + visitExtend: function (extendNode, visitArgs) { + return []; + }, + visitComment: function (commentNode, visitArgs) { if (commentNode.isSilent(this._env)) { return []; @@ -28,7 +32,20 @@ return commentNode; }, + visitMedia: function(mediaNode, visitArgs) { + mediaNode.accept(this._visitor); + visitArgs.visitDeeper = false; + + if (!mediaNode.rules.length) { + return []; + } + return mediaNode; + }, + visitDirective: function(directiveNode, visitArgs) { + if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { + return []; + } if (directiveNode.name === "@charset") { // Only output the debug info together with subsequent @charset definitions // a comment (or @media statement) before the actual @charset directive would @@ -67,6 +84,9 @@ rulesetNode.paths = rulesetNode.paths .filter(function(p) { var i; + if (p[0].elements[0].combinator.value === ' ') { + p[0].elements[0].combinator = new(tree.Combinator)(''); + } for(i = 0; i < p.length; i++) { if (p[i].getIsReferenced() && p[i].getIsOutput()) { return true; @@ -89,20 +109,90 @@ } // accept the visitor to remove rules and refactor itself // then we can decide now whether we want it or not - if (rulesetNode.rules.length > 0 && rulesetNode.paths.length > 0) { + if (rulesetNode.rules.length > 0) { rulesetNode.accept(this._visitor); } visitArgs.visitDeeper = false; + this._mergeRules(rulesetNode.rules); + this._removeDuplicateRules(rulesetNode.rules); + // now decide whether we keep the ruleset - if (rulesets.length > 0 && rulesetNode.rules.length > 0 && rulesetNode.paths.length > 0) { + if (rulesetNode.rules.length > 0 && rulesetNode.paths.length > 0) { + rulesets.splice(0, 0, rulesetNode); + } + } else { + rulesetNode.accept(this._visitor); + visitArgs.visitDeeper = false; + if (rulesetNode.firstRoot || rulesetNode.rules.length > 0) { rulesets.splice(0, 0, rulesetNode); } } - if (rulesets.length === 0) { - rulesets = rulesetNode; + if (rulesets.length === 1) { + return rulesets[0]; } return rulesets; + }, + + _removeDuplicateRules: function(rules) { + // remove duplicates + var ruleCache = {}, + ruleList, rule, i; + for(i = rules.length - 1; i >= 0 ; i--) { + rule = rules[i]; + if (rule instanceof tree.Rule) { + if (!ruleCache[rule.name]) { + ruleCache[rule.name] = rule; + } else { + ruleList = ruleCache[rule.name]; + if (ruleList instanceof tree.Rule) { + ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; + } + var ruleCSS = rule.toCSS(this._env); + if (ruleList.indexOf(ruleCSS) !== -1) { + rules.splice(i, 1); + } else { + ruleList.push(ruleCSS); + } + } + } + } + }, + + _mergeRules: function (rules) { + var groups = {}, + parts, + rule, + key; + + for (var i = 0; i < rules.length; i++) { + rule = rules[i]; + + if ((rule instanceof tree.Rule) && rule.merge) { + key = [rule.name, + rule.important ? "!" : ""].join(","); + + if (!groups[key]) { + parts = groups[key] = []; + } else { + rules.splice(i--, 1); + } + + parts.push(rule); + } + } + + Object.keys(groups).map(function (k) { + parts = groups[k]; + + if (parts.length > 1) { + rule = parts[0]; + + rule.value = new (tree.Value)(parts.map(function (p) { + return p.value; + })); + } + }); } }; diff --git a/lib/less/tree.js b/lib/less/tree.js index 9aee4613..d44d2339 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -1,6 +1,6 @@ (function (tree) { -tree.debugInfo = function(env, ctx) { +tree.debugInfo = function(env, ctx, lineSeperator) { var result=""; if (env.dumpLineNumbers && !env.compress) { switch(env.dumpLineNumbers) { @@ -11,7 +11,7 @@ tree.debugInfo = function(env, ctx) { result = tree.debugInfo.asMediaQuery(ctx); break; case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); + result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); break; } } @@ -24,22 +24,52 @@ tree.debugInfo.asComment = function(ctx) { tree.debugInfo.asMediaQuery = function(ctx) { return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function(a){if(a=='\\') a = '\/'; return '\\' + a}) + + ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function (a) { + if (a == '\\') { + a = '\/'; + } + return '\\' + a; + }) + '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; }; tree.find = function (obj, fun) { for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } + if (r = fun.call(obj, obj[i])) { return r; } } return null; }; + tree.jsify = function (obj) { if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; + return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; } else { return obj.toCSS(false); } }; +tree.toCSS = function (env) { + var strs = []; + this.genCSS(env, { + add: function(chunk, node) { + strs.push(chunk); + } + }); + return strs.join(''); +}; + +tree.outputRuleset = function (env, output, rules) { + output.add((env.compress ? '{' : ' {\n')); + env.tabLevel = (env.tabLevel || 0) + 1; + var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), + tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "); + for(var i = 0; i < rules.length; i++) { + output.add(tabRuleStr); + rules[i].genCSS(env, output); + output.add(env.compress ? '' : '\n'); + } + env.tabLevel--; + output.add(tabSetStr + "}"); +}; + })(require('./tree')); diff --git a/lib/less/tree/alpha.js b/lib/less/tree/alpha.js index cb8db113..e827fd08 100644 --- a/lib/less/tree/alpha.js +++ b/lib/less/tree/alpha.js @@ -9,13 +9,21 @@ tree.Alpha.prototype = { this.value = visitor.visit(this.value); }, eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env); } + if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } return this; }, - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - } + genCSS: function (env, output) { + output.add("alpha(opacity="); + + if (this.value.genCSS) { + this.value.genCSS(env, output); + } else { + output.add(this.value); + } + + output.add(")"); + }, + toCSS: tree.toCSS }; })(require('../tree')); diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index a009cf27..216a43db 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -5,10 +5,7 @@ tree.Anonymous = function (string) { }; tree.Anonymous.prototype = { type: "Anonymous", - toCSS: function () { - return this.value; - }, - eval: function () { return this }, + eval: function () { return this; }, compare: function (x) { if (!x.toCSS) { return -1; @@ -22,7 +19,11 @@ tree.Anonymous.prototype = { } return left < right ? -1 : 1; - } + }, + genCSS: function (env, output) { + output.add(this.value); + }, + toCSS: tree.toCSS }; })(require('../tree')); diff --git a/lib/less/tree/assignment.js b/lib/less/tree/assignment.js index b0e2c555..61d22fca 100644 --- a/lib/less/tree/assignment.js +++ b/lib/less/tree/assignment.js @@ -9,15 +9,21 @@ tree.Assignment.prototype = { accept: function (visitor) { this.value = visitor.visit(this.value); }, - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, eval: function (env) { if (this.value.eval) { return new(tree.Assignment)(this.key, this.value.eval(env)); } return this; - } + }, + genCSS: function (env, output) { + output.add(this.key + '='); + if (this.value.genCSS) { + this.value.genCSS(env, output); + } else { + output.add(this.value); + } + }, + toCSS: tree.toCSS }; -})(require('../tree')); \ No newline at end of file +})(require('../tree')); diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index 20a1fc0e..6d262b81 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -36,6 +36,7 @@ tree.Call.prototype = { try { func = new tree.functionCall(env, this.currentFileInfo); result = func[nameLC].apply(func, args); + /*jshint eqnull:true */ if (result != null) { return result; } @@ -46,15 +47,24 @@ tree.Call.prototype = { index: this.index, filename: this.currentFileInfo.filename }; } } - - // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env); }).join(', ') + ")"); + + return new tree.Call(this.name, args, this.index, this.currentFileInfo); }, - toCSS: function (env) { - return this.eval(env).toCSS(); - } + genCSS: function (env, output) { + output.add(this.name + "(", this.currentFileInfo, this.index); + + for(var i = 0; i < this.args.length; i++) { + this.args[i].genCSS(env, output); + if (i + 1 < this.args.length) { + output.add(", "); + } + } + + output.add(")"); + }, + + toCSS: tree.toCSS }; })(require('../tree')); diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index 95d5a101..c5ad64fc 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -24,40 +24,36 @@ tree.Color = function (rgb, a) { }; tree.Color.prototype = { type: "Color", - eval: function () { return this }, + eval: function () { return this; }, luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // + genCSS: function (env, output) { + output.add(this.toCSS(env)); + }, toCSS: function (env, doNotCompress) { var compress = env && env.compress && !doNotCompress; + + // If we have some transparency, the only way to represent it + // is via `rgba`. Otherwise, we use the hex representation, + // which has better compatibility with older browsers. + // Values are capped between `0` and `255`, rounded and zero-padded. if (this.alpha < 1.0) { return "rgba(" + this.rgb.map(function (c) { return Math.round(c); }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")"; } else { - var color = this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); + var color = this.toRGB(); if (compress) { - color = color.split(''); + var splitcolor = color.split(''); // Convert color to short format - if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) { - color = color[0] + color[2] + color[4]; - } else { - color = color.join(''); + if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { + color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5]; } } - return '#' + color; + return color; } }, @@ -80,6 +76,14 @@ tree.Color.prototype = { return new(tree.Color)(result, this.alpha + other.alpha); }, + toRGB: function () { + return '#' + this.rgb.map(function (i) { + i = Math.round(i); + i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); + return i.length === 1 ? '0' + i : i; + }).join(''); + }, + toHSL: function () { var r = this.rgb[0] / 255, g = this.rgb[1] / 255, diff --git a/lib/less/tree/comment.js b/lib/less/tree/comment.js index b9724633..c39606e0 100644 --- a/lib/less/tree/comment.js +++ b/lib/less/tree/comment.js @@ -7,13 +7,13 @@ tree.Comment = function (value, silent, index, currentFileInfo) { }; tree.Comment.prototype = { type: "Comment", - toCSS: function (env) { - var debugInfo = ""; + genCSS: function (env, output) { if (this.debugInfo) { - debugInfo = tree.debugInfo(env, this); + output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); } - return debugInfo + this.value; + output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n }, + toCSS: tree.toCSS, isSilent: function(env) { var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), isCompressed = env.compress && !this.value.match(/^\/\*!/); diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js index 8608b69d..47b1d008 100644 --- a/lib/less/tree/condition.js +++ b/lib/less/tree/condition.js @@ -19,7 +19,7 @@ tree.Condition.prototype = { var i = this.index, result; - var result = (function (op) { + result = (function (op) { switch (op) { case 'and': return a && b; diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index 9850e6ff..7d5044d9 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -20,7 +20,7 @@ tree.Dimension.prototype = { toColor: function () { return new(tree.Color)([this.value, this.value, this.value]); }, - toCSS: function (env) { + genCSS: function (env, output) { if ((env && env.strictUnits) && !this.unit.isSingular()) { throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); } @@ -36,7 +36,8 @@ tree.Dimension.prototype = { if (env && env.compress) { // Zero values doesn't need a unit if (value === 0 && !this.unit.isAngle()) { - return strValue; + output.add(strValue); + return; } // Float values doesn't need a leading zero @@ -45,13 +46,16 @@ tree.Dimension.prototype = { } } - return strValue + this.unit.toCSS(env); + output.add(strValue); + this.unit.genCSS(env, output); }, + toCSS: tree.toCSS, // In an operation between two Dimensions, // we default to the first Dimension's unit, // so `1px + 2` will yield `3px`. operate: function (env, op, other) { + /*jshint noempty:false */ var value = tree.operate(env, op, this.value, other.value), unit = this.unit.clone(); @@ -59,7 +63,7 @@ tree.Dimension.prototype = { if (unit.numerator.length === 0 && unit.denominator.length === 0) { unit.numerator = other.unit.numerator.slice(0); unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length == 0 && unit.denominator.length == 0) { + } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { // do nothing } else { other = other.convertTo(this.unit.usedUnits()); @@ -104,202 +108,206 @@ tree.Dimension.prototype = { }, unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); + return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); }, convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, conversion, targetUnit, derivedConversions = {}; + var value = this.value, unit = this.unit.clone(), + i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(function (atomicUnit, denominator) { + if (typeof conversions === 'string') { + for(i in tree.UnitConversions) { + if (tree.UnitConversions[i].hasOwnProperty(conversions)) { + derivedConversions = {}; + derivedConversions[i] = conversions; + } + } + conversions = derivedConversions; + } + applyUnit = function (atomicUnit, denominator) { + /*jshint loopfunc:true */ if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } + if (denominator) { + value = value / (group[atomicUnit] / group[targetUnit]); + } else { + value = value * (group[atomicUnit] / group[targetUnit]); + } - return targetUnit; + return targetUnit; } return atomicUnit; - }); + }; + + for (groupName in conversions) { + if (conversions.hasOwnProperty(groupName)) { + targetUnit = conversions[groupName]; + group = tree.UnitConversions[groupName]; + + unit.map(applyUnit); + } } - } - unit.cancel(); + unit.cancel(); - return new(tree.Dimension)(value, unit); + return new(tree.Dimension)(value, unit); } }; // http://www.w3.org/TR/css3-values/#absolute-lengths tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } + length: { + 'm': 1, + 'cm': 0.01, + 'mm': 0.001, + 'in': 0.0254, + 'pt': 0.0254 / 72, + 'pc': 0.0254 / 72 * 12 + }, + duration: { + 's': 1, + 'ms': 0.001 + }, + angle: { + 'rad': 1/(2*Math.PI), + 'deg': 1/360, + 'grad': 1/400, + 'turn': 1 + } }; tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; + this.numerator = numerator ? numerator.slice(0).sort() : []; + this.denominator = denominator ? denominator.slice(0).sort() : []; + this.backupUnit = backupUnit; }; tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, + type: "Unit", + clone: function () { + return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); + }, + genCSS: function (env, output) { + if (this.numerator.length >= 1) { + output.add(this.numerator[0]); + } else + if (this.denominator.length >= 1) { + output.add(this.denominator[0]); + } else + if ((!env || !env.strictUnits) && this.backupUnit) { + output.add(this.backupUnit); + } + }, + toCSS: tree.toCSS, - toCSS: function (env) { - if (this.numerator.length >= 1) { - return this.numerator[0]; - } - if (this.denominator.length >= 1) { - return this.denominator[0]; - } - if ((!env || !env.strictUnits) && this.backupUnit) { - return this.backupUnit; - } - return ""; - }, - - toString: function () { + toString: function () { var i, returnStr = this.numerator.join("*"); for (i = 0; i < this.denominator.length; i++) { returnStr += "/" + this.denominator[i]; } return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, + }, - is: function (unitString) { - return this.toString() === unitString; - }, + compare: function (other) { + return this.is(other.toString()) ? 0 : -1; + }, - isAngle: function () { - return tree.UnitConversions.angle.hasOwnProperty(this.toCSS()); - }, + is: function (unitString) { + return this.toString() === unitString; + }, - isEmpty: function () { - return this.numerator.length == 0 && this.denominator.length == 0; - }, + isAngle: function () { + return tree.UnitConversions.angle.hasOwnProperty(this.toCSS()); + }, - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length == 0; - }, + isEmpty: function () { + return this.numerator.length === 0 && this.denominator.length === 0; + }, - map: function(callback) { - var i; + isSingular: function() { + return this.numerator.length <= 1 && this.denominator.length === 0; + }, - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } + map: function(callback) { + var i; - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, groupName, result = {}; - - for (groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(function (atomicUnit) { - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; + for (i = 0; i < this.numerator.length; i++) { + this.numerator[i] = callback(this.numerator[i], false); } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; + for (i = 0; i < this.denominator.length; i++) { + this.denominator[i] = callback(this.denominator[i], true); } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } + }, - this.numerator = []; - this.denominator = []; + usedUnits: function() { + var group, result = {}, mapUnit; - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; + mapUnit = function (atomicUnit) { + /*jshint loopfunc:true */ + if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { + result[groupName] = atomicUnit; + } - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } + return atomicUnit; + }; + + for (var groupName in tree.UnitConversions) { + if (tree.UnitConversions.hasOwnProperty(groupName)) { + group = tree.UnitConversions[groupName]; + + this.map(mapUnit); + } } - } - } - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } + return result; + }, - this.numerator.sort(); - this.denominator.sort(); - } + cancel: function () { + var counter = {}, atomicUnit, i, backup; + + for (i = 0; i < this.numerator.length; i++) { + atomicUnit = this.numerator[i]; + if (!backup) { + backup = atomicUnit; + } + counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; + } + + for (i = 0; i < this.denominator.length; i++) { + atomicUnit = this.denominator[i]; + if (!backup) { + backup = atomicUnit; + } + counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; + } + + this.numerator = []; + this.denominator = []; + + for (atomicUnit in counter) { + if (counter.hasOwnProperty(atomicUnit)) { + var count = counter[atomicUnit]; + + if (count > 0) { + for (i = 0; i < count; i++) { + this.numerator.push(atomicUnit); + } + } else if (count < 0) { + for (i = 0; i < -count; i++) { + this.denominator.push(atomicUnit); + } + } + } + } + + if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { + this.backupUnit = backup; + } + + this.numerator.sort(); + this.denominator.sort(); + } }; })(require('../tree')); diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js index 1e9500bc..64082593 100644 --- a/lib/less/tree/directive.js +++ b/lib/less/tree/directive.js @@ -10,6 +10,7 @@ tree.Directive = function (name, value, index, currentFileInfo) { this.value = value; } this.currentFileInfo = currentFileInfo; + }; tree.Directive.prototype = { type: "Directive", @@ -17,24 +18,17 @@ tree.Directive.prototype = { this.rules = visitor.visit(this.rules); this.value = visitor.visit(this.value); }, - toCSS: function (env) { - - if (this.currentFileInfo.reference && !this.isReferenced) { - return ""; - } - + genCSS: function (env, output) { + output.add(this.name, this.currentFileInfo, this.index); if (this.rules) { - var css = ""; - for(var i = 0; i < this.rules.length; i++) { - //this.rules[i].root = true; - css += this.rules[i].toCSS(env).trim() + "\n"; - } - css = css.trim().replace(/\n/g, '\n '); - return this.name + (env.compress ? '{' : ' {\n ') + css + (env.compress ? '}': '\n}\n'); + tree.outputRuleset(env, output, this.rules); } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; + output.add(' '); + this.value.genCSS(env, output); + output.add(';'); } }, + toCSS: tree.toCSS, eval: function (env) { var evaldDirective = this; if (this.rules) { diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index 55d47903..1c25aeec 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -1,6 +1,6 @@ (function (tree) { -tree.Element = function (combinator, value, index) { +tree.Element = function (combinator, value, index, currentFileInfo) { this.combinator = combinator instanceof tree.Combinator ? combinator : new(tree.Combinator)(combinator); @@ -12,6 +12,7 @@ tree.Element = function (combinator, value, index) { this.value = ""; } this.index = index; + this.currentFileInfo = currentFileInfo; }; tree.Element.prototype = { type: "Element", @@ -22,11 +23,15 @@ tree.Element.prototype = { eval: function (env) { return new(tree.Element)(this.combinator, this.value.eval ? this.value.eval(env) : this.value, - this.index); + this.index, + this.currentFileInfo); + }, + genCSS: function (env, output) { + output.add(this.toCSS(env), this.currentFileInfo, this.index); }, toCSS: function (env) { var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { + if (value === '' && this.combinator.value.charAt(0) === '&') { return ''; } else { return this.combinator.toCSS(env || {}) + value; @@ -48,6 +53,9 @@ tree.Attribute.prototype = { return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); }, + genCSS: function (env, output) { + output.add(this.toCSS(env)); + }, toCSS: function (env) { var value = this.key.toCSS ? this.key.toCSS(env) : this.key; @@ -69,17 +77,28 @@ tree.Combinator = function (value) { }; tree.Combinator.prototype = { type: "Combinator", - toCSS: function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : '|' - }[this.value]; - } + _outputMap: { + '' : '', + ' ' : ' ', + ':' : ' :', + '+' : ' + ', + '~' : ' ~ ', + '>' : ' > ', + '|' : '|' + }, + _outputMapCompressed: { + '' : '', + ' ' : ' ', + ':' : ' :', + '+' : '+', + '~' : '~', + '>' : '>', + '|' : '|' + }, + genCSS: function (env, output) { + output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); + }, + toCSS: tree.toCSS }; })(require('../tree')); diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index 790c2b6f..23969297 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -33,11 +33,15 @@ tree.Expression.prototype = { } return returnValue; }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); + genCSS: function (env, output) { + for(var i = 0; i < this.value.length; i++) { + this.value[i].genCSS(env, output); + if (i + 1 < this.value.length) { + output.add(" "); + } + } }, + toCSS: tree.toCSS, throwAwayComments: function () { this.value = this.value.filter(function(v) { return !(v instanceof tree.Comment); diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index f81b09c0..e4cfd9e2 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -12,8 +12,6 @@ // the file has been fetched, and parsed. // tree.Import = function (path, features, options, index, currentFileInfo) { - var that = this; - this.options = options; this.index = index; this.path = path; @@ -48,15 +46,18 @@ tree.Import.prototype = { this.root = visitor.visit(this.root); } }, - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - + genCSS: function (env, output) { if (this.css) { - return "@import " + this.path.toCSS() + features + ';\n'; - } else { - return ""; + output.add("@import ", this.currentFileInfo, this.index); + this.path.genCSS(env, output); + if (this.features) { + output.add(" "); + this.features.genCSS(env, output); + } + output.add(';'); } }, + toCSS: tree.toCSS, getPath: function () { if (this.path instanceof tree.Quoted) { var path = this.path.value; diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index eaa9c0fe..428a56d6 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -24,6 +24,7 @@ tree.JavaScript.prototype = { } for (var k in env.frames[0].variables()) { + /*jshint loopfunc:true */ context[k.slice(1)] = { value: env.frames[0].variables()[k].value, toJS: function () { diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index 3184fce2..8cfbb09c 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -1,10 +1,13 @@ (function (tree) { -tree.Keyword = function (value) { this.value = value }; +tree.Keyword = function (value) { this.value = value; }; tree.Keyword.prototype = { type: "Keyword", eval: function () { return this; }, - toCSS: function () { return this.value; }, + genCSS: function (env, output) { + output.add(this.value); + }, + toCSS: tree.toCSS, compare: function (other) { if (other instanceof tree.Keyword) { return other.value === this.value ? 0 : 1; diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 57d68a2d..c5cdbd78 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -16,24 +16,12 @@ tree.Media.prototype = { this.features = visitor.visit(this.features); this.rules = visitor.visit(this.rules); }, - toCSS: function (env) { - var features = this.features.toCSS(env); - - var content = ""; - - for(var i = 0; i < this.rules.length; i++) { - content += this.rules[i].toCSS(env).trim() + "\n"; - } - - content = content.trim().replace(/\n/g, '\n '); - - if (content.match(/\S/)) { - return '@media ' + features + (env.compress ? '{' : ' {\n ') + content + - (env.compress ? '}': '\n}\n'); - } else { - return ""; - } + genCSS: function (env, output) { + output.add('@media ', this.currentFileInfo, this.index); + this.features.genCSS(env, output); + tree.outputRuleset(env, output, this.rules); }, + toCSS: tree.toCSS, eval: function (env) { if (!env.mediaBlocks) { env.mediaBlocks = []; @@ -69,13 +57,13 @@ tree.Media.prototype = { env.mediaPath.pop(); return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) + media.evalNested(env); }, variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); + var el = new(tree.Element)('', '&', this.index, this.currentFileInfo); return [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; }, markReferenced: function () { diff --git a/lib/less/tree/mixin.js b/lib/less/tree/mixin.js index b7e7f3e8..cd8bf173 100644 --- a/lib/less/tree/mixin.js +++ b/lib/less/tree/mixin.js @@ -88,7 +88,7 @@ tree.mixin.Call.prototype = { tree.mixin.Definition = function (name, params, rules, condition, variadic) { this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; + this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; this.params = params; this.condition = condition; this.variadic = variadic; @@ -96,8 +96,8 @@ tree.mixin.Definition = function (name, params, rules, condition, variadic) { this.rules = rules; this._lookups = {}; this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } + if (!p.name || (p.name && !p.value)) { return count + 1; } + else { return count; } }, 0); this.parent = tree.Ruleset.prototype; this.frames = []; @@ -109,13 +109,13 @@ tree.mixin.Definition.prototype = { this.rules = visitor.visit(this.rules); this.condition = visitor.visit(this.condition); }, - toCSS: function () { return ""; }, variable: function (name) { return this.parent.variable.call(this, name); }, variables: function () { return this.parent.variables.call(this); }, find: function () { return this.parent.find.apply(this, arguments); }, rulesets: function () { return this.parent.rulesets.apply(this); }, evalParams: function (env, mixinEnv, args, evaldArguments) { + /*jshint boss:true */ var frame = new(tree.Ruleset)(null, []), varargs, arg, params = this.params.slice(0), @@ -151,7 +151,7 @@ tree.mixin.Definition.prototype = { } argIndex = 0; for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; + if (evaldArguments[i]) { continue; } arg = args && args[argIndex]; @@ -193,7 +193,7 @@ tree.mixin.Definition.prototype = { var _arguments = [], mixinFrames = this.frames.concat(env.frames), frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - context, rules, start, ruleset; + rules, ruleset; frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); @@ -216,12 +216,12 @@ tree.mixin.Definition.prototype = { return true; }, matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; + var argsLength = (args && args.length) || 0, len; if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } + if (argsLength < this.required) { return false; } + if (argsLength > this.params.length) { return false; } + if ((this.required > 0) && (argsLength > this.params.length)) { return false; } } len = Math.min(argsLength, this.arity); diff --git a/lib/less/tree/negative.js b/lib/less/tree/negative.js index 5971d70b..12dbcc63 100644 --- a/lib/less/tree/negative.js +++ b/lib/less/tree/negative.js @@ -8,9 +8,11 @@ tree.Negative.prototype = { accept: function (visitor) { this.value = visitor.visit(this.value); }, - toCSS: function (env) { - return '-' + this.value.toCSS(env); + genCSS: function (env, output) { + output.add('-'); + this.value.genCSS(env, output); }, + toCSS: tree.toCSS, eval: function (env) { if (env.isMathOn()) { return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); diff --git a/lib/less/tree/operation.js b/lib/less/tree/operation.js index d9d4af3e..ec806e29 100644 --- a/lib/less/tree/operation.js +++ b/lib/less/tree/operation.js @@ -34,10 +34,18 @@ tree.Operation.prototype = { return new(tree.Operation)(this.op, [a, b], this.isSpaced); } }, - toCSS: function (env) { - var separator = this.isSpaced ? " " : ""; - return this.operands[0].toCSS() + separator + this.op + separator + this.operands[1].toCSS(); - } + genCSS: function (env, output) { + this.operands[0].genCSS(env, output); + if (this.isSpaced) { + output.add(" "); + } + output.add(this.op); + if (this.isSpaced) { + output.add(" "); + } + this.operands[1].genCSS(env, output); + }, + toCSS: tree.toCSS }; tree.operate = function (env, op, a, b) { diff --git a/lib/less/tree/paren.js b/lib/less/tree/paren.js index df6fa95c..e27b8d66 100644 --- a/lib/less/tree/paren.js +++ b/lib/less/tree/paren.js @@ -9,9 +9,12 @@ tree.Paren.prototype = { accept: function (visitor) { this.value = visitor.visit(this.value); }, - toCSS: function (env) { - return '(' + this.value.toCSS(env).trim() + ')'; + genCSS: function (env, output) { + output.add('('); + this.value.genCSS(env, output); + output.add(')'); }, + toCSS: tree.toCSS, eval: function (env) { return new(tree.Paren)(this.value.eval(env)); } diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index 9ca4b010..29fc22b0 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -9,13 +9,16 @@ tree.Quoted = function (str, content, escaped, index, currentFileInfo) { }; tree.Quoted.prototype = { type: "Quoted", - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; + genCSS: function (env, output) { + if (!this.escaped) { + output.add(this.quote, this.currentFileInfo, this.index); + } + output.add(this.value); + if (!this.escaped) { + output.add(this.quote); } }, + toCSS: tree.toCSS, eval: function (env) { var that = this; var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js index ea419f9a..20562094 100644 --- a/lib/less/tree/rule.js +++ b/lib/less/tree/rule.js @@ -16,21 +16,19 @@ tree.Rule.prototype = { accept: function (visitor) { this.value = visitor.visit(this.value); }, - toCSS: function (env) { - if (this.variable) { return ""; } - else { - try { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } + genCSS: function (env, output) { + output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); + try { + this.value.genCSS(env, output); } + catch(e) { + e.index = this.index; + e.filename = this.currentFileInfo.filename; + throw e; + } + output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); }, + toCSS: tree.toCSS, eval: function (env) { var strictMathBypass = false; if (this.name === "font" && !env.strictMath) { diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 9f7f0574..25a92e38 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -9,13 +9,21 @@ tree.Ruleset = function (selectors, rules, strictImports) { tree.Ruleset.prototype = { type: "Ruleset", accept: function (visitor) { - this.selectors = visitor.visit(this.selectors); + if (this.paths) { + for(var i = 0; i < this.paths.length; i++) { + this.paths[i] = visitor.visit(this.paths[i]); + } + } else { + this.selectors = visitor.visit(this.selectors); + } this.rules = visitor.visit(this.rules); }, eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); + var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env); }); var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); var rules; + var rule; + var i; ruleset.originalRuleset = this; ruleset.root = this.root; @@ -42,7 +50,7 @@ tree.Ruleset.prototype = { // Store the frames around mixin definitions, // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { + for (i = 0; i < ruleset.rules.length; i++) { if (ruleset.rules[i] instanceof tree.mixin.Definition) { ruleset.rules[i].frames = env.frames.slice(0); } @@ -51,8 +59,9 @@ tree.Ruleset.prototype = { var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { + for (i = 0; i < ruleset.rules.length; i++) { if (ruleset.rules[i] instanceof tree.mixin.Call) { + /*jshint loopfunc:true */ rules = ruleset.rules[i].eval(env).filter(function(r) { if ((r instanceof tree.Rule) && r.variable) { // do not pollute the scope if the variable is @@ -69,7 +78,7 @@ tree.Ruleset.prototype = { } // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { + for (i = 0; i < ruleset.rules.length; i++) { rule = ruleset.rules[i]; if (! (rule instanceof tree.mixin.Definition)) { @@ -82,7 +91,7 @@ tree.Ruleset.prototype = { env.selectors.shift(); if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { + for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { env.mediaBlocks[i].bubbleSelectors(selectors); } } @@ -132,7 +141,7 @@ tree.Ruleset.prototype = { this._lookups = {}; }, variables: function () { - if (this._variables) { return this._variables } + if (this._variables) { return this._variables; } else { return this._variables = this.rules.reduce(function (hash, r) { if (r instanceof tree.Rule && r.variable === true) { @@ -152,10 +161,10 @@ tree.Ruleset.prototype = { }, find: function (selector, self) { self = self || this; - var rules = [], rule, match, + var rules = [], match, key = selector.toCSS(); - if (key in this._lookups) { return this._lookups[key] } + if (key in this._lookups) { return this._lookups[key]; } this.rulesets().forEach(function (rule) { if (rule !== self) { @@ -174,91 +183,99 @@ tree.Ruleset.prototype = { }); return this._lookups[key] = rules; }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - selector, // The fully rendered selector + genCSS: function (env, output) { + var i, j, + ruleNodes = [], + rulesetNodes = [], debugInfo, // Line number debugging - rule; + rule, + firstRuleset = true, + path; - this.mergeRules(); + env.tabLevel = (env.tabLevel || 0); - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive) { - rulesets.push(rule.toCSS(env)); - } else if (rule instanceof tree.Comment) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } else { - if (rule.toCSS) { - rules.push(rule.toCSS(env)); - } else if (rule.value) { - rules.push(rule.value.toString()); - } - } - } - - // Remove last semicolon - if (env.compress && rules.length) { - rule = rules[rules.length - 1]; - if (rule.charAt(rule.length - 1) === ';') { - rules[rules.length - 1] = rule.substring(0, rule.length - 1); - } + if (!this.root) { + env.tabLevel++; } - rulesets = rulesets.join(''); + var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), + tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "); + + for (i = 0; i < this.rules.length; i++) { + rule = this.rules[i]; + if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { + rulesetNodes.push(rule); + } else { + ruleNodes.push(rule); + } + } // If this is the root node, we don't render // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = this.paths - .map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); + if (!this.root) { + debugInfo = tree.debugInfo(env, this, tabSetStr); - if (selector) { - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (rules[i].slice(0, 2) === "/*" || _rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; + if (debugInfo) { + output.add(debugInfo); + output.add(tabSetStr); + } - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); + for(i = 0; i < this.paths.length; i++) { + path = this.paths[i]; + env.firstSelector = true; + for(j = 0; j < path.length; j++) { + output.add(path[j].genCSS(env, output)); + env.firstSelector = false; + } + if (i + 1 < this.paths.length) { + output.add(env.compress ? ',' : (',\n' + tabSetStr)); } } + + output.add((env.compress ? '{' : ' {\n') + tabRuleStr); } - css.push(rulesets); - return css.join('') + (env.compress ? '\n' : ''); + // Compile rules and rulesets + for (i = 0; i < ruleNodes.length; i++) { + rule = ruleNodes[i]; + + if (i + 1 === ruleNodes.length) { + env.lastRule = true; + } + + if (rule.toCSS) { + output.add(rule.genCSS(env, output)); + } else if (rule.value) { + output.add(rule.value.toString()); + } + + if (!env.lastRule) { + output.add(env.compress ? '' : ('\n' + tabRuleStr)); + } else { + env.lastRule = false; + } + } + + if (!this.root) { + output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); + env.tabLevel--; + } + + for (i = 0; i < rulesetNodes.length; i++) { + if (ruleNodes.length && firstRuleset) { + output.add("\n" + (this.root ? tabRuleStr : tabSetStr)); + } + if (!firstRuleset) { + output.add('\n' + (this.root ? tabRuleStr : tabSetStr)); + } + firstRuleset = false; + output.add(rulesetNodes[i].genCSS(env, output)); + } + + output.add(!env.compress && this.firstRoot ? '\n' : ''); }, - toCSSRoot: function (env) { - }, + toCSS: tree.toCSS, markReferenced: function () { for (var s = 0; s < this.selectors.length; s++) { @@ -289,7 +306,7 @@ tree.Ruleset.prototype = { if (!hasParentSelector) { if (context.length > 0) { - for(i = 0; i < context.length; i++) { + for (i = 0; i < context.length; i++) { paths.push(context[i].concat(selector)); } } @@ -333,22 +350,22 @@ tree.Ruleset.prototype = { } // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { + for (j = 0; j < newSelectors.length; j++) { sel = newSelectors[j]; // if we don't have any parent paths, the & might be in a mixin so that it can be used // whether there are parents or not - if (context.length == 0) { + if (context.length === 0) { // the combinator used on el should now be applied to the next element instead so that // it is not lost if (sel.length > 0) { sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); + sel[0].elements.push(new(tree.Element)(el.combinator, '', 0, el.index, el.currentFileInfo)); } selectorsMultiplied.push(sel); } else { // and the parent selectors - for(k = 0; k < context.length; k++) { + for (k = 0; k < context.length; k++) { parentSel = context[k]; // We need to put the current selectors // then join the last selector's elements on to the parents selectors @@ -380,7 +397,7 @@ tree.Ruleset.prototype = { newJoinedSelectorEmpty = false; // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); + newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); } @@ -410,7 +427,7 @@ tree.Ruleset.prototype = { this.mergeElementsOnToSelectors(currentElements, newSelectors); } - for(i = 0; i < newSelectors.length; i++) { + for (i = 0; i < newSelectors.length; i++) { if (newSelectors[i].length > 0) { paths.push(newSelectors[i]); } @@ -418,14 +435,14 @@ tree.Ruleset.prototype = { }, mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel, extendList; + var i, sel; - if (selectors.length == 0) { + if (selectors.length === 0) { selectors.push([ new(tree.Selector)(elements) ]); return; } - for(i = 0; i < selectors.length; i++) { + for (i = 0; i < selectors.length; i++) { sel = selectors[i]; // if the previous thing in sel is a parent this needs to join on to it @@ -436,42 +453,6 @@ tree.Ruleset.prototype = { sel.push(new(tree.Selector)(elements)); } } - }, - - mergeRules: function () { - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - parts = groups[key] = []; - } else { - this.rules.splice(i--, 1); - } - - parts.push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); } }; })(require('../tree')); diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index 0fc8582d..ac38b993 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -18,6 +18,7 @@ tree.Selector.prototype = { this.condition = visitor.visit(this.condition); }, createDerived: function(elements, extendList, evaldCondition) { + /*jshint eqnull:true */ evaldCondition = evaldCondition != null ? evaldCondition : this.evaldCondition; var newSelector = new(tree.Selector)(elements, extendList || this.extendList, this.condition, this.index, this.currentFileInfo, this.isReferenced); newSelector.evaldCondition = evaldCondition; @@ -53,25 +54,20 @@ tree.Selector.prototype = { return extend.eval(env); }), evaldCondition); }, - toCSS: function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; + genCSS: function (env, output) { + var i, element; + if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { + output.add(' ', this.currentFileInfo, this.index); } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); + if (!this._css) { + //TODO caching? speed comparison? + for(i = 0; i < this.elements.length; i++) { + element = this.elements[i]; + element.genCSS(env, output); } - }).join(''); - - return this._css; + } }, + toCSS: tree.toCSS, markReferenced: function () { this.isReferenced = true; }, diff --git a/lib/less/tree/unicode-descriptor.js b/lib/less/tree/unicode-descriptor.js index 3f725127..7bf98ea5 100644 --- a/lib/less/tree/unicode-descriptor.js +++ b/lib/less/tree/unicode-descriptor.js @@ -5,10 +5,11 @@ tree.UnicodeDescriptor = function (value) { }; tree.UnicodeDescriptor.prototype = { type: "UnicodeDescriptor", - toCSS: function (env) { - return this.value; + genCSS: function (env, output) { + output.add(this.value); }, - eval: function () { return this } + toCSS: tree.toCSS, + eval: function () { return this; } }; })(require('../tree')); diff --git a/lib/less/tree/url.js b/lib/less/tree/url.js index 82ef8d7a..7ba562fa 100644 --- a/lib/less/tree/url.js +++ b/lib/less/tree/url.js @@ -9,9 +9,12 @@ tree.URL.prototype = { accept: function (visitor) { this.value = visitor.visit(this.value); }, - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; + genCSS: function (env, output) { + output.add("url("); + this.value.genCSS(env, output); + output.add(")"); }, + toCSS: tree.toCSS, eval: function (ctx) { var val = this.value.eval(ctx), rootpath; diff --git a/lib/less/tree/value.js b/lib/less/tree/value.js index 5aae88eb..7f110f65 100644 --- a/lib/less/tree/value.js +++ b/lib/less/tree/value.js @@ -17,11 +17,16 @@ tree.Value.prototype = { })); } }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } + genCSS: function (env, output) { + var i; + for(i = 0; i < this.value.length; i++) { + this.value[i].genCSS(env, output); + if (i+1 < this.value.length) { + output.add(env.compress ? ',' : ', '); + } + } + }, + toCSS: tree.toCSS }; })(require('../tree')); diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index ff57494c..8f146932 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -1,12 +1,16 @@ (function (tree) { -tree.Variable = function (name, index, currentFileInfo) { this.name = name, this.index = index, this.currentFileInfo = currentFileInfo }; +tree.Variable = function (name, index, currentFileInfo) { + this.name = name; + this.index = index; + this.currentFileInfo = currentFileInfo; +}; tree.Variable.prototype = { type: "Variable", eval: function (env) { var variable, v, name = this.name; - if (name.indexOf('@@') == 0) { + if (name.indexOf('@@') === 0) { name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; } diff --git a/lib/less/visitor.js b/lib/less/visitor.js index 5a4cd402..52f734f1 100644 --- a/lib/less/visitor.js +++ b/lib/less/visitor.js @@ -35,12 +35,11 @@ return node; }, visitArray: function(nodes) { - var i, newNodes = [], visitor = this; + var i, newNodes = []; for(i = 0; i < nodes.length; i++) { var evald = this.visit(nodes[i]); if (evald instanceof Array) { evald = this.flatten(evald); - //evald.forEach(this.doAccept, this); newNodes = newNodes.concat(evald); } else { newNodes.push(evald); diff --git a/package.json b/package.json index 550ec8d7..0f3c4c24 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,7 @@ "version": "1.4.2", "description": "Leaner CSS", "homepage": "http://lesscss.org", - "author": { - "name": "Alexis Sellier", - "email": "self@cloudhead.net" - }, + "author": "Alexis Sellier ", "contributors": [ "The Core Less Team" ], @@ -17,12 +14,6 @@ "type": "git", "url": "https://github.com/less/less.js.git" }, - "licenses": [ - { - "type": "Apache v2", - "url": "https://github.com/less/less.js/blob/master/LICENSE" - } - ], "bin": { "lessc": "./bin/lessc" }, @@ -37,23 +28,20 @@ "node": ">=0.4.2" }, "scripts": { - "test": "grunt test" + "pretest": "make jshint", + "test": "make test" }, "optionalDependencies": { "mime": "1.2.x", "request": ">=2.12.0", "mkdirp": "~0.3.4", - "ycssmin": ">=1.0.1" + "clean-css": "1.0.x", + "source-map": "0.1.x" }, "devDependencies": { - "diff": "~1.0.5", - "grunt": "~0.4.1", - "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-concat": "~0.3.0", - "grunt-contrib-jshint": "~0.6.0", - "grunt-contrib-uglify": "~0.2.2", - "grunt-shell": "~0.3.1", - "matchdep": "~0.1.2" + "diff": "~1.0", + "jshint": "~2.1.4", + "http-server": "~0.5.5" }, "keywords": [ "compile less", @@ -79,5 +67,11 @@ "stylesheet", "variables in css", "css less" + ], + "licenses": [ + { + "type": "Apache v2", + "url": "https://github.com/less/less.js/blob/master/LICENSE" + } ] } diff --git a/test/browser-test-prepare.js b/test/browser-test-prepare.js index 1a1556f6..95a99703 100644 --- a/test/browser-test-prepare.js +++ b/test/browser-test-prepare.js @@ -1,13 +1,12 @@ var path = require('path'), - fs = require('fs'), - sys = require('util'); + fs = require('fs'); var readDirFilesSync = function(dir, regex, callback) { fs.readdirSync(dir).forEach(function (file) { if (! regex.test(file)) { return; } callback(file); }); -} +}; var createTestRunnerPage = function(dir, exclude, testSuiteName, dir2) { var output = '\n'; @@ -33,7 +32,7 @@ var removeFiles = function(dir, regex) { console.log("Failed to delete " + file); }); }); -} +}; removeFiles("test/browser", /test-runner-[a-zA-Z-]*\.htm$/); createTestRunnerPage("", /javascript|urls/, "main"); diff --git a/test/browser/css/rootpath-relative/urls.css b/test/browser/css/rootpath-relative/urls.css index 817d5818..20b08339 100644 --- a/test/browser/css/rootpath-relative/urls.css +++ b/test/browser/css/rootpath-relative/urls.css @@ -1,5 +1,4 @@ @import "https://www.github.com/cloudhead/imports/modify-this.css"; - @import "https://www.github.com/cloudhead/imports/modify-again.css"; .modify { my-url: url("https://www.github.com/cloudhead/imports/a.png"); diff --git a/test/browser/css/urls.css b/test/browser/css/urls.css index ddb46b15..7001990e 100644 --- a/test/browser/css/urls.css +++ b/test/browser/css/urls.css @@ -1,5 +1,4 @@ @import "http://localhost:8081/browser/less/modify-this.css"; - @import "http://localhost:8081/browser/less/modify-again.css"; .modify { my-url: url("http://localhost:8081/browser/less/a.png"); diff --git a/test/css/comments.css b/test/css/comments.css index 2306c6a8..b85f5b4f 100644 --- a/test/css/comments.css +++ b/test/css/comments.css @@ -32,7 +32,6 @@ color: red; /* A C-style comment */ /* A C-style comment */ - background-color: orange; font-size: 12px; /* lost comment */ @@ -59,7 +58,12 @@ #last { color: #0000ff; } -/* *//* { *//* *//* *//* */#div { +/* */ +/* { */ +/* */ +/* */ +/* */ +#div { color: #A33; } /* } */ diff --git a/test/css/compression/compression.css b/test/css/compression/compression.css index 3fdd6a21..e3582bf3 100644 --- a/test/css/compression/compression.css +++ b/test/css/compression/compression.css @@ -1,5 +1,4 @@ #colours{color1:#fea;color2:#fea;color3:rgba(255,238,170,0.1);string:"#ffeeaa";/*! but not this type Note preserved whitespace - */ -} + */} dimensions{val:.1px;val:0;val:4cm;val:.2;val:5;angles-must-have-unit:0deg;width:auto\9} \ No newline at end of file diff --git a/test/css/css-guards.css b/test/css/css-guards.css index e190f0b0..dbb27dae 100644 --- a/test/css/css-guards.css +++ b/test/css/css-guards.css @@ -10,3 +10,9 @@ .multiple-conditions-1 { color: red; } +.inheritance .test { + color: black; +} +.inheritance:hover { + color: pink; +} diff --git a/test/css/extend-selector.css b/test/css/extend-selector.css index dc03d1cd..4a525746 100644 --- a/test/css/extend-selector.css +++ b/test/css/extend-selector.css @@ -52,9 +52,9 @@ div.ext7, .c.replace + .replace .replace, .replace.replace .c, .c.replace + .replace .c, -.rep_ace .rep_ace .rep_ace, +.rep_ace.rep_ace .rep_ace, .c.rep_ace + .rep_ace .rep_ace, -.rep_ace .rep_ace .c, +.rep_ace.rep_ace .c, .c.rep_ace + .rep_ace .c { prop: copy-paste-replace; } diff --git a/test/css/import-inline.css b/test/css/import-inline.css index 1c17ab2a..f198d3c1 100644 --- a/test/css/import-inline.css +++ b/test/css/import-inline.css @@ -1,3 +1,5 @@ -this isn't very valid CSS. @media (min-width: 600px) { +this isn't very valid CSS. +@media (min-width: 600px) { #css { color: yellow; } + } diff --git a/test/css/import.css b/test/css/import.css index 616657ac..a3749181 100644 --- a/test/css/import.css +++ b/test/css/import.css @@ -1,7 +1,5 @@ @import url(http://fonts.googleapis.com/css?family=Open+Sans); - @import url(/absolute/something.css) screen and (color) and (max-width: 600px); - @import url("//ha.com/file.css") (min-width: 100px); #import-test { height: 10px; diff --git a/test/css/media.css b/test/css/media.css index aeba6e77..d90c1e8d 100644 --- a/test/css/media.css +++ b/test/css/media.css @@ -118,7 +118,8 @@ margin: 1cm; } @page :first { - size: 8.5in 11in;@top-left { + size: 8.5in 11in; + @top-left { margin: 1cm; } @top-left-corner { diff --git a/test/css/static-urls/urls.css b/test/css/static-urls/urls.css index b5a690e9..565ccb44 100644 --- a/test/css/static-urls/urls.css +++ b/test/css/static-urls/urls.css @@ -1,5 +1,4 @@ @import "folder (1)/../css/background.css"; - @import "folder (1)/import-test-d.css"; @font-face { src: url("/fonts/garamond-pro.ttf"); diff --git a/test/css/urls.css b/test/css/urls.css index 21b33bca..ef812605 100644 --- a/test/css/urls.css +++ b/test/css/urls.css @@ -1,7 +1,5 @@ @import "import/../css/background.css"; - @import "import/import-test-d.css"; - @import "file.css"; @font-face { src: url("/fonts/garamond-pro.ttf"); diff --git a/test/less-test.js b/test/less-test.js index ae4755db..bc00bc6c 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -9,6 +9,8 @@ var globals = Object.keys(global); var oneTestOnly = process.argv[2]; +var isVerbose = process.env.npm_config_loglevel === 'verbose'; + var totalTests = 0, failedTests = 0, passedTests = 0; @@ -20,7 +22,7 @@ less.tree.functions.increment = function (a) { return new(less.tree.Dimension)(a.value + 1); }; less.tree.functions._color = function (str) { - if (str.value === "evil red") { return new(less.tree.Color)("600") } + if (str.value === "evil red") { return new(less.tree.Color)("600"); } }; sys.puts("\n" + stylize("LESS", 'underline') + "\n"); @@ -39,6 +41,8 @@ runTestSet({strictMath: true, dumpLineNumbers: 'all'}, "debug/", null, runTestSet({strictMath: true, relativeUrls: false, rootpath: "folder (1)/"}, "static-urls/"); runTestSet({strictMath: true, compress: true}, "compression/"); runTestSet({}, "legacy/"); +runTestSet({strictMath: true, strictUnits: true, sourceMap: true }, "sourcemaps/", + testSourcemap, null, null, function(filename) { return path.join('test/sourcemaps', filename) + '.json'; }); testNoOptions(); @@ -53,6 +57,24 @@ function getErrorPathReplacementFunction(dir) { }; } +function testSourcemap(name, err, compiledLess, doReplacements, sourcemap) { + fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) { + sys.print("- " + name + ": "); + if (sourcemap === expectedSourcemap) { + ok('OK'); + } else if (err) { + fail("ERROR: " + (err && err.message)); + if (isVerbose) { + console.error(); + console.error(err.stack); + } + } else { + difference("FAIL", expectedSourcemap, sourcemap); + } + sys.puts(""); + }); +} + function testErrors(name, err, compiledLess, doReplacements) { fs.readFile(path.join('test/less/', name) + '.txt', 'utf8', function (e, expectedErr) { sys.print("- " + name + ": "); @@ -94,14 +116,14 @@ function checkGlobalLeaks() { }); } -function runTestSet(options, foldername, verifyFunction, nameModifier, doReplacements) { +function runTestSet(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) { foldername = foldername || ""; if(!doReplacements) doReplacements = globalReplacements; fs.readdirSync(path.join('test/less/', foldername)).forEach(function (file) { - if (! /\.less/.test(file)) { return } + if (! /\.less/.test(file)) { return; } var name = foldername + path.basename(file, '.less'); @@ -109,20 +131,34 @@ function runTestSet(options, foldername, verifyFunction, nameModifier, doReplace totalTests++; + if (options.sourceMap) { + var sourceMapOutput; + options.writeSourceMap = function(output) { + sourceMapOutput = output; + }; + options.sourceMapOutputFilename = name + ".css"; + options.sourceMapBasepath = path.join(process.cwd(), "test/less"); + options.sourceMapRootpath = "testweb/"; + } + toCSS(options, path.join('test/less/', foldername + file), function (err, less) { if (verifyFunction) { - return verifyFunction(name, err, less, doReplacements); + return verifyFunction(name, err, less, doReplacements, sourceMapOutput); } var css_name = name; - if(nameModifier) css_name=nameModifier(name); + if(nameModifier) { css_name = nameModifier(name); } fs.readFile(path.join('test/css', css_name) + '.css', 'utf8', function (e, css) { - sys.print("- " + css_name + ": ") + sys.print("- " + css_name + ": "); css = css && doReplacements(css, 'test/less/' + foldername); if (less === css) { ok('OK'); } else if (err) { fail("ERROR: " + (err && err.message)); + if (isVerbose) { + console.error(); + console.error(err.stack); + } } else { difference("FAIL", css, less); } @@ -136,7 +172,8 @@ function diff(left, right) { sys.puts(""); require('diff').diffLines(left, right).forEach(function(item) { if(item.added || item.removed) { - sys.print(stylize(item.value, item.added ? 'green' : 'red')); + var text = item.value.replace("\n", String.fromCharCode(182) + "\n"); + sys.print(stylize(text, item.added ? 'green' : 'red')); } else { sys.print(item.value); } @@ -186,10 +223,10 @@ function endTest() { } function toCSS(options, path, callback) { - var tree, css; + var css; options = options || {}; fs.readFile(path, 'utf8', function (e, str) { - if (e) { return callback(e) } + if (e) { return callback(e); } options.paths = [require('path').dirname(path)]; options.filename = require('path').resolve(process.cwd(), path); @@ -214,7 +251,7 @@ function testNoOptions() { totalTests++; try { sys.print("- Integration - creating parser without options: "); - new(less.Parser); + new(less.Parser)(); } catch(e) { fail(stylize("FAIL\n", "red")); return; diff --git a/test/less/css-guards.less b/test/less/css-guards.less index 64e72589..41fbfbf5 100644 --- a/test/less/css-guards.less +++ b/test/less/css-guards.less @@ -28,4 +28,37 @@ @b: 2; @c: 3; -@d: 3; \ No newline at end of file +@d: 3; + +.inheritance when (@b = 2) { + .test { + color: black; + } + &:hover { + color: pink; + } + .hideme when (@b = 1) { + color: green; + } + & when (@b = 1) { + hideme: green; + } +} + +.hideme when (@b = 1) { + .test { + color: black; + } + &:hover { + color: pink; + } + .hideme when (@b = 1) { + color: green; + } +} + +& when (@b = 1) { + .hideme { + color: red; + } +} \ No newline at end of file diff --git a/test/less/errors/svg-gradient1.txt b/test/less/errors/svg-gradient1.txt index c31bcb98..ec662fe6 100644 --- a/test/less/errors/svg-gradient1.txt +++ b/test/less/errors/svg-gradient1.txt @@ -1,4 +1,4 @@ -ArgumentError: error evaluating function `svg-gradient`: svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'radial' in {path}svg-gradient1.less on line 2, column 6: +ArgumentError: error evaluating function `svg-gradient`: svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center' in {path}svg-gradient1.less on line 2, column 6: 1 .a { 2 a: svg-gradient(horizontal, black, white); 3 } diff --git a/test/less/import/invalid-css.less b/test/less/import/invalid-css.less index b7cad97b..ed585d63 100644 --- a/test/less/import/invalid-css.less +++ b/test/less/import/invalid-css.less @@ -1 +1 @@ -this isn't very valid CSS. \ No newline at end of file +this isn't very valid CSS. \ No newline at end of file diff --git a/test/less/sourcemaps/basic.less b/test/less/sourcemaps/basic.less new file mode 100644 index 00000000..a3f4bbc5 --- /dev/null +++ b/test/less/sourcemaps/basic.less @@ -0,0 +1,26 @@ +@var: black; + +.a() { + color: red; +} + +.b { + color: green; + .a(); + color: blue; + background: @var; +} + +.a, .b { + background: green; + .c, .d { + background: gray; + & + & { + color: red; + } + } +} + +.extend:extend(.a all) { + color: pink; +} \ No newline at end of file diff --git a/test/sourcemaps/basic.json b/test/sourcemaps/basic.json new file mode 100644 index 00000000..50e568d5 --- /dev/null +++ b/test/sourcemaps/basic.json @@ -0,0 +1 @@ +{"version":3,"file":"sourcemaps/basic.css","sources":["testweb/sourcemaps/basic.less"],"names":[],"mappings":"AAMG;EACD,YAAA;EAJA,UAAA;EAWA,iBAAA;EALA,WAAA;EACA,mBAAA;;AAJC,EASC;AATD,EASM;EACL,gBAAA;;AACE,EAFF,GAEM,KAFN;AAEE,EAFF,GAEM,KAFD;AAEH,EAFG,GAEC,KAFN;AAEE,EAFG,GAEC,KAFD;EAGH,UAAA;;AALJ;AAAK;AAUA;EATL,iBAAA;;AADA,EAEE;AAFG,EAEH;AAFF,EAEO;AAFF,EAEE;AAQF,OARH;AAQG,OARE;EACL,gBAAA;;AACE,EAFF,GAEM,KAFN;AAEE,EAFF,GAEM,KAFN;AAEE,EAFF,GAEM,KAFD;AAEH,EAFF,GAEM,KAFD;AAEH,EAFF,GAEM,KAFN;AAEE,EAFF,GAEM,KAFN;AAEE,EAFF,GAEM,KAFD;AAEH,EAFF,GAEM,KAFD;AAEH,EAFG,GAEC,KAFN;AAEE,EAFG,GAEC,KAFN;AAEE,EAFG,GAEC,KAFD;AAEH,EAFG,GAEC,KAFD;AAEH,EAFG,GAEC,KAFN;AAEE,EAFG,GAEC,KAFN;AAEE,EAFG,GAEC,KAFD;AAEH,EAFG,GAEC,KAFD;AAQF,OARH,GAQG,UARH;AAQG,OARH,GAEM,KAFN;AAQG,OARH,GAQG,UARE;AAQF,OARH,GAEM,KAFD;AAEH,EAFF,GAQG,UARH;AAEE,EAFF,GAQG,UARE;AAQF,OARE,GAQF,UARH;AAQG,OARE,GAEC,KAFN;AAQG,OARE,GAQF,UARE;AAQF,OARE,GAEC,KAFD;AAEH,EAFG,GAQF,UARH;AAEE,EAFG,GAQF,UARE;EAGH,UAAA;;AAKC;EACL,WAAA"} \ No newline at end of file diff --git a/test/sourcemaps/index.html b/test/sourcemaps/index.html new file mode 100644 index 00000000..205bd44f --- /dev/null +++ b/test/sourcemaps/index.html @@ -0,0 +1,16 @@ + + + + + + +
    id import-test
    +
    id import-test
    +
    class mixin
    +
    class a
    +
    class b
    +
    class b
    class c
    +
    class a
    class d
    +
    class extend
    class c
    + + \ No newline at end of file From a22d1171a7d9eb23176ddbed00f63fa7c25172c2 Mon Sep 17 00:00:00 2001 From: meri Date: Fri, 2 Aug 2013 15:03:08 +0200 Subject: [PATCH 11/36] Added grunt dependency into package.json. --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f3c4c24..ae281938 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "devDependencies": { "diff": "~1.0", "jshint": "~2.1.4", - "http-server": "~0.5.5" + "http-server": "~0.5.5", + "grunt": "~0.4.1" }, "keywords": [ "compile less", From 26138831ffa631c4beff777867b8ba4bb8eac130 Mon Sep 17 00:00:00 2001 From: meri Date: Fri, 2 Aug 2013 15:05:17 +0200 Subject: [PATCH 12/36] Added missing matchdep dependency into package.json. --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ae281938..955bcf7e 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,8 @@ "diff": "~1.0", "jshint": "~2.1.4", "http-server": "~0.5.5", - "grunt": "~0.4.1" + "grunt": "~0.4.1", + "matchdep": "~0.1.2" }, "keywords": [ "compile less", From 6afd04c05f87a0e2f15d4d0275596522700fa250 Mon Sep 17 00:00:00 2001 From: meri Date: Fri, 2 Aug 2013 15:30:17 +0200 Subject: [PATCH 13/36] Added issing grunt-contrib-* dependencies. --- package.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 955bcf7e..10ae85de 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,14 @@ "jshint": "~2.1.4", "http-server": "~0.5.5", "grunt": "~0.4.1", - "matchdep": "~0.1.2" + "matchdep": "~0.1.2", + "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-concat": "~0.3.0", + "grunt-contrib-jshint": "~0.6.0", + "grunt-contrib-uglify": "~0.2.2", + "grunt-shell": "~0.3.1", + "grunt-contrib-connect": "~0.3.0", + "grunt-contrib-jasmine": "~0.5.1" }, "keywords": [ "compile less", From 9eddb1b5af9eaf8b0114602a73667e74314caa3d Mon Sep 17 00:00:00 2001 From: meri Date: Sat, 3 Aug 2013 16:20:06 +0200 Subject: [PATCH 14/36] Added template and config for main runner. --- Gruntfile.js | 42 ++++++++++++++++++++++++-- dist/less-1.4.2.min.js | 24 ++++++++------- test/browser/common.js | 2 +- test/browser/test-runner-template.tmpl | 41 +++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 test/browser/test-runner-template.tmpl diff --git a/Gruntfile.js b/Gruntfile.js index 12b242e5..020e1bec 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -47,7 +47,7 @@ module.exports = function(grunt) { }, // Browser versions browser: { - src: ['<%= build.browser %>'], + src: ['<%= !build.browser %>'], dest: 'test/browser/less.js' }, alpha: { @@ -115,6 +115,38 @@ module.exports = function(grunt) { } }, + connect: { + server: { + options: { + port: 8081 + // grunt-contrib-jasmine assumes that web-server runs in root directory + // it 's outfile is relativized against root + //base: 'test' + } + } + }, + + jasmine: { + options: { + keepRunner: true, //TODO meri: remove after it is done + host: 'http://localhost:8081/', + helpers: 'test/browser/common.js', + template: 'test/browser/test-runner-template.tmpl' + }, + main: { + //TODO meri: find better location for less.js - reference can go to template and compiled browser to dist + //src is used to build list of less files to compile + src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less'], + options: { + specs: 'test/browser/runner-main.js', + outfile: 'test/browser/test-runner-main.html', + templateOptions: { + originalLess: '', + expectedCss: '' + } + } + } + }, // Before running tests, clean out the results // of any previous tests. (this will need to be // setup based on configuration of browser tests. @@ -162,11 +194,17 @@ module.exports = function(grunt) { 'uglify:beta' ]); + // Run all tests + grunt.registerTask('browserTest', [ + 'connect:server', + ]); + // Run all tests grunt.registerTask('test', [ 'jshint:lib', 'clean', - 'shell:test' + 'shell:test', + 'browserTest' // 'shell:browser', // 'shell:phantom' ]); diff --git a/dist/less-1.4.2.min.js b/dist/less-1.4.2.min.js index e4a34ff2..10b264d8 100644 --- a/dist/less-1.4.2.min.js +++ b/dist/less-1.4.2.min.js @@ -1,11 +1,13 @@ -/* - * LESS - Leaner CSS v1.4.2 - * http://lesscss.org - * - * Copyright (c) 2009-2013, Alexis Sellier - * Licensed under the Apache 2.0 License. - * - * @licence - */(function(e,t){function n(t){return e.less[t.split("/")[1]]}function f(){r.env==="development"?(r.optimization=0,r.watchTimer=setInterval(function(){r.watchMode&&g(function(e,t,n,i,s){e?k(e,i.href):t&&S(t.toCSS(r),i,s.lastModified)})},r.poll)):r.optimization=3}function m(){var e=document.getElementsByTagName("style");for(var t=0;t0&&(s.splice(o-1,2),o-=2)}return i.hostPart=r[1],i.directories=s,i.path=r[1]+s.join("/"),i.fileUrl=i.path+(r[4]||""),i.url=i.fileUrl+(r[5]||""),i}function w(t,n,i,s){var o=b(t.href,e.location.href),u=o.url,a=l&&l.getItem(u),f=l&&l.getItem(u+":timestamp"),c={css:a,timestamp:f},h,p={relativeUrls:r.relativeUrls,currentDirectory:o.path,filename:u};t instanceof r.tree.parseEnv?(h=new r.tree.parseEnv(t),p.entryPath=h.currentFileInfo.entryPath,p.rootpath=h.currentFileInfo.rootpath,p.rootFilename=h.currentFileInfo.rootFilename):(h=new r.tree.parseEnv(r),h.mime=t.type,p.entryPath=o.path,p.rootpath=r.rootpath||o.path,p.rootFilename=u),h.relativeUrls&&(r.rootpath?p.rootpath=b(r.rootpath+y(o.path,p.entryPath)).path:p.rootpath=o.path),x(u,t.type,function(e,a){v+=e.replace(/@import .+?;/ig,"");if(!i&&c&&a&&(new Date(a)).valueOf()===(new Date(c.timestamp)).valueOf())S(c.css,t),n(null,null,e,t,{local:!0,remaining:s},u);else try{h.contents[u]=e,h.paths=[o.path],h.currentFileInfo=p,(new r.Parser(h)).parse(e,function(r,i){if(r)return n(r,null,null,t);try{n(r,i,e,t,{local:!1,lastModified:a,remaining:s},u),h.currentFileInfo.rootFilename===u&&N(document.getElementById("less-error-message:"+E(u)))}catch(r){n(r,null,null,t)}})}catch(f){n(f,null,null,t)}},function(e,r){n({type:"File",message:"'"+r+"' wasn't found ("+e+")"},null,null,t)})}function E(e){return e.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function S(e,t,n){var r=t.href||"",i="less:"+(t.title||E(r)),s=document.getElementById(i),o=!1,u=document.createElement("style");u.setAttribute("type","text/css"),t.media&&u.setAttribute("media",t.media),u.id=i;if(u.styleSheet)try{u.styleSheet.cssText=e}catch(a){throw new Error("Couldn't reassign styleSheet.cssText.")}else u.appendChild(document.createTextNode(e)),o=s!==null&&s.childNodes.length>0&&u.childNodes.length>0&&s.firstChild.nodeValue===u.firstChild.nodeValue;var f=document.getElementsByTagName("head")[0];if(s==null||o===!1){var c=t&&t.nextSibling||null;(c||document.getElementsByTagName("head")[0]).parentNode.insertBefore(u,c)}s&&o===!1&&f.removeChild(s);if(n&&l){C("saving "+r+" to cache.");try{l.setItem(r,e),l.setItem(r+":timestamp",n)}catch(a){C("failed to save")}}}function x(e,t,n,i){function a(t,n,r){t.status>=200&&t.status<300?n(t.responseText,t.getResponseHeader("Last-Modified")):typeof r=="function"&&r(t.status,e)}var s=T(),u=o?r.fileAsync:r.async;typeof s.overrideMimeType=="function"&&s.overrideMimeType("text/css"),s.open("GET",e,u),s.setRequestHeader("Accept",t||"text/x-less, text/css; q=0.9, */*; q=0.5"),s.send(null),o&&!r.fileAsync?s.status===0||s.status>=200&&s.status<300?n(s.responseText):i(s.status,e):u?s.onreadystatechange=function(){s.readyState==4&&a(s,n,i)}:a(s,n,i)}function T(){if(e.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(t){return C("browser doesn't support AJAX."),null}}function N(e){return e&&e.parentNode.removeChild(e)}function C(e){r.env=="development"&&typeof console!="undefined"&&console.log("less: "+e)}function k(e,n){var i="less-error-message:"+E(n||""),s='
  • {content}
  • ',o=document.createElement("div"),u,a,f=[],l=e.filename||n,c=l.match(/([^\/]+(\?.*)?)$/)[1];o.id=i,o.className="less-error-message",a="

    "+(e.type||"Syntax")+"Error: "+(e.message||"There is an error in your .less file")+"

    "+'

    in '+c+" ";var h=function(e,n,r){e.extract[n]!=t&&f.push(s.replace(/\{line\}/,(parseInt(e.line)||0)+(n-1)).replace(/\{class\}/,r).replace(/\{content\}/,e.extract[n]))};e.extract?(h(e,0,""),h(e,1,"line"),h(e,2,""),a+="on line "+e.line+", column "+(e.column+1)+":

    "+"
      "+f.join("")+"
    "):e.stack&&(a+="
    "+e.stack.split("\n").slice(1).join("
    ")),o.innerHTML=a,S([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),o.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),r.env=="development"&&(u=setInterval(function(){document.body&&(document.getElementById(i)?document.body.replaceChild(o,document.getElementById(i)):document.body.insertBefore(o,document.body.firstChild),clearInterval(u))},10))}var r,i,s;typeof environment=="object"&&{}.toString.call(environment)==="[object Environment]"?(typeof e=="undefined"?r={}:r=e.less={},i=r.tree={},r.mode="rhino"):typeof e=="undefined"?(r=exports,i=n("./tree"),r.mode="node"):(typeof e.less=="undefined"&&(e.less={}),r=e.less,i=e.less.tree={},r.mode="browser"),r.Parser=function(t){function m(){a=c[u],f=o,h=o}function g(){c[u]=a,o=f,h=o}function y(){o>h&&(c[u]=c[u].slice(o-h),h=o)}function b(e){var t=e.charCodeAt(0);return t===32||t===10||t===9}function w(e){var t,n,r,i,a;if(e instanceof Function)return e.call(p.parsers);if(typeof e=="string")t=s.charAt(o)===e?e:null,r=1,y();else{y();if(!(t=e.exec(c[u])))return null;r=t[0].length}if(t)return E(r),typeof t=="string"?t:t.length===1?t[0]:t}function E(e){var t=o,n=u,r=o+c[u].length,i=o+=e;while(o=0&&t.charAt(n)!=="\n";n--)r++;return{line:typeof e=="number"?(t.slice(0,e).match(/\n/g)||"").length:null,column:r}}function k(e,t,i){var s=i.currentFileInfo.filename;return r.mode!=="browser"&&r.mode!=="rhino"&&(s=n("path").resolve(s)),{lineNumber:C(e,t).line+1,fileName:s}}function L(e,t){var n=N(e,t),r=C(e.index,n),i=r.line,s=r.column,o=n.split("\n");this.type=e.type||"Syntax",this.message=e.message,this.filename=e.filename||t.currentFileInfo.filename,this.index=e.index,this.line=typeof i=="number"?i+1:null,this.callLine=e.call&&C(e.call,n).line+1,this.callExtract=o[C(e.call,n).line],this.stack=e.stack,this.column=s,this.extract=[o[i-1],o[i],o[i+1]]}var s,o,u,a,f,l,c,h,p,d=this;t instanceof i.parseEnv||(t=new i.parseEnv(t));var v=this.imports={paths:t.paths||[],queue:[],files:t.files,contents:t.contents,mime:t.mime,error:null,push:function(e,n,i){var s=this;this.queue.push(e),r.Parser.importer(e,n,function(t,n,r){s.queue.splice(s.queue.indexOf(e),1);var o=r in s.files;s.files[r]=n,t&&!s.error&&(s.error=t),i(t,n,o)},t)}};return L.prototype=new Error,L.prototype.constructor=L,this.env=t=t||{},this.optimization="optimization"in this.env?this.env.optimization:1,p={imports:v,parse:function(e,a){var f,d,v,m,g,y,b=[],E,S=null;o=u=h=l=0,s=e.replace(/\r\n/g,"\n"),s=s.replace(/^\uFEFF/,""),c=function(e){var n=0,r=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,i=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,o=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,u=0,a,f=e[0],l;for(var c=0,h,p;c0?"missing closing `}`":"missing opening `{`",filename:t.currentFileInfo.filename},t)),e.map(function(e){return e.join("")})}([[]]);if(S)return a(new L(S,t));try{f=new i.Ruleset([],w(this.parsers.primary)),f.root=!0,f.firstRoot=!0}catch(x){return a(new L(x,t))}f.toCSS=function(e){var s,o,u;return function(s,o){s=s||{};var u,a=new i.evalEnv(s);typeof o=="object"&&!Array.isArray(o)&&(o=Object.keys(o).map(function(e){var t=o[e];return t instanceof i.Value||(t instanceof i.Expression||(t=new i.Expression([t])),t=new i.Value([t])),new i.Rule("@"+e,t,!1,0)}),a.frames=[new i.Ruleset(null,o)]);try{var f=e.call(this,a);(new i.joinSelectorVisitor).run(f),(new i.processExtendsVisitor).run(f);var l=f.toCSS({compress:Boolean(s.compress),dumpLineNumbers:t.dumpLineNumbers,strictUnits:Boolean(s.strictUnits)})}catch(c){throw new L(c,t)}return s.yuicompress&&r.mode==="node"?n("ycssmin").cssmin(l,s.maxLineLen):s.compress?l.replace(/(\s)+/g,"$1"):l}}(f.eval);if(o=0&&s.charAt(T)!=="\n";T--)N++;S={type:"Parse",message:"Unrecognised input",index:o,filename:t.currentFileInfo.filename,line:g,column:N,extract:[y[g-2],y[g-1],y[g]]}}var C=function(e){e=S||e||p.imports.error,e?(e instanceof L||(e=new L(e,t)),a(e)):a(null,f)};t.processImports!==!1?(new i.importVisitor(this.imports,C)).run(f):C()},parsers:{primary:function(){var e,t=[];while((e=w(this.extendRule)||w(this.mixin.definition)||w(this.rule)||w(this.ruleset)||w(this.mixin.call)||w(this.comment)||w(this.directive))||w(/^[\s\n]+/)||w(/^;+/))e&&t.push(e);return t},comment:function(){var e;if(s.charAt(o)!=="/")return;if(s.charAt(o+1)==="/")return new i.Comment(w(/^\/\/.*/),!0);if(e=w(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new i.Comment(e)},entities:{quoted:function(){var e,n=o,r,u=o;s.charAt(n)==="~"&&(n++,r=!0);if(s.charAt(n)!=='"'&&s.charAt(n)!=="'")return;r&&w("~");if(e=w(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new i.Quoted(e[0],e[1]||e[2],r,u,t.currentFileInfo)},keyword:function(){var e;if(e=w(/^[_A-Za-z-][_A-Za-z0-9-]*/))return i.colors.hasOwnProperty(e)?new i.Color(i.colors[e].slice(1)):new i.Keyword(e)},call:function(){var e,n,r,s,a=o;if(!(e=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(c[u])))return;e=e[1],n=e.toLowerCase();if(n==="url")return null;o+=e.length;if(n==="alpha"){s=w(this.alpha);if(typeof s!="undefined")return s}w("("),r=w(this.entities.arguments);if(!w(")"))return;if(e)return new i.Call(e,r,a,t.currentFileInfo)},arguments:function(){var e=[],t;while(t=w(this.entities.assignment)||w(this.expression)){e.push(t);if(!w(","))break}return e},literal:function(){return w(this.entities.dimension)||w(this.entities.color)||w(this.entities.quoted)||w(this.entities.unicodeDescriptor)},assignment:function(){var e,t;if((e=w(/^\w+(?=\s?=)/i))&&w("=")&&(t=w(this.entity)))return new i.Assignment(e,t)},url:function(){var e;if(s.charAt(o)!=="u"||!w(/^url\(/))return;return e=w(this.entities.quoted)||w(this.entities.variable)||w(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",S(")"),new i.URL(e.value!=null||e instanceof i.Variable?e:new i.Anonymous(e),t.currentFileInfo)},variable:function(){var e,n=o;if(s.charAt(o)==="@"&&(e=w(/^@@?[\w-]+/)))return new i.Variable(e,n,t.currentFileInfo)},variableCurly:function(){var e,n,r=o;if(s.charAt(o)==="@"&&(n=w(/^@\{([\w-]+)\}/)))return new i.Variable("@"+n[1],r,t.currentFileInfo)},color:function(){var e;if(s.charAt(o)==="#"&&(e=w(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/)))return new i.Color(e[1])},dimension:function(){var e,t=s.charCodeAt(o);if(t>57||t<43||t===47||t==44)return;if(e=w(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/))return new i.Dimension(e[1],e[2])},unicodeDescriptor:function(){var e;if(e=w(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))return new i.UnicodeDescriptor(e[0])},javascript:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=="`")return;n&&w("~");if(e=w(/^`([^`]*)`/))return new i.JavaScript(e[1],o,n)}},variable:function(){var e;if(s.charAt(o)==="@"&&(e=w(/^(@[\w-]+)\s*:/)))return e[1]},extend:function(e){var t,n,r=o,s,u=[];if(!w(e?/^&:extend\(/:/^:extend\(/))return;do{s=null,t=[];for(;;){s=w(/^(all)(?=\s*(\)|,))/);if(s)break;n=w(this.element);if(!n)break;t.push(n)}s=s&&s[1],u.push(new i.Extend(new i.Selector(t),s,r))}while(w(","));return S(/^\)/),e&&S(/^;/),u},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var e=[],n,r,u,a,f,l=o,c=s.charAt(o),h=!1;if(c!=="."&&c!=="#")return;m();while(n=w(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/))e.push(new i.Element(r,n,o)),r=w(">");w("(")&&(u=this.mixin.args.call(this,!0).args,S(")")),u=u||[],w(this.important)&&(h=!0);if(e.length>0&&(w(";")||T("}")))return new i.mixin.Call(e,u,l,t.currentFileInfo,h);g()},args:function(e){var t=[],n=[],r,u=[],a,f,l,c,h,p={args:null,variadic:!1};for(;;){if(e)h=w(this.expression);else{w(this.comment);if(s.charAt(o)==="."&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({variadic:!0});break}h=w(this.entities.variable)||w(this.entities.literal)||w(this.entities.keyword)}if(!h)break;l=null,h.throwAwayComments&&h.throwAwayComments(),c=h;var d=null;if(e){if(h.value.length==1)var d=h.value[0]}else d=h;if(d&&d instanceof i.Variable)if(w(":"))t.length>0&&(r&&x("Cannot mix ; and , as delimiter types"),a=!0),c=S(this.expression),l=f=d.name;else{if(!e&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({name:h.name,variadic:!0});break}e||(f=l=d.name,c=null)}c&&t.push(c),u.push({name:l,value:c});if(w(","))continue;if(w(";")||r)a&&x("Cannot mix ; and , as delimiter types"),r=!0,t.length>1&&(c=new i.Value(t)),n.push({name:f,value:c}),f=null,t=[],a=!1}return p.args=r?n:u,p},definition:function(){var e,t=[],n,r,u,a,f,c=!1;if(s.charAt(o)!=="."&&s.charAt(o)!=="#"||T(/^[^{]*\}/))return;m();if(n=w(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){e=n[1];var h=this.mixin.args.call(this,!1);t=h.args,c=h.variadic,w(")")||(l=o,g()),w(this.comment),w(/^when/)&&(f=S(this.conditions,"expected condition")),r=w(this.block);if(r)return new i.mixin.Definition(e,t,r,f,c);g()}}},entity:function(){return w(this.entities.literal)||w(this.entities.variable)||w(this.entities.url)||w(this.entities.call)||w(this.entities.keyword)||w(this.entities.javascript)||w(this.comment)},end:function(){return w(";")||T("}")},alpha:function(){var e;if(!w(/^\(opacity=/i))return;if(e=w(/^\d+/)||w(this.entities.variable))return S(")"),new i.Alpha(e)},element:function(){var e,t,n,r;n=w(this.combinator),e=w(/^(?:\d+\.\d+|\d+)%/)||w(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||w("*")||w("&")||w(this.attribute)||w(/^\([^()@]+\)/)||w(/^[\.#](?=@)/)||w(this.entities.variableCurly),e||w("(")&&(r=w(this.selector))&&w(")")&&(e=new i.Paren(r));if(e)return new i.Element(n,e,o)},combinator:function(){var e=s.charAt(o);if(e===">"||e==="+"||e==="~"||e==="|"){o++;while(s.charAt(o).match(/\s/))o++;return new i.Combinator(e)}return s.charAt(o-1).match(/\s/)?new i.Combinator(" "):new i.Combinator(null)},selector:function(){var e,t,n=[],r,u,a=[];while((u=w(this.extend))||(t=w(this.element))){u?a.push.apply(a,u):(a.length&&x("Extend can only be used at the end of selector"),r=s.charAt(o),n.push(t),t=null);if(r==="{"||r==="}"||r===";"||r===","||r===")")break}if(n.length>0)return new i.Selector(n,a);a.length&&x("Extend must be used to extend a selector, it cannot be used on its own")},attribute:function(){var e="",t,n,r;if(!w("["))return;(t=w(this.entities.variableCurly))||(t=S(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/));if(r=w(/^[|~*$^]?=/))n=w(this.entities.quoted)||w(/^[\w-]+/)||w(this.entities.variableCurly);return S("]"),new i.Attribute(t,r,n)},block:function(){var e;if(w("{")&&(e=w(this.primary))&&w("}"))return e},ruleset:function(){var e=[],n,r,u;m(),t.dumpLineNumbers&&(u=k(o,s,t));while(n=w(this.selector)){e.push(n),w(this.comment);if(!w(","))break;w(this.comment)}if(e.length>0&&(r=w(this.block))){var a=new i.Ruleset(e,r,t.strictImports);return t.dumpLineNumbers&&(a.debugInfo=u),a}l=o,g()},rule:function(e){var n,r,u=s.charAt(o),a;m();if(u==="."||u==="#"||u==="&")return;if(n=w(this.variable)||w(this.property)){r=!e&&(t.compress||n.charAt(0)==="@")?w(this.value)||w(this.anonymousValue):w(this.anonymousValue)||w(this.value),a=w(this.important);if(r&&w(this.end))return new i.Rule(n,r,a,f,t.currentFileInfo);l=o,g();if(r&&!e)return this.rule(!0)}},anonymousValue:function(){var e;if(e=/^([^@+\/'"*`(;{}-]*);/.exec(c[u]))return o+=e[0].length-1,new i.Anonymous(e[1])},"import":function(){var e,n,r=o;m();var s=w(/^@import?\s+/),u=(s?w(this.importOptions):null)||{};if(s&&(e=w(this.entities.quoted)||w(this.entities.url))){n=w(this.mediaFeatures);if(w(";"))return n=n&&new i.Value(n),new i.Import(e,n,u,r,t.currentFileInfo)}g()},importOptions:function(){var e,t={},n,r;if(!w("("))return null;do if(e=w(this.importOption)){n=e,r=!0;switch(n){case"css":n="less",r=!1;break;case"once":n="multiple",r=!1}t[n]=r;if(!w(","))break}while(e);return S(")"),t},importOption:function(){var e=w(/^(less|css|multiple|once)/);if(e)return e[1]},mediaFeature:function(){var e,n,r=[];do if(e=w(this.entities.keyword))r.push(e);else if(w("(")){n=w(this.property),e=w(this.value);if(!w(")"))return null;if(n&&e)r.push(new i.Paren(new i.Rule(n,e,null,o,t.currentFileInfo,!0)));else{if(!e)return null;r.push(new i.Paren(e))}}while(e);if(r.length>0)return new i.Expression(r)},mediaFeatures:function(){var e,t=[];do if(e=w(this.mediaFeature)){t.push(e);if(!w(","))break}else if(e=w(this.entities.variable)){t.push(e);if(!w(","))break}while(e);return t.length>0?t:null},media:function(){var e,n,r,u;t.dumpLineNumbers&&(u=k(o,s,t));if(w(/^@media/)){e=w(this.mediaFeatures);if(n=w(this.block))return r=new i.Media(n,e),t.dumpLineNumbers&&(r.debugInfo=u),r}},directive:function(){var e,n,r,u,a,f,l,c,h,p;if(s.charAt(o)!=="@")return;if(n=w(this["import"])||w(this.media))return n;m(),e=w(/^@[a-z-]+/);if(!e)return;l=e,e.charAt(1)=="-"&&e.indexOf("-",2)>0&&(l="@"+e.slice(e.indexOf("-",2)+1));switch(l){case"@font-face":c=!0;break;case"@viewport":case"@top-left":case"@top-left-corner":case"@top-center":case"@top-right":case"@top-right-corner":case"@bottom-left":case"@bottom-left-corner":case"@bottom-center":case"@bottom-right":case"@bottom-right-corner":case"@left-top":case"@left-middle":case"@left-bottom":case"@right-top":case"@right-middle":case"@right-bottom":c=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":c=!0,h=!0;break;case"@namespace":p=!0}h&&(e+=" "+(w(/^[^{]+/)||"").trim());if(c){if(r=w(this.block))return new i.Directive(e,r)}else if((n=p?w(this.expression):w(this.entity))&&w(";")){var d=new i.Directive(e,n);return t.dumpLineNumbers&&(d.debugInfo=k(o,s,t)),d}g()},value:function(){var e,t=[],n;while(e=w(this.expression)){t.push(e);if(!w(","))break}if(t.length>0)return new i.Value(t)},important:function(){if(s.charAt(o)==="!")return w(/^! *important/)},sub:function(){var e,t;if(w("("))if(e=w(this.addition))return t=new i.Expression([e]),S(")"),t.parens=!0,t},multiplication:function(){var e,t,n,r,u,a=[];if(e=w(this.operand)){u=b(s.charAt(o-1));while(!T(/^\/[*\/]/)&&(n=w("/")||w("*"))){if(!(t=w(this.operand)))break;e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1))}return r||e}},addition:function(){var e,t,n,r,u;if(e=w(this.multiplication)){u=b(s.charAt(o-1));while((n=w(/^[-+]\s+/)||!u&&(w("+")||w("-")))&&(t=w(this.multiplication)))e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1));return r||e}},conditions:function(){var e,t,n=o,r;if(e=w(this.condition)){while(w(",")&&(t=w(this.condition)))r=new i.Condition("or",r||e,t,n);return r||e}},condition:function(){var e,t,n,r,s=o,u=!1;w(/^not/)&&(u=!0),S("(");if(e=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))return(r=w(/^(?:>=|=<|[<=>])/))?(t=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))?n=new i.Condition(r,e,t,s,u):x("expected expression"):n=new i.Condition("=",e,new i.Keyword("true"),s,u),S(")"),w(/^and/)?new i.Condition("and",n,w(this.condition)):n},operand:function(){var e,t=s.charAt(o+1);s.charAt(o)==="-"&&(t==="@"||t==="(")&&(e=w("-"));var n=w(this.sub)||w(this.entities.dimension)||w(this.entities.color)||w(this.entities.variable)||w(this.entities.call);return e&&(n.parensInOp=!0,n=new i.Negative(n)),n},expression:function(){var e,t,n=[],r;while(e=w(this.addition)||w(this.entity))n.push(e),!T(/^\/[\/*]/)&&(t=w("/"))&&n.push(new i.Anonymous(t));if(n.length>0)return new i.Expression(n)},property:function(){var e;if(e=w(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/))return e[1]}}}};if(r.mode==="browser"||r.mode==="rhino")r.Parser.importer=function(e,t,n,r){!/^([a-z-]+:)?\//.test(e)&&t.currentDirectory&&(e=t.currentDirectory+e);var i=r.toSheet(e);i.processImports=!1,i.currentFileInfo=t,w(i,function(e,t,r,i,s,o){n.call(null,e,t,o)},!0)};(function(r){function u(e){return r.functions.hsla(e.h,e.s,e.l,e.a)}function a(e,t){return e instanceof r.Dimension&&e.unit.is("%")?parseFloat(e.value*t/100):f(e)}function f(e){if(e instanceof r.Dimension)return parseFloat(e.unit.is("%")?e.value/100:e.value);if(typeof e=="number")return e;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function l(e){return Math.min(1,Math.max(0,e))}r.functions={rgb:function(e,t,n){return this.rgba(e,t,n,1)},rgba:function(e,t,n,i){var s=[e,t,n].map(function(e){return a(e,256)});return i=f(i),new r.Color(s,i)},hsl:function(e,t,n){return this.hsla(e,t,n,1)},hsla:function(e,t,n,r){function o(e){return e=e<0?e+1:e>1?e-1:e,e*6<1?s+(i-s)*e*6:e*2<1?i:e*3<2?s+(i-s)*(2/3-e)*6:s}e=f(e)%360/360,t=l(f(t)),n=l(f(n)),r=l(f(r));var i=n<=.5?n*(t+1):n+t-n*t,s=n*2-i;return this.rgba(o(e+1/3)*255,o(e)*255,o(e-1/3)*255,r)},hsv:function(e,t,n){return this.hsva(e,t,n,1)},hsva:function(e,t,n,r){e=f(e)%360/360*360,t=f(t),n=f(n),r=f(r);var i,s;i=Math.floor(e/60%6),s=e/60-i;var o=[n,n*(1-t),n*(1-s*t),n*(1-(1-s)*t)],u=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(o[u[i][0]]*255,o[u[i][1]]*255,o[u[i][2]]*255,r)},hue:function(e){return new r.Dimension(Math.round(e.toHSL().h))},saturation:function(e){return new r.Dimension(Math.round(e.toHSL().s*100),"%")},lightness:function(e){return new r.Dimension(Math.round(e.toHSL().l*100),"%")},hsvhue:function(e){return new r.Dimension(Math.round(e.toHSV().h))},hsvsaturation:function(e){return new r.Dimension(Math.round(e.toHSV().s*100),"%")},hsvvalue:function(e){return new r.Dimension(Math.round(e.toHSV().v*100),"%")},red:function(e){return new r.Dimension(e.rgb[0])},green:function(e){return new r.Dimension(e.rgb[1])},blue:function(e){return new r.Dimension(e.rgb[2])},alpha:function(e){return new r.Dimension(e.toHSL().a)},luma:function(e){return new r.Dimension(Math.round(e.luma()*e.alpha*100),"%")},saturate:function(e,t){var n=e.toHSL();return n.s+=t.value/100,n.s=l(n.s),u(n)},desaturate:function(e,t){var n=e.toHSL();return n.s-=t.value/100,n.s=l(n.s),u(n)},lighten:function(e,t){var n=e.toHSL();return n.l+=t.value/100,n.l=l(n.l),u(n)},darken:function(e,t){var n=e.toHSL();return n.l-=t.value/100,n.l=l(n.l),u(n)},fadein:function(e,t){var n=e.toHSL();return n.a+=t.value/100,n.a=l(n.a),u(n)},fadeout:function(e,t){var n=e.toHSL();return n.a-=t.value/100,n.a=l(n.a),u(n)},fade:function(e,t){var n=e.toHSL();return n.a=t.value/100,n.a=l(n.a),u(n)},spin:function(e,t){var n=e.toHSL(),r=(n.h+t.value)%360;return n.h=r<0?360+r:r,u(n)},mix:function(e,t,n){n||(n=new r.Dimension(50));var i=n.value/100,s=i*2-1,o=e.toHSL().a-t.toHSL().a,u=((s*o==-1?s:(s+o)/(1+s*o))+1)/2,a=1-u,f=[e.rgb[0]*u+t.rgb[0]*a,e.rgb[1]*u+t.rgb[1]*a,e.rgb[2]*u+t.rgb[2]*a],l=e.alpha*i+t.alpha*(1-i);return new r.Color(f,l)},greyscale:function(e){return this.desaturate(e,new r.Dimension(100))},contrast:function(e,t,n,r){if(!e.rgb)return null;typeof n=="undefined"&&(n=this.rgba(255,255,255,1)),typeof t=="undefined"&&(t=this.rgba(0,0,0,1));if(t.luma()>n.luma()){var i=n;n=t,t=i}return typeof r=="undefined"?r=.43:r=f(r),e.luma()*e.alpha=d){if(this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",o,v,d),(new r.URL(i||t,this.currentFileInfo)).eval(this.env);this.env.silent||console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!",o,v,d)}p=f?p.toString("base64"):encodeURIComponent(p);var m="'data:"+s+","+p+"'";return new r.URL(new r.Anonymous(m))}},r._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(e){var i=n("path").extname(e),s=r._mime._types[i];if(s===t)throw new Error('Optional dependency "mime" is required for '+i);return s},charsets:{lookup:function(e){return e&&/^text\//.test(e)?"UTF-8":""}}};var i=[{name:"ceil"},{name:"floor"},{name:"sqrt"},{name:"abs"},{name:"tan",unit:""},{name:"sin",unit:""},{name:"cos",unit:""},{name:"atan",unit:"rad"},{name:"asin",unit:"rad"},{name:"acos",unit:"rad"}],s=function(e,t){return function(n){return t!=null&&(n=n.unify()),this._math(Math[e],t,n)}};for(var o=0;o255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("");return n&&(r=r.split(""),r[0]==r[1]&&r[2]==r[3]&&r[4]==r[5]?r=r[0]+r[2]+r[4]:r=r.join("")),"#"+r},operate:function(t,n,r){var i=[];r instanceof e.Color||(r=r.toColor());for(var s=0;s<3;s++)i[s]=e.operate(t,n,this.rgb[s],r.rgb[s]);return new e.Color(i,this.alpha+r.alpha)},toHSL:function(){var e=this.rgb[0]/255,t=this.rgb[1]/255,n=this.rgb[2]/255,r=this.alpha,i=Math.max(e,t,n),s=Math.min(e,t,n),o,u,a=(i+s)/2,f=i-s;if(i===s)o=u=0;else{u=a>.5?f/(2-i-s):f/(i+s);switch(i){case e:o=(t-n)/f+(t255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},compare:function(e){return e.rgb?e.rgb[0]===this.rgb[0]&&e.rgb[1]===this.rgb[1]&&e.rgb[2]===this.rgb[2]&&e.alpha===this.alpha?0:-1:-1}}}(n("../tree")),function(e){e.Comment=function(e,t){this.value=e,this.silent=!!t},e.Comment.prototype={type:"Comment",toCSS:function(e){return e.compress?"":this.value},eval:function(){return this}}}(n("../tree")),function(e){e.Condition=function(e,t,n,r,i){this.op=e.trim(),this.lvalue=t,this.rvalue=n,this.index=r,this.negate=i},e.Condition.prototype={type:"Condition",accept:function(e){this.lvalue=e.visit(this.lvalue),this.rvalue=e.visit(this.rvalue)},eval:function(e){var t=this.lvalue.eval(e),n=this.rvalue.eval(e),r=this.index,i,i=function(e){switch(e){case"and":return t&&n;case"or":return t||n;default:if(t.compare)i=t.compare(n);else{if(!n.compare)throw{type:"Type",message:"Unable to perform comparison",index:r};i=n.compare(t)}switch(i){case-1:return e==="<"||e==="=<";case 0:return e==="="||e===">="||e==="=<";case 1:return e===">"||e===">="}}}(this.op);return this.negate?!i:i}}}(n("../tree")),function(e){e.Dimension=function(n,r){this.value=parseFloat(n),this.unit=r&&r instanceof e.Unit?r:new e.Unit(r?[r]:t)},e.Dimension.prototype={type:"Dimension",accept:function(e){this.unit=e.visit(this.unit)},eval:function(e){return this},toColor:function(){return new e.Color([this.value,this.value,this.value])},toCSS:function(e){if(e&&e.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var t=this.value,n=String(t);t!==0&&t<1e-6&&t>-0.000001&&(n=t.toFixed(20).replace(/0+$/,""));if(e&&e.compress){if(t===0&&!this.unit.isAngle())return n;t>0&&t<1&&(n=n.substr(1))}return n+this.unit.toCSS(e)},operate:function(t,n,r){var i=e.operate(t,n,this.value,r.value),s=this.unit.clone();if(n==="+"||n==="-"){if(s.numerator.length===0&&s.denominator.length===0)s.numerator=r.unit.numerator.slice(0),s.denominator=r.unit.denominator.slice(0);else if(r.unit.numerator.length!=0||s.denominator.length!=0){r=r.convertTo(this.unit.usedUnits());if(t.strictUnits&&r.unit.toString()!==s.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+s.toString()+"' and '"+r.unit.toString()+"'.");i=e.operate(t,n,this.value,r.value)}}else n==="*"?(s.numerator=s.numerator.concat(r.unit.numerator).sort(),s.denominator=s.denominator.concat(r.unit.denominator).sort(),s.cancel()):n==="/"&&(s.numerator=s.numerator.concat(r.unit.denominator).sort(),s.denominator=s.denominator.concat(r.unit.numerator).sort(),s.cancel());return new e.Dimension(i,s)},compare:function(t){if(t instanceof e.Dimension){var n=this.unify(),r=t.unify(),i=n.value,s=r.value;return s>i?-1:s=1?this.numerator[0]:this.denominator.length>=1?this.denominator[0]:(!e||!e.strictUnits)&&this.backupUnit?this.backupUnit:""},toString:function(){var e,t=this.numerator.join("*");for(e=0;e0)for(n=0;n":e.compress?">":" > ","|":e.compress?"|":" | "}[this.value]}}}(n("../tree")),function(e){e.Expression=function(e){this.value=e},e.Expression.prototype={type:"Expression",accept:function(e){this.value=e.visit(this.value)},eval:function(t){var n,r=this.parens&&!this.parensInOp,i=!1;return r&&t.inParenthesis(),this.value.length>1?n=new e.Expression(this.value.map(function(e){return e.eval(t)})):this.value.length===1?(this.value[0].parens&&!this.value[0].parensInOp&&(i=!0),n=this.value[0].eval(t)):n=this,r&&t.outOfParenthesis(),this.parens&&this.parensInOp&&!t.isMathOn()&&!i&&(n=new e.Paren(n)),n},toCSS:function(e){return this.value.map(function(t){return t.toCSS?t.toCSS(e):""}).join(" ")},throwAwayComments:function(){this.value=this.value.filter(function(t){return!(t instanceof e.Comment)})}}}(n("../tree")),function(e){e.Extend=function(t,n,r){this.selector=t,this.option=n,this.index=r;switch(n){case"all":this.allowBefore=!0,this.allowAfter=!0;break;default:this.allowBefore=!1,this.allowAfter=!1}},e.Extend.prototype={type:"Extend",accept:function(e){this.selector=e.visit(this.selector)},eval:function(t){return new e.Extend(this.selector.eval(t),this.option,this.index)},clone:function(t){return new e.Extend(this.selector,this.option,this.index)},findSelfSelectors:function(e){var t=[],n;for(n=0;n1){var r=this.emptySelectors();n=new e.Ruleset(r,t.mediaBlocks),n.multiMedia=!0}return delete t.mediaBlocks,delete t.mediaPath,n},evalNested:function(t){var n,r,i=t.mediaPath.concat([this]);for(n=0;n0;n--)t.splice(n,0,new e.Anonymous("and"));return new e.Expression(t)})),new e.Ruleset([],[])},permute:function(e){if(e.length===0)return[];if(e.length===1)return e[0];var t=[],n=this.permute(e.slice(1));for(var r=0;r0){c=!0;for(a=0;athis.params.length)return!1;if(this.required>0&&n>this.params.length)return!1}r=Math.min(n,this.arity);for(var s=0;si.selectors[o].elements.length?Array.prototype.push.apply(r,i.find(new e.Selector(t.elements.slice(1)),n)):r.push(i);break}}),this._lookups[o]=r)},toCSS:function(t){var n=[],r=[],i=[],s=[],o,u,a;for(var f=0;f0){u=e.debugInfo(t,this),o=this.paths.map(function(e){return e.map(function(e){return e.toCSS(t)}).join("").trim()}).join(t.compress?",":",\n");for(var f=r.length-1;f>=0;f--)(r[f].slice(0,2)==="/*"||i.indexOf(r[f])===-1)&&i.unshift(r[f]);r=i,n.push(u+o+(t.compress?"{":" {\n ")+r.join(t.compress?"":"\n ")+(t.compress?"}":"\n}\n"))}return n.push(s),n.join("")+(t.compress?"\n":"")},joinSelectors:function(e,t,n){for(var r=0;r0)for(i=0;i0&&this.mergeElementsOnToSelectors(g,a);for(s=0;s0&&(l[0].elements=l[0].elements.slice(0),l[0].elements.push(new e.Element(f.combinator,"",0))),y.push(l);else for(o=0;o0?(h=l.slice(0),m=h.pop(),d=new e.Selector(m.elements.slice(0),r.extendList),v=!1):d=new e.Selector([],r.extendList),c.length>1&&(p=p.concat(c.slice(1))),c.length>0&&(v=!1,d.elements.push(new e.Element(f.combinator,c[0].elements[0].value,0)),d.elements=d.elements.concat(c[0].elements.slice(1))),v||h.push(d),h=h.concat(p),y.push(h)}a=y,g=[]}}g.length>0&&this.mergeElementsOnToSelectors(g,a);for(i=0;i0&&t.push(a[i])},mergeElementsOnToSelectors:function(t,n){var r,i,s;if(n.length==0){n.push([new e.Selector(t)]);return}for(r=0;r0?i[i.length-1]=new e.Selector(i[i.length-1].elements.concat(t),i[i.length-1].extendList):i.push(new e.Selector(t))}}}(n("../tree")),function(e){e.Selector=function(e,t){this.elements=e,this.extendList=t||[]},e.Selector.prototype={type:"Selector",accept:function(e){this.elements=e.visit(this.elements),this.extendList=e.visit(this.extendList)},match:function(e){var t=this.elements,n=t.length,r,i,s,o;r=e.elements.slice(e.elements.length&&e.elements[0].value==="&"?1:0),i=r.length,s=Math.min(n,i);if(i===0||n1?"["+e.value.map(function(e){return e.toCSS(!1)}).join(", ")+"]":e.toCSS(!1)}}(n("./tree")),function(e){var t=["paths","optimization","files","contents","relativeUrls","strictImports","dumpLineNumbers","compress","processImports","syncImport","mime","currentFileInfo"];e.parseEnv=function(e){r(e,this,t),this.contents||(this.contents={}),this.files||(this.files={});if(!this.currentFileInfo){var n=e&&e.filename||"input",i=n.replace(/[^\/\\]*$/,"");e&&(e.filename=null),this.currentFileInfo={filename:n,relativeUrls:this.relativeUrls,rootpath:e&&e.rootpath||"",currentDirectory:i,entryPath:i,rootFilename:n}}},e.parseEnv.prototype.toSheet=function(t){var n=new e.parseEnv(this);return n.href=t,n.type=this.mime,n};var n=["silent","verbose","compress","yuicompress","ieCompat","strictMath","strictUnits"];e.evalEnv=function(e,t){r(e,this,n),this.frames=t||[]},e.evalEnv.prototype.inParenthesis=function(){this.parensStack||(this.parensStack=[]),this.parensStack.push(!0)},e.evalEnv.prototype.outOfParenthesis=function(){this.parensStack.pop()},e.evalEnv.prototype.isMathOn=function(){return this.strictMath?this.parensStack&&this.parensStack.length:!0},e.evalEnv.prototype.isPathRelative=function(e){return!/^(?:[a-z-]+:|\/)/.test(e)};var r=function(e,t,n){if(!e)return;for(var r=0;r100){var d="{unable to calculate}",v="{unable to calculate}";try{d=u[0].selfSelectors[0].toCSS(),v=u[0].selector.toCSS()}catch(m){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+d+":extend("+v+")"}}return u.concat(f.doExtendChaining(u,n,r+1))}return u},inInheritanceChain:function(e,t){if(e===t)return!0;if(t.parents){if(this.inInheritanceChain(e,t.parents[0]))return!0;if(this.inInheritanceChain(e,t.parents[1]))return!0}return!1},visitRule:function(e,t){t.visitDeeper=!1},visitMixinDefinition:function(e,t){t.visitDeeper=!1},visitSelector:function(e,t){t.visitDeeper=!1},visitRuleset:function(e,t){if(e.root)return;var n,r,i,s=this.allExtendsStack[this.allExtendsStack.length-1],o=[],u=this,a;for(i=0;i0&&f[c.matched].combinator.value!==o?c=null:c.matched++,c&&(c.finished=c.matched===f.length,c.finished&&!e.allowAfter&&(i+1i&&s>0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,l.pathIndex)),o.push(new e.Selector(a.elements.slice(s,l.index).concat([f]).concat(r.elements.slice(1)))),i=l.endPathIndex,s=l.endPathElementIndex,s>=a.elements.length&&(s=0,i++);return i0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,n.length)),o},visitRulesetOut:function(e){},visitMedia:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitMediaOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitDirectiveOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(n("./tree"));var o=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);r.env=r.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||o?"development":"production"),r.async=r.async||!1,r.fileAsync=r.fileAsync||!1,r.poll=r.poll||(o?1e3:1500);if(r.functions)for(var u in r.functions)r.tree.functions[u]=r.functions[u];var a=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);a&&(r.dumpLineNumbers=a[1]),r.watch=function(){return r.watchMode||(r.env="development",f()),this.watchMode=!0},r.unwatch=function(){return clearInterval(r.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&r.watch();var l=null;if(r.env!="development")try{l=typeof e.localStorage=="undefined"?null:e.localStorage}catch(c){}var h=document.getElementsByTagName("link"),p=/^text\/(x-)?less$/;r.sheets=[];for(var d=0;d + * Licensed under the Apache v2 License. + * + * @licence + */ + +function require(a){return window.less[a.split("/")[1]]}function initRunningMode(){"development"===less.env?(less.optimization=0,less.watchTimer=setInterval(function(){less.watchMode&&loadStyleSheets(function(a,b,c,d,e){a?error(a,d.href):b&&createCSS(b.toCSS(less),d,e.lastModified)})},less.poll)):less.optimization=3}function loadStyles(a){for(var b,c=document.getElementsByTagName("style"),d=0;dc&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function loadStyleSheet(a,b,c,d,e){var f=new less.tree.parseEnv(less);f.mime=a.type,e&&(f.useFileCache=!0),loadFile(a.href,null,function(e,g,h,i,j){if(j){j.remaining=d;var k=cache&&cache.getItem(h),l=cache&&cache.getItem(h+":timestamp");if(!c&&l&&j.lastModified&&new Date(j.lastModified).valueOf()===new Date(l).valueOf())return createCSS(k,a),j.local=!0,b(null,null,g,a,j,h),void 0}removeNode(document.getElementById("less-error-message:"+extractId(h))),g?(f.currentFileInfo=i,new less.Parser(f).parse(g,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,g,a,j,h)}catch(c){b(c,null,null,a)}})):b(e,null,null,a,j,h)},f,e)}function loadFile(a,b,c,d,e){b&&b.currentDirectory&&!/^([a-z-]+:)?\//.test(a)&&(a=b.currentDirectory+a);var f=extractUrlParts(a,window.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(b?(h.entryPath=b.entryPath,h.rootpath=b.rootpath,h.rootFilename=b.rootFilename,h.relativeUrls=b.relativeUrls):(h.entryPath=f.path,h.rootpath=less.rootpath||f.path,h.rootFilename=g,h.relativeUrls=d.relativeUrls),h.relativeUrls&&(h.rootpath=d.rootpath?extractUrlParts(d.rootpath+pathDiff(f.path,h.entryPath)).path:f.path),d.useFileCache&&fileCache[g])try{var i=fileCache[g];e&&(i+="\n"+e),c(null,i,g,h,{lastModified:new Date})}catch(j){c(j,null,g)}else doXHR(g,d.mime,function(a,b){fileCache[g]=a;try{c(null,a,g,h,{lastModified:b})}catch(d){c(d,null,g)}},function(a,b){c({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function extractId(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function createCSS(a,b,c){var d=b.href||"",e="less:"+(b.title||extractId(d)),f=document.getElementById(e),g=!1,h=document.createElement("style");if(h.setAttribute("type","text/css"),b.media&&h.setAttribute("media",b.media),h.id=e,h.styleSheet)try{h.styleSheet.cssText=a}catch(i){throw new Error("Couldn't reassign styleSheet.cssText.")}else h.appendChild(document.createTextNode(a)),g=null!==f&&f.childNodes.length>0&&h.childNodes.length>0&&f.firstChild.nodeValue===h.firstChild.nodeValue;var j=document.getElementsByTagName("head")[0];if(null===f||g===!1){var k=b&&b.nextSibling||null;(k||document.getElementsByTagName("head")[0]).parentNode.insertBefore(h,k)}if(f&&g===!1&&j.removeChild(f),c&&cache){log("saving "+d+" to cache.");try{cache.setItem(d,a),cache.setItem(d+":timestamp",c)}catch(i){log("failed to save")}}}function doXHR(a,b,c,d){function e(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var f=getXMLHttpRequest(),g=isFileProtocol?less.fileAsync:less.async;"function"==typeof f.overrideMimeType&&f.overrideMimeType("text/css"),log("XHR: Getting '"+a+"'"),f.open("GET",a,g),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),isFileProtocol&&!less.fileAsync?0===f.status||f.status>=200&&f.status<300?c(f.responseText):d(f.status,a):g?f.onreadystatechange=function(){4==f.readyState&&e(f,c,d)}:e(f,c,d)}function getXMLHttpRequest(){if(window.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(a){return log("browser doesn't support AJAX."),null}}function removeNode(a){return a&&a.parentNode.removeChild(a)}function log(a){"development"==less.env&&"undefined"!=typeof console&&console.log("less: "+a)}function error(a,b){var c,d,e="less-error-message:"+extractId(b||""),f='
  • {content}
  • ',g=document.createElement("div"),h=[],i=a.filename||b,j=i.match(/([^\/]+(\?.*)?)$/)[1];g.id=e,g.className="less-error-message",d="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+"

    "+'

    in '+j+" ";var k=function(a,b,c){void 0!==a.extract[b]&&h.push(f.replace(/\{line\}/,(parseInt(a.line,10)||0)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.extract?(k(a,0,""),k(a,1,"line"),k(a,2,""),d+="on line "+a.line+", column "+(a.column+1)+":

    "+"
      "+h.join("")+"
    "):a.stack&&(d+="
    "+a.stack.split("\n").slice(1).join("
    ")),g.innerHTML=d,createCSS([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),g.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==less.env&&(c=setInterval(function(){document.body&&(document.getElementById(e)?document.body.replaceChild(g,document.getElementById(e)):document.body.insertBefore(g,document.body.firstChild),clearInterval(c))},10))}var less,tree;void 0===less&&(less=exports,tree=require("./tree"),less.mode="node"),less.Parser=function(a){function b(){r=u[q],s=p,v=p}function c(){u[q]=r,p=s,v=p}function d(){p>v&&(u[q]=u[q].slice(p-v),v=p)}function e(a){var b=a.charCodeAt(0);return 32===b||10===b||9===b}function f(a){var b,c;if(a instanceof Function)return a.call(w.parsers);if("string"==typeof a)b=o.charAt(p)===a?a:null,c=1,d();else{if(d(),!(b=a.exec(u[q])))return null;c=b[0].length}return b?(g(c),"string"==typeof b?b:1===b.length?b[0]:b):void 0}function g(a){for(var b=p,c=q,d=p+u[q].length,f=p+=a;d>p&&e(o.charAt(p));)p++;return u[q]=u[q].slice(a+(p-f)),v=p,0===u[q].length&&q=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function m(a,b,c){var d=c.currentFileInfo.filename;return"browser"!==less.mode&&"rhino"!==less.mode&&(d=require("path").resolve(d)),{lineNumber:l(a,b).line+1,fileName:d}}function n(a,b){var c=k(a,b),d=l(a.index,c),e=d.line,f=d.column,g=a.call&&l(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var o,p,q,r,s,t,u,v,w;a instanceof tree.parseEnv||(a=new tree.parseEnv(a));var x=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d in f.files;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g)};less.Parser.importer?less.Parser.importer(b,c,g,a):less.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return g(b),void 0;var i=new tree.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new less.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}};return n.prototype=new Error,n.prototype.constructor=n,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,w={imports:x,parse:function(b,c){var d,e,g,h=null;if(p=q=v=t=0,o=b.replace(/\r\n/g,"\n"),o=o.replace(/^\uFEFF/,""),w.imports.contents[a.currentFileInfo.filename]=o,u=function(b){for(var c,d,e,f,g=0,i=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,j=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,k=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,l=0,m=b[0],p=0;p0?"missing closing `}`":"missing opening `{`",filename:a.currentFileInfo.filename},a)),b.map(function(a){return a.join("")})}([[]]),h)return c(new n(h,a));try{d=new tree.Ruleset([],f(this.parsers.primary)),d.root=!0,d.firstRoot=!0}catch(i){return c(new n(i,a))}if(d.toCSS=function(b){return function(c,d){c=c||{};var e,f,g=new tree.evalEnv(c);"object"!=typeof d||Array.isArray(d)||(d=Object.keys(d).map(function(a){var b=d[a];return b instanceof tree.Value||(b instanceof tree.Expression||(b=new tree.Expression([b])),b=new tree.Value([b])),new tree.Rule("@"+a,b,!1,null,0)}),g.frames=[new tree.Ruleset(null,d)]);try{e=b.call(this,g),(new tree.joinSelectorVisitor).run(e),(new tree.processExtendsVisitor).run(e),new tree.toCSSVisitor({compress:Boolean(c.compress)}).run(e),c.sourceMap&&(e=new tree.sourceMapOutput({writeSourceMap:c.writeSourceMap,rootNode:e,contentsMap:w.imports.contents,sourceMapFilename:c.sourceMapFilename,outputFilename:c.sourceMapOutputFilename,sourceMapBasepath:c.sourceMapBasepath,sourceMapRootpath:c.sourceMapRootpath,outputSourceFiles:c.outputSourceFiles})),f=e.toCSS({compress:Boolean(c.compress),dumpLineNumbers:a.dumpLineNumbers,strictUnits:Boolean(c.strictUnits)})}catch(h){throw new n(h,a)}return c.cleancss&&"node"===less.mode?require("clean-css").process(f):c.compress?f.replace(/(^(\s)+)|((\s)+$)/g,""):f}}(d.eval),p57||43>b||47===b||44==b))return(a=f(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/))?new tree.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return(a=f(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))?new tree.UnicodeDescriptor(a[0]):void 0},javascript:function(){var b,c,d=p;return"~"===o.charAt(d)&&(d++,c=!0),"`"===o.charAt(d)?(void 0===a.javascriptEnabled||a.javascriptEnabled||i("You are using JavaScript, which has been disabled."),c&&f("~"),(b=f(/^`([^`]*)`/))?new tree.JavaScript(b[1],p,c):void 0):void 0}},variable:function(){var a;return"@"===o.charAt(p)&&(a=f(/^(@[\w-]+)\s*:/))?a[1]:void 0},extend:function(a){var b,c,d,e=p,g=[];if(f(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=[];;){if(d=f(/^(all)(?=\s*(\)|,))/))break;if(c=f(this.element),!c)break;b.push(c)}d=d&&d[1],g.push(new tree.Extend(new tree.Selector(b),d,e))}while(f(","));return h(/^\)/),a&&h(/^;/),g}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var d,e,g,i=[],k=p,l=o.charAt(p),m=!1;if("."===l||"#"===l){for(b();d=f(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);)i.push(new tree.Element(e,d,p,a.currentFileInfo)),e=f(">");return f("(")&&(g=this.mixin.args.call(this,!0).args,h(")")),g=g||[],f(this.important)&&(m=!0),i.length>0&&(f(";")||j("}"))?new tree.mixin.Call(i,g,k,a.currentFileInfo,m):(c(),void 0)}},args:function(a){for(var b,c,d,e,g,j,k=[],l=[],m=[],n={args:null,variadic:!1};;){if(a)j=f(this.expression);else{if(f(this.comments),"."===o.charAt(p)&&f(/^\.{3}/)){n.variadic=!0,f(";")&&!b&&(b=!0),(b?l:m).push({variadic:!0});break}j=f(this.entities.variable)||f(this.entities.literal)||f(this.entities.keyword)}if(!j)break;e=null,j.throwAwayComments&&j.throwAwayComments(),g=j;var q=null;if(a?1==j.value.length&&(q=j.value[0]):q=j,q&&q instanceof tree.Variable)if(f(":"))k.length>0&&(b&&i("Cannot mix ; and , as delimiter types"),c=!0),g=h(this.expression),e=d=q.name;else{if(!a&&f(/^\.{3}/)){n.variadic=!0,f(";")&&!b&&(b=!0),(b?l:m).push({name:j.name,variadic:!0});break}a||(d=e=q.name,g=null)}g&&k.push(g),m.push({name:e,value:g}),f(",")||(f(";")||b)&&(c&&i("Cannot mix ; and , as delimiter types"),b=!0,k.length>1&&(g=new tree.Value(k)),l.push({name:d,value:g}),d=null,k=[],c=!1)}return n.args=b?l:m,n},definition:function(){var a,d,e,g,i=[],k=!1;if(!("."!==o.charAt(p)&&"#"!==o.charAt(p)||j(/^[^{]*\}/))&&(b(),d=f(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/))){a=d[1];var l=this.mixin.args.call(this,!1);if(i=l.args,k=l.variadic,f(")")||(t=p,c()),f(this.comments),f(/^when/)&&(g=h(this.conditions,"expected condition")),e=f(this.block))return new tree.mixin.Definition(a,i,e,g,k);c()}}},entity:function(){return f(this.entities.literal)||f(this.entities.variable)||f(this.entities.url)||f(this.entities.call)||f(this.entities.keyword)||f(this.entities.javascript)||f(this.comment)},end:function(){return f(";")||j("}")},alpha:function(){var a;if(f(/^\(opacity=/i))return(a=f(/^\d+/)||f(this.entities.variable))?(h(")"),new tree.Alpha(a)):void 0},element:function(){var b,c,d;return c=f(this.combinator),b=f(/^(?:\d+\.\d+|\d+)%/)||f(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||f("*")||f("&")||f(this.attribute)||f(/^\([^()@]+\)/)||f(/^[\.#](?=@)/)||f(this.entities.variableCurly),b||f("(")&&(d=f(this.selector))&&f(")")&&(b=new tree.Paren(d)),b?new tree.Element(c,b,p,a.currentFileInfo):void 0},combinator:function(){var a=o.charAt(p);if(">"===a||"+"===a||"~"===a||"|"===a){for(p++;o.charAt(p).match(/\s/);)p++;return new tree.Combinator(a)}return o.charAt(p-1).match(/\s/)?new tree.Combinator(" "):new tree.Combinator(null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,g,j,k=[],l=[];(b&&(e=f(this.extend))||b&&(g=f(/^when/))||(c=f(this.element)))&&(g?j=h(this.conditions,"expected condition"):j?i("CSS guard can only be used at the end of selector"):e?l.push.apply(l,e):(l.length&&i("Extend can only be used at the end of selector"),d=o.charAt(p),k.push(c),c=null),"{"!==d&&"}"!==d&&";"!==d&&","!==d&&")"!==d););return k.length>0?new tree.Selector(k,l,j,p,a.currentFileInfo):(l.length&&i("Extend must be used to extend a selector, it cannot be used on its own"),void 0)},attribute:function(){var a,b,c;if(f("["))return(a=f(this.entities.variableCurly))||(a=h(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),(c=f(/^[|~*$^]?=/))&&(b=f(this.entities.quoted)||f(/^[\w-]+/)||f(this.entities.variableCurly)),h("]"),new tree.Attribute(a,c,b)},block:function(){var a;return f("{")&&(a=f(this.primary))&&f("}")?a:void 0},ruleset:function(){var d,e,g,h=[];for(b(),a.dumpLineNumbers&&(g=m(p,o,a));(d=f(this.lessSelector))&&(h.push(d),f(this.comments),f(","));)d.condition&&i("Guards are only currently allowed on a single selector"),f(this.comments);if(h.length>0&&(e=f(this.block))){var j=new tree.Ruleset(h,e,a.strictImports);return a.dumpLineNumbers&&(j.debugInfo=g),j}t=p,c()},rule:function(d){var e,g,h,i=o.charAt(p),j=!1;if(b(),"."!==i&&"#"!==i&&"&"!==i&&(e=f(this.variable)||f(this.ruleProperty))){if(g=d||!a.compress&&"@"!==e.charAt(0)?f(this.anonymousValue)||f(this.value):f(this.value)||f(this.anonymousValue),h=f(this.important),"+"===e[e.length-1]&&(j=!0,e=e.substr(0,e.length-1)),g&&f(this.end))return new tree.Rule(e,g,h,j,s,a.currentFileInfo);if(t=p,c(),g&&!d)return this.rule(!0)}},anonymousValue:function(){var a;return(a=/^([^@+\/'"*`(;{}-]*);/.exec(u[q]))?(p+=a[0].length-1,new tree.Anonymous(a[1])):void 0},"import":function(){var d,e,g=p;b();var h=f(/^@import?\s+/),i=(h?f(this.importOptions):null)||{};return h&&(d=f(this.entities.quoted)||f(this.entities.url))&&(e=f(this.mediaFeatures),f(";"))?(e=e&&new tree.Value(e),new tree.Import(d,e,i,g,a.currentFileInfo)):(c(),void 0)},importOptions:function(){var a,b,c,d={};if(!f("("))return null;do if(a=f(this.importOption)){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!f(","))break}while(a);return h(")"),d},importOption:function(){var a=f(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=[];do if(b=f(this.entities.keyword))d.push(b);else if(f("(")){if(c=f(this.property),b=f(this.value),!f(")"))return null;if(c&&b)d.push(new tree.Paren(new tree.Rule(c,b,null,null,p,a.currentFileInfo,!0)));else{if(!b)return null;d.push(new tree.Paren(b))}}while(b);return d.length>0?new tree.Expression(d):void 0},mediaFeatures:function(){var a,b=[];do if(a=f(this.mediaFeature)){if(b.push(a),!f(","))break}else if((a=f(this.entities.variable))&&(b.push(a),!f(",")))break;while(a);return b.length>0?b:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=m(p,o,a)),f(/^@media/)&&(b=f(this.mediaFeatures),c=f(this.block))?(d=new tree.Media(c,b,p,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var d,e,g,h,i,j,k;if("@"===o.charAt(p)){if(e=f(this["import"])||f(this.media))return e;if(b(),d=f(/^@[a-z-]+/)){switch(h=d,"-"==d.charAt(1)&&d.indexOf("-",2)>0&&(h="@"+d.slice(d.indexOf("-",2)+1)),h){case"@font-face":i=!0;break;case"@viewport":case"@top-left":case"@top-left-corner":case"@top-center":case"@top-right":case"@top-right-corner":case"@bottom-left":case"@bottom-left-corner":case"@bottom-center":case"@bottom-right":case"@bottom-right-corner":case"@left-top":case"@left-middle":case"@left-bottom":case"@right-top":case"@right-middle":case"@right-bottom":i=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":i=!0,j=!0;break;case"@namespace":k=!0}if(j&&(d+=" "+(f(/^[^{]+/)||"").trim()),i){if(g=f(this.block))return new tree.Directive(d,g,p,a.currentFileInfo)}else if((e=k?f(this.expression):f(this.entity))&&f(";")){var l=new tree.Directive(d,e,p,a.currentFileInfo);return a.dumpLineNumbers&&(l.debugInfo=m(p,o,a)),l}c()}}},value:function(){for(var a,b=[];(a=f(this.expression))&&(b.push(a),f(",")););return b.length>0?new tree.Value(b):void 0},important:function(){return"!"===o.charAt(p)?f(/^! *important/):void 0},sub:function(){var a,b;return f("(")&&(a=f(this.addition))?(b=new tree.Expression([a]),h(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,g;if(a=f(this.operand)){for(g=e(o.charAt(p-1));!j(/^\/[*\/]/)&&(c=f("/")||f("*"))&&(b=f(this.operand));)a.parensInOp=!0,b.parensInOp=!0,d=new tree.Operation(c,[d||a,b],g),g=e(o.charAt(p-1));return d||a}},addition:function(){var a,b,c,d,g;if(a=f(this.multiplication)){for(g=e(o.charAt(p-1));(c=f(/^[-+]\s+/)||!g&&(f("+")||f("-")))&&(b=f(this.multiplication));)a.parensInOp=!0,b.parensInOp=!0,d=new tree.Operation(c,[d||a,b],g),g=e(o.charAt(p-1));return d||a}},conditions:function(){var a,b,c,d=p;if(a=f(this.condition)){for(;f(",")&&(b=f(this.condition));)c=new tree.Condition("or",c||a,b,d);return c||a}},condition:function(){var a,b,c,d,e=p,g=!1;return f(/^not/)&&(g=!0),h("("),(a=f(this.addition)||f(this.entities.keyword)||f(this.entities.quoted))?((d=f(/^(?:>=|=<|[<=>])/))?(b=f(this.addition)||f(this.entities.keyword)||f(this.entities.quoted))?c=new tree.Condition(d,a,b,e,g):i("expected expression"):c=new tree.Condition("=",a,new tree.Keyword("true"),e,g),h(")"),f(/^and/)?new tree.Condition("and",c,f(this.condition)):c):void 0},operand:function(){var a,b=o.charAt(p+1);"-"!==o.charAt(p)||"@"!==b&&"("!==b||(a=f("-"));var c=f(this.sub)||f(this.entities.dimension)||f(this.entities.color)||f(this.entities.variable)||f(this.entities.call);return a&&(c.parensInOp=!0,c=new tree.Negative(c)),c},expression:function(){for(var a,b,c=[];a=f(this.addition)||f(this.entity);)c.push(a),!j(/^\/[\/*]/)&&(b=f("/"))&&c.push(new tree.Anonymous(b));return c.length>0?new tree.Expression(c):void 0},property:function(){var a;return(a=f(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/))?a[1]:void 0},ruleProperty:function(){var a;return(a=f(/^(\*?-?[_a-zA-Z0-9-]+)\s*(\+?)\s*:/))?a[1]+(a[2]||""):void 0}}}},function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b,c){return b instanceof a.Dimension&&b.unit.is("%")?parseFloat(b.value*c/100):d(b)}function d(b){if(b instanceof a.Dimension)return parseFloat(b.unit.is("%")?b.value/100:b.value);if("number"==typeof b)return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function e(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,e,f,g){var h=[b,e,f].map(function(a){return c(a,256)});return g=d(g),new a.Color(h,g)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,f){function g(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?i+6*(h-i)*a:1>2*a?h:2>3*a?i+6*(h-i)*(2/3-a):i}a=d(a)%360/360,b=e(d(b)),c=e(d(c)),f=e(d(f));var h=.5>=c?c*(b+1):c+b-c*b,i=2*c-h;return this.rgba(255*g(a+1/3),255*g(a),255*g(a-1/3),f)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,e){a=360*(d(a)%360/360),b=d(b),c=d(c),e=d(e);var f,g;f=Math.floor(a/60%6),g=a/60-f;var h=[c,c*(1-b),c*(1-g*b),c*(1-(1-g)*b)],i=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*h[i[f][0]],255*h[i[f][1]],255*h[i[f][2]],e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(100*b.toHSL().s),"%")},lightness:function(b){return new a.Dimension(Math.round(100*b.toHSL().l),"%")},hsvhue:function(b){return new a.Dimension(Math.round(b.toHSV().h))},hsvsaturation:function(b){return new a.Dimension(Math.round(100*b.toHSV().s),"%")},hsvvalue:function(b){return new a.Dimension(Math.round(100*b.toHSV().v),"%")},red:function(b){return new a.Dimension(b.rgb[0])},green:function(b){return new a.Dimension(b.rgb[1])},blue:function(b){return new a.Dimension(b.rgb[2])},alpha:function(b){return new a.Dimension(b.toHSL().a)},luma:function(b){return new a.Dimension(Math.round(100*b.luma()*b.alpha),"%")},saturate:function(a,c){if(!a.rgb)return null;var d=a.toHSL();return d.s+=c.value/100,d.s=e(d.s),b(d)},desaturate:function(a,c){var d=a.toHSL();return d.s-=c.value/100,d.s=e(d.s),b(d)},lighten:function(a,c){var d=a.toHSL();return d.l+=c.value/100,d.l=e(d.l),b(d)},darken:function(a,c){var d=a.toHSL();return d.l-=c.value/100,d.l=e(d.l),b(d)},fadein:function(a,c){var d=a.toHSL();return d.a+=c.value/100,d.a=e(d.a),b(d)},fadeout:function(a,c){var d=a.toHSL();return d.a-=c.value/100,d.a=e(d.a),b(d)},fade:function(a,c){var d=a.toHSL();return d.a=c.value/100,d.a=e(d.a),b(d)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=0>e?360+e:e,b(d)},mix:function(b,c,d){d||(d=new a.Dimension(50));var e=d.value/100,f=2*e-1,g=b.toHSL().a-c.toHSL().a,h=((-1==f*g?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},contrast:function(a,b,c,e){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var f=c;c=b,b=f}return e="undefined"==typeof e?.43:d(e),a.luma()*a.alphah.value)&&(j[e]=f)):(k[i]=j.length,j.push(f))):j.push(f);return 1==j.length?j[0]:(c=j.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new a.Anonymous((b?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},argb:function(b){return new a.Anonymous(b.toARGB())},percentage:function(b){return new a.Dimension(100*b.value,"%")},color:function(b){if(b instanceof a.Quoted)return new a.Color(b.value.slice(1));throw{type:"Argument",message:"argument must be a string"}},iscolor:function(b){return this._isa(b,a.Color)},isnumber:function(b){return this._isa(b,a.Dimension)},isstring:function(b){return this._isa(b,a.Quoted)},iskeyword:function(b){return this._isa(b,a.Keyword)},isurl:function(b){return this._isa(b,a.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(b,c){return b instanceof a.Dimension&&b.unit.is(c.value||c)?a.True:a.False},_isa:function(b,c){return b instanceof c?a.True:a.False},multiply:function(a,b){var c=a.rgb[0]*b.rgb[0]/255,d=a.rgb[1]*b.rgb[1]/255,e=a.rgb[2]*b.rgb[2]/255;return this.rgb(c,d,e)},screen:function(a,b){var c=255-(255-a.rgb[0])*(255-b.rgb[0])/255,d=255-(255-a.rgb[1])*(255-b.rgb[1])/255,e=255-(255-a.rgb[2])*(255-b.rgb[2])/255;return this.rgb(c,d,e)},overlay:function(a,b){var c=a.rgb[0]<128?2*a.rgb[0]*b.rgb[0]/255:255-2*(255-a.rgb[0])*(255-b.rgb[0])/255,d=a.rgb[1]<128?2*a.rgb[1]*b.rgb[1]/255:255-2*(255-a.rgb[1])*(255-b.rgb[1])/255,e=a.rgb[2]<128?2*a.rgb[2]*b.rgb[2]/255:255-2*(255-a.rgb[2])*(255-b.rgb[2])/255;return this.rgb(c,d,e)},softlight:function(a,b){var c=b.rgb[0]*a.rgb[0]/255,d=c+a.rgb[0]*(255-(255-a.rgb[0])*(255-b.rgb[0])/255-c)/255;c=b.rgb[1]*a.rgb[1]/255;var e=c+a.rgb[1]*(255-(255-a.rgb[1])*(255-b.rgb[1])/255-c)/255;c=b.rgb[2]*a.rgb[2]/255;var f=c+a.rgb[2]*(255-(255-a.rgb[2])*(255-b.rgb[2])/255-c)/255;return this.rgb(d,e,f)},hardlight:function(a,b){var c=b.rgb[0]<128?2*b.rgb[0]*a.rgb[0]/255:255-2*(255-b.rgb[0])*(255-a.rgb[0])/255,d=b.rgb[1]<128?2*b.rgb[1]*a.rgb[1]/255:255-2*(255-b.rgb[1])*(255-a.rgb[1])/255,e=b.rgb[2]<128?2*b.rgb[2]*a.rgb[2]/255:255-2*(255-b.rgb[2])*(255-a.rgb[2])/255;return this.rgb(c,d,e)},difference:function(a,b){var c=Math.abs(a.rgb[0]-b.rgb[0]),d=Math.abs(a.rgb[1]-b.rgb[1]),e=Math.abs(a.rgb[2]-b.rgb[2]);return this.rgb(c,d,e)},exclusion:function(a,b){var c=a.rgb[0]+b.rgb[0]*(255-a.rgb[0]-a.rgb[0])/255,d=a.rgb[1]+b.rgb[1]*(255-a.rgb[1]-a.rgb[1])/255,e=a.rgb[2]+b.rgb[2]*(255-a.rgb[2]-a.rgb[2])/255; +return this.rgb(c,d,e)},average:function(a,b){var c=(a.rgb[0]+b.rgb[0])/2,d=(a.rgb[1]+b.rgb[1])/2,e=(a.rgb[2]+b.rgb[2])/2;return this.rgb(c,d,e)},negation:function(a,b){var c=255-Math.abs(255-b.rgb[0]-a.rgb[0]),d=255-Math.abs(255-b.rgb[1]-a.rgb[1]),e=255-Math.abs(255-b.rgb[2]-a.rgb[2]);return this.rgb(c,d,e)},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,a.value[b]},"data-uri":function(b,c){if("undefined"!=typeof window)return new a.URL(c||b,this.currentFileInfo).eval(this.env);var d=b.value,e=c&&c.value,f=require("fs"),g=require("path"),h=!1;if(arguments.length<2&&(e=d),this.env.isPathRelative(e)&&(e=this.currentFileInfo.relativeUrls?g.join(this.currentFileInfo.currentDirectory,e):g.join(this.currentFileInfo.entryPath,e)),arguments.length<2){var i;try{i=require("mime")}catch(j){i=a._mime}d=i.lookup(e);var k=i.charsets.lookup(d);h=["US-ASCII","UTF-8"].indexOf(k)<0,h&&(d+=";base64")}else h=/;base64$/.test(d);var l=f.readFileSync(e),m=32,n=parseInt(l.length/1024,10);if(n>=m){if(this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",e,n,m),new a.URL(c||b,this.currentFileInfo).eval(this.env);this.env.silent||console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!",e,n,m)}l=h?l.toString("base64"):encodeURIComponent(l);var o="'data:"+d+","+l+"'";return new a.URL(new a.Anonymous(o))},"svg-gradient":function(b){function c(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&c();var d,e,f,g,h,i,j,k=Array.prototype.slice.call(arguments,1),l="linear",m='x="0" y="0" width="1" height="1"',n=!0,o={compress:!1},p=b.toCSS(o);switch(p){case"to bottom":d='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":d='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":d='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":d='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":l="radial",d='cx="50%" cy="50%" r="75%"',m='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(e='<'+l+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+d+">",f=0;fj?' stop-opacity="'+j+'"':"")+"/>";if(e+=""+"',n)try{e=new Buffer(e).toString("base64")}catch(q){n=!1}return e="'data:image/svg+xml"+(n?";base64":"")+","+e+"'",new a.URL(new a.Anonymous(e))}},a._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(b){var c=require("path").extname(b),d=a._mime._types[c];if(void 0===d)throw new Error('Optional dependency "mime" is required for '+c);return d},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};for(var f=[{name:"ceil"},{name:"floor"},{name:"sqrt"},{name:"abs"},{name:"tan",unit:""},{name:"sin",unit:""},{name:"cos",unit:""},{name:"atan",unit:"rad"},{name:"asin",unit:"rad"},{name:"acos",unit:"rad"}],g=function(a,b){return function(c){return null!=b&&(c=c.unify()),this._math(Math[a],b,c)}},h=0;hb?-1:1},genCSS:function(a,b){b.add(this.value)},toCSS:a.toCSS}}(require("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(require("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args=a.visit(this.args)},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;cf;f++)e[f]=a.operate(b,c,this.rgb[f],d.rgb[f]);return new a.Color(e,this.alpha+d.alpha)},toRGB:function(){return"#"+this.rgb.map(function(a){return a=Math.round(a),a=(a>255?255:0>a?0:a).toString(16),1===a.length?"0"+a:a}).join("")},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){var a=[Math.round(255*this.alpha)].concat(this.rgb);return"#"+a.map(function(a){return a=Math.round(a),a=(a>255?255:0>a?0:a).toString(16),1===a.length?"0"+a:a}).join("")},compare:function(a){return a.rgb?a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1:-1}}}(require("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(require("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a;case 0:return"="===a||">="===a||"=<"===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(require("../tree")),function(a){a.Dimension=function(b,c){this.value=parseFloat(b),this.unit=c&&c instanceof a.Unit?c:new a.Unit(c?[c]:void 0)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(a,b){if(a&&a.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var c=this.value,d=String(c);if(0!==c&&1e-6>c&&c>-1e-6&&(d=c.toFixed(20).replace(/0+$/,"")),a&&a.compress){if(0===c&&!this.unit.isAngle())return b.add(d),void 0;c>0&&1>c&&(d=d.substr(1))}b.add(d),this.unit.genCSS(a,b)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c=this.unify(),d=b.unify(),e=c.value,f=d.value;return f>e?-1:e>f?1:d.unit.isEmpty()||0===c.unit.compare(d.unit)?0:-1}return-1},unify:function(){return this.convertTo({length:"m",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,pt:.0254/72,pc:12*(.0254/72)},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(require("../tree")),function(a){a.Directive=function(b,c,d,e){this.name=b,Array.isArray(c)?(this.rules=[new a.Ruleset([],c)],this.rules[0].allowImports=!0):this.value=c,this.currentFileInfo=e},a.Directive.prototype={type:"Directive",accept:function(a){this.rules=a.visit(this.rules),this.value=a.visit(this.value)},genCSS:function(b,c){c.add(this.name,this.currentFileInfo,this.index),this.rules?a.outputRuleset(b,c,this.rules):(c.add(" "),this.value.genCSS(b,c),c.add(";"))},toCSS:a.toCSS,eval:function(b){var c=this;return this.rules&&(b.frames.unshift(this),c=new a.Directive(this.name,null,this.index,this.currentFileInfo),c.rules=[this.rules[0].eval(b)],c.rules[0].root=!0,b.frames.shift()),c},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules[0].rules,a=0;a":" > ","|":"|"},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(require("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value=a.visit(this.value)},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gthis.params.length)return!1;if(this.required>0&&d>this.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(require("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(require("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c,d=this.operands[0].eval(b),e=this.operands[1].eval(b);if(b.isMathOn()){if(d instanceof a.Dimension&&e instanceof a.Color){if("*"!==this.op&&"+"!==this.op)throw{type:"Operation",message:"Can't substract or divide a color from a number"};c=e,e=d,d=c}if(!d.operate)throw{type:"Operation",message:"Operation on an invalid type"};return d.operate(b,this.op,e)}return new a.Operation(this.op,[d,e],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(require("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(require("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index) +},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1}}}(require("../tree")),function(a){a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable="@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(b){var c=!1;"font"!==this.name||b.strictMath||(c=!0,b.strictMath=!0);try{return new a.Rule(this.name,this.value.eval(b),this.important,this.merge,this.index,this.currentFileInfo,this.inline)}finally{c&&(b.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(require("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){if(this.paths)for(var b=0;bf.selectors[g].elements.length?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(1)),c)):e.push(f);break}}),this._lookups[f]=e)},genCSS:function(b,c){var d,e,f,g,h,i=[],j=[],k=!0;b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l=b.compress?"":Array(b.tabLevel+1).join(" "),m=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;d0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",0,j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(require("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b||[],this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements=a.visit(this.elements),this.extendList=a.visit(this.extendList),this.condition=a.visit(this.condition)},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,this.condition,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e},match:function(a){var b,c,d,e,f=this.elements,g=f.length;if(b=a.elements.slice(a.elements.length&&"&"===a.elements[0].value?1:0),c=b.length,d=Math.min(g,c),0===c||c>g)return!1;for(e=0;d>e;e++)if(f[e].value!==b[e].value)return!1;return!0},eval:function(a){var b=this.condition&&this.condition.eval(a);return this.createDerived(this.elements.map(function(b){return b.eval(a)}),this.extendList.map(function(b){return b.eval(a)}),b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)}}),b.join("")},a.outputRuleset=function(a,b,c){b.add(a.compress?"{":" {\n"),a.tabLevel=(a.tabLevel||0)+1;for(var d=a.compress?"":Array(a.tabLevel+1).join(" "),e=a.compress?"":Array(a.tabLevel).join(" "),f=0;f0)&&e.splice(0,0,b);else{b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;c0&&b.accept(this._visitor),c.visitDeeper=!1,this._mergeRules(b.rules),this._removeDuplicateRules(b.rules),b.rules.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d},_mergeRules:function(b){for(var c,d,e,f={},g=0;g1&&(d=c[0],d.value=new a.Value(c.map(function(a){return a.value})))})}}}(require("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[];for(c=0;c100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},inInheritanceChain:function(a,b){if(a===b)return!0;if(b.parents){if(this.inInheritanceChain(a,b.parents[0]))return!0;if(this.inInheritanceChain(a,b.parents[1]))return!0}return!1},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1i&&j>0&&(k[k.length-1].elements=k[k.length-1].elements.concat(c[i].elements.slice(j)),j=0,i++),k=k.concat(c.slice(i,h.pathIndex)),k.push(new a.Selector(f.elements.slice(j,h.index).concat([g]).concat(d.elements.slice(1)))),i=h.endPathIndex,j=h.endPathElementIndex,j>=f.elements.length&&(j=0,i++);return i0&&(k[k.length-1].elements=k[k.length-1].elements.concat(c[i].elements.slice(j)),j=0,i++),k=k.concat(c.slice(i,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(require("./tree"));var isFileProtocol=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);if(less.env=less.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port.length>0||isFileProtocol?"development":"production"),less.async=less.async||!1,less.fileAsync=less.fileAsync||!1,less.poll=less.poll||(isFileProtocol?1e3:1500),less.functions)for(var func in less.functions)less.tree.functions[func]=less.functions[func];var dumpLineNumbers=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);dumpLineNumbers&&(less.dumpLineNumbers=dumpLineNumbers[1]),less.watch=function(){return less.watchMode||(less.env="development",initRunningMode()),this.watchMode=!0},less.unwatch=function(){return clearInterval(less.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&less.watch();var cache=null;if("development"!=less.env)try{cache="undefined"==typeof window.localStorage?null:window.localStorage}catch(_){}var links=document.getElementsByTagName("link"),typePattern=/^text\/(x-)?less$/;less.sheets=[];for(var i=0;i +<% +var generateScriptsTasgs = function(allScripts) { + allScripts.forEach(function(script){ %> +<% }); +}; +%> + + + + Jasmine Spec Runner + + +<% + scripts.src.forEach(function(fullLessName) { + var pathParts = fullLessName.split('/'), + fullCssName = fullLessName.replace(/less/g, 'css'), + lessName = pathParts[pathParts.length-1], + name = lessName.split('.')[0]; + %> + + <% + }); +%> + + +<% css.forEach(function(style){ %> + +<% }) %> + +<% generateScriptsTasgs([].concat(scripts.polyfills, scripts.jasmine)); %> + +<% generateScriptsTasgs(scripts.vendor); +%> +<% generateScriptsTasgs([].concat(scripts.helpers, scripts.specs)); %> + +<% generateScriptsTasgs([].concat(scripts.reporters, scripts.start)); %> + + + + From 744eb355f022f29608aa60ab242554cc8bd30bba Mon Sep 17 00:00:00 2001 From: meri Date: Sat, 3 Aug 2013 16:27:49 +0200 Subject: [PATCH 15/36] Undo meaningless change. --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 020e1bec..ac3cbfe2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -47,7 +47,7 @@ module.exports = function(grunt) { }, // Browser versions browser: { - src: ['<%= !build.browser %>'], + src: ['<%= build.browser %>'], dest: 'test/browser/less.js' }, alpha: { From 81d532495c189defd8b268ef2ea21d2308acfc60 Mon Sep 17 00:00:00 2001 From: meri Date: Sat, 3 Aug 2013 19:20:45 +0200 Subject: [PATCH 16/36] Removed obsolete option. --- Gruntfile.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index ac3cbfe2..48a8e22c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -139,11 +139,7 @@ module.exports = function(grunt) { src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less'], options: { specs: 'test/browser/runner-main.js', - outfile: 'test/browser/test-runner-main.html', - templateOptions: { - originalLess: '', - expectedCss: '' - } + outfile: 'test/browser/test-runner-main.html' } } }, From ac8cff4a6a51e256b87e674af6645514530bb26c Mon Sep 17 00:00:00 2001 From: meri Date: Sat, 3 Aug 2013 19:23:51 +0200 Subject: [PATCH 17/36] Clean up --- Gruntfile.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 48a8e22c..10f0d600 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -119,9 +119,6 @@ module.exports = function(grunt) { server: { options: { port: 8081 - // grunt-contrib-jasmine assumes that web-server runs in root directory - // it 's outfile is relativized against root - //base: 'test' } } }, @@ -134,7 +131,6 @@ module.exports = function(grunt) { template: 'test/browser/test-runner-template.tmpl' }, main: { - //TODO meri: find better location for less.js - reference can go to template and compiled browser to dist //src is used to build list of less files to compile src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less'], options: { From 7217ad767b0cd8787e61ee72c156a4f27b1af620 Mon Sep 17 00:00:00 2001 From: meri Date: Sat, 3 Aug 2013 19:45:54 +0200 Subject: [PATCH 18/36] Added browser header reference. --- build/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/build.yml b/build/build.yml index 40c6dea9..32895227 100644 --- a/build/build.yml +++ b/build/build.yml @@ -19,7 +19,7 @@ lib: lib/less # General # ================================= prepend: - browser: build/require.js + browser: [build/require.js, build/browser-header.js] rhino: build/require-rhino.js append: @@ -143,4 +143,4 @@ tree: - <%= build.lib %>/tree/unicode-descriptor.js - <%= build.lib %>/tree/url.js - <%= build.lib %>/tree/value.js - - <%= build.lib %>/tree/variable.js \ No newline at end of file + - <%= build.lib %>/tree/variable.js From 3765bb2926285e374aa7d8eb9633bb696aaf014c Mon Sep 17 00:00:00 2001 From: meri Date: Mon, 5 Aug 2013 11:32:06 +0200 Subject: [PATCH 19/36] Did legacy tests and working on errors. --- Gruntfile.js | 23 +++++++++++++++-- test/browser/common.js | 25 ++++++++++++++++--- test/browser/runner-errors-options.js | 3 +++ ...runner-errors.js => runner-errors-spec.js} | 4 +-- test/browser/runner-legacy-options.js | 4 +++ test/browser/runner-legacy-spec.js | 3 +++ test/browser/runner-main-options.js | 13 ++++++++++ test/browser/runner-main-spec.js | 3 +++ test/browser/test-runner-template.tmpl | 16 ++++++------ 9 files changed, 77 insertions(+), 17 deletions(-) create mode 100644 test/browser/runner-errors-options.js rename test/browser/{runner-errors.js => runner-errors-spec.js} (72%) create mode 100644 test/browser/runner-legacy-options.js create mode 100644 test/browser/runner-legacy-spec.js create mode 100644 test/browser/runner-main-options.js create mode 100644 test/browser/runner-main-spec.js diff --git a/Gruntfile.js b/Gruntfile.js index 10f0d600..2bd71bb7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -127,18 +127,36 @@ module.exports = function(grunt) { options: { keepRunner: true, //TODO meri: remove after it is done host: 'http://localhost:8081/', - helpers: 'test/browser/common.js', + vendor: 'test/browser/common.js', template: 'test/browser/test-runner-template.tmpl' }, main: { //src is used to build list of less files to compile src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less'], options: { - specs: 'test/browser/runner-main.js', + helpers: 'test/browser/runner-main-options.js', + specs: 'test/browser/runner-main-spec.js', outfile: 'test/browser/test-runner-main.html' } + }, + legacy: { + src: ['test/less/legacy/*.less'], + options: { + helpers: 'test/browser/runner-legacy-options.js', + specs: 'test/browser/runner-legacy-spec.js', + outfile: 'test/browser/test-runner-legacy.html' + } + }, + errors: { + src: ['test/less/errors/*.less', '!test/less/errors/javascript-error.less'], + options: { + helpers: 'test/browser/runner-errors-options.js', + specs: 'test/browser/runner-errors-spec.js', + outfile: 'test/browser/test-runner-errors.html' + } } }, + // Before running tests, clean out the results // of any previous tests. (this will need to be // setup based on configuration of browser tests. @@ -189,6 +207,7 @@ module.exports = function(grunt) { // Run all tests grunt.registerTask('browserTest', [ 'connect:server', + 'jasmine' ]); // Run all tests diff --git a/test/browser/common.js b/test/browser/common.js index 45b4d09c..246ab0b7 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -1,7 +1,15 @@ -/*if not async then phantomjs fails to run the webserver and the test concurrently*/ -var less = { async: true, strictMath: true }; - /* record log messages for testing */ +var logAllIds = function() { + var allTags = document.head.getElementsByTagName('style'); + var ids = []; + for (var tg = 0; tg< allTags.length; tg++) { + var tag = allTags[tg]; + if (tag.id) { + console.log(tag.id); + } + } +}; + var logMessages = [], realConsoleLog = console.log; console.log = function(msg) { @@ -48,10 +56,19 @@ var testSheet = function(sheet) { }); }; +function extractId(href) { + return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain + .replace(/^\//, '' ) // Remove root / + .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension + .replace(/[^\.\w-]+/g, '-') // Replace illegal characters + .replace(/\./g, ':'); // Replace dots with colons(for valid id) +} + var testErrorSheet = function(sheet) { it(sheet.id + " should match an error", function() { var lessHref = sheet.href, - id = sheet.id.replace(/^original-less:/, "less-error-message:"), + id = "less-error-message:"+extractId(lessHref), +// id = sheet.id.replace(/^original-less:/, "less-error-message:"), errorHref = lessHref.replace(/.less$/, ".txt"), errorFile = loadFile(errorHref), actualErrorElement = document.getElementById(id), diff --git a/test/browser/runner-errors-options.js b/test/browser/runner-errors-options.js new file mode 100644 index 00000000..acbcdf96 --- /dev/null +++ b/test/browser/runner-errors-options.js @@ -0,0 +1,3 @@ +var less = {}; +less.strictUnits = true; + diff --git a/test/browser/runner-errors.js b/test/browser/runner-errors-spec.js similarity index 72% rename from test/browser/runner-errors.js rename to test/browser/runner-errors-spec.js index 93994a57..9d68d04a 100644 --- a/test/browser/runner-errors.js +++ b/test/browser/runner-errors-spec.js @@ -1,5 +1,3 @@ -less.strictUnits = true; - describe("less.js error tests", function() { testLessErrorsInDocument(); -}); \ No newline at end of file +}); diff --git a/test/browser/runner-legacy-options.js b/test/browser/runner-legacy-options.js new file mode 100644 index 00000000..9e4b9592 --- /dev/null +++ b/test/browser/runner-legacy-options.js @@ -0,0 +1,4 @@ +var less = {}; +less.strictMath = false; +less.strictUnits = false; + diff --git a/test/browser/runner-legacy-spec.js b/test/browser/runner-legacy-spec.js new file mode 100644 index 00000000..6ba7bfae --- /dev/null +++ b/test/browser/runner-legacy-spec.js @@ -0,0 +1,3 @@ +describe("less.js legacy tests", function() { + testLessEqualsInDocument(); +}); diff --git a/test/browser/runner-main-options.js b/test/browser/runner-main-options.js new file mode 100644 index 00000000..d43b630f --- /dev/null +++ b/test/browser/runner-main-options.js @@ -0,0 +1,13 @@ +var less = {}; +less.functions = { + add: function (a, b) { + return new(less.tree.Dimension)(a.value + b.value); + }, + increment: function (a) { + return new(less.tree.Dimension)(a.value + 1); + }, + _color: function (str) { + if (str.value === "evil red") { return new(less.tree.Color)("600") } + } +}; + diff --git a/test/browser/runner-main-spec.js b/test/browser/runner-main-spec.js new file mode 100644 index 00000000..b5261e75 --- /dev/null +++ b/test/browser/runner-main-spec.js @@ -0,0 +1,3 @@ +describe("less.js main tests", function() { + testLessEqualsInDocument(); +}); diff --git a/test/browser/test-runner-template.tmpl b/test/browser/test-runner-template.tmpl index 0deb04e9..78ab6d0b 100644 --- a/test/browser/test-runner-template.tmpl +++ b/test/browser/test-runner-template.tmpl @@ -18,22 +18,22 @@ var generateScriptsTasgs = function(allScripts) { lessName = pathParts[pathParts.length-1], name = lessName.split('.')[0]; %> - + <% }); %> - + <% css.forEach(function(style){ %> <% }) %> - + <% generateScriptsTasgs([].concat(scripts.polyfills, scripts.jasmine)); %> - -<% generateScriptsTasgs(scripts.vendor); -%> -<% generateScriptsTasgs([].concat(scripts.helpers, scripts.specs)); %> - + +<% generateScriptsTasgs([].concat(scripts.vendor, scripts.helpers)); +%> +<% generateScriptsTasgs(scripts.specs); %> + <% generateScriptsTasgs([].concat(scripts.reporters, scripts.start)); %> From 1ea0416adbcdc57a9c071a632a858ab2eee363f6 Mon Sep 17 00:00:00 2001 From: meri Date: Mon, 5 Aug 2013 15:58:04 +0200 Subject: [PATCH 20/36] Added wait into error tests. --- Gruntfile.js | 8 ++++++++ test/browser/common.js | 14 ++++++++++++-- test/browser/runner-errors-spec.js | 5 +++-- test/browser/runner-no-js-errors-options.js | 4 ++++ test/browser/runner-no-js-errors-spec.js | 4 ++++ 5 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 test/browser/runner-no-js-errors-options.js create mode 100644 test/browser/runner-no-js-errors-spec.js diff --git a/Gruntfile.js b/Gruntfile.js index 2bd71bb7..59b97870 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -154,6 +154,14 @@ module.exports = function(grunt) { specs: 'test/browser/runner-errors-spec.js', outfile: 'test/browser/test-runner-errors.html' } + }, + noJsErrors: { + src: ['test/less/no-js-errors/*.less'], + options: { + helpers: 'test/browser/runner-no-js-errors-options.js', + specs: 'test/browser/runner-no-js-errors-spec.js', + outfile: 'test/browser/test-runner-no-js-errors.html' + } } }, diff --git a/test/browser/common.js b/test/browser/common.js index 246ab0b7..8d978373 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -56,6 +56,7 @@ var testSheet = function(sheet) { }); }; +//TODO: do it cleaner - the same way as in css function extractId(href) { return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain .replace(/^\//, '' ) // Remove root / @@ -71,17 +72,26 @@ var testErrorSheet = function(sheet) { // id = sheet.id.replace(/^original-less:/, "less-error-message:"), errorHref = lessHref.replace(/.less$/, ".txt"), errorFile = loadFile(errorHref), - actualErrorElement = document.getElementById(id), + actualErrorElement, actualErrorMsg; + // Less.js sets 10ms timer in order to add error message on top of page. + waitsFor(function() { + actualErrorElement = document.getElementById(id); + return actualErrorElement!==null; + }, "failed to load expected outout", 70); + + describe("the error", function() { expect(actualErrorElement).not.toBe(null); }); - actualErrorMsg = actualErrorElement.innerText + runs(function() { + actualErrorMsg = actualErrorElement.innerText .replace(/\n\d+/g, function(lineNo) { return lineNo + " "; }) .replace(/\n\s*in /g, " in ") .replace("\n\n", "\n"); + }); waitsFor(function() { return errorFile.loaded; diff --git a/test/browser/runner-errors-spec.js b/test/browser/runner-errors-spec.js index 9d68d04a..8bb8c6b7 100644 --- a/test/browser/runner-errors-spec.js +++ b/test/browser/runner-errors-spec.js @@ -1,3 +1,4 @@ describe("less.js error tests", function() { - testLessErrorsInDocument(); -}); + testLessErrorsInDocument(); +}) + diff --git a/test/browser/runner-no-js-errors-options.js b/test/browser/runner-no-js-errors-options.js new file mode 100644 index 00000000..2a464944 --- /dev/null +++ b/test/browser/runner-no-js-errors-options.js @@ -0,0 +1,4 @@ +var less = {}; + +less.strictUnits = true; +less.javascriptEnabled = false; diff --git a/test/browser/runner-no-js-errors-spec.js b/test/browser/runner-no-js-errors-spec.js new file mode 100644 index 00000000..38aefe29 --- /dev/null +++ b/test/browser/runner-no-js-errors-spec.js @@ -0,0 +1,4 @@ +describe("less.js javascript disabled error tests", function() { + testLessErrorsInDocument(); +}); + From c1e1edc0c3259947dbcd7955a7f864857441909d Mon Sep 17 00:00:00 2001 From: meri Date: Mon, 5 Aug 2013 15:59:29 +0200 Subject: [PATCH 21/36] Changed error message. --- test/browser/common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/browser/common.js b/test/browser/common.js index 8d978373..758e957b 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -79,7 +79,7 @@ var testErrorSheet = function(sheet) { waitsFor(function() { actualErrorElement = document.getElementById(id); return actualErrorElement!==null; - }, "failed to load expected outout", 70); + }, "error message was not generated", 70); describe("the error", function() { From 38d72f10300c8f2f6d2f1921df1a520e03b1c492 Mon Sep 17 00:00:00 2001 From: meri Date: Mon, 5 Aug 2013 16:11:36 +0200 Subject: [PATCH 22/36] Removed dead code. --- test/browser/common.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/browser/common.js b/test/browser/common.js index 758e957b..4b2df350 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -81,11 +81,6 @@ var testErrorSheet = function(sheet) { return actualErrorElement!==null; }, "error message was not generated", 70); - - describe("the error", function() { - expect(actualErrorElement).not.toBe(null); - }); - runs(function() { actualErrorMsg = actualErrorElement.innerText .replace(/\n\d+/g, function(lineNo) { return lineNo + " "; }) From e41d32184e4075052d7b5ff315cf65867ec4aa7c Mon Sep 17 00:00:00 2001 From: meri Date: Tue, 6 Aug 2013 16:45:59 +0200 Subject: [PATCH 23/36] Converted browser/browser tests into jasmine. --- Gruntfile.js | 8 ++++++ test/browser/common.js | 14 ++++++++- test/browser/css/urls.css | 27 ++++++++--------- test/browser/less/urls.less | 2 +- test/browser/runner-browser-options.js | 40 ++++++++++++++++++++++++++ test/browser/runner-browser-spec.js | 12 ++++++++ 6 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 test/browser/runner-browser-options.js create mode 100644 test/browser/runner-browser-spec.js diff --git a/Gruntfile.js b/Gruntfile.js index 59b97870..0cc862a0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -162,6 +162,14 @@ module.exports = function(grunt) { specs: 'test/browser/runner-no-js-errors-spec.js', outfile: 'test/browser/test-runner-no-js-errors.html' } + }, + browser: { + src: ['test/browser/less/*.less'], + options: { + helpers: 'test/browser/runner-browser-options.js', + specs: 'test/browser/runner-browser-spec.js', + outfile: 'test/browser/test-runner-browser.html' + } } }, diff --git a/test/browser/common.js b/test/browser/common.js index 4b2df350..1e144582 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -41,11 +41,23 @@ var testSheet = function(sheet) { it(sheet.id + " should match the expected output", function() { var lessOutputId = sheet.id.replace("original-", ""), expectedOutputId = "expected-" + lessOutputId, - lessOutput = document.getElementById(lessOutputId).innerText, + lessOutputObj, + lessOutput, expectedOutputHref = document.getElementById(expectedOutputId).href, expectedOutput = loadFile(expectedOutputHref); + // Browser spec generates less on the fly, so we need to loose control waitsFor(function() { + lessOutputObj = document.getElementById(lessOutputId); + // the type condition is necessary because of inline browser tests + return lessOutputObj!==null && lessOutputObj.type==="text/css"; + }, "generation of " + lessOutputId + "", 700); + + runs(function() { + lessOutput = lessOutputObj.innerText; + }); + + waitsFor(function() { return expectedOutput.loaded; }, "failed to load expected outout", 10000); diff --git a/test/browser/css/urls.css b/test/browser/css/urls.css index 7001990e..7757cada 100644 --- a/test/browser/css/urls.css +++ b/test/browser/css/urls.css @@ -1,20 +1,21 @@ -@import "http://localhost:8081/browser/less/modify-this.css"; -@import "http://localhost:8081/browser/less/modify-again.css"; +@import "http://localhost:8081/test/browser/less/modify-this.css"; + +@import "http://localhost:8081/test/browser/less/modify-again.css"; .modify { - my-url: url("http://localhost:8081/browser/less/a.png"); + my-url: url("http://localhost:8081/test/browser/less/a.png"); } .modify { - my-url: url("http://localhost:8081/browser/less/b.png"); + my-url: url("http://localhost:8081/test/browser/less/b.png"); } @font-face { src: url("/fonts/garamond-pro.ttf"); - src: local(Futura-Medium), url(http://localhost:8081/browser/less/fonts.svg#MyGeometricModern) format("svg"); + src: local(Futura-Medium), url(http://localhost:8081/test/browser/less/fonts.svg#MyGeometricModern) format("svg"); } #shorthands { background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; } #misc { - background-image: url(http://localhost:8081/browser/less/images/image.jpg); + background-image: url(http://localhost:8081/test/browser/less/images/image.jpg); } #data-uri { background: url(data:image/png;charset=utf-8;base64, @@ -28,23 +29,23 @@ background: transparent url('data:image/svg+xml, '); } .comma-delimited { - 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); + background: url(http://localhost:8081/test/browser/less/bg.jpg) no-repeat, url(http://localhost:8081/test/browser/less/bg.png) repeat-x top left, url(http://localhost:8081/test/browser/less/bg); } .values { - url: url('http://localhost:8081/browser/less/Trebuchet'); + url: url('http://localhost:8081/test/browser/less/Trebuchet'); } #data-uri { - uri: url('http://localhost:8081/browser/less/../../data/image.jpg'); + uri: url('http://localhost:8081/test/browser/less/../../data/image.jpg'); } #data-uri-guess { - uri: url('http://localhost:8081/browser/less/../../data/image.jpg'); + uri: url('http://localhost:8081/test/browser/less/../../data/image.jpg'); } #data-uri-ascii { - uri-1: url('http://localhost:8081/browser/less/../../data/page.html'); - uri-2: url('http://localhost:8081/browser/less/../../data/page.html'); + uri-1: url('http://localhost:8081/test/browser/less/../../data/page.html'); + uri-2: url('http://localhost:8081/test/browser/less/../../data/page.html'); } #data-uri-toobig { - uri: url('http://localhost:8081/browser/less/../../data/data-uri-fail.png'); + uri: url('http://localhost:8081/test/browser/less/../../data/data-uri-fail.png'); } #svg-functions { background-image: url('data:image/svg+xml,'); diff --git a/test/browser/less/urls.less b/test/browser/less/urls.less index e640693b..596e5e7b 100644 --- a/test/browser/less/urls.less +++ b/test/browser/less/urls.less @@ -1,5 +1,5 @@ @import "imports/urls.less"; -@import "http://localhost:8081/browser/less/imports/urls2.less"; +@import "http://localhost:8081/test/browser/less/imports/urls2.less"; @font-face { src: url("/fonts/garamond-pro.ttf"); src: local(Futura-Medium), diff --git a/test/browser/runner-browser-options.js b/test/browser/runner-browser-options.js new file mode 100644 index 00000000..ccc9b587 --- /dev/null +++ b/test/browser/runner-browser-options.js @@ -0,0 +1,40 @@ +var less = {}; + +// There originally run inside describe method. However, since they have not +// been inside it, they run at jasmine compile time (not runtime). It all +// worked cause less.js was in async mode and custom phantom runner had +// different setup then grunt-contrib-jasmine. They have been created before +// less.js run, even as they have been defined in spec. + +// test inline less in style tags by grabbing an assortment of less files and doing `@import`s +var testFiles = ['charsets', 'colors', 'comments', 'css-3', 'strings', 'media', 'mixins'], + testSheets = []; + + // setup style tags with less and link tags pointing to expected css output + for (var i = 0; i < testFiles.length; i++) { + var file = testFiles[i], + lessPath = '/test/less/' + file + '.less', + cssPath = '/test/css/' + file + '.css', + lessStyle = document.createElement('style'), + cssLink = document.createElement('link'), + lessText = '@import "' + lessPath + '";'; + lessStyle.type = 'text/less'; + lessStyle.id = file; + lessStyle.href = file; + if (lessStyle.styleSheet) { + lessStyle.styleSheet.cssText = lessText; + } else { + lessStyle.innerHTML = lessText; + } + cssLink.rel = 'stylesheet'; + cssLink.type = 'text/css'; + cssLink.href = cssPath; + cssLink.id = 'expected-' + file; + + var head = document.getElementsByTagName('head')[0]; + head.appendChild(lessStyle); + head.appendChild(cssLink); + testSheets[i] = lessStyle; + } + + diff --git a/test/browser/runner-browser-spec.js b/test/browser/runner-browser-spec.js new file mode 100644 index 00000000..ead27b4a --- /dev/null +++ b/test/browser/runner-browser-spec.js @@ -0,0 +1,12 @@ +describe("less.js browser behaviour", function() { + testLessEqualsInDocument(); + + it("has some log messages", function() { + expect(logMessages.length).toBeGreaterThan(0); + }); + + for (var i = 0; i < testFiles.length; i++) { + var sheet = testSheets[i]; + testSheet(sheet); + } +}); From 58f35b4cd1d033f78bca3590488ef0d4a9953cd7 Mon Sep 17 00:00:00 2001 From: meri Date: Wed, 7 Aug 2013 09:17:19 +0200 Subject: [PATCH 24/36] Added relative urls tests. --- Gruntfile.js | 36 ++++++++++++-------- test/browser/css/relative-urls/urls.css | 16 ++++----- test/browser/less/relative-urls/urls.less | 2 +- test/browser/runner-relative-urls-options.js | 3 ++ test/browser/runner-relative-urls-spec.js | 3 ++ 5 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 test/browser/runner-relative-urls-options.js create mode 100644 test/browser/runner-relative-urls-spec.js diff --git a/Gruntfile.js b/Gruntfile.js index 0cc862a0..f9fccbb7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -126,50 +126,58 @@ module.exports = function(grunt) { jasmine: { options: { keepRunner: true, //TODO meri: remove after it is done - host: 'http://localhost:8081/', - vendor: 'test/browser/common.js', - template: 'test/browser/test-runner-template.tmpl' + host: 'http://localhost:8081/', + vendor: 'test/browser/common.js', + template: 'test/browser/test-runner-template.tmpl' }, main: { - //src is used to build list of less files to compile + //src is used to build list of less files to compile src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less'], - options: { - helpers: 'test/browser/runner-main-options.js', + options: { + helpers: 'test/browser/runner-main-options.js', specs: 'test/browser/runner-main-spec.js', outfile: 'test/browser/test-runner-main.html' } }, legacy: { src: ['test/less/legacy/*.less'], - options: { - helpers: 'test/browser/runner-legacy-options.js', + options: { + helpers: 'test/browser/runner-legacy-options.js', specs: 'test/browser/runner-legacy-spec.js', outfile: 'test/browser/test-runner-legacy.html' } }, errors: { src: ['test/less/errors/*.less', '!test/less/errors/javascript-error.less'], - options: { - helpers: 'test/browser/runner-errors-options.js', + options: { + helpers: 'test/browser/runner-errors-options.js', specs: 'test/browser/runner-errors-spec.js', outfile: 'test/browser/test-runner-errors.html' } }, noJsErrors: { src: ['test/less/no-js-errors/*.less'], - options: { - helpers: 'test/browser/runner-no-js-errors-options.js', + options: { + helpers: 'test/browser/runner-no-js-errors-options.js', specs: 'test/browser/runner-no-js-errors-spec.js', outfile: 'test/browser/test-runner-no-js-errors.html' } }, browser: { src: ['test/browser/less/*.less'], - options: { - helpers: 'test/browser/runner-browser-options.js', + options: { + helpers: 'test/browser/runner-browser-options.js', specs: 'test/browser/runner-browser-spec.js', outfile: 'test/browser/test-runner-browser.html' } + }, + relativeUrls: { + src: ['test/browser/less/relative-urls/*.less'], + options: { + helpers: 'test/browser/runner-relative-urls-options.js', + specs: 'test/browser/runner-relative-urls-spec.js', + outfile: 'test/browser/test-runner-relative-urls.html' + } } }, diff --git a/test/browser/css/relative-urls/urls.css b/test/browser/css/relative-urls/urls.css index d0da4990..0606a9e6 100644 --- a/test/browser/css/relative-urls/urls.css +++ b/test/browser/css/relative-urls/urls.css @@ -1,21 +1,21 @@ -@import "http://localhost:8081/browser/less/imports/modify-this.css"; +@import "http://localhost:8081/test/browser/less/imports/modify-this.css"; -@import "http://localhost:8081/browser/less/imports/modify-again.css"; +@import "http://localhost:8081/test/browser/less/imports/modify-again.css"; .modify { - my-url: url("http://localhost:8081/browser/less/imports/a.png"); + my-url: url("http://localhost:8081/test/browser/less/imports/a.png"); } .modify { - my-url: url("http://localhost:8081/browser/less/imports/b.png"); + my-url: url("http://localhost:8081/test/browser/less/imports/b.png"); } @font-face { src: url("/fonts/garamond-pro.ttf"); - src: local(Futura-Medium), url(http://localhost:8081/browser/less/relative-urls/fonts.svg#MyGeometricModern) format("svg"); + src: local(Futura-Medium), url(http://localhost:8081/test/browser/less/relative-urls/fonts.svg#MyGeometricModern) format("svg"); } #shorthands { background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; } #misc { - background-image: url(http://localhost:8081/browser/less/relative-urls/images/image.jpg); + background-image: url(http://localhost:8081/test/browser/less/relative-urls/images/image.jpg); } #data-uri { background: url(data:image/png;charset=utf-8;base64, @@ -29,8 +29,8 @@ background: transparent url('data:image/svg+xml, '); } .comma-delimited { - 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); + background: url(http://localhost:8081/test/browser/less/relative-urls/bg.jpg) no-repeat, url(http://localhost:8081/test/browser/less/relative-urls/bg.png) repeat-x top left, url(http://localhost:8081/test/browser/less/relative-urls/bg); } .values { - url: url('http://localhost:8081/browser/less/relative-urls/Trebuchet'); + url: url('http://localhost:8081/test/browser/less/relative-urls/Trebuchet'); } diff --git a/test/browser/less/relative-urls/urls.less b/test/browser/less/relative-urls/urls.less index 7923d4c8..b33fdfa6 100644 --- a/test/browser/less/relative-urls/urls.less +++ b/test/browser/less/relative-urls/urls.less @@ -1,5 +1,5 @@ @import ".././imports/urls.less"; -@import "http://localhost:8081/browser/less/imports/urls2.less"; +@import "http://localhost:8081/test/browser/less/imports/urls2.less"; @font-face { src: url("/fonts/garamond-pro.ttf"); src: local(Futura-Medium), diff --git a/test/browser/runner-relative-urls-options.js b/test/browser/runner-relative-urls-options.js new file mode 100644 index 00000000..a20eb591 --- /dev/null +++ b/test/browser/runner-relative-urls-options.js @@ -0,0 +1,3 @@ +var less = {}; +less.relativeUrls = true; + diff --git a/test/browser/runner-relative-urls-spec.js b/test/browser/runner-relative-urls-spec.js new file mode 100644 index 00000000..b13911ee --- /dev/null +++ b/test/browser/runner-relative-urls-spec.js @@ -0,0 +1,3 @@ +describe("less.js browser test - relative url's", function() { + testLessEqualsInDocument(); +}); From 1f4bab64533366f4312621f6cbcfe3b8f95b3820 Mon Sep 17 00:00:00 2001 From: meri Date: Wed, 7 Aug 2013 09:41:59 +0200 Subject: [PATCH 25/36] Added rootpath tests. --- Gruntfile.js | 8 ++++++++ test/browser/less/rootpath/urls.less | 2 +- test/browser/runner-rootpath-options.js | 3 +++ test/browser/runner-rootpath-spec.js | 3 +++ 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 test/browser/runner-rootpath-options.js create mode 100644 test/browser/runner-rootpath-spec.js diff --git a/Gruntfile.js b/Gruntfile.js index f9fccbb7..08ac20bb 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -178,6 +178,14 @@ module.exports = function(grunt) { specs: 'test/browser/runner-relative-urls-spec.js', outfile: 'test/browser/test-runner-relative-urls.html' } + }, + rootpath: { + src: ['test/browser/less/rootpath/*.less'], + options: { + helpers: 'test/browser/runner-rootpath-options.js', + specs: 'test/browser/runner-rootpath-spec.js', + outfile: 'test/browser/test-runner-rootpath.html' + } } }, diff --git a/test/browser/less/rootpath/urls.less b/test/browser/less/rootpath/urls.less index 1c5ac888..1843fb22 100644 --- a/test/browser/less/rootpath/urls.less +++ b/test/browser/less/rootpath/urls.less @@ -1,5 +1,5 @@ @import "../imports/urls.less"; -@import "http://localhost:8081/browser/less/imports/urls2.less"; +@import "http://localhost:8081/test/browser/less/imports/urls2.less"; @font-face { src: url("/fonts/garamond-pro.ttf"); src: local(Futura-Medium), diff --git a/test/browser/runner-rootpath-options.js b/test/browser/runner-rootpath-options.js new file mode 100644 index 00000000..5e475bb4 --- /dev/null +++ b/test/browser/runner-rootpath-options.js @@ -0,0 +1,3 @@ +var less = {}; +less.rootpath = "https://www.github.com/"; + diff --git a/test/browser/runner-rootpath-spec.js b/test/browser/runner-rootpath-spec.js new file mode 100644 index 00000000..b7b9ba1d --- /dev/null +++ b/test/browser/runner-rootpath-spec.js @@ -0,0 +1,3 @@ +describe("less.js browser test - rootpath url's", function() { + testLessEqualsInDocument(); +}); From 296dac360c6b7ea57cec6b3bcd91643c1fbd3c0e Mon Sep 17 00:00:00 2001 From: meri Date: Wed, 7 Aug 2013 11:09:48 +0200 Subject: [PATCH 26/36] Added rootpath-relative tests. --- Gruntfile.js | 8 ++++++++ test/browser/css/rootpath-relative/urls.css | 1 + test/browser/less/rootpath-relative/urls.less | 2 +- test/browser/runner-rootpath-relative-options.js | 4 ++++ test/browser/runner-rootpath-relative-spec.js | 3 +++ 5 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/browser/runner-rootpath-relative-options.js create mode 100644 test/browser/runner-rootpath-relative-spec.js diff --git a/Gruntfile.js b/Gruntfile.js index 08ac20bb..23660c16 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -186,6 +186,14 @@ module.exports = function(grunt) { specs: 'test/browser/runner-rootpath-spec.js', outfile: 'test/browser/test-runner-rootpath.html' } + }, + rootpathRelative: { + src: ['test/browser/less/rootpath-relative/*.less'], + options: { + helpers: 'test/browser/runner-rootpath-relative-options.js', + specs: 'test/browser/runner-rootpath-relative-spec.js', + outfile: 'test/browser/test-runner-rootpath-relative.html' + } } }, diff --git a/test/browser/css/rootpath-relative/urls.css b/test/browser/css/rootpath-relative/urls.css index 20b08339..817d5818 100644 --- a/test/browser/css/rootpath-relative/urls.css +++ b/test/browser/css/rootpath-relative/urls.css @@ -1,4 +1,5 @@ @import "https://www.github.com/cloudhead/imports/modify-this.css"; + @import "https://www.github.com/cloudhead/imports/modify-again.css"; .modify { my-url: url("https://www.github.com/cloudhead/imports/a.png"); diff --git a/test/browser/less/rootpath-relative/urls.less b/test/browser/less/rootpath-relative/urls.less index 1c5ac888..1843fb22 100644 --- a/test/browser/less/rootpath-relative/urls.less +++ b/test/browser/less/rootpath-relative/urls.less @@ -1,5 +1,5 @@ @import "../imports/urls.less"; -@import "http://localhost:8081/browser/less/imports/urls2.less"; +@import "http://localhost:8081/test/browser/less/imports/urls2.less"; @font-face { src: url("/fonts/garamond-pro.ttf"); src: local(Futura-Medium), diff --git a/test/browser/runner-rootpath-relative-options.js b/test/browser/runner-rootpath-relative-options.js new file mode 100644 index 00000000..f97baa4b --- /dev/null +++ b/test/browser/runner-rootpath-relative-options.js @@ -0,0 +1,4 @@ +var less = {}; +less.rootpath = "https://www.github.com/cloudhead/less.js/"; +less.relativeUrls = true; + diff --git a/test/browser/runner-rootpath-relative-spec.js b/test/browser/runner-rootpath-relative-spec.js new file mode 100644 index 00000000..cd905739 --- /dev/null +++ b/test/browser/runner-rootpath-relative-spec.js @@ -0,0 +1,3 @@ +describe("less.js browser test - rootpath and relative url's", function() { + testLessEqualsInDocument(); +}); From c7a3fc5ae1bb3a35b69de3adc86858d1a8895ae4 Mon Sep 17 00:00:00 2001 From: meri Date: Wed, 7 Aug 2013 11:10:06 +0200 Subject: [PATCH 27/36] Added grunt working directory into gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3fa214b2..2fe5b599 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ test/browser/less.js test/browser/test-runner-*.htm test/sourcemaps/*.map test/sourcemaps/*.css + +# grunt +.grunt From d2c60d2884538066906bf6bef07a31e6a3a69173 Mon Sep 17 00:00:00 2001 From: meri Date: Wed, 7 Aug 2013 11:30:16 +0200 Subject: [PATCH 28/36] Added production tests. --- Gruntfile.js | 10 +++++++++- test/browser/runner-production-options.js | 3 +++ test/browser/runner-production-spec.js | 5 +++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/browser/runner-production-options.js create mode 100644 test/browser/runner-production-spec.js diff --git a/Gruntfile.js b/Gruntfile.js index 23660c16..b6170653 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -194,7 +194,15 @@ module.exports = function(grunt) { specs: 'test/browser/runner-rootpath-relative-spec.js', outfile: 'test/browser/test-runner-rootpath-relative.html' } - } + }, + production: { + src: ['test/browser/less/production/*.less'], + options: { + helpers: 'test/browser/runner-production-options.js', + specs: 'test/browser/runner-production-spec.js', + outfile: 'test/browser/test-runner-production.html' + } + }, }, // Before running tests, clean out the results diff --git a/test/browser/runner-production-options.js b/test/browser/runner-production-options.js new file mode 100644 index 00000000..f8189d6a --- /dev/null +++ b/test/browser/runner-production-options.js @@ -0,0 +1,3 @@ +var less = {}; +less.env = "production"; + diff --git a/test/browser/runner-production-spec.js b/test/browser/runner-production-spec.js new file mode 100644 index 00000000..8c8d8f42 --- /dev/null +++ b/test/browser/runner-production-spec.js @@ -0,0 +1,5 @@ +describe("less.js production behaviour", function() { + it("doesn't log any messages", function() { + expect(logMessages.length).toEqual(0); + }); +}); From 12da566398c2f859a3ba521ca2a9002cc674df8b Mon Sep 17 00:00:00 2001 From: meri Date: Wed, 7 Aug 2013 13:52:10 +0200 Subject: [PATCH 29/36] Added modify vars tests. --- Gruntfile.js | 8 +++++ test/browser/runner-modify-vars-options.js | 2 ++ test/browser/runner-modify-vars-spec.js | 39 ++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 test/browser/runner-modify-vars-options.js create mode 100644 test/browser/runner-modify-vars-spec.js diff --git a/Gruntfile.js b/Gruntfile.js index b6170653..f84d823b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -203,6 +203,14 @@ module.exports = function(grunt) { outfile: 'test/browser/test-runner-production.html' } }, + modifyVars: { + src: ['test/browser/less/modify-vars/*.less'], + options: { + helpers: 'test/browser/runner-modify-vars-options.js', + specs: 'test/browser/runner-modify-vars-spec.js', + outfile: 'test/browser/test-runner-modify-vars.html' + } + } }, // Before running tests, clean out the results diff --git a/test/browser/runner-modify-vars-options.js b/test/browser/runner-modify-vars-options.js new file mode 100644 index 00000000..f47836f4 --- /dev/null +++ b/test/browser/runner-modify-vars-options.js @@ -0,0 +1,2 @@ +var less = {}; + diff --git a/test/browser/runner-modify-vars-spec.js b/test/browser/runner-modify-vars-spec.js new file mode 100644 index 00000000..a70f9d0e --- /dev/null +++ b/test/browser/runner-modify-vars-spec.js @@ -0,0 +1,39 @@ +var alreadyRun = false; + +describe("less.js modify vars", function() { + beforeEach(function(){ + // simulating "setUp" or "beforeAll" method + var lessOutputObj; + if (alreadyRun) + return ; + + alreadyRun = true; + + // wait until the sheet is compiled first time + waitsFor(function() { + lessOutputObj = document.getElementById("less:test-less-simple"); + return lessOutputObj!==null; + }, "first generation of less:test-less-simple", 7000); + + // modify variables + runs(function() { + lessOutputObj.type="not compiled yet"; + less.modifyVars({var1: "green", var2: "purple"}); + }); + + // wait until variables are modified + waitsFor(function() { + lessOutputObj = document.getElementById("less:test-less-simple"); + return lessOutputObj!==null && lessOutputObj.type==="text/css"; + }, "second generation of less:test-less-simple", 7000); + + }); + + testLessEqualsInDocument(); + it("Should log only 2 XHR requests", function() { + var xhrLogMessages = logMessages.filter(function(item) { + return /XHR: Getting '/.test(item); + }) + expect(xhrLogMessages.length).toEqual(2); + }); +}); From e3f573d6b2e4424704b2e7489f9e4366f09ca971 Mon Sep 17 00:00:00 2001 From: meri Date: Wed, 7 Aug 2013 14:07:52 +0200 Subject: [PATCH 30/36] Removed files that are not used anymore. --- test/browser-test-prepare.js | 47 ---- test/browser/runner-browser.js | 42 ---- test/browser/runner-legacy.js | 6 - test/browser/runner-main.js | 15 -- test/browser/runner-modify-vars.js | 14 -- test/browser/runner-no-js-errors.js | 6 - test/browser/runner-production.js | 7 - test/browser/runner-relative-urls.js | 4 - test/browser/runner-rootpath-relative.js | 5 - test/browser/runner-rootpath.js | 4 - test/less-test.js | 260 ----------------------- 11 files changed, 410 deletions(-) delete mode 100644 test/browser-test-prepare.js delete mode 100644 test/browser/runner-browser.js delete mode 100644 test/browser/runner-legacy.js delete mode 100644 test/browser/runner-main.js delete mode 100644 test/browser/runner-modify-vars.js delete mode 100644 test/browser/runner-no-js-errors.js delete mode 100644 test/browser/runner-production.js delete mode 100644 test/browser/runner-relative-urls.js delete mode 100644 test/browser/runner-rootpath-relative.js delete mode 100644 test/browser/runner-rootpath.js delete mode 100644 test/less-test.js diff --git a/test/browser-test-prepare.js b/test/browser-test-prepare.js deleted file mode 100644 index 95a99703..00000000 --- a/test/browser-test-prepare.js +++ /dev/null @@ -1,47 +0,0 @@ -var path = require('path'), - fs = require('fs'); - -var readDirFilesSync = function(dir, regex, callback) { - fs.readdirSync(dir).forEach(function (file) { - if (! regex.test(file)) { return; } - callback(file); - }); -}; - -var createTestRunnerPage = function(dir, exclude, testSuiteName, dir2) { - var output = '\n'; - - readDirFilesSync(path.join("test", dir, 'less', dir2 || ""), /\.less$/, function (file) { - var name = path.basename(file, '.less'), - id = (dir ? dir + '-' : "") + 'less-' + (dir2 ? dir2 + "-" : "") + name; - - if (exclude && name.match(exclude)) { return; } - - output += '\n'; - output += '\n'; - }); - - output += String(fs.readFileSync(path.join('test/browser', 'template.htm'))).replace("{runner-name}", testSuiteName); - - fs.writeFileSync(path.join('test/browser', 'test-runner-'+testSuiteName+'.htm'), output); -}; - -var removeFiles = function(dir, regex) { - readDirFilesSync(dir, regex, function(file) { - fs.unlinkSync(path.join(dir, file), function() { - console.log("Failed to delete " + file); - }); - }); -}; - -removeFiles("test/browser", /test-runner-[a-zA-Z-]*\.htm$/); -createTestRunnerPage("", /javascript|urls/, "main"); -createTestRunnerPage("", null, "legacy", "legacy"); -createTestRunnerPage("", /javascript/, "errors", "errors"); -createTestRunnerPage("", null, "no-js-errors", "no-js-errors"); -createTestRunnerPage("browser", null, "browser"); -createTestRunnerPage("browser", null, "relative-urls", "relative-urls"); -createTestRunnerPage("browser", null, "rootpath", "rootpath"); -createTestRunnerPage("browser", null, "rootpath-relative", "rootpath-relative"); -createTestRunnerPage("browser", null, "production"); -createTestRunnerPage("browser", null, "modify-vars", "modify-vars"); diff --git a/test/browser/runner-browser.js b/test/browser/runner-browser.js deleted file mode 100644 index e34b1790..00000000 --- a/test/browser/runner-browser.js +++ /dev/null @@ -1,42 +0,0 @@ -describe("less.js browser behaviour", function() { - testLessEqualsInDocument(); - - it("has some log messages", function() { - expect(logMessages.length).toBeGreaterThan(0); - }); - - // test inline less in style tags by grabbing an assortment of less files and doing `@import`s - var testFiles = ['charsets', 'colors', 'comments', 'css-3', 'strings', 'media', 'mixins'], - testSheets = []; - - // setup style tags with less and link tags pointing to expected css output - for (var i = 0; i < testFiles.length; i++) { - var file = testFiles[i], - lessPath = '/less/' + file + '.less', - cssPath = '/css/' + file + '.css', - lessStyle = document.createElement('style'), - cssLink = document.createElement('link'), - lessText = '@import "' + lessPath + '";'; - lessStyle.type = 'text/less'; - lessStyle.id = file; - if (lessStyle.styleSheet) { - lessStyle.styleSheet.cssText = lessText; - } else { - lessStyle.innerHTML = lessText; - } - cssLink.rel = 'stylesheet'; - cssLink.type = 'text/css'; - cssLink.href = cssPath; - cssLink.id = 'expected-' + file; - - var head = document.getElementsByTagName('head')[0]; - head.appendChild(lessStyle); - head.appendChild(cssLink); - testSheets[i] = lessStyle; - } - - for (var i = 0; i < testFiles.length; i++) { - var sheet = testSheets[i]; - testSheet(sheet); - } -}); \ No newline at end of file diff --git a/test/browser/runner-legacy.js b/test/browser/runner-legacy.js deleted file mode 100644 index e60c84c8..00000000 --- a/test/browser/runner-legacy.js +++ /dev/null @@ -1,6 +0,0 @@ -less.strictMath = false; -less.strictUnits = false; - -describe("less.js legacy tests", function() { - testLessEqualsInDocument(); -}); \ No newline at end of file diff --git a/test/browser/runner-main.js b/test/browser/runner-main.js deleted file mode 100644 index d9637c01..00000000 --- a/test/browser/runner-main.js +++ /dev/null @@ -1,15 +0,0 @@ -less.functions = { - add: function (a, b) { - return new(less.tree.Dimension)(a.value + b.value); - }, - increment: function (a) { - return new(less.tree.Dimension)(a.value + 1); - }, - _color: function (str) { - if (str.value === "evil red") { return new(less.tree.Color)("600") } - } -}; - -describe("less.js main tests", function() { - testLessEqualsInDocument(); -}); \ No newline at end of file diff --git a/test/browser/runner-modify-vars.js b/test/browser/runner-modify-vars.js deleted file mode 100644 index 4918d8f8..00000000 --- a/test/browser/runner-modify-vars.js +++ /dev/null @@ -1,14 +0,0 @@ - -setTimeout(function(){ - less.modifyVars({var1: "green", var2: "purple"}); -}, 1000); - -describe("less.js modify vars", function() { - testLessEqualsInDocument(); - it("Should log only 2 XHR requests", function() { - var xhrLogMessages = logMessages.filter(function(item) { - return /XHR: Getting '/.test(item); - }) - expect(xhrLogMessages.length).toEqual(2); - }); -}); \ No newline at end of file diff --git a/test/browser/runner-no-js-errors.js b/test/browser/runner-no-js-errors.js deleted file mode 100644 index 7c53aa98..00000000 --- a/test/browser/runner-no-js-errors.js +++ /dev/null @@ -1,6 +0,0 @@ -less.strictUnits = true; -less.javascriptEnabled = false; - -describe("less.js javascript disabled error tests", function() { - testLessErrorsInDocument(); -}); \ No newline at end of file diff --git a/test/browser/runner-production.js b/test/browser/runner-production.js deleted file mode 100644 index 94c0c8da..00000000 --- a/test/browser/runner-production.js +++ /dev/null @@ -1,7 +0,0 @@ -less.env = "production"; - -describe("less.js production behaviour", function() { - it("doesn't log any messages", function() { - expect(logMessages.length).toEqual(0); - }); -}); \ No newline at end of file diff --git a/test/browser/runner-relative-urls.js b/test/browser/runner-relative-urls.js deleted file mode 100644 index 4e47f639..00000000 --- a/test/browser/runner-relative-urls.js +++ /dev/null @@ -1,4 +0,0 @@ -less.relativeUrls = true; -describe("less.js browser test - relative url's", function() { - testLessEqualsInDocument(); -}); \ No newline at end of file diff --git a/test/browser/runner-rootpath-relative.js b/test/browser/runner-rootpath-relative.js deleted file mode 100644 index b318b42b..00000000 --- a/test/browser/runner-rootpath-relative.js +++ /dev/null @@ -1,5 +0,0 @@ -less.rootpath = "https://www.github.com/cloudhead/less.js/"; -less.relativeUrls = true; -describe("less.js browser test - rootpath and relative url's", function() { - testLessEqualsInDocument(); -}); \ No newline at end of file diff --git a/test/browser/runner-rootpath.js b/test/browser/runner-rootpath.js deleted file mode 100644 index 2c7edd7d..00000000 --- a/test/browser/runner-rootpath.js +++ /dev/null @@ -1,4 +0,0 @@ -less.rootpath = "https://www.github.com/"; -describe("less.js browser test - rootpath url's", function() { - testLessEqualsInDocument(); -}); \ No newline at end of file diff --git a/test/less-test.js b/test/less-test.js deleted file mode 100644 index bc00bc6c..00000000 --- a/test/less-test.js +++ /dev/null @@ -1,260 +0,0 @@ -var path = require('path'), - fs = require('fs'), - sys = require('util'); - -var less = require('../lib/less'); -var stylize = require('../lib/less/lessc_helper').stylize; - -var globals = Object.keys(global); - -var oneTestOnly = process.argv[2]; - -var isVerbose = process.env.npm_config_loglevel === 'verbose'; - -var totalTests = 0, - failedTests = 0, - passedTests = 0; - -less.tree.functions.add = function (a, b) { - return new(less.tree.Dimension)(a.value + b.value); -}; -less.tree.functions.increment = function (a) { - return new(less.tree.Dimension)(a.value + 1); -}; -less.tree.functions._color = function (str) { - if (str.value === "evil red") { return new(less.tree.Color)("600"); } -}; - -sys.puts("\n" + stylize("LESS", 'underline') + "\n"); - -runTestSet({strictMath: true, relativeUrls: true, silent: true}); -runTestSet({strictMath: true, strictUnits: true}, "errors/", - testErrors, null, getErrorPathReplacementFunction("errors")); -runTestSet({strictMath: true, strictUnits: true, javascriptEnabled: false}, "no-js-errors/", - testErrors, null, getErrorPathReplacementFunction("no-js-errors")); -runTestSet({strictMath: true, dumpLineNumbers: 'comments'}, "debug/", null, - function(name) { return name + '-comments'; }); -runTestSet({strictMath: true, dumpLineNumbers: 'mediaquery'}, "debug/", null, - function(name) { return name + '-mediaquery'; }); -runTestSet({strictMath: true, dumpLineNumbers: 'all'}, "debug/", null, - function(name) { return name + '-all'; }); -runTestSet({strictMath: true, relativeUrls: false, rootpath: "folder (1)/"}, "static-urls/"); -runTestSet({strictMath: true, compress: true}, "compression/"); -runTestSet({}, "legacy/"); -runTestSet({strictMath: true, strictUnits: true, sourceMap: true }, "sourcemaps/", - testSourcemap, null, null, function(filename) { return path.join('test/sourcemaps', filename) + '.json'; }); - -testNoOptions(); - -function getErrorPathReplacementFunction(dir) { - return function(input) { - return input.replace( - "{path}", path.join(process.cwd(), "/test/less/" + dir + "/")) - .replace("{pathrel}", path.join("test", "less", dir + "/")) - .replace("{pathhref}", "") - .replace("{404status}", "") - .replace(/\r\n/g, '\n'); - }; -} - -function testSourcemap(name, err, compiledLess, doReplacements, sourcemap) { - fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) { - sys.print("- " + name + ": "); - if (sourcemap === expectedSourcemap) { - ok('OK'); - } else if (err) { - fail("ERROR: " + (err && err.message)); - if (isVerbose) { - console.error(); - console.error(err.stack); - } - } else { - difference("FAIL", expectedSourcemap, sourcemap); - } - sys.puts(""); - }); -} - -function testErrors(name, err, compiledLess, doReplacements) { - fs.readFile(path.join('test/less/', name) + '.txt', 'utf8', function (e, expectedErr) { - sys.print("- " + name + ": "); - expectedErr = doReplacements(expectedErr, 'test/less/errors/'); - if (!err) { - if (compiledLess) { - fail("No Error", 'red'); - } else { - fail("No Error, No Output"); - } - } else { - var errMessage = less.formatError(err); - if (errMessage === expectedErr) { - ok('OK'); - } else { - difference("FAIL", expectedErr, errMessage); - } - } - sys.puts(""); - }); -} - -function globalReplacements(input, directory) { - var p = path.join(process.cwd(), directory), - pathimport = path.join(process.cwd(), directory + "import/"), - pathesc = p.replace(/[.:/\\]/g, function(a) { return '\\' + (a=='\\' ? '\/' : a); }), - pathimportesc = pathimport.replace(/[.:/\\]/g, function(a) { return '\\' + (a=='\\' ? '\/' : a); }); - - return input.replace(/\{path\}/g, p) - .replace(/\{pathesc\}/g, pathesc) - .replace(/\{pathimport\}/g, pathimport) - .replace(/\{pathimportesc\}/g, pathimportesc) - .replace(/\r\n/g, '\n'); -} - -function checkGlobalLeaks() { - return Object.keys(global).filter(function(v) { - return globals.indexOf(v) < 0; - }); -} - -function runTestSet(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) { - foldername = foldername || ""; - - if(!doReplacements) - doReplacements = globalReplacements; - - fs.readdirSync(path.join('test/less/', foldername)).forEach(function (file) { - if (! /\.less/.test(file)) { return; } - - var name = foldername + path.basename(file, '.less'); - - if (oneTestOnly && name !== oneTestOnly) { return; } - - totalTests++; - - if (options.sourceMap) { - var sourceMapOutput; - options.writeSourceMap = function(output) { - sourceMapOutput = output; - }; - options.sourceMapOutputFilename = name + ".css"; - options.sourceMapBasepath = path.join(process.cwd(), "test/less"); - options.sourceMapRootpath = "testweb/"; - } - - toCSS(options, path.join('test/less/', foldername + file), function (err, less) { - - if (verifyFunction) { - return verifyFunction(name, err, less, doReplacements, sourceMapOutput); - } - var css_name = name; - if(nameModifier) { css_name = nameModifier(name); } - fs.readFile(path.join('test/css', css_name) + '.css', 'utf8', function (e, css) { - sys.print("- " + css_name + ": "); - - css = css && doReplacements(css, 'test/less/' + foldername); - if (less === css) { ok('OK'); } - else if (err) { - fail("ERROR: " + (err && err.message)); - if (isVerbose) { - console.error(); - console.error(err.stack); - } - } else { - difference("FAIL", css, less); - } - sys.puts(""); - }); - }); - }); -} - -function diff(left, right) { - sys.puts(""); - require('diff').diffLines(left, right).forEach(function(item) { - if(item.added || item.removed) { - var text = item.value.replace("\n", String.fromCharCode(182) + "\n"); - sys.print(stylize(text, item.added ? 'green' : 'red')); - } else { - sys.print(item.value); - } - }); -} - -function fail(msg) { - sys.print(stylize(msg, 'red')); - failedTests++; - endTest(); -} - -function difference(msg, left, right) { - sys.print(stylize(msg, 'yellow')); - failedTests++; - - diff(left, right); - endTest(); -} - -function ok(msg) { - sys.print(stylize(msg, 'green')); - passedTests++; - endTest(); -} - -function endTest() { - var leaked = checkGlobalLeaks(); - if (failedTests + passedTests === totalTests) { - sys.puts(""); - sys.puts(""); - if (failedTests > 0) { - sys.print(failedTests); - sys.print(stylize(" Failed", "red")); - sys.print(", " + passedTests + " passed"); - } else { - sys.print(stylize("All Passed ", "green")); - sys.print(passedTests + " run"); - } - if (leaked.length > 0) { - sys.puts(""); - sys.puts(""); - sys.print(stylize("Global leak detected: ", "red") + leaked.join(', ')); - sys.print("\n"); - } - } -} - -function toCSS(options, path, callback) { - var css; - options = options || {}; - fs.readFile(path, 'utf8', function (e, str) { - if (e) { return callback(e); } - - options.paths = [require('path').dirname(path)]; - options.filename = require('path').resolve(process.cwd(), path); - options.optimization = options.optimization || 0; - - new(less.Parser)(options).parse(str, function (err, tree) { - if (err) { - callback(err); - } else { - try { - css = tree.toCSS(options); - callback(null, css); - } catch (e) { - callback(e); - } - } - }); - }); -} - -function testNoOptions() { - totalTests++; - try { - sys.print("- Integration - creating parser without options: "); - new(less.Parser)(); - } catch(e) { - fail(stylize("FAIL\n", "red")); - return; - } - ok(stylize("OK\n", "green")); -} From 1105f1e02fb9861af56449676dde2af9bb9b7ebc Mon Sep 17 00:00:00 2001 From: meri Date: Wed, 7 Aug 2013 16:18:50 +0200 Subject: [PATCH 31/36] Raised timeout for error test, it did not made it till the end occasionally. --- Gruntfile.js | 11 ++--------- test/browser/common.js | 2 +- test/browser/runner-errors-spec.js | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index f84d823b..8d949549 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,12 +28,6 @@ module.exports = function(grunt) { test: { command: 'node test/less-test.js' }, - browser: { - command: 'node test/browser-test-prepare.js' - }, - phantom: { - command: 'phantomjs test/browser/phantom-runner.js' - }, benchmark: { command: 'node benchmark/less-benchmark.js' } @@ -125,7 +119,7 @@ module.exports = function(grunt) { jasmine: { options: { - keepRunner: true, //TODO meri: remove after it is done + keepRunner: true, host: 'http://localhost:8081/', vendor: 'test/browser/common.js', template: 'test/browser/test-runner-template.tmpl' @@ -150,6 +144,7 @@ module.exports = function(grunt) { errors: { src: ['test/less/errors/*.less', '!test/less/errors/javascript-error.less'], options: { + timeout: 20000, helpers: 'test/browser/runner-errors-options.js', specs: 'test/browser/runner-errors-spec.js', outfile: 'test/browser/test-runner-errors.html' @@ -272,8 +267,6 @@ module.exports = function(grunt) { 'clean', 'shell:test', 'browserTest' - // 'shell:browser', - // 'shell:phantom' ]); // Run benchmark diff --git a/test/browser/common.js b/test/browser/common.js index 1e144582..5af09598 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -89,7 +89,7 @@ var testErrorSheet = function(sheet) { // Less.js sets 10ms timer in order to add error message on top of page. waitsFor(function() { - actualErrorElement = document.getElementById(id); + actualErrorElement = document.getElementById(id); return actualErrorElement!==null; }, "error message was not generated", 70); diff --git a/test/browser/runner-errors-spec.js b/test/browser/runner-errors-spec.js index 8bb8c6b7..0b720676 100644 --- a/test/browser/runner-errors-spec.js +++ b/test/browser/runner-errors-spec.js @@ -1,4 +1,4 @@ describe("less.js error tests", function() { testLessErrorsInDocument(); -}) +}); From 3722ff0cb9b36d4e04ceeb2110789ad4ea433102 Mon Sep 17 00:00:00 2001 From: meri Date: Wed, 7 Aug 2013 16:41:03 +0200 Subject: [PATCH 32/36] Added missing files to build. --- build/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build/build.yml b/build/build.yml index 32895227..26a5ebd3 100644 --- a/build/build.yml +++ b/build/build.yml @@ -45,6 +45,8 @@ less: to_css : <%= build.lib %>/to-css-visitor.js extend : <%= build.lib %>/extend-visitor.js browser : <%= build.lib %>/browser.js + source_map_output : <%= build.lib %>/source-map-output.js + to_css_visitor : <%= build.lib %>/to-css-visitor.js # glob all files in ./lib/less/tree directory treedir : <%= build.lib %>/tree/*.js @@ -72,6 +74,8 @@ browser: - <%= build.less.join %> - <%= build.less.to_css %> - <%= build.less.extend %> + - <%= build.less.source_map_output %> + - <%= build.less.to_css_visitor %> # append browser-specific code - <%= build.append.browser %> @@ -99,6 +103,8 @@ rhino: - <%= build.less.colors %> - <%= build.less.treedir %> # glob all files - <%= build.less.tree %> + - <%= build.less.source_map_output %> + - <%= build.less.to_css_visitor %> # append rhino-specific code - <%= build.append.rhino %> From d995f57d1f821e2a2e0e4896b8a851bcc2529966 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Fri, 9 Aug 2013 05:46:39 -0400 Subject: [PATCH 33/36] remove makefile that added from previous merge conflict --- Makefile | 114 ------------------------------------------------------- 1 file changed, 114 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index c9920c5e..00000000 --- a/Makefile +++ /dev/null @@ -1,114 +0,0 @@ -# -# Run all tests -# -test: - node test/less-test.js - -# -# Run benchmark -# -benchmark: - node benchmark/less-benchmark.js - -# -# Build less.js -# -SRC = lib/less -HEADER = build/header.js -VERSION = `cat package.json | grep version \ - | grep -o '[0-9]\.[0-9]\.[0-9]\+'` -DIST = dist/less-${VERSION}.js -RHINO = dist/less-rhino-${VERSION}.js -DIST_MIN = dist/less-${VERSION}.min.js - -browser-prepare: DIST := test/browser/less.js - -alpha: DIST := dist/less-${VERSION}-alpha.js -alpha: DIST_MIN := dist/less-${VERSION}-alpha.min.js - -beta: DIST := dist/less-${VERSION}-beta.js -beta: DIST_MIN := dist/less-${VERSION}-beta.min.js - -less: - @@mkdir -p dist - @@touch ${DIST} - @@cat ${HEADER} | sed s/@VERSION/${VERSION}/ > ${DIST} - @@echo "(function (window, undefined) {" >> ${DIST} - @@cat build/require.js\ - build/browser-header.js\ - ${SRC}/parser.js\ - ${SRC}/functions.js\ - ${SRC}/colors.js\ - ${SRC}/tree/*.js\ - ${SRC}/tree.js\ - ${SRC}/env.js\ - ${SRC}/visitor.js\ - ${SRC}/import-visitor.js\ - ${SRC}/join-selector-visitor.js\ - ${SRC}/to-css-visitor.js\ - ${SRC}/extend-visitor.js\ - ${SRC}/browser.js\ - build/amd.js >> ${DIST} - @@echo "})(window);" >> ${DIST} - @@echo ${DIST} built. - -browser-prepare: less - node test/browser-test-prepare.js - -browser-test: browser-prepare - phantomjs test/browser/phantom-runner.js - -browser-test-server: browser-prepare - phantomjs test/browser/phantom-runner.js --no-tests - -jshint: - node_modules/.bin/jshint --config ./.jshintrc . - -test-sourcemaps: - node bin/lessc --source-map --source-map-inline test/less/import.less test/sourcemaps/import.css - node bin/lessc --source-map --source-map-inline test/less/sourcemaps/basic.less test/sourcemaps/basic.css - node node_modules/http-server/bin/http-server test/sourcemaps -p 8083 - -rhino: - @@mkdir -p dist - @@touch ${RHINO} - @@cat build/require-rhino.js\ - build/rhino-header.js\ - ${SRC}/parser.js\ - ${SRC}/env.js\ - ${SRC}/visitor.js\ - ${SRC}/import-visitor.js\ - ${SRC}/join-selector-visitor.js\ - ${SRC}/to-css-visitor.js\ - ${SRC}/extend-visitor.js\ - ${SRC}/functions.js\ - ${SRC}/colors.js\ - ${SRC}/tree/*.js\ - ${SRC}/tree.js\ - ${SRC}/rhino.js > ${RHINO} - @@echo ${RHINO} built. - -min: less - @@echo minifying... - @@uglifyjs ${DIST} > ${DIST_MIN} - @@echo ${DIST_MIN} built. - -alpha: min - -beta: min - -alpha-release: alpha - git add dist/*.js - git commit -m "Update alpha ${VERSION}" - -dist: min rhino - git add dist/* - git commit -a -m "(dist) build ${VERSION}" - git archive master --prefix=less/ -o less-${VERSION}.tar.gz - npm publish less-${VERSION}.tar.gz - -stable: - npm tag less@${VERSION} stable - - -.PHONY: test benchmark From 15c38b1ac28ae4852ef13a3395961660363c352d Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Wed, 21 Aug 2013 16:28:33 -0400 Subject: [PATCH 34/36] fixed some typos, ran jshint and reformatted for clarity --- test/browser/common.js | 202 +++++++++---------- test/browser/phantom-runner.js | 215 +++++++++++---------- test/browser/runner-browser-options.js | 60 +++--- test/browser/runner-main-options.js | 21 +- test/browser/runner-modify-vars-options.js | 4 +- test/browser/runner-modify-vars-spec.js | 66 ++++--- test/browser/template.htm | 18 +- test/browser/test-runner-template.tmpl | 78 ++++---- 8 files changed, 344 insertions(+), 320 deletions(-) diff --git a/test/browser/common.js b/test/browser/common.js index 5af09598..abe2562b 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -1,133 +1,139 @@ /* record log messages for testing */ -var logAllIds = function() { - var allTags = document.head.getElementsByTagName('style'); - var ids = []; - for (var tg = 0; tg< allTags.length; tg++) { - var tag = allTags[tg]; - if (tag.id) { - console.log(tag.id); - } - } -}; +// var logAllIds = function() { +// var allTags = document.head.getElementsByTagName('style'); +// var ids = []; +// for (var tg = 0; tg < allTags.length; tg++) { +// var tag = allTags[tg]; +// if (tag.id) { +// console.log(tag.id); +// } +// } +// }; var logMessages = [], - realConsoleLog = console.log; + realConsoleLog = console.log; console.log = function(msg) { - logMessages.push(msg); - realConsoleLog.call(console, msg); + logMessages.push(msg); + realConsoleLog.call(console, msg); }; var testLessEqualsInDocument = function() { - testLessInDocument(testSheet); + testLessInDocument(testSheet); }; var testLessErrorsInDocument = function() { - testLessInDocument(testErrorSheet); + testLessInDocument(testErrorSheet); }; var testLessInDocument = function(testFunc) { - var links = document.getElementsByTagName('link'), - typePattern = /^text\/(x-)?less$/; + var links = document.getElementsByTagName('link'), + typePattern = /^text\/(x-)?less$/; - for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - testFunc(links[i]); - } + for (var i = 0; i < links.length; i++) { + if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && + (links[i].type.match(typePattern)))) { + testFunc(links[i]); } + } }; var testSheet = function(sheet) { - it(sheet.id + " should match the expected output", function() { - var lessOutputId = sheet.id.replace("original-", ""), - expectedOutputId = "expected-" + lessOutputId, - lessOutputObj, - lessOutput, - expectedOutputHref = document.getElementById(expectedOutputId).href, - expectedOutput = loadFile(expectedOutputHref); + it(sheet.id + " should match the expected output", function() { + var lessOutputId = sheet.id.replace("original-", ""), + expectedOutputId = "expected-" + lessOutputId, + lessOutputObj, + lessOutput, + expectedOutputHref = document.getElementById(expectedOutputId).href, + expectedOutput = loadFile(expectedOutputHref); - // Browser spec generates less on the fly, so we need to loose control - waitsFor(function() { - lessOutputObj = document.getElementById(lessOutputId); - // the type condition is necessary because of inline browser tests - return lessOutputObj!==null && lessOutputObj.type==="text/css"; - }, "generation of " + lessOutputId + "", 700); + // Browser spec generates less on the fly, so we need to loose control + waitsFor(function() { + lessOutputObj = document.getElementById(lessOutputId); + // the type condition is necessary because of inline browser tests + return lessOutputObj !== null && lessOutputObj.type === "text/css"; + }, "generation of " + lessOutputId + "", 700); - runs(function() { - lessOutput = lessOutputObj.innerText; - }); - - waitsFor(function() { - return expectedOutput.loaded; - }, "failed to load expected outout", 10000); - - runs(function() { - // use sheet to do testing - expect(lessOutput).toEqual(expectedOutput.text); - }); + runs(function() { + lessOutput = lessOutputObj.innerText; }); + + waitsFor(function() { + return expectedOutput.loaded; + }, "failed to load expected outout", 10000); + + runs(function() { + // use sheet to do testing + expect(lessOutput).toEqual(expectedOutput.text); + }); + }); }; //TODO: do it cleaner - the same way as in css + function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) + return href.replace(/^[a-z-]+:\/+?[^\/]+/, '') // Remove protocol & domain + .replace(/^\//, '') // Remove root / + .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension + .replace(/[^\.\w-]+/g, '-') // Replace illegal characters + .replace(/\./g, ':'); // Replace dots with colons(for valid id) } var testErrorSheet = function(sheet) { - it(sheet.id + " should match an error", function() { - var lessHref = sheet.href, - id = "less-error-message:"+extractId(lessHref), -// id = sheet.id.replace(/^original-less:/, "less-error-message:"), - errorHref = lessHref.replace(/.less$/, ".txt"), - errorFile = loadFile(errorHref), - actualErrorElement, - actualErrorMsg; + it(sheet.id + " should match an error", function() { + var lessHref = sheet.href, + id = "less-error-message:" + extractId(lessHref), + // id = sheet.id.replace(/^original-less:/, "less-error-message:"), + errorHref = lessHref.replace(/.less$/, ".txt"), + errorFile = loadFile(errorHref), + actualErrorElement, + actualErrorMsg; - // Less.js sets 10ms timer in order to add error message on top of page. - waitsFor(function() { - actualErrorElement = document.getElementById(id); - return actualErrorElement!==null; - }, "error message was not generated", 70); + // Less.js sets 10ms timer in order to add error message on top of page. + waitsFor(function() { + actualErrorElement = document.getElementById(id); + return actualErrorElement !== null; + }, "error message was not generated", 70); - runs(function() { - actualErrorMsg = actualErrorElement.innerText - .replace(/\n\d+/g, function(lineNo) { return lineNo + " "; }) - .replace(/\n\s*in /g, " in ") - .replace("\n\n", "\n"); - }); - - waitsFor(function() { - return errorFile.loaded; - }, "failed to load expected outout", 10000); - - runs(function() { - var errorTxt = errorFile.text - .replace("{path}", "") - .replace("{pathrel}", "") - .replace("{pathhref}", "http://localhost:8081/less/errors/") - .replace("{404status}", " (404)"); - expect(actualErrorMsg).toEqual(errorTxt); - if (errorTxt == actualErrorMsg) { - actualErrorElement.style.display = "none"; - } - }); + runs(function() { + actualErrorMsg = actualErrorElement.innerText + .replace(/\n\d+/g, function(lineNo) { + return lineNo + " "; + }) + .replace(/\n\s*in /g, " in ") + .replace("\n\n", "\n"); }); + + waitsFor(function() { + return errorFile.loaded; + }, "failed to load expected outout", 10000); + + runs(function() { + var errorTxt = errorFile.text + .replace("{path}", "") + .replace("{pathrel}", "") + .replace("{pathhref}", "http://localhost:8081/less/errors/") + .replace("{404status}", " (404)"); + expect(actualErrorMsg).toEqual(errorTxt); + if (errorTxt == actualErrorMsg) { + actualErrorElement.style.display = "none"; + } + }); + }); }; var loadFile = function(href) { - var request = new XMLHttpRequest(), - response = { loaded: false, text: ""}; - request.open('GET', href, true); - request.onload = function(e) { - response.text = request.response.replace(/\r/g, ""); - response.loaded = true; - } - request.send(); - return response; + var request = new XMLHttpRequest(), + response = { + loaded: false, + text: "" + }; + request.open('GET', href, true); + request.onload = function(e) { + response.text = request.response.replace(/\r/g, ""); + response.loaded = true; + }; + request.send(); + return response; }; (function() { @@ -153,8 +159,8 @@ var loadFile = function(href) { function execJasmine() { setTimeout(function() { - jasmineEnv.execute(); + jasmineEnv.execute(); }, 3000); } -})(); +})(); \ No newline at end of file diff --git a/test/browser/phantom-runner.js b/test/browser/phantom-runner.js index 0325ba5b..8233deb8 100644 --- a/test/browser/phantom-runner.js +++ b/test/browser/phantom-runner.js @@ -2,31 +2,35 @@ var webpage = require('webpage'); var server = require('webserver').create(); var system = require('system'); var fs = require('fs'); -var host, port = 8081; +var host; +var port = 8081; -var listening = server.listen(port, function (request, response) { - //console.log("Requested "+request.url); - - var filename = ("test/" + request.url.slice(1)).replace(/[\\\/]/g, fs.separator); - - if (!fs.exists(filename) || !fs.isFile(filename)) { - response.statusCode = 404; - response.write("

    File Not Found

    File:"+filename+"

    "); - response.close(); - return; - } +var listening = server.listen(port, function(request, response) { + //console.log("Requested " + request.url); - // we set the headers here - response.statusCode = 200; - response.headers = {"Cache": "no-cache", "Content-Type": "text/html"}; - - response.write(fs.read(filename)); - + var filename = ("test/" + request.url.slice(1)).replace(/[\\\/]/g, fs.separator); + + if (!fs.exists(filename) || !fs.isFile(filename)) { + response.statusCode = 404; + response.write("

    File Not Found

    File:" + filename + "

    "); response.close(); + return; + } + + // we set the headers here + response.statusCode = 200; + response.headers = { + "Cache": "no-cache", + "Content-Type": "text/html" + }; + + response.write(fs.read(filename)); + + response.close(); }); if (!listening) { - console.log("could not create web server listening on port " + port); - phantom.exit(); + console.log("could not create web server listening on port " + port); + phantom.exit(); } /** @@ -42,100 +46,105 @@ if (!listening) { * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. * @param timeOutErrorMessage the error message if time out occurs */ + function waitFor(testFx, onReady, timeOutMillis, timeOutErrorMessage) { - var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 10001, //< Default Max Timeout is 10s - start = new Date().getTime(), - condition = false, - interval = setInterval(function() { - if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) { - // If not time-out yet and condition not yet fulfilled - condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code - } else { - if(!condition) { - // If condition still not fulfilled (timeout but condition is 'false') - console.log(timeOutErrorMessage || "'waitFor()' timeout"); - phantom.exit(1); - } else { - // Condition fulfilled (timeout and/or condition is 'true') - typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled - clearInterval(interval); //< Stop this interval - } - } - }, 100); //< repeat check every 100ms -}; + var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 10001, //< Default Max Timeout is 10s + start = new Date().getTime(), + condition = false, + interval = setInterval(function() { + if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) { + // If not time-out yet and condition not yet fulfilled + condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code + } else { + if (!condition) { + // If condition still not fulfilled (timeout but condition is 'false') + console.log(timeOutErrorMessage || "'waitFor()' timeout"); + phantom.exit(1); + } else { + // Condition fulfilled (timeout and/or condition is 'true') + typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled + clearInterval(interval); //< Stop this interval + } + } + }, 100); //< repeat check every 100ms +} function testPage(url) { - var page = webpage.create(); - page.open(url, function (status) { - if (status !== "success") { - console.log("Unable to access network - " + status); - phantom.exit(); - } else { - waitFor(function(){ - return page.evaluate(function(){ - return document.body && document.body.querySelector && - document.body.querySelector('.symbolSummary .pending') === null && - document.body.querySelector('.results') !== null; - }); - }, function(){ - page.onConsoleMessage = function (msg) { - console.log(msg); - }; - var exitCode = page.evaluate(function(){ - console.log(''); - console.log(document.body.querySelector('.description').innerText); - var list = document.body.querySelectorAll('.results > #details > .specDetail.failed'); - if (list && list.length > 0) { - console.log(''); - console.log(list.length + ' test(s) FAILED:'); - for (var i = 0; i < list.length; ++i) { - var el = list[i], - desc = el.querySelector('.description'), - msg = el.querySelector('.resultMessage.fail'); - console.log(''); - console.log(desc.innerText); - console.log(msg.innerText); - console.log(''); - } - return 1; - } else { - console.log(document.body.querySelector('.alert > .passingAlert.bar').innerText); - return 0; - } - }); - testFinished(exitCode); - }, - 10000, // 10 second timeout - "Test failed waiting for jasmine results on page: " + url); - } - }); + var page = webpage.create(); + page.open(url, function(status) { + if (status !== "success") { + console.log("Unable to access network - " + status); + phantom.exit(); + } else { + waitFor(function() { + return page.evaluate(function() { + return document.body && document.body.querySelector && + document.body.querySelector('.symbolSummary .pending') === null && + document.body.querySelector('.results') !== null; + }); + }, function() { + page.onConsoleMessage = function(msg) { + console.log(msg); + }; + var exitCode = page.evaluate(function() { + console.log(''); + console.log(document.body.querySelector('.description').innerText); + var list = document.body.querySelectorAll('.results > #details > .specDetail.failed'); + if (list && list.length > 0) { + console.log(''); + console.log(list.length + ' test(s) FAILED:'); + for (var i = 0; i < list.length; ++i) { + var el = list[i], + desc = el.querySelector('.description'), + msg = el.querySelector('.resultMessage.fail'); + console.log(''); + console.log(desc.innerText); + console.log(msg.innerText); + console.log(''); + } + return 1; + } else { + console.log(document.body.querySelector('.alert > .passingAlert.bar').innerText); + return 0; + } + }); + testFinished(exitCode); + }, + 10000, // 10 second timeout + "Test failed waiting for jasmine results on page: " + url); + } + }); } function scanDirectory(path, regex) { - var files = []; - fs.list(path).forEach(function (file) { - if (file.match(regex)) { - files.push(file); - } - }); - return files; -}; + var files = []; + fs.list(path).forEach(function(file) { + if (file.match(regex)) { + files.push(file); + } + }); + return files; +} var totalTests = 0, - totalFailed = 0, - totalDone = 0; + totalFailed = 0, + totalDone = 0; function testFinished(failed) { - if (failed) { totalFailed++; } - totalDone++; - if (totalDone === totalTests) { phantom.exit(totalFailed > 0 ? 1 : 0); } + if (failed) { + totalFailed++; + } + totalDone++; + if (totalDone === totalTests) { + phantom.exit(totalFailed > 0 ? 1 : 0); + } } if (system.args.length != 2 && system.args[1] != "--no-tests") { - var files = scanDirectory("test/browser/", /^test-runner-.+\.htm$/); - totalTests = files.length; - console.log("found " + files.length + " tests"); - files.forEach(function(file) { - testPage("http://localhost:8081/browser/" + file); - }); + var files = scanDirectory("test/browser/", /^test-runner-.+\.htm$/); + totalTests = files.length; + console.log("found " + files.length + " tests"); + files.forEach(function(file) { + testPage("http://localhost:8081/browser/" + file); + }); } \ No newline at end of file diff --git a/test/browser/runner-browser-options.js b/test/browser/runner-browser-options.js index ccc9b587..3d73323e 100644 --- a/test/browser/runner-browser-options.js +++ b/test/browser/runner-browser-options.js @@ -1,40 +1,42 @@ var less = {}; -// There originally run inside describe method. However, since they have not -// been inside it, they run at jasmine compile time (not runtime). It all -// worked cause less.js was in async mode and custom phantom runner had -// different setup then grunt-contrib-jasmine. They have been created before +// There originally run inside describe method. However, since they have not +// been inside it, they run at jasmine compile time (not runtime). It all +// worked cause less.js was in async mode and custom phantom runner had +// different setup then grunt-contrib-jasmine. They have been created before // less.js run, even as they have been defined in spec. // test inline less in style tags by grabbing an assortment of less files and doing `@import`s var testFiles = ['charsets', 'colors', 'comments', 'css-3', 'strings', 'media', 'mixins'], testSheets = []; - // setup style tags with less and link tags pointing to expected css output - for (var i = 0; i < testFiles.length; i++) { - var file = testFiles[i], - lessPath = '/test/less/' + file + '.less', - cssPath = '/test/css/' + file + '.css', - lessStyle = document.createElement('style'), - cssLink = document.createElement('link'), - lessText = '@import "' + lessPath + '";'; - lessStyle.type = 'text/less'; - lessStyle.id = file; - lessStyle.href = file; - if (lessStyle.styleSheet) { - lessStyle.styleSheet.cssText = lessText; - } else { - lessStyle.innerHTML = lessText; - } - cssLink.rel = 'stylesheet'; - cssLink.type = 'text/css'; - cssLink.href = cssPath; - cssLink.id = 'expected-' + file; +// setup style tags with less and link tags pointing to expected css output +for (var i = 0; i < testFiles.length; i++) { + var file = testFiles[i], + lessPath = '/test/less/' + file + '.less', + cssPath = '/test/css/' + file + '.css', + lessStyle = document.createElement('style'), + cssLink = document.createElement('link'), + lessText = '@import "' + lessPath + '";'; - var head = document.getElementsByTagName('head')[0]; - head.appendChild(lessStyle); - head.appendChild(cssLink); - testSheets[i] = lessStyle; - } + lessStyle.type = 'text/less'; + lessStyle.id = file; + lessStyle.href = file; + if (lessStyle.styleSheet) { + lessStyle.styleSheet.cssText = lessText; + } else { + lessStyle.innerHTML = lessText; + } + cssLink.rel = 'stylesheet'; + cssLink.type = 'text/css'; + cssLink.href = cssPath; + cssLink.id = 'expected-' + file; + + var head = document.getElementsByTagName('head')[0]; + + head.appendChild(lessStyle); + head.appendChild(cssLink); + testSheets[i] = lessStyle; +} \ No newline at end of file diff --git a/test/browser/runner-main-options.js b/test/browser/runner-main-options.js index d43b630f..2433fa9c 100644 --- a/test/browser/runner-main-options.js +++ b/test/browser/runner-main-options.js @@ -1,13 +1,14 @@ var less = {}; less.functions = { - add: function (a, b) { - return new(less.tree.Dimension)(a.value + b.value); - }, - increment: function (a) { - return new(less.tree.Dimension)(a.value + 1); - }, - _color: function (str) { - if (str.value === "evil red") { return new(less.tree.Color)("600") } + add: function(a, b) { + return new(less.tree.Dimension)(a.value + b.value); + }, + increment: function(a) { + return new(less.tree.Dimension)(a.value + 1); + }, + _color: function(str) { + if (str.value === "evil red") { + return new(less.tree.Color)("600"); } -}; - + } +}; \ No newline at end of file diff --git a/test/browser/runner-modify-vars-options.js b/test/browser/runner-modify-vars-options.js index f47836f4..2799ad55 100644 --- a/test/browser/runner-modify-vars-options.js +++ b/test/browser/runner-modify-vars-options.js @@ -1,2 +1,2 @@ -var less = {}; - +/* exported less */ +var less = {}; \ No newline at end of file diff --git a/test/browser/runner-modify-vars-spec.js b/test/browser/runner-modify-vars-spec.js index a70f9d0e..ee100356 100644 --- a/test/browser/runner-modify-vars-spec.js +++ b/test/browser/runner-modify-vars-spec.js @@ -1,39 +1,43 @@ var alreadyRun = false; describe("less.js modify vars", function() { - beforeEach(function(){ - // simulating "setUp" or "beforeAll" method - var lessOutputObj; - if (alreadyRun) - return ; - - alreadyRun = true; + beforeEach(function() { + // simulating "setUp" or "beforeAll" method + var lessOutputObj; + if (alreadyRun) + return; - // wait until the sheet is compiled first time - waitsFor(function() { - lessOutputObj = document.getElementById("less:test-less-simple"); - return lessOutputObj!==null; - }, "first generation of less:test-less-simple", 7000); + alreadyRun = true; - // modify variables - runs(function() { - lessOutputObj.type="not compiled yet"; - less.modifyVars({var1: "green", var2: "purple"}); - }); + // wait until the sheet is compiled first time + waitsFor(function() { + lessOutputObj = document.getElementById("less:test-less-simple"); + return lessOutputObj !== null; + }, "first generation of less:test-less-simple", 7000); - // wait until variables are modified - waitsFor(function() { - lessOutputObj = document.getElementById("less:test-less-simple"); - return lessOutputObj!==null && lessOutputObj.type==="text/css"; - }, "second generation of less:test-less-simple", 7000); - + // modify variables + runs(function() { + lessOutputObj.type = "not compiled yet"; + less.modifyVars({ + var1: "green", + var2: "purple" + }); }); - - testLessEqualsInDocument(); - it("Should log only 2 XHR requests", function() { - var xhrLogMessages = logMessages.filter(function(item) { - return /XHR: Getting '/.test(item); - }) - expect(xhrLogMessages.length).toEqual(2); + + // wait until variables are modified + waitsFor(function() { + lessOutputObj = document.getElementById("less:test-less-simple"); + return lessOutputObj !== null && lessOutputObj.type === "text/css"; + }, "second generation of less:test-less-simple", 7000); + + }); + + testLessEqualsInDocument(); + it("Should log only 2 XHR requests", function() { + var xhrLogMessages = logMessages.filter(function(item) { + var filename = item.replace(/^.*[\\\/]/, ''); + return (/XHR: Getting '/).test(filename).magenta; }); -}); + expect(xhrLogMessages.length).toEqual(2); + }); +}); \ No newline at end of file diff --git a/test/browser/template.htm b/test/browser/template.htm index c000fdc2..7080f35b 100644 --- a/test/browser/template.htm +++ b/test/browser/template.htm @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/test/browser/test-runner-template.tmpl b/test/browser/test-runner-template.tmpl index 78ab6d0b..0aeaf041 100644 --- a/test/browser/test-runner-template.tmpl +++ b/test/browser/test-runner-template.tmpl @@ -1,41 +1,43 @@ - -<% -var generateScriptsTasgs = function(allScripts) { - allScripts.forEach(function(script){ %> -<% }); -}; -%> - - - - Jasmine Spec Runner + + + + + Jasmine Spec Runner - -<% - scripts.src.forEach(function(fullLessName) { - var pathParts = fullLessName.split('/'), - fullCssName = fullLessName.replace(/less/g, 'css'), - lessName = pathParts[pathParts.length-1], - name = lessName.split('.')[0]; - %> - - <% - }); -%> + + <% var generateScriptTags = function(allScripts) { allScripts.forEach(function(script){ %> + + <% }); }; %> - -<% css.forEach(function(style){ %> - -<% }) %> - -<% generateScriptsTasgs([].concat(scripts.polyfills, scripts.jasmine)); %> - -<% generateScriptsTasgs([].concat(scripts.vendor, scripts.helpers)); -%> -<% generateScriptsTasgs(scripts.specs); %> - -<% generateScriptsTasgs([].concat(scripts.reporters, scripts.start)); %> - - - + + <% scripts.src.forEach(function(fullLessName) { + var pathParts = fullLessName.split('/'); + var fullCssName = fullLessName.replace(/less/g, 'css'); + var lessName = pathParts[pathParts.length - 1]; + var name = lessName.split('.')[0]; %> + + + + <% }); %> + + + <% css.forEach(function(style){ %> + + <% }) %> + + + <% generateScriptTags([].concat(scripts.polyfills, scripts.jasmine)); %> + + + <% generateScriptTags([].concat(scripts.vendor, scripts.helpers)); %> + + <% generateScriptTags(scripts.specs); %> + + + <% generateScriptTags([].concat(scripts.reporters, scripts.start)); %> + + + + + From 5bcc0494509ef51a25e9b78082b584a7277857b8 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Wed, 21 Aug 2013 16:33:36 -0400 Subject: [PATCH 35/36] consolidates jshint targets since they aren't being run separately. cleans up some tasks. --- Gruntfile.js | 109 +++++++++++++++++++++------------------------------ README.md | 3 +- 2 files changed, 46 insertions(+), 66 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 8d949549..ea89099d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2,12 +2,15 @@ module.exports = function(grunt) { + // Report the elapsed execution time of tasks. + require('time-grunt')(grunt); + // Project configuration. grunt.initConfig({ + + // Metadata required for build. build: grunt.file.readYAML('build/build.yml'), pkg: grunt.file.readJSON('package.json'), - - // Metadata meta: { license: '<%= _.pluck(pkg.licenses, "type").join(", ") %>', copyright: 'Copyright (c) 2009-<%= grunt.template.today("yyyy") %>', @@ -35,7 +38,7 @@ module.exports = function(grunt) { concat: { options: { - stripBanners: true, + stripBanners: 'all', banner: '<%= meta.banner %>\n\n(function (window, undefined) {', footer: '\n})(window);' }, @@ -63,10 +66,8 @@ module.exports = function(grunt) { }, // Generate readme readme: { - options: { - process: true, - banner: '' // override task-level banner - }, + // override task-level banner and footer + options: {process: true, banner: '', footer: ''}, src: ['build/README.md'], dest: 'README.md' } @@ -92,20 +93,14 @@ module.exports = function(grunt) { }, jshint: { - options: { - jshintrc: '.jshintrc' - }, - benchmark: { - src: ['benchmark/**/*.js'] - }, - gruntfile: { - src: 'Gruntfile.js' - }, - lib: { - src: ['lib/**/*.js'] - }, - test: { - src: ['test/*.js', 'test/browser/runner-*.js'] + options: {jshintrc: '.jshintrc'}, + files: { + src: [ + 'Gruntfile.js', + 'lib/**/*.js', + 'test/**/*.js', + 'benchmark/*.js' + ] } }, @@ -119,98 +114,99 @@ module.exports = function(grunt) { jasmine: { options: { + // version: '2.0.0-rc2', keepRunner: true, host: 'http://localhost:8081/', vendor: 'test/browser/common.js', template: 'test/browser/test-runner-template.tmpl' }, main: { - //src is used to build list of less files to compile - src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less'], + // src is used to build list of less files to compile + src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less'], options: { helpers: 'test/browser/runner-main-options.js', specs: 'test/browser/runner-main-spec.js', - outfile: 'test/browser/test-runner-main.html' + outfile: 'tmp/browser/test-runner-main.html' } }, legacy: { - src: ['test/less/legacy/*.less'], + src: ['test/less/legacy/*.less'], options: { helpers: 'test/browser/runner-legacy-options.js', specs: 'test/browser/runner-legacy-spec.js', - outfile: 'test/browser/test-runner-legacy.html' + outfile: 'tmp/browser/test-runner-legacy.html' } }, errors: { - src: ['test/less/errors/*.less', '!test/less/errors/javascript-error.less'], + src: ['test/less/errors/*.less', '!test/less/errors/javascript-error.less'], options: { timeout: 20000, helpers: 'test/browser/runner-errors-options.js', specs: 'test/browser/runner-errors-spec.js', - outfile: 'test/browser/test-runner-errors.html' + outfile: 'tmp/browser/test-runner-errors.html' } }, - noJsErrors: { - src: ['test/less/no-js-errors/*.less'], + noJsErrors: { + src: ['test/less/no-js-errors/*.less'], options: { helpers: 'test/browser/runner-no-js-errors-options.js', specs: 'test/browser/runner-no-js-errors-spec.js', - outfile: 'test/browser/test-runner-no-js-errors.html' + outfile: 'tmp/browser/test-runner-no-js-errors.html' } }, browser: { - src: ['test/browser/less/*.less'], + src: ['test/browser/less/*.less'], options: { helpers: 'test/browser/runner-browser-options.js', specs: 'test/browser/runner-browser-spec.js', - outfile: 'test/browser/test-runner-browser.html' + outfile: 'tmp/browser/test-runner-browser.html' } }, relativeUrls: { - src: ['test/browser/less/relative-urls/*.less'], + src: ['test/browser/less/relative-urls/*.less'], options: { helpers: 'test/browser/runner-relative-urls-options.js', specs: 'test/browser/runner-relative-urls-spec.js', - outfile: 'test/browser/test-runner-relative-urls.html' + outfile: 'tmp/browser/test-runner-relative-urls.html' } }, rootpath: { - src: ['test/browser/less/rootpath/*.less'], + src: ['test/browser/less/rootpath/*.less'], options: { helpers: 'test/browser/runner-rootpath-options.js', specs: 'test/browser/runner-rootpath-spec.js', - outfile: 'test/browser/test-runner-rootpath.html' + outfile: 'tmp/browser/test-runner-rootpath.html' } }, rootpathRelative: { - src: ['test/browser/less/rootpath-relative/*.less'], + src: ['test/browser/less/rootpath-relative/*.less'], options: { helpers: 'test/browser/runner-rootpath-relative-options.js', specs: 'test/browser/runner-rootpath-relative-spec.js', - outfile: 'test/browser/test-runner-rootpath-relative.html' + outfile: 'tmp/browser/test-runner-rootpath-relative.html' } }, production: { - src: ['test/browser/less/production/*.less'], + src: ['test/browser/less/production/*.less'], options: { helpers: 'test/browser/runner-production-options.js', specs: 'test/browser/runner-production-spec.js', - outfile: 'test/browser/test-runner-production.html' + outfile: 'tmp/browser/test-runner-production.html' } }, modifyVars: { - src: ['test/browser/less/modify-vars/*.less'], + src: ['test/browser/less/modify-vars/*.less'], options: { helpers: 'test/browser/runner-modify-vars-options.js', specs: 'test/browser/runner-modify-vars-spec.js', - outfile: 'test/browser/test-runner-modify-vars.html' + outfile: 'tmp/browser/test-runner-modify-vars.html' } } }, // Before running tests, clean out the results // of any previous tests. (this will need to be - // setup based on configuration of browser tests. + // setup based on configuration of browser tests). clean: { test: ['test/browser/test-runner-*.htm'] } @@ -224,31 +220,16 @@ module.exports = function(grunt) { // Default task to build Less.js grunt.registerTask('default', [ - 'browser', - 'rhino' - ]); - - // Browser - grunt.registerTask('browser', [ 'concat:browser', 'uglify:browser' ]); - // Rhino - grunt.registerTask('rhino', [ - 'concat:rhino' - // 'uglify:rhino' - ]); - // Minify - grunt.registerTask('min', [ - 'uglify' - ]); - // Alpha grunt.registerTask('alpha', [ 'concat:alpha', 'uglify:alpha' ]); + // Beta grunt.registerTask('beta', [ 'concat:beta', @@ -256,17 +237,17 @@ module.exports = function(grunt) { ]); // Run all tests - grunt.registerTask('browserTest', [ - 'connect:server', + grunt.registerTask('browser', [ + 'connect', 'jasmine' ]); // Run all tests grunt.registerTask('test', [ - 'jshint:lib', 'clean', + 'jshint', 'shell:test', - 'browserTest' + 'browser' ]); // Run benchmark diff --git a/README.md b/README.md index 286b8fd1..6783ffc6 100644 --- a/README.md +++ b/README.md @@ -327,5 +327,4 @@ Licensed under the [Apache License](LICENSE). [so]: http://stackoverflow.com/questions/tagged/twitter-bootstrap+less "StackOverflow.com" [issues]: https://github.com/less/less.js/issues "GitHub Issues for Less.js" [wiki]: https://github.com/less/less.js/wiki "The official wiki for Less.js" -[download]: https://github.com/less/less.js/zipball/master "Download Less.js" -})(window); \ No newline at end of file +[download]: https://github.com/less/less.js/zipball/master "Download Less.js" \ No newline at end of file From ab43e5cd2cc4ca41ded642599b050f0c5456a5c5 Mon Sep 17 00:00:00 2001 From: Jon Schlinkert Date: Wed, 21 Aug 2013 16:34:34 -0400 Subject: [PATCH 36/36] update metadata and build.yml to reflect latest changes in 1.5.0 branch --- build/build.yml | 45 ++++++++++++++++++++------------------------ build/tasks/.gitkeep | 1 + package.json | 34 +++++++++++++++++---------------- 3 files changed, 39 insertions(+), 41 deletions(-) create mode 100644 build/tasks/.gitkeep diff --git a/build/build.yml b/build/build.yml index 26a5ebd3..b4f28bd7 100644 --- a/build/build.yml +++ b/build/build.yml @@ -19,7 +19,7 @@ lib: lib/less # General # ================================= prepend: - browser: [build/require.js, build/browser-header.js] + browser: ['build/require.js', 'build/browser-header.js'] rhino: build/require-rhino.js append: @@ -34,22 +34,19 @@ append: # <%= build.less.* %> less: - parser : <%= build.lib %>/parser.js - functions: <%= build.lib %>/functions.js - colors : <%= build.lib %>/colors.js - tree : <%= build.lib %>/tree.js - env : <%= build.lib %>/env.js - visitor : <%= build.lib %>/visitor.js - import : <%= build.lib %>/import-visitor.js - join : <%= build.lib %>/join-selector-visitor.js - to_css : <%= build.lib %>/to-css-visitor.js - extend : <%= build.lib %>/extend-visitor.js - browser : <%= build.lib %>/browser.js - source_map_output : <%= build.lib %>/source-map-output.js - to_css_visitor : <%= build.lib %>/to-css-visitor.js - - # glob all files in ./lib/less/tree directory - treedir : <%= build.lib %>/tree/*.js + parser : <%= build.lib %>/parser.js + functions : <%= build.lib %>/functions.js + colors : <%= build.lib %>/colors.js + tree : <%= build.lib %>/tree.js + treedir : <%= build.lib %>/tree/*.js # glob all files in ./lib/less/tree directory + env : <%= build.lib %>/env.js + visitor : <%= build.lib %>/visitor.js + import_visitor : <%= build.lib %>/import-visitor.js + join : <%= build.lib %>/join-selector-visitor.js + to_css_visitor : <%= build.lib %>/to-css-visitor.js + extend_visitor : <%= build.lib %>/extend-visitor.js + browser : <%= build.lib %>/browser.js + source_map_output: <%= build.lib %>/source-map-output.js # ================================= @@ -70,12 +67,11 @@ browser: - <%= build.less.tree %> - <%= build.less.env %> - <%= build.less.visitor %> - - <%= build.less.import %> + - <%= build.less.import_visitor %> - <%= build.less.join %> - - <%= build.less.to_css %> - - <%= build.less.extend %> - - <%= build.less.source_map_output %> - <%= build.less.to_css_visitor %> + - <%= build.less.extend_visitor %> + - <%= build.less.source_map_output %> # append browser-specific code - <%= build.append.browser %> @@ -95,16 +91,15 @@ rhino: - <%= build.less.parser %> - <%= build.less.env %> - <%= build.less.visitor %> - - <%= build.less.import %> + - <%= build.less.import_visitor %> - <%= build.less.join %> - - <%= build.less.to_css %> - - <%= build.less.extend %> + - <%= build.less.to_css_visitor %> + - <%= build.less.extend_visitor %> - <%= build.less.functions %> - <%= build.less.colors %> - <%= build.less.treedir %> # glob all files - <%= build.less.tree %> - <%= build.less.source_map_output %> - - <%= build.less.to_css_visitor %> # append rhino-specific code - <%= build.append.rhino %> diff --git a/build/tasks/.gitkeep b/build/tasks/.gitkeep new file mode 100644 index 00000000..9a69c4cb --- /dev/null +++ b/build/tasks/.gitkeep @@ -0,0 +1 @@ +# Reserved for specialized Less.js tasks. \ No newline at end of file diff --git a/package.json b/package.json index 10ae85de..831baca4 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,10 @@ "version": "1.4.2", "description": "Leaner CSS", "homepage": "http://lesscss.org", - "author": "Alexis Sellier ", + "author": { + "name": "Alexis Sellier", + "email": "self@cloudhead.net" + }, "contributors": [ "The Core Less Team" ], @@ -14,6 +17,12 @@ "type": "git", "url": "https://github.com/less/less.js.git" }, + "licenses": [ + { + "type": "Apache v2", + "url": "https://github.com/less/less.js/blob/master/LICENSE" + } + ], "bin": { "lessc": "./bin/lessc" }, @@ -28,8 +37,7 @@ "node": ">=0.4.2" }, "scripts": { - "pretest": "make jshint", - "test": "make test" + "test": "grunt test" }, "optionalDependencies": { "mime": "1.2.x", @@ -40,18 +48,18 @@ }, "devDependencies": { "diff": "~1.0", - "jshint": "~2.1.4", - "http-server": "~0.5.5", "grunt": "~0.4.1", - "matchdep": "~0.1.2", "grunt-contrib-clean": "~0.5.0", "grunt-contrib-concat": "~0.3.0", + "grunt-contrib-connect": "~0.3.0", + "grunt-contrib-jasmine": "~0.5.1", "grunt-contrib-jshint": "~0.6.0", "grunt-contrib-uglify": "~0.2.2", "grunt-shell": "~0.3.1", - "grunt-contrib-connect": "~0.3.0", - "grunt-contrib-jasmine": "~0.5.1" - }, + "http-server": "~0.5.5", + "matchdep": "~0.1.2", + "time-grunt": "~0.1.1" + }, "keywords": [ "compile less", "css nesting", @@ -76,11 +84,5 @@ "stylesheet", "variables in css", "css less" - ], - "licenses": [ - { - "type": "Apache v2", - "url": "https://github.com/less/less.js/blob/master/LICENSE" - } ] -} +} \ No newline at end of file