less 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +4 -0
  3. data/Gemfile +1 -1
  4. data/less.gemspec +1 -1
  5. data/lib/less/defaults.rb +3 -3
  6. data/lib/less/java_script/v8_context.rb +1 -1
  7. data/lib/less/js/.gitattributes +3 -2
  8. data/lib/less/js/.gitignore +18 -2
  9. data/lib/less/js/.jshintrc +11 -0
  10. data/lib/less/js/CHANGELOG.md +87 -2
  11. data/lib/less/js/CONTRIBUTING.md +4 -3
  12. data/lib/less/js/Gruntfile.js +290 -0
  13. data/lib/less/js/README.md +342 -12
  14. data/lib/less/js/benchmark/benchmark.less +194 -194
  15. data/lib/less/js/benchmark/less-benchmark.js +9 -10
  16. data/lib/less/js/bin/lessc +154 -12
  17. data/lib/less/js/bower.json +18 -0
  18. data/lib/less/js/build.gradle +347 -0
  19. data/lib/less/js/build/README.md +350 -0
  20. data/lib/less/js/build/browser-header.js +4 -0
  21. data/lib/less/js/build/build.yml +160 -0
  22. data/lib/less/js/build/require-rhino.js +7 -2
  23. data/lib/less/js/build/rhino-header.js +4 -0
  24. data/lib/less/js/build/rhino-modules.js +131 -0
  25. data/lib/less/js/build/tasks/.gitkeep +1 -0
  26. data/lib/less/js/dist/less-1.5.0.js +6914 -0
  27. data/lib/less/js/dist/less-1.5.0.min.js +13 -0
  28. data/lib/less/js/dist/less-1.5.1.js +6941 -0
  29. data/lib/less/js/dist/less-1.5.1.min.js +13 -0
  30. data/lib/less/js/dist/less-1.6.0.js +7485 -0
  31. data/lib/less/js/dist/less-1.6.0.min.js +16 -0
  32. data/lib/less/js/dist/less-1.6.1.js +7513 -0
  33. data/lib/less/js/dist/less-1.6.1.min.js +16 -0
  34. data/lib/less/js/dist/less-1.6.2.js +7624 -0
  35. data/lib/less/js/dist/less-1.6.2.min.js +16 -0
  36. data/lib/less/js/dist/less-rhino-1.5.1.js +6831 -0
  37. data/lib/less/js/dist/less-rhino-1.6.2.js +9017 -0
  38. data/lib/less/js/dist/lessc-rhino-1.6.2.js +449 -0
  39. data/lib/less/js/gradle/wrapper/gradle-wrapper.jar +0 -0
  40. data/lib/less/js/gradle/wrapper/gradle-wrapper.properties +6 -0
  41. data/lib/less/js/gradlew +164 -0
  42. data/lib/less/js/gradlew.bat +90 -0
  43. data/lib/less/js/lib/less/browser.js +482 -367
  44. data/lib/less/js/lib/less/colors.js +0 -1
  45. data/lib/less/js/lib/less/encoder.js +4 -0
  46. data/lib/less/js/lib/less/env.js +50 -19
  47. data/lib/less/js/lib/less/extend-visitor.js +66 -41
  48. data/lib/less/js/lib/less/functions.js +309 -104
  49. data/lib/less/js/lib/less/import-visitor.js +21 -10
  50. data/lib/less/js/lib/less/index.js +90 -68
  51. data/lib/less/js/lib/less/join-selector-visitor.js +11 -4
  52. data/lib/less/js/lib/less/lessc_helper.js +56 -45
  53. data/lib/less/js/lib/less/parser.js +830 -460
  54. data/lib/less/js/lib/less/rhino.js +380 -58
  55. data/lib/less/js/lib/less/source-map-output.js +141 -0
  56. data/lib/less/js/lib/less/to-css-visitor.js +215 -0
  57. data/lib/less/js/lib/less/tree.js +57 -5
  58. data/lib/less/js/lib/less/tree/alpha.js +13 -5
  59. data/lib/less/js/lib/less/tree/anonymous.js +11 -5
  60. data/lib/less/js/lib/less/tree/assignment.js +11 -5
  61. data/lib/less/js/lib/less/tree/call.js +19 -8
  62. data/lib/less/js/lib/less/tree/color.js +59 -36
  63. data/lib/less/js/lib/less/tree/comment.js +17 -4
  64. data/lib/less/js/lib/less/tree/condition.js +3 -3
  65. data/lib/less/js/lib/less/tree/dimension.js +161 -153
  66. data/lib/less/js/lib/less/tree/directive.js +39 -18
  67. data/lib/less/js/lib/less/tree/element.js +41 -18
  68. data/lib/less/js/lib/less/tree/expression.js +11 -5
  69. data/lib/less/js/lib/less/tree/extend.js +11 -1
  70. data/lib/less/js/lib/less/tree/import.js +34 -20
  71. data/lib/less/js/lib/less/tree/javascript.js +16 -10
  72. data/lib/less/js/lib/less/tree/keyword.js +5 -2
  73. data/lib/less/js/lib/less/tree/media.js +39 -22
  74. data/lib/less/js/lib/less/tree/mixin.js +135 -56
  75. data/lib/less/js/lib/less/tree/negative.js +4 -2
  76. data/lib/less/js/lib/less/tree/operation.js +17 -12
  77. data/lib/less/js/lib/less/tree/paren.js +5 -2
  78. data/lib/less/js/lib/less/tree/quoted.js +9 -6
  79. data/lib/less/js/lib/less/tree/rule.js +39 -21
  80. data/lib/less/js/lib/less/tree/ruleset.js +229 -145
  81. data/lib/less/js/lib/less/tree/selector.js +101 -34
  82. data/lib/less/js/lib/less/tree/unicode-descriptor.js +4 -3
  83. data/lib/less/js/lib/less/tree/url.js +33 -11
  84. data/lib/less/js/lib/less/tree/value.js +13 -6
  85. data/lib/less/js/lib/less/tree/variable.js +13 -8
  86. data/lib/less/js/lib/less/visitor.js +117 -25
  87. data/lib/less/js/lib/source-map/source-map-0.1.31.js +1933 -0
  88. data/lib/less/js/lib/source-map/source-map-footer.js +4 -0
  89. data/lib/less/js/lib/source-map/source-map-header.js +3 -0
  90. data/lib/less/js/package.json +30 -15
  91. data/lib/less/js/test/browser/common.js +131 -56
  92. data/lib/less/js/test/browser/css/global-vars/simple.css +3 -0
  93. data/lib/less/js/test/browser/css/modify-vars/simple.css +8 -0
  94. data/lib/less/js/test/browser/css/relative-urls/urls.css +8 -9
  95. data/lib/less/js/test/browser/css/rootpath-relative/urls.css +0 -1
  96. data/lib/less/js/test/browser/css/rootpath/urls.css +0 -1
  97. data/lib/less/js/test/browser/css/urls.css +18 -14
  98. data/lib/less/js/test/browser/es5.js +27 -0
  99. data/lib/less/js/test/{less/errors/color-operation-error.less → browser/less/console-errors/test-error.less} +0 -0
  100. data/lib/less/js/test/browser/less/console-errors/test-error.txt +2 -0
  101. data/lib/less/js/test/browser/less/global-vars/simple.less +3 -0
  102. data/lib/less/js/test/browser/less/modify-vars/imports/simple2.less +4 -0
  103. data/lib/less/js/test/browser/less/modify-vars/simple.less +8 -0
  104. data/lib/less/js/test/browser/less/relative-urls/urls.less +1 -1
  105. data/lib/less/js/test/browser/less/rootpath-relative/urls.less +1 -1
  106. data/lib/less/js/test/browser/less/rootpath/urls.less +1 -1
  107. data/lib/less/js/test/browser/less/urls.less +9 -1
  108. data/lib/less/js/test/browser/phantom-runner.js +112 -103
  109. data/lib/less/js/test/browser/runner-browser-options.js +42 -0
  110. data/lib/less/js/test/browser/{runner-browser.js → runner-browser-spec.js} +7 -2
  111. data/lib/less/js/test/browser/runner-console-errors.js +5 -0
  112. data/lib/less/js/test/browser/runner-errors-options.js +5 -0
  113. data/lib/less/js/test/browser/runner-errors-spec.js +4 -0
  114. data/lib/less/js/test/browser/runner-global-vars-options.js +4 -0
  115. data/lib/less/js/test/browser/runner-global-vars-spec.js +3 -0
  116. data/lib/less/js/test/browser/runner-legacy-options.js +4 -0
  117. data/lib/less/js/test/browser/{runner-legacy.js → runner-legacy-spec.js} +1 -4
  118. data/lib/less/js/test/browser/runner-main-options.js +15 -0
  119. data/lib/less/js/test/browser/runner-main-spec.js +3 -0
  120. data/lib/less/js/test/browser/runner-modify-vars-options.js +2 -0
  121. data/lib/less/js/test/browser/runner-modify-vars-spec.js +43 -0
  122. data/lib/less/js/test/browser/runner-no-js-errors-options.js +4 -0
  123. data/lib/less/js/test/browser/runner-no-js-errors-spec.js +4 -0
  124. data/lib/less/js/test/browser/runner-production-options.js +3 -0
  125. data/lib/less/js/test/browser/{runner-production.js → runner-production-spec.js} +1 -3
  126. data/lib/less/js/test/browser/runner-relative-urls-options.js +3 -0
  127. data/lib/less/js/test/browser/{runner-relative-urls.js → runner-relative-urls-spec.js} +1 -2
  128. data/lib/less/js/test/browser/runner-rootpath-options.js +3 -0
  129. data/lib/less/js/test/browser/runner-rootpath-relative-options.js +4 -0
  130. data/lib/less/js/test/browser/{runner-rootpath-relative.js → runner-rootpath-relative-spec.js} +1 -3
  131. data/lib/less/js/test/browser/{runner-rootpath.js → runner-rootpath-spec.js} +1 -2
  132. data/lib/less/js/test/browser/test-runner-template.tmpl +47 -0
  133. data/lib/less/js/test/css/colors.css +7 -0
  134. data/lib/less/js/test/css/comments.css +9 -4
  135. data/lib/less/js/test/css/compression/compression.css +3 -2
  136. data/lib/less/js/test/css/css-3.css +17 -5
  137. data/lib/less/js/test/css/css-guards.css +37 -0
  138. data/lib/less/js/test/css/debug/linenumbers-all.css +6 -0
  139. data/lib/less/js/test/css/debug/linenumbers-comments.css +5 -0
  140. data/lib/less/js/test/css/debug/linenumbers-mediaquery.css +5 -0
  141. data/lib/less/js/test/css/empty.css +0 -0
  142. data/lib/less/js/test/css/extend-chaining.css +9 -0
  143. data/lib/less/js/test/css/extend-selector.css +10 -2
  144. data/lib/less/js/test/css/extract-and-length.css +133 -0
  145. data/lib/less/js/test/css/functions.css +23 -10
  146. data/lib/less/js/test/css/globalVars/extended.css +12 -0
  147. data/lib/less/js/test/css/globalVars/simple.css +6 -0
  148. data/lib/less/js/test/css/import-inline.css +5 -0
  149. data/lib/less/js/test/css/import-once.css +12 -0
  150. data/lib/less/js/test/css/import-reference.css +49 -0
  151. data/lib/less/js/test/css/import.css +0 -2
  152. data/lib/less/js/test/css/media.css +21 -5
  153. data/lib/less/js/test/css/merge.css +26 -0
  154. data/lib/less/js/test/css/mixins-guards-default-func.css +129 -0
  155. data/lib/less/js/test/css/mixins-guards.css +6 -0
  156. data/lib/less/js/test/css/mixins-important.css +7 -0
  157. data/lib/less/js/test/css/mixins-interpolated.css +39 -0
  158. data/lib/less/js/test/css/mixins.css +20 -0
  159. data/lib/less/js/test/css/modifyVars/extended.css +9 -0
  160. data/lib/less/js/test/css/no-output.css +0 -0
  161. data/lib/less/js/test/css/parens.css +3 -0
  162. data/lib/less/js/test/css/property-name-interp.css +20 -0
  163. data/lib/less/js/test/css/selectors.css +12 -0
  164. data/lib/less/js/test/css/static-urls/urls.css +7 -4
  165. data/lib/less/js/test/css/strings.css +3 -0
  166. data/lib/less/js/test/css/url-args/urls.css +56 -0
  167. data/lib/less/js/test/css/urls.css +21 -9
  168. data/lib/less/js/test/index.js +45 -0
  169. data/lib/less/js/test/less-test.js +234 -191
  170. data/lib/less/js/test/less/colors.less +6 -0
  171. data/lib/less/js/test/less/comments.less +7 -1
  172. data/lib/less/js/test/less/compression/compression.less +21 -1
  173. data/lib/less/js/test/less/css-3.less +12 -0
  174. data/lib/less/js/test/less/css-guards.less +99 -0
  175. data/lib/less/js/test/less/debug/linenumbers.less +11 -1
  176. data/lib/less/js/test/less/empty.less +0 -0
  177. data/lib/less/js/test/less/errors/add-mixed-units.txt +4 -2
  178. data/lib/less/js/test/less/errors/add-mixed-units2.txt +4 -2
  179. data/lib/less/js/test/less/errors/color-func-invalid-color.less +3 -0
  180. data/lib/less/js/test/less/errors/color-func-invalid-color.txt +4 -0
  181. data/lib/less/js/test/less/errors/css-guard-default-func.less +4 -0
  182. data/lib/less/js/test/less/errors/css-guard-default-func.txt +4 -0
  183. data/lib/less/js/test/less/errors/import-subfolder2.txt +4 -2
  184. data/lib/less/js/test/less/errors/javascript-undefined-var.less +3 -0
  185. data/lib/less/js/test/less/errors/javascript-undefined-var.txt +4 -0
  186. data/lib/less/js/test/less/errors/mixins-guards-default-func-1.less +9 -0
  187. data/lib/less/js/test/less/errors/mixins-guards-default-func-1.txt +4 -0
  188. data/lib/less/js/test/less/errors/mixins-guards-default-func-2.less +9 -0
  189. data/lib/less/js/test/less/errors/mixins-guards-default-func-2.txt +4 -0
  190. data/lib/less/js/test/less/errors/mixins-guards-default-func-3.less +9 -0
  191. data/lib/less/js/test/less/errors/mixins-guards-default-func-3.txt +4 -0
  192. data/lib/less/js/test/less/errors/multiple-guards-on-css-selectors.less +4 -0
  193. data/lib/less/js/test/less/errors/multiple-guards-on-css-selectors.txt +4 -0
  194. data/lib/less/js/test/less/errors/multiple-guards-on-css-selectors2.less +4 -0
  195. data/lib/less/js/test/less/errors/multiple-guards-on-css-selectors2.txt +4 -0
  196. data/lib/less/js/test/less/errors/parse-error-curly-bracket.less +4 -1
  197. data/lib/less/js/test/less/errors/parse-error-curly-bracket.txt +4 -2
  198. data/lib/less/js/test/less/errors/parse-error-extra-parens.less +5 -0
  199. data/lib/less/js/test/less/errors/parse-error-extra-parens.txt +3 -0
  200. data/lib/less/js/test/less/errors/parse-error-missing-bracket.txt +2 -2
  201. data/lib/less/js/test/less/errors/parse-error-missing-parens.less +5 -0
  202. data/lib/less/js/test/less/errors/parse-error-missing-parens.txt +3 -0
  203. data/lib/less/js/test/less/errors/property-asterisk-only-name.less +3 -0
  204. data/lib/less/js/test/less/errors/property-asterisk-only-name.txt +4 -0
  205. data/lib/less/js/test/less/errors/property-interp-not-defined.less +1 -0
  206. data/lib/less/js/test/less/errors/property-interp-not-defined.txt +2 -0
  207. data/lib/less/js/test/less/errors/svg-gradient1.less +3 -0
  208. data/lib/less/js/test/less/errors/svg-gradient1.txt +4 -0
  209. data/lib/less/js/test/less/errors/svg-gradient2.less +3 -0
  210. data/lib/less/js/test/less/errors/svg-gradient2.txt +4 -0
  211. data/lib/less/js/test/less/errors/svg-gradient3.less +3 -0
  212. data/lib/less/js/test/less/errors/svg-gradient3.txt +4 -0
  213. data/lib/less/js/test/less/errors/unit-function.less +3 -0
  214. data/lib/less/js/test/less/errors/unit-function.txt +4 -0
  215. data/lib/less/js/test/less/extend-chaining.less +12 -0
  216. data/lib/less/js/test/less/extend-selector.less +15 -0
  217. data/lib/less/js/test/less/extract-and-length.less +133 -0
  218. data/lib/less/js/test/less/functions.less +15 -2
  219. data/lib/less/js/test/less/globalVars/extended.json +5 -0
  220. data/lib/less/js/test/less/globalVars/extended.less +10 -0
  221. data/lib/less/js/test/less/globalVars/simple.json +3 -0
  222. data/lib/less/js/test/less/globalVars/simple.less +3 -0
  223. data/lib/less/js/test/less/import-inline.less +2 -0
  224. data/lib/less/js/test/less/import-once.less +2 -0
  225. data/lib/less/js/test/less/import-reference.less +18 -0
  226. data/lib/less/js/test/less/import/import-and-relative-paths-test.less +11 -0
  227. data/lib/less/js/test/less/import/import-reference.less +43 -0
  228. data/lib/less/js/test/less/import/import-test-f.less +5 -0
  229. data/lib/less/js/test/less/import/invalid-css.less +1 -0
  230. data/lib/less/js/test/less/media.less +25 -1
  231. data/lib/less/js/test/less/merge.less +59 -0
  232. data/lib/less/js/test/less/mixins-args.less +10 -0
  233. data/lib/less/js/test/less/mixins-guards-default-func.less +195 -0
  234. data/lib/less/js/test/less/mixins-guards.less +16 -0
  235. data/lib/less/js/test/less/mixins-important.less +4 -1
  236. data/lib/less/js/test/less/mixins-interpolated.less +69 -0
  237. data/lib/less/js/test/less/mixins.less +27 -0
  238. data/lib/less/js/test/less/modifyVars/extended.json +5 -0
  239. data/lib/less/js/test/less/modifyVars/extended.less +11 -0
  240. data/lib/less/js/test/less/no-js-errors/no-js-errors.less +3 -0
  241. data/lib/less/js/test/less/no-js-errors/no-js-errors.txt +4 -0
  242. data/lib/less/js/test/less/no-output.less +2 -0
  243. data/lib/less/js/test/less/parens.less +4 -0
  244. data/lib/less/js/test/less/property-name-interp.less +53 -0
  245. data/lib/less/js/test/less/selectors.less +13 -0
  246. data/lib/less/js/test/less/sourcemaps/basic.json +3 -0
  247. data/lib/less/js/test/less/sourcemaps/basic.less +27 -0
  248. data/lib/less/js/test/less/sourcemaps/imported.css +7 -0
  249. data/lib/less/js/test/less/strings.less +6 -0
  250. data/lib/less/js/test/less/url-args/urls.less +63 -0
  251. data/lib/less/js/test/less/urls.less +15 -0
  252. data/lib/less/js/test/rhino/test-header.js +15 -0
  253. data/lib/less/js/test/sourcemaps/basic.json +1 -0
  254. data/lib/less/js/test/sourcemaps/index.html +17 -0
  255. data/lib/less/loader.rb +48 -40
  256. data/lib/less/version.rb +1 -1
  257. data/spec/less/parser_spec.rb +15 -15
  258. metadata +146 -40
  259. data/lib/less/js/Makefile +0 -102
  260. data/lib/less/js/build/header.js +0 -9
  261. data/lib/less/js/test/browser-test-prepare.js +0 -46
  262. data/lib/less/js/test/browser/runner-errors.js +0 -5
  263. data/lib/less/js/test/browser/runner-main.js +0 -15
  264. data/lib/less/js/test/browser/template.htm +0 -10
  265. data/lib/less/js/test/less/errors/color-operation-error.txt +0 -2
