serialize.go

  1package cascadia
  2
  3import (
  4	"fmt"
  5	"strconv"
  6	"strings"
  7)
  8
  9// implements the reverse operation Sel -> string
 10
 11var specialCharReplacer *strings.Replacer
 12
 13func 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// espace special CSS char
 22func escape(s string) string { return specialCharReplacer.Replace(s) }
 23
 24func (c tagSelector) String() string {
 25	return c.tag
 26}
 27
 28func (c idSelector) String() string {
 29	return "#" + escape(c.id)
 30}
 31
 32func (c classSelector) String() string {
 33	return "." + escape(c.class)
 34}
 35
 36func (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
 53func (c relativePseudoClassSelector) String() string {
 54	return fmt.Sprintf(":%s(%s)", c.name, c.match.String())
 55}
 56
 57func (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
 65func (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
 73func (c nthPseudoClassSelector) String() string {
 74	if c.a == 0 && c.b == 1 { // special cases
 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 { // avoid +-8 invalid syntax
 99		s = strconv.Itoa(c.b)
100	}
101	return fmt.Sprintf(":%s(%dn%s)", name, c.a, s)
102}
103
104func (c onlyChildPseudoClassSelector) String() string {
105	if c.ofType {
106		return ":only-of-type"
107	}
108	return ":only-child"
109}
110
111func (c inputPseudoClassSelector) String() string {
112	return ":input"
113}
114
115func (c emptyElementPseudoClassSelector) String() string {
116	return ":empty"
117}
118
119func (c rootPseudoClassSelector) String() string {
120	return ":root"
121}
122
123func (c linkPseudoClassSelector) String() string {
124	return ":link"
125}
126
127func (c langPseudoClassSelector) String() string {
128	return fmt.Sprintf(":lang(%s)", c.lang)
129}
130
131func (c neverMatchSelector) String() string {
132	return c.value
133}
134
135func (c enabledPseudoClassSelector) String() string {
136	return ":enabled"
137}
138
139func (c disabledPseudoClassSelector) String() string {
140	return ":disabled"
141}
142
143func (c checkedPseudoClassSelector) String() string {
144	return ":checked"
145}
146
147func (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
162func (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
170func (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}