/**
 * @fileoverview Based on https://www.w3.org/TR/SVG11/intro.html#Definitions.
 */

/**
 * @type {Readonly<Record<string, Set<string>>>}
 */
export const elemsGroups = {
  animation: new Set([
    'animate',
    'animateColor',
    'animateMotion',
    'animateTransform',
    'set',
  ]),
  descriptive: new Set(['desc', 'metadata', 'title']),
  shape: new Set([
    'circle',
    'ellipse',
    'line',
    'path',
    'polygon',
    'polyline',
    'rect',
  ]),
  structural: new Set(['defs', 'g', 'svg', 'symbol', 'use']),
  paintServer: new Set([
    'hatch',
    'linearGradient',
    'meshGradient',
    'pattern',
    'radialGradient',
    'solidColor',
  ]),
  nonRendering: new Set([
    'clipPath',
    'filter',
    'linearGradient',
    'marker',
    'mask',
    'pattern',
    'radialGradient',
    'solidColor',
    'symbol',
  ]),
  container: new Set([
    'a',
    'defs',
    'foreignObject',
    'g',
    'marker',
    'mask',
    'missing-glyph',
    'pattern',
    'svg',
    'switch',
    'symbol',
  ]),
  textContent: new Set([
    'a',
    'altGlyph',
    'altGlyphDef',
    'altGlyphItem',
    'glyph',
    'glyphRef',
    'text',
    'textPath',
    'tref',
    'tspan',
  ]),
  textContentChild: new Set(['altGlyph', 'textPath', 'tref', 'tspan']),
  lightSource: new Set([
    'feDiffuseLighting',
    'feDistantLight',
    'fePointLight',
    'feSpecularLighting',
    'feSpotLight',
  ]),
  filterPrimitive: new Set([
    'feBlend',
    'feColorMatrix',
    'feComponentTransfer',
    'feComposite',
    'feConvolveMatrix',
    'feDiffuseLighting',
    'feDisplacementMap',
    'feDropShadow',
    'feFlood',
    'feFuncA',
    'feFuncB',
    'feFuncG',
    'feFuncR',
    'feGaussianBlur',
    'feImage',
    'feMerge',
    'feMergeNode',
    'feMorphology',
    'feOffset',
    'feSpecularLighting',
    'feTile',
    'feTurbulence',
  ]),
};

/**
 * Elements where adding or removing whitespace may affect rendering, metadata,
 * or semantic meaning.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre
 * @type {Readonly<Set<string>>}
 */
export const textElems = new Set([...elemsGroups.textContent, 'pre', 'title']);

/**
 * @type {Readonly<Set<string>>}
 */
export const pathElems = new Set(['glyph', 'missing-glyph', 'path']);

/**
 * @type {Readonly<Record<string, Set<string>>>}
 * @see https://www.w3.org/TR/SVG11/intro.html#Definitions
 */
export const attrsGroups = {
  animationAddition: new Set(['additive', 'accumulate']),
  animationAttributeTarget: new Set(['attributeType', 'attributeName']),
  animationEvent: new Set(['onbegin', 'onend', 'onrepeat', 'onload']),
  animationTiming: new Set([
    'begin',
    'dur',
    'end',
    'fill',
    'max',
    'min',
    'repeatCount',
    'repeatDur',
    'restart',
  ]),
  animationValue: new Set([
    'by',
    'calcMode',
    'from',
    'keySplines',
    'keyTimes',
    'to',
    'values',
  ]),
  conditionalProcessing: new Set([
    'requiredExtensions',
    'requiredFeatures',
    'systemLanguage',
  ]),
  core: new Set(['id', 'tabindex', 'xml:base', 'xml:lang', 'xml:space']),
  graphicalEvent: new Set([
    'onactivate',
    'onclick',
    'onfocusin',
    'onfocusout',
    'onload',
    'onmousedown',
    'onmousemove',
    'onmouseout',
    'onmouseover',
    'onmouseup',
  ]),
  presentation: new Set([
    'alignment-baseline',
    'baseline-shift',
    'clip-path',
    'clip-rule',
    'clip',
    'color-interpolation-filters',
    'color-interpolation',
    'color-profile',
    'color-rendering',
    'color',
    'cursor',
    'direction',
    'display',
    'dominant-baseline',
    'enable-background',
    'fill-opacity',
    'fill-rule',
    'fill',
    'filter',
    'flood-color',
    'flood-opacity',
    'font-family',
    'font-size-adjust',
    'font-size',
    'font-stretch',
    'font-style',
    'font-variant',
    'font-weight',
    'glyph-orientation-horizontal',
    'glyph-orientation-vertical',
    'image-rendering',
    'letter-spacing',
    'lighting-color',
    'marker-end',
    'marker-mid',
    'marker-start',
    'mask',
    'opacity',
    'overflow',
    'paint-order',
    'pointer-events',
    'shape-rendering',
    'stop-color',
    'stop-opacity',
    'stroke-dasharray',
    'stroke-dashoffset',
    'stroke-linecap',
    'stroke-linejoin',
    'stroke-miterlimit',
    'stroke-opacity',
    'stroke-width',
    'stroke',
    'text-anchor',
    'text-decoration',
    'text-overflow',
    'text-rendering',
    'transform-origin',
    'transform',
    'unicode-bidi',
    'vector-effect',
    'visibility',
    'word-spacing',
    'writing-mode',
  ]),
  xlink: new Set([
    'xlink:actuate',
    'xlink:arcrole',
    'xlink:href',
    'xlink:role',
    'xlink:show',
    'xlink:title',
    'xlink:type',
  ]),
  documentEvent: new Set([
    'onabort',
    'onerror',
    'onresize',
    'onscroll',
    'onunload',
    'onzoom',
  ]),
  documentElementEvent: new Set(['oncopy', 'oncut', 'onpaste']),
  globalEvent: new Set([
    'oncancel',
    'oncanplay',
    'oncanplaythrough',
    'onchange',
    'onclick',
    'onclose',
    'oncuechange',
    'ondblclick',
    'ondrag',
    'ondragend',
    'ondragenter',
    'ondragleave',
    'ondragover',
    'ondragstart',
    'ondrop',
    'ondurationchange',
    'onemptied',
    'onended',
    'onerror',
    'onfocus',
    'oninput',
    'oninvalid',
    'onkeydown',
    'onkeypress',
    'onkeyup',
    'onload',
    'onloadeddata',
    'onloadedmetadata',
    'onloadstart',
    'onmousedown',
    'onmouseenter',
    'onmouseleave',
    'onmousemove',
    'onmouseout',
    'onmouseover',
    'onmouseup',
    'onmousewheel',
    'onpause',
    'onplay',
    'onplaying',
    'onprogress',
    'onratechange',
    'onreset',
    'onresize',
    'onscroll',
    'onseeked',
    'onseeking',
    'onselect',
    'onshow',
    'onstalled',
    'onsubmit',
    'onsuspend',
    'ontimeupdate',
    'ontoggle',
    'onvolumechange',
    'onwaiting',
  ]),
  filterPrimitive: new Set(['x', 'y', 'width', 'height', 'result']),
  transferFunction: new Set([
    'amplitude',
    'exponent',
    'intercept',
    'offset',
    'slope',
    'tableValues',
    'type',
  ]),
};

