less 2.2.2 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (441) hide show
  1. checksums.yaml +7 -0
  2. data/Changelog.md +17 -0
  3. data/Gemfile +7 -3
  4. data/README.md +29 -13
  5. data/bin/lessc +3 -1
  6. data/less.gemspec +2 -6
  7. data/lib/less/defaults.rb +3 -3
  8. data/lib/less/java_script/rhino_context.rb +17 -27
  9. data/lib/less/java_script/v8_context.rb +11 -11
  10. data/lib/less/js/.gitattributes +10 -0
  11. data/lib/less/js/.gitignore +24 -0
  12. data/lib/less/js/.jshintrc +11 -0
  13. data/lib/less/js/.npmignore +1 -0
  14. data/lib/less/js/.travis.yml +8 -0
  15. data/lib/less/js/CHANGELOG.md +299 -0
  16. data/lib/less/js/CONTRIBUTING.md +50 -0
  17. data/lib/less/js/Gruntfile.js +298 -0
  18. data/lib/less/js/README.md +43 -12
  19. data/lib/less/js/benchmark/benchmark.less +194 -194
  20. data/lib/less/js/benchmark/less-benchmark.js +9 -10
  21. data/lib/less/js/bin/lessc +310 -37
  22. data/lib/less/js/bower.json +18 -0
  23. data/lib/less/js/build/README.md +51 -0
  24. data/lib/less/js/build/amd.js +1 -1
  25. data/lib/less/js/build/browser-header.js +4 -0
  26. data/lib/less/js/build/build.yml +162 -0
  27. data/lib/less/js/build/require-rhino.js +7 -2
  28. data/lib/less/js/build/rhino-header.js +4 -0
  29. data/lib/less/js/build/rhino-modules.js +131 -0
  30. data/lib/less/js/build/tasks/.gitkeep +1 -0
  31. data/lib/less/js/build.gradle +347 -0
  32. data/lib/less/js/dist/less-1.3.1.js +4011 -0
  33. data/lib/less/js/dist/less-1.3.1.min.js +9 -0
  34. data/lib/less/js/dist/less-1.3.2.js +4401 -0
  35. data/lib/less/js/dist/less-1.3.2.min.js +9 -0
  36. data/lib/less/js/dist/less-1.3.3.js +4413 -0
  37. data/lib/less/js/dist/less-1.3.3.min.js +9 -0
  38. data/lib/less/js/dist/less-1.4.0-beta.js +5830 -0
  39. data/lib/less/js/dist/less-1.4.0-beta.min.js +11 -0
  40. data/lib/less/js/dist/less-1.4.0.js +5830 -0
  41. data/lib/less/js/dist/less-1.4.0.min.js +11 -0
  42. data/lib/less/js/dist/less-1.4.1.js +5837 -0
  43. data/lib/less/js/dist/less-1.4.1.min.js +11 -0
  44. data/lib/less/js/dist/less-1.4.2.js +5837 -0
  45. data/lib/less/js/dist/less-1.4.2.min.js +11 -0
  46. data/lib/less/js/dist/less-1.5.0.js +6914 -0
  47. data/lib/less/js/dist/less-1.5.0.min.js +13 -0
  48. data/lib/less/js/dist/less-1.5.1.js +6941 -0
  49. data/lib/less/js/dist/less-1.5.1.min.js +13 -0
  50. data/lib/less/js/dist/less-1.6.0.js +7485 -0
  51. data/lib/less/js/dist/less-1.6.0.min.js +16 -0
  52. data/lib/less/js/dist/less-1.6.1.js +7513 -0
  53. data/lib/less/js/dist/less-1.6.1.min.js +16 -0
  54. data/lib/less/js/dist/less-1.6.2.js +7624 -0
  55. data/lib/less/js/dist/less-1.6.2.min.js +16 -0
  56. data/lib/less/js/dist/less-1.6.3.js +7627 -0
  57. data/lib/less/js/dist/less-1.6.3.min.js +16 -0
  58. data/lib/less/js/dist/less-1.7.0.js +7921 -0
  59. data/lib/less/js/dist/less-1.7.0.min.js +16 -0
  60. data/lib/less/js/dist/less-rhino-1.3.1.js +3725 -0
  61. data/lib/less/js/dist/less-rhino-1.3.2.js +3990 -0
  62. data/lib/less/js/dist/less-rhino-1.3.3.js +4002 -0
  63. data/lib/less/js/dist/less-rhino-1.4.0.js +4273 -0
  64. data/lib/less/js/dist/less-rhino-1.5.1.js +6831 -0
  65. data/lib/less/js/dist/less-rhino-1.6.2.js +9017 -0
  66. data/lib/less/js/dist/less-rhino-1.6.3.js +9020 -0
  67. data/lib/less/js/dist/less-rhino-1.7.0.js +9301 -0
  68. data/lib/less/js/dist/lessc-rhino-1.6.2.js +449 -0
  69. data/lib/less/js/dist/lessc-rhino-1.6.3.js +449 -0
  70. data/lib/less/js/dist/lessc-rhino-1.7.0.js +449 -0
  71. data/lib/less/js/gradle/wrapper/gradle-wrapper.jar +0 -0
  72. data/lib/less/js/gradle/wrapper/gradle-wrapper.properties +6 -0
  73. data/lib/less/js/gradlew +164 -0
  74. data/lib/less/js/gradlew.bat +90 -0
  75. data/lib/less/js/lib/less/browser.js +547 -250
  76. data/lib/less/js/lib/less/encoder.js +4 -0
  77. data/lib/less/js/lib/less/env.js +136 -0
  78. data/lib/less/js/lib/less/extend-visitor.js +416 -0
  79. data/lib/less/js/lib/less/functions.js +576 -36
  80. data/lib/less/js/lib/less/import-visitor.js +142 -0
  81. data/lib/less/js/lib/less/index.js +187 -83
  82. data/lib/less/js/lib/less/join-selector-visitor.js +44 -0
  83. data/lib/less/js/lib/less/lessc_helper.js +82 -0
  84. data/lib/less/js/lib/less/parser.js +1286 -523
  85. data/lib/less/js/lib/less/rhino.js +415 -29
  86. data/lib/less/js/lib/less/source-map-output.js +141 -0
  87. data/lib/less/js/lib/less/to-css-visitor.js +240 -0
  88. data/lib/less/js/lib/less/tree/alpha.js +17 -5
  89. data/lib/less/js/lib/less/tree/anonymous.js +25 -4
  90. data/lib/less/js/lib/less/tree/assignment.js +17 -5
  91. data/lib/less/js/lib/less/tree/call.js +36 -13
  92. data/lib/less/js/lib/less/tree/color.js +119 -31
  93. data/lib/less/js/lib/less/tree/comment.js +18 -4
  94. data/lib/less/js/lib/less/tree/condition.js +35 -28
  95. data/lib/less/js/lib/less/tree/detached-ruleset.js +20 -0
  96. data/lib/less/js/lib/less/tree/dimension.js +291 -16
  97. data/lib/less/js/lib/less/tree/directive.js +56 -22
  98. data/lib/less/js/lib/less/tree/element.js +78 -17
  99. data/lib/less/js/lib/less/tree/expression.js +39 -8
  100. data/lib/less/js/lib/less/tree/extend.js +53 -0
  101. data/lib/less/js/lib/less/tree/import.js +81 -36
  102. data/lib/less/js/lib/less/tree/javascript.js +17 -10
  103. data/lib/less/js/lib/less/tree/keyword.js +8 -3
  104. data/lib/less/js/lib/less/tree/media.js +70 -27
  105. data/lib/less/js/lib/less/tree/mixin.js +250 -70
  106. data/lib/less/js/lib/less/tree/negative.js +24 -0
  107. data/lib/less/js/lib/less/tree/operation.js +38 -13
  108. data/lib/less/js/lib/less/tree/paren.js +9 -2
  109. data/lib/less/js/lib/less/tree/quoted.js +29 -10
  110. data/lib/less/js/lib/less/tree/rule.js +77 -29
  111. data/lib/less/js/lib/less/tree/ruleset-call.js +16 -0
  112. data/lib/less/js/lib/less/tree/ruleset.js +445 -107
  113. data/lib/less/js/lib/less/tree/selector.js +117 -30
  114. data/lib/less/js/lib/less/tree/unicode-descriptor.js +15 -0
  115. data/lib/less/js/lib/less/tree/url.js +43 -15
  116. data/lib/less/js/lib/less/tree/value.js +16 -6
  117. data/lib/less/js/lib/less/tree/variable.js +26 -8
  118. data/lib/less/js/lib/less/tree.js +82 -2
  119. data/lib/less/js/lib/less/visitor.js +146 -0
  120. data/lib/less/js/lib/source-map/source-map-0.1.31.js +1933 -0
  121. data/lib/less/js/lib/source-map/source-map-footer.js +4 -0
  122. data/lib/less/js/lib/source-map/source-map-header.js +3 -0
  123. data/lib/less/js/package.json +86 -11
  124. data/lib/less/js/test/browser/common.js +201 -0
  125. data/lib/less/js/test/browser/css/global-vars/simple.css +3 -0
  126. data/lib/less/js/test/browser/css/modify-vars/simple.css +8 -0
  127. data/lib/less/js/test/browser/css/postProcessor/postProcessor.css +4 -0
  128. data/lib/less/js/test/browser/css/relative-urls/urls.css +35 -0
  129. data/lib/less/js/test/browser/css/rootpath/urls.css +35 -0
  130. data/lib/less/js/test/browser/css/rootpath-relative/urls.css +35 -0
  131. data/lib/less/js/test/browser/css/urls.css +53 -0
  132. data/lib/less/js/test/browser/es5.js +27 -0
  133. data/lib/less/js/test/browser/jasmine-html.js +681 -0
  134. data/lib/less/js/test/browser/jasmine.css +82 -0
  135. data/lib/less/js/test/browser/jasmine.js +2600 -0
  136. data/lib/less/js/test/browser/less/console-errors/test-error.less +3 -0
  137. data/lib/less/js/test/browser/less/console-errors/test-error.txt +2 -0
  138. data/lib/less/js/test/browser/less/global-vars/simple.less +3 -0
  139. data/lib/less/js/test/browser/less/imports/urls.less +4 -0
  140. data/lib/less/js/test/browser/less/imports/urls2.less +4 -0
  141. data/lib/less/js/test/browser/less/modify-vars/imports/simple2.less +4 -0
  142. data/lib/less/js/test/browser/less/modify-vars/simple.less +8 -0
  143. data/lib/less/js/test/browser/less/postProcessor/postProcessor.less +4 -0
  144. data/lib/less/js/test/browser/less/relative-urls/urls.less +33 -0
  145. data/lib/less/js/test/browser/less/rootpath/urls.less +33 -0
  146. data/lib/less/js/test/browser/less/rootpath-relative/urls.less +33 -0
  147. data/lib/less/js/test/browser/less/urls.less +57 -0
  148. data/lib/less/js/test/browser/phantom-runner.js +150 -0
  149. data/lib/less/js/test/browser/runner-browser-options.js +42 -0
  150. data/lib/less/js/test/browser/runner-browser-spec.js +12 -0
  151. data/lib/less/js/test/browser/runner-console-errors.js +5 -0
  152. data/lib/less/js/test/browser/runner-errors-options.js +5 -0
  153. data/lib/less/js/test/browser/runner-errors-spec.js +4 -0
  154. data/lib/less/js/test/browser/runner-global-vars-options.js +4 -0
  155. data/lib/less/js/test/browser/runner-global-vars-spec.js +3 -0
  156. data/lib/less/js/test/browser/runner-legacy-options.js +4 -0
  157. data/lib/less/js/test/browser/runner-legacy-spec.js +3 -0
  158. data/lib/less/js/test/browser/runner-main-options.js +15 -0
  159. data/lib/less/js/test/browser/runner-main-spec.js +3 -0
  160. data/lib/less/js/test/browser/runner-modify-vars-options.js +2 -0
  161. data/lib/less/js/test/browser/runner-modify-vars-spec.js +43 -0
  162. data/lib/less/js/test/browser/runner-no-js-errors-options.js +4 -0
  163. data/lib/less/js/test/browser/runner-no-js-errors-spec.js +4 -0
  164. data/lib/less/js/test/browser/runner-postProcessor-options.js +4 -0
  165. data/lib/less/js/test/browser/runner-postProcessor.js +3 -0
  166. data/lib/less/js/test/browser/runner-production-options.js +3 -0
  167. data/lib/less/js/test/browser/runner-production-spec.js +5 -0
  168. data/lib/less/js/test/browser/runner-relative-urls-options.js +3 -0
  169. data/lib/less/js/test/browser/runner-relative-urls-spec.js +3 -0
  170. data/lib/less/js/test/browser/runner-rootpath-options.js +3 -0
  171. data/lib/less/js/test/browser/runner-rootpath-relative-options.js +4 -0
  172. data/lib/less/js/test/browser/runner-rootpath-relative-spec.js +3 -0
  173. data/lib/less/js/test/browser/runner-rootpath-spec.js +3 -0
  174. data/lib/less/js/test/browser/test-runner-template.tmpl +47 -0
  175. data/lib/less/js/test/css/charsets.css +1 -0
  176. data/lib/less/js/test/css/colors.css +29 -0
  177. data/lib/less/js/test/css/comments.css +15 -2
  178. data/lib/less/js/test/css/compression/compression.css +3 -0
  179. data/lib/less/js/test/css/css-3.css +73 -2
  180. data/lib/less/js/test/css/css-escapes.css +4 -0
  181. data/lib/less/js/test/css/css-guards.css +37 -0
  182. data/lib/less/js/test/css/css.css +20 -14
  183. data/lib/less/js/test/css/debug/linenumbers-all.css +49 -0
  184. data/lib/less/js/test/css/debug/linenumbers-comments.css +40 -0
  185. data/lib/less/js/test/css/debug/linenumbers-mediaquery.css +40 -0
  186. data/lib/less/js/test/css/detached-rulesets.css +71 -0
  187. data/lib/less/js/test/css/empty.css +0 -0
  188. data/lib/less/js/test/css/extend-chaining.css +81 -0
  189. data/lib/less/js/test/css/extend-clearfix.css +19 -0
  190. data/lib/less/js/test/css/extend-exact.css +37 -0
  191. data/lib/less/js/test/css/extend-media.css +24 -0
  192. data/lib/less/js/test/css/extend-nest.css +57 -0
  193. data/lib/less/js/test/css/extend-selector.css +80 -0
  194. data/lib/less/js/test/css/extend.css +76 -0
  195. data/lib/less/js/test/css/extract-and-length.css +133 -0
  196. data/lib/less/js/test/css/functions.css +117 -2
  197. data/lib/less/js/test/css/globalVars/extended.css +12 -0
  198. data/lib/less/js/test/css/globalVars/simple.css +6 -0
  199. data/lib/less/js/test/css/ie-filters.css +7 -3
  200. data/lib/less/js/test/css/import-inline.css +5 -0
  201. data/lib/less/js/test/css/import-interpolation.css +6 -0
  202. data/lib/less/js/test/css/import-once.css +15 -0
  203. data/lib/less/js/test/css/import-reference.css +68 -0
  204. data/lib/less/js/test/css/import.css +23 -10
  205. data/lib/less/js/test/css/javascript.css +3 -2
  206. data/lib/less/js/test/css/legacy/legacy.css +7 -0
  207. data/lib/less/js/test/css/media.css +141 -1
  208. data/lib/less/js/test/css/merge.css +34 -0
  209. data/lib/less/js/test/css/mixins-args.css +41 -4
  210. data/lib/less/js/test/css/mixins-guards-default-func.css +129 -0
  211. data/lib/less/js/test/css/mixins-guards.css +24 -0
  212. data/lib/less/js/test/css/mixins-important.css +28 -0
  213. data/lib/less/js/test/css/mixins-interpolated.css +39 -0
  214. data/lib/less/js/test/css/mixins-named-args.css +27 -0
  215. data/lib/less/js/test/css/mixins-pattern.css +4 -0
  216. data/lib/less/js/test/css/mixins.css +70 -0
  217. data/lib/less/js/test/css/modifyVars/extended.css +9 -0
  218. data/lib/less/js/test/css/no-output.css +0 -0
  219. data/lib/less/js/test/css/parens.css +21 -5
  220. data/lib/less/js/test/css/property-name-interp.css +20 -0
  221. data/lib/less/js/test/css/scope.css +23 -0
  222. data/lib/less/js/test/css/selectors.css +84 -0
  223. data/lib/less/js/test/css/static-urls/urls.css +45 -0
  224. data/lib/less/js/test/css/strings.css +5 -2
  225. data/lib/less/js/test/css/url-args/urls.css +56 -0
  226. data/lib/less/js/test/css/urls.css +71 -0
  227. data/lib/less/js/test/css/variables-in-at-rules.css +18 -0
  228. data/lib/less/js/test/css/variables.css +22 -4
  229. data/lib/less/js/test/css/whitespace.css +4 -0
  230. data/lib/less/js/test/data/data-uri-fail.png +0 -0
  231. data/lib/less/js/test/data/image.jpg +1 -0
  232. data/lib/less/js/test/data/page.html +1 -0
  233. data/lib/less/js/test/index.js +45 -0
  234. data/lib/less/js/test/less/charsets.less +3 -0
  235. data/lib/less/js/test/less/colors.less +37 -4
  236. data/lib/less/js/test/less/comments.less +21 -3
  237. data/lib/less/js/test/less/compression/compression.less +36 -0
  238. data/lib/less/js/test/less/css-3.less +70 -6
  239. data/lib/less/js/test/less/css-escapes.less +6 -1
  240. data/lib/less/js/test/less/css-guards.less +102 -0
  241. data/lib/less/js/test/less/css.less +23 -15
  242. data/lib/less/js/test/less/debug/import/test.less +25 -0
  243. data/lib/less/js/test/less/debug/linenumbers.less +33 -0
  244. data/lib/less/js/test/less/detached-rulesets.less +103 -0
  245. data/lib/less/js/test/less/empty.less +0 -0
  246. data/lib/less/js/test/less/errors/add-mixed-units.less +3 -0
  247. data/lib/less/js/test/less/errors/add-mixed-units.txt +4 -0
  248. data/lib/less/js/test/less/errors/add-mixed-units2.less +3 -0
  249. data/lib/less/js/test/less/errors/add-mixed-units2.txt +4 -0
  250. data/lib/less/js/test/less/errors/at-rules-undefined-var.less +4 -0
  251. data/lib/less/js/test/less/errors/at-rules-undefined-var.txt +4 -0
  252. data/lib/less/js/test/less/errors/bad-variable-declaration1.less +1 -0
  253. data/lib/less/js/test/less/errors/bad-variable-declaration1.txt +2 -0
  254. data/lib/less/js/test/less/errors/color-func-invalid-color.less +3 -0
  255. data/lib/less/js/test/less/errors/color-func-invalid-color.txt +4 -0
  256. data/lib/less/js/test/less/errors/comment-in-selector.less +1 -0
  257. data/lib/less/js/test/less/errors/comment-in-selector.txt +2 -0
  258. data/lib/less/js/test/less/errors/css-guard-default-func.less +4 -0
  259. data/lib/less/js/test/less/errors/css-guard-default-func.txt +4 -0
  260. data/lib/less/js/test/less/errors/detached-ruleset-1.less +6 -0
  261. data/lib/less/js/test/less/errors/detached-ruleset-1.txt +4 -0
  262. data/lib/less/js/test/less/errors/detached-ruleset-2.less +6 -0
  263. data/lib/less/js/test/less/errors/detached-ruleset-2.txt +4 -0
  264. data/lib/less/js/test/less/errors/detached-ruleset-3.less +4 -0
  265. data/lib/less/js/test/less/errors/detached-ruleset-3.txt +4 -0
  266. data/lib/less/js/test/less/errors/detached-ruleset-4.less +5 -0
  267. data/lib/less/js/test/less/errors/detached-ruleset-4.txt +3 -0
  268. data/lib/less/js/test/less/errors/detached-ruleset-5.less +4 -0
  269. data/lib/less/js/test/less/errors/detached-ruleset-5.txt +3 -0
  270. data/lib/less/js/test/less/errors/detached-ruleset-6.less +5 -0
  271. data/lib/less/js/test/less/errors/detached-ruleset-6.txt +4 -0
  272. data/lib/less/js/test/less/errors/divide-mixed-units.less +3 -0
  273. data/lib/less/js/test/less/errors/divide-mixed-units.txt +4 -0
  274. data/lib/less/js/test/less/errors/extend-no-selector.less +3 -0
  275. data/lib/less/js/test/less/errors/extend-no-selector.txt +3 -0
  276. data/lib/less/js/test/less/errors/extend-not-at-end.less +3 -0
  277. data/lib/less/js/test/less/errors/extend-not-at-end.txt +3 -0
  278. data/lib/less/js/test/less/errors/import-missing.less +6 -0
  279. data/lib/less/js/test/less/errors/import-missing.txt +3 -0
  280. data/lib/less/js/test/less/errors/import-no-semi.less +1 -0
  281. data/lib/less/js/test/less/errors/import-no-semi.txt +2 -0
  282. data/lib/less/js/test/less/errors/import-subfolder1.less +1 -0
  283. data/lib/less/js/test/less/errors/import-subfolder1.txt +3 -0
  284. data/lib/less/js/test/less/errors/import-subfolder2.less +1 -0
  285. data/lib/less/js/test/less/errors/import-subfolder2.txt +4 -0
  286. data/lib/less/js/test/less/errors/imports/import-subfolder1.less +1 -0
  287. data/lib/less/js/test/less/errors/imports/import-subfolder2.less +1 -0
  288. data/lib/less/js/test/less/errors/imports/import-test.less +4 -0
  289. data/lib/less/js/test/less/errors/imports/subfolder/mixin-not-defined.less +1 -0
  290. data/lib/less/js/test/less/errors/imports/subfolder/parse-error-curly-bracket.less +1 -0
  291. data/lib/less/js/test/less/errors/javascript-error.less +3 -0
  292. data/lib/less/js/test/less/errors/javascript-error.txt +4 -0
  293. data/lib/less/js/test/less/errors/javascript-undefined-var.less +3 -0
  294. data/lib/less/js/test/less/errors/javascript-undefined-var.txt +4 -0
  295. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-1.less +6 -0
  296. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-1.txt +4 -0
  297. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-2.less +6 -0
  298. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-2.txt +4 -0
  299. data/lib/less/js/test/less/errors/mixin-not-defined.less +11 -0
  300. data/lib/less/js/test/less/errors/mixin-not-defined.txt +3 -0
  301. data/lib/less/js/test/less/errors/mixin-not-matched.less +6 -0
  302. data/lib/less/js/test/less/errors/mixin-not-matched.txt +3 -0
  303. data/lib/less/js/test/less/errors/mixin-not-matched2.less +6 -0
  304. data/lib/less/js/test/less/errors/mixin-not-matched2.txt +3 -0
  305. data/lib/less/js/test/less/errors/mixin-not-visible-in-scope-1.less +9 -0
  306. data/lib/less/js/test/less/errors/mixin-not-visible-in-scope-1.txt +4 -0
  307. data/lib/less/js/test/less/errors/mixins-guards-default-func-1.less +9 -0
  308. data/lib/less/js/test/less/errors/mixins-guards-default-func-1.txt +4 -0
  309. data/lib/less/js/test/less/errors/mixins-guards-default-func-2.less +9 -0
  310. data/lib/less/js/test/less/errors/mixins-guards-default-func-2.txt +4 -0
  311. data/lib/less/js/test/less/errors/mixins-guards-default-func-3.less +9 -0
  312. data/lib/less/js/test/less/errors/mixins-guards-default-func-3.txt +4 -0
  313. data/lib/less/js/test/less/errors/multiple-guards-on-css-selectors.less +4 -0
  314. data/lib/less/js/test/less/errors/multiple-guards-on-css-selectors.txt +4 -0
  315. data/lib/less/js/test/less/errors/multiple-guards-on-css-selectors2.less +4 -0
  316. data/lib/less/js/test/less/errors/multiple-guards-on-css-selectors2.txt +4 -0
  317. data/lib/less/js/test/less/errors/multiply-mixed-units.less +7 -0
  318. data/lib/less/js/test/less/errors/multiply-mixed-units.txt +4 -0
  319. data/lib/less/js/test/less/errors/parens-error-1.less +3 -0
  320. data/lib/less/js/test/less/errors/parens-error-1.txt +4 -0
  321. data/lib/less/js/test/less/errors/parens-error-2.less +3 -0
  322. data/lib/less/js/test/less/errors/parens-error-2.txt +4 -0
  323. data/lib/less/js/test/less/errors/parens-error-3.less +3 -0
  324. data/lib/less/js/test/less/errors/parens-error-3.txt +4 -0
  325. data/lib/less/js/test/less/errors/parse-error-curly-bracket.less +4 -0
  326. data/lib/less/js/test/less/errors/parse-error-curly-bracket.txt +4 -0
  327. data/lib/less/js/test/less/errors/parse-error-extra-parens.less +5 -0
  328. data/lib/less/js/test/less/errors/parse-error-extra-parens.txt +3 -0
  329. data/lib/less/js/test/less/errors/parse-error-missing-bracket.less +2 -0
  330. data/lib/less/js/test/less/errors/parse-error-missing-bracket.txt +3 -0
  331. data/lib/less/js/test/less/errors/parse-error-missing-parens.less +5 -0
  332. data/lib/less/js/test/less/errors/parse-error-missing-parens.txt +3 -0
  333. data/lib/less/js/test/less/errors/parse-error-with-import.less +13 -0
  334. data/lib/less/js/test/less/errors/parse-error-with-import.txt +4 -0
  335. data/lib/less/js/test/less/errors/percentage-missing-space.less +3 -0
  336. data/lib/less/js/test/less/errors/percentage-missing-space.txt +4 -0
  337. data/lib/less/js/test/less/errors/property-asterisk-only-name.less +3 -0
  338. data/lib/less/js/test/less/errors/property-asterisk-only-name.txt +4 -0
  339. data/lib/less/js/test/less/errors/property-ie5-hack.less +3 -0
  340. data/lib/less/js/test/less/errors/property-ie5-hack.txt +4 -0
  341. data/lib/less/js/test/less/errors/property-in-root.less +4 -0
  342. data/lib/less/js/test/less/errors/property-in-root.txt +4 -0
  343. data/lib/less/js/test/less/errors/property-in-root2.less +1 -0
  344. data/lib/less/js/test/less/errors/property-in-root2.txt +4 -0
  345. data/lib/less/js/test/less/errors/property-in-root3.less +4 -0
  346. data/lib/less/js/test/less/errors/property-in-root3.txt +3 -0
  347. data/lib/less/js/test/less/errors/property-interp-not-defined.less +1 -0
  348. data/lib/less/js/test/less/errors/property-interp-not-defined.txt +2 -0
  349. data/lib/less/js/test/less/errors/recursive-variable.less +1 -0
  350. data/lib/less/js/test/less/errors/recursive-variable.txt +2 -0
  351. data/lib/less/js/test/less/errors/svg-gradient1.less +3 -0
  352. data/lib/less/js/test/less/errors/svg-gradient1.txt +4 -0
  353. data/lib/less/js/test/less/errors/svg-gradient2.less +3 -0
  354. data/lib/less/js/test/less/errors/svg-gradient2.txt +4 -0
  355. data/lib/less/js/test/less/errors/svg-gradient3.less +3 -0
  356. data/lib/less/js/test/less/errors/svg-gradient3.txt +4 -0
  357. data/lib/less/js/test/less/errors/unit-function.less +3 -0
  358. data/lib/less/js/test/less/errors/unit-function.txt +4 -0
  359. data/lib/less/js/test/less/extend-chaining.less +91 -0
  360. data/lib/less/js/test/less/extend-clearfix.less +19 -0
  361. data/lib/less/js/test/less/extend-exact.less +46 -0
  362. data/lib/less/js/test/less/extend-media.less +24 -0
  363. data/lib/less/js/test/less/extend-nest.less +65 -0
  364. data/lib/less/js/test/less/extend-selector.less +99 -0
  365. data/lib/less/js/test/less/extend.less +81 -0
  366. data/lib/less/js/test/less/extract-and-length.less +133 -0
  367. data/lib/less/js/test/less/functions.less +130 -6
  368. data/lib/less/js/test/less/globalVars/extended.json +5 -0
  369. data/lib/less/js/test/less/globalVars/extended.less +10 -0
  370. data/lib/less/js/test/less/globalVars/simple.json +3 -0
  371. data/lib/less/js/test/less/globalVars/simple.less +3 -0
  372. data/lib/less/js/test/less/ie-filters.less +7 -0
  373. data/lib/less/js/test/less/import/deeper/import-once-test-a.less +1 -0
  374. data/lib/less/js/test/less/import/import-and-relative-paths-test.less +17 -0
  375. data/lib/less/js/test/less/import/import-charset-test.less +1 -0
  376. data/lib/less/js/test/less/import/import-interpolation.less +1 -0
  377. data/lib/less/js/test/less/import/import-interpolation2.less +5 -0
  378. data/lib/less/js/test/less/import/import-once-test-c.less +6 -0
  379. data/lib/less/js/test/less/import/import-reference.less +51 -0
  380. data/lib/less/js/test/less/import/import-test-a.less +1 -0
  381. data/lib/less/js/test/less/import/import-test-c.less +0 -1
  382. data/lib/less/js/test/less/import/import-test-f.less +5 -0
  383. data/lib/less/js/test/less/import/imports/font.less +8 -0
  384. data/lib/less/js/test/less/import/imports/logo.less +5 -0
  385. data/lib/less/js/test/less/import/invalid-css.less +1 -0
  386. data/lib/less/js/test/less/import/urls.less +1 -0
  387. data/lib/less/js/test/less/import-inline.less +2 -0
  388. data/lib/less/js/test/less/import-interpolation.less +8 -0
  389. data/lib/less/js/test/less/import-once.less +6 -0
  390. data/lib/less/js/test/less/import-reference.less +21 -0
  391. data/lib/less/js/test/less/import.less +13 -3
  392. data/lib/less/js/test/less/javascript.less +5 -3
  393. data/lib/less/js/test/less/legacy/legacy.less +7 -0
  394. data/lib/less/js/test/less/media.less +158 -3
  395. data/lib/less/js/test/less/merge.less +78 -0
  396. data/lib/less/js/test/less/mixins-args.less +93 -15
  397. data/lib/less/js/test/less/mixins-guards-default-func.less +195 -0
  398. data/lib/less/js/test/less/mixins-guards.less +65 -0
  399. data/lib/less/js/test/less/mixins-important.less +8 -1
  400. data/lib/less/js/test/less/mixins-interpolated.less +69 -0
  401. data/lib/less/js/test/less/mixins-named-args.less +36 -0
  402. data/lib/less/js/test/less/mixins-nested.less +2 -2
  403. data/lib/less/js/test/less/mixins-pattern.less +4 -1
  404. data/lib/less/js/test/less/mixins.less +74 -0
  405. data/lib/less/js/test/less/modifyVars/extended.json +5 -0
  406. data/lib/less/js/test/less/modifyVars/extended.less +11 -0
  407. data/lib/less/js/test/less/no-js-errors/no-js-errors.less +3 -0
  408. data/lib/less/js/test/less/no-js-errors/no-js-errors.txt +4 -0
  409. data/lib/less/js/test/less/no-output.less +2 -0
  410. data/lib/less/js/test/less/operations.less +27 -27
  411. data/lib/less/js/test/less/parens.less +24 -5
  412. data/lib/less/js/test/less/property-name-interp.less +53 -0
  413. data/lib/less/js/test/less/scope.less +73 -1
  414. data/lib/less/js/test/less/selectors.less +101 -0
  415. data/lib/less/js/test/less/sourcemaps/basic.json +3 -0
  416. data/lib/less/js/test/less/sourcemaps/basic.less +27 -0
  417. data/lib/less/js/test/less/sourcemaps/imported.css +7 -0
  418. data/lib/less/js/test/less/static-urls/urls.less +33 -0
  419. data/lib/less/js/test/less/strings.less +7 -1
  420. data/lib/less/js/test/less/url-args/urls.less +63 -0
  421. data/lib/less/js/test/less/urls.less +72 -0
  422. data/lib/less/js/test/less/variables-in-at-rules.less +20 -0
  423. data/lib/less/js/test/less/variables.less +42 -13
  424. data/lib/less/js/test/less/whitespace.less +7 -0
  425. data/lib/less/js/test/less-test.js +251 -65
  426. data/lib/less/js/test/rhino/test-header.js +15 -0
  427. data/lib/less/js/test/sourcemaps/basic.json +1 -0
  428. data/lib/less/js/test/sourcemaps/index.html +17 -0
  429. data/lib/less/loader.rb +191 -27
  430. data/lib/less/parser.rb +47 -8
  431. data/lib/less/version.rb +2 -2
  432. data/spec/less/loader_spec.rb +45 -0
  433. data/spec/less/parser_spec.rb +43 -17
  434. data/spec/less/some/some.css +0 -0
  435. data/spec/less/some/some.less +4 -0
  436. metadata +352 -57
  437. data/lib/less/js/CHANGELOG +0 -41
  438. data/lib/less/js/Makefile +0 -75
  439. data/lib/less/js/build/ecma-5.js +0 -120
  440. data/lib/less/js/build/header.js +0 -7
  441. data/lib/less/js/lib/less/cssmin.js +0 -355
