mirror of
https://github.com/miniflux/v2.git
synced 2025-10-10 19:32:06 +00:00
First commit
This commit is contained in:
commit
8ffb773f43
2121 changed files with 1118910 additions and 0 deletions
559
vendor/github.com/tdewolff/minify/css/css.go
generated
vendored
Normal file
559
vendor/github.com/tdewolff/minify/css/css.go
generated
vendored
Normal file
|
@ -0,0 +1,559 @@
|
|||
// Package css minifies CSS3 following the specifications at http://www.w3.org/TR/css-syntax-3/.
|
||||
package css // import "github.com/tdewolff/minify/css"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/tdewolff/minify"
|
||||
"github.com/tdewolff/parse"
|
||||
"github.com/tdewolff/parse/css"
|
||||
)
|
||||
|
||||
var (
|
||||
spaceBytes = []byte(" ")
|
||||
colonBytes = []byte(":")
|
||||
semicolonBytes = []byte(";")
|
||||
commaBytes = []byte(",")
|
||||
leftBracketBytes = []byte("{")
|
||||
rightBracketBytes = []byte("}")
|
||||
zeroBytes = []byte("0")
|
||||
msfilterBytes = []byte("-ms-filter")
|
||||
backgroundNoneBytes = []byte("0 0")
|
||||
)
|
||||
|
||||
type cssMinifier struct {
|
||||
m *minify.M
|
||||
w io.Writer
|
||||
p *css.Parser
|
||||
o *Minifier
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// DefaultMinifier is the default minifier.
|
||||
var DefaultMinifier = &Minifier{Decimals: -1}
|
||||
|
||||
// Minifier is a CSS minifier.
|
||||
type Minifier struct {
|
||||
Decimals int
|
||||
}
|
||||
|
||||
// Minify minifies CSS data, it reads from r and writes to w.
|
||||
func Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
|
||||
return DefaultMinifier.Minify(m, w, r, params)
|
||||
}
|
||||
|
||||
// Minify minifies CSS data, it reads from r and writes to w.
|
||||
func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
|
||||
isInline := params != nil && params["inline"] == "1"
|
||||
c := &cssMinifier{
|
||||
m: m,
|
||||
w: w,
|
||||
p: css.NewParser(r, isInline),
|
||||
o: o,
|
||||
}
|
||||
defer c.p.Restore()
|
||||
|
||||
if err := c.minifyGrammar(); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cssMinifier) minifyGrammar() error {
|
||||
semicolonQueued := false
|
||||
for {
|
||||
gt, _, data := c.p.Next()
|
||||
if gt == css.ErrorGrammar {
|
||||
if perr, ok := c.p.Err().(*parse.Error); ok && perr.Message == "unexpected token in declaration" {
|
||||
if semicolonQueued {
|
||||
if _, err := c.w.Write(semicolonBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// write out the offending declaration
|
||||
if _, err := c.w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, val := range c.p.Values() {
|
||||
if _, err := c.w.Write(val.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
semicolonQueued = true
|
||||
continue
|
||||
} else {
|
||||
return c.p.Err()
|
||||
}
|
||||
} else if gt == css.EndAtRuleGrammar || gt == css.EndRulesetGrammar {
|
||||
if _, err := c.w.Write(rightBracketBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
semicolonQueued = false
|
||||
continue
|
||||
}
|
||||
|
||||
if semicolonQueued {
|
||||
if _, err := c.w.Write(semicolonBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
semicolonQueued = false
|
||||
}
|
||||
|
||||
if gt == css.AtRuleGrammar {
|
||||
if _, err := c.w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, val := range c.p.Values() {
|
||||
if _, err := c.w.Write(val.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
semicolonQueued = true
|
||||
} else if gt == css.BeginAtRuleGrammar {
|
||||
if _, err := c.w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, val := range c.p.Values() {
|
||||
if _, err := c.w.Write(val.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := c.w.Write(leftBracketBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if gt == css.QualifiedRuleGrammar {
|
||||
if err := c.minifySelectors(data, c.p.Values()); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.w.Write(commaBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if gt == css.BeginRulesetGrammar {
|
||||
if err := c.minifySelectors(data, c.p.Values()); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.w.Write(leftBracketBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if gt == css.DeclarationGrammar {
|
||||
if _, err := c.w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.w.Write(colonBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.minifyDeclaration(data, c.p.Values()); err != nil {
|
||||
return err
|
||||
}
|
||||
semicolonQueued = true
|
||||
} else if gt == css.CustomPropertyGrammar {
|
||||
if _, err := c.w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.w.Write(colonBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.w.Write(c.p.Values()[0].Data); err != nil {
|
||||
return err
|
||||
}
|
||||
semicolonQueued = true
|
||||
} else if gt == css.CommentGrammar {
|
||||
if len(data) > 5 && data[1] == '*' && data[2] == '!' {
|
||||
if _, err := c.w.Write(data[:3]); err != nil {
|
||||
return err
|
||||
}
|
||||
comment := parse.TrimWhitespace(parse.ReplaceMultipleWhitespace(data[3 : len(data)-2]))
|
||||
if _, err := c.w.Write(comment); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.w.Write(data[len(data)-2:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if _, err := c.w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cssMinifier) minifySelectors(property []byte, values []css.Token) error {
|
||||
inAttr := false
|
||||
isClass := false
|
||||
for _, val := range c.p.Values() {
|
||||
if !inAttr {
|
||||
if val.TokenType == css.IdentToken {
|
||||
if !isClass {
|
||||
parse.ToLower(val.Data)
|
||||
}
|
||||
isClass = false
|
||||
} else if val.TokenType == css.DelimToken && val.Data[0] == '.' {
|
||||
isClass = true
|
||||
} else if val.TokenType == css.LeftBracketToken {
|
||||
inAttr = true
|
||||
}
|
||||
} else {
|
||||
if val.TokenType == css.StringToken && len(val.Data) > 2 {
|
||||
s := val.Data[1 : len(val.Data)-1]
|
||||
if css.IsIdent([]byte(s)) {
|
||||
if _, err := c.w.Write(s); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
} else if val.TokenType == css.RightBracketToken {
|
||||
inAttr = false
|
||||
}
|
||||
}
|
||||
if _, err := c.w.Write(val.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cssMinifier) minifyDeclaration(property []byte, values []css.Token) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
prop := css.ToHash(property)
|
||||
inProgid := false
|
||||
for i, value := range values {
|
||||
if inProgid {
|
||||
if value.TokenType == css.FunctionToken {
|
||||
inProgid = false
|
||||
}
|
||||
continue
|
||||
} else if value.TokenType == css.IdentToken && css.ToHash(value.Data) == css.Progid {
|
||||
inProgid = true
|
||||
continue
|
||||
}
|
||||
value.TokenType, value.Data = c.shortenToken(prop, value.TokenType, value.Data)
|
||||
if prop == css.Font || prop == css.Font_Family || prop == css.Font_Weight {
|
||||
if value.TokenType == css.IdentToken && (prop == css.Font || prop == css.Font_Weight) {
|
||||
val := css.ToHash(value.Data)
|
||||
if val == css.Normal && prop == css.Font_Weight {
|
||||
// normal could also be specified for font-variant, not just font-weight
|
||||
value.TokenType = css.NumberToken
|
||||
value.Data = []byte("400")
|
||||
} else if val == css.Bold {
|
||||
value.TokenType = css.NumberToken
|
||||
value.Data = []byte("700")
|
||||
}
|
||||
} else if value.TokenType == css.StringToken && (prop == css.Font || prop == css.Font_Family) && len(value.Data) > 2 {
|
||||
unquote := true
|
||||
parse.ToLower(value.Data)
|
||||
s := value.Data[1 : len(value.Data)-1]
|
||||
if len(s) > 0 {
|
||||
for _, split := range bytes.Split(s, spaceBytes) {
|
||||
val := css.ToHash(split)
|
||||
// if len is zero, it contains two consecutive spaces
|
||||
if val == css.Inherit || val == css.Serif || val == css.Sans_Serif || val == css.Monospace || val == css.Fantasy || val == css.Cursive || val == css.Initial || val == css.Default ||
|
||||
len(split) == 0 || !css.IsIdent(split) {
|
||||
unquote = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if unquote {
|
||||
value.Data = s
|
||||
}
|
||||
}
|
||||
} else if prop == css.Outline || prop == css.Border || prop == css.Border_Bottom || prop == css.Border_Left || prop == css.Border_Right || prop == css.Border_Top {
|
||||
if css.ToHash(value.Data) == css.None {
|
||||
value.TokenType = css.NumberToken
|
||||
value.Data = zeroBytes
|
||||
}
|
||||
}
|
||||
values[i].TokenType, values[i].Data = value.TokenType, value.Data
|
||||
}
|
||||
|
||||
important := false
|
||||
if len(values) > 2 && values[len(values)-2].TokenType == css.DelimToken && values[len(values)-2].Data[0] == '!' && css.ToHash(values[len(values)-1].Data) == css.Important {
|
||||
values = values[:len(values)-2]
|
||||
important = true
|
||||
}
|
||||
|
||||
if len(values) == 1 {
|
||||
if prop == css.Background && css.ToHash(values[0].Data) == css.None {
|
||||
values[0].Data = backgroundNoneBytes
|
||||
} else if bytes.Equal(property, msfilterBytes) {
|
||||
alpha := []byte("progid:DXImageTransform.Microsoft.Alpha(Opacity=")
|
||||
if values[0].TokenType == css.StringToken && bytes.HasPrefix(values[0].Data[1:len(values[0].Data)-1], alpha) {
|
||||
values[0].Data = append(append([]byte{values[0].Data[0]}, []byte("alpha(opacity=")...), values[0].Data[1+len(alpha):]...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if prop == css.Margin || prop == css.Padding || prop == css.Border_Width {
|
||||
if (values[0].TokenType == css.NumberToken || values[0].TokenType == css.DimensionToken || values[0].TokenType == css.PercentageToken) && (len(values)+1)%2 == 0 {
|
||||
valid := true
|
||||
for i := 1; i < len(values); i += 2 {
|
||||
if values[i].TokenType != css.WhitespaceToken || values[i+1].TokenType != css.NumberToken && values[i+1].TokenType != css.DimensionToken && values[i+1].TokenType != css.PercentageToken {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if valid {
|
||||
n := (len(values) + 1) / 2
|
||||
if n == 2 {
|
||||
if bytes.Equal(values[0].Data, values[2].Data) {
|
||||
values = values[:1]
|
||||
}
|
||||
} else if n == 3 {
|
||||
if bytes.Equal(values[0].Data, values[2].Data) && bytes.Equal(values[0].Data, values[4].Data) {
|
||||
values = values[:1]
|
||||
} else if bytes.Equal(values[0].Data, values[4].Data) {
|
||||
values = values[:3]
|
||||
}
|
||||
} else if n == 4 {
|
||||
if bytes.Equal(values[0].Data, values[2].Data) && bytes.Equal(values[0].Data, values[4].Data) && bytes.Equal(values[0].Data, values[6].Data) {
|
||||
values = values[:1]
|
||||
} else if bytes.Equal(values[0].Data, values[4].Data) && bytes.Equal(values[2].Data, values[6].Data) {
|
||||
values = values[:3]
|
||||
} else if bytes.Equal(values[2].Data, values[6].Data) {
|
||||
values = values[:5]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if prop == css.Filter && len(values) == 11 {
|
||||
if bytes.Equal(values[0].Data, []byte("progid")) &&
|
||||
values[1].TokenType == css.ColonToken &&
|
||||
bytes.Equal(values[2].Data, []byte("DXImageTransform")) &&
|
||||
values[3].Data[0] == '.' &&
|
||||
bytes.Equal(values[4].Data, []byte("Microsoft")) &&
|
||||
values[5].Data[0] == '.' &&
|
||||
bytes.Equal(values[6].Data, []byte("Alpha(")) &&
|
||||
bytes.Equal(parse.ToLower(values[7].Data), []byte("opacity")) &&
|
||||
values[8].Data[0] == '=' &&
|
||||
values[10].Data[0] == ')' {
|
||||
values = values[6:]
|
||||
values[0].Data = []byte("alpha(")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(values); i++ {
|
||||
if values[i].TokenType == css.FunctionToken {
|
||||
n, err := c.minifyFunction(values[i:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i += n - 1
|
||||
} else if _, err := c.w.Write(values[i].Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if important {
|
||||
if _, err := c.w.Write([]byte("!important")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cssMinifier) minifyFunction(values []css.Token) (int, error) {
|
||||
n := 1
|
||||
simple := true
|
||||
for i, value := range values[1:] {
|
||||
if value.TokenType == css.RightParenthesisToken {
|
||||
n++
|
||||
break
|
||||
}
|
||||
if i%2 == 0 && (value.TokenType != css.NumberToken && value.TokenType != css.PercentageToken) || (i%2 == 1 && value.TokenType != css.CommaToken) {
|
||||
simple = false
|
||||
}
|
||||
n++
|
||||
}
|
||||
values = values[:n]
|
||||
if simple && (n-1)%2 == 0 {
|
||||
fun := css.ToHash(values[0].Data[:len(values[0].Data)-1])
|
||||
nArgs := (n - 1) / 2
|
||||
if (fun == css.Rgba || fun == css.Hsla) && nArgs == 4 {
|
||||
d, _ := strconv.ParseFloat(string(values[7].Data), 32) // can never fail because if simple == true than this is a NumberToken or PercentageToken
|
||||
if d-1.0 > -minify.Epsilon {
|
||||
if fun == css.Rgba {
|
||||
values[0].Data = []byte("rgb(")
|
||||
fun = css.Rgb
|
||||
} else {
|
||||
values[0].Data = []byte("hsl(")
|
||||
fun = css.Hsl
|
||||
}
|
||||
values = values[:len(values)-2]
|
||||
values[len(values)-1].Data = []byte(")")
|
||||
nArgs = 3
|
||||
} else if d < minify.Epsilon {
|
||||
values[0].Data = []byte("transparent")
|
||||
values = values[:1]
|
||||
fun = 0
|
||||
nArgs = 0
|
||||
}
|
||||
}
|
||||
if fun == css.Rgb && nArgs == 3 {
|
||||
var err [3]error
|
||||
rgb := [3]byte{}
|
||||
for j := 0; j < 3; j++ {
|
||||
val := values[j*2+1]
|
||||
if val.TokenType == css.NumberToken {
|
||||
var d int64
|
||||
d, err[j] = strconv.ParseInt(string(val.Data), 10, 32)
|
||||
if d < 0 {
|
||||
d = 0
|
||||
} else if d > 255 {
|
||||
d = 255
|
||||
}
|
||||
rgb[j] = byte(d)
|
||||
} else if val.TokenType == css.PercentageToken {
|
||||
var d float64
|
||||
d, err[j] = strconv.ParseFloat(string(val.Data[:len(val.Data)-1]), 32)
|
||||
if d < 0.0 {
|
||||
d = 0.0
|
||||
} else if d > 100.0 {
|
||||
d = 100.0
|
||||
}
|
||||
rgb[j] = byte((d / 100.0 * 255.0) + 0.5)
|
||||
}
|
||||
}
|
||||
if err[0] == nil && err[1] == nil && err[2] == nil {
|
||||
val := make([]byte, 7)
|
||||
val[0] = '#'
|
||||
hex.Encode(val[1:], rgb[:])
|
||||
parse.ToLower(val)
|
||||
if s, ok := ShortenColorHex[string(val)]; ok {
|
||||
if _, err := c.w.Write(s); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
if len(val) == 7 && val[1] == val[2] && val[3] == val[4] && val[5] == val[6] {
|
||||
val[2] = val[3]
|
||||
val[3] = val[5]
|
||||
val = val[:4]
|
||||
}
|
||||
if _, err := c.w.Write(val); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
} else if fun == css.Hsl && nArgs == 3 {
|
||||
if values[1].TokenType == css.NumberToken && values[3].TokenType == css.PercentageToken && values[5].TokenType == css.PercentageToken {
|
||||
h, err1 := strconv.ParseFloat(string(values[1].Data), 32)
|
||||
s, err2 := strconv.ParseFloat(string(values[3].Data[:len(values[3].Data)-1]), 32)
|
||||
l, err3 := strconv.ParseFloat(string(values[5].Data[:len(values[5].Data)-1]), 32)
|
||||
if err1 == nil && err2 == nil && err3 == nil {
|
||||
r, g, b := css.HSL2RGB(h/360.0, s/100.0, l/100.0)
|
||||
rgb := []byte{byte((r * 255.0) + 0.5), byte((g * 255.0) + 0.5), byte((b * 255.0) + 0.5)}
|
||||
val := make([]byte, 7)
|
||||
val[0] = '#'
|
||||
hex.Encode(val[1:], rgb[:])
|
||||
parse.ToLower(val)
|
||||
if s, ok := ShortenColorHex[string(val)]; ok {
|
||||
if _, err := c.w.Write(s); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
if len(val) == 7 && val[1] == val[2] && val[3] == val[4] && val[5] == val[6] {
|
||||
val[2] = val[3]
|
||||
val[3] = val[5]
|
||||
val = val[:4]
|
||||
}
|
||||
if _, err := c.w.Write(val); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, value := range values {
|
||||
if _, err := c.w.Write(value.Data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *cssMinifier) shortenToken(prop css.Hash, tt css.TokenType, data []byte) (css.TokenType, []byte) {
|
||||
if tt == css.NumberToken || tt == css.PercentageToken || tt == css.DimensionToken {
|
||||
if tt == css.NumberToken && (prop == css.Z_Index || prop == css.Counter_Increment || prop == css.Counter_Reset || prop == css.Orphans || prop == css.Widows) {
|
||||
return tt, data // integers
|
||||
}
|
||||
n := len(data)
|
||||
if tt == css.PercentageToken {
|
||||
n--
|
||||
} else if tt == css.DimensionToken {
|
||||
n = parse.Number(data)
|
||||
}
|
||||
dim := data[n:]
|
||||
parse.ToLower(dim)
|
||||
data = minify.Number(data[:n], c.o.Decimals)
|
||||
if tt == css.PercentageToken && (len(data) != 1 || data[0] != '0' || prop == css.Color) {
|
||||
data = append(data, '%')
|
||||
} else if tt == css.DimensionToken && (len(data) != 1 || data[0] != '0' || requiredDimension[string(dim)]) {
|
||||
data = append(data, dim...)
|
||||
}
|
||||
} else if tt == css.IdentToken {
|
||||
//parse.ToLower(data) // TODO: not all identifiers are case-insensitive; all <custom-ident> properties are case-sensitive
|
||||
if hex, ok := ShortenColorName[css.ToHash(data)]; ok {
|
||||
tt = css.HashToken
|
||||
data = hex
|
||||
}
|
||||
} else if tt == css.HashToken {
|
||||
parse.ToLower(data)
|
||||
if ident, ok := ShortenColorHex[string(data)]; ok {
|
||||
tt = css.IdentToken
|
||||
data = ident
|
||||
} else if len(data) == 7 && data[1] == data[2] && data[3] == data[4] && data[5] == data[6] {
|
||||
tt = css.HashToken
|
||||
data[2] = data[3]
|
||||
data[3] = data[5]
|
||||
data = data[:4]
|
||||
}
|
||||
} else if tt == css.StringToken {
|
||||
// remove any \\\r\n \\\r \\\n
|
||||
for i := 1; i < len(data)-2; i++ {
|
||||
if data[i] == '\\' && (data[i+1] == '\n' || data[i+1] == '\r') {
|
||||
// encountered first replacee, now start to move bytes to the front
|
||||
j := i + 2
|
||||
if data[i+1] == '\r' && len(data) > i+2 && data[i+2] == '\n' {
|
||||
j++
|
||||
}
|
||||
for ; j < len(data); j++ {
|
||||
if data[j] == '\\' && len(data) > j+1 && (data[j+1] == '\n' || data[j+1] == '\r') {
|
||||
if data[j+1] == '\r' && len(data) > j+2 && data[j+2] == '\n' {
|
||||
j++
|
||||
}
|
||||
j++
|
||||
} else {
|
||||
data[i] = data[j]
|
||||
i++
|
||||
}
|
||||
}
|
||||
data = data[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if tt == css.URLToken {
|
||||
parse.ToLower(data[:3])
|
||||
if len(data) > 10 {
|
||||
uri := data[4 : len(data)-1]
|
||||
delim := byte('"')
|
||||
if uri[0] == '\'' || uri[0] == '"' {
|
||||
delim = uri[0]
|
||||
uri = uri[1 : len(uri)-1]
|
||||
}
|
||||
uri = minify.DataURI(c.m, uri)
|
||||
if css.IsURLUnquoted(uri) {
|
||||
data = append(append([]byte("url("), uri...), ')')
|
||||
} else {
|
||||
data = append(append(append([]byte("url("), delim), uri...), delim, ')')
|
||||
}
|
||||
}
|
||||
}
|
||||
return tt, data
|
||||
}
|
234
vendor/github.com/tdewolff/minify/css/css_test.go
generated
vendored
Normal file
234
vendor/github.com/tdewolff/minify/css/css_test.go
generated
vendored
Normal file
|
@ -0,0 +1,234 @@
|
|||
package css // import "github.com/tdewolff/minify/css"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/tdewolff/minify"
|
||||
"github.com/tdewolff/test"
|
||||
)
|
||||
|
||||
func TestCSS(t *testing.T) {
|
||||
cssTests := []struct {
|
||||
css string
|
||||
expected string
|
||||
}{
|
||||
{"/*comment*/", ""},
|
||||
{"/*! bang comment */", "/*!bang comment*/"},
|
||||
{"i{}/*! bang comment */", "i{}/*!bang comment*/"},
|
||||
{"i { key: value; key2: value; }", "i{key:value;key2:value}"},
|
||||
{".cla .ss > #id { x:y; }", ".cla .ss>#id{x:y}"},
|
||||
{".cla[id ^= L] { x:y; }", ".cla[id^=L]{x:y}"},
|
||||
{"area:focus { outline : 0;}", "area:focus{outline:0}"},
|
||||
{"@import 'file';", "@import 'file'"},
|
||||
{"@font-face { x:y; }", "@font-face{x:y}"},
|
||||
|
||||
{"input[type=\"radio\"]{x:y}", "input[type=radio]{x:y}"},
|
||||
{"DIV{margin:1em}", "div{margin:1em}"},
|
||||
{".CLASS{margin:1em}", ".CLASS{margin:1em}"},
|
||||
{"@MEDIA all{}", "@media all{}"},
|
||||
{"@media only screen and (max-width : 800px){}", "@media only screen and (max-width:800px){}"},
|
||||
{"@media (-webkit-min-device-pixel-ratio:1.5),(min-resolution:1.5dppx){}", "@media(-webkit-min-device-pixel-ratio:1.5),(min-resolution:1.5dppx){}"},
|
||||
{"[class^=icon-] i[class^=icon-],i[class*=\" icon-\"]{x:y}", "[class^=icon-] i[class^=icon-],i[class*=\" icon-\"]{x:y}"},
|
||||
{"html{line-height:1;}html{line-height:1;}", "html{line-height:1}html{line-height:1}"},
|
||||
{"a { b: 1", "a{b:1}"},
|
||||
|
||||
{":root { --custom-variable:0px; }", ":root{--custom-variable:0px}"},
|
||||
|
||||
// case sensitivity
|
||||
{"@counter-style Ident{}", "@counter-style Ident{}"},
|
||||
|
||||
// coverage
|
||||
{"a, b + c { x:y; }", "a,b+c{x:y}"},
|
||||
|
||||
// bad declaration
|
||||
{".clearfix { *zoom: 1px; }", ".clearfix{*zoom:1px}"},
|
||||
{".clearfix { *zoom: 1px }", ".clearfix{*zoom:1px}"},
|
||||
{".clearfix { color:green; *zoom: 1px; color:red; }", ".clearfix{color:green;*zoom:1px;color:red}"},
|
||||
|
||||
// go-fuzz
|
||||
{"input[type=\"\x00\"] { a: b\n}.a{}", "input[type=\"\x00\"]{a:b}.a{}"},
|
||||
{"a{a:)'''", "a{a:)'''}"},
|
||||
}
|
||||
|
||||
m := minify.New()
|
||||
for _, tt := range cssTests {
|
||||
t.Run(tt.css, func(t *testing.T) {
|
||||
r := bytes.NewBufferString(tt.css)
|
||||
w := &bytes.Buffer{}
|
||||
err := Minify(m, w, r, nil)
|
||||
test.Minify(t, tt.css, err, w.String(), tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSSInline(t *testing.T) {
|
||||
cssTests := []struct {
|
||||
css string
|
||||
expected string
|
||||
}{
|
||||
{"/*comment*/", ""},
|
||||
{"/*! bang comment */", ""},
|
||||
{";", ""},
|
||||
{"empty:", "empty:"},
|
||||
{"key: value;", "key:value"},
|
||||
{"margin: 0 1; padding: 0 1;", "margin:0 1;padding:0 1"},
|
||||
{"color: #FF0000;", "color:red"},
|
||||
{"color: #000000;", "color:#000"},
|
||||
{"color: black;", "color:#000"},
|
||||
{"color: rgb(255,255,255);", "color:#fff"},
|
||||
{"color: rgb(100%,100%,100%);", "color:#fff"},
|
||||
{"color: rgba(255,0,0,1);", "color:red"},
|
||||
{"color: rgba(255,0,0,2);", "color:red"},
|
||||
{"color: rgba(255,0,0,0.5);", "color:rgba(255,0,0,.5)"},
|
||||
{"color: rgba(255,0,0,-1);", "color:transparent"},
|
||||
{"color: rgba(0%,15%,25%,0.2);", "color:rgba(0%,15%,25%,.2)"},
|
||||
{"color: rgba(0,0,0,0.5);", "color:rgba(0,0,0,.5)"},
|
||||
{"color: hsla(5,0%,10%,0.75);", "color:hsla(5,0%,10%,.75)"},
|
||||
{"color: hsl(0,100%,50%);", "color:red"},
|
||||
{"color: hsla(1,2%,3%,1);", "color:#080807"},
|
||||
{"color: hsla(1,2%,3%,0);", "color:transparent"},
|
||||
{"color: hsl(48,100%,50%);", "color:#fc0"},
|
||||
{"font-weight: bold; font-weight: normal;", "font-weight:700;font-weight:400"},
|
||||
{"font: bold \"Times new Roman\",\"Sans-Serif\";", "font:700 times new roman,\"sans-serif\""},
|
||||
{"outline: none;", "outline:0"},
|
||||
{"outline: none !important;", "outline:0!important"},
|
||||
{"border-left: none;", "border-left:0"},
|
||||
{"margin: 1 1 1 1;", "margin:1"},
|
||||
{"margin: 1 2 1 2;", "margin:1 2"},
|
||||
{"margin: 1 2 3 2;", "margin:1 2 3"},
|
||||
{"margin: 1 2 3 4;", "margin:1 2 3 4"},
|
||||
{"margin: 1 1 1 a;", "margin:1 1 1 a"},
|
||||
{"margin: 1 1 1 1 !important;", "margin:1!important"},
|
||||
{"padding:.2em .4em .2em", "padding:.2em .4em"},
|
||||
{"margin: 0em;", "margin:0"},
|
||||
{"font-family:'Arial', 'Times New Roman';", "font-family:arial,times new roman"},
|
||||
{"background:url('http://domain.com/image.png');", "background:url(http://domain.com/image.png)"},
|
||||
{"filter: progid : DXImageTransform.Microsoft.BasicImage(rotation=1);", "filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"},
|
||||
{"filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);", "filter:alpha(opacity=0)"},
|
||||
{"content: \"a\\\nb\";", "content:\"ab\""},
|
||||
{"content: \"a\\\r\nb\\\r\nc\";", "content:\"abc\""},
|
||||
{"content: \"\";", "content:\"\""},
|
||||
|
||||
{"font:27px/13px arial,sans-serif", "font:27px/13px arial,sans-serif"},
|
||||
{"text-decoration: none !important", "text-decoration:none!important"},
|
||||
{"color:#fff", "color:#fff"},
|
||||
{"border:2px rgb(255,255,255);", "border:2px #fff"},
|
||||
{"margin:-1px", "margin:-1px"},
|
||||
{"margin:+1px", "margin:1px"},
|
||||
{"margin:0.5em", "margin:.5em"},
|
||||
{"margin:-0.5em", "margin:-.5em"},
|
||||
{"margin:05em", "margin:5em"},
|
||||
{"margin:.50em", "margin:.5em"},
|
||||
{"margin:5.0em", "margin:5em"},
|
||||
{"margin:5000em", "margin:5e3em"},
|
||||
{"color:#c0c0c0", "color:silver"},
|
||||
{"-ms-filter: \"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)\";", "-ms-filter:\"alpha(opacity=80)\""},
|
||||
{"filter: progid:DXImageTransform.Microsoft.Alpha(Opacity = 80);", "filter:alpha(opacity=80)"},
|
||||
{"MARGIN:1EM", "margin:1em"},
|
||||
//{"color:CYAN", "color:cyan"}, // TODO
|
||||
{"width:attr(Name em)", "width:attr(Name em)"},
|
||||
{"content:CounterName", "content:CounterName"},
|
||||
{"background:URL(x.PNG);", "background:url(x.PNG)"},
|
||||
{"background:url(/*nocomment*/)", "background:url(/*nocomment*/)"},
|
||||
{"background:url(data:,text)", "background:url(data:,text)"},
|
||||
{"background:url('data:text/xml; version = 2.0,content')", "background:url(data:text/xml;version=2.0,content)"},
|
||||
{"background:url('data:\\'\",text')", "background:url('data:\\'\",text')"},
|
||||
{"margin:0 0 18px 0;", "margin:0 0 18px"},
|
||||
{"background:none", "background:0 0"},
|
||||
{"background:none 1 1", "background:none 1 1"},
|
||||
{"z-index:1000", "z-index:1000"},
|
||||
|
||||
{"any:0deg 0s 0ms 0dpi 0dpcm 0dppx 0hz 0khz", "any:0 0s 0ms 0dpi 0dpcm 0dppx 0hz 0khz"},
|
||||
{"--custom-variable:0px;", "--custom-variable:0px"},
|
||||
{"--foo: if(x > 5) this.width = 10", "--foo: if(x > 5) this.width = 10"},
|
||||
{"--foo: ;", "--foo: "},
|
||||
|
||||
// case sensitivity
|
||||
{"animation:Ident", "animation:Ident"},
|
||||
{"animation-name:Ident", "animation-name:Ident"},
|
||||
|
||||
// coverage
|
||||
{"margin: 1 1;", "margin:1"},
|
||||
{"margin: 1 2;", "margin:1 2"},
|
||||
{"margin: 1 1 1;", "margin:1"},
|
||||
{"margin: 1 2 1;", "margin:1 2"},
|
||||
{"margin: 1 2 3;", "margin:1 2 3"},
|
||||
{"margin: 0%;", "margin:0"},
|
||||
{"color: rgb(255,64,64);", "color:#ff4040"},
|
||||
{"color: rgb(256,-34,2342435);", "color:#f0f"},
|
||||
{"color: rgb(120%,-45%,234234234%);", "color:#f0f"},
|
||||
{"color: rgb(0, 1, ident);", "color:rgb(0,1,ident)"},
|
||||
{"color: rgb(ident);", "color:rgb(ident)"},
|
||||
{"margin: rgb(ident);", "margin:rgb(ident)"},
|
||||
{"filter: progid:b().c.Alpha(rgba(x));", "filter:progid:b().c.Alpha(rgba(x))"},
|
||||
|
||||
// go-fuzz
|
||||
{"FONT-FAMILY: ru\"", "font-family:ru\""},
|
||||
}
|
||||
|
||||
m := minify.New()
|
||||
params := map[string]string{"inline": "1"}
|
||||
for _, tt := range cssTests {
|
||||
t.Run(tt.css, func(t *testing.T) {
|
||||
r := bytes.NewBufferString(tt.css)
|
||||
w := &bytes.Buffer{}
|
||||
err := Minify(m, w, r, params)
|
||||
test.Minify(t, tt.css, err, w.String(), tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderErrors(t *testing.T) {
|
||||
r := test.NewErrorReader(0)
|
||||
w := &bytes.Buffer{}
|
||||
m := minify.New()
|
||||
err := Minify(m, w, r, nil)
|
||||
test.T(t, err, test.ErrPlain, "return error at first read")
|
||||
}
|
||||
|
||||
func TestWriterErrors(t *testing.T) {
|
||||
errorTests := []struct {
|
||||
css string
|
||||
n []int
|
||||
}{
|
||||
{`@import 'file'`, []int{0, 2}},
|
||||
{`@media all{}`, []int{0, 2, 3, 4}},
|
||||
{`a[id^="L"]{margin:2in!important;color:red}`, []int{0, 4, 6, 7, 8, 9, 10, 11}},
|
||||
{`a{color:rgb(255,0,0)}`, []int{4}},
|
||||
{`a{color:rgb(255,255,255)}`, []int{4}},
|
||||
{`a{color:hsl(0,100%,50%)}`, []int{4}},
|
||||
{`a{color:hsl(360,100%,100%)}`, []int{4}},
|
||||
{`a{color:f(arg)}`, []int{4}},
|
||||
{`<!--`, []int{0}},
|
||||
{`/*!comment*/`, []int{0, 1, 2}},
|
||||
{`a{--var:val}`, []int{2, 3, 4}},
|
||||
{`a{*color:0}`, []int{2, 3}},
|
||||
{`a{color:0;baddecl 5}`, []int{5}},
|
||||
}
|
||||
|
||||
m := minify.New()
|
||||
for _, tt := range errorTests {
|
||||
for _, n := range tt.n {
|
||||
t.Run(fmt.Sprint(tt.css, " ", tt.n), func(t *testing.T) {
|
||||
r := bytes.NewBufferString(tt.css)
|
||||
w := test.NewErrorWriter(n)
|
||||
err := Minify(m, w, r, nil)
|
||||
test.T(t, err, test.ErrPlain)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
func ExampleMinify() {
|
||||
m := minify.New()
|
||||
m.AddFunc("text/css", Minify)
|
||||
|
||||
if err := m.Minify("text/css", os.Stdout, os.Stdin); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
153
vendor/github.com/tdewolff/minify/css/table.go
generated
vendored
Normal file
153
vendor/github.com/tdewolff/minify/css/table.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
|||
package css
|
||||
|
||||
import "github.com/tdewolff/parse/css"
|
||||
|
||||
var requiredDimension = map[string]bool{
|
||||
"s": true,
|
||||
"ms": true,
|
||||
"dpi": true,
|
||||
"dpcm": true,
|
||||
"dppx": true,
|
||||
"hz": true,
|
||||
"khz": true,
|
||||
}
|
||||
|
||||
// Uses http://www.w3.org/TR/2010/PR-css3-color-20101028/ for colors
|
||||
|
||||
// ShortenColorHex maps a color hexcode to its shorter name
|
||||
var ShortenColorHex = map[string][]byte{
|
||||
"#000080": []byte("navy"),
|
||||
"#008000": []byte("green"),
|
||||
"#008080": []byte("teal"),
|
||||
"#4b0082": []byte("indigo"),
|
||||
"#800000": []byte("maroon"),
|
||||
"#800080": []byte("purple"),
|
||||
"#808000": []byte("olive"),
|
||||
"#808080": []byte("gray"),
|
||||
"#a0522d": []byte("sienna"),
|
||||
"#a52a2a": []byte("brown"),
|
||||
"#c0c0c0": []byte("silver"),
|
||||
"#cd853f": []byte("peru"),
|
||||
"#d2b48c": []byte("tan"),
|
||||
"#da70d6": []byte("orchid"),
|
||||
"#dda0dd": []byte("plum"),
|
||||
"#ee82ee": []byte("violet"),
|
||||
"#f0e68c": []byte("khaki"),
|
||||
"#f0ffff": []byte("azure"),
|
||||
"#f5deb3": []byte("wheat"),
|
||||
"#f5f5dc": []byte("beige"),
|
||||
"#fa8072": []byte("salmon"),
|
||||
"#faf0e6": []byte("linen"),
|
||||
"#ff6347": []byte("tomato"),
|
||||
"#ff7f50": []byte("coral"),
|
||||
"#ffa500": []byte("orange"),
|
||||
"#ffc0cb": []byte("pink"),
|
||||
"#ffd700": []byte("gold"),
|
||||
"#ffe4c4": []byte("bisque"),
|
||||
"#fffafa": []byte("snow"),
|
||||
"#fffff0": []byte("ivory"),
|
||||
"#ff0000": []byte("red"),
|
||||
"#f00": []byte("red"),
|
||||
}
|
||||
|
||||
// ShortenColorName maps a color name to its shorter hexcode
|
||||
var ShortenColorName = map[css.Hash][]byte{
|
||||
css.Black: []byte("#000"),
|
||||
css.Darkblue: []byte("#00008b"),
|
||||
css.Mediumblue: []byte("#0000cd"),
|
||||
css.Darkgreen: []byte("#006400"),
|
||||
css.Darkcyan: []byte("#008b8b"),
|
||||
css.Deepskyblue: []byte("#00bfff"),
|
||||
css.Darkturquoise: []byte("#00ced1"),
|
||||
css.Mediumspringgreen: []byte("#00fa9a"),
|
||||
css.Springgreen: []byte("#00ff7f"),
|
||||
css.Midnightblue: []byte("#191970"),
|
||||
css.Dodgerblue: []byte("#1e90ff"),
|
||||
css.Lightseagreen: []byte("#20b2aa"),
|
||||
css.Forestgreen: []byte("#228b22"),
|
||||
css.Seagreen: []byte("#2e8b57"),
|
||||
css.Darkslategray: []byte("#2f4f4f"),
|
||||
css.Limegreen: []byte("#32cd32"),
|
||||
css.Mediumseagreen: []byte("#3cb371"),
|
||||
css.Turquoise: []byte("#40e0d0"),
|
||||
css.Royalblue: []byte("#4169e1"),
|
||||
css.Steelblue: []byte("#4682b4"),
|
||||
css.Darkslateblue: []byte("#483d8b"),
|
||||
css.Mediumturquoise: []byte("#48d1cc"),
|
||||
css.Darkolivegreen: []byte("#556b2f"),
|
||||
css.Cadetblue: []byte("#5f9ea0"),
|
||||
css.Cornflowerblue: []byte("#6495ed"),
|
||||
css.Mediumaquamarine: []byte("#66cdaa"),
|
||||
css.Slateblue: []byte("#6a5acd"),
|
||||
css.Olivedrab: []byte("#6b8e23"),
|
||||
css.Slategray: []byte("#708090"),
|
||||
css.Lightslateblue: []byte("#789"),
|
||||
css.Mediumslateblue: []byte("#7b68ee"),
|
||||
css.Lawngreen: []byte("#7cfc00"),
|
||||
css.Chartreuse: []byte("#7fff00"),
|
||||
css.Aquamarine: []byte("#7fffd4"),
|
||||
css.Lightskyblue: []byte("#87cefa"),
|
||||
css.Blueviolet: []byte("#8a2be2"),
|
||||
css.Darkmagenta: []byte("#8b008b"),
|
||||
css.Saddlebrown: []byte("#8b4513"),
|
||||
css.Darkseagreen: []byte("#8fbc8f"),
|
||||
css.Lightgreen: []byte("#90ee90"),
|
||||
css.Mediumpurple: []byte("#9370db"),
|
||||
css.Darkviolet: []byte("#9400d3"),
|
||||
css.Palegreen: []byte("#98fb98"),
|
||||
css.Darkorchid: []byte("#9932cc"),
|
||||
css.Yellowgreen: []byte("#9acd32"),
|
||||
css.Darkgray: []byte("#a9a9a9"),
|
||||
css.Lightblue: []byte("#add8e6"),
|
||||
css.Greenyellow: []byte("#adff2f"),
|
||||
css.Paleturquoise: []byte("#afeeee"),
|
||||
css.Lightsteelblue: []byte("#b0c4de"),
|
||||
css.Powderblue: []byte("#b0e0e6"),
|
||||
css.Firebrick: []byte("#b22222"),
|
||||
css.Darkgoldenrod: []byte("#b8860b"),
|
||||
css.Mediumorchid: []byte("#ba55d3"),
|
||||
css.Rosybrown: []byte("#bc8f8f"),
|
||||
css.Darkkhaki: []byte("#bdb76b"),
|
||||
css.Mediumvioletred: []byte("#c71585"),
|
||||
css.Indianred: []byte("#cd5c5c"),
|
||||
css.Chocolate: []byte("#d2691e"),
|
||||
css.Lightgray: []byte("#d3d3d3"),
|
||||
css.Goldenrod: []byte("#daa520"),
|
||||
css.Palevioletred: []byte("#db7093"),
|
||||
css.Gainsboro: []byte("#dcdcdc"),
|
||||
css.Burlywood: []byte("#deb887"),
|
||||
css.Lightcyan: []byte("#e0ffff"),
|
||||
css.Lavender: []byte("#e6e6fa"),
|
||||
css.Darksalmon: []byte("#e9967a"),
|
||||
css.Palegoldenrod: []byte("#eee8aa"),
|
||||
css.Lightcoral: []byte("#f08080"),
|
||||
css.Aliceblue: []byte("#f0f8ff"),
|
||||
css.Honeydew: []byte("#f0fff0"),
|
||||
css.Sandybrown: []byte("#f4a460"),
|
||||
css.Whitesmoke: []byte("#f5f5f5"),
|
||||
css.Mintcream: []byte("#f5fffa"),
|
||||
css.Ghostwhite: []byte("#f8f8ff"),
|
||||
css.Antiquewhite: []byte("#faebd7"),
|
||||
css.Lightgoldenrodyellow: []byte("#fafad2"),
|
||||
css.Fuchsia: []byte("#f0f"),
|
||||
css.Magenta: []byte("#f0f"),
|
||||
css.Deeppink: []byte("#ff1493"),
|
||||
css.Orangered: []byte("#ff4500"),
|
||||
css.Darkorange: []byte("#ff8c00"),
|
||||
css.Lightsalmon: []byte("#ffa07a"),
|
||||
css.Lightpink: []byte("#ffb6c1"),
|
||||
css.Peachpuff: []byte("#ffdab9"),
|
||||
css.Navajowhite: []byte("#ffdead"),
|
||||
css.Moccasin: []byte("#ffe4b5"),
|
||||
css.Mistyrose: []byte("#ffe4e1"),
|
||||
css.Blanchedalmond: []byte("#ffebcd"),
|
||||
css.Papayawhip: []byte("#ffefd5"),
|
||||
css.Lavenderblush: []byte("#fff0f5"),
|
||||
css.Seashell: []byte("#fff5ee"),
|
||||
css.Cornsilk: []byte("#fff8dc"),
|
||||
css.Lemonchiffon: []byte("#fffacd"),
|
||||
css.Floralwhite: []byte("#fffaf0"),
|
||||
css.Yellow: []byte("#ff0"),
|
||||
css.Lightyellow: []byte("#ffffe0"),
|
||||
css.White: []byte("#fff"),
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue