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