@@ -1,23 +1,10 @@
1
1
  var less, tree;
2
2
 
3
- if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") {
4
- // Rhino
5
- // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88
6
- if (typeof(window) === 'undefined') { less = {} }
7
- else { less = window.less = {} }
8
- tree = less.tree = {};
9
- less.mode = 'rhino';
10
- } else if (typeof(window) === 'undefined') {
11
- // Node.js
12
- less = exports,
3
+ // Node.js does not have a header file added which defines less
4
+ if (less === undefined) {
5
+ less = exports;
13
6
  tree = require('./tree');
14
7
  less.mode = 'node';
15
- } else {
16
- // Browser
17
- if (typeof(window.less) === 'undefined') { window.less = {} }
18
- less = window.less,
19
- tree = window.less.tree = {};
20
- less.mode = 'browser';
21
8
  }
22
9
  //
23
10
  // less.js - parser
@@ -32,7 +19,7 @@ if (typeof environment === "object" && ({}).toString.call(environment) === "[obj
32
19
  // - Matching and slicing on a huge input is often cause of slowdowns.
33
20
  // The solution is to chunkify the input into smaller strings.
34
21
  // The chunks are stored in the `chunks` var,
35
- // `j` holds the current chunk index, and `current` holds
22
+ // `j` holds the current chunk index, and `currentPos` holds
36
23
  // the index of the current chunk in relation to `input`.
37
24
  // This gives us an almost 4x speed-up.
38
25
  //
@@ -56,125 +43,212 @@ less.Parser = function Parser(env) {
56
43
  var input, // LeSS input string
57
44
  i, // current index in `input`
58
45
  j, // current chunk
59
- temp, // temporarily holds a chunk's state, for backtracking
60
- memo, // temporarily holds `i`, when backtracking
46
+ saveStack = [], // holds state for backtracking
61
47
  furthest, // furthest index the parser has gone to
62
48
  chunks, // chunkified input
63
- current, // index of current chunk, in `input`
64
- parser;
65
-
66
- var that = this;
67
-
68
- // This function is called after all files
69
- // have been imported through `@import`.
70
- var finish = function () {};
49
+ current, // current chunk
50
+ currentPos, // index of current chunk, in `input`
51
+ parser,
52
+ parsers,
53
+ rootFilename = env && env.filename;
54
+
55
+ // Top parser on an import tree must be sure there is one "env"
56
+ // which will then be passed around by reference.
57
+ if (!(env instanceof tree.parseEnv)) {
58
+ env = new tree.parseEnv(env);
59
+ }
71
60
 
72
61
  var imports = this.imports = {
73
- paths: env && env.paths || [], // Search paths, when importing
74
- queue: [], // Files which haven't been imported yet
75
- files: {}, // Holds the imported parse trees
76
- contents: {}, // Holds the imported file contents
77
- mime: env && env.mime, // MIME type of .less files
78
- error: null, // Error in parsing/evaluating an import
79
- push: function (path, callback) {
80
- var that = this;
62
+ paths: env.paths || [], // Search paths, when importing
63
+ queue: [], // Files which haven't been imported yet
64
+ files: env.files, // Holds the imported parse trees
65
+ contents: env.contents, // Holds the imported file contents
66
+ contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less
67
+ mime: env.mime, // MIME type of .less files
68
+ error: null, // Error in parsing/evaluating an import
69
+ push: function (path, currentFileInfo, importOptions, callback) {
70
+ var parserImports = this;
81
71
  this.queue.push(path);
82
72
 
83
- //
84
- // Import a file asynchronously
85
- //
86
- less.Parser.importer(path, this.paths, function (e, root, contents) {
87
- that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue
88
- that.files[path] = root; // Store the root
89
- that.contents[path] = contents;
73
+ var fileParsedFunc = function (e, root, fullPath) {
74
+ parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue
75
+
76
+ var importedPreviously = fullPath === rootFilename;
77
+
78
+ parserImports.files[fullPath] = root; // Store the root
90
79
 
91
- if (e && !that.error) { that.error = e }
92
- callback(e, root);
80
+ if (e && !parserImports.error) { parserImports.error = e; }
93
81
 
94
- if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing
95
- }, env);
82
+ callback(e, root, importedPreviously, fullPath);
83
+ };
84
+
85
+ if (less.Parser.importer) {
86
+ less.Parser.importer(path, currentFileInfo, fileParsedFunc, env);
87
+ } else {
88
+ less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) {
89
+ if (e) {fileParsedFunc(e); return;}
90
+
91
+ var newEnv = new tree.parseEnv(env);
92
+
93
+ newEnv.currentFileInfo = newFileInfo;
94
+ newEnv.processImports = false;
95
+ newEnv.contents[fullPath] = contents;
96
+
97
+ if (currentFileInfo.reference || importOptions.reference) {
98
+ newFileInfo.reference = true;
99
+ }
100
+
101
+ if (importOptions.inline) {
102
+ fileParsedFunc(null, contents, fullPath);
103
+ } else {
104
+ new(less.Parser)(newEnv).parse(contents, function (e, root) {
105
+ fileParsedFunc(e, root, fullPath);
106
+ });
107
+ }
108
+ }, env);
109
+ }
96
110
  }
97
111
  };
98
112
 
99
- function save() { temp = chunks[j], memo = i, current = i }
100
- function restore() { chunks[j] = temp, i = memo, current = i }
113
+ function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); }
114
+ function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; }
115
+ function forget() { saveStack.pop(); }
101
116
 