@@ -140,7 +140,6 @@
140
140
  'teal':'#008080',
141
141
  'thistle':'#d8bfd8',
142
142
  'tomato':'#ff6347',
143
- // 'transparent':'rgba(0,0,0,0)',
144
143
  'turquoise':'#40e0d0',
145
144
  'violet':'#ee82ee',
146
145
  'wheat':'#f5deb3',
@@ -0,0 +1,4 @@
1
+ // base64 encoder implementation for node
2
+ exports.encodeBase64 = function(str) {
3
+ return new Buffer(str).toString('base64');
4
+ };
@@ -4,14 +4,19 @@
4
4
  'paths', // option - unmodified - paths to search for imports on
5
5
  'optimization', // option - optimization level (for the chunker)
6
6
  'files', // list of files that have been imported, used for import-once
7
- 'contents', // browser-only, contents of all the files
7
+ 'contents', // map - filename to contents of all the files
8
+ 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore
8
9
  'relativeUrls', // option - whether to adjust URL's to be relative
10
+ 'rootpath', // option - rootpath to append to URL's
9
11
  'strictImports', // option -
12
+ 'insecure', // option - whether to allow imports from insecure ssl hosts
10
13
  'dumpLineNumbers', // option - whether to dump line numbers
11
14
  'compress', // option - whether to compress