/**
 * @type {Readonly<Record<string, Record<string, string>>>}
 */
export const attrsGroupsDefaults = {
  core: { 'xml:space': 'default' },
  presentation: {
    clip: 'auto',
    'clip-path': 'none',
    'clip-rule': 'nonzero',
    mask: 'none',
    opacity: '1',
    'stop-color': '#000',
    'stop-opacity': '1',
    'fill-opacity': '1',
    'fill-rule': 'nonzero',
    fill: '#000',
    stroke: 'none',
    'stroke-width': '1',
    'stroke-linecap': 'butt',
    'stroke-linejoin': 'miter',
    'stroke-miterlimit': '4',
    'stroke-dasharray': 'none',
    'stroke-dashoffset': '0',
    'stroke-opacity': '1',
    'paint-order': 'normal',
    'vector-effect': 'none',
    display: 'inline',
    visibility: 'visible',
    'marker-start': 'none',
    'marker-mid': 'none',
    'marker-end': 'none',
    'color-interpolation': 'sRGB',
    'color-interpolation-filters': 'linearRGB',
    'color-rendering': 'auto',
    'shape-rendering': 'auto',
    'text-rendering': 'auto',
    'image-rendering': 'auto',
    'font-style': 'normal',
    'font-variant': 'normal',
    'font-weight': 'normal',
    'font-stretch': 'normal',
    'font-size': 'medium',
    'font-size-adjust': 'none',
    kerning: 'auto',
    'letter-spacing': 'normal',
    'word-spacing': 'normal',
    'text-decoration': 'none',
    'text-anchor': 'start',
    'text-overflow': 'clip',
    'writing-mode': 'lr-tb',
    'glyph-orientation-vertical': 'auto',
    'glyph-orientation-horizontal': '0deg',
    direction: 'ltr',
    'unicode-bidi': 'normal',
    'dominant-baseline': 'auto',
    'alignment-baseline': 'baseline',
    'baseline-shift': 'baseline',
  },
  transferFunction: {
    slope: '1',
    intercept: '0',
    amplitude: '1',
    exponent: '1',
    offset: '0',
  },
};

/**
 * @type {Readonly<Record<string, { safe?: Set<string>; unsafe?: Set<string> }>>}
 * @see https://www.w3.org/TR/SVG11/intro.html#Definitions
 */
export const attrsGroupsDeprecated = {
  animationAttributeTarget: { unsafe: new Set(['attributeType']) },
  conditionalProcessing: { unsafe: new Set(['requiredFeatures']) },
  core: { unsafe: new Set(['xml:base', 'xml:lang', 'xml:space']) },
  presentation: {
    unsafe: new Set([
      'clip',
      'color-profile',
      'enable-background',
      'glyph-orientation-horizontal',
      'glyph-orientation-vertical',
      'kerning',
    ]),
  },
};

/**
 * @type {Readonly<Record<string, {
 *   attrsGroups: Set<string>,
 *   attrs?: Set<string>,
 *   defaults?: Record<string, string>,
 *   deprecated?: {
 *     safe?: Set<string>,
 *     unsafe?: Set<string>,
 *   },
 *   contentGroups?: Set<string>,
 *   content?: Set<string>,
 * }>>}
 * @see https://www.w3.org/TR/SVG11/eltindex.html
 */