102
117
  function sync() {
103
- if (i > current) {
104
- chunks[j] = chunks[j].slice(i - current);
105
- current = i;
118
+ if (i > currentPos) {
119
+ current = current.slice(i - currentPos);
120
+ currentPos = i;
106
121
  }
107
122
  }
123
+ function isWhitespace(str, pos) {
124
+ var code = str.charCodeAt(pos | 0);
125
+ return (code <= 32) && (code === 32 || code === 10 || code === 9);
126
+ }
108
127
  //
109
128
  // Parse from a token, regexp or string, and move forward if match
110
129
  //
111
130
  function $(tok) {
112
- var match, args, length, c, index, endIndex, k, mem;
131
+ var tokType = typeof tok,
132
+ match, length;
113
133
 
134
+ // Either match a single character in the input,
135
+ // or match a regexp in the current chunk (`current`).
114
136
  //
115
- // Non-terminal
116
- //
117
- if (tok instanceof Function) {
118
- return tok.call(parser.parsers);
119
- //
120
- // Terminal
121
- //
122
- // Either match a single character in the input,
123
- // or match a regexp in the current chunk (chunk[j]).
124
- //
125
- } else if (typeof(tok) === 'string') {
126
- match = input.charAt(i) === tok ? tok : null;
127
- length = 1;
128
- sync ();
129
- } else {
130
- sync ();
131
-
132
- if (match = tok.exec(chunks[j])) {
133
- length = match[0].length;
134
- } else {
137
+ if (tokType === "string") {
138
+ if (input.charAt(i) !== tok) {
135
139
  return null;
136
140
  }
141
+ skipWhitespace(1);
142
+ return tok;
143
+ }
144
+
145
+ // regexp
146
+ sync ();
147
+ if (! (match = tok.exec(current))) {
148
+ return null;
137
149
  }
138
150
 
151
+ length = match[0].length;
152
+
139
153
  // The match is confirmed, add the match length to `i`,
140
154
  // and consume any extra white-space characters (' ' || '\n')
141
155
  // which come after that. The reason for this is that LeSS's
142
156
  // grammar is mostly white-space insensitive.
143
157
  //
144
- if (match) {
145
- mem = i += length;
146
- endIndex = i + chunks[j].length - length;
147
-
148
- while (i < endIndex) {
149
- c = input.charCodeAt(i);
150
- if (! (c === 32 || c === 10 || c === 9)) { break }
151
- i++;
152
- }
153
- chunks[j] = chunks[j].slice(length + (i - mem));
154
- current = i;
158
+ skipWhitespace(length);
159
+
160
+ if(typeof(match) === 'string') {
161
+ return match;
162
+ } else {
163
+ return match.length === 1 ? match[0] : match;
164
+ }
165
+ }
155
166
 
156
- if (chunks[j].length === 0 && j < chunks.length - 1) { j++ }
167
+ // Specialization of $(tok)
168
+ function $re(tok) {
169
+ if (i > currentPos) {
170
+ current = current.slice(i - currentPos);
171
+ currentPos = i;
172
+ }
173
+ var m = tok.exec(current);
174
+ if (!m) {
175
+ return null;
176
+ }
157
177
 
158
- if(typeof(match) === 'string') {
159
- return match;
160
- } else {
161
- return match.length === 1 ? match[0] : match;
178
+ skipWhitespace(m[0].length);
179
+ if(typeof m === "string") {
180
+ return m;
181
+ }
182
+
183
+ return m.length === 1 ? m[0] : m;
184
+ }
185
+
186
+ var _$re = $re;
187
+
188
+ // Specialization of $(tok)
189
+ function $char(tok) {
190
+ if (input.charAt(i) !== tok) {
191
+ return null;
192
+ }
193
+ skipWhitespace(1);
194
+ return tok;
195
+ }
196
+
197
+ function skipWhitespace(length) {
198
+ var oldi = i, oldj = j,
199
+ curr = i - currentPos,
200
+ endIndex = i + current.length - curr,
201
+ mem = (i += length),
202
+ inp = input,
203
+ c;
204
+
205
+ for (; i < endIndex; i++) {
206
+ c = inp.charCodeAt(i);
207
+ if (c > 32) {
208
+ break;
209
+ }
210
+
211
+ if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) {
212
+ break;
162
213
  }
214
+ }
215
+
216
+ current = current.slice(length + i - mem + curr);
217
+ currentPos = i;
218
+
219
+ if (!current.length && (j < chunks.length - 1)) {
220
+ current = chunks[++j];
221
+ skipWhitespace(0); // skip space at the beginning of a chunk
222
+ return true; // things changed
163
223
  }
224
+
225
+ return oldi !== i || oldj !== j;
164
226
  }
165
227
 
166
228
  function expect(arg, msg) {
167
- var result = $(arg);
168
- if (! result) {
169
- error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'"
170
- : "unexpected token"));
171
- } else {
229
+ // some older browsers return typeof 'function' for RegExp
230
+ var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg);
231
+ if (result) {
172
232
  return result;
173
233
  }
234
+ error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'"
235
+ : "unexpected token"));
236
+ }
237
+
238
+ // Specialization of expect()
239
+ function expectChar(arg, msg) {
240
+ if (input.charAt(i) === arg) {
241
+ skipWhitespace(1);
242
+ return arg;
243
+ }
244
+ error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'");
174
245
  }
