...
1 package cascadia
2
3 import (
4 "fmt"
5 "strconv"
6 "strings"
7 )
8
9
10
11 var specialCharReplacer *strings.Replacer
12
13 func init() {
14 var pairs []string
15 for _, s := range ",!\"#$%&'()*+ -./:;<=>?@[\\]^`{|}~" {
16 pairs = append(pairs, string(s), "\\"+string(s))
17 }
18 specialCharReplacer = strings.NewReplacer(pairs...)
19 }
20
21
22 func escape(s string) string { return specialCharReplacer.Replace(s) }
23
24 func (c tagSelector) String() string {
25 return c.tag
26 }
27
28 func (c idSelector) String() string {
29 return "#" + escape(c.id)
30 }
31
32 func (c classSelector) String() string {
33 return "." + escape(c.class)
34 }
35
36 func (c attrSelector) String() string {
37 val := c.val
38 if c.operation == "#=" {
39 val = c.regexp.String()
40 } else if c.operation != "" {
41 val = fmt.Sprintf(`"%s"`, val)
42 }
43
44 ignoreCase := ""
45
46 if c.insensitive {
47 ignoreCase = " i"
48 }
49
50 return fmt.Sprintf(`[%s%s%s%s]`, c.key, c.operation, val, ignoreCase)
51 }
52
53 func (c relativePseudoClassSelector) String() string {
54 return fmt.Sprintf(":%s(%s)", c.name, c.match.String())
55 }
56
57 func (c containsPseudoClassSelector) String() string {
58 s := "contains"
59 if c.own {
60 s += "Own"
61 }
62 return fmt.Sprintf(`:%s("%s")`, s, c.value)
63 }
64
65 func (c regexpPseudoClassSelector) String() string {
66 s := "matches"
67 if c.own {
68 s += "Own"
69 }
70 return fmt.Sprintf(":%s(%s)", s, c.regexp.String())
71 }
72
73 func (c nthPseudoClassSelector) String() string {
74 if c.a == 0 && c.b == 1 {
75 s := ":first-"
76 if c.last {
77 s = ":last-"
78 }
79 if c.ofType {
80 s += "of-type"
81 } else {
82 s += "child"
83 }
84 return s
85 }
86 var name string
87 switch [2]bool{c.last, c.ofType} {
88 case [2]bool{true, true}:
89 name = "nth-last-of-type"
90 case [2]bool{true, false}:
91 name = "nth-last-child"
92 case [2]bool{false, true}:
93 name = "nth-of-type"
94 case [2]bool{false, false}:
95 name = "nth-child"
96 }
97 s := fmt.Sprintf("+%d", c.b)
98 if c.b < 0 {
99 s = strconv.Itoa(c.b)
100 }
101 return fmt.Sprintf(":%s(%dn%s)", name, c.a, s)
102 }
103
104 func (c onlyChildPseudoClassSelector) String() string {
105 if c.ofType {
106 return ":only-of-type"
107 }
108 return ":only-child"
109 }
110
111 func (c inputPseudoClassSelector) String() string {
112 return ":input"
113 }
114
115 func (c emptyElementPseudoClassSelector) String() string {
116 return ":empty"
117 }
118
119 func (c rootPseudoClassSelector) String() string {
120 return ":root"
121 }
122
123 func (c linkPseudoClassSelector) String() string {
124 return ":link"
125 }
126
127 func (c langPseudoClassSelector) String() string {
128 return fmt.Sprintf(":lang(%s)", c.lang)
129 }
130
131 func (c neverMatchSelector) String() string {
132 return c.value
133 }
134
135 func (c enabledPseudoClassSelector) String() string {
136 return ":enabled"
137 }
138
139 func (c disabledPseudoClassSelector) String() string {
140 return ":disabled"
141 }
142
143 func (c checkedPseudoClassSelector) String() string {
144 return ":checked"
145 }
146
147 func (c compoundSelector) String() string {
148 if len(c.selectors) == 0 && c.pseudoElement == "" {
149 return "*"
150 }
151 chunks := make([]string, len(c.selectors))
152 for i, sel := range c.selectors {
153 chunks[i] = sel.String()
154 }
155 s := strings.Join(chunks, "")
156 if c.pseudoElement != "" {
157 s += "::" + c.pseudoElement
158 }
159 return s
160 }
161
162 func (c combinedSelector) String() string {
163 start := c.first.String()
164 if c.second != nil {
165 start += fmt.Sprintf(" %s %s", string(c.combinator), c.second.String())
166 }
167 return start
168 }
169
170 func (c SelectorGroup) String() string {
171 ck := make([]string, len(c))
172 for i, s := range c {
173 ck[i] = s.String()
174 }
175 return strings.Join(ck, ", ")
176 }
177
View as plain text