export const elems = {
  a: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
      'xlink',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'style',
      'target',
      'transform',
    ]),
    defaults: {
      target: '_self',
    },
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
      // not spec compliant
      'tspan',
    ]),
  },
  altGlyph: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
      'xlink',
    ]),
    attrs: new Set([
      'class',
      'dx',
      'dy',
      'externalResourcesRequired',
      'format',
      'glyphRef',
      'rotate',
      'style',
      'x',
      'y',
    ]),
  },
  altGlyphDef: {
    attrsGroups: new Set(['core']),
    content: new Set(['glyphRef']),
  },
  altGlyphItem: {
    attrsGroups: new Set(['core']),
    content: new Set(['glyphRef', 'altGlyphItem']),
  },
  animate: {
    attrsGroups: new Set([
      'animationAddition',
      'animationAttributeTarget',
      'animationEvent',
      'animationTiming',
      'animationValue',
      'conditionalProcessing',
      'core',
      'presentation',
      'xlink',
    ]),
    attrs: new Set(['externalResourcesRequired']),
    contentGroups: new Set(['descriptive']),
  },
  animateColor: {
    attrsGroups: new Set([
      'animationAddition',
      'animationAttributeTarget',
      'animationEvent',
      'animationTiming',
      'animationValue',
      'conditionalProcessing',
      'core',
      'presentation',
      'xlink',
    ]),
    attrs: new Set(['externalResourcesRequired']),
    contentGroups: new Set(['descriptive']),
  },
  animateMotion: {
    attrsGroups: new Set([
      'animationAddition',
      'animationEvent',
      'animationTiming',
      'animationValue',
      'conditionalProcessing',
      'core',
      'xlink',
    ]),
    attrs: new Set([
      'externalResourcesRequired',
      'keyPoints',
      'origin',
      'path',
      'rotate',
    ]),
    defaults: {
      rotate: '0',
    },
    contentGroups: new Set(['descriptive']),
    content: new Set(['mpath']),
  },
  animateTransform: {
    attrsGroups: new Set([
      'animationAddition',
      'animationAttributeTarget',
      'animationEvent',
      'animationTiming',
      'animationValue',
      'conditionalProcessing',
      'core',
      'xlink',
    ]),
    attrs: new Set(['externalResourcesRequired', 'type']),
    contentGroups: new Set(['descriptive']),
  },
  circle: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'cx',
      'cy',
      'externalResourcesRequired',
      'r',
      'style',
      'transform',
    ]),
    defaults: {
      cx: '0',
      cy: '0',
    },
    contentGroups: new Set(['animation', 'descriptive']),
  },
  clipPath: {
    attrsGroups: new Set(['conditionalProcessing', 'core', 'presentation']),
    attrs: new Set([
      'class',
      'clipPathUnits',
      'externalResourcesRequired',
      'style',
      'transform',
    ]),
    defaults: {
      clipPathUnits: 'userSpaceOnUse',
    },
    contentGroups: new Set(['animation', 'descriptive', 'shape']),
    content: new Set(['text', 'use']),
  },
  'color-profile': {
    attrsGroups: new Set(['core', 'xlink']),
    attrs: new Set(['local', 'name', 'rendering-intent']),
    defaults: {
      name: 'sRGB',
      'rendering-intent': 'auto',
    },
    deprecated: { unsafe: new Set(['name']) },
    contentGroups: new Set(['descriptive']),
  },
  cursor: {
    attrsGroups: new Set(['core', 'conditionalProcessing', 'xlink']),
    attrs: new Set(['externalResourcesRequired', 'x', 'y']),
    defaults: {
      x: '0',
      y: '0',
    },
    contentGroups: new Set(['descriptive']),
  },
  defs: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'style',
      'transform',
    ]),
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  desc: {
    attrsGroups: new Set(['core']),
    attrs: new Set(['class', 'style']),
  },
  ellipse: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'cx',
      'cy',
      'externalResourcesRequired',
      'rx',
      'ry',
      'style',
      'transform',
    ]),
    defaults: {
      cx: '0',
      cy: '0',
    },
    contentGroups: new Set(['animation', 'descriptive']),
  },
  feBlend: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set([
      'class',
      'style',
      // TODO: in - 'If no value is provided and this is the first filter primitive,
      // then this filter primitive will use SourceGraphic as its input'
      'in',
      'in2',
      'mode',
    ]),
    defaults: {
      mode: 'normal',
    },
    content: new Set(['animate', 'set']),
  },
  feColorMatrix: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set(['class', 'style', 'in', 'type', 'values']),
    defaults: {
      type: 'matrix',
    },
    content: new Set(['animate', 'set']),
  },
  feComponentTransfer: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set(['class', 'style', 'in']),
    content: new Set(['feFuncA', 'feFuncB', 'feFuncG', 'feFuncR']),
  },
  feComposite: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set([
      'class',
      'in',
      'in2',
      'k1',
      'k2',
      'k3',
      'k4',
      'operator',
      'style',
    ]),
    defaults: {
      operator: 'over',
      k1: '0',
      k2: '0',
      k3: '0',
      k4: '0',
    },
    content: new Set(['animate', 'set']),
  },
  feConvolveMatrix: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set([
      'class',
      'in',
      'kernelMatrix',
      'order',
      'style',
      // TODO: divisor - 'The default value is the sum of all values in kernelMatrix,
      // with the exception that if the sum is zero, then the divisor is set to 1'
      'bias',
      'divisor',
      // TODO: targetX - 'By default, the convolution matrix is centered in X over each
      // pixel of the input image (i.e., targetX = floor ( orderX / 2 ))'
      'edgeMode',
      'targetX',
      'targetY',
      // TODO: kernelUnitLength - 'The first number is the <dx> value. The second number
      // is the <dy> value. If the <dy> value is not specified, it defaults to the same value as <dx>'
      'kernelUnitLength',
      'preserveAlpha',
    ]),
    defaults: {
      order: '3',
      bias: '0',
      edgeMode: 'duplicate',
      preserveAlpha: 'false',
    },
    content: new Set(['animate', 'set']),
  },
  feDiffuseLighting: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set([
      'class',
      'diffuseConstant',
      'in',
      'kernelUnitLength',
      'style',
      'surfaceScale',
    ]),
    defaults: {
      surfaceScale: '1',
      diffuseConstant: '1',
    },
    contentGroups: new Set(['descriptive']),
    content: new Set([
      // TODO: 'exactly one light source element, in any order'
      'feDistantLight',
      'fePointLight',
      'feSpotLight',
    ]),
  },
  feDisplacementMap: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set([
      'class',
      'in',
      'in2',
      'scale',
      'style',
      'xChannelSelector',
      'yChannelSelector',
    ]),
    defaults: {
      scale: '0',
      xChannelSelector: 'A',
      yChannelSelector: 'A',
    },
    content: new Set(['animate', 'set']),
  },
  feDistantLight: {
    attrsGroups: new Set(['core']),
    attrs: new Set(['azimuth', 'elevation']),
    defaults: {
      azimuth: '0',
      elevation: '0',
    },
    content: new Set(['animate', 'set']),
  },
  feFlood: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set(['class', 'style']),
    content: new Set(['animate', 'animateColor', 'set']),
  },
  feFuncA: {
    attrsGroups: new Set(['core', 'transferFunction']),
    content: new Set(['set', 'animate']),
  },
  feFuncB: {
    attrsGroups: new Set(['core', 'transferFunction']),
    content: new Set(['set', 'animate']),
  },
  feFuncG: {
    attrsGroups: new Set(['core', 'transferFunction']),
    content: new Set(['set', 'animate']),
  },
  feFuncR: {
    attrsGroups: new Set(['core', 'transferFunction']),
    content: new Set(['set', 'animate']),
  },
  feGaussianBlur: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set(['class', 'style', 'in', 'stdDeviation']),
    defaults: {
      stdDeviation: '0',
    },
    content: new Set(['set', 'animate']),
  },
  feImage: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive', 'xlink']),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'href',
      'preserveAspectRatio',
      'style',
      'xlink:href',
    ]),
    defaults: {
      preserveAspectRatio: 'xMidYMid meet',
    },
    content: new Set(['animate', 'animateTransform', 'set']),
  },
  feMerge: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set(['class', 'style']),
    content: new Set(['feMergeNode']),
  },
  feMergeNode: {
    attrsGroups: new Set(['core']),
    attrs: new Set(['in']),
    content: new Set(['animate', 'set']),
  },
  feMorphology: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set(['class', 'style', 'in', 'operator', 'radius']),
    defaults: {
      operator: 'erode',
      radius: '0',
    },
    content: new Set(['animate', 'set']),
  },
  feOffset: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set(['class', 'style', 'in', 'dx', 'dy']),
    defaults: {
      dx: '0',
      dy: '0',
    },
    content: new Set(['animate', 'set']),
  },
  fePointLight: {
    attrsGroups: new Set(['core']),
    attrs: new Set(['x', 'y', 'z']),
    defaults: {
      x: '0',
      y: '0',
      z: '0',
    },
    content: new Set(['animate', 'set']),
  },
  feSpecularLighting: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set([
      'class',
      'in',
      'kernelUnitLength',
      'specularConstant',
      'specularExponent',
      'style',
      'surfaceScale',
    ]),
    defaults: {
      surfaceScale: '1',
      specularConstant: '1',
      specularExponent: '1',
    },
    contentGroups: new Set([
      'descriptive',
      // TODO: exactly one 'light source element'
      'lightSource',
    ]),
  },
  feSpotLight: {
    attrsGroups: new Set(['core']),
    attrs: new Set([
      'limitingConeAngle',
      'pointsAtX',
      'pointsAtY',
      'pointsAtZ',
      'specularExponent',
      'x',
      'y',
      'z',
    ]),
    defaults: {
      x: '0',
      y: '0',
      z: '0',
      pointsAtX: '0',
      pointsAtY: '0',
      pointsAtZ: '0',
      specularExponent: '1',
    },
    content: new Set(['animate', 'set']),
  },
  feTile: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set(['class', 'style', 'in']),
    content: new Set(['animate', 'set']),
  },
  feTurbulence: {
    attrsGroups: new Set(['core', 'presentation', 'filterPrimitive']),
    attrs: new Set([
      'baseFrequency',
      'class',
      'numOctaves',
      'seed',
      'stitchTiles',
      'style',
      'type',
    ]),
    defaults: {
      baseFrequency: '0',
      numOctaves: '1',
      seed: '0',
      stitchTiles: 'noStitch',
      type: 'turbulence',
    },
    content: new Set(['animate', 'set']),
  },
  filter: {
    attrsGroups: new Set(['core', 'presentation', 'xlink']),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'filterRes',
      'filterUnits',
      'height',
      'href',
      'primitiveUnits',
      'style',
      'width',
      'x',
      'xlink:href',
      'y',
    ]),
    defaults: {
      primitiveUnits: 'userSpaceOnUse',
      x: '-10%',
      y: '-10%',
      width: '120%',
      height: '120%',
    },
    deprecated: { unsafe: new Set(['filterRes']) },
    contentGroups: new Set(['descriptive', 'filterPrimitive']),
    content: new Set(['animate', 'set']),
  },
  font: {
    attrsGroups: new Set(['core', 'presentation']),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'horiz-adv-x',
      'horiz-origin-x',
      'horiz-origin-y',
      'style',
      'vert-adv-y',
      'vert-origin-x',
      'vert-origin-y',
    ]),
    defaults: {
      'horiz-origin-x': '0',
      'horiz-origin-y': '0',
    },
    deprecated: {
      unsafe: new Set([
        'horiz-origin-x',
        'horiz-origin-y',
        'vert-adv-y',
        'vert-origin-x',
        'vert-origin-y',
      ]),
    },
    contentGroups: new Set(['descriptive']),
    content: new Set(['font-face', 'glyph', 'hkern', 'missing-glyph', 'vkern']),
  },
  'font-face': {
    attrsGroups: new Set(['core']),
    attrs: new Set([
      'font-family',
      'font-style',
      'font-variant',
      'font-weight',
      'font-stretch',
      'font-size',
      'unicode-range',
      'units-per-em',
      'panose-1',
      'stemv',
      'stemh',
      'slope',
      'cap-height',
      'x-height',
      'accent-height',
      'ascent',
      'descent',
      'widths',
      'bbox',
      'ideographic',
      'alphabetic',
      'mathematical',
      'hanging',
      'v-ideographic',
      'v-alphabetic',
      'v-mathematical',
      'v-hanging',
      'underline-position',
      'underline-thickness',
      'strikethrough-position',
      'strikethrough-thickness',
      'overline-position',
      'overline-thickness',
    ]),
    defaults: {
      'font-style': 'all',
      'font-variant': 'normal',
      'font-weight': 'all',
      'font-stretch': 'normal',
      'unicode-range': 'U+0-10FFFF',
      'units-per-em': '1000',
      'panose-1': '0 0 0 0 0 0 0 0 0 0',
      slope: '0',
    },
    deprecated: {
      unsafe: new Set([
        'accent-height',
        'alphabetic',
        'ascent',
        'bbox',
        'cap-height',
        'descent',
        'hanging',
        'ideographic',
        'mathematical',
        'panose-1',
        'slope',
        'stemh',
        'stemv',
        'unicode-range',
        'units-per-em',
        'v-alphabetic',
        'v-hanging',
        'v-ideographic',
        'v-mathematical',
        'widths',
        'x-height',
      ]),
    },
    contentGroups: new Set(['descriptive']),
    content: new Set([
      // TODO: "at most one 'font-face-src' element"
      'font-face-src',
    ]),
  },
  // TODO: empty content
  'font-face-format': {
    attrsGroups: new Set(['core']),
    attrs: new Set(['string']),
    deprecated: { unsafe: new Set(['string']) },
  },
  'font-face-name': {
    attrsGroups: new Set(['core']),
    attrs: new Set(['name']),
    deprecated: { unsafe: new Set(['name']) },
  },
  'font-face-src': {
    attrsGroups: new Set(['core']),
    content: new Set(['font-face-name', 'font-face-uri']),
  },
  'font-face-uri': {
    attrsGroups: new Set(['core', 'xlink']),
    attrs: new Set(['href', 'xlink:href']),
    content: new Set(['font-face-format']),
  },
  foreignObject: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'height',
      'style',
      'transform',
      'width',
      'x',
      'y',
    ]),
    defaults: {
      x: '0',
      y: '0',
    },
  },
  g: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'style',
      'transform',
    ]),
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  glyph: {
    attrsGroups: new Set(['core', 'presentation']),
    attrs: new Set([
      'arabic-form',
      'class',
      'd',
      'glyph-name',
      'horiz-adv-x',
      'lang',
      'orientation',
      'style',
      'unicode',
      'vert-adv-y',
      'vert-origin-x',
      'vert-origin-y',
    ]),
    defaults: {
      'arabic-form': 'initial',
    },
    deprecated: {
      unsafe: new Set([
        'arabic-form',
        'glyph-name',
        'horiz-adv-x',
        'orientation',
        'unicode',
        'vert-adv-y',
        'vert-origin-x',
        'vert-origin-y',
      ]),
    },
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  glyphRef: {
    attrsGroups: new Set(['core', 'presentation']),
    attrs: new Set([
      'class',
      'd',
      'horiz-adv-x',
      'style',
      'vert-adv-y',
      'vert-origin-x',
      'vert-origin-y',
    ]),
    deprecated: {
      unsafe: new Set([
        'horiz-adv-x',
        'vert-adv-y',
        'vert-origin-x',
        'vert-origin-y',
      ]),
    },
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  hatch: {
    attrsGroups: new Set(['core', 'presentation', 'xlink']),
    attrs: new Set([
      'class',
      'hatchContentUnits',
      'hatchUnits',
      'pitch',
      'rotate',
      'style',
      'transform',
      'x',
      'y',
    ]),
    defaults: {
      hatchUnits: 'objectBoundingBox',
      hatchContentUnits: 'userSpaceOnUse',
      x: '0',
      y: '0',
      pitch: '0',
      rotate: '0',
    },
    contentGroups: new Set(['animation', 'descriptive']),
    content: new Set(['hatchPath']),
  },
  hatchPath: {
    attrsGroups: new Set(['core', 'presentation', 'xlink']),
    attrs: new Set(['class', 'style', 'd', 'offset']),
    defaults: {
      offset: '0',
    },
    contentGroups: new Set(['animation', 'descriptive']),
  },
  hkern: {
    attrsGroups: new Set(['core']),
    attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']),
    deprecated: { unsafe: new Set(['g1', 'g2', 'k', 'u1', 'u2']) },
  },
  image: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
      'xlink',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'height',
      'href',
      'preserveAspectRatio',
      'style',
      'transform',
      'width',
      'x',
      'xlink:href',
      'y',
    ]),
    defaults: {
      x: '0',
      y: '0',
      preserveAspectRatio: 'xMidYMid meet',
    },
    contentGroups: new Set(['animation', 'descriptive']),
  },
  line: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'style',
      'transform',
      'x1',
      'x2',
      'y1',
      'y2',
    ]),
    defaults: {
      x1: '0',
      y1: '0',
      x2: '0',
      y2: '0',
    },
    contentGroups: new Set(['animation', 'descriptive']),
  },
  linearGradient: {
    attrsGroups: new Set(['core', 'presentation', 'xlink']),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'gradientTransform',
      'gradientUnits',
      'href',
      'spreadMethod',
      'style',
      'x1',
      'x2',
      'xlink:href',
      'y1',
      'y2',
    ]),
    defaults: {
      x1: '0',
      y1: '0',
      x2: '100%',
      y2: '0',
      spreadMethod: 'pad',
    },
    contentGroups: new Set(['descriptive']),
    content: new Set(['animate', 'animateTransform', 'set', 'stop']),
  },
  marker: {
    attrsGroups: new Set(['core', 'presentation']),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'markerHeight',
      'markerUnits',
      'markerWidth',
      'orient',
      'preserveAspectRatio',
      'refX',
      'refY',
      'style',
      'viewBox',
    ]),
    defaults: {
      markerUnits: 'strokeWidth',
      refX: '0',
      refY: '0',
      markerWidth: '3',
      markerHeight: '3',
    },
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  mask: {
    attrsGroups: new Set(['conditionalProcessing', 'core', 'presentation']),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'height',
      'mask-type',
      'maskContentUnits',
      'maskUnits',
      'style',
      'width',
      'x',
      'y',
    ]),
    defaults: {
      maskUnits: 'objectBoundingBox',
      maskContentUnits: 'userSpaceOnUse',
      x: '-10%',
      y: '-10%',
      width: '120%',
      height: '120%',
    },
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  metadata: {
    attrsGroups: new Set(['core']),
  },
  'missing-glyph': {
    attrsGroups: new Set(['core', 'presentation']),
    attrs: new Set([
      'class',
      'd',
      'horiz-adv-x',
      'style',
      'vert-adv-y',
      'vert-origin-x',
      'vert-origin-y',
    ]),
    deprecated: {
      unsafe: new Set([
        'horiz-adv-x',
        'vert-adv-y',
        'vert-origin-x',
        'vert-origin-y',
      ]),
    },
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  mpath: {
    attrsGroups: new Set(['core', 'xlink']),
    attrs: new Set(['externalResourcesRequired', 'href', 'xlink:href']),
    contentGroups: new Set(['descriptive']),
  },
  path: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'd',
      'externalResourcesRequired',
      'pathLength',
      'style',
      'transform',
    ]),
    contentGroups: new Set(['animation', 'descriptive']),
  },
  pattern: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'presentation',
      'xlink',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'height',
      'href',
      'patternContentUnits',
      'patternTransform',
      'patternUnits',
      'preserveAspectRatio',
      'style',
      'viewBox',
      'width',
      'x',
      'xlink:href',
      'y',
    ]),
    defaults: {
      patternUnits: 'objectBoundingBox',
      patternContentUnits: 'userSpaceOnUse',
      x: '0',
      y: '0',
      width: '0',
      height: '0',
      preserveAspectRatio: 'xMidYMid meet',
    },
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  polygon: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'points',
      'style',
      'transform',
    ]),
    contentGroups: new Set(['animation', 'descriptive']),
  },
  polyline: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'points',
      'style',
      'transform',
    ]),
    contentGroups: new Set(['animation', 'descriptive']),
  },
  radialGradient: {
    attrsGroups: new Set(['core', 'presentation', 'xlink']),
    attrs: new Set([
      'class',
      'cx',
      'cy',
      'externalResourcesRequired',
      'fr',
      'fx',
      'fy',
      'gradientTransform',
      'gradientUnits',
      'href',
      'r',
      'spreadMethod',
      'style',
      'xlink:href',
    ]),
    defaults: {
      gradientUnits: 'objectBoundingBox',
      cx: '50%',
      cy: '50%',
      r: '50%',
    },
    contentGroups: new Set(['descriptive']),
    content: new Set(['animate', 'animateTransform', 'set', 'stop']),
  },
  meshGradient: {
    attrsGroups: new Set(['core', 'presentation', 'xlink']),
    attrs: new Set(['class', 'style', 'x', 'y', 'gradientUnits', 'transform']),
    contentGroups: new Set(['descriptive', 'paintServer', 'animation']),
    content: new Set(['meshRow']),
  },
  meshRow: {
    attrsGroups: new Set(['core', 'presentation']),
    attrs: new Set(['class', 'style']),
    contentGroups: new Set(['descriptive']),
    content: new Set(['meshPatch']),
  },
  meshPatch: {
    attrsGroups: new Set(['core', 'presentation']),
    attrs: new Set(['class', 'style']),
    contentGroups: new Set(['descriptive']),
    content: new Set(['stop']),
  },
  rect: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'height',
      'rx',
      'ry',
      'style',
      'transform',
      'width',
      'x',
      'y',
    ]),
    defaults: {
      x: '0',
      y: '0',
    },
    contentGroups: new Set(['animation', 'descriptive']),
  },
  script: {
    attrsGroups: new Set(['core', 'xlink']),
    attrs: new Set(['externalResourcesRequired', 'type', 'href', 'xlink:href']),
  },
  set: {
    attrsGroups: new Set([
      'animation',
      'animationAttributeTarget',
      'animationTiming',
      'conditionalProcessing',
      'core',
      'xlink',
    ]),
    attrs: new Set(['externalResourcesRequired', 'to']),
    contentGroups: new Set(['descriptive']),
  },
  solidColor: {
    attrsGroups: new Set(['core', 'presentation']),
    attrs: new Set(['class', 'style']),
    contentGroups: new Set(['paintServer']),
  },
  stop: {
    attrsGroups: new Set(['core', 'presentation']),
    attrs: new Set(['class', 'style', 'offset', 'path']),
    content: new Set(['animate', 'animateColor', 'set']),
  },
  style: {
    attrsGroups: new Set(['core']),
    attrs: new Set(['type', 'media', 'title']),
    defaults: {
      type: 'text/css',
    },
  },
  svg: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'documentEvent',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'baseProfile',
      'class',
      'contentScriptType',
      'contentStyleType',
      'height',
      'preserveAspectRatio',
      'style',
      'version',
      'viewBox',
      'width',
      'x',
      'y',
      'zoomAndPan',
    ]),
    defaults: {
      x: '0',
      y: '0',
      width: '100%',
      height: '100%',
      preserveAspectRatio: 'xMidYMid meet',
      zoomAndPan: 'magnify',
      version: '1.1',
      baseProfile: 'none',
      contentScriptType: 'application/ecmascript',
      contentStyleType: 'text/css',
    },
    deprecated: {
      safe: new Set(['version']),
      unsafe: new Set([
        'baseProfile',
        'contentScriptType',
        'contentStyleType',
        'zoomAndPan',
      ]),
    },
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  switch: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'style',
      'transform',
    ]),
    contentGroups: new Set(['animation', 'descriptive', 'shape']),
    content: new Set([
      'a',
      'foreignObject',
      'g',
      'image',
      'svg',
      'switch',
      'text',
      'use',
    ]),
  },
  symbol: {
    attrsGroups: new Set(['core', 'graphicalEvent', 'presentation']),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'preserveAspectRatio',
      'refX',
      'refY',
      'style',
      'viewBox',
    ]),
    defaults: {
      refX: '0',
      refY: '0',
    },
    contentGroups: new Set([
      'animation',
      'descriptive',
      'paintServer',
      'shape',
      'structural',
    ]),
    content: new Set([
      'a',
      'altGlyphDef',
      'clipPath',
      'color-profile',
      'cursor',
      'filter',
      'font-face',
      'font',
      'foreignObject',
      'image',
      'marker',
      'mask',
      'pattern',
      'script',
      'style',
      'switch',
      'text',
      'view',
    ]),
  },
  text: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'dx',
      'dy',
      'externalResourcesRequired',
      'lengthAdjust',
      'rotate',
      'style',
      'textLength',
      'transform',
      'x',
      'y',
    ]),
    defaults: {
      x: '0',
      y: '0',
      lengthAdjust: 'spacing',
    },
    contentGroups: new Set(['animation', 'descriptive', 'textContentChild']),
    content: new Set(['a']),
  },
  textPath: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
      'xlink',
    ]),
    attrs: new Set([
      'class',
      'd',
      'externalResourcesRequired',
      'href',
      'method',
      'spacing',
      'startOffset',
      'style',
      'xlink:href',
    ]),
    defaults: {
      startOffset: '0',
      method: 'align',
      spacing: 'exact',
    },
    contentGroups: new Set(['descriptive']),
    content: new Set([
      'a',
      'altGlyph',
      'animate',
      'animateColor',
      'set',
      'tref',
      'tspan',
    ]),
  },
  title: {
    attrsGroups: new Set(['core']),
    attrs: new Set(['class', 'style']),
  },
  tref: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
      'xlink',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'href',
      'style',
      'xlink:href',
    ]),
    contentGroups: new Set(['descriptive']),
    content: new Set(['animate', 'animateColor', 'set']),
  },
  tspan: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
    ]),
    attrs: new Set([
      'class',
      'dx',
      'dy',
      'externalResourcesRequired',
      'lengthAdjust',
      'rotate',
      'style',
      'textLength',
      'x',
      'y',
    ]),
    contentGroups: new Set(['descriptive']),
    content: new Set([
      'a',
      'altGlyph',
      'animate',
      'animateColor',
      'set',
      'tref',
      'tspan',
    ]),
  },
  use: {
    attrsGroups: new Set([
      'conditionalProcessing',
      'core',
      'graphicalEvent',
      'presentation',
      'xlink',
    ]),
    attrs: new Set([
      'class',
      'externalResourcesRequired',
      'height',
      'href',
      'style',
      'transform',
      'width',
      'x',
      'xlink:href',
      'y',
    ]),
    defaults: {
      x: '0',
      y: '0',
    },
    contentGroups: new Set(['animation', 'descriptive']),
  },
  view: {
    attrsGroups: new Set(['core']),
    attrs: new Set([
      'externalResourcesRequired',
      'preserveAspectRatio',
      'viewBox',
      'viewTarget',
      'zoomAndPan',
    ]),
    deprecated: { unsafe: new Set(['viewTarget', 'zoomAndPan']) },
    contentGroups: new Set(['descriptive']),
  },
  vkern: {
    attrsGroups: new Set(['core']),
    attrs: new Set(['u1', 'g1', 'u2', 'g2', 'k']),
    deprecated: { unsafe: new Set(['g1', 'g2', 'k', 'u1', 'u2']) },
  },
};

