diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 94f7bae0..00000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -packages/*/lib/* -packages/grunt-purgecss/tasks/purgecss.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 9559bd8c..d3a15f55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# [3.0](https://github.com/FullHuman/purgecss/compare/v2.3.0...v) (2020-09-06) + + +### Features + +* **CLI:** add blocklist option ([3961afb](https://github.com/FullHuman/purgecss/commit/3961afbc6d90eae83fe4862a4498857fa7ba3ff6)) +* add blocklist option ([04223f7](https://github.com/FullHuman/purgecss/commit/04223f7fe27f8d818961a53900a7c5293d2322b6)) +* add safelist option, replace whitelist +* add safelist keyframes and css variables ([dc59d30](https://github.com/FullHuman/purgecss/commit/dc59d309a4a4be9845c40966a19f9705c42a33a1)), closes [#418](https://github.com/FullHuman/purgecss/issues/418) [#439](https://github.com/FullHuman/purgecss/issues/439) + + # [](https://github.com/FullHuman/purgecss/compare/v2.0.1-alpha.0...v) (2019-11-23) diff --git a/README.md b/README.md index d04a94e0..a04a9447 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,10 @@ When you are building a website, chances are that you are using a css framework This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your css files. Then it matches the selectors used in your files with the one in your content files. It removes unused selectors from your css, resulting in smaller css files. +## Sponsors 🥰 + +[](tailwindcss.com) + ## Documentation You can find the PurgeCSS documentation on [this website](https://purgecss.com). @@ -28,7 +32,7 @@ You can find the PurgeCSS documentation on [this website](https://purgecss.com). - [Configuration](https://purgecss.com/configuration.html) - [Command Line Interface](https://purgecss.com/CLI.html) - [Programmatic API](https://purgecss.com/api.html) -- [Whitelisting](https://purgecss.com/whitelisting.html) +- [Safelisting](https://purgecss.com/safelisting.html) - [Extractors](https://purgecss.com/extractors.html) - [Comparison](https://purgecss.com/comparison.html) @@ -80,5 +84,5 @@ This repository is a monorepo that we manage using [Lerna](https://github.com/le | [purgecss-from-html](/packages/purgecss-from-html) | ![npm](https://img.shields.io/npm/v/purgecss-from-html?style=flat-square) | Html extractor for PurgeCSS | | [purgecss-from-js](/packages/purgecss-from-js) | ![npm](https://img.shields.io/npm/v/purgecss-from-js?style=flat-square) | Js extractor for PurgeCSS | | [purgecss-from-pug](/packages/purgecss-from-pug) | ![npm](https://img.shields.io/npm/v/purgecss-from-pug?style=flat-square) | Pug extractor for PurgeCSS | -| [purgecss-with-wordpress](/packages/purgecss-with-wordpress) | ![npm](https://img.shields.io/npm/v/purgecss-with-wordpress?style=flat-square) | Collection of whitelist items for WordPress | +| [purgecss-with-wordpress](/packages/purgecss-with-wordpress) | ![npm](https://img.shields.io/npm/v/purgecss-with-wordpress?style=flat-square) | Collection of safelist items for WordPress | | [vue-cli-plugin-purgecss](/packages/vue-cli-plugin-purgecss) | ![npm](https://img.shields.io/npm/v/@fullhuman/vue-cli-plugin-purgecss?style=flat-square) | Vue CLI Plugin for PurgeCSS | diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 45cde807..d72c1529 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -99,7 +99,7 @@ module.exports = { ["configuration", "Configuration"], ["CLI", "Command Line Interface"], ["api", "Programmatic API"], - ["whitelisting", "Whitelisting"], + ["safelisting", "Safelisting"], ["extractors", "Extractors"], ], }, diff --git a/docs/CLI.md b/docs/CLI.md index 4c9c471e..db510969 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -28,14 +28,14 @@ To see the available options for the CLI: `purgecss --help` Usage: purgecss --css --content [options] Options: - -con, --content glob of content files (comma separated) - -css, --css glob of css files (comma separated) + -con, --content glob of content files + -css, --css glob of css files -c, --config path to the configuration file -o, --output file path directory to write purged css files to -font, --font-face option to remove unused font-faces -keyframes, --keyframes option to remove unused keyframes -rejected, --rejected option to output rejected selectors - -w, --whitelist list of classes that should not be removed (comma separated) + -s, --safelist list of classes that should not be removed -h, --help display help for command ``` @@ -44,15 +44,15 @@ The options available through the CLI are similar to the ones available with a c ### --css ```text -purgecss --css css/app.css,css/palette.css --content src/index.html +purgecss --css css/app.css css/palette.css --content src/index.html ``` ### --content -You can specify content that should be analyzed by PurgeCSS with an array of filenames or [globs](https://github.com/isaacs/node-glob/blob/master/README.md#glob-primer). These files can be HTML, Pug, Blade, etc. The files should be comma separated. +You can specify content that should be analyzed by PurgeCSS with an array of filenames or [globs](https://github.com/isaacs/node-glob/blob/master/README.md#glob-primer). These files can be HTML, Pug, Blade, etc. ```text -purgecss --css css/app.css --content src/index.html,src/**/*.js +purgecss --css css/app.css --content src/index.html src/**/*.js ``` ### --config @@ -68,13 +68,13 @@ purgecss --config ./purgecss.config.js By default, the CLI outputs the result in the console. If you wish to return the CSS as files, specify the directory to write the purified CSS files to. ```text -purgecss --css css/app.css --content src/index.html,"src/**/*.js" --output build/css/ +purgecss --css css/app.css --content src/index.html "src/**/*.js" --output build/css/ ``` -### --whitelist +### --safelist -If you wish to prevent PurgeCSS from removing a specific CSS selector, you can whitelist it. +If you wish to prevent PurgeCSS from removing a specific CSS selector, you can add it to the safelist. ```text -purgecss --css css/app.css --content src/index.html --whitelist classnameToWhitelist +purgecss --css css/app.css --content src/index.html --safelist classnameToSafelist ``` diff --git a/docs/README.md b/docs/README.md index b9cac411..5174a595 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,11 +1,25 @@ --- -title: Introduction | PurgeCSS +title: PurgeCSS - Remove unused CSS lang: en-US meta: - name: description content: PurgeCSS is a tool for removing CSS that you're not actually using in your project. It can be used as part of your development workflow. PurgeCSS comes with a JavaScript API, a CLI, and plugins for popular build tools. - - name: keywords - content: PurgeCSS remove unused CSS optimization web + - itemprop: description + content: PurgeCSS is a tool for removing CSS that you're not actually using in your project. It can be used as part of your development workflow. PurgeCSS comes with a JavaScript API, a CLI, and plugins for popular build tools. + - property: og:url + content: https://purgecss.com + - property: og:site_name + content: purgecss.com + - property: og:type + content: website + - property: og:locale + content: en_US + - property: og:title + content: Remove unused CSS - PurgeCSS + - property: og:description + content: PurgeCSS is a tool for removing CSS that you're not actually using in your project. It can be used as part of your development workflow. PurgeCSS comes with a JavaScript API, a CLI, and plugins for popular build tools. + + --- # About PurgeCSS @@ -13,7 +27,12 @@ meta: PurgeCSS is a tool to remove unused CSS. It can be part of your development workflow. When you are building a website, you might decide to use a CSS framework like TailwindCSS, Bootstrap, MaterializeCSS, Foundation, etc... But you will only use a small set of the framework, and a lot of unused CSS styles will be included. -This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your css files. Then it matches the selectors used in your files with the one in your content files. It removes unused selectors from your css, resulting in smaller css files. +This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your CSS files. Then it matches the selectors used in your files with the one in your content files. It removes unused selectors from your CSS, resulting in smaller CSS files. + +## Sponsors 🥰 + +[](tailwindcss.com) + ## Table of Contents @@ -22,7 +41,7 @@ This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your - [Configuration](configuration.md) - [Command Line Interface](CLI.md) - [Programmatic API](api.md) -- [Whitelisting](whitelisting.md) +- [Safelisting](safelisting.md) - [Extractors](extractors.md) - [Comparison](comparison.md) @@ -42,3 +61,8 @@ This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your - [Next.js](guides/next.md) - [Razzle](guides/razzle.md) - [WordPress](guides/wordpress.md) + +### Common Questions + +- [How to use with CSS modules?](css_modules.md) +- [How to use with Ant Design?](ant_design.md) \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index 56b504b6..df9e8d26 100644 --- a/docs/api.md +++ b/docs/api.md @@ -64,8 +64,3 @@ interface ResultPurge { rejected?: string[]; } ``` - -::: tip - Take a look at the type definition file to get the complete information. - [Definition file](https://github.com/FullHuman/purgecss/tree/master/packages/purgecss/lib/purgecss.d.ts) -::: diff --git a/docs/configuration.md b/docs/configuration.md index 494843e8..b76a0a3e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -37,20 +37,19 @@ The options are defined by the following types: ```ts interface UserDefinedOptions { - content: Array - css: Array - defaultExtractor?: ExtractorFunction - extractors?: Array - fontFace?: boolean - keyframes?: boolean - output?: string - rejected?: boolean - stdin?: boolean - stdout?: boolean - variables?: boolean - whitelist?: string[] - whitelistPatterns?: Array - whitelistPatternsChildren?: Array + content: Array; + css: Array; + defaultExtractor?: ExtractorFunction; + extractors?: Array; + fontFace?: boolean; + keyframes?: boolean; + output?: string; + rejected?: boolean; + stdin?: boolean; + stdout?: boolean; + variables?: boolean; + safelist?: UserDefinedSafelist; + blocklist?: StringRegExpArray; } interface RawContent { @@ -61,6 +60,18 @@ interface RawContent { interface RawCSS { raw: string } + +type StringRegExpArray = Array; + +type ComplexSafelist = { + standard?: StringRegExpArray; + deep?: RegExp[]; + greedy?: RegExp[]; + variables?: StringRegExpArray; + keyframes?: StringRegExpArray; +}; + +type UserDefinedSafelist = StringRegExpArray | ComplexSafelist; ``` - **content** @@ -168,7 +179,7 @@ You can learn more about extractors [here](extractors.md). - **fontFace \(default: false\)** -If there are any unused @font-face rules in your css, you can remove them by setting the `fontFace` option to `true` +If there are any unused @font-face rules in your css, you can remove them by setting the `fontFace` option to `true`. ```js await new PurgeCSS().purge({ @@ -192,7 +203,7 @@ await new PurgeCSS().purge({ - **variables \(default: false\)** -If your are using Custom Properties (CSS variables), or a library using them such as Bootstrap, you can remove unused CSS variables by setting the `variables` option to `true`. +If you are using Custom Properties (CSS variables), or a library using them such as Bootstrap, you can remove unused CSS variables by setting the `variables` option to `true`. ```js await new PurgeCSS().purge({ @@ -204,8 +215,7 @@ await new PurgeCSS().purge({ - **rejected \(default: false\)** -It can sometimes be more practical to scan through the removed list to see if there's anything obviously wrong. -If you want to do it, use the `rejected` option. +It can sometimes be more practical to scan through the removed list to see if there's anything obviously wrong. If you want to do it, use the `rejected` option. ```js await new PurgeCSS().purge({ @@ -215,45 +225,89 @@ await new PurgeCSS().purge({ }) ``` -- **whitelist** +- **safelist** + +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the option `safelist`. + +Two forms are available for this option. + +```ts +safelist: ['random', 'yep', 'button', /^nav-/] +``` + +In this form, safelist is an array that can take a string or a regex. + +The _complex_ form is: + +```ts +safelist: { + standard: ['random', 'yep', 'button', /^nav-/], + deep: [], + greedy: [], + keyframes: [], + variables: [] +} +``` + +e.g: -You can whitelist selectors to stop PurgeCSS from removing them from your CSS. This can be accomplished with the options `whitelist` and `whitelistPatterns`. +```js +const purgecss = await new PurgeCSS().purge({ + content: [], // content + css: [], // css + safelist: ['random', 'yep', 'button'] +}) +``` + +In this example, the selectors `.random`, `#yep`, `button` will be left in the final CSS. ```js const purgecss = await new PurgeCSS().purge({ content: [], // content css: [], // css - whitelist: ['random', 'yep', 'button'] + safelist: [/red$/] }) ``` -In the example, the selectors `.random`, `#yep`, `button` will be left in the final CSS. +In this example, selectors ending with `red` such as `.bg-red` will be left in the final CSS. -- **whitelistPatterns** +- **safelist.deep** -You can whitelist selectors based on a regular expression with `whitelistPatterns`. +You can safelist selectors and their children based on a regular expression with `safelist.deep`. ```js const purgecss = await new PurgeCSS().purge({ content: [], // content css: [], // css - whitelistPatterns: [/red$/] + safelist: { + deep: [/red$/] + } }) ``` -In the example, selectors ending with `red` such as `.bg-red` will be left in the final CSS. +In this example, selectors such as `.bg-red .child-of-bg` will be left in the final CSS, even if `child-of-bg` is not found. -- **whitelistPatternsChildren** +- **safelist.greedy** -You can whitelist selectors based on a regular expression with `whitelistPatternsChildren`. Contrary to `whitelistPatterns`, it will also whitelist children of the selectors. +Finally, you can safelist whole selectors if any part of that selector matches a regular expression with `safelist.greedy`. ```js const purgecss = await new PurgeCSS().purge({ content: [], // content css: [], // css - whitelistPatternsChildren: [/red$/] + safelist: { + greedy: [/red$/] + } }) ``` -In the example, selectors such as `red p` or `.bg-red .child-of-bg` will be left in the final CSS. +In this example, selectors such as `button.bg-red.nonexistent-class` will be left in the final CSS, even if `button` and `nonexistent-class` are not found. + +- **blocklist** +Blocklist will block the CSS selectors from appearing in the final output CSS. The selectors will be removed even when they are seen as used by PurgeCSS. + +```ts +blocklist: ['usedClass', /^nav-/] +``` +Even if nav-links and usedClass are found by an extractor, they will be removed. diff --git a/docs/extractors.md b/docs/extractors.md index 99dcd163..7dcbf0f2 100644 --- a/docs/extractors.md +++ b/docs/extractors.md @@ -65,7 +65,7 @@ const purgeFromJs = (content) => { ::: warning Those extractors are still a work in progress. -It is not encourage to use them in production yet. +It is not encouraged to use them in production yet. ::: - [purgecss-from-html](https://github.com/FullHuman/purgecss/blob/master/packages/purgecss-from-html): HTML files (.html) diff --git a/docs/guides/next.md b/docs/guides/next.md index 74e30a5f..ff1d73b7 100644 --- a/docs/guides/next.md +++ b/docs/guides/next.md @@ -12,9 +12,45 @@ meta: > Next.js is a React framework for production grade applications that scale. The world's leading companies use Next.js to build server-rendered applications, static websites, and more. -You can use PurgeCSS with Next.js by using the [Next.js plugin](https://github.com/lucleray/next-purgecss) or the PostCSS plugin. +You can use PurgeCSS with Next.js by using the postCSS plugin in the Next.js configuration -## Next.js plugin +## Customize PostCSS configuration (Next.js >= 9.3) + +To customize the PostCSS configuration, create a postcss.config.json file in the root of your project. + +Add PurgeCSS to the default configuration: + +```js +{ + "plugins": [ + "postcss-flexbugs-fixes", + [ + "postcss-preset-env", + { + "autoprefixer": { + "flexbox": "no-2009" + }, + "stage": 3, + "features": { + "custom-properties": false + } + } + ], + [ + '@fullhuman/postcss-purgecss', + { + content: [ + './pages/**/*.{js,jsx,ts,tsx}', + './components/**/*.{js,jsx,ts,tsx}' + ], + defaultExtractor: content => content.match(/[\w-/:]+(? !dev && !isServer // Only enable PurgeCSS for client-side production builds + purgeCssEnabled: ({ dev, isServer }) => !dev && !isServer, // Only enable PurgeCSS for client-side production builds }) -) +); ``` #### `purgeCssPaths` @@ -78,12 +114,12 @@ directories for classnames. You can change that by defining `purgeCssPaths`. module.exports = withCss( withPurgeCss({ purgeCssPaths: [ - 'pages/**/*', - 'components/**/*', - 'other-components/**/*' // also scan other-components folder - ] + "pages/**/*", + "components/**/*", + "other-components/**/*", // also scan other-components folder + ], }) -) +); ``` #### `purgeCss` @@ -97,10 +133,10 @@ You can pass custom options to module.exports = withCss( withPurgeCss({ purgeCss: { - whitelist: () => ['my-custom-class'] - } + whitelist: () => ["my-custom-class"], + }, }) -) +); ``` The list of available options are documented in [`purgecss-webpack-plugin` @@ -108,4 +144,4 @@ docs](https://github.com/FullHuman/purgecss-webpack-plugin#options). ::: warning `purgeCss.paths` will overwrite `purgeCssPaths` -::: \ No newline at end of file +::: diff --git a/docs/guides/nuxt.md b/docs/guides/nuxt.md index fee7537e..47e26d89 100644 --- a/docs/guides/nuxt.md +++ b/docs/guides/nuxt.md @@ -26,7 +26,7 @@ to the configuration. ```js { - modules: [ + buildModules: [ // if you are using nuxt < 2.9.0, use modules property instead. 'nuxt-purgecss', ], @@ -120,6 +120,6 @@ npm i --save-dev @fullhuman/postcss-purgecss ```javascript '@fullhuman/postcss-purgecss': { content: ['./pages/**/*.vue', './layouts/**/*.vue', './components/**/*.vue'], - whitelist: ['html', 'body'] + safelist: ['html', 'body'] } ``` diff --git a/docs/guides/vue.md b/docs/guides/vue.md index 0ac85564..154e867f 100644 --- a/docs/guides/vue.md +++ b/docs/guides/vue.md @@ -45,7 +45,6 @@ Below are the PurgeCSS options set by this plugin: const contentWithoutStyleBlocks = content.replace(/<\/style>/gi, '') return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || [] }, - whitelist: [], - whitelistPatterns: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], + safelist: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], } ``` diff --git a/docs/guides/wordpress.md b/docs/guides/wordpress.md index 08cb319c..804a514c 100644 --- a/docs/guides/wordpress.md +++ b/docs/guides/wordpress.md @@ -3,14 +3,14 @@ title: WordPress | PurgeCSS lang: en-US meta: - name: description - content: PurgeCSS can be used for WordPress development. A module exists to ease the process and provide common whitelist items. + content: PurgeCSS can be used for WordPress development. A module exists to ease the process and provide common safelist items. - name: keywords content: PurgeCSS WordPress purgecss-with-wordpress remove unused css --- # WordPress -If you want to use PurgeCSS with WordPress, you might need to whitelist classes generated by WordPress to avoid them being removed by PurgeCSS. `purgecss-with-wordpress` contains the classes needed to be whitelisted. +If you want to use PurgeCSS with WordPress, you might need to safelist classes generated by WordPress to avoid them being removed by PurgeCSS. `purgecss-with-wordpress` contains the classes needed to be safelisted. ## Installation @@ -30,23 +30,19 @@ import purgecssWordpress from 'purgecss-with-wordpress' const purgeCss = new Purgecss({ content: ['**/*.html'], css: ['**/*.css'], - whitelist: purgecssWordpress.whitelist, - whitelistPatterns: purgecssWordpress.whitelistPatterns + safelist: purgecssWordpress.safelist }) const result = purgecss.purge() ``` -If you have additional classes you want to include in either of the `whitelist` or `whitelistPatterns`, you can include them using the spread operator: +If you have additional classes you want to include, you can include them using the spread operator: ```js { - whitelist: [ - ...purgecssWordpress.whitelist, + safelist: [ + ...purgecssWordpress.safelist, 'red', 'blue', - ], - whitelistPatterns: [ - ...purgecssWordpress.whitelistPatterns, /^red/, /blue$/, ] diff --git a/docs/plugins/grunt.md b/docs/plugins/grunt.md index 45d537fd..2b5521cd 100644 --- a/docs/plugins/grunt.md +++ b/docs/plugins/grunt.md @@ -62,17 +62,29 @@ Type: `Array` Purgecss can be adapted to suit your needs. If you notice a lot of unused CSS is not being removed, you might want to use a custom extractor. More information about extractors [here](https://www.purgecss.com/extractors.html). -#### options.whitelist +#### options.safelist -Type: `Array` +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the option `safelist`. -You can whitelist selectors to stop Purgecss from removing them from your CSS. This can be accomplished with the options `whitelist` and `whitelistPatterns`. +Two forms are available for this option. -#### options.whitelistPatterns +```ts +safelist: ['random', 'yep', 'button', /^nav-/] +``` + +In this form, safelist is an array that can take a string or a regex. -Type: `Array` +The _complex_ form is: -You can whitelist selectors based on a regular expression with `whitelistPatterns`. +```ts +safelist: { + standard: ['random', 'yep', 'button', /^nav-/], + deep: [], + greedy: [], + keyframes: [], + variables: [] +} +``` #### options.keyframes @@ -107,8 +119,7 @@ grunt.initConfig({ }, extension: ['html', 'blade'] }, - whitelist: ['random', 'yep', 'button'], - whitelistPatterns: [/red$/], + safelist: ['random', 'yep', 'button', /red$/], keyframes: true, fontFace: true }, diff --git a/docs/plugins/postcss.md b/docs/plugins/postcss.md index cc23d70d..21708b7e 100644 --- a/docs/plugins/postcss.md +++ b/docs/plugins/postcss.md @@ -51,18 +51,20 @@ All of the options of PurgeCSS are available to use with the plugins. You will find below the type definition of the main options available. For the complete list, go to the [PurgeCSS documentation website](https://www.purgecss.com/configuration.html#options). ```ts -interface UserDefinedOptions { - content: Array - css: Array - defaultExtractor?: ExtractorFunction - extractors?: Array - fontFace?: boolean - keyframes?: boolean - output?: string - variables?: boolean - whitelist?: string[] - whitelistPatterns?: Array - whitelistPatternsChildren?: Array +export interface UserDefinedOptions { + content?: Array; + contentFunction?: (sourceFile: string) => Array; + defaultExtractor?: ExtractorFunction; + extractors?: Array; + fontFace?: boolean; + keyframes?: boolean; + output?: string; + rejected?: boolean; + stdin?: boolean; + stdout?: boolean; + variables?: boolean; + safelist?: UserDefinedSafelist; + blocklist?: StringRegExpArray; } interface RawContent { @@ -73,4 +75,6 @@ interface RawContent { interface RawCSS { raw: string } + +type StringRegExpArray = Array; ``` diff --git a/docs/plugins/webpack.md b/docs/plugins/webpack.md index 46fc099c..1f893c1d 100644 --- a/docs/plugins/webpack.md +++ b/docs/plugins/webpack.md @@ -125,34 +125,26 @@ new PurgecssPlugin({ }) ``` -* #### whitelist, whitelistPatterns and whitelistPatternsChildren +* #### safelist -Similar as for the `paths` option, you can also define functions for the following options: +Similar as for the `paths` option, you also can define a function for this option: ```js -function collectWhitelist() { - // do something to collect the whitelist - return ['whitelisted']; -} - -function collectWhitelistPatterns() { - // do something to collect the whitelist - return [/^whitelisted-/]; -} - -function collectWhitelistPatternsChildren() { - // do something to collect the whitelist - return [/^whitelisted-/]; +function collectSafelist() { + return { + standard: ['safelisted', /^safelisted-/], + deep: [/^safelisted-deep-/], + greedy: [/^safelisted-greedy/] + } } // In the webpack configuration -new PurgecssPlugin({ - whitelist: collectWhitelist, - whitelistPatterns: collectWhitelistPatterns, - whitelistPatternsChildren: collectWhitelistPatternsChildren +new PurgeCSSPlugin({ + safelist: collectSafelist }) ``` + * #### rejected When this option is set to `true` all removed selectors are added to the [Stats Data](https://webpack.js.org/api/stats/) as `purged`. diff --git a/docs/whitelisting.md b/docs/safelisting.md similarity index 58% rename from docs/whitelisting.md rename to docs/safelisting.md index 40c0ad32..b51da7e7 100644 --- a/docs/whitelisting.md +++ b/docs/safelisting.md @@ -1,26 +1,26 @@ --- -title: Whitelisting | PurgeCSS +title: Safelisting | PurgeCSS lang: en-US meta: - name: description - content: To avoid PurgeCSS to remove unused CSS that you want to keep, you can whitelist selectors. + content: To avoid PurgeCSS to remove unused CSS that you want to keep, you can safelist selectors. - name: keywords content: PurgeCSS remove unused CSS optimization web --- -# Whitelisting +# Safelisting -You can whitelist selectors to stop PurgeCSS from removing them from your CSS. This can be accomplished with the PurgeCSS options `whitelist`, `whitelistPatterns`, `whitelistPatternsChildren`, or directly in your CSS with a special comment. +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the PurgeCSS option `safelist`, or directly in your CSS with a special comment. ## Specific selectors -You can whitelist selectors with `whitelist`. +You can add selectors to the safelist with `safelist`. ```javascript const purgecss = new Purgecss({ content: [], // content css: [], // css - whitelist: ['random', 'yep', 'button'] + safelist: ['random', 'yep', 'button'] }) ``` @@ -28,25 +28,28 @@ In the example, the selectors `.random`, `#yep`, `button` will be left in the fi ## Patterns -You can whitelist selectors based on a regular expression with `whitelistPatterns` and `whitelistPatternsChildren`. +You can safelist selectors based on a regular expression with `safelist.standard`, `safelist.deep`, and `safelist.greedy`. ```javascript const purgecss = new Purgecss({ content: [], // content css: [], // css - whitelistPatterns: [/red$/], - whitelistPatternsChildren: [/blue$/] + safelist: { + standard: [/red$/], + deep: [/blue$/], + greedy: [/yellow$/] + } }) ``` -In the example, selectors ending with `red` such as `.bg-red`, and children of selectors ending with `blue` such as `blue p` or `.bg-blue .child-of-bg`, will be left in the final CSS. +In the example, selectors ending with `red` such as `.bg-red`, selectors ending with `blue` as well as their children such as `blue p` or `.bg-blue .child-of-bg`, and selectors that have any part ending with `yellow` such as `button.bg-yellow.other-class`, will be left in the final CSS. Patterns are regular expressions. You can use [regexr](https://regexr.com) to verify the regular expressions correspond to what you are looking for. ## In the CSS directly -You can whitelist directly in your CSS with a special comment. -Use `/* purgecss ignore */` to whitelist the next rule. +You can safelist directly in your CSS with a special comment. +Use `/* purgecss ignore */` to safelist the next rule. ```css /* purgecss ignore */ @@ -55,7 +58,7 @@ h1 { } ``` -Use `/* purgecss ignore current */` to whitelist the current rule. +Use `/* purgecss ignore current */` to safelist the current rule. ```css h1 { @@ -64,7 +67,7 @@ h1 { } ``` -You can use `/* purgecss start ignore */` and `/* purgecss end ignore */` to whitelist a range of rules. +You can use `/* purgecss start ignore */` and `/* purgecss end ignore */` to safelist a range of rules. ```css /* purgecss start ignore */ diff --git a/lerna.json b/lerna.json index 75e90219..5c0f9b24 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "packages": [ "packages/*" ], - "version": "2.3.0" + "version": "3.0.0" } diff --git a/package.json b/package.json index 0ca37d77..97e3d55f 100644 --- a/package.json +++ b/package.json @@ -13,27 +13,27 @@ "vue-cli-plugin-purgecss" ], "devDependencies": { - "@types/jest": "^25.2.1", + "@types/jest": "^26.0.3", "@types/node": "^14.0.5", - "@typescript-eslint/eslint-plugin": "^2.28.0", - "@typescript-eslint/parser": "^2.28.0", - "@vuepress/plugin-google-analytics": "^1.4.1", - "@vuepress/theme-default": "^1.4.1", - "@wessberg/rollup-plugin-ts": "^1.2.24", - "conventional-changelog-cli": "^2.0.31", - "eslint": "^6.8.0", - "husky": "^4.2.5", - "jest": "^24.9.0", - "lerna": "^3.20.2", - "lint-staged": "^10.1.6", - "prettier": "^2.0.4", - "rollup": "^2.6.1", + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", + "@vuepress/plugin-google-analytics": "^1.5.4", + "@vuepress/theme-default": "^1.5.4", + "@wessberg/rollup-plugin-ts": "^1.3.4", + "conventional-changelog-cli": "^2.1.0", + "eslint": "^7.8.0", + "husky": "^4.3.0", + "jest": "^26.4.0", + "lerna": "^3.22.1", + "lint-staged": "^10.3.0", + "prettier": "^2.1.1", + "rollup": "^2.26.11", "rollup-plugin-node-resolve": "^5.2.0", - "rollup-plugin-terser": "^6.1.0", - "ts-jest": "^24.3.0", - "ts-node": "^8.8.2", - "typescript": "^3.8.3", - "vuepress": "^1.4.1" + "rollup-plugin-terser": "^7.0.1", + "ts-jest": "^26.3.0", + "ts-node": "^9.0.0", + "typescript": "^4.0.2", + "vuepress": "^1.5.4" }, "scripts": { "build": "ts-node scripts/build.ts", @@ -53,7 +53,7 @@ }, "lint-staged": { "*.js": [ - "prettier --write", + "prettier --write --ignore-path .gitignore", "git add" ], "*.ts": [ diff --git a/packages/grunt-purgecss/README.md b/packages/grunt-purgecss/README.md index 489fea7c..f770fe7e 100644 --- a/packages/grunt-purgecss/README.md +++ b/packages/grunt-purgecss/README.md @@ -67,17 +67,29 @@ Type: `Array` Purgecss can be adapted to suit your needs. If you notice a lot of unused CSS is not being removed, you might want to use a custom extractor. More information about extractors [here](https://www.purgecss.com/extractors.html). -#### options.whitelist +#### options.safelist -Type: `Array` +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the option `safelist`. -You can whitelist selectors to stop Purgecss from removing them from your CSS. This can be accomplished with the options `whitelist` and `whitelistPatterns`. +Two forms are available for this option. -#### options.whitelistPatterns +```ts +safelist: ['random', 'yep', 'button', /^nav-/] +``` + +In this form, safelist is an array that can take a string or a regex. -Type: `Array` +The _complex_ form is: -You can whitelist selectors based on a regular expression with `whitelistPatterns`. +```ts +safelist: { + standard: ['random', 'yep', 'button', /^nav-/], + deep: [], + greedy: [], + keyframes: [], + variables: [] +} +``` #### options.keyframes @@ -112,8 +124,7 @@ grunt.initConfig({ }, extension: ['html', 'blade'] }, - whitelist: ['random', 'yep', 'button'], - whitelistPatterns: [/red$/], + safelist: ['random', 'yep', 'button', /red$/] keyframes: true, fontFace: true }, diff --git a/packages/grunt-purgecss/package.json b/packages/grunt-purgecss/package.json index 86e47c70..930b5aab 100644 --- a/packages/grunt-purgecss/package.json +++ b/packages/grunt-purgecss/package.json @@ -1,6 +1,6 @@ { "name": "grunt-purgecss", - "version": "2.3.0", + "version": "3.0.0", "description": "Grunt plugin for PurgeCSS", "author": "Ffloriel", "homepage": "https://purgecss.com", @@ -30,11 +30,11 @@ "test": "echo \"Error: run tests from root\" && exit 1" }, "dependencies": { - "purgecss": "^2.3.0" + "purgecss": "^3.0.0" }, "devDependencies": { "@types/grunt": "^0.4.25", - "grunt": "~1.1.0" + "grunt": "~1.3.0" }, "bugs": { "url": "https://github.com/FullHuman/purgecss/issues" diff --git a/packages/grunt-purgecss/tasks/purgecss.js b/packages/grunt-purgecss/tasks/purgecss.js index 55333df4..e85fd7e8 100644 --- a/packages/grunt-purgecss/tasks/purgecss.js +++ b/packages/grunt-purgecss/tasks/purgecss.js @@ -1,30 +1 @@ -"use strict"; -var t, - e = require("purgecss"), - s = (t = e) && "object" == typeof t && "default" in t ? t.default : t; -function i(t, e = []) { - return e.filter( - (e) => - !!t.file.exists(e) || (t.log.warn(`Source file "${e}" not found.`), !1) - ); -} -module.exports = function (t) { - t.registerMultiTask("purgecss", "Grunt plugin for PurgeCSS", function () { - const n = this.async(), - o = this.options(e.defaultOptions); - for (const e of this.files) { - const r = i(t, e.src); - new s() - .purge({ ...o, css: r }) - .then((s) => { - if (void 0 === e.dest) throw new Error("Destination file not found"); - t.file.write(e.dest, s[0].css), - t.log.writeln(`File "${e.dest}" created.`), - n(); - }) - .catch(() => { - n(!1); - }); - } - }); -}; +"use strict";var t=require("purgecss");function e(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=e(t);function r(t,e=[]){return e.filter(e=>!!t.file.exists(e)||(t.log.warn(`Source file "${e}" not found.`),!1))}module.exports=function(e){e.registerMultiTask("purgecss","Grunt plugin for PurgeCSS",(function(){const s=this.async(),i=this.options(t.defaultOptions);for(const t of this.files){const o=r(e,t.src);(new n.default).purge({...i,css:o}).then(n=>{if(void 0===t.dest)throw new Error("Destination file not found");e.file.write(t.dest,n[0].css),e.log.writeln(`File "${t.dest}" created.`),s()}).catch(()=>{s(!1)})}}))}; diff --git a/packages/gulp-purgecss/package.json b/packages/gulp-purgecss/package.json index cfeddfab..f48d2a2e 100644 --- a/packages/gulp-purgecss/package.json +++ b/packages/gulp-purgecss/package.json @@ -1,6 +1,6 @@ { "name": "gulp-purgecss", - "version": "2.3.0", + "version": "3.0.0", "description": "Gulp plugin for purgecss", "author": "Ffloriel", "homepage": "https://purgecss.com", @@ -37,8 +37,8 @@ "dependencies": { "glob": "^7.1.6", "plugin-error": "^1.0.1", - "purgecss": "^2.3.0", - "through2": "^3.0.1" + "purgecss": "^3.0.0", + "through2": "^4.0.1" }, "devDependencies": { "@types/through2": "^2.0.34", diff --git a/packages/gulp-purgecss/src/index.ts b/packages/gulp-purgecss/src/index.ts index acb977fe..c536e4e2 100644 --- a/packages/gulp-purgecss/src/index.ts +++ b/packages/gulp-purgecss/src/index.ts @@ -60,7 +60,7 @@ function gulpPurgeCSS(options: UserDefinedOptions): internal.Transform { optionsGulp.rejected && purge.rejected ? purge.rejected.join(" {}\n") + " {}" : purge.css; - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore streamFile.contents = Buffer.from(result, "utf-8"); callback(null, file); diff --git a/packages/gulp-purgecss/src/types/index.ts b/packages/gulp-purgecss/src/types/index.ts index 6d12094b..1ae4a6b0 100644 --- a/packages/gulp-purgecss/src/types/index.ts +++ b/packages/gulp-purgecss/src/types/index.ts @@ -1,27 +1,6 @@ -export interface RawContent { - extension: string; - raw: T; -} -export interface RawCSS { - raw: string; -} -type ExtractorFunction = (content: T) => string[]; -export interface Extractors { - extensions: string[]; - extractor: ExtractorFunction; -} -export interface UserDefinedOptions { - content: Array; - defaultExtractor?: ExtractorFunction; - extractors?: Array; - fontFace?: boolean; - keyframes?: boolean; - output?: string; - rejected?: boolean; - stdin?: boolean; - stdout?: boolean; - variables?: boolean; - whitelist?: string[]; - whitelistPatterns?: Array; - whitelistPatternsChildren?: Array; +import { UserDefinedOptions as PurgeCSSUserDefinedOptions } from "../../../purgecss/src/types/index"; + +export interface UserDefinedOptions + extends Omit { + content: string[]; } diff --git a/packages/postcss-purgecss/README.md b/packages/postcss-purgecss/README.md index 8a4a8ec7..b9a18829 100644 --- a/packages/postcss-purgecss/README.md +++ b/packages/postcss-purgecss/README.md @@ -61,15 +61,39 @@ Type: `Array` Purgecss can be adapted to suit your needs. If you notice a lot of unused CSS is not being removed, you might want to use a custom extractor. More information about extractors [here](https://www.purgecss.com/extractors.html). -### `whitelist` -Type: `Array` +### `safelist` + +You can indicate which selectors are safe to leave in the final CSS. This can be accomplished with the option `safelist`. + +Two forms are available for this option. + +```ts +safelist: ['random', 'yep', 'button', /^nav-/] +``` + +In this form, safelist is an array that can take a string or a regex. -You can whitelist selectors to stop Purgecss from removing them from your CSS. This can be accomplished with the options whitelist and whitelistPatterns. +The _complex_ form is: -### `whitelistPatterns` -Type: `Array` +```ts +safelist: { + standard: ['random', 'yep', 'button', /^nav-/], + deep: [], + greedy: [], + keyframes: [], + variables: [] +} +``` + +### `blocklist` + +Blocklist will block the CSS selectors from appearing in the final output CSS. The selectors will be removed even when they are seen as used by PurgeCSS. + +```ts +blocklist: ['usedClass', /^nav-/] +``` +Even if nav-links and usedClass are found by an extractor, they will be removed. -You can whitelist selectors based on a regular expression with whitelistPatterns. ### `rejected` Type: `boolean` diff --git a/packages/postcss-purgecss/package.json b/packages/postcss-purgecss/package.json index 503b3d29..2b86706c 100644 --- a/packages/postcss-purgecss/package.json +++ b/packages/postcss-purgecss/package.json @@ -1,6 +1,6 @@ { "name": "@fullhuman/postcss-purgecss", - "version": "2.3.0", + "version": "3.0.0", "description": "PostCSS plugin for PurgeCSS", "author": "FoundrySH ", "homepage": "https://github.com/FullHuman/purgecss#readme", @@ -27,6 +27,6 @@ }, "dependencies": { "postcss": "7.0.32", - "purgecss": "^2.3.0" + "purgecss": "^3.0.0" } } diff --git a/packages/postcss-purgecss/src/index.ts b/packages/postcss-purgecss/src/index.ts index 7342cdb4..d2cf432d 100644 --- a/packages/postcss-purgecss/src/index.ts +++ b/packages/postcss-purgecss/src/index.ts @@ -1,5 +1,9 @@ import postcss from "postcss"; -import PurgeCSS, { defaultOptions, mergeExtractorSelectors } from "purgecss"; +import PurgeCSS, { + defaultOptions, + mergeExtractorSelectors, + standardizeSafelist, +} from "purgecss"; import { RawContent, UserDefinedOptions } from "./types"; @@ -11,6 +15,7 @@ const purgeCSSPlugin = postcss.plugin>( const options = { ...defaultOptions, ...opts, + safelist: standardizeSafelist(opts?.safelist), }; if (opts && typeof opts.contentFunction === "function") { diff --git a/packages/postcss-purgecss/src/types/index.ts b/packages/postcss-purgecss/src/types/index.ts index 88951bdb..20a5352b 100644 --- a/packages/postcss-purgecss/src/types/index.ts +++ b/packages/postcss-purgecss/src/types/index.ts @@ -1,3 +1,8 @@ +import { + UserDefinedSafelist, + StringRegExpArray, +} from "../../../purgecss/src/types/index"; + export interface RawContent { extension: string; raw: T; @@ -10,10 +15,10 @@ export interface Extractors { extensions: string[]; extractor: ExtractorFunction; } + export interface UserDefinedOptions { content?: Array; contentFunction?: (sourceFile: string) => Array; - css: Array; defaultExtractor?: ExtractorFunction; extractors?: Array; fontFace?: boolean; @@ -23,7 +28,6 @@ export interface UserDefinedOptions { stdin?: boolean; stdout?: boolean; variables?: boolean; - whitelist?: string[]; - whitelistPatterns?: Array; - whitelistPatternsChildren?: Array; + safelist?: UserDefinedSafelist; + blocklist?: StringRegExpArray; } diff --git a/packages/purgecss-from-html/package.json b/packages/purgecss-from-html/package.json index 120a241a..d6a24de5 100644 --- a/packages/purgecss-from-html/package.json +++ b/packages/purgecss-from-html/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-from-html", - "version": "2.2.0", + "version": "3.0.0", "description": "HTML extractor for PurgeCSS", "author": "Ffloriel", "homepage": "https://github.com/FullHuman/purgecss#readme", diff --git a/packages/purgecss-from-pug/package.json b/packages/purgecss-from-pug/package.json index a830f669..40e0d407 100644 --- a/packages/purgecss-from-pug/package.json +++ b/packages/purgecss-from-pug/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-from-pug", - "version": "2.3.0", + "version": "3.0.0", "description": "Pug extractor for PurgeCSS", "author": "Ffloriel", "homepage": "https://github.com/FullHuman/purgecss#readme", @@ -25,6 +25,6 @@ }, "devDependencies": { "pug-lexer": "^5.0.0", - "pug-parser": "^5.0.1" + "pug-parser": "^6.0.0" } } diff --git a/packages/purgecss-from-twig/package.json b/packages/purgecss-from-twig/package.json index 48663ff2..3093c5ac 100644 --- a/packages/purgecss-from-twig/package.json +++ b/packages/purgecss-from-twig/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-from-twig", - "version": "2.3.0", + "version": "3.0.0", "description": "Twig extractor for PurgeCSS", "author": "Ffloriel", "homepage": "https://github.com/FullHuman/purgecss#readme", diff --git a/packages/purgecss-webpack-plugin/README.md b/packages/purgecss-webpack-plugin/README.md index 5c1e87c0..70a7a40a 100644 --- a/packages/purgecss-webpack-plugin/README.md +++ b/packages/purgecss-webpack-plugin/README.md @@ -25,7 +25,7 @@ npm i purgecss-webpack-plugin -D const path = require('path') const glob = require('glob') const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const PurgecssPlugin = require('purgecss-webpack-plugin') +const PurgeCSSPlugin = require('purgecss-webpack-plugin') const PATHS = { src: path.join(__dirname, 'src') @@ -64,7 +64,7 @@ module.exports = { new MiniCssExtractPlugin({ filename: "[name].css", }), - new PurgecssPlugin({ + new PurgeCSSPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), }), ] @@ -73,50 +73,13 @@ module.exports = { ### Multiple paths If you need multiple paths use the npm package `glob-all` instead of `glob`, then you can use this syntax: ```javascript -new PurgecssPlugin({ +new PurgeCSSPlugin({ paths: glob.sync([ // ... ]) }), ``` to filter out directories see the glob-all documentation [here](https://www.npmjs.com/package/glob-all#filtering-out-directories). - ### Options @@ -124,24 +87,24 @@ The options available in purgecss [Configuration](https://www.purgecss.com/confi * #### paths -With the webpack plugin, you can specified the content that should be analyzed by purgecss with an array of filename. It can be html, pug, blade, ... files. You can use a module like `glob` or `glob-all` to easily get a list of files. +With the webpack plugin, you can specify the content that should be analyzed by purgecss with an array of filename. It can be html, pug, blade, ... files. You can use a module like `glob` or `glob-all` to easily get a list of files. ```js -const PurgecssPlugin = require('purgecss-webpack-plugin') +const PurgeCSSPlugin = require('purgecss-webpack-plugin') const glob = require('glob') const PATHS = { src: path.join(__dirname, 'src') } // In the webpack configuration -new PurgecssPlugin({ +new PurgeCSSPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }) }) ``` If you want to regenerate the paths list on every compilation (e.g. with `--watch`), then you can also pass a function: ```js -new PurgecssPlugin({ +new PurgeCSSPlugin({ paths: () => glob.sync(`${PATHS.src}/**/*`, { nodir: true }) }) ``` @@ -151,36 +114,28 @@ new PurgecssPlugin({ You can specify entrypoints to the purgecss-webpack-plugin with the option only: ```js -new PurgecssPlugin({ +new PurgeCSSPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), only: ['bundle', 'vendor'] }) ``` -* #### whitelist, whitelistPatterns and whitelistPatternsChildren +* #### safelist -Similar as for the `paths` option, you also can define functions for the these options: +Similar as for the `paths` option, you also can define a function for this option: ```js -function collectWhitelist() { - // do something to collect the whitelist - return ['whitelisted']; -} -function collectWhitelistPatterns() { - // do something to collect the whitelist - return [/^whitelisted-/]; -} - -function collectWhitelistPatternsChildren() { - // do something to collect the whitelist - return [/^whitelisted-/]; +function collectSafelist() { + return { + standard: ['safelisted', /^safelisted-/], + deep: [/^safelisted-deep-/], + greedy: [/^safelisted-greedy/] + } } // In the webpack configuration -new PurgecssPlugin({ - whitelist: collectWhitelist, - whitelistPatterns: collectWhitelistPatterns, - whitelistPatternsChildren: collectWhitelistPatternsChildren +new PurgeCSSPlugin({ + safelist: collectSafelist }) ``` diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/expected/styles.css b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/expected/styles.css similarity index 62% rename from packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/expected/styles.css rename to packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/expected/styles.css index 4a1473b6..74f173a3 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/expected/styles.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/expected/styles.css @@ -2,15 +2,15 @@ color: red; } -.whitelisted { +.safelisted { color: green; } -.whitelistedPattern { +.safelistedPattern { color: rebeccapurple; } -.whitelistedPatternChildren > p a { +.safelistedPatternChildren > p a { color: orange; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/content.html b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/content.html similarity index 100% rename from packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/content.html rename to packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/content.html diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/index.js b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/index.js new file mode 100644 index 00000000..4fe51c72 --- /dev/null +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/index.js @@ -0,0 +1 @@ +import "./style.css"; diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/style.css b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/style.css similarity index 67% rename from packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/style.css rename to packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/style.css index a16dad3e..6eee65a3 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/style.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/src/style.css @@ -6,15 +6,15 @@ color: blue; } -.whitelisted { +.safelisted { color: green; } -.whitelistedPattern { +.safelistedPattern { color: rebeccapurple; } -.whitelistedPatternChildren > p a { +.safelistedPatternChildren > p a { color: orange; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/webpack.config.js new file mode 100644 index 00000000..b42fd71c --- /dev/null +++ b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-safelist-functions/webpack.config.js @@ -0,0 +1,57 @@ +const path = require("path"); +const glob = require("glob"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const PurgecssPlugin = require("../../../src/").default; + +const customExtractor = (content) => content.match(/[A-z0-9-:/]+/g) || []; + +const PATHS = { + src: path.join(__dirname, "src"), +}; + +module.exports = { + mode: "development", + entry: "./src/index.js", + context: path.resolve(__dirname), + optimization: { + splitChunks: { + cacheGroups: { + styles: { + name: "styles", + test: /\.css$/, + chunks: "all", + enforce: true, + }, + }, + }, + }, + module: { + rules: [ + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "[name].css", + }), + new PurgecssPlugin({ + paths: () => glob.sync(`${PATHS.src}/*`), + safelist: () => { + return { + standard: ["safelisted", /^safelistedPat/], + deep: [/^safelistedPatternChildren/], + greedy: [/^safelistedPatternGreedy/], + }; + }, + extractors: [ + { + extractor: customExtractor, + extensions: ["html", "js"], + }, + ], + }), + ], +}; diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js deleted file mode 100644 index 47cfd8d3..00000000 --- a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/webpack.config.js +++ /dev/null @@ -1,53 +0,0 @@ -const path = require('path') -const glob = require('glob') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const PurgecssPlugin = require('../../../src/').default - -const customExtractor = (content) => content.match(/[A-z0-9-:/]+/g) || []; - -const PATHS = { - src: path.join(__dirname, 'src') -} - -module.exports = { - mode: 'development', - entry: './src/index.js', - context: path.resolve(__dirname), - optimization: { - splitChunks: { - cacheGroups: { - styles: { - name: 'styles', - test: /\.css$/, - chunks: 'all', - enforce: true - } - } - } - }, - module: { - rules: [ - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader'] - } - ] - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: '[name].css' - }), - new PurgecssPlugin({ - paths: () => glob.sync(`${PATHS.src}/*`), - whitelist: () => ['whitelisted'], - whitelistPatterns: () => [/^whitelistedPat/], - whitelistPatternsChildren: () => [/^whitelistedPatternChildren/], - extractors: [ - { - extractor: customExtractor, - extensions: ['html', 'js'] - } - ] - }) - ] -} diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/styles.css b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/styles.css index 3ca96dce..11fc3e1a 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/styles.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/expected/styles.css @@ -6,7 +6,7 @@ color: blue; } -.whitelisted { +.safelisted { color: green; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style_that_we_want_to_purge.css b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style_that_we_want_to_purge.css index 065927db..e2d156dd 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style_that_we_want_to_purge.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/src/style_that_we_want_to_purge.css @@ -6,7 +6,7 @@ color: blue; } -.whitelisted { +.safelisted { color: green; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js index 6703dcec..0571262c 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple-with-exclusion/webpack.config.js @@ -1,55 +1,55 @@ -const path = require('path') -const glob = require('glob') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const PurgecssPlugin = require('../../../src/').default +const path = require("path"); +const glob = require("glob"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const PurgecssPlugin = require("../../../src/").default; -const customExtractor = (content) => content.match(/[A-z0-9-:/]+/g) || [] +const customExtractor = (content) => content.match(/[A-z0-9-:/]+/g) || []; const PATHS = { - src: path.join(__dirname, 'src') -} + src: path.join(__dirname, "src"), +}; module.exports = { - mode: 'development', - entry: { - bundle: './src/index.js', - legacy: './src/legacy.js' + mode: "development", + entry: { + bundle: "./src/index.js", + legacy: "./src/legacy.js", + }, + optimization: { + splitChunks: { + cacheGroups: { + styles: { + name: "styles", + test: /\.css$/, + chunks: "all", + enforce: true, + }, + }, }, - optimization: { - splitChunks: { - cacheGroups: { - styles: { - name: 'styles', - test: /\.css$/, - chunks: 'all', - enforce: true - } - } - } - }, - module: { - rules: [ - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader'] - } - ] - }, - plugins: [ - new MiniCssExtractPlugin({ - filename: '[name].css' - }), - new PurgecssPlugin({ - paths: glob.sync(`${PATHS.src}/*`), - styleExtensions: ['.css'], - whitelist: ['whitelisted'], - only: ['bundle'], - extractors: [ - { - extractor: customExtractor, - extensions: ['html', 'js'] - } - ] - }) - ] -} + }, + module: { + rules: [ + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "[name].css", + }), + new PurgecssPlugin({ + paths: glob.sync(`${PATHS.src}/*`), + styleExtensions: [".css"], + safelist: ["safelisted"], + only: ["bundle"], + extractors: [ + { + extractor: customExtractor, + extensions: ["html", "js"], + }, + ], + }), + ], +}; diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple/expected/styles.css b/packages/purgecss-webpack-plugin/__tests__/cases/simple/expected/styles.css index ffac9135..88f614e7 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple/expected/styles.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple/expected/styles.css @@ -2,7 +2,7 @@ color: red; } -.whitelisted { +.safelisted { color: green; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/style.css b/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/style.css index 065927db..e2d156dd 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/style.css +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple/src/style.css @@ -6,7 +6,7 @@ color: blue; } -.whitelisted { +.safelisted { color: green; } diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js b/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js index 78bbc824..965d143d 100644 --- a/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js +++ b/packages/purgecss-webpack-plugin/__tests__/cases/simple/webpack.config.js @@ -1,54 +1,54 @@ -const path = require('path') -const glob = require('glob') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const PurgecssPlugin = require('../../../src/').default +const path = require("path"); +const glob = require("glob"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const PurgecssPlugin = require("../../../src/").default; const customExtractor = (content) => { - const res = content.match(/[A-z0-9-:/]+/g) || [] - // console.log('hel', res) - return res + const res = content.match(/[A-z0-9-:/]+/g) || []; + // console.log('hel', res) + return res; }; const PATHS = { - src: path.join(__dirname, 'src') -} + src: path.join(__dirname, "src"), +}; module.exports = { - mode: 'development', - optimization: { - splitChunks: { - cacheGroups: { - styles: { - name: 'styles', - test: /\.css$/, - chunks: 'all', - enforce: true - } - } - } - }, - entry: './src/index.js', - module: { - rules: [ - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader'] - } - ] + mode: "development", + optimization: { + splitChunks: { + cacheGroups: { + styles: { + name: "styles", + test: /\.css$/, + chunks: "all", + enforce: true, + }, + }, }, - plugins: [ - new MiniCssExtractPlugin({ - filename: '[name].css' - }), - new PurgecssPlugin({ - paths: glob.sync(`${PATHS.src}/*`), - whitelist: ['whitelisted'], - extractors: [ - { - extractor: customExtractor, - extensions: ['html', 'js'] - } - ] - }) - ] -} + }, + entry: "./src/index.js", + module: { + rules: [ + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "[name].css", + }), + new PurgecssPlugin({ + paths: glob.sync(`${PATHS.src}/*`), + safelist: ["safelisted"], + extractors: [ + { + extractor: customExtractor, + extensions: ["html", "js"], + }, + ], + }), + ], +}; diff --git a/packages/purgecss-webpack-plugin/__tests__/index.test.ts b/packages/purgecss-webpack-plugin/__tests__/index.test.ts index 26482567..50189e0c 100644 --- a/packages/purgecss-webpack-plugin/__tests__/index.test.ts +++ b/packages/purgecss-webpack-plugin/__tests__/index.test.ts @@ -34,7 +34,7 @@ describe("Webpack integration", () => { }); const cases: string[] = [ - "path-and-whitelist-functions", + "path-and-safelist-functions", "simple", "simple-with-exclusion", ]; diff --git a/packages/purgecss-webpack-plugin/__tests__/search.test.ts b/packages/purgecss-webpack-plugin/__tests__/search.test.ts index 16a2592e..03799128 100644 --- a/packages/purgecss-webpack-plugin/__tests__/search.test.ts +++ b/packages/purgecss-webpack-plugin/__tests__/search.test.ts @@ -1,5 +1,5 @@ import { getAssets, files } from "../src/search"; -import { File } from "../src/types"; +import { Chunk, File } from "../src/types"; describe("Search assets", () => { it("returns matches based on a pattern", () => { @@ -46,30 +46,12 @@ describe("Search assets", () => { }); }); -interface Chunk { - modulesIterable?: string[] | File[]; -} - describe("Search files", () => { let chunk: Chunk; beforeEach(() => { - chunk = {}; - }); - - it("returns matches based on extension", () => { - chunk.modulesIterable = ["foobar.txt", "barbar.css"]; - const extensions = [".txt"]; - const matches = ["foobar.txt"]; - - expect(files(chunk, extensions, (a: string) => a)).toEqual(matches); - }); - - it("does not fail with missing modules", () => { - chunk.modulesIterable = ["foobar.txt", "", "barbar.css"]; - const extensions = [".txt"]; - const matches = ["foobar.txt"]; - - expect(files(chunk, extensions, (a: string) => a)).toEqual(matches); + chunk = { + modulesIterable: [], + }; }); it("returns matches based on extension with a customized getter", () => { diff --git a/packages/purgecss-webpack-plugin/package.json b/packages/purgecss-webpack-plugin/package.json index e394cd0b..ec591cd4 100644 --- a/packages/purgecss-webpack-plugin/package.json +++ b/packages/purgecss-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-webpack-plugin", - "version": "2.3.0", + "version": "3.0.0", "description": "PurgeCSS plugin for webpack - Remove unused css", "author": "Ffloriel", "homepage": "https://purgecss.com", @@ -36,7 +36,7 @@ "test": "echo \"Error: run tests from root\" && exit 1" }, "dependencies": { - "purgecss": "^2.3.0", + "purgecss": "^3.0.0", "webpack": "^4.42.1", "webpack-sources": "^1.4.3" }, @@ -46,8 +46,8 @@ "devDependencies": { "@types/webpack": "^4.41.12", "@types/webpack-sources": "^1.4.0", - "css-loader": "^3.5.2", - "mini-css-extract-plugin": "^0.9.0" + "css-loader": "^4.0.0", + "mini-css-extract-plugin": "^0.11.0" }, "peerDependencies": { "webpack": "*" diff --git a/packages/purgecss-webpack-plugin/src/index.ts b/packages/purgecss-webpack-plugin/src/index.ts index 6e8ad031..f6b59943 100644 --- a/packages/purgecss-webpack-plugin/src/index.ts +++ b/packages/purgecss-webpack-plugin/src/index.ts @@ -35,7 +35,7 @@ export default class PurgeCSSPlugin { } if (this.options.rejected) { - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore stats["purged"] = this.purgedStats; } @@ -107,14 +107,8 @@ export default class PurgeCSSPlugin { ], }; - if (typeof options.whitelist === "function") { - options.whitelist = options.whitelist(); - } - if (typeof options.whitelistPatterns === "function") { - options.whitelistPatterns = options.whitelistPatterns(); - } - if (typeof options.whitelistPatternsChildren === "function") { - options.whitelistPatternsChildren = options.whitelistPatternsChildren(); + if (typeof options.safelist === "function") { + options.safelist = options.safelist(); } const purgecss = await new PurgeCSS().purge({ @@ -127,9 +121,7 @@ export default class PurgeCSSPlugin { output: options.output, rejected: options.rejected, variables: options.variables, - whitelist: options.whitelist, - whitelistPatterns: options.whitelistPatterns, - whitelistPatternsChildren: options.whitelistPatternsChildren, + safelist: options.safelist, }); const purged = purgecss[0]; diff --git a/packages/purgecss-webpack-plugin/src/search.ts b/packages/purgecss-webpack-plugin/src/search.ts index 933ae184..c4091730 100644 --- a/packages/purgecss-webpack-plugin/src/search.ts +++ b/packages/purgecss-webpack-plugin/src/search.ts @@ -1,5 +1,5 @@ import path from "path"; -import { PurgeAsset } from "./types"; +import { Chunk, File, PurgeAsset } from "./types"; /** * Get the filename without ?hash @@ -50,10 +50,9 @@ export function getAssets( } export function files( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - chunk: any, + chunk: Chunk, extensions: string[], - getter: Function + getter: (file: File) => string | undefined ): string[] { const mods = []; for (const module of Array.from(chunk.modulesIterable || [])) { diff --git a/packages/purgecss-webpack-plugin/src/types/index.ts b/packages/purgecss-webpack-plugin/src/types/index.ts index 52d7d8f7..c23072b2 100644 --- a/packages/purgecss-webpack-plugin/src/types/index.ts +++ b/packages/purgecss-webpack-plugin/src/types/index.ts @@ -1,3 +1,8 @@ +import { + ComplexSafelist, + StringRegExpArray, +} from "./../../../purgecss/src/types/index"; + export interface RawContent { extension: string; raw: T; @@ -12,8 +17,8 @@ export interface Extractors { } type PathFunction = () => string[]; -type WhitelistFunction = () => string[]; -type WhitelistPatternsFunction = () => Array; + +type SafelistFunction = () => ComplexSafelist; export interface UserDefinedOptions { paths: string[] | PathFunction; @@ -28,9 +33,8 @@ export interface UserDefinedOptions { stdout?: boolean; variables?: boolean; verbose?: boolean; - whitelist?: string[] | WhitelistFunction; - whitelistPatterns?: Array | WhitelistPatternsFunction; - whitelistPatternsChildren?: Array | WhitelistPatternsFunction; + safelist?: StringRegExpArray | ComplexSafelist | SafelistFunction; + blocklist?: StringRegExpArray; only?: string[]; } @@ -48,3 +52,7 @@ export interface PurgeAsset { export interface File { resource?: string; } + +export type Chunk = { + modulesIterable: File[]; +}; diff --git a/packages/purgecss-with-wordpress/README.md b/packages/purgecss-with-wordpress/README.md index fc9d9109..11bc47c3 100644 --- a/packages/purgecss-with-wordpress/README.md +++ b/packages/purgecss-with-wordpress/README.md @@ -24,21 +24,21 @@ import purgecssWordpress from 'purgecss-with-wordpress' const purgeCSSResults = await new PurgeCSS().purge({ content: ['**/*.html'], css: ['**/*.css'], - whitelist: purgecssWordpress.whitelist, - whitelistPatterns: purgecssWordpress.whitelistPatterns + safelist: purgecssWordpress.safelist, + safelistPatterns: purgecssWordpress.safelistPatterns }) ``` -If you have additional classes you want to include in either of the `whitelist` or `whitelistPatterns`, you can include them using the spread operator: +If you have additional classes you want to include in either of the `safelist` or `safelistPatterns`, you can include them using the spread operator: ```js -whitelist: [ - ...purgecssWordpress.whitelist, +safelist: [ + ...purgecssWordpress.safelist, 'red', 'blue', ], -whitelistPatterns: [ - ...purgecssWordpress.whitelistPatterns, +safelistPatterns: [ + ...purgecssWordpress.safelistPatterns, /^red/, /blue$/, ] diff --git a/packages/purgecss-with-wordpress/index.js b/packages/purgecss-with-wordpress/index.js index 56933f14..47f40988 100644 --- a/packages/purgecss-with-wordpress/index.js +++ b/packages/purgecss-with-wordpress/index.js @@ -1,26 +1,24 @@ module.exports = { - whitelist: [ - 'rtl', - 'home', - 'blog', - 'archive', - 'date', - 'error404', - 'logged-in', - 'admin-bar', - 'no-customize-support', - 'custom-background', - 'wp-custom-logo', - 'alignnone', - 'alignright', - 'alignleft', - 'wp-caption', - 'wp-caption-text', - 'screen-reader-text', - 'comment-list', - 'wp-social-link', - ], - whitelistPatterns: [ + safelist: [ + "rtl", + "home", + "blog", + "archive", + "date", + "error404", + "logged-in", + "admin-bar", + "no-customize-support", + "custom-background", + "wp-custom-logo", + "alignnone", + "alignright", + "alignleft", + "wp-caption", + "wp-caption-text", + "screen-reader-text", + "comment-list", + "wp-social-link", /^search(-.*)?$/, /^(.*)-template(-.*)?$/, /^(.*)?-?single(-.*)?$/, diff --git a/packages/purgecss-with-wordpress/package.json b/packages/purgecss-with-wordpress/package.json index 3831dce8..dce82ba4 100644 --- a/packages/purgecss-with-wordpress/package.json +++ b/packages/purgecss-with-wordpress/package.json @@ -1,6 +1,6 @@ { "name": "purgecss-with-wordpress", - "version": "2.3.0", + "version": "3.0.0", "description": "PurgeCSS with wordpress", "author": "Ffloriel", "homepage": "https://purgecss.com", diff --git a/packages/purgecss/README.md b/packages/purgecss/README.md index 48c10de6..a685a5bd 100644 --- a/packages/purgecss/README.md +++ b/packages/purgecss/README.md @@ -17,9 +17,9 @@ When you are building a website, chances are that you are using a css framework This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your css files. Then it matches the selectors used in your files with the one in your content files. It removes unused selectors from your css, resulting in smaller css files. -## Version 1 +## Sponsors 🥰 -If you are looking for [PurgeCSS 1](https://github.com/FullHuman/purgecss/tree/v1), the documentation is [here](https://v1.purgecss.com) +[](tailwindcss.com) ## Documentation @@ -32,7 +32,7 @@ You can find the PurgeCSS documentation on [this website](https://purgecss.com). - [Configuration](https://purgecss.com/configuration.html) - [Command Line Interface](https://purgecss.com/CLI.html) - [Programmatic API](https://purgecss.com/api.html) -- [Whitelisting](https://purgecss.com/whitelisting.html) +- [Safelisting](https://purgecss.com/safelisting.html) - [Extractors](https://purgecss.com/extractors.html) - [Comparison](https://purgecss.com/comparison.html) diff --git a/packages/purgecss/__tests__/attributes.test.ts b/packages/purgecss/__tests__/attributes.test.ts index 734fa4dd..87bcf189 100644 --- a/packages/purgecss/__tests__/attributes.test.ts +++ b/packages/purgecss/__tests__/attributes.test.ts @@ -1,17 +1,16 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("attributes", () => { let purgedCSS: string; - beforeAll(async (done) => { + beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}attribute_selector/attribute_selector.html`], - css: [`${root}attribute_selector/attribute_selector.css`], + content: [`${ROOT_TEST_EXAMPLES}attributes/attribute_selector.html`], + css: [`${ROOT_TEST_EXAMPLES}attributes/attribute_selector.css`], }); purgedCSS = resultsPurge[0].css; - done(); }); it("always keep attribute when attribute is 'value'", () => { diff --git a/packages/purgecss/__tests__/chaining-rules.test.ts b/packages/purgecss/__tests__/chaining-rules.test.ts index af9ed184..71ba9c37 100644 --- a/packages/purgecss/__tests__/chaining-rules.test.ts +++ b/packages/purgecss/__tests__/chaining-rules.test.ts @@ -1,27 +1,20 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES, notFindInCSS } from "./utils"; describe("chaining rules", () => { let purgedCSS: string; - beforeAll(async (done) => { + beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}chaining_rules/index.html`], - css: [`${root}chaining_rules/index.css`], + content: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.html`], + css: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.css`], }); purgedCSS = resultsPurge[0].css; - done(); }); it("keeps parent1 selector", () => { expect(purgedCSS.includes("parent1")).toBe(true); }); - it("removes parent3 selector", () => { - expect(purgedCSS.includes("parent3")).toBe(false); - }); - it("removes d33ef1 selector", () => { - expect(purgedCSS.includes("d33ef1")).toBe(false); - }); - it("removes .parent2", () => { - expect(purgedCSS.includes("parent2")).toBe(false); + it("removes parent3, d33ef1, .parent2", () => { + notFindInCSS(expect, ["parent3", "d33ef1", "parent2"], purgedCSS); }); }); diff --git a/packages/purgecss/__tests__/cli.test.ts b/packages/purgecss/__tests__/cli.test.ts new file mode 100644 index 00000000..d1b6132e --- /dev/null +++ b/packages/purgecss/__tests__/cli.test.ts @@ -0,0 +1,17 @@ +import { promisify } from "util"; +import { exec } from "child_process"; +import path from "path"; + +const asyncExec = promisify(exec); + +describe("PurgeCSS CLI", () => { + const purgeCSSExecutable = path.resolve(__dirname, "./../bin/purgecss.js"); + const testFolder = path.resolve(__dirname, "./test_examples/cli/simple/"); + it("should print the correct output", async () => { + const response = await asyncExec( + `${purgeCSSExecutable} --content ${testFolder}/src/content.html ${testFolder}/src/*.js --css ${testFolder}/src/style.css` + ); + const result = JSON.parse(response.stdout); + expect(result[0].css).toBe(".hello {\n color: red;\n}\n"); + }); +}); diff --git a/packages/purgecss/__tests__/comments.test.ts b/packages/purgecss/__tests__/comments.test.ts index ea4fa186..fe8cea35 100644 --- a/packages/purgecss/__tests__/comments.test.ts +++ b/packages/purgecss/__tests__/comments.test.ts @@ -1,13 +1,12 @@ import PurgeCSS from "./../src/index"; - -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES, findInCSS } from "./utils"; describe("ignore comment", () => { let purgedCSS: string; beforeAll(async (done) => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}ignore_comment/ignore_comment.html`], - css: [`${root}ignore_comment/ignore_comment.css`], + content: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment.html`], + css: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment.css`], }); purgedCSS = resultsPurge[0].css; done(); @@ -27,16 +26,14 @@ describe("ignore comment range", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}ignore_comment_range/index.html`], - css: [`${root}ignore_comment_range/index.css`], + content: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment_range.html`], + css: [`${ROOT_TEST_EXAMPLES}comments/ignore_comment_range.css`], }); purgedCSS = resultsPurge[0].css; }); it("ignores h1, h3, h5, h6", () => { - ["h1", "h3", "h5", "h6"].forEach((selector) => { - expect(purgedCSS.includes(selector)).toBe(true); - }); + findInCSS(expect, ["h1", "h3", "h5", "h6"], purgedCSS); }); it("removes h4", () => { diff --git a/packages/purgecss/__tests__/css-variables.test.ts b/packages/purgecss/__tests__/css-variables.test.ts index 02f38880..7c5b44ef 100644 --- a/packages/purgecss/__tests__/css-variables.test.ts +++ b/packages/purgecss/__tests__/css-variables.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("purge unused css variables", () => { let purgedCSS: string; beforeAll(async (done) => { const resultPurge = await new PurgeCSS().purge({ - content: [`${root}variables/variables.html`], - css: [`${root}variables/variables.css`], + content: [`${ROOT_TEST_EXAMPLES}css-variables/variables.html`], + css: [`${ROOT_TEST_EXAMPLES}css-variables/variables.css`], variables: true, }); purgedCSS = resultPurge[0].css; diff --git a/packages/purgecss/__tests__/delimited.test.ts b/packages/purgecss/__tests__/delimited.test.ts index 44cfa8e5..9f209a59 100644 --- a/packages/purgecss/__tests__/delimited.test.ts +++ b/packages/purgecss/__tests__/delimited.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("delimited", () => { let purgedCSS: string; beforeAll(async (done) => { const resultPurge = await new PurgeCSS().purge({ - content: [`${root}delimited/delimited.html`], - css: [`${root}delimited/delimited.css`], + content: [`${ROOT_TEST_EXAMPLES}delimited/delimited.html`], + css: [`${ROOT_TEST_EXAMPLES}delimited/delimited.css`], }); purgedCSS = resultPurge[0].css; done(); diff --git a/packages/purgecss/__tests__/font-faces.test.ts b/packages/purgecss/__tests__/font-faces.test.ts index 2d7097d7..16dbd36b 100644 --- a/packages/purgecss/__tests__/font-faces.test.ts +++ b/packages/purgecss/__tests__/font-faces.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("purge unused font-face", () => { let purgedCSS: string; beforeAll(async (done) => { const resultPurge = await new PurgeCSS().purge({ - content: [`${root}font_face/font_face.html`], - css: [`${root}font_face/font_face.css`], + content: [`${ROOT_TEST_EXAMPLES}font-faces/font_face.html`], + css: [`${ROOT_TEST_EXAMPLES}font-faces/font_face.css`], fontFace: true, }); purgedCSS = resultPurge[0].css; diff --git a/packages/purgecss/__tests__/index.test.ts b/packages/purgecss/__tests__/index.test.ts index 4414454c..f3056376 100644 --- a/packages/purgecss/__tests__/index.test.ts +++ b/packages/purgecss/__tests__/index.test.ts @@ -1,7 +1,7 @@ import PurgeCSS from "./../src/index"; import { ExtractorResult } from "../src/types"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("purgecss with config file", () => { it("initialize without error with a config file specified", () => { @@ -26,8 +26,8 @@ describe("filters out unused selectors", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}remove_unused/remove_unused.js`], - css: [`${root}remove_unused/remove_unused.css`], + content: [`${ROOT_TEST_EXAMPLES}others/remove_unused.js`], + css: [`${ROOT_TEST_EXAMPLES}others/remove_unused.css`], }); purgedCSS = resultsPurge[0].css; }); @@ -51,8 +51,8 @@ describe("special characters, with custom Extractor", () => { beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}special_characters/special_characters.js`], - css: [`${root}special_characters/special_characters.css`], + content: [`${ROOT_TEST_EXAMPLES}others/special_characters.js`], + css: [`${ROOT_TEST_EXAMPLES}others/special_characters.css`], extractors: [ { extractor: CustomExtractor, diff --git a/packages/purgecss/__tests__/keyframes.test.ts b/packages/purgecss/__tests__/keyframes.test.ts index 9ecc0124..4cda3c3b 100644 --- a/packages/purgecss/__tests__/keyframes.test.ts +++ b/packages/purgecss/__tests__/keyframes.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("keyframes", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}keyframes/keyframes.html`], - css: [`${root}keyframes/keyframes.css`], + content: [`${ROOT_TEST_EXAMPLES}keyframes/keyframes.html`], + css: [`${ROOT_TEST_EXAMPLES}keyframes/keyframes.css`], keyframes: true, }); purgedCSS = resultsPurge[0].css; @@ -28,8 +28,8 @@ describe("purge unused keyframe animations", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}keyframes/index.html`], - css: [`${root}keyframes/index.css`], + content: [`${ROOT_TEST_EXAMPLES}keyframes/index.html`], + css: [`${ROOT_TEST_EXAMPLES}keyframes/index.css`], keyframes: true, }); purgedCSS = resultsPurge[0].css; @@ -46,8 +46,8 @@ describe("do not purge keyframes if option set to false", () => { let purgedCSS: string; beforeAll(async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}keyframes/index.html`], - css: [`${root}keyframes/index.css`], + content: [`${ROOT_TEST_EXAMPLES}keyframes/index.html`], + css: [`${ROOT_TEST_EXAMPLES}keyframes/index.css`], keyframes: false, }); purgedCSS = resultsPurge[0].css; diff --git a/packages/purgecss/__tests__/media-queries.test.ts b/packages/purgecss/__tests__/media-queries.test.ts index 476c24c1..6439f1b7 100644 --- a/packages/purgecss/__tests__/media-queries.test.ts +++ b/packages/purgecss/__tests__/media-queries.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("media queries", () => { let purgecssResult: string; beforeAll(async (done) => { const purgecss = await new PurgeCSS().purge({ - content: [`${root}media_queries/media_queries.html`], - css: [`${root}media_queries/media_queries.css`], + content: [`${ROOT_TEST_EXAMPLES}media-queries/media_queries.html`], + css: [`${ROOT_TEST_EXAMPLES}media-queries/media_queries.css`], }); purgecssResult = purgecss[0].css; done(); diff --git a/packages/purgecss/__tests__/pseudo-class.test.ts b/packages/purgecss/__tests__/pseudo-class.test.ts index ef7415c3..759946b4 100644 --- a/packages/purgecss/__tests__/pseudo-class.test.ts +++ b/packages/purgecss/__tests__/pseudo-class.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe(":not pseudo class", () => { let purgedCSS: string; beforeAll(async (done) => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}not/not.html`], - css: [`${root}not/not.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/not.html`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/not.css`], }); purgedCSS = resultsPurge[0].css; done(); @@ -25,8 +25,8 @@ describe("pseudo selectors", () => { let purgedCSS: string; beforeAll(async (done) => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}pseudo_selector/pseudo_selector.html`], - css: [`${root}pseudo_selector/pseudo_selector.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_selector.html`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_selector.css`], }); purgedCSS = resultsPurge[0].css; done(); @@ -68,8 +68,8 @@ describe("nth-child", () => { let purgedCSS: string; beforeAll(async (done) => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}nth_child/nth_child.html`], - css: [`${root}nth_child/nth_child.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/nth_child.html`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/nth_child.css`], }); purgedCSS = resultsPurge[0].css; done(); @@ -88,8 +88,8 @@ describe("nth-child", () => { describe("pseudo classes", () => { it("finds div:before", async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}pseudo_class/pseudo_class.js`], - css: [`${root}pseudo_class/pseudo_class.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_class.js`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_class.css`], }); const purgedCSS = resultsPurge[0].css; expect(purgedCSS.includes("div:before")).toBe(true); @@ -97,8 +97,8 @@ describe("pseudo classes", () => { it("removes row:after", async () => { const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}pseudo_class/pseudo_class.js`], - css: [`${root}pseudo_class/pseudo_class.css`], + content: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_class.js`], + css: [`${ROOT_TEST_EXAMPLES}pseudo-class/pseudo_class.css`], }); const purgedCSS = resultsPurge[0].css; expect(purgedCSS.includes("row:after")).toBe(false); diff --git a/packages/purgecss/__tests__/rejected.test.ts b/packages/purgecss/__tests__/rejected.test.ts index e062100b..762906f2 100644 --- a/packages/purgecss/__tests__/rejected.test.ts +++ b/packages/purgecss/__tests__/rejected.test.ts @@ -1,13 +1,13 @@ import PurgeCSS from "./../src/index"; -const root = "./packages/purgecss/__tests__/test_examples/"; +import { ROOT_TEST_EXAMPLES } from "./utils"; describe("rejected", () => { it("does not return the rejected selectors if rejected set to false", async (done) => { expect.assertions(1); const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}simple/simple.js`], - css: [`${root}simple/simple.css`], + content: [`${ROOT_TEST_EXAMPLES}rejected/simple.js`], + css: [`${ROOT_TEST_EXAMPLES}rejected/simple.css`], }); expect(resultsPurge[0].rejected).toBe(undefined); done(); @@ -16,8 +16,8 @@ describe("rejected", () => { it("returns an empty array if no selectors are rejected", async (done) => { expect.assertions(1); const purgecssResult = await new PurgeCSS().purge({ - content: [`${root}simple/simple.js`], - css: [`${root}simple/simple.css`], + content: [`${ROOT_TEST_EXAMPLES}rejected/simple.js`], + css: [`${ROOT_TEST_EXAMPLES}rejected/simple.css`], rejected: true, }); expect(purgecssResult[0].rejected).toEqual([]); @@ -27,8 +27,8 @@ describe("rejected", () => { it("returns the list of rejected selectors", async (done) => { expect.assertions(1); const purgecssResult = await new PurgeCSS().purge({ - content: [`${root}remove_unused/remove_unused.js`], - css: [`${root}remove_unused/remove_unused.css`], + content: [`${ROOT_TEST_EXAMPLES}others/remove_unused.js`], + css: [`${ROOT_TEST_EXAMPLES}others/remove_unused.css`], rejected: true, }); expect(purgecssResult[0].rejected).toEqual([ @@ -41,8 +41,8 @@ describe("rejected", () => { it("returns the list of rejected selectors with chaining rules", async (done) => { expect.assertions(1); const purgecssResult = await new PurgeCSS().purge({ - content: [`${root}chaining_rules/index.html`], - css: [`${root}chaining_rules/index.css`], + content: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.html`], + css: [`${ROOT_TEST_EXAMPLES}chaining-rules/index.css`], rejected: true, }); expect(purgecssResult[0].rejected).toEqual([ diff --git a/packages/purgecss/__tests__/safelist.test.ts b/packages/purgecss/__tests__/safelist.test.ts new file mode 100644 index 00000000..6b669cac --- /dev/null +++ b/packages/purgecss/__tests__/safelist.test.ts @@ -0,0 +1,202 @@ +import PurgeCSS from "./../src/index"; + +import { ROOT_TEST_EXAMPLES, findInCSS, notFindInCSS } from "./utils"; + +describe("safelist string", () => { + let purgedCSS = ""; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist.css`], + safelist: ["random", "h1", "yep", "button"], + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS(expect, [".random", "h1", "#yep", "button"], purgedCSS); + }); +}); + +describe("safelist regular expression", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist.css`], + safelist: [/nav-/, /data-v-.*/], + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS(expect, [".nav-blue", ".nav-red", "[data-v-test]"], purgedCSS); + }); +}); + +describe("safelist option: standard", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist.css`], + safelist: { + standard: ["random", "h1", "yep", "button", /nav-/, /data-v-.*/], + }, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS( + expect, + [ + ".random", + "h1", + "#yep", + "button", + ".nav-blue", + ".nav-red", + "[data-v-test]", + ], + purgedCSS + ); + }); +}); + +describe("safelist option: deep", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [ + `${ROOT_TEST_EXAMPLES}safelist/safelist_patterns_children.html`, + ], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist_patterns_children.css`], + safelist: { + deep: [/^card$/], + }, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS( + expect, + [".card", ".card .content", ".btn", ".card .btn .yellow"], + purgedCSS + ); + }); + + it("excludes selectors not safelisted", () => { + notFindInCSS(expect, [".title", ".btn .red", ".btn__green"], purgedCSS); + }); +}); + +describe("safelist option: greedy", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist_patterns_greedy.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist_patterns_greedy.css`], + safelist: { + greedy: [/data-v-.*/], + }, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted selectors", () => { + findInCSS( + expect, + [ + ".card", + ".card[data-v-test]", + ".card[data-v-test].card--large", + ".card[data-v-test] .card-content", + ], + purgedCSS + ); + }); + + it("excludes selectors not safelisted", () => { + notFindInCSS( + expect, + [".card.card--large", ".card .card-content"], + purgedCSS + ); + }); +}); + +describe("safelist option: keyframes", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist_keyframes.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist_keyframes.css`], + safelist: { + keyframes: [/^scale/, "spin"], + }, + keyframes: true, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted keyframes", () => { + findInCSS( + expect, + ["@keyframes scale", "@keyframes scale-down", "@keyframes spin"], + purgedCSS + ); + }); + + it("excludes non-safelisted keyframes", () => { + notFindInCSS(expect, ["flash"], purgedCSS); + }); +}); + +describe("safelist option: variables", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/safelist_css_variables.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/safelist_css_variables.css`], + safelist: { + variables: [/^--b/, "--unused-color"], + }, + variables: true, + }); + purgedCSS = resultsPurge[0].css; + }); + + it("finds safelisted css variables", () => { + findInCSS(expect, ["--unused-color", "--button-color"], purgedCSS); + }); + + it("excludes non-safelisted css variables", () => { + notFindInCSS(expect, ["--tertiary-color:"], purgedCSS); + }); +}); + +describe("blocklist option", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/blocklist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/blocklist.css`], + blocklist: ["h1", "yep", "button", /nav-/], + }); + purgedCSS = resultsPurge[0].css; + }); + + it("excludes blocklisted selectors", () => { + notFindInCSS( + expect, + ["h1", "yep", "button", "nav-blue", "nav-red"], + purgedCSS + ); + }); + + it("includes non-blocklisted selectors", () => { + findInCSS(expect, ["data-v-test", ".random"], purgedCSS); + }); +}); diff --git a/packages/purgecss/__tests__/test_examples/attribute_selector/attribute_selector.css b/packages/purgecss/__tests__/test_examples/attributes/attribute_selector.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/attribute_selector/attribute_selector.css rename to packages/purgecss/__tests__/test_examples/attributes/attribute_selector.css diff --git a/packages/purgecss/__tests__/test_examples/attribute_selector/attribute_selector.html b/packages/purgecss/__tests__/test_examples/attributes/attribute_selector.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/attribute_selector/attribute_selector.html rename to packages/purgecss/__tests__/test_examples/attributes/attribute_selector.html diff --git a/packages/purgecss/__tests__/test_examples/bootstrap/modified-bootstrap.css b/packages/purgecss/__tests__/test_examples/bootstrap/modified-bootstrap.css deleted file mode 100644 index df6d7985..00000000 --- a/packages/purgecss/__tests__/test_examples/bootstrap/modified-bootstrap.css +++ /dev/null @@ -1,6702 +0,0 @@ -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -body { - margin: 0; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -menu, -nav, -section, -summary { - display: block; -} -audio, -canvas, -progress, -video { - display: inline-block; - vertical-align: baseline; -} -audio:not([controls]) { - display: none; - height: 0; -} -template { - display: none; -} -a { - background-color: transparent; -} -a:active, -a:hover { - outline: 0; -} -abbr[title] { - border-bottom: 1px dotted; -} -b, -strong { - font-weight: bold; -} -dfn { - font-style: italic; -} -h1 { - margin: .67em 0; - font-size: 2em; -} -mark { - color: #000; - background: #ff0; -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -.5em; -} -sub { - bottom: -.25em; -} -img { - border: 0; -} -svg:not(:root) { - overflow: hidden; -} -figure { - margin: 1em 40px; -} -hr { - height: 0; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -pre { - overflow: auto; -} -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; - font: inherit; - color: inherit; -} -button { - overflow: visible; -} -button, -select { - text-transform: none; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -button[disabled], -html input[disabled] { - cursor: default; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} -input { - line-height: normal; -} -input[type="checkbox"], -input[type="radio"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 0; -} -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; -} -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -fieldset { - padding: .35em .625em .75em; - margin: 0 2px; - border: 1px solid #c0c0c0; -} -legend { - padding: 0; - border: 0; -} -textarea { - overflow: auto; -} -optgroup { - font-weight: bold; -} -table { - border-spacing: 0; - border-collapse: collapse; -} -td, -th { - padding: 0; -} - -@media print { - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - a[href^="#"]:after, - a[href^="javascript:"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - .navbar { - display: none; - } - .btn > .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #ddd !important; - } -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\002a"; -} -.glyphicon-plus:before { - content: "\002b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -html { - font-size: 10px; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #337ab7; - text-decoration: none; -} -a:hover, -a:focus { - color: #23527c; - text-decoration: underline; -} -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -mark, -.mark { - padding: .2em; - background-color: #fcf8e3; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777; -} -.text-primary { - color: #337ab7; -} -a.text-primary:hover, -a.text-primary:focus { - color: #286090; -} -.text-success { - color: #3c763d; -} -a.text-success:hover, -a.text-success:focus { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover, -a.text-info:focus { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover, -a.text-warning:focus { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover, -a.text-danger:focus { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #337ab7; -} -a.bg-primary:hover, -a.bg-primary:focus { - background-color: #286090; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover, -a.bg-success:focus { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover, -a.bg-info:focus { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover, -a.bg-warning:focus { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover, -a.bg-danger:focus { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - -webkit-box-shadow: none; - box-shadow: none; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: auto; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - background-color: transparent; -} -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left; -} -th { - text-align: left; -} -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr:hover > .active, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr:hover > .info, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr:hover > .warning, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr:hover > .danger, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -.table-responsive { - min-height: .01%; - overflow-x: auto; -} -@media screen and (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #eee; - opacity: 1; -} -.form-control[disabled], -fieldset[disabled] .form-control { - cursor: not-allowed; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"].form-control, - input[type="time"].form-control, - input[type="datetime-local"].form-control, - input[type="month"].form-control { - line-height: 34px; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm, - .input-group-sm input[type="date"], - .input-group-sm input[type="time"], - .input-group-sm input[type="datetime-local"], - .input-group-sm input[type="month"] { - line-height: 30px; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg, - .input-group-lg input[type="date"], - .input-group-lg input[type="time"], - .input-group-lg input[type="datetime-local"], - .input-group-lg input[type="month"] { - line-height: 46px; - } -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"].disabled, -input[type="checkbox"].disabled, -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"] { - cursor: not-allowed; -} -.radio-inline.disabled, -.checkbox-inline.disabled, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.radio.disabled label, -.checkbox.disabled label, -fieldset[disabled] .radio label, -fieldset[disabled] .checkbox label { - cursor: not-allowed; -} -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0; -} -.form-control-static.input-lg, -.form-control-static.input-sm { - padding-right: 0; - padding-left: 0; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.form-group-sm select.form-control { - height: 30px; - line-height: 30px; -} -.form-group-sm textarea.form-control, -.form-group-sm select[multiple].form-control { - height: auto; -} -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 6px 10px; - font-size: 12px; - line-height: 1.5; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.form-group-lg select.form-control { - height: 46px; - line-height: 46px; -} -.form-group-lg textarea.form-control, -.form-group-lg select[multiple].form-control { - height: auto; -} -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 11px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none; -} -.input-lg + .form-control-feedback, -.input-group-lg + .form-control-feedback, -.form-group-lg .form-control + .form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px; -} -.input-sm + .form-control-feedback, -.input-group-sm + .form-control-feedback, -.form-group-sm .form-control + .form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline, -.has-success.radio label, -.has-success.checkbox label, -.has-success.radio-inline label, -.has-success.checkbox-inline label { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline, -.has-warning.radio label, -.has-warning.checkbox label, -.has-warning.radio-inline label, -.has-warning.checkbox-inline label { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline, -.has-error.radio label, -.has-error.checkbox label, -.has-error.radio-inline label, -.has-error.checkbox-inline label { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.has-feedback label ~ .form-control-feedback { - top: 25px; -} -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-static { - display: inline-block; - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle; - } - .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn, - .form-inline .input-group .form-control { - width: auto; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio label, - .form-inline .checkbox label { - padding-left: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - right: 15px; -} -@media (min-width: 768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 11px; - font-size: 18px; - } -} -@media (min-width: 768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px; - font-size: 12px; - } -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus, -.btn.focus, -.btn:active.focus, -.btn.active.focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus, -.btn.focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -a.btn.disabled, -fieldset[disabled] a.btn { - pointer-events: none; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:focus, -.btn-default.focus { - color: #333; - background-color: #e6e6e6; - border-color: #8c8c8c; -} -.btn-default:hover { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active:hover, -.btn-default.active:hover, -.open > .dropdown-toggle.btn-default:hover, -.btn-default:active:focus, -.btn-default.active:focus, -.open > .dropdown-toggle.btn-default:focus, -.btn-default:active.focus, -.btn-default.active.focus, -.open > .dropdown-toggle.btn-default.focus { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary:focus, -.btn-primary.focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.btn-primary:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active:hover, -.btn-primary.active:hover, -.open > .dropdown-toggle.btn-primary:hover, -.btn-primary:active:focus, -.btn-primary.active:focus, -.open > .dropdown-toggle.btn-primary:focus, -.btn-primary:active.focus, -.btn-primary.active.focus, -.open > .dropdown-toggle.btn-primary.focus { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus { - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary .badge { - color: #337ab7; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625; -} -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active:hover, -.btn-success.active:hover, -.open > .dropdown-toggle.btn-success:hover, -.btn-success:active:focus, -.btn-success.active:focus, -.open > .dropdown-toggle.btn-success:focus, -.btn-success:active.focus, -.btn-success.active.focus, -.open > .dropdown-toggle.btn-success.focus { - color: #fff; - background-color: #398439; - border-color: #255625; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #31b0d5; - border-color: #1b6d85; -} -.btn-info:hover { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active:hover, -.btn-info.active:hover, -.open > .dropdown-toggle.btn-info:hover, -.btn-info:active:focus, -.btn-info.active:focus, -.open > .dropdown-toggle.btn-info:focus, -.btn-info:active.focus, -.btn-info.active.focus, -.open > .dropdown-toggle.btn-info.focus { - color: #fff; - background-color: #269abc; - border-color: #1b6d85; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:focus, -.btn-warning.focus { - color: #fff; - background-color: #ec971f; - border-color: #985f0d; -} -.btn-warning:hover { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active:hover, -.btn-warning.active:hover, -.open > .dropdown-toggle.btn-warning:hover, -.btn-warning:active:focus, -.btn-warning.active:focus, -.open > .dropdown-toggle.btn-warning:focus, -.btn-warning:active.focus, -.btn-warning.active.focus, -.open > .dropdown-toggle.btn-warning.focus { - color: #fff; - background-color: #d58512; - border-color: #985f0d; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19; -} -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active:hover, -.btn-danger.active:hover, -.open > .dropdown-toggle.btn-danger:hover, -.btn-danger:active:focus, -.btn-danger.active:focus, -.open > .dropdown-toggle.btn-danger:focus, -.btn-danger:active.focus, -.btn-danger.active.focus, -.open > .dropdown-toggle.btn-danger.focus { - color: #fff; - background-color: #ac2925; - border-color: #761c19; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #337ab7; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link.active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #777; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-top: 4px solid \9; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #777; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px dashed; - border-bottom: 4px solid \9; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn, -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:last-child:not(:first-child) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -.btn-group-justified > .btn-group .dropdown-menu { - left: auto; -} -[data-toggle="buttons"] > .btn input[type="radio"], -[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], -[data-toggle="buttons"] > .btn input[type="checkbox"], -[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group .form-control:focus { - z-index: 3; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - z-index: 2; - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #777; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #337ab7; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #337ab7; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.navbar-fixed-top .navbar-collapse, -.navbar-fixed-bottom .navbar-collapse { - max-height: 340px; -} -@media (max-device-width: 480px) and (orientation: landscape) { - .navbar-fixed-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - max-height: 200px; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-brand > img { - display: block; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: 0; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .form-control-static { - display: inline-block; - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle; - } - .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn, - .navbar-form .input-group .form-control { - width: auto; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio label, - .navbar-form .checkbox label { - padding-left: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } - .navbar-form .form-group:last-child { - margin-bottom: 0; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-default .btn-link { - color: #777; -} -.navbar-default .btn-link:hover, -.navbar-default .btn-link:focus { - color: #333; -} -.navbar-default .btn-link[disabled]:hover, -fieldset[disabled] .navbar-default .btn-link:hover, -.navbar-default .btn-link[disabled]:focus, -fieldset[disabled] .navbar-default .btn-link:focus { - color: #ccc; -} -.navbar-inverse { - background-color: #222; - border-color: #080808; -} -.navbar-inverse .navbar-brand { - color: #9d9d9d; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #080808; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #080808; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #9d9d9d; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #9d9d9d; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.navbar-inverse .btn-link { - color: #9d9d9d; -} -.navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link:focus { - color: #fff; -} -.navbar-inverse .btn-link[disabled]:hover, -fieldset[disabled] .navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link[disabled]:focus, -fieldset[disabled] .navbar-inverse .btn-link:focus { - color: #444; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #777; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - z-index: 2; - color: #23527c; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 3; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #777; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -a.label:hover, -a.label:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #777; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #5e5e5e; -} -.label-primary { - background-color: #337ab7; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #286090; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: #777; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge, -.btn-group-xs > .btn .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #337ab7; - background-color: #fff; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.jumbotron > hr { - border-top-color: #d5d5d5; -} -.container .jumbotron, -.container-fluid .jumbotron { - padding-right: 15px; - padding-left: 15px; - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron, - .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #337ab7; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable, -.alert-dismissible { - padding-right: 35px; -} -.alert-dismissable .close, -.alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar, -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .progress-bar, -.progress-bar.active { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media-body { - width: 10000px; -} -.media-object { - display: block; -} -.media-object.img-thumbnail { - max-width: none; -} -.media-right, -.media > .pull-right { - padding-left: 10px; -} -.media-left, -.media > .pull-left { - padding-right: 10px; -} -.media-left, -.media-right, -.media-body { - display: table-cell; - vertical-align: top; -} -.media-middle { - vertical-align: middle; -} -.media-bottom { - vertical-align: bottom; -} -.media-heading { - margin-top: 0; - margin-bottom: 5px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -a.list-group-item, -button.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading, -button.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -button.list-group-item:hover, -a.list-group-item:focus, -button.list-group-item:focus { - color: #555; - text-decoration: none; - background-color: #f5f5f5; -} -button.list-group-item { - width: 100%; - text-align: left; -} -.list-group-item.disabled, -.list-group-item.disabled:hover, -.list-group-item.disabled:focus { - color: #777; - cursor: not-allowed; - background-color: #eee; -} -.list-group-item.disabled .list-group-item-heading, -.list-group-item.disabled:hover .list-group-item-heading, -.list-group-item.disabled:focus .list-group-item-heading { - color: inherit; -} -.list-group-item.disabled .list-group-item-text, -.list-group-item.disabled:hover .list-group-item-text, -.list-group-item.disabled:focus .list-group-item-text { - color: #777; -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.list-group-item.active .list-group-item-heading, -.list-group-item.active:hover .list-group-item-heading, -.list-group-item.active:focus .list-group-item-heading, -.list-group-item.active .list-group-item-heading > small, -.list-group-item.active:hover .list-group-item-heading > small, -.list-group-item.active:focus .list-group-item-heading > small, -.list-group-item.active .list-group-item-heading > .small, -.list-group-item.active:hover .list-group-item-heading > .small, -.list-group-item.active:focus .list-group-item-heading > .small { - color: inherit; -} -.list-group-item.active .list-group-item-text, -.list-group-item.active:hover .list-group-item-text, -.list-group-item.active:focus .list-group-item-text { - color: #c7ddef; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success, -button.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading, -button.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -button.list-group-item-success:hover, -a.list-group-item-success:focus, -button.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -button.list-group-item-success.active, -a.list-group-item-success.active:hover, -button.list-group-item-success.active:hover, -a.list-group-item-success.active:focus, -button.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info, -button.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading, -button.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -button.list-group-item-info:hover, -a.list-group-item-info:focus, -button.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -button.list-group-item-info.active, -a.list-group-item-info.active:hover, -button.list-group-item-info.active:hover, -a.list-group-item-info.active:focus, -button.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning, -button.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading, -button.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -button.list-group-item-warning:hover, -a.list-group-item-warning:focus, -button.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -button.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -button.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus, -button.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger, -button.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading, -button.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -button.list-group-item-danger:hover, -a.list-group-item-danger:focus, -button.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -button.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -button.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus, -button.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a, -.panel-title > small, -.panel-title > .small, -.panel-title > small > a, -.panel-title > .small > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group, -.panel > .panel-collapse > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item, -.panel > .panel-collapse > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child, -.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child, -.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.list-group + .panel-footer { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table, -.panel > .panel-collapse > .table { - margin-bottom: 0; -} -.panel > .table caption, -.panel > .table-responsive > .table caption, -.panel > .panel-collapse > .table caption { - padding-right: 15px; - padding-left: 15px; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive, -.panel > .table + .panel-body, -.panel > .table-responsive + .panel-body { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse > .panel-body, -.panel-group .panel-heading + .panel-collapse > .list-group { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-heading .badge { - color: #f5f5f5; - background-color: #333; -} -.panel-default > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #337ab7; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #337ab7; -} -.panel-primary > .panel-heading .badge { - color: #337ab7; - background-color: #fff; -} -.panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #337ab7; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-heading .badge { - color: #dff0d8; - background-color: #3c763d; -} -.panel-success > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-heading .badge { - color: #d9edf7; - background-color: #31708f; -} -.panel-info > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b; -} -.panel-warning > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-heading .badge { - color: #f2dede; - background-color: #a94442; -} -.panel-danger > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ebccd1; -} -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden; -} -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} -.embed-responsive-16by9 { - padding-bottom: 56.25%; -} -.embed-responsive-4by3 { - padding-bottom: 75%; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 15px; -} -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - filter: alpha(opacity=0); - opacity: 0; - - line-break: auto; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - - line-break: auto; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -@media all and (transform-3d), (-webkit-transform-3d) { - .carousel-inner > .item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; - } - .carousel-inner > .item.next, - .carousel-inner > .item.active.right { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - .carousel-inner > .item.prev, - .carousel-inner > .item.active.left { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - .carousel-inner > .item.next.left, - .carousel-inner > .item.prev.right, - .carousel-inner > .item.active { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - background-color: rgba(0, 0, 0, 0); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: 0; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - margin-top: -10px; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - font-family: serif; - line-height: 1; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -10px; - font-size: 30px; - } - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: -10px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-right: -10px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-header:before, -.modal-header:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-header:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline { - display: inline !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline-block { - display: inline-block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table !important; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-block { - display: block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline { - display: inline !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline-block { - display: inline-block !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table !important; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg-block { - display: block !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline { - display: inline !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline-block { - display: inline-block !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table !important; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} -.boomshaka { - color: black; -} diff --git a/packages/purgecss/__tests__/test_examples/camel_case/camel_case.css b/packages/purgecss/__tests__/test_examples/camel_case/camel_case.css deleted file mode 100644 index 5efb47ac..00000000 --- a/packages/purgecss/__tests__/test_examples/camel_case/camel_case.css +++ /dev/null @@ -1,7 +0,0 @@ -.testFoo { - color: black; -} - -#camelCase { - color: black; -} \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/camel_case/camel_case.js b/packages/purgecss/__tests__/test_examples/camel_case/camel_case.js deleted file mode 100644 index 7de25f6b..00000000 --- a/packages/purgecss/__tests__/test_examples/camel_case/camel_case.js +++ /dev/null @@ -1,3 +0,0 @@ -'testFoo' - -'camelCase' diff --git a/packages/purgecss/__tests__/test_examples/chaining_rules/index.css b/packages/purgecss/__tests__/test_examples/chaining-rules/index.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/chaining_rules/index.css rename to packages/purgecss/__tests__/test_examples/chaining-rules/index.css diff --git a/packages/purgecss/__tests__/test_examples/chaining_rules/index.html b/packages/purgecss/__tests__/test_examples/chaining-rules/index.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/chaining_rules/index.html rename to packages/purgecss/__tests__/test_examples/chaining-rules/index.html diff --git a/packages/purgecss/__tests__/test_examples/cli/simple/expected/styles.css b/packages/purgecss/__tests__/test_examples/cli/simple/expected/styles.css new file mode 100644 index 00000000..88f614e7 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/cli/simple/expected/styles.css @@ -0,0 +1,12 @@ +.hello { + color: red; +} + +.safelisted { + color: green; +} + +md\:w-2\/3 { + color: red; +} + diff --git a/packages/purgecss/__tests__/test_examples/cli/simple/src/content.html b/packages/purgecss/__tests__/test_examples/cli/simple/src/content.html new file mode 100644 index 00000000..8fbbb60f --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/cli/simple/src/content.html @@ -0,0 +1,13 @@ + + + + Purgecss webpack test + + + +
+
+
+ + + \ No newline at end of file diff --git a/packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/index.js b/packages/purgecss/__tests__/test_examples/cli/simple/src/index.js similarity index 100% rename from packages/purgecss-webpack-plugin/__tests__/cases/path-and-whitelist-functions/src/index.js rename to packages/purgecss/__tests__/test_examples/cli/simple/src/index.js diff --git a/packages/purgecss/__tests__/test_examples/cli/simple/src/style.css b/packages/purgecss/__tests__/test_examples/cli/simple/src/style.css new file mode 100644 index 00000000..e2d156dd --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/cli/simple/src/style.css @@ -0,0 +1,15 @@ +.hello { + color: red; +} + +.unused { + color: blue; +} + +.safelisted { + color: green; +} + +md\:w-2\/3 { + color: red; +} diff --git a/packages/purgecss/__tests__/test_examples/combined/combined.css b/packages/purgecss/__tests__/test_examples/combined/combined.css deleted file mode 100644 index 84d1f4cc..00000000 --- a/packages/purgecss/__tests__/test_examples/combined/combined.css +++ /dev/null @@ -1,7 +0,0 @@ -.added-together { - color: black; -} - -.array-joined { - color: black; -} diff --git a/packages/purgecss/__tests__/test_examples/combined/combined.js b/packages/purgecss/__tests__/test_examples/combined/combined.js deleted file mode 100644 index 4bca8745..00000000 --- a/packages/purgecss/__tests__/test_examples/combined/combined.js +++ /dev/null @@ -1,3 +0,0 @@ -const x = "added-" + "together"; - -const y = ["array", "joined"].join("-"); diff --git a/packages/purgecss/__tests__/test_examples/ignore_comment/ignore_comment.css b/packages/purgecss/__tests__/test_examples/comments/ignore_comment.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/ignore_comment/ignore_comment.css rename to packages/purgecss/__tests__/test_examples/comments/ignore_comment.css diff --git a/packages/purgecss/__tests__/test_examples/ignore_comment/ignore_comment.html b/packages/purgecss/__tests__/test_examples/comments/ignore_comment.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/ignore_comment/ignore_comment.html rename to packages/purgecss/__tests__/test_examples/comments/ignore_comment.html diff --git a/packages/purgecss/__tests__/test_examples/ignore_comment_range/index.css b/packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/ignore_comment_range/index.css rename to packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.css diff --git a/packages/purgecss/__tests__/test_examples/ignore_comment_range/index.html b/packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/ignore_comment_range/index.html rename to packages/purgecss/__tests__/test_examples/comments/ignore_comment_range.html diff --git a/packages/purgecss/__tests__/test_examples/variables/variables.css b/packages/purgecss/__tests__/test_examples/css-variables/variables.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/variables/variables.css rename to packages/purgecss/__tests__/test_examples/css-variables/variables.css diff --git a/packages/purgecss/__tests__/test_examples/variables/variables.html b/packages/purgecss/__tests__/test_examples/css-variables/variables.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/variables/variables.html rename to packages/purgecss/__tests__/test_examples/css-variables/variables.html diff --git a/packages/purgecss/__tests__/test_examples/font_face/font_face.css b/packages/purgecss/__tests__/test_examples/font-faces/font_face.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/font_face/font_face.css rename to packages/purgecss/__tests__/test_examples/font-faces/font_face.css diff --git a/packages/purgecss/__tests__/test_examples/font_face/font_face.html b/packages/purgecss/__tests__/test_examples/font-faces/font_face.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/font_face/font_face.html rename to packages/purgecss/__tests__/test_examples/font-faces/font_face.html diff --git a/packages/purgecss/__tests__/test_examples/media_queries/media_queries.css b/packages/purgecss/__tests__/test_examples/media-queries/media_queries.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/media_queries/media_queries.css rename to packages/purgecss/__tests__/test_examples/media-queries/media_queries.css diff --git a/packages/purgecss/__tests__/test_examples/media_queries/media_queries.html b/packages/purgecss/__tests__/test_examples/media-queries/media_queries.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/media_queries/media_queries.html rename to packages/purgecss/__tests__/test_examples/media-queries/media_queries.html diff --git a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.css b/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.css deleted file mode 100644 index fc5c1d3c..00000000 --- a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.css +++ /dev/null @@ -1,3 +0,0 @@ -.blank-space { - color: black; -} diff --git a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.html b/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.html deleted file mode 100644 index 41868f6b..00000000 --- a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.html +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.js b/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.js deleted file mode 100644 index 3d5c18e2..00000000 --- a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files.js +++ /dev/null @@ -1 +0,0 @@ -'blank-space' diff --git a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files2.css b/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files2.css deleted file mode 100644 index 9562d03c..00000000 --- a/packages/purgecss/__tests__/test_examples/multiple_files/multiple_files2.css +++ /dev/null @@ -1,7 +0,0 @@ -.taylor-swift { - color: black; -} - -.shake-it-off { - color: black; -} diff --git a/packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.css b/packages/purgecss/__tests__/test_examples/others/remove_unused.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.css rename to packages/purgecss/__tests__/test_examples/others/remove_unused.css diff --git a/packages/purgecss/__tests__/test_examples/others/remove_unused.js b/packages/purgecss/__tests__/test_examples/others/remove_unused.js new file mode 100644 index 00000000..7401750c --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/others/remove_unused.js @@ -0,0 +1 @@ +".used-class"; diff --git a/packages/purgecss/__tests__/test_examples/special_characters/special_characters.css b/packages/purgecss/__tests__/test_examples/others/special_characters.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/special_characters/special_characters.css rename to packages/purgecss/__tests__/test_examples/others/special_characters.css diff --git a/packages/purgecss/__tests__/test_examples/others/special_characters.js b/packages/purgecss/__tests__/test_examples/others/special_characters.js new file mode 100644 index 00000000..a3325ede --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/others/special_characters.js @@ -0,0 +1,7 @@ +"@home"; + +"+rounded"; + +"button"; + +".md:w-1/3"; diff --git a/packages/purgecss/__tests__/test_examples/not/not.css b/packages/purgecss/__tests__/test_examples/pseudo-class/not.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/not/not.css rename to packages/purgecss/__tests__/test_examples/pseudo-class/not.css diff --git a/packages/purgecss/__tests__/test_examples/not/not.html b/packages/purgecss/__tests__/test_examples/pseudo-class/not.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/not/not.html rename to packages/purgecss/__tests__/test_examples/pseudo-class/not.html diff --git a/packages/purgecss/__tests__/test_examples/nth_child/nth_child.css b/packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/nth_child/nth_child.css rename to packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.css diff --git a/packages/purgecss/__tests__/test_examples/nth_child/nth_child.html b/packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/nth_child/nth_child.html rename to packages/purgecss/__tests__/test_examples/pseudo-class/nth_child.html diff --git a/packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.css b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.css rename to packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.css diff --git a/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.js b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.js new file mode 100644 index 00000000..14e54281 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_class.js @@ -0,0 +1 @@ +"div"; diff --git a/packages/purgecss/__tests__/test_examples/pseudo_selector/pseudo_selector.css b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/pseudo_selector/pseudo_selector.css rename to packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.css diff --git a/packages/purgecss/__tests__/test_examples/pseudo_selector/pseudo_selector.html b/packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/pseudo_selector/pseudo_selector.html rename to packages/purgecss/__tests__/test_examples/pseudo-class/pseudo_selector.html diff --git a/packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.js b/packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.js deleted file mode 100644 index 99900fbc..00000000 --- a/packages/purgecss/__tests__/test_examples/pseudo_class/pseudo_class.js +++ /dev/null @@ -1 +0,0 @@ -'div' diff --git a/packages/purgecss/__tests__/test_examples/simple/simple.css b/packages/purgecss/__tests__/test_examples/rejected/simple.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/simple/simple.css rename to packages/purgecss/__tests__/test_examples/rejected/simple.css diff --git a/packages/purgecss/__tests__/test_examples/rejected/simple.js b/packages/purgecss/__tests__/test_examples/rejected/simple.js new file mode 100644 index 00000000..74ba283f --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/rejected/simple.js @@ -0,0 +1,5 @@ +"single"; + +"double-class"; + +"triple-simple-class"; diff --git a/packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.js b/packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.js deleted file mode 100644 index 76539783..00000000 --- a/packages/purgecss/__tests__/test_examples/remove_unused/remove_unused.js +++ /dev/null @@ -1 +0,0 @@ -'.used-class' diff --git a/packages/purgecss/__tests__/test_examples/safelist/blocklist.css b/packages/purgecss/__tests__/test_examples/safelist/blocklist.css new file mode 100644 index 00000000..f28ae760 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/blocklist.css @@ -0,0 +1,28 @@ +h1 { + color: blue; + } + + .random { + color: green; + } + + #yep { + color: red; + } + + button { + color: rebeccapurple; + } + + .nav-blue { + background-color: blue; + } + + .nav-red { + background-color: red; + } + + [data-v-test] { + color: green; + } + \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/safelist/blocklist.html b/packages/purgecss/__tests__/test_examples/safelist/blocklist.html new file mode 100644 index 00000000..985eb619 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/blocklist.html @@ -0,0 +1,11 @@ + + + + + +

Title

+
random
+ + + + \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/whitelist/whitelist.css b/packages/purgecss/__tests__/test_examples/safelist/safelist.css similarity index 90% rename from packages/purgecss/__tests__/test_examples/whitelist/whitelist.css rename to packages/purgecss/__tests__/test_examples/safelist/safelist.css index 2b9fe22b..06554780 100644 --- a/packages/purgecss/__tests__/test_examples/whitelist/whitelist.css +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist.css @@ -22,6 +22,6 @@ button { background-color: red; } -.random[data-v-test] { +[data-v-test] { color: green; } diff --git a/packages/purgecss/__tests__/test_examples/whitelist/whitelist.html b/packages/purgecss/__tests__/test_examples/safelist/safelist.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/whitelist/whitelist.html rename to packages/purgecss/__tests__/test_examples/safelist/safelist.html diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.css b/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.css new file mode 100644 index 00000000..1956037d --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.css @@ -0,0 +1,22 @@ +:root { + --primary-color: blue; + --secondary-color: indigo; + --tertiary-color: aqua; + --unused-color: violet; + --used-color: rebeccapurple; + --accent-color: orange; + } + + .button { + --button-color: var(--tertiary-color); + --border-color: linear-gradient(to top, var(--secondary-color), var(--used-color, white)); + + background-color: var(--primary-color); + color: var(--accent-color); + border-color: var(--border-color); + } + + .button:focus { + background-color: var(--accent-color); + color: var(--primary-color); + } \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.html b/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.html new file mode 100644 index 00000000..65214131 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_css_variables.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.css b/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.css new file mode 100644 index 00000000..3be12e29 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.css @@ -0,0 +1,61 @@ +@keyframes bounce { + from, 20%, 53%, 80%, to { + animation-timing-function: cubic-bezier(0.3, 0.1, 0.9, 1.000); + transform: translate3d(1, 1, 0); + } +} + +.bounce { + -webkit-animation-name: bounce; + animation-name: bounce; + -webkit-transform-origin: center bottom; + transform-origin: center bottom; +} + +@keyframes flash { + from, 50%, to { + opacity: 1; + } + + 25%, 75% { + opacity: 0.5; + } +} + +.flash { + animation: flash +} + +@keyframes scale { + from { + transform: scale(1); + } + + to { + transform: scale(2); + } +} + +@keyframes scale-down { + from { + transform: scale(2); + } + + to { + transform: scale(1); + } +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} + +.scale-spin { + animation: spin 300ms linear infinite forwards,scale 300ms linear infinite alternate; +} diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.html b/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.html new file mode 100644 index 00000000..7b1f750a --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_keyframes.html @@ -0,0 +1,2 @@ +
+
diff --git a/packages/purgecss/__tests__/test_examples/whitelist_patterns_children/whitelist_patterns_children.css b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.css similarity index 100% rename from packages/purgecss/__tests__/test_examples/whitelist_patterns_children/whitelist_patterns_children.css rename to packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.css diff --git a/packages/purgecss/__tests__/test_examples/whitelist_patterns_children/whitelist_patterns_children.html b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.html similarity index 100% rename from packages/purgecss/__tests__/test_examples/whitelist_patterns_children/whitelist_patterns_children.html rename to packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_children.html diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.css b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.css new file mode 100644 index 00000000..a1d69aa9 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.css @@ -0,0 +1,6 @@ +.card {} +.card[data-v-test] {} +.card.card--large {} +.card[data-v-test].card--large {} +.card .card-content {} +.card[data-v-test] .card-content {} diff --git a/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.html b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.html new file mode 100644 index 00000000..64ca52ef --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/safelist_patterns_greedy.html @@ -0,0 +1,5 @@ + + +
+ + diff --git a/packages/purgecss/__tests__/test_examples/simple/simple.js b/packages/purgecss/__tests__/test_examples/simple/simple.js deleted file mode 100644 index 53693f99..00000000 --- a/packages/purgecss/__tests__/test_examples/simple/simple.js +++ /dev/null @@ -1,5 +0,0 @@ -'single' - -'double-class' - -'triple-simple-class' diff --git a/packages/purgecss/__tests__/test_examples/special_characters/special_characters.js b/packages/purgecss/__tests__/test_examples/special_characters/special_characters.js deleted file mode 100644 index 3f7d0d42..00000000 --- a/packages/purgecss/__tests__/test_examples/special_characters/special_characters.js +++ /dev/null @@ -1,7 +0,0 @@ -'@home' - -'+rounded' - -'button' - -'.md:w-1/3' diff --git a/packages/purgecss/__tests__/test_examples/tables/tables.css b/packages/purgecss/__tests__/test_examples/tables/tables.css deleted file mode 100644 index 73417e9b..00000000 --- a/packages/purgecss/__tests__/test_examples/tables/tables.css +++ /dev/null @@ -1,8 +0,0 @@ -.table-responsive > .table > tbody > tr > td, -.table-responsive > .table > tbody > tr > th, -.table-responsive > .table > tfoot > tr > td, -.table-responsive > .table > tfoot > tr > th, -.table-responsive > .table > thead > tr > td, -.table-responsive > .table > thead > tr > th { - white-space: normal; -} \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/tables/tables.html b/packages/purgecss/__tests__/test_examples/tables/tables.html deleted file mode 100644 index 1504bc8f..00000000 --- a/packages/purgecss/__tests__/test_examples/tables/tables.html +++ /dev/null @@ -1,9 +0,0 @@ -
- - - - - - -
Something
-
\ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/wildcard/wildcard.css b/packages/purgecss/__tests__/test_examples/wildcard/wildcard.css deleted file mode 100644 index 62362c98..00000000 --- a/packages/purgecss/__tests__/test_examples/wildcard/wildcard.css +++ /dev/null @@ -1,21 +0,0 @@ -*, -::before, -::after { - background: black; -} - -:root { - background: black; -} - -::selection { - background: black; -} - -::-webkit-scrollbar { - background: black; -} - -::-webkit-scrollbar:vertical { - background: black; -} diff --git a/packages/purgecss/__tests__/test_examples/wildcard/wildcard.html b/packages/purgecss/__tests__/test_examples/wildcard/wildcard.html deleted file mode 100644 index 834fdf11..00000000 --- a/packages/purgecss/__tests__/test_examples/wildcard/wildcard.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - wildcard - - - - - diff --git a/packages/purgecss/__tests__/utils.ts b/packages/purgecss/__tests__/utils.ts new file mode 100644 index 00000000..69663d66 --- /dev/null +++ b/packages/purgecss/__tests__/utils.ts @@ -0,0 +1,22 @@ +export const ROOT_TEST_EXAMPLES = + "./packages/purgecss/__tests__/test_examples/"; + +export function findInCSS( + expect: jest.Expect, + selectors: string[], + css: string +): void { + selectors.forEach((selector) => { + expect(css.includes(selector)).toBe(true); + }); +} + +export function notFindInCSS( + expect: jest.Expect, + selectors: string[], + css: string +): void { + selectors.forEach((selector) => { + expect(css.includes(selector)).toBe(false); + }); +} diff --git a/packages/purgecss/__tests__/whitelist.test.ts b/packages/purgecss/__tests__/whitelist.test.ts deleted file mode 100644 index e04a57a4..00000000 --- a/packages/purgecss/__tests__/whitelist.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import PurgeCSS from "./../src/index"; - -const root = "./packages/purgecss/__tests__/test_examples/"; - -describe("whitelist", () => { - let purgedCSS = ""; - beforeAll(async (done) => { - const resultsPurge = await new PurgeCSS().purge({ - content: [`${root}whitelist/whitelist.html`], - css: [`${root}whitelist/whitelist.css`], - whitelist: ["random", "h1", "yep", "button"], - whitelistPatterns: [/nav-/, /data-v-.*/], - }); - purgedCSS = resultsPurge[0].css; - done(); - }); - - it("finds attr", () => { - expect(purgedCSS.includes("[data-v-test]")).toBe(true); - }); - - it("finds random class", () => { - expect(purgedCSS.includes(".random")).toBe(true); - }); - - it("finds h1", () => { - expect(purgedCSS.includes("h1")).toBe(true); - }); - - it("finds #yep", () => { - expect(purgedCSS.includes("#yep")).toBe(true); - }); - - it("finds button", () => { - expect(purgedCSS.includes("button")).toBe(true); - }); - - it("finds .nav-blue", () => { - expect(purgedCSS.includes(".nav-blue")).toBe(true); - }); - - it("finds .nav-red", () => { - expect(purgedCSS.includes(".nav-red")).toBe(true); - }); -}); - -describe("whitelistPatternsChildren", () => { - let purgedCSS: string; - beforeAll(async () => { - const resultsPurge = await new PurgeCSS().purge({ - content: [ - `${root}whitelist_patterns_children/whitelist_patterns_children.html`, - ], - css: [ - `${root}whitelist_patterns_children/whitelist_patterns_children.css`, - ], - whitelistPatternsChildren: [/^card$/], - }); - purgedCSS = resultsPurge[0].css; - }); - - it("finds card class", () => { - expect(purgedCSS.includes(".card")).toBe(true); - }); - - it("finds card--title", () => { - expect(purgedCSS.includes(".title")).toBe(false); - }); - - it("finds card--content", () => { - expect(purgedCSS.includes(".card .content")).toBe(true); - }); - - it("finds btn", () => { - expect(purgedCSS.includes(".btn")).toBe(true); - }); - - it("finds btn yellow", () => { - expect(purgedCSS.includes(".card .btn .yellow")).toBe(true); - }); - - it("finds btn red", () => { - expect(purgedCSS.includes(".btn .red")).toBe(false); - }); - - it("excludes btn--green", () => { - expect(purgedCSS.includes(".btn__green")).toBe(false); - }); -}); diff --git a/packages/purgecss/bin/purgecss b/packages/purgecss/bin/purgecss deleted file mode 100755 index dad60e5e..00000000 --- a/packages/purgecss/bin/purgecss +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env node -const program = require('commander'); -const fs = require('fs'); -const { default: PurgeCSS, defaultOptions, setOptions } = require('../lib/purgecss') - -function getList(list) { - return list.split(',') -} - -async function writeCSSToFile(filePath, css) { - try { - await fs.promises.writeFile(filePath, css); - } catch(err) { - console.error(err.message); - } -} - -program - .usage('--css --content [options]') - .option('-con, --content ', 'glob of content files (comma separated)', getList) - .option('-css, --css ', 'glob of css files (comma separated)', getList) - .option('-c, --config ', 'path to the configuration file') - .option('-o, --output ', 'file path directory to write purged css files to') - .option('-font, --font-face', 'option to remove unused font-faces') - .option('-keyframes, --keyframes', 'option to remove unused keyframes') - .option('-rejected, --rejected', 'option to output rejected selectors') - .option('-w, --whitelist ', 'list of classes that should not be removed (comma separated)', getList); - -program.parse(process.argv); - -// config file is not specified or the content and css are not, -// PurgeCSS will not run -if (!program.config && !(program.content && program.css)) { - program.help(); -} - -(async() => { - // if the config file is present, use it - // other options specified will override - let options = defaultOptions; - if (program.config) { - options = await setOptions(program.config); - } - if (program.content) options.content = program.content; - if (program.css) options.css = program.css; - if (program.fontFace) options.fontFace = program.fontFace; - if (program.keyframes) options.keyframes = program.keyframes; - if (program.rejected) options.rejected = program.rejected; - if (program.variables) options.variables = program.variables; - if (program.whitelist) options.whitelist = program.whitelist; - - // if (!options.css) { - // options.css = [{ - // raw: stdInCSS - // }] - // } - - const purged = await new PurgeCSS().purge(options); - - const output = options.output || program.output; - // output results in specified directory - if (output) { - if (purged.length === 1 && output.endsWith('.css')) { - await writeCSSToFile(output, purged[0].css) - return - } - - for (const purgedResult of purged) { - const fileName = purgedResult.file.split("/").pop(); - await writeCSSToFile(`${output}/${fileName}`, purgedResult.css); - } - } else { - console.log(JSON.stringify(purged)); - } - -})() - diff --git a/packages/purgecss/bin/purgecss.js b/packages/purgecss/bin/purgecss.js new file mode 100755 index 00000000..23d408e0 --- /dev/null +++ b/packages/purgecss/bin/purgecss.js @@ -0,0 +1,86 @@ +#!/usr/bin/env node +const program = require("commander"); +const fs = require("fs"); +const { + default: PurgeCSS, + defaultOptions, + setOptions, +} = require("../lib/purgecss"); + +async function writeCSSToFile(filePath, css) { + try { + await fs.promises.writeFile(filePath, css); + } catch (err) { + console.error(err.message); + } +} + +program + .usage("--css --content [options]") + .option("-con, --content ", "glob of content files") + .option("-css, --css ", "glob of css files") + .option("-c, --config ", "path to the configuration file") + .option( + "-o, --output ", + "file path directory to write purged css files to" + ) + .option("-font, --font-face", "option to remove unused font-faces") + .option("-keyframes, --keyframes", "option to remove unused keyframes") + .option("-rejected, --rejected", "option to output rejected selectors") + .option( + "-s, --safelist ", + "list of classes that should not be removed" + ) + .option( + "-b, --blocklist ", + "list of selectors that should be removed" + ); + +program.parse(process.argv); + +const run = async () => { + // config file is not specified or the content and css are not, + // PurgeCSS will not run + if (!program.config && !(program.content && program.css)) { + program.help(); + } + + // if the config file is present, use it + // other options specified will override + let options = defaultOptions; + if (program.config) { + options = await setOptions(program.config); + } + if (program.content) options.content = program.content; + if (program.css) options.css = program.css; + if (program.fontFace) options.fontFace = program.fontFace; + if (program.keyframes) options.keyframes = program.keyframes; + if (program.rejected) options.rejected = program.rejected; + if (program.variables) options.variables = program.variables; + if (program.safelist) options.safelist = program.safelist; + if (program.blocklist) options.blocklist = program.blocklist; + + const purged = await new PurgeCSS().purge(options); + const output = options.output || program.output; + // output results in specified directory + if (output) { + if (purged.length === 1 && output.endsWith(".css")) { + await writeCSSToFile(output, purged[0].css); + return; + } + + for (const purgedResult of purged) { + const fileName = purgedResult.file.split("/").pop(); + await writeCSSToFile(`${output}/${fileName}`, purgedResult.css); + } + } else { + console.log(JSON.stringify(purged)); + } +}; + +try { + run(); +} catch (error) { + console.error(error.message); + process.exit(1); +} diff --git a/packages/purgecss/package.json b/packages/purgecss/package.json index 5105389c..62861b15 100644 --- a/packages/purgecss/package.json +++ b/packages/purgecss/package.json @@ -1,6 +1,6 @@ { "name": "purgecss", - "version": "2.3.0", + "version": "3.0.0", "description": "Remove unused css selectors", "author": "Ffloriel", "homepage": "https://purgecss.com", @@ -21,7 +21,7 @@ "module": "./lib/purgecss.esm.js", "types": "./lib/purgecss.d.ts", "bin": { - "purgecss": "bin/purgecss" + "purgecss": "bin/purgecss.js" }, "directories": { "lib": "lib", @@ -39,7 +39,7 @@ "test": "echo \"Error: run tests from root\" && exit 1" }, "dependencies": { - "commander": "^5.0.0", + "commander": "^6.0.0", "glob": "^7.0.0", "postcss": "7.0.32", "postcss-selector-parser": "^6.0.2" diff --git a/packages/purgecss/src/VariablesStructure.ts b/packages/purgecss/src/VariablesStructure.ts index 06f9b666..af5a9221 100644 --- a/packages/purgecss/src/VariablesStructure.ts +++ b/packages/purgecss/src/VariablesStructure.ts @@ -1,4 +1,5 @@ import postcss from "postcss"; +import { StringRegExpArray } from "./types"; class VariableNode { public nodes: VariableNode[] = []; @@ -13,6 +14,7 @@ class VariableNode { class VariablesStructure { public nodes: Map = new Map(); public usedVariables: Set = new Set(); + public safelist: StringRegExpArray = []; addVariable(declaration: postcss.Declaration): void { const { prop } = declaration; @@ -63,12 +65,20 @@ class VariablesStructure { for (const used of this.usedVariables) { this.setAsUsed(used); } - for (const [, declaration] of this.nodes) { - if (!declaration.isUsed) { + for (const [name, declaration] of this.nodes) { + if (!declaration.isUsed && !this.isVariablesSafelisted(name)) { declaration.value.remove(); } } } + + isVariablesSafelisted(variable: string): boolean { + return this.safelist.some((safelistItem) => { + return typeof safelistItem === "string" + ? safelistItem === variable + : safelistItem.test(variable); + }); + } } export default VariablesStructure; diff --git a/packages/purgecss/src/index.ts b/packages/purgecss/src/index.ts index d040a78a..f922f69c 100644 --- a/packages/purgecss/src/index.ts +++ b/packages/purgecss/src/index.ts @@ -20,6 +20,8 @@ import { RawCSS, UserDefinedOptions, ExtractorResultDetailed, + UserDefinedSafelist, + ComplexSafelist, } from "./types"; import { @@ -30,7 +32,7 @@ import { IGNORE_ANNOTATION_END, IGNORE_ANNOTATION_CURRENT, } from "./constants"; -import { CSS_WHITELIST } from "./internal-whitelist"; +import { CSS_SAFELIST } from "./internal-safelist"; import VariablesStructure from "./VariablesStructure"; import ExtractorResultSets from "./ExtractorResultSets"; @@ -39,6 +41,21 @@ const asyncFs = { readFile: promisify(fs.readFile), }; +export function standardizeSafelist( + userDefinedSafelist: UserDefinedSafelist = [] +): Required { + if (Array.isArray(userDefinedSafelist)) { + return { + ...defaultOptions.safelist, + standard: userDefinedSafelist, + }; + } + return { + ...defaultOptions.safelist, + ...userDefinedSafelist, + }; +} + /** * Load the configuration file from the path * @param configFile Path of the config file @@ -56,6 +73,7 @@ export async function setOptions( return { ...defaultOptions, ...options, + safelist: standardizeSafelist(options.safelist), }; } @@ -525,29 +543,59 @@ class PurgeCSS { } /** - * Check if the selector is whitelisted by the option whitelist or whitelistPatterns + * Check if the keyframe is safelisted with the option safelist keyframes + * @param keyframesName name of the keyframe animation + */ + private isKeyframesSafelisted(keyframesName: string): boolean { + return this.options.safelist.keyframes.some((safelistItem) => { + return typeof safelistItem === "string" + ? safelistItem === keyframesName + : safelistItem.test(keyframesName); + }); + } + + /** + * Check if the selector is blocklisted with the option blocklist * @param selector css selector */ - private isSelectorWhitelisted(selector: string): boolean { - return ( - CSS_WHITELIST.includes(selector) || - (this.options.whitelist && - this.options.whitelist.some((v: string) => v === selector)) || - (this.options.whitelistPatterns && - this.options.whitelistPatterns.some((v: RegExp) => v.test(selector))) + private isSelectorBlocklisted(selector: string): boolean { + return this.options.blocklist.some((blocklistItem) => { + return typeof blocklistItem === "string" + ? blocklistItem === selector + : blocklistItem.test(selector); + }); + } + + /** + * Check if the selector is safelisted with the option safelist standard + * @param selector css selector + */ + private isSelectorSafelisted(selector: string): boolean { + const isSafelisted = this.options.safelist.standard.some((safelistItem) => { + return typeof safelistItem === "string" + ? safelistItem === selector + : safelistItem.test(selector); + }); + return CSS_SAFELIST.includes(selector) || isSafelisted; + } + + /** + * Check if the selector is safelisted with the option safelist deep + * @param selector selector + */ + private isSelectorSafelistedDeep(selector: string): boolean { + return this.options.safelist.deep.some((safelistItem) => + safelistItem.test(selector) ); } /** - * Check if the selector is whitelisted by the whitelistPatternsChildren option + * Check if the selector is safelisted with the option safelist greedy * @param selector selector */ - private isSelectorWhitelistedChildren(selector: string): boolean { - return ( - this.options.whitelistPatternsChildren && - this.options.whitelistPatternsChildren.some((pattern: RegExp) => - pattern.test(selector) - ) + private isSelectorSafelistedGreedy(selector: string): boolean { + return this.options.safelist.greedy.some((safelistItem) => + safelistItem.test(selector) ); } @@ -564,8 +612,13 @@ class PurgeCSS { : { ...defaultOptions, ...userOptions, + safelist: standardizeSafelist(userOptions.safelist), }; - const { content, css, extractors } = this.options; + const { content, css, extractors, safelist } = this.options; + + if (this.options.variables) { + this.variablesStructure.safelist = safelist.variables || []; + } const fileFormatContents = content.filter( (o) => typeof o === "string" @@ -612,12 +665,24 @@ class PurgeCSS { */ public removeUnusedKeyframes(): void { for (const node of this.atRules.keyframes) { - if (!this.usedAnimations.has(node.params)) { + if ( + !this.usedAnimations.has(node.params) && + !this.isKeyframesSafelisted(node.params) + ) { node.remove(); } } } + /** + * Transform a selector node into a string + */ + private getSelectorValue(selector: selectorParser.Node): string | undefined { + return ( + (selector.type === "attribute" && selector.attribute) || selector.value + ); + } + /** * Determine if the selector should be kept, based on the selectors found in the files * @param selector set of css selectors found in the content files or string @@ -630,58 +695,78 @@ class PurgeCSS { // ignore the selector if it is inside a pseudo class if (isInPseudoClass(selector)) return true; + // if there is any greedy safelist pattern, run all the selector parts through them + // if there is any match, return true + if (this.options.safelist.greedy.length > 0) { + const selectorParts = selector.nodes.map(this.getSelectorValue); + if ( + selectorParts.some( + (selectorPart) => + selectorPart && this.isSelectorSafelistedGreedy(selectorPart) + ) + ) { + return true; + } + } + let isPresent = false; - for (const nodeSelector of selector.nodes) { - const val = - (nodeSelector.type === "attribute" && nodeSelector.attribute) || - nodeSelector.value; + for (const selectorNode of selector.nodes) { + const selectorValue = this.getSelectorValue(selectorNode); - // if the selector is whitelisted with children + // if the selector is safelisted with children // returns true to keep all children selectors - if (val && this.isSelectorWhitelistedChildren(val)) { + if (selectorValue && this.isSelectorSafelistedDeep(selectorValue)) { return true; } - // The selector is found in the internal and user-defined whitelist + // The selector is found in the internal and user-defined safelist if ( - val && - (CSS_WHITELIST.includes(val) || this.isSelectorWhitelisted(val)) + selectorValue && + (CSS_SAFELIST.includes(selectorValue) || + this.isSelectorSafelisted(selectorValue)) ) { isPresent = true; continue; } - switch (nodeSelector.type) { + // The selector is present in the blocklist + if (selectorValue && this.isSelectorBlocklisted(selectorValue)) { + isPresent = false; + continue; + } + + switch (selectorNode.type) { case "attribute": // `value` is a dynamic attribute, highly used in input element // the choice is to always leave `value` as it can change based on the user // idem for `checked`, `selected`, `open` isPresent = ["value", "checked", "selected", "open"].includes( - nodeSelector.attribute + selectorNode.attribute ) ? true - : isAttributeFound(nodeSelector, selectorsFromExtractor); + : isAttributeFound(selectorNode, selectorsFromExtractor); break; case "class": - isPresent = isClassFound(nodeSelector, selectorsFromExtractor); + isPresent = isClassFound(selectorNode, selectorsFromExtractor); break; case "id": - isPresent = isIdentifierFound(nodeSelector, selectorsFromExtractor); + isPresent = isIdentifierFound(selectorNode, selectorsFromExtractor); break; case "tag": - isPresent = isTagFound(nodeSelector, selectorsFromExtractor); + isPresent = isTagFound(selectorNode, selectorsFromExtractor); break; default: - break; + continue; } - // selector is not in whitelist children or in whitelist - // and it has not been found as an attribute/class/identifier/tag + // selector is not safelisted + // and it has not been found as an attribute/class/id/tag if (!isPresent) { return false; } } + return isPresent; } diff --git a/packages/purgecss/src/internal-whitelist.ts b/packages/purgecss/src/internal-safelist.ts similarity index 74% rename from packages/purgecss/src/internal-whitelist.ts rename to packages/purgecss/src/internal-safelist.ts index 23db9099..edd91b7d 100644 --- a/packages/purgecss/src/internal-whitelist.ts +++ b/packages/purgecss/src/internal-safelist.ts @@ -1,4 +1,4 @@ -export const CSS_WHITELIST = [ +export const CSS_SAFELIST = [ "*", "::-webkit-scrollbar", "::selection", diff --git a/packages/purgecss/src/options.ts b/packages/purgecss/src/options.ts index 12a28453..1d416bd8 100644 --- a/packages/purgecss/src/options.ts +++ b/packages/purgecss/src/options.ts @@ -12,7 +12,12 @@ export const defaultOptions: Options = { stdin: false, stdout: false, variables: false, - whitelist: [], - whitelistPatterns: [], - whitelistPatternsChildren: [], + safelist: { + standard: [], + deep: [], + greedy: [], + variables: [], + keyframes: [], + }, + blocklist: [], }; diff --git a/packages/purgecss/src/types/index.ts b/packages/purgecss/src/types/index.ts index 55da47a2..5b52ae1e 100644 --- a/packages/purgecss/src/types/index.ts +++ b/packages/purgecss/src/types/index.ts @@ -40,6 +40,18 @@ export interface Extractors { export type IgnoreType = "end" | "start" | "next"; +export type StringRegExpArray = Array; + +export type ComplexSafelist = { + standard?: StringRegExpArray; + deep?: RegExp[]; + greedy?: RegExp[]; + variables?: StringRegExpArray; + keyframes?: StringRegExpArray; +}; + +export type UserDefinedSafelist = StringRegExpArray | ComplexSafelist; + export interface UserDefinedOptions { content: Array; css: Array; @@ -52,9 +64,8 @@ export interface UserDefinedOptions { stdin?: boolean; stdout?: boolean; variables?: boolean; - whitelist?: string[]; - whitelistPatterns?: Array; - whitelistPatternsChildren?: Array; + safelist?: UserDefinedSafelist; + blocklist?: StringRegExpArray; } export interface Options { @@ -69,9 +80,8 @@ export interface Options { stdin: boolean; stdout: boolean; variables: boolean; - whitelist: string[]; - whitelistPatterns: Array; - whitelistPatternsChildren: Array; + safelist: Required; + blocklist: StringRegExpArray; } export interface ResultPurge { diff --git a/packages/vue-cli-plugin-purgecss/README.md b/packages/vue-cli-plugin-purgecss/README.md index a0713ab9..12d5ea3a 100644 --- a/packages/vue-cli-plugin-purgecss/README.md +++ b/packages/vue-cli-plugin-purgecss/README.md @@ -33,8 +33,7 @@ Below are the PurgeCSS options set by this plugin: const contentWithoutStyleBlocks = content.replace(/<\/style>/gi, '') return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || [] }, - whitelist: [], - whitelistPatterns: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], + safelist: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], } ``` diff --git a/packages/vue-cli-plugin-purgecss/generator/templates/postcss.config.js b/packages/vue-cli-plugin-purgecss/generator/templates/postcss.config.js index 1353779a..1da633c3 100644 --- a/packages/vue-cli-plugin-purgecss/generator/templates/postcss.config.js +++ b/packages/vue-cli-plugin-purgecss/generator/templates/postcss.config.js @@ -1,15 +1,27 @@ -const IN_PRODUCTION = process.env.NODE_ENV === 'production' +const IN_PRODUCTION = process.env.NODE_ENV === "production"; module.exports = { plugins: [ - IN_PRODUCTION && require('@fullhuman/postcss-purgecss')({ - content: [ `./public/**/*.html`, `./src/**/*.vue` ], - defaultExtractor (content) { - const contentWithoutStyleBlocks = content.replace(/<\/style>/gi, '') - return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || [] - }, - whitelist: [], - whitelistPatterns: [ /-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/ ], - }) + IN_PRODUCTION && + require("@fullhuman/postcss-purgecss")({ + content: [`./public/**/*.html`, `./src/**/*.vue`], + defaultExtractor(content) { + const contentWithoutStyleBlocks = content.replace( + /<\/style>/gi, + "" + ); + return ( + contentWithoutStyleBlocks.match( + /[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g + ) || [] + ); + }, + safelist: [ + /-(leave|enter|appear)(|-(to|from|active))$/, + /^(?!(|.*?:)cursor-move).+-move$/, + /^router-link(|-exact)-active$/, + /data-v-.*/, + ], + }), ], -} \ No newline at end of file +}; diff --git a/packages/vue-cli-plugin-purgecss/package.json b/packages/vue-cli-plugin-purgecss/package.json index 2f945f13..4455be34 100644 --- a/packages/vue-cli-plugin-purgecss/package.json +++ b/packages/vue-cli-plugin-purgecss/package.json @@ -1,6 +1,6 @@ { "name": "@fullhuman/vue-cli-plugin-purgecss", - "version": "2.2.0", + "version": "3.0.0", "description": "vue-cli plugin to add PurgeCSS", "author": "Ffloriel", "homepage": "https://purgecss.com", diff --git a/scripts/build.ts b/scripts/build.ts index e3082d3c..2bce4278 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -57,6 +57,7 @@ async function build(): Promise { }); await bundle.write({ + exports: "auto", file: path.resolve(packagesDirectory, pkg.name, `./lib/${pkg.name}.js`), format: "cjs", }); @@ -69,6 +70,7 @@ async function build(): Promise { external: ["purgecss"], }); await gruntBundle.write({ + exports: "auto", file: path.resolve( packagesDirectory, "grunt-purgecss",