-
-
Notifications
You must be signed in to change notification settings - Fork 179
/
CSSParser.ts
102 lines (88 loc) · 3.03 KB
/
CSSParser.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import CSSRule from './CSSRule';
import CSSStyleSheet from './CSSStyleSheet';
import CSSStyleRule from './rules/CSSStyleRule';
import CSSKeyframeRule from './rules/CSSKeyframeRule';
import CSSKeyframesRule from './rules/CSSKeyframesRule';
import CSSMediaRule from './rules/CSSMediaRule';
const COMMENT_REGEXP = /\/\*[^*]*\*\//gm;
/**
* CSS parser.
*/
export default class CSSParser {
/**
* Parses HTML and returns a root element.
*
* @param parentStyleSheet Parent style sheet.
* @param cssText CSS code.
* @returns Root element.
*/
public static parseFromString(parentStyleSheet: CSSStyleSheet, cssText: string): CSSRule[] {
const css = cssText.replace(COMMENT_REGEXP, '');
const cssRules = [];
const regExp = /{|}/gm;
const stack: CSSRule[] = [];
let parentRule: CSSRule = null;
let lastIndex = 0;
let match: RegExpMatchArray;
while ((match = regExp.exec(css))) {
if (match[0] === '{') {
const selectorText = css.substring(lastIndex, match.index).trim();
if (selectorText.startsWith('@keyframes')) {
const newRule = new CSSKeyframesRule();
(<string>newRule.name) = selectorText.replace('@keyframes ', '');
newRule.parentStyleSheet = parentStyleSheet;
cssRules.push(newRule);
parentRule = newRule;
} else if (selectorText.startsWith('@media')) {
const mediums = selectorText.replace('@media', '').split(',');
const newRule = new CSSMediaRule();
for (const medium of mediums) {
newRule.media.appendMedium(medium.trim());
}
newRule.parentStyleSheet = parentStyleSheet;
cssRules.push(newRule);
parentRule = newRule;
} else if (parentRule && parentRule.type === CSSRule.KEYFRAMES_RULE) {
const newRule = new CSSKeyframeRule();
(<string>newRule.keyText) = selectorText.trim();
newRule.parentStyleSheet = parentStyleSheet;
newRule.parentRule = parentRule;
(<CSSKeyframesRule>parentRule).cssRules.push(<CSSKeyframeRule>newRule);
parentRule = newRule;
} else if (parentRule && parentRule.type === CSSRule.MEDIA_RULE) {
const newRule = new CSSStyleRule();
(<string>newRule.selectorText) = selectorText;
newRule.parentStyleSheet = parentStyleSheet;
newRule.parentRule = parentRule;
(<CSSMediaRule>parentRule).cssRules.push(newRule);
parentRule = newRule;
} else {
const newRule = new CSSStyleRule();
(<string>newRule.selectorText) = selectorText;
newRule.parentStyleSheet = parentStyleSheet;
newRule.parentRule = parentRule;
if (!parentRule) {
cssRules.push(newRule);
}
parentRule = newRule;
}
stack.push(parentRule);
} else {
if (parentRule) {
const cssText = css.substring(lastIndex, match.index).trim();
switch (parentRule.type) {
case CSSRule.FONT_FACE_RULE:
case CSSRule.KEYFRAME_RULE:
case CSSRule.STYLE_RULE:
(<CSSStyleRule>parentRule)._cssText = cssText;
break;
}
}
stack.pop();
parentRule = stack[stack.length - 1] || null;
}
lastIndex = match.index + 1;
}
return cssRules;
}
}