/**
 * @type {Readonly<Set<string>>}
 * @see https://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes
 */
export const editorNamespaces = new Set([
  'http://creativecommons.org/ns#',
  'http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd',
  'http://krita.org/namespaces/svg/krita',
  'http://ns.adobe.com/AdobeIllustrator/10.0/',
  'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/',
  'http://ns.adobe.com/Extensibility/1.0/',
  'http://ns.adobe.com/Flows/1.0/',
  'http://ns.adobe.com/GenericCustomNamespace/1.0/',
  'http://ns.adobe.com/Graphs/1.0/',
  'http://ns.adobe.com/ImageReplacement/1.0/',
  'http://ns.adobe.com/SaveForWeb/1.0/',
  'http://ns.adobe.com/Variables/1.0/',
  'http://ns.adobe.com/XPath/1.0/',
  'http://purl.org/dc/elements/1.1/',
  'http://schemas.microsoft.com/visio/2003/SVGExtensions/',
  'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',
  'http://taptrix.com/vectorillustrator/svg_extensions',
  'http://www.bohemiancoding.com/sketch/ns',
  'http://www.figma.com/figma/ns',
  'http://www.inkscape.org/namespaces/inkscape',
  'http://www.serif.com/',
  'http://www.vector.evaxdesign.sk',
  'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
  'https://boxy-svg.com',
]);

