element.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package premailer
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/PuerkitoBio/goquery"
  6. "github.com/vanng822/css"
  7. )
  8. type void struct{}
  9. type elementRules struct {
  10. element *goquery.Selection
  11. rules []*styleRule
  12. cssToAttributes bool
  13. keepBangImportant bool
  14. }
  15. func (er *elementRules) inline() {
  16. inline, _ := er.element.Attr("style")
  17. inlineStyles := make([]*css.CSSStyleDeclaration, 0)
  18. if inline != "" {
  19. inlineStyles = css.ParseBlock(inline)
  20. }
  21. // we collect all occurrences
  22. orders := make([]string, 0)
  23. styles := make(map[string]string)
  24. for _, rule := range er.rules {
  25. for _, s := range rule.styles {
  26. prop := s.Property
  27. styles[prop] = s.Value.Text()
  28. if er.keepBangImportant && s.Important {
  29. styles[prop] += " !important"
  30. }
  31. orders = append(orders, prop)
  32. }
  33. }
  34. if len(inlineStyles) > 0 {
  35. for _, s := range inlineStyles {
  36. prop := s.Property
  37. styles[prop] = s.Value.Text()
  38. orders = append(orders, prop)
  39. }
  40. }
  41. // now collect the order of property
  42. // each prop will be unique and the last one
  43. // should have the highest priority
  44. // We could inline them all but this will make output cleaner
  45. props := make([]string, 0)
  46. seen := make(map[string]void)
  47. for i := len(orders) - 1; i >= 0; i-- {
  48. prop := orders[i]
  49. if _, ok := seen[prop]; !ok {
  50. seen[prop] = void{}
  51. props = append(props, prop)
  52. }
  53. }
  54. final := make([]string, 0, len(styles))
  55. for i := len(props) - 1; i >= 0; i-- {
  56. p := props[i]
  57. v := styles[p]
  58. final = append(final, fmt.Sprintf("%s:%s", p, v))
  59. if er.cssToAttributes {
  60. er.styleToBasicHtmlAttribute(p, v)
  61. }
  62. }
  63. style := strings.Join(final, ";")
  64. if style != "" {
  65. er.element.SetAttr("style", style)
  66. }
  67. }
  68. func (er *elementRules) styleToBasicHtmlAttribute(prop, value string) {
  69. switch prop {
  70. case "width":
  71. fallthrough
  72. case "height":
  73. // If we are keeping "!important" in our styles, we need to strip it out
  74. // here so that the proper px value can be parsed
  75. if er.keepBangImportant {
  76. value = strings.Replace(value, " !important", "", 1)
  77. }
  78. if strings.HasSuffix(value, "px") {
  79. value = value[:len(value)-2]
  80. er.element.SetAttr(prop, value)
  81. } else if value == "0" {
  82. er.element.SetAttr(prop, value)
  83. }
  84. }
  85. }