less 2.2.2 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Changelog.md +17 -0
- data/Gemfile +7 -3
- data/README.md +29 -13
- data/bin/lessc +3 -1
- data/less.gemspec +2 -6
- data/lib/less/defaults.rb +3 -3
- data/lib/less/java_script/rhino_context.rb +17 -27
- data/lib/less/java_script/v8_context.rb +11 -11
- 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 +194 -194
- data/lib/less/js/benchmark/less-benchmark.js +9 -10
- data/lib/less/js/bin/lessc +310 -37
- data/lib/less/js/bower.json +18 -0
- data/lib/less/js/build/README.md +51 -0
- data/lib/less/js/build/amd.js +1 -1
- 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 +7 -2
- 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.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.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 +547 -250
- 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 +576 -36
- data/lib/less/js/lib/less/import-visitor.js +142 -0
- data/lib/less/js/lib/less/index.js +187 -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 +1286 -523
- data/lib/less/js/lib/less/rhino.js +415 -29
- 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 +17 -5
- data/lib/less/js/lib/less/tree/anonymous.js +25 -4
- data/lib/less/js/lib/less/tree/assignment.js +17 -5
- data/lib/less/js/lib/less/tree/call.js +36 -13
- data/lib/less/js/lib/less/tree/color.js +119 -31
- data/lib/less/js/lib/less/tree/comment.js +18 -4
- data/lib/less/js/lib/less/tree/condition.js +35 -28
- data/lib/less/js/lib/less/tree/detached-ruleset.js +20 -0
- data/lib/less/js/lib/less/tree/dimension.js +291 -16
- data/lib/less/js/lib/less/tree/directive.js +56 -22
- data/lib/less/js/lib/less/tree/element.js +78 -17
- data/lib/less/js/lib/less/tree/expression.js +39 -8
- data/lib/less/js/lib/less/tree/extend.js +53 -0
- data/lib/less/js/lib/less/tree/import.js +81 -36
- data/lib/less/js/lib/less/tree/javascript.js +17 -10
- data/lib/less/js/lib/less/tree/keyword.js +8 -3
- data/lib/less/js/lib/less/tree/media.js +70 -27
- data/lib/less/js/lib/less/tree/mixin.js +250 -70
- data/lib/less/js/lib/less/tree/negative.js +24 -0
- data/lib/less/js/lib/less/tree/operation.js +38 -13
- data/lib/less/js/lib/less/tree/paren.js +9 -2
- data/lib/less/js/lib/less/tree/quoted.js +29 -10
- data/lib/less/js/lib/less/tree/rule.js +77 -29
- data/lib/less/js/lib/less/tree/ruleset-call.js +16 -0
- data/lib/less/js/lib/less/tree/ruleset.js +445 -107
- data/lib/less/js/lib/less/tree/selector.js +117 -30
- data/lib/less/js/lib/less/tree/unicode-descriptor.js +15 -0
- data/lib/less/js/lib/less/tree/url.js +43 -15
- data/lib/less/js/lib/less/tree/value.js +16 -6
- data/lib/less/js/lib/less/tree/variable.js +26 -8
- data/lib/less/js/lib/less/tree.js +82 -2
- 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 +29 -0
- data/lib/less/js/test/css/comments.css +15 -2
- data/lib/less/js/test/css/compression/compression.css +3 -0
- data/lib/less/js/test/css/css-3.css +73 -2
- data/lib/less/js/test/css/css-escapes.css +4 -0
- data/lib/less/js/test/css/css-guards.css +37 -0
- data/lib/less/js/test/css/css.css +20 -14
- 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 +117 -2
- 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 +7 -3
- 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 +23 -10
- 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 +141 -1
- data/lib/less/js/test/css/merge.css +34 -0
- data/lib/less/js/test/css/mixins-args.css +41 -4
- data/lib/less/js/test/css/mixins-guards-default-func.css +129 -0
- data/lib/less/js/test/css/mixins-guards.css +24 -0
- data/lib/less/js/test/css/mixins-important.css +28 -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 +4 -0
- data/lib/less/js/test/css/mixins.css +70 -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/parens.css +21 -5
- data/lib/less/js/test/css/property-name-interp.css +20 -0
- data/lib/less/js/test/css/scope.css +23 -0
- data/lib/less/js/test/css/selectors.css +84 -0
- data/lib/less/js/test/css/static-urls/urls.css +45 -0
- data/lib/less/js/test/css/strings.css +5 -2
- 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 +22 -4
- data/lib/less/js/test/css/whitespace.css +4 -0
- 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 +37 -4
- data/lib/less/js/test/less/comments.less +21 -3
- data/lib/less/js/test/less/compression/compression.less +36 -0
- data/lib/less/js/test/less/css-3.less +70 -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 +23 -15
- 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 +130 -6
- 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 +7 -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-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 +13 -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 +158 -3
- data/lib/less/js/test/less/merge.less +78 -0
- data/lib/less/js/test/less/mixins-args.less +93 -15
- data/lib/less/js/test/less/mixins-guards-default-func.less +195 -0
- data/lib/less/js/test/less/mixins-guards.less +65 -0
- data/lib/less/js/test/less/mixins-important.less +8 -1
- 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 +4 -1
- data/lib/less/js/test/less/mixins.less +74 -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 +27 -27
- 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 +101 -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 +7 -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 +42 -13
- data/lib/less/js/test/less/whitespace.less +7 -0
- data/lib/less/js/test/less-test.js +251 -65
- 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 +191 -27
- data/lib/less/parser.rb +47 -8
- data/lib/less/version.rb +2 -2
- data/spec/less/loader_spec.rb +45 -0
- data/spec/less/parser_spec.rb +43 -17
- data/spec/less/some/some.css +0 -0
- data/spec/less/some/some.less +4 -0
- metadata +352 -57
- data/lib/less/js/CHANGELOG +0 -41
- data/lib/less/js/Makefile +0 -75
- data/lib/less/js/build/ecma-5.js +0 -120
- data/lib/less/js/build/header.js +0 -7
- data/lib/less/js/lib/less/cssmin.js +0 -355
@@ -1,23 +1,10 @@
|
|
1
1
|
var less, tree;
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
if (typeof(window) === 'undefined') { less = {} }
|
7
|
-
else { less = window.less = {} }
|
8
|
-
tree = less.tree = {};
|
9
|
-
less.mode = 'rhino';
|
10
|
-
} else if (typeof(window) === 'undefined') {
|
11
|
-
// Node.js
|
12
|
-
less = exports,
|
3
|
+
// Node.js does not have a header file added which defines less
|
4
|
+
if (less === undefined) {
|
5
|
+
less = exports;
|
13
6
|
tree = require('./tree');
|
14
7
|
less.mode = 'node';
|
15
|
-
} else {
|
16
|
-
// Browser
|
17
|
-
if (typeof(window.less) === 'undefined') { window.less = {} }
|
18
|
-
less = window.less,
|
19
|
-
tree = window.less.tree = {};
|
20
|
-
less.mode = 'browser';
|
21
8
|
}
|
22
9
|
//
|
23
10
|
// less.js - parser
|
@@ -32,7 +19,7 @@ if (typeof environment === "object" && ({}).toString.call(environment) === "[obj
|
|
32
19
|
// - Matching and slicing on a huge input is often cause of slowdowns.
|
33
20
|
// The solution is to chunkify the input into smaller strings.
|
34
21
|
// The chunks are stored in the `chunks` var,
|
35
|
-
// `j` holds the current chunk index, and `
|
22
|
+
// `j` holds the current chunk index, and `currentPos` holds
|
36
23
|
// the index of the current chunk in relation to `input`.
|
37
24
|
// This gives us an almost 4x speed-up.
|
38
25
|
//
|
@@ -56,125 +43,212 @@ less.Parser = function Parser(env) {
|
|
56
43
|
var input, // LeSS input string
|
57
44
|
i, // current index in `input`
|
58
45
|
j, // current chunk
|
59
|
-
|
60
|
-
memo, // temporarily holds `i`, when backtracking
|
46
|
+
saveStack = [], // holds state for backtracking
|
61
47
|
furthest, // furthest index the parser has gone to
|
62
48
|
chunks, // chunkified input
|
63
|
-
current, //
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
//
|
70
|
-
|
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
|
+
}
|
71
60
|
|
72
61
|
var imports = this.imports = {
|
73
|
-
paths: env
|
74
|
-
queue: [],
|
75
|
-
files:
|
76
|
-
contents:
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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;
|
81
71
|
this.queue.push(path);
|
82
72
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
that.contents[path] = contents;
|
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
|
90
79
|
|
91
|
-
if (e && !
|
92
|
-
callback(e, root);
|
80
|
+
if (e && !parserImports.error) { parserImports.error = e; }
|
93
81
|
|
94
|
-
|
95
|
-
}
|
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;
|
96
|
+
|
97
|
+
if (currentFileInfo.reference || importOptions.reference) {
|
98
|
+
newFileInfo.reference = true;
|
99
|
+
}
|
100
|
+
|
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
|
+
}
|
96
110
|
}
|
97
111
|
};
|
98
112
|
|
99
|
-
function save() {
|
100
|
-
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(); }
|
101
116
|
|
102
117
|
function sync() {
|
103
|
-
if (i >
|
104
|
-
|
105
|
-
|
118
|
+
if (i > currentPos) {
|
119
|
+
current = current.slice(i - currentPos);
|
120
|
+
currentPos = i;
|
106
121
|
}
|
107
122
|
}
|
123
|
+
function isWhitespace(str, pos) {
|
124
|
+
var code = str.charCodeAt(pos | 0);
|
125
|
+
return (code <= 32) && (code === 32 || code === 10 || code === 9);
|
126
|
+
}
|
108
127
|
//
|
109
128
|
// Parse from a token, regexp or string, and move forward if match
|
110
129
|
//
|
111
130
|
function $(tok) {
|
112
|
-
var
|
131
|
+
var tokType = typeof tok,
|
132
|
+
match, length;
|
113
133
|
|
134
|
+
// Either match a single character in the input,
|
135
|
+
// or match a regexp in the current chunk (`current`).
|
114
136
|
//
|
115
|
-
|
116
|
-
|
117
|
-
if (tok instanceof Function) {
|
118
|
-
return tok.call(parser.parsers);
|
119
|
-
//
|
120
|
-
// Terminal
|
121
|
-
//
|
122
|
-
// Either match a single character in the input,
|
123
|
-
// or match a regexp in the current chunk (chunk[j]).
|
124
|
-
//
|
125
|
-
} else if (typeof(tok) === 'string') {
|
126
|
-
match = input.charAt(i) === tok ? tok : null;
|
127
|
-
length = 1;
|
128
|
-
sync ();
|
129
|
-
} else {
|
130
|
-
sync ();
|
131
|
-
|
132
|
-
if (match = tok.exec(chunks[j])) {
|
133
|
-
length = match[0].length;
|
134
|
-
} else {
|
137
|
+
if (tokType === "string") {
|
138
|
+
if (input.charAt(i) !== tok) {
|
135
139
|
return null;
|
136
140
|
}
|
141
|
+
skipWhitespace(1);
|
142
|
+
return tok;
|
143
|
+
}
|
144
|
+
|
145
|
+
// regexp
|
146
|
+
sync ();
|
147
|
+
if (! (match = tok.exec(current))) {
|
148
|
+
return null;
|
137
149
|
}
|
138
150
|
|
151
|
+
length = match[0].length;
|
152
|
+
|
139
153
|
// The match is confirmed, add the match length to `i`,
|
140
154
|
// and consume any extra white-space characters (' ' || '\n')
|
141
155
|
// which come after that. The reason for this is that LeSS's
|
142
156
|
// grammar is mostly white-space insensitive.
|
143
157
|
//
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
}
|
153
|
-
chunks[j] = chunks[j].slice(length + (i - mem));
|
154
|
-
current = i;
|
158
|
+
skipWhitespace(length);
|
159
|
+
|
160
|
+
if(typeof(match) === 'string') {
|
161
|
+
return match;
|
162
|
+
} else {
|
163
|
+
return match.length === 1 ? match[0] : match;
|
164
|
+
}
|
165
|
+
}
|
155
166
|
|
156
|
-
|
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
|
+
}
|
157
177
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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;
|
209
|
+
}
|
210
|
+
|
211
|
+
if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) {
|
212
|
+
break;
|
162
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
|
163
223
|
}
|
224
|
+
|
225
|
+
return oldi !== i || oldj !== j;
|
164
226
|
}
|
165
227
|
|
166
228
|
function expect(arg, msg) {
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
: "unexpected token"));
|
171
|
-
} else {
|
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) {
|
172
232
|
return result;
|
173
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) + "'");
|
174
245
|
}
|
175
246
|
|
176
247
|
function error(msg, type) {
|
177
|
-
|
248
|
+
var e = new Error(msg);
|
249
|
+
e.index = i;
|
250
|
+
e.type = type || 'Syntax';
|
251
|
+
throw e;
|
178
252
|
}
|
179
253
|
|
180
254
|
// Same as $(), but don't change the state of the parser,
|
@@ -183,37 +257,53 @@ less.Parser = function Parser(env) {
|
|
183
257
|
if (typeof(tok) === 'string') {
|
184
258
|
return input.charAt(i) === tok;
|
185
259
|
} else {
|
186
|
-
|
187
|
-
return true;
|
188
|
-
} else {
|
189
|
-
return false;
|
190
|
-
}
|
260
|
+
return tok.test(current);
|
191
261
|
}
|
192
262
|
}
|
193
263
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
} else {
|
198
|
-
return pathname.match(/[^\/]+$/)[0];
|
199
|
-
}
|
264
|
+
// Specialization of peek()
|
265
|
+
function peekChar(tok) {
|
266
|
+
return input.charAt(i) === tok;
|
200
267
|
}
|
201
268
|
|
269
|
+
|
202
270
|
function getInput(e, env) {
|
203
|
-
if (e.filename && env.filename && (e.filename !== env.filename)) {
|
204
|
-
return parser.imports.contents[
|
271
|
+
if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) {
|
272
|
+
return parser.imports.contents[e.filename];
|
205
273
|
} else {
|
206
274
|
return input;
|
207
275
|
}
|
208
276
|
}
|
209
277
|
|
210
|
-
function getLocation(index,
|
211
|
-
|
212
|
-
|
213
|
-
|
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);
|
301
|
+
}
|
214
302
|
|
215
|
-
return {
|
216
|
-
|
303
|
+
return {
|
304
|
+
lineNumber: getLocation(index, inputStream).line + 1,
|
305
|
+
fileName: filename
|
306
|
+
};
|
217
307
|
}
|
218
308
|
|
219
309
|
function LessError(e, env) {
|
@@ -221,15 +311,16 @@ less.Parser = function Parser(env) {
|
|
221
311
|
loc = getLocation(e.index, input),
|
222
312
|
line = loc.line,
|
223
313
|
col = loc.column,
|
314
|
+
callLine = e.call && getLocation(e.call, input).line,
|
224
315
|
lines = input.split('\n');
|
225
316
|
|
226
317
|
this.type = e.type || 'Syntax';
|
227
318
|
this.message = e.message;
|
228
|
-
this.filename = e.filename || env.filename;
|
319
|
+
this.filename = e.filename || env.currentFileInfo.filename;
|
229
320
|
this.index = e.index;
|
230
321
|
this.line = typeof(line) === 'number' ? line + 1 : null;
|
231
|
-
this.callLine =
|
232
|
-
this.callExtract = lines[
|
322
|
+
this.callLine = callLine + 1;
|
323
|
+
this.callExtract = lines[callLine];
|
233
324
|
this.stack = e.stack;
|
234
325
|
this.column = col;
|
235
326
|
this.extract = [
|
@@ -239,6 +330,9 @@ less.Parser = function Parser(env) {
|
|
239
330
|
];
|
240
331
|
}
|
241
332
|
|
333
|
+
LessError.prototype = new Error();
|
334
|
+
LessError.prototype.constructor = LessError;
|
335
|
+
|
242
336
|
this.env = env = env || {};
|
243
337
|
|
244
338
|
// The optimization level dictates the thoroughness of the parser,
|
@@ -247,109 +341,183 @@ less.Parser = function Parser(env) {
|
|
247
341
|
// the individual nodes in the tree.
|
248
342
|
this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;
|
249
343
|
|
250
|
-
this.env.filename = this.env.filename || null;
|
251
|
-
|
252
344
|
//
|
253
345
|
// The Parser
|
254
346
|
//
|
255
|
-
|
347
|
+
parser = {
|
256
348
|
|
257
349
|
imports: imports,
|
258
350
|
//
|
259
351
|
// Parse an input string into an abstract syntax tree,
|
260
|
-
//
|
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
|
261
355
|
//
|
262
|
-
parse: function (str, callback) {
|
263
|
-
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;
|
360
|
+
|
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
|
+
}
|
264
368
|
|
265
|
-
|
266
|
-
|
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;
|
267
373
|
|
268
374
|
// Split the input into chunks.
|
269
|
-
chunks = (function (
|
270
|
-
var
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
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;
|
286
394
|
}
|
287
|
-
|
288
|
-
|
395
|
+
chunks.push(input.slice(emitFrom, parserCurrentIndex + 1));
|
396
|
+
emitFrom = parserCurrentIndex + 1;
|
397
|
+
}
|
289
398
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
}
|
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;
|
296
404
|
}
|
297
405
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
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++;
|
306
446
|
}
|
307
447
|
}
|
308
|
-
|
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;
|
309
479
|
}
|
480
|
+
}
|
310
481
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
default: chunk.push(c);
|
482
|
+
if (level !== 0) {
|
483
|
+
if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
|
484
|
+
return fail("missing closing `}` or `*/`", lastOpening);
|
485
|
+
} else {
|
486
|
+
return fail("missing closing `}`", lastOpening);
|
317
487
|
}
|
318
|
-
}
|
319
|
-
|
320
|
-
error = new(LessError)({
|
321
|
-
index: i,
|
322
|
-
type: 'Parse',
|
323
|
-
message: "missing closing `}`",
|
324
|
-
filename: env.filename
|
325
|
-
}, env);
|
488
|
+
} else if (parenLevel !== 0) {
|
489
|
+
return fail("missing closing `)`", lastOpeningParen);
|
326
490
|
}
|
327
491
|
|
328
|
-
|
329
|
-
|
492
|
+
emitChunk(true);
|
493
|
+
return chunks;
|
494
|
+
})(str);
|
330
495
|
|
331
496
|
if (error) {
|
332
|
-
return callback(error);
|
497
|
+
return callback(new(LessError)(error, env));
|
333
498
|
}
|
334
499
|
|
500
|
+
current = chunks[0];
|
501
|
+
|
335
502
|
// Start with the primary rule.
|
336
503
|
// The whole syntax tree is held under a Ruleset node,
|
337
504
|
// with the `root` property set to true, so no `{}` are
|
338
505
|
// output. The callback is called when the input is parsed.
|
339
506
|
try {
|
340
|
-
root = new(tree.Ruleset)(
|
507
|
+
root = new(tree.Ruleset)(null, this.parsers.primary());
|
341
508
|
root.root = true;
|
509
|
+
root.firstRoot = true;
|
342
510
|
} catch (e) {
|
343
511
|
return callback(new(LessError)(e, env));
|
344
512
|
}
|
345
513
|
|
346
514
|
root.toCSS = (function (evaluate) {
|
347
|
-
var line, lines, column;
|
348
|
-
|
349
515
|
return function (options, variables) {
|
350
|
-
var frames = [], importError;
|
351
|
-
|
352
516
|
options = options || {};
|
517
|
+
var evaldRoot,
|
518
|
+
css,
|
519
|
+
evalEnv = new tree.evalEnv(options);
|
520
|
+
|
353
521
|
//
|
354
522
|
// Allows setting variables with a hash, so:
|
355
523
|
//
|
@@ -373,27 +541,85 @@ less.Parser = function Parser(env) {
|
|
373
541
|
}
|
374
542
|
value = new(tree.Value)([value]);
|
375
543
|
}
|
376
|
-
return new(tree.Rule)('@' + k, value, false, 0);
|
544
|
+
return new(tree.Rule)('@' + k, value, false, null, 0);
|
377
545
|
});
|
378
|
-
frames = [new(tree.Ruleset)(null, variables)];
|
546
|
+
evalEnv.frames = [new(tree.Ruleset)(null, variables)];
|
379
547
|
}
|
380
548
|
|
381
549
|
try {
|
382
|
-
var
|
383
|
-
|
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});
|
384
603
|
} catch (e) {
|
385
604
|
throw new(LessError)(e, env);
|
386
605
|
}
|
387
606
|
|
388
|
-
if (
|
389
|
-
|
390
|
-
|
391
|
-
}
|
607
|
+
if (options.cleancss && less.mode === 'node') {
|
608
|
+
var CleanCSS = require('clean-css'),
|
609
|
+
cleancssOptions = options.cleancssOptions || {};
|
392
610
|
|
393
|
-
|
394
|
-
|
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);
|
395
621
|
} else if (options.compress) {
|
396
|
-
return css.replace(/(\s)
|
622
|
+
return css.replace(/(^(\s)+)|((\s)+$)/g, "");
|
397
623
|
} else {
|
398
624
|
return css;
|
399
625
|
}
|
@@ -410,18 +636,17 @@ less.Parser = function Parser(env) {
|
|
410
636
|
// and the part which didn't), so we can color them differently.
|
411
637
|
if (i < input.length - 1) {
|
412
638
|
i = furthest;
|
639
|
+
var loc = getLocation(i, input);
|
413
640
|
lines = input.split('\n');
|
414
|
-
line =
|
415
|
-
|
416
|
-
for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ }
|
641
|
+
line = loc.line + 1;
|
417
642
|
|
418
643
|
error = {
|
419
644
|
type: "Parse",
|
420
|
-
message: "
|
645
|
+
message: "Unrecognised input",
|
421
646
|
index: i,
|
422
|
-
filename: env.filename,
|
647
|
+
filename: env.currentFileInfo.filename,
|
423
648
|
line: line,
|
424
|
-
column: column,
|
649
|
+
column: loc.column,
|
425
650
|
extract: [
|
426
651
|
lines[line - 2],
|
427
652
|
lines[line - 1],
|
@@ -430,10 +655,26 @@ less.Parser = function Parser(env) {
|
|
430
655
|
};
|
431
656
|
}
|
432
657
|
|
433
|
-
|
434
|
-
|
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);
|
435
676
|
} else {
|
436
|
-
|
677
|
+
return finish();
|
437
678
|
}
|
438
679
|
},
|
439
680
|
|
@@ -466,7 +707,7 @@ less.Parser = function Parser(env) {
|
|
466
707
|
// value is truly, will return a new node, of the relevant type. Sometimes, we need to check
|
467
708
|
// first, before parsing, that's when we use `peek()`.
|
468
709
|
//
|
469
|
-
parsers: {
|
710
|
+
parsers: parsers = {
|
470
711
|
//
|
471
712
|
// The `primary` rule is the *entry* and *exit* point of the parser.
|
472
713
|
// The rules here can appear at any level of the parse tree.
|
@@ -483,13 +724,24 @@ less.Parser = function Parser(env) {
|
|
483
724
|
// block rule: at the root level.
|
484
725
|
//
|
485
726
|
primary: function () {
|
486
|
-
var
|
487
|
-
|
488
|
-
while (
|
489
|
-
|
490
|
-
|
491
|
-
|
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
|
+
}
|
492
743
|
}
|
744
|
+
|
493
745
|
return root;
|
494
746
|
},
|
495
747
|
|
@@ -499,13 +751,29 @@ less.Parser = function Parser(env) {
|
|
499
751
|
comment: function () {
|
500
752
|
var comment;
|
501
753
|
|
502
|
-
if (input.charAt(i) !== '/') return;
|
754
|
+
if (input.charAt(i) !== '/') { return; }
|
503
755
|
|
504
756
|
if (input.charAt(i + 1) === '/') {
|
505
|
-
return new(tree.Comment)($(/^\/\/.*/), true);
|
506
|
-
}
|
507
|
-
|
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);
|
762
|
+
}
|
763
|
+
},
|
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);
|
508
774
|
}
|
775
|
+
|
776
|
+
return comments;
|
509
777
|
},
|
510
778
|
|
511
779
|
//
|
@@ -518,15 +786,16 @@ less.Parser = function Parser(env) {
|
|
518
786
|
// "milky way" 'he\'s the one!'
|
519
787
|
//
|
520
788
|
quoted: function () {
|
521
|
-
var str, j = i, e;
|
789
|
+
var str, j = i, e, index = i;
|
522
790
|
|
523
|
-
if (input.charAt(j) === '~') { j
|
524
|
-
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; }
|
525
793
|
|
526
|
-
e
|
794
|
+
if (e) { $char('~'); }
|
527
795
|
|
528
|
-
|
529
|
-
|
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);
|
530
799
|
}
|
531
800
|
},
|
532
801
|
|
@@ -538,13 +807,13 @@ less.Parser = function Parser(env) {
|
|
538
807
|
keyword: function () {
|
539
808
|
var k;
|
540
809
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
return new(tree.Keyword)(k);
|
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;
|
547
815
|
}
|
816
|
+
return new(tree.Keyword)(k);
|
548
817
|
}
|
549
818
|
},
|
550
819
|
|
@@ -559,38 +828,56 @@ less.Parser = function Parser(env) {
|
|
559
828
|
// The arguments are parsed with the `entities.arguments` parser.
|
560
829
|
//
|
561
830
|
call: function () {
|
562
|
-
var name, args, index = i;
|
831
|
+
var name, nameLC, args, alpha_ret, index = i;
|
563
832
|
|
564
|
-
|
833
|
+
name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current);
|
834
|
+
if (!name) { return; }
|
565
835
|
|
566
|
-
name = name[1]
|
836
|
+
name = name[1];
|
837
|
+
nameLC = name.toLowerCase();
|
838
|
+
if (nameLC === 'url') {
|
839
|
+
return null;
|
840
|
+
}
|
567
841
|
|
568
|
-
|
569
|
-
else { i += name.length }
|
842
|
+
i += name.length;
|
570
843
|
|
571
|
-
if (
|
844
|
+
if (nameLC === 'alpha') {
|
845
|
+
alpha_ret = parsers.alpha();
|
846
|
+
if(typeof alpha_ret !== 'undefined') {
|
847
|
+
return alpha_ret;
|
848
|
+
}
|
849
|
+
}
|
572
850
|
|
573
|
-
$('('); // Parse the '(' and consume whitespace.
|
851
|
+
$char('('); // Parse the '(' and consume whitespace.
|
574
852
|
|
575
|
-
args =
|
853
|
+
args = this.arguments();
|
576
854
|
|
577
|
-
if (! $(')'))
|
855
|
+
if (! $char(')')) {
|
856
|
+
return;
|
857
|
+
}
|
578
858
|
|
579
|
-
if (name) { return new(tree.Call)(name, args, index, env.
|
859
|
+
if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); }
|
580
860
|
},
|
581
861
|
arguments: function () {
|
582
862
|
var args = [], arg;
|
583
863
|
|
584
|
-
while (
|
864
|
+
while (true) {
|
865
|
+
arg = this.assignment() || parsers.expression();
|
866
|
+
if (!arg) {
|
867
|
+
break;
|
868
|
+
}
|
585
869
|
args.push(arg);
|
586
|
-
if (! $(',')) {
|
870
|
+
if (! $char(',')) {
|
871
|
+
break;
|
872
|
+
}
|
587
873
|
}
|
588
874
|
return args;
|
589
875
|
},
|
590
876
|
literal: function () {
|
591
|
-
return
|
592
|
-
|
593
|
-
|
877
|
+
return this.dimension() ||
|
878
|
+
this.color() ||
|
879
|
+
this.quoted() ||
|
880
|
+
this.unicodeDescriptor();
|
594
881
|
},
|
595
882
|
|
596
883
|
// Assignments are argument entities for calls.
|
@@ -601,7 +888,15 @@ less.Parser = function Parser(env) {
|
|
601
888
|
|
602
889
|
assignment: function () {
|
603
890
|
var key, value;
|
604
|
-
|
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) {
|
605
900
|
return new(tree.Assignment)(key, value);
|
606
901
|
}
|
607
902
|
},
|
@@ -616,28 +911,17 @@ less.Parser = function Parser(env) {
|
|
616
911
|
url: function () {
|
617
912
|
var value;
|
618
913
|
|
619
|
-
if (input.charAt(i) !== 'u' || !$(/^url\(/))
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
expect(')');
|
624
|
-
|
625
|
-
return new(tree.URL)((value.value || value.data || value instanceof tree.Variable)
|
626
|
-
? value : new(tree.Anonymous)(value), imports.paths);
|
627
|
-
},
|
914
|
+
if (input.charAt(i) !== 'u' || !$re(/^url\(/)) {
|
915
|
+
return;
|
916
|
+
}
|
628
917
|
|
629
|
-
|
630
|
-
|
918
|
+
value = this.quoted() || this.variable() ||
|
919
|
+
$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
|
631
920
|
|
632
|
-
|
633
|
-
obj = {};
|
634
|
-
obj.mime = $(/^[^\/]+\/[^,;)]+/) || '';
|
635
|
-
obj.charset = $(/^;\s*charset=[^,;)]+/) || '';
|
636
|
-
obj.base64 = $(/^;\s*base64/) || '';
|
637
|
-
obj.data = $(/^,\s*[^)]+/);
|
921
|
+
expectChar(')');
|
638
922
|
|
639
|
-
|
640
|
-
|
923
|
+
return new(tree.URL)((value.value != null || value instanceof tree.Variable)
|
924
|
+
? value : new(tree.Anonymous)(value), env.currentFileInfo);
|
641
925
|
},
|
642
926
|
|
643
927
|
//
|
@@ -651,8 +935,17 @@ less.Parser = function Parser(env) {
|
|
651
935
|
variable: function () {
|
652
936
|
var name, index = i;
|
653
937
|
|
654
|
-
if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) {
|
655
|
-
return new(tree.Variable)(name, index, env.
|
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);
|
656
949
|
}
|
657
950
|
},
|
658
951
|
|
@@ -666,7 +959,7 @@ less.Parser = function Parser(env) {
|
|
666
959
|
color: function () {
|
667
960
|
var rgb;
|
668
961
|
|
669
|
-
if (input.charAt(i) === '#' && (rgb = $(/^#([
|
962
|
+
if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
|
670
963
|
return new(tree.Color)(rgb[1]);
|
671
964
|
}
|
672
965
|
},
|
@@ -678,13 +971,31 @@ less.Parser = function Parser(env) {
|
|
678
971
|
//
|
679
972
|
dimension: function () {
|
680
973
|
var value, c = input.charCodeAt(i);
|
681
|
-
|
974
|
+
//Is the first char of the dimension 0-9, '.', '+' or '-'
|
975
|
+
if ((c > 57 || c < 43) || c === 47 || c == 44) {
|
976
|
+
return;
|
977
|
+
}
|
682
978
|
|
683
|
-
|
979
|
+
value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/);
|
980
|
+
if (value) {
|
684
981
|
return new(tree.Dimension)(value[1], value[2]);
|
685
982
|
}
|
686
983
|
},
|
687
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
|
+
|
688
999
|
//
|
689
1000
|
// JavaScript code to be evaluated
|
690
1001
|
//
|
@@ -693,12 +1004,16 @@ less.Parser = function Parser(env) {
|
|
693
1004
|
javascript: function () {
|
694
1005
|
var str, j = i, e;
|
695
1006
|
|
696
|
-
if (input.charAt(j) === '~') { j
|
697
|
-
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
|
+
}
|
698
1012
|
|
699
|
-
e
|
1013
|
+
if (e) { $char('~'); }
|
700
1014
|
|
701
|
-
|
1015
|
+
str = $re(/^`([^`]*)`/);
|
1016
|
+
if (str) {
|
702
1017
|
return new(tree.JavaScript)(str[1], i, e);
|
703
1018
|
}
|
704
1019
|
}
|
@@ -712,26 +1027,62 @@ less.Parser = function Parser(env) {
|
|
712
1027
|
variable: function () {
|
713
1028
|
var name;
|
714
1029
|
|
715
|
-
if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] }
|
1030
|
+
if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; }
|
716
1031
|
},
|
717
1032
|
|
718
1033
|
//
|
719
|
-
//
|
1034
|
+
// The variable part of a variable definition. Used in the `rule` parser
|
1035
|
+
//
|
1036
|
+
// @fink();
|
720
1037
|
//
|
721
|
-
|
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
|
+
|
722
1046
|
//
|
723
|
-
//
|
1047
|
+
// extend syntax - used to extend selectors
|
724
1048
|
//
|
725
|
-
|
726
|
-
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];
|
727
1064
|
|
728
|
-
|
1065
|
+
extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
|
1066
|
+
if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
|
729
1067
|
|
730
|
-
|
731
|
-
|
1068
|
+
} while($char(","));
|
1069
|
+
|
1070
|
+
expect(/^\)/);
|
1071
|
+
|
1072
|
+
if (isRule) {
|
1073
|
+
expect(/^;/);
|
732
1074
|
}
|
1075
|
+
|
1076
|
+
return extendList;
|
733
1077
|
},
|
734
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
|
+
|
735
1086
|
//
|
736
1087
|
// Mixins
|
737
1088
|
//
|
@@ -748,25 +1099,158 @@ less.Parser = function Parser(env) {
|
|
748
1099
|
// selector for now.
|
749
1100
|
//
|
750
1101
|
call: function () {
|
751
|
-
var
|
1102
|
+
var s = input.charAt(i), important = false, index = i, elemIndex,
|
1103
|
+
elements, elem, e, c, args;
|
752
1104
|
|
753
|
-
if (s !== '.' && s !== '#') { return }
|
1105
|
+
if (s !== '.' && s !== '#') { return; }
|
754
1106
|
|
755
|
-
|
756
|
-
elements.push(new(tree.Element)(c, e, i));
|
757
|
-
c = $('>');
|
758
|
-
}
|
759
|
-
$('(') && (args = $(this.entities.arguments)) && $(')');
|
1107
|
+
save(); // stop us absorbing part of an invalid selector
|
760
1108
|
|
761
|
-
|
762
|
-
|
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('>');
|
763
1118
|
}
|
764
1119
|
|
765
|
-
if (elements
|
766
|
-
|
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
|
+
}
|
767
1134
|
}
|
1135
|
+
|
1136
|
+
restore();
|
768
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;
|
769
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();
|
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
|
+
},
|
770
1254
|
//
|
771
1255
|
// A Mixin definition, with a list of parameters
|
772
1256
|
//
|
@@ -787,54 +1271,49 @@ less.Parser = function Parser(env) {
|
|
787
1271
|
// the `{...}` block.
|
788
1272
|
//
|
789
1273
|
definition: function () {
|
790
|
-
var name, params = [], match, ruleset,
|
1274
|
+
var name, params = [], match, ruleset, cond, variadic = false;
|
791
1275
|
if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') ||
|
792
|
-
peek(/^[^{]
|
1276
|
+
peek(/^[^{]*\}/)) {
|
1277
|
+
return;
|
1278
|
+
}
|
793
1279
|
|
794
1280
|
save();
|
795
1281
|
|
796
|
-
|
1282
|
+
match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/);
|
1283
|
+
if (match) {
|
797
1284
|
name = match[1];
|
798
1285
|
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
params.push({ name: param.name });
|
816
|
-
}
|
817
|
-
} else {
|
818
|
-
params.push({ value: param });
|
819
|
-
}
|
820
|
-
} else {
|
821
|
-
break;
|
822
|
-
}
|
823
|
-
} while ($(','))
|
824
|
-
|
825
|
-
expect(')');
|
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;
|
1299
|
+
}
|
1300
|
+
|
1301
|
+
parsers.comments();
|
826
1302
|
|
827
|
-
if ($(/^when/)) { // Guard
|
828
|
-
cond = expect(
|
1303
|
+
if ($re(/^when/)) { // Guard
|
1304
|
+
cond = expect(parsers.conditions, 'expected condition');
|
829
1305
|
}
|
830
1306
|
|
831
|
-
ruleset =
|
1307
|
+
ruleset = parsers.block();
|
832
1308
|
|
833
1309
|
if (ruleset) {
|
1310
|
+
forget();
|
834
1311
|
return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
|
835
1312
|
} else {
|
836
1313
|
restore();
|
837
1314
|
}
|
1315
|
+
} else {
|
1316
|
+
forget();
|
838
1317
|
}
|
839
1318
|
}
|
840
1319
|
},
|
@@ -844,9 +1323,11 @@ less.Parser = function Parser(env) {
|
|
844
1323
|
// and can be found inside a rule's value.
|
845
1324
|
//
|
846
1325
|
entity: function () {
|
847
|
-
|
848
|
-
|
849
|
-
|
1326
|
+
var entities = this.entities;
|
1327
|
+
|
1328
|
+
return entities.literal() || entities.variable() || entities.url() ||
|
1329
|
+
entities.call() || entities.keyword() || entities.javascript() ||
|
1330
|
+
this.comment();
|
850
1331
|
},
|
851
1332
|
|
852
1333
|
//
|
@@ -855,7 +1336,7 @@ less.Parser = function Parser(env) {
|
|
855
1336
|
// it's there, if ';' was ommitted.
|
856
1337
|
//
|
857
1338
|
end: function () {
|
858
|
-
return $(';') ||
|
1339
|
+
return $char(';') || peekChar('}');
|
859
1340
|
},
|
860
1341
|
|
861
1342
|
//
|
@@ -866,9 +1347,10 @@ less.Parser = function Parser(env) {
|
|
866
1347
|
alpha: function () {
|
867
1348
|
var value;
|
868
1349
|
|
869
|
-
if (! $(/^\(opacity=/i)) return;
|
870
|
-
|
871
|
-
|
1350
|
+
if (! $re(/^\(opacity=/i)) { return; }
|
1351
|
+
value = $re(/^\d+/) || this.entities.variable();
|
1352
|
+
if (value) {
|
1353
|
+
expectChar(')');
|
872
1354
|
return new(tree.Alpha)(value);
|
873
1355
|
}
|
874
1356
|
},
|
@@ -886,21 +1368,29 @@ less.Parser = function Parser(env) {
|
|
886
1368
|
// and an element name, such as a tag a class, or `*`.
|
887
1369
|
//
|
888
1370
|
element: function () {
|
889
|
-
var e,
|
1371
|
+
var e, c, v, index = i;
|
890
1372
|
|
891
|
-
c =
|
892
|
-
|
893
|
-
|
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();
|
894
1378
|
|
895
1379
|
if (! e) {
|
896
|
-
|
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
|
+
}
|
897
1391
|
}
|
898
1392
|
|
899
|
-
if (e) { return new(tree.Element)(c, e,
|
900
|
-
|
901
|
-
if (c.value && c.value.charAt(0) === '&') {
|
902
|
-
return new(tree.Element)(c, null, i);
|
903
|
-
}
|
1393
|
+
if (e) { return new(tree.Element)(c, e, index, env.currentFileInfo); }
|
904
1394
|
},
|
905
1395
|
|
906
1396
|
//
|
@@ -913,27 +1403,29 @@ less.Parser = function Parser(env) {
|
|
913
1403
|
// we deal with this in *combinator.js*.
|
914
1404
|
//
|
915
1405
|
combinator: function () {
|
916
|
-
var
|
917
|
-
|
918
|
-
if (c === '>' || c === '+' || c === '~') {
|
919
|
-
i++;
|
920
|
-
while (input.charAt(i) === ' ') { i++ }
|
921
|
-
return new(tree.Combinator)(c);
|
922
|
-
} else if (c === '&') {
|
923
|
-
match = '&';
|
1406
|
+
var c = input.charAt(i);
|
1407
|
+
|
1408
|
+
if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') {
|
924
1409
|
i++;
|
925
|
-
if(input.charAt(i) === '
|
926
|
-
|
1410
|
+
if (input.charAt(i) === '^') {
|
1411
|
+
c = '^^';
|
1412
|
+
i++;
|
927
1413
|
}
|
928
|
-
while (input
|
929
|
-
return new(tree.Combinator)(
|
930
|
-
} else if (input
|
1414
|
+
while (isWhitespace(input, i)) { i++; }
|
1415
|
+
return new(tree.Combinator)(c);
|
1416
|
+
} else if (isWhitespace(input, i - 1)) {
|
931
1417
|
return new(tree.Combinator)(" ");
|
932
1418
|
} else {
|
933
1419
|
return new(tree.Combinator)(null);
|
934
1420
|
}
|
935
1421
|
},
|
936
|
-
|
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
|
+
},
|
937
1429
|
//
|
938
1430
|
// A CSS Selector
|
939
1431
|
//
|
@@ -942,41 +1434,48 @@ less.Parser = function Parser(env) {
|
|
942
1434
|
//
|
943
1435
|
// Selectors are made out of one or more Elements, see above.
|
944
1436
|
//
|
945
|
-
selector: function () {
|
946
|
-
var
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
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
|
+
}
|
958
1456
|
}
|
959
1457
|
|
960
|
-
if (elements
|
961
|
-
|
962
|
-
tag: function () {
|
963
|
-
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"); }
|
964
1460
|
},
|
965
1461
|
attribute: function () {
|
966
|
-
|
1462
|
+
if (! $char('[')) { return; }
|
1463
|
+
|
1464
|
+
var entities = this.entities,
|
1465
|
+
key, val, op;
|
967
1466
|
|
968
|
-
if (!
|
1467
|
+
if (!(key = entities.variableCurly())) {
|
1468
|
+
key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/);
|
1469
|
+
}
|
969
1470
|
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
attr = [key, op, val.toCSS ? val.toCSS() : val].join('');
|
974
|
-
} else { attr = key }
|
1471
|
+
op = $re(/^[|~*$^]?=/);
|
1472
|
+
if (op) {
|
1473
|
+
val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly();
|
975
1474
|
}
|
976
1475
|
|
977
|
-
|
1476
|
+
expectChar(']');
|
978
1477
|
|
979
|
-
|
1478
|
+
return new(tree.Attribute)(key, op, val);
|
980
1479
|
},
|
981
1480
|
|
982
1481
|
//
|
@@ -985,57 +1484,119 @@ less.Parser = function Parser(env) {
|
|
985
1484
|
//
|
986
1485
|
block: function () {
|
987
1486
|
var content;
|
988
|
-
|
989
|
-
if ($('{') && (content = $(this.primary)) && $('}')) {
|
1487
|
+
if ($char('{') && (content = this.primary()) && $char('}')) {
|
990
1488
|
return content;
|
991
1489
|
}
|
992
1490
|
},
|
993
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
|
+
|
994
1508
|
//
|
995
1509
|
// div, .class, body > p {...}
|
996
1510
|
//
|
997
1511
|
ruleset: function () {
|
998
|
-
var selectors
|
1512
|
+
var selectors, s, rules, debugInfo;
|
1513
|
+
|
999
1514
|
save();
|
1000
1515
|
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
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.");
|
1529
|
+
}
|
1530
|
+
if (! $char(',')) { break; }
|
1531
|
+
if (s.condition) {
|
1532
|
+
error("Guards are only currently allowed on a single selector.");
|
1533
|
+
}
|
1534
|
+
this.comments();
|
1006
1535
|
}
|
1007
1536
|
|
1008
|
-
if (selectors
|
1009
|
-
|
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;
|
1010
1544
|
} else {
|
1011
1545
|
// Backtrack
|
1012
1546
|
furthest = i;
|
1013
1547
|
restore();
|
1014
1548
|
}
|
1015
1549
|
},
|
1016
|
-
rule: function () {
|
1017
|
-
var name, value, c = input.charAt(
|
1018
|
-
save();
|
1550
|
+
rule: function (tryAnonymous) {
|
1551
|
+
var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable;
|
1019
1552
|
|
1020
|
-
if (c === '.' || c === '#' || c === '&') { return }
|
1553
|
+
if (c === '.' || c === '#' || c === '&') { return; }
|
1021
1554
|
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
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;
|
1030
1578
|
}
|
1031
|
-
important = $(this.important);
|
1032
1579
|
|
1033
|
-
if (value &&
|
1034
|
-
|
1580
|
+
if (value && this.end()) {
|
1581
|
+
forget();
|
1582
|
+
return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo);
|
1035
1583
|
} else {
|
1036
1584
|
furthest = i;
|
1037
1585
|
restore();
|
1586
|
+
if (value && !tryAnonymous) {
|
1587
|
+
return this.rule(true);
|
1588
|
+
}
|
1038
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]);
|
1039
1600
|
}
|
1040
1601
|
},
|
1041
1602
|
|
@@ -1051,33 +1612,78 @@ less.Parser = function Parser(env) {
|
|
1051
1612
|
//
|
1052
1613
|
"import": function () {
|
1053
1614
|
var path, features, index = i;
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
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);
|
1059
1628
|
}
|
1060
1629
|
}
|
1630
|
+
|
1631
|
+
restore();
|
1061
1632
|
},
|
1062
1633
|
|
1063
|
-
|
1064
|
-
var
|
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
|
+
},
|
1065
1668
|
|
1669
|
+
mediaFeature: function () {
|
1670
|
+
var entities = this.entities, nodes = [], e, p;
|
1066
1671
|
do {
|
1067
|
-
|
1672
|
+
e = entities.keyword() || entities.variable();
|
1673
|
+
if (e) {
|
1068
1674
|
nodes.push(e);
|
1069
|
-
} else if ($('(')) {
|
1070
|
-
p =
|
1071
|
-
e =
|
1072
|
-
if ($(')')) {
|
1675
|
+
} else if ($char('(')) {
|
1676
|
+
p = this.property();
|
1677
|
+
e = this.value();
|
1678
|
+
if ($char(')')) {
|
1073
1679
|
if (p && e) {
|
1074
|
-
nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true)));
|
1680
|
+
nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true)));
|
1075
1681
|
} else if (e) {
|
1076
1682
|
nodes.push(new(tree.Paren)(e));
|
1077
1683
|
} else {
|
1078
1684
|
return null;
|
1079
1685
|
}
|
1080
|
-
} else { return null }
|
1686
|
+
} else { return null; }
|
1081
1687
|
}
|
1082
1688
|
} while (e);
|
1083
1689
|
|
@@ -1087,29 +1693,41 @@ less.Parser = function Parser(env) {
|
|
1087
1693
|
},
|
1088
1694
|
|
1089
1695
|
mediaFeatures: function () {
|
1090
|
-
var
|
1091
|
-
|
1696
|
+
var entities = this.entities, features = [], e;
|
1092
1697
|
do {
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
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
|
+
}
|
1100
1709
|
} while (e);
|
1101
|
-
|
1710
|
+
|
1102
1711
|
return features.length > 0 ? features : null;
|
1103
1712
|
},
|
1104
1713
|
|
1105
1714
|
media: function () {
|
1106
|
-
var features, rules;
|
1715
|
+
var features, rules, media, debugInfo;
|
1107
1716
|
|
1108
|
-
if (
|
1109
|
-
|
1717
|
+
if (env.dumpLineNumbers) {
|
1718
|
+
debugInfo = getDebugInfo(i, input, env);
|
1719
|
+
}
|
1720
|
+
|
1721
|
+
if ($re(/^@media/)) {
|
1722
|
+
features = this.mediaFeatures();
|
1110
1723
|
|
1111
|
-
|
1112
|
-
|
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;
|
1113
1731
|
}
|
1114
1732
|
}
|
1115
1733
|
},
|
@@ -1120,42 +1738,97 @@ less.Parser = function Parser(env) {
|
|
1120
1738
|
// @charset "utf-8";
|
1121
1739
|
//
|
1122
1740
|
directive: function () {
|
1123
|
-
var name, value, rules,
|
1741
|
+
var index = i, name, value, rules, nonVendorSpecificName,
|
1742
|
+
hasIdentifier, hasExpression, hasUnknown, hasBlock = true;
|
1124
1743
|
|
1125
|
-
if (input.charAt(i) !== '@') return;
|
1744
|
+
if (input.charAt(i) !== '@') { return; }
|
1126
1745
|
|
1127
|
-
|
1746
|
+
value = this['import']() || this.media();
|
1747
|
+
if (value) {
|
1128
1748
|
return value;
|
1129
|
-
} else if (name = $(/^@page|@keyframes/) || $(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)) {
|
1130
|
-
types = ($(/^[^{]+/) || '').trim();
|
1131
|
-
if (rules = $(this.block)) {
|
1132
|
-
return new(tree.Directive)(name + " " + types, rules);
|
1133
|
-
}
|
1134
|
-
} else if (name = $(/^@[-a-z]+/)) {
|
1135
|
-
if (name === '@font-face') {
|
1136
|
-
if (rules = $(this.block)) {
|
1137
|
-
return new(tree.Directive)(name, rules);
|
1138
|
-
}
|
1139
|
-
} else if ((value = $(this.entity)) && $(';')) {
|
1140
|
-
return new(tree.Directive)(name, value);
|
1141
|
-
}
|
1142
1749
|
}
|
1143
|
-
},
|
1144
|
-
font: function () {
|
1145
|
-
var value = [], expression = [], weight, shorthand, font, e;
|
1146
1750
|
|
1147
|
-
|
1148
|
-
|
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;
|
1149
1802
|
}
|
1150
|
-
value.push(new(tree.Expression)(expression));
|
1151
1803
|
|
1152
|
-
if (
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1804
|
+
if (hasIdentifier) {
|
1805
|
+
value = this.entity();
|
1806
|
+
if (!value) {
|
1807
|
+
error("expected " + name + " identifier");
|
1156
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);
|
1157
1829
|
}
|
1158
|
-
|
1830
|
+
|
1831
|
+
restore();
|
1159
1832
|
},
|
1160
1833
|
|
1161
1834
|
//
|
@@ -1167,12 +1840,15 @@ less.Parser = function Parser(env) {
|
|
1167
1840
|
// and before the `;`.
|
1168
1841
|
//
|
1169
1842
|
value: function () {
|
1170
|
-
var e, expressions = []
|
1843
|
+
var e, expressions = [];
|
1171
1844
|
|
1172
|
-
|
1173
|
-
|
1174
|
-
if (
|
1175
|
-
|
1845
|
+
do {
|
1846
|
+
e = this.expression();
|
1847
|
+
if (e) {
|
1848
|
+
expressions.push(e);
|
1849
|
+
if (! $char(',')) { break; }
|
1850
|
+
}
|
1851
|
+
} while(e);
|
1176
1852
|
|
1177
1853
|
if (expressions.length > 0) {
|
1178
1854
|
return new(tree.Value)(expressions);
|
@@ -1180,31 +1856,66 @@ less.Parser = function Parser(env) {
|
|
1180
1856
|
},
|
1181
1857
|
important: function () {
|
1182
1858
|
if (input.charAt(i) === '!') {
|
1183
|
-
return $(/^! *important/);
|
1859
|
+
return $re(/^! *important/);
|
1184
1860
|
}
|
1185
1861
|
},
|
1186
1862
|
sub: function () {
|
1187
|
-
var e;
|
1188
|
-
|
1189
|
-
if ($('(')
|
1190
|
-
|
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
|
+
}
|
1191
1873
|
}
|
1192
1874
|
},
|
1193
1875
|
multiplication: function () {
|
1194
|
-
var m, a, op, operation;
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
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);
|
1198
1896
|
}
|
1199
1897
|
return operation || m;
|
1200
1898
|
}
|
1201
1899
|
},
|
1202
1900
|
addition: function () {
|
1203
|
-
var m, a, op, operation;
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
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);
|
1208
1919
|
}
|
1209
1920
|
return operation || m;
|
1210
1921
|
}
|
@@ -1212,21 +1923,33 @@ less.Parser = function Parser(env) {
|
|
1212
1923
|
conditions: function () {
|
1213
1924
|
var a, b, index = i, condition;
|
1214
1925
|
|
1215
|
-
|
1216
|
-
|
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
|
+
}
|
1217
1936
|
condition = new(tree.Condition)('or', condition || a, b, index);
|
1218
1937
|
}
|
1219
1938
|
return condition || a;
|
1220
1939
|
}
|
1221
1940
|
},
|
1222
1941
|
condition: function () {
|
1223
|
-
var
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
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) {
|
1230
1953
|
c = new(tree.Condition)(op, a, b, index, negate);
|
1231
1954
|
} else {
|
1232
1955
|
error('expected expression');
|
@@ -1234,8 +1957,8 @@ less.Parser = function Parser(env) {
|
|
1234
1957
|
} else {
|
1235
1958
|
c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);
|
1236
1959
|
}
|
1237
|
-
|
1238
|
-
return $(/^and/) ? new(tree.Condition)('and', c,
|
1960
|
+
expectChar(')');
|
1961
|
+
return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c;
|
1239
1962
|
}
|
1240
1963
|
},
|
1241
1964
|
|
@@ -1244,14 +1967,20 @@ less.Parser = function Parser(env) {
|
|
1244
1967
|
// such as a Color, or a Variable
|
1245
1968
|
//
|
1246
1969
|
operand: function () {
|
1247
|
-
var
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
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;
|
1255
1984
|
},
|
1256
1985
|
|
1257
1986
|
//
|
@@ -1262,44 +1991,78 @@ less.Parser = function Parser(env) {
|
|
1262
1991
|
// @var * 2
|
1263
1992
|
//
|
1264
1993
|
expression: function () {
|
1265
|
-
var
|
1994
|
+
var entities = [], e, delim;
|
1266
1995
|
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
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);
|
1270
2009
|
if (entities.length > 0) {
|
1271
2010
|
return new(tree.Expression)(entities);
|
1272
2011
|
}
|
1273
2012
|
},
|
1274
2013
|
property: function () {
|
1275
|
-
var name;
|
1276
|
-
|
1277
|
-
if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) {
|
2014
|
+
var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);
|
2015
|
+
if (name) {
|
1278
2016
|
return name[1];
|
1279
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
|
+
}
|
1280
2051
|
}
|
1281
2052
|
}
|
1282
2053
|
};
|
2054
|
+
return parser;
|
1283
2055
|
};
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
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) === ';') ? '' : ';');
|
1292
2064
|
}
|
1293
|
-
|
1294
|
-
// This is so we can get the syntax tree as opposed to just the CSS output,
|
1295
|
-
// as we need this to evaluate the current stylesheet.
|
1296
|
-
loadStyleSheet({ href: path, title: path, type: env.mime }, function (e) {
|
1297
|
-
if (e && typeof(env.errback) === "function") {
|
1298
|
-
env.errback.call(null, path, paths, callback, env);
|
1299
|
-
} else {
|
1300
|
-
callback.apply(null, arguments);
|
1301
|
-
}
|
1302
|
-
}, true);
|
1303
|
-
};
|
1304
|
-
}
|
2065
|
+
}
|
1305
2066
|
|
2067
|
+
return s;
|
2068
|
+
};
|