/**
 * @type {Readonly<Set<string>>}
 * @see https://www.w3.org/TR/SVG11/linking.html#processingIRI
 */
export const referencesProps = new Set([
  'clip-path',
  'color-profile',
  'fill',
  'filter',
  'marker-end',
  'marker-mid',
  'marker-start',
  'mask',
  'stroke',
  'style',
]);

/**
 * @type {Readonly<Set<string>>}
 * @see https://www.w3.org/TR/SVG11/propidx.html
 */
export const inheritableAttrs = new Set([
  'clip-rule',
  'color-interpolation-filters',
  'color-interpolation',
  'color-profile',
  'color-rendering',
  'color',
  'cursor',
  'direction',
  'dominant-baseline',
  'fill-opacity',
  'fill-rule',
  'fill',
  'font-family',
  'font-size-adjust',
  'font-size',
  'font-stretch',
  'font-style',
  'font-variant',
  'font-weight',
  'font',
  'glyph-orientation-horizontal',
  'glyph-orientation-vertical',
  'image-rendering',
  'letter-spacing',
  'marker-end',
  'marker-mid',
  'marker-start',
  'marker',
  'paint-order',
  'pointer-events',
  'shape-rendering',
  'stroke-dasharray',
  'stroke-dashoffset',
  'stroke-linecap',
  'stroke-linejoin',
  'stroke-miterlimit',
  'stroke-opacity',
  'stroke-width',
  'stroke',
  'text-anchor',
  'text-rendering',
  'transform',
  'visibility',
  'word-spacing',
  'writing-mode',
]);