12
15
  'processImports', // option - whether to process imports. if false then imports will not be imported
13
16
  'syncImport', // option - whether to import synchronously
17
+ 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
14
18
  'mime', // browser only - mime type for sheet import
19
+ 'useFileCache', // browser only - whether to use the per file session cache
15
20
  'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.
16
21
  ];
17
22
 
@@ -21,12 +26,14 @@
21
26
  // 'rootpath' - path to append to normal URLs for this node
22
27
  // 'currentDirectory' - path to the current file, absolute
23
28
  // 'rootFilename' - filename of the base file
24
- // 'entryPath' = absolute path to the entry file
29
+ // 'entryPath' - absolute path to the entry file
30
+ // 'reference' - whether the file should not be output and only output parts that are referenced
25
31
 
26
32
  tree.parseEnv = function(options) {
27
33
  copyFromOriginal(options, this, parseCopyProperties);
28
34
 
29
35
  if (!this.contents) { this.contents = {}; }
36
+ if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; }
30
37
  if (!this.files) { this.files = {}; }
31
38
 
32
39
  if (!this.currentFileInfo) {
@@ -46,22 +53,18 @@
46
53
  }
47
54
  };
48
55
 
49
- tree.parseEnv.prototype.toSheet = function (path) {
50
- var env = new tree.parseEnv(this);
51
- env.href = path;
52
- //env.title = path;
53
- env.type = this.mime;
54
- return env;
55
- };
56
-
57
56
  var evalCopyProperties = [
58
- 'silent', // whether to swallow errors and warnings
59
- 'verbose', // whether to log more activity
60
- 'compress', // whether to compress
61
- 'yuicompress', // whether to compress with the outside tool yui compressor
62
- 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
63
- 'strictMath', // whether math has to be within parenthesis
64
- 'strictUnits' // whether units need to evaluate correctly
57
+ 'silent', // whether to swallow errors and warnings
58
+ 'verbose', // whether to log more activity
59
+ 'compress', // whether to compress
60
+ 'yuicompress', // whether to compress with the outside tool yui compressor
61
+ 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
62
+ 'strictMath', // whether math has to be within parenthesis
63
+ 'strictUnits', // whether units need to evaluate correctly
64
+ 'cleancss', // whether to compress with clean-css
65
+ 'sourceMap', // whether to output a source map
66
+ 'importMultiple', // whether we are currently importing multiple copies
67
+ 'urlArgs' // whether to add args into url tokens
65
68
  ];
