├── .gitignore ├── README.md ├── pom.xml └── src ├── it ├── basic │ ├── invoker.properties │ ├── pom.xml │ ├── src │ │ └── main │ │ │ └── less │ │ │ └── test.less │ └── verify.groovy ├── custom-lessjs │ ├── invoker.properties │ ├── pom.xml │ ├── src │ │ └── main │ │ │ └── webapp │ │ │ ├── js │ │ │ └── less-rhino-1.6.2.js │ │ │ └── less │ │ │ └── test.less │ └── verify.groovy ├── excludes │ ├── invoker.properties │ ├── pom.xml │ ├── src │ │ └── main │ │ │ └── less │ │ │ ├── exclude.less │ │ │ └── test.less │ └── verify.groovy ├── filename-suffix │ ├── invoker.properties │ ├── pom.xml │ ├── src │ │ └── main │ │ │ └── less │ │ │ └── test.less │ └── verify.groovy ├── includes │ ├── invoker.properties │ ├── pom.xml │ ├── src │ │ └── main │ │ │ └── less │ │ │ ├── include.less │ │ │ └── test.less │ └── verify.groovy ├── list │ ├── invoker.properties │ ├── pom.xml │ ├── src │ │ └── main │ │ │ └── less │ │ │ ├── import2 │ │ │ ├── import2a │ │ │ │ └── less1import2a1.less │ │ │ ├── less1import2a.less │ │ │ └── less1import2b.less │ │ │ ├── less1.less │ │ │ ├── less1import1.less │ │ │ ├── less1import1a.less │ │ │ ├── less1import2.less │ │ │ ├── less1import3.less │ │ │ └── less2.less │ └── verify.groovy ├── nodejs │ ├── invoker.properties │ ├── pom.xml │ ├── src │ │ └── main │ │ │ └── less │ │ │ └── test.less │ └── verify.groovy ├── settings.xml ├── skip │ ├── invoker.properties │ ├── pom.xml │ └── verify.groovy └── webapp │ ├── invoker.properties │ ├── pom.xml │ ├── src │ └── main │ │ └── webapp │ │ └── less │ │ └── test.less │ └── verify.groovy ├── main ├── java │ └── org │ │ └── lesscss │ │ └── mojo │ │ ├── AbstractLessCssMojo.java │ │ ├── CompileMojo.java │ │ ├── ListMojo.java │ │ └── NodeJsLessCompiler.java └── resources │ ├── META-INF │ └── m2e │ │ └── lifecycle-mapping-metadata.xml │ └── org │ └── lesscss │ └── mojo │ └── js │ ├── less │ ├── browser.js │ ├── colors.js │ ├── env.js │ ├── extend-visitor.js │ ├── functions.js │ ├── import-visitor.js │ ├── index.js │ ├── join-selector-visitor.js │ ├── lessc_helper.js │ ├── parser.js │ ├── rhino.js │ ├── source-map-output.js │ ├── to-css-visitor.js │ ├── tree.js │ ├── tree │ │ ├── alpha.js │ │ ├── anonymous.js │ │ ├── assignment.js │ │ ├── call.js │ │ ├── color.js │ │ ├── comment.js │ │ ├── condition.js │ │ ├── dimension.js │ │ ├── directive.js │ │ ├── element.js │ │ ├── expression.js │ │ ├── extend.js │ │ ├── import.js │ │ ├── javascript.js │ │ ├── keyword.js │ │ ├── media.js │ │ ├── mixin.js │ │ ├── negative.js │ │ ├── operation.js │ │ ├── paren.js │ │ ├── quoted.js │ │ ├── ratio.js │ │ ├── rule.js │ │ ├── ruleset.js │ │ ├── selector.js │ │ ├── unicode-descriptor.js │ │ ├── url.js │ │ ├── value.js │ │ └── variable.js │ └── visitor.js │ └── lessc.js └── test ├── java └── org │ └── lesscss │ └── mojo │ ├── AbstractLessCssMojoTest.java │ ├── CompileMojoTest.java │ ├── CompileOnChangeMojoTest.java │ └── ListMojoTest.java └── resources ├── less ├── 1 │ └── reset.less ├── 2 │ └── 21 │ │ └── variables.less └── bootstrap.less └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .classpath 3 | .project 4 | .settings/ 5 | target/ 6 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Official LESS CSS Maven Plugin 2 | ============================== 3 | 4 | **Latest release** 1.7.0.1.1 - The 1.1 release that is compatible with less 1.7.0 5 | 6 | 7 | Usage 8 | ----- 9 | 10 | Declare the plugin and its goals. The process-sources phase is bound to by default: 11 | 12 | 13 | org.lesscss 14 | lesscss-maven-plugin 15 | 1.7.0.1.1 16 | 17 | 18 | 19 | compile 20 | 21 | 22 | 23 | 24 | 25 | To run the compiler manually just execute: mvn lesscss:compile 26 | 27 | 28 | Example configuration for web project 29 | ------------------------------------- 30 | 31 | 32 | org.lesscss 33 | lesscss-maven-plugin 34 | 1.7.0.1.1 35 | 36 | ${project.basedir}/src/main/webapp/less 37 | ${project.build.directory}/${project.build.finalName}/css 38 | true 39 | 40 | main.less 41 | 42 | 43 | 44 | 45 | 46 | compile 47 | 48 | 49 | 50 | 51 | 52 | 53 | All configuration options 54 | ------------------------- 55 | 56 | + **outputDirectory** `File` - The directory for compiled CSS stylesheets. Default value is: ${project.build.directory}. 57 | 58 | + **sourceDirectory** `File` - The source directory containing the LESS sources. Default value is: ${project.basedir}/src/main/less. 59 | 60 | + **compress** `boolean` - When true the LESS compiler will compress the CSS stylesheets. Default value is: false. 61 | + **encoding** `String` The character encoding the LESS compiler will use for writing the CSS stylesheets. Default value is: ${project.build.sourceEncoding}. 62 | 63 | + **excludes** `String[]` - List of files to exclude. Specified as fileset patterns which are relative to the source directory. 64 | 65 | + **force** `boolean` - When true forces the LESS compiler to always compile the LESS sources. By default LESS sources are only compiled when modified (including imports) or the CSS stylesheet does not exists. Default value is: false. 66 | 67 | + **includes** `String[]` - List of files to include. Specified as fileset patterns which are relative to the source directory. Default value is: { "**\/*.less" } 68 | 69 | + **lessJs** `String` - The location of the LESS JavaScript file. 70 | 71 | + **watch** `boolean` - When true the plugin watches the sourceDirectory and recompiles the included files after they changed. Instead of configuring it in the pom you can use that option at the command line like this "mvn lesscss:compile -Dlesscss.watch=true". Then it doesn't interfere with other maven lifecycle phases and you can just kill the watch process e.g. with crtl-c. Default value is: false. 72 | 73 | + **watchInterval** `int` - The interval in milliseconds the plugin waits between the check for file changes. Default value is: 1000 ms. 74 | 75 | + **outputFileFormat** `String` - The format of the output files. Default is: '{fileName}.css'. examples: '{fileName}.min.css', 'min-{fileName}.css', '{fileName}.anything'. 76 | 77 | List sources 78 | ------------ 79 | 80 | To list the LESS sources in your project the lesscss:list goal can be used. It lists the LESS sources and it's imports based on sourceDirectory and optionally includes and excludes configuration options. 81 | 82 | 83 | Support 84 | ------- 85 | 86 | Have a question, or found an issue? Just create a issue: https://github.com/marceloverdijk/lesscss-maven-plugin/issues 87 | 88 | 89 | Authors 90 | ------- 91 | 92 | **Marcel Overdijk** 93 | 94 | + marcel@overdijk.me 95 | + http://twitter.com/marceloverdijk 96 | + http://github.com/marceloverdijk 97 | 98 | **Christophe Popov** 99 | 100 | + http://twitter.com/chpopov 101 | + http://uk.linkedin.com/in/hpopov/ 102 | 103 | 104 | Copyright and License 105 | --------------------- 106 | 107 | Copyright 2012 Marcel Overdijk 108 | 109 | Licensed under the Apache License, Version 2.0 (the "License"); 110 | you may not use this file except in compliance with the License. 111 | You may obtain a copy of the License at 112 | 113 | http://www.apache.org/licenses/LICENSE-2.0 114 | 115 | Unless required by applicable law or agreed to in writing, software 116 | distributed under the License is distributed on an "AS IS" BASIS, 117 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 118 | See the License for the specific language governing permissions and 119 | limitations under the License. 120 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss 5 | lesscss-maven-plugin 6 | 1.7.0.1.2-SNAPSHOT 7 | maven-plugin 8 | Official LESS CSS Maven Plugin 9 | Official LESS CSS Maven Plugin 10 | http://github.com/marceloverdijk/lesscss-maven-plugin 11 | 12 | 13 | org.sonatype.oss 14 | oss-parent 15 | 7 16 | 17 | 18 | 19 | UTF-8 20 | UTF-8 21 | 22 | 23 | 24 | 25 | commons-logging 26 | commons-logging 27 | 1.1.1 28 | 29 | 30 | commons-io 31 | commons-io 32 | 2.4 33 | 34 | 35 | junit 36 | junit 37 | 4.10 38 | test 39 | 40 | 41 | org.slf4j 42 | slf4j-api 43 | 1.7.2 44 | test 45 | 46 | 47 | org.apache.maven 48 | maven-plugin-api 49 | 2.0 50 | 51 | 52 | org.apache.maven.plugin-testing 53 | maven-plugin-testing-harness 54 | 1.3 55 | test 56 | 57 | 58 | slf4j-jdk14 59 | org.slf4j 60 | 61 | 62 | slf4j-nop 63 | org.slf4j 64 | 65 | 66 | 67 | 68 | org.codehaus.plexus 69 | plexus-utils 70 | 3.0 71 | 72 | 73 | org.lesscss 74 | lesscss 75 | 1.7.0.1.1 76 | 77 | 78 | org.mockito 79 | mockito-core 80 | 1.9.0 81 | test 82 | 83 | 84 | org.powermock 85 | powermock-module-junit4 86 | 1.4.11 87 | test 88 | 89 | 90 | org.powermock 91 | powermock-api-mockito 92 | 1.4.11 93 | test 94 | 95 | 96 | org.sonatype.plexus 97 | plexus-build-api 98 | 0.0.7 99 | 100 | 101 | 102 | 103 | 104 | 105 | org.apache.maven.plugins 106 | maven-compiler-plugin 107 | 2.3.2 108 | 109 | UTF-8 110 | 1.5 111 | 1.5 112 | 113 | 114 | 115 | org.apache.maven.plugins 116 | maven-invoker-plugin 117 | 1.5 118 | 119 | ${project.build.directory}/it 120 | true 121 | ${project.build.directory}/local-repo 122 | verify 123 | src/it 124 | src/it/settings.xml 125 | 129 | 130 | 131 | 132 | integration-test 133 | 134 | install 135 | run 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | The Apache Software License, Version 2.0 146 | http://www.apache.org/licenses/LICENSE-2.0.txt 147 | repo 148 | 149 | 150 | 151 | 152 | 153 | marceloverdijk 154 | Marcel Overdijk 155 | marcel@overdijk.me 156 | 157 | Lead Developer 158 | 159 | +2 160 | 161 | 162 | cpopov 163 | Christophe Popov 164 | chrpopov.gmail.com 165 | http://uk.linkedin.com/in/hpopov/ 166 | 167 | Developer 168 | 169 | 0 170 | 171 | 172 | 173 | 174 | GitHub 175 | http://github.com/marceloverdijk/lesscss-maven-plugin/issues 176 | 177 | 178 | 179 | scm:git:git@github.com:marceloverdijk/lesscss-maven-plugin.git 180 | scm:git:git@github.com:marceloverdijk/lesscss-maven-plugin.git 181 | http://github.com/marceloverdijk/lesscss-maven-plugin 182 | 183 | 184 | 185 | 2.0 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /src/it/basic/invoker.properties: -------------------------------------------------------------------------------- 1 | invoker.goals = clean compile -------------------------------------------------------------------------------- /src/it/basic/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | basic 6 | testing 7 | 8 | 9 | 10 | 11 | org.lesscss 12 | lesscss-maven-plugin 13 | @project.version@ 14 | 15 | 16 | 17 | compile 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/it/basic/src/main/less/test.less: -------------------------------------------------------------------------------- 1 | @color: #4d926f; 2 | 3 | #header { 4 | color: @color; 5 | } 6 | h2 { 7 | color: @color; 8 | } -------------------------------------------------------------------------------- /src/it/basic/verify.groovy: -------------------------------------------------------------------------------- 1 | expected = """#header { 2 | color: #4d926f; 3 | } 4 | h2 { 5 | color: #4d926f; 6 | } 7 | 8 | """ 9 | 10 | css = new File(basedir, "target/test.css") 11 | assert css.exists() 12 | assert css.getText().equals(expected) 13 | -------------------------------------------------------------------------------- /src/it/custom-lessjs/invoker.properties: -------------------------------------------------------------------------------- 1 | invoker.goals = clean compile -------------------------------------------------------------------------------- /src/it/custom-lessjs/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | custom-lessjs 6 | testing 7 | 8 | 9 | 10 | 11 | org.lesscss 12 | lesscss-maven-plugin 13 | @project.version@ 14 | 15 | ${project.basedir}/src/main/webapp/js/less-rhino-1.6.2.js 16 | ${project.basedir}/src/main/webapp/less 17 | ${project.build.directory}/${project.build.finalName}/css 18 | 19 | 20 | 21 | 22 | compile 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/it/custom-lessjs/src/main/webapp/less/test.less: -------------------------------------------------------------------------------- 1 | @color: #4d926f; 2 | 3 | #header { 4 | color: @color; 5 | } 6 | h2 { 7 | color: @color; 8 | } -------------------------------------------------------------------------------- /src/it/custom-lessjs/verify.groovy: -------------------------------------------------------------------------------- 1 | expected = """#header { 2 | color: #4d926f; 3 | } 4 | h2 { 5 | color: #4d926f; 6 | } 7 | 8 | """ 9 | 10 | css = new File(basedir, "target/custom-lessjs-testing/css/test.css") 11 | assert css.exists() 12 | assert css.getText().equals(expected) -------------------------------------------------------------------------------- /src/it/excludes/invoker.properties: -------------------------------------------------------------------------------- 1 | invoker.goals = clean compile -------------------------------------------------------------------------------- /src/it/excludes/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | excludes 6 | testing 7 | 8 | 9 | 10 | 11 | org.lesscss 12 | lesscss-maven-plugin 13 | @project.version@ 14 | 15 | 16 | exclude.less 17 | 18 | 19 | 20 | 21 | 22 | compile 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/it/excludes/src/main/less/exclude.less: -------------------------------------------------------------------------------- 1 | @color: #4d926f; 2 | 3 | #header { 4 | color: @color; 5 | } 6 | h2 { 7 | color: @color; 8 | } -------------------------------------------------------------------------------- /src/it/excludes/src/main/less/test.less: -------------------------------------------------------------------------------- 1 | @color: #4d926f; 2 | 3 | #header { 4 | color: @color; 5 | } 6 | h2 { 7 | color: @color; 8 | } -------------------------------------------------------------------------------- /src/it/excludes/verify.groovy: -------------------------------------------------------------------------------- 1 | assert new File(basedir, "target/test.css").exists() 2 | assert !new File(basedir, "target/exclude.css").exists() 3 | -------------------------------------------------------------------------------- /src/it/filename-suffix/invoker.properties: -------------------------------------------------------------------------------- 1 | invoker.goals = clean compile -------------------------------------------------------------------------------- /src/it/filename-suffix/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | filename-suffix 6 | testing 7 | 8 | 9 | 10 | 11 | org.lesscss 12 | lesscss-maven-plugin 13 | @project.version@ 14 | 15 | min-{fileName}-1.33.7.css 16 | 17 | 18 | 19 | 20 | compile 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/it/filename-suffix/src/main/less/test.less: -------------------------------------------------------------------------------- 1 | @color: #4d926f; 2 | 3 | #header { 4 | color: @color; 5 | } 6 | h2 { 7 | color: @color; 8 | } -------------------------------------------------------------------------------- /src/it/filename-suffix/verify.groovy: -------------------------------------------------------------------------------- 1 | expected = """#header { 2 | color: #4d926f; 3 | } 4 | h2 { 5 | color: #4d926f; 6 | } 7 | 8 | """ 9 | 10 | css = new File(basedir, "target/min-test-1.33.7.css") 11 | assert css.exists() 12 | assert css.getText().equals(expected) 13 | -------------------------------------------------------------------------------- /src/it/includes/invoker.properties: -------------------------------------------------------------------------------- 1 | invoker.goals = clean compile -------------------------------------------------------------------------------- /src/it/includes/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | includes 6 | testing 7 | 8 | 9 | 10 | 11 | org.lesscss 12 | lesscss-maven-plugin 13 | @project.version@ 14 | 15 | 16 | include.less 17 | 18 | 19 | 20 | 21 | 22 | compile 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/it/includes/src/main/less/include.less: -------------------------------------------------------------------------------- 1 | @color: #4d926f; 2 | 3 | #header { 4 | color: @color; 5 | } 6 | h2 { 7 | color: @color; 8 | } -------------------------------------------------------------------------------- /src/it/includes/src/main/less/test.less: -------------------------------------------------------------------------------- 1 | @color: #4d926f; 2 | 3 | #header { 4 | color: @color; 5 | } 6 | h2 { 7 | color: @color; 8 | } -------------------------------------------------------------------------------- /src/it/includes/verify.groovy: -------------------------------------------------------------------------------- 1 | assert !new File(basedir, "target/test.css").exists() 2 | assert new File(basedir, "target/include.css").exists() 3 | -------------------------------------------------------------------------------- /src/it/list/invoker.properties: -------------------------------------------------------------------------------- 1 | invoker.goals = clean lesscss:list -------------------------------------------------------------------------------- /src/it/list/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | list 6 | testing 7 | 8 | 9 | 10 | 11 | org.lesscss 12 | lesscss-maven-plugin 13 | @project.version@ 14 | 15 | 16 | less1.less 17 | less2.less 18 | 19 | 20 | 21 | 22 | 23 | compile 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/it/list/src/main/less/import2/import2a/less1import2a1.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marceloverdijk/lesscss-maven-plugin/0ce62351150c0cb3d2e1b6e413d29bbd9ddab4cd/src/it/list/src/main/less/import2/import2a/less1import2a1.less -------------------------------------------------------------------------------- /src/it/list/src/main/less/import2/less1import2a.less: -------------------------------------------------------------------------------- 1 | @import "import2a/less1import2a1.less"; -------------------------------------------------------------------------------- /src/it/list/src/main/less/import2/less1import2b.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marceloverdijk/lesscss-maven-plugin/0ce62351150c0cb3d2e1b6e413d29bbd9ddab4cd/src/it/list/src/main/less/import2/less1import2b.less -------------------------------------------------------------------------------- /src/it/list/src/main/less/less1.less: -------------------------------------------------------------------------------- 1 | @import "less1import1.less"; 2 | @import "less1import2.less"; 3 | @import "less1import3.less"; -------------------------------------------------------------------------------- /src/it/list/src/main/less/less1import1.less: -------------------------------------------------------------------------------- 1 | @import "less1import1a.less"; -------------------------------------------------------------------------------- /src/it/list/src/main/less/less1import1a.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marceloverdijk/lesscss-maven-plugin/0ce62351150c0cb3d2e1b6e413d29bbd9ddab4cd/src/it/list/src/main/less/less1import1a.less -------------------------------------------------------------------------------- /src/it/list/src/main/less/less1import2.less: -------------------------------------------------------------------------------- 1 | @import "import2/less1import2a.less"; 2 | @import "import2/less1import2b.less"; -------------------------------------------------------------------------------- /src/it/list/src/main/less/less1import3.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marceloverdijk/lesscss-maven-plugin/0ce62351150c0cb3d2e1b6e413d29bbd9ddab4cd/src/it/list/src/main/less/less1import3.less -------------------------------------------------------------------------------- /src/it/list/src/main/less/less2.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marceloverdijk/lesscss-maven-plugin/0ce62351150c0cb3d2e1b6e413d29bbd9ddab4cd/src/it/list/src/main/less/less2.less -------------------------------------------------------------------------------- /src/it/list/verify.groovy: -------------------------------------------------------------------------------- 1 | log = new File(basedir, "build.log") 2 | assert log.getText().contains("The following LESS sources have been resolved:"); 3 | assert log.getText().contains("less1.less"); 4 | assert log.getText().contains("|-- less1import1.less"); 5 | assert log.getText().contains("| `-- less1import1a.less"); 6 | assert log.getText().contains("|-- less1import2.less"); 7 | assert log.getText().contains("| |-- import2/less1import2a.less"); 8 | assert log.getText().contains("| | `-- import2a/less1import2a1.less"); 9 | assert log.getText().contains("| `-- import2/less1import2b.less"); 10 | assert log.getText().contains("`-- less1import3.less"); 11 | assert log.getText().contains("less2.less"); -------------------------------------------------------------------------------- /src/it/nodejs/invoker.properties: -------------------------------------------------------------------------------- 1 | invoker.goals = clean compile -------------------------------------------------------------------------------- /src/it/nodejs/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | nodejs 6 | testing 7 | 8 | 9 | 10 | 11 | com.github.skwakman.nodejs-maven-plugin 12 | nodejs-maven-plugin 13 | 1.0.3 14 | 15 | 16 | initialize 17 | 18 | extract 19 | 20 | 21 | 22 | 23 | ${project.build.directory}/nodejs 24 | 25 | 26 | 27 | org.lesscss 28 | lesscss-maven-plugin 29 | @project.version@ 30 | 31 | 32 | 33 | compile 34 | 35 | 36 | 37 | 38 | ${project.build.directory}/nodejs/node 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/it/nodejs/src/main/less/test.less: -------------------------------------------------------------------------------- 1 | @color: #4d926f; 2 | 3 | #header { 4 | color: @color; 5 | } 6 | h2 { 7 | color: @color; 8 | } -------------------------------------------------------------------------------- /src/it/nodejs/verify.groovy: -------------------------------------------------------------------------------- 1 | expected = """#header { 2 | color: #4d926f; 3 | } 4 | h2 { 5 | color: #4d926f; 6 | } 7 | """ 8 | 9 | css = new File(basedir, "target/test.css") 10 | assert css.exists() 11 | assert css.getText().equals(expected) 12 | -------------------------------------------------------------------------------- /src/it/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | it-repo 5 | 6 | true 7 | 8 | 9 | 10 | local.central 11 | @localRepositoryUrl@ 12 | 13 | true 14 | 15 | 16 | true 17 | 18 | 19 | 20 | 21 | 22 | local.central 23 | @localRepositoryUrl@ 24 | 25 | true 26 | 27 | 28 | true 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/it/skip/invoker.properties: -------------------------------------------------------------------------------- 1 | invoker.goals = clean compile -------------------------------------------------------------------------------- /src/it/skip/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | skip 6 | testing 7 | 8 | 9 | true 10 | 11 | 12 | 13 | 14 | org.lesscss 15 | lesscss-maven-plugin 16 | @project.version@ 17 | 18 | 19 | 20 | compile 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/it/skip/verify.groovy: -------------------------------------------------------------------------------- 1 | log = new File(basedir, "build.log") 2 | assert log.getText().contains("Skipping plugin execution per configuration"); -------------------------------------------------------------------------------- /src/it/webapp/invoker.properties: -------------------------------------------------------------------------------- 1 | invoker.goals = clean compile -------------------------------------------------------------------------------- /src/it/webapp/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | webapp 6 | testing 7 | 8 | 9 | 10 | 11 | org.lesscss 12 | lesscss-maven-plugin 13 | @project.version@ 14 | 15 | ${project.basedir}/src/main/webapp/less 16 | ${project.build.directory}/${project.build.finalName}/css 17 | 18 | 19 | 20 | 21 | compile 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/it/webapp/src/main/webapp/less/test.less: -------------------------------------------------------------------------------- 1 | @color: #4d926f; 2 | 3 | #header { 4 | color: @color; 5 | } 6 | h2 { 7 | color: @color; 8 | } -------------------------------------------------------------------------------- /src/it/webapp/verify.groovy: -------------------------------------------------------------------------------- 1 | expected = """#header { 2 | color: #4d926f; 3 | } 4 | h2 { 5 | color: #4d926f; 6 | } 7 | 8 | """ 9 | 10 | css = new File(basedir, "target/webapp-testing/css/test.css") 11 | assert css.exists() 12 | assert css.getText().equals(expected) 13 | -------------------------------------------------------------------------------- /src/main/java/org/lesscss/mojo/AbstractLessCssMojo.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011-2012 The Apache Software Foundation. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.lesscss.mojo; 16 | 17 | import java.io.File; 18 | 19 | import org.apache.maven.plugin.AbstractMojo; 20 | import org.codehaus.plexus.util.Scanner; 21 | import org.sonatype.plexus.build.incremental.BuildContext; 22 | 23 | /** 24 | * Abstract class which provides common configuration properties and methods. 25 | * 26 | * @author Marcel Overdijk 27 | */ 28 | public abstract class AbstractLessCssMojo extends AbstractMojo { 29 | 30 | /** @component */ 31 | protected BuildContext buildContext; 32 | 33 | /** 34 | * The source directory containing the LESS sources. 35 | * 36 | * @parameter expression="${lesscss.sourceDirectory}" default-value="${project.basedir}/src/main/less" 37 | * @required 38 | */ 39 | protected File sourceDirectory; 40 | 41 | /** 42 | * List of files to include. Specified as fileset patterns which are relative to the source directory. Default value is: { "**\/*.less" } 43 | * 44 | * @parameter 45 | */ 46 | protected String[] includes = new String[] { "**/*.less" }; 47 | 48 | /** 49 | * List of files to exclude. Specified as fileset patterns which are relative to the source directory. 50 | * 51 | * @parameter 52 | */ 53 | protected String[] excludes = new String[] {}; 54 | 55 | /** 56 | * Scans for the LESS sources that should be compiled. 57 | * 58 | * @return The list of LESS sources. 59 | */ 60 | protected String[] getIncludedFiles() { 61 | Scanner scanner = buildContext.newScanner(sourceDirectory, true); 62 | scanner.setIncludes(includes); 63 | scanner.setExcludes(excludes); 64 | scanner.scan(); 65 | return scanner.getIncludedFiles(); 66 | } 67 | 68 | /** 69 | * Whether to skip plugin execution. 70 | * This makes the build more controllable from profiles. 71 | * 72 | * @parameter expression="${lesscss.skip}" default-value="false" 73 | */ 74 | protected boolean skip; 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/lesscss/mojo/CompileMojo.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011-2012 The Apache Software Foundation. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.lesscss.mojo; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.net.MalformedURLException; 20 | import java.util.Arrays; 21 | 22 | import org.apache.maven.plugin.MojoExecutionException; 23 | import org.codehaus.plexus.util.StringUtils; 24 | import org.lesscss.LessCompiler; 25 | import org.lesscss.LessException; 26 | import org.lesscss.LessSource; 27 | import org.sonatype.plexus.build.incremental.BuildContext; 28 | 29 | /** 30 | * Goal which compiles the LESS sources to CSS stylesheets. 31 | * 32 | * @author Marcel Overdijk 33 | * @goal compile 34 | * @phase process-sources 35 | */ 36 | public class CompileMojo extends AbstractLessCssMojo { 37 | 38 | /** 39 | * The directory for compiled CSS stylesheets. 40 | * 41 | * @parameter expression="${lesscss.outputDirectory}" default-value="${project.build.directory}" 42 | * @required 43 | */ 44 | protected File outputDirectory; 45 | 46 | /** 47 | * When true the LESS compiler will compress the CSS stylesheets. 48 | * 49 | * @parameter expression="${lesscss.compress}" default-value="false" 50 | */ 51 | private boolean compress; 52 | 53 | /** 54 | * When true the plugin will watch for changes in LESS files and compile if it detects one. 55 | * 56 | * @parameter expression="${lesscss.watch}" default-value="false" 57 | */ 58 | protected boolean watch=false; 59 | 60 | /** 61 | * When true the plugin will watch for changes in LESS files and compile if it detects one. 62 | * 63 | * @parameter expression="${lesscss.watchInterval}" default-value="1000" 64 | */ 65 | private int watchInterval=1000; 66 | 67 | /** 68 | * The character encoding the LESS compiler will use for writing the CSS stylesheets. 69 | * 70 | * @parameter expression="${lesscss.encoding}" default-value="${project.build.sourceEncoding}" 71 | */ 72 | private String encoding; 73 | 74 | /** 75 | * When true forces the LESS compiler to always compile the LESS sources. By default LESS sources are only compiled when modified (including imports) or the CSS stylesheet does not exists. 76 | * 77 | * @parameter expression="${lesscss.force}" default-value="false" 78 | */ 79 | private boolean force; 80 | 81 | /** 82 | * The location of the LESS JavasSript file. 83 | * 84 | * @parameter 85 | */ 86 | private File lessJs; 87 | 88 | /** 89 | * The location of the NodeJS executable. 90 | * 91 | * @parameter 92 | */ 93 | private String nodeExecutable; 94 | 95 | /** 96 | * The format of the output file names. 97 | * 98 | * @parameter 99 | */ 100 | private String outputFileFormat; 101 | 102 | private static final String FILE_NAME_FORMAT_PARAMETER_REGEX = "\\{fileName\\}"; 103 | 104 | /** 105 | * Execute the MOJO. 106 | * 107 | * @throws MojoExecutionException 108 | * if something unexpected occurs. 109 | */ 110 | public void execute() throws MojoExecutionException { 111 | if (getLog().isDebugEnabled()) { 112 | getLog().debug("sourceDirectory = " + sourceDirectory); 113 | getLog().debug("outputDirectory = " + outputDirectory); 114 | getLog().debug("includes = " + Arrays.toString(includes)); 115 | getLog().debug("excludes = " + Arrays.toString(excludes)); 116 | getLog().debug("force = " + force); 117 | getLog().debug("lessJs = " + lessJs); 118 | getLog().debug("skip = " + skip); 119 | } 120 | 121 | if(!skip){ 122 | executeInternal(); 123 | } else { 124 | getLog().info("Skipping plugin execution per configuration"); 125 | } 126 | } 127 | 128 | private void executeInternal() throws MojoExecutionException { 129 | long start = System.currentTimeMillis(); 130 | 131 | String[] files = getIncludedFiles(); 132 | 133 | if (files == null || files.length < 1) { 134 | getLog().info("Nothing to compile - no LESS sources found"); 135 | } else { 136 | if (getLog().isDebugEnabled()) { 137 | getLog().debug("included files = " + Arrays.toString(files)); 138 | } 139 | 140 | Object lessCompiler = initLessCompiler(); 141 | if (watch){ 142 | getLog().info("Watching "+sourceDirectory); 143 | if (force){ 144 | force=false; 145 | getLog().info("Disabled the 'force' flag in watch mode."); 146 | } 147 | Thread.currentThread().setPriority(Thread.MIN_PRIORITY); 148 | while (watch && !Thread.currentThread().isInterrupted()){ 149 | compileIfChanged(files, lessCompiler); 150 | try { 151 | Thread.sleep(watchInterval); 152 | } catch (InterruptedException e) { 153 | System.out.println("interrupted"); 154 | } 155 | } 156 | } else { 157 | compileIfChanged(files, lessCompiler); 158 | } 159 | 160 | getLog().info("Complete Less compile job finished in " + (System.currentTimeMillis() - start) + " ms"); 161 | } 162 | } 163 | 164 | private void compileIfChanged(String[] files, Object lessCompiler) throws MojoExecutionException { 165 | try { 166 | for (String file : files) { 167 | File input = new File(sourceDirectory, file); 168 | 169 | buildContext.removeMessages(input); 170 | 171 | if(outputFileFormat != null){ 172 | file = outputFileFormat.replaceAll(FILE_NAME_FORMAT_PARAMETER_REGEX, file.replace(".less", "")); 173 | } 174 | 175 | File output = new File(outputDirectory, file.replace(".less", ".css")); 176 | 177 | if (!output.getParentFile().exists() && !output.getParentFile().mkdirs()) { 178 | throw new MojoExecutionException("Cannot create output directory " + output.getParentFile()); 179 | } 180 | 181 | try { 182 | LessSource lessSource = new LessSource(input); 183 | if (force || !output.exists() || output.lastModified() < lessSource.getLastModifiedIncludingImports()) { 184 | long compilationStarted = System.currentTimeMillis(); 185 | getLog().info("Compiling LESS source: " + file + "..."); 186 | if (lessCompiler instanceof LessCompiler) { 187 | ((LessCompiler) lessCompiler).compile(lessSource, output, force); 188 | } else { 189 | ((NodeJsLessCompiler) lessCompiler).compile(lessSource, output, force); 190 | } 191 | buildContext.refresh(output); 192 | getLog().info("Finished compilation to "+outputDirectory+" in " + (System.currentTimeMillis() - compilationStarted) + " ms"); 193 | } 194 | else if (!watch) { 195 | getLog().info("Bypassing LESS source: " + file + " (not modified)"); 196 | } 197 | } catch (IOException e) { 198 | buildContext.addMessage(input, 0, 0, "Error compiling LESS source", BuildContext.SEVERITY_ERROR, e); 199 | throw new MojoExecutionException("Error while compiling LESS source: " + file, e); 200 | } catch (LessException e) { 201 | String message = e.getMessage(); 202 | if (StringUtils.isEmpty(message)) { 203 | message = "Error compiling LESS source"; 204 | } 205 | buildContext.addMessage(input, 0, 0, "Error compiling LESS source", BuildContext.SEVERITY_ERROR, e); 206 | throw new MojoExecutionException("Error while compiling LESS source: " + file, e); 207 | } catch (InterruptedException e) { 208 | buildContext.addMessage(input, 0, 0, "Error compiling LESS source", BuildContext.SEVERITY_ERROR, e); 209 | throw new MojoExecutionException("Error while compiling LESS source: " + file, e); 210 | } 211 | } 212 | } finally { 213 | if (lessCompiler instanceof NodeJsLessCompiler) { 214 | ((NodeJsLessCompiler) lessCompiler).close(); 215 | } 216 | } 217 | } 218 | 219 | private Object initLessCompiler() throws MojoExecutionException { 220 | if (nodeExecutable != null) { 221 | NodeJsLessCompiler lessCompiler; 222 | try { 223 | lessCompiler = new NodeJsLessCompiler(nodeExecutable, compress, encoding, getLog()); 224 | } catch (IOException e) { 225 | throw new MojoExecutionException(e.getMessage(), e); 226 | } 227 | if (lessJs != null) { 228 | throw new MojoExecutionException( 229 | "Custom LESS JavaScript is not currently supported when using nodeExecutable"); 230 | } 231 | return lessCompiler; 232 | } else { 233 | LessCompiler lessCompiler = new LessCompiler(); 234 | lessCompiler.setCompress(compress); 235 | lessCompiler.setEncoding(encoding); 236 | if (lessJs != null) { 237 | try { 238 | lessCompiler.setLessJs(lessJs.toURI().toURL()); 239 | } catch (MalformedURLException e) { 240 | throw new MojoExecutionException( 241 | "Error while loading LESS JavaScript: " + lessJs.getAbsolutePath(), e); 242 | } 243 | } 244 | return lessCompiler; 245 | } 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/main/java/org/lesscss/mojo/ListMojo.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011-2012 The Apache Software Foundation. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.lesscss.mojo; 16 | 17 | import java.io.File; 18 | import java.io.FileNotFoundException; 19 | import java.io.IOException; 20 | import java.util.Arrays; 21 | import java.util.Iterator; 22 | import java.util.Map.Entry; 23 | 24 | import org.apache.maven.plugin.MojoExecutionException; 25 | import org.lesscss.LessSource; 26 | 27 | /** 28 | * Goal which list the LESS sources and its imports. 29 | * 30 | * @author Marcel Overdijk 31 | * @goal list 32 | */ 33 | public class ListMojo extends AbstractLessCssMojo { 34 | 35 | /** 36 | * Execute the MOJO. 37 | * 38 | * @throws MojoExecutionException 39 | * if something unexpected occurs. 40 | */ 41 | public void execute() throws MojoExecutionException { 42 | if (getLog().isDebugEnabled()) { 43 | getLog().debug("sourceDirectory = " + sourceDirectory); 44 | getLog().debug("includes = " + Arrays.toString(includes)); 45 | getLog().debug("excludes = " + Arrays.toString(excludes)); 46 | } 47 | 48 | String[] files = getIncludedFiles(); 49 | 50 | if (files == null || files.length < 1) { 51 | getLog().info("No LESS sources found"); 52 | } else { 53 | getLog().info("The following LESS sources have been resolved:"); 54 | 55 | for (String file : files) { 56 | File lessFile = new File(sourceDirectory, file); 57 | try { 58 | LessSource lessSource = new LessSource(lessFile); 59 | listLessSource(lessSource, file, 0, false); 60 | } catch (FileNotFoundException e) { 61 | throw new MojoExecutionException("Error while loading LESS source: " + lessFile.getAbsolutePath(), e); 62 | } catch (IOException e) { 63 | throw new MojoExecutionException("Error while loading LESS source: " + lessFile.getAbsolutePath(), e); 64 | } 65 | } 66 | } 67 | } 68 | 69 | private void listLessSource(LessSource lessSource, String path, int level, boolean last) { 70 | String prefix = ""; 71 | if (level > 0) { 72 | for (int i = 1; i <= level; i++) { 73 | if (i == level && last) { 74 | prefix = prefix + "`-- "; 75 | } else if (i == level) { 76 | prefix = prefix + "|-- "; 77 | } else { 78 | prefix = prefix + "| "; 79 | } 80 | } 81 | } 82 | 83 | getLog().info(prefix + path); 84 | 85 | Iterator> it = lessSource.getImports().entrySet().iterator(); 86 | while (it.hasNext()) { 87 | Entry entry = it.next(); 88 | listLessSource(entry.getValue(), entry.getKey(), level + 1, !it.hasNext()); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/org/lesscss/mojo/NodeJsLessCompiler.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 the original author or authors. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.lesscss.mojo; 16 | 17 | import java.io.File; 18 | import java.io.FileInputStream; 19 | import java.io.FileOutputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.util.Arrays; 23 | import java.util.List; 24 | 25 | import org.apache.commons.io.FileUtils; 26 | import org.apache.commons.io.IOUtils; 27 | import org.apache.maven.plugin.logging.Log; 28 | import org.lesscss.LessException; 29 | import org.lesscss.LessSource; 30 | 31 | public class NodeJsLessCompiler { 32 | 33 | private static final List resources = Arrays.asList( 34 | "lessc.js", 35 | "less/visitor.js", 36 | "less/tree.js", 37 | "less/to-css-visitor.js", 38 | "less/source-map-output.js", 39 | "less/rhino.js", 40 | "less/parser.js", 41 | "less/lessc_helper.js", 42 | "less/join-selector-visitor.js", 43 | "less/index.js", 44 | "less/import-visitor.js", 45 | "less/functions.js", 46 | "less/extend-visitor.js", 47 | "less/env.js", 48 | "less/colors.js", 49 | "less/browser.js", 50 | "less/tree/variable.js", 51 | "less/tree/value.js", 52 | "less/tree/url.js", 53 | "less/tree/unicode-descriptor.js", 54 | "less/tree/selector.js", 55 | "less/tree/ruleset.js", 56 | "less/tree/rule.js", 57 | "less/tree/ratio.js", 58 | "less/tree/quoted.js", 59 | "less/tree/paren.js", 60 | "less/tree/operation.js", 61 | "less/tree/negative.js", 62 | "less/tree/mixin.js", 63 | "less/tree/media.js", 64 | "less/tree/keyword.js", 65 | "less/tree/javascript.js", 66 | "less/tree/import.js", 67 | "less/tree/extend.js", 68 | "less/tree/expression.js", 69 | "less/tree/element.js", 70 | "less/tree/directive.js", 71 | "less/tree/dimension.js", 72 | "less/tree/condition.js", 73 | "less/tree/comment.js", 74 | "less/tree/color.js", 75 | "less/tree/call.js", 76 | "less/tree/assignment.js", 77 | "less/tree/anonymous.js", 78 | "less/tree/alpha.js"); 79 | 80 | private final Log log; 81 | 82 | private final boolean compress; 83 | 84 | private final String encoding; 85 | 86 | private final File tempDir; 87 | 88 | private final String nodeExecutablePath; 89 | 90 | public NodeJsLessCompiler(boolean compress, String encoding, Log log) throws IOException { 91 | this("node", compress, encoding, log); 92 | } 93 | 94 | public NodeJsLessCompiler(String nodeExecutablePath, boolean compress, 95 | String encoding, Log log) throws IOException { 96 | this.compress = compress; 97 | this.encoding = encoding; 98 | this.log = log; 99 | this.nodeExecutablePath = nodeExecutablePath; 100 | 101 | tempDir = createTempDir("lessc"); 102 | new File(tempDir, "less/tree").mkdirs(); 103 | for (String resource : resources) { 104 | InputStream in = NodeJsLessCompiler.class.getClassLoader() 105 | .getResourceAsStream("org/lesscss/mojo/js/" + resource); 106 | FileOutputStream out = new FileOutputStream(new File(tempDir, resource)); 107 | IOUtils.copy(in, out); 108 | in.close(); 109 | out.close(); 110 | } 111 | } 112 | 113 | public void close() { 114 | for (String resource : resources) { 115 | File tempFile = new File(tempDir, resource); 116 | if (!tempFile.delete()) { 117 | log.warn("Could not delete temp file: " + tempFile.getAbsolutePath()); 118 | } 119 | } 120 | File lessSubdir = new File(tempDir, "less"); 121 | File treeSubdir = new File(lessSubdir, "tree"); 122 | if (!treeSubdir.delete()) { 123 | log.warn("Could not delete temp dir: " + treeSubdir.getAbsolutePath()); 124 | } 125 | if (!lessSubdir.delete()) { 126 | log.warn("Could not delete temp dir: " + lessSubdir.getAbsolutePath()); 127 | } 128 | if (!tempDir.delete()) { 129 | log.warn("Could not delete temp dir: " + tempDir.getAbsolutePath()); 130 | } 131 | } 132 | 133 | public void compile(LessSource input, File output, boolean force) 134 | throws IOException, LessException, InterruptedException { 135 | if (force || !output.exists() || output.lastModified() < input.getLastModifiedIncludingImports()) { 136 | String data = compile(input.getNormalizedContent()); 137 | FileUtils.writeStringToFile(output, data, encoding); 138 | } 139 | } 140 | 141 | private String compile(String input) throws LessException, IOException, InterruptedException { 142 | long start = System.currentTimeMillis(); 143 | 144 | File inputFile = File.createTempFile("lessc-input-", ".less"); 145 | FileOutputStream out = new FileOutputStream(inputFile); 146 | IOUtils.write(input, out); 147 | out.close(); 148 | File outputFile = File.createTempFile("lessc-output-", ".css"); 149 | File lesscJsFile = new File(tempDir, "lessc.js"); 150 | 151 | ProcessBuilder pb = new ProcessBuilder(nodeExecutablePath, lesscJsFile.getAbsolutePath(), 152 | inputFile.getAbsolutePath(), outputFile.getAbsolutePath(), String.valueOf(compress)); 153 | pb.redirectErrorStream(true); 154 | Process process = pb.start(); 155 | IOUtils.copy(process.getInputStream(), System.out); 156 | 157 | int exitStatus = process.waitFor(); 158 | 159 | FileInputStream in = new FileInputStream(outputFile); 160 | String result = IOUtils.toString(in); 161 | in.close(); 162 | if (!inputFile.delete()) { 163 | log.warn("Could not delete temp file: " + inputFile.getAbsolutePath()); 164 | } 165 | if (!outputFile.delete()) { 166 | log.warn("Could not delete temp file: " + outputFile.getAbsolutePath()); 167 | } 168 | if (exitStatus != 0) { 169 | throw new LessException(result, null); 170 | } 171 | 172 | log.debug("Finished compilation of LESS source in " + (System.currentTimeMillis() - start) + " ms."); 173 | 174 | return result; 175 | } 176 | 177 | // copied from guava's Files.createTempDir, with added prefix 178 | private static File createTempDir(String prefix) { 179 | final int tempDirAttempts = 10000; 180 | File baseDir = new File(System.getProperty("java.io.tmpdir")); 181 | String baseName = prefix + "-" + System.currentTimeMillis() + "-"; 182 | for (int counter = 0; counter < tempDirAttempts; counter++) { 183 | File tempDir = new File(baseDir, baseName + counter); 184 | if (tempDir.mkdir()) { 185 | return tempDir; 186 | } 187 | } 188 | throw new IllegalStateException("Failed to create directory within " + tempDirAttempts 189 | + " attempts (tried " + baseName + "0 to " + baseName + (tempDirAttempts - 1) + ')'); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | compile 7 | 8 | 9 | 10 | 11 | true 12 | false 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/colors.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | tree.colors = { 3 | 'aliceblue':'#f0f8ff', 4 | 'antiquewhite':'#faebd7', 5 | 'aqua':'#00ffff', 6 | 'aquamarine':'#7fffd4', 7 | 'azure':'#f0ffff', 8 | 'beige':'#f5f5dc', 9 | 'bisque':'#ffe4c4', 10 | 'black':'#000000', 11 | 'blanchedalmond':'#ffebcd', 12 | 'blue':'#0000ff', 13 | 'blueviolet':'#8a2be2', 14 | 'brown':'#a52a2a', 15 | 'burlywood':'#deb887', 16 | 'cadetblue':'#5f9ea0', 17 | 'chartreuse':'#7fff00', 18 | 'chocolate':'#d2691e', 19 | 'coral':'#ff7f50', 20 | 'cornflowerblue':'#6495ed', 21 | 'cornsilk':'#fff8dc', 22 | 'crimson':'#dc143c', 23 | 'cyan':'#00ffff', 24 | 'darkblue':'#00008b', 25 | 'darkcyan':'#008b8b', 26 | 'darkgoldenrod':'#b8860b', 27 | 'darkgray':'#a9a9a9', 28 | 'darkgrey':'#a9a9a9', 29 | 'darkgreen':'#006400', 30 | 'darkkhaki':'#bdb76b', 31 | 'darkmagenta':'#8b008b', 32 | 'darkolivegreen':'#556b2f', 33 | 'darkorange':'#ff8c00', 34 | 'darkorchid':'#9932cc', 35 | 'darkred':'#8b0000', 36 | 'darksalmon':'#e9967a', 37 | 'darkseagreen':'#8fbc8f', 38 | 'darkslateblue':'#483d8b', 39 | 'darkslategray':'#2f4f4f', 40 | 'darkslategrey':'#2f4f4f', 41 | 'darkturquoise':'#00ced1', 42 | 'darkviolet':'#9400d3', 43 | 'deeppink':'#ff1493', 44 | 'deepskyblue':'#00bfff', 45 | 'dimgray':'#696969', 46 | 'dimgrey':'#696969', 47 | 'dodgerblue':'#1e90ff', 48 | 'firebrick':'#b22222', 49 | 'floralwhite':'#fffaf0', 50 | 'forestgreen':'#228b22', 51 | 'fuchsia':'#ff00ff', 52 | 'gainsboro':'#dcdcdc', 53 | 'ghostwhite':'#f8f8ff', 54 | 'gold':'#ffd700', 55 | 'goldenrod':'#daa520', 56 | 'gray':'#808080', 57 | 'grey':'#808080', 58 | 'green':'#008000', 59 | 'greenyellow':'#adff2f', 60 | 'honeydew':'#f0fff0', 61 | 'hotpink':'#ff69b4', 62 | 'indianred':'#cd5c5c', 63 | 'indigo':'#4b0082', 64 | 'ivory':'#fffff0', 65 | 'khaki':'#f0e68c', 66 | 'lavender':'#e6e6fa', 67 | 'lavenderblush':'#fff0f5', 68 | 'lawngreen':'#7cfc00', 69 | 'lemonchiffon':'#fffacd', 70 | 'lightblue':'#add8e6', 71 | 'lightcoral':'#f08080', 72 | 'lightcyan':'#e0ffff', 73 | 'lightgoldenrodyellow':'#fafad2', 74 | 'lightgray':'#d3d3d3', 75 | 'lightgrey':'#d3d3d3', 76 | 'lightgreen':'#90ee90', 77 | 'lightpink':'#ffb6c1', 78 | 'lightsalmon':'#ffa07a', 79 | 'lightseagreen':'#20b2aa', 80 | 'lightskyblue':'#87cefa', 81 | 'lightslategray':'#778899', 82 | 'lightslategrey':'#778899', 83 | 'lightsteelblue':'#b0c4de', 84 | 'lightyellow':'#ffffe0', 85 | 'lime':'#00ff00', 86 | 'limegreen':'#32cd32', 87 | 'linen':'#faf0e6', 88 | 'magenta':'#ff00ff', 89 | 'maroon':'#800000', 90 | 'mediumaquamarine':'#66cdaa', 91 | 'mediumblue':'#0000cd', 92 | 'mediumorchid':'#ba55d3', 93 | 'mediumpurple':'#9370d8', 94 | 'mediumseagreen':'#3cb371', 95 | 'mediumslateblue':'#7b68ee', 96 | 'mediumspringgreen':'#00fa9a', 97 | 'mediumturquoise':'#48d1cc', 98 | 'mediumvioletred':'#c71585', 99 | 'midnightblue':'#191970', 100 | 'mintcream':'#f5fffa', 101 | 'mistyrose':'#ffe4e1', 102 | 'moccasin':'#ffe4b5', 103 | 'navajowhite':'#ffdead', 104 | 'navy':'#000080', 105 | 'oldlace':'#fdf5e6', 106 | 'olive':'#808000', 107 | 'olivedrab':'#6b8e23', 108 | 'orange':'#ffa500', 109 | 'orangered':'#ff4500', 110 | 'orchid':'#da70d6', 111 | 'palegoldenrod':'#eee8aa', 112 | 'palegreen':'#98fb98', 113 | 'paleturquoise':'#afeeee', 114 | 'palevioletred':'#d87093', 115 | 'papayawhip':'#ffefd5', 116 | 'peachpuff':'#ffdab9', 117 | 'peru':'#cd853f', 118 | 'pink':'#ffc0cb', 119 | 'plum':'#dda0dd', 120 | 'powderblue':'#b0e0e6', 121 | 'purple':'#800080', 122 | 'red':'#ff0000', 123 | 'rosybrown':'#bc8f8f', 124 | 'royalblue':'#4169e1', 125 | 'saddlebrown':'#8b4513', 126 | 'salmon':'#fa8072', 127 | 'sandybrown':'#f4a460', 128 | 'seagreen':'#2e8b57', 129 | 'seashell':'#fff5ee', 130 | 'sienna':'#a0522d', 131 | 'silver':'#c0c0c0', 132 | 'skyblue':'#87ceeb', 133 | 'slateblue':'#6a5acd', 134 | 'slategray':'#708090', 135 | 'slategrey':'#708090', 136 | 'snow':'#fffafa', 137 | 'springgreen':'#00ff7f', 138 | 'steelblue':'#4682b4', 139 | 'tan':'#d2b48c', 140 | 'teal':'#008080', 141 | 'thistle':'#d8bfd8', 142 | 'tomato':'#ff6347', 143 | 'turquoise':'#40e0d0', 144 | 'violet':'#ee82ee', 145 | 'wheat':'#f5deb3', 146 | 'white':'#ffffff', 147 | 'whitesmoke':'#f5f5f5', 148 | 'yellow':'#ffff00', 149 | 'yellowgreen':'#9acd32' 150 | }; 151 | })(require('./tree')); 152 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/env.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | var parseCopyProperties = [ 4 | 'paths', // option - unmodified - paths to search for imports on 5 | 'optimization', // option - optimization level (for the chunker) 6 | 'files', // list of files that have been imported, used for import-once 7 | 'contents', // browser-only, contents of all the files 8 | 'relativeUrls', // option - whether to adjust URL's to be relative 9 | 'rootpath', // option - rootpath to append to URL's 10 | 'strictImports', // option - 11 | 'insecure', // option - whether to allow imports from insecure ssl hosts 12 | 'dumpLineNumbers', // option - whether to dump line numbers 13 | 'compress', // option - whether to compress 14 | 'processImports', // option - whether to process imports. if false then imports will not be imported 15 | 'syncImport', // option - whether to import synchronously 16 | 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true 17 | 'mime', // browser only - mime type for sheet import 18 | 'useFileCache', // browser only - whether to use the per file session cache 19 | 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. 20 | ]; 21 | 22 | //currentFileInfo = { 23 | // 'relativeUrls' - option - whether to adjust URL's to be relative 24 | // 'filename' - full resolved filename of current file 25 | // 'rootpath' - path to append to normal URLs for this node 26 | // 'currentDirectory' - path to the current file, absolute 27 | // 'rootFilename' - filename of the base file 28 | // 'entryPath' - absolute path to the entry file 29 | // 'reference' - whether the file should not be output and only output parts that are referenced 30 | 31 | tree.parseEnv = function(options) { 32 | copyFromOriginal(options, this, parseCopyProperties); 33 | 34 | if (!this.contents) { this.contents = {}; } 35 | if (!this.files) { this.files = {}; } 36 | 37 | if (!this.currentFileInfo) { 38 | var filename = (options && options.filename) || "input"; 39 | var entryPath = filename.replace(/[^\/\\]*$/, ""); 40 | if (options) { 41 | options.filename = null; 42 | } 43 | this.currentFileInfo = { 44 | filename: filename, 45 | relativeUrls: this.relativeUrls, 46 | rootpath: (options && options.rootpath) || "", 47 | currentDirectory: entryPath, 48 | entryPath: entryPath, 49 | rootFilename: filename 50 | }; 51 | } 52 | }; 53 | 54 | var evalCopyProperties = [ 55 | 'silent', // whether to swallow errors and warnings 56 | 'verbose', // whether to log more activity 57 | 'compress', // whether to compress 58 | 'yuicompress', // whether to compress with the outside tool yui compressor 59 | 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) 60 | 'strictMath', // whether math has to be within parenthesis 61 | 'strictUnits', // whether units need to evaluate correctly 62 | 'cleancss', // whether to compress with clean-css 63 | 'sourceMap', // whether to output a source map 64 | 'importMultiple'// whether we are currently importing multiple copies 65 | ]; 66 | 67 | tree.evalEnv = function(options, frames) { 68 | copyFromOriginal(options, this, evalCopyProperties); 69 | 70 | this.frames = frames || []; 71 | }; 72 | 73 | tree.evalEnv.prototype.inParenthesis = function () { 74 | if (!this.parensStack) { 75 | this.parensStack = []; 76 | } 77 | this.parensStack.push(true); 78 | }; 79 | 80 | tree.evalEnv.prototype.outOfParenthesis = function () { 81 | this.parensStack.pop(); 82 | }; 83 | 84 | tree.evalEnv.prototype.isMathOn = function () { 85 | return this.strictMath ? (this.parensStack && this.parensStack.length) : true; 86 | }; 87 | 88 | tree.evalEnv.prototype.isPathRelative = function (path) { 89 | return !/^(?:[a-z-]+:|\/)/.test(path); 90 | }; 91 | 92 | tree.evalEnv.prototype.normalizePath = function( path ) { 93 | var 94 | segments = path.split("/").reverse(), 95 | segment; 96 | 97 | path = []; 98 | while (segments.length !== 0 ) { 99 | segment = segments.pop(); 100 | switch( segment ) { 101 | case ".": 102 | break; 103 | case "..": 104 | if ((path.length === 0) || (path[path.length - 1] === "..")) { 105 | path.push( segment ); 106 | } else { 107 | path.pop(); 108 | } 109 | break; 110 | default: 111 | path.push( segment ); 112 | break; 113 | } 114 | } 115 | 116 | return path.join("/"); 117 | }; 118 | 119 | //todo - do the same for the toCSS env 120 | //tree.toCSSEnv = function (options) { 121 | //}; 122 | 123 | var copyFromOriginal = function(original, destination, propertiesToCopy) { 124 | if (!original) { return; } 125 | 126 | for(var i = 0; i < propertiesToCopy.length; i++) { 127 | if (original.hasOwnProperty(propertiesToCopy[i])) { 128 | destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; 129 | } 130 | } 131 | }; 132 | 133 | })(require('./tree')); 134 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/import-visitor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | tree.importVisitor = function(importer, finish, evalEnv) { 3 | this._visitor = new tree.visitor(this); 4 | this._importer = importer; 5 | this._finish = finish; 6 | this.env = evalEnv || new tree.evalEnv(); 7 | this.importCount = 0; 8 | }; 9 | 10 | tree.importVisitor.prototype = { 11 | isReplacing: true, 12 | run: function (root) { 13 | var error; 14 | try { 15 | // process the contents 16 | this._visitor.visit(root); 17 | } 18 | catch(e) { 19 | error = e; 20 | } 21 | 22 | this.isFinished = true; 23 | 24 | if (this.importCount === 0) { 25 | this._finish(error); 26 | } 27 | }, 28 | visitImport: function (importNode, visitArgs) { 29 | var importVisitor = this, 30 | evaldImportNode, 31 | inlineCSS = importNode.options.inline; 32 | 33 | if (!importNode.css || inlineCSS) { 34 | 35 | try { 36 | evaldImportNode = importNode.evalForImport(this.env); 37 | } catch(e){ 38 | if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } 39 | // attempt to eval properly and treat as css 40 | importNode.css = true; 41 | // if that fails, this error will be thrown 42 | importNode.error = e; 43 | } 44 | 45 | if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { 46 | importNode = evaldImportNode; 47 | this.importCount++; 48 | var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); 49 | 50 | if (importNode.options.multiple) { 51 | env.importMultiple = true; 52 | } 53 | 54 | this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { 55 | if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } 56 | 57 | if (imported && !env.importMultiple) { importNode.skip = imported; } 58 | 59 | var subFinish = function(e) { 60 | importVisitor.importCount--; 61 | 62 | if (importVisitor.importCount === 0 && importVisitor.isFinished) { 63 | importVisitor._finish(e); 64 | } 65 | }; 66 | 67 | if (root) { 68 | importNode.root = root; 69 | importNode.importedFilename = fullPath; 70 | if (!inlineCSS && !importNode.skip) { 71 | new(tree.importVisitor)(importVisitor._importer, subFinish, env) 72 | .run(root); 73 | return; 74 | } 75 | } 76 | 77 | subFinish(); 78 | }); 79 | } 80 | } 81 | visitArgs.visitDeeper = false; 82 | return importNode; 83 | }, 84 | visitRule: function (ruleNode, visitArgs) { 85 | visitArgs.visitDeeper = false; 86 | return ruleNode; 87 | }, 88 | visitDirective: function (directiveNode, visitArgs) { 89 | this.env.frames.unshift(directiveNode); 90 | return directiveNode; 91 | }, 92 | visitDirectiveOut: function (directiveNode) { 93 | this.env.frames.shift(); 94 | }, 95 | visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { 96 | this.env.frames.unshift(mixinDefinitionNode); 97 | return mixinDefinitionNode; 98 | }, 99 | visitMixinDefinitionOut: function (mixinDefinitionNode) { 100 | this.env.frames.shift(); 101 | }, 102 | visitRuleset: function (rulesetNode, visitArgs) { 103 | this.env.frames.unshift(rulesetNode); 104 | return rulesetNode; 105 | }, 106 | visitRulesetOut: function (rulesetNode) { 107 | this.env.frames.shift(); 108 | }, 109 | visitMedia: function (mediaNode, visitArgs) { 110 | this.env.frames.unshift(mediaNode.ruleset); 111 | return mediaNode; 112 | }, 113 | visitMediaOut: function (mediaNode) { 114 | this.env.frames.shift(); 115 | } 116 | }; 117 | 118 | })(require('./tree')); -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | url = require('url'), 3 | request, 4 | fs = require('fs'); 5 | 6 | var less = { 7 | version: [1, 5, 1], 8 | Parser: require('./parser').Parser, 9 | tree: require('./tree'), 10 | render: function (input, options, callback) { 11 | options = options || {}; 12 | 13 | if (typeof(options) === 'function') { 14 | callback = options, options = {}; 15 | } 16 | 17 | var parser = new(less.Parser)(options), 18 | ee; 19 | 20 | if (callback) { 21 | parser.parse(input, function (e, root) { 22 | try { callback(e, root && root.toCSS && root.toCSS(options)); } 23 | catch (err) { callback(err); } 24 | }); 25 | } else { 26 | ee = new (require('events').EventEmitter)(); 27 | 28 | process.nextTick(function () { 29 | parser.parse(input, function (e, root) { 30 | if (e) { return ee.emit('error', e); } 31 | try { ee.emit('success', root.toCSS(options)); } 32 | catch (err) { ee.emit('error', err); } 33 | }); 34 | }); 35 | return ee; 36 | } 37 | }, 38 | formatError: function(ctx, options) { 39 | options = options || {}; 40 | 41 | var message = ""; 42 | var extract = ctx.extract; 43 | var error = []; 44 | var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; 45 | 46 | // only output a stack if it isn't a less error 47 | if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } 48 | 49 | if (!ctx.hasOwnProperty('index') || !extract) { 50 | return ctx.stack || ctx.message; 51 | } 52 | 53 | if (typeof(extract[0]) === 'string') { 54 | error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); 55 | } 56 | 57 | if (typeof(extract[1]) === 'string') { 58 | var errorTxt = ctx.line + ' '; 59 | if (extract[1]) { 60 | errorTxt += extract[1].slice(0, ctx.column) + 61 | stylize(stylize(stylize(extract[1][ctx.column], 'bold') + 62 | extract[1].slice(ctx.column + 1), 'red'), 'inverse'); 63 | } 64 | error.push(errorTxt); 65 | } 66 | 67 | if (typeof(extract[2]) === 'string') { 68 | error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); 69 | } 70 | error = error.join('\n') + stylize('', 'reset') + '\n'; 71 | 72 | message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); 73 | ctx.filename && (message += stylize(' in ', 'red') + ctx.filename + 74 | stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey')); 75 | 76 | message += '\n' + error; 77 | 78 | if (ctx.callLine) { 79 | message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; 80 | message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; 81 | } 82 | 83 | return message; 84 | }, 85 | writeError: function (ctx, options) { 86 | options = options || {}; 87 | if (options.silent) { return; } 88 | console.error(less.formatError(ctx, options)); 89 | } 90 | }; 91 | 92 | ['color', 'directive', 'operation', 'dimension', 93 | 'keyword', 'variable', 'ruleset', 'element', 94 | 'selector', 'quoted', 'expression', 'rule', 95 | 'call', 'url', 'alpha', 'import', 96 | 'mixin', 'comment', 'anonymous', 'value', 97 | 'javascript', 'assignment', 'condition', 'paren', 98 | 'media', 'unicode-descriptor', 'negative', 'extend' 99 | ].forEach(function (n) { 100 | require('./tree/' + n); 101 | }); 102 | 103 | 104 | var isUrlRe = /^(?:https?:)?\/\//i; 105 | 106 | less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { 107 | var pathname, dirname, data, 108 | newFileInfo = { 109 | relativeUrls: env.relativeUrls, 110 | entryPath: currentFileInfo.entryPath, 111 | rootpath: currentFileInfo.rootpath, 112 | rootFilename: currentFileInfo.rootFilename 113 | }; 114 | 115 | function handleDataAndCallCallback(data) { 116 | var j = file.lastIndexOf('/'); 117 | 118 | // Pass on an updated rootpath if path of imported file is relative and file 119 | // is in a (sub|sup) directory 120 | // 121 | // Examples: 122 | // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', 123 | // then rootpath should become 'less/module/nav/' 124 | // - If path of imported file is '../mixins.less' and rootpath is 'less/', 125 | // then rootpath should become 'less/../' 126 | if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { 127 | var relativeSubDirectory = file.slice(0, j+1); 128 | newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file 129 | } 130 | newFileInfo.currentDirectory = pathname.replace(/[^\\\/]*$/, ""); 131 | newFileInfo.filename = pathname; 132 | 133 | callback(null, data, pathname, newFileInfo); 134 | } 135 | 136 | var isUrl = isUrlRe.test( file ); 137 | if (isUrl || isUrlRe.test(currentFileInfo.currentDirectory)) { 138 | if (request === undefined) { 139 | try { request = require('request'); } 140 | catch(e) { request = null; } 141 | } 142 | if (!request) { 143 | callback({ type: 'File', message: "optional dependency 'request' required to import over http(s)\n" }); 144 | return; 145 | } 146 | 147 | var urlStr = isUrl ? file : url.resolve(currentFileInfo.currentDirectory, file), 148 | urlObj = url.parse(urlStr); 149 | 150 | request.get({uri: urlStr, strictSSL: !env.insecure }, function (error, res, body) { 151 | if (res.statusCode === 404) { 152 | callback({ type: 'File', message: "resource '" + urlStr + "' was not found\n" }); 153 | return; 154 | } 155 | if (!body) { 156 | console.error( 'Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"' ); 157 | } 158 | if (error) { 159 | callback({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n "+ error +"\n" }); 160 | } 161 | pathname = urlStr; 162 | dirname = urlObj.protocol +'//'+ urlObj.host + urlObj.pathname.replace(/[^\/]*$/, ''); 163 | handleDataAndCallCallback(body); 164 | }); 165 | } else { 166 | 167 | var paths = [currentFileInfo.currentDirectory].concat(env.paths); 168 | paths.push('.'); 169 | 170 | if (env.syncImport) { 171 | for (var i = 0; i < paths.length; i++) { 172 | try { 173 | pathname = path.join(paths[i], file); 174 | fs.statSync(pathname); 175 | break; 176 | } catch (e) { 177 | pathname = null; 178 | } 179 | } 180 | 181 | if (!pathname) { 182 | callback({ type: 'File', message: "'" + file + "' wasn't found" }); 183 | return; 184 | } 185 | 186 | try { 187 | data = fs.readFileSync(pathname, 'utf-8'); 188 | handleDataAndCallCallback(data); 189 | } catch (e) { 190 | callback(e); 191 | } 192 | } else { 193 | (function tryPathIndex(i) { 194 | if (i < paths.length) { 195 | pathname = path.join(paths[i], file); 196 | fs.stat(pathname, function (err) { 197 | if (err) { 198 | tryPathIndex(i + 1); 199 | } else { 200 | fs.readFile(pathname, 'utf-8', function(e, data) { 201 | if (e) { callback(e); } 202 | handleDataAndCallCallback(data); 203 | }); 204 | } 205 | }); 206 | } else { 207 | callback({ type: 'File', message: "'" + file + "' wasn't found" }); 208 | } 209 | }(0)); 210 | } 211 | } 212 | }; 213 | 214 | require('./env'); 215 | require('./functions'); 216 | require('./colors'); 217 | require('./visitor.js'); 218 | require('./import-visitor.js'); 219 | require('./extend-visitor.js'); 220 | require('./join-selector-visitor.js'); 221 | require('./to-css-visitor.js'); 222 | require('./source-map-output.js'); 223 | 224 | for (var k in less) { exports[k] = less[k]; } 225 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/join-selector-visitor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | tree.joinSelectorVisitor = function() { 3 | this.contexts = [[]]; 4 | this._visitor = new tree.visitor(this); 5 | }; 6 | 7 | tree.joinSelectorVisitor.prototype = { 8 | run: function (root) { 9 | return this._visitor.visit(root); 10 | }, 11 | visitRule: function (ruleNode, visitArgs) { 12 | visitArgs.visitDeeper = false; 13 | }, 14 | visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { 15 | visitArgs.visitDeeper = false; 16 | }, 17 | 18 | visitRuleset: function (rulesetNode, visitArgs) { 19 | var context = this.contexts[this.contexts.length - 1]; 20 | var paths = []; 21 | this.contexts.push(paths); 22 | 23 | if (! rulesetNode.root) { 24 | rulesetNode.selectors = rulesetNode.selectors.filter(function(selector) { return selector.getIsOutput(); }); 25 | if (rulesetNode.selectors.length === 0) { 26 | rulesetNode.rules.length = 0; 27 | } 28 | rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); 29 | rulesetNode.paths = paths; 30 | } 31 | }, 32 | visitRulesetOut: function (rulesetNode) { 33 | this.contexts.length = this.contexts.length - 1; 34 | }, 35 | visitMedia: function (mediaNode, visitArgs) { 36 | var context = this.contexts[this.contexts.length - 1]; 37 | mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); 38 | } 39 | }; 40 | 41 | })(require('./tree')); -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/lessc_helper.js: -------------------------------------------------------------------------------- 1 | // lessc_helper.js 2 | // 3 | // helper functions for lessc 4 | var lessc_helper = { 5 | 6 | //Stylize a string 7 | stylize : function(str, style) { 8 | var styles = { 9 | 'reset' : [0, 0], 10 | 'bold' : [1, 22], 11 | 'inverse' : [7, 27], 12 | 'underline' : [4, 24], 13 | 'yellow' : [33, 39], 14 | 'green' : [32, 39], 15 | 'red' : [31, 39], 16 | 'grey' : [90, 39] 17 | }; 18 | return '\033[' + styles[style][0] + 'm' + str + 19 | '\033[' + styles[style][1] + 'm'; 20 | }, 21 | 22 | //Print command line options 23 | printUsage: function() { 24 | console.log("usage: lessc [option option=parameter ...] [destination]"); 25 | console.log(""); 26 | console.log("If source is set to `-' (dash or hyphen-minus), input is read from stdin."); 27 | console.log(""); 28 | console.log("options:"); 29 | console.log(" -h, --help Print help (this message) and exit."); 30 | console.log(" --include-path=PATHS Set include paths. Separated by `:'. Use `;' on Windows."); 31 | console.log(" -M, --depends Output a makefile import dependency list to stdout"); 32 | console.log(" --no-color Disable colorized output."); 33 | console.log(" --no-ie-compat Disable IE compatibility checks."); 34 | console.log(" --no-js Disable JavaScript in less files"); 35 | console.log(" -l, --lint Syntax check only (lint)."); 36 | console.log(" -s, --silent Suppress output of error messages."); 37 | console.log(" --strict-imports Force evaluation of imports."); 38 | console.log(" --insecure Allow imports from insecure https hosts."); 39 | console.log(" -v, --version Print version number and exit."); 40 | console.log(" -x, --compress Compress output by removing some whitespaces."); 41 | console.log(" --clean-css Compress output using clean-css"); 42 | console.log(" --source-map[=FILENAME] Outputs a v3 sourcemap to the filename (or output filename.map)"); 43 | console.log(" --source-map-rootpath=X adds this path onto the sourcemap filename and less file paths"); 44 | console.log(" --source-map-basepath=X Sets sourcemap base path, defaults to current working directory."); 45 | console.log(" --source-map-less-inline puts the less files into the map instead of referencing them"); 46 | console.log(" --source-map-map-inline puts the map (and any less files) into the output css file"); 47 | console.log(" --source-map-url=URL the complete url and filename put in the less file"); 48 | console.log(" -rp, --rootpath=URL Set rootpath for url rewriting in relative imports and urls."); 49 | console.log(" Works with or without the relative-urls option."); 50 | console.log(" -ru, --relative-urls re-write relative urls to the base less file."); 51 | console.log(" -sm=on|off Turn on or off strict math, where in strict mode, math"); 52 | console.log(" --strict-math=on|off requires brackets. This option may default to on and then"); 53 | console.log(" be removed in the future."); 54 | console.log(" -su=on|off Allow mixed units, e.g. 1px+1em or 1px*1px which have units"); 55 | console.log(" --strict-units=on|off that cannot be represented."); 56 | console.log(" --global-var='VAR=VALUE' Defines a variable that can be referenced by the file."); 57 | console.log(" --modify-var='VAR=VALUE' Modifies a variable already declared in the file."); 58 | console.log(""); 59 | console.log("-------------------------- Deprecated ----------------"); 60 | console.log(" -O0, -O1, -O2 Set the parser's optimization level. The lower"); 61 | console.log(" the number, the less nodes it will create in the"); 62 | console.log(" tree. This could matter for debugging, or if you"); 63 | console.log(" want to access the individual nodes in the tree."); 64 | console.log(" --line-numbers=TYPE Outputs filename and line numbers."); 65 | console.log(" TYPE can be either 'comments', which will output"); 66 | console.log(" the debug info within comments, 'mediaquery'"); 67 | console.log(" that will output the information within a fake"); 68 | console.log(" media query which is compatible with the SASS"); 69 | console.log(" format, and 'all' which will do both."); 70 | console.log(" --verbose Be verbose."); 71 | console.log(""); 72 | console.log("Report bugs to: http://github.com/less/less.js/issues"); 73 | console.log("Home page: "); 74 | } 75 | }; 76 | 77 | // Exports helper functions 78 | for (var h in lessc_helper) { exports[h] = lessc_helper[h]; } 79 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/rhino.js: -------------------------------------------------------------------------------- 1 | /*jshint rhino:true, unused: false */ 2 | /*global name:true, less, loadStyleSheet */ 3 | var name; 4 | 5 | function error(e, filename) { 6 | 7 | var content = "Error : " + filename + "\n"; 8 | 9 | filename = e.filename || filename; 10 | 11 | if (e.message) { 12 | content += e.message + "\n"; 13 | } 14 | 15 | var errorline = function (e, i, classname) { 16 | if (e.extract[i]) { 17 | content += 18 | String(parseInt(e.line, 10) + (i - 1)) + 19 | ":" + e.extract[i] + "\n"; 20 | } 21 | }; 22 | 23 | if (e.stack) { 24 | content += e.stack; 25 | } else if (e.extract) { 26 | content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n'; 27 | errorline(e, 0); 28 | errorline(e, 1); 29 | errorline(e, 2); 30 | } 31 | print(content); 32 | } 33 | 34 | function loadStyleSheet(sheet, callback, reload, remaining) { 35 | var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), 36 | sheetName = name.slice(0, endOfPath + 1) + sheet.href, 37 | contents = sheet.contents || {}, 38 | input = readFile(sheetName); 39 | 40 | input = input.replace(/^\xEF\xBB\xBF/, ''); 41 | 42 | contents[sheetName] = input; 43 | 44 | var parser = new less.Parser({ 45 | paths: [sheet.href.replace(/[\w\.-]+$/, '')], 46 | contents: contents 47 | }); 48 | parser.parse(input, function (e, root) { 49 | if (e) { 50 | return error(e, sheetName); 51 | } 52 | try { 53 | callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); 54 | } catch(e) { 55 | error(e, sheetName); 56 | } 57 | }); 58 | } 59 | 60 | function writeFile(filename, content) { 61 | var fstream = new java.io.FileWriter(filename); 62 | var out = new java.io.BufferedWriter(fstream); 63 | out.write(content); 64 | out.close(); 65 | } 66 | 67 | // Command line integration via Rhino 68 | (function (args) { 69 | var output, 70 | compress = false, 71 | i, 72 | path; 73 | 74 | for(i = 0; i < args.length; i++) { 75 | switch(args[i]) { 76 | case "-x": 77 | compress = true; 78 | break; 79 | default: 80 | if (!name) { 81 | name = args[i]; 82 | } else if (!output) { 83 | output = args[i]; 84 | } else { 85 | print("unrecognised parameters"); 86 | print("input_file [output_file] [-x]"); 87 | } 88 | } 89 | } 90 | 91 | if (!name) { 92 | print('No files present in the fileset; Check your pattern match in build.xml'); 93 | quit(1); 94 | } 95 | path = name.split("/");path.pop();path=path.join("/"); 96 | 97 | var input = readFile(name); 98 | 99 | if (!input) { 100 | print('lesscss: couldn\'t open file ' + name); 101 | quit(1); 102 | } 103 | 104 | var result; 105 | try { 106 | var parser = new less.Parser(); 107 | parser.parse(input, function (e, root) { 108 | if (e) { 109 | error(e, name); 110 | quit(1); 111 | } else { 112 | result = root.toCSS({compress: compress || false}); 113 | if (output) { 114 | writeFile(output, result); 115 | print("Written to " + output); 116 | } else { 117 | print(result); 118 | } 119 | quit(0); 120 | } 121 | }); 122 | } 123 | catch(e) { 124 | error(e, name); 125 | quit(1); 126 | } 127 | print("done"); 128 | }(arguments)); -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/source-map-output.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.sourceMapOutput = function (options) { 4 | this._css = []; 5 | this._rootNode = options.rootNode; 6 | this._writeSourceMap = options.writeSourceMap; 7 | this._contentsMap = options.contentsMap; 8 | this._sourceMapFilename = options.sourceMapFilename; 9 | this._outputFilename = options.outputFilename; 10 | this._sourceMapURL = options.sourceMapURL; 11 | this._sourceMapBasepath = options.sourceMapBasepath; 12 | this._sourceMapRootpath = options.sourceMapRootpath; 13 | this._outputSourceFiles = options.outputSourceFiles; 14 | this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; 15 | 16 | if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { 17 | this._sourceMapRootpath += '/'; 18 | } 19 | 20 | this._lineNumber = 0; 21 | this._column = 0; 22 | }; 23 | 24 | tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { 25 | if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { 26 | filename = filename.substring(this._sourceMapBasepath.length); 27 | if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { 28 | filename = filename.substring(1); 29 | } 30 | } 31 | return (this._sourceMapRootpath || "") + filename.replace(/\\/g, '/'); 32 | }; 33 | 34 | tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { 35 | 36 | //ignore adding empty strings 37 | if (!chunk) { 38 | return; 39 | } 40 | 41 | var lines, 42 | sourceLines, 43 | columns, 44 | sourceColumns, 45 | i; 46 | 47 | if (fileInfo) { 48 | var inputSource = this._contentsMap[fileInfo.filename].substring(0, index); 49 | sourceLines = inputSource.split("\n"); 50 | sourceColumns = sourceLines[sourceLines.length-1]; 51 | } 52 | 53 | lines = chunk.split("\n"); 54 | columns = lines[lines.length-1]; 55 | 56 | if (fileInfo) { 57 | if (!mapLines) { 58 | this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, 59 | original: { line: sourceLines.length, column: sourceColumns.length}, 60 | source: this.normalizeFilename(fileInfo.filename)}); 61 | } else { 62 | for(i = 0; i < lines.length; i++) { 63 | this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, 64 | original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, 65 | source: this.normalizeFilename(fileInfo.filename)}); 66 | } 67 | } 68 | } 69 | 70 | if (lines.length === 1) { 71 | this._column += columns.length; 72 | } else { 73 | this._lineNumber += lines.length - 1; 74 | this._column = columns.length; 75 | } 76 | 77 | this._css.push(chunk); 78 | }; 79 | 80 | tree.sourceMapOutput.prototype.isEmpty = function() { 81 | return this._css.length === 0; 82 | }; 83 | 84 | tree.sourceMapOutput.prototype.toCSS = function(env) { 85 | this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); 86 | 87 | if (this._outputSourceFiles) { 88 | for(var filename in this._contentsMap) { 89 | this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), this._contentsMap[filename]); 90 | } 91 | } 92 | 93 | this._rootNode.genCSS(env, this); 94 | 95 | if (this._css.length > 0) { 96 | var sourceMapURL, 97 | sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); 98 | 99 | if (this._sourceMapURL) { 100 | sourceMapURL = this._sourceMapURL; 101 | } else if (this._sourceMapFilename) { 102 | sourceMapURL = this.normalizeFilename(this._sourceMapFilename); 103 | } 104 | 105 | if (this._writeSourceMap) { 106 | this._writeSourceMap(sourceMapContent); 107 | } else { 108 | sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); 109 | } 110 | 111 | if (sourceMapURL) { 112 | this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); 113 | } 114 | } 115 | 116 | return this._css.join(''); 117 | }; 118 | 119 | })(require('./tree')); 120 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/to-css-visitor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | tree.toCSSVisitor = function(env) { 3 | this._visitor = new tree.visitor(this); 4 | this._env = env; 5 | }; 6 | 7 | tree.toCSSVisitor.prototype = { 8 | isReplacing: true, 9 | run: function (root) { 10 | return this._visitor.visit(root); 11 | }, 12 | 13 | visitRule: function (ruleNode, visitArgs) { 14 | if (ruleNode.variable) { 15 | return []; 16 | } 17 | return ruleNode; 18 | }, 19 | 20 | visitMixinDefinition: function (mixinNode, visitArgs) { 21 | return []; 22 | }, 23 | 24 | visitExtend: function (extendNode, visitArgs) { 25 | return []; 26 | }, 27 | 28 | visitComment: function (commentNode, visitArgs) { 29 | if (commentNode.isSilent(this._env)) { 30 | return []; 31 | } 32 | return commentNode; 33 | }, 34 | 35 | visitMedia: function(mediaNode, visitArgs) { 36 | mediaNode.accept(this._visitor); 37 | visitArgs.visitDeeper = false; 38 | 39 | if (!mediaNode.rules.length) { 40 | return []; 41 | } 42 | return mediaNode; 43 | }, 44 | 45 | visitDirective: function(directiveNode, visitArgs) { 46 | if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { 47 | return []; 48 | } 49 | if (directiveNode.name === "@charset") { 50 | // Only output the debug info together with subsequent @charset definitions 51 | // a comment (or @media statement) before the actual @charset directive would 52 | // be considered illegal css as it has to be on the first line 53 | if (this.charset) { 54 | if (directiveNode.debugInfo) { 55 | var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); 56 | comment.debugInfo = directiveNode.debugInfo; 57 | return this._visitor.visit(comment); 58 | } 59 | return []; 60 | } 61 | this.charset = true; 62 | } 63 | return directiveNode; 64 | }, 65 | 66 | checkPropertiesInRoot: function(rules) { 67 | var ruleNode; 68 | for(var i = 0; i < rules.length; i++) { 69 | ruleNode = rules[i]; 70 | if (ruleNode instanceof tree.Rule && !ruleNode.variable) { 71 | throw { message: "properties must be inside selector blocks, they cannot be in the root.", 72 | index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; 73 | } 74 | } 75 | }, 76 | 77 | visitRuleset: function (rulesetNode, visitArgs) { 78 | var rule, rulesets = []; 79 | if (rulesetNode.firstRoot) { 80 | this.checkPropertiesInRoot(rulesetNode.rules); 81 | } 82 | if (! rulesetNode.root) { 83 | 84 | rulesetNode.paths = rulesetNode.paths 85 | .filter(function(p) { 86 | var i; 87 | if (p[0].elements[0].combinator.value === ' ') { 88 | p[0].elements[0].combinator = new(tree.Combinator)(''); 89 | } 90 | for(i = 0; i < p.length; i++) { 91 | if (p[i].getIsReferenced() && p[i].getIsOutput()) { 92 | return true; 93 | } 94 | return false; 95 | } 96 | }); 97 | 98 | // Compile rules and rulesets 99 | for (var i = 0; i < rulesetNode.rules.length; i++) { 100 | rule = rulesetNode.rules[i]; 101 | 102 | if (rule.rules) { 103 | // visit because we are moving them out from being a child 104 | rulesets.push(this._visitor.visit(rule)); 105 | rulesetNode.rules.splice(i, 1); 106 | i--; 107 | continue; 108 | } 109 | } 110 | // accept the visitor to remove rules and refactor itself 111 | // then we can decide now whether we want it or not 112 | if (rulesetNode.rules.length > 0) { 113 | rulesetNode.accept(this._visitor); 114 | } 115 | visitArgs.visitDeeper = false; 116 | 117 | this._mergeRules(rulesetNode.rules); 118 | this._removeDuplicateRules(rulesetNode.rules); 119 | 120 | // now decide whether we keep the ruleset 121 | if (rulesetNode.rules.length > 0 && rulesetNode.paths.length > 0) { 122 | rulesets.splice(0, 0, rulesetNode); 123 | } 124 | } else { 125 | rulesetNode.accept(this._visitor); 126 | visitArgs.visitDeeper = false; 127 | if (rulesetNode.firstRoot || rulesetNode.rules.length > 0) { 128 | rulesets.splice(0, 0, rulesetNode); 129 | } 130 | } 131 | if (rulesets.length === 1) { 132 | return rulesets[0]; 133 | } 134 | return rulesets; 135 | }, 136 | 137 | _removeDuplicateRules: function(rules) { 138 | // remove duplicates 139 | var ruleCache = {}, 140 | ruleList, rule, i; 141 | for(i = rules.length - 1; i >= 0 ; i--) { 142 | rule = rules[i]; 143 | if (rule instanceof tree.Rule) { 144 | if (!ruleCache[rule.name]) { 145 | ruleCache[rule.name] = rule; 146 | } else { 147 | ruleList = ruleCache[rule.name]; 148 | if (ruleList instanceof tree.Rule) { 149 | ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; 150 | } 151 | var ruleCSS = rule.toCSS(this._env); 152 | if (ruleList.indexOf(ruleCSS) !== -1) { 153 | rules.splice(i, 1); 154 | } else { 155 | ruleList.push(ruleCSS); 156 | } 157 | } 158 | } 159 | } 160 | }, 161 | 162 | _mergeRules: function (rules) { 163 | var groups = {}, 164 | parts, 165 | rule, 166 | key; 167 | 168 | for (var i = 0; i < rules.length; i++) { 169 | rule = rules[i]; 170 | 171 | if ((rule instanceof tree.Rule) && rule.merge) { 172 | key = [rule.name, 173 | rule.important ? "!" : ""].join(","); 174 | 175 | if (!groups[key]) { 176 | parts = groups[key] = []; 177 | } else { 178 | rules.splice(i--, 1); 179 | } 180 | 181 | parts.push(rule); 182 | } 183 | } 184 | 185 | Object.keys(groups).map(function (k) { 186 | parts = groups[k]; 187 | 188 | if (parts.length > 1) { 189 | rule = parts[0]; 190 | 191 | rule.value = new (tree.Value)(parts.map(function (p) { 192 | return p.value; 193 | })); 194 | } 195 | }); 196 | } 197 | }; 198 | 199 | })(require('./tree')); -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.debugInfo = function(env, ctx, lineSeperator) { 4 | var result=""; 5 | if (env.dumpLineNumbers && !env.compress) { 6 | switch(env.dumpLineNumbers) { 7 | case 'comments': 8 | result = tree.debugInfo.asComment(ctx); 9 | break; 10 | case 'mediaquery': 11 | result = tree.debugInfo.asMediaQuery(ctx); 12 | break; 13 | case 'all': 14 | result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); 15 | break; 16 | } 17 | } 18 | return result; 19 | }; 20 | 21 | tree.debugInfo.asComment = function(ctx) { 22 | return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; 23 | }; 24 | 25 | tree.debugInfo.asMediaQuery = function(ctx) { 26 | return '@media -sass-debug-info{filename{font-family:' + 27 | ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function (a) { 28 | if (a == '\\') { 29 | a = '\/'; 30 | } 31 | return '\\' + a; 32 | }) + 33 | '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; 34 | }; 35 | 36 | tree.find = function (obj, fun) { 37 | for (var i = 0, r; i < obj.length; i++) { 38 | if (r = fun.call(obj, obj[i])) { return r; } 39 | } 40 | return null; 41 | }; 42 | 43 | tree.jsify = function (obj) { 44 | if (Array.isArray(obj.value) && (obj.value.length > 1)) { 45 | return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; 46 | } else { 47 | return obj.toCSS(false); 48 | } 49 | }; 50 | 51 | tree.toCSS = function (env) { 52 | var strs = []; 53 | this.genCSS(env, { 54 | add: function(chunk, fileInfo, index) { 55 | strs.push(chunk); 56 | }, 57 | isEmpty: function () { 58 | return strs.length === 0; 59 | } 60 | }); 61 | return strs.join(''); 62 | }; 63 | 64 | tree.outputRuleset = function (env, output, rules) { 65 | output.add((env.compress ? '{' : ' {\n')); 66 | env.tabLevel = (env.tabLevel || 0) + 1; 67 | var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), 68 | tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "); 69 | for(var i = 0; i < rules.length; i++) { 70 | output.add(tabRuleStr); 71 | rules[i].genCSS(env, output); 72 | output.add(env.compress ? '' : '\n'); 73 | } 74 | env.tabLevel--; 75 | output.add(tabSetStr + "}"); 76 | }; 77 | 78 | })(require('./tree')); 79 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/alpha.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Alpha = function (val) { 4 | this.value = val; 5 | }; 6 | tree.Alpha.prototype = { 7 | type: "Alpha", 8 | accept: function (visitor) { 9 | this.value = visitor.visit(this.value); 10 | }, 11 | eval: function (env) { 12 | if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } 13 | return this; 14 | }, 15 | genCSS: function (env, output) { 16 | output.add("alpha(opacity="); 17 | 18 | if (this.value.genCSS) { 19 | this.value.genCSS(env, output); 20 | } else { 21 | output.add(this.value); 22 | } 23 | 24 | output.add(")"); 25 | }, 26 | toCSS: tree.toCSS 27 | }; 28 | 29 | })(require('../tree')); 30 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/anonymous.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Anonymous = function (string, index, currentFileInfo, mapLines) { 4 | this.value = string.value || string; 5 | this.index = index; 6 | this.mapLines = mapLines; 7 | this.currentFileInfo = currentFileInfo; 8 | }; 9 | tree.Anonymous.prototype = { 10 | type: "Anonymous", 11 | eval: function () { return this; }, 12 | compare: function (x) { 13 | if (!x.toCSS) { 14 | return -1; 15 | } 16 | 17 | var left = this.toCSS(), 18 | right = x.toCSS(); 19 | 20 | if (left === right) { 21 | return 0; 22 | } 23 | 24 | return left < right ? -1 : 1; 25 | }, 26 | genCSS: function (env, output) { 27 | output.add(this.value, this.currentFileInfo, this.index, this.mapLines); 28 | }, 29 | toCSS: tree.toCSS 30 | }; 31 | 32 | })(require('../tree')); 33 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/assignment.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Assignment = function (key, val) { 4 | this.key = key; 5 | this.value = val; 6 | }; 7 | tree.Assignment.prototype = { 8 | type: "Assignment", 9 | accept: function (visitor) { 10 | this.value = visitor.visit(this.value); 11 | }, 12 | eval: function (env) { 13 | if (this.value.eval) { 14 | return new(tree.Assignment)(this.key, this.value.eval(env)); 15 | } 16 | return this; 17 | }, 18 | genCSS: function (env, output) { 19 | output.add(this.key + '='); 20 | if (this.value.genCSS) { 21 | this.value.genCSS(env, output); 22 | } else { 23 | output.add(this.value); 24 | } 25 | }, 26 | toCSS: tree.toCSS 27 | }; 28 | 29 | })(require('../tree')); 30 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/call.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | // 4 | // A function call node. 5 | // 6 | tree.Call = function (name, args, index, currentFileInfo) { 7 | this.name = name; 8 | this.args = args; 9 | this.index = index; 10 | this.currentFileInfo = currentFileInfo; 11 | }; 12 | tree.Call.prototype = { 13 | type: "Call", 14 | accept: function (visitor) { 15 | this.args = visitor.visit(this.args); 16 | }, 17 | // 18 | // When evaluating a function call, 19 | // we either find the function in `tree.functions` [1], 20 | // in which case we call it, passing the evaluated arguments, 21 | // if this returns null or we cannot find the function, we 22 | // simply print it out as it appeared originally [2]. 23 | // 24 | // The *functions.js* file contains the built-in functions. 25 | // 26 | // The reason why we evaluate the arguments, is in the case where 27 | // we try to pass a variable to a function, like: `saturate(@color)`. 28 | // The function should receive the value, not the variable. 29 | // 30 | eval: function (env) { 31 | var args = this.args.map(function (a) { return a.eval(env); }), 32 | nameLC = this.name.toLowerCase(), 33 | result, func; 34 | 35 | if (nameLC in tree.functions) { // 1. 36 | try { 37 | func = new tree.functionCall(env, this.currentFileInfo); 38 | result = func[nameLC].apply(func, args); 39 | /*jshint eqnull:true */ 40 | if (result != null) { 41 | return result; 42 | } 43 | } catch (e) { 44 | throw { type: e.type || "Runtime", 45 | message: "error evaluating function `" + this.name + "`" + 46 | (e.message ? ': ' + e.message : ''), 47 | index: this.index, filename: this.currentFileInfo.filename }; 48 | } 49 | } 50 | 51 | return new tree.Call(this.name, args, this.index, this.currentFileInfo); 52 | }, 53 | 54 | genCSS: function (env, output) { 55 | output.add(this.name + "(", this.currentFileInfo, this.index); 56 | 57 | for(var i = 0; i < this.args.length; i++) { 58 | this.args[i].genCSS(env, output); 59 | if (i + 1 < this.args.length) { 60 | output.add(", "); 61 | } 62 | } 63 | 64 | output.add(")"); 65 | }, 66 | 67 | toCSS: tree.toCSS 68 | }; 69 | 70 | })(require('../tree')); 71 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/color.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | // 3 | // RGB Colors - #ff0014, #eee 4 | // 5 | tree.Color = function (rgb, a) { 6 | // 7 | // The end goal here, is to parse the arguments 8 | // into an integer triplet, such as `128, 255, 0` 9 | // 10 | // This facilitates operations and conversions. 11 | // 12 | if (Array.isArray(rgb)) { 13 | this.rgb = rgb; 14 | } else if (rgb.length == 6) { 15 | this.rgb = rgb.match(/.{2}/g).map(function (c) { 16 | return parseInt(c, 16); 17 | }); 18 | } else { 19 | this.rgb = rgb.split('').map(function (c) { 20 | return parseInt(c + c, 16); 21 | }); 22 | } 23 | this.alpha = typeof(a) === 'number' ? a : 1; 24 | }; 25 | 26 | var transparentKeyword = "transparent"; 27 | 28 | tree.Color.prototype = { 29 | type: "Color", 30 | eval: function () { return this; }, 31 | luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, 32 | 33 | genCSS: function (env, output) { 34 | output.add(this.toCSS(env)); 35 | }, 36 | toCSS: function (env, doNotCompress) { 37 | var compress = env && env.compress && !doNotCompress; 38 | 39 | // If we have some transparency, the only way to represent it 40 | // is via `rgba`. Otherwise, we use the hex representation, 41 | // which has better compatibility with older browsers. 42 | // Values are capped between `0` and `255`, rounded and zero-padded. 43 | if (this.alpha < 1.0) { 44 | if (this.alpha === 0 && this.isTransparentKeyword) { 45 | return transparentKeyword; 46 | } 47 | return "rgba(" + this.rgb.map(function (c) { 48 | return Math.round(c); 49 | }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")"; 50 | } else { 51 | var color = this.toRGB(); 52 | 53 | if (compress) { 54 | var splitcolor = color.split(''); 55 | 56 | // Convert color to short format 57 | if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { 58 | color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5]; 59 | } 60 | } 61 | 62 | return color; 63 | } 64 | }, 65 | 66 | // 67 | // Operations have to be done per-channel, if not, 68 | // channels will spill onto each other. Once we have 69 | // our result, in the form of an integer triplet, 70 | // we create a new Color node to hold the result. 71 | // 72 | operate: function (env, op, other) { 73 | var result = []; 74 | 75 | if (! (other instanceof tree.Color)) { 76 | other = other.toColor(); 77 | } 78 | 79 | for (var c = 0; c < 3; c++) { 80 | result[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); 81 | } 82 | return new(tree.Color)(result, this.alpha + other.alpha); 83 | }, 84 | 85 | toRGB: function () { 86 | return '#' + this.rgb.map(function (i) { 87 | i = Math.round(i); 88 | i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); 89 | return i.length === 1 ? '0' + i : i; 90 | }).join(''); 91 | }, 92 | 93 | toHSL: function () { 94 | var r = this.rgb[0] / 255, 95 | g = this.rgb[1] / 255, 96 | b = this.rgb[2] / 255, 97 | a = this.alpha; 98 | 99 | var max = Math.max(r, g, b), min = Math.min(r, g, b); 100 | var h, s, l = (max + min) / 2, d = max - min; 101 | 102 | if (max === min) { 103 | h = s = 0; 104 | } else { 105 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 106 | 107 | switch (max) { 108 | case r: h = (g - b) / d + (g < b ? 6 : 0); break; 109 | case g: h = (b - r) / d + 2; break; 110 | case b: h = (r - g) / d + 4; break; 111 | } 112 | h /= 6; 113 | } 114 | return { h: h * 360, s: s, l: l, a: a }; 115 | }, 116 | //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript 117 | toHSV: function () { 118 | var r = this.rgb[0] / 255, 119 | g = this.rgb[1] / 255, 120 | b = this.rgb[2] / 255, 121 | a = this.alpha; 122 | 123 | var max = Math.max(r, g, b), min = Math.min(r, g, b); 124 | var h, s, v = max; 125 | 126 | var d = max - min; 127 | if (max === 0) { 128 | s = 0; 129 | } else { 130 | s = d / max; 131 | } 132 | 133 | if (max === min) { 134 | h = 0; 135 | } else { 136 | switch(max){ 137 | case r: h = (g - b) / d + (g < b ? 6 : 0); break; 138 | case g: h = (b - r) / d + 2; break; 139 | case b: h = (r - g) / d + 4; break; 140 | } 141 | h /= 6; 142 | } 143 | return { h: h * 360, s: s, v: v, a: a }; 144 | }, 145 | toARGB: function () { 146 | var argb = [Math.round(this.alpha * 255)].concat(this.rgb); 147 | return '#' + argb.map(function (i) { 148 | i = Math.round(i); 149 | i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); 150 | return i.length === 1 ? '0' + i : i; 151 | }).join(''); 152 | }, 153 | compare: function (x) { 154 | if (!x.rgb) { 155 | return -1; 156 | } 157 | 158 | return (x.rgb[0] === this.rgb[0] && 159 | x.rgb[1] === this.rgb[1] && 160 | x.rgb[2] === this.rgb[2] && 161 | x.alpha === this.alpha) ? 0 : -1; 162 | } 163 | }; 164 | 165 | tree.Color.fromKeyword = function(keyword) { 166 | if (tree.colors.hasOwnProperty(keyword)) { 167 | // detect named color 168 | return new(tree.Color)(tree.colors[keyword].slice(1)); 169 | } 170 | if (keyword === transparentKeyword) { 171 | var transparent = new(tree.Color)([0, 0, 0], 0); 172 | transparent.isTransparentKeyword = true; 173 | return transparent; 174 | } 175 | }; 176 | 177 | 178 | })(require('../tree')); 179 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/comment.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Comment = function (value, silent, index, currentFileInfo) { 4 | this.value = value; 5 | this.silent = !!silent; 6 | this.currentFileInfo = currentFileInfo; 7 | }; 8 | tree.Comment.prototype = { 9 | type: "Comment", 10 | genCSS: function (env, output) { 11 | if (this.debugInfo) { 12 | output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); 13 | } 14 | output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n 15 | }, 16 | toCSS: tree.toCSS, 17 | isSilent: function(env) { 18 | var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), 19 | isCompressed = env.compress && !this.value.match(/^\/\*!/); 20 | return this.silent || isReference || isCompressed; 21 | }, 22 | eval: function () { return this; }, 23 | markReferenced: function () { 24 | this.isReferenced = true; 25 | } 26 | }; 27 | 28 | })(require('../tree')); 29 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/condition.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Condition = function (op, l, r, i, negate) { 4 | this.op = op.trim(); 5 | this.lvalue = l; 6 | this.rvalue = r; 7 | this.index = i; 8 | this.negate = negate; 9 | }; 10 | tree.Condition.prototype = { 11 | type: "Condition", 12 | accept: function (visitor) { 13 | this.lvalue = visitor.visit(this.lvalue); 14 | this.rvalue = visitor.visit(this.rvalue); 15 | }, 16 | eval: function (env) { 17 | var a = this.lvalue.eval(env), 18 | b = this.rvalue.eval(env); 19 | 20 | var i = this.index, result; 21 | 22 | result = (function (op) { 23 | switch (op) { 24 | case 'and': 25 | return a && b; 26 | case 'or': 27 | return a || b; 28 | default: 29 | if (a.compare) { 30 | result = a.compare(b); 31 | } else if (b.compare) { 32 | result = b.compare(a); 33 | } else { 34 | throw { type: "Type", 35 | message: "Unable to perform comparison", 36 | index: i }; 37 | } 38 | switch (result) { 39 | case -1: return op === '<' || op === '=<' || op === '<='; 40 | case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; 41 | case 1: return op === '>' || op === '>='; 42 | } 43 | } 44 | })(this.op); 45 | return this.negate ? !result : result; 46 | } 47 | }; 48 | 49 | })(require('../tree')); 50 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/dimension.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | // 4 | // A number with a unit 5 | // 6 | tree.Dimension = function (value, unit) { 7 | this.value = parseFloat(value); 8 | this.unit = (unit && unit instanceof tree.Unit) ? unit : 9 | new(tree.Unit)(unit ? [unit] : undefined); 10 | }; 11 | 12 | tree.Dimension.prototype = { 13 | type: "Dimension", 14 | accept: function (visitor) { 15 | this.unit = visitor.visit(this.unit); 16 | }, 17 | eval: function (env) { 18 | return this; 19 | }, 20 | toColor: function () { 21 | return new(tree.Color)([this.value, this.value, this.value]); 22 | }, 23 | genCSS: function (env, output) { 24 | if ((env && env.strictUnits) && !this.unit.isSingular()) { 25 | throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); 26 | } 27 | 28 | var value = this.value, 29 | strValue = String(value); 30 | 31 | if (value !== 0 && value < 0.000001 && value > -0.000001) { 32 | // would be output 1e-6 etc. 33 | strValue = value.toFixed(20).replace(/0+$/, ""); 34 | } 35 | 36 | if (env && env.compress) { 37 | // Zero values doesn't need a unit 38 | if (value === 0 && this.unit.isLength()) { 39 | output.add(strValue); 40 | return; 41 | } 42 | 43 | // Float values doesn't need a leading zero 44 | if (value > 0 && value < 1) { 45 | strValue = (strValue).substr(1); 46 | } 47 | } 48 | 49 | output.add(strValue); 50 | this.unit.genCSS(env, output); 51 | }, 52 | toCSS: tree.toCSS, 53 | 54 | // In an operation between two Dimensions, 55 | // we default to the first Dimension's unit, 56 | // so `1px + 2` will yield `3px`. 57 | operate: function (env, op, other) { 58 | /*jshint noempty:false */ 59 | var value = tree.operate(env, op, this.value, other.value), 60 | unit = this.unit.clone(); 61 | 62 | if (op === '+' || op === '-') { 63 | if (unit.numerator.length === 0 && unit.denominator.length === 0) { 64 | unit.numerator = other.unit.numerator.slice(0); 65 | unit.denominator = other.unit.denominator.slice(0); 66 | } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { 67 | // do nothing 68 | } else { 69 | other = other.convertTo(this.unit.usedUnits()); 70 | 71 | if(env.strictUnits && other.unit.toString() !== unit.toString()) { 72 | throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + 73 | "' and '" + other.unit.toString() + "'."); 74 | } 75 | 76 | value = tree.operate(env, op, this.value, other.value); 77 | } 78 | } else if (op === '*') { 79 | unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); 80 | unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); 81 | unit.cancel(); 82 | } else if (op === '/') { 83 | unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); 84 | unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); 85 | unit.cancel(); 86 | } 87 | return new(tree.Dimension)(value, unit); 88 | }, 89 | 90 | compare: function (other) { 91 | if (other instanceof tree.Dimension) { 92 | var a = this.unify(), b = other.unify(), 93 | aValue = a.value, bValue = b.value; 94 | 95 | if (bValue > aValue) { 96 | return -1; 97 | } else if (bValue < aValue) { 98 | return 1; 99 | } else { 100 | if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { 101 | return -1; 102 | } 103 | return 0; 104 | } 105 | } else { 106 | return -1; 107 | } 108 | }, 109 | 110 | unify: function () { 111 | return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); 112 | }, 113 | 114 | convertTo: function (conversions) { 115 | var value = this.value, unit = this.unit.clone(), 116 | i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; 117 | 118 | if (typeof conversions === 'string') { 119 | for(i in tree.UnitConversions) { 120 | if (tree.UnitConversions[i].hasOwnProperty(conversions)) { 121 | derivedConversions = {}; 122 | derivedConversions[i] = conversions; 123 | } 124 | } 125 | conversions = derivedConversions; 126 | } 127 | applyUnit = function (atomicUnit, denominator) { 128 | /*jshint loopfunc:true */ 129 | if (group.hasOwnProperty(atomicUnit)) { 130 | if (denominator) { 131 | value = value / (group[atomicUnit] / group[targetUnit]); 132 | } else { 133 | value = value * (group[atomicUnit] / group[targetUnit]); 134 | } 135 | 136 | return targetUnit; 137 | } 138 | 139 | return atomicUnit; 140 | }; 141 | 142 | for (groupName in conversions) { 143 | if (conversions.hasOwnProperty(groupName)) { 144 | targetUnit = conversions[groupName]; 145 | group = tree.UnitConversions[groupName]; 146 | 147 | unit.map(applyUnit); 148 | } 149 | } 150 | 151 | unit.cancel(); 152 | 153 | return new(tree.Dimension)(value, unit); 154 | } 155 | }; 156 | 157 | // http://www.w3.org/TR/css3-values/#absolute-lengths 158 | tree.UnitConversions = { 159 | length: { 160 | 'm': 1, 161 | 'cm': 0.01, 162 | 'mm': 0.001, 163 | 'in': 0.0254, 164 | 'pt': 0.0254 / 72, 165 | 'pc': 0.0254 / 72 * 12 166 | }, 167 | duration: { 168 | 's': 1, 169 | 'ms': 0.001 170 | }, 171 | angle: { 172 | 'rad': 1/(2*Math.PI), 173 | 'deg': 1/360, 174 | 'grad': 1/400, 175 | 'turn': 1 176 | } 177 | }; 178 | 179 | tree.Unit = function (numerator, denominator, backupUnit) { 180 | this.numerator = numerator ? numerator.slice(0).sort() : []; 181 | this.denominator = denominator ? denominator.slice(0).sort() : []; 182 | this.backupUnit = backupUnit; 183 | }; 184 | 185 | tree.Unit.prototype = { 186 | type: "Unit", 187 | clone: function () { 188 | return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); 189 | }, 190 | genCSS: function (env, output) { 191 | if (this.numerator.length >= 1) { 192 | output.add(this.numerator[0]); 193 | } else 194 | if (this.denominator.length >= 1) { 195 | output.add(this.denominator[0]); 196 | } else 197 | if ((!env || !env.strictUnits) && this.backupUnit) { 198 | output.add(this.backupUnit); 199 | } 200 | }, 201 | toCSS: tree.toCSS, 202 | 203 | toString: function () { 204 | var i, returnStr = this.numerator.join("*"); 205 | for (i = 0; i < this.denominator.length; i++) { 206 | returnStr += "/" + this.denominator[i]; 207 | } 208 | return returnStr; 209 | }, 210 | 211 | compare: function (other) { 212 | return this.is(other.toString()) ? 0 : -1; 213 | }, 214 | 215 | is: function (unitString) { 216 | return this.toString() === unitString; 217 | }, 218 | 219 | isLength: function () { 220 | return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); 221 | }, 222 | 223 | isEmpty: function () { 224 | return this.numerator.length === 0 && this.denominator.length === 0; 225 | }, 226 | 227 | isSingular: function() { 228 | return this.numerator.length <= 1 && this.denominator.length === 0; 229 | }, 230 | 231 | map: function(callback) { 232 | var i; 233 | 234 | for (i = 0; i < this.numerator.length; i++) { 235 | this.numerator[i] = callback(this.numerator[i], false); 236 | } 237 | 238 | for (i = 0; i < this.denominator.length; i++) { 239 | this.denominator[i] = callback(this.denominator[i], true); 240 | } 241 | }, 242 | 243 | usedUnits: function() { 244 | var group, result = {}, mapUnit; 245 | 246 | mapUnit = function (atomicUnit) { 247 | /*jshint loopfunc:true */ 248 | if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { 249 | result[groupName] = atomicUnit; 250 | } 251 | 252 | return atomicUnit; 253 | }; 254 | 255 | for (var groupName in tree.UnitConversions) { 256 | if (tree.UnitConversions.hasOwnProperty(groupName)) { 257 | group = tree.UnitConversions[groupName]; 258 | 259 | this.map(mapUnit); 260 | } 261 | } 262 | 263 | return result; 264 | }, 265 | 266 | cancel: function () { 267 | var counter = {}, atomicUnit, i, backup; 268 | 269 | for (i = 0; i < this.numerator.length; i++) { 270 | atomicUnit = this.numerator[i]; 271 | if (!backup) { 272 | backup = atomicUnit; 273 | } 274 | counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; 275 | } 276 | 277 | for (i = 0; i < this.denominator.length; i++) { 278 | atomicUnit = this.denominator[i]; 279 | if (!backup) { 280 | backup = atomicUnit; 281 | } 282 | counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; 283 | } 284 | 285 | this.numerator = []; 286 | this.denominator = []; 287 | 288 | for (atomicUnit in counter) { 289 | if (counter.hasOwnProperty(atomicUnit)) { 290 | var count = counter[atomicUnit]; 291 | 292 | if (count > 0) { 293 | for (i = 0; i < count; i++) { 294 | this.numerator.push(atomicUnit); 295 | } 296 | } else if (count < 0) { 297 | for (i = 0; i < -count; i++) { 298 | this.denominator.push(atomicUnit); 299 | } 300 | } 301 | } 302 | } 303 | 304 | if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { 305 | this.backupUnit = backup; 306 | } 307 | 308 | this.numerator.sort(); 309 | this.denominator.sort(); 310 | } 311 | }; 312 | 313 | })(require('../tree')); 314 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/directive.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Directive = function (name, value, index, currentFileInfo) { 4 | this.name = name; 5 | 6 | if (Array.isArray(value)) { 7 | this.rules = [new(tree.Ruleset)([], value)]; 8 | this.rules[0].allowImports = true; 9 | } else { 10 | this.value = value; 11 | } 12 | this.currentFileInfo = currentFileInfo; 13 | 14 | }; 15 | tree.Directive.prototype = { 16 | type: "Directive", 17 | accept: function (visitor) { 18 | this.rules = visitor.visit(this.rules); 19 | this.value = visitor.visit(this.value); 20 | }, 21 | genCSS: function (env, output) { 22 | output.add(this.name, this.currentFileInfo, this.index); 23 | if (this.rules) { 24 | tree.outputRuleset(env, output, this.rules); 25 | } else { 26 | output.add(' '); 27 | this.value.genCSS(env, output); 28 | output.add(';'); 29 | } 30 | }, 31 | toCSS: tree.toCSS, 32 | eval: function (env) { 33 | var evaldDirective = this; 34 | if (this.rules) { 35 | env.frames.unshift(this); 36 | evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); 37 | evaldDirective.rules = [this.rules[0].eval(env)]; 38 | evaldDirective.rules[0].root = true; 39 | env.frames.shift(); 40 | } 41 | return evaldDirective; 42 | }, 43 | variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, 44 | find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, 45 | rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, 46 | markReferenced: function () { 47 | var i, rules; 48 | this.isReferenced = true; 49 | if (this.rules) { 50 | rules = this.rules[0].rules; 51 | for (i = 0; i < rules.length; i++) { 52 | if (rules[i].markReferenced) { 53 | rules[i].markReferenced(); 54 | } 55 | } 56 | } 57 | } 58 | }; 59 | 60 | })(require('../tree')); 61 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/element.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Element = function (combinator, value, index, currentFileInfo) { 4 | this.combinator = combinator instanceof tree.Combinator ? 5 | combinator : new(tree.Combinator)(combinator); 6 | 7 | if (typeof(value) === 'string') { 8 | this.value = value.trim(); 9 | } else if (value) { 10 | this.value = value; 11 | } else { 12 | this.value = ""; 13 | } 14 | this.index = index; 15 | this.currentFileInfo = currentFileInfo; 16 | }; 17 | tree.Element.prototype = { 18 | type: "Element", 19 | accept: function (visitor) { 20 | this.combinator = visitor.visit(this.combinator); 21 | this.value = visitor.visit(this.value); 22 | }, 23 | eval: function (env) { 24 | return new(tree.Element)(this.combinator, 25 | this.value.eval ? this.value.eval(env) : this.value, 26 | this.index, 27 | this.currentFileInfo); 28 | }, 29 | genCSS: function (env, output) { 30 | output.add(this.toCSS(env), this.currentFileInfo, this.index); 31 | }, 32 | toCSS: function (env) { 33 | var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); 34 | if (value === '' && this.combinator.value.charAt(0) === '&') { 35 | return ''; 36 | } else { 37 | return this.combinator.toCSS(env || {}) + value; 38 | } 39 | } 40 | }; 41 | 42 | tree.Attribute = function (key, op, value) { 43 | this.key = key; 44 | this.op = op; 45 | this.value = value; 46 | }; 47 | tree.Attribute.prototype = { 48 | type: "Attribute", 49 | accept: function (visitor) { 50 | this.value = visitor.visit(this.value); 51 | }, 52 | eval: function (env) { 53 | return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, 54 | this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); 55 | }, 56 | genCSS: function (env, output) { 57 | output.add(this.toCSS(env)); 58 | }, 59 | toCSS: function (env) { 60 | var value = this.key.toCSS ? this.key.toCSS(env) : this.key; 61 | 62 | if (this.op) { 63 | value += this.op; 64 | value += (this.value.toCSS ? this.value.toCSS(env) : this.value); 65 | } 66 | 67 | return '[' + value + ']'; 68 | } 69 | }; 70 | 71 | tree.Combinator = function (value) { 72 | if (value === ' ') { 73 | this.value = ' '; 74 | } else { 75 | this.value = value ? value.trim() : ""; 76 | } 77 | }; 78 | tree.Combinator.prototype = { 79 | type: "Combinator", 80 | _outputMap: { 81 | '' : '', 82 | ' ' : ' ', 83 | ':' : ' :', 84 | '+' : ' + ', 85 | '~' : ' ~ ', 86 | '>' : ' > ', 87 | '|' : '|' 88 | }, 89 | _outputMapCompressed: { 90 | '' : '', 91 | ' ' : ' ', 92 | ':' : ' :', 93 | '+' : '+', 94 | '~' : '~', 95 | '>' : '>', 96 | '|' : '|' 97 | }, 98 | genCSS: function (env, output) { 99 | output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); 100 | }, 101 | toCSS: tree.toCSS 102 | }; 103 | 104 | })(require('../tree')); 105 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/expression.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Expression = function (value) { this.value = value; }; 4 | tree.Expression.prototype = { 5 | type: "Expression", 6 | accept: function (visitor) { 7 | this.value = visitor.visit(this.value); 8 | }, 9 | eval: function (env) { 10 | var returnValue, 11 | inParenthesis = this.parens && !this.parensInOp, 12 | doubleParen = false; 13 | if (inParenthesis) { 14 | env.inParenthesis(); 15 | } 16 | if (this.value.length > 1) { 17 | returnValue = new(tree.Expression)(this.value.map(function (e) { 18 | return e.eval(env); 19 | })); 20 | } else if (this.value.length === 1) { 21 | if (this.value[0].parens && !this.value[0].parensInOp) { 22 | doubleParen = true; 23 | } 24 | returnValue = this.value[0].eval(env); 25 | } else { 26 | returnValue = this; 27 | } 28 | if (inParenthesis) { 29 | env.outOfParenthesis(); 30 | } 31 | if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { 32 | returnValue = new(tree.Paren)(returnValue); 33 | } 34 | return returnValue; 35 | }, 36 | genCSS: function (env, output) { 37 | for(var i = 0; i < this.value.length; i++) { 38 | this.value[i].genCSS(env, output); 39 | if (i + 1 < this.value.length) { 40 | output.add(" "); 41 | } 42 | } 43 | }, 44 | toCSS: tree.toCSS, 45 | throwAwayComments: function () { 46 | this.value = this.value.filter(function(v) { 47 | return !(v instanceof tree.Comment); 48 | }); 49 | } 50 | }; 51 | 52 | })(require('../tree')); 53 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/extend.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Extend = function Extend(selector, option, index) { 4 | this.selector = selector; 5 | this.option = option; 6 | this.index = index; 7 | 8 | switch(option) { 9 | case "all": 10 | this.allowBefore = true; 11 | this.allowAfter = true; 12 | break; 13 | default: 14 | this.allowBefore = false; 15 | this.allowAfter = false; 16 | break; 17 | } 18 | }; 19 | 20 | tree.Extend.prototype = { 21 | type: "Extend", 22 | accept: function (visitor) { 23 | this.selector = visitor.visit(this.selector); 24 | }, 25 | eval: function (env) { 26 | return new(tree.Extend)(this.selector.eval(env), this.option, this.index); 27 | }, 28 | clone: function (env) { 29 | return new(tree.Extend)(this.selector, this.option, this.index); 30 | }, 31 | findSelfSelectors: function (selectors) { 32 | var selfElements = [], 33 | i, 34 | selectorElements; 35 | 36 | for(i = 0; i < selectors.length; i++) { 37 | selectorElements = selectors[i].elements; 38 | // duplicate the logic in genCSS function inside the selector node. 39 | // future TODO - move both logics into the selector joiner visitor 40 | if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { 41 | selectorElements[0].combinator.value = ' '; 42 | } 43 | selfElements = selfElements.concat(selectors[i].elements); 44 | } 45 | 46 | this.selfSelectors = [{ elements: selfElements }]; 47 | } 48 | }; 49 | 50 | })(require('../tree')); 51 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/import.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | // 3 | // CSS @import node 4 | // 5 | // The general strategy here is that we don't want to wait 6 | // for the parsing to be completed, before we start importing 7 | // the file. That's because in the context of a browser, 8 | // most of the time will be spent waiting for the server to respond. 9 | // 10 | // On creation, we push the import path to our import queue, though 11 | // `import,push`, we also pass it a callback, which it'll call once 12 | // the file has been fetched, and parsed. 13 | // 14 | tree.Import = function (path, features, options, index, currentFileInfo) { 15 | this.options = options; 16 | this.index = index; 17 | this.path = path; 18 | this.features = features; 19 | this.currentFileInfo = currentFileInfo; 20 | 21 | if (this.options.less !== undefined || this.options.inline) { 22 | this.css = !this.options.less || this.options.inline; 23 | } else { 24 | var pathValue = this.getPath(); 25 | if (pathValue && /css([\?;].*)?$/.test(pathValue)) { 26 | this.css = true; 27 | } 28 | } 29 | }; 30 | 31 | // 32 | // The actual import node doesn't return anything, when converted to CSS. 33 | // The reason is that it's used at the evaluation stage, so that the rules 34 | // it imports can be treated like any other rules. 35 | // 36 | // In `eval`, we make sure all Import nodes get evaluated, recursively, so 37 | // we end up with a flat structure, which can easily be imported in the parent 38 | // ruleset. 39 | // 40 | tree.Import.prototype = { 41 | type: "Import", 42 | accept: function (visitor) { 43 | this.features = visitor.visit(this.features); 44 | this.path = visitor.visit(this.path); 45 | if (!this.options.inline) { 46 | this.root = visitor.visit(this.root); 47 | } 48 | }, 49 | genCSS: function (env, output) { 50 | if (this.css) { 51 | output.add("@import ", this.currentFileInfo, this.index); 52 | this.path.genCSS(env, output); 53 | if (this.features) { 54 | output.add(" "); 55 | this.features.genCSS(env, output); 56 | } 57 | output.add(';'); 58 | } 59 | }, 60 | toCSS: tree.toCSS, 61 | getPath: function () { 62 | if (this.path instanceof tree.Quoted) { 63 | var path = this.path.value; 64 | return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; 65 | } else if (this.path instanceof tree.URL) { 66 | return this.path.value.value; 67 | } 68 | return null; 69 | }, 70 | evalForImport: function (env) { 71 | return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); 72 | }, 73 | evalPath: function (env) { 74 | var path = this.path.eval(env); 75 | var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; 76 | 77 | if (!(path instanceof tree.URL)) { 78 | if (rootpath) { 79 | var pathValue = path.value; 80 | // Add the base path if the import is relative 81 | if (pathValue && env.isPathRelative(pathValue)) { 82 | path.value = rootpath + pathValue; 83 | } 84 | } 85 | path.value = env.normalizePath(path.value); 86 | } 87 | 88 | return path; 89 | }, 90 | eval: function (env) { 91 | var ruleset, features = this.features && this.features.eval(env); 92 | 93 | if (this.skip) { return []; } 94 | 95 | if (this.options.inline) { 96 | //todo needs to reference css file not import 97 | var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); 98 | return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; 99 | } else if (this.css) { 100 | var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); 101 | if (!newImport.css && this.error) { 102 | throw this.error; 103 | } 104 | return newImport; 105 | } else { 106 | ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); 107 | 108 | ruleset.evalImports(env); 109 | 110 | return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; 111 | } 112 | } 113 | }; 114 | 115 | })(require('../tree')); 116 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/javascript.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.JavaScript = function (string, index, escaped) { 4 | this.escaped = escaped; 5 | this.expression = string; 6 | this.index = index; 7 | }; 8 | tree.JavaScript.prototype = { 9 | type: "JavaScript", 10 | eval: function (env) { 11 | var result, 12 | that = this, 13 | context = {}; 14 | 15 | var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { 16 | return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); 17 | }); 18 | 19 | try { 20 | expression = new(Function)('return (' + expression + ')'); 21 | } catch (e) { 22 | throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , 23 | index: this.index }; 24 | } 25 | 26 | for (var k in env.frames[0].variables()) { 27 | /*jshint loopfunc:true */ 28 | context[k.slice(1)] = { 29 | value: env.frames[0].variables()[k].value, 30 | toJS: function () { 31 | return this.value.eval(env).toCSS(); 32 | } 33 | }; 34 | } 35 | 36 | try { 37 | result = expression.call(context); 38 | } catch (e) { 39 | throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , 40 | index: this.index }; 41 | } 42 | if (typeof(result) === 'string') { 43 | return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); 44 | } else if (Array.isArray(result)) { 45 | return new(tree.Anonymous)(result.join(', ')); 46 | } else { 47 | return new(tree.Anonymous)(result); 48 | } 49 | } 50 | }; 51 | 52 | })(require('../tree')); 53 | 54 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/keyword.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Keyword = function (value) { this.value = value; }; 4 | tree.Keyword.prototype = { 5 | type: "Keyword", 6 | eval: function () { return this; }, 7 | genCSS: function (env, output) { 8 | output.add(this.value); 9 | }, 10 | toCSS: tree.toCSS, 11 | compare: function (other) { 12 | if (other instanceof tree.Keyword) { 13 | return other.value === this.value ? 0 : 1; 14 | } else { 15 | return -1; 16 | } 17 | } 18 | }; 19 | 20 | tree.True = new(tree.Keyword)('true'); 21 | tree.False = new(tree.Keyword)('false'); 22 | 23 | })(require('../tree')); 24 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/media.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Media = function (value, features, index, currentFileInfo) { 4 | this.index = index; 5 | this.currentFileInfo = currentFileInfo; 6 | 7 | var selectors = this.emptySelectors(); 8 | 9 | this.features = new(tree.Value)(features); 10 | this.rules = [new(tree.Ruleset)(selectors, value)]; 11 | this.rules[0].allowImports = true; 12 | }; 13 | tree.Media.prototype = { 14 | type: "Media", 15 | accept: function (visitor) { 16 | this.features = visitor.visit(this.features); 17 | this.rules = visitor.visit(this.rules); 18 | }, 19 | genCSS: function (env, output) { 20 | output.add('@media ', this.currentFileInfo, this.index); 21 | this.features.genCSS(env, output); 22 | tree.outputRuleset(env, output, this.rules); 23 | }, 24 | toCSS: tree.toCSS, 25 | eval: function (env) { 26 | if (!env.mediaBlocks) { 27 | env.mediaBlocks = []; 28 | env.mediaPath = []; 29 | } 30 | 31 | var media = new(tree.Media)([], [], this.index, this.currentFileInfo); 32 | if(this.debugInfo) { 33 | this.rules[0].debugInfo = this.debugInfo; 34 | media.debugInfo = this.debugInfo; 35 | } 36 | var strictMathBypass = false; 37 | if (!env.strictMath) { 38 | strictMathBypass = true; 39 | env.strictMath = true; 40 | } 41 | try { 42 | media.features = this.features.eval(env); 43 | } 44 | finally { 45 | if (strictMathBypass) { 46 | env.strictMath = false; 47 | } 48 | } 49 | 50 | env.mediaPath.push(media); 51 | env.mediaBlocks.push(media); 52 | 53 | env.frames.unshift(this.rules[0]); 54 | media.rules = [this.rules[0].eval(env)]; 55 | env.frames.shift(); 56 | 57 | env.mediaPath.pop(); 58 | 59 | return env.mediaPath.length === 0 ? media.evalTop(env) : 60 | media.evalNested(env); 61 | }, 62 | variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, 63 | find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, 64 | rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, 65 | emptySelectors: function() { 66 | var el = new(tree.Element)('', '&', this.index, this.currentFileInfo); 67 | return [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; 68 | }, 69 | markReferenced: function () { 70 | var i, rules = this.rules[0].rules; 71 | this.isReferenced = true; 72 | for (i = 0; i < rules.length; i++) { 73 | if (rules[i].markReferenced) { 74 | rules[i].markReferenced(); 75 | } 76 | } 77 | }, 78 | 79 | evalTop: function (env) { 80 | var result = this; 81 | 82 | // Render all dependent Media blocks. 83 | if (env.mediaBlocks.length > 1) { 84 | var selectors = this.emptySelectors(); 85 | result = new(tree.Ruleset)(selectors, env.mediaBlocks); 86 | result.multiMedia = true; 87 | } 88 | 89 | delete env.mediaBlocks; 90 | delete env.mediaPath; 91 | 92 | return result; 93 | }, 94 | evalNested: function (env) { 95 | var i, value, 96 | path = env.mediaPath.concat([this]); 97 | 98 | // Extract the media-query conditions separated with `,` (OR). 99 | for (i = 0; i < path.length; i++) { 100 | value = path[i].features instanceof tree.Value ? 101 | path[i].features.value : path[i].features; 102 | path[i] = Array.isArray(value) ? value : [value]; 103 | } 104 | 105 | // Trace all permutations to generate the resulting media-query. 106 | // 107 | // (a, b and c) with nested (d, e) -> 108 | // a and d 109 | // a and e 110 | // b and c and d 111 | // b and c and e 112 | this.features = new(tree.Value)(this.permute(path).map(function (path) { 113 | path = path.map(function (fragment) { 114 | return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); 115 | }); 116 | 117 | for(i = path.length - 1; i > 0; i--) { 118 | path.splice(i, 0, new(tree.Anonymous)("and")); 119 | } 120 | 121 | return new(tree.Expression)(path); 122 | })); 123 | 124 | // Fake a tree-node that doesn't output anything. 125 | return new(tree.Ruleset)([], []); 126 | }, 127 | permute: function (arr) { 128 | if (arr.length === 0) { 129 | return []; 130 | } else if (arr.length === 1) { 131 | return arr[0]; 132 | } else { 133 | var result = []; 134 | var rest = this.permute(arr.slice(1)); 135 | for (var i = 0; i < rest.length; i++) { 136 | for (var j = 0; j < arr[0].length; j++) { 137 | result.push([arr[0][j]].concat(rest[i])); 138 | } 139 | } 140 | return result; 141 | } 142 | }, 143 | bubbleSelectors: function (selectors) { 144 | this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; 145 | } 146 | }; 147 | 148 | })(require('../tree')); 149 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/mixin.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.mixin = {}; 4 | tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { 5 | this.selector = new(tree.Selector)(elements); 6 | this.arguments = args; 7 | this.index = index; 8 | this.currentFileInfo = currentFileInfo; 9 | this.important = important; 10 | }; 11 | tree.mixin.Call.prototype = { 12 | type: "MixinCall", 13 | accept: function (visitor) { 14 | this.selector = visitor.visit(this.selector); 15 | this.arguments = visitor.visit(this.arguments); 16 | }, 17 | eval: function (env) { 18 | var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule; 19 | 20 | args = this.arguments && this.arguments.map(function (a) { 21 | return { name: a.name, value: a.value.eval(env) }; 22 | }); 23 | 24 | for (i = 0; i < env.frames.length; i++) { 25 | if ((mixins = env.frames[i].find(this.selector)).length > 0) { 26 | isOneFound = true; 27 | for (m = 0; m < mixins.length; m++) { 28 | mixin = mixins[m]; 29 | isRecursive = false; 30 | for(f = 0; f < env.frames.length; f++) { 31 | if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { 32 | isRecursive = true; 33 | break; 34 | } 35 | } 36 | if (isRecursive) { 37 | continue; 38 | } 39 | if (mixin.matchArgs(args, env)) { 40 | if (!mixin.matchCondition || mixin.matchCondition(args, env)) { 41 | try { 42 | if (!(mixin instanceof tree.mixin.Definition)) { 43 | mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); 44 | mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; 45 | } 46 | //if (this.important) { 47 | // isImportant = env.isImportant; 48 | // env.isImportant = true; 49 | //} 50 | Array.prototype.push.apply( 51 | rules, mixin.eval(env, args, this.important).rules); 52 | //if (this.important) { 53 | // env.isImportant = isImportant; 54 | //} 55 | } catch (e) { 56 | throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; 57 | } 58 | } 59 | match = true; 60 | } 61 | } 62 | if (match) { 63 | if (!this.currentFileInfo || !this.currentFileInfo.reference) { 64 | for (i = 0; i < rules.length; i++) { 65 | rule = rules[i]; 66 | if (rule.markReferenced) { 67 | rule.markReferenced(); 68 | } 69 | } 70 | } 71 | return rules; 72 | } 73 | } 74 | } 75 | if (isOneFound) { 76 | throw { type: 'Runtime', 77 | message: 'No matching definition was found for `' + 78 | this.selector.toCSS().trim() + '(' + 79 | (args ? args.map(function (a) { 80 | var argValue = ""; 81 | if (a.name) { 82 | argValue += a.name + ":"; 83 | } 84 | if (a.value.toCSS) { 85 | argValue += a.value.toCSS(); 86 | } else { 87 | argValue += "???"; 88 | } 89 | return argValue; 90 | }).join(', ') : "") + ")`", 91 | index: this.index, filename: this.currentFileInfo.filename }; 92 | } else { 93 | throw { type: 'Name', 94 | message: this.selector.toCSS().trim() + " is undefined", 95 | index: this.index, filename: this.currentFileInfo.filename }; 96 | } 97 | } 98 | }; 99 | 100 | tree.mixin.Definition = function (name, params, rules, condition, variadic) { 101 | this.name = name; 102 | this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; 103 | this.params = params; 104 | this.condition = condition; 105 | this.variadic = variadic; 106 | this.arity = params.length; 107 | this.rules = rules; 108 | this._lookups = {}; 109 | this.required = params.reduce(function (count, p) { 110 | if (!p.name || (p.name && !p.value)) { return count + 1; } 111 | else { return count; } 112 | }, 0); 113 | this.parent = tree.Ruleset.prototype; 114 | this.frames = []; 115 | }; 116 | tree.mixin.Definition.prototype = { 117 | type: "MixinDefinition", 118 | accept: function (visitor) { 119 | this.params = visitor.visit(this.params); 120 | this.rules = visitor.visit(this.rules); 121 | this.condition = visitor.visit(this.condition); 122 | }, 123 | variable: function (name) { return this.parent.variable.call(this, name); }, 124 | variables: function () { return this.parent.variables.call(this); }, 125 | find: function () { return this.parent.find.apply(this, arguments); }, 126 | rulesets: function () { return this.parent.rulesets.apply(this); }, 127 | 128 | evalParams: function (env, mixinEnv, args, evaldArguments) { 129 | /*jshint boss:true */ 130 | var frame = new(tree.Ruleset)(null, []), 131 | varargs, arg, 132 | params = this.params.slice(0), 133 | i, j, val, name, isNamedFound, argIndex; 134 | 135 | mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); 136 | 137 | if (args) { 138 | args = args.slice(0); 139 | 140 | for(i = 0; i < args.length; i++) { 141 | arg = args[i]; 142 | if (name = (arg && arg.name)) { 143 | isNamedFound = false; 144 | for(j = 0; j < params.length; j++) { 145 | if (!evaldArguments[j] && name === params[j].name) { 146 | evaldArguments[j] = arg.value.eval(env); 147 | frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); 148 | isNamedFound = true; 149 | break; 150 | } 151 | } 152 | if (isNamedFound) { 153 | args.splice(i, 1); 154 | i--; 155 | continue; 156 | } else { 157 | throw { type: 'Runtime', message: "Named argument for " + this.name + 158 | ' ' + args[i].name + ' not found' }; 159 | } 160 | } 161 | } 162 | } 163 | argIndex = 0; 164 | for (i = 0; i < params.length; i++) { 165 | if (evaldArguments[i]) { continue; } 166 | 167 | arg = args && args[argIndex]; 168 | 169 | if (name = params[i].name) { 170 | if (params[i].variadic && args) { 171 | varargs = []; 172 | for (j = argIndex; j < args.length; j++) { 173 | varargs.push(args[j].value.eval(env)); 174 | } 175 | frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); 176 | } else { 177 | val = arg && arg.value; 178 | if (val) { 179 | val = val.eval(env); 180 | } else if (params[i].value) { 181 | val = params[i].value.eval(mixinEnv); 182 | frame.resetCache(); 183 | } else { 184 | throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + 185 | ' (' + args.length + ' for ' + this.arity + ')' }; 186 | } 187 | 188 | frame.rules.unshift(new(tree.Rule)(name, val)); 189 | evaldArguments[i] = val; 190 | } 191 | } 192 | 193 | if (params[i].variadic && args) { 194 | for (j = argIndex; j < args.length; j++) { 195 | evaldArguments[j] = args[j].value.eval(env); 196 | } 197 | } 198 | argIndex++; 199 | } 200 | 201 | return frame; 202 | }, 203 | eval: function (env, args, important) { 204 | var _arguments = [], 205 | mixinFrames = this.frames.concat(env.frames), 206 | frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), 207 | rules, ruleset; 208 | 209 | frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); 210 | 211 | rules = this.rules.slice(0); 212 | 213 | ruleset = new(tree.Ruleset)(null, rules); 214 | ruleset.originalRuleset = this; 215 | ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); 216 | if (important) { 217 | ruleset = this.parent.makeImportant.apply(ruleset); 218 | } 219 | return ruleset; 220 | }, 221 | matchCondition: function (args, env) { 222 | if (this.condition && !this.condition.eval( 223 | new(tree.evalEnv)(env, 224 | [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables 225 | .concat(this.frames) // the parent namespace/mixin frames 226 | .concat(env.frames)))) { // the current environment frames 227 | return false; 228 | } 229 | return true; 230 | }, 231 | matchArgs: function (args, env) { 232 | var argsLength = (args && args.length) || 0, len; 233 | 234 | if (! this.variadic) { 235 | if (argsLength < this.required) { return false; } 236 | if (argsLength > this.params.length) { return false; } 237 | } else { 238 | if (argsLength < (this.required - 1)) { return false; } 239 | } 240 | 241 | len = Math.min(argsLength, this.arity); 242 | 243 | for (var i = 0; i < len; i++) { 244 | if (!this.params[i].name && !this.params[i].variadic) { 245 | if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { 246 | return false; 247 | } 248 | } 249 | } 250 | return true; 251 | } 252 | }; 253 | 254 | })(require('../tree')); 255 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/negative.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Negative = function (node) { 4 | this.value = node; 5 | }; 6 | tree.Negative.prototype = { 7 | type: "Negative", 8 | accept: function (visitor) { 9 | this.value = visitor.visit(this.value); 10 | }, 11 | genCSS: function (env, output) { 12 | output.add('-'); 13 | this.value.genCSS(env, output); 14 | }, 15 | toCSS: tree.toCSS, 16 | eval: function (env) { 17 | if (env.isMathOn()) { 18 | return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); 19 | } 20 | return new(tree.Negative)(this.value.eval(env)); 21 | } 22 | }; 23 | 24 | })(require('../tree')); 25 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/operation.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Operation = function (op, operands, isSpaced) { 4 | this.op = op.trim(); 5 | this.operands = operands; 6 | this.isSpaced = isSpaced; 7 | }; 8 | tree.Operation.prototype = { 9 | type: "Operation", 10 | accept: function (visitor) { 11 | this.operands = visitor.visit(this.operands); 12 | }, 13 | eval: function (env) { 14 | var a = this.operands[0].eval(env), 15 | b = this.operands[1].eval(env), 16 | temp; 17 | 18 | if (env.isMathOn()) { 19 | if (a instanceof tree.Dimension && b instanceof tree.Color) { 20 | if (this.op === '*' || this.op === '+') { 21 | temp = b, b = a, a = temp; 22 | } else { 23 | throw { type: "Operation", 24 | message: "Can't substract or divide a color from a number" }; 25 | } 26 | } 27 | if (!a.operate) { 28 | throw { type: "Operation", 29 | message: "Operation on an invalid type" }; 30 | } 31 | 32 | return a.operate(env, this.op, b); 33 | } else { 34 | return new(tree.Operation)(this.op, [a, b], this.isSpaced); 35 | } 36 | }, 37 | genCSS: function (env, output) { 38 | this.operands[0].genCSS(env, output); 39 | if (this.isSpaced) { 40 | output.add(" "); 41 | } 42 | output.add(this.op); 43 | if (this.isSpaced) { 44 | output.add(" "); 45 | } 46 | this.operands[1].genCSS(env, output); 47 | }, 48 | toCSS: tree.toCSS 49 | }; 50 | 51 | tree.operate = function (env, op, a, b) { 52 | switch (op) { 53 | case '+': return a + b; 54 | case '-': return a - b; 55 | case '*': return a * b; 56 | case '/': return a / b; 57 | } 58 | }; 59 | 60 | })(require('../tree')); 61 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/paren.js: -------------------------------------------------------------------------------- 1 | 2 | (function (tree) { 3 | 4 | tree.Paren = function (node) { 5 | this.value = node; 6 | }; 7 | tree.Paren.prototype = { 8 | type: "Paren", 9 | accept: function (visitor) { 10 | this.value = visitor.visit(this.value); 11 | }, 12 | genCSS: function (env, output) { 13 | output.add('('); 14 | this.value.genCSS(env, output); 15 | output.add(')'); 16 | }, 17 | toCSS: tree.toCSS, 18 | eval: function (env) { 19 | return new(tree.Paren)(this.value.eval(env)); 20 | } 21 | }; 22 | 23 | })(require('../tree')); 24 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/quoted.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Quoted = function (str, content, escaped, index, currentFileInfo) { 4 | this.escaped = escaped; 5 | this.value = content || ''; 6 | this.quote = str.charAt(0); 7 | this.index = index; 8 | this.currentFileInfo = currentFileInfo; 9 | }; 10 | tree.Quoted.prototype = { 11 | type: "Quoted", 12 | genCSS: function (env, output) { 13 | if (!this.escaped) { 14 | output.add(this.quote, this.currentFileInfo, this.index); 15 | } 16 | output.add(this.value); 17 | if (!this.escaped) { 18 | output.add(this.quote); 19 | } 20 | }, 21 | toCSS: tree.toCSS, 22 | eval: function (env) { 23 | var that = this; 24 | var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { 25 | return new(tree.JavaScript)(exp, that.index, true).eval(env).value; 26 | }).replace(/@\{([\w-]+)\}/g, function (_, name) { 27 | var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); 28 | return (v instanceof tree.Quoted) ? v.value : v.toCSS(); 29 | }); 30 | return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); 31 | }, 32 | compare: function (x) { 33 | if (!x.toCSS) { 34 | return -1; 35 | } 36 | 37 | var left = this.toCSS(), 38 | right = x.toCSS(); 39 | 40 | if (left === right) { 41 | return 0; 42 | } 43 | 44 | return left < right ? -1 : 1; 45 | } 46 | }; 47 | 48 | })(require('../tree')); 49 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/ratio.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Ratio = function (value) { 4 | this.value = value; 5 | }; 6 | tree.Ratio.prototype = { 7 | toCSS: function (env) { 8 | return this.value; 9 | }, 10 | eval: function () { return this } 11 | }; 12 | 13 | })(require('../tree')); 14 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/rule.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { 4 | this.name = name; 5 | this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); 6 | this.important = important ? ' ' + important.trim() : ''; 7 | this.merge = merge; 8 | this.index = index; 9 | this.currentFileInfo = currentFileInfo; 10 | this.inline = inline || false; 11 | this.variable = (name.charAt(0) === '@'); 12 | }; 13 | 14 | tree.Rule.prototype = { 15 | type: "Rule", 16 | accept: function (visitor) { 17 | this.value = visitor.visit(this.value); 18 | }, 19 | genCSS: function (env, output) { 20 | output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); 21 | try { 22 | this.value.genCSS(env, output); 23 | } 24 | catch(e) { 25 | e.index = this.index; 26 | e.filename = this.currentFileInfo.filename; 27 | throw e; 28 | } 29 | output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); 30 | }, 31 | toCSS: tree.toCSS, 32 | eval: function (env) { 33 | var strictMathBypass = false; 34 | if (this.name === "font" && !env.strictMath) { 35 | strictMathBypass = true; 36 | env.strictMath = true; 37 | } 38 | try { 39 | return new(tree.Rule)(this.name, 40 | this.value.eval(env), 41 | this.important, 42 | this.merge, 43 | this.index, this.currentFileInfo, this.inline); 44 | } 45 | finally { 46 | if (strictMathBypass) { 47 | env.strictMath = false; 48 | } 49 | } 50 | }, 51 | makeImportant: function () { 52 | return new(tree.Rule)(this.name, 53 | this.value, 54 | "!important", 55 | this.merge, 56 | this.index, this.currentFileInfo, this.inline); 57 | } 58 | }; 59 | 60 | })(require('../tree')); 61 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/selector.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { 4 | this.elements = elements; 5 | this.extendList = extendList || []; 6 | this.condition = condition; 7 | this.currentFileInfo = currentFileInfo || {}; 8 | this.isReferenced = isReferenced; 9 | if (!condition) { 10 | this.evaldCondition = true; 11 | } 12 | }; 13 | tree.Selector.prototype = { 14 | type: "Selector", 15 | accept: function (visitor) { 16 | this.elements = visitor.visit(this.elements); 17 | this.extendList = visitor.visit(this.extendList); 18 | this.condition = visitor.visit(this.condition); 19 | }, 20 | createDerived: function(elements, extendList, evaldCondition) { 21 | /*jshint eqnull:true */ 22 | evaldCondition = evaldCondition != null ? evaldCondition : this.evaldCondition; 23 | var newSelector = new(tree.Selector)(elements, extendList || this.extendList, this.condition, this.index, this.currentFileInfo, this.isReferenced); 24 | newSelector.evaldCondition = evaldCondition; 25 | return newSelector; 26 | }, 27 | match: function (other) { 28 | var elements = this.elements, 29 | len = elements.length, 30 | oelements, olen, max, i; 31 | 32 | oelements = other.elements.slice( 33 | (other.elements.length && other.elements[0].value === "&") ? 1 : 0); 34 | olen = oelements.length; 35 | max = Math.min(len, olen); 36 | 37 | if (olen === 0 || len < olen) { 38 | return 0; 39 | } else { 40 | for (i = 0; i < max; i++) { 41 | if (elements[i].value !== oelements[i].value) { 42 | return 0; 43 | } 44 | } 45 | } 46 | return max; // return number of matched selectors 47 | }, 48 | eval: function (env) { 49 | var evaldCondition = this.condition && this.condition.eval(env); 50 | 51 | return this.createDerived(this.elements.map(function (e) { 52 | return e.eval(env); 53 | }), this.extendList.map(function(extend) { 54 | return extend.eval(env); 55 | }), evaldCondition); 56 | }, 57 | genCSS: function (env, output) { 58 | var i, element; 59 | if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { 60 | output.add(' ', this.currentFileInfo, this.index); 61 | } 62 | if (!this._css) { 63 | //TODO caching? speed comparison? 64 | for(i = 0; i < this.elements.length; i++) { 65 | element = this.elements[i]; 66 | element.genCSS(env, output); 67 | } 68 | } 69 | }, 70 | toCSS: tree.toCSS, 71 | markReferenced: function () { 72 | this.isReferenced = true; 73 | }, 74 | getIsReferenced: function() { 75 | return !this.currentFileInfo.reference || this.isReferenced; 76 | }, 77 | getIsOutput: function() { 78 | return this.evaldCondition; 79 | } 80 | }; 81 | 82 | })(require('../tree')); 83 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/unicode-descriptor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.UnicodeDescriptor = function (value) { 4 | this.value = value; 5 | }; 6 | tree.UnicodeDescriptor.prototype = { 7 | type: "UnicodeDescriptor", 8 | genCSS: function (env, output) { 9 | output.add(this.value); 10 | }, 11 | toCSS: tree.toCSS, 12 | eval: function () { return this; } 13 | }; 14 | 15 | })(require('../tree')); 16 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/url.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.URL = function (val, currentFileInfo) { 4 | this.value = val; 5 | this.currentFileInfo = currentFileInfo; 6 | }; 7 | tree.URL.prototype = { 8 | type: "Url", 9 | accept: function (visitor) { 10 | this.value = visitor.visit(this.value); 11 | }, 12 | genCSS: function (env, output) { 13 | output.add("url("); 14 | this.value.genCSS(env, output); 15 | output.add(")"); 16 | }, 17 | toCSS: tree.toCSS, 18 | eval: function (ctx) { 19 | var val = this.value.eval(ctx), rootpath; 20 | 21 | // Add the base path if the URL is relative 22 | rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; 23 | if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { 24 | if (!val.quote) { 25 | rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); 26 | } 27 | val.value = rootpath + val.value; 28 | } 29 | 30 | val.value = ctx.normalizePath(val.value); 31 | 32 | return new(tree.URL)(val, null); 33 | } 34 | }; 35 | 36 | })(require('../tree')); 37 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/value.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Value = function (value) { 4 | this.value = value; 5 | }; 6 | tree.Value.prototype = { 7 | type: "Value", 8 | accept: function (visitor) { 9 | this.value = visitor.visit(this.value); 10 | }, 11 | eval: function (env) { 12 | if (this.value.length === 1) { 13 | return this.value[0].eval(env); 14 | } else { 15 | return new(tree.Value)(this.value.map(function (v) { 16 | return v.eval(env); 17 | })); 18 | } 19 | }, 20 | genCSS: function (env, output) { 21 | var i; 22 | for(i = 0; i < this.value.length; i++) { 23 | this.value[i].genCSS(env, output); 24 | if (i+1 < this.value.length) { 25 | output.add((env && env.compress) ? ',' : ', '); 26 | } 27 | } 28 | }, 29 | toCSS: tree.toCSS 30 | }; 31 | 32 | })(require('../tree')); 33 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/tree/variable.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.Variable = function (name, index, currentFileInfo) { 4 | this.name = name; 5 | this.index = index; 6 | this.currentFileInfo = currentFileInfo; 7 | }; 8 | tree.Variable.prototype = { 9 | type: "Variable", 10 | eval: function (env) { 11 | var variable, v, name = this.name; 12 | 13 | if (name.indexOf('@@') === 0) { 14 | name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; 15 | } 16 | 17 | if (this.evaluating) { 18 | throw { type: 'Name', 19 | message: "Recursive variable definition for " + name, 20 | filename: this.currentFileInfo.file, 21 | index: this.index }; 22 | } 23 | 24 | this.evaluating = true; 25 | 26 | if (variable = tree.find(env.frames, function (frame) { 27 | if (v = frame.variable(name)) { 28 | return v.value.eval(env); 29 | } 30 | })) { 31 | this.evaluating = false; 32 | return variable; 33 | } 34 | else { 35 | throw { type: 'Name', 36 | message: "variable " + name + " is undefined", 37 | filename: this.currentFileInfo.filename, 38 | index: this.index }; 39 | } 40 | } 41 | }; 42 | 43 | })(require('../tree')); 44 | -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/less/visitor.js: -------------------------------------------------------------------------------- 1 | (function (tree) { 2 | 3 | tree.visitor = function(implementation) { 4 | this._implementation = implementation; 5 | }; 6 | 7 | tree.visitor.prototype = { 8 | visit: function(node) { 9 | 10 | if (node instanceof Array) { 11 | return this.visitArray(node); 12 | } 13 | 14 | if (!node || !node.type) { 15 | return node; 16 | } 17 | 18 | var funcName = "visit" + node.type, 19 | func = this._implementation[funcName], 20 | visitArgs, newNode; 21 | if (func) { 22 | visitArgs = {visitDeeper: true}; 23 | newNode = func.call(this._implementation, node, visitArgs); 24 | if (this._implementation.isReplacing) { 25 | node = newNode; 26 | } 27 | } 28 | if ((!visitArgs || visitArgs.visitDeeper) && node && node.accept) { 29 | node.accept(this); 30 | } 31 | funcName = funcName + "Out"; 32 | if (this._implementation[funcName]) { 33 | this._implementation[funcName](node); 34 | } 35 | return node; 36 | }, 37 | visitArray: function(nodes) { 38 | var i, newNodes = []; 39 | for(i = 0; i < nodes.length; i++) { 40 | var evald = this.visit(nodes[i]); 41 | if (evald instanceof Array) { 42 | evald = this.flatten(evald); 43 | newNodes = newNodes.concat(evald); 44 | } else { 45 | newNodes.push(evald); 46 | } 47 | } 48 | if (this._implementation.isReplacing) { 49 | return newNodes; 50 | } 51 | return nodes; 52 | }, 53 | doAccept: function (node) { 54 | node.accept(this); 55 | }, 56 | flatten: function(arr, master) { 57 | return arr.reduce(this.flattenReduce.bind(this), master || []); 58 | }, 59 | flattenReduce: function(sum, element) { 60 | if (element instanceof Array) { 61 | sum = this.flatten(element, sum); 62 | } else { 63 | sum.push(element); 64 | } 65 | return sum; 66 | } 67 | }; 68 | 69 | })(require('./tree')); -------------------------------------------------------------------------------- /src/main/resources/org/lesscss/mojo/js/lessc.js: -------------------------------------------------------------------------------- 1 | var less = require('./less/index'); 2 | var fs = require('fs'); 3 | 4 | var inputFile = process.argv[2]; 5 | var outputFile = process.argv[3]; 6 | var compress = (process.argv[4] !== 'false'); 7 | 8 | var inputText = fs.readFileSync(inputFile, 'utf8'); 9 | var parser = new less.Parser(); 10 | parser.parse(inputText, function(e, tree) { 11 | if (e instanceof Object) { 12 | throw e; 13 | }; 14 | try { 15 | var result = tree.toCSS({compress: compress}); 16 | fs.writeFileSync(outputFile, result); 17 | } catch (e) { 18 | fs.writeFileSync(outputFile, e.message); 19 | process.exit(1); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /src/test/java/org/lesscss/mojo/AbstractLessCssMojoTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011-2012 The Apache Software Foundation. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.lesscss.mojo; 16 | 17 | import static org.mockito.Matchers.eq; 18 | import static org.mockito.Matchers.same; 19 | import static org.mockito.Mockito.verify; 20 | import static org.powermock.api.mockito.PowerMockito.when; 21 | 22 | import java.io.File; 23 | 24 | import org.apache.maven.plugin.MojoExecutionException; 25 | import org.apache.maven.plugin.testing.AbstractMojoTestCase; 26 | import org.codehaus.plexus.util.Scanner; 27 | import org.junit.After; 28 | import org.junit.Before; 29 | import org.junit.Test; 30 | import org.junit.runner.RunWith; 31 | import org.lesscss.mojo.AbstractLessCssMojo; 32 | import org.mockito.Mock; 33 | import org.powermock.core.classloader.annotations.PrepareForTest; 34 | import org.powermock.modules.junit4.PowerMockRunner; 35 | import org.sonatype.plexus.build.incremental.BuildContext; 36 | 37 | @PrepareForTest(AbstractLessCssMojo.class) 38 | @RunWith(PowerMockRunner.class) 39 | public class AbstractLessCssMojoTest extends AbstractMojoTestCase { 40 | 41 | private AbstractLessCssMojo mojo; 42 | 43 | private File sourceDirectory = new File("./source"); 44 | 45 | private String[] includes = new String[] { "include" }; 46 | 47 | private String[] excludes = new String[] { "exclude" }; 48 | 49 | private String[] files = new String[] { "file" }; 50 | 51 | @Mock 52 | private BuildContext buildContext; 53 | 54 | @Mock 55 | private Scanner scanner; 56 | 57 | @Before 58 | public void setUp() throws Exception { 59 | mojo = new AbstractLessCssMojo() { 60 | public void execute() throws MojoExecutionException { 61 | } 62 | }; 63 | 64 | setVariableValueToObject(mojo, "buildContext", buildContext); 65 | setVariableValueToObject(mojo, "sourceDirectory", sourceDirectory); 66 | setVariableValueToObject(mojo, "includes", includes); 67 | setVariableValueToObject(mojo, "excludes", excludes); 68 | } 69 | 70 | @Test 71 | public void testGetFiles() throws Exception { 72 | when(buildContext.newScanner(sourceDirectory, true)).thenReturn(scanner); 73 | when(scanner.getIncludedFiles()).thenReturn(files); 74 | 75 | assertSame(files, mojo.getIncludedFiles()); 76 | 77 | verify(buildContext).newScanner(same(sourceDirectory), eq(true)); 78 | verify(scanner).setIncludes(same(includes)); 79 | verify(scanner).setExcludes(same(excludes)); 80 | verify(scanner).scan(); 81 | } 82 | 83 | @After 84 | public void tearDown() { 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/org/lesscss/mojo/CompileOnChangeMojoTest.java: -------------------------------------------------------------------------------- 1 | package org.lesscss.mojo; 2 | 3 | import java.io.File; 4 | 5 | import org.apache.maven.plugin.MojoExecutionException; 6 | import org.apache.maven.plugin.testing.AbstractMojoTestCase; 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.rules.TemporaryFolder; 10 | 11 | /** 12 | * Tests the watch mode of the CompileMojo. 13 | */ 14 | public class CompileOnChangeMojoTest extends AbstractMojoTestCase { 15 | @Rule 16 | public TemporaryFolder tempFolder = new TemporaryFolder(); 17 | 18 | /** {@inheritDoc} */ 19 | protected void setUp() throws Exception { 20 | // required 21 | super.setUp(); 22 | } 23 | 24 | /** {@inheritDoc} */ 25 | protected void tearDown() throws Exception { 26 | // required 27 | super.tearDown(); 28 | } 29 | 30 | /** 31 | * @throws Exception 32 | * This test touches two files and checks if the bootstrap.css 33 | * file has been recompiled after every touch. 34 | */ 35 | @Test 36 | public void testIfFileChangeCausesRecompilation() throws Exception { 37 | File pom = getTestFile("src/test/resources/pom.xml"); 38 | assertNotNull(pom); 39 | assertTrue(pom.exists()); 40 | final CompileMojo compileMojo = (CompileMojo) lookupMojo("compile", pom); 41 | assertNotNull(compileMojo); 42 | 43 | File generatedFile = new File(compileMojo.outputDirectory, "bootstrap.css"); 44 | long oldLastModified = generatedFile.lastModified(); 45 | 46 | compileMojo.outputDirectory = tempFolder.newFolder(); 47 | 48 | /* 49 | * Run the watching compileMojo in a new thread. This way we can test 50 | * recompilation and stop the watching mode asynchronous in this thread. 51 | */ 52 | Thread watchThread = new Thread() { 53 | public void run() { 54 | try { 55 | compileMojo.execute(); 56 | } catch (MojoExecutionException e) { 57 | assertTrue(e.getLongMessage(), true); 58 | } 59 | } 60 | }; 61 | watchThread.start(); 62 | 63 | long newLastModified = checkIfFileHasBeenChanged(compileMojo, oldLastModified); 64 | 65 | touchFile(compileMojo, "1/reset.less"); 66 | newLastModified = checkIfFileHasBeenChanged(compileMojo, newLastModified); 67 | 68 | touchFile(compileMojo, "2/21/variables.less"); 69 | checkIfFileHasBeenChanged(compileMojo, newLastModified); 70 | watchThread.interrupt(); 71 | } 72 | 73 | private long checkIfFileHasBeenChanged(final CompileMojo compileMojo, long lastModified) throws InterruptedException { 74 | long newLastModified = lastModified; 75 | long oldLastModified = lastModified; 76 | 77 | File generatedFile = null; 78 | long startMillies = System.currentTimeMillis(); 79 | long currentMillies = startMillies; 80 | // check for a maximum of ten seconds if the file has been changed 81 | while (oldLastModified >= newLastModified && ((currentMillies - startMillies) < 10000)) { 82 | Thread.sleep(200); // wait for 0.2 seconds 83 | generatedFile = new File(compileMojo.outputDirectory, "bootstrap.css"); 84 | newLastModified = generatedFile.lastModified(); 85 | currentMillies = System.currentTimeMillis(); 86 | } 87 | assertTrue("No recompilation has been done within " + (currentMillies - startMillies) + " ms.", 88 | newLastModified > oldLastModified); 89 | return newLastModified; 90 | } 91 | 92 | private long touchFile(final CompileMojo compileMojo, String file2Touch) throws InterruptedException { 93 | File lessFile = new File(compileMojo.sourceDirectory, file2Touch); 94 | 95 | /* 96 | * The last modified value might be rounded down on a second by the 97 | * platform. This way the last compilation and the touch may end up with 98 | * the same millis avoiding the compilation. To prevent that the thread 99 | * has to sleep a second. 100 | */ 101 | Thread.sleep(1000); 102 | long currentMillies = System.currentTimeMillis(); 103 | // touch the less file 104 | lessFile.setLastModified(currentMillies); 105 | return currentMillies; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/test/java/org/lesscss/mojo/ListMojoTest.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2011-2012 The Apache Software Foundation. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.lesscss.mojo; 16 | 17 | import static org.mockito.Mockito.inOrder; 18 | import static org.mockito.Mockito.verify; 19 | import static org.powermock.api.mockito.PowerMockito.when; 20 | import static org.powermock.api.mockito.PowerMockito.whenNew; 21 | 22 | import java.io.File; 23 | import java.io.FileNotFoundException; 24 | import java.io.IOException; 25 | import java.util.LinkedHashMap; 26 | 27 | import org.apache.maven.plugin.MojoExecutionException; 28 | import org.apache.maven.plugin.logging.Log; 29 | import org.apache.maven.plugin.testing.AbstractMojoTestCase; 30 | import org.codehaus.plexus.util.Scanner; 31 | import org.junit.After; 32 | import org.junit.Before; 33 | import org.junit.Test; 34 | import org.junit.runner.RunWith; 35 | import org.lesscss.LessSource; 36 | import org.lesscss.mojo.ListMojo; 37 | import org.mockito.InOrder; 38 | import org.mockito.Mock; 39 | import org.powermock.core.classloader.annotations.PrepareForTest; 40 | import org.powermock.modules.junit4.PowerMockRunner; 41 | import org.sonatype.plexus.build.incremental.BuildContext; 42 | 43 | @PrepareForTest(ListMojo.class) 44 | @RunWith(PowerMockRunner.class) 45 | public class ListMojoTest extends AbstractMojoTestCase { 46 | 47 | private ListMojo mojo; 48 | 49 | private File sourceDirectory = new File("./source"); 50 | 51 | private String[] includes = new String[] { "include" }; 52 | 53 | private String[] excludes = new String[] { "exclude" }; 54 | 55 | private String[] files = new String[] { "file" }; 56 | 57 | @Mock 58 | private Log log; 59 | 60 | @Mock 61 | private LessSource lessSource1; 62 | 63 | @Mock 64 | private LessSource lessSource1import1; 65 | 66 | @Mock 67 | private LessSource lessSource1import1a; 68 | 69 | @Mock 70 | private LessSource lessSource1import2; 71 | 72 | @Mock 73 | private LessSource lessSource1import2a; 74 | 75 | @Mock 76 | private LessSource lessSource1import2b; 77 | 78 | @Mock 79 | private LessSource lessSource1import3; 80 | 81 | @Mock 82 | private LessSource lessSource2; 83 | 84 | @Mock 85 | private BuildContext buildContext; 86 | 87 | @Mock 88 | private Scanner scanner; 89 | 90 | @Before 91 | public void setUp() throws Exception { 92 | mojo = new ListMojo(); 93 | mojo.setLog(log); 94 | 95 | setVariableValueToObject(mojo, "buildContext", buildContext); 96 | setVariableValueToObject(mojo, "sourceDirectory", sourceDirectory); 97 | setVariableValueToObject(mojo, "includes", includes); 98 | setVariableValueToObject(mojo, "excludes", excludes); 99 | } 100 | 101 | @SuppressWarnings("serial") 102 | @Test 103 | public void testExecution() throws Exception { 104 | files = new String[] { "less1.less", "less2.less" }; 105 | 106 | when(buildContext.newScanner(sourceDirectory, true)).thenReturn(scanner); 107 | when(scanner.getIncludedFiles()).thenReturn(files); 108 | 109 | whenNew(LessSource.class).withArguments(new File(sourceDirectory, "less1.less")).thenReturn(lessSource1); 110 | when(lessSource1.getImports()).thenReturn(new LinkedHashMap() { 111 | { 112 | put("less1import1.less", lessSource1import1); 113 | put("less1import2.less", lessSource1import2); 114 | put("less1import3.less", lessSource1import3); 115 | } 116 | }); 117 | when(lessSource1import1.getImports()).thenReturn(new LinkedHashMap() { 118 | { 119 | put("less1import1a.less", lessSource1import1a); 120 | } 121 | }); 122 | when(lessSource1import2.getImports()).thenReturn(new LinkedHashMap() { 123 | { 124 | put("less1import2a.less", lessSource1import2a); 125 | put("less1import2b.less", lessSource1import2b); 126 | } 127 | }); 128 | 129 | whenNew(LessSource.class).withArguments(new File(sourceDirectory, "less2.less")).thenReturn(lessSource2); 130 | 131 | mojo.execute(); 132 | 133 | InOrder inOrder = inOrder(log); 134 | inOrder.verify(log).info("The following LESS sources have been resolved:"); 135 | inOrder.verify(log).info("less1.less"); 136 | inOrder.verify(log).info("|-- less1import1.less"); 137 | inOrder.verify(log).info("| `-- less1import1a.less"); 138 | inOrder.verify(log).info("|-- less1import2.less"); 139 | inOrder.verify(log).info("| |-- less1import2a.less"); 140 | inOrder.verify(log).info("| `-- less1import2b.less"); 141 | inOrder.verify(log).info("`-- less1import3.less"); 142 | inOrder.verify(log).info("less2.less"); 143 | inOrder.verifyNoMoreInteractions(); 144 | } 145 | 146 | @Test 147 | public void testExecutionIncludedFilesEmpty() throws Exception { 148 | files = new String[] {}; 149 | 150 | when(buildContext.newScanner(sourceDirectory, true)).thenReturn(scanner); 151 | when(scanner.getIncludedFiles()).thenReturn(files); 152 | 153 | mojo.execute(); 154 | 155 | verify(log).info("No LESS sources found"); 156 | } 157 | 158 | @Test 159 | public void testExecutionIncludedFilesNull() throws Exception { 160 | files = null; 161 | 162 | when(buildContext.newScanner(sourceDirectory, true)).thenReturn(scanner); 163 | when(scanner.getIncludedFiles()).thenReturn(files); 164 | 165 | mojo.execute(); 166 | 167 | verify(log).info("No LESS sources found"); 168 | } 169 | 170 | @Test(expected = MojoExecutionException.class) 171 | public void testExecutionFileNotFoundException() throws Exception { 172 | files = new String[] { "less.less" }; 173 | 174 | when(buildContext.newScanner(sourceDirectory, true)).thenReturn(scanner); 175 | when(scanner.getIncludedFiles()).thenReturn(files); 176 | 177 | whenNew(LessSource.class).withArguments(new File(sourceDirectory, "less.less")).thenThrow(new FileNotFoundException("")); 178 | 179 | mojo.execute(); 180 | } 181 | 182 | @Test(expected = MojoExecutionException.class) 183 | public void testExecutionIOException() throws Exception { 184 | files = new String[] { "less.less" }; 185 | 186 | when(buildContext.newScanner(sourceDirectory, true)).thenReturn(scanner); 187 | when(scanner.getIncludedFiles()).thenReturn(files); 188 | 189 | whenNew(LessSource.class).withArguments(new File(sourceDirectory, "less.less")).thenThrow(new IOException("")); 190 | 191 | mojo.execute(); 192 | } 193 | 194 | @After 195 | public void tearDown() { 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/test/resources/less/1/reset.less: -------------------------------------------------------------------------------- 1 | // 2 | // Modals 3 | // Adapted from http://github.com/necolas/normalize.css 4 | // -------------------------------------------------- 5 | 6 | 7 | // Display in IE6-9 and FF3 8 | // ------------------------- 9 | 10 | article, 11 | aside, 12 | details, 13 | figcaption, 14 | figure, 15 | footer, 16 | header, 17 | hgroup, 18 | nav, 19 | section { 20 | display: block; 21 | } 22 | 23 | // Display block in IE6-9 and FF3 24 | // ------------------------- 25 | 26 | audio, 27 | canvas, 28 | video { 29 | display: inline-block; 30 | *display: inline; 31 | *zoom: 1; 32 | } 33 | 34 | // Prevents modern browsers from displaying 'audio' without controls 35 | // ------------------------- 36 | 37 | audio:not([controls]) { 38 | display: none; 39 | } 40 | 41 | // Base settings 42 | // ------------------------- 43 | 44 | html { 45 | font-size: 100%; 46 | -webkit-text-size-adjust: 100%; 47 | -ms-text-size-adjust: 100%; 48 | } 49 | 50 | // Hover & Active 51 | a:hover, 52 | a:active { 53 | outline: 0; 54 | } 55 | 56 | // Prevents sub and sup affecting line-height in all browsers 57 | // ------------------------- 58 | 59 | sub, 60 | sup { 61 | position: relative; 62 | font-size: 75%; 63 | line-height: 0; 64 | vertical-align: baseline; 65 | } 66 | sup { 67 | top: -0.5em; 68 | } 69 | sub { 70 | bottom: -0.25em; 71 | } 72 | 73 | // Img border in a's and image quality 74 | // ------------------------- 75 | 76 | img { 77 | /* Responsive images (ensure images don't scale beyond their parents) */ 78 | max-width: 100%; /* Part 1: Set a maxium relative to the parent */ 79 | width: auto\9; /* IE7-8 need help adjusting responsive images */ 80 | height: auto; /* Part 2: Scale the height according to the width, otherwise you get stretching */ 81 | 82 | vertical-align: middle; 83 | border: 0; 84 | -ms-interpolation-mode: bicubic; 85 | } 86 | 87 | // Prevent max-width from affecting Google Maps 88 | #map_canvas img { 89 | max-width: none; 90 | } 91 | 92 | // Forms 93 | // ------------------------- 94 | 95 | // Font size in all browsers, margin changes, misc consistency 96 | button, 97 | input, 98 | select, 99 | textarea { 100 | margin: 0; 101 | font-size: 100%; 102 | vertical-align: middle; 103 | } 104 | button, 105 | input { 106 | *overflow: visible; // Inner spacing ie IE6/7 107 | line-height: normal; // FF3/4 have !important on line-height in UA stylesheet 108 | } 109 | button::-moz-focus-inner, 110 | input::-moz-focus-inner { // Inner padding and border oddities in FF3/4 111 | padding: 0; 112 | border: 0; 113 | } 114 | button, 115 | input[type="button"], 116 | input[type="reset"], 117 | input[type="submit"] { 118 | cursor: pointer; // Cursors on all buttons applied consistently 119 | -webkit-appearance: button; // Style clickable inputs in iOS 120 | } 121 | input[type="search"] { // Appearance in Safari/Chrome 122 | -webkit-box-sizing: content-box; 123 | -moz-box-sizing: content-box; 124 | box-sizing: content-box; 125 | -webkit-appearance: textfield; 126 | } 127 | input[type="search"]::-webkit-search-decoration, 128 | input[type="search"]::-webkit-search-cancel-button { 129 | -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5 130 | } 131 | textarea { 132 | overflow: auto; // Remove vertical scrollbar in IE6-9 133 | vertical-align: top; // Readability and alignment cross-browser 134 | } 135 | -------------------------------------------------------------------------------- /src/test/resources/less/2/21/variables.less: -------------------------------------------------------------------------------- 1 | // Variables 2 | // -------------------------------------------------- 3 | 4 | 5 | // Global values 6 | // -------------------------------------------------- 7 | 8 | 9 | // Grays 10 | // ------------------------- 11 | @black: #000; 12 | @grayDarker: #222; 13 | @grayDark: #333; 14 | @gray: #555; 15 | @grayLight: #999; 16 | @grayLighter: #eee; 17 | @white: #fff; 18 | 19 | 20 | // Accent colors 21 | // ------------------------- 22 | @blue: #049cdb; 23 | @blueDark: #0064cd; 24 | @green: #46a546; 25 | @red: #9d261d; 26 | @yellow: #ffc40d; 27 | @orange: #f89406; 28 | @pink: #c3325f; 29 | @purple: #7a43b6; 30 | 31 | 32 | // Scaffolding 33 | // ------------------------- 34 | @bodyBackground: yellow; 35 | @textColor: yellow; 36 | //#c0c0c0; 37 | 38 | 39 | // Links 40 | // ------------------------- 41 | @linkColor: #c0c0c0; 42 | @linkColorHover: darken(@linkColor, 15%); 43 | 44 | 45 | // Typography 46 | // ------------------------- 47 | @sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; 48 | @serifFontFamily: Georgia, "Times New Roman", Times, serif; 49 | @monoFontFamily: Monaco, Menlo, Consolas, "Courier New", monospace; 50 | 51 | @baseFontSize: 14px; 52 | @baseFontFamily: @sansFontFamily; 53 | @baseLineHeight: 20px; 54 | @altFontFamily: @serifFontFamily; 55 | 56 | @headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily 57 | @headingsFontWeight: bold; // instead of browser default, bold 58 | @headingsColor: inherit; // empty to use BS default, @textColor 59 | 60 | 61 | // Tables 62 | // ------------------------- 63 | @tableBackground: transparent; // overall background-color 64 | @tableBackgroundAccent: #f9f9f9; // for striping 65 | @tableBackgroundHover: #f5f5f5; // for hover 66 | @tableBorder: #ddd; // table and cell border 67 | 68 | 69 | // Buttons 70 | // ------------------------- 71 | @btnBackground: @white; 72 | @btnBackgroundHighlight: darken(@white, 10%); 73 | @btnBorder: #bbb; 74 | 75 | @btnPrimaryBackground: @linkColor; 76 | @btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 20%); 77 | 78 | @btnInfoBackground: #5bc0de; 79 | @btnInfoBackgroundHighlight: #2f96b4; 80 | 81 | @btnSuccessBackground: #62c462; 82 | @btnSuccessBackgroundHighlight: #51a351; 83 | 84 | @btnWarningBackground: lighten(@orange, 15%); 85 | @btnWarningBackgroundHighlight: @orange; 86 | 87 | @btnDangerBackground: #ee5f5b; 88 | @btnDangerBackgroundHighlight: #bd362f; 89 | 90 | @btnInverseBackground: #444; 91 | @btnInverseBackgroundHighlight: @grayDarker; 92 | 93 | 94 | // Forms 95 | // ------------------------- 96 | @inputBackground: @white; 97 | @inputBorder: #ccc; 98 | @inputBorderRadius: 3px; 99 | @inputDisabledBackground: @grayLighter; 100 | @formActionsBackground: #f5f5f5; 101 | 102 | // Dropdowns 103 | // ------------------------- 104 | @dropdownBackground: @white; 105 | @dropdownBorder: rgba(0,0,0,.2); 106 | @dropdownDividerTop: #e5e5e5; 107 | @dropdownDividerBottom: @white; 108 | 109 | @dropdownLinkColor: @grayDark; 110 | 111 | @dropdownLinkColorHover: @white; 112 | @dropdownLinkBackgroundHover: @dropdownLinkBackgroundActive; 113 | 114 | @dropdownLinkColorActive: @dropdownLinkColor; 115 | @dropdownLinkBackgroundActive: @linkColor; 116 | 117 | 118 | 119 | // COMPONENT VARIABLES 120 | // -------------------------------------------------- 121 | 122 | // Z-index master list 123 | // ------------------------- 124 | // Used for a bird's eye view of components dependent on the z-axis 125 | // Try to avoid customizing these :) 126 | @zindexDropdown: 1000; 127 | @zindexPopover: 1010; 128 | @zindexTooltip: 1030; 129 | @zindexFixedNavbar: 1030; 130 | @zindexModalBackdrop: 1040; 131 | @zindexModal: 1050; 132 | 133 | 134 | // Sprite icons path 135 | // ------------------------- 136 | @iconSpritePath: "../img/glyphicons-halflings.png"; 137 | @iconWhiteSpritePath: "../img/glyphicons-halflings-white.png"; 138 | 139 | 140 | // Input placeholder text color 141 | // ------------------------- 142 | @placeholderText: @grayLight; 143 | 144 | 145 | // Hr border color 146 | // ------------------------- 147 | @hrBorder: @grayLighter; 148 | 149 | 150 | // Horizontal forms & lists 151 | // ------------------------- 152 | @horizontalComponentOffset: 180px; 153 | 154 | 155 | // Wells 156 | // ------------------------- 157 | @wellBackground: #f5f5f5; 158 | 159 | 160 | // Navbar 161 | // ------------------------- 162 | @navbarCollapseWidth: 979px; 163 | 164 | @navbarHeight: 40px; 165 | @navbarBackground: darken(@navbarBackgroundHighlight, 5%); 166 | @navbarBackgroundHighlight: #ffffff; 167 | @navbarBorder: darken(@navbarBackground, 12%); 168 | 169 | @navbarText: @gray; 170 | @navbarLinkColor: @gray; 171 | @navbarLinkColorHover: @grayDark; 172 | @navbarLinkColorActive: @gray; 173 | @navbarLinkBackgroundHover: transparent; 174 | @navbarLinkBackgroundActive: darken(@navbarBackground, 5%); 175 | 176 | @navbarBrandColor: @navbarLinkColor; 177 | 178 | // Inverted navbar 179 | @navbarInverseBackground: #111111; 180 | @navbarInverseBackgroundHighlight: #222222; 181 | @navbarInverseBorder: #252525; 182 | 183 | @navbarInverseText: @grayLight; 184 | @navbarInverseLinkColor: @grayLight; 185 | @navbarInverseLinkColorHover: @white; 186 | @navbarInverseLinkColorActive: @navbarInverseLinkColorHover; 187 | @navbarInverseLinkBackgroundHover: transparent; 188 | @navbarInverseLinkBackgroundActive: @navbarInverseBackground; 189 | 190 | @navbarInverseSearchBackground: lighten(@navbarInverseBackground, 25%); 191 | @navbarInverseSearchBackgroundFocus: @white; 192 | @navbarInverseSearchBorder: @navbarInverseBackground; 193 | @navbarInverseSearchPlaceholderColor: #ccc; 194 | 195 | @navbarInverseBrandColor: @navbarInverseLinkColor; 196 | 197 | 198 | // Pagination 199 | // ------------------------- 200 | @paginationBackground: #fff; 201 | @paginationBorder: #ddd; 202 | @paginationActiveBackground: #f5f5f5; 203 | 204 | 205 | // Hero unit 206 | // ------------------------- 207 | @heroUnitBackground: @grayLighter; 208 | @heroUnitHeadingColor: inherit; 209 | @heroUnitLeadColor: inherit; 210 | 211 | 212 | // Form states and alerts 213 | // ------------------------- 214 | @warningText: #c09853; 215 | @warningBackground: #fcf8e3; 216 | @warningBorder: darken(spin(@warningBackground, -10), 3%); 217 | 218 | @errorText: #b94a48; 219 | @errorBackground: #f2dede; 220 | @errorBorder: darken(spin(@errorBackground, -10), 3%); 221 | 222 | @successText: #468847; 223 | @successBackground: #dff0d8; 224 | @successBorder: darken(spin(@successBackground, -10), 5%); 225 | 226 | @infoText: #3a87ad; 227 | @infoBackground: #d9edf7; 228 | @infoBorder: darken(spin(@infoBackground, -10), 7%); 229 | 230 | 231 | // Tooltips and popovers 232 | // ------------------------- 233 | @tooltipColor: #fff; 234 | @tooltipBackground: #000; 235 | @tooltipArrowWidth: 5px; 236 | @tooltipArrowColor: @tooltipBackground; 237 | 238 | @popoverBackground: #fff; 239 | @popoverArrowWidth: 10px; 240 | @popoverArrowColor: #fff; 241 | @popoverTitleBackground: darken(@popoverBackground, 3%); 242 | 243 | // Special enhancement for popovers 244 | @popoverArrowOuterWidth: @popoverArrowWidth + 1; 245 | @popoverArrowOuterColor: rgba(0,0,0,.25); 246 | 247 | 248 | 249 | // GRID 250 | // -------------------------------------------------- 251 | 252 | 253 | // Default 940px grid 254 | // ------------------------- 255 | @gridColumns: 12; 256 | @gridColumnWidth: 60px; 257 | @gridGutterWidth: 20px; 258 | @gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1)); 259 | 260 | // 1200px min 261 | @gridColumnWidth1200: 70px; 262 | @gridGutterWidth1200: 30px; 263 | @gridRowWidth1200: (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1)); 264 | 265 | // 768px-979px 266 | @gridColumnWidth768: 42px; 267 | @gridGutterWidth768: 20px; 268 | @gridRowWidth768: (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1)); 269 | 270 | 271 | // Fluid grid 272 | // ------------------------- 273 | @fluidGridColumnWidth: percentage(@gridColumnWidth/@gridRowWidth); 274 | @fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth); 275 | 276 | // 1200px min 277 | @fluidGridColumnWidth1200: percentage(@gridColumnWidth1200/@gridRowWidth1200); 278 | @fluidGridGutterWidth1200: percentage(@gridGutterWidth1200/@gridRowWidth1200); 279 | 280 | // 768px-979px 281 | @fluidGridColumnWidth768: percentage(@gridColumnWidth768/@gridRowWidth768); 282 | @fluidGridGutterWidth768: percentage(@gridGutterWidth768/@gridRowWidth768); 283 | -------------------------------------------------------------------------------- /src/test/resources/less/bootstrap.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v2.1.1 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | /* 12 | Attention: 13 | Any changes of this file will be lost on the next compile run of the .less files! 14 | */ 15 | 16 | // CSS Reset 17 | @import "1/reset.less"; 18 | 19 | // Core variables and mixins 20 | @import "2/21/variables.less"; // Modify this for custom colors, font-sizes, etc 21 | -------------------------------------------------------------------------------- /src/test/resources/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.lesscss.it 5 | includes 6 | testing 7 | 8 | 9 | 10 | org.lesscss 11 | lesscss-maven-plugin 12 | @project.version@ 13 | 14 | src/test/resources/less 15 | true 16 | 17 | bootstrap.less 18 | 19 | 20 | 21 | 22 | 23 | compile 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | --------------------------------------------------------------------------------