/**
 * @type {Readonly<Set<string>>}
 */
export const presentationNonInheritableGroupAttrs = new Set([
  'clip-path',
  'display',
  'filter',
  'mask',
  'opacity',
  'text-decoration',
  'transform',
  'unicode-bidi',
]);

/**
 * @type {Readonly<Record<string, string>>}
 * @see https://www.w3.org/TR/SVG11/single-page.html#types-ColorKeywords
 */
export const colorsNames = {
  aliceblue: '#f0f8ff',
  antiquewhite: '#faebd7',
  aqua: '#0ff',
  aquamarine: '#7fffd4',
  azure: '#f0ffff',
  beige: '#f5f5dc',
  bisque: '#ffe4c4',
  black: '#000',
  blanchedalmond: '#ffebcd',
  blue: '#00f',
  blueviolet: '#8a2be2',
  brown: '#a52a2a',
  burlywood: '#deb887',
  cadetblue: '#5f9ea0',
  chartreuse: '#7fff00',
  chocolate: '#d2691e',
  coral: '#ff7f50',
  cornflowerblue: '#6495ed',
  cornsilk: '#fff8dc',
  crimson: '#dc143c',
  cyan: '#0ff',
  darkblue: '#00008b',
  darkcyan: '#008b8b',
  darkgoldenrod: '#b8860b',
  darkgray: '#a9a9a9',
  darkgreen: '#006400',
  darkgrey: '#a9a9a9',
  darkkhaki: '#bdb76b',
  darkmagenta: '#8b008b',
  darkolivegreen: '#556b2f',
  darkorange: '#ff8c00',
  darkorchid: '#9932cc',
  darkred: '#8b0000',
  darksalmon: '#e9967a',
  darkseagreen: '#8fbc8f',
  darkslateblue: '#483d8b',
  darkslategray: '#2f4f4f',
  darkslategrey: '#2f4f4f',
  darkturquoise: '#00ced1',
  darkviolet: '#9400d3',
  deeppink: '#ff1493',
  deepskyblue: '#00bfff',
  dimgray: '#696969',
  dimgrey: '#696969',
  dodgerblue: '#1e90ff',
  firebrick: '#b22222',
  floralwhite: '#fffaf0',
  forestgreen: '#228b22',
  fuchsia: '#f0f',
  gainsboro: '#dcdcdc',
  ghostwhite: '#f8f8ff',
  gold: '#ffd700',
  goldenrod: '#daa520',
  gray: '#808080',
  green: '#008000',
  greenyellow: '#adff2f',
  grey: '#808080',
  honeydew: '#f0fff0',
  hotpink: '#ff69b4',
  indianred: '#cd5c5c',
  indigo: '#4b0082',
  ivory: '#fffff0',
  khaki: '#f0e68c',
  lavender: '#e6e6fa',
  lavenderblush: '#fff0f5',
  lawngreen: '#7cfc00',
  lemonchiffon: '#fffacd',
  lightblue: '#add8e6',
  lightcoral: '#f08080',
  lightcyan: '#e0ffff',
  lightgoldenrodyellow: '#fafad2',
  lightgray: '#d3d3d3',
  lightgreen: '#90ee90',
  lightgrey: '#d3d3d3',
  lightpink: '#ffb6c1',
  lightsalmon: '#ffa07a',
  lightseagreen: '#20b2aa',
  lightskyblue: '#87cefa',
  lightslategray: '#789',
  lightslategrey: '#789',
  lightsteelblue: '#b0c4de',
  lightyellow: '#ffffe0',
  lime: '#0f0',
  limegreen: '#32cd32',
  linen: '#faf0e6',
  magenta: '#f0f',
  maroon: '#800000',
  mediumaquamarine: '#66cdaa',
  mediumblue: '#0000cd',
  mediumorchid: '#ba55d3',
  mediumpurple: '#9370db',
  mediumseagreen: '#3cb371',
  mediumslateblue: '#7b68ee',
  mediumspringgreen: '#00fa9a',
  mediumturquoise: '#48d1cc',
  mediumvioletred: '#c71585',
  midnightblue: '#191970',
  mintcream: '#f5fffa',
  mistyrose: '#ffe4e1',
  moccasin: '#ffe4b5',
  navajowhite: '#ffdead',
  navy: '#000080',
  oldlace: '#fdf5e6',
  olive: '#808000',
  olivedrab: '#6b8e23',
  orange: '#ffa500',
  orangered: '#ff4500',
  orchid: '#da70d6',
  palegoldenrod: '#eee8aa',
  palegreen: '#98fb98',
  paleturquoise: '#afeeee',
  palevioletred: '#db7093',
  papayawhip: '#ffefd5',
  peachpuff: '#ffdab9',
  peru: '#cd853f',
  pink: '#ffc0cb',
  plum: '#dda0dd',
  powderblue: '#b0e0e6',
  purple: '#800080',
  rebeccapurple: '#639',
  red: '#f00',
  rosybrown: '#bc8f8f',
  royalblue: '#4169e1',
  saddlebrown: '#8b4513',
  salmon: '#fa8072',
  sandybrown: '#f4a460',
  seagreen: '#2e8b57',
  seashell: '#fff5ee',
  sienna: '#a0522d',
  silver: '#c0c0c0',
  skyblue: '#87ceeb',
  slateblue: '#6a5acd',
  slategray: '#708090',
  slategrey: '#708090',
  snow: '#fffafa',
  springgreen: '#00ff7f',
  steelblue: '#4682b4',
  tan: '#d2b48c',
  teal: '#008080',
  thistle: '#d8bfd8',
  tomato: '#ff6347',
  turquoise: '#40e0d0',
  violet: '#ee82ee',
  wheat: '#f5deb3',
  white: '#fff',
  whitesmoke: '#f5f5f5',
  yellow: '#ff0',
  yellowgreen: '#9acd32',
};

