less 2.1.1 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +1 -0
  2. package/.idea/jsLinters/jscs.xml +4 -0
  3. package/.idea/jsLinters/jshint.xml +58 -4
  4. package/.idea/workspace.xml +451 -758
  5. package/.jscsrc +38 -0
  6. package/.jshintrc +0 -2
  7. package/.travis.yml +0 -1
  8. package/CHANGELOG.md +52 -0
  9. package/Gruntfile.js +37 -3
  10. package/bin/lessc +380 -316
  11. package/bower.json +1 -1
  12. package/dist/less.js +790 -479
  13. package/dist/less.min.js +7 -7
  14. package/lib/less/contexts.js +1 -1
  15. package/lib/less/data/unit-conversions.js +3 -3
  16. package/lib/less/environment/abstract-file-manager.js +4 -4
  17. package/lib/less/environment/environment.js +9 -0
  18. package/lib/less/functions/color-blending.js +3 -3
  19. package/lib/less/functions/color.js +8 -8
  20. package/lib/less/functions/data-uri.js +39 -31
  21. package/lib/less/functions/string.js +3 -1
  22. package/lib/less/functions/svg.js +12 -15
  23. package/lib/less/functions/types.js +15 -2
  24. package/lib/less/import-manager.js +10 -5
  25. package/lib/less/index.js +2 -1
  26. package/lib/less/parse-tree.js +8 -2
  27. package/lib/less/parse.js +65 -0
  28. package/lib/less/parser/parser-input.js +3 -2
  29. package/lib/less/parser/parser.js +24 -11
  30. package/lib/less/render.js +7 -36
  31. package/lib/less/source-map-output.js +3 -3
  32. package/lib/less/transform-tree.js +1 -1
  33. package/lib/less/tree/assignment.js +0 -1
  34. package/lib/less/tree/color.js +4 -4
  35. package/lib/less/tree/condition.js +1 -1
  36. package/lib/less/tree/debug-info.js +6 -2
  37. package/lib/less/tree/dimension.js +1 -1
  38. package/lib/less/tree/directive.js +25 -6
  39. package/lib/less/tree/expression.js +1 -1
  40. package/lib/less/tree/import.js +2 -2
  41. package/lib/less/tree/index.js +1 -0
  42. package/lib/less/tree/media.js +3 -2
  43. package/lib/less/tree/mixin-call.js +3 -4
  44. package/lib/less/tree/mixin-definition.js +4 -3
  45. package/lib/less/tree/quoted.js +1 -1
  46. package/lib/less/tree/rule.js +2 -3
  47. package/lib/less/tree/ruleset.js +263 -157
  48. package/lib/less/tree/selector.js +2 -1
  49. package/lib/less/tree/url.js +5 -2
  50. package/lib/less/tree/value.js +1 -1
  51. package/lib/less/tree/variable.js +1 -1
  52. package/lib/less/visitors/extend-visitor.js +60 -25
  53. package/lib/less/visitors/import-sequencer.js +20 -14
  54. package/lib/less/visitors/import-visitor.js +13 -10
  55. package/lib/less/visitors/to-css-visitor.js +37 -5
  56. package/lib/less-browser/add-default-options.js +5 -1
  57. package/lib/less-browser/bootstrap.js +10 -8
  58. package/lib/less-browser/cache.js +1 -1
  59. package/lib/less-browser/index.js +13 -12
  60. package/lib/less-browser/utils.js +9 -6
  61. package/lib/less-node/file-manager.js +51 -28
  62. package/lib/less-node/image-size.js +34 -0
  63. package/lib/less-node/index.js +3 -0
  64. package/lib/less-node/lessc-helper.js +3 -2
  65. package/lib/less-node/url-file-manager.js +2 -2
  66. package/lib/less-rhino/index.js +2 -4
  67. package/package.json +10 -7
  68. package/test/browser/common.js +13 -11
  69. package/test/browser/css/urls.css +6 -3
  70. package/test/browser/jasmine-jsreporter.js +2 -3
  71. package/test/browser/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  72. package/test/browser/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  73. package/test/browser/less/urls.less +1 -0
  74. package/test/browser/less.js +15 -9424
  75. package/test/browser/runner-errors-options.js +0 -1
  76. package/test/browser/runner-errors-spec.js +0 -1
  77. package/test/browser/runner-legacy-options.js +0 -1
  78. package/test/browser/runner-no-js-errors-spec.js +0 -1
  79. package/test/browser/runner-production-options.js +0 -1
  80. package/test/browser/runner-relative-urls-options.js +0 -1
  81. package/test/browser/runner-rootpath-options.js +0 -1
  82. package/test/browser/runner-rootpath-relative-options.js +0 -1
  83. package/test/browser/runner-strict-units-options.js +0 -1
  84. package/test/copy-bom.js +72 -0
  85. package/test/css/comments.css +1 -0
  86. package/test/css/css-3.css +2 -0
  87. package/test/css/functions.css +2 -0
  88. package/test/css/import-interpolation.css +6 -0
  89. package/test/css/import-reference.css +25 -0
  90. package/test/css/include-path/include-path.css +6 -0
  91. package/test/css/mixins-important.css +6 -0
  92. package/test/css/selectors.css +10 -1
  93. package/test/css/url-args/urls.css +3 -3
  94. package/test/css/urls.css +12 -4
  95. package/test/css/variables.css +7 -1
  96. package/test/data/image.svg +3 -0
  97. package/test/index.js +14 -10
  98. package/test/less/comments.less +1 -0
  99. package/test/less/css-3.less +3 -0
  100. package/test/less/css-guards.less +2 -1
  101. package/test/less/errors/detached-ruleset-2.txt +1 -1
  102. package/test/less/errors/import-missing.txt +1 -1
  103. package/test/less/errors/parens-error-1.txt +1 -1
  104. package/test/less/errors/parens-error-2.txt +1 -1
  105. package/test/less/errors/parens-error-3.txt +1 -1
  106. package/test/less/errors/single-character.less +1 -0
  107. package/test/less/errors/single-character.txt +2 -0
  108. package/test/less/functions.less +5 -0
  109. package/test/less/import/import-interpolation.less +1 -0
  110. package/test/less/import/import-reference.less +38 -0
  111. package/test/less/import-reference.less +3 -1
  112. package/test/less/import.less +2 -0
  113. package/test/less/include-path/include-path.less +6 -0
  114. package/test/less/mixins-important.less +12 -0
  115. package/test/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  116. package/test/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  117. package/test/less/selectors.less +19 -4
  118. package/test/less/urls.less +10 -1
  119. package/test/less/variables.less +17 -3
  120. package/test/less-bom/charsets.less +3 -0
  121. package/test/less-bom/colors.less +98 -0
  122. package/test/less-bom/comments.less +102 -0
  123. package/test/less-bom/comments2.less +20 -0
  124. package/test/less-bom/compression/compression.less +36 -0
  125. package/test/less-bom/css-3.less +154 -0
  126. package/test/less-bom/css-escapes.less +33 -0
  127. package/test/less-bom/css-guards.less +103 -0
  128. package/test/less-bom/css.less +108 -0
  129. package/test/less-bom/debug/import/test.less +25 -0
  130. package/test/less-bom/debug/linenumbers.less +33 -0
  131. package/test/less-bom/detached-rulesets.less +103 -0
  132. package/test/less-bom/empty.less +1 -0
  133. package/test/less-bom/errors/add-mixed-units.less +3 -0
  134. package/test/less-bom/errors/add-mixed-units.txt +4 -0
  135. package/test/less-bom/errors/add-mixed-units2.less +3 -0
  136. package/test/less-bom/errors/add-mixed-units2.txt +4 -0
  137. package/test/less-bom/errors/at-rules-undefined-var.less +4 -0
  138. package/test/less-bom/errors/at-rules-undefined-var.txt +4 -0
  139. package/test/less-bom/errors/bad-variable-declaration1.less +1 -0
  140. package/test/less-bom/errors/bad-variable-declaration1.txt +2 -0
  141. package/test/less-bom/errors/color-func-invalid-color.less +3 -0
  142. package/test/less-bom/errors/color-func-invalid-color.txt +4 -0
  143. package/test/less-bom/errors/color-invalid-hex-code.less +3 -0
  144. package/test/less-bom/errors/color-invalid-hex-code.txt +4 -0
  145. package/test/less-bom/errors/color-invalid-hex-code2.less +3 -0
  146. package/test/less-bom/errors/color-invalid-hex-code2.txt +4 -0
  147. package/test/less-bom/errors/css-guard-default-func.less +4 -0
  148. package/test/less-bom/errors/css-guard-default-func.txt +4 -0
  149. package/test/less-bom/errors/detached-ruleset-1.less +6 -0
  150. package/test/less-bom/errors/detached-ruleset-1.txt +4 -0
  151. package/test/less-bom/errors/detached-ruleset-2.less +6 -0
  152. package/test/less-bom/errors/detached-ruleset-2.txt +4 -0
  153. package/test/less-bom/errors/detached-ruleset-3.less +4 -0
  154. package/test/less-bom/errors/detached-ruleset-3.txt +4 -0
  155. package/test/less-bom/errors/detached-ruleset-4.less +5 -0
  156. package/test/less-bom/errors/detached-ruleset-4.txt +4 -0
  157. package/test/less-bom/errors/detached-ruleset-5.less +4 -0
  158. package/test/less-bom/errors/detached-ruleset-5.txt +3 -0
  159. package/test/less-bom/errors/detached-ruleset-6.less +5 -0
  160. package/test/less-bom/errors/detached-ruleset-6.txt +4 -0
  161. package/test/less-bom/errors/divide-mixed-units.less +3 -0
  162. package/test/less-bom/errors/divide-mixed-units.txt +4 -0
  163. package/test/less-bom/errors/extend-no-selector.less +3 -0
  164. package/test/less-bom/errors/extend-no-selector.txt +3 -0
  165. package/test/less-bom/errors/extend-not-at-end.less +3 -0
  166. package/test/less-bom/errors/extend-not-at-end.txt +3 -0
  167. package/test/less-bom/errors/import-malformed.less +1 -0
  168. package/test/less-bom/errors/import-malformed.txt +3 -0
  169. package/test/less-bom/errors/import-missing.less +6 -0
  170. package/test/less-bom/errors/import-missing.txt +3 -0
  171. package/test/less-bom/errors/import-no-semi.less +1 -0
  172. package/test/less-bom/errors/import-no-semi.txt +2 -0
  173. package/test/less-bom/errors/import-subfolder1.less +1 -0
  174. package/test/less-bom/errors/import-subfolder1.txt +3 -0
  175. package/test/less-bom/errors/import-subfolder2.less +1 -0
  176. package/test/less-bom/errors/import-subfolder2.txt +4 -0
  177. package/test/less-bom/errors/imports/import-subfolder1.less +1 -0
  178. package/test/less-bom/errors/imports/import-subfolder2.less +1 -0
  179. package/test/less-bom/errors/imports/import-test.less +4 -0
  180. package/test/less-bom/errors/imports/subfolder/mixin-not-defined.less +1 -0
  181. package/test/less-bom/errors/imports/subfolder/parse-error-curly-bracket.less +1 -0
  182. package/test/less-bom/errors/javascript-error.less +3 -0
  183. package/test/less-bom/errors/javascript-error.txt +4 -0
  184. package/test/less-bom/errors/javascript-undefined-var.less +3 -0
  185. package/test/less-bom/errors/javascript-undefined-var.txt +4 -0
  186. package/test/less-bom/errors/mixed-mixin-definition-args-1.less +6 -0
  187. package/test/less-bom/errors/mixed-mixin-definition-args-1.txt +4 -0
  188. package/test/less-bom/errors/mixed-mixin-definition-args-2.less +6 -0
  189. package/test/less-bom/errors/mixed-mixin-definition-args-2.txt +4 -0
  190. package/test/less-bom/errors/mixin-not-defined.less +11 -0
  191. package/test/less-bom/errors/mixin-not-defined.txt +3 -0
  192. package/test/less-bom/errors/mixin-not-matched.less +6 -0
  193. package/test/less-bom/errors/mixin-not-matched.txt +3 -0
  194. package/test/less-bom/errors/mixin-not-matched2.less +6 -0
  195. package/test/less-bom/errors/mixin-not-matched2.txt +3 -0
  196. package/test/less-bom/errors/mixin-not-visible-in-scope-1.less +9 -0
  197. package/test/less-bom/errors/mixin-not-visible-in-scope-1.txt +4 -0
  198. package/test/less-bom/errors/mixins-guards-default-func-1.less +9 -0
  199. package/test/less-bom/errors/mixins-guards-default-func-1.txt +4 -0
  200. package/test/less-bom/errors/mixins-guards-default-func-2.less +9 -0
  201. package/test/less-bom/errors/mixins-guards-default-func-2.txt +4 -0
  202. package/test/less-bom/errors/mixins-guards-default-func-3.less +9 -0
  203. package/test/less-bom/errors/mixins-guards-default-func-3.txt +4 -0
  204. package/test/less-bom/errors/multiple-guards-on-css-selectors.less +4 -0
  205. package/test/less-bom/errors/multiple-guards-on-css-selectors.txt +4 -0
  206. package/test/less-bom/errors/multiple-guards-on-css-selectors2.less +4 -0
  207. package/test/less-bom/errors/multiple-guards-on-css-selectors2.txt +4 -0
  208. package/test/less-bom/errors/multiply-mixed-units.less +7 -0
  209. package/test/less-bom/errors/multiply-mixed-units.txt +4 -0
  210. package/test/less-bom/errors/parens-error-1.less +3 -0
  211. package/test/less-bom/errors/parens-error-1.txt +4 -0
  212. package/test/less-bom/errors/parens-error-2.less +3 -0
  213. package/test/less-bom/errors/parens-error-2.txt +4 -0
  214. package/test/less-bom/errors/parens-error-3.less +3 -0
  215. package/test/less-bom/errors/parens-error-3.txt +4 -0
  216. package/test/less-bom/errors/parse-error-curly-bracket.less +4 -0
  217. package/test/less-bom/errors/parse-error-curly-bracket.txt +4 -0
  218. package/test/less-bom/errors/parse-error-extra-parens.less +5 -0
  219. package/test/less-bom/errors/parse-error-extra-parens.txt +3 -0
  220. package/test/less-bom/errors/parse-error-missing-bracket.less +2 -0
  221. package/test/less-bom/errors/parse-error-missing-bracket.txt +3 -0
  222. package/test/less-bom/errors/parse-error-missing-parens.less +5 -0
  223. package/test/less-bom/errors/parse-error-missing-parens.txt +3 -0
  224. package/test/less-bom/errors/parse-error-with-import.less +13 -0
  225. package/test/less-bom/errors/parse-error-with-import.txt +4 -0
  226. package/test/less-bom/errors/percentage-missing-space.less +3 -0
  227. package/test/less-bom/errors/percentage-missing-space.txt +4 -0
  228. package/test/less-bom/errors/property-asterisk-only-name.less +3 -0
  229. package/test/less-bom/errors/property-asterisk-only-name.txt +4 -0
  230. package/test/less-bom/errors/property-ie5-hack.less +3 -0
  231. package/test/less-bom/errors/property-ie5-hack.txt +3 -0
  232. package/test/less-bom/errors/property-in-root.less +4 -0
  233. package/test/less-bom/errors/property-in-root.txt +4 -0
  234. package/test/less-bom/errors/property-in-root2.less +1 -0
  235. package/test/less-bom/errors/property-in-root2.txt +4 -0
  236. package/test/less-bom/errors/property-in-root3.less +4 -0
  237. package/test/less-bom/errors/property-in-root3.txt +3 -0
  238. package/test/less-bom/errors/property-interp-not-defined.less +1 -0
  239. package/test/less-bom/errors/property-interp-not-defined.txt +2 -0
  240. package/test/less-bom/errors/recursive-variable.less +1 -0
  241. package/test/less-bom/errors/recursive-variable.txt +2 -0
  242. package/test/less-bom/errors/single-character.less +1 -0
  243. package/test/less-bom/errors/single-character.txt +2 -0
  244. package/test/less-bom/errors/svg-gradient1.less +3 -0
  245. package/test/less-bom/errors/svg-gradient1.txt +4 -0
  246. package/test/less-bom/errors/svg-gradient2.less +3 -0
  247. package/test/less-bom/errors/svg-gradient2.txt +4 -0
  248. package/test/less-bom/errors/svg-gradient3.less +3 -0
  249. package/test/less-bom/errors/svg-gradient3.txt +4 -0
  250. package/test/less-bom/errors/unit-function.less +3 -0
  251. package/test/less-bom/errors/unit-function.txt +4 -0
  252. package/test/less-bom/extend-chaining.less +91 -0
  253. package/test/less-bom/extend-clearfix.less +19 -0
  254. package/test/less-bom/extend-exact.less +46 -0
  255. package/test/less-bom/extend-media.less +24 -0
  256. package/test/less-bom/extend-nest.less +65 -0
  257. package/test/less-bom/extend-selector.less +99 -0
  258. package/test/less-bom/extend.less +81 -0
  259. package/test/less-bom/extract-and-length.less +133 -0
  260. package/test/less-bom/functions.less +201 -0
  261. package/test/less-bom/globalVars/extended.json +5 -0
  262. package/test/less-bom/globalVars/extended.less +10 -0
  263. package/test/less-bom/globalVars/simple.json +3 -0
  264. package/test/less-bom/globalVars/simple.less +3 -0
  265. package/test/less-bom/ie-filters.less +15 -0
  266. package/test/less-bom/import/deeper/deeper-2/url-import-2.less +3 -0
  267. package/test/less-bom/import/deeper/deeper-2/url-import.less +1 -0
  268. package/test/less-bom/import/deeper/import-once-test-a.less +1 -0
  269. package/test/less-bom/import/deeper/url-import.less +1 -0
  270. package/test/less-bom/import/import-and-relative-paths-test.less +17 -0
  271. package/test/less-bom/import/import-charset-test.less +1 -0
  272. package/test/less-bom/import/import-interpolation.less +2 -0
  273. package/test/less-bom/import/import-interpolation2.less +5 -0
  274. package/test/less-bom/import/import-once-test-c.less +6 -0
  275. package/test/less-bom/import/import-reference.less +89 -0
  276. package/test/less-bom/import/import-test-a.less +4 -0
  277. package/test/less-bom/import/import-test-b.less +8 -0
  278. package/test/less-bom/import/import-test-c.less +6 -0
  279. package/test/less-bom/import/import-test-d.css +1 -0
  280. package/test/less-bom/import/import-test-e.less +2 -0
  281. package/test/less-bom/import/import-test-f.less +5 -0
  282. package/test/less-bom/import/imports/font.less +8 -0
  283. package/test/less-bom/import/imports/logo.less +5 -0
  284. package/test/less-bom/import/interpolation-vars.less +6 -0
  285. package/test/less-bom/import/invalid-css.less +1 -0
  286. package/test/less-bom/import/urls.less +1 -0
  287. package/test/less-bom/import-inline.less +3 -0
  288. package/test/less-bom/import-interpolation.less +8 -0
  289. package/test/less-bom/import-once.less +6 -0
  290. package/test/less-bom/import-reference.less +23 -0
  291. package/test/less-bom/import.less +30 -0
  292. package/test/less-bom/include-path/include-path.less +6 -0
  293. package/test/less-bom/javascript.less +38 -0
  294. package/test/less-bom/lazy-eval.less +6 -0
  295. package/test/less-bom/legacy/legacy.less +7 -0
  296. package/test/less-bom/media.less +234 -0
  297. package/test/less-bom/merge.less +78 -0
  298. package/test/less-bom/mixins-args.less +215 -0
  299. package/test/less-bom/mixins-closure.less +26 -0
  300. package/test/less-bom/mixins-guards-default-func.less +195 -0
  301. package/test/less-bom/mixins-guards.less +271 -0
  302. package/test/less-bom/mixins-important.less +37 -0
  303. package/test/less-bom/mixins-interpolated.less +75 -0
  304. package/test/less-bom/mixins-named-args.less +36 -0
  305. package/test/less-bom/mixins-nested.less +22 -0
  306. package/test/less-bom/mixins-pattern.less +102 -0
  307. package/test/less-bom/mixins.less +145 -0
  308. package/test/less-bom/modifyVars/extended.json +5 -0
  309. package/test/less-bom/modifyVars/extended.less +11 -0
  310. package/test/less-bom/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  311. package/test/less-bom/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  312. package/test/less-bom/no-js-errors/no-js-errors.less +3 -0
  313. package/test/less-bom/no-js-errors/no-js-errors.txt +4 -0
  314. package/test/less-bom/no-output.less +2 -0
  315. package/test/less-bom/operations.less +62 -0
  316. package/test/less-bom/parens.less +45 -0
  317. package/test/less-bom/property-name-interp.less +56 -0
  318. package/test/less-bom/rulesets.less +30 -0
  319. package/test/less-bom/scope.less +104 -0
  320. package/test/less-bom/selectors.less +174 -0
  321. package/test/less-bom/sourcemaps/basic.json +3 -0
  322. package/test/less-bom/sourcemaps/basic.less +27 -0
  323. package/test/less-bom/sourcemaps/imported.css +7 -0
  324. package/test/less-bom/static-urls/urls.less +33 -0
  325. package/test/less-bom/strict-units/strict-units.less +4 -0
  326. package/test/less-bom/strings.less +73 -0
  327. package/test/less-bom/url-args/urls.less +63 -0
  328. package/test/less-bom/urls.less +94 -0
  329. package/test/less-bom/variables-in-at-rules.less +20 -0
  330. package/test/less-bom/variables.less +119 -0
  331. package/test/less-bom/whitespace.less +44 -0
  332. package/test/less-test.js +127 -28
  333. package/test/modify-vars.js +6 -4
