├── .github └── workflows │ └── build.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── eslint.config.mjs ├── lib ├── CSSStyleDeclaration.js ├── allExtraProperties.js ├── allProperties.js ├── allWebkitProperties.js ├── constants.js ├── named_colors.json ├── parsers.js ├── properties │ ├── azimuth.js │ ├── background.js │ ├── backgroundAttachment.js │ ├── backgroundColor.js │ ├── backgroundImage.js │ ├── backgroundPosition.js │ ├── backgroundRepeat.js │ ├── border.js │ ├── borderBottom.js │ ├── borderBottomColor.js │ ├── borderBottomStyle.js │ ├── borderBottomWidth.js │ ├── borderCollapse.js │ ├── borderColor.js │ ├── borderLeft.js │ ├── borderLeftColor.js │ ├── borderLeftStyle.js │ ├── borderLeftWidth.js │ ├── borderRight.js │ ├── borderRightColor.js │ ├── borderRightStyle.js │ ├── borderRightWidth.js │ ├── borderSpacing.js │ ├── borderStyle.js │ ├── borderTop.js │ ├── borderTopColor.js │ ├── borderTopStyle.js │ ├── borderTopWidth.js │ ├── borderWidth.js │ ├── bottom.js │ ├── clear.js │ ├── clip.js │ ├── color.js │ ├── cssFloat.js │ ├── flex.js │ ├── flexBasis.js │ ├── flexGrow.js │ ├── flexShrink.js │ ├── float.js │ ├── floodColor.js │ ├── font.js │ ├── fontFamily.js │ ├── fontSize.js │ ├── fontStyle.js │ ├── fontVariant.js │ ├── fontWeight.js │ ├── height.js │ ├── left.js │ ├── lightingColor.js │ ├── lineHeight.js │ ├── margin.js │ ├── marginBottom.js │ ├── marginLeft.js │ ├── marginRight.js │ ├── marginTop.js │ ├── opacity.js │ ├── outlineColor.js │ ├── padding.js │ ├── paddingBottom.js │ ├── paddingLeft.js │ ├── paddingRight.js │ ├── paddingTop.js │ ├── right.js │ ├── stopColor.js │ ├── textLineThroughColor.js │ ├── textOverlineColor.js │ ├── textUnderlineColor.js │ ├── top.js │ ├── webkitBorderAfterColor.js │ ├── webkitBorderBeforeColor.js │ ├── webkitBorderEndColor.js │ ├── webkitBorderStartColor.js │ ├── webkitColumnRuleColor.js │ ├── webkitMatchNearestMailBlockquoteColor.js │ ├── webkitTapHighlightColor.js │ ├── webkitTextEmphasisColor.js │ ├── webkitTextFillColor.js │ ├── webkitTextStrokeColor.js │ └── width.js └── utils │ └── getBasicPropertyDescriptor.js ├── package-lock.json ├── package.json ├── prettier.config.mjs ├── scripts ├── downloadLatestProperties.mjs ├── generateImplementedProperties.mjs └── generate_properties.js └── test ├── CSSStyleDeclaration.js └── parsers.js /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | push: 7 | branches: 8 | - main 9 | jobs: 10 | build: 11 | name: Lint and tests 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | node-version: 17 | - 18 18 | - 20 19 | - latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - run: npm ci 26 | - run: npm run test-ci 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | lib/implementedProperties.js 4 | lib/properties.js 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /* 2 | !lib/ 3 | !LICENSE 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Chad Walker 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSSStyleDeclaration 2 | 3 | A Node.js implementation of the CSS Object Model [`CSSStyleDeclaration` class](https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface). 4 | 5 | ## Background 6 | 7 | This package is an extension of the `CSSStyleDeclaration` class in Nikita Vasilyev's [CSSOM](https://github.com/NV/CSSOM), with added support for modern specifications. The primary use case is for testing browser code in a Node environment. 8 | 9 | It was originally created by Chad Walker, it is now maintained by the jsdom community. 10 | 11 | Bug reports and pull requests are welcome. 12 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; 2 | import globals from 'globals'; 3 | 4 | export default [ 5 | { 6 | ignores: ['lib/implementedProperties.js', 'lib/properties.js'], 7 | }, 8 | { 9 | files: ['**/*.js'], 10 | languageOptions: { 11 | sourceType: 'commonjs', 12 | }, 13 | }, 14 | eslintPluginPrettierRecommended, 15 | { 16 | files: ['scripts/**/*'], 17 | rules: { 18 | 'no-console': 'off', 19 | }, 20 | }, 21 | { 22 | files: ['scripts/**/*', 'tests/**/*'], 23 | languageOptions: { 24 | globals: globals.node, 25 | }, 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /lib/CSSStyleDeclaration.js: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * This is a fork from the CSS Style Declaration part of 3 | * https://github.com/NV/CSSOM 4 | ********************************************************************/ 5 | 'use strict'; 6 | var CSSOM = require('rrweb-cssom'); 7 | var allProperties = require('./allProperties'); 8 | var allExtraProperties = require('./allExtraProperties'); 9 | var implementedProperties = require('./implementedProperties'); 10 | var { dashedToCamelCase } = require('./parsers'); 11 | var getBasicPropertyDescriptor = require('./utils/getBasicPropertyDescriptor'); 12 | 13 | /** 14 | * @constructor 15 | * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration 16 | */ 17 | var CSSStyleDeclaration = function CSSStyleDeclaration(onChangeCallback) { 18 | this._values = {}; 19 | this._importants = {}; 20 | this._length = 0; 21 | this._onChange = onChangeCallback; 22 | this._setInProgress = false; 23 | }; 24 | CSSStyleDeclaration.prototype = { 25 | constructor: CSSStyleDeclaration, 26 | 27 | /** 28 | * 29 | * @param {string} name 30 | * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue 31 | * @return {string} the value of the property if it has been explicitly set for this declaration block. 32 | * Returns the empty string if the property has not been set. 33 | */ 34 | getPropertyValue: function (name) { 35 | if (!this._values.hasOwnProperty(name)) { 36 | return ''; 37 | } 38 | return this._values[name].toString(); 39 | }, 40 | 41 | /** 42 | * 43 | * @param {string} name 44 | * @param {string} value 45 | * @param {string} [priority=null] "important" or null 46 | * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty 47 | */ 48 | setProperty: function (name, value, priority) { 49 | if (value === undefined) { 50 | return; 51 | } 52 | if (value === null || value === '') { 53 | this.removeProperty(name); 54 | return; 55 | } 56 | var isCustomProperty = 57 | name.indexOf('--') === 0 || 58 | (typeof value === 'string' && /^var\(--[-\w]+,?.*\)$/.test(value)); 59 | if (isCustomProperty) { 60 | this._setProperty(name, value, priority); 61 | return; 62 | } 63 | var lowercaseName = name.toLowerCase(); 64 | if (!allProperties.has(lowercaseName) && !allExtraProperties.has(lowercaseName)) { 65 | return; 66 | } 67 | 68 | this[lowercaseName] = value; 69 | this._importants[lowercaseName] = priority; 70 | }, 71 | _setProperty: function (name, value, priority) { 72 | if (value === undefined) { 73 | return; 74 | } 75 | if (value === null || value === '') { 76 | this.removeProperty(name); 77 | return; 78 | } 79 | 80 | var originalText; 81 | if (this._onChange) { 82 | originalText = this.cssText; 83 | } 84 | 85 | if (this._values[name]) { 86 | // Property already exist. Overwrite it. 87 | var index = Array.prototype.indexOf.call(this, name); 88 | if (index < 0) { 89 | this[this._length] = name; 90 | this._length++; 91 | } 92 | } else { 93 | // New property. 94 | this[this._length] = name; 95 | this._length++; 96 | } 97 | this._values[name] = value; 98 | this._importants[name] = priority; 99 | if (this._onChange && this.cssText !== originalText && !this._setInProgress) { 100 | this._onChange(this.cssText); 101 | } 102 | }, 103 | 104 | /** 105 | * 106 | * @param {string} name 107 | * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty 108 | * @return {string} the value of the property if it has been explicitly set for this declaration block. 109 | * Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property. 110 | */ 111 | removeProperty: function (name) { 112 | if (!this._values.hasOwnProperty(name)) { 113 | return ''; 114 | } 115 | 116 | var prevValue = this._values[name]; 117 | delete this._values[name]; 118 | delete this._importants[name]; 119 | 120 | var index = Array.prototype.indexOf.call(this, name); 121 | if (index < 0) { 122 | return prevValue; 123 | } 124 | 125 | // That's what WebKit and Opera do 126 | Array.prototype.splice.call(this, index, 1); 127 | 128 | // That's what Firefox does 129 | //this[index] = "" 130 | 131 | if (this._onChange) { 132 | this._onChange(this.cssText); 133 | } 134 | return prevValue; 135 | }, 136 | 137 | /** 138 | * 139 | * @param {String} name 140 | */ 141 | getPropertyPriority: function (name) { 142 | return this._importants[name] || ''; 143 | }, 144 | 145 | getPropertyCSSValue: function () { 146 | //FIXME 147 | return; 148 | }, 149 | 150 | /** 151 | * element.style.overflow = "auto" 152 | * element.style.getPropertyShorthand("overflow-x") 153 | * -> "overflow" 154 | */ 155 | getPropertyShorthand: function () { 156 | //FIXME 157 | return; 158 | }, 159 | 160 | isPropertyImplicit: function () { 161 | //FIXME 162 | return; 163 | }, 164 | 165 | /** 166 | * http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-item 167 | */ 168 | item: function (index) { 169 | index = parseInt(index, 10); 170 | if (index < 0 || index >= this._length) { 171 | return ''; 172 | } 173 | return this[index]; 174 | }, 175 | }; 176 | 177 | Object.defineProperties(CSSStyleDeclaration.prototype, { 178 | cssText: { 179 | get: function () { 180 | var properties = []; 181 | var i; 182 | var name; 183 | var value; 184 | var priority; 185 | for (i = 0; i < this._length; i++) { 186 | name = this[i]; 187 | value = this.getPropertyValue(name); 188 | priority = this.getPropertyPriority(name); 189 | if (priority !== '') { 190 | priority = ' !' + priority; 191 | } 192 | properties.push([name, ': ', value, priority, ';'].join('')); 193 | } 194 | return properties.join(' '); 195 | }, 196 | set: function (value) { 197 | var i; 198 | this._values = {}; 199 | Array.prototype.splice.call(this, 0, this._length); 200 | this._importants = {}; 201 | var dummyRule; 202 | try { 203 | dummyRule = CSSOM.parse('#bogus{' + value + '}').cssRules[0].style; 204 | } catch (err) { 205 | // malformed css, just return 206 | return; 207 | } 208 | this._setInProgress = true; 209 | var rule_length = dummyRule.length; 210 | var name; 211 | for (i = 0; i < rule_length; ++i) { 212 | name = dummyRule[i]; 213 | this.setProperty( 214 | dummyRule[i], 215 | dummyRule.getPropertyValue(name), 216 | dummyRule.getPropertyPriority(name) 217 | ); 218 | } 219 | this._setInProgress = false; 220 | if (this._onChange) { 221 | this._onChange(this.cssText); 222 | } 223 | }, 224 | enumerable: true, 225 | configurable: true, 226 | }, 227 | parentRule: { 228 | get: function () { 229 | return null; 230 | }, 231 | enumerable: true, 232 | configurable: true, 233 | }, 234 | length: { 235 | get: function () { 236 | return this._length; 237 | }, 238 | /** 239 | * This deletes indices if the new length is less then the current 240 | * length. If the new length is more, it does nothing, the new indices 241 | * will be undefined until set. 242 | **/ 243 | set: function (value) { 244 | var i; 245 | for (i = value; i < this._length; i++) { 246 | delete this[i]; 247 | } 248 | this._length = value; 249 | }, 250 | enumerable: true, 251 | configurable: true, 252 | }, 253 | }); 254 | 255 | require('./properties')(CSSStyleDeclaration.prototype); 256 | 257 | allProperties.forEach(function (property) { 258 | if (!implementedProperties.has(property)) { 259 | var declaration = getBasicPropertyDescriptor(property); 260 | Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration); 261 | Object.defineProperty(CSSStyleDeclaration.prototype, dashedToCamelCase(property), declaration); 262 | } 263 | }); 264 | 265 | allExtraProperties.forEach(function (property) { 266 | if (!implementedProperties.has(property)) { 267 | var declaration = getBasicPropertyDescriptor(property); 268 | Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration); 269 | Object.defineProperty(CSSStyleDeclaration.prototype, dashedToCamelCase(property), declaration); 270 | } 271 | }); 272 | 273 | exports.CSSStyleDeclaration = CSSStyleDeclaration; 274 | -------------------------------------------------------------------------------- /lib/allExtraProperties.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * This file contains all implemented properties that are not a part of any 5 | * current specifications or drafts, but are handled by browsers nevertheless. 6 | */ 7 | 8 | var allWebkitProperties = require('./allWebkitProperties'); 9 | 10 | module.exports = new Set( 11 | [ 12 | 'background-position-x', 13 | 'background-position-y', 14 | 'background-repeat-x', 15 | 'background-repeat-y', 16 | 'color-interpolation', 17 | 'color-profile', 18 | 'color-rendering', 19 | 'css-float', 20 | 'enable-background', 21 | 'fill', 22 | 'fill-opacity', 23 | 'fill-rule', 24 | 'glyph-orientation-horizontal', 25 | 'image-rendering', 26 | 'kerning', 27 | 'marker', 28 | 'marker-end', 29 | 'marker-mid', 30 | 'marker-offset', 31 | 'marker-start', 32 | 'marks', 33 | 'pointer-events', 34 | 'shape-rendering', 35 | 'size', 36 | 'src', 37 | 'stop-color', 38 | 'stop-opacity', 39 | 'stroke', 40 | 'stroke-dasharray', 41 | 'stroke-dashoffset', 42 | 'stroke-linecap', 43 | 'stroke-linejoin', 44 | 'stroke-miterlimit', 45 | 'stroke-opacity', 46 | 'stroke-width', 47 | 'text-anchor', 48 | 'text-line-through', 49 | 'text-line-through-color', 50 | 'text-line-through-mode', 51 | 'text-line-through-style', 52 | 'text-line-through-width', 53 | 'text-overline', 54 | 'text-overline-color', 55 | 'text-overline-mode', 56 | 'text-overline-style', 57 | 'text-overline-width', 58 | 'text-rendering', 59 | 'text-underline', 60 | 'text-underline-color', 61 | 'text-underline-mode', 62 | 'text-underline-style', 63 | 'text-underline-width', 64 | 'unicode-range', 65 | 'vector-effect', 66 | ].concat(allWebkitProperties) 67 | ); 68 | -------------------------------------------------------------------------------- /lib/allProperties.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // autogenerated - 2024-09-07 3 | // https://www.w3.org/Style/CSS/all-properties.en.html 4 | 5 | module.exports = new Set([ 6 | '-webkit-line-clamp', 7 | 'accent-color', 8 | 'align-content', 9 | 'align-items', 10 | 'align-self', 11 | 'alignment-baseline', 12 | 'all', 13 | 'anchor-name', 14 | 'anchor-scope', 15 | 'animation', 16 | 'animation-composition', 17 | 'animation-delay', 18 | 'animation-direction', 19 | 'animation-duration', 20 | 'animation-fill-mode', 21 | 'animation-iteration-count', 22 | 'animation-name', 23 | 'animation-play-state', 24 | 'animation-range', 25 | 'animation-range-end', 26 | 'animation-range-start', 27 | 'animation-timeline', 28 | 'animation-timing-function', 29 | 'appearance', 30 | 'aspect-ratio', 31 | 'azimuth', 32 | 'backface-visibility', 33 | 'background', 34 | 'background-attachment', 35 | 'background-blend-mode', 36 | 'background-clip', 37 | 'background-color', 38 | 'background-image', 39 | 'background-origin', 40 | 'background-position', 41 | 'background-repeat', 42 | 'background-size', 43 | 'baseline-shift', 44 | 'baseline-source', 45 | 'block-ellipsis', 46 | 'block-size', 47 | 'bookmark-label', 48 | 'bookmark-level', 49 | 'bookmark-state', 50 | 'border', 51 | 'border-block', 52 | 'border-block-color', 53 | 'border-block-end', 54 | 'border-block-end-color', 55 | 'border-block-end-style', 56 | 'border-block-end-width', 57 | 'border-block-start', 58 | 'border-block-start-color', 59 | 'border-block-start-style', 60 | 'border-block-start-width', 61 | 'border-block-style', 62 | 'border-block-width', 63 | 'border-bottom', 64 | 'border-bottom-color', 65 | 'border-bottom-left-radius', 66 | 'border-bottom-right-radius', 67 | 'border-bottom-style', 68 | 'border-bottom-width', 69 | 'border-boundary', 70 | 'border-collapse', 71 | 'border-color', 72 | 'border-end-end-radius', 73 | 'border-end-start-radius', 74 | 'border-image', 75 | 'border-image-outset', 76 | 'border-image-repeat', 77 | 'border-image-slice', 78 | 'border-image-source', 79 | 'border-image-width', 80 | 'border-inline', 81 | 'border-inline-color', 82 | 'border-inline-end', 83 | 'border-inline-end-color', 84 | 'border-inline-end-style', 85 | 'border-inline-end-width', 86 | 'border-inline-start', 87 | 'border-inline-start-color', 88 | 'border-inline-start-style', 89 | 'border-inline-start-width', 90 | 'border-inline-style', 91 | 'border-inline-width', 92 | 'border-left', 93 | 'border-left-color', 94 | 'border-left-style', 95 | 'border-left-width', 96 | 'border-radius', 97 | 'border-right', 98 | 'border-right-color', 99 | 'border-right-style', 100 | 'border-right-width', 101 | 'border-spacing', 102 | 'border-start-end-radius', 103 | 'border-start-start-radius', 104 | 'border-style', 105 | 'border-top', 106 | 'border-top-color', 107 | 'border-top-left-radius', 108 | 'border-top-right-radius', 109 | 'border-top-style', 110 | 'border-top-width', 111 | 'border-width', 112 | 'bottom', 113 | 'box-decoration-break', 114 | 'box-shadow', 115 | 'box-sizing', 116 | 'box-snap', 117 | 'break-after', 118 | 'break-before', 119 | 'break-inside', 120 | 'caption-side', 121 | 'caret', 122 | 'caret-color', 123 | 'caret-shape', 124 | 'clear', 125 | 'clip', 126 | 'clip-path', 127 | 'clip-rule', 128 | 'color', 129 | 'color-adjust', 130 | 'color-interpolation-filters', 131 | 'color-scheme', 132 | 'column-count', 133 | 'column-fill', 134 | 'column-gap', 135 | 'column-rule', 136 | 'column-rule-color', 137 | 'column-rule-style', 138 | 'column-rule-width', 139 | 'column-span', 140 | 'column-width', 141 | 'columns', 142 | 'contain', 143 | 'contain-intrinsic-block-size', 144 | 'contain-intrinsic-height', 145 | 'contain-intrinsic-inline-size', 146 | 'contain-intrinsic-size', 147 | 'contain-intrinsic-width', 148 | 'container', 149 | 'container-name', 150 | 'container-type', 151 | 'content', 152 | 'content-visibility', 153 | 'continue', 154 | 'counter-increment', 155 | 'counter-reset', 156 | 'counter-set', 157 | 'cue', 158 | 'cue-after', 159 | 'cue-before', 160 | 'cursor', 161 | 'direction', 162 | 'display', 163 | 'dominant-baseline', 164 | 'elevation', 165 | 'empty-cells', 166 | 'filter', 167 | 'flex', 168 | 'flex-basis', 169 | 'flex-direction', 170 | 'flex-flow', 171 | 'flex-grow', 172 | 'flex-shrink', 173 | 'flex-wrap', 174 | 'float', 175 | 'flood-color', 176 | 'flood-opacity', 177 | 'flow-from', 178 | 'flow-into', 179 | 'font', 180 | 'font-family', 181 | 'font-feature-settings', 182 | 'font-kerning', 183 | 'font-language-override', 184 | 'font-optical-sizing', 185 | 'font-palette', 186 | 'font-size', 187 | 'font-size-adjust', 188 | 'font-stretch', 189 | 'font-style', 190 | 'font-synthesis', 191 | 'font-synthesis-position', 192 | 'font-synthesis-small-caps', 193 | 'font-synthesis-style', 194 | 'font-synthesis-weight', 195 | 'font-variant', 196 | 'font-variant-alternates', 197 | 'font-variant-caps', 198 | 'font-variant-east-asian', 199 | 'font-variant-emoji', 200 | 'font-variant-ligatures', 201 | 'font-variant-numeric', 202 | 'font-variant-position', 203 | 'font-variation-settings', 204 | 'font-weight', 205 | 'font-width', 206 | 'footnote-display', 207 | 'footnote-policy', 208 | 'forced-color-adjust', 209 | 'gap', 210 | 'glyph-orientation-vertical', 211 | 'grid', 212 | 'grid-area', 213 | 'grid-auto-columns', 214 | 'grid-auto-flow', 215 | 'grid-auto-rows', 216 | 'grid-column', 217 | 'grid-column-end', 218 | 'grid-column-start', 219 | 'grid-row', 220 | 'grid-row-end', 221 | 'grid-row-start', 222 | 'grid-template', 223 | 'grid-template-areas', 224 | 'grid-template-columns', 225 | 'grid-template-rows', 226 | 'hanging-punctuation', 227 | 'height', 228 | 'hyphenate-character', 229 | 'hyphenate-limit-chars', 230 | 'hyphenate-limit-last', 231 | 'hyphenate-limit-lines', 232 | 'hyphenate-limit-zone', 233 | 'hyphens', 234 | 'image-orientation', 235 | 'image-rendering', 236 | 'image-resolution', 237 | 'initial-letter', 238 | 'initial-letter-align', 239 | 'initial-letter-wrap', 240 | 'inline-size', 241 | 'inline-sizing', 242 | 'inset', 243 | 'inset-area', 244 | 'inset-block', 245 | 'inset-block-end', 246 | 'inset-block-start', 247 | 'inset-inline', 248 | 'inset-inline-end', 249 | 'inset-inline-start', 250 | 'isolation', 251 | 'justify-content', 252 | 'justify-items', 253 | 'justify-self', 254 | 'left', 255 | 'letter-spacing', 256 | 'lighting-color', 257 | 'line-break', 258 | 'line-clamp', 259 | 'line-fit-edge', 260 | 'line-grid', 261 | 'line-height', 262 | 'line-padding', 263 | 'line-snap', 264 | 'list-style', 265 | 'list-style-image', 266 | 'list-style-position', 267 | 'list-style-type', 268 | 'margin', 269 | 'margin-block', 270 | 'margin-block-end', 271 | 'margin-block-start', 272 | 'margin-bottom', 273 | 'margin-inline', 274 | 'margin-inline-end', 275 | 'margin-inline-start', 276 | 'margin-left', 277 | 'margin-right', 278 | 'margin-top', 279 | 'margin-trim', 280 | 'marker-side', 281 | 'mask', 282 | 'mask-border', 283 | 'mask-border-mode', 284 | 'mask-border-outset', 285 | 'mask-border-repeat', 286 | 'mask-border-slice', 287 | 'mask-border-source', 288 | 'mask-border-width', 289 | 'mask-clip', 290 | 'mask-composite', 291 | 'mask-image', 292 | 'mask-mode', 293 | 'mask-origin', 294 | 'mask-position', 295 | 'mask-repeat', 296 | 'mask-size', 297 | 'mask-type', 298 | 'max-block-size', 299 | 'max-height', 300 | 'max-inline-size', 301 | 'max-lines', 302 | 'max-width', 303 | 'min-block-size', 304 | 'min-height', 305 | 'min-inline-size', 306 | 'min-intrinsic-sizing', 307 | 'min-width', 308 | 'mix-blend-mode', 309 | 'nav-down', 310 | 'nav-left', 311 | 'nav-right', 312 | 'nav-up', 313 | 'object-fit', 314 | 'object-position', 315 | 'offset', 316 | 'offset-anchor', 317 | 'offset-distance', 318 | 'offset-path', 319 | 'offset-position', 320 | 'offset-rotate', 321 | 'opacity', 322 | 'order', 323 | 'orphans', 324 | 'outline', 325 | 'outline-color', 326 | 'outline-offset', 327 | 'outline-style', 328 | 'outline-width', 329 | 'overflow', 330 | 'overflow-anchor', 331 | 'overflow-block', 332 | 'overflow-clip-margin', 333 | 'overflow-clip-margin-block', 334 | 'overflow-clip-margin-block-end', 335 | 'overflow-clip-margin-block-start', 336 | 'overflow-clip-margin-bottom', 337 | 'overflow-clip-margin-inline', 338 | 'overflow-clip-margin-inline-end', 339 | 'overflow-clip-margin-inline-start', 340 | 'overflow-clip-margin-left', 341 | 'overflow-clip-margin-right', 342 | 'overflow-clip-margin-top', 343 | 'overflow-inline', 344 | 'overflow-wrap', 345 | 'overflow-x', 346 | 'overflow-y', 347 | 'padding', 348 | 'padding-block', 349 | 'padding-block-end', 350 | 'padding-block-start', 351 | 'padding-bottom', 352 | 'padding-inline', 353 | 'padding-inline-end', 354 | 'padding-inline-start', 355 | 'padding-left', 356 | 'padding-right', 357 | 'padding-top', 358 | 'page', 359 | 'page-break-after', 360 | 'page-break-before', 361 | 'page-break-inside', 362 | 'pause', 363 | 'pause-after', 364 | 'pause-before', 365 | 'perspective', 366 | 'perspective-origin', 367 | 'pitch', 368 | 'pitch-range', 369 | 'place-content', 370 | 'place-items', 371 | 'place-self', 372 | 'play-during', 373 | 'position', 374 | 'position-anchor', 375 | 'position-try', 376 | 'position-try-options', 377 | 'position-try-order', 378 | 'print-color-adjust', 379 | 'quotes', 380 | 'region-fragment', 381 | 'resize', 382 | 'rest', 383 | 'rest-after', 384 | 'rest-before', 385 | 'richness', 386 | 'right', 387 | 'rotate', 388 | 'row-gap', 389 | 'ruby-align', 390 | 'ruby-merge', 391 | 'ruby-overhang', 392 | 'ruby-position', 393 | 'running', 394 | 'scale', 395 | 'scroll-behavior', 396 | 'scroll-margin', 397 | 'scroll-margin-block', 398 | 'scroll-margin-block-end', 399 | 'scroll-margin-block-start', 400 | 'scroll-margin-bottom', 401 | 'scroll-margin-inline', 402 | 'scroll-margin-inline-end', 403 | 'scroll-margin-inline-start', 404 | 'scroll-margin-left', 405 | 'scroll-margin-right', 406 | 'scroll-margin-top', 407 | 'scroll-padding', 408 | 'scroll-padding-block', 409 | 'scroll-padding-block-end', 410 | 'scroll-padding-block-start', 411 | 'scroll-padding-bottom', 412 | 'scroll-padding-inline', 413 | 'scroll-padding-inline-end', 414 | 'scroll-padding-inline-start', 415 | 'scroll-padding-left', 416 | 'scroll-padding-right', 417 | 'scroll-padding-top', 418 | 'scroll-snap-align', 419 | 'scroll-snap-stop', 420 | 'scroll-snap-type', 421 | 'scroll-timeline', 422 | 'scroll-timeline-axis', 423 | 'scroll-timeline-name', 424 | 'scrollbar-color', 425 | 'scrollbar-gutter', 426 | 'scrollbar-width', 427 | 'shape-image-threshold', 428 | 'shape-inside', 429 | 'shape-margin', 430 | 'shape-outside', 431 | 'spatial-navigation-action', 432 | 'spatial-navigation-contain', 433 | 'spatial-navigation-function', 434 | 'speak', 435 | 'speak-as', 436 | 'speak-header', 437 | 'speak-numeral', 438 | 'speak-punctuation', 439 | 'speech-rate', 440 | 'stress', 441 | 'string-set', 442 | 'tab-size', 443 | 'table-layout', 444 | 'text-align', 445 | 'text-align-all', 446 | 'text-align-last', 447 | 'text-autospace', 448 | 'text-box', 449 | 'text-box-edge', 450 | 'text-box-trim', 451 | 'text-combine-upright', 452 | 'text-decoration', 453 | 'text-decoration-color', 454 | 'text-decoration-line', 455 | 'text-decoration-skip', 456 | 'text-decoration-skip-box', 457 | 'text-decoration-skip-ink', 458 | 'text-decoration-skip-inset', 459 | 'text-decoration-skip-self', 460 | 'text-decoration-skip-spaces', 461 | 'text-decoration-style', 462 | 'text-decoration-thickness', 463 | 'text-emphasis', 464 | 'text-emphasis-color', 465 | 'text-emphasis-position', 466 | 'text-emphasis-skip', 467 | 'text-emphasis-style', 468 | 'text-group-align', 469 | 'text-indent', 470 | 'text-justify', 471 | 'text-orientation', 472 | 'text-overflow', 473 | 'text-shadow', 474 | 'text-spacing', 475 | 'text-spacing-trim', 476 | 'text-transform', 477 | 'text-underline-offset', 478 | 'text-underline-position', 479 | 'text-wrap', 480 | 'text-wrap-mode', 481 | 'text-wrap-style', 482 | 'timeline-scope', 483 | 'top', 484 | 'transform', 485 | 'transform-box', 486 | 'transform-origin', 487 | 'transform-style', 488 | 'transition', 489 | 'transition-delay', 490 | 'transition-duration', 491 | 'transition-property', 492 | 'transition-timing-function', 493 | 'translate', 494 | 'unicode-bidi', 495 | 'user-select', 496 | 'vertical-align', 497 | 'view-timeline', 498 | 'view-timeline-axis', 499 | 'view-timeline-inset', 500 | 'view-timeline-name', 501 | 'view-transition-name', 502 | 'visibility', 503 | 'voice-balance', 504 | 'voice-duration', 505 | 'voice-family', 506 | 'voice-pitch', 507 | 'voice-range', 508 | 'voice-rate', 509 | 'voice-stress', 510 | 'voice-volume', 511 | 'volume', 512 | 'white-space', 513 | 'white-space-collapse', 514 | 'white-space-trim', 515 | 'widows', 516 | 'width', 517 | 'will-change', 518 | 'word-break', 519 | 'word-space-transform', 520 | 'word-spacing', 521 | 'word-wrap', 522 | 'wrap-after', 523 | 'wrap-before', 524 | 'wrap-flow', 525 | 'wrap-inside', 526 | 'wrap-through', 527 | 'writing-mode', 528 | 'z-index', 529 | ]); 530 | -------------------------------------------------------------------------------- /lib/allWebkitProperties.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * This file contains all implemented properties that are not a part of any 5 | * current specifications or drafts, but are handled by browsers nevertheless. 6 | */ 7 | 8 | module.exports = [ 9 | 'animation', 10 | 'animation-delay', 11 | 'animation-direction', 12 | 'animation-duration', 13 | 'animation-fill-mode', 14 | 'animation-iteration-count', 15 | 'animation-name', 16 | 'animation-play-state', 17 | 'animation-timing-function', 18 | 'appearance', 19 | 'aspect-ratio', 20 | 'backface-visibility', 21 | 'background-clip', 22 | 'background-composite', 23 | 'background-origin', 24 | 'background-size', 25 | 'border-after', 26 | 'border-after-color', 27 | 'border-after-style', 28 | 'border-after-width', 29 | 'border-before', 30 | 'border-before-color', 31 | 'border-before-style', 32 | 'border-before-width', 33 | 'border-end', 34 | 'border-end-color', 35 | 'border-end-style', 36 | 'border-end-width', 37 | 'border-fit', 38 | 'border-horizontal-spacing', 39 | 'border-image', 40 | 'border-radius', 41 | 'border-start', 42 | 'border-start-color', 43 | 'border-start-style', 44 | 'border-start-width', 45 | 'border-vertical-spacing', 46 | 'box-align', 47 | 'box-direction', 48 | 'box-flex', 49 | 'box-flex-group', 50 | 'box-lines', 51 | 'box-ordinal-group', 52 | 'box-orient', 53 | 'box-pack', 54 | 'box-reflect', 55 | 'box-shadow', 56 | 'color-correction', 57 | 'column-axis', 58 | 'column-break-after', 59 | 'column-break-before', 60 | 'column-break-inside', 61 | 'column-count', 62 | 'column-gap', 63 | 'column-rule', 64 | 'column-rule-color', 65 | 'column-rule-style', 66 | 'column-rule-width', 67 | 'columns', 68 | 'column-span', 69 | 'column-width', 70 | 'filter', 71 | 'flex-align', 72 | 'flex-direction', 73 | 'flex-flow', 74 | 'flex-item-align', 75 | 'flex-line-pack', 76 | 'flex-order', 77 | 'flex-pack', 78 | 'flex-wrap', 79 | 'flow-from', 80 | 'flow-into', 81 | 'font-feature-settings', 82 | 'font-kerning', 83 | 'font-size-delta', 84 | 'font-smoothing', 85 | 'font-variant-ligatures', 86 | 'highlight', 87 | 'hyphenate-character', 88 | 'hyphenate-limit-after', 89 | 'hyphenate-limit-before', 90 | 'hyphenate-limit-lines', 91 | 'hyphens', 92 | 'line-align', 93 | 'line-box-contain', 94 | 'line-break', 95 | 'line-clamp', 96 | 'line-grid', 97 | 'line-snap', 98 | 'locale', 99 | 'logical-height', 100 | 'logical-width', 101 | 'margin-after', 102 | 'margin-after-collapse', 103 | 'margin-before', 104 | 'margin-before-collapse', 105 | 'margin-bottom-collapse', 106 | 'margin-collapse', 107 | 'margin-end', 108 | 'margin-start', 109 | 'margin-top-collapse', 110 | 'marquee', 111 | 'marquee-direction', 112 | 'marquee-increment', 113 | 'marquee-repetition', 114 | 'marquee-speed', 115 | 'marquee-style', 116 | 'mask', 117 | 'mask-attachment', 118 | 'mask-box-image', 119 | 'mask-box-image-outset', 120 | 'mask-box-image-repeat', 121 | 'mask-box-image-slice', 122 | 'mask-box-image-source', 123 | 'mask-box-image-width', 124 | 'mask-clip', 125 | 'mask-composite', 126 | 'mask-image', 127 | 'mask-origin', 128 | 'mask-position', 129 | 'mask-position-x', 130 | 'mask-position-y', 131 | 'mask-repeat', 132 | 'mask-repeat-x', 133 | 'mask-repeat-y', 134 | 'mask-size', 135 | 'match-nearest-mail-blockquote-color', 136 | 'max-logical-height', 137 | 'max-logical-width', 138 | 'min-logical-height', 139 | 'min-logical-width', 140 | 'nbsp-mode', 141 | 'overflow-scrolling', 142 | 'padding-after', 143 | 'padding-before', 144 | 'padding-end', 145 | 'padding-start', 146 | 'perspective', 147 | 'perspective-origin', 148 | 'perspective-origin-x', 149 | 'perspective-origin-y', 150 | 'print-color-adjust', 151 | 'region-break-after', 152 | 'region-break-before', 153 | 'region-break-inside', 154 | 'region-overflow', 155 | 'rtl-ordering', 156 | 'svg-shadow', 157 | 'tap-highlight-color', 158 | 'text-combine', 159 | 'text-decorations-in-effect', 160 | 'text-emphasis', 161 | 'text-emphasis-color', 162 | 'text-emphasis-position', 163 | 'text-emphasis-style', 164 | 'text-fill-color', 165 | 'text-orientation', 166 | 'text-security', 167 | 'text-size-adjust', 168 | 'text-stroke', 169 | 'text-stroke-color', 170 | 'text-stroke-width', 171 | 'transform', 172 | 'transform-origin', 173 | 'transform-origin-x', 174 | 'transform-origin-y', 175 | 'transform-origin-z', 176 | 'transform-style', 177 | 'transition', 178 | 'transition-delay', 179 | 'transition-duration', 180 | 'transition-property', 181 | 'transition-timing-function', 182 | 'user-drag', 183 | 'user-modify', 184 | 'user-select', 185 | 'wrap', 186 | 'wrap-flow', 187 | 'wrap-margin', 188 | 'wrap-padding', 189 | 'wrap-shape-inside', 190 | 'wrap-shape-outside', 191 | 'wrap-through', 192 | 'writing-mode', 193 | 'zoom', 194 | ].map((prop) => 'webkit-' + prop); 195 | -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.POSITION_AT_SHORTHAND = { 4 | first: 0, 5 | second: 1, 6 | }; 7 | 8 | // CSS global values 9 | // see https://drafts.csswg.org/css-cascade-5/#defaulting-keywords 10 | module.exports.GLOBAL_VALUES = Object.freeze([ 11 | 'initial', 12 | 'inherit', 13 | 'unset', 14 | 'revert', 15 | 'revert-layer', 16 | ]); 17 | -------------------------------------------------------------------------------- /lib/named_colors.json: -------------------------------------------------------------------------------- 1 | [ 2 | "aliceblue", 3 | "antiquewhite", 4 | "aqua", 5 | "aquamarine", 6 | "azure", 7 | "beige", 8 | "bisque", 9 | "black", 10 | "blanchedalmond", 11 | "blue", 12 | "blueviolet", 13 | "brown", 14 | "burlywood", 15 | "cadetblue", 16 | "chartreuse", 17 | "chocolate", 18 | "coral", 19 | "cornflowerblue", 20 | "cornsilk", 21 | "crimson", 22 | "cyan", 23 | "darkblue", 24 | "darkcyan", 25 | "darkgoldenrod", 26 | "darkgray", 27 | "darkgreen", 28 | "darkgrey", 29 | "darkkhaki", 30 | "darkmagenta", 31 | "darkolivegreen", 32 | "darkorange", 33 | "darkorchid", 34 | "darkred", 35 | "darksalmon", 36 | "darkseagreen", 37 | "darkslateblue", 38 | "darkslategray", 39 | "darkslategrey", 40 | "darkturquoise", 41 | "darkviolet", 42 | "deeppink", 43 | "deepskyblue", 44 | "dimgray", 45 | "dimgrey", 46 | "dodgerblue", 47 | "firebrick", 48 | "floralwhite", 49 | "forestgreen", 50 | "fuchsia", 51 | "gainsboro", 52 | "ghostwhite", 53 | "gold", 54 | "goldenrod", 55 | "gray", 56 | "green", 57 | "greenyellow", 58 | "grey", 59 | "honeydew", 60 | "hotpink", 61 | "indianred", 62 | "indigo", 63 | "ivory", 64 | "khaki", 65 | "lavender", 66 | "lavenderblush", 67 | "lawngreen", 68 | "lemonchiffon", 69 | "lightblue", 70 | "lightcoral", 71 | "lightcyan", 72 | "lightgoldenrodyellow", 73 | "lightgray", 74 | "lightgreen", 75 | "lightgrey", 76 | "lightpink", 77 | "lightsalmon", 78 | "lightseagreen", 79 | "lightskyblue", 80 | "lightslategray", 81 | "lightslategrey", 82 | "lightsteelblue", 83 | "lightyellow", 84 | "lime", 85 | "limegreen", 86 | "linen", 87 | "magenta", 88 | "maroon", 89 | "mediumaquamarine", 90 | "mediumblue", 91 | "mediumorchid", 92 | "mediumpurple", 93 | "mediumseagreen", 94 | "mediumslateblue", 95 | "mediumspringgreen", 96 | "mediumturquoise", 97 | "mediumvioletred", 98 | "midnightblue", 99 | "mintcream", 100 | "mistyrose", 101 | "moccasin", 102 | "navajowhite", 103 | "navy", 104 | "oldlace", 105 | "olive", 106 | "olivedrab", 107 | "orange", 108 | "orangered", 109 | "orchid", 110 | "palegoldenrod", 111 | "palegreen", 112 | "paleturquoise", 113 | "palevioletred", 114 | "papayawhip", 115 | "peachpuff", 116 | "peru", 117 | "pink", 118 | "plum", 119 | "powderblue", 120 | "purple", 121 | "rebeccapurple", 122 | "red", 123 | "rosybrown", 124 | "royalblue", 125 | "saddlebrown", 126 | "salmon", 127 | "sandybrown", 128 | "seagreen", 129 | "seashell", 130 | "sienna", 131 | "silver", 132 | "skyblue", 133 | "slateblue", 134 | "slategray", 135 | "slategrey", 136 | "snow", 137 | "springgreen", 138 | "steelblue", 139 | "tan", 140 | "teal", 141 | "thistle", 142 | "tomato", 143 | "turquoise", 144 | "violet", 145 | "wheat", 146 | "white", 147 | "whitesmoke", 148 | "yellow", 149 | "yellowgreen", 150 | "transparent", 151 | "currentcolor" 152 | ] 153 | -------------------------------------------------------------------------------- /lib/parsers.js: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * These are commonly used parsers for CSS Values they take a string * 3 | * to parse and return a string after it's been converted, if needed * 4 | ********************************************************************/ 5 | 'use strict'; 6 | 7 | // FIXME: should move shorthandGetter(), shorthandSetter(), implicitSetter() and 8 | // subImplicitSetter() to CSSStyleDeclaration() 9 | 10 | const { resolve: resolveColor, utils } = require('@asamuzakjp/css-color'); 11 | const { GLOBAL_VALUES } = require('./constants'); 12 | 13 | const { cssCalc, isColor, isGradient, splitValue } = utils; 14 | 15 | exports.TYPES = { 16 | UNDEFINED: 0, 17 | NULL_OR_EMPTY_STR: 1, 18 | VAR: 2, 19 | NUMBER: 4, 20 | PERCENT: 8, 21 | LENGTH: 0x10, 22 | ANGLE: 0x20, 23 | CALC: 0x40, 24 | COLOR: 0x80, 25 | STRING: 0x100, 26 | KEYWORD: 0x200, 27 | UNIDENT: 0x8000, 28 | }; 29 | 30 | // regular expressions 31 | var DIGIT = '(?:0|[1-9]\\d*)'; 32 | var NUMBER = `[+-]?(?:${DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${DIGIT})?`; 33 | var unitRegEx = new RegExp(`^(${NUMBER})([a-z]+|%)?$`); 34 | var angleRegEx = new RegExp(`^${NUMBER}(?:deg|g?rad|turn)$`); 35 | var urlRegEx = /^url\(\s*((?:[^)]|\\\))*)\s*\)$/; 36 | var keywordRegEx = /^[a-z]+(?:\-[a-z]+)*$/i; 37 | var stringRegEx = /^("[^"]*"|'[^']*')$/; 38 | var varRegEx = /^var\(/; 39 | var varContainedRegEx = /(?<=[*/\s(])var\(/; 40 | var calcRegEx = 41 | /^(?:a?(?:cos|sin|tan)|abs|atan2|calc|clamp|exp|hypot|log|max|min|mod|pow|rem|round|sign|sqrt)\(/; 42 | 43 | // This will return one of the above types based on the passed in string 44 | exports.valueType = function valueType(val) { 45 | // see https://webidl.spec.whatwg.org/#LegacyNullToEmptyString 46 | if (val === '' || val === null) { 47 | return exports.TYPES.NULL_OR_EMPTY_STR; 48 | } 49 | if (typeof val === 'number') { 50 | val = val.toString(); 51 | } 52 | if (typeof val !== 'string') { 53 | return exports.TYPES.UNDEFINED; 54 | } 55 | if (varRegEx.test(val)) { 56 | return exports.TYPES.VAR; 57 | } 58 | if (calcRegEx.test(val)) { 59 | return exports.TYPES.CALC; 60 | } 61 | if (unitRegEx.test(val)) { 62 | var [, , unit] = unitRegEx.exec(val); 63 | if (!unit) { 64 | return exports.TYPES.NUMBER; 65 | } 66 | if (unit === '%') { 67 | return exports.TYPES.PERCENT; 68 | } 69 | if (/^(?:[cm]m|[dls]?v(?:[bhiw]|max|min)|in|p[ctx]|q|r?(?:[cl]h|cap|e[mx]|ic))$/.test(unit)) { 70 | return exports.TYPES.LENGTH; 71 | } 72 | if (/^(?:deg|g?rad|turn)$/.test(unit)) { 73 | return exports.TYPES.ANGLE; 74 | } 75 | } 76 | if (isColor(val)) { 77 | return exports.TYPES.COLOR; 78 | } 79 | if (stringRegEx.test(val)) { 80 | return exports.TYPES.STRING; 81 | } 82 | 83 | switch (val.toLowerCase()) { 84 | // system color keywords 85 | case 'accentcolor': 86 | case 'accentcolortext': 87 | case 'activetext': 88 | case 'buttonborder': 89 | case 'buttonface': 90 | case 'buttontext': 91 | case 'canvas': 92 | case 'canvastext': 93 | case 'field': 94 | case 'fieldtext': 95 | case 'graytext': 96 | case 'highlight': 97 | case 'highlighttext': 98 | case 'linktext': 99 | case 'mark': 100 | case 'marktext': 101 | case 'visitedtext': 102 | // the following are deprecated in CSS3 103 | case 'activeborder': 104 | case 'activecaption': 105 | case 'appworkspace': 106 | case 'background': 107 | case 'buttonface': 108 | case 'buttonhighlight': 109 | case 'buttonshadow': 110 | case 'buttontext': 111 | case 'captiontext': 112 | case 'graytext': 113 | case 'highlight': 114 | case 'highlighttext': 115 | case 'inactiveborder': 116 | case 'inactivecaption': 117 | case 'inactivecaptiontext': 118 | case 'infobackground': 119 | case 'infotext': 120 | case 'menu': 121 | case 'menutext': 122 | case 'scrollbar': 123 | case 'threeddarkshadow': 124 | case 'threedface': 125 | case 'threedhighlight': 126 | case 'threedlightshadow': 127 | case 'threedshadow': 128 | case 'window': 129 | case 'windowframe': 130 | case 'windowtext': 131 | return exports.TYPES.COLOR; 132 | default: 133 | if (keywordRegEx.test(val)) { 134 | return exports.TYPES.KEYWORD; 135 | } 136 | return exports.TYPES.UNIDENT; 137 | } 138 | }; 139 | 140 | exports.parseNumber = function parseNumber(val) { 141 | var type = exports.valueType(val); 142 | switch (type) { 143 | case exports.TYPES.NULL_OR_EMPTY_STR: 144 | case exports.TYPES.VAR: 145 | return val; 146 | case exports.TYPES.NUMBER: 147 | return `${parseFloat(val)}`; 148 | case exports.TYPES.CALC: 149 | return cssCalc(val, { 150 | format: 'specifiedValue', 151 | }); 152 | default: 153 | if (varContainedRegEx.test(val)) { 154 | return val; 155 | } 156 | return undefined; 157 | } 158 | }; 159 | 160 | exports.parseLength = function parseLength(val) { 161 | var type = exports.valueType(val); 162 | switch (type) { 163 | case exports.TYPES.NULL_OR_EMPTY_STR: 164 | case exports.TYPES.VAR: 165 | return val; 166 | case exports.TYPES.CALC: 167 | return cssCalc(val, { 168 | format: 'specifiedValue', 169 | }); 170 | case exports.TYPES.LENGTH: { 171 | var [, numVal, unit] = unitRegEx.exec(val); 172 | return `${parseFloat(numVal)}${unit}`; 173 | } 174 | default: 175 | if (varContainedRegEx.test(val)) { 176 | return val; 177 | } 178 | if (type === exports.TYPES.NUMBER && parseFloat(val) === 0) { 179 | return '0px'; 180 | } 181 | return undefined; 182 | } 183 | }; 184 | 185 | exports.parsePercent = function parsePercent(val) { 186 | var type = exports.valueType(val); 187 | switch (type) { 188 | case exports.TYPES.NULL_OR_EMPTY_STR: 189 | case exports.TYPES.VAR: 190 | return val; 191 | case exports.TYPES.CALC: 192 | return cssCalc(val, { 193 | format: 'specifiedValue', 194 | }); 195 | case exports.TYPES.PERCENT: { 196 | var [, numVal, unit] = unitRegEx.exec(val); 197 | return `${parseFloat(numVal)}${unit}`; 198 | } 199 | default: 200 | if (varContainedRegEx.test(val)) { 201 | return val; 202 | } 203 | if (type === exports.TYPES.NUMBER && parseFloat(val) === 0) { 204 | return '0%'; 205 | } 206 | return undefined; 207 | } 208 | }; 209 | 210 | // either a length or a percent 211 | exports.parseMeasurement = function parseMeasurement(val) { 212 | var type = exports.valueType(val); 213 | switch (type) { 214 | case exports.TYPES.NULL_OR_EMPTY_STR: 215 | case exports.TYPES.VAR: 216 | return val; 217 | case exports.TYPES.CALC: 218 | return cssCalc(val, { 219 | format: 'specifiedValue', 220 | }); 221 | case exports.TYPES.LENGTH: 222 | case exports.TYPES.PERCENT: { 223 | var [, numVal, unit] = unitRegEx.exec(val); 224 | return `${parseFloat(numVal)}${unit}`; 225 | } 226 | default: 227 | if (varContainedRegEx.test(val)) { 228 | return val; 229 | } 230 | if (type === exports.TYPES.NUMBER && parseFloat(val) === 0) { 231 | return '0px'; 232 | } 233 | return undefined; 234 | } 235 | }; 236 | 237 | exports.parseInheritingMeasurement = function parseInheritingMeasurement(val) { 238 | if (/^(?:auto|inherit)$/i.test(val)) { 239 | return val.toLowerCase(); 240 | } 241 | return exports.parseMeasurement(val); 242 | }; 243 | 244 | exports.parseUrl = function parseUrl(val) { 245 | var type = exports.valueType(val); 246 | if (type === exports.TYPES.NULL_OR_EMPTY_STR) { 247 | return ''; 248 | } 249 | var res = urlRegEx.exec(val); 250 | // does it match the regex? 251 | if (!res) { 252 | return undefined; 253 | } 254 | var str = res[1]; 255 | // if it starts with single or double quotes, does it end with the same? 256 | if ((str[0] === '"' || str[0] === "'") && str[0] !== str[str.length - 1]) { 257 | return undefined; 258 | } 259 | if (str[0] === '"' || str[0] === "'") { 260 | str = str.substr(1, str.length - 2); 261 | } 262 | 263 | var urlstr = ''; 264 | var escaped = false; 265 | var i; 266 | for (i = 0; i < str.length; i++) { 267 | switch (str[i]) { 268 | case '\\': 269 | if (escaped) { 270 | urlstr += '\\\\'; 271 | escaped = false; 272 | } else { 273 | escaped = true; 274 | } 275 | break; 276 | case '(': 277 | case ')': 278 | case ' ': 279 | case '\t': 280 | case '\n': 281 | case "'": 282 | if (!escaped) { 283 | return undefined; 284 | } 285 | urlstr += str[i]; 286 | escaped = false; 287 | break; 288 | case '"': 289 | if (!escaped) { 290 | return undefined; 291 | } 292 | urlstr += '\\"'; 293 | escaped = false; 294 | break; 295 | default: 296 | urlstr += str[i]; 297 | escaped = false; 298 | } 299 | } 300 | 301 | return 'url("' + urlstr + '")'; 302 | }; 303 | 304 | // NOTE: seems not in use? 305 | exports.parseString = function parseString(val) { 306 | var type = exports.valueType(val); 307 | if (type === exports.TYPES.NULL_OR_EMPTY_STR) { 308 | return ''; 309 | } 310 | if (type !== exports.TYPES.STRING) { 311 | return undefined; 312 | } 313 | var i; 314 | for (i = 1; i < val.length - 1; i++) { 315 | switch (val[i]) { 316 | case val[0]: 317 | return undefined; 318 | case '\\': 319 | i++; 320 | while (i < val.length - 1 && /[0-9A-Fa-f]/.test(val[i])) { 321 | i++; 322 | } 323 | break; 324 | } 325 | } 326 | if (i >= val.length) { 327 | return undefined; 328 | } 329 | return val; 330 | }; 331 | 332 | exports.parseKeyword = function parseKeyword(val, validKeywords = []) { 333 | var type = exports.valueType(val); 334 | if (type === exports.TYPES.NULL_OR_EMPTY_STR) { 335 | return ''; 336 | } 337 | if (type === exports.TYPES.VAR) { 338 | return val; 339 | } 340 | if (type !== exports.TYPES.KEYWORD) { 341 | return undefined; 342 | } 343 | val = val.toString().toLowerCase(); 344 | if (validKeywords.includes(val) || GLOBAL_VALUES.includes(val)) { 345 | return val; 346 | } 347 | return undefined; 348 | }; 349 | 350 | exports.parseColor = function parseColor(val) { 351 | var type = exports.valueType(val); 352 | if (type === exports.TYPES.NULL_OR_EMPTY_STR) { 353 | return ''; 354 | } 355 | if (type === exports.TYPES.UNDEFINED) { 356 | return undefined; 357 | } 358 | if (type === exports.TYPES.VAR) { 359 | return val; 360 | } 361 | if (type === exports.TYPES.KEYWORD) { 362 | return exports.parseKeyword(val); 363 | } 364 | if (/^[a-z]+$/i.test(val) && type === exports.TYPES.COLOR) { 365 | return val; 366 | } 367 | var res = resolveColor(val, { 368 | format: 'specifiedValue', 369 | }); 370 | if (res) { 371 | return res; 372 | } 373 | return undefined; 374 | }; 375 | 376 | // FIXME: 377 | // This function seems to be incorrect. 378 | // However, this has no impact so far, as this function is only used by the deprecated `azimuth` property. 379 | exports.parseAngle = function parseAngle(val) { 380 | var type = exports.valueType(val); 381 | if (type === exports.TYPES.NULL_OR_EMPTY_STR) { 382 | return ''; 383 | } 384 | if (type !== exports.TYPES.ANGLE) { 385 | return undefined; 386 | } 387 | var res = angleRegEx.exec(val); 388 | var flt = parseFloat(res[1]); 389 | if (res[2] === 'rad') { 390 | flt *= 180 / Math.PI; 391 | } else if (res[2] === 'grad') { 392 | flt *= 360 / 400; 393 | } 394 | 395 | while (flt < 0) { 396 | flt += 360; 397 | } 398 | while (flt > 360) { 399 | flt -= 360; 400 | } 401 | return flt + 'deg'; 402 | }; 403 | 404 | exports.parseImage = function parseImage(val) { 405 | var type = exports.valueType(val); 406 | if (type === exports.TYPES.NULL_OR_EMPTY_STR) { 407 | return ''; 408 | } 409 | if (type === exports.TYPES.UNDEFINED) { 410 | return undefined; 411 | } 412 | if (type === exports.TYPES.VAR) { 413 | return val; 414 | } 415 | if (type === exports.TYPES.KEYWORD) { 416 | return exports.parseKeyword(val, ['none']); 417 | } 418 | var values = splitValue(val, { 419 | delimiter: ',', 420 | preserveComment: varContainedRegEx.test(val), 421 | }); 422 | var isImage = !!values.length; 423 | var i; 424 | for (i = 0; i < values.length; i++) { 425 | var image = values[i]; 426 | if (exports.valueType(image) === exports.TYPES.NULL_OR_EMPTY_STR) { 427 | return image; 428 | } 429 | if (isGradient(image) || /^(?:none|inherit)$/i.test(image)) { 430 | continue; 431 | } 432 | var imageUrl = exports.parseUrl(image); 433 | if (imageUrl) { 434 | values[i] = imageUrl; 435 | } else { 436 | isImage = false; 437 | break; 438 | } 439 | } 440 | if (isImage) { 441 | return values.join(', '); 442 | } 443 | return undefined; 444 | }; 445 | 446 | // utility to translate from border-width to borderWidth 447 | exports.dashedToCamelCase = function (dashed) { 448 | if (dashed.startsWith('--')) { 449 | return dashed; 450 | } 451 | // skip leading hyphen in vendor prefixed value, e.g. -webkit-foo 452 | var i = /^\-webkit/.test(dashed) ? 1 : 0; 453 | var camel = ''; 454 | var nextCap = false; 455 | for (; i < dashed.length; i++) { 456 | if (dashed[i] !== '-') { 457 | camel += nextCap ? dashed[i].toUpperCase() : dashed[i]; 458 | nextCap = false; 459 | } else { 460 | nextCap = true; 461 | } 462 | } 463 | return camel; 464 | }; 465 | 466 | exports.camelToDashed = function (camelCase) { 467 | var dashed = camelCase.replace(/(?<=[a-z])[A-Z]/g, '-$&').toLowerCase(); 468 | var match = dashed.match(/^webkit\-/); 469 | if (match) { 470 | dashed = '-' + dashed; 471 | } 472 | return dashed; 473 | }; 474 | 475 | // this either returns undefined meaning that it isn't valid 476 | // or returns an object where the keys are dashed short 477 | // hand properties and the values are the values to set 478 | // on them 479 | // FIXME: need additional argument which indicates syntax 480 | // and/or use Map() for shorthandFor to ensure order of the longhand properties. 481 | // Note that there is `constants.js` that is presumably for this purpose? 482 | exports.shorthandParser = function parse(v, shorthandFor) { 483 | var obj = {}; 484 | var type = exports.valueType(v); 485 | if (type === exports.TYPES.NULL_OR_EMPTY_STR) { 486 | Object.keys(shorthandFor).forEach(function (property) { 487 | obj[property] = ''; 488 | }); 489 | return obj; 490 | } 491 | if (type === exports.TYPES.UNDEFINED) { 492 | return undefined; 493 | } 494 | if (typeof v === 'number') { 495 | v = v.toString(); 496 | } 497 | if (typeof v !== 'string') { 498 | return undefined; 499 | } 500 | if (v.toLowerCase() === 'inherit') { 501 | return {}; 502 | } 503 | var parts = splitValue(v); 504 | var valid = true; 505 | parts.forEach(function (part, i) { 506 | var partValid = false; 507 | Object.keys(shorthandFor).forEach(function (property) { 508 | if (shorthandFor[property].isValid(part, i)) { 509 | partValid = true; 510 | obj[property] = part; 511 | } 512 | }); 513 | if (valid) { 514 | valid = partValid; 515 | } 516 | }); 517 | if (!valid) { 518 | return undefined; 519 | } 520 | return obj; 521 | }; 522 | 523 | // FIXME: check against shorthandParser and reduce Object.keys().forEach() loops 524 | exports.shorthandSetter = function (property, shorthandFor) { 525 | return function (v) { 526 | if (v === undefined) { 527 | return; 528 | } 529 | if (v === null) { 530 | v = ''; 531 | } 532 | var obj = exports.shorthandParser(v, shorthandFor); 533 | if (obj === undefined) { 534 | return; 535 | } 536 | Object.keys(obj).forEach(function (subprop) { 537 | // in case subprop is an implicit property, this will clear 538 | // *its* subpropertiesX 539 | var camel = exports.dashedToCamelCase(subprop); 540 | this[camel] = obj[subprop]; 541 | // in case it gets translated into something else (0 -> 0px) 542 | obj[subprop] = this[camel]; 543 | this.removeProperty(subprop); 544 | // don't add in empty properties 545 | if (obj[subprop] !== '') { 546 | this._values[subprop] = obj[subprop]; 547 | } 548 | }, this); 549 | Object.keys(shorthandFor).forEach(function (subprop) { 550 | if (!obj.hasOwnProperty(subprop)) { 551 | this.removeProperty(subprop); 552 | delete this._values[subprop]; 553 | } 554 | }, this); 555 | // in case the value is something like 'none' that removes all values, 556 | // check that the generated one is not empty, first remove the property 557 | // if it already exists, then call the shorthandGetter, if it's an empty 558 | // string, don't set the property 559 | this.removeProperty(property); 560 | var calculated = exports.shorthandGetter(property, shorthandFor).call(this); 561 | if (calculated !== '') { 562 | this._setProperty(property, calculated); 563 | } 564 | }; 565 | }; 566 | 567 | exports.shorthandGetter = function (property, shorthandFor) { 568 | return function () { 569 | if (this._values[property] !== undefined) { 570 | return this.getPropertyValue(property); 571 | } 572 | return Object.keys(shorthandFor) 573 | .map(function (subprop) { 574 | return this.getPropertyValue(subprop); 575 | }, this) 576 | .filter(function (value) { 577 | return value !== ''; 578 | }) 579 | .join(' '); 580 | }; 581 | }; 582 | 583 | // isValid(){1,4} | inherit 584 | // if one, it applies to all 585 | // if two, the first applies to the top and bottom, and the second to left and right 586 | // if three, the first applies to the top, the second to left and right, the third bottom 587 | // if four, top, right, bottom, left 588 | exports.implicitSetter = function (propertyBefore, propertyAfter, isValid, parser) { 589 | propertyAfter = propertyAfter || ''; 590 | if (propertyAfter !== '') { 591 | propertyAfter = '-' + propertyAfter; 592 | } 593 | var partNames = ['top', 'right', 'bottom', 'left']; 594 | 595 | return function (v) { 596 | if (typeof v === 'number') { 597 | v = v.toString(); 598 | } 599 | if (typeof v !== 'string') { 600 | return undefined; 601 | } 602 | var parts; 603 | if (v.toLowerCase() === 'inherit' || v === '') { 604 | parts = [v]; 605 | } else { 606 | parts = splitValue(v); 607 | } 608 | if (parts.length < 1 || parts.length > 4) { 609 | return undefined; 610 | } 611 | 612 | if (!parts.every(isValid)) { 613 | return undefined; 614 | } 615 | 616 | parts = parts.map(function (part) { 617 | return parser(part); 618 | }); 619 | this._setProperty(propertyBefore + propertyAfter, parts.join(' ')); 620 | if (parts.length === 1) { 621 | parts[1] = parts[0]; 622 | } 623 | if (parts.length === 2) { 624 | parts[2] = parts[0]; 625 | } 626 | if (parts.length === 3) { 627 | parts[3] = parts[1]; 628 | } 629 | 630 | for (var i = 0; i < 4; i++) { 631 | var property = propertyBefore + '-' + partNames[i] + propertyAfter; 632 | this.removeProperty(property); 633 | if (parts[i] !== '') { 634 | this._values[property] = parts[i]; 635 | } 636 | } 637 | return v; 638 | }; 639 | }; 640 | 641 | // Companion to implicitSetter, but for the individual parts. 642 | // This sets the individual value, and checks to see if all four 643 | // sub-parts are set. If so, it sets the shorthand version and removes 644 | // the individual parts from the cssText. 645 | exports.subImplicitSetter = function (prefix, part, isValid, parser) { 646 | var property = prefix + '-' + part; 647 | var subparts = [prefix + '-top', prefix + '-right', prefix + '-bottom', prefix + '-left']; 648 | 649 | return function (v) { 650 | if (typeof v === 'number') { 651 | v = v.toString(); 652 | } 653 | if (v === null) { 654 | v = ''; 655 | } 656 | if (typeof v !== 'string') { 657 | return undefined; 658 | } 659 | if (!isValid(v)) { 660 | return undefined; 661 | } 662 | v = parser(v); 663 | this._setProperty(property, v); 664 | 665 | var combinedPriority = this.getPropertyPriority(prefix); 666 | var parts = subparts.map((subpart) => this._values[subpart]); 667 | var priorities = subparts.map((subpart) => this.getPropertyPriority(subpart)); 668 | // Combine into a single property if all values are set and have the same priority 669 | if ( 670 | parts.every((p) => p !== '' && p != null) && 671 | priorities.every((p) => p === priorities[0]) && 672 | priorities[0] === combinedPriority 673 | ) { 674 | for (var i = 0; i < subparts.length; i++) { 675 | this.removeProperty(subparts[i]); 676 | this._values[subparts[i]] = parts[i]; 677 | } 678 | this._setProperty(prefix, parts.join(' '), priorities[0]); 679 | } else { 680 | this.removeProperty(prefix); 681 | for (var j = 0; j < subparts.length; j++) { 682 | // The property we're setting won't be important, the rest will either keep their priority or inherit it from the combined property 683 | var priority = subparts[j] === property ? '' : priorities[j] || combinedPriority; 684 | this._setProperty(subparts[j], parts[j], priority); 685 | } 686 | } 687 | return v; 688 | }; 689 | }; 690 | -------------------------------------------------------------------------------- /lib/properties/azimuth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | var valueType = parsers.valueType(v); 8 | if (valueType === parsers.TYPES.ANGLE) { 9 | return this._setProperty('azimuth', parsers.parseAngle(v)); 10 | } 11 | if (valueType === parsers.TYPES.KEYWORD) { 12 | var keywords = v.toLowerCase().trim().split(/\s+/); 13 | var hasBehind = false; 14 | if (keywords.length > 2) { 15 | return; 16 | } 17 | var behindIndex = keywords.indexOf('behind'); 18 | hasBehind = behindIndex !== -1; 19 | 20 | if (keywords.length === 2) { 21 | if (!hasBehind) { 22 | return; 23 | } 24 | keywords.splice(behindIndex, 1); 25 | } 26 | if (keywords[0] === 'leftwards' || keywords[0] === 'rightwards') { 27 | if (hasBehind) { 28 | return; 29 | } 30 | return this._setProperty('azimuth', keywords[0]); 31 | } 32 | if (keywords[0] === 'behind') { 33 | return this._setProperty('azimuth', '180deg'); 34 | } 35 | switch (keywords[0]) { 36 | case 'left-side': 37 | return this._setProperty('azimuth', '270deg'); 38 | case 'far-left': 39 | return this._setProperty('azimuth', (hasBehind ? 240 : 300) + 'deg'); 40 | case 'left': 41 | return this._setProperty('azimuth', (hasBehind ? 220 : 320) + 'deg'); 42 | case 'center-left': 43 | return this._setProperty('azimuth', (hasBehind ? 200 : 340) + 'deg'); 44 | case 'center': 45 | return this._setProperty('azimuth', (hasBehind ? 180 : 0) + 'deg'); 46 | case 'center-right': 47 | return this._setProperty('azimuth', (hasBehind ? 160 : 20) + 'deg'); 48 | case 'right': 49 | return this._setProperty('azimuth', (hasBehind ? 140 : 40) + 'deg'); 50 | case 'far-right': 51 | return this._setProperty('azimuth', (hasBehind ? 120 : 60) + 'deg'); 52 | case 'right-side': 53 | return this._setProperty('azimuth', '90deg'); 54 | default: 55 | return; 56 | } 57 | } 58 | }, 59 | get: function () { 60 | return this.getPropertyValue('azimuth'); 61 | }, 62 | enumerable: true, 63 | configurable: true, 64 | }; 65 | -------------------------------------------------------------------------------- /lib/properties/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var shorthandSetter = require('../parsers').shorthandSetter; 4 | var shorthandGetter = require('../parsers').shorthandGetter; 5 | 6 | var shorthand_for = { 7 | 'background-color': require('./backgroundColor'), 8 | 'background-image': require('./backgroundImage'), 9 | 'background-repeat': require('./backgroundRepeat'), 10 | 'background-attachment': require('./backgroundAttachment'), 11 | 'background-position': require('./backgroundPosition'), 12 | }; 13 | 14 | module.exports.definition = { 15 | set: shorthandSetter('background', shorthand_for), 16 | get: shorthandGetter('background', shorthand_for), 17 | enumerable: true, 18 | configurable: true, 19 | }; 20 | -------------------------------------------------------------------------------- /lib/properties/backgroundAttachment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | 5 | var isValid = (module.exports.isValid = function isValid(v) { 6 | return ( 7 | parsers.valueType(v) === parsers.TYPES.KEYWORD && 8 | (v.toLowerCase() === 'scroll' || v.toLowerCase() === 'fixed' || v.toLowerCase() === 'inherit') 9 | ); 10 | }); 11 | 12 | module.exports.definition = { 13 | set: function (v) { 14 | if (!isValid(v)) { 15 | return; 16 | } 17 | this._setProperty('background-attachment', v); 18 | }, 19 | get: function () { 20 | return this.getPropertyValue('background-attachment'); 21 | }, 22 | enumerable: true, 23 | configurable: true, 24 | }; 25 | -------------------------------------------------------------------------------- /lib/properties/backgroundColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | 5 | var parse = function parse(v) { 6 | var parsed = parsers.parseColor(v); 7 | if (parsed !== undefined) { 8 | return parsed; 9 | } 10 | if (parsers.valueType(v) === parsers.TYPES.KEYWORD && v.toLowerCase() === 'inherit') { 11 | return v; 12 | } 13 | return undefined; 14 | }; 15 | 16 | module.exports.isValid = function isValid(v) { 17 | return parse(v) !== undefined; 18 | }; 19 | 20 | module.exports.definition = { 21 | set: function (v) { 22 | var parsed = parse(v); 23 | if (parsed === undefined) { 24 | return; 25 | } 26 | this._setProperty('background-color', parsed); 27 | }, 28 | get: function () { 29 | return this.getPropertyValue('background-color'); 30 | }, 31 | enumerable: true, 32 | configurable: true, 33 | }; 34 | -------------------------------------------------------------------------------- /lib/properties/backgroundImage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | 5 | var parse = function parse(v) { 6 | var parsed = parsers.parseImage(v); 7 | if (parsed !== undefined) { 8 | return parsed; 9 | } 10 | return undefined; 11 | }; 12 | 13 | module.exports.isValid = function isValid(v) { 14 | return parse(v) !== undefined; 15 | }; 16 | 17 | module.exports.definition = { 18 | set: function (v) { 19 | this._setProperty('background-image', parse(v)); 20 | }, 21 | get: function () { 22 | return this.getPropertyValue('background-image'); 23 | }, 24 | enumerable: true, 25 | configurable: true, 26 | }; 27 | -------------------------------------------------------------------------------- /lib/properties/backgroundPosition.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | 5 | var valid_keywords = ['top', 'center', 'bottom', 'left', 'right']; 6 | 7 | var parse = function parse(v) { 8 | if (v === '' || v === null) { 9 | return undefined; 10 | } 11 | var parts = v.split(/\s+/); 12 | if (parts.length > 2 || parts.length < 1) { 13 | return undefined; 14 | } 15 | var types = []; 16 | parts.forEach(function (part, index) { 17 | types[index] = parsers.valueType(part); 18 | }); 19 | if (parts.length === 1) { 20 | if (types[0] === parsers.TYPES.LENGTH || types[0] === parsers.TYPES.PERCENT) { 21 | return v; 22 | } 23 | if (types[0] === parsers.TYPES.KEYWORD) { 24 | if (valid_keywords.indexOf(v.toLowerCase()) !== -1 || v.toLowerCase() === 'inherit') { 25 | return v; 26 | } 27 | } 28 | return undefined; 29 | } 30 | if ( 31 | (types[0] === parsers.TYPES.LENGTH || types[0] === parsers.TYPES.PERCENT) && 32 | (types[1] === parsers.TYPES.LENGTH || types[1] === parsers.TYPES.PERCENT) 33 | ) { 34 | return v; 35 | } 36 | if (types[0] !== parsers.TYPES.KEYWORD || types[1] !== parsers.TYPES.KEYWORD) { 37 | return undefined; 38 | } 39 | if (valid_keywords.indexOf(parts[0]) !== -1 && valid_keywords.indexOf(parts[1]) !== -1) { 40 | return v; 41 | } 42 | return undefined; 43 | }; 44 | 45 | module.exports.isValid = function isValid(v) { 46 | return parse(v) !== undefined; 47 | }; 48 | 49 | module.exports.definition = { 50 | set: function (v) { 51 | this._setProperty('background-position', parse(v)); 52 | }, 53 | get: function () { 54 | return this.getPropertyValue('background-position'); 55 | }, 56 | enumerable: true, 57 | configurable: true, 58 | }; 59 | -------------------------------------------------------------------------------- /lib/properties/backgroundRepeat.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | 5 | var parse = function parse(v) { 6 | if ( 7 | parsers.valueType(v) === parsers.TYPES.KEYWORD && 8 | (v.toLowerCase() === 'repeat' || 9 | v.toLowerCase() === 'repeat-x' || 10 | v.toLowerCase() === 'repeat-y' || 11 | v.toLowerCase() === 'no-repeat' || 12 | v.toLowerCase() === 'inherit') 13 | ) { 14 | return v; 15 | } 16 | return undefined; 17 | }; 18 | 19 | module.exports.isValid = function isValid(v) { 20 | return parse(v) !== undefined; 21 | }; 22 | 23 | module.exports.definition = { 24 | set: function (v) { 25 | this._setProperty('background-repeat', parse(v)); 26 | }, 27 | get: function () { 28 | return this.getPropertyValue('background-repeat'); 29 | }, 30 | enumerable: true, 31 | configurable: true, 32 | }; 33 | -------------------------------------------------------------------------------- /lib/properties/border.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var shorthandSetter = require('../parsers').shorthandSetter; 4 | var shorthandGetter = require('../parsers').shorthandGetter; 5 | 6 | var shorthand_for = { 7 | 'border-width': require('./borderWidth'), 8 | 'border-style': require('./borderStyle'), 9 | 'border-color': require('./borderColor'), 10 | }; 11 | 12 | var myShorthandSetter = shorthandSetter('border', shorthand_for); 13 | var myShorthandGetter = shorthandGetter('border', shorthand_for); 14 | 15 | module.exports.definition = { 16 | set: function (v) { 17 | if (v.toString().toLowerCase() === 'none') { 18 | v = ''; 19 | } 20 | myShorthandSetter.call(this, v); 21 | this.removeProperty('border-top'); 22 | this.removeProperty('border-left'); 23 | this.removeProperty('border-right'); 24 | this.removeProperty('border-bottom'); 25 | this._values['border-top'] = this._values.border; 26 | this._values['border-left'] = this._values.border; 27 | this._values['border-right'] = this._values.border; 28 | this._values['border-bottom'] = this._values.border; 29 | }, 30 | get: myShorthandGetter, 31 | enumerable: true, 32 | configurable: true, 33 | }; 34 | -------------------------------------------------------------------------------- /lib/properties/borderBottom.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var shorthandSetter = require('../parsers').shorthandSetter; 4 | var shorthandGetter = require('../parsers').shorthandGetter; 5 | 6 | var shorthand_for = { 7 | 'border-bottom-width': require('./borderBottomWidth'), 8 | 'border-bottom-style': require('./borderBottomStyle'), 9 | 'border-bottom-color': require('./borderBottomColor'), 10 | }; 11 | 12 | module.exports.definition = { 13 | set: shorthandSetter('border-bottom', shorthand_for), 14 | get: shorthandGetter('border-bottom', shorthand_for), 15 | enumerable: true, 16 | configurable: true, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/properties/borderBottomColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = (module.exports.isValid = require('./borderColor').isValid); 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | if (isValid(v)) { 8 | this._setProperty('border-bottom-color', v); 9 | } 10 | }, 11 | get: function () { 12 | return this.getPropertyValue('border-bottom-color'); 13 | }, 14 | enumerable: true, 15 | configurable: true, 16 | }; 17 | -------------------------------------------------------------------------------- /lib/properties/borderBottomStyle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = require('./borderStyle').isValid; 4 | module.exports.isValid = isValid; 5 | 6 | module.exports.definition = { 7 | set: function (v) { 8 | if (isValid(v)) { 9 | if (v.toLowerCase() === 'none') { 10 | v = ''; 11 | this.removeProperty('border-bottom-width'); 12 | } 13 | this._setProperty('border-bottom-style', v); 14 | } 15 | }, 16 | get: function () { 17 | return this.getPropertyValue('border-bottom-style'); 18 | }, 19 | enumerable: true, 20 | configurable: true, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/properties/borderBottomWidth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = (module.exports.isValid = require('./borderWidth').isValid); 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | if (isValid(v)) { 8 | this._setProperty('border-bottom-width', v); 9 | } 10 | }, 11 | get: function () { 12 | return this.getPropertyValue('border-bottom-width'); 13 | }, 14 | enumerable: true, 15 | configurable: true, 16 | }; 17 | -------------------------------------------------------------------------------- /lib/properties/borderCollapse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | 5 | var parse = function parse(v) { 6 | if ( 7 | parsers.valueType(v) === parsers.TYPES.KEYWORD && 8 | (v.toLowerCase() === 'collapse' || 9 | v.toLowerCase() === 'separate' || 10 | v.toLowerCase() === 'inherit') 11 | ) { 12 | return v; 13 | } 14 | return undefined; 15 | }; 16 | 17 | module.exports.definition = { 18 | set: function (v) { 19 | this._setProperty('border-collapse', parse(v)); 20 | }, 21 | get: function () { 22 | return this.getPropertyValue('border-collapse'); 23 | }, 24 | enumerable: true, 25 | configurable: true, 26 | }; 27 | -------------------------------------------------------------------------------- /lib/properties/borderColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | var implicitSetter = require('../parsers').implicitSetter; 5 | 6 | var parser = function (v) { 7 | var parsed = parsers.parseColor(v); 8 | if (parsed !== undefined) { 9 | return parsed; 10 | } 11 | if (parsers.valueType(v) === parsers.TYPES.KEYWORD && v.toLowerCase() === 'inherit') { 12 | return v; 13 | } 14 | return undefined; 15 | }; 16 | 17 | module.exports.isValid = function parse(v) { 18 | return parser(v) !== undefined; 19 | }; 20 | var isValid = module.exports.isValid; 21 | 22 | module.exports.definition = { 23 | set: implicitSetter('border', 'color', isValid, parser), 24 | get: function () { 25 | return this.getPropertyValue('border-color'); 26 | }, 27 | enumerable: true, 28 | configurable: true, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/properties/borderLeft.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var shorthandSetter = require('../parsers').shorthandSetter; 4 | var shorthandGetter = require('../parsers').shorthandGetter; 5 | 6 | var shorthand_for = { 7 | 'border-left-width': require('./borderLeftWidth'), 8 | 'border-left-style': require('./borderLeftStyle'), 9 | 'border-left-color': require('./borderLeftColor'), 10 | }; 11 | 12 | module.exports.definition = { 13 | set: shorthandSetter('border-left', shorthand_for), 14 | get: shorthandGetter('border-left', shorthand_for), 15 | enumerable: true, 16 | configurable: true, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/properties/borderLeftColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = (module.exports.isValid = require('./borderColor').isValid); 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | if (isValid(v)) { 8 | this._setProperty('border-left-color', v); 9 | } 10 | }, 11 | get: function () { 12 | return this.getPropertyValue('border-left-color'); 13 | }, 14 | enumerable: true, 15 | configurable: true, 16 | }; 17 | -------------------------------------------------------------------------------- /lib/properties/borderLeftStyle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = require('./borderStyle').isValid; 4 | module.exports.isValid = isValid; 5 | 6 | module.exports.definition = { 7 | set: function (v) { 8 | if (isValid(v)) { 9 | if (v.toLowerCase() === 'none') { 10 | v = ''; 11 | this.removeProperty('border-left-width'); 12 | } 13 | this._setProperty('border-left-style', v); 14 | } 15 | }, 16 | get: function () { 17 | return this.getPropertyValue('border-left-style'); 18 | }, 19 | enumerable: true, 20 | configurable: true, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/properties/borderLeftWidth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = (module.exports.isValid = require('./borderWidth').isValid); 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | if (isValid(v)) { 8 | this._setProperty('border-left-width', v); 9 | } 10 | }, 11 | get: function () { 12 | return this.getPropertyValue('border-left-width'); 13 | }, 14 | enumerable: true, 15 | configurable: true, 16 | }; 17 | -------------------------------------------------------------------------------- /lib/properties/borderRight.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var shorthandSetter = require('../parsers').shorthandSetter; 4 | var shorthandGetter = require('../parsers').shorthandGetter; 5 | 6 | var shorthand_for = { 7 | 'border-right-width': require('./borderRightWidth'), 8 | 'border-right-style': require('./borderRightStyle'), 9 | 'border-right-color': require('./borderRightColor'), 10 | }; 11 | 12 | module.exports.definition = { 13 | set: shorthandSetter('border-right', shorthand_for), 14 | get: shorthandGetter('border-right', shorthand_for), 15 | enumerable: true, 16 | configurable: true, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/properties/borderRightColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = (module.exports.isValid = require('./borderColor').isValid); 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | if (isValid(v)) { 8 | this._setProperty('border-right-color', v); 9 | } 10 | }, 11 | get: function () { 12 | return this.getPropertyValue('border-right-color'); 13 | }, 14 | enumerable: true, 15 | configurable: true, 16 | }; 17 | -------------------------------------------------------------------------------- /lib/properties/borderRightStyle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = require('./borderStyle').isValid; 4 | module.exports.isValid = isValid; 5 | 6 | module.exports.definition = { 7 | set: function (v) { 8 | if (isValid(v)) { 9 | if (v.toLowerCase() === 'none') { 10 | v = ''; 11 | this.removeProperty('border-right-width'); 12 | } 13 | this._setProperty('border-right-style', v); 14 | } 15 | }, 16 | get: function () { 17 | return this.getPropertyValue('border-right-style'); 18 | }, 19 | enumerable: true, 20 | configurable: true, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/properties/borderRightWidth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = (module.exports.isValid = require('./borderWidth').isValid); 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | if (isValid(v)) { 8 | this._setProperty('border-right-width', v); 9 | } 10 | }, 11 | get: function () { 12 | return this.getPropertyValue('border-right-width'); 13 | }, 14 | enumerable: true, 15 | configurable: true, 16 | }; 17 | -------------------------------------------------------------------------------- /lib/properties/borderSpacing.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | 5 | // ? | inherit 6 | // if one, it applies to both horizontal and verical spacing 7 | // if two, the first applies to the horizontal and the second applies to vertical spacing 8 | 9 | var parse = function parse(v) { 10 | if (v === '' || v === null) { 11 | return undefined; 12 | } 13 | if (v === 0) { 14 | return '0px'; 15 | } 16 | if (v.toLowerCase() === 'inherit') { 17 | return v; 18 | } 19 | var parts = v.split(/\s+/); 20 | if (parts.length !== 1 && parts.length !== 2) { 21 | return undefined; 22 | } 23 | parts.forEach(function (part) { 24 | if (parsers.valueType(part) !== parsers.TYPES.LENGTH) { 25 | return undefined; 26 | } 27 | }); 28 | 29 | return v; 30 | }; 31 | 32 | module.exports.definition = { 33 | set: function (v) { 34 | this._setProperty('border-spacing', parse(v)); 35 | }, 36 | get: function () { 37 | return this.getPropertyValue('border-spacing'); 38 | }, 39 | enumerable: true, 40 | configurable: true, 41 | }; 42 | -------------------------------------------------------------------------------- /lib/properties/borderStyle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var implicitSetter = require('../parsers').implicitSetter; 4 | 5 | // the valid border-styles: 6 | var styles = [ 7 | 'none', 8 | 'hidden', 9 | 'dotted', 10 | 'dashed', 11 | 'solid', 12 | 'double', 13 | 'groove', 14 | 'ridge', 15 | 'inset', 16 | 'outset', 17 | ]; 18 | 19 | module.exports.isValid = function parse(v) { 20 | return typeof v === 'string' && (v === '' || styles.indexOf(v) !== -1); 21 | }; 22 | var isValid = module.exports.isValid; 23 | 24 | var parser = function (v) { 25 | if (isValid(v)) { 26 | return v.toLowerCase(); 27 | } 28 | return undefined; 29 | }; 30 | 31 | module.exports.definition = { 32 | set: implicitSetter('border', 'style', isValid, parser), 33 | get: function () { 34 | return this.getPropertyValue('border-style'); 35 | }, 36 | enumerable: true, 37 | configurable: true, 38 | }; 39 | -------------------------------------------------------------------------------- /lib/properties/borderTop.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var shorthandSetter = require('../parsers').shorthandSetter; 4 | var shorthandGetter = require('../parsers').shorthandGetter; 5 | 6 | var shorthand_for = { 7 | 'border-top-width': require('./borderTopWidth'), 8 | 'border-top-style': require('./borderTopStyle'), 9 | 'border-top-color': require('./borderTopColor'), 10 | }; 11 | 12 | module.exports.definition = { 13 | set: shorthandSetter('border-top', shorthand_for), 14 | get: shorthandGetter('border-top', shorthand_for), 15 | enumerable: true, 16 | configurable: true, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/properties/borderTopColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = (module.exports.isValid = require('./borderColor').isValid); 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | if (isValid(v)) { 8 | this._setProperty('border-top-color', v); 9 | } 10 | }, 11 | get: function () { 12 | return this.getPropertyValue('border-top-color'); 13 | }, 14 | enumerable: true, 15 | configurable: true, 16 | }; 17 | -------------------------------------------------------------------------------- /lib/properties/borderTopStyle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = require('./borderStyle').isValid; 4 | module.exports.isValid = isValid; 5 | 6 | module.exports.definition = { 7 | set: function (v) { 8 | if (isValid(v)) { 9 | if (v.toLowerCase() === 'none') { 10 | v = ''; 11 | this.removeProperty('border-top-width'); 12 | } 13 | this._setProperty('border-top-style', v); 14 | } 15 | }, 16 | get: function () { 17 | return this.getPropertyValue('border-top-style'); 18 | }, 19 | enumerable: true, 20 | configurable: true, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/properties/borderTopWidth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var isValid = require('./borderWidth').isValid; 4 | module.exports.isValid = isValid; 5 | 6 | module.exports.definition = { 7 | set: function (v) { 8 | if (isValid(v)) { 9 | this._setProperty('border-top-width', v); 10 | } 11 | }, 12 | get: function () { 13 | return this.getPropertyValue('border-top-width'); 14 | }, 15 | enumerable: true, 16 | configurable: true, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/properties/borderWidth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers'); 4 | var implicitSetter = require('../parsers').implicitSetter; 5 | 6 | // the valid border-widths: 7 | var widths = ['thin', 'medium', 'thick']; 8 | 9 | module.exports.isValid = function parse(v) { 10 | var length = parsers.parseLength(v); 11 | if (length !== undefined) { 12 | return true; 13 | } 14 | if (typeof v !== 'string') { 15 | return false; 16 | } 17 | if (v === '') { 18 | return true; 19 | } 20 | v = v.toLowerCase(); 21 | if (widths.indexOf(v) === -1) { 22 | return false; 23 | } 24 | return true; 25 | }; 26 | var isValid = module.exports.isValid; 27 | 28 | var parser = function (v) { 29 | var length = parsers.parseLength(v); 30 | if (length !== undefined) { 31 | return length; 32 | } 33 | if (isValid(v)) { 34 | return v.toLowerCase(); 35 | } 36 | return undefined; 37 | }; 38 | 39 | module.exports.definition = { 40 | set: implicitSetter('border', 'width', isValid, parser), 41 | get: function () { 42 | return this.getPropertyValue('border-width'); 43 | }, 44 | enumerable: true, 45 | configurable: true, 46 | }; 47 | -------------------------------------------------------------------------------- /lib/properties/bottom.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseMeasurement = require('../parsers').parseInheritingMeasurement; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('bottom', parseMeasurement(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('bottom'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/clear.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseKeyword = require('../parsers').parseKeyword; 4 | 5 | var clear_keywords = ['none', 'left', 'right', 'both', 'inherit']; 6 | 7 | module.exports.definition = { 8 | set: function (v) { 9 | this._setProperty('clear', parseKeyword(v, clear_keywords)); 10 | }, 11 | get: function () { 12 | return this.getPropertyValue('clear'); 13 | }, 14 | enumerable: true, 15 | configurable: true, 16 | }; 17 | -------------------------------------------------------------------------------- /lib/properties/clip.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseMeasurement = require('../parsers').parseMeasurement; 4 | 5 | var shape_regex = /^rect\((.*)\)$/i; 6 | 7 | var parse = function (val) { 8 | if (val === '' || val === null) { 9 | return val; 10 | } 11 | if (typeof val !== 'string') { 12 | return undefined; 13 | } 14 | val = val.toLowerCase(); 15 | if (val === 'auto' || val === 'inherit') { 16 | return val; 17 | } 18 | var matches = val.match(shape_regex); 19 | if (!matches) { 20 | return undefined; 21 | } 22 | var parts = matches[1].split(/\s*,\s*/); 23 | if (parts.length !== 4) { 24 | return undefined; 25 | } 26 | var valid = parts.every(function (part, index) { 27 | var measurement = parseMeasurement(part); 28 | parts[index] = measurement; 29 | return measurement !== undefined; 30 | }); 31 | if (!valid) { 32 | return undefined; 33 | } 34 | parts = parts.join(', '); 35 | return val.replace(matches[1], parts); 36 | }; 37 | 38 | module.exports.definition = { 39 | set: function (v) { 40 | this._setProperty('clip', parse(v)); 41 | }, 42 | get: function () { 43 | return this.getPropertyValue('clip'); 44 | }, 45 | enumerable: true, 46 | configurable: true, 47 | }; 48 | -------------------------------------------------------------------------------- /lib/properties/color.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/cssFloat.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.definition = { 4 | set: function (v) { 5 | this._setProperty('float', v); 6 | }, 7 | get: function () { 8 | return this.getPropertyValue('float'); 9 | }, 10 | enumerable: true, 11 | configurable: true, 12 | }; 13 | -------------------------------------------------------------------------------- /lib/properties/flex.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var shorthandParser = require('../parsers').shorthandParser; 4 | var shorthandSetter = require('../parsers').shorthandSetter; 5 | var shorthandGetter = require('../parsers').shorthandGetter; 6 | 7 | var shorthand_for = { 8 | 'flex-grow': require('./flexGrow'), 9 | 'flex-shrink': require('./flexShrink'), 10 | 'flex-basis': require('./flexBasis'), 11 | }; 12 | 13 | var myShorthandSetter = shorthandSetter('flex', shorthand_for); 14 | 15 | module.exports.isValid = function isValid(v) { 16 | return shorthandParser(v, shorthand_for) !== undefined; 17 | }; 18 | 19 | module.exports.definition = { 20 | set: function (v) { 21 | var normalizedValue = String(v).trim().toLowerCase(); 22 | 23 | if (normalizedValue === 'none') { 24 | myShorthandSetter.call(this, '0 0 auto'); 25 | return; 26 | } 27 | if (normalizedValue === 'initial') { 28 | myShorthandSetter.call(this, '0 1 auto'); 29 | return; 30 | } 31 | if (normalizedValue === 'auto') { 32 | this.removeProperty('flex-grow'); 33 | this.removeProperty('flex-shrink'); 34 | this.setProperty('flex-basis', normalizedValue); 35 | return; 36 | } 37 | 38 | myShorthandSetter.call(this, v); 39 | }, 40 | get: shorthandGetter('flex', shorthand_for), 41 | enumerable: true, 42 | configurable: true, 43 | }; 44 | -------------------------------------------------------------------------------- /lib/properties/flexBasis.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseMeasurement = require('../parsers').parseMeasurement; 4 | 5 | function parse(v) { 6 | if (String(v).toLowerCase() === 'auto') { 7 | return 'auto'; 8 | } 9 | if (String(v).toLowerCase() === 'inherit') { 10 | return 'inherit'; 11 | } 12 | return parseMeasurement(v); 13 | } 14 | 15 | module.exports.isValid = function isValid(v) { 16 | return parse(v) !== undefined; 17 | }; 18 | 19 | module.exports.definition = { 20 | set: function (v) { 21 | this._setProperty('flex-basis', parse(v)); 22 | }, 23 | get: function () { 24 | return this.getPropertyValue('flex-basis'); 25 | }, 26 | enumerable: true, 27 | configurable: true, 28 | }; 29 | -------------------------------------------------------------------------------- /lib/properties/flexGrow.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseNumber = require('../parsers').parseNumber; 4 | var POSITION_AT_SHORTHAND = require('../constants').POSITION_AT_SHORTHAND; 5 | 6 | module.exports.isValid = function isValid(v, positionAtFlexShorthand) { 7 | return parseNumber(v) !== undefined && positionAtFlexShorthand === POSITION_AT_SHORTHAND.first; 8 | }; 9 | 10 | module.exports.definition = { 11 | set: function (v) { 12 | this._setProperty('flex-grow', parseNumber(v)); 13 | }, 14 | get: function () { 15 | return this.getPropertyValue('flex-grow'); 16 | }, 17 | enumerable: true, 18 | configurable: true, 19 | }; 20 | -------------------------------------------------------------------------------- /lib/properties/flexShrink.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseNumber = require('../parsers').parseNumber; 4 | var POSITION_AT_SHORTHAND = require('../constants').POSITION_AT_SHORTHAND; 5 | 6 | module.exports.isValid = function isValid(v, positionAtFlexShorthand) { 7 | return parseNumber(v) !== undefined && positionAtFlexShorthand === POSITION_AT_SHORTHAND.second; 8 | }; 9 | 10 | module.exports.definition = { 11 | set: function (v) { 12 | this._setProperty('flex-shrink', parseNumber(v)); 13 | }, 14 | get: function () { 15 | return this.getPropertyValue('flex-shrink'); 16 | }, 17 | enumerable: true, 18 | configurable: true, 19 | }; 20 | -------------------------------------------------------------------------------- /lib/properties/float.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.definition = { 4 | set: function (v) { 5 | this._setProperty('float', v); 6 | }, 7 | get: function () { 8 | return this.getPropertyValue('float'); 9 | }, 10 | enumerable: true, 11 | configurable: true, 12 | }; 13 | -------------------------------------------------------------------------------- /lib/properties/floodColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('flood-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('flood-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/font.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var TYPES = require('../parsers').TYPES; 4 | var valueType = require('../parsers').valueType; 5 | var shorthandParser = require('../parsers').shorthandParser; 6 | var shorthandSetter = require('../parsers').shorthandSetter; 7 | var shorthandGetter = require('../parsers').shorthandGetter; 8 | 9 | var shorthand_for = { 10 | 'font-family': require('./fontFamily'), 11 | 'font-size': require('./fontSize'), 12 | 'font-style': require('./fontStyle'), 13 | 'font-variant': require('./fontVariant'), 14 | 'font-weight': require('./fontWeight'), 15 | 'line-height': require('./lineHeight'), 16 | }; 17 | 18 | var static_fonts = [ 19 | 'caption', 20 | 'icon', 21 | 'menu', 22 | 'message-box', 23 | 'small-caption', 24 | 'status-bar', 25 | 'inherit', 26 | ]; 27 | 28 | var setter = shorthandSetter('font', shorthand_for); 29 | 30 | module.exports.definition = { 31 | set: function (v) { 32 | var short = shorthandParser(v, shorthand_for); 33 | if (short !== undefined) { 34 | return setter.call(this, v); 35 | } 36 | if (valueType(v) === TYPES.KEYWORD && static_fonts.indexOf(v.toLowerCase()) !== -1) { 37 | this._setProperty('font', v); 38 | } 39 | }, 40 | get: shorthandGetter('font', shorthand_for), 41 | enumerable: true, 42 | configurable: true, 43 | }; 44 | -------------------------------------------------------------------------------- /lib/properties/fontFamily.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var TYPES = require('../parsers').TYPES; 4 | var valueType = require('../parsers').valueType; 5 | 6 | var partsRegEx = /\s*,\s*/; 7 | module.exports.isValid = function isValid(v) { 8 | if (v === '' || v === null) { 9 | return true; 10 | } 11 | var parts = v.split(partsRegEx); 12 | var len = parts.length; 13 | var i; 14 | var type; 15 | for (i = 0; i < len; i++) { 16 | type = valueType(parts[i]); 17 | if (type === TYPES.STRING || type === TYPES.KEYWORD) { 18 | return true; 19 | } 20 | } 21 | return false; 22 | }; 23 | 24 | module.exports.definition = { 25 | set: function (v) { 26 | this._setProperty('font-family', v); 27 | }, 28 | get: function () { 29 | return this.getPropertyValue('font-family'); 30 | }, 31 | enumerable: true, 32 | configurable: true, 33 | }; 34 | -------------------------------------------------------------------------------- /lib/properties/fontSize.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var TYPES = require('../parsers').TYPES; 4 | var valueType = require('../parsers').valueType; 5 | var parseMeasurement = require('../parsers').parseMeasurement; 6 | 7 | var absoluteSizes = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large']; 8 | var relativeSizes = ['larger', 'smaller']; 9 | 10 | module.exports.isValid = function (v) { 11 | var type = valueType(v.toLowerCase()); 12 | return ( 13 | type === TYPES.LENGTH || 14 | type === TYPES.PERCENT || 15 | (type === TYPES.KEYWORD && absoluteSizes.indexOf(v.toLowerCase()) !== -1) || 16 | (type === TYPES.KEYWORD && relativeSizes.indexOf(v.toLowerCase()) !== -1) 17 | ); 18 | }; 19 | 20 | function parse(v) { 21 | const valueAsString = String(v).toLowerCase(); 22 | const optionalArguments = absoluteSizes.concat(relativeSizes); 23 | const isOptionalArgument = optionalArguments.some( 24 | (stringValue) => stringValue.toLowerCase() === valueAsString 25 | ); 26 | return isOptionalArgument ? valueAsString : parseMeasurement(v); 27 | } 28 | 29 | module.exports.definition = { 30 | set: function (v) { 31 | this._setProperty('font-size', parse(v)); 32 | }, 33 | get: function () { 34 | return this.getPropertyValue('font-size'); 35 | }, 36 | enumerable: true, 37 | configurable: true, 38 | }; 39 | -------------------------------------------------------------------------------- /lib/properties/fontStyle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var valid_styles = ['normal', 'italic', 'oblique', 'inherit']; 4 | 5 | module.exports.isValid = function (v) { 6 | return valid_styles.indexOf(v.toLowerCase()) !== -1; 7 | }; 8 | 9 | module.exports.definition = { 10 | set: function (v) { 11 | this._setProperty('font-style', v); 12 | }, 13 | get: function () { 14 | return this.getPropertyValue('font-style'); 15 | }, 16 | enumerable: true, 17 | configurable: true, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/properties/fontVariant.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var valid_variants = ['normal', 'small-caps', 'inherit']; 4 | 5 | module.exports.isValid = function isValid(v) { 6 | return valid_variants.indexOf(v.toLowerCase()) !== -1; 7 | }; 8 | 9 | module.exports.definition = { 10 | set: function (v) { 11 | this._setProperty('font-variant', v); 12 | }, 13 | get: function () { 14 | return this.getPropertyValue('font-variant'); 15 | }, 16 | enumerable: true, 17 | configurable: true, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/properties/fontWeight.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var valid_weights = [ 4 | 'normal', 5 | 'bold', 6 | 'bolder', 7 | 'lighter', 8 | '100', 9 | '200', 10 | '300', 11 | '400', 12 | '500', 13 | '600', 14 | '700', 15 | '800', 16 | '900', 17 | 'inherit', 18 | ]; 19 | 20 | module.exports.isValid = function isValid(v) { 21 | return valid_weights.indexOf(v.toLowerCase()) !== -1; 22 | }; 23 | 24 | module.exports.definition = { 25 | set: function (v) { 26 | this._setProperty('font-weight', v); 27 | }, 28 | get: function () { 29 | return this.getPropertyValue('font-weight'); 30 | }, 31 | enumerable: true, 32 | configurable: true, 33 | }; 34 | -------------------------------------------------------------------------------- /lib/properties/height.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseMeasurement = require('../parsers').parseMeasurement; 4 | 5 | function parse(v) { 6 | if (String(v).toLowerCase() === 'auto') { 7 | return 'auto'; 8 | } 9 | if (String(v).toLowerCase() === 'inherit') { 10 | return 'inherit'; 11 | } 12 | return parseMeasurement(v); 13 | } 14 | 15 | module.exports.definition = { 16 | set: function (v) { 17 | this._setProperty('height', parse(v)); 18 | }, 19 | get: function () { 20 | return this.getPropertyValue('height'); 21 | }, 22 | enumerable: true, 23 | configurable: true, 24 | }; 25 | -------------------------------------------------------------------------------- /lib/properties/left.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseMeasurement = require('../parsers').parseInheritingMeasurement; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('left', parseMeasurement(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('left'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/lightingColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('lighting-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('lighting-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/lineHeight.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var TYPES = require('../parsers').TYPES; 4 | var valueType = require('../parsers').valueType; 5 | 6 | module.exports.isValid = function isValid(v) { 7 | var type = valueType(v); 8 | return ( 9 | (type === TYPES.KEYWORD && v.toLowerCase() === 'normal') || 10 | v.toLowerCase() === 'inherit' || 11 | type === TYPES.NUMBER || 12 | type === TYPES.LENGTH || 13 | type === TYPES.PERCENT 14 | ); 15 | }; 16 | 17 | module.exports.definition = { 18 | set: function (v) { 19 | this._setProperty('line-height', v); 20 | }, 21 | get: function () { 22 | return this.getPropertyValue('line-height'); 23 | }, 24 | enumerable: true, 25 | configurable: true, 26 | }; 27 | -------------------------------------------------------------------------------- /lib/properties/margin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers.js'); 4 | var TYPES = parsers.TYPES; 5 | 6 | var isValid = function (v) { 7 | if (v.toLowerCase() === 'auto') { 8 | return true; 9 | } 10 | var type = parsers.valueType(v); 11 | return ( 12 | type === TYPES.NULL_OR_EMPTY_STR || 13 | type === TYPES.LENGTH || 14 | type === TYPES.PERCENT || 15 | type === TYPES.CALC || 16 | (type === TYPES.NUMBER && parseFloat(v) === 0) 17 | ); 18 | }; 19 | 20 | var parser = function (v) { 21 | var V = v.toLowerCase(); 22 | if (V === 'auto') { 23 | return V; 24 | } 25 | return parsers.parseMeasurement(v); 26 | }; 27 | 28 | var mySetter = parsers.implicitSetter('margin', '', isValid, parser); 29 | var myGlobal = parsers.implicitSetter( 30 | 'margin', 31 | '', 32 | function () { 33 | return true; 34 | }, 35 | function (v) { 36 | return v; 37 | } 38 | ); 39 | 40 | module.exports.definition = { 41 | set: function (v) { 42 | if (typeof v === 'number') { 43 | v = String(v); 44 | } 45 | if (v === null) { 46 | v = ''; 47 | } 48 | if (typeof v !== 'string') { 49 | return; 50 | } 51 | var V = v.toLowerCase(); 52 | switch (V) { 53 | case 'inherit': 54 | case 'initial': 55 | case 'unset': 56 | case '': 57 | myGlobal.call(this, V); 58 | break; 59 | 60 | default: 61 | mySetter.call(this, v); 62 | break; 63 | } 64 | }, 65 | get: function () { 66 | return this.getPropertyValue('margin'); 67 | }, 68 | enumerable: true, 69 | configurable: true, 70 | }; 71 | 72 | module.exports.isValid = isValid; 73 | module.exports.parser = parser; 74 | -------------------------------------------------------------------------------- /lib/properties/marginBottom.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var margin = require('./margin.js'); 4 | var parsers = require('../parsers.js'); 5 | 6 | module.exports.definition = { 7 | set: parsers.subImplicitSetter('margin', 'bottom', margin.isValid, margin.parser), 8 | get: function () { 9 | return this.getPropertyValue('margin-bottom'); 10 | }, 11 | enumerable: true, 12 | configurable: true, 13 | }; 14 | -------------------------------------------------------------------------------- /lib/properties/marginLeft.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var margin = require('./margin.js'); 4 | var parsers = require('../parsers.js'); 5 | 6 | module.exports.definition = { 7 | set: parsers.subImplicitSetter('margin', 'left', margin.isValid, margin.parser), 8 | get: function () { 9 | return this.getPropertyValue('margin-left'); 10 | }, 11 | enumerable: true, 12 | configurable: true, 13 | }; 14 | -------------------------------------------------------------------------------- /lib/properties/marginRight.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var margin = require('./margin.js'); 4 | var parsers = require('../parsers.js'); 5 | 6 | module.exports.definition = { 7 | set: parsers.subImplicitSetter('margin', 'right', margin.isValid, margin.parser), 8 | get: function () { 9 | return this.getPropertyValue('margin-right'); 10 | }, 11 | enumerable: true, 12 | configurable: true, 13 | }; 14 | -------------------------------------------------------------------------------- /lib/properties/marginTop.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var margin = require('./margin.js'); 4 | var parsers = require('../parsers.js'); 5 | 6 | module.exports.definition = { 7 | set: parsers.subImplicitSetter('margin', 'top', margin.isValid, margin.parser), 8 | get: function () { 9 | return this.getPropertyValue('margin-top'); 10 | }, 11 | enumerable: true, 12 | configurable: true, 13 | }; 14 | -------------------------------------------------------------------------------- /lib/properties/opacity.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseNumber = require('../parsers').parseNumber; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('opacity', parseNumber(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('opacity'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/outlineColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('outline-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('outline-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/padding.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../parsers.js'); 4 | var TYPES = parsers.TYPES; 5 | 6 | var isValid = function (v) { 7 | var type = parsers.valueType(v); 8 | return ( 9 | type === TYPES.NULL_OR_EMPTY_STR || 10 | type === TYPES.LENGTH || 11 | type === TYPES.PERCENT || 12 | type === TYPES.CALC || 13 | (type === TYPES.NUMBER && parseFloat(v) === 0) 14 | ); 15 | }; 16 | 17 | var parser = function (v) { 18 | return parsers.parseMeasurement(v); 19 | }; 20 | 21 | var mySetter = parsers.implicitSetter('padding', '', isValid, parser); 22 | var myGlobal = parsers.implicitSetter( 23 | 'padding', 24 | '', 25 | function () { 26 | return true; 27 | }, 28 | function (v) { 29 | return v; 30 | } 31 | ); 32 | 33 | module.exports.definition = { 34 | set: function (v) { 35 | if (typeof v === 'number') { 36 | v = String(v); 37 | } 38 | if (v === null) { 39 | v = ''; 40 | } 41 | if (typeof v !== 'string') { 42 | return; 43 | } 44 | var V = v.toLowerCase(); 45 | switch (V) { 46 | case 'inherit': 47 | case 'initial': 48 | case 'unset': 49 | case '': 50 | myGlobal.call(this, V); 51 | break; 52 | 53 | default: 54 | mySetter.call(this, v); 55 | break; 56 | } 57 | }, 58 | get: function () { 59 | return this.getPropertyValue('padding'); 60 | }, 61 | enumerable: true, 62 | configurable: true, 63 | }; 64 | 65 | module.exports.isValid = isValid; 66 | module.exports.parser = parser; 67 | -------------------------------------------------------------------------------- /lib/properties/paddingBottom.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var padding = require('./padding.js'); 4 | var parsers = require('../parsers.js'); 5 | 6 | module.exports.definition = { 7 | set: parsers.subImplicitSetter('padding', 'bottom', padding.isValid, padding.parser), 8 | get: function () { 9 | return this.getPropertyValue('padding-bottom'); 10 | }, 11 | enumerable: true, 12 | configurable: true, 13 | }; 14 | -------------------------------------------------------------------------------- /lib/properties/paddingLeft.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var padding = require('./padding.js'); 4 | var parsers = require('../parsers.js'); 5 | 6 | module.exports.definition = { 7 | set: parsers.subImplicitSetter('padding', 'left', padding.isValid, padding.parser), 8 | get: function () { 9 | return this.getPropertyValue('padding-left'); 10 | }, 11 | enumerable: true, 12 | configurable: true, 13 | }; 14 | -------------------------------------------------------------------------------- /lib/properties/paddingRight.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var padding = require('./padding.js'); 4 | var parsers = require('../parsers.js'); 5 | 6 | module.exports.definition = { 7 | set: parsers.subImplicitSetter('padding', 'right', padding.isValid, padding.parser), 8 | get: function () { 9 | return this.getPropertyValue('padding-right'); 10 | }, 11 | enumerable: true, 12 | configurable: true, 13 | }; 14 | -------------------------------------------------------------------------------- /lib/properties/paddingTop.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var padding = require('./padding.js'); 4 | var parsers = require('../parsers.js'); 5 | 6 | module.exports.definition = { 7 | set: parsers.subImplicitSetter('padding', 'top', padding.isValid, padding.parser), 8 | get: function () { 9 | return this.getPropertyValue('padding-top'); 10 | }, 11 | enumerable: true, 12 | configurable: true, 13 | }; 14 | -------------------------------------------------------------------------------- /lib/properties/right.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseMeasurement = require('../parsers').parseInheritingMeasurement; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('right', parseMeasurement(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('right'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/stopColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('stop-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('stop-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/textLineThroughColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('text-line-through-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('text-line-through-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/textOverlineColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('text-overline-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('text-overline-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/textUnderlineColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('text-underline-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('text-underline-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/top.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseMeasurement = require('../parsers').parseInheritingMeasurement; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('top', parseMeasurement(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('top'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitBorderAfterColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-border-after-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-border-after-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitBorderBeforeColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-border-before-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-border-before-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitBorderEndColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-border-end-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-border-end-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitBorderStartColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-border-start-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-border-start-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitColumnRuleColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-column-rule-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-column-rule-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitMatchNearestMailBlockquoteColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-match-nearest-mail-blockquote-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-match-nearest-mail-blockquote-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitTapHighlightColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-tap-highlight-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-tap-highlight-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitTextEmphasisColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-text-emphasis-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-text-emphasis-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitTextFillColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-text-fill-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-text-fill-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/webkitTextStrokeColor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseColor = require('../parsers').parseColor; 4 | 5 | module.exports.definition = { 6 | set: function (v) { 7 | this._setProperty('-webkit-text-stroke-color', parseColor(v)); 8 | }, 9 | get: function () { 10 | return this.getPropertyValue('-webkit-text-stroke-color'); 11 | }, 12 | enumerable: true, 13 | configurable: true, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/properties/width.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parseMeasurement = require('../parsers').parseMeasurement; 4 | 5 | function parse(v) { 6 | if (String(v).toLowerCase() === 'auto') { 7 | return 'auto'; 8 | } 9 | if (String(v).toLowerCase() === 'inherit') { 10 | return 'inherit'; 11 | } 12 | return parseMeasurement(v); 13 | } 14 | 15 | module.exports.definition = { 16 | set: function (v) { 17 | this._setProperty('width', parse(v)); 18 | }, 19 | get: function () { 20 | return this.getPropertyValue('width'); 21 | }, 22 | enumerable: true, 23 | configurable: true, 24 | }; 25 | -------------------------------------------------------------------------------- /lib/utils/getBasicPropertyDescriptor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function getBasicPropertyDescriptor(name) { 4 | return { 5 | set: function (v) { 6 | this._setProperty(name, v); 7 | }, 8 | get: function () { 9 | return this.getPropertyValue(name); 10 | }, 11 | enumerable: true, 12 | configurable: true, 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cssstyle", 3 | "description": "CSSStyleDeclaration Object Model implementation", 4 | "keywords": [ 5 | "CSS", 6 | "CSSStyleDeclaration", 7 | "StyleSheet" 8 | ], 9 | "version": "4.3.1", 10 | "homepage": "https://github.com/jsdom/cssstyle", 11 | "maintainers": [ 12 | { 13 | "name": "Jon Sakas", 14 | "email": "jon.sakas@gmail.com", 15 | "url": "https://jon.sakas.co/" 16 | }, 17 | { 18 | "name": "Rafał Ruciński", 19 | "email": "fatfisz@gmail.com", 20 | "url": "https://fatfisz.com" 21 | } 22 | ], 23 | "contributors": [ 24 | { 25 | "name": "Chad Walker", 26 | "email": "chad@chad-cat-lore-eddie.com", 27 | "url": "https://github.com/chad3814" 28 | } 29 | ], 30 | "repository": "jsdom/cssstyle", 31 | "bugs": "https://github.com/jsdom/cssstyle/issues", 32 | "directories": { 33 | "lib": "./lib" 34 | }, 35 | "files": [ 36 | "lib/" 37 | ], 38 | "main": "./lib/CSSStyleDeclaration.js", 39 | "dependencies": { 40 | "@asamuzakjp/css-color": "^3.1.7", 41 | "rrweb-cssom": "^0.8.0" 42 | }, 43 | "devDependencies": { 44 | "@babel/generator": "^7.26.9", 45 | "@babel/parser": "^7.26.9", 46 | "@babel/traverse": "^7.26.9", 47 | "@babel/types": "^7.26.9", 48 | "eslint": "^9.22.0", 49 | "eslint-config-prettier": "^10.1.1", 50 | "eslint-plugin-prettier": "^5.2.3", 51 | "globals": "^16.0.0", 52 | "npm-run-all": "^4.1.5", 53 | "prettier": "^3.5.3", 54 | "resolve": "^1.22.10" 55 | }, 56 | "scripts": { 57 | "download": "node ./scripts/downloadLatestProperties.mjs && eslint lib/allProperties.js --fix", 58 | "generate": "run-p generate:*", 59 | "generate:implemented_properties": "node ./scripts/generateImplementedProperties.mjs", 60 | "generate:properties": "node ./scripts/generate_properties.js", 61 | "lint": "npm run generate && eslint --max-warnings 0", 62 | "lint:fix": "eslint --fix --max-warnings 0", 63 | "prepublishOnly": "npm run lint && npm run test", 64 | "test": "npm run generate && node --test", 65 | "test-ci": "npm run lint && npm run test" 66 | }, 67 | "license": "MIT", 68 | "engines": { 69 | "node": ">=18" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /prettier.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | printWidth: 100, 3 | singleQuote: true, 4 | trailingComma: 'es5', 5 | }; 6 | -------------------------------------------------------------------------------- /scripts/downloadLatestProperties.mjs: -------------------------------------------------------------------------------- 1 | // The W3C CSSWG provides a JSON list of all CSS properties and their status in the standard. 2 | // See the documentation at https://www.w3.org/Style/CSS/all-properties.en.html. 3 | // 4 | // This script downloads that file and uses it to write an appropriately-filtered Set of property 5 | // names to a file. 6 | 7 | import fs from 'node:fs'; 8 | import path from 'node:path'; 9 | import { fileURLToPath } from 'node:url'; 10 | 11 | const url = 'https://www.w3.org/Style/CSS/all-properties.en.json'; 12 | const outputFile = resolve('../lib/allProperties.js'); 13 | const unwantedStatuses = new Set(['NOTE', 'ED', 'FPWD']); 14 | 15 | console.log('Downloading CSS properties...'); 16 | 17 | const res = await fetch(url); 18 | if (res.status !== 200) { 19 | throw new Error('Bad status code: ' + res.status); 20 | } 21 | 22 | const rawCSSProperties = await res.json(); 23 | 24 | const propertyNames = new Set(); 25 | for (const cssProp of rawCSSProperties) { 26 | // Filter out properties from too-new statuses. 27 | // Also, '--*' needs additional logic to this module, so filter it out for now. 28 | if (unwantedStatuses.has(cssProp.status) || cssProp.property === '--*') { 29 | continue; 30 | } 31 | 32 | propertyNames.add(cssProp.property); 33 | } 34 | 35 | const propertyNamesJSON = JSON.stringify(Array.from(propertyNames), undefined, 2); 36 | const dateToday = new Date(); 37 | const dateTodayFormatted = dateToday.toISOString().split('T')[0]; 38 | 39 | const output = ` 40 | 'use strict'; 41 | // autogenerated - ${dateTodayFormatted} 42 | // https://www.w3.org/Style/CSS/all-properties.en.html 43 | 44 | module.exports = new Set(${propertyNamesJSON}); 45 | `; 46 | 47 | fs.writeFileSync(outputFile, output); 48 | 49 | // TODO: remove when we can drop Node.js 18 support and use import.meta.dirname. 50 | function resolve(relativePath) { 51 | return path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath); 52 | } 53 | -------------------------------------------------------------------------------- /scripts/generateImplementedProperties.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | import { camelToDashed } from '../lib/parsers.js'; 5 | 6 | const dashedProperties = fs 7 | .readdirSync(resolve('../lib/properties')) 8 | .filter((propertyFile) => path.extname(propertyFile) === '.js') 9 | .map((propertyFile) => camelToDashed(path.basename(propertyFile, '.js'))); 10 | 11 | const outputFile = resolve('../lib/implementedProperties.js'); 12 | 13 | const propertyNamesJSON = JSON.stringify(dashedProperties, undefined, 2); 14 | const dateToday = new Date(); 15 | const dateTodayFormatted = dateToday.toISOString().split('T')[0]; 16 | 17 | const output = ` 18 | 'use strict'; 19 | // autogenerated - ${dateTodayFormatted} 20 | // https://www.w3.org/Style/CSS/all-properties.en.html 21 | 22 | module.exports = new Set(${propertyNamesJSON}); 23 | `; 24 | 25 | fs.writeFileSync(outputFile, output); 26 | 27 | // TODO: remove when we can drop Node.js 18 support and use import.meta.dirname. 28 | function resolve(relativePath) { 29 | return path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath); 30 | } 31 | -------------------------------------------------------------------------------- /scripts/generate_properties.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var parser = require('@babel/parser'); 6 | var t = require('@babel/types'); 7 | var generate = require('@babel/generator').default; 8 | var traverse = require('@babel/traverse').default; 9 | var resolve = require('resolve'); 10 | 11 | var camelToDashed = require('../lib/parsers').camelToDashed; 12 | 13 | var basename = path.basename; 14 | var dirname = path.dirname; 15 | 16 | var uniqueIndex = 0; 17 | function getUniqueIndex() { 18 | return uniqueIndex++; 19 | } 20 | 21 | var property_files = fs 22 | .readdirSync(path.resolve(__dirname, '../lib/properties')) 23 | .filter(function (property) { 24 | return property.substr(-3) === '.js'; 25 | }); 26 | var out_file = fs.createWriteStream(path.resolve(__dirname, '../lib/properties.js'), { 27 | encoding: 'utf-8', 28 | }); 29 | var date_today = new Date(); 30 | out_file.write( 31 | "'use strict';\n\n// autogenerated - " + 32 | (date_today.getMonth() + 1 + '/' + date_today.getDate() + '/' + date_today.getFullYear()) + 33 | '\n\n' 34 | ); 35 | out_file.write('/*\n *\n * https://www.w3.org/Style/CSS/all-properties.en.html\n */\n\n'); 36 | 37 | function isModuleDotExports(node) { 38 | return ( 39 | t.isMemberExpression(node, { computed: false }) && 40 | t.isIdentifier(node.object, { name: 'module' }) && 41 | t.isIdentifier(node.property, { name: 'exports' }) 42 | ); 43 | } 44 | function isRequire(node, filename) { 45 | if ( 46 | t.isCallExpression(node) && 47 | t.isIdentifier(node.callee, { name: 'require' }) && 48 | node.arguments.length === 1 && 49 | t.isStringLiteral(node.arguments[0]) 50 | ) { 51 | var relative = node.arguments[0].value; 52 | var fullPath = resolve.sync(relative, { basedir: dirname(filename) }); 53 | return { relative: relative, fullPath: fullPath }; 54 | } else { 55 | return false; 56 | } 57 | } 58 | 59 | // step 1: parse all files and figure out their dependencies 60 | var parsedFilesByPath = {}; 61 | property_files.map(function (property) { 62 | var filename = path.resolve(__dirname, '../lib/properties/' + property); 63 | var src = fs.readFileSync(filename, 'utf8'); 64 | property = basename(property, '.js'); 65 | var ast = parser.parse(src); 66 | var dependencies = []; 67 | traverse(ast, { 68 | enter(path) { 69 | var r; 70 | if ((r = isRequire(path.node, filename))) { 71 | dependencies.push(r.fullPath); 72 | } 73 | }, 74 | }); 75 | parsedFilesByPath[filename] = { 76 | filename: filename, 77 | property: property, 78 | ast: ast, 79 | dependencies: dependencies, 80 | }; 81 | }); 82 | 83 | // step 2: serialize the files in an order where dependencies are always above 84 | // the files they depend on 85 | var externalDependencies = []; 86 | var parsedFiles = []; 87 | var addedFiles = {}; 88 | function addFile(filename, dependencyPath) { 89 | if (dependencyPath.indexOf(filename) !== -1) { 90 | throw new Error( 91 | 'Circular dependency: ' + 92 | dependencyPath.slice(dependencyPath.indexOf(filename)).concat([filename]).join(' -> ') 93 | ); 94 | } 95 | var file = parsedFilesByPath[filename]; 96 | if (addedFiles[filename]) { 97 | return; 98 | } 99 | if (!file) { 100 | externalDependencies.push(filename); 101 | } else { 102 | file.dependencies.forEach(function (dependency) { 103 | addFile(dependency, dependencyPath.concat([filename])); 104 | }); 105 | parsedFiles.push(parsedFilesByPath[filename]); 106 | } 107 | addedFiles[filename] = true; 108 | } 109 | Object.keys(parsedFilesByPath).forEach(function (filename) { 110 | addFile(filename, []); 111 | }); 112 | // Step 3: add files to output 113 | // renaming exports to local variables `moduleName_export_exportName` 114 | // and updating require calls as appropriate 115 | var moduleExportsByPath = {}; 116 | var statements = []; 117 | externalDependencies.forEach(function (filename, i) { 118 | var id = t.identifier( 119 | 'external_dependency_' + basename(filename, '.js').replace(/[^A-Za-z]/g, '') + '_' + i 120 | ); 121 | moduleExportsByPath[filename] = { defaultExports: id }; 122 | var relativePath = path.relative(path.resolve(__dirname + '/../lib'), filename); 123 | if (relativePath[0] !== '.') { 124 | relativePath = './' + relativePath; 125 | } 126 | statements.push( 127 | t.variableDeclaration('var', [ 128 | t.variableDeclarator( 129 | id, 130 | t.callExpression(t.identifier('require'), [t.stringLiteral(relativePath)]) 131 | ), 132 | ]) 133 | ); 134 | }); 135 | function getRequireValue(node, file) { 136 | var r, e; 137 | // replace require("./foo").bar with the named export from foo 138 | if ( 139 | t.isMemberExpression(node, { computed: false }) && 140 | (r = isRequire(node.object, file.filename)) 141 | ) { 142 | e = moduleExportsByPath[r.fullPath]; 143 | if (!e) { 144 | return; 145 | } 146 | if (!e.namedExports) { 147 | return t.memberExpression(e.defaultExports, node.property); 148 | } 149 | if (!e.namedExports[node.property.name]) { 150 | throw new Error(r.relative + ' does not export ' + node.property.name); 151 | } 152 | return e.namedExports[node.property.name]; 153 | 154 | // replace require("./foo") with the default export of foo 155 | } else if ((r = isRequire(node, file.filename))) { 156 | e = moduleExportsByPath[r.fullPath]; 157 | if (!e) { 158 | if (/^\.\.\//.test(r.relative)) { 159 | node.arguments[0].value = r.relative.substr(1); 160 | } 161 | return; 162 | } 163 | return e.defaultExports; 164 | } 165 | } 166 | parsedFiles.forEach(function (file) { 167 | var namedExports = {}; 168 | var localVariableMap = {}; 169 | 170 | traverse(file.ast, { 171 | enter(path) { 172 | // replace require calls with the corresponding value 173 | var r; 174 | if ((r = getRequireValue(path.node, file))) { 175 | path.replaceWith(r); 176 | return; 177 | } 178 | 179 | // if we see `var foo = require('bar')` we can just inline the variable 180 | // representing `require('bar')` wherever `foo` was used. 181 | if ( 182 | t.isVariableDeclaration(path.node) && 183 | path.node.declarations.length === 1 && 184 | t.isIdentifier(path.node.declarations[0].id) && 185 | (r = getRequireValue(path.node.declarations[0].init, file)) 186 | ) { 187 | var newName = 'compiled_local_variable_reference_' + getUniqueIndex(); 188 | path.scope.rename(path.node.declarations[0].id.name, newName); 189 | localVariableMap[newName] = r; 190 | path.remove(); 191 | return; 192 | } 193 | 194 | // rename all top level functions to keep them local to the module 195 | if (t.isFunctionDeclaration(path.node) && t.isProgram(path.parent)) { 196 | path.scope.rename(path.node.id.name, file.property + '_local_fn_' + path.node.id.name); 197 | return; 198 | } 199 | 200 | // rename all top level variables to keep them local to the module 201 | if (t.isVariableDeclaration(path.node) && t.isProgram(path.parent)) { 202 | path.node.declarations.forEach(function (declaration) { 203 | path.scope.rename( 204 | declaration.id.name, 205 | file.property + '_local_var_' + declaration.id.name 206 | ); 207 | }); 208 | return; 209 | } 210 | 211 | // replace module.exports.bar with a variable for the named export 212 | if ( 213 | t.isMemberExpression(path.node, { computed: false }) && 214 | isModuleDotExports(path.node.object) 215 | ) { 216 | var name = path.node.property.name; 217 | var identifier = t.identifier(file.property + '_export_' + name); 218 | path.replaceWith(identifier); 219 | namedExports[name] = identifier; 220 | } 221 | }, 222 | }); 223 | traverse(file.ast, { 224 | enter(path) { 225 | if ( 226 | t.isIdentifier(path.node) && 227 | Object.prototype.hasOwnProperty.call(localVariableMap, path.node.name) 228 | ) { 229 | path.replaceWith(localVariableMap[path.node.name]); 230 | } 231 | }, 232 | }); 233 | var defaultExports = t.objectExpression( 234 | Object.keys(namedExports).map(function (name) { 235 | return t.objectProperty(t.identifier(name), namedExports[name]); 236 | }) 237 | ); 238 | moduleExportsByPath[file.filename] = { 239 | namedExports: namedExports, 240 | defaultExports: defaultExports, 241 | }; 242 | statements.push( 243 | t.variableDeclaration( 244 | 'var', 245 | Object.keys(namedExports).map(function (name) { 246 | return t.variableDeclarator(namedExports[name]); 247 | }) 248 | ) 249 | ); 250 | statements.push.apply(statements, file.ast.program.body); 251 | }); 252 | var propertyDefinitions = []; 253 | parsedFiles.forEach(function (file) { 254 | var dashed = camelToDashed(file.property); 255 | propertyDefinitions.push( 256 | t.objectProperty( 257 | t.identifier(file.property), 258 | t.identifier(file.property + '_export_definition') 259 | ) 260 | ); 261 | if (file.property !== dashed) { 262 | propertyDefinitions.push( 263 | t.objectProperty(t.stringLiteral(dashed), t.identifier(file.property + '_export_definition')) 264 | ); 265 | } 266 | }); 267 | var definePropertiesCall = t.callExpression( 268 | t.memberExpression(t.identifier('Object'), t.identifier('defineProperties')), 269 | [t.identifier('prototype'), t.objectExpression(propertyDefinitions)] 270 | ); 271 | statements.push( 272 | t.expressionStatement( 273 | t.assignmentExpression( 274 | '=', 275 | t.memberExpression(t.identifier('module'), t.identifier('exports')), 276 | t.functionExpression( 277 | null, 278 | [t.identifier('prototype')], 279 | t.blockStatement([t.expressionStatement(definePropertiesCall)]) 280 | ) 281 | ) 282 | ) 283 | ); 284 | out_file.write(generate(t.program(statements)).code + '\n'); 285 | out_file.end(function (err) { 286 | if (err) { 287 | throw err; 288 | } 289 | }); 290 | -------------------------------------------------------------------------------- /test/CSSStyleDeclaration.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { describe, it } = require('node:test'); 4 | const assert = require('node:assert/strict'); 5 | var { CSSStyleDeclaration } = require('../lib/CSSStyleDeclaration'); 6 | 7 | var allProperties = require('../lib/allProperties'); 8 | var allExtraProperties = require('../lib/allExtraProperties'); 9 | var implementedProperties = require('../lib/implementedProperties'); 10 | var parsers = require('../lib/parsers'); 11 | 12 | var dashedProperties = [...allProperties, ...allExtraProperties]; 13 | var allowedProperties = dashedProperties.map(parsers.dashedToCamelCase); 14 | implementedProperties = Array.from(implementedProperties).map(parsers.dashedToCamelCase); 15 | var invalidProperties = implementedProperties.filter((prop) => !allowedProperties.includes(prop)); 16 | 17 | describe('CSSStyleDeclaration', () => { 18 | it('has only valid properties implemented', () => { 19 | assert.strictEqual(invalidProperties.length, 0); 20 | }); 21 | 22 | it('has all properties', () => { 23 | var style = new CSSStyleDeclaration(); 24 | allProperties.forEach((property) => { 25 | assert.ok(style.__lookupGetter__(property)); 26 | assert.ok(style.__lookupSetter__(property)); 27 | }); 28 | }); 29 | 30 | it('has dashed properties', () => { 31 | var style = new CSSStyleDeclaration(); 32 | dashedProperties.forEach((property) => { 33 | assert.ok(style.__lookupGetter__(property)); 34 | assert.ok(style.__lookupSetter__(property)); 35 | }); 36 | }); 37 | 38 | it('has all functions', () => { 39 | var style = new CSSStyleDeclaration(); 40 | 41 | assert.strictEqual(typeof style.item, 'function'); 42 | assert.strictEqual(typeof style.getPropertyValue, 'function'); 43 | assert.strictEqual(typeof style.setProperty, 'function'); 44 | assert.strictEqual(typeof style.getPropertyPriority, 'function'); 45 | assert.strictEqual(typeof style.removeProperty, 'function'); 46 | 47 | // TODO - deprecated according to MDN and not implemented at all, can we remove? 48 | assert.strictEqual(typeof style.getPropertyCSSValue, 'function'); 49 | }); 50 | 51 | it('has special properties', () => { 52 | var style = new CSSStyleDeclaration(); 53 | 54 | assert.ok(style.__lookupGetter__('cssText')); 55 | assert.ok(style.__lookupSetter__('cssText')); 56 | assert.ok(style.__lookupGetter__('length')); 57 | assert.ok(style.__lookupSetter__('length')); 58 | assert.ok(style.__lookupGetter__('parentRule')); 59 | }); 60 | 61 | it('from style string', () => { 62 | var style = new CSSStyleDeclaration(); 63 | style.cssText = 'color: blue; background-color: red; width: 78%; height: 50vh;'; 64 | assert.strictEqual(style.length, 4); 65 | assert.strictEqual( 66 | style.cssText, 67 | 'color: blue; background-color: red; width: 78%; height: 50vh;' 68 | ); 69 | assert.strictEqual(style.getPropertyValue('color'), 'blue'); 70 | assert.strictEqual(style.item(0), 'color'); 71 | assert.strictEqual(style[1], 'background-color'); 72 | assert.strictEqual(style.backgroundColor, 'red'); 73 | style.cssText = ''; 74 | assert.strictEqual(style.cssText, ''); 75 | assert.strictEqual(style.length, 0); 76 | }); 77 | 78 | it('from properties', () => { 79 | var style = new CSSStyleDeclaration(); 80 | style.color = 'blue'; 81 | assert.strictEqual(style.length, 1); 82 | assert.strictEqual(style[0], 'color'); 83 | assert.strictEqual(style.cssText, 'color: blue;'); 84 | assert.strictEqual(style.item(0), 'color'); 85 | assert.strictEqual(style.color, 'blue'); 86 | style.backgroundColor = 'red'; 87 | assert.strictEqual(style.length, 2); 88 | assert.strictEqual(style[0], 'color'); 89 | assert.strictEqual(style[1], 'background-color'); 90 | assert.strictEqual(style.cssText, 'color: blue; background-color: red;'); 91 | assert.strictEqual(style.backgroundColor, 'red'); 92 | style.removeProperty('color'); 93 | assert.strictEqual(style[0], 'background-color'); 94 | }); 95 | 96 | it('shorthand properties', () => { 97 | var style = new CSSStyleDeclaration(); 98 | style.background = 'blue url(http://www.example.com/some_img.jpg)'; 99 | assert.strictEqual(style.backgroundColor, 'blue'); 100 | assert.strictEqual(style.backgroundImage, 'url("http://www.example.com/some_img.jpg")'); 101 | assert.strictEqual(style.background, 'blue url("http://www.example.com/some_img.jpg")'); 102 | style.border = '0 solid black'; 103 | assert.strictEqual(style.borderWidth, '0px'); 104 | assert.strictEqual(style.borderStyle, 'solid'); 105 | assert.strictEqual(style.borderColor, 'black'); 106 | assert.strictEqual(style.borderTopWidth, '0px'); 107 | assert.strictEqual(style.borderLeftStyle, 'solid'); 108 | assert.strictEqual(style.borderBottomColor, 'black'); 109 | style.font = '12em monospace'; 110 | assert.strictEqual(style.fontSize, '12em'); 111 | assert.strictEqual(style.fontFamily, 'monospace'); 112 | }); 113 | 114 | it('width and height properties and null and empty strings', () => { 115 | var style = new CSSStyleDeclaration(); 116 | style.height = 6; 117 | assert.strictEqual(style.height, ''); 118 | style.width = 0; 119 | assert.strictEqual(style.width, '0px'); 120 | style.height = '34%'; 121 | assert.strictEqual(style.height, '34%'); 122 | style.height = '100vh'; 123 | assert.strictEqual(style.height, '100vh'); 124 | style.height = '100vw'; 125 | assert.strictEqual(style.height, '100vw'); 126 | style.height = ''; 127 | assert.strictEqual(style.length, 1); 128 | assert.strictEqual(style.cssText, 'width: 0px;'); 129 | style.width = null; 130 | assert.strictEqual(style.length, 0); 131 | assert.strictEqual(style.cssText, ''); 132 | }); 133 | 134 | it('implicit properties', () => { 135 | var style = new CSSStyleDeclaration(); 136 | style.borderWidth = 0; 137 | assert.strictEqual(style.length, 1); 138 | assert.strictEqual(style.borderWidth, '0px'); 139 | assert.strictEqual(style.borderTopWidth, '0px'); 140 | assert.strictEqual(style.borderBottomWidth, '0px'); 141 | assert.strictEqual(style.borderLeftWidth, '0px'); 142 | assert.strictEqual(style.borderRightWidth, '0px'); 143 | assert.strictEqual(style.cssText, 'border-width: 0px;'); 144 | }); 145 | 146 | it('top, left, right, bottom properties', () => { 147 | var style = new CSSStyleDeclaration(); 148 | style.top = 0; 149 | style.left = '0%'; 150 | style.right = '5em'; 151 | style.bottom = '12pt'; 152 | assert.strictEqual(style.top, '0px'); 153 | assert.strictEqual(style.left, '0%'); 154 | assert.strictEqual(style.right, '5em'); 155 | assert.strictEqual(style.bottom, '12pt'); 156 | assert.strictEqual(style.length, 4); 157 | assert.strictEqual(style.cssText, 'top: 0px; left: 0%; right: 5em; bottom: 12pt;'); 158 | }); 159 | 160 | it('top, left, right, bottom properties should accept "auto"', () => { 161 | var style = new CSSStyleDeclaration(); 162 | style.cssText = `top: auto; right: auto; bottom: auto; left: auto;`; 163 | assert.strictEqual(style.top, 'auto'); 164 | assert.strictEqual(style.right, 'auto'); 165 | assert.strictEqual(style.bottom, 'auto'); 166 | assert.strictEqual(style.left, 'auto'); 167 | }); 168 | 169 | it('clear and clip properties', () => { 170 | var style = new CSSStyleDeclaration(); 171 | style.clear = 'none'; 172 | assert.strictEqual(style.clear, 'none'); 173 | style.clear = 'lfet'; 174 | assert.strictEqual(style.clear, 'none'); 175 | style.clear = 'left'; 176 | assert.strictEqual(style.clear, 'left'); 177 | style.clear = 'right'; 178 | assert.strictEqual(style.clear, 'right'); 179 | style.clear = 'both'; 180 | assert.strictEqual(style.clear, 'both'); 181 | style.clip = 'elipse(5px, 10px)'; 182 | assert.strictEqual(style.clip, ''); 183 | assert.strictEqual(style.length, 1); 184 | style.clip = 'rect(0, 3Em, 2pt, 50%)'; 185 | assert.strictEqual(style.clip, 'rect(0px, 3em, 2pt, 50%)'); 186 | assert.strictEqual(style.length, 2); 187 | assert.strictEqual(style.cssText, 'clear: both; clip: rect(0px, 3em, 2pt, 50%);'); 188 | }); 189 | 190 | it('colors', () => { 191 | var style = new CSSStyleDeclaration(); 192 | style.color = 'rgba(0,0,0,0)'; 193 | assert.strictEqual(style.color, 'rgba(0, 0, 0, 0)'); 194 | style.color = 'rgba(5%, 10%, 20%, 0.4)'; 195 | assert.strictEqual(style.color, 'rgba(13, 26, 51, 0.4)'); 196 | style.color = 'rgb(33%, 34%, 33%)'; 197 | assert.strictEqual(style.color, 'rgb(84, 87, 84)'); 198 | style.color = 'rgba(300, 200, 100, 1.5)'; 199 | assert.strictEqual(style.color, 'rgb(255, 200, 100)'); 200 | style.color = 'hsla(0, 1%, 2%, 0.5)'; 201 | assert.strictEqual(style.color, 'rgba(5, 5, 5, 0.5)'); 202 | style.color = 'hsl(0, 1%, 2%)'; 203 | assert.strictEqual(style.color, 'rgb(5, 5, 5)'); 204 | style.color = 'rebeccapurple'; 205 | assert.strictEqual(style.color, 'rebeccapurple'); 206 | style.color = 'transparent'; 207 | assert.strictEqual(style.color, 'transparent'); 208 | style.color = 'currentcolor'; 209 | assert.strictEqual(style.color, 'currentcolor'); 210 | style.color = '#ffffffff'; 211 | assert.strictEqual(style.color, 'rgb(255, 255, 255)'); 212 | style.color = '#fffa'; 213 | assert.strictEqual(style.color, 'rgba(255, 255, 255, 0.667)'); 214 | style.color = '#ffffff66'; 215 | assert.strictEqual(style.color, 'rgba(255, 255, 255, 0.4)'); 216 | }); 217 | 218 | it('invalid hex color value', () => { 219 | var style = new CSSStyleDeclaration(); 220 | style.color = '#1234567'; 221 | assert.strictEqual(style.color, ''); 222 | }); 223 | 224 | it('short hand properties with embedded spaces', () => { 225 | var style = new CSSStyleDeclaration(); 226 | style.background = 'rgb(0, 0, 0) url(/something/somewhere.jpg)'; 227 | assert.strictEqual(style.backgroundColor, 'rgb(0, 0, 0)'); 228 | assert.strictEqual(style.backgroundImage, 'url("/something/somewhere.jpg")'); 229 | assert.strictEqual(style.cssText, 'background: rgb(0, 0, 0) url("/something/somewhere.jpg");'); 230 | style = new CSSStyleDeclaration(); 231 | style.border = ' 1px solid black '; 232 | assert.strictEqual(style.border, '1px solid black'); 233 | }); 234 | 235 | it('setting shorthand properties to an empty string should clear all dependent properties', () => { 236 | var style = new CSSStyleDeclaration(); 237 | style.borderWidth = '1px'; 238 | assert.strictEqual(style.cssText, 'border-width: 1px;'); 239 | style.border = ''; 240 | assert.strictEqual(style.cssText, ''); 241 | }); 242 | 243 | it('setting implicit properties to an empty string should clear all dependent properties', () => { 244 | var style = new CSSStyleDeclaration(); 245 | style.borderTopWidth = '1px'; 246 | assert.strictEqual(style.cssText, 'border-top-width: 1px;'); 247 | style.borderWidth = ''; 248 | assert.strictEqual(style.cssText, ''); 249 | }); 250 | 251 | it('setting a shorthand property, whose shorthands are implicit properties, to an empty string should clear all dependent properties', () => { 252 | var style = new CSSStyleDeclaration(); 253 | style.borderTopWidth = '1px'; 254 | assert.strictEqual(style.cssText, 'border-top-width: 1px;'); 255 | style.border = ''; 256 | assert.strictEqual(style.cssText, ''); 257 | style.borderTop = '1px solid black'; 258 | assert.strictEqual(style.cssText, 'border-top: 1px solid black;'); 259 | style.border = ''; 260 | assert.strictEqual(style.cssText, ''); 261 | }); 262 | 263 | it('setting border values to "none" should clear dependent values', () => { 264 | var style = new CSSStyleDeclaration(); 265 | style.borderTopWidth = '1px'; 266 | assert.strictEqual(style.cssText, 'border-top-width: 1px;'); 267 | style.border = 'none'; 268 | assert.strictEqual(style.cssText, ''); 269 | style.borderTopWidth = '1px'; 270 | assert.strictEqual(style.cssText, 'border-top-width: 1px;'); 271 | style.borderTopStyle = 'none'; 272 | assert.strictEqual(style.cssText, ''); 273 | style.borderTopWidth = '1px'; 274 | assert.strictEqual(style.cssText, 'border-top-width: 1px;'); 275 | style.borderTop = 'none'; 276 | assert.strictEqual(style.cssText, ''); 277 | style.borderTopWidth = '1px'; 278 | style.borderLeftWidth = '1px'; 279 | assert.strictEqual(style.cssText, 'border-top-width: 1px; border-left-width: 1px;'); 280 | style.borderTop = 'none'; 281 | assert.strictEqual(style.cssText, 'border-left-width: 1px;'); 282 | }); 283 | 284 | it('setting border to 0 should be okay', () => { 285 | var style = new CSSStyleDeclaration(); 286 | style.border = 0; 287 | assert.strictEqual(style.cssText, 'border: 0px;'); 288 | }); 289 | 290 | it('setting borderColor to var() should be okay', () => { 291 | var style = new CSSStyleDeclaration(); 292 | style.borderColor = 'var(--foo)'; 293 | assert.strictEqual(style.cssText, 'border-color: var(--foo);'); 294 | }); 295 | 296 | it('setting borderColor to inherit should be okay', () => { 297 | var style = new CSSStyleDeclaration(); 298 | style.borderColor = 'inherit'; 299 | assert.strictEqual(style.cssText, 'border-color: inherit;'); 300 | }); 301 | 302 | it('setting values implicit and shorthand properties via csstext and setproperty should propagate to dependent properties', () => { 303 | var style = new CSSStyleDeclaration(); 304 | style.cssText = 'border: 1px solid black;'; 305 | assert.strictEqual(style.cssText, 'border: 1px solid black;'); 306 | assert.strictEqual(style.borderTop, '1px solid black'); 307 | style.border = ''; 308 | assert.strictEqual(style.cssText, ''); 309 | style.setProperty('border', '1px solid black'); 310 | assert.strictEqual(style.cssText, 'border: 1px solid black;'); 311 | }); 312 | 313 | it('setting opacity should work', () => { 314 | var style = new CSSStyleDeclaration(); 315 | style.setProperty('opacity', 0.75); 316 | assert.strictEqual(style.cssText, 'opacity: 0.75;'); 317 | style.opacity = '0.50'; 318 | assert.strictEqual(style.cssText, 'opacity: 0.5;'); 319 | style.opacity = 1; 320 | assert.strictEqual(style.cssText, 'opacity: 1;'); 321 | }); 322 | 323 | it('width and height of auto should work', () => { 324 | var style = new CSSStyleDeclaration(); 325 | style.width = 'auto'; 326 | assert.strictEqual(style.cssText, 'width: auto;'); 327 | assert.strictEqual(style.width, 'auto'); 328 | style = new CSSStyleDeclaration(); 329 | style.height = 'auto'; 330 | assert.strictEqual(style.cssText, 'height: auto;'); 331 | assert.strictEqual(style.height, 'auto'); 332 | }); 333 | 334 | it('padding and margin should set/clear shorthand properties', () => { 335 | var style = new CSSStyleDeclaration(); 336 | var parts = ['Top', 'Right', 'Bottom', 'Left']; 337 | var testParts = function (name, v, V) { 338 | style[name] = v; 339 | for (var i = 0; i < 4; i++) { 340 | var part = name + parts[i]; 341 | assert.strictEqual(style[part], V[i]); 342 | } 343 | 344 | assert.strictEqual(style[name], v); 345 | style[name] = ''; 346 | }; 347 | testParts('padding', '1px', ['1px', '1px', '1px', '1px']); 348 | testParts('padding', '1px 2%', ['1px', '2%', '1px', '2%']); 349 | testParts('padding', '1px 2px 3px', ['1px', '2px', '3px', '2px']); 350 | testParts('padding', '1px 2px 3px 4px', ['1px', '2px', '3px', '4px']); 351 | style.paddingTop = style.paddingRight = style.paddingBottom = style.paddingLeft = '1px'; 352 | testParts('padding', '', ['', '', '', '']); 353 | testParts('margin', '1px', ['1px', '1px', '1px', '1px']); 354 | testParts('margin', '1px auto', ['1px', 'auto', '1px', 'auto']); 355 | testParts('margin', '1px 2% 3px', ['1px', '2%', '3px', '2%']); 356 | testParts('margin', '1px 2px 3px 4px', ['1px', '2px', '3px', '4px']); 357 | style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = '1px'; 358 | testParts('margin', '', ['', '', '', '']); 359 | }); 360 | 361 | it('padding and margin shorthands should set main properties', () => { 362 | var style = new CSSStyleDeclaration(); 363 | var parts = ['Top', 'Right', 'Bottom', 'Left']; 364 | var testParts = function (name, v, V) { 365 | var expected; 366 | for (var i = 0; i < 4; i++) { 367 | style[name] = v; 368 | style[name + parts[i]] = V; 369 | expected = v.split(/ /); 370 | expected[i] = V; 371 | expected = expected.join(' '); 372 | 373 | assert.strictEqual(style[name], expected); 374 | } 375 | }; 376 | testParts('padding', '1px 2px 3px 4px', '10px'); 377 | testParts('margin', '1px 2px 3px 4px', '10px'); 378 | testParts('margin', '1px 2px 3px 4px', 'auto'); 379 | }); 380 | 381 | it('setting individual padding and margin properties to an empty string should clear them', () => { 382 | var style = new CSSStyleDeclaration(); 383 | 384 | var properties = ['padding', 'margin']; 385 | var parts = ['Top', 'Right', 'Bottom', 'Left']; 386 | for (var i = 0; i < properties.length; i++) { 387 | for (var j = 0; j < parts.length; j++) { 388 | var property = properties[i] + parts[j]; 389 | style[property] = '12px'; 390 | assert.strictEqual(style[property], '12px'); 391 | 392 | style[property] = ''; 393 | assert.strictEqual(style[property], ''); 394 | } 395 | } 396 | }); 397 | 398 | it('removing and setting individual margin properties updates the combined property accordingly', () => { 399 | var style = new CSSStyleDeclaration(); 400 | style.margin = '1px 2px 3px 4px'; 401 | 402 | style.marginTop = ''; 403 | assert.strictEqual(style.margin, ''); 404 | assert.strictEqual(style.marginRight, '2px'); 405 | assert.strictEqual(style.marginBottom, '3px'); 406 | assert.strictEqual(style.marginLeft, '4px'); 407 | 408 | style.marginBottom = ''; 409 | assert.strictEqual(style.margin, ''); 410 | assert.strictEqual(style.marginRight, '2px'); 411 | assert.strictEqual(style.marginLeft, '4px'); 412 | 413 | style.marginBottom = '5px'; 414 | assert.strictEqual(style.margin, ''); 415 | assert.strictEqual(style.marginRight, '2px'); 416 | assert.strictEqual(style.marginBottom, '5px'); 417 | assert.strictEqual(style.marginLeft, '4px'); 418 | 419 | style.marginTop = '6px'; 420 | assert.strictEqual(style.cssText, 'margin: 6px 2px 5px 4px;'); 421 | }); 422 | 423 | for (const property of ['padding', 'margin']) { 424 | it(`removing an individual ${property} property should remove the combined property and replace it with the remaining individual ones`, () => { 425 | var style = new CSSStyleDeclaration(); 426 | var parts = ['Top', 'Right', 'Bottom', 'Left']; 427 | var partValues = ['1px', '2px', '3px', '4px']; 428 | 429 | for (var j = 0; j < parts.length; j++) { 430 | var partToRemove = parts[j]; 431 | style[property] = partValues.join(' '); 432 | style[property + partToRemove] = ''; 433 | 434 | // Main property should have been removed 435 | assert.strictEqual(style[property], ''); 436 | 437 | // Expect other parts to still be there 438 | for (var k = 0; k < parts.length; k++) { 439 | var propertyCss = property + '-' + parts[k].toLowerCase() + ': ' + partValues[k] + ';'; 440 | if (k === j) { 441 | assert.strictEqual(style[property + parts[k]], ''); 442 | assert.strictEqual(style.cssText.includes(propertyCss), false); 443 | } else { 444 | assert.strictEqual(style[property + parts[k]], partValues[k]); 445 | assert.strictEqual(style.cssText.includes(propertyCss), true); 446 | } 447 | } 448 | } 449 | }); 450 | 451 | it(`setting additional ${property} properties keeps important status of others`, () => { 452 | var style = new CSSStyleDeclaration(); 453 | var importantProperty = property + '-top: 3px !important;'; 454 | style.cssText = importantProperty; 455 | assert.strictEqual(style.cssText.includes(importantProperty), true); 456 | 457 | style[property + 'Right'] = '4px'; 458 | style[property + 'Bottom'] = '5px'; 459 | style[property + 'Left'] = '6px'; 460 | 461 | assert.strictEqual(style.cssText.includes(importantProperty), true); 462 | assert.strictEqual(style.cssText.includes(property + '-right: 4px;'), true); 463 | assert.strictEqual(style.cssText.includes(property + '-bottom: 5px;'), true); 464 | assert.strictEqual(style.cssText.includes(property + '-left: 6px;'), true); 465 | assert.strictEqual(style.cssText.includes('margin:'), false); 466 | }); 467 | 468 | it(`setting individual ${property} keeps important status of others`, () => { 469 | var style = new CSSStyleDeclaration(); 470 | style.cssText = `${property}: 3px !important;`; 471 | 472 | style[property + 'Top'] = '4px'; 473 | 474 | assert.strictEqual(style.cssText.includes(`${property}-top: 4px;`), true); 475 | assert.strictEqual(style.cssText.includes(`${property}-right: 3px !important;`), true); 476 | assert.strictEqual(style.cssText.includes(`${property}-bottom: 3px !important;`), true); 477 | assert.strictEqual(style.cssText.includes(`${property}-left: 3px !important;`), true); 478 | assert.strictEqual(style.cssText.includes('margin:'), false); 479 | }); 480 | } 481 | 482 | it('setting a value to 0 should return the string value', () => { 483 | var style = new CSSStyleDeclaration(); 484 | style.setProperty('fill-opacity', 0); 485 | assert.strictEqual(style.fillOpacity, '0'); 486 | }); 487 | 488 | it('onchange callback should be called when the csstext changes', () => { 489 | var called = 0; 490 | var style = new CSSStyleDeclaration(function (cssText) { 491 | called++; 492 | assert.strictEqual(cssText, 'opacity: 0;'); 493 | }); 494 | style.cssText = 'opacity: 0;'; 495 | assert.strictEqual(called, 1); 496 | style.cssText = 'opacity: 0;'; 497 | assert.strictEqual(called, 2); 498 | }); 499 | 500 | it('onchange callback should be called only once when multiple properties were added', () => { 501 | var called = 0; 502 | var style = new CSSStyleDeclaration(function (cssText) { 503 | called++; 504 | assert.strictEqual(cssText, 'width: 100px; height: 100px;'); 505 | }); 506 | style.cssText = 'width: 100px;height:100px;'; 507 | assert.strictEqual(called, 1); 508 | }); 509 | 510 | it('onchange callback should not be called when property is set to the same value', () => { 511 | var called = 0; 512 | var style = new CSSStyleDeclaration(function () { 513 | called++; 514 | }); 515 | 516 | style.setProperty('opacity', 0); 517 | assert.strictEqual(called, 1); 518 | style.setProperty('opacity', 0); 519 | assert.strictEqual(called, 1); 520 | }); 521 | 522 | it('onchange callback should not be called when removeProperty was called on non-existing property', () => { 523 | var called = 0; 524 | var style = new CSSStyleDeclaration(function () { 525 | called++; 526 | }); 527 | style.removeProperty('opacity'); 528 | assert.strictEqual(called, 0); 529 | }); 530 | 531 | it('setting float should work the same as cssfloat', () => { 532 | var style = new CSSStyleDeclaration(); 533 | style.float = 'left'; 534 | assert.strictEqual(style.cssFloat, 'left'); 535 | }); 536 | 537 | it('setting improper css to csstext should not throw', () => { 538 | var style = new CSSStyleDeclaration(); 539 | style.cssText = 'color: '; 540 | assert.strictEqual(style.cssText, ''); 541 | style.color = 'black'; 542 | style.cssText = 'float: '; 543 | assert.strictEqual(style.cssText, ''); 544 | }); 545 | 546 | it('url parsing works with quotes', () => { 547 | var style = new CSSStyleDeclaration(); 548 | style.backgroundImage = 'url(http://some/url/here1.png)'; 549 | assert.strictEqual(style.backgroundImage, 'url("http://some/url/here1.png")'); 550 | style.backgroundImage = "url('http://some/url/here2.png')"; 551 | assert.strictEqual(style.backgroundImage, 'url("http://some/url/here2.png")'); 552 | style.backgroundImage = 'url("http://some/url/here3.png")'; 553 | assert.strictEqual(style.backgroundImage, 'url("http://some/url/here3.png")'); 554 | }); 555 | 556 | it('setting 0 to a padding or margin works', () => { 557 | var style = new CSSStyleDeclaration(); 558 | style.padding = 0; 559 | assert.strictEqual(style.cssText, 'padding: 0px;'); 560 | style.margin = '1em'; 561 | style.marginTop = '0'; 562 | assert.strictEqual(style.marginTop, '0px'); 563 | }); 564 | 565 | it('setting ex units to a padding or margin works', () => { 566 | var style = new CSSStyleDeclaration(); 567 | style.padding = '1ex'; 568 | assert.strictEqual(style.cssText, 'padding: 1ex;'); 569 | style.margin = '1em'; 570 | style.marginTop = '0.5ex'; 571 | assert.strictEqual(style.marginTop, '0.5ex'); 572 | }); 573 | 574 | it('setting empty string and null to a padding or margin works', () => { 575 | var style = new CSSStyleDeclaration(); 576 | var parts = ['Top', 'Right', 'Bottom', 'Left']; 577 | function testParts(base, nullValue) { 578 | var props = [base].concat(parts.map((part) => base + part)); 579 | for (let prop of props) { 580 | assert.strictEqual(style[prop], ''); 581 | style[prop] = '10px'; 582 | assert.strictEqual(style[prop], '10px'); 583 | style[prop] = nullValue; 584 | assert.strictEqual(style[prop], ''); 585 | } 586 | } 587 | 588 | testParts('margin', ''); 589 | testParts('margin', null); 590 | testParts('padding', ''); 591 | testParts('padding', null); 592 | }); 593 | 594 | it('setting undefined to a padding or margin does nothing', () => { 595 | var style = new CSSStyleDeclaration(); 596 | var parts = ['Top', 'Right', 'Bottom', 'Left']; 597 | function testParts(base) { 598 | var props = [base].concat(parts.map((part) => base + part)); 599 | for (let prop of props) { 600 | style[prop] = '10px'; 601 | assert.strictEqual(style[prop], '10px'); 602 | style[prop] = undefined; 603 | assert.strictEqual(style[prop], '10px'); 604 | } 605 | } 606 | 607 | testParts('margin'); 608 | testParts('padding'); 609 | }); 610 | 611 | it('setting null to background works', () => { 612 | var style = new CSSStyleDeclaration(); 613 | style.background = 'red'; 614 | assert.strictEqual(style.cssText, 'background: red;'); 615 | style.background = null; 616 | assert.strictEqual(style.cssText, ''); 617 | }); 618 | 619 | it('flex properties should keep their values', () => { 620 | var style = new CSSStyleDeclaration(); 621 | style.flexDirection = 'column'; 622 | assert.strictEqual(style.cssText, 'flex-direction: column;'); 623 | style.flexDirection = 'row'; 624 | assert.strictEqual(style.cssText, 'flex-direction: row;'); 625 | }); 626 | 627 | it('camelcase properties are not assigned with `.setproperty()`', () => { 628 | var style = new CSSStyleDeclaration(); 629 | style.setProperty('fontSize', '12px'); 630 | assert.strictEqual(style.cssText, ''); 631 | }); 632 | 633 | it('casing is ignored in `.setproperty()`', () => { 634 | var style = new CSSStyleDeclaration(); 635 | style.setProperty('FoNt-SiZe', '12px'); 636 | assert.strictEqual(style.fontSize, '12px'); 637 | assert.strictEqual(style.getPropertyValue('font-size'), '12px'); 638 | }); 639 | 640 | it('support non string entries in border-spacing', () => { 641 | var style = new CSSStyleDeclaration(); 642 | style.borderSpacing = 0; 643 | assert.strictEqual(style.cssText, 'border-spacing: 0px;'); 644 | }); 645 | 646 | it('float should be valid property for `.setproperty()`', () => { 647 | var style = new CSSStyleDeclaration(); 648 | style.setProperty('float', 'left'); 649 | assert.strictEqual(style.float, 'left'); 650 | assert.strictEqual(style.getPropertyValue('float'), 'left'); 651 | }); 652 | 653 | it('flex-shrink works', () => { 654 | var style = new CSSStyleDeclaration(); 655 | style.setProperty('flex-shrink', 0); 656 | assert.strictEqual(style.getPropertyValue('flex-shrink'), '0'); 657 | style.setProperty('flex-shrink', 1); 658 | assert.strictEqual(style.getPropertyValue('flex-shrink'), '1'); 659 | assert.strictEqual(style.cssText, 'flex-shrink: 1;'); 660 | }); 661 | 662 | it('flex-grow works', () => { 663 | var style = new CSSStyleDeclaration(); 664 | style.setProperty('flex-grow', 2); 665 | assert.strictEqual(style.getPropertyValue('flex-grow'), '2'); 666 | assert.strictEqual(style.cssText, 'flex-grow: 2;'); 667 | }); 668 | 669 | it('flex-basis works', () => { 670 | var style = new CSSStyleDeclaration(); 671 | style.setProperty('flex-basis', 0); 672 | assert.strictEqual(style.getPropertyValue('flex-basis'), '0px'); 673 | style.setProperty('flex-basis', '250px'); 674 | assert.strictEqual(style.getPropertyValue('flex-basis'), '250px'); 675 | style.setProperty('flex-basis', '10em'); 676 | assert.strictEqual(style.getPropertyValue('flex-basis'), '10em'); 677 | style.setProperty('flex-basis', '30%'); 678 | assert.strictEqual(style.getPropertyValue('flex-basis'), '30%'); 679 | assert.strictEqual(style.cssText, 'flex-basis: 30%;'); 680 | }); 681 | 682 | it('shorthand flex works', () => { 683 | var style = new CSSStyleDeclaration(); 684 | style.setProperty('flex', 'none'); 685 | assert.strictEqual(style.getPropertyValue('flex-grow'), '0'); 686 | assert.strictEqual(style.getPropertyValue('flex-shrink'), '0'); 687 | assert.strictEqual(style.getPropertyValue('flex-basis'), 'auto'); 688 | style.removeProperty('flex'); 689 | style.removeProperty('flex-basis'); 690 | style.setProperty('flex', 'auto'); 691 | assert.strictEqual(style.getPropertyValue('flex-grow'), ''); 692 | assert.strictEqual(style.getPropertyValue('flex-shrink'), ''); 693 | assert.strictEqual(style.getPropertyValue('flex-basis'), 'auto'); 694 | style.removeProperty('flex'); 695 | style.setProperty('flex', '0 1 250px'); 696 | assert.strictEqual(style.getPropertyValue('flex'), '0 1 250px'); 697 | assert.strictEqual(style.getPropertyValue('flex-grow'), '0'); 698 | assert.strictEqual(style.getPropertyValue('flex-shrink'), '1'); 699 | assert.strictEqual(style.getPropertyValue('flex-basis'), '250px'); 700 | style.removeProperty('flex'); 701 | style.setProperty('flex', '2'); 702 | assert.strictEqual(style.getPropertyValue('flex-grow'), '2'); 703 | assert.strictEqual(style.getPropertyValue('flex-shrink'), ''); 704 | assert.strictEqual(style.getPropertyValue('flex-basis'), ''); 705 | style.removeProperty('flex'); 706 | style.setProperty('flex', '20%'); 707 | assert.strictEqual(style.getPropertyValue('flex-grow'), ''); 708 | assert.strictEqual(style.getPropertyValue('flex-shrink'), ''); 709 | assert.strictEqual(style.getPropertyValue('flex-basis'), '20%'); 710 | style.removeProperty('flex'); 711 | style.setProperty('flex', '2 2'); 712 | assert.strictEqual(style.getPropertyValue('flex-grow'), '2'); 713 | assert.strictEqual(style.getPropertyValue('flex-shrink'), '2'); 714 | assert.strictEqual(style.getPropertyValue('flex-basis'), ''); 715 | style.removeProperty('flex'); 716 | }); 717 | 718 | it('font-size get a valid value', () => { 719 | var style = new CSSStyleDeclaration(); 720 | const invalidValue = '1r5px'; 721 | style.cssText = 'font-size: 15px'; 722 | assert.strictEqual(1, style.length); 723 | style.cssText = `font-size: ${invalidValue}`; 724 | assert.strictEqual(0, style.length); 725 | assert.strictEqual(undefined, style[0]); 726 | }); 727 | 728 | it('getPropertyValue for custom properties in cssText', () => { 729 | const style = new CSSStyleDeclaration(); 730 | style.cssText = '--foo: red'; 731 | 732 | assert.strictEqual(style.getPropertyValue('--foo'), 'red'); 733 | }); 734 | 735 | it('getPropertyValue for custom properties with setProperty', () => { 736 | const style = new CSSStyleDeclaration(); 737 | style.setProperty('--bar', 'blue'); 738 | 739 | assert.strictEqual(style.getPropertyValue('--bar'), 'blue'); 740 | }); 741 | 742 | it('getPropertyValue for custom properties with object setter', () => { 743 | const style = new CSSStyleDeclaration(); 744 | style['--baz'] = 'yellow'; 745 | 746 | assert.strictEqual(style.getPropertyValue('--baz'), ''); 747 | }); 748 | 749 | it('custom properties are case-sensitive', () => { 750 | const style = new CSSStyleDeclaration(); 751 | style.cssText = '--fOo: purple'; 752 | 753 | assert.strictEqual(style.getPropertyValue('--foo'), ''); 754 | assert.strictEqual(style.getPropertyValue('--fOo'), 'purple'); 755 | }); 756 | 757 | for (const property of [ 758 | 'width', 759 | 'height', 760 | 'margin', 761 | 'margin-top', 762 | 'bottom', 763 | 'right', 764 | 'padding', 765 | ]) { 766 | it(`supports calc for ${property}`, () => { 767 | const style = new CSSStyleDeclaration(); 768 | style.setProperty(property, 'calc(100% - 100px)'); 769 | assert.strictEqual(style.getPropertyValue(property), 'calc(100% - 100px)'); 770 | }); 771 | } 772 | 773 | it('supports nested calc', () => { 774 | const style = new CSSStyleDeclaration(); 775 | style.setProperty('width', 'calc(100% - calc(200px - 100px))'); 776 | assert.strictEqual(style.getPropertyValue('width'), 'calc(100% - 100px)'); 777 | }); 778 | 779 | it('supports nested calc', () => { 780 | const style = new CSSStyleDeclaration(); 781 | style.setProperty('width', 'calc(100% * calc(2 / 3))'); 782 | assert.strictEqual(style.getPropertyValue('width'), 'calc(66.6667%)'); 783 | }); 784 | 785 | it('supports var', () => { 786 | const style = new CSSStyleDeclaration(); 787 | style.setProperty('width', 'var(--foo)'); 788 | assert.strictEqual(style.getPropertyValue('width'), 'var(--foo)'); 789 | }); 790 | 791 | it('supports var with fallback', () => { 792 | const style = new CSSStyleDeclaration(); 793 | style.setProperty('width', 'var(--foo, 100px)'); 794 | assert.strictEqual(style.getPropertyValue('width'), 'var(--foo, 100px)'); 795 | }); 796 | 797 | it('supports var with var fallback', () => { 798 | const style = new CSSStyleDeclaration(); 799 | style.setProperty('width', 'var(--foo, var(--bar))'); 800 | assert.strictEqual(style.getPropertyValue('width'), 'var(--foo, var(--bar))'); 801 | }); 802 | 803 | it('supports calc with var inside', () => { 804 | const style = new CSSStyleDeclaration(); 805 | style.setProperty('width', 'calc(100% - var(--foo))'); 806 | assert.strictEqual(style.getPropertyValue('width'), 'calc(100% - var(--foo))'); 807 | }); 808 | 809 | it('supports var with calc inside', () => { 810 | const style = new CSSStyleDeclaration(); 811 | style.setProperty('width', 'var(--foo, calc(var(--bar) + 3px))'); 812 | assert.strictEqual(style.getPropertyValue('width'), 'var(--foo, calc(var(--bar) + 3px))'); 813 | }); 814 | 815 | it('supports color var', () => { 816 | const style = new CSSStyleDeclaration(); 817 | style.setProperty('color', 'var(--foo)'); 818 | assert.strictEqual(style.getPropertyValue('color'), 'var(--foo)'); 819 | }); 820 | 821 | it('should not normalize if var() is included', () => { 822 | const style = new CSSStyleDeclaration(); 823 | style.setProperty('width', 'calc( /* comment */ 100% - calc(var(--foo) *2 ))'); 824 | assert.strictEqual( 825 | style.getPropertyValue('width'), 826 | 'calc( /* comment */ 100% - calc(var(--foo) *2 ))' 827 | ); 828 | }); 829 | 830 | it('supports abs', () => { 831 | const style = new CSSStyleDeclaration(); 832 | style.setProperty('width', 'abs(1 + 2 + 3)'); 833 | assert.strictEqual(style.getPropertyValue('width'), 'calc(6)'); 834 | }); 835 | 836 | it('supports abs inside calc', () => { 837 | const style = new CSSStyleDeclaration(); 838 | style.setProperty('width', 'calc(abs(1) + abs(2))'); 839 | assert.strictEqual(style.getPropertyValue('width'), 'calc(3)'); 840 | }); 841 | 842 | it('supports sign', () => { 843 | const style = new CSSStyleDeclaration(); 844 | style.setProperty('width', 'sign(.1)'); 845 | assert.strictEqual(style.getPropertyValue('width'), 'calc(1)'); 846 | }); 847 | 848 | it('supports sign inside calc', () => { 849 | const style = new CSSStyleDeclaration(); 850 | style.setProperty('width', 'calc(sign(.1) + sign(.2))'); 851 | assert.strictEqual(style.getPropertyValue('width'), 'calc(2)'); 852 | }); 853 | }); 854 | -------------------------------------------------------------------------------- /test/parsers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { describe, it } = require('node:test'); 4 | const assert = require('node:assert/strict'); 5 | const parsers = require('../lib/parsers'); 6 | 7 | describe('valueType', () => { 8 | it('returns null or empty string for null', () => { 9 | let input = null; 10 | let output = parsers.valueType(input); 11 | 12 | assert.strictEqual(output, parsers.TYPES.NULL_OR_EMPTY_STR); 13 | }); 14 | 15 | it('returns null or empty string for empty string', () => { 16 | let input = ''; 17 | let output = parsers.valueType(input); 18 | 19 | assert.strictEqual(output, parsers.TYPES.NULL_OR_EMPTY_STR); 20 | }); 21 | 22 | it('returns undefined for undefined', () => { 23 | let input = undefined; 24 | let output = parsers.valueType(input); 25 | 26 | assert.strictEqual(output, parsers.TYPES.UNDEFINED); 27 | }); 28 | 29 | it('returns number for 1', () => { 30 | let input = 1; 31 | let output = parsers.valueType(input); 32 | 33 | assert.strictEqual(output, parsers.TYPES.NUMBER); 34 | }); 35 | 36 | it('returns number for 1.1', () => { 37 | let input = 1.1; 38 | let output = parsers.valueType(input); 39 | 40 | assert.strictEqual(output, parsers.TYPES.NUMBER); 41 | }); 42 | 43 | it('returns number for ".1"', () => { 44 | let input = '.1'; 45 | let output = parsers.valueType(input); 46 | 47 | assert.strictEqual(output, parsers.TYPES.NUMBER); 48 | }); 49 | 50 | it('returns length for 100ch', () => { 51 | let input = '100ch'; 52 | let output = parsers.valueType(input); 53 | 54 | assert.strictEqual(output, parsers.TYPES.LENGTH); 55 | }); 56 | 57 | it('returns percent for 10%', () => { 58 | let input = '10%'; 59 | let output = parsers.valueType(input); 60 | 61 | assert.strictEqual(output, parsers.TYPES.PERCENT); 62 | }); 63 | 64 | it('returns unidentified for url(https://example.com)', () => { 65 | let input = 'url(https://example.com)'; 66 | let output = parsers.valueType(input); 67 | 68 | assert.strictEqual(output, parsers.TYPES.UNIDENT); 69 | }); 70 | 71 | it('returns unidentified for url("https://example.com")', () => { 72 | let input = 'url("https://example.com")'; 73 | let output = parsers.valueType(input); 74 | 75 | assert.strictEqual(output, parsers.TYPES.UNIDENT); 76 | }); 77 | 78 | it('returns unidentified for url(foo.png)', () => { 79 | let input = 'url(foo.png)'; 80 | let output = parsers.valueType(input); 81 | 82 | assert.strictEqual(output, parsers.TYPES.UNIDENT); 83 | }); 84 | 85 | it('returns unidentified for url("foo.png")', () => { 86 | let input = 'url("foo.png")'; 87 | let output = parsers.valueType(input); 88 | 89 | assert.strictEqual(output, parsers.TYPES.UNIDENT); 90 | }); 91 | 92 | it('returns unidentified for url(var(--foo))', () => { 93 | let input = 'url(var(--foo))'; 94 | let output = parsers.valueType(input); 95 | 96 | assert.strictEqual(output, parsers.TYPES.UNIDENT); 97 | }); 98 | 99 | it('returns calc from calc(100px * var(--foo))', () => { 100 | let input = 'calc(100px * var(--foo))'; 101 | let output = parsers.valueType(input); 102 | 103 | assert.strictEqual(output, parsers.TYPES.CALC); 104 | }); 105 | 106 | it('returns var from var(--foo)', () => { 107 | let input = 'var(--foo)'; 108 | let output = parsers.valueType(input); 109 | 110 | assert.strictEqual(output, parsers.TYPES.VAR); 111 | }); 112 | 113 | it('returns var from var(--foo, var(--bar))', () => { 114 | let input = 'var(--foo, var(--bar))'; 115 | let output = parsers.valueType(input); 116 | 117 | assert.strictEqual(output, parsers.TYPES.VAR); 118 | }); 119 | 120 | it('returns var from var(--foo, calc(var(--bar) * 2))', () => { 121 | let input = 'var(--foo, calc(var(--bar) * 2))'; 122 | let output = parsers.valueType(input); 123 | 124 | assert.strictEqual(output, parsers.TYPES.VAR); 125 | }); 126 | 127 | it('returns calc from calc(100px * 2)', () => { 128 | let input = 'calc(100px * 2)'; 129 | let output = parsers.valueType(input); 130 | 131 | assert.strictEqual(output, parsers.TYPES.CALC); 132 | }); 133 | 134 | it('returns calc from calc(100px * calc(2 * 1))', () => { 135 | let input = 'calc(100px * calc(2 * 1))'; 136 | let output = parsers.valueType(input); 137 | 138 | assert.strictEqual(output, parsers.TYPES.CALC); 139 | }); 140 | 141 | it('returns string from "foo"', () => { 142 | let input = '"foo"'; 143 | let output = parsers.valueType(input); 144 | 145 | assert.strictEqual(output, parsers.TYPES.STRING); 146 | }); 147 | 148 | it("returns string from 'foo'", () => { 149 | let input = "'foo'"; 150 | let output = parsers.valueType(input); 151 | 152 | assert.strictEqual(output, parsers.TYPES.STRING); 153 | }); 154 | 155 | it('returns angle for 90deg', () => { 156 | let input = '90deg'; 157 | let output = parsers.valueType(input); 158 | 159 | assert.strictEqual(output, parsers.TYPES.ANGLE); 160 | }); 161 | 162 | it('returns color for red', () => { 163 | let input = 'red'; 164 | let output = parsers.valueType(input); 165 | 166 | assert.strictEqual(output, parsers.TYPES.COLOR); 167 | }); 168 | 169 | it('returns color for #nnnnnn', () => { 170 | let input = '#fefefe'; 171 | let output = parsers.valueType(input); 172 | 173 | assert.strictEqual(output, parsers.TYPES.COLOR); 174 | }); 175 | 176 | it('returns color for rgb(n, n, n)', () => { 177 | let input = 'rgb(10, 10, 10)'; 178 | let output = parsers.valueType(input); 179 | 180 | assert.strictEqual(output, parsers.TYPES.COLOR); 181 | }); 182 | 183 | it('returns color for rgb(p, p, p)', () => { 184 | let input = 'rgb(10%, 10%, 10%)'; 185 | let output = parsers.valueType(input); 186 | 187 | assert.strictEqual(output, parsers.TYPES.COLOR); 188 | }); 189 | 190 | it('returns color for rgba(n, n, n, n)', () => { 191 | let input = 'rgba(10, 10, 10, 1)'; 192 | let output = parsers.valueType(input); 193 | 194 | assert.strictEqual(output, parsers.TYPES.COLOR); 195 | }); 196 | 197 | it('returns color for rgba(n, n, n, n) with decimal alpha', () => { 198 | let input = 'rgba(10, 10, 10, 0.5)'; 199 | let output = parsers.valueType(input); 200 | 201 | assert.strictEqual(output, parsers.TYPES.COLOR); 202 | }); 203 | 204 | it('returns color for rgba(p, p, p, n)', () => { 205 | let input = 'rgba(10%, 10%, 10%, 1)'; 206 | let output = parsers.valueType(input); 207 | 208 | assert.strictEqual(output, parsers.TYPES.COLOR); 209 | }); 210 | 211 | it('returns color for rgba(p, p, p, n) with decimal alpha', () => { 212 | let input = 'rgba(10%, 10%, 10%, 0.5)'; 213 | let output = parsers.valueType(input); 214 | 215 | assert.strictEqual(output, parsers.TYPES.COLOR); 216 | }); 217 | 218 | it('returns color for transparent keyword', () => { 219 | let input = 'transparent'; 220 | let output = parsers.valueType(input); 221 | 222 | assert.strictEqual(output, parsers.TYPES.COLOR); 223 | }); 224 | 225 | it('returns unidentified for linear-gradient(red, blue)', () => { 226 | let input = 'linear-gradient(red, blue)'; 227 | let output = parsers.valueType(input); 228 | 229 | assert.strictEqual(output, parsers.TYPES.UNIDENT); 230 | }); 231 | 232 | it('returns color for accentcolor', () => { 233 | let input = 'AccentColor'; 234 | let output = parsers.valueType(input); 235 | 236 | assert.strictEqual(output, parsers.TYPES.COLOR); 237 | }); 238 | 239 | it('returns color for legacy activeborder', () => { 240 | let input = 'ActiveBorder'; 241 | let output = parsers.valueType(input); 242 | 243 | assert.strictEqual(output, parsers.TYPES.COLOR); 244 | }); 245 | 246 | it('returns keyword for foo', () => { 247 | let input = 'foo'; 248 | let output = parsers.valueType(input); 249 | 250 | assert.strictEqual(output, parsers.TYPES.KEYWORD); 251 | }); 252 | 253 | it('returns keyword for foo-bar', () => { 254 | let input = 'foo-bar'; 255 | let output = parsers.valueType(input); 256 | 257 | assert.strictEqual(output, parsers.TYPES.KEYWORD); 258 | }); 259 | 260 | it('returns unidentified for foo(bar)', () => { 261 | let input = 'foo(bar)'; 262 | let output = parsers.valueType(input); 263 | 264 | assert.strictEqual(output, parsers.TYPES.UNIDENT); 265 | }); 266 | }); 267 | 268 | describe('parseNumber', () => { 269 | it('should return null', () => { 270 | let input = null; 271 | let output = parsers.parseNumber(input); 272 | 273 | assert.strictEqual(output, null); 274 | }); 275 | 276 | it('should return empty string', () => { 277 | let input = ''; 278 | let output = parsers.parseNumber(input); 279 | 280 | assert.strictEqual(output, ''); 281 | }); 282 | 283 | it('should return undefined', () => { 284 | let input = 'foo'; 285 | let output = parsers.parseNumber(input); 286 | 287 | assert.strictEqual(output, undefined); 288 | }); 289 | 290 | it('should return undefined', () => { 291 | let input = undefined; 292 | let output = parsers.parseNumber(input); 293 | 294 | assert.strictEqual(output, undefined); 295 | }); 296 | 297 | it('should return "1"', () => { 298 | let input = 1; 299 | let output = parsers.parseNumber(input); 300 | 301 | assert.strictEqual(output, '1'); 302 | }); 303 | 304 | it('should return "1"', () => { 305 | let input = '1'; 306 | let output = parsers.parseNumber(input); 307 | 308 | assert.strictEqual(output, '1'); 309 | }); 310 | 311 | it('should return "0.5"', () => { 312 | let input = 0.5; 313 | let output = parsers.parseNumber(input); 314 | 315 | assert.strictEqual(output, '0.5'); 316 | }); 317 | 318 | it('should return "0.5"', () => { 319 | let input = '0.5'; 320 | let output = parsers.parseNumber(input); 321 | 322 | assert.strictEqual(output, '0.5'); 323 | }); 324 | 325 | it('should return "0.5"', () => { 326 | let input = '.5'; 327 | let output = parsers.parseNumber(input); 328 | 329 | assert.strictEqual(output, '0.5'); 330 | }); 331 | 332 | it('should return calculated value', () => { 333 | let input = 'calc(2 / 3)'; 334 | let output = parsers.parseLength(input); 335 | 336 | assert.strictEqual(output, 'calc(0.666667)'); 337 | }); 338 | }); 339 | 340 | describe('parseLength', () => { 341 | it('should return null', () => { 342 | let input = null; 343 | let output = parsers.parseLength(input); 344 | 345 | assert.strictEqual(output, null); 346 | }); 347 | 348 | it('should return empty string', () => { 349 | let input = ''; 350 | let output = parsers.parseLength(input); 351 | 352 | assert.strictEqual(output, ''); 353 | }); 354 | 355 | it('should return value as is', () => { 356 | let input = 'var(/* comment */ --foo)'; 357 | let output = parsers.parseLength(input); 358 | 359 | assert.strictEqual(output, 'var(/* comment */ --foo)'); 360 | }); 361 | 362 | it('should return calculated value', () => { 363 | let input = 'calc(2em / 3)'; 364 | let output = parsers.parseLength(input); 365 | 366 | assert.strictEqual(output, 'calc(0.666667em)'); 367 | }); 368 | 369 | it('should return serialized value', () => { 370 | let input = 'calc(10px + 20%)'; 371 | let output = parsers.parseLength(input); 372 | 373 | assert.strictEqual(output, 'calc(20% + 10px)'); 374 | }); 375 | 376 | it('should return serialized value', () => { 377 | let input = 'calc(100vh + 10px)'; 378 | let output = parsers.parseLength(input); 379 | 380 | assert.strictEqual(output, 'calc(10px + 100vh)'); 381 | }); 382 | }); 383 | 384 | describe('parsePercent', () => { 385 | it('should return null', () => { 386 | let input = null; 387 | let output = parsers.parsePercent(input); 388 | 389 | assert.strictEqual(output, null); 390 | }); 391 | 392 | it('should return empty string', () => { 393 | let input = ''; 394 | let output = parsers.parsePercent(input); 395 | 396 | assert.strictEqual(output, ''); 397 | }); 398 | 399 | it('should return value as is', () => { 400 | let input = 'var(/* comment */ --foo)'; 401 | let output = parsers.parsePercent(input); 402 | 403 | assert.strictEqual(output, 'var(/* comment */ --foo)'); 404 | }); 405 | 406 | it('should return calculated value', () => { 407 | let input = 'calc(100% / 3)'; 408 | let output = parsers.parsePercent(input); 409 | 410 | assert.strictEqual(output, 'calc(33.3333%)'); 411 | }); 412 | 413 | it('should return serialized value', () => { 414 | let input = 'calc(10px + 20%)'; 415 | let output = parsers.parsePercent(input); 416 | 417 | assert.strictEqual(output, 'calc(20% + 10px)'); 418 | }); 419 | }); 420 | 421 | describe('parseMeasurement', () => { 422 | it('should return null', () => { 423 | let input = null; 424 | let output = parsers.parseMeasurement(input); 425 | 426 | assert.strictEqual(output, null); 427 | }); 428 | 429 | it('should return empty string', () => { 430 | let input = ''; 431 | let output = parsers.parseMeasurement(input); 432 | 433 | assert.strictEqual(output, ''); 434 | }); 435 | 436 | it('should return value with em unit', () => { 437 | let input = '1em'; 438 | let output = parsers.parseMeasurement(input); 439 | 440 | assert.strictEqual(output, '1em'); 441 | }); 442 | 443 | it('should return value with percent', () => { 444 | let input = '100%'; 445 | let output = parsers.parseMeasurement(input); 446 | 447 | assert.strictEqual(output, '100%'); 448 | }); 449 | 450 | it('should return value as is', () => { 451 | let input = 'var(/* comment */ --foo)'; 452 | let output = parsers.parseMeasurement(input); 453 | 454 | assert.strictEqual(output, 'var(/* comment */ --foo)'); 455 | }); 456 | 457 | it('should return calculated value', () => { 458 | let input = 'calc(2em / 3)'; 459 | let output = parsers.parseMeasurement(input); 460 | 461 | assert.strictEqual(output, 'calc(0.666667em)'); 462 | }); 463 | 464 | it('should return calculated value', () => { 465 | let input = 'calc(100% / 3)'; 466 | let output = parsers.parseMeasurement(input); 467 | 468 | assert.strictEqual(output, 'calc(33.3333%)'); 469 | }); 470 | 471 | it('should return serialized value', () => { 472 | let input = 'calc(10px + 20%)'; 473 | let output = parsers.parseMeasurement(input); 474 | 475 | assert.strictEqual(output, 'calc(20% + 10px)'); 476 | }); 477 | 478 | it('should return serialized value', () => { 479 | let input = 'calc(100vh + 10px)'; 480 | let output = parsers.parseMeasurement(input); 481 | 482 | assert.strictEqual(output, 'calc(10px + 100vh)'); 483 | }); 484 | 485 | it('should return 0px for 0', () => { 486 | let input = 0; 487 | let output = parsers.parseMeasurement(input); 488 | 489 | assert.strictEqual(output, '0px'); 490 | }); 491 | 492 | it('should return 0px for "0"', () => { 493 | let input = '0'; 494 | let output = parsers.parseMeasurement(input); 495 | 496 | assert.strictEqual(output, '0px'); 497 | }); 498 | }); 499 | 500 | describe('parseInheritingMeasurement', () => { 501 | it('should return auto', () => { 502 | let input = 'auto'; 503 | let output = parsers.parseInheritingMeasurement(input); 504 | 505 | assert.strictEqual(output, 'auto'); 506 | }); 507 | 508 | it('should return auto', () => { 509 | let input = 'AUTO'; 510 | let output = parsers.parseInheritingMeasurement(input); 511 | 512 | assert.strictEqual(output, 'auto'); 513 | }); 514 | 515 | it('should return inherit', () => { 516 | let input = 'inherit'; 517 | let output = parsers.parseInheritingMeasurement(input); 518 | 519 | assert.strictEqual(output, 'inherit'); 520 | }); 521 | 522 | it('should return inherit', () => { 523 | let input = 'INHERIT'; 524 | let output = parsers.parseInheritingMeasurement(input); 525 | 526 | assert.strictEqual(output, 'inherit'); 527 | }); 528 | 529 | it('should return value with em unit', () => { 530 | let input = '1em'; 531 | let output = parsers.parseInheritingMeasurement(input); 532 | 533 | assert.strictEqual(output, '1em'); 534 | }); 535 | 536 | it('should return value with percent', () => { 537 | let input = '100%'; 538 | let output = parsers.parseInheritingMeasurement(input); 539 | 540 | assert.strictEqual(output, '100%'); 541 | }); 542 | }); 543 | 544 | describe('parseUrl', () => { 545 | it('should return empty string', () => { 546 | let input = null; 547 | let output = parsers.parseUrl(input); 548 | 549 | assert.strictEqual(output, ''); 550 | }); 551 | 552 | it('should return empty string', () => { 553 | let input = ''; 554 | let output = parsers.parseUrl(input); 555 | 556 | assert.strictEqual(output, ''); 557 | }); 558 | 559 | it('should return undefined', () => { 560 | let input = 'url(var(--foo))'; 561 | let output = parsers.parseUrl(input); 562 | 563 | assert.strictEqual(output, undefined); 564 | }); 565 | 566 | it('should return undefined', () => { 567 | let input = undefined; 568 | let output = parsers.parseUrl(input); 569 | 570 | assert.strictEqual(output, undefined); 571 | }); 572 | 573 | it('should return quoted url string', () => { 574 | let input = 'url(sample.png)'; 575 | let output = parsers.parseUrl(input); 576 | 577 | assert.strictEqual(output, 'url("sample.png")'); 578 | }); 579 | 580 | it('should return quoted url string', () => { 581 | let input = "url('sample.png')"; 582 | let output = parsers.parseUrl(input); 583 | 584 | assert.strictEqual(output, 'url("sample.png")'); 585 | }); 586 | 587 | it('should return quoted url string', () => { 588 | let input = 'url("sample.png")'; 589 | let output = parsers.parseUrl(input); 590 | 591 | assert.strictEqual(output, 'url("sample.png")'); 592 | }); 593 | 594 | it('should return quoted url string without escape', () => { 595 | let input = 'url(sample\\-escaped.png)'; 596 | let output = parsers.parseUrl(input); 597 | 598 | assert.strictEqual(output, 'url("sample-escaped.png")'); 599 | }); 600 | 601 | it('should return quoted url string with escape', () => { 602 | let input = 'url(sample\\\\-escaped.png)'; 603 | let output = parsers.parseUrl(input); 604 | 605 | assert.strictEqual(output, 'url("sample\\\\-escaped.png")'); 606 | }); 607 | 608 | it('should return undefined', () => { 609 | let input = 'url(sample unescaped -space.png)'; 610 | let output = parsers.parseUrl(input); 611 | 612 | assert.strictEqual(output, undefined); 613 | }); 614 | 615 | it('should return quoted url string without escape', () => { 616 | let input = 'url(sample\\ escaped\\ -space.png)'; 617 | let output = parsers.parseUrl(input); 618 | 619 | assert.strictEqual(output, 'url("sample escaped -space.png")'); 620 | }); 621 | 622 | it('should return undefined', () => { 623 | let input = 'url(sample\tunescaped\t-tab.png)'; 624 | let output = parsers.parseUrl(input); 625 | 626 | assert.strictEqual(output, undefined); 627 | }); 628 | 629 | it('should return quoted url string without escape', () => { 630 | let input = 'url(sample\\\tescaped\\\t-tab.png)'; 631 | let output = parsers.parseUrl(input); 632 | 633 | assert.strictEqual(output, 'url("sample\tescaped\t-tab.png")'); 634 | }); 635 | 636 | it('should return undefined', () => { 637 | let input = 'url(sample\nunescaped\n-lf.png)'; 638 | let output = parsers.parseUrl(input); 639 | 640 | assert.strictEqual(output, undefined); 641 | }); 642 | 643 | it('should return quoted url string without escape', () => { 644 | let input = 'url(sample\\\nescaped\\\n-lf.png)'; 645 | let output = parsers.parseUrl(input); 646 | 647 | assert.strictEqual(output, 'url("sample\nescaped\n-lf.png")'); 648 | }); 649 | 650 | it('should return undefined', () => { 651 | let input = "url(sample'unescaped'-quote.png)"; 652 | let output = parsers.parseUrl(input); 653 | 654 | assert.strictEqual(output, undefined); 655 | }); 656 | 657 | it('should return quoted url string without escape', () => { 658 | let input = "url(sample\\'escaped\\'-quote.png)"; 659 | let output = parsers.parseUrl(input); 660 | 661 | // prettier-ignore 662 | assert.strictEqual(output, "url(\"sample'escaped'-quote.png\")"); 663 | }); 664 | 665 | it('should return undefined', () => { 666 | let input = 'url(sample"unescaped"-double-quote.png)'; 667 | let output = parsers.parseUrl(input); 668 | 669 | assert.strictEqual(output, undefined); 670 | }); 671 | 672 | it('should return quoted url string with escape', () => { 673 | let input = 'url(sample\\"escaped\\"-double-quote.png)'; 674 | let output = parsers.parseUrl(input); 675 | 676 | assert.strictEqual(output, 'url("sample\\"escaped\\"-double-quote.png")'); 677 | }); 678 | 679 | it('should return quoted empty url string', () => { 680 | let input = 'url()'; 681 | let output = parsers.parseUrl(input); 682 | 683 | assert.strictEqual(output, 'url("")'); 684 | }); 685 | 686 | it('should return quoted empty url string', () => { 687 | let input = 'url("")'; 688 | let output = parsers.parseUrl(input); 689 | 690 | assert.strictEqual(output, 'url("")'); 691 | }); 692 | }); 693 | 694 | describe('parseString', () => { 695 | it.todo('test'); 696 | }); 697 | 698 | describe('parseColor', () => { 699 | it('should return empty string', () => { 700 | let input = null; 701 | let output = parsers.parseColor(input); 702 | 703 | assert.strictEqual(output, ''); 704 | }); 705 | 706 | it('should return empty string', () => { 707 | let input = ''; 708 | let output = parsers.parseColor(input); 709 | 710 | assert.strictEqual(output, ''); 711 | }); 712 | 713 | it('should return undefined', () => { 714 | let input = undefined; 715 | let output = parsers.parseColor(input); 716 | 717 | assert.strictEqual(output, undefined); 718 | }); 719 | 720 | it('should return inherit', () => { 721 | let input = 'inherit'; 722 | let output = parsers.parseColor(input); 723 | 724 | assert.strictEqual(output, 'inherit'); 725 | }); 726 | 727 | it('should convert hsl to rgb values', () => { 728 | let input = 'hsla(0, 1%, 2%)'; 729 | let output = parsers.parseColor(input); 730 | 731 | assert.strictEqual(output, 'rgb(5, 5, 5)'); 732 | }); 733 | 734 | it('should convert hsla to rgba values', () => { 735 | let input = 'hsla(0, 1%, 2%, 0.5)'; 736 | let output = parsers.parseColor(input); 737 | 738 | assert.strictEqual(output, 'rgba(5, 5, 5, 0.5)'); 739 | }); 740 | 741 | it('should convert not zero hsl with non zero hue 120 to rgb(0, 255, 0)', () => { 742 | let input = 'hsl(120, 100%, 50%)'; 743 | let output = parsers.parseColor(input); 744 | 745 | assert.strictEqual(output, 'rgb(0, 255, 0)'); 746 | }); 747 | 748 | it('should convert not zero hsl with non zero hue 240 to rgb(0, 0, 255)', () => { 749 | let input = 'hsl(240, 100%, 50%)'; 750 | let output = parsers.parseColor(input); 751 | 752 | assert.strictEqual(output, 'rgb(0, 0, 255)'); 753 | }); 754 | 755 | it('should convert modern rgb to rgb values', () => { 756 | let input = 'rgb(128 0 128 / 1)'; 757 | let output = parsers.parseColor(input); 758 | 759 | assert.strictEqual(output, 'rgb(128, 0, 128)'); 760 | }); 761 | 762 | it('should convert modern rgb with none values to rgb values', () => { 763 | let input = 'rgb(128 0 none)'; 764 | let output = parsers.parseColor(input); 765 | 766 | assert.strictEqual(output, 'rgb(128, 0, 0)'); 767 | }); 768 | 769 | it('should convert modern rgba to rgba values', () => { 770 | let input = 'rgba(127.5 0 127.5 / .5)'; 771 | let output = parsers.parseColor(input); 772 | 773 | assert.strictEqual(output, 'rgba(128, 0, 128, 0.5)'); 774 | }); 775 | 776 | it('should normalize lab values', () => { 777 | let input = 'lab(46.2775% -47.5621 48.5837 / 1.0)'; // green 778 | let output = parsers.parseColor(input); 779 | 780 | assert.strictEqual(output, 'lab(46.2775 -47.5621 48.5837)'); 781 | }); 782 | 783 | it('should normalize color function values', () => { 784 | let input = 'color(srgb 0 .5 0 / 1.0)'; 785 | let output = parsers.parseColor(input); 786 | 787 | assert.strictEqual(output, 'color(srgb 0 0.5 0)'); 788 | }); 789 | 790 | it('should normalize color-mix values', () => { 791 | let input = 'color-mix(in srgb, rgb(255 0 0), #0000ff 40%)'; 792 | let output = parsers.parseColor(input); 793 | 794 | assert.strictEqual(output, 'color-mix(in srgb, rgb(255, 0, 0) 60%, rgb(0, 0, 255))'); 795 | }); 796 | 797 | it('should not remove comments, trim or lower case letters if var() is used', () => { 798 | let input = 'var( --custom-Color /* comment */)'; 799 | let output = parsers.parseColor(input); 800 | 801 | assert.strictEqual(output, 'var( --custom-Color /* comment */)'); 802 | }); 803 | 804 | it('should output transparent keyword', () => { 805 | let input = 'transparent'; 806 | let output = parsers.parseColor(input); 807 | 808 | assert.strictEqual(output, 'transparent'); 809 | }); 810 | 811 | it('should return value as is with var()', () => { 812 | let input = 'rgb(var(--my-var, 0, 0, 0))'; 813 | let output = parsers.parseColor(input); 814 | 815 | assert.strictEqual(output, 'rgb(var(--my-var, 0, 0, 0))'); 816 | }); 817 | }); 818 | 819 | describe('parseAngle', () => { 820 | it.todo('test'); 821 | }); 822 | 823 | describe('parseKeyword', () => { 824 | it('should return value', () => { 825 | let input = 'inherit'; 826 | let output = parsers.parseKeyword(input); 827 | 828 | assert.strictEqual(output, 'inherit'); 829 | }); 830 | 831 | it('should return value', () => { 832 | let input = 'foo'; 833 | let output = parsers.parseKeyword(input, ['foo', 'bar']); 834 | 835 | assert.strictEqual(output, 'foo'); 836 | }); 837 | 838 | it('should return value', () => { 839 | let input = 'Bar'; 840 | let output = parsers.parseKeyword(input, ['foo', 'bar']); 841 | 842 | assert.strictEqual(output, 'bar'); 843 | }); 844 | 845 | it('should return undefined', () => { 846 | let input = 'baz'; 847 | let output = parsers.parseKeyword(input, ['foo', 'bar']); 848 | 849 | assert.strictEqual(output, undefined); 850 | }); 851 | }); 852 | 853 | describe('parseImage', () => { 854 | it('should return empty string', () => { 855 | let input = ''; 856 | let output = parsers.parseImage(input); 857 | 858 | assert.strictEqual(output, ''); 859 | }); 860 | 861 | it('should return empty string', () => { 862 | let input = null; 863 | let output = parsers.parseImage(input); 864 | 865 | assert.strictEqual(output, ''); 866 | }); 867 | 868 | it('should return undefined', () => { 869 | let input = 'foo'; 870 | let output = parsers.parseImage(input); 871 | 872 | assert.strictEqual(output, undefined); 873 | }); 874 | 875 | it('should return none', () => { 876 | let input = 'none'; 877 | let output = parsers.parseImage(input); 878 | 879 | assert.strictEqual(output, 'none'); 880 | }); 881 | 882 | it('should return inherit', () => { 883 | let input = 'inherit'; 884 | let output = parsers.parseImage(input); 885 | 886 | assert.strictEqual(output, 'inherit'); 887 | }); 888 | 889 | it('should return undefined for negative radii', () => { 890 | let input = 'radial-gradient(circle -10px at center, red, blue)'; 891 | let output = parsers.parseImage(input); 892 | 893 | assert.strictEqual(output, undefined); 894 | }); 895 | 896 | it('should return value', () => { 897 | let input = 'url(example.png)'; 898 | let output = parsers.parseImage(input); 899 | 900 | assert.strictEqual(output, 'url("example.png")'); 901 | }); 902 | 903 | it('should return value', () => { 904 | let input = 'url(example.png), url("example2.png")'; 905 | let output = parsers.parseImage(input); 906 | 907 | assert.strictEqual(output, 'url("example.png"), url("example2.png")'); 908 | }); 909 | 910 | it('should return value', () => { 911 | let input = 'none, url(example.png)'; 912 | let output = parsers.parseImage(input); 913 | 914 | assert.strictEqual(output, 'none, url("example.png")'); 915 | }); 916 | 917 | it('should return value', () => { 918 | let input = 'linear-gradient(green, blue), url(example.png)'; 919 | let output = parsers.parseImage(input); 920 | 921 | assert.strictEqual(output, 'linear-gradient(green, blue), url("example.png")'); 922 | }); 923 | 924 | it('should return value as is if var() is included', () => { 925 | let input = 'radial-gradient(transparent, /* comment */ var(--custom-color)), url(example.png)'; 926 | let output = parsers.parseImage(input); 927 | 928 | assert.strictEqual( 929 | output, 930 | 'radial-gradient(transparent, /* comment */ var(--custom-color)), url("example.png")' 931 | ); 932 | }); 933 | 934 | it('should return undefined if invalid image type is included', () => { 935 | let input = 'radial-gradient(transparent, var(--custom-color)), red'; 936 | let output = parsers.parseImage(input); 937 | 938 | assert.strictEqual(output, undefined); 939 | }); 940 | 941 | it('should return undefined if value is not image type', () => { 942 | let input = 'rgb(var(--my-var, 0, 0, 0))'; 943 | let output = parsers.parseImage(input); 944 | 945 | assert.strictEqual(output, undefined); 946 | }); 947 | }); 948 | 949 | describe('dashedToCamelCase', () => { 950 | it('should not camelize custom property', () => { 951 | let input = '--foo-bar-baz'; 952 | let output = parsers.dashedToCamelCase(input); 953 | 954 | assert.strictEqual(output, '--foo-bar-baz'); 955 | }); 956 | 957 | it('should camelize value', () => { 958 | let input = 'foo-bar-baz'; 959 | let output = parsers.dashedToCamelCase(input); 960 | 961 | assert.strictEqual(output, 'fooBarBaz'); 962 | }); 963 | 964 | it('should camelize vendor prefixed value', () => { 965 | let input = '-webkit-foo'; 966 | let output = parsers.dashedToCamelCase(input); 967 | 968 | assert.strictEqual(output, 'webkitFoo'); 969 | }); 970 | 971 | it('should not camelize snake cased value', () => { 972 | let input = 'foo_bar_baz'; 973 | let output = parsers.dashedToCamelCase(input); 974 | 975 | assert.strictEqual(output, 'foo_bar_baz'); 976 | }); 977 | }); 978 | 979 | describe('shorthandParser', () => { 980 | const flexGrow = require('../lib/properties/flexGrow'); 981 | const flexShrink = require('../lib/properties/flexShrink'); 982 | const flexBasis = require('../lib/properties/flexBasis'); 983 | const shorthandFor = { 984 | 'flex-grow': flexGrow, 985 | 'flex-shrink': flexShrink, 986 | 'flex-basis': flexBasis, 987 | }; 988 | 989 | it('should return undefined for keyword', () => { 990 | let input = 'none'; 991 | let output = parsers.shorthandParser(input, shorthandFor); 992 | 993 | assert.strictEqual(output, undefined); 994 | }); 995 | 996 | it('should return object', () => { 997 | let input = '0 0 auto'; 998 | let output = parsers.shorthandParser(input, shorthandFor); 999 | 1000 | assert.deepEqual(output, { 1001 | 'flex-grow': '0', 1002 | 'flex-shrink': '0', 1003 | 'flex-basis': 'auto', 1004 | }); 1005 | }); 1006 | 1007 | it('should return object', () => { 1008 | let input = '0 1 auto'; 1009 | let output = parsers.shorthandParser(input, shorthandFor); 1010 | 1011 | assert.deepEqual(output, { 1012 | 'flex-grow': '0', 1013 | 'flex-shrink': '1', 1014 | 'flex-basis': 'auto', 1015 | }); 1016 | }); 1017 | 1018 | it('should return object', () => { 1019 | let input = '2'; 1020 | let output = parsers.shorthandParser(input, shorthandFor); 1021 | 1022 | assert.deepEqual(output, { 1023 | 'flex-grow': '2', 1024 | }); 1025 | }); 1026 | 1027 | it('should return object', () => { 1028 | let input = '2 1'; 1029 | let output = parsers.shorthandParser(input, shorthandFor); 1030 | 1031 | assert.deepEqual(output, { 1032 | 'flex-grow': '2', 1033 | 'flex-shrink': '1', 1034 | }); 1035 | }); 1036 | 1037 | it('should return object', () => { 1038 | let input = '10px'; 1039 | let output = parsers.shorthandParser(input, shorthandFor); 1040 | 1041 | assert.deepEqual(output, { 1042 | 'flex-basis': '10px', 1043 | }); 1044 | }); 1045 | 1046 | it('should return object', () => { 1047 | let input = '2 10px'; 1048 | let output = parsers.shorthandParser(input, shorthandFor); 1049 | 1050 | assert.deepEqual(output, { 1051 | 'flex-grow': '2', 1052 | 'flex-basis': '10px', 1053 | }); 1054 | }); 1055 | 1056 | // FIXME: 1057 | it.skip('should return undefined', () => { 1058 | let input = '2 10px 20px'; 1059 | let output = parsers.shorthandParser(input, shorthandFor); 1060 | 1061 | assert.deepEqual(output, undefined); 1062 | }); 1063 | 1064 | it.todo('test'); 1065 | }); 1066 | 1067 | describe('shorthandSetter', () => { 1068 | it.todo('test'); 1069 | }); 1070 | describe('shorthandGetter', () => { 1071 | it.todo('test'); 1072 | }); 1073 | describe('implicitSetter', () => { 1074 | it.todo('test'); 1075 | }); 1076 | describe('subImplicitSetter', () => { 1077 | it.todo('test'); 1078 | }); 1079 | 1080 | describe('camelToDashed', () => { 1081 | it('should return dashed value', () => { 1082 | let input = 'fooBarBaz'; 1083 | let output = parsers.camelToDashed(input); 1084 | 1085 | assert.strictEqual(output, 'foo-bar-baz'); 1086 | }); 1087 | 1088 | it('should return dashed value', () => { 1089 | let input = 'FooBarBaz'; 1090 | let output = parsers.camelToDashed(input); 1091 | 1092 | assert.strictEqual(output, 'foo-bar-baz'); 1093 | }); 1094 | 1095 | it('should return dashed value', () => { 1096 | let input = 'webkitFooBar'; 1097 | let output = parsers.camelToDashed(input); 1098 | 1099 | assert.strictEqual(output, '-webkit-foo-bar'); 1100 | }); 1101 | 1102 | it('should return dashed value', () => { 1103 | let input = 'WebkitFooBar'; 1104 | let output = parsers.camelToDashed(input); 1105 | 1106 | assert.strictEqual(output, '-webkit-foo-bar'); 1107 | }); 1108 | }); 1109 | --------------------------------------------------------------------------------