175
246
 
176
247
  function error(msg, type) {
177
- throw { index: i, type: type || 'Syntax', message: msg };
248
+ var e = new Error(msg);
249
+ e.index = i;
250
+ e.type = type || 'Syntax';
251
+ throw e;
178
252
  }
179
253
 
180
254
  // Same as $(), but don't change the state of the parser,
@@ -183,37 +257,53 @@ less.Parser = function Parser(env) {
183
257
  if (typeof(tok) === 'string') {
184
258
  return input.charAt(i) === tok;
185
259
  } else {
186
- if (tok.test(chunks[j])) {
187
- return true;
188
- } else {
189
- return false;
190
- }
260
+ return tok.test(current);
191
261
  }
192
262
  }
193
263
 
194
- function basename(pathname) {
195
- if (less.mode === 'node') {
196
- return require('path').basename(pathname);
197
- } else {
198
- return pathname.match(/[^\/]+$/)[0];
199
- }
264
+ // Specialization of peek()
265
+ function peekChar(tok) {
266
+ return input.charAt(i) === tok;
200
267
  }
201
268
 
269
+
202
270
  function getInput(e, env) {
203
- if (e.filename && env.filename && (e.filename !== env.filename)) {
204
- return parser.imports.contents[basename(e.filename)];
271
+ if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) {
272
+ return parser.imports.contents[e.filename];
205
273
  } else {
206
274
  return input;
207
275
  }
208
276
  }
209
277
 
210
- function getLocation(index, input) {
211
- for (var n = index, column = -1;
212
- n >= 0 && input.charAt(n) !== '\n';
213
- n--) { column++ }
278
+ function getLocation(index, inputStream) {
279
+ var n = index + 1,
280
+ line = null,
281
+ column = -1;
282
+
283
+ while (--n >= 0 && inputStream.charAt(n) !== '\n') {
284
+ column++;
285
+ }
286
+
287
+ if (typeof index === 'number') {
288
+ line = (inputStream.slice(0, index).match(/\n/g) || "").length;
289
+ }
290
+
291
+ return {
292
+ line: line,
293
+ column: column
294
+ };
295
+ }
296
+
297
+ function getDebugInfo(index, inputStream, env) {
298
+ var filename = env.currentFileInfo.filename;
299
+ if(less.mode !== 'browser' && less.mode !== 'rhino') {
300
+ filename = require('path').resolve(filename);
301
+ }
214
302
 
215
- return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null,
216
- column: column };
303
+ return {
304
+ lineNumber: getLocation(index, inputStream).line + 1,
305
+ fileName: filename
306
+ };
217
307
  }
218
308
 
219
309
  function LessError(e, env) {
@@ -221,15 +311,16 @@ less.Parser = function Parser(env) {
221
311
  loc = getLocation(e.index, input),
222
312
  line = loc.line,
223
313
  col = loc.column,
314
+ callLine = e.call && getLocation(e.call, input).line,
224
315
  lines = input.split('\n');
225
316
 
226
317
  this.type = e.type || 'Syntax';
227
318
  this.message = e.message;
228
- this.filename = e.filename || env.filename;
319
+ this.filename = e.filename || env.currentFileInfo.filename;
229
320
  this.index = e.index;
230
321
  this.line = typeof(line) === 'number' ? line + 1 : null;
231
- this.callLine = e.call && (getLocation(e.call, input).line + 1);
232
- this.callExtract = lines[getLocation(e.call, input).line];
322
+ this.callLine = callLine + 1;
323
+ this.callExtract = lines[callLine];
233
324
  this.stack = e.stack;
234
325
  this.column = col;
235
326
  this.extract = [
@@ -239,6 +330,9 @@ less.Parser = function Parser(env) {
239
330
  ];
240
331
  }
241
332
 
333
+ LessError.prototype = new Error();
334
+ LessError.prototype.constructor = LessError;
335
+
242
336
  this.env = env = env || {};
243
337
 
244
338
  // The optimization level dictates the thoroughness of the parser,
@@ -247,109 +341,183 @@ less.Parser = function Parser(env) {
247
341
  // the individual nodes in the tree.
248
342
  this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;
249
343
 
250
- this.env.filename = this.env.filename || null;
251
-
252
344
  //
253
345
  // The Parser
254
346
  //
255
- return parser = {
347
+ parser = {
256
348
 
257
349
  imports: imports,
258
350
  //
259
351
  // Parse an input string into an abstract syntax tree,
260
- // call `callback` when done.
352
+ // @param str A string containing 'less' markup
353
+ // @param callback call `callback` when done.
354
+ // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply
261
355
  //
262
- parse: function (str, callback) {
263
- var root, start, end, zone, line, lines, buff = [], c, error = null;
356
+ parse: function (str, callback, additionalData) {
357
+ var root, line, lines, error = null, globalVars, modifyVars, preText = "";
358
+
359
+ i = j = currentPos = furthest = 0;
360
+
361
+ globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : '';
362
+ modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : '';
363
+
364
+ if (globalVars || (additionalData && additionalData.banner)) {
365
+ preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars;
366
+ parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length;
367
+ }
264
368
 
265
- i = j = current = furthest = 0;
266
- input = str.replace(/\r\n/g, '\n');
369
+ str = str.replace(/\r\n/g, '\n');
370
+ // Remove potential UTF Byte Order Mark
371
+ input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars;
372
+ parser.imports.contents[env.currentFileInfo.filename] = str;
267
373
 
268
374
  // Split the input into chunks.
269
- chunks = (function (chunks) {
270
- var j = 0,
271
- skip = /[^"'`\{\}\/\(\)\\]+/g,
272
- comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,
273
- string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`\\\r\n]|\\.)*)`/g,
274
- level = 0,
275
- match,
276
- chunk = chunks[0],
277
- inParam;
278
-
279
- for (var i = 0, c, cc; i < input.length; i++) {
280
- skip.lastIndex = i;
281
- if (match = skip.exec(input)) {
282
- if (match.index === i) {
283
- i += match[0].length;
284
- chunk.push(match[0]);
285
- }
375
+ chunks = (function (input) {
376
+ var len = input.length, level = 0, parenLevel = 0,
377
+ lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace,
378
+ chunks = [], emitFrom = 0,
379
+ parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched;
380
+
381
+ function fail(msg, index) {
382
+ error = new(LessError)({
383
+ index: index || parserCurrentIndex,
384
+ type: 'Parse',
385
+ message: msg,
386
+ filename: env.currentFileInfo.filename
387
+ }, env);
388
+ }
389
+
390
+ function emitChunk(force) {
391
+ var len = parserCurrentIndex - emitFrom;
392
+ if (((len < 512) && !force) || !len) {
393
+ return;
286
394
  }
287
- c = input.charAt(i);
288
- comment.lastIndex = string.lastIndex = i;
395
+ chunks.push(input.slice(emitFrom, parserCurrentIndex + 1));
396
+ emitFrom = parserCurrentIndex + 1;
397
+ }
289
398
 
290
- if (match = string.exec(input)) {
291
- if (match.index === i) {
292
- i += match[0].length;
293
- chunk.push(match[0]);
294
- c = input.charAt(i);
295
- }
399
+ for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) {
400
+ cc = input.charCodeAt(parserCurrentIndex);
401
+ if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
402
+ // a-z or whitespace
403
+ continue;
296
404
  }
297
405
 
298
- if (!inParam && c === '/') {
299
- cc = input.charAt(i + 1);
300
- if (cc === '/' || cc === '*') {
301
- if (match = comment.exec(input)) {
302
- if (match.index === i) {
303
- i += match[0].length;
304
- chunk.push(match[0]);
305
- c = input.charAt(i);
406
+ switch (cc) {
407
+ case 40: // (
408
+ parenLevel++;
409
+ lastOpeningParen = parserCurrentIndex;
410
+ continue;
411
+ case 41: // )
412
+ if (--parenLevel < 0) {
413
+ return fail("missing opening `(`");
414
+ }
415
+ continue;
416
+ case 59: // ;
417
+ if (!parenLevel) { emitChunk(); }
418
+ continue;
419
+ case 123: // {
420
+ level++;
421
+ lastOpening = parserCurrentIndex;
422
+ continue;
423
+ case 125: // }
424
+ if (--level < 0) {
425
+ return fail("missing opening `{`");
426
+ }
427
+ if (!level && !parenLevel) { emitChunk(); }
428
+ continue;
429
+ case 92: // \
430
+ if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; }
431
+ return fail("unescaped `\\`");
432
+ case 34:
433
+ case 39:
434
+ case 96: // ", ' and `
435
+ matched = 0;
436
+ currentChunkStartIndex = parserCurrentIndex;
437
+ for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) {
438
+ cc2 = input.charCodeAt(parserCurrentIndex);
439
+ if (cc2 > 96) { continue; }
440
+ if (cc2 == cc) { matched = 1; break; }
441
+ if (cc2 == 92) { // \
442
+ if (parserCurrentIndex == len - 1) {
443
+ return fail("unescaped `\\`");
444
+ }
445
+ parserCurrentIndex++;
306
446
  }
307
447
  }
308
- }
448
+ if (matched) { continue; }
449
+ return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
450
+ case 47: // /, check for comment
451
+ if (parenLevel || (parserCurrentIndex == len - 1)) { continue; }
452
+ cc2 = input.charCodeAt(parserCurrentIndex + 1);
453
+ if (cc2 == 47) {
454
+ // //, find lnfeed
455
+ for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) {
456
+ cc2 = input.charCodeAt(parserCurrentIndex);
457
+ if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
458
+ }
459
+ } else if (cc2 == 42) {
460
+ // /*, find */
461
+ lastMultiComment = currentChunkStartIndex = parserCurrentIndex;
462
+ for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) {
463
+ cc2 = input.charCodeAt(parserCurrentIndex);
464
+ if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; }
465
+ if (cc2 != 42) { continue; }
466
+ if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; }
467
+ }
468
+ if (parserCurrentIndex == len - 1) {
469
+ return fail("missing closing `*/`", currentChunkStartIndex);
470
+ }
471
+ parserCurrentIndex++;
472
+ }
473
+ continue;
474
+ case 42: // *, check for unmatched */
475
+ if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) {
476
+ return fail("unmatched `/*`");
477
+ }
478
+ continue;
309
479
  }
480
+ }
310
481
 
311
- switch (c) {
312
- case '{': if (! inParam) { level ++; chunk.push(c); break }
313
- case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break }
314
- case '(': if (! inParam) { inParam = true; chunk.push(c); break }
315
- case ')': if ( inParam) { inParam = false; chunk.push(c); break }
316
- default: chunk.push(c);
482
+ if (level !== 0) {
483
+ if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
484
+ return fail("missing closing `}` or `*/`", lastOpening);
485
+ } else {
486
+ return fail("missing closing `}`", lastOpening);
317
487
  }
318
- }
319
- if (level > 0) {
320
- error = new(LessError)({
321
- index: i,
322
- type: 'Parse',
323
- message: "missing closing `}`",
324
- filename: env.filename
325
- }, env);
488
+ } else if (parenLevel !== 0) {
489
+ return fail("missing closing `)`", lastOpeningParen);
326
490
  }
327
491
 
328
- return chunks.map(function (c) { return c.join('') });;
329
- })([[]]);
492
+ emitChunk(true);
493
+ return chunks;
494
+ })(str);
330
495
 
331
496
  if (error) {
332
- return callback(error);
497
+ return callback(new(LessError)(error, env));
333
498
  }
334
499
 
500
+ current = chunks[0];
501
+
335
502
  // Start with the primary rule.
336
503
  // The whole syntax tree is held under a Ruleset node,
337
504
  // with the `root` property set to true, so no `{}` are
338
505
  // output. The callback is called when the input is parsed.
339
506
  try {
340
- root = new(tree.Ruleset)([], $(this.parsers.primary));
507
+ root = new(tree.Ruleset)(null, this.parsers.primary());
341
508
  root.root = true;
509
+ root.firstRoot = true;
342
510
  } catch (e) {
343
511
  return callback(new(LessError)(e, env));
344
512
  }
345
513
 
346
514
  root.toCSS = (function (evaluate) {
347
- var line, lines, column;
348
-
349
515
  return function (options, variables) {
350
- var frames = [], importError;
351
-
352
516
  options = options || {};
517
+ var evaldRoot,
518
+ css,
519
+ evalEnv = new tree.evalEnv(options);
520
+
353
521
  //
354
522
  // Allows setting variables with a hash, so:
355
523
  //
@@ -373,27 +541,85 @@ less.Parser = function Parser(env) {
373
541
  }
374
542
  value = new(tree.Value)([value]);
375
543
  }
376
- return new(tree.Rule)('@' + k, value, false, 0);
544
+ return new(tree.Rule)('@' + k, value, false, null, 0);
377
545
  });
378
- frames = [new(tree.Ruleset)(null, variables)];
546
+ evalEnv.frames = [new(tree.Ruleset)(null, variables)];
379
547
  }
380
548
 
381
549
  try {
382
- var css = evaluate.call(this, { frames: frames })
383
- .toCSS([], { compress: options.compress || false });
550
+ var preEvalVisitors = [],
551
+ visitors = [
552
+ new(tree.joinSelectorVisitor)(),
553
+ new(tree.processExtendsVisitor)(),
554
+ new(tree.toCSSVisitor)({compress: Boolean(options.compress)})
555
+ ], i, root = this;
556
+
557
+ if (options.plugins) {
558
+ for(i =0; i < options.plugins.length; i++) {
559
+ if (options.plugins[i].isPreEvalVisitor) {
560
+ preEvalVisitors.push(options.plugins[i]);
561
+ } else {
562
+ if (options.plugins[i].isPreVisitor) {
563
+ visitors.splice(0, 0, options.plugins[i]);
564
+ } else {
565
+ visitors.push(options.plugins[i]);
566
+ }
567
+ }
568
+ }
569
+ }
570
+
571
+ for(i = 0; i < preEvalVisitors.length; i++) {
572
+ preEvalVisitors[i].run(root);
573
+ }
574
+
575
+ evaldRoot = evaluate.call(root, evalEnv);
576
+
577
+ for(i = 0; i < visitors.length; i++) {
578
+ visitors[i].run(evaldRoot);
579
+ }
580
+
581
+ if (options.sourceMap) {
582
+ evaldRoot = new tree.sourceMapOutput(
583
+ {
584
+ contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars,
585
+ writeSourceMap: options.writeSourceMap,
586
+ rootNode: evaldRoot,
587
+ contentsMap: parser.imports.contents,
588
+ sourceMapFilename: options.sourceMapFilename,
589
+ sourceMapURL: options.sourceMapURL,
590
+ outputFilename: options.sourceMapOutputFilename,
591
+ sourceMapBasepath: options.sourceMapBasepath,
592
+ sourceMapRootpath: options.sourceMapRootpath,
593
+ outputSourceFiles: options.outputSourceFiles,
594
+ sourceMapGenerator: options.sourceMapGenerator
595
+ });
596
+ }
597
+
598
+ css = evaldRoot.toCSS({
599
+ compress: Boolean(options.compress),
600
+ dumpLineNumbers: env.dumpLineNumbers,
601
+ strictUnits: Boolean(options.strictUnits),
602
+ numPrecision: 8});
384
603
  } catch (e) {
385
604
  throw new(LessError)(e, env);
386
605
  }
387
606
 
388
- if ((importError = parser.imports.error)) { // Check if there was an error during importing
389
- if (importError instanceof LessError) throw importError;
390
- else throw new(LessError)(importError, env);
391
- }
607
+ if (options.cleancss && less.mode === 'node') {
608
+ var CleanCSS = require('clean-css'),
609
+ cleancssOptions = options.cleancssOptions || {};
392
610
 
393
- if (options.yuicompress && less.mode === 'node') {
394
- return require('./cssmin').compressor.cssmin(css);
611
+ if (cleancssOptions.keepSpecialComments === undefined) {
612
+ cleancssOptions.keepSpecialComments = "*";
613
+ }
614
+ cleancssOptions.processImport = false;
615
+ cleancssOptions.noRebase = true;
616
+ if (cleancssOptions.noAdvanced === undefined) {
617
+ cleancssOptions.noAdvanced = true;
618
+ }
619
+
620
+ return new CleanCSS(cleancssOptions).minify(css);
395
621
  } else if (options.compress) {
396
- return css.replace(/(\s)+/g, "$1");
622
+ return css.replace(/(^(\s)+)|((\s)+$)/g, "");
397
623
  } else {
398
624
  return css;
399
625
  }
@@ -410,18 +636,17 @@ less.Parser = function Parser(env) {
410
636
  // and the part which didn't), so we can color them differently.
411
637
  if (i < input.length - 1) {
412
638
  i = furthest;
639
+ var loc = getLocation(i, input);
413
640
  lines = input.split('\n');
414
- line = (input.slice(0, i).match(/\n/g) || "").length + 1;
415
-
416
- for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ }
641
+ line = loc.line + 1;
417
642
 
418
643
  error = {
419
644
  type: "Parse",
420
- message: "Syntax Error on line " + line,
645
+ message: "Unrecognised input",
421
646
  index: i,
422
- filename: env.filename,
647
+ filename: env.currentFileInfo.filename,
423
648
  line: line,
424
- column: column,
649
+ column: loc.column,
425
650
  extract: [
426
651
  lines[line - 2],
427
652
  lines[line - 1],
@@ -430,10 +655,26 @@ less.Parser = function Parser(env) {
430
655
  };
431
656
  }
432
657
 
433
- if (this.imports.queue.length > 0) {
434
- finish = function () { callback(error, root) };
658
+ var finish = function (e) {
659
+ e = error || e || parser.imports.error;
660
+
661
+ if (e) {
662
+ if (!(e instanceof LessError)) {
663
+ e = new(LessError)(e, env);
664
+ }
665
+
666
+ return callback(e);
667
+ }
668
+ else {
669
+ return callback(null, root);
670
+ }
671
+ };
672
+
673
+ if (env.processImports !== false) {
674
+ new tree.importVisitor(this.imports, finish)
675
+ .run(root);
435
676
  } else {
436
- callback(error, root);
677
+ return finish();
437
678
  }
438
679
  },
439
680
 
@@ -466,7 +707,7 @@ less.Parser = function Parser(env) {
466
707
  // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
467
708
  // first, before parsing, that's when we use `peek()`.
468
709
  //
469
- parsers: {
710
+ parsers: parsers = {
470
711
  //
471
712
  // The `primary` rule is the *entry* and *exit* point of the parser.
472
713
  // The rules here can appear at any level of the parse tree.
@@ -483,13 +724,24 @@ less.Parser = function Parser(env) {
483
724
  // block rule: at the root level.
484
725
  //
485
726
  primary: function () {
486
- var node, root = [];
487
-
488
- while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) ||
489
- $(this.mixin.call) || $(this.comment) || $(this.directive))
490
- || $(/^[\s\n]+/)) {
491
- node && root.push(node);
727
+ var mixin = this.mixin, $re = _$re, root = [], node;
728
+
729
+ while (current)
730
+ {
731
+ node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() ||
732
+ mixin.call() || this.comment() || this.rulesetCall() || this.directive();
733
+ if (node) {
734
+ root.push(node);
735
+ } else {
736
+ if (!($re(/^[\s\n]+/) || $re(/^;+/))) {
737
+ break;
738
+ }
739
+ }
740
+ if (peekChar('}')) {
741
+ break;
742
+ }
492
743
  }
744
+
493
745
  return root;
494
746
  },
495
747
 
@@ -499,13 +751,29 @@ less.Parser = function Parser(env) {
499
751
  comment: function () {
500
752
  var comment;
501
753
 
502
- if (input.charAt(i) !== '/') return;
754
+ if (input.charAt(i) !== '/') { return; }
503
755
 
504
756
  if (input.charAt(i + 1) === '/') {
505
- return new(tree.Comment)($(/^\/\/.*/), true);
506
- } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) {
507
- return new(tree.Comment)(comment);
757
+ return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo);
758
+ }
759
+ comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/);
760
+ if (comment) {
761
+ return new(tree.Comment)(comment, false, i, env.currentFileInfo);
762
+ }
763
+ },
764
+
765
+ comments: function () {
766
+ var comment, comments = [];
767
+
768
+ while(true) {
769
+ comment = this.comment();
770
+ if (!comment) {
771
+ break;
772
+ }
773
+ comments.push(comment);
508
774
  }
775
+
776
+ return comments;
509
777
  },
510
778
 
511
779
  //
@@ -518,15 +786,16 @@ less.Parser = function Parser(env) {
518
786
  // "milky way" 'he\'s the one!'
519
787
  //
520
788
  quoted: function () {
521
- var str, j = i, e;
789
+ var str, j = i, e, index = i;
522
790
 
523
- if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
524
- if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return;
791
+ if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings
792
+ if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; }
525
793
 
526
- e && $('~');
794
+ if (e) { $char('~'); }
527
795
 
528
- if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
529
- return new(tree.Quoted)(str[0], str[1] || str[2], e);
796
+ str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/);
797
+ if (str) {
798
+ return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo);
530
799
  }
531
800
  },
532
801
 
@@ -538,13 +807,13 @@ less.Parser = function Parser(env) {
538
807
  keyword: function () {
539
808
  var k;
540
809
 
541
- if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) {
542
- if (tree.colors.hasOwnProperty(k)) {
543
- // detect named color
544
- return new(tree.Color)(tree.colors[k].slice(1));
545
- } else {
546
- return new(tree.Keyword)(k);
810
+ k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/);
811
+ if (k) {
812
+ var color = tree.Color.fromKeyword(k);
813
+ if (color) {
814
+ return color;
547
815
  }
816
+ return new(tree.Keyword)(k);
548
817
  }
549
818
  },
550
819
 
@@ -559,38 +828,56 @@ less.Parser = function Parser(env) {
559
828
  // The arguments are parsed with the `entities.arguments` parser.
560
829
  //
561
830
  call: function () {
562
- var name, args, index = i;
831
+ var name, nameLC, args, alpha_ret, index = i;
563
832
 
564
- if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return;
833
+ name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current);
834
+ if (!name) { return; }
565
835
 
566
- name = name[1].toLowerCase();
836
+ name = name[1];
837
+ nameLC = name.toLowerCase();
838
+ if (nameLC === 'url') {
839
+ return null;
840
+ }
567
841
 
568
- if (name === 'url') { return null }
569
- else { i += name.length }
842
+ i += name.length;
570
843
 
571
- if (name === 'alpha') { return $(this.alpha) }
844
+ if (nameLC === 'alpha') {
845
+ alpha_ret = parsers.alpha();
846
+ if(typeof alpha_ret !== 'undefined') {
847
+ return alpha_ret;
848
+ }
849
+ }
572
850
 
573
- $('('); // Parse the '(' and consume whitespace.
851
+ $char('('); // Parse the '(' and consume whitespace.
574
852
 
575
- args = $(this.entities.arguments);
853
+ args = this.arguments();
576
854
 
577
- if (! $(')')) return;
855
+ if (! $char(')')) {
856
+ return;
857
+ }
578
858
 
579
- if (name) { return new(tree.Call)(name, args, index, env.filename) }
859
+ if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); }
580
860
  },
581
861
  arguments: function () {
582
862
  var args = [], arg;
583
863
 
584
- while (arg = $(this.entities.assignment) || $(this.expression)) {
864
+ while (true) {
865
+ arg = this.assignment() || parsers.expression();
866
+ if (!arg) {
867
+ break;
868
+ }
585
869
  args.push(arg);
586
- if (! $(',')) { break }
870
+ if (! $char(',')) {
871
+ break;
872
+ }
587
873
  }
588
874
  return args;
589
875
  },
590
876
  literal: function () {
591
- return $(this.entities.dimension) ||
592
- $(this.entities.color) ||
593
- $(this.entities.quoted);
877
+ return this.dimension() ||
878
+ this.color() ||
879
+ this.quoted() ||
880
+ this.unicodeDescriptor();
594
881
  },
595
882
 
596
883
  // Assignments are argument entities for calls.
@@ -601,7 +888,15 @@ less.Parser = function Parser(env) {
601
888
 
602
889
  assignment: function () {
603
890
  var key, value;
604
- if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) {
891
+ key = $re(/^\w+(?=\s?=)/i);
892
+ if (!key) {
893
+ return;
894
+ }
895
+ if (!$char('=')) {
896
+ return;
897
+ }
898
+ value = parsers.entity();
899
+ if (value) {
605
900
  return new(tree.Assignment)(key, value);
606
901
  }
607
902
  },
@@ -616,28 +911,17 @@ less.Parser = function Parser(env) {
616
911
  url: function () {
617
912
  var value;
618
913
 
619
- if (input.charAt(i) !== 'u' || !$(/^url\(/)) return;
620
- value = $(this.entities.quoted) || $(this.entities.variable) ||
621
- $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || "";
622
-
623
- expect(')');
624
-
625
- return new(tree.URL)((value.value || value.data || value instanceof tree.Variable)
626
- ? value : new(tree.Anonymous)(value), imports.paths);
627
- },
914
+ if (input.charAt(i) !== 'u' || !$re(/^url\(/)) {
915
+ return;
916
+ }
628
917
 
629
- dataURI: function () {
630
- var obj;
918
+ value = this.quoted() || this.variable() ||
919
+ $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
631
920
 
632
- if ($(/^data:/)) {
633
- obj = {};
634
- obj.mime = $(/^[^\/]+\/[^,;)]+/) || '';
635
- obj.charset = $(/^;\s*charset=[^,;)]+/) || '';
636
- obj.base64 = $(/^;\s*base64/) || '';
637
- obj.data = $(/^,\s*[^)]+/);
921
+ expectChar(')');
638
922
 
639
- if (obj.data) { return obj }
640
- }
923
+ return new(tree.URL)((value.value != null || value instanceof tree.Variable)
924
+ ? value : new(tree.Anonymous)(value), env.currentFileInfo);
641
925
  },
642
926
 
643
927
  //
@@ -651,8 +935,17 @@ less.Parser = function Parser(env) {
651
935
  variable: function () {
652
936
  var name, index = i;
653
937
 
654
- if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) {
655
- return new(tree.Variable)(name, index, env.filename);
938
+ if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) {
939
+ return new(tree.Variable)(name, index, env.currentFileInfo);
940
+ }
941
+ },
942
+
943
+ // A variable entity useing the protective {} e.g. @{var}
944
+ variableCurly: function () {
945
+ var curly, index = i;
946
+
947
+ if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) {
948
+ return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo);
656
949
  }
657
950
  },
658
951
 
@@ -666,7 +959,7 @@ less.Parser = function Parser(env) {
666
959
  color: function () {
667
960
  var rgb;
668
961
 
669
- if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) {
962
+ if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
670
963
  return new(tree.Color)(rgb[1]);
671
964
  }
672
965
  },
@@ -678,13 +971,31 @@ less.Parser = function Parser(env) {
678
971
  //
679
972
  dimension: function () {
680
973
  var value, c = input.charCodeAt(i);
681
- if ((c > 57 || c < 45) || c === 47) return;
974
+ //Is the first char of the dimension 0-9, '.', '+' or '-'
975
+ if ((c > 57 || c < 43) || c === 47 || c == 44) {
976
+ return;
977
+ }
682
978
 
683
- if (value = $(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) {
979
+ value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/);
980
+ if (value) {
684
981
  return new(tree.Dimension)(value[1], value[2]);
685
982
  }
686
983
  },
687
984
 
985
+ //
986
+ // A unicode descriptor, as is used in unicode-range
987
+ //
988
+ // U+0?? or U+00A1-00A9
989
+ //
990
+ unicodeDescriptor: function () {
991
+ var ud;
992
+
993
+ ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/);
994
+ if (ud) {
995
+ return new(tree.UnicodeDescriptor)(ud[0]);
996
+ }
997
+ },
998
+
688
999
  //
689
1000
  // JavaScript code to be evaluated
690
1001
  //
@@ -693,12 +1004,16 @@ less.Parser = function Parser(env) {
693
1004
  javascript: function () {
694
1005
  var str, j = i, e;
695
1006
 
696
- if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
697
- if (input.charAt(j) !== '`') { return }
1007
+ if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings
1008
+ if (input.charAt(j) !== '`') { return; }
1009
+ if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) {
1010
+ error("You are using JavaScript, which has been disabled.");
1011
+ }
698
1012
 
699
- e && $('~');
1013
+ if (e) { $char('~'); }
700
1014
 
701
- if (str = $(/^`([^`]*)`/)) {
1015
+ str = $re(/^`([^`]*)`/);
1016
+ if (str) {
702
1017
  return new(tree.JavaScript)(str[1], i, e);
703
1018
  }
704
1019
  }
@@ -712,26 +1027,62 @@ less.Parser = function Parser(env) {
712
1027
  variable: function () {
713
1028
  var name;
714
1029
 
715
- if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] }
1030
+ if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; }
716
1031
  },
717
1032
 
718
1033
  //
719
- // A font size/line-height shorthand
1034
+ // The variable part of a variable definition. Used in the `rule` parser
1035
+ //
1036
+ // @fink();
720
1037
  //
721
- // small/12px
1038
+ rulesetCall: function () {
1039
+ var name;
1040
+
1041
+ if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
1042
+ return new tree.RulesetCall(name[1]);
1043
+ }
1044
+ },
1045
+
722
1046
  //
723
- // We need to peek first, or we'll match on keywords and dimensions
1047
+ // extend syntax - used to extend selectors
724
1048
  //
725
- shorthand: function () {
726
- var a, b;
1049
+ extend: function(isRule) {
1050
+ var elements, e, index = i, option, extendList, extend;
1051
+
1052
+ if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; }
1053
+
1054
+ do {
1055
+ option = null;
1056
+ elements = null;
1057
+ while (! (option = $re(/^(all)(?=\s*(\)|,))/))) {
1058
+ e = this.element();
1059
+ if (!e) { break; }
1060
+ if (elements) { elements.push(e); } else { elements = [ e ]; }
1061
+ }
1062
+
1063
+ option = option && option[1];
727
1064
 
728
- if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return;
1065
+ extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
1066
+ if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
729
1067
 
730
- if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) {
731
- return new(tree.Shorthand)(a, b);
1068
+ } while($char(","));
1069
+
1070
+ expect(/^\)/);
1071
+
1072
+ if (isRule) {
1073
+ expect(/^;/);
732
1074
  }
1075
+
1076
+ return extendList;
733
1077
  },
734
1078
 
1079
+ //
1080
+ // extendRule - used in a rule to extend all the parent selectors
1081
+ //
1082
+ extendRule: function() {
1083
+ return this.extend(true);
1084
+ },
1085
+
735
1086
  //
736
1087
  // Mixins
737
1088
  //
@@ -748,25 +1099,158 @@ less.Parser = function Parser(env) {
748
1099
  // selector for now.
749
1100
  //
750
1101
  call: function () {
751
- var elements = [], e, c, args, index = i, s = input.charAt(i), important = false;
1102
+ var s = input.charAt(i), important = false, index = i, elemIndex,
1103
+ elements, elem, e, c, args;
752
1104
 
753
- if (s !== '.' && s !== '#') { return }
1105
+ if (s !== '.' && s !== '#') { return; }
754
1106
 
755
- while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) {
756
- elements.push(new(tree.Element)(c, e, i));
757
- c = $('>');
758
- }
759
- $('(') && (args = $(this.entities.arguments)) && $(')');
1107
+ save(); // stop us absorbing part of an invalid selector
760
1108
 
761
- if ($(this.important)) {
762
- important = true;
1109
+ while (true) {
1110
+ elemIndex = i;
1111
+ e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);
1112
+ if (!e) {
1113
+ break;
1114
+ }
1115
+ elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo);
1116
+ if (elements) { elements.push(elem); } else { elements = [ elem ]; }
1117
+ c = $char('>');
763
1118
  }
764
1119
 
765
- if (elements.length > 0 && ($(';') || peek('}'))) {
766
- return new(tree.mixin.Call)(elements, args || [], index, env.filename, important);
1120
+ if (elements) {
1121
+ if ($char('(')) {
1122
+ args = this.args(true).args;
1123
+ expectChar(')');
1124
+ }
1125
+
1126
+ if (parsers.important()) {
1127
+ important = true;
1128
+ }
1129
+
1130
+ if (parsers.end()) {
1131
+ forget();
1132
+ return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important);
1133
+ }
767
1134
  }
1135
+
1136
+ restore();
768
1137
  },