66
69
 
67
70
  tree.evalEnv = function(options, frames) {
@@ -89,6 +92,33 @@
89
92
  return !/^(?:[a-z-]+:|\/)/.test(path);
90
93
  };
91
94
 
95
+ tree.evalEnv.prototype.normalizePath = function( path ) {
96
+ var
97
+ segments = path.split("/").reverse(),
98
+ segment;
99
+
100
+ path = [];
101
+ while (segments.length !== 0 ) {
102
+ segment = segments.pop();
103
+ switch( segment ) {
104
+ case ".":
105
+ break;
106
+ case "..":
107
+ if ((path.length === 0) || (path[path.length - 1] === "..")) {
108
+ path.push( segment );
109
+ } else {
110
+ path.pop();
111
+ }
112
+ break;
113
+ default:
114
+ path.push( segment );
115
+ break;
116
+ }
117
+ }
118
+
119
+ return path.join("/");
120
+ };
121
+
92
122
  //todo - do the same for the toCSS env
93
123
  //tree.toCSSEnv = function (options) {
94
124
  //};
@@ -101,5 +131,6 @@
101
131
  destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
102
132
  }
103
133
  }
104
- }
105
- })(require('./tree'));
134
+ };
135
+
136
+ })(require('./tree'));
@@ -1,4 +1,6 @@
1
1
  (function (tree) {
2
+ /*jshint loopfunc:true */
3
+
2
4
  tree.extendFinderVisitor = function() {
3
5
  this._visitor = new tree.visitor(this);
4
6
  this.contexts = [];
@@ -18,7 +20,6 @@
18
20
  visitArgs.visitDeeper = false;
19
21
  },
20
22
  visitRuleset: function (rulesetNode, visitArgs) {
21
-
22
23
  if (rulesetNode.root) {
23
24
  return;
24
25
  }
@@ -26,20 +27,31 @@
26
27
  var i, j, extend, allSelectorsExtendList = [], extendList;
27
28
 
28
29
  // get &:extend(.a); rules which apply to all selectors in this ruleset
29
- for(i = 0; i < rulesetNode.rules.length; i++) {
30
+ var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0;
31
+ for(i = 0; i < ruleCnt; i++) {
30
32
  if (rulesetNode.rules[i] instanceof tree.Extend) {
31
- allSelectorsExtendList.push(rulesetNode.rules[i]);
33
+ allSelectorsExtendList.push(rules[i]);
34
+ rulesetNode.extendOnEveryPath = true;
32
35
  }
33
36
  }
34
37
 
35
38
  // now find every selector and apply the extends that apply to all extends
36
39
  // and the ones which apply to an individual extend
37
- for(i = 0; i < rulesetNode.paths.length; i++) {
38
- var selectorPath = rulesetNode.paths[i],
39
- selector = selectorPath[selectorPath.length-1];
40
- extendList = selector.extendList.slice(0).concat(allSelectorsExtendList).map(function(allSelectorsExtend) {
41
- return allSelectorsExtend.clone();
42
- });
40
+ var paths = rulesetNode.paths;
41
+ for(i = 0; i < paths.length; i++) {
42
+ var selectorPath = paths[i],
43
+ selector = selectorPath[selectorPath.length - 1],
44
+ selExtendList = selector.extendList;
45
+
46
+ extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList)
47
+ : allSelectorsExtendList;
48
+
49
+ if (extendList) {
50
+ extendList = extendList.map(function(allSelectorsExtend) {
51
+ return allSelectorsExtend.clone();
52
+ });
53
+ }
54
+
43
55
  for(j = 0; j < extendList.length; j++) {
44
56
  this.foundExtends = true;
45
57
  extend = extendList[j];
@@ -113,7 +125,7 @@
113
125
  targetExtend = extendsListTarget[targetExtendIndex];
114
126
 
115
127
  // look for circular references
116
- if (this.inInheritanceChain(targetExtend, extend)) { continue; }
128
+ if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; }
117
129
 
118
130
  // find a match in the target extends self selector (the bit before :extend)
119
131
  selectorPath = [targetExtend.selfSelectors[0]];
@@ -139,7 +151,7 @@
139
151
  newExtend.ruleset = targetExtend.ruleset;
140
152
 
141
153
  //remember its parents for circular references
142
- newExtend.parents = [targetExtend, extend];
154
+ newExtend.parent_ids = newExtend.parent_ids.concat(targetExtend.parent_ids, extend.parent_ids);
143
155
 
144
156
  // only process the selector once.. if we have :extend(.a,.b) then multiple
145
157
  // extends will look at the same selector path, so when extending
@@ -175,20 +187,6 @@
175
187
  return extendsToAdd;
176
188
  }
177
189
  },
178
- inInheritanceChain: function (possibleParent, possibleChild) {
179
- if (possibleParent === possibleChild) {
180
- return true;
181
- }
182
- if (possibleChild.parents) {
183
- if (this.inInheritanceChain(possibleParent, possibleChild.parents[0])) {
184
- return true;
185
- }
186
- if (this.inInheritanceChain(possibleParent, possibleChild.parents[1])) {
187
- return true;
188
- }
189
- }
190
- return false;
191
- },
192
190
  visitRule: function (ruleNode, visitArgs) {
193
191
  visitArgs.visitDeeper = false;
194
192
  },
@@ -208,11 +206,12 @@
208
206
 
209
207
  for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) {
210
208
  for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) {
211
-
212
209
  selectorPath = rulesetNode.paths[pathIndex];
213
210
 
214
211
  // extending extends happens initially, before the main pass
215
- if (selectorPath[selectorPath.length-1].extendList.length) { continue; }
212
+ if (rulesetNode.extendOnEveryPath) { continue; }
213
+ var extendList = selectorPath[selectorPath.length-1].extendList;
214
+ if (extendList && extendList.length) { continue; }
216
215
 
217
216
  matches = this.findMatch(allExtends[extendIndex], selectorPath);
218
217
 
@@ -246,7 +245,7 @@
246
245
  haystackElement = hackstackSelector.elements[hackstackElementIndex];
247
246
 
248
247
  // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
249
- if (extend.allowBefore || (haystackSelectorIndex == 0 && hackstackElementIndex == 0)) {
248
+ if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) {
250
249
  potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator});
251
250
  }
252
251
 
@@ -257,7 +256,7 @@
257
256
  // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
258
257
  // what the resulting combinator will be
259
258
  targetCombinator = haystackElement.combinator.value;
260
- if (targetCombinator == '' && hackstackElementIndex === 0) {
259
+ if (targetCombinator === '' && hackstackElementIndex === 0) {
261
260
  targetCombinator = ' ';
262
261
  }
263
262
 
@@ -313,6 +312,24 @@
313
312
  elementValue2 = elementValue2.value.value || elementValue2.value;
314
313
  return elementValue1 === elementValue2;
315
314
  }
315
+ elementValue1 = elementValue1.value;
316
+ elementValue2 = elementValue2.value;
317
+ if (elementValue1 instanceof tree.Selector) {
318
+ if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) {
319
+ return false;
320
+ }
321
+ for(var i = 0; i <elementValue1.elements.length; i++) {
322
+ if (elementValue1.elements[i].combinator.value !== elementValue2.elements[i].combinator.value) {
323
+ if (i !== 0 || (elementValue1.elements[i].combinator.value || ' ') !== (elementValue2.elements[i].combinator.value || ' ')) {
324
+ return false;
325
+ }
326
+ }
327
+ if (!this.isElementValuesEqual(elementValue1.elements[i].value, elementValue2.elements[i].value)) {
328
+ return false;
329
+ }
330
+ }
331
+ return true;
332
+ }
316
333
  return false;
