import { Declaration , Node , Rule , StyleRules } from 'css' ; import { has , propEq } from 'ramda' ; import { NodeType } from 'svg-vdom' ; import { IRuleOption } from '../../typings' ; import { IDom , ITag } from '../../typings/node' ; import { needUnitInStyle } from '../const/definitions' ; import { regularAttr } from '../const/regular-attr' ; import { regularTag } from '../const/regular-tag' ; import { numberFullMatch } from '../const/syntax' ; import { cantTrans } from '../style/cant-trans' ; import { checkApply } from '../style/check-apply' ; import { checkGeometry } from '../style/check-geometry' ; import { parseStyle } from '../style/parse' ; import { styleToValue } from '../style/style-to-value' ; import { hasProp } from '../utils/has-prop' ; import { traversalObj } from '../utils/traversal-obj' ; import { knownCSS } from '../validate/known-css' ; import { legalValue } from '../validate/legal-value' ; import { attrIsEqual , valueIsEqual } from '../xml/attr-is-equal' ; import { parseStyleTree } from '../xml/parse-style-tree' ; const rmCSSNode = ( cssNode , plist ) => { const index = plist . indexOf ( cssNode ) ; if ( index !== - 1 ) { plist . splice ( index , 1 ) ; } } ; const checkUnit = ( name , value ) => needUnitInStyle . includes ( name ) && numberFullMatch . test ( value ) && + value !== 0 ; interface IStyleAttrObj { [ prop ] : { value : string ; fromStyle ? : boolean ; } ; } const checkAttr = ( node , dom , rmAttrEqDefault , ignoreKnownCSS , browsers ) => { parseStyleTree ( dom ) ; const attrObj = { } ; const tagDefine = regularTag [ node . nodeName ] ; const nodeStyle = node . styles ; for ( let i = node . attributes . length ; i -- ; ) { const attr = node . attributes [ i ] ; const attrDefine = regularAttr [ attr . fullname ] ; if ( attr . fullname === 'style' ) { const styleList = parseStyle ( attr . value ) ; const styleUnique = new Set < string > ( ) ; for ( let si = styleList . length ; si -- ; ) { const styleItem = styleList [ si ] ; const styleDefine = regularAttr [ styleItem . fullname ] ; if ( styleUnique . has ( styleItem . fullname ) ) { styleList . splice ( si , 1 ) ; continue ; } if ( ! styleDefine . couldBeStyle ) { if ( ! knownCSS ( styleItem . fullname ) || ignoreKnownCSS ) { styleList . splice ( si , 1 ) ; } else { styleUnique . add ( styleItem . fullname ) ; } continue ; } if ( ! legalValue ( styleDefine , styleItem ) ) { styleList . splice ( si , 1 ) ; continue ; } if ( checkUnit ( styleItem . fullname , styleItem . value ) ) { styleList . splice ( si , 1 ) ; continue ; } if ( ! checkApply ( styleDefine , node , dom ) ) { styleList . splice ( si , 1 ) ; continue ; } if ( rmAttrEqDefault ) { const parentStyle = ( node . parentNode as ITag ) . styles ; const nodeStyle = node . styles ; if ( ! styleDefine . inherited || ! parentStyle || ! hasProp ( parentStyle , styleItem . fullname ) ) { if ( ( ! nodeStyle || ! nodeStyle [ styleItem . fullname ] || ! nodeStyle [ styleItem . fullname ] . override ) && attrIsEqual ( styleDefine , styleItem . value , node . nodeName ) ) { styleList . splice ( si , 1 ) ; continue ; } } } styleUnique . add ( styleItem . fullname ) ; const onlyCss = ( nodeStyle && nodeStyle [ styleItem . fullname ] && nodeStyle [ styleItem . fullname ] . override ) || ( ! styleDefine . couldBeStyle && ! checkGeometry ( node . nodeName , styleItem . fullname , browsers ) ) || cantTrans ( tagDefine , styleItem . fullname ) ; if ( ! onlyCss && has ( styleItem . fullname , attrObj ) ) { node . removeAttribute ( styleItem . fullname ) ; } attrObj [ styleItem . fullname ] = { value : styleItem . value , fromStyle : true , } ; } if ( styleList . length ) { node . setAttribute ( 'style' , styleToValue ( styleList ) ) ; } else { node . removeAttribute ( 'style' ) ; } } else if ( attrDefine . couldBeStyle || checkGeometry ( node . nodeName , attr . fullname , browsers ) ) { if ( cantTrans ( tagDefine , attr . fullname ) ) { continue ; } if ( hasProp ( attrObj , attr . fullname ) || ! checkApply ( attrDefine , node , dom ) ) { node . removeAttribute ( attr . fullname ) ; } else { attrObj [ attr . fullname ] = { value : attr . value , } ; } } } } ; export const rmIllegalStyle = async ( dom , { params : { rmAttrEqDefault , ignoreKnownCSS , } , browsers , } ) : Promise < void > => new Promise ( resolve => { if ( dom . stylesheet ) { const cssRules = dom . stylesheet . stylesheet as StyleRules ; traversalObj < Declaration > ( propEq ( 'type' , 'declaration' ) , ( cssNode , parents ) => { const attrDefine = regularAttr [ cssNode . property as string ] ; if ( ! attrDefine . couldBeStyle && ( ! knownCSS ( cssNode . property as string ) || ignoreKnownCSS ) || checkUnit ( cssNode . property as string , cssNode . value as string ) ) { rmCSSNode ( cssNode , parents [ parents . length - 1 ] as Rule [ ] ) ; } else if ( rmAttrEqDefault ) { if ( typeof attrDefine . initValue === 'string' && valueIsEqual ( attrDefine , cssNode . value as string , attrDefine . initValue ) ) { rmCSSNode ( cssNode , parents [ parents . length - 1 ] as Rule [ ] ) ; } } } , cssRules . rules , true ) ; } parseStyleTree ( dom ) ; const tags = dom . querySelectorAll ( NodeType . Tag ) as ITag [ ] ; tags . forEach ( node => { checkAttr ( node , dom , rmAttrEqDefault , ignoreKnownCSS , browsers ) ; } ) ; resolve ( ) ; } ) ;