11export interface ExtendedStyleRule extends CSSStyleRule {
2- isDeclaredAfter : ( selector : string ) => boolean ;
2+ isDeclaredAfter : ( selector : string ) => boolean ;
3+ }
4+ interface ExtendedStyleDeclaration extends CSSStyleDeclaration {
5+ getPropVal : ( prop : string , strip ?: boolean ) => string ;
6+ }
7+
8+ const getIsDeclaredAfter = ( styleRule : CSSStyleRule ) => ( selector : string ) => {
9+ const cssStyleRules = Array . from (
10+ styleRule . parentStyleSheet ?. cssRules || [ ]
11+ ) ?. filter ( ele => ele . type === CSSRule . STYLE_RULE ) as CSSStyleRule [ ] ;
12+ const previousStyleRule = cssStyleRules . find (
13+ ele => ele ?. selectorText === selector
14+ ) ;
15+ if ( ! previousStyleRule ) return false ;
16+ const currPosition = Array . from (
17+ styleRule . parentStyleSheet ?. cssRules || [ ]
18+ ) . indexOf ( styleRule ) ;
19+ const prevPosition = Array . from (
20+ previousStyleRule ?. parentStyleSheet ?. cssRules || [ ]
21+ ) . indexOf ( previousStyleRule ) ;
22+ return currPosition > prevPosition ;
23+ } ;
24+
25+ export class CSSHelp {
26+ doc : Document ;
27+ constructor ( doc : Document ) {
28+ this . doc = doc ;
329 }
4- interface ExtendedStyleDeclaration extends CSSStyleDeclaration {
5- getPropVal : ( prop : string , strip ?: boolean ) => string ;
30+ private _getStyleRules ( ) {
31+ const styleSheet = this . getStyleSheet ( ) ;
32+ return this . styleSheetToCssRulesArray ( styleSheet ) . filter (
33+ ele => ele . type === CSSRule . STYLE_RULE
34+ ) as CSSStyleRule [ ] ;
635 }
7-
8- const getIsDeclaredAfter = ( styleRule : CSSStyleRule ) => ( selector : string ) => {
9- const cssStyleRules = Array . from (
10- styleRule . parentStyleSheet ?. cssRules || [ ]
11- ) ?. filter ( ele => ele . type === CSSRule . STYLE_RULE ) as CSSStyleRule [ ] ;
12- const previousStyleRule = cssStyleRules . find (
36+
37+ getStyleDeclarations ( selector : string ) : CSSStyleDeclaration [ ] {
38+ return this . _getStyleRules ( )
39+ ?. filter ( ele => ele ?. selectorText === selector )
40+ . map ( x => x . style ) ;
41+ }
42+ getStyle ( selector : string ) : ExtendedStyleDeclaration | null {
43+ const style = this . _getStyleRules ( ) . find (
44+ ele => ele ?. selectorText === selector
45+ ) ?. style as ExtendedStyleDeclaration | undefined ;
46+ if ( ! style ) return null ;
47+ style . getPropVal = ( prop : string , strip = false ) => {
48+ return strip
49+ ? style . getPropertyValue ( prop ) . replace ( / \s + / g, '' )
50+ : style . getPropertyValue ( prop ) ;
51+ } ;
52+ return style ;
53+ }
54+ getStyleRule ( selector : string ) : ExtendedStyleRule | null {
55+ const styleRule = this . _getStyleRules ( ) ?. find (
1356 ele => ele ?. selectorText === selector
1457 ) ;
15- if ( ! previousStyleRule ) return false ;
16- const currPosition = Array . from (
17- styleRule . parentStyleSheet ?. cssRules || [ ]
18- ) . indexOf ( styleRule ) ;
19- const prevPosition = Array . from (
20- previousStyleRule ?. parentStyleSheet ?. cssRules || [ ]
21- ) . indexOf ( previousStyleRule ) ;
22- return currPosition > prevPosition ;
23- } ;
24- export class CSSHelp {
25- doc : HTMLDocument ;
26- constructor ( doc : HTMLDocument ) {
27- this . doc = doc ;
28- }
29- private _getStyleRules ( ) {
30- const styleSheet = this . getStyleSheet ( ) ;
31- return this . styleSheetToCssRulesArray ( styleSheet ) . filter (
32- ele => ele . type === CSSRule . STYLE_RULE
33- ) as CSSStyleRule [ ] ;
34- }
35-
36- getStyleDeclarations ( selector : string ) : CSSStyleDeclaration [ ] {
37- return this . _getStyleRules ( )
38- ?. filter ( ele => ele ?. selectorText === selector )
39- . map ( x => x . style ) ;
40- }
41- getStyle ( selector : string ) : ExtendedStyleDeclaration | null {
42- const style = this . _getStyleRules ( ) . find (
43- ele => ele ?. selectorText === selector
44- ) ?. style as ExtendedStyleDeclaration | undefined ;
45- if ( ! style ) return null ;
46- style . getPropVal = ( prop : string , strip = false ) => {
47- return strip
48- ? style . getPropertyValue ( prop ) . replace ( / \s + / g, '' )
49- : style . getPropertyValue ( prop ) ;
58+ if ( styleRule ) {
59+ return {
60+ ...styleRule ,
61+ isDeclaredAfter : ( selector : string ) =>
62+ getIsDeclaredAfter ( styleRule ) ( selector )
5063 } ;
51- return style ;
52- }
53- getStyleRule ( selector : string ) : ExtendedStyleRule | null {
54- const styleRule = this . _getStyleRules ( ) ?. find (
55- ele => ele ?. selectorText === selector
56- ) ;
57- if ( styleRule ) {
58- return {
59- ...styleRule ,
60- isDeclaredAfter : ( selector : string ) =>
61- getIsDeclaredAfter ( styleRule ) ( selector )
62- } ;
63- } else {
64- return null ;
65- }
66- }
67- getCSSRules ( element ?: string ) : CSSRule [ ] {
68- const styleSheet = this . getStyleSheet ( ) ;
69- const cssRules = this . styleSheetToCssRulesArray ( styleSheet ) ;
70- switch ( element ) {
71- case 'media' :
72- return cssRules . filter ( ele => ele . type === CSSRule . MEDIA_RULE ) ;
73- case 'fontface' :
74- return cssRules . filter ( ele => ele . type === CSSRule . FONT_FACE_RULE ) ;
75- case 'import' :
76- return cssRules . filter ( ele => ele . type === CSSRule . IMPORT_RULE ) ;
77- case 'keyframes' :
78- return cssRules . filter ( ele => ele . type === CSSRule . KEYFRAMES_RULE ) ;
79- default :
80- return cssRules ;
81- }
82- }
83- isPropertyUsed ( property : string ) : boolean {
84- return this . _getStyleRules ( ) . some ( ele =>
85- ele . style ?. getPropertyValue ( property )
86- ) ;
87- }
88- getRuleListsWithinMedia ( mediaText : string ) : CSSStyleRule [ ] {
89- const medias = this . getCSSRules ( 'media' ) as CSSMediaRule [ ] ;
90- const cond = medias ?. find ( x => x ?. media ?. mediaText === mediaText ) ;
91- const cssRules = cond ?. cssRules ;
92- return Array . from ( cssRules || [ ] ) as CSSStyleRule [ ] ;
64+ } else {
65+ return null ;
9366 }
94- getStyleSheet ( ) : CSSStyleSheet | null {
95- // TODO: Change selector to match exactly 'styles.css'
96- const link : HTMLLinkElement | null = this . doc ?. querySelector (
97- "link[href*='styles']"
98- ) ;
99- // Most* browser extensions inject styles with class/media attributes
100- const style : HTMLStyleElement | null = this . doc ?. querySelector (
101- 'style:not([class]):not([media])'
102- ) ;
103- if ( link ?. sheet ?. cssRules ?. length ) {
104- return link . sheet ;
105- } else if ( style ) {
106- return style . sheet ;
107- } else {
108- return null ;
109- }
67+ }
68+ getCSSRules ( element ?: string ) : CSSRule [ ] {
69+ const styleSheet = this . getStyleSheet ( ) ;
70+ const cssRules = this . styleSheetToCssRulesArray ( styleSheet ) ;
71+ switch ( element ) {
72+ case 'media' :
73+ return cssRules . filter ( ele => ele . type === CSSRule . MEDIA_RULE ) ;
74+ case 'fontface' :
75+ return cssRules . filter ( ele => ele . type === CSSRule . FONT_FACE_RULE ) ;
76+ case 'import' :
77+ return cssRules . filter ( ele => ele . type === CSSRule . IMPORT_RULE ) ;
78+ case 'keyframes' :
79+ return cssRules . filter ( ele => ele . type === CSSRule . KEYFRAMES_RULE ) ;
80+ default :
81+ return cssRules ;
11082 }
111- styleSheetToCssRulesArray (
112- styleSheet : ReturnType < CSSHelp [ 'getStyleSheet' ] >
113- ) : CSSRule [ ] {
114- return Array . from ( styleSheet ?. cssRules || [ ] ) ;
83+ }
84+ isPropertyUsed ( property : string ) : boolean {
85+ return this . _getStyleRules ( ) . some ( ele =>
86+ ele . style ?. getPropertyValue ( property )
87+ ) ;
88+ }
89+ getRuleListsWithinMedia ( mediaText : string ) : CSSStyleRule [ ] {
90+ const medias = this . getCSSRules ( 'media' ) as CSSMediaRule [ ] ;
91+ const cond = medias ?. find ( x => x ?. media ?. mediaText === mediaText ) ;
92+ const cssRules = cond ?. cssRules ;
93+ return Array . from ( cssRules || [ ] ) as CSSStyleRule [ ] ;
94+ }
95+ getStyleSheet ( ) : CSSStyleSheet | null {
96+ // TODO: Change selector to match exactly 'styles.css'
97+ const link : HTMLLinkElement | null = this . doc ?. querySelector (
98+ "link[href*='styles']"
99+ ) ;
100+
101+ // When using the styles.css tab, we add a 'fcc-injected-styles' class so we can target that. This allows users to add external scripts without them interfering
102+ const stylesDotCss : HTMLStyleElement | null = this . doc ?. querySelector (
103+ 'style.fcc-injected-styles'
104+ ) ;
105+
106+ // For steps that use <style> tags, where they don't add the above class - most* browser extensions inject styles with class/media attributes, so it filters those
107+ const styleTag : HTMLStyleElement | null = this . doc ?. querySelector (
108+ 'style:not([class]):not([media])'
109+ ) ;
110+
111+ if ( link ?. sheet ?. cssRules ?. length ) {
112+ return link . sheet ;
113+ } else if ( stylesDotCss ) {
114+ return stylesDotCss . sheet ;
115+ } else if ( styleTag ) {
116+ return styleTag . sheet ;
117+ } else {
118+ return null ;
115119 }
116120 }
117-
121+ styleSheetToCssRulesArray (
122+ styleSheet : ReturnType < CSSHelp [ 'getStyleSheet' ] >
123+ ) : CSSRule [ ] {
124+ return Array . from ( styleSheet ?. cssRules || [ ] ) ;
125+ }
126+ }
127+
0 commit comments