vendor dependencies with dep
This commit is contained in:
parent
93d8310491
commit
1384296a47
2712 changed files with 965742 additions and 0 deletions
3
vendor/github.com/vanng822/css/.gitignore
generated
vendored
Normal file
3
vendor/github.com/vanng822/css/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/src/
|
||||
/pkg/
|
||||
/bin/
|
||||
16
vendor/github.com/vanng822/css/.travis.yml
generated
vendored
Normal file
16
vendor/github.com/vanng822/css/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
language: golang
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOPATH="$HOME/gopath"
|
||||
- PATH="$HOME/gopath/bin:$HOME/bin:$PATH"
|
||||
|
||||
install:
|
||||
- go get github.com/gorilla/css/scanner
|
||||
- go get github.com/stretchr/testify/assert
|
||||
|
||||
script:
|
||||
- go test -v
|
||||
23
vendor/github.com/vanng822/css/LICENSE
generated
vendored
Normal file
23
vendor/github.com/vanng822/css/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2015 Nguyen Van Nhu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
23
vendor/github.com/vanng822/css/README.md
generated
vendored
Normal file
23
vendor/github.com/vanng822/css/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# css
|
||||
|
||||
Package css is for parsing css stylesheet.
|
||||
|
||||
# Document
|
||||
|
||||
[](https://godoc.org/github.com/vanng822/css)
|
||||
|
||||
# example
|
||||
|
||||
import (
|
||||
"github.com/vanng822/css"
|
||||
"fmt"
|
||||
)
|
||||
func main() {
|
||||
csstext = "td {width: 100px; height: 100px;}"
|
||||
ss := css.Parse(csstext)
|
||||
rules := ss.GetCSSRuleList()
|
||||
for _, rule := range rules {
|
||||
fmt.Println(rule.Style.SelectorText)
|
||||
fmt.Println(rule.Style.Styles)
|
||||
}
|
||||
}
|
||||
140
vendor/github.com/vanng822/css/block_parser.go
generated
vendored
Normal file
140
vendor/github.com/vanng822/css/block_parser.go
generated
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"github.com/gorilla/css/scanner"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type blockParserContext struct {
|
||||
State State
|
||||
NowProperty string
|
||||
NowValue string
|
||||
NowImportant int
|
||||
}
|
||||
|
||||
// ParseBlock take a string of a css block,
|
||||
// parses it and returns a map of css style declarations.
|
||||
func ParseBlock(csstext string) map[string]*CSSStyleDeclaration {
|
||||
s := scanner.New(csstext)
|
||||
return parseBlock(s)
|
||||
}
|
||||
|
||||
func parseBlock(s *scanner.Scanner) map[string]*CSSStyleDeclaration {
|
||||
/* block : '{' S* [ any | block | ATKEYWORD S* | ';' S* ]* '}' S*;
|
||||
property : IDENT;
|
||||
value : [ any | block | ATKEYWORD S* ]+;
|
||||
any : [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
|
||||
| DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
|
||||
| DASHMATCH | ':' | FUNCTION S* [any|unused]* ')'
|
||||
| '(' S* [any|unused]* ')' | '[' S* [any|unused]* ']'
|
||||
] S*;
|
||||
*/
|
||||
decls := make(map[string]*CSSStyleDeclaration)
|
||||
|
||||
context := &blockParserContext{
|
||||
State: STATE_NONE,
|
||||
NowProperty: "",
|
||||
NowValue: "",
|
||||
NowImportant: 0,
|
||||
}
|
||||
|
||||
for {
|
||||
token := s.Next()
|
||||
|
||||
//fmt.Printf("BLOCK(%d): %s:'%s'\n", context.State, token.Type.String(), token.Value)
|
||||
|
||||
if token.Type == scanner.TokenError {
|
||||
break
|
||||
}
|
||||
|
||||
if token.Type == scanner.TokenEOF {
|
||||
if context.State == STATE_VALUE {
|
||||
// we are ending without ; or }
|
||||
// this can happen when we parse only css declaration
|
||||
decl := NewCSSStyleDeclaration(context.NowProperty, strings.TrimSpace(context.NowValue), context.NowImportant)
|
||||
decls[context.NowProperty] = decl
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
switch token.Type {
|
||||
|
||||
case scanner.TokenS:
|
||||
if context.State == STATE_VALUE {
|
||||
context.NowValue += token.Value
|
||||
}
|
||||
case scanner.TokenIdent:
|
||||
if context.State == STATE_NONE {
|
||||
context.State = STATE_PROPERTY
|
||||
context.NowProperty = strings.TrimSpace(token.Value)
|
||||
break
|
||||
}
|
||||
if token.Value == "important" {
|
||||
context.NowImportant = 1
|
||||
} else {
|
||||
context.NowValue += token.Value
|
||||
}
|
||||
case scanner.TokenChar:
|
||||
if context.State == STATE_NONE {
|
||||
if token.Value == "{" {
|
||||
break
|
||||
}
|
||||
}
|
||||
if context.State == STATE_PROPERTY {
|
||||
if token.Value == ":" {
|
||||
context.State = STATE_VALUE
|
||||
}
|
||||
// CHAR and STATE_PROPERTY but not : then weird
|
||||
// break to ignore it
|
||||
break
|
||||
}
|
||||
// should be no state or value
|
||||
if token.Value == ";" {
|
||||
decl := NewCSSStyleDeclaration(context.NowProperty, strings.TrimSpace(context.NowValue), context.NowImportant)
|
||||
decls[context.NowProperty] = decl
|
||||
context.NowProperty = ""
|
||||
context.NowValue = ""
|
||||
context.NowImportant = 0
|
||||
context.State = STATE_NONE
|
||||
} else if token.Value == "}" { // last property in a block can have optional ;
|
||||
if context.State == STATE_VALUE {
|
||||
// only valid if state is still VALUE, could be ;}
|
||||
decl := NewCSSStyleDeclaration(context.NowProperty, strings.TrimSpace(context.NowValue), context.NowImportant)
|
||||
decls[context.NowProperty] = decl
|
||||
}
|
||||
// we are done
|
||||
return decls
|
||||
} else if token.Value != "!" {
|
||||
context.NowValue += token.Value
|
||||
}
|
||||
break
|
||||
|
||||
// any
|
||||
case scanner.TokenNumber:
|
||||
fallthrough
|
||||
case scanner.TokenPercentage:
|
||||
fallthrough
|
||||
case scanner.TokenDimension:
|
||||
fallthrough
|
||||
case scanner.TokenString:
|
||||
fallthrough
|
||||
case scanner.TokenURI:
|
||||
fallthrough
|
||||
case scanner.TokenHash:
|
||||
fallthrough
|
||||
case scanner.TokenUnicodeRange:
|
||||
fallthrough
|
||||
case scanner.TokenIncludes:
|
||||
fallthrough
|
||||
case scanner.TokenDashMatch:
|
||||
fallthrough
|
||||
case scanner.TokenFunction:
|
||||
fallthrough
|
||||
case scanner.TokenSubstringMatch:
|
||||
context.NowValue += token.Value
|
||||
}
|
||||
}
|
||||
|
||||
return decls
|
||||
}
|
||||
50
vendor/github.com/vanng822/css/block_parser_test.go
generated
vendored
Normal file
50
vendor/github.com/vanng822/css/block_parser_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
func TestParseBlock(t *testing.T) {
|
||||
css := ParseBlock(`
|
||||
font-family: "Source Sans Pro", Arial, sans-serif;
|
||||
font-size: 27px;
|
||||
line-height: 35px;`)
|
||||
|
||||
assert.Equal(t, len(css), 3)
|
||||
assert.Equal(t, "35px", css["line-height"].Value)
|
||||
}
|
||||
|
||||
func TestParseBlockOneLine(t *testing.T) {
|
||||
css := ParseBlock("font-family: \"Source Sans Pro\", Arial, sans-serif; font-size: 27px;")
|
||||
|
||||
assert.Equal(t, len(css), 2)
|
||||
assert.Equal(t, "27px", css["font-size"].Value)
|
||||
assert.Equal(t, "\"Source Sans Pro\", Arial, sans-serif", css["font-family"].Value)
|
||||
}
|
||||
|
||||
func TestParseBlockBlankEnd(t *testing.T) {
|
||||
css := ParseBlock("font-size: 27px; width: 10px")
|
||||
|
||||
assert.Equal(t, len(css), 2)
|
||||
assert.Equal(t, "27px", css["font-size"].Value)
|
||||
assert.Equal(t, "10px", css["width"].Value)
|
||||
}
|
||||
|
||||
func TestParseBlockInportant(t *testing.T) {
|
||||
css := ParseBlock("font-size: 27px; width: 10px !important")
|
||||
|
||||
assert.Equal(t, len(css), 2)
|
||||
assert.Equal(t, "27px", css["font-size"].Value)
|
||||
assert.Equal(t, "10px", css["width"].Value)
|
||||
assert.Equal(t, 1, css["width"].Important)
|
||||
}
|
||||
|
||||
func TestParseBlockWithBraces(t *testing.T) {
|
||||
css := ParseBlock("{ font-size: 27px; width: 10px }")
|
||||
|
||||
assert.Equal(t, len(css), 2)
|
||||
assert.Equal(t, "27px", css["font-size"].Value)
|
||||
assert.Equal(t, "10px", css["width"].Value)
|
||||
}
|
||||
51
vendor/github.com/vanng822/css/charset_parser.go
generated
vendored
Normal file
51
vendor/github.com/vanng822/css/charset_parser.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"github.com/gorilla/css/scanner"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func newCharsetRule(statement string) *CSSRule {
|
||||
statement = strings.TrimSpace(statement)
|
||||
if statement != "" {
|
||||
rule := NewRule(CHARSET_RULE)
|
||||
rule.Style.SelectorText = statement
|
||||
return rule
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseCharset(s *scanner.Scanner) *CSSRule {
|
||||
/*
|
||||
|
||||
Syntax:
|
||||
@charset charset;
|
||||
|
||||
Example:
|
||||
@charset "UTF-8";
|
||||
|
||||
*/
|
||||
|
||||
var statement string
|
||||
for {
|
||||
token := s.Next()
|
||||
|
||||
//fmt.Printf("Import: %s:'%s'\n", token.Type.String(), token.Value)
|
||||
|
||||
if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
|
||||
return nil
|
||||
}
|
||||
// take everything for now
|
||||
switch token.Type {
|
||||
case scanner.TokenChar:
|
||||
if token.Value == ";" {
|
||||
return newCharsetRule(statement)
|
||||
}
|
||||
statement += token.Value
|
||||
default:
|
||||
statement += token.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
27
vendor/github.com/vanng822/css/charset_parser_test.go
generated
vendored
Normal file
27
vendor/github.com/vanng822/css/charset_parser_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"github.com/gorilla/css/scanner"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCharsetDoubleQ(t *testing.T) {
|
||||
css := Parse(`@charset "UTF-8";`)
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "\"UTF-8\"")
|
||||
assert.Equal(t, css.CssRuleList[0].Type, CHARSET_RULE)
|
||||
}
|
||||
|
||||
func TestCharsetSingleQ(t *testing.T) {
|
||||
css := Parse(`@charset 'iso-8859-15';`)
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "'iso-8859-15'")
|
||||
assert.Equal(t, css.CssRuleList[0].Type, CHARSET_RULE)
|
||||
}
|
||||
|
||||
func TestCharsetIgnore(t *testing.T) {
|
||||
css := parseCharset(scanner.New(` 'iso-8859-15'`))
|
||||
|
||||
assert.Nil(t, css)
|
||||
}
|
||||
16
vendor/github.com/vanng822/css/doc.go
generated
vendored
Normal file
16
vendor/github.com/vanng822/css/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Package css is for parsing css stylesheet.
|
||||
//
|
||||
// import (
|
||||
// "github.com/vanng822/css"
|
||||
// "fmt"
|
||||
// )
|
||||
// func main() {
|
||||
// csstext = "td {width: 100px; height: 100px;}"
|
||||
// ss := css.Parse(csstext)
|
||||
// rules := ss.GetCSSRuleList()
|
||||
// for _, rule := range rules {
|
||||
// fmt.Println(rule.Style.SelectorText)
|
||||
// fmt.Println(rule.Style.Styles)
|
||||
// }
|
||||
// }
|
||||
package css
|
||||
56
vendor/github.com/vanng822/css/import_parser.go
generated
vendored
Normal file
56
vendor/github.com/vanng822/css/import_parser.go
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"github.com/gorilla/css/scanner"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func newImportRule(statement string) *CSSRule {
|
||||
statement = strings.TrimSpace(statement)
|
||||
if statement != "" {
|
||||
rule := NewRule(IMPORT_RULE)
|
||||
rule.Style.SelectorText = statement
|
||||
return rule
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseImport(s *scanner.Scanner) *CSSRule {
|
||||
/*
|
||||
Syntax:
|
||||
@import url; or
|
||||
@import url list-of-media-queries;
|
||||
|
||||
Example:
|
||||
@import url("fineprint.css") print;
|
||||
@import url("bluish.css") projection, tv;
|
||||
@import 'custom.css';
|
||||
@import url("chrome://communicator/skin/");
|
||||
@import "common.css" screen, projection;
|
||||
@import url('landscape.css') screen and (orientation:landscape);
|
||||
|
||||
*/
|
||||
|
||||
var statement string
|
||||
for {
|
||||
token := s.Next()
|
||||
|
||||
//fmt.Printf("Import: %s:'%s'\n", token.Type.String(), token.Value)
|
||||
|
||||
if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
|
||||
return nil
|
||||
}
|
||||
// take everything for now
|
||||
switch token.Type {
|
||||
case scanner.TokenChar:
|
||||
if token.Value == ";" {
|
||||
return newImportRule(statement)
|
||||
}
|
||||
statement += token.Value
|
||||
default:
|
||||
statement += token.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
35
vendor/github.com/vanng822/css/import_parser_test.go
generated
vendored
Normal file
35
vendor/github.com/vanng822/css/import_parser_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"github.com/gorilla/css/scanner"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestImport(t *testing.T) {
|
||||
css := Parse(`@import url("fineprint.css") print;
|
||||
@import url("bluish.css") projection, tv;
|
||||
@import 'custom.css';
|
||||
@import url("chrome://communicator/skin/");
|
||||
@import "common.css" screen, projection;
|
||||
@import url('landscape.css') screen and (orientation:landscape);`)
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "url(\"fineprint.css\") print")
|
||||
assert.Equal(t, css.CssRuleList[1].Style.SelectorText, "url(\"bluish.css\") projection, tv")
|
||||
assert.Equal(t, css.CssRuleList[2].Style.SelectorText, "'custom.css'")
|
||||
assert.Equal(t, css.CssRuleList[3].Style.SelectorText, "url(\"chrome://communicator/skin/\")")
|
||||
assert.Equal(t, css.CssRuleList[4].Style.SelectorText, "\"common.css\" screen, projection")
|
||||
assert.Equal(t, css.CssRuleList[5].Style.SelectorText, "url('landscape.css') screen and (orientation:landscape)")
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Type, IMPORT_RULE)
|
||||
assert.Equal(t, css.CssRuleList[1].Type, IMPORT_RULE)
|
||||
assert.Equal(t, css.CssRuleList[2].Type, IMPORT_RULE)
|
||||
assert.Equal(t, css.CssRuleList[3].Type, IMPORT_RULE)
|
||||
assert.Equal(t, css.CssRuleList[4].Type, IMPORT_RULE)
|
||||
assert.Equal(t, css.CssRuleList[5].Type, IMPORT_RULE)
|
||||
}
|
||||
|
||||
func TestImportIgnore(t *testing.T) {
|
||||
css := parseImport(scanner.New(` url("fineprint.css") print`))
|
||||
assert.Nil(t, css)
|
||||
}
|
||||
154
vendor/github.com/vanng822/css/parser.go
generated
vendored
Normal file
154
vendor/github.com/vanng822/css/parser.go
generated
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gorilla/css/scanner"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
stylesheet : [ CDO | CDC | S | statement ]*;
|
||||
statement : ruleset | at-rule;
|
||||
at-rule : ATKEYWORD S* any* [ block | ';' S* ];
|
||||
block : '{' S* [ any | block | ATKEYWORD S* | ';' S* ]* '}' S*;
|
||||
ruleset : selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*;
|
||||
selector : any+;
|
||||
declaration : property S* ':' S* value;
|
||||
property : IDENT;
|
||||
value : [ any | block | ATKEYWORD S* ]+;
|
||||
any : [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
|
||||
| DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
|
||||
| DASHMATCH | ':' | FUNCTION S* [any|unused]* ')'
|
||||
| '(' S* [any|unused]* ')' | '[' S* [any|unused]* ']'
|
||||
] S*;
|
||||
unused : block | ATKEYWORD S* | ';' S* | CDO S* | CDC S*;
|
||||
*/
|
||||
|
||||
type State int
|
||||
|
||||
const (
|
||||
STATE_NONE State = iota
|
||||
STATE_SELECTOR
|
||||
STATE_PROPERTY
|
||||
STATE_VALUE
|
||||
)
|
||||
|
||||
type parserContext struct {
|
||||
State State
|
||||
NowSelectorText string
|
||||
NowRuleType RuleType
|
||||
CurrentRule *CSSRule
|
||||
CurrentMediaRule *CSSRule
|
||||
}
|
||||
|
||||
func resetContextStyleRule(context *parserContext) {
|
||||
context.CurrentRule = nil
|
||||
context.NowSelectorText = ""
|
||||
context.NowRuleType = STYLE_RULE
|
||||
context.State = STATE_NONE
|
||||
}
|
||||
|
||||
func parseRule(context *parserContext, s *scanner.Scanner, css *CSSStyleSheet) {
|
||||
context.CurrentRule = NewRule(context.NowRuleType)
|
||||
context.NowSelectorText += parseSelector(s)
|
||||
context.CurrentRule.Style.SelectorText = strings.TrimSpace(context.NowSelectorText)
|
||||
context.CurrentRule.Style.Styles = parseBlock(s)
|
||||
if context.CurrentMediaRule != nil {
|
||||
context.CurrentMediaRule.Rules = append(context.CurrentMediaRule.Rules, context.CurrentRule)
|
||||
} else {
|
||||
css.CssRuleList = append(css.CssRuleList, context.CurrentRule)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse takes a string of valid css rules, stylesheet,
|
||||
// and parses it. Be aware this function has poor error handling
|
||||
// so you should have valid syntax in your css
|
||||
func Parse(csstext string) *CSSStyleSheet {
|
||||
context := &parserContext{
|
||||
State: STATE_NONE,
|
||||
NowSelectorText: "",
|
||||
NowRuleType: STYLE_RULE,
|
||||
CurrentMediaRule: nil,
|
||||
}
|
||||
|
||||
css := &CSSStyleSheet{}
|
||||
css.CssRuleList = make([]*CSSRule, 0)
|
||||
s := scanner.New(csstext)
|
||||
|
||||
for {
|
||||
token := s.Next()
|
||||
|
||||
//fmt.Printf("Parse(%d): %s:'%s'\n", context.State, token.Type.String(), token.Value)
|
||||
|
||||
if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
|
||||
break
|
||||
}
|
||||
|
||||
switch token.Type {
|
||||
case scanner.TokenCDO:
|
||||
break
|
||||
case scanner.TokenCDC:
|
||||
break
|
||||
case scanner.TokenComment:
|
||||
break
|
||||
case scanner.TokenS:
|
||||
break
|
||||
case scanner.TokenAtKeyword:
|
||||
switch token.Value {
|
||||
case "@media":
|
||||
context.NowRuleType = MEDIA_RULE
|
||||
case "@font-face":
|
||||
// Parse as normal rule, would be nice to parse according to syntax
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face
|
||||
context.NowRuleType = FONT_FACE_RULE
|
||||
parseRule(context, s, css)
|
||||
resetContextStyleRule(context)
|
||||
case "@import":
|
||||
// No validation
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/@import
|
||||
rule := parseImport(s)
|
||||
if rule != nil {
|
||||
css.CssRuleList = append(css.CssRuleList, rule)
|
||||
}
|
||||
resetContextStyleRule(context)
|
||||
case "@charset":
|
||||
// No validation
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/@charset
|
||||
rule := parseCharset(s)
|
||||
if rule != nil {
|
||||
css.CssRuleList = append(css.CssRuleList, rule)
|
||||
}
|
||||
resetContextStyleRule(context)
|
||||
|
||||
case "@page":
|
||||
context.NowRuleType = PAGE_RULE
|
||||
parseRule(context, s, css)
|
||||
resetContextStyleRule(context)
|
||||
default:
|
||||
panic(fmt.Sprintf("At rule '%s' is not supported", token.Value))
|
||||
}
|
||||
default:
|
||||
if context.State == STATE_NONE {
|
||||
if token.Value == "}" && context.CurrentMediaRule != nil {
|
||||
// close media rule
|
||||
css.CssRuleList = append(css.CssRuleList, context.CurrentMediaRule)
|
||||
context.CurrentMediaRule = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if context.NowRuleType == MEDIA_RULE {
|
||||
context.CurrentMediaRule = NewRule(context.NowRuleType)
|
||||
context.CurrentMediaRule.Style.SelectorText = strings.TrimSpace(token.Value + parseSelector(s))
|
||||
resetContextStyleRule(context)
|
||||
break
|
||||
} else {
|
||||
context.NowSelectorText += token.Value
|
||||
parseRule(context, s, css)
|
||||
resetContextStyleRule(context)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return css
|
||||
}
|
||||
103
vendor/github.com/vanng822/css/parser_media_test.go
generated
vendored
Normal file
103
vendor/github.com/vanng822/css/parser_media_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMedia(t *testing.T) {
|
||||
css := Parse(`@media only screen and (max-width: 600px) {
|
||||
table[class="body"] img {
|
||||
width: auto !important;
|
||||
height: auto !important
|
||||
}
|
||||
table[class="body"] center {
|
||||
min-width: 0 !important
|
||||
}
|
||||
table[class="body"] .container {
|
||||
width: 95% !important
|
||||
}
|
||||
table[class="body"] .row {
|
||||
width: 100% !important;
|
||||
display: block !important
|
||||
}
|
||||
}`)
|
||||
|
||||
//fmt.Println(css)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "only screen and (max-width: 600px)")
|
||||
assert.Equal(t, css.CssRuleList[0].Type, MEDIA_RULE)
|
||||
assert.Equal(t, len(css.CssRuleList[0].Rules), 4)
|
||||
assert.Equal(t, css.CssRuleList[0].Rules[0].Style.SelectorText, "table[class=\"body\"] img")
|
||||
assert.Equal(t, css.CssRuleList[0].Rules[0].Style.Styles["height"].Value, "auto")
|
||||
assert.Equal(t, css.CssRuleList[0].Rules[0].Style.Styles["height"].Important, 1)
|
||||
assert.Equal(t, css.CssRuleList[0].Rules[1].Style.SelectorText, "table[class=\"body\"] center")
|
||||
assert.Equal(t, css.CssRuleList[0].Rules[2].Style.SelectorText, "table[class=\"body\"] .container")
|
||||
assert.Equal(t, css.CssRuleList[0].Rules[3].Style.SelectorText, "table[class=\"body\"] .row")
|
||||
|
||||
}
|
||||
|
||||
func TestMediaMulti(t *testing.T) {
|
||||
css := Parse(`
|
||||
table.one {
|
||||
width: 30px;
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
table[class="body"] img {
|
||||
width: auto !important;
|
||||
height: auto !important
|
||||
}
|
||||
table[class="body"] center {
|
||||
min-width: 0 !important
|
||||
}
|
||||
table[class="body"] .container {
|
||||
width: 95% !important
|
||||
}
|
||||
table[class="body"] .row {
|
||||
width: 100% !important;
|
||||
display: block !important
|
||||
}
|
||||
}
|
||||
@media all and (min-width: 48em) {
|
||||
blockquote {
|
||||
font-size: 34px;
|
||||
line-height: 40px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
}
|
||||
table.two {
|
||||
width: 80px;
|
||||
}`)
|
||||
|
||||
assert.Equal(t, len(css.CssRuleList), 4)
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "table.one")
|
||||
assert.Equal(t, css.CssRuleList[0].Type, STYLE_RULE)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["width"].Value, "30px")
|
||||
assert.Equal(t, len(css.CssRuleList[0].Rules), 0)
|
||||
|
||||
assert.Equal(t, css.CssRuleList[1].Style.SelectorText, "only screen and (max-width: 600px)")
|
||||
assert.Equal(t, css.CssRuleList[1].Type, MEDIA_RULE)
|
||||
assert.Equal(t, len(css.CssRuleList[1].Rules), 4)
|
||||
assert.Equal(t, css.CssRuleList[1].Rules[0].Style.SelectorText, "table[class=\"body\"] img")
|
||||
assert.Equal(t, css.CssRuleList[1].Rules[0].Style.Styles["height"].Value, "auto")
|
||||
assert.Equal(t, css.CssRuleList[1].Rules[0].Style.Styles["height"].Important, 1)
|
||||
assert.Equal(t, css.CssRuleList[1].Rules[1].Style.SelectorText, "table[class=\"body\"] center")
|
||||
assert.Equal(t, css.CssRuleList[1].Rules[2].Style.SelectorText, "table[class=\"body\"] .container")
|
||||
assert.Equal(t, css.CssRuleList[1].Rules[3].Style.SelectorText, "table[class=\"body\"] .row")
|
||||
|
||||
assert.Equal(t, css.CssRuleList[2].Style.SelectorText, "all and (min-width: 48em)")
|
||||
assert.Equal(t, css.CssRuleList[2].Type, MEDIA_RULE)
|
||||
assert.Equal(t, css.CssRuleList[2].Rules[0].Style.SelectorText, "blockquote")
|
||||
assert.Equal(t, css.CssRuleList[2].Rules[0].Style.Styles["font-size"].Value, "34px")
|
||||
assert.Equal(t, css.CssRuleList[2].Rules[0].Style.Styles["line-height"].Value, "40px")
|
||||
assert.Equal(t, css.CssRuleList[2].Rules[0].Style.Styles["padding-top"].Value, "2px")
|
||||
assert.Equal(t, css.CssRuleList[2].Rules[0].Style.Styles["padding-bottom"].Value, "3px")
|
||||
assert.Equal(t, len(css.CssRuleList[2].Rules), 1)
|
||||
|
||||
assert.Equal(t, css.CssRuleList[3].Style.SelectorText, "table.two")
|
||||
assert.Equal(t, css.CssRuleList[3].Type, STYLE_RULE)
|
||||
assert.Equal(t, css.CssRuleList[3].Style.Styles["width"].Value, "80px")
|
||||
assert.Equal(t, len(css.CssRuleList[3].Rules), 0)
|
||||
}
|
||||
175
vendor/github.com/vanng822/css/parser_selector_test.go
generated
vendored
Normal file
175
vendor/github.com/vanng822/css/parser_selector_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMultipleSelectors(t *testing.T) {
|
||||
css := Parse(`div .a {
|
||||
font-size: 150%;
|
||||
}
|
||||
p .b {
|
||||
font-size: 250%;
|
||||
}`)
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "div .a")
|
||||
assert.Equal(t, css.CssRuleList[1].Style.SelectorText, "p .b")
|
||||
|
||||
}
|
||||
|
||||
func TestIdSelector(t *testing.T) {
|
||||
css := Parse("#div { color: red;}")
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["color"].Value, "red")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "#div")
|
||||
}
|
||||
|
||||
func TestClassSelector(t *testing.T) {
|
||||
css := Parse(".div { color: green;}")
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["color"].Value, "green")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, ".div")
|
||||
}
|
||||
|
||||
func TestStarSelector(t *testing.T) {
|
||||
css := Parse("* { text-rendering: optimizelegibility; }")
|
||||
|
||||
assert.Equal(t, "optimizelegibility", css.CssRuleList[0].Style.Styles["text-rendering"].Value)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "*")
|
||||
}
|
||||
|
||||
func TestStarSelectorMulti(t *testing.T) {
|
||||
css := Parse(`div .a {
|
||||
font-size: 150%;
|
||||
}
|
||||
* { text-rendering: optimizelegibility; }`)
|
||||
|
||||
assert.Equal(t, "150%", css.CssRuleList[0].Style.Styles["font-size"].Value)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "div .a")
|
||||
|
||||
assert.Equal(t, "optimizelegibility", css.CssRuleList[1].Style.Styles["text-rendering"].Value)
|
||||
assert.Equal(t, css.CssRuleList[1].Style.SelectorText, "*")
|
||||
}
|
||||
|
||||
func TestMixedClassSelectors(t *testing.T) {
|
||||
selectors := []string{".footer__content_wrapper--last",
|
||||
"table[class=\"body\"] .footer__content td",
|
||||
"table[class=\"body\"] td.footer__link_wrapper--first",
|
||||
"table[class=\"body\"] td.footer__link_wrapper--last"}
|
||||
|
||||
for _, selector := range selectors {
|
||||
css := Parse(fmt.Sprintf(` %s {
|
||||
border-collapse: separate;
|
||||
padding: 10px 0 0
|
||||
}`, selector))
|
||||
|
||||
assert.Equal(t, "separate", css.CssRuleList[0].Style.Styles["border-collapse"].Value)
|
||||
assert.Equal(t, "10px 0 0", css.CssRuleList[0].Style.Styles["padding"].Value)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, selector)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenericSelectors(t *testing.T) {
|
||||
selectors := []string{
|
||||
"p ~ ul",
|
||||
"div > p",
|
||||
"div > p",
|
||||
"div p",
|
||||
"div, p",
|
||||
"[target]",
|
||||
"[target=_blank]",
|
||||
"[title~=flower]",
|
||||
"[lang|=en]",
|
||||
"a[href^=\"https\"]",
|
||||
"a[href$=\".pdf\"]",
|
||||
"a[href*=\"css\"]",
|
||||
".header + .content",
|
||||
"#firstname",
|
||||
"table[class=\"body\"] .footer__content td",
|
||||
"table[class=\"body\"] td.footer__link_wrapper--first"}
|
||||
|
||||
for _, selector := range selectors {
|
||||
css := Parse(fmt.Sprintf(` %s {
|
||||
border-collapse: separate;
|
||||
padding: 10px 0 0
|
||||
}`, selector))
|
||||
|
||||
assert.Equal(t, "separate", css.CssRuleList[0].Style.Styles["border-collapse"].Value)
|
||||
assert.Equal(t, "10px 0 0", css.CssRuleList[0].Style.Styles["padding"].Value)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, selector)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterSelectors(t *testing.T) {
|
||||
selectors := []string{
|
||||
"a:active",
|
||||
"p::after",
|
||||
"p::before",
|
||||
"input:checked",
|
||||
"input:disabled",
|
||||
"input:in-range",
|
||||
"input:invalid",
|
||||
"input:optional",
|
||||
"input:read-only",
|
||||
"input:enabled",
|
||||
"p:empty",
|
||||
"p:first-child",
|
||||
"p::first-letter",
|
||||
"p::first-line",
|
||||
"p:first-of-type",
|
||||
"input:focus",
|
||||
"a:hover",
|
||||
"p:lang(it)",
|
||||
"p:last-child",
|
||||
"p:last-of-type",
|
||||
"a:link",
|
||||
":not(p)",
|
||||
"p:nth-child(2)",
|
||||
"p:nth-last-child(2)",
|
||||
"p:only-of-type",
|
||||
"p:only-child",
|
||||
"p:nth-last-of-type(2)",
|
||||
"div:not(:nth-child(1))",
|
||||
"div:not(:not(:first-child))",
|
||||
":root",
|
||||
"::selection",
|
||||
"#news:target"}
|
||||
|
||||
for _, selector := range selectors {
|
||||
css := Parse(fmt.Sprintf(` %s {
|
||||
border-collapse: separate;
|
||||
padding: 10px 0 0
|
||||
}`, selector))
|
||||
|
||||
assert.Equal(t, "separate", css.CssRuleList[0].Style.Styles["border-collapse"].Value)
|
||||
assert.Equal(t, "10px 0 0", css.CssRuleList[0].Style.Styles["padding"].Value)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, selector)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFontFace(t *testing.T) {
|
||||
css := Parse(`@font-face {
|
||||
font-family: "Bitstream Vera Serif Bold";
|
||||
src: url("https://mdn.mozillademos.org/files/2468/VeraSeBd.ttf");
|
||||
}
|
||||
|
||||
body { font-family: "Bitstream Vera Serif Bold", serif }`)
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-family"].Value, "\"Bitstream Vera Serif Bold\"")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["src"].Value, "url(\"https://mdn.mozillademos.org/files/2468/VeraSeBd.ttf\")")
|
||||
assert.Equal(t, css.CssRuleList[1].Style.Styles["font-family"].Value, "\"Bitstream Vera Serif Bold\", serif")
|
||||
assert.Equal(t, css.CssRuleList[0].Type, FONT_FACE_RULE)
|
||||
}
|
||||
|
||||
func TestPage(t *testing.T) {
|
||||
css := Parse(`@page :first {
|
||||
margin: 2in 3in;
|
||||
}`)
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, ":first")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["margin"].Value, "2in 3in")
|
||||
assert.Equal(t, css.CssRuleList[0].Type, PAGE_RULE)
|
||||
}
|
||||
148
vendor/github.com/vanng822/css/parser_test.go
generated
vendored
Normal file
148
vendor/github.com/vanng822/css/parser_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWithoutImpotant(t *testing.T) {
|
||||
css := Parse(`div .a { font-size: 150%;}`)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Value, "150%")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Property, "font-size")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Important, 0)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "div .a")
|
||||
|
||||
}
|
||||
|
||||
func TestWithImpotant(t *testing.T) {
|
||||
css := Parse("div .a { font-size: 150% !important;}")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Value, "150%")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Property, "font-size")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Important, 1)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "div .a")
|
||||
}
|
||||
|
||||
func TestMultipleDeclarations(t *testing.T) {
|
||||
css := Parse(`div .a {
|
||||
font-size: 150%;
|
||||
width: 100%
|
||||
}`)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Value, "150%")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Property, "font-size")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Important, 0)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["width"].Value, "100%")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["width"].Property, "width")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["width"].Important, 0)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "div .a")
|
||||
}
|
||||
|
||||
func TestValuePx(t *testing.T) {
|
||||
css := Parse("div .a { font-size: 45px;}")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Value, "45px")
|
||||
}
|
||||
|
||||
func TestValueEm(t *testing.T) {
|
||||
css := Parse("div .a { font-size: 45em;}")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["font-size"].Value, "45em")
|
||||
}
|
||||
|
||||
func TestValueHex(t *testing.T) {
|
||||
css := Parse("div .a { color: #123456;}")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["color"].Value, "#123456")
|
||||
}
|
||||
|
||||
func TestValueRGBFunction(t *testing.T) {
|
||||
css := Parse(".color{ color: rgb(1,2,3);}")
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["color"].Value, "rgb(1,2,3)")
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, ".color")
|
||||
}
|
||||
|
||||
func TestValueString(t *testing.T) {
|
||||
css := Parse("div .center { text-align: center; }")
|
||||
|
||||
assert.Equal(t, css.CssRuleList[0].Style.Styles["text-align"].Value, "center")
|
||||
}
|
||||
|
||||
func TestValueWhiteSpace(t *testing.T) {
|
||||
css := Parse(".div { padding: 10px 0 0 10px}")
|
||||
|
||||
assert.Equal(t, "10px 0 0 10px", css.CssRuleList[0].Style.Styles["padding"].Value)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, ".div")
|
||||
}
|
||||
|
||||
func TestValueMixed(t *testing.T) {
|
||||
css := Parse(`td {
|
||||
padding: 0 12px 0 10px;
|
||||
border-right: 1px solid white
|
||||
}`)
|
||||
|
||||
assert.Equal(t, "0 12px 0 10px", css.CssRuleList[0].Style.Styles["padding"].Value)
|
||||
assert.Equal(t, "1px solid white", css.CssRuleList[0].Style.Styles["border-right"].Value)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "td")
|
||||
}
|
||||
|
||||
func TestQuoteValue(t *testing.T) {
|
||||
css := Parse(`blockquote {
|
||||
font-family: "Source Sans Pro", Arial, sans-serif;
|
||||
font-size: 27px;
|
||||
line-height: 35px;}`)
|
||||
|
||||
assert.Equal(t, "\"Source Sans Pro\", Arial, sans-serif", css.CssRuleList[0].Style.Styles["font-family"].Value)
|
||||
assert.Equal(t, "27px", css.CssRuleList[0].Style.Styles["font-size"].Value)
|
||||
assert.Equal(t, "35px", css.CssRuleList[0].Style.Styles["line-height"].Value)
|
||||
assert.Equal(t, css.CssRuleList[0].Style.SelectorText, "blockquote")
|
||||
}
|
||||
|
||||
func TestDashClassname(t *testing.T) {
|
||||
css := Parse(`.content {
|
||||
padding: 0px;
|
||||
}
|
||||
.content-wrap {
|
||||
padding: 2px;
|
||||
}`)
|
||||
|
||||
assert.Equal(t, ".content", css.CssRuleList[0].Style.SelectorText)
|
||||
assert.Equal(t, ".content-wrap", css.CssRuleList[1].Style.SelectorText)
|
||||
assert.Equal(t, "0px", css.CssRuleList[0].Style.Styles["padding"].Value)
|
||||
assert.Equal(t, "2px", css.CssRuleList[1].Style.Styles["padding"].Value)
|
||||
}
|
||||
|
||||
func TestNotSupportedAtRule(t *testing.T) {
|
||||
rules := []string{
|
||||
`@keyframes mymove {
|
||||
0% {top: 0px;}
|
||||
25% {top: 200px;}
|
||||
50% {top: 100px;}
|
||||
75% {top: 200px;}
|
||||
100% {top: 0px;}
|
||||
}`,
|
||||
`@-webkit-keyframes mymove {
|
||||
0% {top: 0px;}
|
||||
25% {top: 200px;}
|
||||
50% {top: 100px;}
|
||||
75% {top: 200px;}
|
||||
100% {top: 0px;}
|
||||
} `,
|
||||
`@counter-style winners-list {
|
||||
system: fixed;
|
||||
symbols: url(gold-medal.svg) url(silver-medal.svg) url(bronze-medal.svg);
|
||||
suffix: " ";
|
||||
}`,
|
||||
`@namespace url(http://www.w3.org/1999/xhtml);`,
|
||||
`@document url(http://www.w3.org/),
|
||||
url-prefix(http://www.w3.org/Style/),
|
||||
domain(mozilla.org),
|
||||
regexp("https:.*")
|
||||
{
|
||||
|
||||
body { color: purple; background: yellow; }
|
||||
}`,
|
||||
}
|
||||
for _, rule := range rules {
|
||||
assert.Panics(t, func() {
|
||||
Parse(rule)
|
||||
})
|
||||
}
|
||||
}
|
||||
43
vendor/github.com/vanng822/css/rule.go
generated
vendored
Normal file
43
vendor/github.com/vanng822/css/rule.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package css
|
||||
|
||||
import ()
|
||||
|
||||
type RuleType int
|
||||
|
||||
const (
|
||||
STYLE_RULE RuleType = iota
|
||||
CHARSET_RULE
|
||||
IMPORT_RULE
|
||||
MEDIA_RULE
|
||||
FONT_FACE_RULE
|
||||
PAGE_RULE
|
||||
|
||||
)
|
||||
|
||||
var ruleTypeNames = map[RuleType]string{
|
||||
STYLE_RULE: "",
|
||||
MEDIA_RULE: "@media",
|
||||
CHARSET_RULE: "@charset",
|
||||
IMPORT_RULE: "@import",
|
||||
FONT_FACE_RULE: "@font-face",
|
||||
PAGE_RULE: "@page",
|
||||
}
|
||||
|
||||
func (rt RuleType) Text() string {
|
||||
return ruleTypeNames[rt]
|
||||
}
|
||||
|
||||
type CSSRule struct {
|
||||
Type RuleType
|
||||
Style CSSStyleRule
|
||||
Rules []*CSSRule
|
||||
}
|
||||
|
||||
func NewRule(ruleType RuleType) *CSSRule {
|
||||
r := &CSSRule{
|
||||
Type: ruleType,
|
||||
}
|
||||
r.Style.Styles = make(map[string]*CSSStyleDeclaration)
|
||||
r.Rules = make([]*CSSRule, 0)
|
||||
return r
|
||||
}
|
||||
16
vendor/github.com/vanng822/css/rule_test.go
generated
vendored
Normal file
16
vendor/github.com/vanng822/css/rule_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRuleTypeString(t *testing.T) {
|
||||
assert.Equal(t, STYLE_RULE.Text(), "")
|
||||
assert.Equal(t, CHARSET_RULE.Text(), "@charset")
|
||||
assert.Equal(t, IMPORT_RULE.Text(), "@import")
|
||||
assert.Equal(t, MEDIA_RULE.Text(), "@media")
|
||||
assert.Equal(t, FONT_FACE_RULE.Text(), "@font-face")
|
||||
assert.Equal(t, PAGE_RULE.Text(), "@page")
|
||||
}
|
||||
|
||||
69
vendor/github.com/vanng822/css/selector_parser.go
generated
vendored
Normal file
69
vendor/github.com/vanng822/css/selector_parser.go
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"github.com/gorilla/css/scanner"
|
||||
)
|
||||
|
||||
func parseSelector(s *scanner.Scanner) string {
|
||||
/*
|
||||
selector : any+;
|
||||
any : [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
|
||||
| DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
|
||||
| DASHMATCH | ':' | FUNCTION S* [any|unused]* ')'
|
||||
| '(' S* [any|unused]* ')' | '[' S* [any|unused]* ']'
|
||||
] S*;
|
||||
*/
|
||||
|
||||
selector := ""
|
||||
|
||||
for {
|
||||
token := s.Next()
|
||||
|
||||
//fmt.Printf("SELECTOR: %s:'%s'\n", token.Type.String(), token.Value)
|
||||
|
||||
if token.Type == scanner.TokenError || token.Type == scanner.TokenEOF {
|
||||
break
|
||||
}
|
||||
|
||||
switch token.Type {
|
||||
case scanner.TokenChar:
|
||||
if token.Value == "{" {
|
||||
return selector
|
||||
}
|
||||
fallthrough
|
||||
case scanner.TokenIdent:
|
||||
fallthrough
|
||||
case scanner.TokenS:
|
||||
fallthrough
|
||||
case scanner.TokenNumber:
|
||||
fallthrough
|
||||
case scanner.TokenPercentage:
|
||||
fallthrough
|
||||
case scanner.TokenDimension:
|
||||
fallthrough
|
||||
case scanner.TokenString:
|
||||
fallthrough
|
||||
case scanner.TokenURI:
|
||||
fallthrough
|
||||
case scanner.TokenHash:
|
||||
fallthrough
|
||||
case scanner.TokenUnicodeRange:
|
||||
fallthrough
|
||||
case scanner.TokenIncludes:
|
||||
fallthrough
|
||||
case scanner.TokenDashMatch:
|
||||
fallthrough
|
||||
case scanner.TokenFunction:
|
||||
fallthrough
|
||||
case scanner.TokenSuffixMatch:
|
||||
fallthrough
|
||||
case scanner.TokenPrefixMatch:
|
||||
fallthrough
|
||||
case scanner.TokenSubstringMatch:
|
||||
selector += token.Value
|
||||
}
|
||||
}
|
||||
|
||||
return selector
|
||||
}
|
||||
26
vendor/github.com/vanng822/css/styledeclaration.go
generated
vendored
Normal file
26
vendor/github.com/vanng822/css/styledeclaration.go
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CSSStyleDeclaration struct {
|
||||
Property string
|
||||
Value string
|
||||
Important int
|
||||
}
|
||||
|
||||
func NewCSSStyleDeclaration(property, value string, important int) *CSSStyleDeclaration {
|
||||
return &CSSStyleDeclaration{
|
||||
Property: property,
|
||||
Value: value,
|
||||
Important: important,
|
||||
}
|
||||
}
|
||||
|
||||
func (decl *CSSStyleDeclaration) Text() string {
|
||||
if decl.Important == 1 {
|
||||
return fmt.Sprintf("%s: %s !important", decl.Property, decl.Value)
|
||||
}
|
||||
return fmt.Sprintf("%s: %s", decl.Property, decl.Value)
|
||||
}
|
||||
16
vendor/github.com/vanng822/css/styledeclaration_test.go
generated
vendored
Normal file
16
vendor/github.com/vanng822/css/styledeclaration_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeclWithImportan(t *testing.T) {
|
||||
decl := NewCSSStyleDeclaration("width", "100%", 1)
|
||||
assert.Equal(t, decl.Text(), "width: 100% !important")
|
||||
}
|
||||
|
||||
func TestDeclWithoutImportan(t *testing.T) {
|
||||
decl := NewCSSStyleDeclaration("width", "100%", 0)
|
||||
assert.Equal(t, decl.Text(), "width: 100%")
|
||||
}
|
||||
24
vendor/github.com/vanng822/css/stylerule.go
generated
vendored
Normal file
24
vendor/github.com/vanng822/css/stylerule.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CSSStyleRule struct {
|
||||
SelectorText string
|
||||
Styles map[string]*CSSStyleDeclaration
|
||||
}
|
||||
|
||||
func (sr *CSSStyleRule) Text() string {
|
||||
decls := make([]string, 0, len(sr.Styles))
|
||||
|
||||
for _, s := range sr.Styles {
|
||||
decls = append(decls, s.Text())
|
||||
}
|
||||
|
||||
sort.Strings(decls)
|
||||
|
||||
return fmt.Sprintf("%s {\n%s\n}", sr.SelectorText, strings.Join(decls, ";\n"))
|
||||
}
|
||||
16
vendor/github.com/vanng822/css/stylerule_test.go
generated
vendored
Normal file
16
vendor/github.com/vanng822/css/stylerule_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package css
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStyleRuleText(t *testing.T) {
|
||||
sr := CSSStyleRule{}
|
||||
sr.SelectorText = ".box"
|
||||
sr.Styles = make(map[string]*CSSStyleDeclaration)
|
||||
sr.Styles["width"] = NewCSSStyleDeclaration("width", "10px", 0)
|
||||
sr.Styles["height"] = NewCSSStyleDeclaration("height", "100px", 0)
|
||||
|
||||
assert.Equal(t, sr.Text(), ".box {\nheight: 100px;\nwidth: 10px\n}")
|
||||
}
|
||||
13
vendor/github.com/vanng822/css/stylesheet.go
generated
vendored
Normal file
13
vendor/github.com/vanng822/css/stylesheet.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package css
|
||||
|
||||
import ()
|
||||
|
||||
type CSSStyleSheet struct {
|
||||
Type string
|
||||
Media string
|
||||
CssRuleList []*CSSRule
|
||||
}
|
||||
|
||||
func (ss *CSSStyleSheet) GetCSSRuleList() []*CSSRule {
|
||||
return ss.CssRuleList
|
||||
}
|
||||
3
vendor/github.com/vanng822/go-premailer/.gitignore
generated
vendored
Normal file
3
vendor/github.com/vanng822/go-premailer/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/pkg/
|
||||
.project
|
||||
/src/
|
||||
22
vendor/github.com/vanng822/go-premailer/.travis.yml
generated
vendored
Normal file
22
vendor/github.com/vanng822/go-premailer/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
language: golang
|
||||
|
||||
go:
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.8
|
||||
- tip
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOPATH="$HOME/gopath"
|
||||
- PATH="$HOME/gopath/bin:$HOME/bin:$PATH"
|
||||
|
||||
install:
|
||||
- go get github.com/vanng822/css
|
||||
- go get github.com/PuerkitoBio/goquery
|
||||
- go get golang.org/x/net/html
|
||||
- go get github.com/stretchr/testify/assert
|
||||
|
||||
script:
|
||||
- cd premailer && go test -v
|
||||
23
vendor/github.com/vanng822/go-premailer/LICENSE
generated
vendored
Normal file
23
vendor/github.com/vanng822/go-premailer/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2015 Nguyen Van Nhu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
80
vendor/github.com/vanng822/go-premailer/README.md
generated
vendored
Normal file
80
vendor/github.com/vanng822/go-premailer/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# go-premailer
|
||||
|
||||
Inline styling for html in golang
|
||||
|
||||
# Document
|
||||
|
||||
[](https://godoc.org/github.com/vanng822/go-premailer/premailer)
|
||||
|
||||
# install
|
||||
|
||||
go get github.com/vanng822/go-premailer/premailer
|
||||
|
||||
# Example
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/vanng822/go-premailer/premailer"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
prem := premailer.NewPremailerFromFile(inputFile, premailer.NewOptions())
|
||||
html, err := prem.Transform()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(html)
|
||||
}
|
||||
|
||||
## Input
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1 { width: 300px; color:red; }
|
||||
strong { text-decoration:none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<p><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
## Output
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="color:red;width:300px" width="300">Hi!</h1>
|
||||
<p><strong style="text-decoration:none">Yes!</strong></p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
# Commandline
|
||||
|
||||
> go run main.go -i your_email.html
|
||||
> go run main.go -i your_mail.html -o process_mail.html
|
||||
|
||||
# Demo
|
||||
|
||||
http://premailer.isgoodness.com/
|
||||
|
||||
# Conversion endpoint
|
||||
|
||||
http://premailer.isgoodness.com/convert
|
||||
|
||||
request POST:
|
||||
html: your mail
|
||||
cssToAttributes: true|false
|
||||
removeClasses: true|false
|
||||
response:
|
||||
{result: output}
|
||||
|
||||
48
vendor/github.com/vanng822/go-premailer/cmd/main.go
generated
vendored
Normal file
48
vendor/github.com/vanng822/go-premailer/cmd/main.go
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/vanng822/go-premailer/premailer"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
inputFile string
|
||||
outputFile string
|
||||
removeClasses bool
|
||||
skipCssToAttributes bool
|
||||
)
|
||||
flag.StringVar(&inputFile, "i", "", "Input file")
|
||||
flag.StringVar(&outputFile, "o", "", "Output file")
|
||||
flag.BoolVar(&removeClasses, "remove-classes", false, "Remove class attribute")
|
||||
flag.BoolVar(&skipCssToAttributes, "skip-css-to-attributes", false, "No copy of css property to html attribute")
|
||||
flag.Parse()
|
||||
if inputFile == "" {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
start := time.Now()
|
||||
options := premailer.NewOptions()
|
||||
options.RemoveClasses = removeClasses
|
||||
options.CssToAttributes = !skipCssToAttributes
|
||||
prem := premailer.NewPremailerFromFile(inputFile, options)
|
||||
html, err := prem.Transform()
|
||||
log.Printf("took: %v", time.Now().Sub(start))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if outputFile != "" {
|
||||
fd, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer fd.Close()
|
||||
fd.WriteString(html)
|
||||
} else {
|
||||
fmt.Println(html)
|
||||
}
|
||||
}
|
||||
62
vendor/github.com/vanng822/go-premailer/cmd/premailer.go
generated
vendored
Normal file
62
vendor/github.com/vanng822/go-premailer/cmd/premailer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/unrolled/render"
|
||||
"github.com/vanng822/go-premailer/premailer"
|
||||
"github.com/vanng822/r2router"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
host string
|
||||
port int
|
||||
)
|
||||
|
||||
flag.StringVar(&host, "h", "127.0.0.1", "Host to listen on")
|
||||
flag.IntVar(&port, "p", 8080, "Port number to listen on")
|
||||
flag.Parse()
|
||||
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Notify(sigc, os.Kill, os.Interrupt, syscall.SIGTERM, syscall.SIGUSR2)
|
||||
app := r2router.NewSeeforRouter()
|
||||
|
||||
r := render.New()
|
||||
|
||||
app.Get("/", func(w http.ResponseWriter, req *http.Request, _ r2router.Params) {
|
||||
r.JSON(w, http.StatusOK, r2router.M{"usage": "POST / html=HTML&cssToAttributes=boolean&removeClasses=boolean"})
|
||||
})
|
||||
app.Post("/", func(w http.ResponseWriter, req *http.Request, _ r2router.Params) {
|
||||
req.ParseForm()
|
||||
html := req.Form.Get("html")
|
||||
cssToAttributes := req.Form.Get("cssToAttributes")
|
||||
removeClasses := req.Form.Get("removeClasses")
|
||||
var result string
|
||||
if html != "" {
|
||||
options := premailer.NewOptions()
|
||||
if removeClasses == "true" {
|
||||
options.RemoveClasses = true
|
||||
}
|
||||
if cssToAttributes == "false" {
|
||||
options.CssToAttributes = false
|
||||
}
|
||||
pre := premailer.NewPremailerFromString(html, options)
|
||||
result, _ = pre.Transform()
|
||||
} else {
|
||||
result = ""
|
||||
}
|
||||
r.JSON(w, http.StatusOK, r2router.M{"result": result})
|
||||
})
|
||||
|
||||
log.Printf("listening to address %s:%d", host, port)
|
||||
go http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), app)
|
||||
sig := <-sigc
|
||||
log.Printf("Got signal: %s", sig)
|
||||
|
||||
}
|
||||
30
vendor/github.com/vanng822/go-premailer/premailer/data/markup_test.html
generated
vendored
Normal file
30
vendor/github.com/vanng822/go-premailer/premailer/data/markup_test.html
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1 {
|
||||
width: 50px;
|
||||
color:red;
|
||||
}
|
||||
h2 {
|
||||
vertical-align: top;
|
||||
}
|
||||
h3 {
|
||||
text-align: right;
|
||||
}
|
||||
strong {
|
||||
text-decoration:none
|
||||
}
|
||||
div {
|
||||
background-color: green
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<h2>There</h2>
|
||||
<h3>Hello</h3>
|
||||
<p><strong>Yes!</strong></p>
|
||||
<div>Green color</div>
|
||||
</body>
|
||||
</html>
|
||||
45
vendor/github.com/vanng822/go-premailer/premailer/doc.go
generated
vendored
Normal file
45
vendor/github.com/vanng822/go-premailer/premailer/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// Package premailer is for inline styling.
|
||||
//
|
||||
// import (
|
||||
// "fmt"
|
||||
// "github.com/vanng822/go-premailer/premailer"
|
||||
// "log"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
// prem := premailer.NewPremailerFromFile(inputFile, premailer.NewOptions())
|
||||
// html, err := prem.Transform()
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// fmt.Println(html)
|
||||
// }
|
||||
// // Input
|
||||
//
|
||||
// <html>
|
||||
// <head>
|
||||
// <title>Title</title>
|
||||
// <style type="text/css">
|
||||
// h1 { width: 300px; color:red; }
|
||||
// strong { text-decoration:none; }
|
||||
// </style>
|
||||
// </head>
|
||||
// <body>
|
||||
// <h1>Hi!</h1>
|
||||
// <p><strong>Yes!</strong></p>
|
||||
// </body>
|
||||
// </html>
|
||||
//
|
||||
// // Output
|
||||
//
|
||||
// <html>
|
||||
// <head>
|
||||
// <title>Title</title>
|
||||
// </head>
|
||||
// <body>
|
||||
// <h1 style="color:red;width:300px" width="300">Hi!</h1>
|
||||
// <p><strong style="text-decoration:none">Yes!</strong></p>
|
||||
// </body>
|
||||
// </html>
|
||||
package premailer
|
||||
70
vendor/github.com/vanng822/go-premailer/premailer/element.go
generated
vendored
Normal file
70
vendor/github.com/vanng822/go-premailer/premailer/element.go
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/vanng822/css"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type elementRules struct {
|
||||
element *goquery.Selection
|
||||
rules []*styleRule
|
||||
cssToAttributes bool
|
||||
}
|
||||
|
||||
func (er *elementRules) inline() {
|
||||
inline, _ := er.element.Attr("style")
|
||||
|
||||
var inlineStyles map[string]*css.CSSStyleDeclaration
|
||||
if inline != "" {
|
||||
inlineStyles = css.ParseBlock(inline)
|
||||
}
|
||||
|
||||
styles := make(map[string]string)
|
||||
for _, rule := range er.rules {
|
||||
for prop, s := range rule.styles {
|
||||
styles[prop] = s.Value
|
||||
}
|
||||
}
|
||||
|
||||
if len(inlineStyles) > 0 {
|
||||
for prop, s := range inlineStyles {
|
||||
styles[prop] = s.Value
|
||||
}
|
||||
}
|
||||
|
||||
final := make([]string, 0, len(styles))
|
||||
for p, v := range styles {
|
||||
final = append(final, fmt.Sprintf("%s:%s", p, v))
|
||||
if er.cssToAttributes {
|
||||
er.style_to_basic_html_attribute(p, v)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(final)
|
||||
style := strings.Join(final, ";")
|
||||
if style != "" {
|
||||
er.element.SetAttr("style", style)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (er *elementRules) style_to_basic_html_attribute(prop, value string) {
|
||||
switch prop {
|
||||
case "text-align":
|
||||
er.element.SetAttr("align", value)
|
||||
case "vertical-align":
|
||||
er.element.SetAttr("valign", value)
|
||||
case "background-color":
|
||||
er.element.SetAttr("bgcolor", value)
|
||||
case "width":
|
||||
fallthrough
|
||||
case "height":
|
||||
if strings.HasSuffix(value, "px") {
|
||||
value = value[:len(value)-2]
|
||||
}
|
||||
er.element.SetAttr(prop, value)
|
||||
}
|
||||
}
|
||||
16
vendor/github.com/vanng822/go-premailer/premailer/main_test.go
generated
vendored
Normal file
16
vendor/github.com/vanng822/go-premailer/premailer/main_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
fmt.Println("Test starting")
|
||||
args := os.Args[:]
|
||||
retCode := m.Run()
|
||||
os.Args = args
|
||||
fmt.Println("Test ending")
|
||||
os.Exit(retCode)
|
||||
}
|
||||
20
vendor/github.com/vanng822/go-premailer/premailer/options.go
generated
vendored
Normal file
20
vendor/github.com/vanng822/go-premailer/premailer/options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package premailer
|
||||
|
||||
import ()
|
||||
|
||||
// Options for controlling behaviour
|
||||
type Options struct {
|
||||
// Remove class attribute from element
|
||||
// Default false
|
||||
RemoveClasses bool
|
||||
// Copy related CSS properties into HTML attributes (e.g. background-color to bgcolor)
|
||||
// Default true
|
||||
CssToAttributes bool
|
||||
}
|
||||
|
||||
// NewOptions return an Options instance with default value
|
||||
func NewOptions() *Options {
|
||||
options := &Options{}
|
||||
options.CssToAttributes = true
|
||||
return options
|
||||
}
|
||||
208
vendor/github.com/vanng822/go-premailer/premailer/premailer.go
generated
vendored
Normal file
208
vendor/github.com/vanng822/go-premailer/premailer/premailer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/vanng822/css"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// Inteface of Premailer
|
||||
type Premailer interface {
|
||||
// Transform process and inlining css
|
||||
// It start to collect the rules in the document style tags
|
||||
// Calculate specificity and sort the rules based on that
|
||||
// It then collects the affected elements
|
||||
// And applies the rules on those
|
||||
// The leftover rules will put back into a style element
|
||||
Transform() (string, error)
|
||||
}
|
||||
|
||||
var unmergableSelector = regexp.MustCompile("(?i)\\:{1,2}(visited|active|hover|focus|link|root|in-range|invalid|valid|after|before|selection|target|first\\-(line|letter))|^\\@")
|
||||
var notSupportedSelector = regexp.MustCompile("(?i)\\:(checked|disabled|enabled|lang)")
|
||||
|
||||
type premailer struct {
|
||||
doc *goquery.Document
|
||||
elIdAttr string
|
||||
elements map[string]*elementRules
|
||||
rules []*styleRule
|
||||
leftover []*css.CSSRule
|
||||
allRules [][]*css.CSSRule
|
||||
elementId int
|
||||
processed bool
|
||||
options *Options
|
||||
}
|
||||
|
||||
// NewPremailer return a new instance of Premailer
|
||||
// It take a Document as argument and it shouldn't be nil
|
||||
func NewPremailer(doc *goquery.Document, options *Options) Premailer {
|
||||
pr := premailer{}
|
||||
pr.doc = doc
|
||||
pr.rules = make([]*styleRule, 0)
|
||||
pr.allRules = make([][]*css.CSSRule, 0)
|
||||
pr.leftover = make([]*css.CSSRule, 0)
|
||||
pr.elements = make(map[string]*elementRules)
|
||||
pr.elIdAttr = "pr-el-id"
|
||||
if options == nil {
|
||||
options = NewOptions()
|
||||
}
|
||||
pr.options = options
|
||||
return &pr
|
||||
}
|
||||
|
||||
func (pr *premailer) sortRules() {
|
||||
ruleIndex := 0
|
||||
for ruleSetIndex, rules := range pr.allRules {
|
||||
if rules == nil {
|
||||
continue
|
||||
}
|
||||
for _, rule := range rules {
|
||||
if rule.Type != css.STYLE_RULE {
|
||||
pr.leftover = append(pr.leftover, rule)
|
||||
continue
|
||||
}
|
||||
normalStyles := make(map[string]*css.CSSStyleDeclaration)
|
||||
importantStyles := make(map[string]*css.CSSStyleDeclaration)
|
||||
|
||||
for prop, s := range rule.Style.Styles {
|
||||
if s.Important == 1 {
|
||||
importantStyles[prop] = s
|
||||
} else {
|
||||
normalStyles[prop] = s
|
||||
}
|
||||
}
|
||||
|
||||
selectors := strings.Split(rule.Style.SelectorText, ",")
|
||||
for _, selector := range selectors {
|
||||
if unmergableSelector.MatchString(selector) || notSupportedSelector.MatchString(selector) {
|
||||
// cause longer css
|
||||
pr.leftover = append(pr.leftover, copyRule(selector, rule))
|
||||
continue
|
||||
}
|
||||
if strings.Contains(selector, "*") {
|
||||
// keep this?
|
||||
pr.leftover = append(pr.leftover, copyRule(selector, rule))
|
||||
continue
|
||||
}
|
||||
if len(normalStyles) > 0 {
|
||||
pr.rules = append(pr.rules, &styleRule{makeSpecificity(0, ruleSetIndex, ruleIndex, selector), selector, normalStyles})
|
||||
ruleIndex += 1
|
||||
}
|
||||
if len(importantStyles) > 0 {
|
||||
pr.rules = append(pr.rules, &styleRule{makeSpecificity(1, ruleSetIndex, ruleIndex, selector), selector, importantStyles})
|
||||
ruleIndex += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Sort(bySpecificity(pr.rules))
|
||||
}
|
||||
|
||||
func (pr *premailer) collectRules() {
|
||||
var wg sync.WaitGroup
|
||||
pr.doc.Find("style:not([data-premailer='ignore'])").Each(func(_ int, s *goquery.Selection) {
|
||||
if _, exist := s.Attr("media"); exist {
|
||||
return
|
||||
}
|
||||
wg.Add(1)
|
||||
pr.allRules = append(pr.allRules, nil)
|
||||
go func(ruleSetIndex int) {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
if r := recover(); r != nil {
|
||||
pr.allRules[ruleSetIndex] = nil
|
||||
log.Println("Got error when passing css")
|
||||
log.Println(r)
|
||||
}
|
||||
}()
|
||||
ss := css.Parse(s.Text())
|
||||
pr.allRules[ruleSetIndex] = ss.GetCSSRuleList()
|
||||
s.ReplaceWithHtml("")
|
||||
}(len(pr.allRules) - 1)
|
||||
})
|
||||
wg.Wait()
|
||||
|
||||
}
|
||||
|
||||
func (pr *premailer) collectElements() {
|
||||
for _, rule := range pr.rules {
|
||||
pr.doc.Find(rule.selector).Each(func(_ int, s *goquery.Selection) {
|
||||
if id, exist := s.Attr(pr.elIdAttr); exist {
|
||||
pr.elements[id].rules = append(pr.elements[id].rules, rule)
|
||||
} else {
|
||||
id := strconv.Itoa(pr.elementId)
|
||||
s.SetAttr(pr.elIdAttr, id)
|
||||
rules := make([]*styleRule, 0)
|
||||
rules = append(rules, rule)
|
||||
pr.elements[id] = &elementRules{element: s, rules: rules, cssToAttributes: pr.options.CssToAttributes}
|
||||
pr.elementId += 1
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *premailer) applyInline() {
|
||||
for _, element := range pr.elements {
|
||||
element.inline()
|
||||
element.element.RemoveAttr(pr.elIdAttr)
|
||||
if pr.options.RemoveClasses {
|
||||
element.element.RemoveAttr("class")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *premailer) addLeftover() {
|
||||
if len(pr.leftover) > 0 {
|
||||
headNode := pr.doc.Find("head")
|
||||
|
||||
styleNode := &html.Node{}
|
||||
styleNode.Type = html.ElementNode
|
||||
styleNode.Data = "style"
|
||||
styleNode.Attr = []html.Attribute{html.Attribute{Key: "type", Val: "text/css"}}
|
||||
cssNode := &html.Node{}
|
||||
cssData := make([]string, 0, len(pr.leftover))
|
||||
for _, rule := range pr.leftover {
|
||||
if rule.Type == css.MEDIA_RULE {
|
||||
mcssData := make([]string, 0, len(rule.Rules))
|
||||
for _, mrule := range rule.Rules {
|
||||
mcssData = append(mcssData, makeRuleImportant(mrule))
|
||||
}
|
||||
cssData = append(cssData, fmt.Sprintf("%s %s{\n%s\n}\n",
|
||||
rule.Type.Text(),
|
||||
rule.Style.SelectorText,
|
||||
strings.Join(mcssData, "\n")))
|
||||
} else {
|
||||
cssData = append(cssData, makeRuleImportant(rule))
|
||||
}
|
||||
}
|
||||
cssNode.Data = strings.Join(cssData, "")
|
||||
cssNode.Type = html.TextNode
|
||||
styleNode.AppendChild(cssNode)
|
||||
headNode.AppendNodes(styleNode)
|
||||
}
|
||||
}
|
||||
|
||||
// Transform process and inlining css
|
||||
// It start to collect the rules in the document style tags
|
||||
// Calculate specificity and sort the rules based on that
|
||||
// It then collects the affected elements
|
||||
// And applies the rules on those
|
||||
// The leftover rules will put back into a style element
|
||||
func (pr *premailer) Transform() (string, error) {
|
||||
if !pr.processed {
|
||||
pr.collectRules()
|
||||
pr.sortRules()
|
||||
pr.collectElements()
|
||||
pr.applyInline()
|
||||
pr.addLeftover()
|
||||
}
|
||||
return pr.doc.Html()
|
||||
}
|
||||
24
vendor/github.com/vanng822/go-premailer/premailer/premailer_from_file.go
generated
vendored
Normal file
24
vendor/github.com/vanng822/go-premailer/premailer/premailer_from_file.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"os"
|
||||
)
|
||||
|
||||
// NewPremailerFromFile take an filename
|
||||
// Read the content of this file
|
||||
// and create a goquery.Document
|
||||
// and then create and Premailer instance.
|
||||
// It will panic if any error happens
|
||||
func NewPremailerFromFile(filename string, options *Options) Premailer {
|
||||
fd, err := os.Open(filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fd.Close()
|
||||
d, err := goquery.NewDocumentFromReader(fd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return NewPremailer(d, options)
|
||||
}
|
||||
24
vendor/github.com/vanng822/go-premailer/premailer/premailer_from_file_test.go
generated
vendored
Normal file
24
vendor/github.com/vanng822/go-premailer/premailer/premailer_from_file_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBasicHTMLFromFile(t *testing.T) {
|
||||
p := NewPremailerFromFile("data/markup_test.html", nil)
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1 style=\"color:red;width:50px\" width=\"50\">Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<h2 style=\"vertical-align:top\" valign=\"top\">There</h2>")
|
||||
assert.Contains(t, result_html, "<h3 style=\"text-align:right\" align=\"right\">Hello</h3>")
|
||||
assert.Contains(t, result_html, "<p><strong style=\"text-decoration:none\">Yes!</strong></p>")
|
||||
assert.Contains(t, result_html, "<div style=\"background-color:green\" bgcolor=\"green\">Green color</div>")
|
||||
}
|
||||
|
||||
func TestFromFilePanic(t *testing.T) {
|
||||
assert.Panics(t, func() {
|
||||
NewPremailerFromFile("data/blablabla.html", nil)
|
||||
})
|
||||
}
|
||||
19
vendor/github.com/vanng822/go-premailer/premailer/premailer_from_string.go
generated
vendored
Normal file
19
vendor/github.com/vanng822/go-premailer/premailer/premailer_from_string.go
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewPremailerFromString take in a document in string format
|
||||
// and create a goquery.Document
|
||||
// and then create and Premailer instance.
|
||||
// It will panic if any error happens
|
||||
func NewPremailerFromString(doc string, options *Options) Premailer {
|
||||
read := strings.NewReader(doc)
|
||||
d, err := goquery.NewDocumentFromReader(read)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return NewPremailer(d, options)
|
||||
}
|
||||
406
vendor/github.com/vanng822/go-premailer/premailer/premailer_test.go
generated
vendored
Normal file
406
vendor/github.com/vanng822/go-premailer/premailer/premailer_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBasicHTML(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1 {
|
||||
width: 50px;
|
||||
color:red;
|
||||
}
|
||||
h2 {
|
||||
vertical-align: top;
|
||||
}
|
||||
h3 {
|
||||
text-align: right;
|
||||
}
|
||||
strong {
|
||||
text-decoration:none
|
||||
}
|
||||
div {
|
||||
background-color: green
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<h2>There</h2>
|
||||
<h3>Hello</h3>
|
||||
<p><strong>Yes!</strong></p>
|
||||
<div>Green color</div>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
p := NewPremailerFromString(html, nil)
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1 style=\"color:red;width:50px\" width=\"50\">Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<h2 style=\"vertical-align:top\" valign=\"top\">There</h2>")
|
||||
assert.Contains(t, result_html, "<h3 style=\"text-align:right\" align=\"right\">Hello</h3>")
|
||||
assert.Contains(t, result_html, "<p><strong style=\"text-decoration:none\">Yes!</strong></p>")
|
||||
assert.Contains(t, result_html, "<div style=\"background-color:green\" bgcolor=\"green\">Green color</div>")
|
||||
}
|
||||
|
||||
func TestDataPremailerIgnore(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css" data-premailer="ignore">
|
||||
h1, h2 {
|
||||
color:red;
|
||||
}
|
||||
strong {
|
||||
text-decoration:none
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<p><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
p := NewPremailerFromString(html, nil)
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1>Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<p><strong>Yes!</strong></p>")
|
||||
}
|
||||
|
||||
func TestWithInline(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1, h2 {
|
||||
width: 50px;
|
||||
color:red;
|
||||
}
|
||||
strong {
|
||||
text-decoration:none
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="width: 100%;">Hi!</h1>
|
||||
<p><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
p := NewPremailerFromString(html, nil)
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1 style=\"color:red;width:100%\" width=\"100%\">Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<p><strong style=\"text-decoration:none\">Yes!</strong></p>")
|
||||
assert.NotContains(t, result_html, "<style type=\"text/css\">")
|
||||
}
|
||||
|
||||
func TestPseudoSelectors(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
a:active {
|
||||
color: red;
|
||||
font-size: 12px;
|
||||
}
|
||||
a:first-child {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<p>
|
||||
<a href="/home">Yes!</a>
|
||||
<a href="/away">No!</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
p := NewPremailerFromString(html, nil)
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<a href=\"/home\" style=\"color:green\">Yes!</a>")
|
||||
assert.Contains(t, result_html, "<style type=\"text/css\">")
|
||||
}
|
||||
|
||||
func TestRemoveClass(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1, h2 {
|
||||
color:red;
|
||||
}
|
||||
.big {
|
||||
font-size: 40px;
|
||||
width: 150px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 class="big">Hi!</h1>
|
||||
<p><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
options := &Options{}
|
||||
options.RemoveClasses = true
|
||||
p := NewPremailerFromString(html, options)
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1 style=\"color:red;font-size:40px;width:150px\">Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<p><strong>Yes!</strong></p>")
|
||||
}
|
||||
|
||||
func TestCssToAttributesFalse(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1, h2 {
|
||||
color:red;
|
||||
}
|
||||
.wide {
|
||||
width: 1000px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 class="wide">Hi!</h1>
|
||||
<p><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
options := &Options{}
|
||||
options.CssToAttributes = false
|
||||
p := NewPremailerFromString(html, options)
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1 class=\"wide\" style=\"color:red;width:1000px\">Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<p><strong>Yes!</strong></p>")
|
||||
}
|
||||
|
||||
func TestWithImportant(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1, h2 {
|
||||
color:red;
|
||||
}
|
||||
p {
|
||||
width: 100px !important;
|
||||
color: blue
|
||||
}
|
||||
.wide {
|
||||
width: 1000px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<p class="wide"><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
p := NewPremailerFromString(html, NewOptions())
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1 style=\"color:red\">Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<p class=\"wide\" style=\"color:blue;width:100px\" width=\"100\"><strong>Yes!</strong></p>")
|
||||
}
|
||||
|
||||
func TestWithMediaRule(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1, h2 {
|
||||
color:red;
|
||||
}
|
||||
p {
|
||||
width: 100px !important;
|
||||
color: blue
|
||||
}
|
||||
.wide {
|
||||
width: 1000px;
|
||||
}
|
||||
@media all and (min-width: 62em) {
|
||||
h1 {
|
||||
font-size: 55px;
|
||||
line-height: 60px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 5px
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<p class="wide"><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
p := NewPremailerFromString(html, NewOptions())
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1 style=\"color:red\">Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<p class=\"wide\" style=\"color:blue;width:100px\" width=\"100\"><strong>Yes!</strong></p>")
|
||||
|
||||
assert.Contains(t, result_html, "@media all and (min-width: 62em){")
|
||||
assert.Contains(t, result_html, "font-size: 55px !important;")
|
||||
assert.Contains(t, result_html, "line-height: 60px !important;")
|
||||
assert.Contains(t, result_html, "padding-bottom: 5px !important;")
|
||||
assert.Contains(t, result_html, "padding-top: 0 !important")
|
||||
}
|
||||
|
||||
func TestWithMediaAttribute(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1, h2 {
|
||||
color:red;
|
||||
}
|
||||
p {
|
||||
width: 100px !important;
|
||||
color: blue
|
||||
}
|
||||
.wide {
|
||||
width: 1000px;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css" media="all and (min-width: 62em)">
|
||||
h1 {
|
||||
font-size: 55px;
|
||||
line-height: 60px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 5px
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<p class="wide"><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
p := NewPremailerFromString(html, NewOptions())
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1 style=\"color:red\">Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<p class=\"wide\" style=\"color:blue;width:100px\" width=\"100\"><strong>Yes!</strong></p>")
|
||||
|
||||
assert.Contains(t, result_html, "<style type=\"text/css\" media=\"all and (min-width: 62em)\">")
|
||||
assert.Contains(t, result_html, "font-size: 55px;")
|
||||
assert.Contains(t, result_html, "line-height: 60px;")
|
||||
assert.Contains(t, result_html, "padding-top: 0;")
|
||||
assert.Contains(t, result_html, "padding-bottom: 5px")
|
||||
}
|
||||
|
||||
func TestIndexOutOfRange(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
h1, h2 {
|
||||
color:red;
|
||||
}
|
||||
p {
|
||||
width: 100px !important;
|
||||
color: blue
|
||||
}
|
||||
.wide {
|
||||
width: 1000px;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css" media="all and (min-width: 62em)">
|
||||
h1 {
|
||||
font-size: 55px;
|
||||
line-height: 60px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 5px
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.some {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<p class="wide"><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
p := NewPremailerFromString(html, NewOptions())
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, "<h1 style=\"color:red\">Hi!</h1>")
|
||||
assert.Contains(t, result_html, "<p class=\"wide\" style=\"color:blue;width:100px\" width=\"100\"><strong>Yes!</strong></p>")
|
||||
|
||||
assert.Contains(t, result_html, "<style type=\"text/css\" media=\"all and (min-width: 62em)\">")
|
||||
assert.Contains(t, result_html, "font-size: 55px;")
|
||||
assert.Contains(t, result_html, "line-height: 60px;")
|
||||
assert.Contains(t, result_html, "padding-top: 0;")
|
||||
assert.Contains(t, result_html, "padding-bottom: 5px")
|
||||
}
|
||||
|
||||
func TestSpecificity(t *testing.T) {
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
table.bar-chart td.bar-area {
|
||||
padding: 10px;
|
||||
}
|
||||
table { width: 91%; }
|
||||
table { width: 92%; }
|
||||
table { width: 93%; }
|
||||
table { width: 94%; }
|
||||
table { width: 95%; }
|
||||
table { width: 96%; }
|
||||
table { width: 97%; }
|
||||
table.bar-chart td {
|
||||
padding: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table class="bar-chart">
|
||||
<tr><td>1</td></tr>
|
||||
<tr><td class="bar-area">2</td></tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
p := NewPremailerFromString(html, NewOptions())
|
||||
result_html, err := p.Transform()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Contains(t, result_html, `<tr><td style="padding:5px">1</td></tr>`)
|
||||
assert.Contains(t, result_html, `<tr><td class="bar-area" style="padding:10px">2</td></tr>`)
|
||||
}
|
||||
112
vendor/github.com/vanng822/go-premailer/premailer/selector_compatible_test.go
generated
vendored
Normal file
112
vendor/github.com/vanng822/go-premailer/premailer/selector_compatible_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSupportedSelectors(t *testing.T) {
|
||||
selectors := []string{
|
||||
".footer__content_wrapper--last",
|
||||
"table[class=\"body\"] .footer__content td",
|
||||
"table[class=\"body\"] td.footer__link_wrapper--first",
|
||||
".header + .content",
|
||||
"#firstname",
|
||||
"p ~ ul",
|
||||
"div > p",
|
||||
"div > p",
|
||||
"div p",
|
||||
"div, p",
|
||||
"[target]",
|
||||
"[target=_blank]",
|
||||
"[title~=flower]",
|
||||
"[lang|=en]",
|
||||
"a[href^=\"https\"]",
|
||||
"a[href$=\".pdf\"]",
|
||||
"a[href*=\"css\"]",
|
||||
"p:empty",
|
||||
"p:first-child",
|
||||
"p:first-of-type",
|
||||
"p:last-child",
|
||||
"p:last-of-type",
|
||||
":not(p)",
|
||||
"p:nth-child(2)",
|
||||
"p:nth-last-child(2)",
|
||||
"p:nth-of-type(2)",
|
||||
"p:only-child",
|
||||
"p:nth-last-of-type(2)",
|
||||
"div:not(:nth-child(1))",
|
||||
"div:not(:not(:first-child))",
|
||||
}
|
||||
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css" data-premailer="ignore">
|
||||
h1, h2 {
|
||||
color:red;
|
||||
}
|
||||
strong {
|
||||
text-decoration:none
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<p><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
read := strings.NewReader(html)
|
||||
doc, _ := goquery.NewDocumentFromReader(read)
|
||||
|
||||
pr := premailer{}
|
||||
pr.doc = doc
|
||||
for _, selector := range selectors {
|
||||
assert.NotPanics(t, func() {
|
||||
pr.doc.Find(selector)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotSupportedSelectors(t *testing.T) {
|
||||
|
||||
html := `<html>
|
||||
<head>
|
||||
<title>Title</title>
|
||||
<style type="text/css" data-premailer="ignore">
|
||||
h1, h2 {
|
||||
color:red;
|
||||
}
|
||||
strong {
|
||||
text-decoration:none
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hi!</h1>
|
||||
<p><strong>Yes!</strong></p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
read := strings.NewReader(html)
|
||||
doc, _ := goquery.NewDocumentFromReader(read)
|
||||
|
||||
pr := premailer{}
|
||||
pr.doc = doc
|
||||
|
||||
notSupportedSelectors := []string{
|
||||
"input:checked",
|
||||
"input:disabled",
|
||||
"input:enabled",
|
||||
"input:optional",
|
||||
"input:read-only",
|
||||
"p:lang(it)",
|
||||
}
|
||||
|
||||
for _, selector := range notSupportedSelectors {
|
||||
assert.Equal(t, 0, pr.doc.Find(selector).Length())
|
||||
}
|
||||
}
|
||||
68
vendor/github.com/vanng822/go-premailer/premailer/specificity.go
generated
vendored
Normal file
68
vendor/github.com/vanng822/go-premailer/premailer/specificity.go
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/Reference#Selectors
|
||||
|
||||
type specificity struct {
|
||||
important int
|
||||
idCount int
|
||||
classCount int
|
||||
typeCount int
|
||||
attrCount int
|
||||
ruleSetIndex int
|
||||
ruleIndex int
|
||||
}
|
||||
|
||||
func (s *specificity) importantOrders() []int {
|
||||
return []int{s.important, s.idCount,
|
||||
s.classCount, s.attrCount,
|
||||
s.typeCount, s.ruleSetIndex,
|
||||
s.ruleIndex}
|
||||
}
|
||||
|
||||
var _type_selector_regex = regexp.MustCompile("(^|\\s)\\w")
|
||||
|
||||
func makeSpecificity(important, ruleSetIndex, ruleIndex int, selector string) *specificity {
|
||||
spec := specificity{}
|
||||
// determine values for priority
|
||||
if important > 0 {
|
||||
spec.important = 1
|
||||
} else {
|
||||
spec.important = 0
|
||||
}
|
||||
spec.idCount = strings.Count(selector, "#")
|
||||
spec.classCount = strings.Count(selector, ".")
|
||||
spec.attrCount = strings.Count(selector, "[")
|
||||
spec.typeCount = len(_type_selector_regex.FindAllString(selector, -1))
|
||||
spec.ruleSetIndex = ruleSetIndex
|
||||
spec.ruleIndex = ruleIndex
|
||||
return &spec
|
||||
}
|
||||
|
||||
type bySpecificity []*styleRule
|
||||
|
||||
func (bs bySpecificity) Len() int {
|
||||
return len(bs)
|
||||
}
|
||||
func (bs bySpecificity) Swap(i, j int) {
|
||||
bs[i], bs[j] = bs[j], bs[i]
|
||||
}
|
||||
|
||||
func (bs bySpecificity) Less(i, j int) bool {
|
||||
iorders := bs[i].specificity.importantOrders()
|
||||
jorders := bs[j].specificity.importantOrders()
|
||||
for n, v := range iorders {
|
||||
if v < jorders[n] {
|
||||
return true
|
||||
}
|
||||
if v > jorders[n] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
118
vendor/github.com/vanng822/go-premailer/premailer/specificity_test.go
generated
vendored
Normal file
118
vendor/github.com/vanng822/go-premailer/premailer/specificity_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSpecificitySelectorType(t *testing.T) {
|
||||
spec := makeSpecificity(1, 2, 100, "table")
|
||||
expected := []int{1, 0, 0, 0, 1, 2, 100}
|
||||
assert.Equal(t, expected, spec.importantOrders())
|
||||
}
|
||||
|
||||
func TestSpecificitySelectorClass(t *testing.T) {
|
||||
// class
|
||||
spec := makeSpecificity(1, 2, 102, "table.red")
|
||||
expected := []int{1, 0, 1, 0, 1, 2, 102}
|
||||
assert.Equal(t, expected, spec.importantOrders())
|
||||
}
|
||||
|
||||
func TestSpecificitySelectorAttr(t *testing.T) {
|
||||
// Attribute
|
||||
spec := makeSpecificity(1, 3, 103, "span[lang~=\"en-us\"]")
|
||||
expected := []int{1, 0, 0, 1, 1, 3, 103}
|
||||
assert.Equal(t, expected, spec.importantOrders())
|
||||
}
|
||||
|
||||
func TestSpecificitySelectorId(t *testing.T) {
|
||||
// id
|
||||
spec := makeSpecificity(0, 3, 104, "#example")
|
||||
expected := []int{0, 1, 0, 0, 0, 3, 104}
|
||||
assert.Equal(t, expected, spec.importantOrders())
|
||||
}
|
||||
|
||||
func TestSpecificitySort(t *testing.T) {
|
||||
undertest := make([]*styleRule, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
undertest[i] = &styleRule{}
|
||||
}
|
||||
specificity0 := makeSpecificity(1, 2, 100, "table")
|
||||
undertest[0].specificity = specificity0
|
||||
specificity1 := makeSpecificity(1, 2, 102, "table.red")
|
||||
undertest[1].specificity = specificity1
|
||||
specificity2 := makeSpecificity(1, 3, 103, "span[lang~=\"en-us\"]")
|
||||
undertest[2].specificity = specificity2
|
||||
specificity3 := makeSpecificity(0, 3, 104, "#example")
|
||||
undertest[3].specificity = specificity3
|
||||
|
||||
// expected order
|
||||
/*
|
||||
expected3 := []int{0, 1, 0, 0, 0, 3, 104}
|
||||
expected0 := []int{1, 0, 0, 0, 1, 2, 100}
|
||||
expected2 := []int{1, 0, 0, 1, 1, 3, 103}
|
||||
expected1 := []int{1, 0, 1, 0, 1, 2, 102}
|
||||
*/
|
||||
sort.Sort(bySpecificity(undertest))
|
||||
|
||||
assert.Equal(t, specificity3, undertest[0].specificity)
|
||||
assert.Equal(t, specificity0, undertest[1].specificity)
|
||||
assert.Equal(t, specificity2, undertest[2].specificity)
|
||||
assert.Equal(t, specificity1, undertest[3].specificity)
|
||||
}
|
||||
|
||||
func TestSpecificitySortRuleSetIndex(t *testing.T) {
|
||||
undertest := make([]*styleRule, 2)
|
||||
for i := 0; i < 2; i++ {
|
||||
undertest[i] = &styleRule{}
|
||||
}
|
||||
specificity0 := makeSpecificity(1, 2, 102, "table")
|
||||
undertest[0].specificity = specificity0
|
||||
specificity1 := makeSpecificity(1, 1, 102, "table")
|
||||
undertest[1].specificity = specificity1
|
||||
|
||||
sort.Sort(bySpecificity(undertest))
|
||||
|
||||
assert.Equal(t, specificity1, undertest[0].specificity)
|
||||
assert.Equal(t, specificity0, undertest[1].specificity)
|
||||
}
|
||||
|
||||
func TestSpecificitySortRuleIndex(t *testing.T) {
|
||||
undertest := make([]*styleRule, 2)
|
||||
for i := 0; i < 2; i++ {
|
||||
undertest[i] = &styleRule{}
|
||||
}
|
||||
specificity0 := makeSpecificity(1, 1, 102, "table")
|
||||
undertest[0].specificity = specificity0
|
||||
specificity1 := makeSpecificity(1, 1, 100, "table")
|
||||
undertest[1].specificity = specificity1
|
||||
|
||||
sort.Sort(bySpecificity(undertest))
|
||||
|
||||
assert.Equal(t, specificity1, undertest[0].specificity)
|
||||
assert.Equal(t, specificity0, undertest[1].specificity)
|
||||
}
|
||||
|
||||
func TestSpecificitySortLongArray(t *testing.T) {
|
||||
// It has to be longer than 6 due to internal implementation of sort.Sort(),
|
||||
rules := []*styleRule{
|
||||
&styleRule{specificity: makeSpecificity(0, 0, 1, "table.padded")},
|
||||
&styleRule{specificity: makeSpecificity(0, 0, 2, "table.padded")},
|
||||
&styleRule{specificity: makeSpecificity(0, 0, 3, "table.padded")},
|
||||
&styleRule{specificity: makeSpecificity(0, 0, 4, "table.padded")},
|
||||
&styleRule{specificity: makeSpecificity(0, 0, 5, "table.padded")},
|
||||
&styleRule{specificity: makeSpecificity(0, 0, 6, "table.padded")},
|
||||
&styleRule{specificity: makeSpecificity(0, 0, 11, "table")},
|
||||
}
|
||||
|
||||
sort.Sort(bySpecificity(rules))
|
||||
|
||||
ruleIndices := make([]int, len(rules))
|
||||
for i := range rules {
|
||||
ruleIndices[i] = rules[i].specificity.ruleIndex
|
||||
}
|
||||
expectedRuleIndices := []int{11, 1, 2, 3, 4, 5, 6}
|
||||
assert.Equal(t, expectedRuleIndices, ruleIndices)
|
||||
}
|
||||
11
vendor/github.com/vanng822/go-premailer/premailer/style_rule.go
generated
vendored
Normal file
11
vendor/github.com/vanng822/go-premailer/premailer/style_rule.go
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"github.com/vanng822/css"
|
||||
)
|
||||
|
||||
type styleRule struct {
|
||||
specificity *specificity
|
||||
selector string
|
||||
styles map[string]*css.CSSStyleDeclaration
|
||||
}
|
||||
24
vendor/github.com/vanng822/go-premailer/premailer/util.go
generated
vendored
Normal file
24
vendor/github.com/vanng822/go-premailer/premailer/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package premailer
|
||||
|
||||
import (
|
||||
"github.com/vanng822/css"
|
||||
)
|
||||
|
||||
func copyRule(selector string, rule *css.CSSRule) *css.CSSRule {
|
||||
// copy rule for each selector
|
||||
styles := make(map[string]*css.CSSStyleDeclaration)
|
||||
for prop, s := range rule.Style.Styles {
|
||||
styles[prop] = css.NewCSSStyleDeclaration(s.Property, s.Value, s.Important)
|
||||
}
|
||||
copiedStyle := css.CSSStyleRule{SelectorText: selector, Styles: styles}
|
||||
copiedRule := &css.CSSRule{Type: rule.Type, Style: copiedStyle}
|
||||
return copiedRule
|
||||
}
|
||||
|
||||
func makeRuleImportant(rule *css.CSSRule) string {
|
||||
// this for using Text() which has nice sorted props
|
||||
for _, s := range rule.Style.Styles {
|
||||
s.Important = 1
|
||||
}
|
||||
return rule.Style.Text()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue