less-execjs 2.6.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (632) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.gitmodules +9 -0
  4. data/.rubocop.yml +8 -0
  5. data/.travis.yml +19 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +177 -0
  8. data/README.md +55 -0
  9. data/Rakefile +8 -0
  10. data/bin/lessc +22 -0
  11. data/less-execjs.gemspec +34 -0
  12. data/lib/less-execjs.rb +1 -0
  13. data/lib/less.rb +29 -0
  14. data/lib/less/compiler.js +24 -0
  15. data/lib/less/defaults.rb +15 -0
  16. data/lib/less/errors.rb +16 -0
  17. data/lib/less/js/image-size.js +4 -0
  18. data/lib/less/js/image-size/.gitignore +7 -0
  19. data/lib/less/js/image-size/.jshintrc +19 -0
  20. data/lib/less/js/image-size/.travis.yml +12 -0
  21. data/lib/less/js/image-size/Contributors.md +9 -0
  22. data/lib/less/js/image-size/LICENSE +9 -0
  23. data/lib/less/js/image-size/Readme.md +87 -0
  24. data/lib/less/js/image-size/bin/image-size.js +36 -0
  25. data/lib/less/js/image-size/lib/detector.js +19 -0
  26. data/lib/less/js/image-size/lib/index.js +104 -0
  27. data/lib/less/js/image-size/lib/readUInt.js +11 -0
  28. data/lib/less/js/image-size/lib/types.js +12 -0
  29. data/lib/less/js/image-size/lib/types/bmp.js +17 -0
  30. data/lib/less/js/image-size/lib/types/gif.js +19 -0
  31. data/lib/less/js/image-size/lib/types/jpg.js +62 -0
  32. data/lib/less/js/image-size/lib/types/png.js +23 -0
  33. data/lib/less/js/image-size/lib/types/psd.js +17 -0
  34. data/lib/less/js/image-size/lib/types/svg.js +78 -0
  35. data/lib/less/js/image-size/lib/types/tiff.js +118 -0
  36. data/lib/less/js/image-size/lib/types/webp.js +51 -0
  37. data/lib/less/js/image-size/package.json +49 -0
  38. data/lib/less/js/image-size/specs/.jshintrc +9 -0
  39. data/lib/less/js/image-size/specs/complexity.js +58 -0
  40. data/lib/less/js/image-size/specs/fs-close.spec.js +61 -0
  41. data/lib/less/js/image-size/specs/images/invalid/malformed.svg +1 -0
  42. data/lib/less/js/image-size/specs/images/invalid/sample.png +0 -0
  43. data/lib/less/js/image-size/specs/images/invalid/width.svg +1 -0
  44. data/lib/less/js/image-size/specs/images/valid/bmp/sample.bmp +0 -0
  45. data/lib/less/js/image-size/specs/images/valid/gif/sample.gif +0 -0
  46. data/lib/less/js/image-size/specs/images/valid/jpg/large.jpg +0 -0
  47. data/lib/less/js/image-size/specs/images/valid/jpg/progressive.jpg +0 -0
  48. data/lib/less/js/image-size/specs/images/valid/jpg/sample.jpg +0 -0
  49. data/lib/less/js/image-size/specs/images/valid/jpg/sampleExported.jpg +0 -0
  50. data/lib/less/js/image-size/specs/images/valid/jpg/very-large.jpg +0 -0
  51. data/lib/less/js/image-size/specs/images/valid/png/sample.png +0 -0
  52. data/lib/less/js/image-size/specs/images/valid/psd/sample.psd +0 -0
  53. data/lib/less/js/image-size/specs/images/valid/svg/percentage.svg +4 -0
  54. data/lib/less/js/image-size/specs/images/valid/svg/single-quotes.svg +5 -0
  55. data/lib/less/js/image-size/specs/images/valid/svg/viewbox-height.svg +4 -0
  56. data/lib/less/js/image-size/specs/images/valid/svg/viewbox-width-height.svg +4 -0
  57. data/lib/less/js/image-size/specs/images/valid/svg/viewbox-width.svg +4 -0
  58. data/lib/less/js/image-size/specs/images/valid/svg/viewbox.svg +4 -0
  59. data/lib/less/js/image-size/specs/images/valid/svg/width-height.svg +5 -0
  60. data/lib/less/js/image-size/specs/images/valid/tiff/big-endian.tiff +0 -0
  61. data/lib/less/js/image-size/specs/images/valid/tiff/jpeg.tiff +0 -0
  62. data/lib/less/js/image-size/specs/images/valid/tiff/little-endian.tiff +0 -0
  63. data/lib/less/js/image-size/specs/images/valid/webp/lossless.webp +0 -0
  64. data/lib/less/js/image-size/specs/images/valid/webp/lossy.webp +0 -0
  65. data/lib/less/js/image-size/specs/invalid.spec.js +53 -0
  66. data/lib/less/js/image-size/specs/others.spec.js +77 -0
  67. data/lib/less/js/image-size/specs/unsupported.spec.js +34 -0
  68. data/lib/less/js/image-size/specs/valid.spec.js +75 -0
  69. data/lib/less/js/less/.gitattributes +11 -0
  70. data/lib/less/js/less/.gitignore +27 -0
  71. data/lib/less/js/less/.jscsrc +73 -0
  72. data/lib/less/js/less/.jshintrc +11 -0
  73. data/lib/less/js/less/.npmignore +9 -0
  74. data/lib/less/js/less/.travis.yml +14 -0
  75. data/lib/less/js/less/CHANGELOG.md +561 -0
  76. data/lib/less/js/less/CONTRIBUTING.md +50 -0
  77. data/lib/less/js/less/Gruntfile.js +459 -0
  78. data/lib/less/js/less/LICENSE +177 -0
  79. data/lib/less/js/less/README.md +56 -0
  80. data/lib/less/js/less/appveyor.yml +32 -0
  81. data/lib/less/js/less/benchmark/benchmark.less +3979 -0
  82. data/lib/less/js/less/benchmark/index.js +55 -0
  83. data/lib/less/js/less/bin/lessc +488 -0
  84. data/lib/less/js/less/bower.json +24 -0
  85. data/lib/less/js/less/browser.js +1 -0
  86. data/lib/less/js/less/build.gradle +347 -0
  87. data/lib/less/js/less/build/amd.js +6 -0
  88. data/lib/less/js/less/build/build.yml +130 -0
  89. data/lib/less/js/less/build/require-rhino.js +12 -0
  90. data/lib/less/js/less/build/rhino-header.js +4 -0
  91. data/lib/less/js/less/build/rhino-modules.js +131 -0
  92. data/lib/less/js/less/build/tasks/.gitkeep +1 -0
  93. data/lib/less/js/less/dist/less.js +10500 -0
  94. data/lib/less/js/less/dist/less.min.js +20 -0
  95. data/lib/less/js/less/gradle/wrapper/gradle-wrapper.jar +0 -0
  96. data/lib/less/js/less/gradle/wrapper/gradle-wrapper.properties +6 -0
  97. data/lib/less/js/less/gradlew +164 -0
  98. data/lib/less/js/less/gradlew.bat +90 -0
  99. data/lib/less/js/less/index.js +1 -0
  100. data/lib/less/js/less/lib/less-browser/add-default-options.js +46 -0
  101. data/lib/less/js/less/lib/less-browser/bootstrap.js +51 -0
  102. data/lib/less/js/less/lib/less-browser/browser.js +64 -0
  103. data/lib/less/js/less/lib/less-browser/cache.js +42 -0
  104. data/lib/less/js/less/lib/less-browser/error-reporting.js +170 -0
  105. data/lib/less/js/less/lib/less-browser/file-manager.js +119 -0
  106. data/lib/less/js/less/lib/less-browser/image-size.js +28 -0
  107. data/lib/less/js/less/lib/less-browser/index.js +263 -0
  108. data/lib/less/js/less/lib/less-browser/log-listener.js +43 -0
  109. data/lib/less/js/less/lib/less-browser/utils.js +24 -0
  110. data/lib/less/js/less/lib/less-node/environment.js +14 -0
  111. data/lib/less/js/less/lib/less-node/file-manager.js +108 -0
  112. data/lib/less/js/less/lib/less-node/fs.js +10 -0
  113. data/lib/less/js/less/lib/less-node/image-size.js +57 -0
  114. data/lib/less/js/less/lib/less-node/index.js +74 -0
  115. data/lib/less/js/less/lib/less-node/lessc-helper.js +82 -0
  116. data/lib/less/js/less/lib/less-node/plugin-loader.js +91 -0
  117. data/lib/less/js/less/lib/less-node/url-file-manager.js +56 -0
  118. data/lib/less/js/less/lib/less-rhino/index.js +450 -0
  119. data/lib/less/js/less/lib/less/contexts.js +111 -0
  120. data/lib/less/js/less/lib/less/data/colors.js +150 -0
  121. data/lib/less/js/less/lib/less/data/index.js +4 -0
  122. data/lib/less/js/less/lib/less/data/unit-conversions.js +21 -0
  123. data/lib/less/js/less/lib/less/environment/abstract-file-manager.js +123 -0
  124. data/lib/less/js/less/lib/less/environment/environment-api.js +25 -0
  125. data/lib/less/js/less/lib/less/environment/environment.js +51 -0
  126. data/lib/less/js/less/lib/less/environment/file-manager-api.js +103 -0
  127. data/lib/less/js/less/lib/less/functions/color-blending.js +74 -0
  128. data/lib/less/js/less/lib/less/functions/color.js +322 -0
  129. data/lib/less/js/less/lib/less/functions/data-uri.js +85 -0
  130. data/lib/less/js/less/lib/less/functions/default.js +27 -0
  131. data/lib/less/js/less/lib/less/functions/function-caller.js +46 -0
  132. data/lib/less/js/less/lib/less/functions/function-registry.js +29 -0
  133. data/lib/less/js/less/lib/less/functions/index.js +19 -0
  134. data/lib/less/js/less/lib/less/functions/math-helper.js +16 -0
  135. data/lib/less/js/less/lib/less/functions/math.js +29 -0
  136. data/lib/less/js/less/lib/less/functions/number.js +81 -0
  137. data/lib/less/js/less/lib/less/functions/string.js +37 -0
  138. data/lib/less/js/less/lib/less/functions/svg.js +88 -0
  139. data/lib/less/js/less/lib/less/functions/types.js +89 -0
  140. data/lib/less/js/less/lib/less/import-manager.js +131 -0
  141. data/lib/less/js/less/lib/less/index.js +29 -0
  142. data/lib/less/js/less/lib/less/less-error.js +42 -0
  143. data/lib/less/js/less/lib/less/logger.js +34 -0
  144. data/lib/less/js/less/lib/less/parse-tree.js +60 -0
  145. data/lib/less/js/less/lib/less/parse.js +68 -0
  146. data/lib/less/js/less/lib/less/parser/chunker.js +112 -0
  147. data/lib/less/js/less/lib/less/parser/parser-input.js +259 -0
  148. data/lib/less/js/less/lib/less/parser/parser.js +1851 -0
  149. data/lib/less/js/less/lib/less/plugin-manager.js +114 -0
  150. data/lib/less/js/less/lib/less/plugins/function-importer.js +35 -0
  151. data/lib/less/js/less/lib/less/render.js +41 -0
  152. data/lib/less/js/less/lib/less/source-map-builder.js +69 -0
  153. data/lib/less/js/less/lib/less/source-map-output.js +138 -0
  154. data/lib/less/js/less/lib/less/transform-tree.js +74 -0
  155. data/lib/less/js/less/lib/less/tree/alpha.js +28 -0
  156. data/lib/less/js/less/lib/less/tree/anonymous.js +26 -0
  157. data/lib/less/js/less/lib/less/tree/assignment.js +27 -0
  158. data/lib/less/js/less/lib/less/tree/attribute.js +27 -0
  159. data/lib/less/js/less/lib/less/tree/call.js +62 -0
  160. data/lib/less/js/less/lib/less/tree/color.js +189 -0
  161. data/lib/less/js/less/lib/less/tree/combinator.js +23 -0
  162. data/lib/less/js/less/lib/less/tree/comment.js +21 -0
  163. data/lib/less/js/less/lib/less/tree/condition.js +37 -0
  164. data/lib/less/js/less/lib/less/tree/debug-info.js +38 -0
  165. data/lib/less/js/less/lib/less/tree/detached-ruleset.js +21 -0
  166. data/lib/less/js/less/lib/less/tree/dimension.js +157 -0
  167. data/lib/less/js/less/lib/less/tree/directive.js +133 -0
  168. data/lib/less/js/less/lib/less/tree/element.js +60 -0
  169. data/lib/less/js/less/lib/less/tree/expression.js +56 -0
  170. data/lib/less/js/less/lib/less/tree/extend.js +56 -0
  171. data/lib/less/js/less/lib/less/tree/import.js +165 -0
  172. data/lib/less/js/less/lib/less/tree/index.js +41 -0
  173. data/lib/less/js/less/lib/less/tree/javascript.js +28 -0
  174. data/lib/less/js/less/lib/less/tree/js-eval-node.js +61 -0
  175. data/lib/less/js/less/lib/less/tree/keyword.js +14 -0
  176. data/lib/less/js/less/lib/less/tree/media.js +144 -0
  177. data/lib/less/js/less/lib/less/tree/mixin-call.js +182 -0
  178. data/lib/less/js/less/lib/less/tree/mixin-definition.js +200 -0
  179. data/lib/less/js/less/lib/less/tree/negative.js +20 -0
  180. data/lib/less/js/less/lib/less/tree/node.js +123 -0
  181. data/lib/less/js/less/lib/less/tree/operation.js +48 -0
  182. data/lib/less/js/less/lib/less/tree/paren.js +16 -0
  183. data/lib/less/js/less/lib/less/tree/quoted.js +55 -0
  184. data/lib/less/js/less/lib/less/tree/rule.js +95 -0
  185. data/lib/less/js/less/lib/less/tree/ruleset-call.js +13 -0
  186. data/lib/less/js/less/lib/less/tree/ruleset.js +709 -0
  187. data/lib/less/js/less/lib/less/tree/selector.js +111 -0
  188. data/lib/less/js/less/lib/less/tree/unicode-descriptor.js +9 -0
  189. data/lib/less/js/less/lib/less/tree/unit.js +120 -0
  190. data/lib/less/js/less/lib/less/tree/url.js +54 -0
  191. data/lib/less/js/less/lib/less/tree/value.js +34 -0
  192. data/lib/less/js/less/lib/less/tree/variable.js +53 -0
  193. data/lib/less/js/less/lib/less/utils.js +20 -0
  194. data/lib/less/js/less/lib/less/visitors/extend-visitor.js +460 -0
  195. data/lib/less/js/less/lib/less/visitors/import-sequencer.js +54 -0
  196. data/lib/less/js/less/lib/less/visitors/import-visitor.js +189 -0
  197. data/lib/less/js/less/lib/less/visitors/index.js +10 -0
  198. data/lib/less/js/less/lib/less/visitors/join-selector-visitor.js +51 -0
  199. data/lib/less/js/less/lib/less/visitors/set-tree-visibility-visitor.js +38 -0
  200. data/lib/less/js/less/lib/less/visitors/to-css-visitor.js +383 -0
  201. data/lib/less/js/less/lib/less/visitors/visitor.js +152 -0
  202. data/lib/less/js/less/lib/source-map/source-map-0.1.31.js +1933 -0
  203. data/lib/less/js/less/lib/source-map/source-map-footer.js +4 -0
  204. data/lib/less/js/less/lib/source-map/source-map-header.js +3 -0
  205. data/lib/less/js/less/package.json +89 -0
  206. data/lib/less/js/less/test/browser/common.js +206 -0
  207. data/lib/less/js/less/test/browser/css/global-vars/simple.css +3 -0
  208. data/lib/less/js/less/test/browser/css/modify-vars/simple.css +8 -0
  209. data/lib/less/js/less/test/browser/css/postProcessor/postProcessor.css +4 -0
  210. data/lib/less/js/less/test/browser/css/relative-urls/urls.css +36 -0
  211. data/lib/less/js/less/test/browser/css/rootpath-relative/urls.css +35 -0
  212. data/lib/less/js/less/test/browser/css/rootpath/urls.css +35 -0
  213. data/lib/less/js/less/test/browser/css/urls.css +57 -0
  214. data/lib/less/js/less/test/browser/jasmine-jsreporter.js +391 -0
  215. data/lib/less/js/less/test/browser/less/console-errors/test-error.less +3 -0
  216. data/lib/less/js/less/test/browser/less/console-errors/test-error.txt +2 -0
  217. data/lib/less/js/less/test/browser/less/errors/image-height-error.less +3 -0
  218. data/lib/less/js/less/test/browser/less/errors/image-height-error.txt +4 -0
  219. data/lib/less/js/less/test/browser/less/errors/image-size-error.less +3 -0
  220. data/lib/less/js/less/test/browser/less/errors/image-size-error.txt +4 -0
  221. data/lib/less/js/less/test/browser/less/errors/image-width-error.less +3 -0
  222. data/lib/less/js/less/test/browser/less/errors/image-width-error.txt +4 -0
  223. data/lib/less/js/less/test/browser/less/global-vars/simple.less +3 -0
  224. data/lib/less/js/less/test/browser/less/imports/urls.less +4 -0
  225. data/lib/less/js/less/test/browser/less/imports/urls2.less +4 -0
  226. data/lib/less/js/less/test/browser/less/modify-vars/imports/simple2.less +4 -0
  227. data/lib/less/js/less/test/browser/less/modify-vars/simple.less +8 -0
  228. data/lib/less/js/less/test/browser/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  229. data/lib/less/js/less/test/browser/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  230. data/lib/less/js/less/test/browser/less/postProcessor/postProcessor.less +4 -0
  231. data/lib/less/js/less/test/browser/less/relative-urls/urls.less +34 -0
  232. data/lib/less/js/less/test/browser/less/rootpath-relative/urls.less +33 -0
  233. data/lib/less/js/less/test/browser/less/rootpath/urls.less +33 -0
  234. data/lib/less/js/less/test/browser/less/urls.less +65 -0
  235. data/lib/less/js/less/test/browser/runner-VisitorPlugin-options.js +3 -0
  236. data/lib/less/js/less/test/browser/runner-VisitorPlugin.js +3 -0
  237. data/lib/less/js/less/test/browser/runner-browser-options.js +51 -0
  238. data/lib/less/js/less/test/browser/runner-browser-spec.js +12 -0
  239. data/lib/less/js/less/test/browser/runner-console-errors.js +5 -0
  240. data/lib/less/js/less/test/browser/runner-errors-options.js +4 -0
  241. data/lib/less/js/less/test/browser/runner-errors-spec.js +3 -0
  242. data/lib/less/js/less/test/browser/runner-filemanagerPlugin-options.js +4 -0
  243. data/lib/less/js/less/test/browser/runner-filemanagerPlugin.js +3 -0
  244. data/lib/less/js/less/test/browser/runner-global-vars-options.js +7 -0
  245. data/lib/less/js/less/test/browser/runner-global-vars-spec.js +3 -0
  246. data/lib/less/js/less/test/browser/runner-legacy-options.js +5 -0
  247. data/lib/less/js/less/test/browser/runner-legacy-spec.js +3 -0
  248. data/lib/less/js/less/test/browser/runner-main-options.js +18 -0
  249. data/lib/less/js/less/test/browser/runner-main-spec.js +7 -0
  250. data/lib/less/js/less/test/browser/runner-modify-vars-options.js +5 -0
  251. data/lib/less/js/less/test/browser/runner-modify-vars-spec.js +33 -0
  252. data/lib/less/js/less/test/browser/runner-no-js-errors-options.js +4 -0
  253. data/lib/less/js/less/test/browser/runner-no-js-errors-spec.js +3 -0
  254. data/lib/less/js/less/test/browser/runner-postProcessor-options.js +5 -0
  255. data/lib/less/js/less/test/browser/runner-postProcessor.js +3 -0
  256. data/lib/less/js/less/test/browser/runner-postProcessorPlugin-options.js +3 -0
  257. data/lib/less/js/less/test/browser/runner-postProcessorPlugin.js +3 -0
  258. data/lib/less/js/less/test/browser/runner-preProcessorPlugin-options.js +3 -0
  259. data/lib/less/js/less/test/browser/runner-preProcessorPlugin.js +3 -0
  260. data/lib/less/js/less/test/browser/runner-production-options.js +3 -0
  261. data/lib/less/js/less/test/browser/runner-production-spec.js +5 -0
  262. data/lib/less/js/less/test/browser/runner-relative-urls-options.js +3 -0
  263. data/lib/less/js/less/test/browser/runner-relative-urls-spec.js +3 -0
  264. data/lib/less/js/less/test/browser/runner-rootpath-options.js +3 -0
  265. data/lib/less/js/less/test/browser/runner-rootpath-relative-options.js +4 -0
  266. data/lib/less/js/less/test/browser/runner-rootpath-relative-spec.js +3 -0
  267. data/lib/less/js/less/test/browser/runner-rootpath-spec.js +3 -0
  268. data/lib/less/js/less/test/browser/runner-strict-units-options.js +5 -0
  269. data/lib/less/js/less/test/browser/runner-strict-units-spec.js +3 -0
  270. data/lib/less/js/less/test/browser/test-runner-template.tmpl +95 -0
  271. data/lib/less/js/less/test/copy-bom.js +72 -0
  272. data/lib/less/js/less/test/css/charsets.css +1 -0
  273. data/lib/less/js/less/test/css/colors.css +87 -0
  274. data/lib/less/js/less/test/css/comments.css +83 -0
  275. data/lib/less/js/less/test/css/comments2.css +18 -0
  276. data/lib/less/js/less/test/css/compression/compression.css +3 -0
  277. data/lib/less/js/less/test/css/css-3.css +157 -0
  278. data/lib/less/js/less/test/css/css-escapes.css +24 -0
  279. data/lib/less/js/less/test/css/css-guards.css +37 -0
  280. data/lib/less/js/less/test/css/css.css +95 -0
  281. data/lib/less/js/less/test/css/debug/linenumbers-all.css +50 -0
  282. data/lib/less/js/less/test/css/debug/linenumbers-comments.css +41 -0
  283. data/lib/less/js/less/test/css/debug/linenumbers-mediaquery.css +41 -0
  284. data/lib/less/js/less/test/css/detached-rulesets.css +76 -0
  285. data/lib/less/js/less/test/css/directives-bubling.css +119 -0
  286. data/lib/less/js/less/test/css/empty.css +0 -0
  287. data/lib/less/js/less/test/css/extend-chaining.css +81 -0
  288. data/lib/less/js/less/test/css/extend-clearfix.css +19 -0
  289. data/lib/less/js/less/test/css/extend-exact.css +37 -0
  290. data/lib/less/js/less/test/css/extend-media.css +24 -0
  291. data/lib/less/js/less/test/css/extend-nest.css +57 -0
  292. data/lib/less/js/less/test/css/extend-selector.css +87 -0
  293. data/lib/less/js/less/test/css/extend.css +76 -0
  294. data/lib/less/js/less/test/css/extract-and-length.css +133 -0
  295. data/lib/less/js/less/test/css/filemanagerPlugin/filemanager.css +3 -0
  296. data/lib/less/js/less/test/css/functions.css +201 -0
  297. data/lib/less/js/less/test/css/globalVars/extended.css +12 -0
  298. data/lib/less/js/less/test/css/globalVars/simple.css +6 -0
  299. data/lib/less/js/less/test/css/ie-filters.css +9 -0
  300. data/lib/less/js/less/test/css/import-inline.css +8 -0
  301. data/lib/less/js/less/test/css/import-interpolation.css +13 -0
  302. data/lib/less/js/less/test/css/import-once.css +15 -0
  303. data/lib/less/js/less/test/css/import-reference-issues.css +24 -0
  304. data/lib/less/js/less/test/css/import-reference.css +97 -0
  305. data/lib/less/js/less/test/css/import.css +49 -0
  306. data/lib/less/js/less/test/css/include-path-string/include-path-string.css +3 -0
  307. data/lib/less/js/less/test/css/include-path/include-path.css +9 -0
  308. data/lib/less/js/less/test/css/javascript.css +28 -0
  309. data/lib/less/js/less/test/css/lazy-eval.css +3 -0
  310. data/lib/less/js/less/test/css/legacy/legacy.css +21 -0
  311. data/lib/less/js/less/test/css/media.css +218 -0
  312. data/lib/less/js/less/test/css/merge.css +34 -0
  313. data/lib/less/js/less/test/css/mixins-args.css +163 -0
  314. data/lib/less/js/less/test/css/mixins-closure.css +9 -0
  315. data/lib/less/js/less/test/css/mixins-guards-default-func.css +129 -0
  316. data/lib/less/js/less/test/css/mixins-guards.css +211 -0
  317. data/lib/less/js/less/test/css/mixins-important.css +57 -0
  318. data/lib/less/js/less/test/css/mixins-interpolated.css +43 -0
  319. data/lib/less/js/less/test/css/mixins-named-args.css +27 -0
  320. data/lib/less/js/less/test/css/mixins-nested.css +14 -0
  321. data/lib/less/js/less/test/css/mixins-pattern.css +51 -0
  322. data/lib/less/js/less/test/css/mixins.css +144 -0
  323. data/lib/less/js/less/test/css/modifyVars/extended.css +9 -0
  324. data/lib/less/js/less/test/css/no-output.css +0 -0
  325. data/lib/less/js/less/test/css/no-strict-math/mixins-guards.css +12 -0
  326. data/lib/less/js/less/test/css/no-strict-math/no-sm-operations.css +12 -0
  327. data/lib/less/js/less/test/css/operations.css +50 -0
  328. data/lib/less/js/less/test/css/parens.css +36 -0
  329. data/lib/less/js/less/test/css/plugin.css +44 -0
  330. data/lib/less/js/less/test/css/postProcessorPlugin/postProcessor.css +4 -0
  331. data/lib/less/js/less/test/css/preProcessorPlugin/preProcessor.css +3 -0
  332. data/lib/less/js/less/test/css/property-name-interp.css +21 -0
  333. data/lib/less/js/less/test/css/rulesets.css +33 -0
  334. data/lib/less/js/less/test/css/scope.css +38 -0
  335. data/lib/less/js/less/test/css/selectors.css +170 -0
  336. data/lib/less/js/less/test/css/static-urls/urls.css +46 -0
  337. data/lib/less/js/less/test/css/strict-units/strict-units.css +4 -0
  338. data/lib/less/js/less/test/css/strings.css +55 -0
  339. data/lib/less/js/less/test/css/url-args/urls.css +56 -0
  340. data/lib/less/js/less/test/css/urls.css +87 -0
  341. data/lib/less/js/less/test/css/variables-in-at-rules.css +18 -0
  342. data/lib/less/js/less/test/css/variables.css +68 -0
  343. data/lib/less/js/less/test/css/visitorPlugin/visitor.css +3 -0
  344. data/lib/less/js/less/test/css/whitespace.css +42 -0
  345. data/lib/less/js/less/test/data/data-uri-fail.png +0 -0
  346. data/lib/less/js/less/test/data/image.jpg +0 -0
  347. data/lib/less/js/less/test/data/image.svg +4 -0
  348. data/lib/less/js/less/test/data/page.html +1 -0
  349. data/lib/less/js/less/test/index.js +59 -0
  350. data/lib/less/js/less/test/less-test.js +386 -0
  351. data/lib/less/js/less/test/less/charsets.less +3 -0
  352. data/lib/less/js/less/test/less/colors.less +98 -0
  353. data/lib/less/js/less/test/less/comments.less +102 -0
  354. data/lib/less/js/less/test/less/comments2.less +31 -0
  355. data/lib/less/js/less/test/less/compression/compression.less +36 -0
  356. data/lib/less/js/less/test/less/css-3.less +162 -0
  357. data/lib/less/js/less/test/less/css-escapes.less +33 -0
  358. data/lib/less/js/less/test/less/css-guards.less +103 -0
  359. data/lib/less/js/less/test/less/css.less +108 -0
  360. data/lib/less/js/less/test/less/debug/import/test.less +25 -0
  361. data/lib/less/js/less/test/less/debug/linenumbers.less +33 -0
  362. data/lib/less/js/less/test/less/detached-rulesets.less +112 -0
  363. data/lib/less/js/less/test/less/directives-bubling.less +142 -0
  364. data/lib/less/js/less/test/less/empty.less +0 -0
  365. data/lib/less/js/less/test/less/errors/add-mixed-units.less +3 -0
  366. data/lib/less/js/less/test/less/errors/add-mixed-units.txt +4 -0
  367. data/lib/less/js/less/test/less/errors/add-mixed-units2.less +3 -0
  368. data/lib/less/js/less/test/less/errors/add-mixed-units2.txt +4 -0
  369. data/lib/less/js/less/test/less/errors/at-rules-undefined-var.less +4 -0
  370. data/lib/less/js/less/test/less/errors/at-rules-undefined-var.txt +4 -0
  371. data/lib/less/js/less/test/less/errors/bad-variable-declaration1.less +1 -0
  372. data/lib/less/js/less/test/less/errors/bad-variable-declaration1.txt +2 -0
  373. data/lib/less/js/less/test/less/errors/color-func-invalid-color.less +3 -0
  374. data/lib/less/js/less/test/less/errors/color-func-invalid-color.txt +4 -0
  375. data/lib/less/js/less/test/less/errors/color-invalid-hex-code.less +3 -0
  376. data/lib/less/js/less/test/less/errors/color-invalid-hex-code.txt +4 -0
  377. data/lib/less/js/less/test/less/errors/color-invalid-hex-code2.less +3 -0
  378. data/lib/less/js/less/test/less/errors/color-invalid-hex-code2.txt +4 -0
  379. data/lib/less/js/less/test/less/errors/css-guard-default-func.less +4 -0
  380. data/lib/less/js/less/test/less/errors/css-guard-default-func.txt +4 -0
  381. data/lib/less/js/less/test/less/errors/detached-ruleset-1.less +6 -0
  382. data/lib/less/js/less/test/less/errors/detached-ruleset-1.txt +4 -0
  383. data/lib/less/js/less/test/less/errors/detached-ruleset-2.less +6 -0
  384. data/lib/less/js/less/test/less/errors/detached-ruleset-2.txt +4 -0
  385. data/lib/less/js/less/test/less/errors/detached-ruleset-3.less +4 -0
  386. data/lib/less/js/less/test/less/errors/detached-ruleset-3.txt +4 -0
  387. data/lib/less/js/less/test/less/errors/detached-ruleset-5.less +4 -0
  388. data/lib/less/js/less/test/less/errors/detached-ruleset-5.txt +3 -0
  389. data/lib/less/js/less/test/less/errors/detached-ruleset-6.less +5 -0
  390. data/lib/less/js/less/test/less/errors/detached-ruleset-6.txt +4 -0
  391. data/lib/less/js/less/test/less/errors/divide-mixed-units.less +3 -0
  392. data/lib/less/js/less/test/less/errors/divide-mixed-units.txt +4 -0
  393. data/lib/less/js/less/test/less/errors/extend-no-selector.less +3 -0
  394. data/lib/less/js/less/test/less/errors/extend-no-selector.txt +3 -0
  395. data/lib/less/js/less/test/less/errors/extend-not-at-end.less +3 -0
  396. data/lib/less/js/less/test/less/errors/extend-not-at-end.txt +3 -0
  397. data/lib/less/js/less/test/less/errors/import-malformed.less +1 -0
  398. data/lib/less/js/less/test/less/errors/import-malformed.txt +3 -0
  399. data/lib/less/js/less/test/less/errors/import-missing.less +6 -0
  400. data/lib/less/js/less/test/less/errors/import-missing.txt +3 -0
  401. data/lib/less/js/less/test/less/errors/import-no-semi.less +1 -0
  402. data/lib/less/js/less/test/less/errors/import-no-semi.txt +2 -0
  403. data/lib/less/js/less/test/less/errors/import-subfolder1.less +1 -0
  404. data/lib/less/js/less/test/less/errors/import-subfolder1.txt +3 -0
  405. data/lib/less/js/less/test/less/errors/import-subfolder2.less +1 -0
  406. data/lib/less/js/less/test/less/errors/import-subfolder2.txt +4 -0
  407. data/lib/less/js/less/test/less/errors/imports/import-subfolder1.less +1 -0
  408. data/lib/less/js/less/test/less/errors/imports/import-subfolder2.less +1 -0
  409. data/lib/less/js/less/test/less/errors/imports/import-test.less +4 -0
  410. data/lib/less/js/less/test/less/errors/imports/subfolder/mixin-not-defined.less +1 -0
  411. data/lib/less/js/less/test/less/errors/imports/subfolder/parse-error-curly-bracket.less +1 -0
  412. data/lib/less/js/less/test/less/errors/javascript-error.less +3 -0
  413. data/lib/less/js/less/test/less/errors/javascript-error.txt +4 -0
  414. data/lib/less/js/less/test/less/errors/javascript-undefined-var.less +3 -0
  415. data/lib/less/js/less/test/less/errors/javascript-undefined-var.txt +4 -0
  416. data/lib/less/js/less/test/less/errors/mixed-mixin-definition-args-1.less +6 -0
  417. data/lib/less/js/less/test/less/errors/mixed-mixin-definition-args-1.txt +4 -0
  418. data/lib/less/js/less/test/less/errors/mixed-mixin-definition-args-2.less +6 -0
  419. data/lib/less/js/less/test/less/errors/mixed-mixin-definition-args-2.txt +4 -0
  420. data/lib/less/js/less/test/less/errors/mixin-not-defined.less +11 -0
  421. data/lib/less/js/less/test/less/errors/mixin-not-defined.txt +3 -0
  422. data/lib/less/js/less/test/less/errors/mixin-not-matched.less +6 -0
  423. data/lib/less/js/less/test/less/errors/mixin-not-matched.txt +3 -0
  424. data/lib/less/js/less/test/less/errors/mixin-not-matched2.less +6 -0
  425. data/lib/less/js/less/test/less/errors/mixin-not-matched2.txt +3 -0
  426. data/lib/less/js/less/test/less/errors/mixin-not-visible-in-scope-1.less +9 -0
  427. data/lib/less/js/less/test/less/errors/mixin-not-visible-in-scope-1.txt +4 -0
  428. data/lib/less/js/less/test/less/errors/mixins-guards-default-func-1.less +9 -0
  429. data/lib/less/js/less/test/less/errors/mixins-guards-default-func-1.txt +4 -0
  430. data/lib/less/js/less/test/less/errors/mixins-guards-default-func-2.less +9 -0
  431. data/lib/less/js/less/test/less/errors/mixins-guards-default-func-2.txt +4 -0
  432. data/lib/less/js/less/test/less/errors/mixins-guards-default-func-3.less +9 -0
  433. data/lib/less/js/less/test/less/errors/mixins-guards-default-func-3.txt +4 -0
  434. data/lib/less/js/less/test/less/errors/multiple-guards-on-css-selectors.less +4 -0
  435. data/lib/less/js/less/test/less/errors/multiple-guards-on-css-selectors.txt +4 -0
  436. data/lib/less/js/less/test/less/errors/multiple-guards-on-css-selectors2.less +4 -0
  437. data/lib/less/js/less/test/less/errors/multiple-guards-on-css-selectors2.txt +4 -0
  438. data/lib/less/js/less/test/less/errors/multiply-mixed-units.less +7 -0
  439. data/lib/less/js/less/test/less/errors/multiply-mixed-units.txt +4 -0
  440. data/lib/less/js/less/test/less/errors/parens-error-1.less +3 -0
  441. data/lib/less/js/less/test/less/errors/parens-error-1.txt +4 -0
  442. data/lib/less/js/less/test/less/errors/parens-error-2.less +3 -0
  443. data/lib/less/js/less/test/less/errors/parens-error-2.txt +4 -0
  444. data/lib/less/js/less/test/less/errors/parens-error-3.less +3 -0
  445. data/lib/less/js/less/test/less/errors/parens-error-3.txt +4 -0
  446. data/lib/less/js/less/test/less/errors/parse-error-curly-bracket.less +4 -0
  447. data/lib/less/js/less/test/less/errors/parse-error-curly-bracket.txt +4 -0
  448. data/lib/less/js/less/test/less/errors/parse-error-media-no-block-1.less +5 -0
  449. data/lib/less/js/less/test/less/errors/parse-error-media-no-block-1.txt +3 -0
  450. data/lib/less/js/less/test/less/errors/parse-error-media-no-block-2.less +1 -0
  451. data/lib/less/js/less/test/less/errors/parse-error-media-no-block-2.txt +2 -0
  452. data/lib/less/js/less/test/less/errors/parse-error-media-no-block-3.less +4 -0
  453. data/lib/less/js/less/test/less/errors/parse-error-media-no-block-3.txt +3 -0
  454. data/lib/less/js/less/test/less/errors/parse-error-missing-bracket.less +2 -0
  455. data/lib/less/js/less/test/less/errors/parse-error-missing-bracket.txt +3 -0
  456. data/lib/less/js/less/test/less/errors/parse-error-missing-parens.less +5 -0
  457. data/lib/less/js/less/test/less/errors/parse-error-missing-parens.txt +3 -0
  458. data/lib/less/js/less/test/less/errors/parse-error-with-import.less +13 -0
  459. data/lib/less/js/less/test/less/errors/parse-error-with-import.txt +4 -0
  460. data/lib/less/js/less/test/less/errors/percentage-missing-space.less +3 -0
  461. data/lib/less/js/less/test/less/errors/percentage-missing-space.txt +4 -0
  462. data/lib/less/js/less/test/less/errors/percentage-non-number-argument.less +3 -0
  463. data/lib/less/js/less/test/less/errors/percentage-non-number-argument.txt +4 -0
  464. data/lib/less/js/less/test/less/errors/property-asterisk-only-name.less +3 -0
  465. data/lib/less/js/less/test/less/errors/property-asterisk-only-name.txt +4 -0
  466. data/lib/less/js/less/test/less/errors/property-ie5-hack.less +3 -0
  467. data/lib/less/js/less/test/less/errors/property-ie5-hack.txt +3 -0
  468. data/lib/less/js/less/test/less/errors/property-in-root.less +4 -0
  469. data/lib/less/js/less/test/less/errors/property-in-root.txt +4 -0
  470. data/lib/less/js/less/test/less/errors/property-in-root2.less +1 -0
  471. data/lib/less/js/less/test/less/errors/property-in-root2.txt +4 -0
  472. data/lib/less/js/less/test/less/errors/property-in-root3.less +4 -0
  473. data/lib/less/js/less/test/less/errors/property-in-root3.txt +3 -0
  474. data/lib/less/js/less/test/less/errors/property-interp-not-defined.less +1 -0
  475. data/lib/less/js/less/test/less/errors/property-interp-not-defined.txt +2 -0
  476. data/lib/less/js/less/test/less/errors/recursive-variable.less +1 -0
  477. data/lib/less/js/less/test/less/errors/recursive-variable.txt +2 -0
  478. data/lib/less/js/less/test/less/errors/single-character.less +1 -0
  479. data/lib/less/js/less/test/less/errors/single-character.txt +2 -0
  480. data/lib/less/js/less/test/less/errors/svg-gradient1.less +3 -0
  481. data/lib/less/js/less/test/less/errors/svg-gradient1.txt +4 -0
  482. data/lib/less/js/less/test/less/errors/svg-gradient2.less +3 -0
  483. data/lib/less/js/less/test/less/errors/svg-gradient2.txt +4 -0
  484. data/lib/less/js/less/test/less/errors/svg-gradient3.less +3 -0
  485. data/lib/less/js/less/test/less/errors/svg-gradient3.txt +4 -0
  486. data/lib/less/js/less/test/less/errors/svg-gradient4.less +4 -0
  487. data/lib/less/js/less/test/less/errors/svg-gradient4.txt +4 -0
  488. data/lib/less/js/less/test/less/errors/svg-gradient5.less +4 -0
  489. data/lib/less/js/less/test/less/errors/svg-gradient5.txt +4 -0
  490. data/lib/less/js/less/test/less/errors/svg-gradient6.less +4 -0
  491. data/lib/less/js/less/test/less/errors/svg-gradient6.txt +4 -0
  492. data/lib/less/js/less/test/less/errors/unit-function.less +3 -0
  493. data/lib/less/js/less/test/less/errors/unit-function.txt +4 -0
  494. data/lib/less/js/less/test/less/extend-chaining.less +91 -0
  495. data/lib/less/js/less/test/less/extend-clearfix.less +19 -0
  496. data/lib/less/js/less/test/less/extend-exact.less +46 -0
  497. data/lib/less/js/less/test/less/extend-media.less +24 -0
  498. data/lib/less/js/less/test/less/extend-nest.less +65 -0
  499. data/lib/less/js/less/test/less/extend-selector.less +110 -0
  500. data/lib/less/js/less/test/less/extend.less +81 -0
  501. data/lib/less/js/less/test/less/extract-and-length.less +133 -0
  502. data/lib/less/js/less/test/less/filemanagerPlugin/colors.test +1 -0
  503. data/lib/less/js/less/test/less/filemanagerPlugin/filemanager.less +4 -0
  504. data/lib/less/js/less/test/less/functions.less +232 -0
  505. data/lib/less/js/less/test/less/globalVars/extended.json +5 -0
  506. data/lib/less/js/less/test/less/globalVars/extended.less +10 -0
  507. data/lib/less/js/less/test/less/globalVars/simple.json +3 -0
  508. data/lib/less/js/less/test/less/globalVars/simple.less +3 -0
  509. data/lib/less/js/less/test/less/ie-filters.less +15 -0
  510. data/lib/less/js/less/test/less/import-inline.less +3 -0
  511. data/lib/less/js/less/test/less/import-interpolation.less +8 -0
  512. data/lib/less/js/less/test/less/import-once.less +6 -0
  513. data/lib/less/js/less/test/less/import-reference-issues.less +50 -0
  514. data/lib/less/js/less/test/less/import-reference-issues/appender-reference-1968.less +6 -0
  515. data/lib/less/js/less/test/less/import-reference-issues/global-scope-import.less +13 -0
  516. data/lib/less/js/less/test/less/import-reference-issues/global-scope-nested.less +3 -0
  517. data/lib/less/js/less/test/less/import-reference-issues/mixin-1968.less +8 -0
  518. data/lib/less/js/less/test/less/import-reference-issues/multiple-import-nested.less +12 -0
  519. data/lib/less/js/less/test/less/import-reference-issues/multiple-import.less +10 -0
  520. data/lib/less/js/less/test/less/import-reference-issues/simple-mixin.css +3 -0
  521. data/lib/less/js/less/test/less/import-reference-issues/simple-ruleset-2162.less +3 -0
  522. data/lib/less/js/less/test/less/import-reference.less +26 -0
  523. data/lib/less/js/less/test/less/import.less +31 -0
  524. data/lib/less/js/less/test/less/import/css-import.less +1 -0
  525. data/lib/less/js/less/test/less/import/deeper/deeper-2/url-import-2.less +3 -0
  526. data/lib/less/js/less/test/less/import/deeper/deeper-2/url-import.less +1 -0
  527. data/lib/less/js/less/test/less/import/deeper/import-once-test-a.less +1 -0
  528. data/lib/less/js/less/test/less/import/deeper/url-import.less +1 -0
  529. data/lib/less/js/less/test/less/import/import-and-relative-paths-test.less +17 -0
  530. data/lib/less/js/less/test/less/import/import-charset-test.less +1 -0
  531. data/lib/less/js/less/test/less/import/import-inline-invalid-css.less +1 -0
  532. data/lib/less/js/less/test/less/import/import-interpolation.less +2 -0
  533. data/lib/less/js/less/test/less/import/import-interpolation2.less +5 -0
  534. data/lib/less/js/less/test/less/import/import-once-test-c.less +6 -0
  535. data/lib/less/js/less/test/less/import/import-reference.less +98 -0
  536. data/lib/less/js/less/test/less/import/import-test-a.less +5 -0
  537. data/lib/less/js/less/test/less/import/import-test-b.less +8 -0
  538. data/lib/less/js/less/test/less/import/import-test-c.less +6 -0
  539. data/lib/less/js/less/test/less/import/import-test-d.css +1 -0
  540. data/lib/less/js/less/test/less/import/import-test-e.less +2 -0
  541. data/lib/less/js/less/test/less/import/import-test-f.less +5 -0
  542. data/lib/less/js/less/test/less/import/imports/font.less +8 -0
  543. data/lib/less/js/less/test/less/import/imports/logo.less +6 -0
  544. data/lib/less/js/less/test/less/import/interpolation-vars.less +6 -0
  545. data/lib/less/js/less/test/less/import/invalid-css.less +1 -0
  546. data/lib/less/js/less/test/less/import/urls.less +1 -0
  547. data/lib/less/js/less/test/less/include-path-string/include-path-string.less +3 -0
  548. data/lib/less/js/less/test/less/include-path/include-path.less +8 -0
  549. data/lib/less/js/less/test/less/javascript.less +38 -0
  550. data/lib/less/js/less/test/less/lazy-eval.less +6 -0
  551. data/lib/less/js/less/test/less/legacy/legacy.less +21 -0
  552. data/lib/less/js/less/test/less/media.less +234 -0
  553. data/lib/less/js/less/test/less/merge.less +78 -0
  554. data/lib/less/js/less/test/less/mixins-args.less +263 -0
  555. data/lib/less/js/less/test/less/mixins-closure.less +26 -0
  556. data/lib/less/js/less/test/less/mixins-guards-default-func.less +195 -0
  557. data/lib/less/js/less/test/less/mixins-guards.less +358 -0
  558. data/lib/less/js/less/test/less/mixins-important.less +53 -0
  559. data/lib/less/js/less/test/less/mixins-interpolated.less +75 -0
  560. data/lib/less/js/less/test/less/mixins-named-args.less +36 -0
  561. data/lib/less/js/less/test/less/mixins-nested.less +22 -0
  562. data/lib/less/js/less/test/less/mixins-pattern.less +102 -0
  563. data/lib/less/js/less/test/less/mixins.less +145 -0
  564. data/lib/less/js/less/test/less/modifyVars/extended.json +5 -0
  565. data/lib/less/js/less/test/less/modifyVars/extended.less +11 -0
  566. data/lib/less/js/less/test/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  567. data/lib/less/js/less/test/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  568. data/lib/less/js/less/test/less/no-js-errors/no-js-errors.less +3 -0
  569. data/lib/less/js/less/test/less/no-js-errors/no-js-errors.txt +4 -0
  570. data/lib/less/js/less/test/less/no-output.less +2 -0
  571. data/lib/less/js/less/test/less/no-strict-math/mixins-guards.less +25 -0
  572. data/lib/less/js/less/test/less/no-strict-math/no-sm-operations.less +10 -0
  573. data/lib/less/js/less/test/less/operations.less +63 -0
  574. data/lib/less/js/less/test/less/parens.less +45 -0
  575. data/lib/less/js/less/test/less/plugin.less +85 -0
  576. data/lib/less/js/less/test/less/plugin/plugin-global.js +9 -0
  577. data/lib/less/js/less/test/less/plugin/plugin-local.js +8 -0
  578. data/lib/less/js/less/test/less/plugin/plugin-transitive.js +5 -0
  579. data/lib/less/js/less/test/less/plugin/plugin-transitive.less +5 -0
  580. data/lib/less/js/less/test/less/postProcessorPlugin/postProcessor.less +4 -0
  581. data/lib/less/js/less/test/less/preProcessorPlugin/preProcessor.less +3 -0
  582. data/lib/less/js/less/test/less/property-name-interp.less +56 -0
  583. data/lib/less/js/less/test/less/rulesets.less +30 -0
  584. data/lib/less/js/less/test/less/scope.less +104 -0
  585. data/lib/less/js/less/test/less/selectors.less +184 -0
  586. data/lib/less/js/less/test/less/sourcemaps-empty/empty.less +0 -0
  587. data/lib/less/js/less/test/less/sourcemaps-empty/var-defs.less +1 -0
  588. data/lib/less/js/less/test/less/sourcemaps/basic.json +3 -0
  589. data/lib/less/js/less/test/less/sourcemaps/basic.less +27 -0
  590. data/lib/less/js/less/test/less/sourcemaps/imported.css +7 -0
  591. data/lib/less/js/less/test/less/static-urls/urls.less +33 -0
  592. data/lib/less/js/less/test/less/strict-units/strict-units.less +4 -0
  593. data/lib/less/js/less/test/less/strings.less +73 -0
  594. data/lib/less/js/less/test/less/url-args/urls.less +63 -0
  595. data/lib/less/js/less/test/less/urls.less +94 -0
  596. data/lib/less/js/less/test/less/variables-in-at-rules.less +20 -0
  597. data/lib/less/js/less/test/less/variables.less +119 -0
  598. data/lib/less/js/less/test/less/visitorPlugin/visitor.less +4 -0
  599. data/lib/less/js/less/test/less/whitespace.less +44 -0
  600. data/lib/less/js/less/test/modify-vars.js +19 -0
  601. data/lib/less/js/less/test/plugins/filemanager/index.js +19 -0
  602. data/lib/less/js/less/test/plugins/postprocess/index.js +14 -0
  603. data/lib/less/js/less/test/plugins/preprocess/index.js +19 -0
  604. data/lib/less/js/less/test/plugins/visitor/index.js +24 -0
  605. data/lib/less/js/less/test/rhino/test-header.js +15 -0
  606. data/lib/less/js/less/test/sourcemaps/basic.json +1 -0
  607. data/lib/less/js/less/test/sourcemaps/index.html +17 -0
  608. data/lib/less/js/node-mime/.gitignore +2 -0
  609. data/lib/less/js/node-mime/.npmignore +0 -0
  610. data/lib/less/js/node-mime/LICENSE +19 -0
  611. data/lib/less/js/node-mime/README.md +90 -0
  612. data/lib/less/js/node-mime/build/build.js +11 -0
  613. data/lib/less/js/node-mime/build/test.js +57 -0
  614. data/lib/less/js/node-mime/cli.js +8 -0
  615. data/lib/less/js/node-mime/mime.js +108 -0
  616. data/lib/less/js/node-mime/package.json +41 -0
  617. data/lib/less/js/node-mime/types.json +1 -0
  618. data/lib/less/loader.rb +13 -0
  619. data/lib/less/parser.rb +62 -0
  620. data/lib/less/runner.js +20 -0
  621. data/lib/less/version.rb +3 -0
  622. data/script/bootstrap +6 -0
  623. data/script/cibuild +7 -0
  624. data/spec/less/parser_spec.rb +170 -0
  625. data/spec/spec_helper.rb +3 -0
  626. data/spec/support/custom_functions/custom_functions.js +7 -0
  627. data/spec/support/custom_functions/custom_functions.less +3 -0
  628. data/spec/support/faulty/faulty.less +3 -0
  629. data/spec/support/less.js.tests-custom-functions.js +20 -0
  630. data/spec/support/one/one.less +1 -0
  631. data/spec/support/two/two.less +1 -0
  632. metadata +733 -0