1138
+ args: function (isCall) {
1139
+ var parsers = parser.parsers, entities = parsers.entities,
1140
+ returner = { args:null, variadic: false },
1141
+ expressions = [], argsSemiColon = [], argsComma = [],
1142
+ isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg;
769
1143
 
1144
+ save();
1145
+
1146
+ while (true) {
1147
+ if (isCall) {
1148
+ arg = parsers.detachedRuleset() || parsers.expression();
1149
+ } else {
1150
+ parsers.comments();
1151
+ if (input.charAt(i) === '.' && $re(/^\.{3}/)) {
1152
+ returner.variadic = true;
1153
+ if ($char(";") && !isSemiColonSeperated) {
1154
+ isSemiColonSeperated = true;
1155
+ }
1156
+ (isSemiColonSeperated ? argsSemiColon : argsComma)
1157
+ .push({ variadic: true });
1158
+ break;
1159
+ }
1160
+ arg = entities.variable() || entities.literal() || entities.keyword();
1161
+ }
1162
+
1163
+ if (!arg) {
1164
+ break;
1165
+ }
1166
+
1167
+ nameLoop = null;
1168
+ if (arg.throwAwayComments) {
1169
+ arg.throwAwayComments();
1170
+ }
1171
+ value = arg;
1172
+ var val = null;
1173
+
1174
+ if (isCall) {
1175
+ // Variable
1176
+ if (arg.value && arg.value.length == 1) {
1177
+ val = arg.value[0];
1178
+ }
1179
+ } else {
1180
+ val = arg;
1181
+ }
1182
+
1183
+ if (val && val instanceof tree.Variable) {
1184
+ if ($char(':')) {
1185
+ if (expressions.length > 0) {
1186
+ if (isSemiColonSeperated) {
1187
+ error("Cannot mix ; and , as delimiter types");
1188
+ }
1189
+ expressionContainsNamed = true;
1190
+ }
1191
+
1192
+ // we do not support setting a ruleset as a default variable - it doesn't make sense
1193
+ // However if we do want to add it, there is nothing blocking it, just don't error
1194
+ // and remove isCall dependency below
1195
+ value = (isCall && parsers.detachedRuleset()) || parsers.expression();
1196
+
1197
+ if (!value) {
1198
+ if (isCall) {
1199
+ error("could not understand value for named argument");
1200
+ } else {
1201
+ restore();
1202
+ returner.args = [];
1203
+ return returner;
1204
+ }
1205
+ }
1206
+ nameLoop = (name = val.name);
1207
+ } else if (!isCall && $re(/^\.{3}/)) {
1208
+ returner.variadic = true;
1209
+ if ($char(";") && !isSemiColonSeperated) {
1210
+ isSemiColonSeperated = true;
1211
+ }
1212
+ (isSemiColonSeperated ? argsSemiColon : argsComma)
1213
+ .push({ name: arg.name, variadic: true });
1214
+ break;
1215
+ } else if (!isCall) {
1216
+ name = nameLoop = val.name;
1217
+ value = null;
1218
+ }
1219
+ }
1220
+
1221
+ if (value) {
1222
+ expressions.push(value);
1223
+ }
1224
+
1225
+ argsComma.push({ name:nameLoop, value:value });
1226
+
1227
+ if ($char(',')) {
1228
+ continue;
1229
+ }
1230
+
1231
+ if ($char(';') || isSemiColonSeperated) {
1232
+
1233
+ if (expressionContainsNamed) {
1234
+ error("Cannot mix ; and , as delimiter types");
1235
+ }
1236
+
1237
+ isSemiColonSeperated = true;
1238
+
1239
+ if (expressions.length > 1) {
1240
+ value = new(tree.Value)(expressions);
1241
+ }
1242
+ argsSemiColon.push({ name:name, value:value });
1243
+
1244
+ name = null;
1245
+ expressions = [];
1246
+ expressionContainsNamed = false;
1247
+ }
1248
+ }
1249
+
1250
+ forget();
1251
+ returner.args = isSemiColonSeperated ? argsSemiColon : argsComma;
1252
+ return returner;
1253
+ },
770
1254
  //
