1package inliner
2
3import (
4	"fmt"
5	"regexp"
6	"strings"
7
8	"github.com/aymerick/douceur/css"
9)
10
11const (
12	inlineFakeSelector = "*INLINE*"
13
14	// Regular expressions borrowed from premailer:
15	//   https://github.com/premailer/css_parser/blob/master/lib/css_parser/regexps.rb
16	nonIDAttributesAndPseudoClassesRegexpConst = `(?i)(\.[\w]+)|\[(\w+)|(\:(link|visited|active|hover|focus|lang|target|enabled|disabled|checked|indeterminate|root|nth-child|nth-last-child|nth-of-type|nth-last-of-type|first-child|last-child|first-of-type|last-of-type|only-child|only-of-type|empty|contains))`
17	elementsAndPseudoElementsRegexpConst       = `(?i)((^|[\s\+\>\~]+)[\w]+|\:{1,2}(after|before|first-letter|first-line|selection))`
18)
19
20var (
21	nonIDAttrAndPseudoClassesRegexp *regexp.Regexp
22	elementsAndPseudoElementsRegexp *regexp.Regexp
23)
24
25// StyleRule represents a Qualifier Rule for a uniq selector
26type StyleRule struct {
27	// The style rule selector
28	Selector string
29
30	// The style rule properties
31	Declarations []*css.Declaration
32
33	// Selector specificity
34	Specificity int
35}
36
37func init() {
38	nonIDAttrAndPseudoClassesRegexp, _ = regexp.Compile(nonIDAttributesAndPseudoClassesRegexpConst)
39	elementsAndPseudoElementsRegexp, _ = regexp.Compile(elementsAndPseudoElementsRegexpConst)
40}
41
42// NewStyleRule instanciates a new StyleRule
43func NewStyleRule(selector string, declarations []*css.Declaration) *StyleRule {
44	return &StyleRule{
45		Selector:     selector,
46		Declarations: declarations,
47		Specificity:  ComputeSpecificity(selector),
48	}
49}
50
51// Returns the string representation of a style rule
52func (styleRule *StyleRule) String() string {
53	result := ""
54
55	result += styleRule.Selector
56
57	if len(styleRule.Declarations) == 0 {
58		result += ";"
59	} else {
60		result += " {\n"
61
62		for _, decl := range styleRule.Declarations {
63			result += fmt.Sprintf("  %s\n", decl.String())
64		}
65
66		result += "}"
67	}
68
69	return result
70}
71
72// ComputeSpecificity computes style rule specificity
73//
74// cf. http://www.w3.org/TR/selectors/#specificity
75func ComputeSpecificity(selector string) int {
76	result := 0
77
78	if selector == inlineFakeSelector {
79		result += 1000
80	}
81
82	result += 100 * strings.Count(selector, "#")
83	result += 10 * len(nonIDAttrAndPseudoClassesRegexp.FindAllStringSubmatch(selector, -1))
84	result += len(elementsAndPseudoElementsRegexp.FindAllStringSubmatch(selector, -1))
85
86	return result
87}
88