@@ -0,0 +1,112 @@
1
+ // Split the input into chunks.
2
+ module.exports = function (input, fail) {
3
+ var len = input.length, level = 0, parenLevel = 0,
4
+ lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace,
5
+ chunks = [], emitFrom = 0,
6
+ chunkerCurrentIndex, currentChunkStartIndex, cc, cc2, matched;
7
+
8
+ function emitChunk(force) {
9
+ var len = chunkerCurrentIndex - emitFrom;
10
+ if (((len < 512) && !force) || !len) {
11
+ return;
12
+ }
13
+ chunks.push(input.slice(emitFrom, chunkerCurrentIndex + 1));
14
+ emitFrom = chunkerCurrentIndex + 1;
15
+ }
16
+
17
+ for (chunkerCurrentIndex = 0; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
18
+ cc = input.charCodeAt(chunkerCurrentIndex);
19
+ if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
20
+ // a-z or whitespace
21
+ continue;
22
+ }
23
+
24
+ switch (cc) {
25
+ case 40: // (
26
+ parenLevel++;
27
+ lastOpeningParen = chunkerCurrentIndex;
28
+ continue;
29
+ case 41: // )
30
+ if (--parenLevel < 0) {
31
+ return fail("missing opening `(`", chunkerCurrentIndex);
32
+ }
33
+ continue;
34
+ case 59: // ;
35
+ if (!parenLevel) { emitChunk(); }
36
+ continue;
37
+ case 123: // {
38
+ level++;
39
+ lastOpening = chunkerCurrentIndex;
40
+ continue;
41
+ case 125: // }
42
+ if (--level < 0) {
43
+ return fail("missing opening `{`", chunkerCurrentIndex);
44
+ }
45
+ if (!level && !parenLevel) { emitChunk(); }
46
+ continue;
47
+ case 92: // \
48
+ if (chunkerCurrentIndex < len - 1) { chunkerCurrentIndex++; continue; }
49
+ return fail("unescaped `\\`", chunkerCurrentIndex);
50
+ case 34:
51
+ case 39:
52
+ case 96: // ", ' and `
53
+ matched = 0;
54
+ currentChunkStartIndex = chunkerCurrentIndex;
55
+ for (chunkerCurrentIndex = chunkerCurrentIndex + 1; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
56
+ cc2 = input.charCodeAt(chunkerCurrentIndex);
57
+ if (cc2 > 96) { continue; }
58
+ if (cc2 == cc) { matched = 1; break; }
59
+ if (cc2 == 92) { // \
60
+ if (chunkerCurrentIndex == len - 1) {
61
+ return fail("unescaped `\\`", chunkerCurrentIndex);
62
+ }
63
+ chunkerCurrentIndex++;
64
+ }
65
+ }
66
+ if (matched) { continue; }
67
+ return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
68
+ case 47: // /, check for comment
69
+ if (parenLevel || (chunkerCurrentIndex == len - 1)) { continue; }
70
+ cc2 = input.charCodeAt(chunkerCurrentIndex + 1);
71
+ if (cc2 == 47) {
72
+ // //, find lnfeed
73
+ for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
74
+ cc2 = input.charCodeAt(chunkerCurrentIndex);
75
+ if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
76
+ }
77
+ } else if (cc2 == 42) {
78
+ // /*, find */
79
+ lastMultiComment = currentChunkStartIndex = chunkerCurrentIndex;
80
+ for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len - 1; chunkerCurrentIndex++) {
81
+ cc2 = input.charCodeAt(chunkerCurrentIndex);
82
+ if (cc2 == 125) { lastMultiCommentEndBrace = chunkerCurrentIndex; }
83
+ if (cc2 != 42) { continue; }
84
+ if (input.charCodeAt(chunkerCurrentIndex + 1) == 47) { break; }
85
+ }
86
+ if (chunkerCurrentIndex == len - 1) {
87
+ return fail("missing closing `*/`", currentChunkStartIndex);
88
+ }
89
+ chunkerCurrentIndex++;
90
+ }
91
+ continue;
92
+ case 42: // *, check for unmatched */
93
+ if ((chunkerCurrentIndex < len - 1) && (input.charCodeAt(chunkerCurrentIndex + 1) == 47)) {
94
+ return fail("unmatched `/*`", chunkerCurrentIndex);
95
+ }
96
+ continue;
97
+ }
98
+ }
99
+
100
+ if (level !== 0) {
101
+ if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
102
+ return fail("missing closing `}` or `*/`", lastOpening);
103
+ } else {
104
+ return fail("missing closing `}`", lastOpening);
105
+ }
106
+ } else if (parenLevel !== 0) {
107
+ return fail("missing closing `)`", lastOpeningParen);
108
+ }
109
+
110
+ emitChunk(true);
111
+ return chunks;
112
+ };
@@ -0,0 +1,259 @@
1
+ var chunker = require('./chunker');
2
+
3
+ module.exports = function() {
4
+ var input, // LeSS input string
5
+ j, // current chunk
6
+ saveStack = [], // holds state for backtracking
7
+ furthest, // furthest index the parser has gone to
8
+ furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause
9
+ chunks, // chunkified input
10
+ current, // current chunk
11
+ currentPos, // index of current chunk, in `input`
12
+ parserInput = {};
13
+
14
+ var CHARCODE_SPACE = 32,
15
+ CHARCODE_TAB = 9,
16
+ CHARCODE_LF = 10,
17
+ CHARCODE_CR = 13,
18
+ CHARCODE_PLUS = 43,
19
+ CHARCODE_COMMA = 44,
20
+ CHARCODE_FORWARD_SLASH = 47,
21
+ CHARCODE_9 = 57;
22
+
23
+ function skipWhitespace(length) {
24
+ var oldi = parserInput.i, oldj = j,
25
+ curr = parserInput.i - currentPos,
26
+ endIndex = parserInput.i + current.length - curr,
27
+ mem = (parserInput.i += length),
28
+ inp = input,
29
+ c, nextChar, comment;
30
+
31
+ for (; parserInput.i < endIndex; parserInput.i++) {
32
+ c = inp.charCodeAt(parserInput.i);
33
+
34
+ if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) {
35
+ nextChar = inp.charAt(parserInput.i + 1);
36
+ if (nextChar === '/') {
37
+ comment = {index: parserInput.i, isLineComment: true};
38
+ var nextNewLine = inp.indexOf("\n", parserInput.i + 2);
39
+ if (nextNewLine < 0) {
40
+ nextNewLine = endIndex;
41
+ }
42
+ parserInput.i = nextNewLine;
43
+ comment.text = inp.substr(comment.i, parserInput.i - comment.i);
44
+ parserInput.commentStore.push(comment);
45
+ continue;
46
+ } else if (nextChar === '*') {
47
+ var nextStarSlash = inp.indexOf("*/", parserInput.i + 2);
48
+ if (nextStarSlash >= 0) {
49
+ comment = {
50
+ index: parserInput.i,
51
+ text: inp.substr(parserInput.i, nextStarSlash + 2 - parserInput.i),
52
+ isLineComment: false
53
+ };
54
+ parserInput.i += comment.text.length - 1;
55
+ parserInput.commentStore.push(comment);
56
+ continue;
57
+ }
58
+ }
59
+ break;
60
+ }
61
+
62
+ if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) {
63
+ break;
64
+ }
65
+ }
66
+
67
+ current = current.slice(length + parserInput.i - mem + curr);
68
+ currentPos = parserInput.i;
69
+
70
+ if (!current.length) {
71
+ if (j < chunks.length - 1) {
72
+ current = chunks[++j];
73
+ skipWhitespace(0); // skip space at the beginning of a chunk
74
+ return true; // things changed
75
+ }
76
+ parserInput.finished = true;
77
+ }
78
+
79
+ return oldi !== parserInput.i || oldj !== j;
80
+ }
81
+
82
+ parserInput.save = function() {
83
+ currentPos = parserInput.i;
84
+ saveStack.push( { current: current, i: parserInput.i, j: j });
85
+ };
86
+ parserInput.restore = function(possibleErrorMessage) {
87
+
88
+ if (parserInput.i > furthest || (parserInput.i === furthest && possibleErrorMessage && !furthestPossibleErrorMessage)) {
89
+ furthest = parserInput.i;
90
+ furthestPossibleErrorMessage = possibleErrorMessage;
91
+ }
92
+ var state = saveStack.pop();
93
+ current = state.current;
94
+ currentPos = parserInput.i = state.i;
95
+ j = state.j;
96
+ };
97
+ parserInput.forget = function() {
98
+ saveStack.pop();
99
+ };
100
+ parserInput.isWhitespace = function (offset) {
101
+ var pos = parserInput.i + (offset || 0),
102
+ code = input.charCodeAt(pos);
103
+ return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF);
104
+ };
105
+
106
+ // Specialization of $(tok)
107
+ parserInput.$re = function(tok) {
108
+ if (parserInput.i > currentPos) {
109
+ current = current.slice(parserInput.i - currentPos);
110
+ currentPos = parserInput.i;
111
+ }
112
+
113
+ var m = tok.exec(current);
114
+ if (!m) {
115
+ return null;
116
+ }
117
+
118
+ skipWhitespace(m[0].length);
119
+ if (typeof m === "string") {
120
+ return m;
121
+ }
122
+
123
+ return m.length === 1 ? m[0] : m;
124
+ };
125
+
126
+ parserInput.$char = function(tok) {
127
+ if (input.charAt(parserInput.i) !== tok) {
128
+ return null;
129
+ }
130
+ skipWhitespace(1);
131
+ return tok;
132
+ };
133
+
134
+ parserInput.$str = function(tok) {
135
+ var tokLength = tok.length;
136
+
137
+ // https://jsperf.com/string-startswith/21
138
+ for (var i = 0; i < tokLength; i++) {
139
+ if (input.charAt(parserInput.i + i) !== tok.charAt(i)) {
140
+ return null;
141
+ }
142
+ }
143
+
144
+ skipWhitespace(tokLength);
145
+ return tok;
146
+ };
147
+
148
+ parserInput.$quoted = function() {
149
+
150
+ var startChar = input.charAt(parserInput.i);
151
+ if (startChar !== "'" && startChar !== '"') {
152
+ return;
153
+ }
154
+ var length = input.length,
155
+ currentPosition = parserInput.i;
156
+
157
+ for (var i = 1; i + currentPosition < length; i++) {
158
+ var nextChar = input.charAt(i + currentPosition);
159
+ switch(nextChar) {
160
+ case "\\":
161
+ i++;
162
+ continue;
163
+ case "\r":
164
+ case "\n":
165
+ break;
166
+ case startChar:
167
+ var str = input.substr(currentPosition, i + 1);
168
+ skipWhitespace(i + 1);
169
+ return str;
170
+ default:
171
+ }
172
+ }
173
+ return null;
174
+ };
175
+
176
+ parserInput.autoCommentAbsorb = true;
177
+ parserInput.commentStore = [];
178
+ parserInput.finished = false;
179
+
180
+ // Same as $(), but don't change the state of the parser,
181
+ // just return the match.
182
+ parserInput.peek = function(tok) {
183
+ if (typeof tok === 'string') {
184
+ // https://jsperf.com/string-startswith/21
185
+ for (var i = 0; i < tok.length; i++) {
186
+ if (input.charAt(parserInput.i + i) !== tok.charAt(i)) {
187
+ return false;
188
+ }
189
+ }
190
+ return true;
191
+ } else {
192
+ return tok.test(current);
193
+ }
194
+ };
195
+
196
+ // Specialization of peek()
197
+ // TODO remove or change some currentChar calls to peekChar
198
+ parserInput.peekChar = function(tok) {
199
+ return input.charAt(parserInput.i) === tok;
200
+ };
201
+
202
+ parserInput.currentChar = function() {
203
+ return input.charAt(parserInput.i);
204
+ };
205
+
206
+ parserInput.getInput = function() {
207
+ return input;
208
+ };
209
+
210
+ parserInput.peekNotNumeric = function() {
211
+ var c = input.charCodeAt(parserInput.i);
212
+ //Is the first char of the dimension 0-9, '.', '+' or '-'
213
+ return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA;
214
+ };
215
+
216
+ parserInput.start = function(str, chunkInput, failFunction) {
217
+ input = str;
218
+ parserInput.i = j = currentPos = furthest = 0;
219
+
220
+ // chunking apparantly makes things quicker (but my tests indicate
221
+ // it might actually make things slower in node at least)
222
+ // and it is a non-perfect parse - it can't recognise
223
+ // unquoted urls, meaning it can't distinguish comments
224
+ // meaning comments with quotes or {}() in them get 'counted'
225
+ // and then lead to parse errors.
226
+ // In addition if the chunking chunks in the wrong place we might
227
+ // not be able to parse a parser statement in one go
228
+ // this is officially deprecated but can be switched on via an option
229
+ // in the case it causes too much performance issues.
230
+ if (chunkInput) {
231
+ chunks = chunker(str, failFunction);
232
+ } else {
233
+ chunks = [str];
234
+ }
235
+
236
+ current = chunks[0];
237
+
238
+ skipWhitespace(0);
239
+ };
240
+
241
+ parserInput.end = function() {
242
+ var message,
243
+ isFinished = parserInput.i >= input.length;
244
+
245
+ if (parserInput.i < furthest) {
246
+ message = furthestPossibleErrorMessage;
247
+ parserInput.i = furthest;
248
+ }
249
+ return {
250
+ isFinished: isFinished,
251
+ furthest: parserInput.i,
252
+ furthestPossibleErrorMessage: message,
253
+ furthestReachedEnd: parserInput.i >= input.length - 1,
254
+ furthestChar: input[parserInput.i]
255
+ };
256
+ };
257
+
258
+ return parserInput;
259
+ };
@@ -0,0 +1,1851 @@
1
+ var LessError = require('../less-error'),
2
+ tree = require("../tree"),
3
+ visitors = require("../visitors"),
4
+ getParserInput = require("./parser-input"),
5
+ utils = require("../utils");
6
+
7
+ //
8
+ // less.js - parser
9
+ //
10
+ // A relatively straight-forward predictive parser.
11
+ // There is no tokenization/lexing stage, the input is parsed
12
+ // in one sweep.
13
+ //
14
+ // To make the parser fast enough to run in the browser, several
15
+ // optimization had to be made:
16
+ //
17
+ // - Matching and slicing on a huge input is often cause of slowdowns.
18
+ // The solution is to chunkify the input into smaller strings.
19
+ // The chunks are stored in the `chunks` var,
20
+ // `j` holds the current chunk index, and `currentPos` holds
21
+ // the index of the current chunk in relation to `input`.
22
+ // This gives us an almost 4x speed-up.
23
+ //
24
+ // - In many cases, we don't need to match individual tokens;
25
+ // for example, if a value doesn't hold any variables, operations
26
+ // or dynamic references, the parser can effectively 'skip' it,
27
+ // treating it as a literal.
28
+ // An example would be '1px solid #000' - which evaluates to itself,
29
+ // we don't need to know what the individual components are.
30
+ // The drawback, of course is that you don't get the benefits of
31
+ // syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
32
+ // and a smaller speed-up in the code-gen.
33
+ //
34
+ //
35
+ // Token matching is done with the `$` function, which either takes
36
+ // a terminal string or regexp, or a non-terminal function to call.
37
+ // It also takes care of moving all the indices forwards.
38
+ //`
39
+ //
40
+ var Parser = function Parser(context, imports, fileInfo) {
41
+ var parsers,
42
+ parserInput = getParserInput();
43
+
44
+ function error(msg, type) {
45
+ throw new LessError(
46
+ {
47
+ index: parserInput.i,
48
+ filename: fileInfo.filename,
49
+ type: type || 'Syntax',
50
+ message: msg
51
+ },
52
+ imports
53
+ );
54
+ }
55
+
56
+ function expect(arg, msg, index) {
57
+ // some older browsers return typeof 'function' for RegExp
58
+ var result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
59
+ if (result) {
60
+ return result;
61
+ }
62
+ error(msg || (typeof arg === 'string' ? "expected '" + arg + "' got '" + parserInput.currentChar() + "'"
63
+ : "unexpected token"));
64
+ }
65
+
66
+ // Specialization of expect()
67
+ function expectChar(arg, msg) {
68
+ if (parserInput.$char(arg)) {
69
+ return arg;
70
+ }
71
+ error(msg || "expected '" + arg + "' got '" + parserInput.currentChar() + "'");
72
+ }
73
+
74
+ function getDebugInfo(index) {
75
+ var filename = fileInfo.filename;
76
+
77
+ return {
78
+ lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1,
79
+ fileName: filename
80
+ };
81
+ }
82
+
83
+ //
84
+ // The Parser
85
+ //
86
+ return {
87
+
88
+ //
89
+ // Parse an input string into an abstract syntax tree,
90
+ // @param str A string containing 'less' markup
91
+ // @param callback call `callback` when done.
92
+ // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply
93
+ //
94
+ parse: function (str, callback, additionalData) {
95
+ var root, error = null, globalVars, modifyVars, ignored, preText = "";
96
+
97
+ globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : '';
98
+ modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + Parser.serializeVars(additionalData.modifyVars) : '';
99
+
100
+ if (context.pluginManager) {
101
+ var preProcessors = context.pluginManager.getPreProcessors();
102
+ for (var i = 0; i < preProcessors.length; i++) {
103
+ str = preProcessors[i].process(str, { context: context, imports: imports, fileInfo: fileInfo });
104
+ }
105
+ }
106
+
107
+ if (globalVars || (additionalData && additionalData.banner)) {
108
+ preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars;
109
+ ignored = imports.contentsIgnoredChars;
110
+ ignored[fileInfo.filename] = ignored[fileInfo.filename] || 0;
111
+ ignored[fileInfo.filename] += preText.length;
112
+ }
113
+
114
+ str = str.replace(/\r\n?/g, '\n');
115
+ // Remove potential UTF Byte Order Mark
116
+ str = preText + str.replace(/^\uFEFF/, '') + modifyVars;
117
+ imports.contents[fileInfo.filename] = str;
118
+
119
+ // Start with the primary rule.
120
+ // The whole syntax tree is held under a Ruleset node,
121
+ // with the `root` property set to true, so no `{}` are
122
+ // output. The callback is called when the input is parsed.
123
+ try {
124
+ parserInput.start(str, context.chunkInput, function fail(msg, index) {
125
+ throw new LessError({
126
+ index: index,
127
+ type: 'Parse',
128
+ message: msg,
129
+ filename: fileInfo.filename
130
+ }, imports);
131
+ });
132
+
133
+ root = new(tree.Ruleset)(null, this.parsers.primary());
134
+ root.root = true;
135
+ root.firstRoot = true;
136
+ } catch (e) {
137
+ return callback(new LessError(e, imports, fileInfo.filename));
138
+ }
139
+
140
+ // If `i` is smaller than the `input.length - 1`,
141
+ // it means the parser wasn't able to parse the whole
142
+ // string, so we've got a parsing error.
143
+ //
144
+ // We try to extract a \n delimited string,
145
+ // showing the line where the parse error occurred.
146
+ // We split it up into two parts (the part which parsed,
147
+ // and the part which didn't), so we can color them differently.
148
+ var endInfo = parserInput.end();
149
+ if (!endInfo.isFinished) {
150
+
151
+ var message = endInfo.furthestPossibleErrorMessage;
152
+
153
+ if (!message) {
154
+ message = "Unrecognised input";
155
+ if (endInfo.furthestChar === '}') {
156
+ message += ". Possibly missing opening '{'";
157
+ } else if (endInfo.furthestChar === ')') {
158
+ message += ". Possibly missing opening '('";
159
+ } else if (endInfo.furthestReachedEnd) {
160
+ message += ". Possibly missing something";
161
+ }
162
+ }
163
+
164
+ error = new LessError({
165
+ type: "Parse",
166
+ message: message,
167
+ index: endInfo.furthest,
168
+ filename: fileInfo.filename
169
+ }, imports);
170
+ }
171
+
172
+ var finish = function (e) {
173
+ e = error || e || imports.error;
174
+
175
+ if (e) {
176
+ if (!(e instanceof LessError)) {
177
+ e = new LessError(e, imports, fileInfo.filename);
178
+ }
179
+
180
+ return callback(e);
181
+ }
182
+ else {
183
+ return callback(null, root);
184
+ }
185
+ };
186
+
187
+ if (context.processImports !== false) {
188
+ new visitors.ImportVisitor(imports, finish)
189
+ .run(root);
190
+ } else {
191
+ return finish();
192
+ }
193
+ },
194
+
195
+ //
196
+ // Here in, the parsing rules/functions
197
+ //
198
+ // The basic structure of the syntax tree generated is as follows:
199
+ //
200
+ // Ruleset -> Rule -> Value -> Expression -> Entity
201
+ //
202
+ // Here's some Less code:
203
+ //
204
+ // .class {
205
+ // color: #fff;
206
+ // border: 1px solid #000;
207
+ // width: @w + 4px;
208
+ // > .child {...}
209
+ // }
210
+ //
211
+ // And here's what the parse tree might look like:
212
+ //
213
+ // Ruleset (Selector '.class', [
214
+ // Rule ("color", Value ([Expression [Color #fff]]))
215
+ // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
216
+ // Rule ("width", Value ([Expression [Operation " + " [Variable "@w"][Dimension 4px]]]))
217
+ // Ruleset (Selector [Element '>', '.child'], [...])
218
+ // ])
219
+ //
220
+ // In general, most rules will try to parse a token with the `$re()` function, and if the return
221
+ // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
222
+ // first, before parsing, that's when we use `peek()`.
223
+ //
224
+ parsers: parsers = {
225
+ //
226
+ // The `primary` rule is the *entry* and *exit* point of the parser.
227
+ // The rules here can appear at any level of the parse tree.
228
+ //
229
+ // The recursive nature of the grammar is an interplay between the `block`
230
+ // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
231
+ // as represented by this simplified grammar:
232
+ //
233
+ // primary → (ruleset | rule)+
234
+ // ruleset → selector+ block
235
+ // block → '{' primary '}'
236
+ //
237
+ // Only at one point is the primary rule not called from the
238
+ // block rule: at the root level.
239
+ //
240
+ primary: function () {
241
+ var mixin = this.mixin, root = [], node;
242
+
243
+ while (true) {
244
+ while (true) {
245
+ node = this.comment();
246
+ if (!node) { break; }
247
+ root.push(node);
248
+ }
249
+ // always process comments before deciding if finished
250
+ if (parserInput.finished) {
251
+ break;
252
+ }
253
+ if (parserInput.peek('}')) {
254
+ break;
255
+ }
256
+
257
+ node = this.extendRule();
258
+ if (node) {
259
+ root = root.concat(node);
260
+ continue;
261
+ }
262
+
263
+ node = mixin.definition() || this.rule() || this.ruleset() ||
264
+ mixin.call() || this.rulesetCall() || this.directive();
265
+ if (node) {
266
+ root.push(node);
267
+ } else {
268
+ var foundSemiColon = false;
269
+ while (parserInput.$char(";")) {
270
+ foundSemiColon = true;
271
+ }
272
+ if (!foundSemiColon) {
273
+ break;
274
+ }
275
+ }
276
+ }
277
+
278
+ return root;
279
+ },
280
+
281
+ // comments are collected by the main parsing mechanism and then assigned to nodes
282
+ // where the current structure allows it
283
+ comment: function () {
284
+ if (parserInput.commentStore.length) {
285
+ var comment = parserInput.commentStore.shift();
286
+ return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, fileInfo);
287
+ }
288
+ },
289
+
290
+ //
291
+ // Entities are tokens which can be found inside an Expression
292
+ //
293
+ entities: {
294
+ //
295
+ // A string, which supports escaping " and '
296
+ //
297
+ // "milky way" 'he\'s the one!'
298
+ //
299
+ quoted: function () {
300
+ var str, index = parserInput.i, isEscaped = false;
301
+
302
+ parserInput.save();
303
+ if (parserInput.$char("~")) {
304
+ isEscaped = true;
305
+ }
306
+ str = parserInput.$quoted();
307
+ if (!str) {
308
+ parserInput.restore();
309
+ return;
310
+ }
311
+ parserInput.forget();
312
+
313
+ return new(tree.Quoted)(str.charAt(0), str.substr(1, str.length - 2), isEscaped, index, fileInfo);
314
+ },
315
+
316
+ //
317
+ // A catch-all word, such as:
318
+ //
319
+ // black border-collapse
320
+ //
321
+ keyword: function () {
322
+ var k = parserInput.$char("%") || parserInput.$re(/^[_A-Za-z-][_A-Za-z0-9-]*/);
323
+ if (k) {
324
+ return tree.Color.fromKeyword(k) || new(tree.Keyword)(k);
325
+ }
326
+ },
327
+
328
+ //
329
+ // A function call
330
+ //
331
+ // rgb(255, 0, 255)
332
+ //
333
+ // We also try to catch IE's `alpha()`, but let the `alpha` parser
334
+ // deal with the details.
335
+ //
336
+ // The arguments are parsed with the `entities.arguments` parser.
337
+ //
338
+ call: function () {
339
+ var name, nameLC, args, alpha, index = parserInput.i;
340
+
341
+ // http://jsperf.com/case-insensitive-regex-vs-strtolower-then-regex/18
342
+ if (parserInput.peek(/^url\(/i)) {
343
+ return;
344
+ }
345
+
346
+ parserInput.save();
347
+
348
+ name = parserInput.$re(/^([\w-]+|%|progid:[\w\.]+)\(/);
349
+ if (!name) { parserInput.forget(); return; }
350
+
351
+ name = name[1];
352
+ nameLC = name.toLowerCase();
353
+
354
+ if (nameLC === 'alpha') {
355
+ alpha = parsers.alpha();
356
+ if (alpha) {
357
+ parserInput.forget();
358
+ return alpha;
359
+ }
360
+ }
361
+
362
+ args = this.arguments();
363
+
364
+ if (! parserInput.$char(')')) {
365
+ parserInput.restore("Could not parse call arguments or missing ')'");
366
+ return;
367
+ }
368
+
369
+ parserInput.forget();
370
+ return new(tree.Call)(name, args, index, fileInfo);
371
+ },
372
+ arguments: function () {
373
+ var args = [], arg;
374
+
375
+ while (true) {
376
+ arg = this.assignment() || parsers.expression();
377
+ if (!arg) {
378
+ break;
379
+ }
380
+ args.push(arg);
381
+ if (! parserInput.$char(',')) {
382
+ break;
383
+ }
384
+ }
385
+ return args;
386
+ },
387
+ literal: function () {
388
+ return this.dimension() ||
389
+ this.color() ||
390
+ this.quoted() ||
391
+ this.unicodeDescriptor();
392
+ },
393
+
394
+ // Assignments are argument entities for calls.
395
+ // They are present in ie filter properties as shown below.
396
+ //
397
+ // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
398
+ //
399
+
400
+ assignment: function () {
401
+ var key, value;
402
+ parserInput.save();
403
+ key = parserInput.$re(/^\w+(?=\s?=)/i);
404
+ if (!key) {
405
+ parserInput.restore();
406
+ return;
407
+ }
408
+ if (!parserInput.$char('=')) {
409
+ parserInput.restore();
410
+ return;
411
+ }
412
+ value = parsers.entity();
413
+ if (value) {
414
+ parserInput.forget();
415
+ return new(tree.Assignment)(key, value);
416
+ } else {
417
+ parserInput.restore();
418
+ }
419
+ },
420
+
421
+ //
422
+ // Parse url() tokens
423
+ //
424
+ // We use a specific rule for urls, because they don't really behave like
425
+ // standard function calls. The difference is that the argument doesn't have
426
+ // to be enclosed within a string, so it can't be parsed as an Expression.
427
+ //
428
+ url: function () {
429
+ var value, index = parserInput.i;
430
+
431
+ parserInput.autoCommentAbsorb = false;
432
+
433
+ if (!parserInput.$str("url(")) {
434
+ parserInput.autoCommentAbsorb = true;
435
+ return;
436
+ }
437
+
438
+ value = this.quoted() || this.variable() ||
439
+ parserInput.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
440
+
441
+ parserInput.autoCommentAbsorb = true;
442
+
443
+ expectChar(')');
444
+
445
+ return new(tree.URL)((value.value != null || value instanceof tree.Variable) ?
446
+ value : new(tree.Anonymous)(value), index, fileInfo);
447
+ },
448
+
449
+ //
450
+ // A Variable entity, such as `@fink`, in
451
+ //
452
+ // width: @fink + 2px
453
+ //
454
+ // We use a different parser for variable definitions,
455
+ // see `parsers.variable`.
456
+ //
457
+ variable: function () {
458
+ var name, index = parserInput.i;
459
+
460
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
461
+ return new(tree.Variable)(name, index, fileInfo);
462
+ }
463
+ },
464
+
465
+ // A variable entity useing the protective {} e.g. @{var}
466
+ variableCurly: function () {
467
+ var curly, index = parserInput.i;
468
+
469
+ if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
470
+ return new(tree.Variable)("@" + curly[1], index, fileInfo);
471
+ }
472
+ },
473
+
474
+ //
475
+ // A Hexadecimal color
476
+ //
477
+ // #4F3C2F
478
+ //
479
+ // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
480
+ //
481
+ color: function () {
482
+ var rgb;
483
+
484
+ if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
485
+ // strip colons, brackets, whitespaces and other characters that should not
486
+ // definitely be part of color string
487
+ var colorCandidateString = rgb.input.match(/^#([\w]+).*/);
488
+ colorCandidateString = colorCandidateString[1];
489
+ if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters
490
+ error("Invalid HEX color code");
491
+ }
492
+ return new(tree.Color)(rgb[1], undefined, '#' + colorCandidateString);
493
+ }
494
+ },
495
+
496
+ colorKeyword: function () {
497
+ parserInput.save();
498
+ var autoCommentAbsorb = parserInput.autoCommentAbsorb;
499
+ parserInput.autoCommentAbsorb = false;
500
+ var k = parserInput.$re(/^[A-Za-z]+/);
501
+ parserInput.autoCommentAbsorb = autoCommentAbsorb;
502
+ if (!k) {
503
+ parserInput.forget();
504
+ return;
505
+ }
506
+ parserInput.restore();
507
+ var color = tree.Color.fromKeyword(k);
508
+ if (color) {
509
+ parserInput.$str(k);
510
+ return color;
511
+ }
512
+ },
513
+
514
+ //
515
+ // A Dimension, that is, a number and a unit
516
+ //
517
+ // 0.5em 95%
518
+ //
519
+ dimension: function () {
520
+ if (parserInput.peekNotNumeric()) {
521
+ return;
522
+ }
523
+
524
+ var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z_]+)?/i);
525
+ if (value) {
526
+ return new(tree.Dimension)(value[1], value[2]);
527
+ }
528
+ },
529
+
530
+ //
531
+ // A unicode descriptor, as is used in unicode-range
532
+ //
533
+ // U+0?? or U+00A1-00A9
534
+ //
535
+ unicodeDescriptor: function () {
536
+ var ud;
537
+
538
+ ud = parserInput.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/);
539
+ if (ud) {
540
+ return new(tree.UnicodeDescriptor)(ud[0]);
541
+ }
542
+ },
543
+
544
+ //
545
+ // JavaScript code to be evaluated
546
+ //
547
+ // `window.location.href`
548
+ //
549
+ javascript: function () {
550
+ var js, index = parserInput.i;
551
+
552
+ parserInput.save();
553
+
554
+ var escape = parserInput.$char("~");
555
+ var jsQuote = parserInput.$char("`");
556
+
557
+ if (!jsQuote) {
558
+ parserInput.restore();
559
+ return;
560
+ }
561
+
562
+ js = parserInput.$re(/^[^`]*`/);
563
+ if (js) {
564
+ parserInput.forget();
565
+ return new(tree.JavaScript)(js.substr(0, js.length - 1), Boolean(escape), index, fileInfo);
566
+ }
567
+ parserInput.restore("invalid javascript definition");
568
+ }
569
+ },
570
+
571
+ //
572
+ // The variable part of a variable definition. Used in the `rule` parser
573
+ //
574
+ // @fink:
575
+ //
576
+ variable: function () {
577
+ var name;
578
+
579
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; }
580
+ },
581
+
582
+ //
583
+ // The variable part of a variable definition. Used in the `rule` parser
584
+ //
585
+ // @fink();
586
+ //
587
+ rulesetCall: function () {
588
+ var name;
589
+
590
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\(\s*\)\s*;/))) {
591
+ return new tree.RulesetCall(name[1]);
592
+ }
593
+ },
594
+
595
+ //
596
+ // extend syntax - used to extend selectors
597
+ //
598
+ extend: function(isRule) {
599
+ var elements, e, index = parserInput.i, option, extendList, extend;
600
+
601
+ if (!parserInput.$str(isRule ? "&:extend(" : ":extend(")) {
602
+ return;
603
+ }
604
+
605
+ do {
606
+ option = null;
607
+ elements = null;
608
+ while (! (option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
609
+ e = this.element();
610
+ if (!e) {
611
+ break;
612
+ }
613
+ if (elements) {
614
+ elements.push(e);
615
+ } else {
616
+ elements = [ e ];
617
+ }
618
+ }
619
+
620
+ option = option && option[1];
621
+ if (!elements) {
622
+ error("Missing target selector for :extend().");
623
+ }
624
+ extend = new(tree.Extend)(new(tree.Selector)(elements), option, index, fileInfo);
625
+ if (extendList) {
626
+ extendList.push(extend);
627
+ } else {
628
+ extendList = [ extend ];
629
+ }
630
+ } while (parserInput.$char(","));
631
+
632
+ expect(/^\)/);
633
+
634
+ if (isRule) {
635
+ expect(/^;/);
636
+ }
637
+
638
+ return extendList;
639
+ },
640
+
641
+ //
642
+ // extendRule - used in a rule to extend all the parent selectors
643
+ //
644
+ extendRule: function() {
645
+ return this.extend(true);
646
+ },
647
+
648
+ //
649
+ // Mixins
650
+ //
651
+ mixin: {
652
+ //
653
+ // A Mixin call, with an optional argument list
654
+ //
655
+ // #mixins > .square(#fff);
656
+ // .rounded(4px, black);
657
+ // .button;
658
+ //
659
+ // The `while` loop is there because mixins can be
660
+ // namespaced, but we only support the child and descendant
661
+ // selector for now.
662
+ //
663
+ call: function () {
664
+ var s = parserInput.currentChar(), important = false, index = parserInput.i, elemIndex,
665
+ elements, elem, e, c, args;
666
+
667
+ if (s !== '.' && s !== '#') { return; }
668
+
669
+ parserInput.save(); // stop us absorbing part of an invalid selector
670
+
671
+ while (true) {
672
+ elemIndex = parserInput.i;
673
+ e = parserInput.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);
674
+ if (!e) {
675
+ break;
676
+ }
677
+ elem = new(tree.Element)(c, e, elemIndex, fileInfo);
678
+ if (elements) {
679
+ elements.push(elem);
680
+ } else {
681
+ elements = [ elem ];
682
+ }
683
+ c = parserInput.$char('>');
684
+ }
685
+
686
+ if (elements) {
687
+ if (parserInput.$char('(')) {
688
+ args = this.args(true).args;
689
+ expectChar(')');
690
+ }
691
+
692
+ if (parsers.important()) {
693
+ important = true;
694
+ }
695
+
696
+ if (parsers.end()) {
697
+ parserInput.forget();
698
+ return new(tree.mixin.Call)(elements, args, index, fileInfo, important);
699
+ }
700
+ }
701
+
702
+ parserInput.restore();
703
+ },
704
+ args: function (isCall) {
705
+ var entities = parsers.entities,
706
+ returner = { args:null, variadic: false },
707
+ expressions = [], argsSemiColon = [], argsComma = [],
708
+ isSemiColonSeparated, expressionContainsNamed, name, nameLoop,
709
+ value, arg, expand;
710
+
711
+ parserInput.save();
712
+
713
+ while (true) {
714
+ if (isCall) {
715
+ arg = parsers.detachedRuleset() || parsers.expression();
716
+ } else {
717
+ parserInput.commentStore.length = 0;
718
+ if (parserInput.$str("...")) {
719
+ returner.variadic = true;
720
+ if (parserInput.$char(";") && !isSemiColonSeparated) {
721
+ isSemiColonSeparated = true;
722
+ }
723
+ (isSemiColonSeparated ? argsSemiColon : argsComma)
724
+ .push({ variadic: true });
725
+ break;
726
+ }
727
+ arg = entities.variable() || entities.literal() || entities.keyword();
728
+ }
729
+
730
+ if (!arg) {
731
+ break;
732
+ }
733
+
734
+ nameLoop = null;
735
+ if (arg.throwAwayComments) {
736
+ arg.throwAwayComments();
737
+ }
738
+ value = arg;
739
+ var val = null;
740
+
741
+ if (isCall) {
742
+ // Variable
743
+ if (arg.value && arg.value.length == 1) {
744
+ val = arg.value[0];
745
+ }
746
+ } else {
747
+ val = arg;
748
+ }
749
+
750
+ if (val && val instanceof tree.Variable) {
751
+ if (parserInput.$char(':')) {
752
+ if (expressions.length > 0) {
753
+ if (isSemiColonSeparated) {
754
+ error("Cannot mix ; and , as delimiter types");
755
+ }
756
+ expressionContainsNamed = true;
757
+ }
758
+
759
+ value = parsers.detachedRuleset() || parsers.expression();
760
+
761
+ if (!value) {
762
+ if (isCall) {
763
+ error("could not understand value for named argument");
764
+ } else {
765
+ parserInput.restore();
766
+ returner.args = [];
767
+ return returner;
768
+ }
769
+ }
770
+ nameLoop = (name = val.name);
771
+ } else if (parserInput.$str("...")) {
772
+ if (!isCall) {
773
+ returner.variadic = true;
774
+ if (parserInput.$char(";") && !isSemiColonSeparated) {
775
+ isSemiColonSeparated = true;
776
+ }
777
+ (isSemiColonSeparated ? argsSemiColon : argsComma)
778
+ .push({ name: arg.name, variadic: true });
779
+ break;
780
+ } else {
781
+ expand = true;
782
+ }
783
+ } else if (!isCall) {
784
+ name = nameLoop = val.name;
785
+ value = null;
786
+ }
787
+ }
788
+
789
+ if (value) {
790
+ expressions.push(value);
791
+ }
792
+
793
+ argsComma.push({ name:nameLoop, value:value, expand:expand });
794
+
795
+ if (parserInput.$char(',')) {
796
+ continue;
797
+ }
798
+
799
+ if (parserInput.$char(';') || isSemiColonSeparated) {
800
+
801
+ if (expressionContainsNamed) {
802
+ error("Cannot mix ; and , as delimiter types");
803
+ }
804
+
805
+ isSemiColonSeparated = true;
806
+
807
+ if (expressions.length > 1) {
808
+ value = new(tree.Value)(expressions);
809
+ }
810
+ argsSemiColon.push({ name:name, value:value, expand:expand });
811
+
812
+ name = null;
813
+ expressions = [];
814
+ expressionContainsNamed = false;
815
+ }
816
+ }
817
+
818
+ parserInput.forget();
819
+ returner.args = isSemiColonSeparated ? argsSemiColon : argsComma;
820
+ return returner;
821
+ },
822
+ //
823
+ // A Mixin definition, with a list of parameters
824
+ //
825
+ // .rounded (@radius: 2px, @color) {
826
+ // ...
827
+ // }
828
+ //
829
+ // Until we have a finer grained state-machine, we have to
830
+ // do a look-ahead, to make sure we don't have a mixin call.
831
+ // See the `rule` function for more information.
832
+ //
833
+ // We start by matching `.rounded (`, and then proceed on to
834
+ // the argument list, which has optional default values.
835
+ // We store the parameters in `params`, with a `value` key,
836
+ // if there is a value, such as in the case of `@radius`.
837
+ //
838
+ // Once we've got our params list, and a closing `)`, we parse
839
+ // the `{...}` block.
840
+ //
841
+ definition: function () {
842
+ var name, params = [], match, ruleset, cond, variadic = false;
843
+ if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
844
+ parserInput.peek(/^[^{]*\}/)) {
845
+ return;
846
+ }
847
+
848
+ parserInput.save();
849
+
850
+ match = parserInput.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/);
851
+ if (match) {
852
+ name = match[1];
853
+
854
+ var argInfo = this.args(false);
855
+ params = argInfo.args;
856
+ variadic = argInfo.variadic;
857
+
858
+ // .mixincall("@{a}");
859
+ // looks a bit like a mixin definition..
860
+ // also
861
+ // .mixincall(@a: {rule: set;});
862
+ // so we have to be nice and restore
863
+ if (!parserInput.$char(')')) {
864
+ parserInput.restore("Missing closing ')'");
865
+ return;
866
+ }
867
+
868
+ parserInput.commentStore.length = 0;
869
+
870
+ if (parserInput.$str("when")) { // Guard
871
+ cond = expect(parsers.conditions, 'expected condition');
872
+ }
873
+
874
+ ruleset = parsers.block();
875
+
876
+ if (ruleset) {
877
+ parserInput.forget();
878
+ return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
879
+ } else {
880
+ parserInput.restore();
881
+ }
882
+ } else {
883
+ parserInput.forget();
884
+ }
885
+ }
886
+ },
887
+
888
+ //
889
+ // Entities are the smallest recognized token,
890
+ // and can be found inside a rule's value.
891
+ //
892
+ entity: function () {
893
+ var entities = this.entities;
894
+
895
+ return this.comment() || entities.literal() || entities.variable() || entities.url() ||
896
+ entities.call() || entities.keyword() || entities.javascript();
897
+ },
898
+
899
+ //
900
+ // A Rule terminator. Note that we use `peek()` to check for '}',
901
+ // because the `block` rule will be expecting it, but we still need to make sure
902
+ // it's there, if ';' was ommitted.
903
+ //
904
+ end: function () {
905
+ return parserInput.$char(';') || parserInput.peek('}');
906
+ },
907
+
908
+ //
909
+ // IE's alpha function
910
+ //
911
+ // alpha(opacity=88)
912
+ //
913
+ alpha: function () {
914
+ var value;
915
+
916
+ // http://jsperf.com/case-insensitive-regex-vs-strtolower-then-regex/18
917
+ if (! parserInput.$re(/^opacity=/i)) { return; }
918
+ value = parserInput.$re(/^\d+/);
919
+ if (!value) {
920
+ value = expect(this.entities.variable, "Could not parse alpha");
921
+ }
922
+ expectChar(')');
923
+ return new(tree.Alpha)(value);
924
+ },
925
+
926
+ //
927
+ // A Selector Element
928
+ //
929
+ // div
930
+ // + h1
931
+ // #socks
932
+ // input[type="text"]
933
+ //
934
+ // Elements are the building blocks for Selectors,
935
+ // they are made out of a `Combinator` (see combinator rule),
936
+ // and an element name, such as a tag a class, or `*`.
937
+ //
938
+ element: function () {
939
+ var e, c, v, index = parserInput.i;
940
+
941
+ c = this.combinator();
942
+
943
+ e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) ||
944
+ parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
945
+ parserInput.$char('*') || parserInput.$char('&') || this.attribute() ||
946
+ parserInput.$re(/^\([^&()@]+\)/) || parserInput.$re(/^[\.#:](?=@)/) ||
947
+ this.entities.variableCurly();
948
+
949
+ if (! e) {
950
+ parserInput.save();
951
+ if (parserInput.$char('(')) {
952
+ if ((v = this.selector()) && parserInput.$char(')')) {
953
+ e = new(tree.Paren)(v);
954
+ parserInput.forget();
955
+ } else {
956
+ parserInput.restore("Missing closing ')'");
957
+ }
958
+ } else {
959
+ parserInput.forget();
960
+ }
961
+ }
962
+
963
+ if (e) { return new(tree.Element)(c, e, index, fileInfo); }
964
+ },
965
+
966
+ //
967
+ // Combinators combine elements together, in a Selector.
968
+ //
969
+ // Because our parser isn't white-space sensitive, special care
970
+ // has to be taken, when parsing the descendant combinator, ` `,
971
+ // as it's an empty space. We have to check the previous character
972
+ // in the input, to see if it's a ` ` character. More info on how
973
+ // we deal with this in *combinator.js*.
974
+ //
975
+ combinator: function () {
976
+ var c = parserInput.currentChar();
977
+
978
+ if (c === '/') {
979
+ parserInput.save();
980
+ var slashedCombinator = parserInput.$re(/^\/[a-z]+\//i);
981
+ if (slashedCombinator) {
982
+ parserInput.forget();
983
+ return new(tree.Combinator)(slashedCombinator);
984
+ }
985
+ parserInput.restore();
986
+ }
987
+
988
+ if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') {
989
+ parserInput.i++;
990
+ if (c === '^' && parserInput.currentChar() === '^') {
991
+ c = '^^';
992
+ parserInput.i++;
993
+ }
994
+ while (parserInput.isWhitespace()) { parserInput.i++; }
995
+ return new(tree.Combinator)(c);
996
+ } else if (parserInput.isWhitespace(-1)) {
997
+ return new(tree.Combinator)(" ");
998
+ } else {
999
+ return new(tree.Combinator)(null);
1000
+ }
1001
+ },
1002
+ //
1003
+ // A CSS selector (see selector below)
1004
+ // with less extensions e.g. the ability to extend and guard
1005
+ //
1006
+ lessSelector: function () {
1007
+ return this.selector(true);
1008
+ },
1009
+ //
1010
+ // A CSS Selector
1011
+ //
1012
+ // .class > div + h1
1013
+ // li a:hover
1014
+ //
1015
+ // Selectors are made out of one or more Elements, see above.
1016
+ //
1017
+ selector: function (isLess) {
1018
+ var index = parserInput.i, elements, extendList, c, e, allExtends, when, condition;
1019
+
1020
+ while ((isLess && (extendList = this.extend())) || (isLess && (when = parserInput.$str("when"))) || (e = this.element())) {
1021
+ if (when) {
1022
+ condition = expect(this.conditions, 'expected condition');
1023
+ } else if (condition) {
1024
+ error("CSS guard can only be used at the end of selector");
1025
+ } else if (extendList) {
1026
+ if (allExtends) {
1027
+ allExtends = allExtends.concat(extendList);
1028
+ } else {
1029
+ allExtends = extendList;
1030
+ }
1031
+ } else {
1032
+ if (allExtends) { error("Extend can only be used at the end of selector"); }
1033
+ c = parserInput.currentChar();
1034
+ if (elements) {
1035
+ elements.push(e);
1036
+ } else {
1037
+ elements = [ e ];
1038
+ }
1039
+ e = null;
1040
+ }
1041
+ if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') {
1042
+ break;
1043
+ }
1044
+ }
1045
+
1046
+ if (elements) { return new(tree.Selector)(elements, allExtends, condition, index, fileInfo); }
1047
+ if (allExtends) { error("Extend must be used to extend a selector, it cannot be used on its own"); }
1048
+ },
1049
+ attribute: function () {
1050
+ if (! parserInput.$char('[')) { return; }
1051
+
1052
+ var entities = this.entities,
1053
+ key, val, op;
1054
+
1055
+ if (!(key = entities.variableCurly())) {
1056
+ key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/);
1057
+ }
1058
+
1059
+ op = parserInput.$re(/^[|~*$^]?=/);
1060
+ if (op) {
1061
+ val = entities.quoted() || parserInput.$re(/^[0-9]+%/) || parserInput.$re(/^[\w-]+/) || entities.variableCurly();
1062
+ }
1063
+
1064
+ expectChar(']');
1065
+
1066
+ return new(tree.Attribute)(key, op, val);
1067
+ },
1068
+
1069
+ //
1070
+ // The `block` rule is used by `ruleset` and `mixin.definition`.
1071
+ // It's a wrapper around the `primary` rule, with added `{}`.
1072
+ //
1073
+ block: function () {
1074
+ var content;
1075
+ if (parserInput.$char('{') && (content = this.primary()) && parserInput.$char('}')) {
1076
+ return content;
1077
+ }
1078
+ },
1079
+
1080
+ blockRuleset: function() {
1081
+ var block = this.block();
1082
+
1083
+ if (block) {
1084
+ block = new tree.Ruleset(null, block);
1085
+ }
1086
+ return block;
1087
+ },
1088
+
1089
+ detachedRuleset: function() {
1090
+ var blockRuleset = this.blockRuleset();
1091
+ if (blockRuleset) {
1092
+ return new tree.DetachedRuleset(blockRuleset);
1093
+ }
1094
+ },
1095
+
1096
+ //
1097
+ // div, .class, body > p {...}
1098
+ //
1099
+ ruleset: function () {
1100
+ var selectors, s, rules, debugInfo;
1101
+
1102
+ parserInput.save();
1103
+
1104
+ if (context.dumpLineNumbers) {
1105
+ debugInfo = getDebugInfo(parserInput.i);
1106
+ }
1107
+
1108
+ while (true) {
1109
+ s = this.lessSelector();
1110
+ if (!s) {
1111
+ break;
1112
+ }
1113
+ if (selectors) {
1114
+ selectors.push(s);
1115
+ } else {
1116
+ selectors = [ s ];
1117
+ }
1118
+ parserInput.commentStore.length = 0;
1119
+ if (s.condition && selectors.length > 1) {
1120
+ error("Guards are only currently allowed on a single selector.");
1121
+ }
1122
+ if (! parserInput.$char(',')) { break; }
1123
+ if (s.condition) {
1124
+ error("Guards are only currently allowed on a single selector.");
1125
+ }
1126
+ parserInput.commentStore.length = 0;
1127
+ }
1128
+
1129
+ if (selectors && (rules = this.block())) {
1130
+ parserInput.forget();
1131
+ var ruleset = new(tree.Ruleset)(selectors, rules, context.strictImports);
1132
+ if (context.dumpLineNumbers) {
1133
+ ruleset.debugInfo = debugInfo;
1134
+ }
1135
+ return ruleset;
1136
+ } else {
1137
+ parserInput.restore();
1138
+ }
1139
+ },
1140
+ rule: function (tryAnonymous) {
1141
+ var name, value, startOfRule = parserInput.i, c = parserInput.currentChar(), important, merge, isVariable;
1142
+
1143
+ if (c === '.' || c === '#' || c === '&' || c === ':') { return; }
1144
+
1145
+ parserInput.save();
1146
+
1147
+ name = this.variable() || this.ruleProperty();
1148
+ if (name) {
1149
+ isVariable = typeof name === "string";
1150
+
1151
+ if (isVariable) {
1152
+ value = this.detachedRuleset();
1153
+ }
1154
+
1155
+ parserInput.commentStore.length = 0;
1156
+ if (!value) {
1157
+ // a name returned by this.ruleProperty() is always an array of the form:
1158
+ // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
1159
+ // where each item is a tree.Keyword or tree.Variable
1160
+ merge = !isVariable && name.length > 1 && name.pop().value;
1161
+
1162
+ // prefer to try to parse first if its a variable or we are compressing
1163
+ // but always fallback on the other one
1164
+ var tryValueFirst = !tryAnonymous && (context.compress || isVariable);
1165
+
1166
+ if (tryValueFirst) {
1167
+ value = this.value();
1168
+ }
1169
+ if (!value) {
1170
+ value = this.anonymousValue();
1171
+ if (value) {
1172
+ parserInput.forget();
1173
+ // anonymous values absorb the end ';' which is required for them to work
1174
+ return new (tree.Rule)(name, value, false, merge, startOfRule, fileInfo);
1175
+ }
1176
+ }
1177
+ if (!tryValueFirst && !value) {
1178
+ value = this.value();
1179
+ }
1180
+
1181
+ important = this.important();
1182
+ }
1183
+
1184
+ if (value && this.end()) {
1185
+ parserInput.forget();
1186
+ return new (tree.Rule)(name, value, important, merge, startOfRule, fileInfo);
1187
+ } else {
1188
+ parserInput.restore();
1189
+ if (value && !tryAnonymous) {
1190
+ return this.rule(true);
1191
+ }
1192
+ }
1193
+ } else {
1194
+ parserInput.forget();
1195
+ }
1196
+ },
1197
+ anonymousValue: function () {
1198
+ var match = parserInput.$re(/^([^@+\/'"*`(;{}-]*);/);
1199
+ if (match) {
1200
+ return new(tree.Anonymous)(match[1]);
1201
+ }
1202
+ },
1203
+
1204
+ //
1205
+ // An @import directive
1206
+ //
1207
+ // @import "lib";
1208
+ //
1209
+ // Depending on our environment, importing is done differently:
1210
+ // In the browser, it's an XHR request, in Node, it would be a
1211
+ // file-system operation. The function used for importing is
1212
+ // stored in `import`, which we pass to the Import constructor.
1213
+ //
1214
+ "import": function () {
1215
+ var path, features, index = parserInput.i;
1216
+
1217
+ var dir = parserInput.$re(/^@import?\s+/);
1218
+
1219
+ if (dir) {
1220
+ var options = (dir ? this.importOptions() : null) || {};
1221
+
1222
+ if ((path = this.entities.quoted() || this.entities.url())) {
1223
+ features = this.mediaFeatures();
1224
+
1225
+ if (!parserInput.$char(';')) {
1226
+ parserInput.i = index;
1227
+ error("missing semi-colon or unrecognised media features on import");
1228
+ }
1229
+ features = features && new(tree.Value)(features);
1230
+ return new(tree.Import)(path, features, options, index, fileInfo);
1231
+ }
1232
+ else {
1233
+ parserInput.i = index;
1234
+ error("malformed import statement");
1235
+ }
1236
+ }
1237
+ },
1238
+
1239
+ importOptions: function() {
1240
+ var o, options = {}, optionName, value;
1241
+
1242
+ // list of options, surrounded by parens
1243
+ if (! parserInput.$char('(')) { return null; }
1244
+ do {
1245
+ o = this.importOption();
1246
+ if (o) {
1247
+ optionName = o;
1248
+ value = true;
1249
+ switch(optionName) {
1250
+ case "css":
1251
+ optionName = "less";
1252
+ value = false;
1253
+ break;
1254
+ case "once":
1255
+ optionName = "multiple";
1256
+ value = false;
1257
+ break;
1258
+ }
1259
+ options[optionName] = value;
1260
+ if (! parserInput.$char(',')) { break; }
1261
+ }
1262
+ } while (o);
1263
+ expectChar(')');
1264
+ return options;
1265
+ },
1266
+
1267
+ importOption: function() {
1268
+ var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference|optional)/);
1269
+ if (opt) {
1270
+ return opt[1];
1271
+ }
1272
+ },
1273
+
1274
+ mediaFeature: function () {
1275
+ var entities = this.entities, nodes = [], e, p;
1276
+ parserInput.save();
1277
+ do {
1278
+ e = entities.keyword() || entities.variable();
1279
+ if (e) {
1280
+ nodes.push(e);
1281
+ } else if (parserInput.$char('(')) {
1282
+ p = this.property();
1283
+ e = this.value();
1284
+ if (parserInput.$char(')')) {
1285
+ if (p && e) {
1286
+ nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, fileInfo, true)));
1287
+ } else if (e) {
1288
+ nodes.push(new(tree.Paren)(e));
1289
+ } else {
1290
+ error("badly formed media feature definition");
1291
+ }
1292
+ } else {
1293
+ error("Missing closing ')'", "Parse");
1294
+ }
1295
+ }
1296
+ } while (e);
1297
+
1298
+ parserInput.forget();
1299
+ if (nodes.length > 0) {
1300
+ return new(tree.Expression)(nodes);
1301
+ }
1302
+ },
1303
+
1304
+ mediaFeatures: function () {
1305
+ var entities = this.entities, features = [], e;
1306
+ do {
1307
+ e = this.mediaFeature();
1308
+ if (e) {
1309
+ features.push(e);
1310
+ if (! parserInput.$char(',')) { break; }
1311
+ } else {
1312
+ e = entities.variable();
1313
+ if (e) {
1314
+ features.push(e);
1315
+ if (! parserInput.$char(',')) { break; }
1316
+ }
1317
+ }
1318
+ } while (e);
1319
+
1320
+ return features.length > 0 ? features : null;
1321
+ },
1322
+
1323
+ media: function () {
1324
+ var features, rules, media, debugInfo;
1325
+
1326
+ if (context.dumpLineNumbers) {
1327
+ debugInfo = getDebugInfo(parserInput.i);
1328
+ }
1329
+
1330
+ parserInput.save();
1331
+
1332
+ if (parserInput.$str("@media")) {
1333
+ features = this.mediaFeatures();
1334
+
1335
+ rules = this.block();
1336
+
1337
+ if (!rules) {
1338
+ error("media definitions require block statements after any features");
1339
+ }
1340
+
1341
+ parserInput.forget();
1342
+
1343
+ media = new(tree.Media)(rules, features, parserInput.i, fileInfo);
1344
+ if (context.dumpLineNumbers) {
1345
+ media.debugInfo = debugInfo;
1346
+ }
1347
+
1348
+ return media;
1349
+ }
1350
+
1351
+ parserInput.restore();
1352
+ },
1353
+
1354
+ //
1355
+ // A @plugin directive, used to import compiler extensions dynamically.
1356
+ //
1357
+ // @plugin "lib";
1358
+ //
1359
+ // Depending on our environment, importing is done differently:
1360
+ // In the browser, it's an XHR request, in Node, it would be a
1361
+ // file-system operation. The function used for importing is
1362
+ // stored in `import`, which we pass to the Import constructor.
1363
+ //
1364
+ plugin: function () {
1365
+ var path,
1366
+ index = parserInput.i,
1367
+ dir = parserInput.$re(/^@plugin?\s+/);
1368
+
1369
+ if (dir) {
1370
+ var options = { plugin : true };
1371
+
1372
+ if ((path = this.entities.quoted() || this.entities.url())) {
1373
+
1374
+ if (!parserInput.$char(';')) {
1375
+ parserInput.i = index;
1376
+ error("missing semi-colon on plugin");
1377
+ }
1378
+
1379
+ return new(tree.Import)(path, null, options, index, fileInfo);
1380
+ }
1381
+ else {
1382
+ parserInput.i = index;
1383
+ error("malformed plugin statement");
1384
+ }
1385
+ }
1386
+ },
1387
+
1388
+ //
1389
+ // A CSS Directive
1390
+ //
1391
+ // @charset "utf-8";
1392
+ //
1393
+ directive: function () {
1394
+ var index = parserInput.i, name, value, rules, nonVendorSpecificName,
1395
+ hasIdentifier, hasExpression, hasUnknown, hasBlock = true, isRooted = true;
1396
+
1397
+ if (parserInput.currentChar() !== '@') { return; }
1398
+
1399
+ value = this['import']() || this.plugin() || this.media();
1400
+ if (value) {
1401
+ return value;
1402
+ }
1403
+
1404
+ parserInput.save();
1405
+
1406
+ name = parserInput.$re(/^@[a-z-]+/);
1407
+
1408
+ if (!name) { return; }
1409
+
1410
+ nonVendorSpecificName = name;
1411
+ if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
1412
+ nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
1413
+ }
1414
+
1415
+ switch(nonVendorSpecificName) {
1416
+ case "@charset":
1417
+ hasIdentifier = true;
1418
+ hasBlock = false;
1419
+ break;
1420
+ case "@namespace":
1421
+ hasExpression = true;
1422
+ hasBlock = false;
1423
+ break;
1424
+ case "@keyframes":
1425
+ case "@counter-style":
1426
+ hasIdentifier = true;
1427
+ break;
1428
+ case "@document":
1429
+ case "@supports":
1430
+ hasUnknown = true;
1431
+ isRooted = false;
1432
+ break;
1433
+ default:
1434
+ hasUnknown = true;
1435
+ break;
1436
+ }
1437
+
1438
+ parserInput.commentStore.length = 0;
1439
+
1440
+ if (hasIdentifier) {
1441
+ value = this.entity();
1442
+ if (!value) {
1443
+ error("expected " + name + " identifier");
1444
+ }
1445
+ } else if (hasExpression) {
1446
+ value = this.expression();
1447
+ if (!value) {
1448
+ error("expected " + name + " expression");
1449
+ }
1450
+ } else if (hasUnknown) {
1451
+ value = (parserInput.$re(/^[^{;]+/) || '').trim();
1452
+ hasBlock = (parserInput.currentChar() == '{');
1453
+ if (value) {
1454
+ value = new(tree.Anonymous)(value);
1455
+ }
1456
+ }
1457
+
1458
+ if (hasBlock) {
1459
+ rules = this.blockRuleset();
1460
+ }
1461
+
1462
+ if (rules || (!hasBlock && value && parserInput.$char(';'))) {
1463
+ parserInput.forget();
1464
+ return new (tree.Directive)(name, value, rules, index, fileInfo,
1465
+ context.dumpLineNumbers ? getDebugInfo(index) : null,
1466
+ isRooted
1467
+ );
1468
+ }
1469
+
1470
+ parserInput.restore("directive options not recognised");
1471
+ },
1472
+
1473
+ //
1474
+ // A Value is a comma-delimited list of Expressions
1475
+ //
1476
+ // font-family: Baskerville, Georgia, serif;
1477
+ //
1478
+ // In a Rule, a Value represents everything after the `:`,
1479
+ // and before the `;`.
1480
+ //
1481
+ value: function () {
1482
+ var e, expressions = [];
1483
+
1484
+ do {
1485
+ e = this.expression();
1486
+ if (e) {
1487
+ expressions.push(e);
1488
+ if (! parserInput.$char(',')) { break; }
1489
+ }
1490
+ } while (e);
1491
+
1492
+ if (expressions.length > 0) {
1493
+ return new(tree.Value)(expressions);
1494
+ }
1495
+ },
1496
+ important: function () {
1497
+ if (parserInput.currentChar() === '!') {
1498
+ return parserInput.$re(/^! *important/);
1499
+ }
1500
+ },
1501
+ sub: function () {
1502
+ var a, e;
1503
+
1504
+ parserInput.save();
1505
+ if (parserInput.$char('(')) {
1506
+ a = this.addition();
1507
+ if (a && parserInput.$char(')')) {
1508
+ parserInput.forget();
1509
+ e = new(tree.Expression)([a]);
1510
+ e.parens = true;
1511
+ return e;
1512
+ }
1513
+ parserInput.restore("Expected ')'");
1514
+ return;
1515
+ }
1516
+ parserInput.restore();
1517
+ },
1518
+ multiplication: function () {
1519
+ var m, a, op, operation, isSpaced;
1520
+ m = this.operand();
1521
+ if (m) {
1522
+ isSpaced = parserInput.isWhitespace(-1);
1523
+ while (true) {
1524
+ if (parserInput.peek(/^\/[*\/]/)) {
1525
+ break;
1526
+ }
1527
+
1528
+ parserInput.save();
1529
+
1530
+ op = parserInput.$char('/') || parserInput.$char('*');
1531
+
1532
+ if (!op) { parserInput.forget(); break; }
1533
+
1534
+ a = this.operand();
1535
+
1536
+ if (!a) { parserInput.restore(); break; }
1537
+ parserInput.forget();
1538
+
1539
+ m.parensInOp = true;
1540
+ a.parensInOp = true;
1541
+ operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
1542
+ isSpaced = parserInput.isWhitespace(-1);
1543
+ }
1544
+ return operation || m;
1545
+ }
1546
+ },
1547
+ addition: function () {
1548
+ var m, a, op, operation, isSpaced;
1549
+ m = this.multiplication();
1550
+ if (m) {
1551
+ isSpaced = parserInput.isWhitespace(-1);
1552
+ while (true) {
1553
+ op = parserInput.$re(/^[-+]\s+/) || (!isSpaced && (parserInput.$char('+') || parserInput.$char('-')));
1554
+ if (!op) {
1555
+ break;
1556
+ }
1557
+ a = this.multiplication();
1558
+ if (!a) {
1559
+ break;
1560
+ }
1561
+
1562
+ m.parensInOp = true;
1563
+ a.parensInOp = true;
1564
+ operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
1565
+ isSpaced = parserInput.isWhitespace(-1);
1566
+ }
1567
+ return operation || m;
1568
+ }
1569
+ },
1570
+ conditions: function () {
1571
+ var a, b, index = parserInput.i, condition;
1572
+
1573
+ a = this.condition();
1574
+ if (a) {
1575
+ while (true) {
1576
+ if (!parserInput.peek(/^,\s*(not\s*)?\(/) || !parserInput.$char(',')) {
1577
+ break;
1578
+ }
1579
+ b = this.condition();
1580
+ if (!b) {
1581
+ break;
1582
+ }
1583
+ condition = new(tree.Condition)('or', condition || a, b, index);
1584
+ }
1585
+ return condition || a;
1586
+ }
1587
+ },
1588
+ condition: function () {
1589
+ var result, logical, next;
1590
+ function or() {
1591
+ return parserInput.$str("or");
1592
+ }
1593
+
1594
+ result = this.conditionAnd(this);
1595
+ if (!result) {
1596
+ return ;
1597
+ }
1598
+ logical = or();
1599
+ if (logical) {
1600
+ next = this.condition();
1601
+ if (next) {
1602
+ result = new(tree.Condition)(logical, result, next);
1603
+ } else {
1604
+ return ;
1605
+ }
1606
+ }
1607
+ return result;
1608
+ },
1609
+ conditionAnd: function () {
1610
+ var result, logical, next;
1611
+ function insideCondition(me) {
1612
+ return me.negatedCondition() || me.parenthesisCondition();
1613
+ }
1614
+ function and() {
1615
+ return parserInput.$str("and");
1616
+ }
1617
+
1618
+ result = insideCondition(this);
1619
+ if (!result) {
1620
+ return ;
1621
+ }
1622
+ logical = and();
1623
+ if (logical) {
1624
+ next = this.conditionAnd();
1625
+ if (next) {
1626
+ result = new(tree.Condition)(logical, result, next);
1627
+ } else {
1628
+ return ;
1629
+ }
1630
+ }
1631
+ return result;
1632
+ },
1633
+ negatedCondition: function () {
1634
+ if (parserInput.$str("not")) {
1635
+ var result = this.parenthesisCondition();
1636
+ if (result) {
1637
+ result.negate = !result.negate;
1638
+ }
1639
+ return result;
1640
+ }
1641
+ },
1642
+ parenthesisCondition: function () {
1643
+ function tryConditionFollowedByParenthesis(me) {
1644
+ var body;
1645
+ parserInput.save();
1646
+ body = me.condition();
1647
+ if (!body) {
1648
+ parserInput.restore();
1649
+ return ;
1650
+ }
1651
+ if (!parserInput.$char(')')) {
1652
+ parserInput.restore();
1653
+ return ;
1654
+ }
1655
+ parserInput.forget();
1656
+ return body;
1657
+ }
1658
+
1659
+ var body;
1660
+ parserInput.save();
1661
+ if (!parserInput.$str("(")) {
1662
+ parserInput.restore();
1663
+ return ;
1664
+ }
1665
+ body = tryConditionFollowedByParenthesis(this);
1666
+ if (body) {
1667
+ parserInput.forget();
1668
+ return body;
1669
+ }
1670
+
1671
+ body = this.atomicCondition();
1672
+ if (!body) {
1673
+ parserInput.restore();
1674
+ return ;
1675
+ }
1676
+ if (!parserInput.$char(')')) {
1677
+ parserInput.restore("expected ')' got '" + parserInput.currentChar() + "'");
1678
+ return ;
1679
+ }
1680
+ parserInput.forget();
1681
+ return body;
1682
+ },
1683
+ atomicCondition: function () {
1684
+ var entities = this.entities, index = parserInput.i, a, b, c, op;
1685
+
1686
+ a = this.addition() || entities.keyword() || entities.quoted();
1687
+ if (a) {
1688
+ if (parserInput.$char('>')) {
1689
+ if (parserInput.$char('=')) {
1690
+ op = ">=";
1691
+ } else {
1692
+ op = '>';
1693
+ }
1694
+ } else
1695
+ if (parserInput.$char('<')) {
1696
+ if (parserInput.$char('=')) {
1697
+ op = "<=";
1698
+ } else {
1699
+ op = '<';
1700
+ }
1701
+ } else
1702
+ if (parserInput.$char('=')) {
1703
+ if (parserInput.$char('>')) {
1704
+ op = "=>";
1705
+ } else if (parserInput.$char('<')) {
1706
+ op = '=<';
1707
+ } else {
1708
+ op = '=';
1709
+ }
1710
+ }
1711
+ if (op) {
1712
+ b = this.addition() || entities.keyword() || entities.quoted();
1713
+ if (b) {
1714
+ c = new(tree.Condition)(op, a, b, index, false);
1715
+ } else {
1716
+ error('expected expression');
1717
+ }
1718
+ } else {
1719
+ c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, false);
1720
+ }
1721
+ return c;
1722
+ }
1723
+ },
1724
+
1725
+ //
1726
+ // An operand is anything that can be part of an operation,
1727
+ // such as a Color, or a Variable
1728
+ //
1729
+ operand: function () {
1730
+ var entities = this.entities, negate;
1731
+
1732
+ if (parserInput.peek(/^-[@\(]/)) {
1733
+ negate = parserInput.$char('-');
1734
+ }
1735
+
1736
+ var o = this.sub() || entities.dimension() ||
1737
+ entities.color() || entities.variable() ||
1738
+ entities.call() || entities.colorKeyword();
1739
+
1740
+ if (negate) {
1741
+ o.parensInOp = true;
1742
+ o = new(tree.Negative)(o);
1743
+ }
1744
+
1745
+ return o;
1746
+ },
1747
+
1748
+ //
1749
+ // Expressions either represent mathematical operations,
1750
+ // or white-space delimited Entities.
1751
+ //
1752
+ // 1px solid black
1753
+ // @var * 2
1754
+ //
1755
+ expression: function () {
1756
+ var entities = [], e, delim;
1757
+
1758
+ do {
1759
+ e = this.comment();
1760
+ if (e) {
1761
+ entities.push(e);
1762
+ continue;
1763
+ }
1764
+ e = this.addition() || this.entity();
1765
+ if (e) {
1766
+ entities.push(e);
1767
+ // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
1768
+ if (!parserInput.peek(/^\/[\/*]/)) {
1769
+ delim = parserInput.$char('/');
1770
+ if (delim) {
1771
+ entities.push(new(tree.Anonymous)(delim));
1772
+ }
1773
+ }
1774
+ }
1775
+ } while (e);
1776
+ if (entities.length > 0) {
1777
+ return new(tree.Expression)(entities);
1778
+ }
1779
+ },
1780
+ property: function () {
1781
+ var name = parserInput.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);
1782
+ if (name) {
1783
+ return name[1];
1784
+ }
1785
+ },
1786
+ ruleProperty: function () {
1787
+ var name = [], index = [], s, k;
1788
+
1789
+ parserInput.save();
1790
+
1791
+ var simpleProperty = parserInput.$re(/^([_a-zA-Z0-9-]+)\s*:/);
1792
+ if (simpleProperty) {
1793
+ name = [new(tree.Keyword)(simpleProperty[1])];
1794
+ parserInput.forget();
1795
+ return name;
1796
+ }
1797
+
1798
+ function match(re) {
1799
+ var i = parserInput.i,
1800
+ chunk = parserInput.$re(re);
1801
+ if (chunk) {
1802
+ index.push(i);
1803
+ return name.push(chunk[1]);
1804
+ }
1805
+ }
1806
+
1807
+ match(/^(\*?)/);
1808
+ while (true) {
1809
+ if (!match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)) {
1810
+ break;
1811
+ }
1812
+ }
1813
+
1814
+ if ((name.length > 1) && match(/^((?:\+_|\+)?)\s*:/)) {
1815
+ parserInput.forget();
1816
+
1817
+ // at last, we have the complete match now. move forward,
1818
+ // convert name particles to tree objects and return:
1819
+ if (name[0] === '') {
1820
+ name.shift();
1821
+ index.shift();
1822
+ }
1823
+ for (k = 0; k < name.length; k++) {
1824
+ s = name[k];
1825
+ name[k] = (s.charAt(0) !== '@') ?
1826
+ new(tree.Keyword)(s) :
1827
+ new(tree.Variable)('@' + s.slice(2, -1),
1828
+ index[k], fileInfo);
1829
+ }
1830
+ return name;
1831
+ }
1832
+ parserInput.restore();
1833
+ }
1834
+ }
1835
+ };
1836
+ };
1837
+ Parser.serializeVars = function(vars) {
1838
+ var s = '';
1839
+
1840
+ for (var name in vars) {
1841
+ if (Object.hasOwnProperty.call(vars, name)) {
1842
+ var value = vars[name];
1843
+ s += ((name[0] === '@') ? '' : '@') + name + ': ' + value +
1844
+ ((String(value).slice(-1) === ';') ? '' : ';');
1845
+ }
1846
+ }
1847
+
1848
+ return s;
1849
+ };
1850
+
1851
+ module.exports = Parser;