317
334
  },
318
335
  extendSelector:function (matches, selectorPath, replacementSelector) {
@@ -325,7 +342,8 @@
325
342
  matchIndex,
326
343
  selector,
327
344
  firstElement,
328
- match;
345
+ match,
346
+ newElements;
329
347
 
330
348
  for (matchIndex = 0; matchIndex < matches.length; matchIndex++) {
331
349
  match = matches[matchIndex];
@@ -333,7 +351,8 @@
333
351
  firstElement = new tree.Element(
334
352
  match.initialCombinator,
335
353
  replacementSelector.elements[0].value,
336
- replacementSelector.elements[0].index
354
+ replacementSelector.elements[0].index,
355
+ replacementSelector.elements[0].currentFileInfo
337
356
  );
338
357
 
339
358
  if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
@@ -342,17 +361,24 @@
342
361
  currentSelectorPathIndex++;
343
362
  }
344
363
 
345
- path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
364
+ newElements = selector.elements
365
+ .slice(currentSelectorPathElementIndex, match.index)
366
+ .concat([firstElement])
367
+ .concat(replacementSelector.elements.slice(1));
368
+
369
+ if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) {
370
+ path[path.length - 1].elements =
371
+ path[path.length - 1].elements.concat(newElements);
372
+ } else {
373
+ path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
346
374
 
347
- path.push(new tree.Selector(
348
- selector.elements
349
- .slice(currentSelectorPathElementIndex, match.index)
350
- .concat([firstElement])
351
- .concat(replacementSelector.elements.slice(1))
352
- ));
375
+ path.push(new tree.Selector(
376
+ newElements
377
+ ));
378
+ }
353
379
  currentSelectorPathIndex = match.endPathIndex;
354
380
  currentSelectorPathElementIndex = match.endPathElementIndex;