771
1255
  // A Mixin definition, with a list of parameters
772
1256
  //
@@ -787,54 +1271,49 @@ less.Parser = function Parser(env) {
787
1271
  // the `{...}` block.
788
1272
  //
789
1273
  definition: function () {
790
- var name, params = [], match, ruleset, param, value, cond, variadic = false;
1274
+ var name, params = [], match, ruleset, cond, variadic = false;
791
1275
  if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') ||
792
- peek(/^[^{]*(;|})/)) return;
1276
+ peek(/^[^{]*\}/)) {
1277
+ return;
1278
+ }
793
1279
 
794
1280
  save();
795
1281
 
796
- if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) {
1282
+ match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/);
1283
+ if (match) {
797
1284
  name = match[1];
798
1285
 
799
- do {
800
- if (input.charAt(i) === '.' && $(/^\.{3}/)) {
801
- variadic = true;
802
- break;
803
- } else if (param = $(this.entities.variable) || $(this.entities.literal)
804
- || $(this.entities.keyword)) {
805
- // Variable
806
- if (param instanceof tree.Variable) {
807
- if ($(':')) {
808
- value = expect(this.expression, 'expected expression');
809
- params.push({ name: param.name, value: value });
810
- } else if ($(/^\.{3}/)) {
811
- params.push({ name: param.name, variadic: true });
812
- variadic = true;
813
- break;
814
- } else {
815
- params.push({ name: param.name });
816
- }
817
- } else {
818
- params.push({ value: param });
819
- }
820
- } else {
821
- break;
822
- }
823
- } while ($(','))
824
-
825
- expect(')');
1286
+ var argInfo = this.args(false);
1287
+ params = argInfo.args;
1288
+ variadic = argInfo.variadic;
1289
+
1290
+ // .mixincall("@{a}");
1291
+ // looks a bit like a mixin definition..
1292
+ // also
1293
+ // .mixincall(@a: {rule: set;});
1294
+ // so we have to be nice and restore
1295
+ if (!$char(')')) {
1296
+ furthest = i;
1297
+ restore();
1298
+ return;
1299
+ }
1300
+
1301
+ parsers.comments();
826
1302
 
827
- if ($(/^when/)) { // Guard
828
- cond = expect(this.conditions, 'expected condition');
1303
+ if ($re(/^when/)) { // Guard
1304
+ cond = expect(parsers.conditions, 'expected condition');
829
1305
  }
830
1306
 
831
- ruleset = $(this.block);
1307
+ ruleset = parsers.block();
832
1308
 
833
1309
  if (ruleset) {
1310
+ forget();
834
1311
  return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
835
1312
  } else {
836
1313
  restore();
837
1314
  }
1315
+ } else {
1316
+ forget();
838
1317
  }
839
1318
  }
840
1319
  },
@@ -844,9 +1323,11 @@ less.Parser = function Parser(env) {
844
1323
  // and can be found inside a rule's value.
845
1324
  //
846
1325
  entity: function () {
847
- return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) ||
848
- $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) ||
849
- $(this.comment);
1326
+ var entities = this.entities;
1327
+
1328
+ return entities.literal() || entities.variable() || entities.url() ||
1329
+ entities.call() || entities.keyword() || entities.javascript() ||
1330
+ this.comment();
850
1331
  },
851
1332
 
852
1333
  //
