├── .github ├── dependabot.yml └── workflows │ ├── no-response.yml │ ├── publish.yaml │ └── test-package.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example └── main.dart ├── lib ├── parser.dart ├── src │ ├── analyzer.dart │ ├── css_printer.dart │ ├── messages.dart │ ├── polyfill.dart │ ├── preprocessor_options.dart │ ├── property.dart │ ├── token.dart │ ├── token_kind.dart │ ├── tokenizer.dart │ ├── tokenizer_base.dart │ ├── tree.dart │ ├── tree_base.dart │ ├── tree_printer.dart │ └── validate.dart └── visitor.dart ├── pubspec.yaml ├── test ├── big_1_test.dart ├── color_test.dart ├── compiler_test.dart ├── debug_test.dart ├── declaration_test.dart ├── error_test.dart ├── escape_codes_test.dart ├── extend_test.dart ├── keyframes_test.dart ├── mixin_test.dart ├── nested_test.dart ├── repros_test.dart ├── selector_test.dart ├── testing.dart ├── third_party_samples_test.dart ├── var_test.dart └── visitor_test.dart └── third_party ├── README.md ├── base ├── README.md └── index.css ├── bootstrap ├── LICENSE ├── README.md ├── bootstrap-grid.css └── bootstrap.css ├── foundation ├── LICENSE ├── README.md ├── foundation.css └── foundation.min.css ├── html5-boilerplate ├── LICENSE.txt ├── README.md ├── normalize.css └── style.css ├── materialize ├── LICENSE ├── README.md ├── materialize.css └── materialize.min.css ├── mdc ├── LICENSE ├── README.md ├── material-components-web.css └── material-components-web.min.css ├── pure ├── LICENSE ├── README.md ├── main-grid.css └── main.css └── skeleton ├── LICENSE.md ├── README.me ├── normalize.css └── skeleton.css /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot configuration file. 2 | # See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates 3 | version: 2 4 | 5 | updates: 6 | - package-ecosystem: github-actions 7 | directory: / 8 | schedule: 9 | interval: monthly 10 | labels: 11 | - autosubmit 12 | groups: 13 | github-actions: 14 | patterns: 15 | - "*" 16 | -------------------------------------------------------------------------------- /.github/workflows/no-response.yml: -------------------------------------------------------------------------------- 1 | # A workflow to close issues where the author hasn't responded to a request for 2 | # more information; see https://github.com/actions/stale. 3 | 4 | name: No Response 5 | 6 | # Run as a daily cron. 7 | on: 8 | schedule: 9 | # Every day at 8am 10 | - cron: '0 8 * * *' 11 | 12 | # All permissions not specified are set to 'none'. 13 | permissions: 14 | issues: write 15 | pull-requests: write 16 | 17 | jobs: 18 | no-response: 19 | runs-on: ubuntu-latest 20 | if: ${{ github.repository_owner == 'dart-lang' }} 21 | steps: 22 | - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e 23 | with: 24 | # Don't automatically mark inactive issues+PRs as stale. 25 | days-before-stale: -1 26 | # Close needs-info issues and PRs after 14 days of inactivity. 27 | days-before-close: 14 28 | stale-issue-label: "needs-info" 29 | close-issue-message: > 30 | Without additional information we're not able to resolve this issue. 31 | Feel free to add more info or respond to any questions above and we 32 | can reopen the case. Thanks for your contribution! 33 | stale-pr-label: "needs-info" 34 | close-pr-message: > 35 | Without additional information we're not able to resolve this PR. 36 | Feel free to add more info or respond to any questions above. 37 | Thanks for your contribution! 38 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | # A CI configuration to auto-publish pub packages. 2 | 3 | name: Publish 4 | 5 | on: 6 | pull_request: 7 | branches: [ main ] 8 | push: 9 | tags: [ 'v[0-9]+.[0-9]+.[0-9]+' ] 10 | 11 | jobs: 12 | publish: 13 | if: ${{ github.repository_owner == 'dart-lang' }} 14 | uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main 15 | permissions: 16 | id-token: write # Required for authentication using OIDC 17 | pull-requests: write # Required for writing the pull request note 18 | -------------------------------------------------------------------------------- /.github/workflows/test-package.yml: -------------------------------------------------------------------------------- 1 | name: Dart CI 2 | 3 | on: 4 | # Run on PRs and pushes to the default branch. 5 | push: 6 | branches: [ main ] 7 | pull_request: 8 | branches: [ main ] 9 | schedule: 10 | - cron: "0 0 * * 0" 11 | 12 | env: 13 | PUB_ENVIRONMENT: bot.github 14 | 15 | jobs: 16 | # Check code formatting and static analysis on a single OS (linux) 17 | # against Dart dev. 18 | analyze: 19 | runs-on: ubuntu-latest 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | sdk: [dev] 24 | steps: 25 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 26 | - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 27 | with: 28 | sdk: ${{ matrix.sdk }} 29 | - id: install 30 | name: Install dependencies 31 | run: dart pub get 32 | - name: Check formatting 33 | run: dart format --output=none --set-exit-if-changed . 34 | if: always() && steps.install.outcome == 'success' 35 | - name: Analyze code 36 | run: dart analyze --fatal-infos 37 | if: always() && steps.install.outcome == 'success' 38 | 39 | # Run tests on a matrix consisting of two dimensions: 40 | # 1. OS: ubuntu-latest, (macos-latest, windows-latest) 41 | # 2. release channel: dev 42 | test: 43 | needs: analyze 44 | runs-on: ${{ matrix.os }} 45 | strategy: 46 | fail-fast: false 47 | matrix: 48 | # Add macos-latest and/or windows-latest if relevant for this package. 49 | os: [ubuntu-latest, windows-latest] 50 | sdk: [3.1, dev] 51 | steps: 52 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 53 | - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 54 | with: 55 | sdk: ${{ matrix.sdk }} 56 | - id: install 57 | name: Install dependencies 58 | run: dart pub get 59 | - name: Run VM tests 60 | run: dart test --platform vm 61 | if: always() && steps.install.outcome == 'success' 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dart_tool 2 | .packages 3 | pubspec.lock 4 | doc/api/ 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.2-wip 2 | 3 | - Require Dart 3.1 4 | 5 | ## 1.0.1 6 | 7 | - Update `ExpressionsProcessor.processFont` to handle null expressions. 8 | - Require Dart 3.0. 9 | 10 | ## 1.0.0 11 | 12 | - Rev to `1.0.0` (note however that there are no API changes from `0.17.x`). 13 | 14 | ## 0.17.3 15 | 16 | - Add markdown badges to the readme. 17 | - Adopted `package:dart_flutter_team_lints` linting rules. 18 | - Addressed an issue parsing font names not surrounded by quotes. 19 | - Fixed the reported span for `Expression` nodes. 20 | - Fixed a regression parsing declaration values containing spaces. 21 | - Add support for `lh` and `rlh` units. 22 | - Refactor the package example. 23 | - Addressed an issue with the indent level of the `CssPrinter` output. 24 | - Require Dart 2.19. 25 | 26 | ## 0.17.2 27 | 28 | - Fixed a crash caused by `min()`, `max()` and `clamp()` functions that contain 29 | mathematical expressions. 30 | - Add commas between PercentageTerms in keyframe rules. 31 | 32 | ## 0.17.1 33 | 34 | - Fix `Color.css` constructor when there are double values in the `rgba` string. 35 | 36 | ## 0.17.0 37 | 38 | - Migrate to null safety. 39 | - `Font.merge` and `BoxEdge.merge` are now static methods instead of factory 40 | constructors. 41 | - Add a type on the `identList` argument to `TokenKind.matchList`. 42 | - Remove workaround for https://github.com/dart-lang/sdk/issues/43136, which is 43 | now fixed. 44 | 45 | ## 0.16.2 46 | 47 | - Added support for escape codes in identifiers. 48 | 49 | ## 0.16.1 50 | 51 | - Fixed a crash caused by parsing certain calc() expressions and variables names that contain numbers. 52 | 53 | ## 0.16.0 54 | 55 | - Removed support for the shadow-piercing comibnators `/deep/` and `>>>`. These 56 | were dropped from the Shadow DOM specification. 57 | 58 | ## 0.15.0 59 | 60 | - **BREAKING** 61 | - Removed `css` executable from `bin` directory. 62 | - Removed the deprecated `css.dart` library. 63 | - `Message.level` is now of type `MessageLevel` defined in this package. 64 | - Removed dependencies on `package:args` and `package:logging`. 65 | - Require Dart SDK `>=2.1.0`. 66 | 67 | ## 0.14.6 68 | 69 | * Removed whitespace between comma-delimited expressions in compact output. 70 | 71 | Before: 72 | ```css 73 | div{color:rgba(0, 0, 0, 0.5);} 74 | ``` 75 | 76 | After: 77 | ```css 78 | div{color:rgba(0,0,0,0.5);} 79 | ``` 80 | 81 | * Removed last semicolon from declaration groups in compact output. 82 | 83 | Before: 84 | ```css 85 | div{color:red;background:blue;} 86 | ``` 87 | 88 | After: 89 | ```css 90 | div{color:red;background:blue} 91 | ``` 92 | 93 | ## 0.14.5 94 | 95 | * Fixed a crashed caused by parsing `:host()` without an argument and added an 96 | error message explaining that a selector argument is expected. 97 | 98 | ## 0.14.4+1 99 | 100 | * Set max SDK version to `<3.0.0`, and adjust other dependencies. 101 | 102 | ## 0.14.4 103 | 104 | * Reduced whitespace in compact output for the `@page` at-rule and margin boxes. 105 | * Updated SDK version to 2.0.0-dev.17.0. 106 | * Stop using deprecated constants. 107 | 108 | ## 0.14.3 109 | 110 | * Reduced the amount of whitespace in compact output around braces. 111 | 112 | ## 0.14.2 113 | 114 | * Fixed Dart 2 runtime failure. 115 | 116 | ## 0.14.1 117 | 118 | * Deprecated `package:csslib/css.dart`. 119 | Use `parser.dart` and `visitor.dart` instead. 120 | 121 | ## 0.14.0 122 | 123 | ### New features 124 | 125 | * Supports nested at-rules. 126 | * Supports nested HTML comments in CSS comments and vice-versa. 127 | 128 | ### Breaking changes 129 | 130 | * The `List rulesets` field on `MediaDirective`, `HostDirective`, and 131 | `StyletDirective` has been replaced by `List rules` to allow nested 132 | at-rules in addition to rulesets. 133 | 134 | ## 0.13.6 135 | 136 | * Adds support for `@viewport`. 137 | * Adds support for `-webkit-calc()` and `-moz-calc()`. 138 | * Adds support for querying media features without specifying an expression. For 139 | example: `@media (transform-3d) { ... }`. 140 | * Prevents exception being thrown for invalid dimension terms, and instead 141 | issues an error. 142 | 143 | ## 0.13.5 144 | 145 | * Adds support for `@-moz-document`. 146 | * Adds support for `@supports`. 147 | 148 | ## 0.13.4 149 | 150 | * Parses CSS 2.1 pseudo-elements as pseudo-elements instead of pseudo-classes. 151 | * Supports signed decimal numbers with no integer part. 152 | * Fixes parsing hexadecimal numbers when followed by an identifier. 153 | * Fixes parsing strings which contain unicode-range character sequences. 154 | 155 | ## 0.13.3+1 156 | 157 | * Fixes analyzer error. 158 | 159 | ## 0.13.3 160 | 161 | * Adds support for shadow host selectors `:host()` and `:host-context()`. 162 | * Adds support for shadow-piercing descendant combinator `>>>` and its alias 163 | `/deep/` for backwards compatibility. 164 | * Adds support for non-functional IE filter properties (i.e. `filter: FlipH`). 165 | * Fixes emitted CSS for `@page` directive when body includes declarations and 166 | page-margin boxes. 167 | * Exports `Message` from `parser.dart` so it's no longer necessary to import 168 | `src/messages.dart` to use the parser API. 169 | 170 | ## 0.13.2+2 171 | 172 | * Fix static warnings. 173 | 174 | ## 0.13.2+1 175 | 176 | * Fix new strong mode error. 177 | 178 | ## 0.13.2 179 | 180 | * Relax type of TreeNode.visit, to allow returning values from visitors. 181 | 182 | ## 0.13.1 183 | 184 | * Fix two checked mode bugs introduced in 0.13.0. 185 | 186 | ## 0.13.0 187 | 188 | * **BREAKING** Fix all [strong mode][] errors and warnings. 189 | This involved adding more precise on some public APIs, which 190 | is why it may break users. 191 | 192 | [strong mode]: https://github.com/dart-lang/dev_compiler/blob/master/STRONG_MODE.md 193 | 194 | ## 0.12.2 195 | 196 | * Fix to handle calc functions however, the expressions are treated as a 197 | LiteralTerm and not fully parsed into the AST. 198 | 199 | ## 0.12.1 200 | 201 | * Fix to handling of escapes in strings. 202 | 203 | ## 0.12.0+1 204 | 205 | * Allow the latest version of `logging` package. 206 | 207 | ## 0.12.0 208 | 209 | * Top-level methods in `parser.dart` now take `PreprocessorOptions` instead of 210 | `List`. 211 | 212 | * `PreprocessorOptions.inputFile` is now final. 213 | 214 | ## 0.11.0+4 215 | 216 | * Cleanup some ambiguous and some incorrect type signatures. 217 | 218 | ## 0.11.0+3 219 | 220 | * Improve the speed and memory efficiency of parsing. 221 | 222 | ## 0.11.0+2 223 | 224 | * Fix another test that was failing on IE10. 225 | 226 | ## 0.11.0+1 227 | 228 | * Fix a test that was failing on IE10. 229 | 230 | ## 0.11.0 231 | 232 | * Switch from `source_maps`' `Span` class to `source_span`'s `SourceSpan` class. 233 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013, the Dart project authors. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following 11 | disclaimer in the documentation and/or other materials provided 12 | with the distribution. 13 | * Neither the name of Google LLC nor the names of its 14 | contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!IMPORTANT] 2 | > This repo has moved to https://github.com/dart-lang/tools/tree/main/pkgs/csslib 3 | 4 | [![Dart CI](https://github.com/dart-lang/csslib/actions/workflows/test-package.yml/badge.svg)](https://github.com/dart-lang/csslib/actions/workflows/test-package.yml) 5 | [![pub package](https://img.shields.io/pub/v/csslib.svg)](https://pub.dev/packages/csslib) 6 | [![package publisher](https://img.shields.io/pub/publisher/csslib.svg)](https://pub.dev/packages/csslib/publisher) 7 | 8 | A Dart [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS) parser. 9 | 10 | ## Usage 11 | 12 | Parsing CSS is easy! 13 | 14 | ```dart 15 | import 'package:csslib/parser.dart'; 16 | 17 | void main() { 18 | var stylesheet = parse( 19 | '.foo { color: red; left: 20px; top: 20px; width: 100px; height:200px }'); 20 | print(stylesheet.toDebugString()); 21 | } 22 | ``` 23 | 24 | You can pass a `String` or `List` to `parse`. 25 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:dart_flutter_team_lints/analysis_options.yaml 2 | 3 | analyzer: 4 | language: 5 | strict-casts: true 6 | strict-inference: true 7 | strict-raw-types: true 8 | errors: 9 | comment_references: ignore # too many false positives 10 | 11 | linter: 12 | rules: 13 | - prefer_expression_function_bodies 14 | -------------------------------------------------------------------------------- /example/main.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | 4 | import 'package:csslib/parser.dart' as css; 5 | import 'package:csslib/visitor.dart'; 6 | 7 | void main() { 8 | var errors = []; 9 | 10 | // Parse a simple stylesheet. 11 | print('1. Good CSS, parsed CSS emitted:'); 12 | print(' ============================='); 13 | var stylesheet = parseCss(''' 14 | @import "support/at-charset-019.css"; 15 | div { color: red; } 16 | button[type] { background-color: red; } 17 | .foo { 18 | color: red; left: 20px; top: 20px; width: 100px; height:200px 19 | } 20 | #div { 21 | color : #00F578; border-color: #878787; 22 | } 23 | ''', errors: errors); 24 | 25 | if (errors.isNotEmpty) { 26 | print('Got ${errors.length} errors.\n'); 27 | for (var error in errors) { 28 | print(error); 29 | } 30 | } else { 31 | print(prettyPrint(stylesheet)); 32 | } 33 | 34 | // Parse a stylesheet with errors 35 | print('\n2. Catch severe syntax errors:'); 36 | print(' ==========================='); 37 | var stylesheetError = parseCss(''' 38 | .foo #%^&*asdf { 39 | color: red; left: 20px; top: 20px; width: 100px; height:200px 40 | } 41 | ''', errors: errors); 42 | 43 | if (errors.isNotEmpty) { 44 | print('Got ${errors.length} errors.\n'); 45 | for (var error in errors) { 46 | print(error); 47 | } 48 | } else { 49 | print(stylesheetError.toString()); 50 | } 51 | 52 | // Parse a stylesheet that warns (checks) problematic CSS. 53 | print('\n3. Detect CSS problem with checking on:'); 54 | print(' ==================================='); 55 | stylesheetError = parseCss('# div1 { color: red; }', errors: errors); 56 | 57 | if (errors.isNotEmpty) { 58 | print('Detected ${errors.length} problem in checked mode.\n'); 59 | for (var error in errors) { 60 | print(error); 61 | } 62 | } else { 63 | print(stylesheetError.toString()); 64 | } 65 | 66 | // Parse a CSS selector. 67 | print('\n4. Parse a selector only:'); 68 | print(' ======================'); 69 | var selectorAst = css.selector('#div .foo', errors: errors); 70 | if (errors.isNotEmpty) { 71 | print('Got ${errors.length} errors.\n'); 72 | for (var error in errors) { 73 | print(error); 74 | } 75 | } else { 76 | print(prettyPrint(selectorAst)); 77 | } 78 | } 79 | 80 | /// Spin-up CSS parser in checked mode to detect any problematic CSS. Normally, 81 | /// CSS will allow any property/value pairs regardless of validity; all of our 82 | /// tests (by default) will ensure that the CSS is really valid. 83 | StyleSheet parseCss( 84 | String cssInput, { 85 | List? errors, 86 | css.PreprocessorOptions? opts, 87 | }) => 88 | css.parse(cssInput, errors: errors, options: opts ?? _default); 89 | 90 | /// Pretty printer for CSS. 91 | String prettyPrint(StyleSheet ss) => 92 | (CssPrinter()..visitTree(ss, pretty: true)).toString(); 93 | 94 | const _default = css.PreprocessorOptions( 95 | useColors: false, 96 | checked: true, 97 | warningsAsErrors: true, 98 | inputFile: 'memory', 99 | ); 100 | -------------------------------------------------------------------------------- /lib/src/messages.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:source_span/source_span.dart'; 6 | 7 | import 'preprocessor_options.dart'; 8 | 9 | enum MessageLevel { info, warning, severe } 10 | 11 | // TODO(#159): Remove the global messages, use some object that tracks 12 | // compilation state. 13 | 14 | /// The global [Messages] for tracking info/warnings/messages. 15 | late Messages messages; 16 | 17 | // Color constants used for generating messages. 18 | const _greenColor = '\u001b[32m'; 19 | const _redColor = '\u001b[31m'; 20 | const _magentaColor = '\u001b[35m'; 21 | const _noColor = '\u001b[0m'; 22 | 23 | /// Map between error levels and their display color. 24 | const Map _errorColors = { 25 | MessageLevel.severe: _redColor, 26 | MessageLevel.warning: _magentaColor, 27 | MessageLevel.info: _greenColor, 28 | }; 29 | 30 | /// Map between error levels and their friendly name. 31 | const Map _errorLabel = { 32 | MessageLevel.severe: 'error', 33 | MessageLevel.warning: 'warning', 34 | MessageLevel.info: 'info', 35 | }; 36 | 37 | /// A single message from the compiler. 38 | class Message { 39 | final MessageLevel level; 40 | final String message; 41 | final SourceSpan? span; 42 | final bool useColors; 43 | 44 | Message(this.level, this.message, {this.span, this.useColors = false}); 45 | 46 | String get describe { 47 | var span = this.span; 48 | if (span == null) { 49 | return message; 50 | } 51 | 52 | var start = span.start; 53 | return '${start.line + 1}:${start.column + 1}:$message'; 54 | } 55 | 56 | @override 57 | String toString() { 58 | var output = StringBuffer(); 59 | var colors = useColors && _errorColors.containsKey(level); 60 | var levelColor = colors ? _errorColors[level] : null; 61 | if (colors) output.write(levelColor); 62 | output 63 | ..write(_errorLabel[level]) 64 | ..write(' '); 65 | if (colors) output.write(_noColor); 66 | 67 | if (span == null) { 68 | output.write(message); 69 | } else { 70 | output.write('on '); 71 | output.write(span!.message(message, color: levelColor)); 72 | } 73 | 74 | return output.toString(); 75 | } 76 | } 77 | 78 | /// This class tracks and prints information, warnings, and errors emitted by 79 | /// the compiler. 80 | class Messages { 81 | /// Called on every error. Set to blank function to suppress printing. 82 | final void Function(Message obj) printHandler; 83 | 84 | final PreprocessorOptions options; 85 | 86 | final List messages = []; 87 | 88 | Messages({PreprocessorOptions? options, this.printHandler = print}) 89 | : options = options ?? const PreprocessorOptions(); 90 | 91 | /// Report a compile-time CSS error. 92 | void error(String message, SourceSpan? span) { 93 | var msg = Message(MessageLevel.severe, message, 94 | span: span, useColors: options.useColors); 95 | 96 | messages.add(msg); 97 | 98 | printHandler(msg); 99 | } 100 | 101 | /// Report a compile-time CSS warning. 102 | void warning(String message, SourceSpan? span) { 103 | if (options.warningsAsErrors) { 104 | error(message, span); 105 | } else { 106 | var msg = Message(MessageLevel.warning, message, 107 | span: span, useColors: options.useColors); 108 | 109 | messages.add(msg); 110 | } 111 | } 112 | 113 | /// Report and informational message about what the compiler is doing. 114 | void info(String message, SourceSpan span) { 115 | var msg = Message(MessageLevel.info, message, 116 | span: span, useColors: options.useColors); 117 | 118 | messages.add(msg); 119 | 120 | if (options.verbose) printHandler(msg); 121 | } 122 | 123 | /// Merge [newMessages] to this message list. 124 | void mergeMessages(Messages newMessages) { 125 | messages.addAll(newMessages.messages); 126 | newMessages.messages 127 | .where((message) => 128 | message.level == MessageLevel.severe || options.verbose) 129 | .forEach(printHandler); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lib/src/polyfill.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | part of '../parser.dart'; 6 | 7 | /// CSS polyfill emits CSS to be understood by older parsers that which do not 8 | /// understand (var, calc, etc.). 9 | class PolyFill { 10 | final Messages _messages; 11 | Map _allVarDefinitions = {}; 12 | 13 | Set allStyleSheets = {}; 14 | 15 | /// [_pseudoElements] list of known pseudo attributes found in HTML, any 16 | /// CSS pseudo-elements 'name::custom-element' is mapped to the manged name 17 | /// associated with the pseudo-element key. 18 | PolyFill(this._messages); 19 | 20 | /// Run the analyzer on every file that is a style sheet or any component that 21 | /// has a style tag. 22 | void process(StyleSheet styleSheet, {List? includes}) { 23 | if (includes != null) { 24 | processVarDefinitions(includes); 25 | } 26 | processVars(styleSheet); 27 | 28 | // Remove all var definitions for this style sheet. 29 | _RemoveVarDefinitions().visitTree(styleSheet); 30 | } 31 | 32 | /// Process all includes looking for var definitions. 33 | void processVarDefinitions(List includes) { 34 | for (var include in includes) { 35 | _allVarDefinitions = (_VarDefinitionsIncludes(_allVarDefinitions) 36 | ..visitTree(include)) 37 | .varDefs; 38 | } 39 | } 40 | 41 | void processVars(StyleSheet styleSheet) { 42 | // Build list of all var definitions. 43 | var mainStyleSheetVarDefs = (_VarDefAndUsage(_messages, _allVarDefinitions) 44 | ..visitTree(styleSheet)) 45 | .varDefs; 46 | 47 | // Resolve all definitions to a non-VarUsage (terminal expression). 48 | mainStyleSheetVarDefs.forEach((key, value) { 49 | for (var _ in (value.expression as Expressions).expressions) { 50 | mainStyleSheetVarDefs[key] = 51 | _findTerminalVarDefinition(_allVarDefinitions, value); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | /// Build list of all var definitions in all includes. 58 | class _VarDefinitionsIncludes extends Visitor { 59 | final Map varDefs; 60 | 61 | _VarDefinitionsIncludes(this.varDefs); 62 | 63 | @override 64 | void visitTree(StyleSheet tree) { 65 | visitStyleSheet(tree); 66 | } 67 | 68 | @override 69 | void visitVarDefinition(VarDefinition node) { 70 | // Replace with latest variable definition. 71 | varDefs[node.definedName] = node; 72 | super.visitVarDefinition(node); 73 | } 74 | 75 | @override 76 | void visitVarDefinitionDirective(VarDefinitionDirective node) { 77 | visitVarDefinition(node.def); 78 | } 79 | } 80 | 81 | /// Find var- definitions in a style sheet. 82 | /// [found] list of known definitions. 83 | class _VarDefAndUsage extends Visitor { 84 | final Messages _messages; 85 | final Map _knownVarDefs; 86 | final varDefs = {}; 87 | 88 | VarDefinition? currVarDefinition; 89 | List? currentExpressions; 90 | 91 | _VarDefAndUsage(this._messages, this._knownVarDefs); 92 | 93 | @override 94 | void visitTree(StyleSheet tree) { 95 | visitStyleSheet(tree); 96 | } 97 | 98 | @override 99 | void visitVarDefinition(VarDefinition node) { 100 | // Replace with latest variable definition. 101 | currVarDefinition = node; 102 | 103 | _knownVarDefs[node.definedName] = node; 104 | varDefs[node.definedName] = node; 105 | 106 | super.visitVarDefinition(node); 107 | 108 | currVarDefinition = null; 109 | } 110 | 111 | @override 112 | void visitVarDefinitionDirective(VarDefinitionDirective node) { 113 | visitVarDefinition(node.def); 114 | } 115 | 116 | @override 117 | void visitExpressions(Expressions node) { 118 | currentExpressions = node.expressions; 119 | super.visitExpressions(node); 120 | currentExpressions = null; 121 | } 122 | 123 | @override 124 | void visitVarUsage(VarUsage node) { 125 | if (currVarDefinition != null && currVarDefinition!.badUsage) return; 126 | 127 | // Don't process other var() inside of a varUsage. That implies that the 128 | // default is a var() too. Also, don't process any var() inside of a 129 | // varDefinition (they're just place holders until we've resolved all real 130 | // usages. 131 | var expressions = currentExpressions; 132 | var index = expressions!.indexOf(node); 133 | assert(index >= 0); 134 | var def = _knownVarDefs[node.name]; 135 | if (def != null) { 136 | if (def.badUsage) { 137 | // Remove any expressions pointing to a bad var definition. 138 | expressions.removeAt(index); 139 | return; 140 | } 141 | _resolveVarUsage(currentExpressions!, index, 142 | _findTerminalVarDefinition(_knownVarDefs, def)); 143 | } else if (node.defaultValues.any((e) => e is VarUsage)) { 144 | // Don't have a VarDefinition need to use default values resolve all 145 | // default values. 146 | var terminalDefaults = []; 147 | for (var defaultValue in node.defaultValues) { 148 | terminalDefaults.addAll(resolveUsageTerminal(defaultValue as VarUsage)); 149 | } 150 | expressions.replaceRange(index, index + 1, terminalDefaults); 151 | } else if (node.defaultValues.isNotEmpty) { 152 | // No VarDefinition but default value is a terminal expression; use it. 153 | expressions.replaceRange(index, index + 1, node.defaultValues); 154 | } else { 155 | if (currVarDefinition != null) { 156 | currVarDefinition!.badUsage = true; 157 | var mainStyleSheetDef = varDefs[node.name]; 158 | if (mainStyleSheetDef != null) { 159 | varDefs.remove(currVarDefinition!.property); 160 | } 161 | } 162 | // Remove var usage that points at an undefined definition. 163 | expressions.removeAt(index); 164 | _messages.warning('Variable is not defined.', node.span); 165 | } 166 | 167 | var oldExpressions = currentExpressions; 168 | currentExpressions = node.defaultValues; 169 | super.visitVarUsage(node); 170 | currentExpressions = oldExpressions; 171 | } 172 | 173 | List resolveUsageTerminal(VarUsage usage) { 174 | var result = []; 175 | 176 | var varDef = _knownVarDefs[usage.name]; 177 | List expressions; 178 | if (varDef == null) { 179 | // VarDefinition not found try the defaultValues. 180 | expressions = usage.defaultValues; 181 | } else { 182 | // Use the VarDefinition found. 183 | expressions = (varDef.expression as Expressions).expressions; 184 | } 185 | 186 | for (var expr in expressions) { 187 | if (expr is VarUsage) { 188 | // Get terminal value. 189 | result.addAll(resolveUsageTerminal(expr)); 190 | } 191 | } 192 | 193 | // We're at a terminal just return the VarDefinition expression. 194 | if (result.isEmpty && varDef != null) { 195 | result = (varDef.expression as Expressions).expressions; 196 | } 197 | 198 | return result; 199 | } 200 | 201 | void _resolveVarUsage( 202 | List expressions, int index, VarDefinition def) { 203 | var defExpressions = (def.expression as Expressions).expressions; 204 | expressions.replaceRange(index, index + 1, defExpressions); 205 | } 206 | } 207 | 208 | /// Remove all var definitions. 209 | class _RemoveVarDefinitions extends Visitor { 210 | @override 211 | void visitTree(StyleSheet tree) { 212 | visitStyleSheet(tree); 213 | } 214 | 215 | @override 216 | void visitStyleSheet(StyleSheet ss) { 217 | ss.topLevels.removeWhere((e) => e is VarDefinitionDirective); 218 | super.visitStyleSheet(ss); 219 | } 220 | 221 | @override 222 | void visitDeclarationGroup(DeclarationGroup node) { 223 | node.declarations.removeWhere((e) => e is VarDefinition); 224 | super.visitDeclarationGroup(node); 225 | } 226 | } 227 | 228 | /// Find terminal definition (non VarUsage implies real CSS value). 229 | VarDefinition _findTerminalVarDefinition( 230 | Map varDefs, VarDefinition varDef) { 231 | var expressions = varDef.expression as Expressions; 232 | for (var expr in expressions.expressions) { 233 | if (expr is VarUsage) { 234 | var usageName = expr.name; 235 | var foundDef = varDefs[usageName]; 236 | 237 | // If foundDef is unknown check if defaultValues; if it exist then resolve 238 | // to terminal value. 239 | if (foundDef == null) { 240 | // We're either a VarUsage or terminal definition if in varDefs; 241 | // either way replace VarUsage with it's default value because the 242 | // VarDefinition isn't found. 243 | var defaultValues = expr.defaultValues; 244 | var replaceExprs = expressions.expressions; 245 | assert(replaceExprs.length == 1); 246 | replaceExprs.replaceRange(0, 1, defaultValues); 247 | return varDef; 248 | } 249 | return _findTerminalVarDefinition(varDefs, foundDef); 250 | } else { 251 | // Return real CSS property. 252 | return varDef; 253 | } 254 | } 255 | 256 | // Didn't point to a var definition that existed. 257 | return varDef; 258 | } 259 | -------------------------------------------------------------------------------- /lib/src/preprocessor_options.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | class PreprocessorOptions { 6 | /// Generate polyfill code (e.g., var, etc.) 7 | final bool polyfill; 8 | 9 | /// Report warnings as errors. 10 | final bool warningsAsErrors; 11 | 12 | /// Throw an exception on warnings (not used by command line tool). 13 | final bool throwOnWarnings; 14 | 15 | /// Throw an exception on errors (not used by command line tool). 16 | final bool throwOnErrors; 17 | 18 | /// True to show informational messages. The `--verbose` flag. 19 | final bool verbose; 20 | 21 | /// True to show warning messages for bad CSS. The '--checked' flag. 22 | final bool checked; 23 | 24 | // TODO(terry): Add mixin support and nested rules. 25 | /// Subset of Less commands enabled; disable with '--no-less'. 26 | /// Less syntax supported: 27 | /// - @name at root level statically defines variables resolved at compilation 28 | /// time. Essentially a directive e.g., @var-name. 29 | final bool lessSupport; 30 | 31 | /// Whether to use colors to print messages on the terminal. 32 | final bool useColors; 33 | 34 | /// File to process by the compiler. 35 | final String? inputFile; 36 | 37 | // TODO: Should less support really default to being enabled? 38 | 39 | const PreprocessorOptions({ 40 | this.verbose = false, 41 | this.checked = false, 42 | this.lessSupport = true, 43 | this.warningsAsErrors = false, 44 | this.throwOnErrors = false, 45 | this.throwOnWarnings = false, 46 | this.useColors = true, 47 | this.polyfill = false, 48 | this.inputFile, 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/token.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | part of '../parser.dart'; 6 | 7 | /// A single token in the Dart language. 8 | class Token { 9 | /// A member of [TokenKind] specifying what kind of token this is. 10 | final int kind; 11 | 12 | /// The location where this token was parsed from. 13 | final FileSpan span; 14 | 15 | /// The start offset of this token. 16 | int get start => span.start.offset; 17 | 18 | /// The end offset of this token. 19 | int get end => span.end.offset; 20 | 21 | /// Returns the source text corresponding to this [Token]. 22 | String get text => span.text; 23 | 24 | Token(this.kind, this.span); 25 | 26 | /// Returns a pretty representation of this token for error messages. 27 | @override 28 | String toString() { 29 | var kindText = TokenKind.kindToString(kind); 30 | var actualText = text.trim(); 31 | if (actualText.isNotEmpty && kindText != actualText) { 32 | if (actualText.length > 10) { 33 | actualText = '${actualText.substring(0, 8)}...'; 34 | } 35 | return '$kindText($actualText)'; 36 | } else { 37 | return kindText; 38 | } 39 | } 40 | } 41 | 42 | /// A token containing a parsed literal value. 43 | class LiteralToken extends Token { 44 | dynamic value; 45 | LiteralToken(super.kind, super.span, this.value); 46 | } 47 | 48 | /// A token containing error information. 49 | class ErrorToken extends Token { 50 | String? message; 51 | ErrorToken(super.kind, super.span, this.message); 52 | } 53 | 54 | /// CSS ident-token. 55 | /// 56 | /// See and 57 | /// . 58 | class IdentifierToken extends Token { 59 | @override 60 | final String text; 61 | 62 | IdentifierToken(this.text, int kind, FileSpan span) : super(kind, span); 63 | } 64 | -------------------------------------------------------------------------------- /lib/src/tokenizer.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | part of '../parser.dart'; 6 | 7 | // TODO: We should update the tokenization to follow what's described in the 8 | // spec: https://www.w3.org/TR/css-syntax-3/#tokenization. 9 | 10 | class Tokenizer extends TokenizerBase { 11 | /// U+ prefix for unicode characters. 12 | // ignore: non_constant_identifier_names 13 | final UNICODE_U = 'U'.codeUnitAt(0); 14 | // ignore: non_constant_identifier_names 15 | final UNICODE_LOWER_U = 'u'.codeUnitAt(0); 16 | // ignore: non_constant_identifier_names 17 | final UNICODE_PLUS = '+'.codeUnitAt(0); 18 | 19 | // ignore: non_constant_identifier_names 20 | final QUESTION_MARK = '?'.codeUnitAt(0); 21 | 22 | /// CDATA keyword. 23 | // ignore: non_constant_identifier_names 24 | final List CDATA_NAME = 'CDATA'.codeUnits; 25 | 26 | Tokenizer(super.file, super.text, super.skipWhitespace, [super.index]); 27 | 28 | @override 29 | Token next({bool unicodeRange = false}) { 30 | // keep track of our starting position 31 | _startIndex = _index; 32 | 33 | int ch; 34 | ch = _nextChar(); 35 | switch (ch) { 36 | case TokenChar.NEWLINE: 37 | case TokenChar.RETURN: 38 | case TokenChar.SPACE: 39 | case TokenChar.TAB: 40 | return finishWhitespace(); 41 | case TokenChar.END_OF_FILE: 42 | return _finishToken(TokenKind.END_OF_FILE); 43 | case TokenChar.AT: 44 | var peekCh = _peekChar(); 45 | if (TokenizerHelpers.isIdentifierStart(peekCh)) { 46 | var oldIndex = _index; 47 | var oldStartIndex = _startIndex; 48 | 49 | _startIndex = _index; 50 | ch = _nextChar(); 51 | finishIdentifier(); 52 | 53 | // Is it a directive? 54 | var tokId = TokenKind.matchDirectives( 55 | _text, _startIndex, _index - _startIndex); 56 | if (tokId == -1) { 57 | // No, is it a margin directive? 58 | tokId = TokenKind.matchMarginDirectives( 59 | _text, _startIndex, _index - _startIndex); 60 | } 61 | 62 | if (tokId != -1) { 63 | return _finishToken(tokId); 64 | } else { 65 | // Didn't find a CSS directive or margin directive so the @name is 66 | // probably the Less definition '@name: value_variable_definition'. 67 | _startIndex = oldStartIndex; 68 | _index = oldIndex; 69 | } 70 | } 71 | return _finishToken(TokenKind.AT); 72 | case TokenChar.DOT: 73 | var start = _startIndex; // Start where the dot started. 74 | if (maybeEatDigit()) { 75 | // looks like a number dot followed by digit(s). 76 | var number = finishNumber(); 77 | if (number.kind == TokenKind.INTEGER) { 78 | // It's a number but it's preceded by a dot, so make it a double. 79 | _startIndex = start; 80 | return _finishToken(TokenKind.DOUBLE); 81 | } else { 82 | // Don't allow dot followed by a double (e.g, '..1'). 83 | return _errorToken(); 84 | } 85 | } 86 | // It's really a dot. 87 | return _finishToken(TokenKind.DOT); 88 | case TokenChar.LPAREN: 89 | return _finishToken(TokenKind.LPAREN); 90 | case TokenChar.RPAREN: 91 | return _finishToken(TokenKind.RPAREN); 92 | case TokenChar.LBRACE: 93 | return _finishToken(TokenKind.LBRACE); 94 | case TokenChar.RBRACE: 95 | return _finishToken(TokenKind.RBRACE); 96 | case TokenChar.LBRACK: 97 | return _finishToken(TokenKind.LBRACK); 98 | case TokenChar.RBRACK: 99 | if (_maybeEatChar(TokenChar.RBRACK) && 100 | _maybeEatChar(TokenChar.GREATER)) { 101 | // ]]> 102 | return next(); 103 | } 104 | return _finishToken(TokenKind.RBRACK); 105 | case TokenChar.HASH: 106 | return _finishToken(TokenKind.HASH); 107 | case TokenChar.PLUS: 108 | if (_nextCharsAreNumber(ch)) return finishNumber(); 109 | return _finishToken(TokenKind.PLUS); 110 | case TokenChar.MINUS: 111 | if (inSelectorExpression || unicodeRange) { 112 | // If parsing in pseudo function expression then minus is an operator 113 | // not part of identifier e.g., interval value range (e.g. U+400-4ff) 114 | // or minus operator in selector expression. 115 | return _finishToken(TokenKind.MINUS); 116 | } else if (_nextCharsAreNumber(ch)) { 117 | return finishNumber(); 118 | } else if (TokenizerHelpers.isIdentifierStart(ch)) { 119 | return finishIdentifier(); 120 | } 121 | return _finishToken(TokenKind.MINUS); 122 | case TokenChar.GREATER: 123 | return _finishToken(TokenKind.GREATER); 124 | case TokenChar.TILDE: 125 | if (_maybeEatChar(TokenChar.EQUALS)) { 126 | return _finishToken(TokenKind.INCLUDES); // ~= 127 | } 128 | return _finishToken(TokenKind.TILDE); 129 | case TokenChar.ASTERISK: 130 | if (_maybeEatChar(TokenChar.EQUALS)) { 131 | return _finishToken(TokenKind.SUBSTRING_MATCH); // *= 132 | } 133 | return _finishToken(TokenKind.ASTERISK); 134 | case TokenChar.AMPERSAND: 135 | return _finishToken(TokenKind.AMPERSAND); 136 | case TokenChar.NAMESPACE: 137 | if (_maybeEatChar(TokenChar.EQUALS)) { 138 | return _finishToken(TokenKind.DASH_MATCH); // |= 139 | } 140 | return _finishToken(TokenKind.NAMESPACE); 141 | case TokenChar.COLON: 142 | return _finishToken(TokenKind.COLON); 143 | case TokenChar.COMMA: 144 | return _finishToken(TokenKind.COMMA); 145 | case TokenChar.SEMICOLON: 146 | return _finishToken(TokenKind.SEMICOLON); 147 | case TokenChar.PERCENT: 148 | return _finishToken(TokenKind.PERCENT); 149 | case TokenChar.SINGLE_QUOTE: 150 | return _finishToken(TokenKind.SINGLE_QUOTE); 151 | case TokenChar.DOUBLE_QUOTE: 152 | return _finishToken(TokenKind.DOUBLE_QUOTE); 153 | case TokenChar.SLASH: 154 | if (_maybeEatChar(TokenChar.ASTERISK)) return finishMultiLineComment(); 155 | return _finishToken(TokenKind.SLASH); 156 | case TokenChar.LESS: // (CDC). */ 409 | if (_maybeEatChar(TokenChar.MINUS)) { 410 | if (_maybeEatChar(TokenChar.GREATER)) { 411 | if (_inString) { 412 | return next(); 413 | } else { 414 | return _finishToken(TokenKind.HTML_COMMENT); 415 | } 416 | } 417 | } 418 | } 419 | } 420 | } 421 | 422 | @override 423 | Token finishMultiLineComment() { 424 | while (true) { 425 | var ch = _nextChar(); 426 | if (ch == 0) { 427 | return _finishToken(TokenKind.INCOMPLETE_COMMENT); 428 | } else if (ch == 42 /*'*'*/) { 429 | if (_maybeEatChar(47 /*'/'*/)) { 430 | if (_inString) { 431 | return next(); 432 | } else { 433 | return _finishToken(TokenKind.COMMENT); 434 | } 435 | } 436 | } 437 | } 438 | } 439 | } 440 | 441 | /// Static helper methods. 442 | class TokenizerHelpers { 443 | static bool isIdentifierStart(int c) => 444 | isIdentifierStartExpr(c) || c == 45 /*-*/; 445 | 446 | static bool isDigit(int c) => c >= 48 /*0*/ && c <= 57 /*9*/; 447 | 448 | static bool isHexDigit(int c) => 449 | isDigit(c) || 450 | (c >= 97 /*a*/ && c <= 102 /*f*/) || 451 | (c >= 65 /*A*/ && c <= 70 /*F*/); 452 | 453 | static bool isIdentifierPart(int c) => 454 | isIdentifierPartExpr(c) || c == 45 /*-*/; 455 | 456 | /// Pseudo function expressions identifiers can't have a minus sign. 457 | static bool isIdentifierStartExpr(int c) => 458 | (c >= 97 /*a*/ && c <= 122 /*z*/) || 459 | (c >= 65 /*A*/ && c <= 90 /*Z*/) || 460 | // Note: Unicode 10646 chars U+00A0 or higher are allowed, see: 461 | // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier 462 | // http://www.w3.org/TR/CSS21/syndata.html#characters 463 | // Also, escaped character should be allowed. 464 | c == 95 /*_*/ || 465 | c >= 0xA0 || 466 | c == 92 /*\*/; 467 | 468 | /// Pseudo function expressions identifiers can't have a minus sign. 469 | static bool isIdentifierPartExpr(int c) => 470 | isIdentifierStartExpr(c) || isDigit(c); 471 | } 472 | -------------------------------------------------------------------------------- /lib/src/tokenizer_base.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | // Generated by scripts/tokenizer_gen.py. 5 | 6 | part of '../parser.dart'; 7 | 8 | /// Tokenizer state to support look ahead for Less' nested selectors. 9 | class TokenizerState { 10 | final int index; 11 | final int startIndex; 12 | final bool inSelectorExpression; 13 | final bool inSelector; 14 | 15 | TokenizerState(TokenizerBase base) 16 | : index = base._index, 17 | startIndex = base._startIndex, 18 | inSelectorExpression = base.inSelectorExpression, 19 | inSelector = base.inSelector; 20 | } 21 | 22 | /// The base class for our tokenizer. The hand coded parts are in this file, 23 | /// with the generated parts in the subclass Tokenizer. 24 | abstract class TokenizerBase { 25 | final SourceFile _file; 26 | final String _text; 27 | 28 | // TODO: this seems like a bug – this field *is* used 29 | // ignore: prefer_final_fields 30 | bool _inString; 31 | 32 | /// Changes tokenization when in a pseudo function expression. If true then 33 | /// minus signs are handled as operators instead of identifiers. 34 | bool inSelectorExpression = false; 35 | 36 | /// Changes tokenization when in selectors. If true, it prevents identifiers 37 | /// from being treated as units. This would break things like ":lang(fr)" or 38 | /// the HTML (unknown) tag name "px", which is legal to use in a selector. 39 | // TODO(jmesserly): is this a problem elsewhere? "fr" for example will be 40 | // processed as a "fraction" unit token, preventing it from working in 41 | // places where an identifier is expected. This was breaking selectors like: 42 | // :lang(fr) 43 | // The assumption that "fr" always means fraction (and similar issue with 44 | // other units) doesn't seem valid. We probably should defer this 45 | // analysis until we reach places in the parser where units are expected. 46 | // I'm not sure this is tokenizing as described in the specs: 47 | // http://dev.w3.org/csswg/css-syntax/ 48 | // http://dev.w3.org/csswg/selectors4/ 49 | bool inSelector = false; 50 | 51 | int _index = 0; 52 | int _startIndex = 0; 53 | 54 | TokenizerBase(this._file, this._text, this._inString, [this._index = 0]); 55 | 56 | Token next(); 57 | int getIdentifierKind(); 58 | 59 | /// Snapshot of Tokenizer scanning state. 60 | TokenizerState get mark => TokenizerState(this); 61 | 62 | /// Restore Tokenizer scanning state. 63 | void restore(TokenizerState markedData) { 64 | _index = markedData.index; 65 | _startIndex = markedData.startIndex; 66 | inSelectorExpression = markedData.inSelectorExpression; 67 | inSelector = markedData.inSelector; 68 | } 69 | 70 | int _nextChar() { 71 | if (_index < _text.length) { 72 | return _text.codeUnitAt(_index++); 73 | } else { 74 | return 0; 75 | } 76 | } 77 | 78 | int _peekChar([int offset = 0]) { 79 | if (_index + offset < _text.length) { 80 | return _text.codeUnitAt(_index + offset); 81 | } else { 82 | return 0; 83 | } 84 | } 85 | 86 | bool _maybeEatChar(int ch) { 87 | if (_index < _text.length) { 88 | if (_text.codeUnitAt(_index) == ch) { 89 | _index++; 90 | return true; 91 | } else { 92 | return false; 93 | } 94 | } else { 95 | return false; 96 | } 97 | } 98 | 99 | bool _nextCharsAreNumber(int first) { 100 | if (TokenizerHelpers.isDigit(first)) return true; 101 | var second = _peekChar(); 102 | if (first == TokenChar.DOT) return TokenizerHelpers.isDigit(second); 103 | if (first == TokenChar.PLUS || first == TokenChar.MINUS) { 104 | return TokenizerHelpers.isDigit(second) || 105 | (second == TokenChar.DOT && TokenizerHelpers.isDigit(_peekChar(1))); 106 | } 107 | return false; 108 | } 109 | 110 | Token _finishToken(int kind) => Token(kind, _file.span(_startIndex, _index)); 111 | 112 | Token _errorToken([String? message]) => 113 | ErrorToken(TokenKind.ERROR, _file.span(_startIndex, _index), message); 114 | 115 | Token finishWhitespace() { 116 | _index--; 117 | while (_index < _text.length) { 118 | final ch = _text.codeUnitAt(_index++); 119 | if (ch == TokenChar.SPACE || 120 | ch == TokenChar.TAB || 121 | ch == TokenChar.RETURN) { 122 | // do nothing 123 | } else if (ch == TokenChar.NEWLINE) { 124 | if (!_inString) { 125 | return _finishToken(TokenKind.WHITESPACE); // note the newline? 126 | } 127 | } else { 128 | _index--; 129 | if (_inString) { 130 | return next(); 131 | } else { 132 | return _finishToken(TokenKind.WHITESPACE); 133 | } 134 | } 135 | } 136 | return _finishToken(TokenKind.END_OF_FILE); 137 | } 138 | 139 | Token finishMultiLineComment() { 140 | var nesting = 1; 141 | do { 142 | var ch = _nextChar(); 143 | if (ch == 0) { 144 | return _errorToken(); 145 | } else if (ch == TokenChar.ASTERISK) { 146 | if (_maybeEatChar(TokenChar.SLASH)) { 147 | nesting--; 148 | } 149 | } else if (ch == TokenChar.SLASH) { 150 | if (_maybeEatChar(TokenChar.ASTERISK)) { 151 | nesting++; 152 | } 153 | } 154 | } while (nesting > 0); 155 | 156 | if (_inString) { 157 | return next(); 158 | } else { 159 | return _finishToken(TokenKind.COMMENT); 160 | } 161 | } 162 | 163 | void eatDigits() { 164 | while (_index < _text.length) { 165 | if (TokenizerHelpers.isDigit(_text.codeUnitAt(_index))) { 166 | _index++; 167 | } else { 168 | return; 169 | } 170 | } 171 | } 172 | 173 | static int _hexDigit(int c) { 174 | if (c >= 48 /*0*/ && c <= 57 /*9*/) { 175 | return c - 48; 176 | } else if (c >= 97 /*a*/ && c <= 102 /*f*/) { 177 | return c - 87; 178 | } else if (c >= 65 /*A*/ && c <= 70 /*F*/) { 179 | return c - 55; 180 | } else { 181 | return -1; 182 | } 183 | } 184 | 185 | int readHex([int? hexLength]) { 186 | int maxIndex; 187 | if (hexLength == null) { 188 | maxIndex = _text.length - 1; 189 | } else { 190 | // TODO(jimhug): What if this is too long? 191 | maxIndex = _index + hexLength; 192 | if (maxIndex >= _text.length) return -1; 193 | } 194 | var result = 0; 195 | while (_index < maxIndex) { 196 | final digit = _hexDigit(_text.codeUnitAt(_index)); 197 | if (digit == -1) { 198 | if (hexLength == null) { 199 | return result; 200 | } else { 201 | return -1; 202 | } 203 | } 204 | _hexDigit(_text.codeUnitAt(_index)); 205 | // Multiply by 16 rather than shift by 4 since that will result in a 206 | // correct value for numbers that exceed the 32 bit precision of JS 207 | // 'integers'. 208 | // TODO: Figure out a better solution to integer truncation. Issue 638. 209 | result = (result * 16) + digit; 210 | _index++; 211 | } 212 | 213 | return result; 214 | } 215 | 216 | Token finishNumber() { 217 | eatDigits(); 218 | 219 | if (_peekChar() == TokenChar.DOT) { 220 | // Handle the case of 1.toString(). 221 | _nextChar(); 222 | if (TokenizerHelpers.isDigit(_peekChar())) { 223 | eatDigits(); 224 | return finishNumberExtra(TokenKind.DOUBLE); 225 | } else { 226 | _index--; 227 | } 228 | } 229 | 230 | return finishNumberExtra(TokenKind.INTEGER); 231 | } 232 | 233 | Token finishNumberExtra(int kind) { 234 | if (_maybeEatChar(101 /*e*/) || _maybeEatChar(69 /*E*/)) { 235 | kind = TokenKind.DOUBLE; 236 | _maybeEatChar(TokenKind.MINUS); 237 | _maybeEatChar(TokenKind.PLUS); 238 | eatDigits(); 239 | } 240 | if (_peekChar() != 0 && TokenizerHelpers.isIdentifierStart(_peekChar())) { 241 | _nextChar(); 242 | return _errorToken('illegal character in number'); 243 | } 244 | 245 | return _finishToken(kind); 246 | } 247 | 248 | Token _makeStringToken(List buf, bool isPart) { 249 | final s = String.fromCharCodes(buf); 250 | final kind = isPart ? TokenKind.STRING_PART : TokenKind.STRING; 251 | return LiteralToken(kind, _file.span(_startIndex, _index), s); 252 | } 253 | 254 | Token makeIEFilter(int start, int end) { 255 | var filter = _text.substring(start, end); 256 | return LiteralToken(TokenKind.STRING, _file.span(start, end), filter); 257 | } 258 | 259 | Token _makeRawStringToken(bool isMultiline) { 260 | String s; 261 | if (isMultiline) { 262 | // Skip initial newline in multiline strings 263 | var start = _startIndex + 4; 264 | if (_text[start] == '\n') start++; 265 | s = _text.substring(start, _index - 3); 266 | } else { 267 | s = _text.substring(_startIndex + 2, _index - 1); 268 | } 269 | return LiteralToken(TokenKind.STRING, _file.span(_startIndex, _index), s); 270 | } 271 | 272 | Token finishMultilineString(int quote) { 273 | var buf = []; 274 | while (true) { 275 | var ch = _nextChar(); 276 | if (ch == 0) { 277 | return _errorToken(); 278 | } else if (ch == quote) { 279 | if (_maybeEatChar(quote)) { 280 | if (_maybeEatChar(quote)) { 281 | return _makeStringToken(buf, false); 282 | } 283 | buf.add(quote); 284 | } 285 | buf.add(quote); 286 | } else if (ch == TokenChar.BACKSLASH) { 287 | var escapeVal = readEscapeSequence(); 288 | if (escapeVal == -1) { 289 | return _errorToken('invalid hex escape sequence'); 290 | } else { 291 | buf.add(escapeVal); 292 | } 293 | } else { 294 | buf.add(ch); 295 | } 296 | } 297 | } 298 | 299 | Token finishString(int quote) { 300 | if (_maybeEatChar(quote)) { 301 | if (_maybeEatChar(quote)) { 302 | // skip an initial newline 303 | _maybeEatChar(TokenChar.NEWLINE); 304 | return finishMultilineString(quote); 305 | } else { 306 | return _makeStringToken([], false); 307 | } 308 | } 309 | return finishStringBody(quote); 310 | } 311 | 312 | Token finishRawString(int quote) { 313 | if (_maybeEatChar(quote)) { 314 | if (_maybeEatChar(quote)) { 315 | return finishMultilineRawString(quote); 316 | } else { 317 | return _makeStringToken([], false); 318 | } 319 | } 320 | while (true) { 321 | var ch = _nextChar(); 322 | if (ch == quote) { 323 | return _makeRawStringToken(false); 324 | } else if (ch == 0) { 325 | return _errorToken(); 326 | } 327 | } 328 | } 329 | 330 | Token finishMultilineRawString(int quote) { 331 | while (true) { 332 | var ch = _nextChar(); 333 | if (ch == 0) { 334 | return _errorToken(); 335 | } else if (ch == quote && _maybeEatChar(quote) && _maybeEatChar(quote)) { 336 | return _makeRawStringToken(true); 337 | } 338 | } 339 | } 340 | 341 | Token finishStringBody(int quote) { 342 | var buf = []; 343 | while (true) { 344 | var ch = _nextChar(); 345 | if (ch == quote) { 346 | return _makeStringToken(buf, false); 347 | } else if (ch == 0) { 348 | return _errorToken(); 349 | } else if (ch == TokenChar.BACKSLASH) { 350 | var escapeVal = readEscapeSequence(); 351 | if (escapeVal == -1) { 352 | return _errorToken('invalid hex escape sequence'); 353 | } else { 354 | buf.add(escapeVal); 355 | } 356 | } else { 357 | buf.add(ch); 358 | } 359 | } 360 | } 361 | 362 | int readEscapeSequence() { 363 | final ch = _nextChar(); 364 | int hexValue; 365 | switch (ch) { 366 | case 110 /*n*/ : 367 | return TokenChar.NEWLINE; 368 | case 114 /*r*/ : 369 | return TokenChar.RETURN; 370 | case 102 /*f*/ : 371 | return TokenChar.FF; 372 | case 98 /*b*/ : 373 | return TokenChar.BACKSPACE; 374 | case 116 /*t*/ : 375 | return TokenChar.TAB; 376 | case 118 /*v*/ : 377 | return TokenChar.FF; 378 | case 120 /*x*/ : 379 | hexValue = readHex(2); 380 | break; 381 | case 117 /*u*/ : 382 | if (_maybeEatChar(TokenChar.LBRACE)) { 383 | hexValue = readHex(); 384 | if (!_maybeEatChar(TokenChar.RBRACE)) { 385 | return -1; 386 | } 387 | } else { 388 | hexValue = readHex(4); 389 | } 390 | break; 391 | default: 392 | return ch; 393 | } 394 | 395 | if (hexValue == -1) return -1; 396 | 397 | // According to the Unicode standard the high and low surrogate halves 398 | // used by UTF-16 (U+D800 through U+DFFF) and values above U+10FFFF 399 | // are not legal Unicode values. 400 | if (hexValue < 0xD800 || hexValue > 0xDFFF && hexValue <= 0xFFFF) { 401 | return hexValue; 402 | } else if (hexValue <= 0x10FFFF) { 403 | messages.error('unicode values greater than 2 bytes not implemented yet', 404 | _file.span(_startIndex, _startIndex + 1)); 405 | return -1; 406 | } else { 407 | return -1; 408 | } 409 | } 410 | 411 | Token finishDot() { 412 | if (TokenizerHelpers.isDigit(_peekChar())) { 413 | eatDigits(); 414 | return finishNumberExtra(TokenKind.DOUBLE); 415 | } else { 416 | return _finishToken(TokenKind.DOT); 417 | } 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /lib/src/tree_base.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | part of '../visitor.dart'; 6 | 7 | /// The base type for all nodes in a CSS abstract syntax tree. 8 | abstract class TreeNode { 9 | /// The source code this [TreeNode] represents. 10 | final SourceSpan? span; 11 | 12 | TreeNode(this.span); 13 | 14 | TreeNode clone(); 15 | 16 | /// Classic double-dispatch visitor for implementing passes. 17 | dynamic visit(VisitorBase visitor); 18 | 19 | /// A multiline string showing the node and its children. 20 | String toDebugString() { 21 | var to = TreeOutput(); 22 | var tp = _TreePrinter(to, true); 23 | visit(tp); 24 | return to.buf.toString(); 25 | } 26 | } 27 | 28 | /// The base type for expressions. 29 | abstract class Expression extends TreeNode { 30 | Expression(super.span); 31 | @override 32 | Expression clone(); 33 | } 34 | 35 | /// Simple class to provide a textual dump of trees for debugging. 36 | class TreeOutput { 37 | int depth = 0; 38 | final StringBuffer buf = StringBuffer(); 39 | VisitorBase? printer; 40 | 41 | void write(String s) { 42 | for (var i = 0; i < depth; i++) { 43 | buf.write(' '); 44 | } 45 | buf.write(s); 46 | } 47 | 48 | void writeln(String s) { 49 | write(s); 50 | buf.write('\n'); 51 | } 52 | 53 | void heading(String name, [SourceSpan? span]) { 54 | write(name); 55 | if (span != null) { 56 | buf.write(' (${span.message('')})'); 57 | } 58 | buf.write('\n'); 59 | } 60 | 61 | String toValue(dynamic value) { 62 | if (value == null) { 63 | return 'null'; 64 | } else if (value is Identifier) { 65 | return value.name; 66 | } else { 67 | return value.toString(); 68 | } 69 | } 70 | 71 | void writeNode(String label, TreeNode? node) { 72 | write('$label: '); 73 | depth += 1; 74 | if (node != null) { 75 | node.visit(printer!); 76 | } else { 77 | writeln('null'); 78 | } 79 | depth -= 1; 80 | } 81 | 82 | void writeValue(String label, dynamic value) { 83 | var v = toValue(value); 84 | writeln('$label: $v'); 85 | } 86 | 87 | void writeNodeList(String label, List? list) { 88 | writeln('$label ['); 89 | if (list != null) { 90 | depth += 1; 91 | for (var node in list) { 92 | node.visit(printer!); 93 | } 94 | depth -= 1; 95 | writeln(']'); 96 | } 97 | } 98 | 99 | @override 100 | String toString() => buf.toString(); 101 | } 102 | -------------------------------------------------------------------------------- /lib/src/validate.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:source_span/source_span.dart'; 6 | 7 | import '../visitor.dart'; 8 | 9 | /// Can be thrown on any Css runtime problem includes source location. 10 | class CssSelectorException extends SourceSpanException { 11 | CssSelectorException(super.message, [super.span]); 12 | } 13 | 14 | List classes = []; 15 | List ids = []; 16 | 17 | class Validate { 18 | static int _classNameCheck(SimpleSelectorSequence selector, int matches) { 19 | if (selector.isCombinatorDescendant || 20 | (selector.isCombinatorNone && matches == 0)) { 21 | if (matches < 0) { 22 | var tooMany = selector.simpleSelector.toString(); 23 | throw CssSelectorException( 24 | 'Can not mix Id selector with class selector(s). Id ' 25 | 'selector must be singleton too many starting at $tooMany'); 26 | } 27 | 28 | return matches + 1; 29 | } else { 30 | var error = selector.toString(); 31 | throw CssSelectorException( 32 | 'Selectors can not have combinators (>, +, or ~) before $error'); 33 | } 34 | } 35 | 36 | static int _elementIdCheck(SimpleSelectorSequence selector, int matches) { 37 | if (selector.isCombinatorNone && matches == 0) { 38 | // Perfect just one element id returns matches of -1. 39 | return -1; 40 | } else if (selector.isCombinatorDescendant) { 41 | var tooMany = selector.simpleSelector.toString(); 42 | throw CssSelectorException( 43 | 'Use of Id selector must be singleton starting at $tooMany'); 44 | } else { 45 | var error = selector.simpleSelector.toString(); 46 | throw CssSelectorException( 47 | 'Selectors can not have combinators (>, +, or ~) before $error'); 48 | } 49 | } 50 | 51 | // Validate the @{css expression} only .class and #elementId are valid inside 52 | // of @{...}. 53 | static void template(List selectors) { 54 | var found = false; // signal if a selector is matched. 55 | var matches = 0; // < 0 IdSelectors, > 0 ClassSelector 56 | 57 | // At most one selector group (any number of simple selector sequences). 58 | assert(selectors.length <= 1); 59 | 60 | for (final sels in selectors) { 61 | for (final selector in sels.simpleSelectorSequences) { 62 | found = false; 63 | var simpleSelector = selector.simpleSelector; 64 | if (simpleSelector is ClassSelector) { 65 | // Any class name starting with an underscore is a private class name 66 | // that doesn't have to match the world of known classes. 67 | if (!simpleSelector.name.startsWith('_')) { 68 | // TODO(terry): For now iterate through all classes look for faster 69 | // mechanism hash map, etc. 70 | for (final className in classes) { 71 | if (selector.simpleSelector.name == className) { 72 | matches = _classNameCheck(selector, matches); 73 | found = true; // .class found. 74 | break; 75 | } 76 | for (final className2 in classes) { 77 | print(className2); 78 | } 79 | } 80 | } else { 81 | // Don't check any class name that is prefixed with an underscore. 82 | // However, signal as found and bump up matches; it's a valid class 83 | // name. 84 | matches = _classNameCheck(selector, matches); 85 | found = true; // ._class are always okay. 86 | } 87 | } else if (simpleSelector is IdSelector) { 88 | // Any element id starting with an underscore is a private element id 89 | // that doesn't have to match the world of known element ids. 90 | if (!simpleSelector.name.startsWith('_')) { 91 | for (final id in ids) { 92 | if (simpleSelector.name == id) { 93 | matches = _elementIdCheck(selector, matches); 94 | found = true; // #id found. 95 | break; 96 | } 97 | } 98 | } else { 99 | // Don't check any element ID that is prefixed with an underscore. 100 | // Signal as found and bump up matches; it's a valid element ID. 101 | matches = _elementIdCheck(selector, matches); 102 | found = true; // #_id are always okay 103 | } 104 | } else { 105 | var badSelector = simpleSelector.toString(); 106 | throw CssSelectorException('Invalid template selector $badSelector'); 107 | } 108 | 109 | if (!found) { 110 | var unknownName = simpleSelector.toString(); 111 | throw CssSelectorException('Unknown selector name $unknownName'); 112 | } 113 | } 114 | } 115 | 116 | // Every selector must match. 117 | var selector = selectors[0]; 118 | assert((matches >= 0 ? matches : -matches) == 119 | selector.simpleSelectorSequences.length); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: csslib 2 | version: 1.0.2-wip 3 | description: A library for parsing and analyzing CSS (Cascading Style Sheets). 4 | repository: https://github.com/dart-lang/csslib 5 | 6 | topics: 7 | - css 8 | 9 | environment: 10 | sdk: ^3.1.0 11 | 12 | dependencies: 13 | source_span: ^1.8.0 14 | 15 | dev_dependencies: 16 | dart_flutter_team_lints: ^3.0.0 17 | path: ^1.8.0 18 | term_glyph: ^1.2.0 19 | test: ^1.16.0 20 | -------------------------------------------------------------------------------- /test/color_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/parser.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | void main() { 9 | group('css', () { 10 | test('rgb', () { 11 | final color = Color.css('rgb(0, 0, 255)'); 12 | expect(color, equals(Color(0x0000FF))); 13 | }); 14 | 15 | test('rgba', () { 16 | final color = Color.css('rgba(0, 0, 255, 1.0)'); 17 | expect(color, equals(Color.createRgba(0, 0, 255, 1.0))); 18 | }); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /test/debug_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:test/test.dart'; 6 | 7 | import 'testing.dart'; 8 | 9 | void main() { 10 | test('exercise debug', () { 11 | var style = parseCss(_input); 12 | 13 | var debugValue = style.toDebugString(); 14 | expect(debugValue, isNotNull); 15 | 16 | var style2 = style.clone(); 17 | 18 | expect(style2.toDebugString(), debugValue); 19 | }); 20 | } 21 | 22 | const String _input = r''' 23 | .foo { 24 | background-color: #191919; 25 | width: 10PX; 26 | height: 22mM !important; 27 | border-width: 20cm; 28 | margin-width: 33%; 29 | border-height: 30EM; 30 | width: .6in; 31 | length: 1.2in; 32 | -web-stuff: -10Px; 33 | }'''; 34 | -------------------------------------------------------------------------------- /test/error_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/src/messages.dart'; 6 | import 'package:term_glyph/term_glyph.dart' as glyph; 7 | import 'package:test/test.dart'; 8 | 9 | import 'testing.dart'; 10 | 11 | /// Test for unsupported font-weights values of bolder, lighter and inherit. 12 | void testUnsupportedFontWeights() { 13 | var errors = []; 14 | 15 | // TODO(terry): Need to support bolder. 16 | // font-weight value bolder. 17 | var input = '.foobar { font-weight: bolder; }'; 18 | var stylesheet = parseCss(input, errors: errors); 19 | 20 | expect(errors.isEmpty, false); 21 | expect(errors[0].toString(), ''' 22 | error on line 1, column 24: Unknown property value bolder 23 | , 24 | 1 | .foobar { font-weight: bolder; } 25 | | ^^^^^^ 26 | \''''); 27 | 28 | expect(prettyPrint(stylesheet), r''' 29 | .foobar { 30 | font-weight: bolder; 31 | }'''); 32 | 33 | // TODO(terry): Need to support lighter. 34 | // font-weight value lighter. 35 | input = '.foobar { font-weight: lighter; }'; 36 | stylesheet = parseCss(input, errors: errors..clear()); 37 | 38 | expect(errors.isEmpty, false); 39 | expect(errors[0].toString(), ''' 40 | error on line 1, column 24: Unknown property value lighter 41 | , 42 | 1 | .foobar { font-weight: lighter; } 43 | | ^^^^^^^ 44 | \''''); 45 | expect(prettyPrint(stylesheet), r''' 46 | .foobar { 47 | font-weight: lighter; 48 | }'''); 49 | 50 | // TODO(terry): Need to support inherit. 51 | // font-weight value inherit. 52 | input = '.foobar { font-weight: inherit; }'; 53 | stylesheet = parseCss(input, errors: errors..clear()); 54 | 55 | expect(errors.isEmpty, false); 56 | expect(errors[0].toString(), ''' 57 | error on line 1, column 24: Unknown property value inherit 58 | , 59 | 1 | .foobar { font-weight: inherit; } 60 | | ^^^^^^^ 61 | \''''); 62 | expect(prettyPrint(stylesheet), r''' 63 | .foobar { 64 | font-weight: inherit; 65 | }'''); 66 | } 67 | 68 | /// Test for unsupported line-height values of units other than px, pt and 69 | /// inherit. 70 | void testUnsupportedLineHeights() { 71 | var errors = []; 72 | 73 | // line-height value in percentage unit. 74 | var input = '.foobar { line-height: 120%; }'; 75 | var stylesheet = parseCss(input, errors: errors); 76 | 77 | expect(errors.isEmpty, false); 78 | expect(errors[0].toString(), ''' 79 | error on line 1, column 24: Unexpected value for line-height 80 | , 81 | 1 | .foobar { line-height: 120%; } 82 | | ^^^^ 83 | \''''); 84 | expect(prettyPrint(stylesheet), r''' 85 | .foobar { 86 | line-height: 120%; 87 | }'''); 88 | 89 | // TODO(terry): Need to support all units. 90 | // line-height value in cm unit. 91 | input = '.foobar { line-height: 20cm; }'; 92 | stylesheet = parseCss(input, errors: errors..clear()); 93 | 94 | expect(errors.isEmpty, false); 95 | expect(errors[0].toString(), ''' 96 | error on line 1, column 24: Unexpected unit for line-height 97 | , 98 | 1 | .foobar { line-height: 20cm; } 99 | | ^^^^ 100 | \''''); 101 | expect(prettyPrint(stylesheet), r''' 102 | .foobar { 103 | line-height: 20cm; 104 | }'''); 105 | 106 | // TODO(terry): Need to support inherit. 107 | // line-height value inherit. 108 | input = '.foobar { line-height: inherit; }'; 109 | stylesheet = parseCss(input, errors: errors..clear()); 110 | 111 | expect(errors.isEmpty, false); 112 | expect(errors[0].toString(), ''' 113 | error on line 1, column 24: Unknown property value inherit 114 | , 115 | 1 | .foobar { line-height: inherit; } 116 | | ^^^^^^^ 117 | \''''); 118 | expect(prettyPrint(stylesheet), r''' 119 | .foobar { 120 | line-height: inherit; 121 | }'''); 122 | } 123 | 124 | /// Test for bad selectors. 125 | void testBadSelectors() { 126 | var errors = []; 127 | 128 | // Invalid id selector. 129 | var input = '# foo { color: #ff00ff; }'; 130 | parseCss(input, errors: errors); 131 | 132 | expect(errors, isNotEmpty); 133 | expect(errors[0].toString(), ''' 134 | error on line 1, column 1: Not a valid ID selector expected #id 135 | , 136 | 1 | # foo { color: #ff00ff; } 137 | | ^ 138 | \''''); 139 | 140 | // Invalid class selector. 141 | input = '. foo { color: #ff00ff; }'; 142 | parseCss(input, errors: errors..clear()); 143 | 144 | expect(errors, isNotEmpty); 145 | expect(errors[0].toString(), ''' 146 | error on line 1, column 1: Not a valid class selector expected .className 147 | , 148 | 1 | . foo { color: #ff00ff; } 149 | | ^ 150 | \''''); 151 | } 152 | 153 | /// Test for bad hex values. 154 | void testBadHexValues() { 155 | var errors = []; 156 | 157 | // Invalid hex value. 158 | var input = '.foobar { color: #AH787; }'; 159 | var stylesheet = parseCss(input, errors: errors); 160 | 161 | expect(errors.isEmpty, false); 162 | expect(errors[0].toString(), ''' 163 | error on line 1, column 18: Bad hex number 164 | , 165 | 1 | .foobar { color: #AH787; } 166 | | ^^^^^^ 167 | \''''); 168 | expect(prettyPrint(stylesheet), r''' 169 | .foobar { 170 | color: #AH787; 171 | }'''); 172 | 173 | // Bad color constant. 174 | input = '.foobar { color: redder; }'; 175 | stylesheet = parseCss(input, errors: errors..clear()); 176 | 177 | expect(errors.isEmpty, false); 178 | expect(errors[0].toString(), ''' 179 | error on line 1, column 18: Unknown property value redder 180 | , 181 | 1 | .foobar { color: redder; } 182 | | ^^^^^^ 183 | \''''); 184 | 185 | expect(prettyPrint(stylesheet), r''' 186 | .foobar { 187 | color: redder; 188 | }'''); 189 | 190 | // Bad hex color #ffffff. 191 | input = '.foobar { color: # ffffff; }'; 192 | stylesheet = parseCss(input, errors: errors..clear()); 193 | 194 | expect(errors.isEmpty, false); 195 | expect(errors[0].toString(), ''' 196 | error on line 1, column 18: Expected hex number 197 | , 198 | 1 | .foobar { color: # ffffff; } 199 | | ^ 200 | \''''); 201 | 202 | expect(prettyPrint(stylesheet), r''' 203 | .foobar { 204 | color: # ffffff; 205 | }'''); 206 | 207 | // Bad hex color #123fff. 208 | input = '.foobar { color: # 123fff; }'; 209 | stylesheet = parseCss(input, errors: errors..clear()); 210 | 211 | expect(errors.isEmpty, false); 212 | expect(errors[0].toString(), ''' 213 | error on line 1, column 18: Expected hex number 214 | , 215 | 1 | .foobar { color: # 123fff; } 216 | | ^ 217 | \''''); 218 | 219 | // Formating is off with an extra space. However, the entire value is bad 220 | // and isn't processed anyway. 221 | expect(prettyPrint(stylesheet), r''' 222 | .foobar { 223 | color: # 123 fff; 224 | }'''); 225 | } 226 | 227 | void testBadUnicode() { 228 | var errors = []; 229 | final input = ''' 230 | @font-face { 231 | src: url(fonts/BBCBengali.ttf) format("opentype"); 232 | unicode-range: U+400-200; 233 | }'''; 234 | 235 | parseCss(input, errors: errors); 236 | 237 | expect(errors.isEmpty, false); 238 | expect( 239 | errors[0].toString(), 240 | 'error on line 3, column 20: unicode first range can not be greater than ' 241 | 'last\n' 242 | ' ,\n' 243 | '3 | unicode-range: U+400-200;\n' 244 | ' | ^^^^^^^\n' 245 | ' \''); 246 | 247 | final input2 = ''' 248 | @font-face { 249 | src: url(fonts/BBCBengali.ttf) format("opentype"); 250 | unicode-range: U+12FFFF; 251 | }'''; 252 | 253 | parseCss(input2, errors: errors..clear()); 254 | 255 | expect(errors.isEmpty, false); 256 | expect( 257 | errors[0].toString(), 258 | 'error on line 3, column 20: unicode range must be less than 10FFFF\n' 259 | ' ,\n' 260 | '3 | unicode-range: U+12FFFF;\n' 261 | ' | ^^^^^^\n' 262 | ' \''); 263 | } 264 | 265 | void testBadNesting() { 266 | var errors = []; 267 | 268 | // Test for bad declaration in a nested rule. 269 | final input = ''' 270 | div { 271 | width: 20px; 272 | span + ul { color: blue; } 273 | span + ul > #aaaa { 274 | color: #ffghghgh; 275 | } 276 | background-color: red; 277 | } 278 | '''; 279 | 280 | parseCss(input, errors: errors); 281 | expect(errors.length, 1); 282 | var errorMessage = messages.messages[0]; 283 | expect(errorMessage.message, contains('Bad hex number')); 284 | expect(errorMessage.span, isNotNull); 285 | expect(errorMessage.span!.start.line, 4); 286 | expect(errorMessage.span!.start.column, 11); 287 | expect(errorMessage.span!.text, '#ffghghgh'); 288 | 289 | // Test for bad selector syntax. 290 | final input2 = ''' 291 | div { 292 | span + ul #aaaa > (3333) { 293 | color: #ffghghgh; 294 | } 295 | } 296 | '''; 297 | parseCss(input2, errors: errors..clear()); 298 | expect(errors.length, 4); 299 | errorMessage = messages.messages[0]; 300 | expect(errorMessage.message, contains(':, but found +')); 301 | expect(errorMessage.span, isNotNull); 302 | expect(errorMessage.span!.start.line, 1); 303 | expect(errorMessage.span!.start.column, 7); 304 | expect(errorMessage.span!.text, '+'); 305 | 306 | errorMessage = messages.messages[1]; 307 | expect(errorMessage.message, contains('Unknown property value ul')); 308 | expect(errorMessage.span, isNotNull); 309 | expect(errorMessage.span!.start.line, 1); 310 | expect(errorMessage.span!.start.column, 9); 311 | expect(errorMessage.span!.text, 'ul'); 312 | 313 | errorMessage = messages.messages[2]; 314 | expect(errorMessage.message, contains('expected }, but found >')); 315 | expect(errorMessage.span, isNotNull); 316 | expect(errorMessage.span!.start.line, 1); 317 | expect(errorMessage.span!.start.column, 18); 318 | expect(errorMessage.span!.text, '>'); 319 | 320 | errorMessage = messages.messages[3]; 321 | expect(errorMessage.message, contains('premature end of file unknown CSS')); 322 | expect(errorMessage.span, isNotNull); 323 | expect(errorMessage.span!.start.line, 1); 324 | expect(errorMessage.span!.start.column, 20); 325 | expect(errorMessage.span!.text, '('); 326 | 327 | // Test for missing close braces and bad declaration. 328 | final input3 = ''' 329 | div { 330 | span { 331 | color: #green; 332 | } 333 | '''; 334 | parseCss(input3, errors: errors..clear()); 335 | expect(errors.length, 2); 336 | errorMessage = messages.messages[0]; 337 | expect(errorMessage.message, contains('Bad hex number')); 338 | expect(errorMessage.span, isNotNull); 339 | expect(errorMessage.span!.start.line, 2); 340 | expect(errorMessage.span!.start.column, 11); 341 | expect(errorMessage.span!.text, '#green'); 342 | 343 | errorMessage = messages.messages[1]; 344 | expect(errorMessage.message, contains('expected }, but found end of file')); 345 | expect(errorMessage.span, isNotNull); 346 | expect(errorMessage.span!.start.line, 3); 347 | expect(errorMessage.span!.start.column, 1); 348 | expect(errorMessage.span!.text, '\n'); 349 | } 350 | 351 | void main() { 352 | glyph.ascii = true; 353 | test('font-weight value errors', testUnsupportedFontWeights); 354 | test('line-height value errors', testUnsupportedLineHeights); 355 | test('bad selectors', testBadSelectors); 356 | test('bad Hex values', testBadHexValues); 357 | test('bad unicode ranges', testBadUnicode); 358 | test('nested rules', testBadNesting); 359 | } 360 | -------------------------------------------------------------------------------- /test/escape_codes_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/parser.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import 'testing.dart'; 9 | 10 | void main() { 11 | final errors = []; 12 | 13 | tearDown(errors.clear); 14 | 15 | group('handles escape codes', () { 16 | group('in an identifier', () { 17 | test('with trailing space', () { 18 | final selectorAst = selector(r'.\35 00px', errors: errors); 19 | expect(errors, isEmpty); 20 | expect(compactOutput(selectorAst), r'.\35 00px'); 21 | }); 22 | 23 | test('in an attribute selector value', () { 24 | final selectorAst = selector(r'[elevation=\31]', errors: errors); 25 | expect(errors, isEmpty); 26 | expect(compactOutput(selectorAst), r'[elevation=\31]'); 27 | }); 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /test/extend_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/src/messages.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import 'testing.dart'; 9 | 10 | void compileAndValidate(String input, String generated) { 11 | var errors = []; 12 | var stylesheet = compileCss(input, errors: errors, opts: options); 13 | expect(errors.isEmpty, true, reason: errors.toString()); 14 | expect(prettyPrint(stylesheet), generated); 15 | } 16 | 17 | void simpleExtend() { 18 | compileAndValidate(r''' 19 | .error { 20 | border: 1px red; 21 | background-color: #fdd; 22 | } 23 | .seriousError { 24 | @extend .error; 25 | border-width: 3px; 26 | } 27 | ''', r''' 28 | .error, .seriousError { 29 | border: 1px #f00; 30 | background-color: #fdd; 31 | } 32 | .seriousError { 33 | border-width: 3px; 34 | }'''); 35 | } 36 | 37 | void complexSelectors() { 38 | compileAndValidate(r''' 39 | .error { 40 | border: 1px #f00; 41 | background-color: #fdd; 42 | } 43 | .error.intrusion { 44 | background-image: url("/image/hacked.png"); 45 | } 46 | .seriousError { 47 | @extend .error; 48 | border-width: 3px; 49 | } 50 | ''', r''' 51 | .error, .seriousError { 52 | border: 1px #f00; 53 | background-color: #fdd; 54 | } 55 | .error.intrusion, .seriousError.intrusion { 56 | background-image: url("/image/hacked.png"); 57 | } 58 | .seriousError { 59 | border-width: 3px; 60 | }'''); 61 | 62 | compileAndValidate(r''' 63 | a:hover { 64 | text-decoration: underline; 65 | } 66 | .hoverlink { 67 | @extend a:hover; 68 | } 69 | ''', r''' 70 | a:hover, .hoverlink { 71 | text-decoration: underline; 72 | } 73 | .hoverlink { 74 | }'''); 75 | } 76 | 77 | void multipleExtends() { 78 | compileAndValidate(r''' 79 | .error { 80 | border: 1px #f00; 81 | background-color: #fdd; 82 | } 83 | .attention { 84 | font-size: 3em; 85 | background-color: #ff0; 86 | } 87 | .seriousError { 88 | @extend .error; 89 | @extend .attention; 90 | border-width: 3px; 91 | } 92 | ''', r''' 93 | .error, .seriousError { 94 | border: 1px #f00; 95 | background-color: #fdd; 96 | } 97 | .attention, .seriousError { 98 | font-size: 3em; 99 | background-color: #ff0; 100 | } 101 | .seriousError { 102 | border-width: 3px; 103 | }'''); 104 | } 105 | 106 | void chaining() { 107 | compileAndValidate(r''' 108 | .error { 109 | border: 1px #f00; 110 | background-color: #fdd; 111 | } 112 | .seriousError { 113 | @extend .error; 114 | border-width: 3px; 115 | } 116 | .criticalError { 117 | @extend .seriousError; 118 | position: fixed; 119 | top: 10%; 120 | bottom: 10%; 121 | left: 10%; 122 | right: 10%; 123 | } 124 | ''', r''' 125 | .error, .seriousError, .criticalError { 126 | border: 1px #f00; 127 | background-color: #fdd; 128 | } 129 | .seriousError, .criticalError { 130 | border-width: 3px; 131 | } 132 | .criticalError { 133 | position: fixed; 134 | top: 10%; 135 | bottom: 10%; 136 | left: 10%; 137 | right: 10%; 138 | }'''); 139 | } 140 | 141 | void nestedSelectors() { 142 | compileAndValidate(r''' 143 | a { 144 | color: blue; 145 | &:hover { 146 | text-decoration: underline; 147 | } 148 | } 149 | 150 | #fake-links .link { 151 | @extend a; 152 | } 153 | ''', r''' 154 | a, #fake-links .link { 155 | color: #00f; 156 | } 157 | a:hover, #fake-links .link:hover { 158 | text-decoration: underline; 159 | } 160 | #fake-links .link { 161 | }'''); 162 | } 163 | 164 | void nestedMulty() { 165 | compileAndValidate(r''' 166 | .btn { 167 | display: inline-block; 168 | } 169 | 170 | input[type="checkbox"].toggle-button { 171 | color: red; 172 | 173 | + label { 174 | @extend .btn; 175 | } 176 | } 177 | ''', r''' 178 | .btn, input[type="checkbox"].toggle-button label { 179 | display: inline-block; 180 | } 181 | input[type="checkbox"].toggle-button { 182 | color: #f00; 183 | } 184 | input[type="checkbox"].toggle-button label { 185 | }'''); 186 | } 187 | 188 | void nWayExtends() { 189 | compileAndValidate( 190 | r''' 191 | .btn > .btn { 192 | margin-left: 5px; 193 | } 194 | input.second + label { 195 | @extend .btn; 196 | } 197 | ''', 198 | '.btn > .btn, ' 199 | 'input.second + label > .btn, ' 200 | '.btn > input.second + label, ' 201 | 'input.second + label > input.second + label, ' 202 | 'input.second + label > input.second + label {\n' 203 | ' margin-left: 5px;\n}\n' 204 | 'input.second + label {\n' 205 | '}'); 206 | 207 | // TODO(terry): Optimize merge selectors would be: 208 | // 209 | // .btn + .btn, input.second + label + .btn, input.second.btn + label { 210 | // margin-left: 5px; 211 | // } 212 | // input.second + label { 213 | // color: blue; 214 | // } 215 | compileAndValidate( 216 | r''' 217 | .btn + .btn { 218 | margin-left: 5px; 219 | } 220 | input.second + label { 221 | @extend .btn; 222 | color: blue; 223 | } 224 | ''', 225 | '.btn + .btn, ' 226 | 'input.second + label + .btn, ' 227 | '.btn + input.second + label, ' 228 | 'input.second + label + input.second + label, ' 229 | 'input.second + label + input.second + label {\n' 230 | ' margin-left: 5px;\n}\n' 231 | 'input.second + label {\n' 232 | ' color: #00f;\n}'); 233 | } 234 | 235 | void main() { 236 | test('Simple Extend', simpleExtend); 237 | test('complex', complexSelectors); 238 | test('multiple', multipleExtends); 239 | test('chaining', chaining); 240 | test('nested selectors', nestedSelectors); 241 | test('nested many selector sequences', nestedMulty); 242 | test('N-way extends', nWayExtends); 243 | } 244 | -------------------------------------------------------------------------------- /test/keyframes_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/src/messages.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import 'testing.dart'; 9 | 10 | void main() { 11 | test('keyframes', () { 12 | final input = 13 | r'@keyframes ping { 75%, 100% { transform: scale(2); opacity: 0; } }'; 14 | var errors = []; 15 | var stylesheet = compileCss(input, errors: errors, opts: options); 16 | expect(errors, isEmpty); 17 | final expected = r''' 18 | @keyframes ping { 19 | 75%, 100% { 20 | transform: scale(2); 21 | opacity: 0; 22 | } 23 | }'''; 24 | expect(prettyPrint(stylesheet), expected); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /test/mixin_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/src/messages.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import 'testing.dart'; 9 | 10 | void compileAndValidate(String input, String generated) { 11 | var errors = []; 12 | var stylesheet = compileCss(input, errors: errors, opts: options); 13 | expect(errors.isEmpty, true, reason: errors.toString()); 14 | expect(prettyPrint(stylesheet), generated); 15 | } 16 | 17 | void compilePolyfillAndValidate(String input, String generated) { 18 | var errors = []; 19 | var stylesheet = polyFillCompileCss(input, errors: errors, opts: options); 20 | expect(errors.isEmpty, true, reason: errors.toString()); 21 | expect(prettyPrint(stylesheet), generated); 22 | } 23 | 24 | void topLevelMixin() { 25 | compileAndValidate(r''' 26 | @mixin silly-links { 27 | a { 28 | color: blue; 29 | background-color: red; 30 | } 31 | } 32 | 33 | @include silly-links; 34 | ''', r''' 35 | a { 36 | color: #00f; 37 | background-color: #f00; 38 | }'''); 39 | } 40 | 41 | void topLevelMixinTwoIncludes() { 42 | compileAndValidate(r''' 43 | @mixin a { 44 | a { 45 | color: blue; 46 | background-color: red; 47 | } 48 | } 49 | @mixin b { 50 | span { 51 | color: black; 52 | background-color: orange; 53 | } 54 | } 55 | @include a; 56 | @include b; 57 | ''', r''' 58 | a { 59 | color: #00f; 60 | background-color: #f00; 61 | } 62 | span { 63 | color: #000; 64 | background-color: #ffa500; 65 | }'''); 66 | } 67 | 68 | /// Tests top-level mixins that includes another mixin. 69 | void topLevelMixinMultiRulesets() { 70 | compileAndValidate(r''' 71 | @mixin a { 72 | a { 73 | color: blue; 74 | background-color: red; 75 | } 76 | } 77 | @mixin b { 78 | #foo-id { 79 | border-top: 1px solid red; 80 | border-bottom: 2px solid green; 81 | } 82 | } 83 | @mixin c { 84 | span { 85 | color: black; 86 | background-color: orange; 87 | } 88 | @include b; 89 | } 90 | @include a; 91 | @include c; 92 | ''', r''' 93 | a { 94 | color: #00f; 95 | background-color: #f00; 96 | } 97 | span { 98 | color: #000; 99 | background-color: #ffa500; 100 | } 101 | #foo-id { 102 | border-top: 1px solid #f00; 103 | border-bottom: 2px solid #008000; 104 | }'''); 105 | } 106 | 107 | void topLevelMixinDeeplyNestedRulesets() { 108 | compileAndValidate(r''' 109 | @mixin a { 110 | a { 111 | color: blue; 112 | background-color: red; 113 | } 114 | } 115 | @mixin b { 116 | #foo-id { 117 | border-top: 1px solid red; 118 | border-bottom: 2px solid green; 119 | } 120 | } 121 | @mixin f { 122 | #split-bar div { 123 | border: 1px solid lightgray; 124 | } 125 | } 126 | @mixin e { 127 | #split-bar:visited { 128 | color: gray; 129 | } 130 | @include f; 131 | } 132 | @mixin d { 133 | a:hover { 134 | cursor: arrow; 135 | } 136 | @include e 137 | } 138 | @mixin c { 139 | @include a; 140 | span { 141 | color: black; 142 | background-color: orange; 143 | } 144 | @include b; 145 | @include d; 146 | } 147 | @include c; 148 | ''', r''' 149 | a { 150 | color: #00f; 151 | background-color: #f00; 152 | } 153 | span { 154 | color: #000; 155 | background-color: #ffa500; 156 | } 157 | #foo-id { 158 | border-top: 1px solid #f00; 159 | border-bottom: 2px solid #008000; 160 | } 161 | a:hover { 162 | cursor: arrow; 163 | } 164 | #split-bar:visited { 165 | color: #808080; 166 | } 167 | #split-bar div { 168 | border: 1px solid #d3d3d3; 169 | }'''); 170 | } 171 | 172 | /// Tests selector groups and other combinators. 173 | void topLevelMixinSelectors() { 174 | compileAndValidate(r''' 175 | @mixin a { 176 | a, b { 177 | color: blue; 178 | background-color: red; 179 | } 180 | div > span { 181 | color: black; 182 | background-color: orange; 183 | } 184 | } 185 | 186 | @include a; 187 | ''', r''' 188 | a, b { 189 | color: #00f; 190 | background-color: #f00; 191 | } 192 | div > span { 193 | color: #000; 194 | background-color: #ffa500; 195 | }'''); 196 | } 197 | 198 | void declSimpleMixin() { 199 | compileAndValidate(r''' 200 | @mixin div-border { 201 | border: 2px dashed red; 202 | } 203 | div { 204 | @include div-border; 205 | } 206 | ''', r''' 207 | div { 208 | border: 2px dashed #f00; 209 | }'''); 210 | } 211 | 212 | void declMixinTwoIncludes() { 213 | compileAndValidate(r''' 214 | @mixin div-border { 215 | border: 2px dashed red; 216 | } 217 | @mixin div-color { 218 | color: blue; 219 | } 220 | div { 221 | @include div-border; 222 | @include div-color; 223 | } 224 | ''', r''' 225 | div { 226 | border: 2px dashed #f00; 227 | color: #00f; 228 | }'''); 229 | } 230 | 231 | void declMixinNestedIncludes() { 232 | compileAndValidate(r''' 233 | @mixin div-border { 234 | border: 2px dashed red; 235 | } 236 | @mixin div-padding { 237 | padding: .5em; 238 | } 239 | @mixin div-margin { 240 | margin: 5px; 241 | } 242 | @mixin div-color { 243 | @include div-padding; 244 | color: blue; 245 | @include div-margin; 246 | } 247 | div { 248 | @include div-border; 249 | @include div-color; 250 | } 251 | ''', r''' 252 | div { 253 | border: 2px dashed #f00; 254 | padding: .5em; 255 | color: #00f; 256 | margin: 5px; 257 | }'''); 258 | } 259 | 260 | void declMixinDeeperNestedIncludes() { 261 | compileAndValidate(r''' 262 | @mixin div-border { 263 | border: 2px dashed red; 264 | } 265 | @mixin div-padding { 266 | padding: .5em; 267 | } 268 | @mixin div-margin { 269 | margin: 5px; 270 | } 271 | @mixin div-color { 272 | @include div-padding; 273 | @include div-margin; 274 | } 275 | div { 276 | @include div-border; 277 | @include div-color; 278 | } 279 | ''', r''' 280 | div { 281 | border: 2px dashed #f00; 282 | padding: .5em; 283 | margin: 5px; 284 | }'''); 285 | } 286 | 287 | void mixinArg() { 288 | compileAndValidate(r''' 289 | @mixin div-border-1 { 290 | border: 2px dashed red; 291 | } 292 | @mixin div-border-2() { 293 | border: 22px solid blue; 294 | } 295 | @mixin div-left(@dist) { 296 | margin-left: @dist; 297 | } 298 | @mixin div-right(var-margin) { 299 | margin-right: var(margin); 300 | } 301 | div-1 { 302 | @include div-left(10px); 303 | @include div-right(100px); 304 | @include div-border-1; 305 | } 306 | div-2 { 307 | @include div-left(20em); 308 | @include div-right(5in); 309 | @include div-border-2(); 310 | } 311 | div-3 { 312 | @include div-border-1(); 313 | } 314 | div-4 { 315 | @include div-border-2; 316 | } 317 | ''', r''' 318 | div-1 { 319 | margin-left: 10px; 320 | margin-right: 100px; 321 | border: 2px dashed #f00; 322 | } 323 | div-2 { 324 | margin-left: 20em; 325 | margin-right: 5in; 326 | border: 22px solid #00f; 327 | } 328 | div-3 { 329 | border: 2px dashed #f00; 330 | } 331 | div-4 { 332 | border: 22px solid #00f; 333 | }'''); 334 | } 335 | 336 | void mixinArgs() { 337 | compileAndValidate(r''' 338 | @mixin box-shadow(@shadows...) { 339 | -moz-box-shadow: @shadows; 340 | -webkit-box-shadow: @shadows; 341 | box-shadow: var(shadows); 342 | } 343 | 344 | .shadows { 345 | @include box-shadow(0px 4px 5px #666, 2px 6px 10px #999); 346 | }''', r''' 347 | .shadowed { 348 | -moz-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999; 349 | -webkit-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999; 350 | box-shadow: 0px 4px 5px #666, 2px 6px 10px #999; 351 | } 352 | '''); 353 | } 354 | 355 | void mixinManyArgs() { 356 | compileAndValidate(r''' 357 | @mixin border(@border-values) { 358 | border: @border-values 359 | } 360 | 361 | .primary { 362 | @include border(3px solid green); 363 | } 364 | ''', r''' 365 | .primary { 366 | border: 3px solid #008000; 367 | }'''); 368 | 369 | compileAndValidate(r''' 370 | @mixin setup(@border-color, @border-style, @border-size, @color) { 371 | border: @border-size @border-style @border-color; 372 | color: @color; 373 | } 374 | 375 | .primary { 376 | @include setup(red, solid, 5px, blue); 377 | } 378 | ''', r''' 379 | .primary { 380 | border: 5px solid #f00; 381 | color: #00f; 382 | }'''); 383 | 384 | // Test passing a declaration that is multiple parameters. 385 | compileAndValidate(r''' 386 | @mixin colors(@text, @background, @border) { 387 | color: @text; 388 | background-color: @background; 389 | border-color: @border; 390 | } 391 | 392 | @values: #ff0000, #00ff00, #0000ff; 393 | .primary { 394 | @include colors(@values); 395 | } 396 | ''', r''' 397 | var-values: #f00, #0f0, #00f; 398 | .primary { 399 | color: #f00; 400 | background-color: #0f0; 401 | border-color: #00f; 402 | }'''); 403 | 404 | compilePolyfillAndValidate(r''' 405 | @mixin colors(@text, @background, @border) { 406 | color: @text; 407 | background-color: @background; 408 | border-color: @border; 409 | } 410 | 411 | @values: #ff0000, #00ff00, #0000ff; 412 | .primary { 413 | @include colors(@values); 414 | } 415 | ''', r''' 416 | .primary { 417 | color: #f00; 418 | background-color: #0f0; 419 | border-color: #00f; 420 | }'''); 421 | } 422 | 423 | void badDeclarationInclude() { 424 | final errors = []; 425 | final input = r''' 426 | @mixin a { 427 | #foo-id { 428 | color: red; 429 | } 430 | } 431 | @mixin b { 432 | span { 433 | border: 2px dashed red; 434 | @include a; 435 | } 436 | } 437 | @include b; 438 | '''; 439 | 440 | compileCss(input, errors: errors, opts: options); 441 | 442 | expect(errors.isNotEmpty, true); 443 | expect(errors.length, 1, reason: errors.toString()); 444 | var error = errors[0]; 445 | expect(error.message, 'Using top-level mixin a as a declaration'); 446 | expect(error.span!.start.line, 8); 447 | expect(error.span!.end.offset, 105); 448 | } 449 | 450 | void badTopInclude() { 451 | final errors = []; 452 | final input = r''' 453 | @mixin b { 454 | color: red; 455 | } 456 | 457 | @mixin a { 458 | span { 459 | border: 2px dashed red; 460 | } 461 | @include b; 462 | } 463 | 464 | @include a; 465 | '''; 466 | 467 | compileCss(input, errors: errors, opts: options); 468 | 469 | expect(errors.length, 1, reason: errors.toString()); 470 | var error = errors[0]; 471 | expect(error.message, 'Using declaration mixin b as top-level mixin'); 472 | expect(error.span!.start.line, 8); 473 | expect(error.span!.end.offset, 90); 474 | } 475 | 476 | void emptyMixin() { 477 | final errors = []; 478 | final input = r''' 479 | @mixin a { 480 | } 481 | @mixin b { 482 | border: 2px dashed red; 483 | @include a; 484 | } 485 | div { 486 | @include b; 487 | } 488 | '''; 489 | 490 | var generated = r''' 491 | div { 492 | border: 2px dashed #f00; 493 | }'''; 494 | 495 | var stylesheet = compileCss(input, errors: errors, opts: options); 496 | 497 | expect(errors.isEmpty, true, reason: errors.toString()); 498 | expect(prettyPrint(stylesheet), generated); 499 | } 500 | 501 | void undefinedTopLevel() { 502 | final errors = []; 503 | final input = r''' 504 | @mixin a { 505 | @include b; 506 | } 507 | @mixin b { 508 | span { 509 | border: 2px dashed red; 510 | } 511 | @include a; 512 | } 513 | 514 | @include b; 515 | 516 | '''; 517 | 518 | compileCss(input, errors: errors, opts: options); 519 | 520 | expect(errors.isNotEmpty, true); 521 | expect(errors.length, 1, reason: errors.toString()); 522 | var error = errors[0]; 523 | expect(error.message, 'Undefined mixin b'); 524 | expect(error.span!.start.line, 1); 525 | expect(error.span!.start.offset, 14); 526 | } 527 | 528 | void undefinedDeclaration() { 529 | final errors = []; 530 | final input = r''' 531 | @mixin a { 532 | @include b; 533 | } 534 | @mixin b { 535 | border: 2px dashed red; 536 | @include a; 537 | } 538 | div { 539 | @include b; 540 | } 541 | '''; 542 | 543 | compileCss(input, errors: errors, opts: options); 544 | 545 | expect(errors.isNotEmpty, true); 546 | expect(errors.length, 1, reason: errors.toString()); 547 | var error = errors[0]; 548 | expect(error.message, 'Undefined mixin b'); 549 | expect(error.span!.start.line, 1); 550 | expect(error.span!.start.offset, 14); 551 | } 552 | 553 | void includeGrammar() { 554 | compileAndValidate(r''' 555 | @mixin a { 556 | foo { color: red } 557 | } 558 | 559 | @mixin b { 560 | @include a; 561 | @include a; 562 | } 563 | 564 | @include b; 565 | ''', r''' 566 | foo { 567 | color: #f00; 568 | } 569 | foo { 570 | color: #f00; 571 | }'''); 572 | 573 | compileAndValidate(r''' 574 | @mixin a { 575 | color: red 576 | } 577 | 578 | foo { 579 | @include a; 580 | @include a 581 | } 582 | ''', r''' 583 | foo { 584 | color: #f00; 585 | color: #f00; 586 | }'''); 587 | 588 | var errors = []; 589 | var input = r''' 590 | @mixin a { 591 | foo { color: red } 592 | } 593 | 594 | @mixin b { 595 | @include a 596 | @include a 597 | } 598 | 599 | @include b 600 | '''; 601 | 602 | compileCss(input, errors: errors, opts: options); 603 | 604 | expect(errors, hasLength(4)); 605 | expect(errors[0].describe, '7:4:parsing error expected ;'); 606 | expect(errors[1].describe, '8:1:expected :, but found }'); 607 | expect(errors[2].describe, '10:11:expected }, but found end of file'); 608 | expect(errors[3].describe, '6:4:Using top-level mixin a as a declaration'); 609 | } 610 | 611 | void main() { 612 | group('Basic mixin', () { 613 | test('include grammar', includeGrammar); 614 | test('empty mixin content', emptyMixin); 615 | }); 616 | 617 | group('Top-level mixin', () { 618 | test('simple mixin', topLevelMixin); 619 | test('mixin with two @includes', topLevelMixinTwoIncludes); 620 | test('multi rulesets', topLevelMixinMultiRulesets); 621 | test('multi rulesets and nesting', topLevelMixinDeeplyNestedRulesets); 622 | test('selector groups', topLevelMixinSelectors); 623 | }); 624 | 625 | group('Declaration mixin', () { 626 | test('simple', declSimpleMixin); 627 | test('with two @includes', declMixinTwoIncludes); 628 | test('with includes', declMixinNestedIncludes); 629 | test('with deeper nesting', declMixinDeeperNestedIncludes); 630 | }); 631 | 632 | group('Mixin arguments', () { 633 | test('simple arg', mixinArg); 634 | test('many args', mixinArgs, skip: 'hangs at runtime'); 635 | test('multiple args and var decls as args', mixinManyArgs); 636 | }); 637 | 638 | group('Mixin warnings', () { 639 | test('undefined top-level', undefinedTopLevel); 640 | test('undefined declaration', undefinedDeclaration); 641 | test('detect bad top-level as declaration', badDeclarationInclude); 642 | test('detect bad declaration as top-level', badTopInclude); 643 | }); 644 | } 645 | -------------------------------------------------------------------------------- /test/nested_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/src/messages.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import 'testing.dart'; 9 | 10 | void compileAndValidate(String input, String generated) { 11 | var errors = []; 12 | var stylesheet = compileCss(input, errors: errors, opts: simpleOptions); 13 | expect(errors.isEmpty, true, reason: errors.toString()); 14 | expect(prettyPrint(stylesheet), generated); 15 | } 16 | 17 | void selectorVariations() { 18 | final input1 = r'''html { color: red; }'''; 19 | final generated1 = r'''html { 20 | color: #f00; 21 | }'''; 22 | compileAndValidate(input1, generated1); 23 | 24 | final input2 = r'''button { span { height: 200 } }'''; 25 | final generated2 = r'''button { 26 | } 27 | button span { 28 | height: 200; 29 | }'''; 30 | compileAndValidate(input2, generated2); 31 | 32 | final input3 = r'''div { color: red; } button { span { height: 200 } }'''; 33 | final generated3 = r'''div { 34 | color: #f00; 35 | } 36 | button { 37 | } 38 | button span { 39 | height: 200; 40 | }'''; 41 | compileAndValidate(input3, generated3); 42 | 43 | final input4 = r'''#header { color: red; h1 { font-size: 26px; } }'''; 44 | final generated4 = r'''#header { 45 | color: #f00; 46 | } 47 | #header h1 { 48 | font-size: 26px; 49 | }'''; 50 | compileAndValidate(input4, generated4); 51 | 52 | final input5 = r''' 53 | #header { 54 | color: red; 55 | h1 { font-size: 26px; } 56 | background-color: blue; 57 | }'''; 58 | final generated5 = r'''#header { 59 | color: #f00; 60 | background-color: #00f; 61 | } 62 | #header h1 { 63 | font-size: 26px; 64 | }'''; 65 | compileAndValidate(input5, generated5); 66 | 67 | final input6 = r'''html { body {color: red; }}'''; 68 | final generated6 = r'''html { 69 | } 70 | html body { 71 | color: #f00; 72 | }'''; 73 | compileAndValidate(input6, generated6); 74 | 75 | final input7 = r'''html body {color: red; }'''; 76 | final generated7 = r'''html body { 77 | color: #f00; 78 | }'''; 79 | compileAndValidate(input7, generated7); 80 | 81 | final input8 = r''' 82 | html, body { color: red; } 83 | button { height: 200 } 84 | body { width: 300px; }'''; 85 | final generated8 = r'''html, body { 86 | color: #f00; 87 | } 88 | button { 89 | height: 200; 90 | } 91 | body { 92 | width: 300px; 93 | }'''; 94 | compileAndValidate(input8, generated8); 95 | 96 | final input9 = ''' 97 | html, body { 98 | color: red; 99 | button { height: 200 } 100 | div { width: 300px; } 101 | }'''; 102 | final generated9 = r'''html, body { 103 | color: #f00; 104 | } 105 | html button, body button { 106 | height: 200; 107 | } 108 | html div, body div { 109 | width: 300px; 110 | }'''; 111 | compileAndValidate(input9, generated9); 112 | 113 | final input10 = ''' 114 | html { 115 | color: red; 116 | button, div { height: 200 } 117 | body { width: 300px; } 118 | }'''; 119 | final generated10 = r'''html { 120 | color: #f00; 121 | } 122 | html button, html div { 123 | height: 200; 124 | } 125 | html body { 126 | width: 300px; 127 | }'''; 128 | compileAndValidate(input10, generated10); 129 | 130 | final input11 = ''' 131 | html, body { 132 | color: red; 133 | button, div { height: 200 } 134 | table { width: 300px; } 135 | }'''; 136 | final generated11 = r'''html, body { 137 | color: #f00; 138 | } 139 | html button, body button, html div, body div { 140 | height: 200; 141 | } 142 | html table, body table { 143 | width: 300px; 144 | }'''; 145 | compileAndValidate(input11, generated11); 146 | 147 | final input12 = ''' 148 | html, body { 149 | color: red; 150 | button, div { 151 | span, a, ul { height: 200 } 152 | } 153 | table { width: 300px; } 154 | }'''; 155 | final generated12 = r'''html, body { 156 | color: #f00; 157 | } 158 | ''' 159 | 'html button span, body button span, html div span, body div span, ' 160 | 'html button a, body button a, html div a, body div a, html button ul, ' 161 | r'''body button ul, html div ul, body div ul { 162 | height: 200; 163 | } 164 | html table, body table { 165 | width: 300px; 166 | }'''; 167 | compileAndValidate(input12, generated12); 168 | 169 | final input13 = r''' 170 | #header { 171 | div { 172 | width: 100px; 173 | a { height: 200px; } 174 | } 175 | color: blue; 176 | } 177 | span { color: #1f1f1f; } 178 | '''; 179 | final generated13 = r'''#header { 180 | color: #00f; 181 | } 182 | #header div { 183 | width: 100px; 184 | } 185 | #header div a { 186 | height: 200px; 187 | } 188 | span { 189 | color: #1f1f1f; 190 | }'''; 191 | compileAndValidate(input13, generated13); 192 | } 193 | 194 | void simpleNest() { 195 | final input = ''' 196 | div span { color: green; } 197 | #header { 198 | color: red; 199 | h1 { 200 | font-size: 26px; 201 | font-weight: bold; 202 | } 203 | p { 204 | font-size: 12px; 205 | a { 206 | text-decoration: none; 207 | } 208 | } 209 | background-color: blue; 210 | } 211 | div > span[attr="foo"] { color: yellow; } 212 | '''; 213 | 214 | final generated = r'''div span { 215 | color: #008000; 216 | } 217 | #header { 218 | color: #f00; 219 | background-color: #00f; 220 | } 221 | #header h1 { 222 | font-size: 26px; 223 | font-weight: bold; 224 | } 225 | #header p { 226 | font-size: 12px; 227 | } 228 | #header p a { 229 | text-decoration: none; 230 | } 231 | div > span[attr="foo"] { 232 | color: #ff0; 233 | }'''; 234 | compileAndValidate(input, generated); 235 | } 236 | 237 | void complexNest() { 238 | final input = ''' 239 | @font-face { font-family: arial; } 240 | div { color: #f0f0f0; } 241 | #header + div { 242 | color: url(abc.png); 243 | *[attr="bar"] { 244 | font-size: 26px; 245 | font-weight: bold; 246 | } 247 | p~ul { 248 | font-size: 12px; 249 | :not(p) { 250 | text-decoration: none; 251 | div > span[attr="foo"] { color: yellow; } 252 | } 253 | } 254 | background-color: blue; 255 | span { 256 | color: red; 257 | .one { color: blue; } 258 | .two { color: green; } 259 | .three { color: yellow; } 260 | .four { 261 | .four-1 { background-color: #00000f; } 262 | .four-2 { background-color: #0000ff; } 263 | .four-3 { background-color: #000fff; } 264 | .four-4 { 265 | height: 44px; 266 | .four-4-1 { height: 10px; } 267 | .four-4-2 { height: 20px; } 268 | .four-4-3 { height: 30px; } 269 | width: 44px; 270 | } 271 | } 272 | } 273 | } 274 | span { color: #1f1f2f; } 275 | '''; 276 | 277 | final generated = r'''@font-face { 278 | font-family: arial; 279 | } 280 | div { 281 | color: #f0f0f0; 282 | } 283 | #header + div { 284 | color: url("abc.png"); 285 | background-color: #00f; 286 | } 287 | #header + div *[attr="bar"] { 288 | font-size: 26px; 289 | font-weight: bold; 290 | } 291 | #header + div p ~ ul { 292 | font-size: 12px; 293 | } 294 | #header + div p ~ ul :not(p) { 295 | text-decoration: none; 296 | } 297 | #header + div p ~ ul :not(p) div > span[attr="foo"] { 298 | color: #ff0; 299 | } 300 | #header + div span { 301 | color: #f00; 302 | } 303 | #header + div span .one { 304 | color: #00f; 305 | } 306 | #header + div span .two { 307 | color: #008000; 308 | } 309 | #header + div span .three { 310 | color: #ff0; 311 | } 312 | #header + div span .four .four-1 { 313 | background-color: #00000f; 314 | } 315 | #header + div span .four .four-2 { 316 | background-color: #00f; 317 | } 318 | #header + div span .four .four-3 { 319 | background-color: #000fff; 320 | } 321 | #header + div span .four .four-4 { 322 | height: 44px; 323 | width: 44px; 324 | } 325 | #header + div span .four .four-4 .four-4-1 { 326 | height: 10px; 327 | } 328 | #header + div span .four .four-4 .four-4-2 { 329 | height: 20px; 330 | } 331 | #header + div span .four .four-4 .four-4-3 { 332 | height: 30px; 333 | } 334 | span { 335 | color: #1f1f2f; 336 | }'''; 337 | 338 | compileAndValidate(input, generated); 339 | } 340 | 341 | void mediaNesting() { 342 | final input = r''' 343 | @media screen and (-webkit-min-device-pixel-ratio:0) { 344 | #toggle-all { 345 | image: url(test.jpb); 346 | div, table { 347 | background: none; 348 | a { width: 100px; } 349 | } 350 | color: red; 351 | } 352 | } 353 | '''; 354 | final generated = r''' 355 | @media screen AND (-webkit-min-device-pixel-ratio:0) { 356 | #toggle-all { 357 | image: url("test.jpb"); 358 | color: #f00; 359 | } 360 | #toggle-all div, #toggle-all table { 361 | background: none; 362 | } 363 | #toggle-all div a, #toggle-all table a { 364 | width: 100px; 365 | } 366 | }'''; 367 | 368 | compileAndValidate(input, generated); 369 | } 370 | 371 | void simpleThis() { 372 | final input = '''#header { 373 | h1 { 374 | font-size: 26px; 375 | font-weight: bold; 376 | } 377 | p { font-size: 12px; 378 | a { text-decoration: none; 379 | &:hover { border-width: 1px } 380 | } 381 | } 382 | } 383 | '''; 384 | 385 | final generated = r'''#header { 386 | } 387 | #header h1 { 388 | font-size: 26px; 389 | font-weight: bold; 390 | } 391 | #header p { 392 | font-size: 12px; 393 | } 394 | #header p a { 395 | text-decoration: none; 396 | } 397 | #header p a:hover { 398 | border-width: 1px; 399 | }'''; 400 | 401 | compileAndValidate(input, generated); 402 | } 403 | 404 | void complexThis() { 405 | final input1 = r''' 406 | .light { 407 | .leftCol { 408 | .textLink { 409 | color: fooL1; 410 | &:hover { color: barL1;} 411 | } 412 | .picLink { 413 | background-image: url(/fooL1.jpg); 414 | &:hover { background-image: url(/barL1.jpg);} 415 | } 416 | .textWithIconLink { 417 | color: fooL2; 418 | background-image: url(/fooL2.jpg); 419 | &:hover { color: barL2; background-image: url(/barL2.jpg);} 420 | } 421 | } 422 | }'''; 423 | 424 | final generated1 = r'''.light { 425 | } 426 | .light .leftCol .textLink { 427 | color: fooL1; 428 | } 429 | .light .leftCol .textLink:hover { 430 | color: barL1; 431 | } 432 | .light .leftCol .picLink { 433 | background-image: url("/fooL1.jpg"); 434 | } 435 | .light .leftCol .picLink:hover { 436 | background-image: url("/barL1.jpg"); 437 | } 438 | .light .leftCol .textWithIconLink { 439 | color: fooL2; 440 | background-image: url("/fooL2.jpg"); 441 | } 442 | .light .leftCol .textWithIconLink:hover { 443 | color: barL2; 444 | background-image: url("/barL2.jpg"); 445 | }'''; 446 | 447 | compileAndValidate(input1, generated1); 448 | 449 | final input2 = r''' 450 | .textLink { 451 | .light .leftCol & { 452 | color: fooL1; 453 | &:hover { color: barL1; } 454 | } 455 | .light .rightCol & { 456 | color: fooL3; 457 | &:hover { color: barL3; } 458 | } 459 | }'''; 460 | 461 | final generated2 = r''' 462 | .textLink { 463 | } 464 | .light .leftCol .textLink { 465 | color: fooL1; 466 | } 467 | .light .leftCol .textLink:hover { 468 | color: barL1; 469 | } 470 | .light .rightCol .textLink { 471 | color: fooL3; 472 | } 473 | .light .rightCol .textLink:hover { 474 | color: barL3; 475 | }'''; 476 | 477 | compileAndValidate(input2, generated2); 478 | } 479 | 480 | void variationsThis() { 481 | final input1 = r''' 482 | .textLink { 483 | a { 484 | light .leftCol & { 485 | color: red; 486 | } 487 | } 488 | }'''; 489 | final generated1 = r'''.textLink { 490 | } 491 | light .leftCol .textLink a { 492 | color: #f00; 493 | }'''; 494 | 495 | compileAndValidate(input1, generated1); 496 | 497 | final input2 = r'''.textLink { 498 | a { 499 | & light .leftCol & { 500 | color: red; 501 | } 502 | } 503 | }'''; 504 | final generated2 = r'''.textLink { 505 | } 506 | .textLink a light .leftCol .textLink a { 507 | color: #f00; 508 | }'''; 509 | compileAndValidate(input2, generated2); 510 | 511 | final input3 = r''' 512 | .textLink { 513 | a { 514 | & light .leftCol { color: red; } 515 | } 516 | }'''; 517 | final generated3 = r'''.textLink { 518 | } 519 | .textLink a light .leftCol { 520 | color: #f00; 521 | }'''; 522 | compileAndValidate(input3, generated3); 523 | 524 | final input4 = r''' 525 | .textLink { 526 | a { 527 | & light .leftCol { color: red; } 528 | &:hover { width: 100px; } 529 | } 530 | }'''; 531 | final generated4 = r'''.textLink { 532 | } 533 | .textLink a light .leftCol { 534 | color: #f00; 535 | } 536 | .textLink a:hover { 537 | width: 100px; 538 | }'''; 539 | compileAndValidate(input4, generated4); 540 | 541 | final input5 = r'''.textLink { a { &:hover { color: red; } } }'''; 542 | final generated5 = r'''.textLink { 543 | } 544 | .textLink a:hover { 545 | color: #f00; 546 | }'''; 547 | 548 | compileAndValidate(input5, generated5); 549 | 550 | final input6 = r'''.textLink { &:hover { color: red; } }'''; 551 | final generated6 = r'''.textLink { 552 | } 553 | .textLink:hover { 554 | color: #f00; 555 | }'''; 556 | compileAndValidate(input6, generated6); 557 | 558 | final input7 = r'''.textLink { a { & + & { color: red; } } }'''; 559 | final generated7 = r'''.textLink { 560 | } 561 | .textLink a + .textLink a { 562 | color: #f00; 563 | }'''; 564 | compileAndValidate(input7, generated7); 565 | 566 | final input8 = r'''.textLink { a { & { color: red; } } }'''; 567 | final generated8 = r'''.textLink { 568 | } 569 | .textLink a { 570 | color: #f00; 571 | }'''; 572 | compileAndValidate(input8, generated8); 573 | 574 | final input9 = r'''.textLink { a { & ~ & { color: red; } } }'''; 575 | final generated9 = r'''.textLink { 576 | } 577 | .textLink a ~ .textLink a { 578 | color: #f00; 579 | }'''; 580 | compileAndValidate(input9, generated9); 581 | 582 | final input10 = r'''.textLink { a { & & { color: red; } } }'''; 583 | final generated10 = r'''.textLink { 584 | } 585 | .textLink a .textLink a { 586 | color: #f00; 587 | }'''; 588 | compileAndValidate(input10, generated10); 589 | } 590 | 591 | void thisCombinator() { 592 | var input = r''' 593 | .btn { 594 | color: red; 595 | } 596 | .btn + .btn { 597 | margin-left: 5px; 598 | } 599 | input.second { 600 | & + label { 601 | color: blue; 602 | } 603 | } 604 | '''; 605 | 606 | var generated = r'''.btn { 607 | color: #f00; 608 | } 609 | .btn + .btn { 610 | margin-left: 5px; 611 | } 612 | input.second { 613 | } 614 | input.second + label { 615 | color: #00f; 616 | }'''; 617 | 618 | compileAndValidate(input, generated); 619 | } 620 | 621 | void main() { 622 | test('Selector and Nested Variations', selectorVariations); 623 | test('Simple nesting', simpleNest); 624 | test('Complex nesting', complexNest); 625 | test('@media nesting', mediaNesting); 626 | test('Simple &', simpleThis); 627 | test('Variations &', variationsThis); 628 | test('Complex &', complexThis); 629 | test('& with + selector', thisCombinator); 630 | } 631 | -------------------------------------------------------------------------------- /test/repros_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/parser.dart'; 6 | import 'package:csslib/visitor.dart'; 7 | import 'package:test/test.dart'; 8 | 9 | const options = PreprocessorOptions( 10 | useColors: false, 11 | warningsAsErrors: true, 12 | inputFile: 'memory', 13 | ); 14 | 15 | void main() { 16 | // Repro test for https://github.com/dart-lang/csslib/issues/136. 17 | test('repro_136', () { 18 | final errors = []; 19 | 20 | final css = ''' 21 | .hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current), 22 | .hero.is-white strong { 23 | color: inherit; 24 | } 25 | '''; 26 | final stylesheet = parse(css, errors: errors, options: options); 27 | expect(stylesheet.topLevels, hasLength(1)); 28 | var ruleset = stylesheet.topLevels.first as RuleSet; 29 | 30 | // This should be length 2. 31 | expect(ruleset.selectorGroup!.selectors, hasLength(1)); 32 | 33 | // This test is expected to start to fail once we better support pseudo 34 | // selectors. 35 | // This should be empty. 36 | expect(errors, hasLength(3)); 37 | }); 38 | 39 | // Repro test for https://github.com/dart-lang/csslib/issues/171. 40 | test('repro_171', () { 41 | final errors = []; 42 | var stylesheet = 43 | parse('body { width: 1000 px; }', errors: errors, options: options); 44 | expect(errors, isEmpty); 45 | 46 | expect(stylesheet.topLevels, hasLength(1)); 47 | var ruleset = stylesheet.topLevels.first as RuleSet; 48 | expect(ruleset.selectorGroup!.selectors, hasLength(1)); 49 | expect(ruleset.declarationGroup.declarations, hasLength(1)); 50 | 51 | errors.clear(); 52 | stylesheet = 53 | parse('body { width: 1000px; }', errors: errors, options: options); 54 | expect(errors, isEmpty); 55 | 56 | expect(stylesheet.topLevels, hasLength(1)); 57 | ruleset = stylesheet.topLevels.first as RuleSet; 58 | expect(ruleset.selectorGroup!.selectors, hasLength(1)); 59 | expect(ruleset.declarationGroup.declarations, hasLength(1)); 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /test/selector_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/parser.dart'; 6 | import 'package:term_glyph/term_glyph.dart' as glyph; 7 | import 'package:test/test.dart'; 8 | 9 | import 'testing.dart'; 10 | 11 | void testSelectorSuccesses() { 12 | var errors = []; 13 | var selectorAst = selector('#div .foo', errors: errors); 14 | expect(errors.isEmpty, true, reason: errors.toString()); 15 | expect('#div .foo', compactOutput(selectorAst)); 16 | 17 | // Valid selectors for class names. 18 | selectorAst = selector('.foo', errors: errors..clear()); 19 | expect(errors.isEmpty, true, reason: errors.toString()); 20 | expect('.foo', compactOutput(selectorAst)); 21 | 22 | selectorAst = selector('.foobar .xyzzy', errors: errors..clear()); 23 | expect(errors.isEmpty, true, reason: errors.toString()); 24 | expect('.foobar .xyzzy', compactOutput(selectorAst)); 25 | 26 | selectorAst = selector('.foobar .a-story .xyzzy', errors: errors..clear()); 27 | expect(errors.isEmpty, true, reason: errors.toString()); 28 | expect('.foobar .a-story .xyzzy', compactOutput(selectorAst)); 29 | 30 | selectorAst = 31 | selector('.foobar .xyzzy .a-story .b-story', errors: errors..clear()); 32 | expect(errors.isEmpty, true, reason: errors.toString()); 33 | expect('.foobar .xyzzy .a-story .b-story', compactOutput(selectorAst)); 34 | 35 | // Valid selectors for element IDs. 36 | selectorAst = selector('#id1', errors: errors..clear()); 37 | expect(errors.isEmpty, true, reason: errors.toString()); 38 | expect('#id1', compactOutput(selectorAst)); 39 | 40 | selectorAst = selector('#id-number-3', errors: errors..clear()); 41 | expect(errors.isEmpty, true, reason: errors.toString()); 42 | expect('#id-number-3', compactOutput(selectorAst)); 43 | 44 | selectorAst = selector('#_privateId', errors: errors..clear()); 45 | expect(errors.isEmpty, true, reason: errors.toString()); 46 | expect('#_privateId', compactOutput(selectorAst)); 47 | 48 | selectorAst = selector(':host', errors: errors..clear()); 49 | expect(errors.isEmpty, true, reason: errors.toString()); 50 | expect(compactOutput(selectorAst), ':host'); 51 | 52 | selectorAst = selector(':host(.foo)', errors: errors..clear()); 53 | expect(errors.isEmpty, true, reason: errors.toString()); 54 | expect(compactOutput(selectorAst), ':host(.foo)'); 55 | 56 | selectorAst = selector(':host-context(.foo)', errors: errors..clear()); 57 | expect(errors.isEmpty, true, reason: errors.toString()); 58 | expect(compactOutput(selectorAst), ':host-context(.foo)'); 59 | } 60 | 61 | // TODO(terry): Move this failure case to a failure_test.dart when the analyzer 62 | // and validator exit then they'll be a bunch more checks. 63 | void testSelectorFailures() { 64 | var errors = []; 65 | 66 | // Test for invalid class name (can't start with number). 67 | selector('.foobar .1a-story .xyzzy', errors: errors); 68 | expect(errors.isEmpty, false); 69 | expect( 70 | errors[0].toString(), 71 | 'error on line 1, column 9: name must start with a alpha character, but ' 72 | 'found a number\n' 73 | ' ,\n' 74 | '1 | .foobar .1a-story .xyzzy\n' 75 | ' | ^^\n' 76 | ' \''); 77 | 78 | selector(':host()', errors: errors..clear()); 79 | expect( 80 | errors.first.toString(), 81 | 'error on line 1, column 7: expected a selector argument, but found )\n' 82 | ' ,\n' 83 | '1 | :host()\n' 84 | ' | ^\n' 85 | ' \''); 86 | } 87 | 88 | void main() { 89 | glyph.ascii = true; 90 | test('Valid Selectors', testSelectorSuccesses); 91 | test('Invalid Selectors', testSelectorFailures); 92 | } 93 | -------------------------------------------------------------------------------- /test/testing.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | /// Common definitions used for setting up the test environment. 6 | library; 7 | 8 | import 'package:csslib/parser.dart'; 9 | import 'package:csslib/visitor.dart'; 10 | 11 | export 'package:csslib/src/preprocessor_options.dart'; 12 | 13 | const PreprocessorOptions simpleOptionsWithCheckedAndWarningsAsErrors = 14 | PreprocessorOptions( 15 | useColors: false, 16 | checked: true, 17 | warningsAsErrors: true, 18 | inputFile: 'memory', 19 | ); 20 | 21 | const PreprocessorOptions simpleOptions = 22 | PreprocessorOptions(useColors: false, inputFile: 'memory'); 23 | 24 | const PreprocessorOptions options = PreprocessorOptions( 25 | useColors: false, warningsAsErrors: true, inputFile: 'memory'); 26 | 27 | /// Spin-up CSS parser in checked mode to detect any problematic CSS. Normally, 28 | /// CSS will allow any property/value pairs regardless of validity; all of our 29 | /// tests (by default) will ensure that the CSS is really valid. 30 | StyleSheet parseCss(String cssInput, 31 | {List? errors, PreprocessorOptions? opts}) => 32 | parse(cssInput, 33 | errors: errors, 34 | options: opts ?? simpleOptionsWithCheckedAndWarningsAsErrors); 35 | 36 | /// Spin-up CSS parser in checked mode to detect any problematic CSS. Normally, 37 | /// CSS will allow any property/value pairs regardless of validity; all of our 38 | /// tests (by default) will ensure that the CSS is really valid. 39 | StyleSheet compileCss(String cssInput, 40 | {List? errors, 41 | PreprocessorOptions? opts, 42 | bool polyfill = false, 43 | List? includes}) => 44 | compile(cssInput, 45 | errors: errors, 46 | options: opts ?? simpleOptionsWithCheckedAndWarningsAsErrors, 47 | polyfill: polyfill, 48 | includes: includes); 49 | 50 | StyleSheet polyFillCompileCss(String input, 51 | {List? errors, PreprocessorOptions? opts}) => 52 | compileCss(input, errors: errors, polyfill: true, opts: opts); 53 | 54 | /// Pretty printer for CSS. 55 | String prettyPrint(StyleSheet ss) { 56 | // Walk the tree testing basic Visitor class. 57 | walkTree(ss); 58 | return (CssPrinter()..visitTree(ss, pretty: true)).toString(); 59 | } 60 | 61 | /// Helper function to emit compact (non-pretty printed) CSS for suite test 62 | /// comparisons. Spaces, new lines, etc. are reduced for easier comparisons of 63 | /// expected suite test results. 64 | String compactOutput(StyleSheet ss) { 65 | walkTree(ss); 66 | return (CssPrinter()..visitTree(ss, pretty: false)).toString(); 67 | } 68 | 69 | String printExpressions(Expressions node) { 70 | var printer = CssPrinter(); 71 | // TODO: It would be nice if TreeNode had an `accept` method. 72 | printer.visitExpressions(node); 73 | return printer.toString(); 74 | } 75 | 76 | /// Walks the style sheet tree does nothing; insures the basic walker works. 77 | void walkTree(StyleSheet ss) { 78 | Visitor().visitTree(ss); 79 | } 80 | 81 | String dumpTree(StyleSheet ss) => treeToDebugString(ss); 82 | -------------------------------------------------------------------------------- /test/third_party_samples_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | @TestOn('vm') 6 | library; 7 | 8 | import 'dart:io'; 9 | 10 | import 'package:csslib/parser.dart'; 11 | import 'package:path/path.dart' as path; 12 | import 'package:test/test.dart'; 13 | 14 | const testOptions = PreprocessorOptions( 15 | useColors: false, 16 | checked: false, 17 | warningsAsErrors: true, 18 | inputFile: 'memory', 19 | ); 20 | 21 | void testCSSFile(File cssFile) { 22 | final errors = []; 23 | final css = cssFile.readAsStringSync(); 24 | final stylesheet = parse(css, errors: errors, options: testOptions); 25 | 26 | expect(stylesheet, isNotNull); 27 | expect(errors, isEmpty, reason: errors.toString()); 28 | } 29 | 30 | void main() async { 31 | // Iterate over all sub-folders of third_party, 32 | // and then all css files in those. 33 | final thirdParty = path.join(Directory.current.path, 'third_party'); 34 | for (var entity in Directory(thirdParty).listSync()) { 35 | if (await FileSystemEntity.isDirectory(entity.path)) { 36 | for (var element in Directory(entity.path).listSync()) { 37 | if (element is File && element.uri.pathSegments.last.endsWith('.css')) { 38 | test(element.uri.pathSegments.last, () => testCSSFile(element)); 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/visitor_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:csslib/src/messages.dart'; 6 | import 'package:csslib/visitor.dart'; 7 | import 'package:test/test.dart'; 8 | 9 | import 'testing.dart'; 10 | 11 | class ClassVisitor extends Visitor { 12 | final List expectedClasses; 13 | final foundClasses = {}; 14 | 15 | ClassVisitor(this.expectedClasses); 16 | 17 | @override 18 | void visitClassSelector(ClassSelector node) { 19 | foundClasses.add(node.name); 20 | } 21 | 22 | bool get matches { 23 | var match = true; 24 | for (var value in foundClasses) { 25 | match = match && expectedClasses.contains(value); 26 | } 27 | for (var value in expectedClasses) { 28 | match = match && foundClasses.contains(value); 29 | } 30 | 31 | return match; 32 | } 33 | } 34 | 35 | void testClassVisitors() { 36 | var errors = []; 37 | var in1 = '.foobar { }'; 38 | 39 | var s = parseCss(in1, errors: errors); 40 | 41 | expect(errors.isEmpty, true, reason: errors.toString()); 42 | 43 | var clsVisits = ClassVisitor(['foobar'])..visitTree(s); 44 | expect(clsVisits.matches, true); 45 | 46 | in1 = ''' 47 | .foobar1 { } 48 | .xyzzy .foo #my-div { color: red; } 49 | div.hello { font: arial; } 50 | '''; 51 | 52 | s = parseCss(in1, errors: errors..clear(), opts: simpleOptions); 53 | 54 | expect(errors.isEmpty, true, reason: errors.toString()); 55 | 56 | clsVisits = ClassVisitor(['foobar1', 'xyzzy', 'foo', 'hello'])..visitTree(s); 57 | expect(clsVisits.matches, true); 58 | 59 | expect(prettyPrint(s), r''' 60 | .foobar1 { 61 | } 62 | .xyzzy .foo #my-div { 63 | color: #f00; 64 | } 65 | div.hello { 66 | font: arial; 67 | }'''); 68 | } 69 | 70 | class PolyfillEmitter extends CssPrinter { 71 | final String _prefix; 72 | 73 | PolyfillEmitter(this._prefix); 74 | 75 | @override 76 | void visitClassSelector(ClassSelector node) { 77 | emit('.${_prefix}_${node.name}'); 78 | } 79 | } 80 | 81 | String polyfillPrint(String prefix, StyleSheet ss) => 82 | (PolyfillEmitter(prefix)..visitTree(ss, pretty: true)).toString(); 83 | 84 | void testPolyFill() { 85 | var errors = []; 86 | final input = r''' 87 | .foobar { } 88 | div.xyzzy { } 89 | #foo .foo .bar .foobar { } 90 | '''; 91 | 92 | final generated = r''' 93 | .myComponent_foobar { 94 | } 95 | div.myComponent_xyzzy { 96 | } 97 | #foo .myComponent_foo .myComponent_bar .myComponent_foobar { 98 | }'''; 99 | 100 | var s = parseCss(input, errors: errors); 101 | expect(errors.isEmpty, true, reason: errors.toString()); 102 | 103 | final emitted = polyfillPrint('myComponent', s); 104 | expect(emitted, generated); 105 | } 106 | 107 | void main() { 108 | test('Class Visitors', testClassVisitors); 109 | test('Polyfill', testPolyFill); 110 | test('pretty-print', testPrettyPrint); 111 | } 112 | 113 | void testPrettyPrint() { 114 | final input = ''' 115 | .good { color: red; }@media screen { .better { color: blue; } }.best { color: green }'''; 116 | 117 | var styleSheet = parseCss(input); 118 | 119 | // pretty print 120 | expect(prettyPrint(styleSheet), ''' 121 | .good { 122 | color: #f00; 123 | } 124 | @media screen { 125 | .better { 126 | color: #00f; 127 | } 128 | } 129 | .best { 130 | color: #008000; 131 | }'''); 132 | 133 | // compact output 134 | expect(compactOutput(styleSheet), ''' 135 | .good{color:red}@media screen{.better{color:blue}}.best{color:green}'''); 136 | } 137 | -------------------------------------------------------------------------------- /third_party/README.md: -------------------------------------------------------------------------------- 1 | This folder contains resources from third parties. 2 | See each subfolder for details. 3 | -------------------------------------------------------------------------------- /third_party/base/README.md: -------------------------------------------------------------------------------- 1 | This folder contains a sample css file from the open-source project 2 | https://github.com/getbase/base. 3 | 4 | This code was included under the 5 | [MIT Open Source](https://opensource.org/licenses/MIT) license. -------------------------------------------------------------------------------- /third_party/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2021 Twitter, Inc. 4 | Copyright (c) 2011-2021 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /third_party/bootstrap/README.md: -------------------------------------------------------------------------------- 1 | This folder contains sample css files from the open-source project 2 | https://github.com/twbs/bootstrap. 3 | 4 | This code was included under the terms in the `LICENSE` file. -------------------------------------------------------------------------------- /third_party/foundation/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2011-2020 ZURB, Inc. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /third_party/foundation/README.md: -------------------------------------------------------------------------------- 1 | This folder contains sample css files from the open-source project 2 | https://github.com/foundation/foundation-sites. 3 | 4 | This code was included under the terms in the `LICENSE` file. -------------------------------------------------------------------------------- /third_party/html5-boilerplate/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) HTML5 Boilerplate 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /third_party/html5-boilerplate/README.md: -------------------------------------------------------------------------------- 1 | This folder contains sample css files from the open-source project 2 | https://github.com/h5bp/html5-boilerplate. 3 | 4 | This code was included under the terms in the `LICENSE.txt` file. -------------------------------------------------------------------------------- /third_party/html5-boilerplate/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } 350 | -------------------------------------------------------------------------------- /third_party/html5-boilerplate/style.css: -------------------------------------------------------------------------------- 1 | /*! HTML5 Boilerplate v8.0.0 | MIT License | https://html5boilerplate.com/ */ 2 | 3 | /* main.css 2.1.0 | MIT License | https://github.com/h5bp/main.css#readme */ 4 | /* 5 | * What follows is the result of much research on cross-browser styling. 6 | * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, 7 | * Kroc Camen, and the H5BP dev community and team. 8 | */ 9 | 10 | /* ========================================================================== 11 | Base styles: opinionated defaults 12 | ========================================================================== */ 13 | 14 | html { 15 | color: #222; 16 | font-size: 1em; 17 | line-height: 1.4; 18 | } 19 | 20 | /* 21 | * Remove text-shadow in selection highlight: 22 | * https://twitter.com/miketaylr/status/12228805301 23 | * 24 | * Vendor-prefixed and regular ::selection selectors cannot be combined: 25 | * https://stackoverflow.com/a/16982510/7133471 26 | * 27 | * Customize the background color to match your design. 28 | */ 29 | 30 | ::-moz-selection { 31 | background: #b3d4fc; 32 | text-shadow: none; 33 | } 34 | 35 | ::selection { 36 | background: #b3d4fc; 37 | text-shadow: none; 38 | } 39 | 40 | /* 41 | * A better looking default horizontal rule 42 | */ 43 | 44 | hr { 45 | display: block; 46 | height: 1px; 47 | border: 0; 48 | border-top: 1px solid #ccc; 49 | margin: 1em 0; 50 | padding: 0; 51 | } 52 | 53 | /* 54 | * Remove the gap between audio, canvas, iframes, 55 | * images, videos and the bottom of their containers: 56 | * https://github.com/h5bp/html5-boilerplate/issues/440 57 | */ 58 | 59 | audio, 60 | canvas, 61 | iframe, 62 | img, 63 | svg, 64 | video { 65 | vertical-align: middle; 66 | } 67 | 68 | /* 69 | * Remove default fieldset styles. 70 | */ 71 | 72 | fieldset { 73 | border: 0; 74 | margin: 0; 75 | padding: 0; 76 | } 77 | 78 | /* 79 | * Allow only vertical resizing of textareas. 80 | */ 81 | 82 | textarea { 83 | resize: vertical; 84 | } 85 | 86 | /* ========================================================================== 87 | Author's custom styles 88 | ========================================================================== */ 89 | 90 | /* ========================================================================== 91 | Helper classes 92 | ========================================================================== */ 93 | 94 | /* 95 | * Hide visually and from screen readers 96 | */ 97 | 98 | .hidden, 99 | [hidden] { 100 | display: none !important; 101 | } 102 | 103 | /* 104 | * Hide only visually, but have it available for screen readers: 105 | * https://snook.ca/archives/html_and_css/hiding-content-for-accessibility 106 | * 107 | * 1. For long content, line feeds are not interpreted as spaces and small width 108 | * causes content to wrap 1 word per line: 109 | * https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe 110 | */ 111 | 112 | .sr-only { 113 | border: 0; 114 | clip: rect(0, 0, 0, 0); 115 | height: 1px; 116 | margin: -1px; 117 | overflow: hidden; 118 | padding: 0; 119 | position: absolute; 120 | white-space: nowrap; 121 | width: 1px; 122 | /* 1 */ 123 | } 124 | 125 | /* 126 | * Extends the .sr-only class to allow the element 127 | * to be focusable when navigated to via the keyboard: 128 | * https://www.drupal.org/node/897638 129 | */ 130 | 131 | .sr-only.focusable:active, 132 | .sr-only.focusable:focus { 133 | clip: auto; 134 | height: auto; 135 | margin: 0; 136 | overflow: visible; 137 | position: static; 138 | white-space: inherit; 139 | width: auto; 140 | } 141 | 142 | /* 143 | * Hide visually and from screen readers, but maintain layout 144 | */ 145 | 146 | .invisible { 147 | visibility: hidden; 148 | } 149 | 150 | /* 151 | * Clearfix: contain floats 152 | * 153 | * For modern browsers 154 | * 1. The space content is one way to avoid an Opera bug when the 155 | * `contenteditable` attribute is included anywhere else in the document. 156 | * Otherwise it causes space to appear at the top and bottom of elements 157 | * that receive the `clearfix` class. 158 | * 2. The use of `table` rather than `block` is only necessary if using 159 | * `:before` to contain the top-margins of child elements. 160 | */ 161 | 162 | .clearfix::before, 163 | .clearfix::after { 164 | content: " "; 165 | display: table; 166 | } 167 | 168 | .clearfix::after { 169 | clear: both; 170 | } 171 | 172 | /* ========================================================================== 173 | EXAMPLE Media Queries for Responsive Design. 174 | These examples override the primary ('mobile first') styles. 175 | Modify as content requires. 176 | ========================================================================== */ 177 | 178 | @media only screen and (min-width: 35em) { 179 | /* Style adjustments for viewports that meet the condition */ 180 | } 181 | 182 | @media print, 183 | (-webkit-min-device-pixel-ratio: 1.25), 184 | (min-resolution: 1.25dppx), 185 | (min-resolution: 120dpi) { 186 | /* Style adjustments for high resolution devices */ 187 | } 188 | 189 | /* ========================================================================== 190 | Print styles. 191 | Inlined to avoid the additional HTTP request: 192 | https://www.phpied.com/delay-loading-your-print-css/ 193 | ========================================================================== */ 194 | 195 | @media print { 196 | *, 197 | *::before, 198 | *::after { 199 | background: #fff !important; 200 | color: #000 !important; 201 | /* Black prints faster */ 202 | box-shadow: none !important; 203 | text-shadow: none !important; 204 | } 205 | 206 | a, 207 | a:visited { 208 | text-decoration: underline; 209 | } 210 | 211 | a[href]::after { 212 | content: " (" attr(href) ")"; 213 | } 214 | 215 | abbr[title]::after { 216 | content: " (" attr(title) ")"; 217 | } 218 | 219 | /* 220 | * Don't show links that are fragment identifiers, 221 | * or use the `javascript:` pseudo protocol 222 | */ 223 | a[href^="#"]::after, 224 | a[href^="javascript:"]::after { 225 | content: ""; 226 | } 227 | 228 | pre { 229 | white-space: pre-wrap !important; 230 | } 231 | 232 | pre, 233 | blockquote { 234 | border: 1px solid #999; 235 | page-break-inside: avoid; 236 | } 237 | 238 | /* 239 | * Printing Tables: 240 | * https://web.archive.org/web/20180815150934/http://css-discuss.incutio.com/wiki/Printing_Tables 241 | */ 242 | thead { 243 | display: table-header-group; 244 | } 245 | 246 | tr, 247 | img { 248 | page-break-inside: avoid; 249 | } 250 | 251 | p, 252 | h2, 253 | h3 { 254 | orphans: 3; 255 | widows: 3; 256 | } 257 | 258 | h2, 259 | h3 { 260 | page-break-after: avoid; 261 | } 262 | } 263 | 264 | -------------------------------------------------------------------------------- /third_party/materialize/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2019 Materialize 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /third_party/materialize/README.md: -------------------------------------------------------------------------------- 1 | This folder contains sample css files from the open-source project 2 | https://github.com/Dogfalo/materialize. 3 | 4 | This code was included under the terms in the `LICENSE` file. -------------------------------------------------------------------------------- /third_party/mdc/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014-2020 Google, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /third_party/mdc/README.md: -------------------------------------------------------------------------------- 1 | This folder contains sample css files from the open-source project 2 | https://github.com/material-components/material-components-web. 3 | 4 | The generated .css files were retrieved from: 5 | https://unpkg.com/browse/material-components-web@12.0.0/dist/ 6 | 7 | This code was included under the terms in the `LICENSE` file. -------------------------------------------------------------------------------- /third_party/pure/LICENSE: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | ======================================== 3 | 4 | Copyright 2013 Yahoo! Inc. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the Yahoo! Inc. nor the 17 | names of its contributors may be used to endorse or promote products 18 | derived from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR ANY 24 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /third_party/pure/README.md: -------------------------------------------------------------------------------- 1 | This folder contains sample css files from the open-source project 2 | https://github.com/pure-css/pure/ 3 | 4 | This code was included under the terms in the `LICENSE` file. -------------------------------------------------------------------------------- /third_party/pure/main-grid.css: -------------------------------------------------------------------------------- 1 | @media screen and (min-width: 35.5em) { 2 | .u-sm-1, 3 | .u-sm-1-1, 4 | .u-sm-1-2, 5 | .u-sm-1-3, 6 | .u-sm-2-3, 7 | .u-sm-1-4, 8 | .u-sm-3-4, 9 | .u-sm-1-5, 10 | .u-sm-2-5, 11 | .u-sm-3-5, 12 | .u-sm-4-5, 13 | .u-sm-5-5, 14 | .u-sm-1-6, 15 | .u-sm-5-6, 16 | .u-sm-1-8, 17 | .u-sm-3-8, 18 | .u-sm-5-8, 19 | .u-sm-7-8, 20 | .u-sm-1-12, 21 | .u-sm-5-12, 22 | .u-sm-7-12, 23 | .u-sm-11-12, 24 | .u-sm-1-24, 25 | .u-sm-2-24, 26 | .u-sm-3-24, 27 | .u-sm-4-24, 28 | .u-sm-5-24, 29 | .u-sm-6-24, 30 | .u-sm-7-24, 31 | .u-sm-8-24, 32 | .u-sm-9-24, 33 | .u-sm-10-24, 34 | .u-sm-11-24, 35 | .u-sm-12-24, 36 | .u-sm-13-24, 37 | .u-sm-14-24, 38 | .u-sm-15-24, 39 | .u-sm-16-24, 40 | .u-sm-17-24, 41 | .u-sm-18-24, 42 | .u-sm-19-24, 43 | .u-sm-20-24, 44 | .u-sm-21-24, 45 | .u-sm-22-24, 46 | .u-sm-23-24, 47 | .u-sm-24-24 { 48 | display: inline-block; 49 | *display: inline; 50 | zoom: 1; 51 | letter-spacing: normal; 52 | word-spacing: normal; 53 | vertical-align: top; 54 | text-rendering: auto; 55 | } 56 | 57 | .u-sm-1-24 { 58 | width: 4.1667%; 59 | *width: 4.1357%; 60 | } 61 | 62 | .u-sm-1-12, 63 | .u-sm-2-24 { 64 | width: 8.3333%; 65 | *width: 8.3023%; 66 | } 67 | 68 | .u-sm-1-8, 69 | .u-sm-3-24 { 70 | width: 12.5000%; 71 | *width: 12.4690%; 72 | } 73 | 74 | .u-sm-1-6, 75 | .u-sm-4-24 { 76 | width: 16.6667%; 77 | *width: 16.6357%; 78 | } 79 | 80 | .u-sm-1-5 { 81 | width: 20%; 82 | *width: 19.9690%; 83 | } 84 | 85 | .u-sm-5-24 { 86 | width: 20.8333%; 87 | *width: 20.8023%; 88 | } 89 | 90 | .u-sm-1-4, 91 | .u-sm-6-24 { 92 | width: 25%; 93 | *width: 24.9690%; 94 | } 95 | 96 | .u-sm-7-24 { 97 | width: 29.1667%; 98 | *width: 29.1357%; 99 | } 100 | 101 | .u-sm-1-3, 102 | .u-sm-8-24 { 103 | width: 33.3333%; 104 | *width: 33.3023%; 105 | } 106 | 107 | .u-sm-3-8, 108 | .u-sm-9-24 { 109 | width: 37.5000%; 110 | *width: 37.4690%; 111 | } 112 | 113 | .u-sm-2-5 { 114 | width: 40%; 115 | *width: 39.9690%; 116 | } 117 | 118 | .u-sm-5-12, 119 | .u-sm-10-24 { 120 | width: 41.6667%; 121 | *width: 41.6357%; 122 | } 123 | 124 | .u-sm-11-24 { 125 | width: 45.8333%; 126 | *width: 45.8023%; 127 | } 128 | 129 | .u-sm-1-2, 130 | .u-sm-12-24 { 131 | width: 50%; 132 | *width: 49.9690%; 133 | } 134 | 135 | .u-sm-13-24 { 136 | width: 54.1667%; 137 | *width: 54.1357%; 138 | } 139 | 140 | .u-sm-7-12, 141 | .u-sm-14-24 { 142 | width: 58.3333%; 143 | *width: 58.3023%; 144 | } 145 | 146 | .u-sm-3-5 { 147 | width: 60%; 148 | *width: 59.9690%; 149 | } 150 | 151 | .u-sm-5-8, 152 | .u-sm-15-24 { 153 | width: 62.5000%; 154 | *width: 62.4690%; 155 | } 156 | 157 | .u-sm-2-3, 158 | .u-sm-16-24 { 159 | width: 66.6667%; 160 | *width: 66.6357%; 161 | } 162 | 163 | .u-sm-17-24 { 164 | width: 70.8333%; 165 | *width: 70.8023%; 166 | } 167 | 168 | .u-sm-3-4, 169 | .u-sm-18-24 { 170 | width: 75%; 171 | *width: 74.9690%; 172 | } 173 | 174 | .u-sm-19-24 { 175 | width: 79.1667%; 176 | *width: 79.1357%; 177 | } 178 | 179 | .u-sm-4-5 { 180 | width: 80%; 181 | *width: 79.9690%; 182 | } 183 | 184 | .u-sm-5-6, 185 | .u-sm-20-24 { 186 | width: 83.3333%; 187 | *width: 83.3023%; 188 | } 189 | 190 | .u-sm-7-8, 191 | .u-sm-21-24 { 192 | width: 87.5000%; 193 | *width: 87.4690%; 194 | } 195 | 196 | .u-sm-11-12, 197 | .u-sm-22-24 { 198 | width: 91.6667%; 199 | *width: 91.6357%; 200 | } 201 | 202 | .u-sm-23-24 { 203 | width: 95.8333%; 204 | *width: 95.8023%; 205 | } 206 | 207 | .u-sm-1, 208 | .u-sm-1-1, 209 | .u-sm-5-5, 210 | .u-sm-24-24 { 211 | width: 100%; 212 | } 213 | } 214 | 215 | @media screen and (min-width: 48em) { 216 | .u-md-1, 217 | .u-md-1-1, 218 | .u-md-1-2, 219 | .u-md-1-3, 220 | .u-md-2-3, 221 | .u-md-1-4, 222 | .u-md-3-4, 223 | .u-md-1-5, 224 | .u-md-2-5, 225 | .u-md-3-5, 226 | .u-md-4-5, 227 | .u-md-5-5, 228 | .u-md-1-6, 229 | .u-md-5-6, 230 | .u-md-1-8, 231 | .u-md-3-8, 232 | .u-md-5-8, 233 | .u-md-7-8, 234 | .u-md-1-12, 235 | .u-md-5-12, 236 | .u-md-7-12, 237 | .u-md-11-12, 238 | .u-md-1-24, 239 | .u-md-2-24, 240 | .u-md-3-24, 241 | .u-md-4-24, 242 | .u-md-5-24, 243 | .u-md-6-24, 244 | .u-md-7-24, 245 | .u-md-8-24, 246 | .u-md-9-24, 247 | .u-md-10-24, 248 | .u-md-11-24, 249 | .u-md-12-24, 250 | .u-md-13-24, 251 | .u-md-14-24, 252 | .u-md-15-24, 253 | .u-md-16-24, 254 | .u-md-17-24, 255 | .u-md-18-24, 256 | .u-md-19-24, 257 | .u-md-20-24, 258 | .u-md-21-24, 259 | .u-md-22-24, 260 | .u-md-23-24, 261 | .u-md-24-24 { 262 | display: inline-block; 263 | *display: inline; 264 | zoom: 1; 265 | letter-spacing: normal; 266 | word-spacing: normal; 267 | vertical-align: top; 268 | text-rendering: auto; 269 | } 270 | 271 | .u-md-1-24 { 272 | width: 4.1667%; 273 | *width: 4.1357%; 274 | } 275 | 276 | .u-md-1-12, 277 | .u-md-2-24 { 278 | width: 8.3333%; 279 | *width: 8.3023%; 280 | } 281 | 282 | .u-md-1-8, 283 | .u-md-3-24 { 284 | width: 12.5000%; 285 | *width: 12.4690%; 286 | } 287 | 288 | .u-md-1-6, 289 | .u-md-4-24 { 290 | width: 16.6667%; 291 | *width: 16.6357%; 292 | } 293 | 294 | .u-md-1-5 { 295 | width: 20%; 296 | *width: 19.9690%; 297 | } 298 | 299 | .u-md-5-24 { 300 | width: 20.8333%; 301 | *width: 20.8023%; 302 | } 303 | 304 | .u-md-1-4, 305 | .u-md-6-24 { 306 | width: 25%; 307 | *width: 24.9690%; 308 | } 309 | 310 | .u-md-7-24 { 311 | width: 29.1667%; 312 | *width: 29.1357%; 313 | } 314 | 315 | .u-md-1-3, 316 | .u-md-8-24 { 317 | width: 33.3333%; 318 | *width: 33.3023%; 319 | } 320 | 321 | .u-md-3-8, 322 | .u-md-9-24 { 323 | width: 37.5000%; 324 | *width: 37.4690%; 325 | } 326 | 327 | .u-md-2-5 { 328 | width: 40%; 329 | *width: 39.9690%; 330 | } 331 | 332 | .u-md-5-12, 333 | .u-md-10-24 { 334 | width: 41.6667%; 335 | *width: 41.6357%; 336 | } 337 | 338 | .u-md-11-24 { 339 | width: 45.8333%; 340 | *width: 45.8023%; 341 | } 342 | 343 | .u-md-1-2, 344 | .u-md-12-24 { 345 | width: 50%; 346 | *width: 49.9690%; 347 | } 348 | 349 | .u-md-13-24 { 350 | width: 54.1667%; 351 | *width: 54.1357%; 352 | } 353 | 354 | .u-md-7-12, 355 | .u-md-14-24 { 356 | width: 58.3333%; 357 | *width: 58.3023%; 358 | } 359 | 360 | .u-md-3-5 { 361 | width: 60%; 362 | *width: 59.9690%; 363 | } 364 | 365 | .u-md-5-8, 366 | .u-md-15-24 { 367 | width: 62.5000%; 368 | *width: 62.4690%; 369 | } 370 | 371 | .u-md-2-3, 372 | .u-md-16-24 { 373 | width: 66.6667%; 374 | *width: 66.6357%; 375 | } 376 | 377 | .u-md-17-24 { 378 | width: 70.8333%; 379 | *width: 70.8023%; 380 | } 381 | 382 | .u-md-3-4, 383 | .u-md-18-24 { 384 | width: 75%; 385 | *width: 74.9690%; 386 | } 387 | 388 | .u-md-19-24 { 389 | width: 79.1667%; 390 | *width: 79.1357%; 391 | } 392 | 393 | .u-md-4-5 { 394 | width: 80%; 395 | *width: 79.9690%; 396 | } 397 | 398 | .u-md-5-6, 399 | .u-md-20-24 { 400 | width: 83.3333%; 401 | *width: 83.3023%; 402 | } 403 | 404 | .u-md-7-8, 405 | .u-md-21-24 { 406 | width: 87.5000%; 407 | *width: 87.4690%; 408 | } 409 | 410 | .u-md-11-12, 411 | .u-md-22-24 { 412 | width: 91.6667%; 413 | *width: 91.6357%; 414 | } 415 | 416 | .u-md-23-24 { 417 | width: 95.8333%; 418 | *width: 95.8023%; 419 | } 420 | 421 | .u-md-1, 422 | .u-md-1-1, 423 | .u-md-5-5, 424 | .u-md-24-24 { 425 | width: 100%; 426 | } 427 | } 428 | 429 | @media screen and (min-width: 58em) { 430 | .u-lg-1, 431 | .u-lg-1-1, 432 | .u-lg-1-2, 433 | .u-lg-1-3, 434 | .u-lg-2-3, 435 | .u-lg-1-4, 436 | .u-lg-3-4, 437 | .u-lg-1-5, 438 | .u-lg-2-5, 439 | .u-lg-3-5, 440 | .u-lg-4-5, 441 | .u-lg-5-5, 442 | .u-lg-1-6, 443 | .u-lg-5-6, 444 | .u-lg-1-8, 445 | .u-lg-3-8, 446 | .u-lg-5-8, 447 | .u-lg-7-8, 448 | .u-lg-1-12, 449 | .u-lg-5-12, 450 | .u-lg-7-12, 451 | .u-lg-11-12, 452 | .u-lg-1-24, 453 | .u-lg-2-24, 454 | .u-lg-3-24, 455 | .u-lg-4-24, 456 | .u-lg-5-24, 457 | .u-lg-6-24, 458 | .u-lg-7-24, 459 | .u-lg-8-24, 460 | .u-lg-9-24, 461 | .u-lg-10-24, 462 | .u-lg-11-24, 463 | .u-lg-12-24, 464 | .u-lg-13-24, 465 | .u-lg-14-24, 466 | .u-lg-15-24, 467 | .u-lg-16-24, 468 | .u-lg-17-24, 469 | .u-lg-18-24, 470 | .u-lg-19-24, 471 | .u-lg-20-24, 472 | .u-lg-21-24, 473 | .u-lg-22-24, 474 | .u-lg-23-24, 475 | .u-lg-24-24 { 476 | display: inline-block; 477 | *display: inline; 478 | zoom: 1; 479 | letter-spacing: normal; 480 | word-spacing: normal; 481 | vertical-align: top; 482 | text-rendering: auto; 483 | } 484 | 485 | .u-lg-1-24 { 486 | width: 4.1667%; 487 | *width: 4.1357%; 488 | } 489 | 490 | .u-lg-1-12, 491 | .u-lg-2-24 { 492 | width: 8.3333%; 493 | *width: 8.3023%; 494 | } 495 | 496 | .u-lg-1-8, 497 | .u-lg-3-24 { 498 | width: 12.5000%; 499 | *width: 12.4690%; 500 | } 501 | 502 | .u-lg-1-6, 503 | .u-lg-4-24 { 504 | width: 16.6667%; 505 | *width: 16.6357%; 506 | } 507 | 508 | .u-lg-1-5 { 509 | width: 20%; 510 | *width: 19.9690%; 511 | } 512 | 513 | .u-lg-5-24 { 514 | width: 20.8333%; 515 | *width: 20.8023%; 516 | } 517 | 518 | .u-lg-1-4, 519 | .u-lg-6-24 { 520 | width: 25%; 521 | *width: 24.9690%; 522 | } 523 | 524 | .u-lg-7-24 { 525 | width: 29.1667%; 526 | *width: 29.1357%; 527 | } 528 | 529 | .u-lg-1-3, 530 | .u-lg-8-24 { 531 | width: 33.3333%; 532 | *width: 33.3023%; 533 | } 534 | 535 | .u-lg-3-8, 536 | .u-lg-9-24 { 537 | width: 37.5000%; 538 | *width: 37.4690%; 539 | } 540 | 541 | .u-lg-2-5 { 542 | width: 40%; 543 | *width: 39.9690%; 544 | } 545 | 546 | .u-lg-5-12, 547 | .u-lg-10-24 { 548 | width: 41.6667%; 549 | *width: 41.6357%; 550 | } 551 | 552 | .u-lg-11-24 { 553 | width: 45.8333%; 554 | *width: 45.8023%; 555 | } 556 | 557 | .u-lg-1-2, 558 | .u-lg-12-24 { 559 | width: 50%; 560 | *width: 49.9690%; 561 | } 562 | 563 | .u-lg-13-24 { 564 | width: 54.1667%; 565 | *width: 54.1357%; 566 | } 567 | 568 | .u-lg-7-12, 569 | .u-lg-14-24 { 570 | width: 58.3333%; 571 | *width: 58.3023%; 572 | } 573 | 574 | .u-lg-3-5 { 575 | width: 60%; 576 | *width: 59.9690%; 577 | } 578 | 579 | .u-lg-5-8, 580 | .u-lg-15-24 { 581 | width: 62.5000%; 582 | *width: 62.4690%; 583 | } 584 | 585 | .u-lg-2-3, 586 | .u-lg-16-24 { 587 | width: 66.6667%; 588 | *width: 66.6357%; 589 | } 590 | 591 | .u-lg-17-24 { 592 | width: 70.8333%; 593 | *width: 70.8023%; 594 | } 595 | 596 | .u-lg-3-4, 597 | .u-lg-18-24 { 598 | width: 75%; 599 | *width: 74.9690%; 600 | } 601 | 602 | .u-lg-19-24 { 603 | width: 79.1667%; 604 | *width: 79.1357%; 605 | } 606 | 607 | .u-lg-4-5 { 608 | width: 80%; 609 | *width: 79.9690%; 610 | } 611 | 612 | .u-lg-5-6, 613 | .u-lg-20-24 { 614 | width: 83.3333%; 615 | *width: 83.3023%; 616 | } 617 | 618 | .u-lg-7-8, 619 | .u-lg-21-24 { 620 | width: 87.5000%; 621 | *width: 87.4690%; 622 | } 623 | 624 | .u-lg-11-12, 625 | .u-lg-22-24 { 626 | width: 91.6667%; 627 | *width: 91.6357%; 628 | } 629 | 630 | .u-lg-23-24 { 631 | width: 95.8333%; 632 | *width: 95.8023%; 633 | } 634 | 635 | .u-lg-1, 636 | .u-lg-1-1, 637 | .u-lg-5-5, 638 | .u-lg-24-24 { 639 | width: 100%; 640 | } 641 | } 642 | 643 | @media screen and (min-width: 75em) { 644 | .u-xl-1, 645 | .u-xl-1-1, 646 | .u-xl-1-2, 647 | .u-xl-1-3, 648 | .u-xl-2-3, 649 | .u-xl-1-4, 650 | .u-xl-3-4, 651 | .u-xl-1-5, 652 | .u-xl-2-5, 653 | .u-xl-3-5, 654 | .u-xl-4-5, 655 | .u-xl-5-5, 656 | .u-xl-1-6, 657 | .u-xl-5-6, 658 | .u-xl-1-8, 659 | .u-xl-3-8, 660 | .u-xl-5-8, 661 | .u-xl-7-8, 662 | .u-xl-1-12, 663 | .u-xl-5-12, 664 | .u-xl-7-12, 665 | .u-xl-11-12, 666 | .u-xl-1-24, 667 | .u-xl-2-24, 668 | .u-xl-3-24, 669 | .u-xl-4-24, 670 | .u-xl-5-24, 671 | .u-xl-6-24, 672 | .u-xl-7-24, 673 | .u-xl-8-24, 674 | .u-xl-9-24, 675 | .u-xl-10-24, 676 | .u-xl-11-24, 677 | .u-xl-12-24, 678 | .u-xl-13-24, 679 | .u-xl-14-24, 680 | .u-xl-15-24, 681 | .u-xl-16-24, 682 | .u-xl-17-24, 683 | .u-xl-18-24, 684 | .u-xl-19-24, 685 | .u-xl-20-24, 686 | .u-xl-21-24, 687 | .u-xl-22-24, 688 | .u-xl-23-24, 689 | .u-xl-24-24 { 690 | display: inline-block; 691 | *display: inline; 692 | zoom: 1; 693 | letter-spacing: normal; 694 | word-spacing: normal; 695 | vertical-align: top; 696 | text-rendering: auto; 697 | } 698 | 699 | .u-xl-1-24 { 700 | width: 4.1667%; 701 | *width: 4.1357%; 702 | } 703 | 704 | .u-xl-1-12, 705 | .u-xl-2-24 { 706 | width: 8.3333%; 707 | *width: 8.3023%; 708 | } 709 | 710 | .u-xl-1-8, 711 | .u-xl-3-24 { 712 | width: 12.5000%; 713 | *width: 12.4690%; 714 | } 715 | 716 | .u-xl-1-6, 717 | .u-xl-4-24 { 718 | width: 16.6667%; 719 | *width: 16.6357%; 720 | } 721 | 722 | .u-xl-1-5 { 723 | width: 20%; 724 | *width: 19.9690%; 725 | } 726 | 727 | .u-xl-5-24 { 728 | width: 20.8333%; 729 | *width: 20.8023%; 730 | } 731 | 732 | .u-xl-1-4, 733 | .u-xl-6-24 { 734 | width: 25%; 735 | *width: 24.9690%; 736 | } 737 | 738 | .u-xl-7-24 { 739 | width: 29.1667%; 740 | *width: 29.1357%; 741 | } 742 | 743 | .u-xl-1-3, 744 | .u-xl-8-24 { 745 | width: 33.3333%; 746 | *width: 33.3023%; 747 | } 748 | 749 | .u-xl-3-8, 750 | .u-xl-9-24 { 751 | width: 37.5000%; 752 | *width: 37.4690%; 753 | } 754 | 755 | .u-xl-2-5 { 756 | width: 40%; 757 | *width: 39.9690%; 758 | } 759 | 760 | .u-xl-5-12, 761 | .u-xl-10-24 { 762 | width: 41.6667%; 763 | *width: 41.6357%; 764 | } 765 | 766 | .u-xl-11-24 { 767 | width: 45.8333%; 768 | *width: 45.8023%; 769 | } 770 | 771 | .u-xl-1-2, 772 | .u-xl-12-24 { 773 | width: 50%; 774 | *width: 49.9690%; 775 | } 776 | 777 | .u-xl-13-24 { 778 | width: 54.1667%; 779 | *width: 54.1357%; 780 | } 781 | 782 | .u-xl-7-12, 783 | .u-xl-14-24 { 784 | width: 58.3333%; 785 | *width: 58.3023%; 786 | } 787 | 788 | .u-xl-3-5 { 789 | width: 60%; 790 | *width: 59.9690%; 791 | } 792 | 793 | .u-xl-5-8, 794 | .u-xl-15-24 { 795 | width: 62.5000%; 796 | *width: 62.4690%; 797 | } 798 | 799 | .u-xl-2-3, 800 | .u-xl-16-24 { 801 | width: 66.6667%; 802 | *width: 66.6357%; 803 | } 804 | 805 | .u-xl-17-24 { 806 | width: 70.8333%; 807 | *width: 70.8023%; 808 | } 809 | 810 | .u-xl-3-4, 811 | .u-xl-18-24 { 812 | width: 75%; 813 | *width: 74.9690%; 814 | } 815 | 816 | .u-xl-19-24 { 817 | width: 79.1667%; 818 | *width: 79.1357%; 819 | } 820 | 821 | .u-xl-4-5 { 822 | width: 80%; 823 | *width: 79.9690%; 824 | } 825 | 826 | .u-xl-5-6, 827 | .u-xl-20-24 { 828 | width: 83.3333%; 829 | *width: 83.3023%; 830 | } 831 | 832 | .u-xl-7-8, 833 | .u-xl-21-24 { 834 | width: 87.5000%; 835 | *width: 87.4690%; 836 | } 837 | 838 | .u-xl-11-12, 839 | .u-xl-22-24 { 840 | width: 91.6667%; 841 | *width: 91.6357%; 842 | } 843 | 844 | .u-xl-23-24 { 845 | width: 95.8333%; 846 | *width: 95.8023%; 847 | } 848 | 849 | .u-xl-1, 850 | .u-xl-1-1, 851 | .u-xl-5-5, 852 | .u-xl-24-24 { 853 | width: 100%; 854 | } 855 | } -------------------------------------------------------------------------------- /third_party/pure/main.css: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-box-sizing: border-box; 3 | -moz-box-sizing: border-box; 4 | box-sizing: border-box; 5 | } 6 | 7 | *:before, 8 | *:after { 9 | -webkit-box-sizing: border-box; 10 | -moz-box-sizing: border-box; 11 | box-sizing: border-box; 12 | } 13 | 14 | html, button, input, select, textarea, 15 | .pure-g [class *= "pure-u"] { 16 | font-family: Helvetica, Arial, sans-serif; 17 | letter-spacing: 0.01em; 18 | } 19 | 20 | 21 | /* -------------------------- 22 | * Element Styles 23 | * -------------------------- 24 | */ 25 | 26 | body { 27 | min-width: 320px; 28 | background-color: #fff; 29 | color: #777; 30 | line-height: 1.6; 31 | } 32 | 33 | h1, h2, h3, h4, h5, h6 { 34 | font-weight: bold; 35 | color: rgb(75, 75, 75); 36 | } 37 | h3 { 38 | font-size: 1.25em; 39 | } 40 | h4 { 41 | font-size: 1.125em; 42 | } 43 | 44 | a { 45 | color: #3b8bba; /* block-background-text-normal */ 46 | text-decoration: none; 47 | } 48 | 49 | a:visited { 50 | color: #265778; /* block-normal-text-normal */ 51 | } 52 | 53 | dt { 54 | font-weight: bold; 55 | } 56 | dd { 57 | margin: 0 0 10px 0; 58 | } 59 | 60 | aside { 61 | background: #1f8dd6; /* same color as selected state on site menu */ 62 | margin: 1em 0; 63 | padding: 0.3em 1em; 64 | border-radius: 3px; 65 | color: #fff; 66 | } 67 | aside a, aside a:visited { 68 | color: rgb(169, 226, 255); 69 | } 70 | 71 | 72 | /* -------------------------- 73 | * Layout Styles 74 | * -------------------------- 75 | */ 76 | 77 | /* Navigation Push Styles */ 78 | #layout { 79 | position: relative; 80 | padding-left: 0; 81 | } 82 | #layout.active #menu { 83 | left: 160px; 84 | width: 160px; 85 | } 86 | 87 | /* Apply the .box class on the immediate parent of any grid element (pure-u-*) to apply some padding. */ 88 | .l-box { 89 | padding: 1em; 90 | } 91 | 92 | .l-wrap { 93 | margin-left: auto; 94 | margin-right: auto; 95 | } 96 | .content .l-wrap { 97 | margin-left: -1em; 98 | margin-right: -1em; 99 | } 100 | 101 | 102 | /* -------------------------- 103 | * Header Module Styles 104 | * -------------------------- 105 | */ 106 | 107 | .header { 108 | font-family: "Raleway", "Helvetica Neue", Helvetica, Arial, sans-serif; 109 | max-width: 768px; 110 | margin: 0 auto; 111 | padding: 1em; 112 | text-align: center; 113 | border-bottom: 1px solid #eee; 114 | background: #fff; 115 | letter-spacing: 0.05em; 116 | } 117 | .header h1 { 118 | font-size: 300%; 119 | font-weight: 100; 120 | margin: 0; 121 | } 122 | .header h2 { 123 | font-size: 125%; 124 | font-weight: 100; 125 | line-height: 1.5; 126 | margin: 0; 127 | color: #666; 128 | letter-spacing: -0.02em; 129 | } 130 | 131 | 132 | /* -------------------------- 133 | * Content Module Styles 134 | * -------------------------- 135 | */ 136 | 137 | /* The content div is placed as a wrapper around all the docs */ 138 | .content { 139 | margin-left: auto; 140 | margin-right: auto; 141 | padding-left: 1em; 142 | padding-right: 1em; 143 | max-width: 768px; 144 | } 145 | 146 | .content .content-subhead { 147 | margin: 2em 0 1em 0; 148 | font-weight: 300; 149 | color: #888; 150 | position: relative; 151 | } 152 | 153 | .content .content-spaced { 154 | line-height: 1.8; 155 | } 156 | 157 | .content .content-quote { 158 | font-family: "Georgia", serif; 159 | color: #666; 160 | font-style: italic; 161 | line-height: 1.8; 162 | border-left: 5px solid #ddd; 163 | padding-left: 1.5em; 164 | } 165 | 166 | .content-link { 167 | position: absolute; 168 | top: 0; 169 | right: 0; 170 | display: block; 171 | height: 100%; 172 | width: 20px; 173 | background: transparent url('/img/link-icon.png') no-repeat center center; 174 | background-size: 20px 20px; 175 | } 176 | 177 | @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) { 178 | .content-link { 179 | background-image: url('/img/link-icon@2x.png'); 180 | } 181 | } 182 | 183 | 184 | /* -------------------------- 185 | * Code Styles 186 | * -------------------------- 187 | */ 188 | 189 | pre, 190 | code { 191 | font-family: Consolas, Courier, monospace; 192 | color: #333; 193 | background: rgb(250, 250, 250); 194 | } 195 | 196 | code { 197 | padding: 0.2em 0.4em; 198 | white-space: nowrap; 199 | } 200 | .content p code { 201 | font-size: 90%; 202 | } 203 | 204 | .code { 205 | margin-left: -1em; 206 | margin-right: -1em; 207 | border: 1px solid #eee; 208 | border-left-width: 0; 209 | border-right-width: 0; 210 | overflow-x: auto; 211 | } 212 | .code pre { 213 | margin: 0; 214 | } 215 | .code code { 216 | font-size: 95%; 217 | white-space: pre; 218 | word-wrap: normal; 219 | padding: 0; 220 | background: none; 221 | } 222 | .code-wrap code { 223 | white-space: pre-wrap; 224 | word-wrap: break-word; 225 | } 226 | .example .code { 227 | margin-top: 1em; 228 | } 229 | 230 | /* -------------------------- 231 | * Footer Module Styles 232 | * -------------------------- 233 | */ 234 | 235 | .footer { 236 | font-size: 87.5%; 237 | border-top: 1px solid #eee; 238 | margin-top: 3.4286em; 239 | padding: 1.1429em; 240 | background: rgb(250, 250, 250); 241 | } 242 | 243 | .legal { 244 | line-height: 1.6; 245 | text-align: center; 246 | margin: 0 auto; 247 | } 248 | 249 | .legal-license { 250 | margin-top: 0; 251 | } 252 | .legal-links { 253 | list-style: none; 254 | padding: 0; 255 | margin-bottom: 0; 256 | } 257 | .legal-copyright { 258 | margin-top: 0; 259 | margin-bottom: 0; 260 | } 261 | 262 | 263 | /* -------------------------- 264 | * Main Navigation Bar Styles 265 | * -------------------------- 266 | */ 267 | 268 | /* Add transition to containers so they can push in and out */ 269 | #layout, 270 | #menu, 271 | .menu-link { 272 | -webkit-transition: all 0.2s ease-out; 273 | -moz-transition: all 0.2s ease-out; 274 | -ms-transition: all 0.2s ease-out; 275 | -o-transition: all 0.2s ease-out; 276 | transition: all 0.2s ease-out; 277 | } 278 | 279 | #layout.active .menu-link { 280 | left: 160px; 281 | } 282 | 283 | #menu { 284 | margin-left: -160px; /* "#menu" width */ 285 | width: 160px; 286 | position: fixed; 287 | top: 0; 288 | left: 0; 289 | bottom: 0; 290 | z-index: 1000; /* so the menu or its navicon stays above all content */ 291 | background: #191818; 292 | overflow-y: auto; 293 | } 294 | #menu a { 295 | color: #999; 296 | border: none; 297 | white-space: normal; 298 | padding: 0.625em 1em; 299 | } 300 | 301 | #menu .pure-menu-open { 302 | background: transparent; 303 | border: 0; 304 | } 305 | 306 | #menu .pure-menu ul { 307 | border: none; 308 | background: transparent; 309 | display: block; 310 | } 311 | 312 | #menu .pure-menu ul, 313 | #menu .pure-menu .menu-item-divided { 314 | border-top: 1px solid #333; 315 | } 316 | 317 | #menu .pure-menu-list li .pure-menu-link:hover, 318 | #menu .pure-menu-list li .pure-menu-link:focus { 319 | background: #333; 320 | } 321 | 322 | .menu-link { 323 | position: fixed; 324 | display: block; /* show this only on small screens */ 325 | top: 0; 326 | left: 0; /* "#menu width" */ 327 | background: #000; 328 | background: rgba(0,0,0,0.7); 329 | font-size: 11px; /* change this value to increase/decrease button size */ 330 | z-index: 10; 331 | width: 4em; 332 | height: 4em; 333 | padding: 1em; 334 | } 335 | 336 | .menu-link:hover, 337 | .menu-link:focus { 338 | background: #000; 339 | } 340 | 341 | .menu-link span { 342 | position: relative; 343 | display: block; 344 | margin-top: 0.9em; 345 | } 346 | 347 | .menu-link span, 348 | .menu-link span:before, 349 | .menu-link span:after { 350 | background-color: #fff; 351 | pointer-events: none; 352 | width: 100%; 353 | height: .2em; 354 | -webkit-transition: all 0.4s; 355 | -moz-transition: all 0.4s; 356 | -ms-transition: all 0.4s; 357 | -o-transition: all 0.4s; 358 | transition: all 0.4s; 359 | } 360 | 361 | .menu-link span:before, 362 | .menu-link span:after { 363 | position: absolute; 364 | top: -.55em; 365 | content: " "; 366 | } 367 | 368 | .menu-link span:after { 369 | top: .55em; 370 | } 371 | 372 | .menu-link.active span { 373 | background: transparent; 374 | } 375 | 376 | .menu-link.active span:before { 377 | -webkit-transform: rotate(45deg) translate(.5em, .4em); 378 | -moz-transform: rotate(45deg) translate(.5em, .4em); 379 | -ms-transform: rotate(45deg) translate(.5em, .4em); 380 | -o-transform: rotate(45deg) translate(.5em, .4em); 381 | transform: rotate(45deg) translate(.5em, .4em); 382 | } 383 | 384 | .menu-link.active span:after { 385 | -webkit-transform: rotate(-45deg) translate(.4em, -.3em); 386 | -moz-transform: rotate(-45deg) translate(.4em, -.3em); 387 | -ms-transform: rotate(-45deg) translate(.4em, -.3em); 388 | -o-transform: rotate(-45deg) translate(.4em, -.3em); 389 | transform: rotate(-45deg) translate(.4em, -.3em); 390 | } 391 | 392 | #menu .pure-menu-heading { 393 | font-size: 125%; 394 | font-weight: 300; 395 | letter-spacing: 0.1em; 396 | color: #fff; 397 | margin-top: 0; 398 | padding: 0.5em 0.8em; 399 | text-transform: uppercase; 400 | } 401 | #menu .pure-menu-heading:hover, 402 | #menu .pure-menu-heading:focus { 403 | color: #999; 404 | } 405 | 406 | #menu .pure-menu-item .active { 407 | background: #1f8dd6; 408 | color: #fff; 409 | } 410 | 411 | #menu li.pure-menu-item .active:hover, 412 | #menu li.pure-menu-item .active:focus { 413 | background: #1f8dd6; 414 | } 415 | 416 | 417 | /* --------------------- 418 | * Smaller Module Styles 419 | * --------------------- 420 | */ 421 | 422 | .pure-img-responsive { 423 | max-width: 100%; 424 | height: auto; 425 | } 426 | 427 | .pure-paginator .pure-button { 428 | -webkit-box-sizing: content-box; 429 | -moz-box-sizing: content-box; 430 | box-sizing: content-box; 431 | } 432 | 433 | .pure-button { 434 | font-family: inherit; 435 | } 436 | a.pure-button-primary { 437 | color: white; 438 | } 439 | 440 | 441 | /* green call to action button class */ 442 | .notice { 443 | background-color: #61B842; 444 | color: white; 445 | } 446 | 447 | .muted { 448 | color: #ccc; 449 | } 450 | 451 | 452 | 453 | /* ------------- 454 | * Table Styles 455 | * ------------- 456 | */ 457 | .pure-table th, 458 | .pure-table td { 459 | padding: 0.5em 1em; 460 | } 461 | 462 | .table-responsive { 463 | margin-left: -1em; 464 | margin-right: -1em; 465 | overflow-x: auto; 466 | margin-bottom: 1em; 467 | } 468 | .table-responsive table { 469 | width: 100%; 470 | min-width: 35.5em; 471 | border-left-width: 0; 472 | border-right-width: 0; 473 | } 474 | 475 | .table-responsive .mq-table { 476 | width: 100%; 477 | min-width: 44em; 478 | } 479 | .mq-table th.highlight { 480 | background-color: rgb(255, 234, 133); 481 | } 482 | .mq-table td.highlight { 483 | background-color: rgb(255, 250, 229); 484 | } 485 | .mq-table th.highlight code, 486 | .mq-table td.highlight code { 487 | background: rgb(255, 255, 243); 488 | } 489 | .mq-table-mq code { 490 | font-size: 0.875em; 491 | } 492 | 493 | /* ---------------------------- 494 | * Example for full-width Grids 495 | * ---------------------------- 496 | */ 497 | 498 | .grids-example { 499 | background: rgb(250, 250, 250); 500 | margin: 2em auto; 501 | border-top: 1px solid #ddd; 502 | border-bottom: 1px solid #ddd; 503 | } 504 | 505 | /* -------------------------- 506 | * State Rules 507 | * -------------------------- 508 | */ 509 | 510 | 511 | .is-code-full { 512 | text-align: center; 513 | } 514 | .is-code-full .code { 515 | margin-left: auto; 516 | margin-right: auto; 517 | } 518 | .is-code-full code { 519 | display: inline-block; 520 | max-width: 768px; 521 | margin-left: auto; 522 | margin-right: auto; 523 | } 524 | 525 | 526 | /* -------------------------- 527 | * Responsive Styles 528 | * -------------------------- 529 | */ 530 | 531 | @media screen and (min-width: 35.5em) { 532 | 533 | .legal-license { 534 | text-align: left; 535 | margin: 0; 536 | } 537 | .legal-copyright, 538 | .legal-links, 539 | .legal-links li { 540 | text-align: right; 541 | margin: 0; 542 | } 543 | 544 | } 545 | 546 | @media screen and (min-width: 48em) { 547 | 548 | .l-wrap, 549 | .l-wrap .content { 550 | padding-left: 1em; 551 | padding-right: 1em; 552 | } 553 | .content .l-wrap { 554 | margin-left: -2em; 555 | margin-right: -2em; 556 | } 557 | 558 | .header, 559 | .content { 560 | padding-left: 2em; 561 | padding-right: 2em; 562 | } 563 | 564 | .header h1 { 565 | font-size: 320%; 566 | } 567 | .header h2 { 568 | font-size: 128%; 569 | } 570 | 571 | .content p { 572 | font-size: 1.125em; 573 | } 574 | 575 | .code { 576 | margin-left: auto; 577 | margin-right: auto; 578 | border-left-width: 1px; 579 | border-right-width: 1px; 580 | } 581 | 582 | .table-responsive { 583 | margin-left: auto; 584 | margin-right: auto; 585 | } 586 | .table-responsive table { 587 | border-left-width: 1px; 588 | border-right-width: 1px; 589 | } 590 | 591 | } 592 | 593 | @media (max-width: 58em) { 594 | /* Only apply this when the window is smaller. Otherwise, the following 595 | case results in extra padding on the left: 596 | * Make the window small. (Rotate to portrait on a mobile.) 597 | * Tap the menu to trigger the active state. 598 | * Make the window large again. (Rotate to landscape on mobile.) 599 | */ 600 | #layout.active { 601 | position: relative; 602 | left: 160px; 603 | } 604 | } 605 | 606 | @media (min-width: 58em) { 607 | 608 | #layout { 609 | padding-left: 160px; /* left col width "#menu" */ 610 | left: 0; 611 | } 612 | #menu { 613 | left: 160px; 614 | } 615 | .menu-link { 616 | position: fixed; 617 | left: 160px; 618 | display: none; 619 | } 620 | #layout.active .menu-link { 621 | left: 160px; 622 | } 623 | 624 | } 625 | -------------------------------------------------------------------------------- /third_party/skeleton/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2014 Dave Gamache 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /third_party/skeleton/README.me: -------------------------------------------------------------------------------- 1 | This folder contains sample css files from the open-source project 2 | https://github.com/dhg/Skeleton. 3 | 4 | This code was included under the terms in the `LICENSE.md file. -------------------------------------------------------------------------------- /third_party/skeleton/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } -------------------------------------------------------------------------------- /third_party/skeleton/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V2.0.4 3 | * Copyright 2014, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 12/29/2014 8 | */ 9 | 10 | 11 | /* Table of contents 12 | –––––––––––––––––––––––––––––––––––––––––––––––––– 13 | - Grid 14 | - Base Styles 15 | - Typography 16 | - Links 17 | - Buttons 18 | - Forms 19 | - Lists 20 | - Code 21 | - Tables 22 | - Spacing 23 | - Utilities 24 | - Clearing 25 | - Media Queries 26 | */ 27 | 28 | 29 | /* Grid 30 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 31 | .container { 32 | position: relative; 33 | width: 100%; 34 | max-width: 960px; 35 | margin: 0 auto; 36 | padding: 0 20px; 37 | box-sizing: border-box; } 38 | .column, 39 | .columns { 40 | width: 100%; 41 | float: left; 42 | box-sizing: border-box; } 43 | 44 | /* For devices larger than 400px */ 45 | @media (min-width: 400px) { 46 | .container { 47 | width: 85%; 48 | padding: 0; } 49 | } 50 | 51 | /* For devices larger than 550px */ 52 | @media (min-width: 550px) { 53 | .container { 54 | width: 80%; } 55 | .column, 56 | .columns { 57 | margin-left: 4%; } 58 | .column:first-child, 59 | .columns:first-child { 60 | margin-left: 0; } 61 | 62 | .one.column, 63 | .one.columns { width: 4.66666666667%; } 64 | .two.columns { width: 13.3333333333%; } 65 | .three.columns { width: 22%; } 66 | .four.columns { width: 30.6666666667%; } 67 | .five.columns { width: 39.3333333333%; } 68 | .six.columns { width: 48%; } 69 | .seven.columns { width: 56.6666666667%; } 70 | .eight.columns { width: 65.3333333333%; } 71 | .nine.columns { width: 74.0%; } 72 | .ten.columns { width: 82.6666666667%; } 73 | .eleven.columns { width: 91.3333333333%; } 74 | .twelve.columns { width: 100%; margin-left: 0; } 75 | 76 | .one-third.column { width: 30.6666666667%; } 77 | .two-thirds.column { width: 65.3333333333%; } 78 | 79 | .one-half.column { width: 48%; } 80 | 81 | /* Offsets */ 82 | .offset-by-one.column, 83 | .offset-by-one.columns { margin-left: 8.66666666667%; } 84 | .offset-by-two.column, 85 | .offset-by-two.columns { margin-left: 17.3333333333%; } 86 | .offset-by-three.column, 87 | .offset-by-three.columns { margin-left: 26%; } 88 | .offset-by-four.column, 89 | .offset-by-four.columns { margin-left: 34.6666666667%; } 90 | .offset-by-five.column, 91 | .offset-by-five.columns { margin-left: 43.3333333333%; } 92 | .offset-by-six.column, 93 | .offset-by-six.columns { margin-left: 52%; } 94 | .offset-by-seven.column, 95 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 96 | .offset-by-eight.column, 97 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 98 | .offset-by-nine.column, 99 | .offset-by-nine.columns { margin-left: 78.0%; } 100 | .offset-by-ten.column, 101 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 102 | .offset-by-eleven.column, 103 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 104 | 105 | .offset-by-one-third.column, 106 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 107 | .offset-by-two-thirds.column, 108 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 109 | 110 | .offset-by-one-half.column, 111 | .offset-by-one-half.columns { margin-left: 52%; } 112 | 113 | } 114 | 115 | 116 | /* Base Styles 117 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 118 | /* NOTE 119 | html is set to 62.5% so that all the REM measurements throughout Skeleton 120 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 121 | html { 122 | font-size: 62.5%; } 123 | body { 124 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 125 | line-height: 1.6; 126 | font-weight: 400; 127 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 128 | color: #222; } 129 | 130 | 131 | /* Typography 132 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 133 | h1, h2, h3, h4, h5, h6 { 134 | margin-top: 0; 135 | margin-bottom: 2rem; 136 | font-weight: 300; } 137 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} 138 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } 139 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } 140 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } 141 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } 142 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } 143 | 144 | /* Larger than phablet */ 145 | @media (min-width: 550px) { 146 | h1 { font-size: 5.0rem; } 147 | h2 { font-size: 4.2rem; } 148 | h3 { font-size: 3.6rem; } 149 | h4 { font-size: 3.0rem; } 150 | h5 { font-size: 2.4rem; } 151 | h6 { font-size: 1.5rem; } 152 | } 153 | 154 | p { 155 | margin-top: 0; } 156 | 157 | 158 | /* Links 159 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 160 | a { 161 | color: #1EAEDB; } 162 | a:hover { 163 | color: #0FA0CE; } 164 | 165 | 166 | /* Buttons 167 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 168 | .button, 169 | button, 170 | input[type="submit"], 171 | input[type="reset"], 172 | input[type="button"] { 173 | display: inline-block; 174 | height: 38px; 175 | padding: 0 30px; 176 | color: #555; 177 | text-align: center; 178 | font-size: 11px; 179 | font-weight: 600; 180 | line-height: 38px; 181 | letter-spacing: .1rem; 182 | text-transform: uppercase; 183 | text-decoration: none; 184 | white-space: nowrap; 185 | background-color: transparent; 186 | border-radius: 4px; 187 | border: 1px solid #bbb; 188 | cursor: pointer; 189 | box-sizing: border-box; } 190 | .button:hover, 191 | button:hover, 192 | input[type="submit"]:hover, 193 | input[type="reset"]:hover, 194 | input[type="button"]:hover, 195 | .button:focus, 196 | button:focus, 197 | input[type="submit"]:focus, 198 | input[type="reset"]:focus, 199 | input[type="button"]:focus { 200 | color: #333; 201 | border-color: #888; 202 | outline: 0; } 203 | .button.button-primary, 204 | button.button-primary, 205 | input[type="submit"].button-primary, 206 | input[type="reset"].button-primary, 207 | input[type="button"].button-primary { 208 | color: #FFF; 209 | background-color: #33C3F0; 210 | border-color: #33C3F0; } 211 | .button.button-primary:hover, 212 | button.button-primary:hover, 213 | input[type="submit"].button-primary:hover, 214 | input[type="reset"].button-primary:hover, 215 | input[type="button"].button-primary:hover, 216 | .button.button-primary:focus, 217 | button.button-primary:focus, 218 | input[type="submit"].button-primary:focus, 219 | input[type="reset"].button-primary:focus, 220 | input[type="button"].button-primary:focus { 221 | color: #FFF; 222 | background-color: #1EAEDB; 223 | border-color: #1EAEDB; } 224 | 225 | 226 | /* Forms 227 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 228 | input[type="email"], 229 | input[type="number"], 230 | input[type="search"], 231 | input[type="text"], 232 | input[type="tel"], 233 | input[type="url"], 234 | input[type="password"], 235 | textarea, 236 | select { 237 | height: 38px; 238 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 239 | background-color: #fff; 240 | border: 1px solid #D1D1D1; 241 | border-radius: 4px; 242 | box-shadow: none; 243 | box-sizing: border-box; } 244 | /* Removes awkward default styles on some inputs for iOS */ 245 | input[type="email"], 246 | input[type="number"], 247 | input[type="search"], 248 | input[type="text"], 249 | input[type="tel"], 250 | input[type="url"], 251 | input[type="password"], 252 | textarea { 253 | -webkit-appearance: none; 254 | -moz-appearance: none; 255 | appearance: none; } 256 | textarea { 257 | min-height: 65px; 258 | padding-top: 6px; 259 | padding-bottom: 6px; } 260 | input[type="email"]:focus, 261 | input[type="number"]:focus, 262 | input[type="search"]:focus, 263 | input[type="text"]:focus, 264 | input[type="tel"]:focus, 265 | input[type="url"]:focus, 266 | input[type="password"]:focus, 267 | textarea:focus, 268 | select:focus { 269 | border: 1px solid #33C3F0; 270 | outline: 0; } 271 | label, 272 | legend { 273 | display: block; 274 | margin-bottom: .5rem; 275 | font-weight: 600; } 276 | fieldset { 277 | padding: 0; 278 | border-width: 0; } 279 | input[type="checkbox"], 280 | input[type="radio"] { 281 | display: inline; } 282 | label > .label-body { 283 | display: inline-block; 284 | margin-left: .5rem; 285 | font-weight: normal; } 286 | 287 | 288 | /* Lists 289 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 290 | ul { 291 | list-style: circle inside; } 292 | ol { 293 | list-style: decimal inside; } 294 | ol, ul { 295 | padding-left: 0; 296 | margin-top: 0; } 297 | ul ul, 298 | ul ol, 299 | ol ol, 300 | ol ul { 301 | margin: 1.5rem 0 1.5rem 3rem; 302 | font-size: 90%; } 303 | li { 304 | margin-bottom: 1rem; } 305 | 306 | 307 | /* Code 308 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 309 | code { 310 | padding: .2rem .5rem; 311 | margin: 0 .2rem; 312 | font-size: 90%; 313 | white-space: nowrap; 314 | background: #F1F1F1; 315 | border: 1px solid #E1E1E1; 316 | border-radius: 4px; } 317 | pre > code { 318 | display: block; 319 | padding: 1rem 1.5rem; 320 | white-space: pre; } 321 | 322 | 323 | /* Tables 324 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 325 | th, 326 | td { 327 | padding: 12px 15px; 328 | text-align: left; 329 | border-bottom: 1px solid #E1E1E1; } 330 | th:first-child, 331 | td:first-child { 332 | padding-left: 0; } 333 | th:last-child, 334 | td:last-child { 335 | padding-right: 0; } 336 | 337 | 338 | /* Spacing 339 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 340 | button, 341 | .button { 342 | margin-bottom: 1rem; } 343 | input, 344 | textarea, 345 | select, 346 | fieldset { 347 | margin-bottom: 1.5rem; } 348 | pre, 349 | blockquote, 350 | dl, 351 | figure, 352 | table, 353 | p, 354 | ul, 355 | ol, 356 | form { 357 | margin-bottom: 2.5rem; } 358 | 359 | 360 | /* Utilities 361 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 362 | .u-full-width { 363 | width: 100%; 364 | box-sizing: border-box; } 365 | .u-max-full-width { 366 | max-width: 100%; 367 | box-sizing: border-box; } 368 | .u-pull-right { 369 | float: right; } 370 | .u-pull-left { 371 | float: left; } 372 | 373 | 374 | /* Misc 375 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 376 | hr { 377 | margin-top: 3rem; 378 | margin-bottom: 3.5rem; 379 | border-width: 0; 380 | border-top: 1px solid #E1E1E1; } 381 | 382 | 383 | /* Clearing 384 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 385 | 386 | /* Self Clearing Goodness */ 387 | .container:after, 388 | .row:after, 389 | .u-cf { 390 | content: ""; 391 | display: table; 392 | clear: both; } 393 | 394 | 395 | /* Media Queries 396 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 397 | /* 398 | Note: The best way to structure the use of media queries is to create the queries 399 | near the relevant code. For example, if you wanted to change the styles for buttons 400 | on small devices, paste the mobile query code up in the buttons section and style it 401 | there. 402 | */ 403 | 404 | 405 | /* Larger than mobile */ 406 | @media (min-width: 400px) {} 407 | 408 | /* Larger than phablet (also point when grid becomes active) */ 409 | @media (min-width: 550px) {} 410 | 411 | /* Larger than tablet */ 412 | @media (min-width: 750px) {} 413 | 414 | /* Larger than desktop */ 415 | @media (min-width: 1000px) {} 416 | 417 | /* Larger than Desktop HD */ 418 | @media (min-width: 1200px) {} 419 | --------------------------------------------------------------------------------