355
- if (currentSelectorPathElementIndex >= selector.elements.length) {
381
+ if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) {
356
382
  currentSelectorPathElementIndex = 0;
357
383
  currentSelectorPathIndex++;
358
384
  }
@@ -360,7 +386,6 @@
360
386
 
361
387
  if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
362
388
  path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
363
- currentSelectorPathElementIndex = 0;
364
389
  currentSelectorPathIndex++;
365
390
  }
366
391
 
@@ -388,4 +413,4 @@
388
413
  }
389
414
  };
390
415
 
391
- })(require('./tree'));
416
+ })(require('./tree'));
@@ -5,7 +5,7 @@ tree.functions = {
5
5
  return this.rgba(r, g, b, 1.0);
6
6
  },
7
7
  rgba: function (r, g, b, a) {
8
- var rgb = [r, g, b].map(function (c) { return scaled(c, 256); });
8
+ var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
9
9
  a = number(a);
10
10
  return new(tree.Color)(rgb, a);
11
11
  },
@@ -13,6 +13,14 @@ tree.functions = {
13
13
  return this.hsla(h, s, l, 1.0);
14
14
  },
15
15
  hsla: function (h, s, l, a) {
16
+ function hue(h) {
17
+ h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
18
+ if (h * 6 < 1) { return m1 + (m2 - m1) * h * 6; }
19
+ else if (h * 2 < 1) { return m2; }
20
+ else if (h * 3 < 2) { return m1 + (m2 - m1) * (2/3 - h) * 6; }
21
+ else { return m1; }
22
+ }
23
+
16
24
  h = (number(h) % 360) / 360;
17
25
  s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));
18
26
 
@@ -23,14 +31,6 @@ tree.functions = {
23
31
  hue(h) * 255,
24
32
  hue(h - 1/3) * 255,
25
33
  a);
26
-
27
- function hue(h) {
28
- h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
29
- if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
30
- else if (h * 2 < 1) return m2;
31
- else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
32
- else return m1;
33
- }
34
34
  },
35
35
 
