less 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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;