/**
 * @type {Readonly<Record<string, string>>}
 */
export const colorsShortNames = {
  '#f0ffff': 'azure',
  '#f5f5dc': 'beige',
  '#ffe4c4': 'bisque',
  '#a52a2a': 'brown',
  '#ff7f50': 'coral',
  '#ffd700': 'gold',
  '#808080': 'gray',
  '#008000': 'green',
  '#4b0082': 'indigo',
  '#fffff0': 'ivory',
  '#f0e68c': 'khaki',
  '#faf0e6': 'linen',
  '#800000': 'maroon',
  '#000080': 'navy',
  '#808000': 'olive',
  '#ffa500': 'orange',
  '#da70d6': 'orchid',
  '#cd853f': 'peru',
  '#ffc0cb': 'pink',
  '#dda0dd': 'plum',
  '#800080': 'purple',
  '#f00': 'red',
  '#ff0000': 'red',
  '#fa8072': 'salmon',
  '#a0522d': 'sienna',
  '#c0c0c0': 'silver',
  '#fffafa': 'snow',
  '#d2b48c': 'tan',
  '#008080': 'teal',
  '#ff6347': 'tomato',
  '#ee82ee': 'violet',
  '#f5deb3': 'wheat',
};

/**
 * @type {Readonly<Set<string>>}
 * @see https://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor
 */