36
36
  hsv: function(h, s, v) {
@@ -96,6 +96,11 @@ tree.functions = {
96
96
  return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%');
97
97
  },
98
98
  saturate: function (color, amount) {
99
+ // filter: saturate(3.2);
100
+ // should be kept as is, so check for color
101
+ if (!color.rgb) {
102
+ return null;
103
+ }
99
104
  var hsl = color.toHSL();
100
105
 
101
106
  hsl.s += amount.value / 100;
@@ -201,7 +206,7 @@ tree.functions = {
201
206
  } else {
202
207
  threshold = number(threshold);
203
208
  }
204
- if ((color.luma() * color.alpha) < threshold) {
209
+ if (color.luma() < threshold) {
205
210
  return light;
206
211
  } else {
207
212
  return dark;
@@ -218,6 +223,7 @@ tree.functions = {
218
223
  str = quoted.value;
219
224
 
220
225
  for (var i = 0; i < args.length; i++) {
226
+ /*jshint loopfunc:true */
221
227
  str = str.replace(/%[sda]/i, function(token) {
222
228
  var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
223
229
  return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
@@ -227,6 +233,9 @@ tree.functions = {
227
233
  return new(tree.Quoted)('"' + str + '"', str);
228
234
  },
229
235
  unit: function (val, unit) {
236
+ if(!(val instanceof tree.Dimension)) {
237
+ throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") };
238
+ }
230
239
  return new(tree.Dimension)(val.value, unit ? unit.toCSS() : "");
231
240
  },
232
241
  convert: function (val, unit) {
@@ -234,7 +243,7 @@ tree.functions = {
234
243
  },
235
244
  round: function (n, f) {
236
245
  var fraction = typeof(f) === "undefined" ? 0 : f.value;
237
- return this._math(function(num) { return num.toFixed(fraction); }, null, n);
246
+ return _math(function(num) { return num.toFixed(fraction); }, null, n);
238
247
  },
239
248
  pi: function () {
240
249
  return new(tree.Dimension)(Math.PI);
@@ -252,25 +261,67 @@ tree.functions = {
252
261
 
253
262
  return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit);
254
263
  },
255
- _math: function (fn, unit, n) {
256
- if (n instanceof tree.Dimension) {
257
- return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit);
258
- } else if (typeof(n) === 'number') {
259
- return fn(n);
260
- } else {
261
- throw { type: "Argument", message: "argument must be a number" };
264
+ _minmax: function (isMin, args) {
265
+ args = Array.prototype.slice.call(args);
266
+ switch(args.length) {
267
+ case 0: throw { type: "Argument", message: "one or more arguments required" };
268
+ case 1: return args[0];
269
+ }
270
+ var i, j, current, currentUnified, referenceUnified, unit,
271
+ order = [], // elems only contains original argument values.
272
+ values = {}; // key is the unit.toString() for unified tree.Dimension values,
273
+ // value is the index into the order array.
274
+ for (i = 0; i < args.length; i++) {
275
+ current = args[i];
276
+ if (!(current instanceof tree.Dimension)) {
277
+ order.push(current);
278
+ continue;
279
+ }
280
+ currentUnified = current.unify();
281
+ unit = currentUnified.unit.toString();
282
+ j = values[unit];
283
+ if (j === undefined) {
284
+ values[unit] = order.length;
285
+ order.push(current);
286
+ continue;
287
+ }
288
+ referenceUnified = order[j].unify();
289
+ if ( isMin && currentUnified.value < referenceUnified.value ||
290
+ !isMin && currentUnified.value > referenceUnified.value) {
291
+ order[j] = current;
292
+ }
262
293
  }
294
+ if (order.length == 1) {
295
+ return order[0];
296
+ }
297
+ args = order.map(function (a) { return a.toCSS(this.env); })
298
+ .join(this.env.compress ? "," : ", ");
299
+ return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")");
300
+ },
301
+ min: function () {
302
+ return this._minmax(true, arguments);
303
+ },
304
+ max: function () {
305
+ return this._minmax(false, arguments);
263
306
  },
264
307
  argb: function (color) {
265
308
  return new(tree.Anonymous)(color.toARGB());
266
-
267
309
  },
268
310
  percentage: function (n) {
269
311
  return new(tree.Dimension)(n.value * 100, '%');
270
312
  },
271
313
  color: function (n) {
272
314
  if (n instanceof tree.Quoted) {
273
- return new(tree.Color)(n.value.slice(1));
315
+ var colorCandidate = n.value,
316
+ returnColor;
317
+ returnColor = tree.Color.fromKeyword(colorCandidate);
318
+ if (returnColor) {
319
+ return returnColor;
320
+ }
321
+ if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) {
322
+ return new(tree.Color)(colorCandidate.slice(1));
323
+ }
324
+ throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" };
274
325
  } else {
275
326
  throw { type: "Argument", message: "argument must be a string" };
276
327
  }
@@ -305,75 +356,22 @@ tree.functions = {
305
356
  _isa: function (n, Type) {
306
357
  return (n instanceof Type) ? tree.True : tree.False;
307
358
  },
308
-
309
- /* Blending modes */
310
-
311
- multiply: function(color1, color2) {
312
- var r = color1.rgb[0] * color2.rgb[0] / 255;
313
- var g = color1.rgb[1] * color2.rgb[1] / 255;
314
- var b = color1.rgb[2] * color2.rgb[2] / 255;
315
- return this.rgb(r, g, b);
316
- },
317
- screen: function(color1, color2) {
318
- var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255;
319
- var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255;
320
- var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255;
321
- return this.rgb(r, g, b);
322
- },
323
- overlay: function(color1, color2) {
324
- var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255;
325
- var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255;
326
- var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255;
327
- return this.rgb(r, g, b);
328
- },
329
- softlight: function(color1, color2) {
330
- var t = color2.rgb[0] * color1.rgb[0] / 255;
331
- var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255;
332
- t = color2.rgb[1] * color1.rgb[1] / 255;
333
- var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255;
334
- t = color2.rgb[2] * color1.rgb[2] / 255;
335
- var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255;
336
- return this.rgb(r, g, b);
337
- },
338
- hardlight: function(color1, color2) {
339
- var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255;
340
- var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255;
341
- var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255;
342
- return this.rgb(r, g, b);
343
- },
344
- difference: function(color1, color2) {
345
- var r = Math.abs(color1.rgb[0] - color2.rgb[0]);
346
- var g = Math.abs(color1.rgb[1] - color2.rgb[1]);
347
- var b = Math.abs(color1.rgb[2] - color2.rgb[2]);
348
- return this.rgb(r, g, b);
349
- },
350
- exclusion: function(color1, color2) {
351
- var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255;
352
- var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255;
353
- var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255;
354
- return this.rgb(r, g, b);
355
- },
356
- average: function(color1, color2) {
357
- var r = (color1.rgb[0] + color2.rgb[0]) / 2;
358
- var g = (color1.rgb[1] + color2.rgb[1]) / 2;
359
- var b = (color1.rgb[2] + color2.rgb[2]) / 2;
360
- return this.rgb(r, g, b);
361
- },
362
- negation: function(color1, color2) {
363
- var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]);
364
- var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]);
365
- var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]);
366
- return this.rgb(r, g, b);
367
- },
368
359
  tint: function(color, amount) {
369
360
  return this.mix(this.rgb(255,255,255), color, amount);
370
361
  },
371
362
  shade: function(color, amount) {
372
363
  return this.mix(this.rgb(0, 0, 0), color, amount);
373
- },
364
+ },
374
365
  extract: function(values, index) {
375
- index = index.value - 1; // (1-based index)
376
- return values.value[index];
366
+ index = index.value - 1; // (1-based index)
367
+ // handle non-array values as an array of length 1
368
+ // return 'undefined' if index is invalid
369
+ return Array.isArray(values.value)
370
+ ? values.value[index] : Array(values)[index];
371
+ },
372
+ length: function(values) {
373
+ var n = Array.isArray(values.value) ? values.value.length : 1;
374
+ return new tree.Dimension(n);
377
375
  },
378
376
 
379
377
  "data-uri": function(mimetypeNode, filePathNode) {
@@ -385,8 +383,8 @@ tree.functions = {
385
383
  var mimetype = mimetypeNode.value;
386
384
  var filePath = (filePathNode && filePathNode.value);
387
385
 
388
- var fs = require("fs"),
389
- path = require("path"),
386
+ var fs = require('fs'),
387
+ path = require('path'),
390
388
  useBase64 = false;
391
389
 
392
390
  if (arguments.length < 2) {
@@ -415,10 +413,10 @@ tree.functions = {
415
413
  // use base 64 unless it's an ASCII or UTF-8 format
416
414
  var charset = mime.charsets.lookup(mimetype);
417
415
  useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
418
- if (useBase64) mimetype += ';base64';
416
+ if (useBase64) { mimetype += ';base64'; }
419
417
  }
420
418
  else {
421
- useBase64 = /;base64$/.test(mimetype)
419
+ useBase64 = /;base64$/.test(mimetype);
422
420
  }
423
421
 
424
422
  var buf = fs.readFileSync(filePath);
@@ -435,17 +433,90 @@ tree.functions = {
435
433
  }
436
434
 
437
435
  return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
438
- } else if (!this.env.silent) {
439
- // if explicitly disabled (via --no-ie-compat on CLI, or env.ieCompat === false), merely warn
440
- console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
441
436
  }
442
437
  }
443
438
 
444
439
  buf = useBase64 ? buf.toString('base64')
445
440
  : encodeURIComponent(buf);
446
441
 
447
- var uri = "'data:" + mimetype + ',' + buf + "'";
442
+ var uri = "\"data:" + mimetype + ',' + buf + "\"";
448
443
  return new(tree.URL)(new(tree.Anonymous)(uri));
444
+ },
445
+
446
+ "svg-gradient": function(direction) {
447
+
448
+ function throwArgumentDescriptor() {
449
+ throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" };
450
+ }
451
+
452
+ if (arguments.length < 3) {
453
+ throwArgumentDescriptor();
454
+ }
455
+ var stops = Array.prototype.slice.call(arguments, 1),
456
+ gradientDirectionSvg,
457
+ gradientType = "linear",
458
+ rectangleDimension = 'x="0" y="0" width="1" height="1"',
459
+ useBase64 = true,
460
+ renderEnv = {compress: false},
461
+ returner,
462
+ directionValue = direction.toCSS(renderEnv),
463
+ i, color, position, positionValue, alpha;
464
+
465
+ switch (directionValue) {
466
+ case "to bottom":
467
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
468
+ break;
469
+ case "to right":
470
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
471
+ break;
472
+ case "to bottom right":
473
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
474
+ break;
475
+ case "to top right":
476
+ gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
477
+ break;
478
+ case "ellipse":
479
+ case "ellipse at center":
480
+ gradientType = "radial";
481
+ gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
482
+ rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
483
+ break;
484
+ default:
485
+ throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" };
486
+ }
487
+ returner = '<?xml version="1.0" ?>' +
488
+ '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
489
+ '<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
490
+
491
+ for (i = 0; i < stops.length; i+= 1) {
492
+ if (stops[i].value) {
493
+ color = stops[i].value[0];
494
+ position = stops[i].value[1];
495
+ } else {
496
+ color = stops[i];
497
+ position = undefined;
498
+ }
499
+
500
+ if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) {
501
+ throwArgumentDescriptor();
502
+ }
503
+ positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%";
504
+ alpha = color.alpha;
505
+ returner += '<stop offset="' + positionValue + '" stop-color="' + color.toRGB() + '"' + (alpha < 1 ? ' stop-opacity="' + alpha + '"' : '') + '/>';
506
+ }
507
+ returner += '</' + gradientType + 'Gradient>' +
508
+ '<rect ' + rectangleDimension + ' fill="url(#gradient)" /></svg>';
509
+
510
+ if (useBase64) {
511
+ try {
512
+ returner = require('./encoder').encodeBase64(returner); // TODO browser implementation
513
+ } catch(e) {
514
+ useBase64 = false;
515
+ }
516
+ }
517
+
518
+ returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'";
519
+ return new(tree.URL)(new(tree.Anonymous)(returner));
449
520
  }
450
521
  };
451
522
 
@@ -477,22 +548,146 @@ tree._mime = {
477
548
  }
478
549
  };