@@ -41,6 +41,7 @@ contexts.Parse = function(options) {
41
41
  };
42
42
 
43
43
  var evalCopyProperties = [
44
+ 'paths', // additional include paths
44
45
  'compress', // whether to compress
45
46
  'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
46
47
  'strictMath', // whether math has to be within parenthesis
@@ -107,4 +108,3 @@ contexts.Eval.prototype.normalizePath = function( path ) {
107
108
  };
108
109
 
109
110
  //todo - do the same for the toCSS ?
110
-
@@ -13,9 +13,9 @@ module.exports = {
13
13
  'ms': 0.001
14
14
  },
15
15
  angle: {
16
- 'rad': 1/(2*Math.PI),
17
- 'deg': 1/360,
18
- 'grad': 1/400,
16
+ 'rad': 1 / (2 * Math.PI),
17
+ 'deg': 1 / 360,
18
+ 'grad': 1 / 400,
19
19
  'turn': 1
20
20
  }
21
21
  };
@@ -53,10 +53,10 @@ abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) {
53
53
  }
54
54
  baseUrlDirectories = baseUrlParts.directories.slice(i);
55
55
  urlDirectories = urlParts.directories.slice(i);
56
- for(i = 0; i < baseUrlDirectories.length-1; i++) {
56
+ for(i = 0; i < baseUrlDirectories.length - 1; i++) {
57
57
  diff += "../";
58
58
  }
59
- for(i = 0; i < urlDirectories.length-1; i++) {
59
+ for(i = 0; i < urlDirectories.length - 1; i++) {
60
60
  diff += urlDirectories[i] + "/";
61
61
  }
62
62
  return diff;
@@ -81,7 +81,7 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba
81
81
  if (baseUrl && (!urlParts[1] || urlParts[2])) {
82
82
  baseUrlParts = baseUrl.match(urlPartsRegex);
83
83
  if (!baseUrlParts) {
84
- throw new Error("Could not parse page url - '"+baseUrl+"'");
84
+ throw new Error("Could not parse page url - '" + baseUrl + "'");
85
85
  }
86
86
  urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
87
87
  if (!urlParts[2]) {
@@ -102,7 +102,7 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba
102
102
 
103
103
  for(i = 0; i < directories.length; i++) {
104
104
  if (directories[i] === ".." && i > 0) {
105
- directories.splice(i-1, 2);
105
+ directories.splice(i - 1, 2);
106
106
  i -= 2;
107
107
  }
108
108
  }
@@ -1,3 +1,4 @@
1
+ var logger = require("../logger");
1
2
  var environment = function(externalEnvironment, fileManagers) {
2
3
  this.fileManagers = fileManagers || [];
3
4
  externalEnvironment = externalEnvironment || {};
@@ -18,6 +19,14 @@ var environment = function(externalEnvironment, fileManagers) {
18
19
  };
19
20
 
20
21
  environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) {
22
+
23
+ if (!filename) {
24
+ logger.warn("getFileManager called with no filename.. Please report this issue. continuing.");
25
+ }
26
+ if (currentDirectory == null) {
27
+ logger.warn("getFileManager called with null directory.. Please report this issue. continuing.");
28
+ }
29
+
21
30
  var fileManagers = this.fileManagers;
22
31
  if (options.pluginManager) {
23
32
  fileManagers = [].concat(fileManagers).concat(options.pluginManager.getFileManagers());
@@ -33,9 +33,9 @@ var colorBlendModeFunctions = {
33
33
  },
34
34
  overlay: function(cb, cs) {
35
35
  cb *= 2;
36
- return (cb <= 1)
37
- ? colorBlendModeFunctions.multiply(cb, cs)
38
- : colorBlendModeFunctions.screen(cb - 1, cs);
36
+ return (cb <= 1) ?
37
+ colorBlendModeFunctions.multiply(cb, cs) :
38
+ colorBlendModeFunctions.screen(cb - 1, cs);
39
39
  },
40
40
  softlight: function(cb, cs) {
41
41
  var d = 1, e = cb;
@@ -47,7 +47,7 @@ colorFunctions = {
47
47
  h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
48
48
  if (h * 6 < 1) { return m1 + (m2 - m1) * h * 6; }
49
49
  else if (h * 2 < 1) { return m2; }
50
- else if (h * 3 < 2) { return m1 + (m2 - m1) * (2/3 - h) * 6; }
50
+ else if (h * 3 < 2) { return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
51
51
  else { return m1; }
52
52
  }
53
53
 
@@ -57,9 +57,9 @@ colorFunctions = {
57
57
  var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
58
58
  var m1 = l * 2 - m2;
59
59
 
60
- return colorFunctions.rgba(hue(h + 1/3) * 255,
60
+ return colorFunctions.rgba(hue(h + 1 / 3) * 255,
61
61
  hue(h) * 255,
62
- hue(h - 1/3) * 255,
62
+ hue(h - 1 / 3) * 255,
63
63
  a);
64
64
  },
65
65
 
@@ -127,9 +127,9 @@ colorFunctions = {
127
127
  },
128
128
  luminance: function (color) {
129
129
  var luminance =
130
- (0.2126 * color.rgb[0] / 255)
131
- + (0.7152 * color.rgb[1] / 255)
132
- + (0.0722 * color.rgb[2] / 255);
130
+ (0.2126 * color.rgb[0] / 255) +
131
+ (0.7152 * color.rgb[1] / 255) +
132
+ (0.0722 * color.rgb[2] / 255);
133
133
 
134
134
  return new Dimension(luminance * color.alpha * 100, '%');
135
135
  },
@@ -259,7 +259,7 @@ colorFunctions = {
259
259
  return new Color(c.value.slice(1));
260
260
  }
261
261
  if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) {
262
- c.keyword = undefined;
262
+ c.value = undefined;
263
263
  return c;
264
264
  }
265
265
  throw {
@@ -268,7 +268,7 @@ colorFunctions = {
268
268
  };
269
269
  },
270
270
  tint: function(color, amount) {
271
- return colorFunctions.mix(colorFunctions.rgb(255,255,255), color, amount);
271
+ return colorFunctions.mix(colorFunctions.rgb(255, 255, 255), color, amount);
272
272
  },
273
273
  shade: function(color, amount) {
274
274
  return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount);
@@ -1,5 +1,5 @@
1
1
  module.exports = function(environment) {
2
- var Anonymous = require("../tree/anonymous"),
2
+ var Quoted = require("../tree/quoted"),
3
3
  URL = require("../tree/url"),
4
4
  functionRegistry = require("./function-registry"),
5
5
  fallback = function(functionThis, node) {
@@ -9,39 +9,44 @@ module.exports = function(environment) {
9
9
 
10
10
  functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) {
11
11
 
12
- var mimetype = mimetypeNode.value;
13
- var filePath = (filePathNode && filePathNode.value);
14
-
15
- var fileManager = environment.getFileManager(filePath, this.context.currentFileInfo, this.context, environment, true);
16
-
17
- if (!fileManager) {
18
- return fallback(this, filePathNode || mimetypeNode);
12
+ if (!filePathNode) {
13
+ filePathNode = mimetypeNode;
14
+ mimetypeNode = null;
19
15
  }
20
16
 
21
- var useBase64 = false;
22
-
23
- if (arguments.length < 2) {
24
- filePath = mimetype;
25
- }
17
+ var mimetype = mimetypeNode && mimetypeNode.value;
18
+ var filePath = filePathNode.value;
19
+ var currentFileInfo = this.currentFileInfo;
20
+ var currentDirectory = currentFileInfo.relativeUrls ?
21
+ currentFileInfo.currentDirectory : currentFileInfo.entryPath;
26
22
 
27
23
  var fragmentStart = filePath.indexOf('#');
28
24
  var fragment = '';
29
- if (fragmentStart!==-1) {
25
+ if (fragmentStart !== -1) {
30
26
  fragment = filePath.slice(fragmentStart);
31
27
  filePath = filePath.slice(0, fragmentStart);
32
28
  }
33
29
 
34
- var currentDirectory = this.currentFileInfo.relativeUrls ?
35
- this.currentFileInfo.currentDirectory : this.currentFileInfo.entryPath;
30
+ var fileManager = environment.getFileManager(filePath, currentDirectory, this.context, environment, true);
31
+
32
+ if (!fileManager) {
33
+ return fallback(this, filePathNode);
34
+ }
35
+
36
+ var useBase64 = false;
36
37
 
37
38
  // detect the mimetype if not given
38
- if (arguments.length < 2) {
39
+ if (!mimetypeNode) {
39
40
 
40
41
  mimetype = environment.mimeLookup(filePath);
41
42
 
42
- // use base 64 unless it's an ASCII or UTF-8 format
43
- var charset = environment.charsetLookup(mimetype);
44
- useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
43
+ if (mimetype === "image/svg+xml") {
44
+ useBase64 = false;
45
+ } else {
46
+ // use base 64 unless it's an ASCII or UTF-8 format
47
+ var charset = environment.charsetLookup(mimetype);
48
+ useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
49
+ }
45
50
  if (useBase64) { mimetype += ';base64'; }
46
51
  }
47
52
  else {
@@ -50,28 +55,31 @@ module.exports = function(environment) {
50
55
 
51
56
  var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment);
52
57
  if (!fileSync.contents) {
53
- logger.warn("Skipped data-uri embedding because file not found");
58
+ logger.warn("Skipped data-uri embedding of " + filePath + " because file not found");
54
59
  return fallback(this, filePathNode || mimetypeNode);
55
60
  }
56
61
  var buf = fileSync.contents;
62
+ if (useBase64 && !environment.encodeBase64) {
63
+ return fallback(this, filePathNode);
64
+ }
65
+
66
+ buf = useBase64 ? environment.encodeBase64(buf) : encodeURIComponent(buf);
57
67
 
58
- // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
68
+ var uri = "data:" + mimetype + ',' + buf + fragment;
69
+
70
+ // IE8 cannot handle a data-uri larger than 32,768 characters. If this is exceeded
59
71
  // and the --ieCompat flag is enabled, return a normal url() instead.
60
- var DATA_URI_MAX_KB = 32,
61
- fileSizeInKB = parseInt((buf.length / 1024), 10);
62
- if (fileSizeInKB >= DATA_URI_MAX_KB) {
72
+ var DATA_URI_MAX = 32768;
73
+ if (uri.length >= DATA_URI_MAX) {
63
74
 
64
75
  if (this.context.ieCompat !== false) {
65
- logger.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
76
+ logger.warn("Skipped data-uri embedding of " + filePath + " because its size (" + uri.length +
77
+ " characters) exceeds IE8-safe " + DATA_URI_MAX + " characters!");
66
78
 
67
79
  return fallback(this, filePathNode || mimetypeNode);
68
80
  }
69
81
  }
70
82
 
71
- buf = useBase64 ? buf.toString('base64')
72
- : encodeURIComponent(buf);
73
-
74
- var uri = "\"data:" + mimetype + ',' + buf + fragment + "\"";
75
- return new URL(new Anonymous(uri), this.index, this.currentFileInfo);
83
+ return new URL(new Quoted('"' + uri + '"', uri, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
76
84
  });
77
85
  };
@@ -8,7 +8,9 @@ functionRegistry.addMultiple({
8
8
  return new Anonymous(str instanceof JavaScript ? str.evaluated : str.value);
9
9
  },
10
10
  escape: function (str) {
11
- return new Anonymous(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29"));
11
+ return new Anonymous(
12
+ encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B")
13
+ .replace(/\(/g, "%28").replace(/\)/g, "%29"));
12
14
  },
13
15
  replace: function (string, pattern, replacement, flags) {
14
16
  var result = string.value;
@@ -1,14 +1,17 @@
1
1
  module.exports = function(environment) {
2
2
  var Dimension = require("../tree/dimension"),
3
3
  Color = require("../tree/color"),
4
- Anonymous = require("../tree/anonymous"),
4
+ Expression = require("../tree/expression"),
5
+ Quoted = require("../tree/quoted"),
5
6
  URL = require("../tree/url"),
6
7
  functionRegistry = require("./function-registry");
7
8
 
8
9
  functionRegistry.add("svg-gradient", function(direction) {
9
10
 
10
11
  function throwArgumentDescriptor() {
11
- throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" };
12
+ throw { type: "Argument",
13
+ message: "svg-gradient expects direction, start_color [start_position], [color position,]...," +
14
+ " end_color [end_position]" };
12
15
  }
13
16
 
14
17
  if (arguments.length < 3) {
@@ -18,7 +21,6 @@ module.exports = function(environment) {
18
21
  gradientDirectionSvg,
19
22
  gradientType = "linear",
20
23
  rectangleDimension = 'x="0" y="0" width="1" height="1"',
21
- useBase64 = true,
22
24
  renderEnv = {compress: false},
23
25
  returner,
24
26
  directionValue = direction.toCSS(renderEnv),
@@ -44,14 +46,15 @@ module.exports = function(environment) {
44
46
  rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
45
47
  break;
46
48
  default:
47
- throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" };
49
+ throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right'," +
50
+ " 'to bottom right', 'to top right' or 'ellipse at center'" };
48
51
  }
49
52
  returner = '<?xml version="1.0" ?>' +
50
53
  '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
51
54
  '<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
52
55
 
53
56
  for (i = 0; i < stops.length; i+= 1) {
54
- if (stops[i].value) {
57
+ if (stops[i] instanceof Expression) {
55
58
  color = stops[i].value[0];
56
59
  position = stops[i].value[1];
57
60
  } else {
@@ -59,7 +62,7 @@ module.exports = function(environment) {
59
62
  position = undefined;
60
63
  }
61
64
 
62
- if (!(color instanceof Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
65
+ if (!(color instanceof Color) || (!((i === 0 || i + 1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
63
66
  throwArgumentDescriptor();
64
67
  }
65
68
  positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%";
@@ -69,15 +72,9 @@ module.exports = function(environment) {
69
72
  returner += '</' + gradientType + 'Gradient>' +
70
73
  '<rect ' + rectangleDimension + ' fill="url(#gradient)" /></svg>';
71
74
 
72
- if (useBase64) {
73
- try {
74
- returner = environment.encodeBase64(returner);
75
- } catch(e) {
76
- useBase64 = false;
77
- }
78
- }
75
+ returner = encodeURIComponent(returner);
79
76
 
80
- returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'";
81
- return new URL(new Anonymous(returner), this.index, this.currentFileInfo);
77
+ returner = "data:image/svg+xml," + returner;
78
+ return new URL(new Quoted("'" + returner + "'", returner, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
82
79
  });
83
80
  };
@@ -1,4 +1,5 @@
1
1
  var Keyword = require("../tree/keyword"),
2
+ DetachedRuleset = require("../tree/detached-ruleset"),
2
3
  Dimension = require("../tree/dimension"),
3
4
  Color = require("../tree/color"),
4
5
  Quoted = require("../tree/quoted"),
@@ -11,9 +12,19 @@ var isa = function (n, Type) {
11
12
  return (n instanceof Type) ? Keyword.True : Keyword.False;
12
13
  },
13
14
  isunit = function (n, unit) {
14
- return (n instanceof Dimension) && n.unit.is(unit.value || unit) ? Keyword.True : Keyword.False;
15
+ if (unit === undefined) {
16
+ throw { type: "Argument", message: "missing the required second argument to isunit." };
17
+ }
18
+ unit = typeof unit.value === "string" ? unit.value : unit;
19
+ if (typeof unit !== "string") {
20
+ throw { type: "Argument", message: "Second argument to isunit should be a unit or a string." };
21
+ }
22
+ return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
15
23
  };
16
24
  functionRegistry.addMultiple({
25
+ isruleset: function (n) {
26
+ return isa(n, DetachedRuleset);
27
+ },
17
28
  iscolor: function (n) {
18
29
  return isa(n, Color);
19
30
  },
@@ -41,7 +52,9 @@ functionRegistry.addMultiple({
41
52
  isunit: isunit,
42
53
  unit: function (val, unit) {
43
54
  if(!(val instanceof Dimension)) {
44
- throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") };
55
+ throw { type: "Argument",
56
+ message: "the first argument to unit must be a number" +
57
+ (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") };
45
58
  }
46
59
  if (unit) {
47
60
  if (unit instanceof Keyword) {
@@ -40,12 +40,14 @@ module.exports = function(environment) {
40
40
  importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
41
41
 
42
42
  var importedEqualsRoot = fullPath === importManager.rootFilename;
43
-
43
+ if (importOptions.optional && e) {
44
+ callback(null, {rules:[]}, false, null);
45
+ }
46
+ else {
44
47
  importManager.files[fullPath] = root;
45
-
46
48
  if (e && !importManager.error) { importManager.error = e; }
47
-
48
49
  callback(e, root, importedEqualsRoot, fullPath);
50
+ }
49
51
  };
50
52
 
51
53
  var newFileInfo = {
@@ -68,7 +70,7 @@ module.exports = function(environment) {
68
70
 
69
71
  var loadFileCallback = function(loadedFile) {
70
72
  var resolvedFilename = loadedFile.filename,
71
- contents = loadedFile.contents;
73
+ contents = loadedFile.contents.replace(/^\uFEFF/, '');
72
74
 
73
75
  // Pass on an updated rootpath if path of imported file is relative and file
74
76
  // is in a (sub|sup) directory
@@ -80,7 +82,10 @@ module.exports = function(environment) {
80
82
  // then rootpath should become 'less/../'
81
83
  newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename);
82
84
  if(newFileInfo.relativeUrls) {
83
- newFileInfo.rootpath = fileManager.join((importManager.context.rootpath || ""), fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
85
+ newFileInfo.rootpath = fileManager.join(
86
+ (importManager.context.rootpath || ""),
87
+ fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
88
+
84
89
  if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) {
85
90
  newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath);
86
91
  }
package/lib/less/index.js CHANGED
@@ -2,7 +2,7 @@ module.exports = function(environment, fileManagers) {
2
2
  var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
3
3
 
4
4
  var less = {
5
- version: [2, 1, 1],
5
+ version: [2, 3, 1],
6
6
  data: require('./data'),
7
7
  tree: require('./tree'),
8
8
  Environment: (Environment = require("./environment/environment")),
@@ -17,6 +17,7 @@ module.exports = function(environment, fileManagers) {
17
17
  ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)),
18
18
  ImportManager: (ImportManager = require('./import-manager')(environment)),
19
19
  render: require("./render")(environment, ParseTree, ImportManager),
20
+ parse: require("./parse")(environment, ParseTree, ImportManager),
20
21
  LessError: require('./less-error'),
21
22
  transformTree: require('./transform-tree'),
22
23
  utils: require('./utils'),
@@ -1,5 +1,6 @@
1
1
  var LessError = require('./less-error'),
2
- transformTree = require("./transform-tree");
2
+ transformTree = require("./transform-tree"),
3
+ logger = require("./logger");
3
4
 
4
5
  module.exports = function(SourceMapBuilder) {
5
6
  var ParseTree = function(root, imports) {
@@ -16,8 +17,13 @@ ParseTree.prototype.toCSS = function(options) {
16
17
  }
17
18
 
18
19
  try {
20
+ var compress = Boolean(options.compress);
21
+ if (compress) {
22
+ logger.warn("The compress option has been deprecated. We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css.");
23
+ }
24
+
19
25
  var toCSSOptions = {
20
- compress: Boolean(options.compress),
26
+ compress: compress,
21
27
  dumpLineNumbers: options.dumpLineNumbers,
22
28
  strictUnits: Boolean(options.strictUnits),
23
29
  numPrecision: 8};
@@ -0,0 +1,65 @@
1
+ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise,
2
+ contexts = require("./contexts"),
3
+ Parser = require('./parser/parser'),
4
+ PluginManager = require('./plugin-manager');
5
+
6
+ module.exports = function(environment, ParseTree, ImportManager) {
7
+ var parse = function (input, options, callback) {
8
+ options = options || {};
9
+
10
+ if (typeof(options) === 'function') {
11
+ callback = options;
12
+ options = {};
13
+ }
14
+
15
+ if (!callback) {
16
+ var self = this;
17
+ return new PromiseConstructor(function (resolve, reject) {
18
+ parse.call(self, input, options, function(err, output) {
19
+ if (err) {
20
+ reject(err);
21
+ } else {
22
+ resolve(output);
23
+ }
24
+ });
25
+ });
26
+ } else {
27
+ var context,
28
+ rootFileInfo,
29
+ pluginManager = new PluginManager(this);
30
+
31
+ pluginManager.addPlugins(options.plugins);
32
+ options.pluginManager = pluginManager;
33
+
34
+ context = new contexts.Parse(options);
35
+
36
+ if (options.rootFileInfo) {
37
+ rootFileInfo = options.rootFileInfo;
38
+ } else {
39
+ var filename = options.filename || "input";
40
+ var entryPath = filename.replace(/[^\/\\]*$/, "");
41
+ rootFileInfo = {
42
+ filename: filename,
43
+ relativeUrls: context.relativeUrls,
44
+ rootpath: context.rootpath || "",
45
+ currentDirectory: entryPath,
46
+ entryPath: entryPath,
47
+ rootFilename: filename
48
+ };
49
+ // add in a missing trailing slash
50
+ if (rootFileInfo.rootpath && rootFileInfo.rootpath.slice(-1) !== "/") {
51
+ rootFileInfo.rootpath += "/";
52
+ }
53
+ }
54
+
55
+ var imports = new ImportManager(context, rootFileInfo);
56
+
57
+ new Parser(context, imports, rootFileInfo)
58
+ .parse(input, function (e, root) {
59
+ if (e) { return callback(e); }
60
+ callback(null, root, imports, options);
61
+ }, options);
62
+ }
63
+ };
64
+ return parse;
65
+ };
@@ -16,7 +16,8 @@ module.exports = function() {
16
16
  saveStack.push( { current: current, i: parserInput.i, j: j });
17
17
  };
18
18
  parserInput.restore = function(possibleErrorMessage) {
19
- if (parserInput.i > furthest) {
19
+
20
+ if (parserInput.i > furthest || (parserInput.i === furthest && possibleErrorMessage && !furthestPossibleErrorMessage)) {
20
21
  furthest = parserInput.i;
21
22
  furthestPossibleErrorMessage = possibleErrorMessage;
22
23
  }
@@ -238,7 +239,7 @@ module.exports = function() {
238
239
 
239
240
  parserInput.end = function() {
240
241
  var message,
241
- isFinished = parserInput.i >= input.length - 1;
242
+ isFinished = parserInput.i >= input.length;
242
243
 
243
244
  if (parserInput.i < furthest) {
244
245
  message = furthestPossibleErrorMessage;
@@ -204,7 +204,7 @@ var Parser = function Parser(context, imports, fileInfo) {
204
204
  // Ruleset (Selector '.class', [
205
205
  // Rule ("color", Value ([Expression [Color #fff]]))
206
206
  // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
207
- // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
207
+ // Rule ("width", Value ([Expression [Operation " + " [Variable "@w"][Dimension 4px]]]))
208
208
  // Ruleset (Selector [Element '>', '.child'], [...])
209
209
  // ])
210
210
  //
@@ -231,13 +231,17 @@ var Parser = function Parser(context, imports, fileInfo) {
231
231
  primary: function () {
232
232
  var mixin = this.mixin, root = [], node;
233
233
 
234
- while (!parserInput.finished)
234
+ while (true)
235
235
  {
236
236
  while(true) {
237
237
  node = this.comment();
238
238
  if (!node) { break; }
239
239
  root.push(node);
240
240
  }
241
+ // always process comments before deciding if finished
242
+ if (parserInput.finished) {
243
+ break;
244
+ }
241
245
  if (parserInput.peek('}')) {
242
246
  break;
243
247
  }
@@ -450,7 +454,9 @@ var Parser = function Parser(context, imports, fileInfo) {
450
454
  var rgb;
451
455
 
452
456
  if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
453
- var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string
457
+ // strip colons, brackets, whitespaces and other characters that should not
458
+ // definitely be part of color string
459
+ var colorCandidateString = rgb.input.match(/^#([\w]+).*/);
454
460
  colorCandidateString = colorCandidateString[1];
455
461
  if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters
456
462
  error("Invalid HEX color code");
@@ -546,8 +552,9 @@ var Parser = function Parser(context, imports, fileInfo) {
546
552
  }
547
553
 
548
554
  option = option && option[1];
549
- if (!elements)
555
+ if (!elements) {
550
556
  error("Missing target selector for :extend().");
557
+ }
551
558
  extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
552
559
  if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
553
560
 
@@ -857,8 +864,10 @@ var Parser = function Parser(context, imports, fileInfo) {
857
864
 
858
865
  c = this.combinator();
859
866
 
860
- e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) || parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
861
- parserInput.$char('*') || parserInput.$char('&') || this.attribute() || parserInput.$re(/^\([^()@]+\)/) || parserInput.$re(/^[\.#](?=@)/) ||
867
+ e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) ||
868
+ parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
869
+ parserInput.$char('*') || parserInput.$char('&') || this.attribute() ||
870
+ parserInput.$re(/^\([^&()@]+\)/) || parserInput.$re(/^[\.#:](?=@)/) ||
862
871
  this.entities.variableCurly();
863
872
 
864
873
  if (! e) {
@@ -1169,7 +1178,7 @@ var Parser = function Parser(context, imports, fileInfo) {
1169
1178
  },
1170
1179
 
1171
1180
  importOption: function() {
1172
- var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference)/);
1181
+ var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference|optional)/);
1173
1182
  if (opt) {
1174
1183
  return opt[1];
1175
1184
  }
@@ -1383,15 +1392,19 @@ var Parser = function Parser(context, imports, fileInfo) {
1383
1392
  sub: function () {
1384
1393
  var a, e;
1385
1394
 
1395
+ parserInput.save();
1386
1396
  if (parserInput.$char('(')) {
1387
1397
  a = this.addition();
1388
- if (a) {
1398
+ if (a && parserInput.$char(')')) {
1399
+ parserInput.forget();
1389
1400
  e = new(tree.Expression)([a]);
1390
- expectChar(')');
1391
1401
  e.parens = true;
1392
1402
  return e;
1393
1403
  }
1404
+ parserInput.restore("Expected ')'");
1405
+ return;
1394
1406
  }
1407
+ parserInput.restore();
1395
1408
  },
1396
1409
  multiplication: function () {
1397
1410
  var m, a, op, operation, isSpaced;
@@ -1598,8 +1611,8 @@ Parser.serializeVars = function(vars) {
1598
1611
  for (var name in vars) {
1599
1612
  if (Object.hasOwnProperty.call(vars, name)) {
1600
1613
  var value = vars[name];
1601
- s += ((name[0] === '@') ? '' : '@') + name +': '+ value +
1602
- ((('' + value).slice(-1) === ';') ? '' : ';');
1614
+ s += ((name[0] === '@') ? '' : '@') + name + ': ' + value +
1615
+ ((String(value).slice(-1) === ';') ? '' : ';');
1603
1616
  }
1604
1617
  }
1605
1618