diff --git a/experimental/css-has-pseudo/.tape.mjs b/experimental/css-has-pseudo/.tape.mjs
index ba1c9f37e..f3b59acde 100644
--- a/experimental/css-has-pseudo/.tape.mjs
+++ b/experimental/css-has-pseudo/.tape.mjs
@@ -1,68 +1,9 @@
import postcssTape from '../../packages/postcss-tape/dist/index.mjs';
import plugin from '@csstools/css-has-pseudo-experimental';
-import postcssLogical from 'postcss-logical';
-import postcssNesting from 'postcss-nesting';
-import postcssDirPseudoClass from 'postcss-dir-pseudo-class';
postcssTape(plugin)({
'basic': {
- message: 'supports basic usage'
+ message: 'supports basic usage',
+ warnings: 1
},
- 'basic:preserve': {
- message: 'supports { preserve: false } usage',
- options: {
- preserve: false
- }
- },
- 'basic:specificity-matching-name': {
- message: 'supports { specificityMatchingName: "other-thing-that-does-not-exist" } usage',
- options: {
- specificityMatchingName: 'other-thing-that-does-not-exist'
- }
- },
- 'generated-selector-cases': {
- message: 'correctly handles generated cases',
- warnings: 1,
- options: {
- preserve: false
- }
- },
- 'browser': {
- message: 'prepare CSS for chrome test',
- options: {
- preserve: false
- }
- },
- 'plugin-order-logical:before': {
- message: 'works with other plugins that modify selectors',
- plugins: [postcssLogical({preserve: false}), postcssDirPseudoClass({preserve: false}), plugin({preserve: false})],
- },
- 'plugin-order-logical:after': {
- message: 'works with other plugins that modify selectors',
- plugins: [plugin({ preserve: false }), postcssLogical({ preserve: false }), postcssDirPseudoClass({ preserve: false })],
- },
- 'plugin-order-logical:before:preserve': {
- message: 'works with other plugins that modify selectors',
- plugins: [postcssLogical({preserve: true}), postcssDirPseudoClass({preserve: true}), plugin({preserve: true})],
- },
- 'plugin-order-logical:after:preserve': {
- message: 'works with other plugins that modify selectors',
- plugins: [plugin({ preserve: true }), postcssLogical({ preserve: true }), postcssDirPseudoClass({ preserve: true })],
- },
- 'plugin-order-nesting:before': {
- message: 'works with other plugins that modify selectors',
- plugins: [postcssNesting({preserve: false}), plugin({preserve: false})],
- },
- 'plugin-order-nesting:after': {
- message: 'works with other plugins that modify selectors',
- plugins: [postcssNesting({preserve: false}), plugin({preserve: false})],
- },
- 'plugin-order-nesting:before:preserve': {
- message: 'works with other plugins that modify selectors',
- plugins: [plugin({preserve: true}), postcssNesting({preserve: true})],
- },
- 'plugin-order-nesting:after:preserve': {
- message: 'works with other plugins that modify selectors',
- plugins: [plugin({preserve: true}), postcssNesting({preserve: true})],
- }
});
diff --git a/experimental/css-has-pseudo/CHANGELOG.md b/experimental/css-has-pseudo/CHANGELOG.md
index 5ad138f67..88f5f19fd 100644
--- a/experimental/css-has-pseudo/CHANGELOG.md
+++ b/experimental/css-has-pseudo/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changes to CSS Has Pseudo
+### Unreleased
+
+- `@csstools/css-has-pseudo-experimental` is no longer supported. Please use `css-has-pseudo` instead.
+All issues have been resolved in the main plugin and the experimental plugin is no longer maintained.
+
+⚠️ This experimental plugin no longer has any effect on the output of your CSS.
+
### 0.5.2 (June 4, 2022)
- Update `@csstools/selector-specificity` (major)
diff --git a/experimental/css-has-pseudo/INSTALL-POSTCSS.md b/experimental/css-has-pseudo/INSTALL-POSTCSS.md
deleted file mode 100644
index f1f0052ce..000000000
--- a/experimental/css-has-pseudo/INSTALL-POSTCSS.md
+++ /dev/null
@@ -1,165 +0,0 @@
-# Installing PostCSS
-
-[EXPERIMENTAL CSS Has Pseudo] runs in all Node environments, with special instructions for:
-
-⚠️ Experimental version of [CSS Has Pseudo](https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo)
-
-| [Node](#node) | [PostCSS CLI](#postcss-cli) | [Webpack](#webpack) | [Create React App](#create-react-app) | [Gulp](#gulp) | [Grunt](#grunt) |
-| --- | --- | --- | --- | --- | --- |
-
-## Node
-
-Add [CSS Has Pseudo] to your project:
-
-```bash
-npm install css-has-pseudo --save-dev
-```
-
-Use it as a [PostCSS] plugin:
-
-```js
-const postcss = require('postcss');
-const cssHasPseudoExperimental = require('@csstools/css-has-pseudo-experimental');
-
-postcss([
- cssHasPseudoExperimental(/* pluginOptions */)
-]).process(YOUR_CSS /*, processOptions */);
-```
-
-## PostCSS CLI
-
-Add [PostCSS CLI] to your project:
-
-```bash
-npm install postcss-cli --save-dev
-```
-
-Use [CSS Has Pseudo] in your `postcss.config.js` configuration file:
-
-```js
-const cssHasPseudoExperimental = require('@csstools/css-has-pseudo-experimental');
-
-module.exports = {
- plugins: [
- cssHasPseudoExperimental(/* pluginOptions */)
- ]
-}
-```
-
-## Webpack
-
-Add [PostCSS Loader] to your project:
-
-```bash
-npm install postcss-loader --save-dev
-```
-
-Use [CSS Has Pseudo] in your Webpack configuration:
-
-```js
-const cssHasPseudoExperimental = require('@csstools/css-has-pseudo-experimental');
-
-module.exports = {
- module: {
- rules: [
- {
- test: /\.css$/,
- use: [
- 'style-loader',
- { loader: 'css-loader', options: { importLoaders: 1 } },
- { loader: 'postcss-loader', options: {
- ident: 'postcss',
- plugins: () => [
- cssHasPseudoExperimental(/* pluginOptions */)
- ]
- } }
- ]
- }
- ]
- }
-}
-```
-
-## Create React App
-
-Add [React App Rewired] and [React App Rewire PostCSS] to your project:
-
-```bash
-npm install react-app-rewired react-app-rewire-postcss --save-dev
-```
-
-Use [React App Rewire PostCSS] and [CSS Has Pseudo] in your
-`config-overrides.js`
-file:
-
-```js
-const reactAppRewirePostcss = require('react-app-rewire-postcss');
-const cssHasPseudoExperimental = require('@csstools/css-has-pseudo-experimental');
-
-module.exports = config => reactAppRewirePostcss(config, {
- plugins: () => [
- cssHasPseudoExperimental(/* pluginOptions */)
- ]
-});
-```
-
-## Gulp
-
-Add [Gulp PostCSS] to your project:
-
-```bash
-npm install gulp-postcss --save-dev
-```
-
-Use [CSS Has Pseudo] in your Gulpfile:
-
-```js
-const postcss = require('gulp-postcss');
-const cssHasPseudoExperimental = require('@csstools/css-has-pseudo-experimental');
-
-gulp.task('css', () => gulp.src('./src/*.css').pipe(
- postcss([
- cssHasPseudoExperimental(/* pluginOptions */)
- ])
-).pipe(
- gulp.dest('.')
-));
-```
-
-## Grunt
-
-Add [Grunt PostCSS] to your project:
-
-```bash
-npm install grunt-postcss --save-dev
-```
-
-Use [CSS Has Pseudo] in your Gruntfile:
-
-```js
-const cssHasPseudoExperimental = require('@csstools/css-has-pseudo-experimental');
-
-grunt.loadNpmTasks('grunt-postcss');
-
-grunt.initConfig({
- postcss: {
- options: {
- use: [
- cssHasPseudoExperimental(/* pluginOptions */)
- ]
- },
- dist: {
- src: '*.css'
- }
- }
-});
-```
-
-[EXPERIMENTAL CSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/experimental/css-has-pseudo
-[Gulp PostCSS]: https://github.com/postcss/gulp-postcss
-[Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss
-[PostCSS]: https://github.com/postcss/postcss
-[PostCSS CLI]: https://github.com/postcss/postcss-cli
-[PostCSS Loader]: https://github.com/postcss/postcss-loader
-[React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss
-[React App Rewired]: https://github.com/timarney/react-app-rewired
diff --git a/experimental/css-has-pseudo/README-BROWSER.md b/experimental/css-has-pseudo/README-BROWSER.md
deleted file mode 100644
index ad2664c9d..000000000
--- a/experimental/css-has-pseudo/README-BROWSER.md
+++ /dev/null
@@ -1,152 +0,0 @@
-# EXPERIMENTAL : CSS Has Pseudo for Browsers [][EXPERIMENTAL CSS Has Pseudo]
-
-[![NPM Version][npm-img]][npm-url]
-[
][discord]
-
-⚠️ Experimental version of [CSS Has Pseudo](https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo)
-
-[EXPERIMENTAL CSS Has Pseudo] lets you style elements relative to other elements in CSS,
-following the [Selectors Level 4] specification.
-
-## Usage
-
-Add [EXPERIMENTAL CSS Has Pseudo] to your build tool:
-
-```bash
-npm install @csstools/css-has-pseudo-experimental
-```
-
-Then include and initialize it on your document:
-
-```js
-const cssHasPseudo = require('@csstools/css-has-pseudo-experimental/browser');
-
-cssHasPseudo(document);
-```
-
-```html
-
-
-
-```
-
-⚠️ Please use a versioned url, like this : `https://unpkg.com/@csstools/css-has-pseudo-experimental@0.2.0/dist/browser-global.js`
-Without the version, you might unexpectedly get a new major version of the library with breaking changes.
-
-## CORS
-
-⚠️ Applies to you if you load CSS from a different domain than the page.
-In this case the CSS is treated as untrusted and will not be made available to the Javascript polyfill.
-
-Example :
-
-| page | css | CORS applies |
-| --- | --- | --- |
-| https://example.com/ | https://example.com/style.css | no |
-| https://example.com/ | https://other.com/style.css | yes |
-
-**You might see one of these error messages :**
-
-```html
-
-
-```
-
-Chrome :
-
-> DOMException: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules
-
-Safari :
-
-> SecurityError: Not allowed to access cross-origin stylesheet
-
-Firefox :
-
-> DOMException: CSSStyleSheet.cssRules getter: Not allowed to access cross-origin stylesheet
-
-To resolve CORS errors you need to take two steps :
-
-- add HTTP header `Access-Control-Allow-Origin:
][EXPERIMENTAL CSS Has Pseudo]
-
-[![NPM Version][npm-img]][npm-url]
-[
][discord]
-
-⚠️ Experimental version of [CSS Has Pseudo](https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo)
-
-[EXPERIMENTAL CSS Has Pseudo] lets you style elements relative to other elements in CSS,
-following the [Selectors Level 4] specification.
-
-```css
-body:has(:focus) {
- background-color: yellow;
-}
-
-/* becomes */
-
-[csstools-has-2q-33-2s-3d-1m-2w-2p-37-14-1m-2u-33-2r-39-37-15]:not(does-not-exist) {
- background-color: yellow;
-}
-
-body:has(:focus) {
- background-color: yellow;
-}
-```
-
-[EXPERIMENTAL CSS Has Pseudo] duplicates rules using the `:has` pseudo-class with a `[has]`
-attribute selector. The preservation of the original `:has` rule can be
-disabled using the `preserve` option.
-
-## Usage
-
-Add [EXPERIMENTAL CSS Has Pseudo] to your project:
-
-```bash
-npm install @csstools/css-has-pseudo-experimental --save-dev
-```
-
-Use [EXPERIMENTAL CSS Has Pseudo] as a [PostCSS] plugin:
-
-```js
-const postcss = require('postcss');
-const cssHasPseudoExperimental = require('@csstools/css-has-pseudo-experimental');
-
-postcss([
- cssHasPseudoExperimental(/* pluginOptions */)
-]).process(YOUR_CSS /*, processOptions */);
-```
-
-[EXPERIMENTAL CSS Has Pseudo] runs in all Node environments, with special
-instructions for:
-
-| [Node](INSTALL-POSTCSS.md#node) | [PostCSS CLI](INSTALL-POSTCSS.md#postcss-cli) | [Webpack](INSTALL-POSTCSS.md#webpack) | [Create React App](INSTALL-POSTCSS.md#create-react-app) | [Gulp](INSTALL-POSTCSS.md#gulp) | [Grunt](INSTALL-POSTCSS.md#grunt) |
-| --- | --- | --- | --- | --- | --- |
-
-## Options
-
-### preserve
-
-The `preserve` option defines whether the original selector should remain. By
-default, the original selector is preserved.
-
-```js
-cssHasPseudoExperimental({ preserve: false });
-```
-
-```css
-body:has(:focus) {
- background-color: yellow;
-}
-
-/* becomes */
-
-[csstools-has-2q-33-2s-3d-1m-2w-2p-37-14-1m-2u-33-2r-39-37-15]:not(does-not-exist) {
- background-color: yellow;
-}
-```
-
-### specificityMatchingName
-
-The `specificityMatchingName` option allows you to change to selector that is used to adjust specificity.
-The default value is `does-not-exist`.
-If this is an actual class, id or tag name in your code, you will need to set a different option here.
-
-See how `:not` is used to modify [specificity](#specificity).
-
-```js
-postcss([
- cssHasPseudoExperimental({ specificityMatchingName: 'something-random' })
-]).process(YOUR_CSS /*, processOptions */);
-```
-
-[specificity 1, 2, 0](https://polypane.app/css-specificity-calculator/#selector=.x%3Ahas(%3E%20%23a%3Ahover))
-
-Before :
-
-```css
-.x:has(> #a:hover) {
- order: 11;
-}
-```
-
-After :
-
-[specificity 1, 2, 0](https://polypane.app/css-specificity-calculator/#selector=%5Bcsstools-has-1a-3c-1m-2w-2p-37-14-1q-w-z-2p-1m-2w-33-3a-2t-36-15%5D%3Anot(%23does-not-exist)%3Anot(.does-not-exist))
-
-```css
-[csstools-has-1a-3c-1m-2w-2p-37-14-1q-w-z-2p-1m-2w-33-3a-2t-36-15]:not(#does-not-exist):not(.does-not-exist) {
- order: 11;
-}
-```
-
-## ⚠️ Known shortcomings
-
-### Specificity
-
-`:has` transforms will result in at least one tag selector with specificity `0, 1, 0`.
-If your selector has only tags we won't be able to match the original specificity.
-
-Before :
-
-[specificity 0, 0, 2](https://polypane.app/css-specificity-calculator/#selector=figure%3Ahas(%3E%20img))
-
-```css
-figure:has(> img)
-```
-
-After :
-
-[specificity 0, 1, 2](https://polypane.app/css-specificity-calculator/#selector=%5Bcsstools-has-2u-2x-2v-39-36-2t-1m-2w-2p-37-14-1q-w-2x-31-2v-15%5D%3Anot(does-not-exist)%3Anot(does-not-exist))
-
-```css
-[csstools-has-2u-2x-2v-39-36-2t-1m-2w-2p-37-14-1q-w-2x-31-2v-15]:not(does-not-exist):not(does-not-exist)
-```
-
-### Plugin order
-
-As selectors are encoded this plugin (or `postcss-preset-env`) must be run after any other plugin that transforms selectors.
-
-For `postcss-preset-env` we take care to handle this for you.
-
-If other plugins are used you need to place these in your config before `postcss-preset-env` or `css-has-pseudo`.
-
-Please let us know if you have issues with plugins that transform selectors.
-Then we can investigate and maybe fix these.
-
-## PostCSS Preset Env
-
-When you use `postcss-preset-env` you must disable the regular plugin.
-
-The experimental plugin must be added after any other plugin that modifies selectors.
-
-```js
-plugins: [
- // other plugins
- postcssPresetEnv({
- features: {
- 'css-has-pseudo': false
- }
- }),
- // other plugins
- cssHasPseudoExperimental(), // last
-]
-```
-
-[discord]: https://discord.gg/bUadyRwkJS
-[npm-img]: https://img.shields.io/npm/v/@csstools/css-has-pseudo-experimental.svg
-[npm-url]: https://www.npmjs.com/package/@csstools/css-has-pseudo-experimental
-
-[PostCSS]: https://github.com/postcss/postcss
-[EXPERIMENTAL CSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/experimental/css-has-pseudo
-[Selectors Level 4]: https://drafts.csswg.org/selectors-4/#has-pseudo
diff --git a/experimental/css-has-pseudo/README.md b/experimental/css-has-pseudo/README.md
index e267b6cb1..d32523ec7 100644
--- a/experimental/css-has-pseudo/README.md
+++ b/experimental/css-has-pseudo/README.md
@@ -3,111 +3,10 @@
[![NPM Version][npm-img]][npm-url]
[
][discord]
-⚠️ Experimental version of [CSS Has Pseudo](https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo)
+`@csstools/css-has-pseudo-experimental` is no longer supported. Please use `css-has-pseudo` instead.
+All issues have been resolved in the main plugin and the experimental plugin is no longer maintained.
-[EXPERIMENTAL CSS Has Pseudo] lets you style elements relative to other elements in CSS,
-following the [Selectors Level 4] specification.
-
-[](https://caniuse.com/#feat=css-has)
-
-```css
-a:has(> img) {
- /* style links that contain an image */
-}
-
-h1:has(+ p) {
- /* style level 1 headings that are followed by a paragraph */
-}
-
-section:not(:has(h1, h2, h3, h4, h5, h6)) {
- /* style sections that don’t contain any heading elements */
-}
-
-body:has(:focus) {
- /* style the body if it contains a focused element */
-}
-```
-
-Next, use your transformed CSS with this script:
-
-```html
-
-
-
-```
-
-⚠️ Please use a versioned url, like this : `https://unpkg.com/@csstools/css-has-pseudo-experimental@0.1.0/dist/browser-global.js`
-Without the version, you might unexpectedly get a new major version of the library with breaking changes.
-
-⚠️ If you were using an older version via a CDN, please update the entire url.
-The old URL will no longer work.
-
-That’s it. The script works in most browser versions, including
-Internet Explorer 11. With a [Mutation Observer polyfill], the script will work
-down to Internet Explorer 9.
-
-See [README BROWSER](README-BROWSER.md) for more information.
-
-## How it works
-
-The [PostCSS plugin](README-POSTCSS.md) clones rules containing `:has`,
-replacing them with an alternative `[:has]` selector.
-
-```css
-body:has(:focus) {
- background-color: yellow;
-}
-
-section:not(:has(h1, h2, h3, h4, h5, h6)) {
- background-color: gray;
-}
-
-/* becomes */
-
-[csstools-has-2q-33-2s-3d-1m-2w-2p-37-14-1m-2u-33-2r-39-37-15]:not(does-not-exist) {
- background-color: yellow;
-}
-
-body:has(:focus) {
- background-color: yellow;
-}
-
-[csstools-has-37-2t-2r-38-2x-33-32-1m-32-33-38-14-1m-2w-2p-37-14-2w-1d-18-w-2w-1e-18-w-2w-1f-18-w-2w-1g-18-w-2w-1h-18-w-2w-1i-15-15]:not(does-not-exist):not(does-not-exist) {
- background-color: gray;
-}
-
-section:not(:has(h1, h2, h3, h4, h5, h6)) {
- background-color: gray;
-}
-```
-
-Next, the [JavaScript library](README-BROWSER.md) adds a `[:has]` attribute to
-elements otherwise matching `:has` natively.
-
-```html
-
-
-
-```
-
-## PostCSS Preset Env
-
-When you use `postcss-preset-env` you must disable the regular plugin.
-
-The experimental plugin must be added after any other plugin that modifies selectors.
-
-```js
-plugins: [
- // other plugins
- postcssPresetEnv({
- features: {
- 'css-has-pseudo': false
- }
- }),
- // other plugins
- cssHasPseudoExperimental(), // last
-]
-```
+⚠️ This experimental plugin no longer has any effect on the output of your CSS.
[discord]: https://discord.gg/bUadyRwkJS
[npm-img]: https://img.shields.io/npm/v/@csstools/css-has-pseudo-experimental.svg
diff --git a/experimental/css-has-pseudo/package.json b/experimental/css-has-pseudo/package.json
index cbd705593..c8fdb2890 100644
--- a/experimental/css-has-pseudo/package.json
+++ b/experimental/css-has-pseudo/package.json
@@ -35,17 +35,9 @@
"README.md",
"dist"
],
- "dependencies": {
- "@csstools/selector-specificity": "^2.0.1",
- "postcss-selector-parser": "^6.0.10"
- },
"peerDependencies": {
"postcss": "^8.2"
},
- "devDependencies": {
- "@mrhenry/core-web": "^0.7.2",
- "puppeteer": "^13.6.0"
- },
"scripts": {
"build": "rollup -c ../../rollup/default.js",
"clean": "node -e \"fs.rmSync('./dist', { recursive: true, force: true });\"",
@@ -54,11 +46,9 @@
"lint:package-json": "node ../../.github/bin/format-package-json.mjs",
"prepublishOnly": "npm run clean && npm run build && npm run test",
"stryker": "stryker run --logLevel error",
- "test": "node .tape.mjs && npm run test:unit && npm run test:exports",
- "test:browser": "node ./test/_browser.mjs",
+ "test": "node .tape.mjs && npm run test:exports",
"test:exports": "node ./test/_import.mjs && node ./test/_require.cjs",
- "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs",
- "test:unit": "node ./src/encode/test.mjs"
+ "test:rewrite-expects": "REWRITE_EXPECTS=true node .tape.mjs"
},
"homepage": "https://github.com/csstools/postcss-plugins/tree/main/experimental/css-has-pseudo#readme",
"repository": {
diff --git a/experimental/css-has-pseudo/src/browser.js b/experimental/css-has-pseudo/src/browser.js
index 398156d8c..14a87b9a8 100644
--- a/experimental/css-has-pseudo/src/browser.js
+++ b/experimental/css-has-pseudo/src/browser.js
@@ -1,262 +1,3 @@
-/* global MutationObserver,requestAnimationFrame,cancelAnimationFrame,self,HTMLElement */
-
-import '@mrhenry/core-web/modules/~element-qsa-has.js';
-import extractEncodedSelectors from './encode/extract.mjs';
-import encodeCSS from './encode/encode.mjs';
-
-export default function cssHasPseudo(document, options) {
- // OPTIONS
- {
- if (!options) {
- options = {};
- }
-
- options = {
- hover: (!!options.hover) || false,
- debug: (!!options.debug) || false,
- observedAttributes: options.observedAttributes || [],
- forcePolyfill: (!!options.forcePolyfill) || false,
- };
-
- if (!options.forcePolyfill) {
- try {
- // Chrome does not support forgiving selector lists in :has()
- document.querySelector(':has(*, :does-not-exist, > *)');
-
- // Safari incorrectly returns the html element with this query
- if (!document.querySelector(':has(:scope *)')) {
- // Native support detected.
- // Doing early return.
- return;
- }
-
- // fallthrough to polyfill
- } catch (_) {
- // fallthrough to polyfill
- }
- }
-
- if (!Array.isArray(options.observedAttributes)) {
- options.observedAttributes = [];
- }
-
- options.observedAttributes = options.observedAttributes.filter((x) => {
- return (typeof x === 'string');
- });
-
- // https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
- // `data-*` and `style` were omitted
- options.observedAttributes = options.observedAttributes.concat(['accept', 'accept-charset', 'accesskey', 'action', 'align', 'allow', 'alt', 'async', 'autocapitalize', 'autocomplete', 'autofocus', 'autoplay', 'buffered', 'capture', 'challenge', 'charset', 'checked', 'cite', 'class', 'code', 'codebase', 'cols', 'colspan', 'content', 'contenteditable', 'contextmenu', 'controls', 'coords', 'crossorigin', 'csp', 'data', 'datetime', 'decoding', 'default', 'defer', 'dir', 'dirname', 'disabled', 'download', 'draggable', 'enctype', 'enterkeyhint', 'for', 'form', 'formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', 'headers', 'hidden', 'high', 'href', 'hreflang', 'http-equiv', 'icon', 'id', 'importance', 'integrity', 'intrinsicsize', 'inputmode', 'ismap', 'itemprop', 'keytype', 'kind', 'label', 'lang', 'language', 'list', 'loop', 'low', 'manifest', 'max', 'maxlength', 'minlength', 'media', 'method', 'min', 'multiple', 'muted', 'name', 'novalidate', 'open', 'optimum', 'pattern', 'ping', 'placeholder', 'poster', 'preload', 'radiogroup', 'readonly', 'referrerpolicy', 'rel', 'required', 'reversed', 'rows', 'rowspan', 'sandbox', 'scope', 'scoped', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'spellcheck', 'src', 'srcdoc', 'srclang', 'srcset', 'start', 'step', 'summary', 'tabindex', 'target', 'title', 'translate', 'type', 'usemap', 'value', 'width', 'wrap']);
- }
-
- const observedItems = [];
-
- // document.createAttribute() doesn't support `:` in the name. innerHTML does
- const attributeElement = document.createElement('x');
-
- // walk all stylesheets to collect observed css rules
- [].forEach.call(document.styleSheets, walkStyleSheet);
- transformObservedItemsThrottled();
-
- // observe DOM modifications that affect selectors
- if ('MutationObserver' in self) {
- const mutationObserver = new MutationObserver((mutationsList) => {
- mutationsList.forEach(mutation => {
- [].forEach.call(mutation.addedNodes || [], node => {
- // walk stylesheets to collect observed css rules
- if (node.nodeType === 1 && node.sheet) {
- walkStyleSheet(node.sheet);
- }
- });
-
- // transform observed css rules
- cleanupObservedCssRules();
- transformObservedItemsThrottled();
- });
- });
-
- mutationObserver.observe(document, { childList: true, subtree: true, attributes: true, attributeFilter: options.observedAttributes });
- }
-
- // observe DOM events that affect pseudo-selectors
- document.addEventListener('focus', transformObservedItemsThrottled, true);
- document.addEventListener('blur', transformObservedItemsThrottled, true);
- document.addEventListener('input', transformObservedItemsThrottled);
- document.addEventListener('change', transformObservedItemsThrottled, true);
-
- if (options.hover) {
- if ('onpointerenter' in document) {
- document.addEventListener('pointerenter', transformObservedItemsThrottled, true);
- document.addEventListener('pointerleave', transformObservedItemsThrottled, true);
- } else {
- document.addEventListener('mouseover', transformObservedItemsThrottled, true);
- document.addEventListener('mouseout', transformObservedItemsThrottled, true);
- }
- }
-
- // observe Javascript setters that effect pseudo-selectors
- if ('defineProperty' in Object && 'getOwnPropertyDescriptor' in Object && 'hasOwnProperty' in Object) {
- try {
- // eslint-disable-next-line no-inner-declarations
- function observeProperty(proto, property) {
- // eslint-disable-next-line no-prototype-builtins
- if (proto.hasOwnProperty(property)) {
- const descriptor = Object.getOwnPropertyDescriptor(proto, property);
- if (descriptor && descriptor.configurable && 'set' in descriptor) {
- Object.defineProperty(proto, property, {
- configurable: descriptor.configurable,
- enumerable: descriptor.enumerable,
- get: function () {
- return descriptor.get.apply(this, arguments);
- },
- set: function () {
- descriptor.set.apply(this, arguments);
-
- try {
- transformObservedItemsThrottled();
- } catch (_) {
- // should never happen as there is an inner try/catch
- // but just in case
- }
- },
- });
- }
- }
- }
-
- if ('HTMLElement' in self && HTMLElement.prototype) {
- observeProperty(HTMLElement.prototype, 'disabled');
- }
-
- // Not all of these elements have all of these properties.
- // But the code above checks if they exist first.
- ['checked', 'selected', 'readOnly', 'required'].forEach((property) => {
- [
- 'HTMLButtonElement',
- 'HTMLFieldSetElement',
- 'HTMLInputElement',
- 'HTMLMeterElement',
- 'HTMLOptGroupElement',
- 'HTMLOptionElement',
- 'HTMLOutputElement',
- 'HTMLProgressElement',
- 'HTMLSelectElement',
- 'HTMLTextAreaElement',
- ].forEach((elementName) => {
- if (elementName in self && self[elementName].prototype) {
- observeProperty(self[elementName].prototype, property);
- }
- });
- });
- } catch (e) {
- if (options.debug) {
- console.error(e);
- }
- }
- }
-
- let transformObservedItemsThrottledBusy = false;
- function transformObservedItemsThrottled() {
- if (transformObservedItemsThrottledBusy) {
- cancelAnimationFrame(transformObservedItemsThrottledBusy);
- }
-
- transformObservedItemsThrottledBusy = requestAnimationFrame(() => {
- transformObservedItems();
- });
- }
-
- // transform observed css rules
- function transformObservedItems() {
- observedItems.forEach((item) => {
- const nodes = [];
-
- let matches = [];
- try {
- matches = document.querySelectorAll(item.selector);
- } catch (e) {
- if (options.debug) {
- console.error(e);
- }
- return;
- }
-
- [].forEach.call(matches, (element) => {
- // memorize the node
- nodes.push(element);
-
- // set an attribute with an irregular attribute name
- // document.createAttribute() doesn't support special characters
- attributeElement.innerHTML = '