479
550
 
480
- var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"},
481
- {name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""},
482
- {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}],
483
- createMathFunction = function(name, unit) {
484
- return function(n) {
485
- if (unit != null) {
486
- n = n.unify();
487
- }
488
- return this._math(Math[name], unit, n);
489
- };
490
- };
551
+ // Math
552
+
553
+ var mathFunctions = {
554
+ // name, unit
555
+ ceil: null,
556
+ floor: null,
557
+ sqrt: null,
558
+ abs: null,
559
+ tan: "",
560
+ sin: "",
561
+ cos: "",
562
+ atan: "rad",
563
+ asin: "rad",
564
+ acos: "rad"
565
+ };
566
+
567
+ function _math(fn, unit, n) {
568
+ if (!(n instanceof tree.Dimension)) {
569
+ throw { type: "Argument", message: "argument must be a number" };
570
+ }
571
+ if (unit == null) {
572
+ unit = n.unit;
573
+ } else {
574
+ n = n.unify();
575
+ }
576
+ return new(tree.Dimension)(fn(parseFloat(n.value)), unit);
577
+ }
491
578
 
492
- for(var i = 0; i < mathFunctions.length; i++) {
493
- tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit);
579
+ // ~ End of Math
580
+
581
+ // Color Blending
582
+ // ref: http://www.w3.org/TR/compositing-1
583
+
584
+ function colorBlend(mode, color1, color2) {
585
+ var ab = color1.alpha, cb, // backdrop
586
+ as = color2.alpha, cs, // source
587
+ ar, cr, r = []; // result
588
+
589
+ ar = as + ab * (1 - as);
590
+ for (var i = 0; i < 3; i++) {
591
+ cb = color1.rgb[i] / 255;
592
+ cs = color2.rgb[i] / 255;
593
+ cr = mode(cb, cs);
594
+ if (ar) {
595
+ cr = (as * cs + ab * (cb
596
+ - as * (cb + cs - cr))) / ar;
597
+ }
598
+ r[i] = cr * 255;
599
+ }
600
+
601
+ return new(tree.Color)(r, ar);
494
602
  }
495
603
 
604
+ var colorBlendMode = {
605
+ multiply: function(cb, cs) {
606
+ return cb * cs;
607
+ },
608
+ screen: function(cb, cs) {
609
+ return cb + cs - cb * cs;
610
+ },
611
+ overlay: function(cb, cs) {
612
+ cb *= 2;
613
+ return (cb <= 1)
614
+ ? colorBlendMode.multiply(cb, cs)
615
+ : colorBlendMode.screen(cb - 1, cs);
616
+ },
617
+ softlight: function(cb, cs) {
618
+ var d = 1, e = cb;
619
+ if (cs > 0.5) {
620
+ e = 1;
621
+ d = (cb > 0.25) ? Math.sqrt(cb)
622
+ : ((16 * cb - 12) * cb + 4) * cb;
623
+ }
624
+ return cb - (1 - 2 * cs) * e * (d - cb);
625
+ },
626
+ hardlight: function(cb, cs) {
627
+ return colorBlendMode.overlay(cs, cb);
628
+ },
629
+ difference: function(cb, cs) {
630
+ return Math.abs(cb - cs);
631
+ },
632
+ exclusion: function(cb, cs) {
633
+ return cb + cs - 2 * cb * cs;
634
+ },
635
+
636
+ // non-w3c functions:
637
+ average: function(cb, cs) {
638
+ return (cb + cs) / 2;
639
+ },
640
+ negation: function(cb, cs) {
641
+ return 1 - Math.abs(cb + cs - 1);
642
+ }
643
+ };
644
+
645
+ // ~ End of Color Blending
646
+
647
+ tree.defaultFunc = {
648
+ eval: function () {
649
+ var v = this.value_, e = this.error_;
650
+ if (e) {
651
+ throw e;
652
+ }
653
+ if (v != null) {
654
+ return v ? tree.True : tree.False;
655
+ }
656
+ },
657
+ value: function (v) {
658
+ this.value_ = v;
659
+ },
660
+ error: function (e) {
661
+ this.error_ = e;
662
+ },
663
+ reset: function () {
664
+ this.value_ = this.error_ = null;
665
+ }
666
+ };
667
+
668
+ function initFunctions() {
669
+ var f, tf = tree.functions;
670
+
671
+ // math
672
+ for (f in mathFunctions) {
673
+ if (mathFunctions.hasOwnProperty(f)) {
674
+ tf[f] = _math.bind(null, Math[f], mathFunctions[f]);
675
+ }
676
+ }
677
+
678
+ // color blending
679
+ for (f in colorBlendMode) {
680
+ if (colorBlendMode.hasOwnProperty(f)) {
681
+ tf[f] = colorBlend.bind(null, colorBlendMode[f]);
682
+ }
683
+ }
684
+
685
+ // default
686
+ f = tree.defaultFunc;
687
+ tf["default"] = f.eval.bind(f);
688
+
689
+ } initFunctions();
690
+
496
691
  function hsla(color) {
497
692
  return tree.functions.hsla(color.h, color.s, color.l, color.a);
498
693
  }
@@ -522,6 +717,16 @@ function clamp(val) {
522
717
  return Math.min(1, Math.max(0, val));
523
718
  }
524
719
 
720
+ tree.fround = function(env, value) {
721
+ var p;
722
+ if (env && (env.numPrecision != null)) {
723
+ p = Math.pow(10, env.numPrecision);
724
+ return Math.round(value * p) / p;
725
+ } else {
726
+ return value;
727
+ }
728
+ };
729
+
525
730
  tree.functionCall = function(env, currentFileInfo) {
526
731
  this.env = env;
527
732
  this.currentFileInfo = currentFileInfo;