export const colorsProps = new Set([
  'color',
  'fill',
  'flood-color',
  'lighting-color',
  'stop-color',
  'stroke',
]);

/**
 * @type {Readonly<Record<string, Set<string>>>}
 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
 */
export const pseudoClasses = {
  displayState: new Set(['fullscreen', 'modal', 'picture-in-picture']),
  input: new Set([
    'autofill',
    'blank',
    'checked',
    'default',
    'disabled',
    'enabled',
    'in-range',
    'indeterminate',
    'invalid',
    'optional',
    'out-of-range',
    'placeholder-shown',
    'read-only',
    'read-write',
    'required',
    'user-invalid',
    'valid',
  ]),
  linguistic: new Set(['dir', 'lang']),
  location: new Set([
    'any-link',
    'link',
    'local-link',
    'scope',
    'target-within',
    'target',
    'visited',
  ]),
  resourceState: new Set(['playing', 'paused']),
  timeDimensional: new Set(['current', 'past', 'future']),
  treeStructural: new Set([
    'empty',
    'first-child',
    'first-of-type',
    'last-child',
    'last-of-type',
    'nth-child',
    'nth-last-child',
    'nth-last-of-type',
    'nth-of-type',
    'only-child',
    'only-of-type',
    'root',
  ]),
  userAction: new Set([
    'active',
    'focus-visible',
    'focus-within',
    'focus',
    'hover',
  ]),
  functional: new Set(['is', 'not', 'where', 'has']),
};
