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