@@ -855,7 +1336,7 @@ less.Parser = function Parser(env) {
855
1336
  // it's there, if ';' was ommitted.
856
1337
  //
857
1338
  end: function () {
858
- return $(';') || peek('}');
1339
+ return $char(';') || peekChar('}');
859
1340
  },
860
1341
 
861
1342
  //
@@ -866,9 +1347,10 @@ less.Parser = function Parser(env) {
866
1347
  alpha: function () {
867
1348
  var value;
868
1349
 
869
- if (! $(/^\(opacity=/i)) return;
870
- if (value = $(/^\d+/) || $(this.entities.variable)) {
871
- expect(')');
1350
+ if (! $re(/^\(opacity=/i)) { return; }
1351
+ value = $re(/^\d+/) || this.entities.variable();
1352
+ if (value) {
1353
+ expectChar(')');
872
1354
  return new(tree.Alpha)(value);
873
1355
  }
874
1356
  },
@@ -886,21 +1368,29 @@ less.Parser = function Parser(env) {
886
1368
  // and an element name, such as a tag a class, or `*`.
887
1369
  //
888
1370
  element: function () {
889
- var e, t, c, v;
1371
+ var e, c, v, index = i;
890
1372
 
891
- c = $(this.combinator);
892
- e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) ||
893
- $('*') || $(this.attribute) || $(/^\([^)@]+\)/);
1373
+ c = this.combinator();
1374
+
1375
+ e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
1376
+ $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) ||
1377
+ this.entities.variableCurly();
894
1378
 
895
1379
  if (! e) {
896
- $('(') && (v = $(this.entities.variable)) && $(')') && (e = new(tree.Paren)(v));
1380
+ save();
1381
+ if ($char('(')) {
1382
+ if ((v = this.selector()) && $char(')')) {
1383
+ e = new(tree.Paren)(v);
1384
+ forget();
1385
+ } else {
1386
+ restore();
1387
+ }
1388
+ } else {
1389
+ forget();
1390
+ }
897
1391
  }
898
1392
 
899
- if (e) { return new(tree.Element)(c, e, i) }
900
-
901
- if (c.value && c.value.charAt(0) === '&') {
902
- return new(tree.Element)(c, null, i);
903
- }
1393
+ if (e) { return new(tree.Element)(c, e, index, env.currentFileInfo); }
904
1394
  },
905
1395
 
906
1396
  //
@@ -913,27 +1403,29 @@ less.Parser = function Parser(env) {
913
1403
  // we deal with this in *combinator.js*.
914
1404
  //
915
1405
  combinator: function () {
916
- var match, c = input.charAt(i);
917
-
918
- if (c === '>' || c === '+' || c === '~') {
919
- i++;
920
- while (input.charAt(i) === ' ') { i++ }
921
- return new(tree.Combinator)(c);
922
- } else if (c === '&') {
923
- match = '&';
1406
+ var c = input.charAt(i);
1407
+
1408
+ if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') {
924
1409
  i++;
925
- if(input.charAt(i) === ' ') {
926
- match = '& ';
1410
+ if (input.charAt(i) === '^') {
1411
+ c = '^^';
1412
+ i++;
927
1413
  }
928
- while (input.charAt(i) === ' ') { i++ }
929
- return new(tree.Combinator)(match);
930
- } else if (input.charAt(i - 1) === ' ') {
1414
+ while (isWhitespace(input, i)) { i++; }
1415
+ return new(tree.Combinator)(c);
1416
+ } else if (isWhitespace(input, i - 1)) {
931
1417
  return new(tree.Combinator)(" ");
932
1418
  } else {
933
1419
  return new(tree.Combinator)(null);
934
1420
  }
935
1421
  },
936
-
1422
+ //
1423
+ // A CSS selector (see selector below)
1424
+ // with less extensions e.g. the ability to extend and guard
1425
+ //
1426
+ lessSelector: function () {
1427
+ return this.selector(true);
1428
+ },
937
1429
  //
938
1430
  // A CSS Selector
939
1431
  //
@@ -942,41 +1434,48 @@ less.Parser = function Parser(env) {
942
1434
  //
943
1435
  // Selectors are made out of one or more Elements, see above.
944
1436
  //
945
- selector: function () {
946
- var sel, e, elements = [], c, match;
947
-
948
- if ($('(')) {
949
- sel = $(this.entity);
950
- expect(')');
951
- return new(tree.Selector)([new(tree.Element)('', sel, i)]);
952
- }
953
-
954
- while (e = $(this.element)) {
955
- c = input.charAt(i);
956
- elements.push(e)
957
- if (c === '{' || c === '}' || c === ';' || c === ',') { break }
1437
+ selector: function (isLess) {
1438
+ var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition;
1439
+
1440
+ while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) {
1441
+ if (when) {
1442
+ condition = expect(this.conditions, 'expected condition');
1443
+ } else if (condition) {
1444
+ error("CSS guard can only be used at the end of selector");
1445
+ } else if (extend) {
1446
+ if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
1447
+ } else {
1448
+ if (extendList) { error("Extend can only be used at the end of selector"); }
1449
+ c = input.charAt(i);
1450
+ if (elements) { elements.push(e); } else { elements = [ e ]; }
1451
+ e = null;
1452
+ }
1453
+ if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') {
1454
+ break;
1455
+ }
958
1456
  }
959
1457
 
960
- if (elements.length > 0) { return new(tree.Selector)(elements) }
961
- },
962
- tag: function () {
963
- return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*');
1458
+ if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); }
1459
+ if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); }
964
1460
  },
965
1461
  attribute: function () {
966
- var attr = '', key, val, op;
1462
+ if (! $char('[')) { return; }
1463
+
1464
+ var entities = this.entities,
1465
+ key, val, op;
967
1466
 
968
- if (! $('[')) return;
1467
+ if (!(key = entities.variableCurly())) {
1468
+ key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/);
1469
+ }
969
1470
 
970
- if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) {
971
- if ((op = $(/^[|~*$^]?=/)) &&
972
- (val = $(this.entities.quoted) || $(/^[\w-]+/))) {
973
- attr = [key, op, val.toCSS ? val.toCSS() : val].join('');
974
- } else { attr = key }
1471
+ op = $re(/^[|~*$^]?=/);
1472
+ if (op) {
1473
+ val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly();
975
1474
  }
976
1475
 
977
- if (! $(']')) return;
1476
+ expectChar(']');
978
1477
 
979
- if (attr) { return "[" + attr + "]" }
1478
+ return new(tree.Attribute)(key, op, val);
980
1479
  },
981
1480
 
982
1481
  //
@@ -985,57 +1484,119 @@ less.Parser = function Parser(env) {
985
1484
  //
986
1485
  block: function () {
987
1486
  var content;
988
-
989
- if ($('{') && (content = $(this.primary)) && $('}')) {
1487
+ if ($char('{') && (content = this.primary()) && $char('}')) {
990
1488
  return content;
991
1489
  }
992
1490
  },
993
1491
 
1492
+ blockRuleset: function() {
1493
+ var block = this.block();
1494
+
1495
+ if (block) {
1496
+ block = new tree.Ruleset(null, block);
1497
+ }
1498
+ return block;
1499
+ },
1500
+
1501
+ detachedRuleset: function() {
1502
+ var blockRuleset = this.blockRuleset();
1503
+ if (blockRuleset) {
1504
+ return new tree.DetachedRuleset(blockRuleset);
1505
+ }
1506
+ },
1507
+
994
1508
  //
995
1509
  // div, .class, body > p {...}
996
1510
  //
997
1511
  ruleset: function () {
998
- var selectors = [], s, rules, match;
1512
+ var selectors, s, rules, debugInfo;
1513
+
999
1514
  save();
1000
1515
 
1001
- while (s = $(this.selector)) {
1002
- selectors.push(s);
1003
- $(this.comment);
1004
- if (! $(',')) { break }
1005
- $(this.comment);
1516
+ if (env.dumpLineNumbers) {
1517
+ debugInfo = getDebugInfo(i, input, env);
1518
+ }
1519
+
1520
+ while (true) {
1521
+ s = this.lessSelector();
1522
+ if (!s) {
1523
+ break;
1524
+ }
1525
+ if (selectors) { selectors.push(s); } else { selectors = [ s ]; }
1526
+ this.comments();
1527
+ if (s.condition && selectors.length > 1) {
1528
+ error("Guards are only currently allowed on a single selector.");
1529
+ }
1530
+ if (! $char(',')) { break; }
1531
+ if (s.condition) {
1532
+ error("Guards are only currently allowed on a single selector.");
1533
+ }
1534
+ this.comments();
1006
1535
  }
1007
1536
 
1008
- if (selectors.length > 0 && (rules = $(this.block))) {
1009
- return new(tree.Ruleset)(selectors, rules, env.strictImports);
1537
+ if (selectors && (rules = this.block())) {
1538
+ forget();
1539
+ var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports);
1540
+ if (env.dumpLineNumbers) {
1541
+ ruleset.debugInfo = debugInfo;
1542
+ }
1543
+ return ruleset;
1010
1544
  } else {
1011
1545
  // Backtrack
1012
1546
  furthest = i;
1013
1547
  restore();
1014
1548
  }
1015
1549
  },
1016
- rule: function () {
1017
- var name, value, c = input.charAt(i), important, match;
1018
- save();
1550
+ rule: function (tryAnonymous) {
1551
+ var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable;
1019
1552
 
1020
- if (c === '.' || c === '#' || c === '&') { return }
1553
+ if (c === '.' || c === '#' || c === '&') { return; }
1021
1554
 
1022
- if (name = $(this.variable) || $(this.property)) {
1023
- if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) {
1024
- i += match[0].length - 1;
1025
- value = new(tree.Anonymous)(match[1]);
1026
- } else if (name === "font") {
1027
- value = $(this.font);
1028
- } else {
1029
- value = $(this.value);
1555
+ save();
1556
+
1557
+ name = this.variable() || this.ruleProperty();
1558
+ if (name) {
1559
+ isVariable = typeof name === "string";
1560
+
1561
+ if (isVariable) {
1562
+ value = this.detachedRuleset();
1563
+ }
1564
+
1565
+ if (!value) {
1566
+ // prefer to try to parse first if its a variable or we are compressing
1567
+ // but always fallback on the other one
1568
+ value = !tryAnonymous && (env.compress || isVariable) ?
1569
+ (this.value() || this.anonymousValue()) :
1570
+ (this.anonymousValue() || this.value());
1571
+
1572
+ important = this.important();
1573
+
1574
+ // a name returned by this.ruleProperty() is always an array of the form:
1575
+ // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
1576
+ // where each item is a tree.Keyword or tree.Variable
1577
+ merge = !isVariable && name.pop().value;
1030
1578
  }
1031
- important = $(this.important);
1032
1579
 
1033
- if (value && $(this.end)) {
1034
- return new(tree.Rule)(name, value, important, memo);
1580
+ if (value && this.end()) {
1581
+ forget();
1582
+ return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo);
1035
1583
  } else {
1036
1584
  furthest = i;
1037
1585
  restore();
1586
+ if (value && !tryAnonymous) {
1587
+ return this.rule(true);
1588
+ }
1038
1589
  }
1590
+ } else {
1591
+ forget();
1592
+ }
1593
+ },
1594
+ anonymousValue: function () {
1595
+ var match;
1596
+ match = /^([^@+\/'"*`(;{}-]*);/.exec(current);
1597
+ if (match) {
1598
+ i += match[0].length - 1;
1599
+ return new(tree.Anonymous)(match[1]);
1039
1600
  }
1040
1601
  },
1041
1602
 
@@ -1051,33 +1612,78 @@ less.Parser = function Parser(env) {
1051
1612
  //
1052
1613
  "import": function () {
1053
1614
  var path, features, index = i;
1054
- if ($(/^@import\s+/) &&
1055
- (path = $(this.entities.quoted) || $(this.entities.url))) {
1056
- features = $(this.mediaFeatures);
1057
- if ($(';')) {
1058
- return new(tree.Import)(path, imports, features, index);
1615
+
1616
+ save();
1617
+
1618
+ var dir = $re(/^@import?\s+/);
1619
+
1620
+ var options = (dir ? this.importOptions() : null) || {};
1621
+
1622
+ if (dir && (path = this.entities.quoted() || this.entities.url())) {
1623
+ features = this.mediaFeatures();
1624
+ if ($char(';')) {
1625
+ forget();
1626
+ features = features && new(tree.Value)(features);
1627
+ return new(tree.Import)(path, features, options, index, env.currentFileInfo);
1059
1628
  }
1060
1629
  }
1630
+
1631
+ restore();
1061
1632
  },
1062
1633
 
1063
- mediaFeature: function () {
1064
- var e, p, nodes = [];
1634
+ importOptions: function() {
1635
+ var o, options = {}, optionName, value;
1636
+
1637
+ // list of options, surrounded by parens
1638
+ if (! $char('(')) { return null; }
1639
+ do {
1640
+ o = this.importOption();
1641
+ if (o) {
1642
+ optionName = o;
1643
+ value = true;
1644
+ switch(optionName) {
1645
+ case "css":
1646
+ optionName = "less";
1647
+ value = false;
1648
+ break;
1649
+ case "once":
1650
+ optionName = "multiple";
1651
+ value = false;
1652
+ break;
1653
+ }
1654
+ options[optionName] = value;
1655
+ if (! $char(',')) { break; }
1656
+ }
1657
+ } while (o);
1658
+ expectChar(')');
1659
+ return options;
1660
+ },
1661
+
1662
+ importOption: function() {
1663
+ var opt = $re(/^(less|css|multiple|once|inline|reference)/);
1664
+ if (opt) {
1665
+ return opt[1];
1666
+ }
1667
+ },
1065
1668
 
1669
+ mediaFeature: function () {
1670
+ var entities = this.entities, nodes = [], e, p;
1066
1671
  do {
1067
- if (e = $(this.entities.keyword)) {
1672
+ e = entities.keyword() || entities.variable();
1673
+ if (e) {
1068
1674
  nodes.push(e);
1069
- } else if ($('(')) {
1070
- p = $(this.property);
1071
- e = $(this.entity);
1072
- if ($(')')) {
1675
+ } else if ($char('(')) {
1676
+ p = this.property();
1677
+ e = this.value();
1678
+ if ($char(')')) {
1073
1679
  if (p && e) {
1074
- nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true)));
1680
+ nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true)));
1075
1681
  } else if (e) {
1076
1682
  nodes.push(new(tree.Paren)(e));
1077
1683
  } else {
1078
1684
  return null;
1079
1685
  }
1080
- } else { return null }
1686
+ } else { return null; }
1081
1687
  }
1082
1688
  } while (e);
1083
1689
 
@@ -1087,29 +1693,41 @@ less.Parser = function Parser(env) {
1087
1693
  },
1088
1694
 
1089
1695
  mediaFeatures: function () {
1090
- var e, features = [];
1091
-
1696
+ var entities = this.entities, features = [], e;
1092
1697
  do {
1093
- if (e = $(this.mediaFeature)) {
1094
- features.push(e);
1095
- if (! $(',')) { break }
1096
- } else if (e = $(this.entities.variable)) {
1097
- features.push(e);
1098
- if (! $(',')) { break }
1099
- }
1698
+ e = this.mediaFeature();
1699
+ if (e) {
1700
+ features.push(e);
1701
+ if (! $char(',')) { break; }
1702
+ } else {
1703
+ e = entities.variable();
1704
+ if (e) {
1705
+ features.push(e);
1706
+ if (! $char(',')) { break; }
1707
+ }
1708
+ }
1100
1709
  } while (e);
1101
-
1710
+
1102
1711
  return features.length > 0 ? features : null;
1103
1712
  },
1104
1713
 
1105
1714
  media: function () {
1106
- var features, rules;
1715
+ var features, rules, media, debugInfo;
1107
1716
 
1108
- if ($(/^@media/)) {
1109
- features = $(this.mediaFeatures);
1717
+ if (env.dumpLineNumbers) {
1718
+ debugInfo = getDebugInfo(i, input, env);
1719
+ }
1720
+
1721
+ if ($re(/^@media/)) {
1722
+ features = this.mediaFeatures();
1110
1723
 
1111
- if (rules = $(this.block)) {
1112
- return new(tree.Media)(rules, features);
1724
+ rules = this.block();
1725
+ if (rules) {
1726
+ media = new(tree.Media)(rules, features, i, env.currentFileInfo);
1727
+ if (env.dumpLineNumbers) {
1728
+ media.debugInfo = debugInfo;
1729
+ }
1730
+ return media;
1113
1731
  }
1114
1732
  }
1115
1733
  },
@@ -1120,42 +1738,97 @@ less.Parser = function Parser(env) {
1120
1738
  // @charset "utf-8";
1121
1739
  //
1122
1740
  directive: function () {
1123
- var name, value, rules, types, e, nodes;
1741
+ var index = i, name, value, rules, nonVendorSpecificName,
1742
+ hasIdentifier, hasExpression, hasUnknown, hasBlock = true;
1124
1743
 
1125
- if (input.charAt(i) !== '@') return;
1744
+ if (input.charAt(i) !== '@') { return; }
1126
1745
 
1127
- if (value = $(this['import']) || $(this.media)) {
1746
+ value = this['import']() || this.media();
1747
+ if (value) {
1128
1748
  return value;
1129
- } else if (name = $(/^@page|@keyframes/) || $(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)) {
1130
- types = ($(/^[^{]+/) || '').trim();
1131
- if (rules = $(this.block)) {
1132
- return new(tree.Directive)(name + " " + types, rules);
1133
- }
1134
- } else if (name = $(/^@[-a-z]+/)) {
1135
- if (name === '@font-face') {
1136
- if (rules = $(this.block)) {
1137
- return new(tree.Directive)(name, rules);
1138
- }
1139
- } else if ((value = $(this.entity)) && $(';')) {
1140
- return new(tree.Directive)(name, value);
1141
- }
1142
1749
  }
1143
- },
1144
- font: function () {
1145
- var value = [], expression = [], weight, shorthand, font, e;
1146
1750
 
1147
- while (e = $(this.shorthand) || $(this.entity)) {
1148
- expression.push(e);
1751
+ save();
1752
+
1753
+ name = $re(/^@[a-z-]+/);
1754
+
1755
+ if (!name) { return; }
1756
+
1757
+ nonVendorSpecificName = name;
1758
+ if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
1759
+ nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
1760
+ }
1761
+
1762
+ switch(nonVendorSpecificName) {
1763
+ /*
1764
+ case "@font-face":
1765
+ case "@viewport":
1766
+ case "@top-left":
1767
+ case "@top-left-corner":
1768
+ case "@top-center":
1769
+ case "@top-right":
1770
+ case "@top-right-corner":
1771
+ case "@bottom-left":
1772
+ case "@bottom-left-corner":
1773
+ case "@bottom-center":
1774
+ case "@bottom-right":
1775
+ case "@bottom-right-corner":
1776
+ case "@left-top":
1777
+ case "@left-middle":
1778
+ case "@left-bottom":
1779
+ case "@right-top":
1780
+ case "@right-middle":
1781
+ case "@right-bottom":
1782
+ hasBlock = true;
1783
+ break;
1784
+ */
1785
+ case "@charset":
1786
+ hasIdentifier = true;
1787
+ hasBlock = false;
1788
+ break;
1789
+ case "@namespace":
1790
+ hasExpression = true;
1791
+ hasBlock = false;
1792
+ break;
1793
+ case "@keyframes":
1794
+ hasIdentifier = true;
1795
+ break;
1796
+ case "@host":
1797
+ case "@page":
1798
+ case "@document":
1799
+ case "@supports":
1800
+ hasUnknown = true;
1801
+ break;
1149
1802
  }
1150
- value.push(new(tree.Expression)(expression));
1151
1803
 
1152
- if ($(',')) {
1153
- while (e = $(this.expression)) {
1154
- value.push(e);
1155
- if (! $(',')) { break }
1804
+ if (hasIdentifier) {
1805
+ value = this.entity();
1806
+ if (!value) {
1807
+ error("expected " + name + " identifier");
1156
1808
  }
1809
+ } else if (hasExpression) {
1810
+ value = this.expression();
1811
+ if (!value) {
1812
+ error("expected " + name + " expression");
1813
+ }
1814
+ } else if (hasUnknown) {
1815
+ value = ($re(/^[^{;]+/) || '').trim();
1816
+ if (value) {
1817
+ value = new(tree.Anonymous)(value);
1818
+ }
1819
+ }
1820
+
1821
+ if (hasBlock) {
1822
+ rules = this.blockRuleset();
1823
+ }
1824
+
1825
+ if (rules || (!hasBlock && value && $char(';'))) {
1826
+ forget();
1827
+ return new(tree.Directive)(name, value, rules, index, env.currentFileInfo,
1828
+ env.dumpLineNumbers ? getDebugInfo(index, input, env) : null);
1157
1829
  }
1158
- return new(tree.Value)(value);
1830
+
1831
+ restore();
1159
1832
  },
1160
1833
 
1161
1834
  //
@@ -1167,12 +1840,15 @@ less.Parser = function Parser(env) {
1167
1840
  // and before the `;`.
1168
1841
  //
1169
1842
  value: function () {
1170
- var e, expressions = [], important;
1843
+ var e, expressions = [];
1171
1844
 
1172
- while (e = $(this.expression)) {
1173
- expressions.push(e);
1174
- if (! $(',')) { break }
1175
- }
1845
+ do {
1846
+ e = this.expression();
1847
+ if (e) {
1848
+ expressions.push(e);
1849
+ if (! $char(',')) { break; }
1850
+ }
1851
+ } while(e);
1176
1852
 
1177
1853
  if (expressions.length > 0) {
1178
1854
  return new(tree.Value)(expressions);
@@ -1180,31 +1856,66 @@ less.Parser = function Parser(env) {
1180
1856
  },
1181
1857
  important: function () {
1182
1858
  if (input.charAt(i) === '!') {
1183
- return $(/^! *important/);
1859
+ return $re(/^! *important/);
1184
1860
  }
1185
1861
  },
1186
1862
  sub: function () {
1187
- var e;
1188
-
1189
- if ($('(') && (e = $(this.expression)) && $(')')) {
1190
- return e;
1863
+ var a, e;
1864
+
1865
+ if ($char('(')) {
1866
+ a = this.addition();
1867
+ if (a) {
1868
+ e = new(tree.Expression)([a]);
1869
+ expectChar(')');
1870
+ e.parens = true;
1871
+ return e;
1872
+ }
1191
1873
  }
1192
1874
  },
1193
1875
  multiplication: function () {
1194
- var m, a, op, operation;
1195
- if (m = $(this.operand)) {
1196
- while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) {
1197
- operation = new(tree.Operation)(op, [operation || m, a]);
1876
+ var m, a, op, operation, isSpaced;
1877
+ m = this.operand();
1878
+ if (m) {
1879
+ isSpaced = isWhitespace(input, i - 1);
1880
+ while (true) {
1881
+ if (peek(/^\/[*\/]/)) {
1882
+ break;
1883
+ }
1884
+ op = $char('/') || $char('*');
1885
+
1886
+ if (!op) { break; }
1887
+
1888
+ a = this.operand();
1889
+
1890
+ if (!a) { break; }
1891
+
1892
+ m.parensInOp = true;
1893
+ a.parensInOp = true;
1894
+ operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
1895
+ isSpaced = isWhitespace(input, i - 1);
1198
1896
  }
1199
1897
  return operation || m;
1200
1898
  }
1201
1899
  },
1202
1900
  addition: function () {
1203
- var m, a, op, operation;
1204
- if (m = $(this.multiplication)) {
1205
- while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) &&
1206
- (a = $(this.multiplication))) {
1207
- operation = new(tree.Operation)(op, [operation || m, a]);
1901
+ var m, a, op, operation, isSpaced;
1902
+ m = this.multiplication();
1903
+ if (m) {
1904
+ isSpaced = isWhitespace(input, i - 1);
1905
+ while (true) {
1906
+ op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-')));
1907
+ if (!op) {
1908
+ break;
1909
+ }
1910
+ a = this.multiplication();
1911
+ if (!a) {
1912
+ break;
1913
+ }
1914
+
1915
+ m.parensInOp = true;
1916
+ a.parensInOp = true;
1917
+ operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
1918
+ isSpaced = isWhitespace(input, i - 1);
1208
1919
  }
1209
1920
  return operation || m;
1210
1921
  }
@@ -1212,21 +1923,33 @@ less.Parser = function Parser(env) {
1212
1923
  conditions: function () {
1213
1924
  var a, b, index = i, condition;
1214
1925
 
1215
- if (a = $(this.condition)) {
1216
- while ($(',') && (b = $(this.condition))) {
1926
+ a = this.condition();
1927
+ if (a) {
1928
+ while (true) {
1929
+ if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) {
1930
+ break;
1931
+ }
1932
+ b = this.condition();
1933
+ if (!b) {
1934
+ break;
1935
+ }
1217
1936
  condition = new(tree.Condition)('or', condition || a, b, index);
1218
1937
  }
1219
1938
  return condition || a;
1220
1939
  }
1221
1940
  },
1222
1941
  condition: function () {
1223
- var a, b, c, op, index = i, negate = false;
1224
-
1225
- if ($(/^not/)) { negate = true }
1226
- expect('(');
1227
- if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) {
1228
- if (op = $(/^(?:>=|=<|[<=>])/)) {
1229
- if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) {
1942
+ var entities = this.entities, index = i, negate = false,
1943
+ a, b, c, op;
1944
+
1945
+ if ($re(/^not/)) { negate = true; }
1946
+ expectChar('(');
1947
+ a = this.addition() || entities.keyword() || entities.quoted();
1948
+ if (a) {
1949
+ op = $re(/^(?:>=|<=|=<|[<=>])/);
1950
+ if (op) {
1951
+ b = this.addition() || entities.keyword() || entities.quoted();
1952
+ if (b) {
1230
1953
  c = new(tree.Condition)(op, a, b, index, negate);
1231
1954
  } else {
1232
1955
  error('expected expression');
@@ -1234,8 +1957,8 @@ less.Parser = function Parser(env) {
1234
1957
  } else {
1235
1958
  c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);
1236
1959
  }
1237
- expect(')');
1238
- return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c;
1960
+ expectChar(')');
1961
+ return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c;
1239
1962
  }
1240
1963
  },
1241
1964
 
@@ -1244,14 +1967,20 @@ less.Parser = function Parser(env) {
1244
1967
  // such as a Color, or a Variable
1245
1968
  //
1246
1969
  operand: function () {
1247
- var negate, p = input.charAt(i + 1);
1248
-
1249
- if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') }
1250
- var o = $(this.sub) || $(this.entities.dimension) ||
1251
- $(this.entities.color) || $(this.entities.variable) ||
1252
- $(this.entities.call);
1253
- return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o])
1254
- : o;
1970
+ var entities = this.entities,
1971
+ p = input.charAt(i + 1), negate;
1972
+
1973
+ if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); }
1974
+ var o = this.sub() || entities.dimension() ||
1975
+ entities.color() || entities.variable() ||
1976
+ entities.call();
1977
+
1978
+ if (negate) {
1979
+ o.parensInOp = true;
1980
+ o = new(tree.Negative)(o);
1981
+ }
1982
+
1983
+ return o;
1255
1984
  },
1256
1985
 
1257
1986
  //
@@ -1262,44 +1991,78 @@ less.Parser = function Parser(env) {
1262
1991
  // @var * 2
1263
1992
  //
1264
1993
  expression: function () {
1265
- var e, delim, entities = [], d;
1994
+ var entities = [], e, delim;
1266
1995
 
1267
- while (e = $(this.addition) || $(this.entity)) {
1268
- entities.push(e);
1269
- }
1996
+ do {
1997
+ e = this.addition() || this.entity();
1998
+ if (e) {
1999
+ entities.push(e);
2000
+ // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
2001
+ if (!peek(/^\/[\/*]/)) {
2002
+ delim = $char('/');
2003
+ if (delim) {
2004
+ entities.push(new(tree.Anonymous)(delim));
2005
+ }
2006
+ }
2007
+ }
2008
+ } while (e);
1270
2009
  if (entities.length > 0) {
1271
2010
  return new(tree.Expression)(entities);
1272
2011
  }
1273
2012
  },
1274
2013
  property: function () {
1275
- var name;
1276
-
1277
- if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) {
2014
+ var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);
2015
+ if (name) {
1278
2016
  return name[1];
1279
2017
  }
2018
+ },
2019
+ ruleProperty: function () {
2020
+ var c = current, name = [], index = [], length = 0, s, k;
2021
+
2022
+ function match(re) {
2023
+ var a = re.exec(c);
2024
+ if (a) {
2025
+ index.push(i + length);
2026
+ length += a[0].length;
2027
+ c = c.slice(a[1].length);
2028
+ return name.push(a[1]);
2029
+ }
2030
+ }
2031
+
2032
+ match(/^(\*?)/);
2033
+ while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // !
2034
+ if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) {
2035
+ // at last, we have the complete match now. move forward,
2036
+ // convert name particles to tree objects and return:
2037
+ skipWhitespace(length);
2038
+ if (name[0] === '') {
2039
+ name.shift();
2040
+ index.shift();
2041
+ }
2042
+ for (k = 0; k < name.length; k++) {
2043
+ s = name[k];
2044
+ name[k] = (s.charAt(0) !== '@')
2045
+ ? new(tree.Keyword)(s)
2046
+ : new(tree.Variable)('@' + s.slice(2, -1),
2047
+ index[k], env.currentFileInfo);
2048
+ }
2049
+ return name;
2050
+ }
1280
2051
  }
1281
2052
  }
1282
2053
  };
2054
+ return parser;
1283
2055
  };
1284
-
1285
- if (less.mode === 'browser' || less.mode === 'rhino') {
1286
- //
1287
- // Used by `@import` directives
1288
- //
1289
- less.Parser.importer = function (path, paths, callback, env) {
1290
- if (!/^([a-z]+:)?\//.test(path) && paths.length > 0) {
1291
- path = paths[0] + path;
2056
+ less.Parser.serializeVars = function(vars) {
2057
+ var s = '';
2058
+
2059
+ for (var name in vars) {
2060
+ if (Object.hasOwnProperty.call(vars, name)) {
2061
+ var value = vars[name];
2062
+ s += ((name[0] === '@') ? '' : '@') + name +': '+ value +
2063
+ ((('' + value).slice(-1) === ';') ? '' : ';');
1292
2064
  }
1293
- // We pass `true` as 3rd argument, to force the reload of the import.
1294
- // This is so we can get the syntax tree as opposed to just the CSS output,
1295
- // as we need this to evaluate the current stylesheet.
1296
- loadStyleSheet({ href: path, title: path, type: env.mime }, function (e) {
1297
- if (e && typeof(env.errback) === "function") {
1298
- env.errback.call(null, path, paths, callback, env);
1299
- } else {
1300
- callback.apply(null, arguments);
1301
- }
1302
- }, true);
1303
- };
1304
- }
2065
+ }
1305
2066
 
2067
+ return s;
2068
+ };