aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/tdewolff/minify
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/tdewolff/minify')
-rw-r--r--vendor/github.com/tdewolff/minify/v2/.gitattributes2
-rw-r--r--vendor/github.com/tdewolff/minify/v2/.gitignore30
-rw-r--r--vendor/github.com/tdewolff/minify/v2/.golangci.yml16
-rw-r--r--vendor/github.com/tdewolff/minify/v2/Dockerfile17
-rw-r--r--vendor/github.com/tdewolff/minify/v2/LICENSE22
-rw-r--r--vendor/github.com/tdewolff/minify/v2/Makefile58
-rw-r--r--vendor/github.com/tdewolff/minify/v2/README.md735
-rw-r--r--vendor/github.com/tdewolff/minify/v2/common.go524
-rw-r--r--vendor/github.com/tdewolff/minify/v2/css/css.go1549
-rw-r--r--vendor/github.com/tdewolff/minify/v2/css/hash.go1392
-rw-r--r--vendor/github.com/tdewolff/minify/v2/css/table.go198
-rw-r--r--vendor/github.com/tdewolff/minify/v2/css/util.go55
-rw-r--r--vendor/github.com/tdewolff/minify/v2/html/buffer.go137
-rw-r--r--vendor/github.com/tdewolff/minify/v2/html/hash.go543
-rw-r--r--vendor/github.com/tdewolff/minify/v2/html/html.go514
-rw-r--r--vendor/github.com/tdewolff/minify/v2/html/table.go1346
-rw-r--r--vendor/github.com/tdewolff/minify/v2/js/js.go1277
-rw-r--r--vendor/github.com/tdewolff/minify/v2/js/stmtlist.go341
-rw-r--r--vendor/github.com/tdewolff/minify/v2/js/util.go1361
-rw-r--r--vendor/github.com/tdewolff/minify/v2/js/vars.go443
-rw-r--r--vendor/github.com/tdewolff/minify/v2/minify.go371
21 files changed, 10931 insertions, 0 deletions
diff --git a/vendor/github.com/tdewolff/minify/v2/.gitattributes b/vendor/github.com/tdewolff/minify/v2/.gitattributes
new file mode 100644
index 0000000..16a3a8b
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/.gitattributes
@@ -0,0 +1,2 @@
1benchmarks/sample_* linguist-generated
2tests/*/corpus/* linguist-generated
diff --git a/vendor/github.com/tdewolff/minify/v2/.gitignore b/vendor/github.com/tdewolff/minify/v2/.gitignore
new file mode 100644
index 0000000..c9ea38d
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/.gitignore
@@ -0,0 +1,30 @@
1release.sh
2dist
3benchmarks/*
4!benchmarks/*.go
5!benchmarks/sample_*
6tests/*/fuzz-fuzz.zip
7tests/*/crashers
8tests/*/suppressions
9tests/*/corpus/*
10!tests/*/corpus/*.*
11parse/tests/*/fuzz-fuzz.zip
12parse/tests/*/crashers
13parse/tests/*/suppressions
14parse/tests/*/corpus/*
15!parse/tests/*/corpus/*.*
16bindings/js/build
17bindings/js/prebuilds
18bindings/js/minify.h
19bindings/js/minify.a
20bindings/js/node_modules
21bindings/js/example/package-lock.json
22bindings/js/example/node_modules
23bindings/js/example/test.min.html
24bindings/py/go.mod
25bindings/py/go.sum
26bindings/py/minify.h
27bindings/py/minify.so
28bindings/py/tdewolff_minify.egg-info
29bindings/py/example/example.min.html
30bindings/py/dist
diff --git a/vendor/github.com/tdewolff/minify/v2/.golangci.yml b/vendor/github.com/tdewolff/minify/v2/.golangci.yml
new file mode 100644
index 0000000..7009f92
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/.golangci.yml
@@ -0,0 +1,16 @@
1linters:
2 enable:
3 - depguard
4 - dogsled
5 - gofmt
6 - goimports
7 - golint
8 - gosec
9 - govet
10 - megacheck
11 - misspell
12 - nakedret
13 - prealloc
14 - unconvert
15 - unparam
16 - wastedassign
diff --git a/vendor/github.com/tdewolff/minify/v2/Dockerfile b/vendor/github.com/tdewolff/minify/v2/Dockerfile
new file mode 100644
index 0000000..0f7fde4
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/Dockerfile
@@ -0,0 +1,17 @@
1# Use this image to build the executable
2FROM golang:1.18-alpine AS build
3
4WORKDIR /go/src/github.com/tdewolff/minify
5COPY . /go/src/github.com/tdewolff/minify/
6
7RUN apk add --no-cache git ca-certificates make bash
8RUN /usr/bin/env bash -c make install
9
10
11# Final image containing the executable from the previous step
12FROM alpine:3
13
14COPY --from=build /go/bin/minify /usr/bin/minify
15COPY "containerfiles/container-entrypoint.sh" "/init.sh"
16
17ENTRYPOINT ["/init.sh"]
diff --git a/vendor/github.com/tdewolff/minify/v2/LICENSE b/vendor/github.com/tdewolff/minify/v2/LICENSE
new file mode 100644
index 0000000..41677de
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/LICENSE
@@ -0,0 +1,22 @@
1Copyright (c) 2015 Taco de Wolff
2
3 Permission is hereby granted, free of charge, to any person
4 obtaining a copy of this software and associated documentation
5 files (the "Software"), to deal in the Software without
6 restriction, including without limitation the rights to use,
7 copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following
10 conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/github.com/tdewolff/minify/v2/Makefile b/vendor/github.com/tdewolff/minify/v2/Makefile
new file mode 100644
index 0000000..9eede28
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/Makefile
@@ -0,0 +1,58 @@
1SHELL=/usr/bin/env bash
2NAME=minify
3CMD=./cmd/minify
4TARGETS=linux_amd64 linux_arm64 darwin_amd64 darwin_arm64 freebsd_amd64 netbsd_amd64 openbsd_amd64 windows_amd64
5VERSION=`git describe --tags`
6FLAGS=-ldflags "-s -w -X 'main.Version=${VERSION}'" -trimpath
7ENVS=GO111MODULES=on CGO_ENABLED=0
8
9all: install
10
11install:
12 echo "Installing ${VERSION}"
13 ${ENVS} go install ${FLAGS} ./cmd/minify
14 . cmd/minify/bash_completion
15
16release:
17 TAG=$(shell git describe --tags --exact-match 2> /dev/null);
18 if [ "${.SHELLSTATUS}" -eq 0 ]; then \
19 echo "Releasing ${VERSION}"; \
20 else \
21 echo "ERROR: commit is not tagged with a version"; \
22 echo ""; \
23 exit 1; \
24 fi
25 rm -rf dist
26 mkdir -p dist
27 for t in ${TARGETS}; do \
28 echo Building $$t...; \
29 mkdir dist/$$t; \
30 os=$$(echo $$t | cut -f1 -d_); \
31 arch=$$(echo $$t | cut -f2 -d_); \
32 ${ENVS} GOOS=$$os GOARCH=$$arch go build ${FLAGS} -o dist/$$t/${NAME} ${CMD}; \
33 \
34 cp LICENSE dist/$$t/.; \
35 cp cmd/minify/README.md dist/$$t/.; \
36 if [ "$$os" == "windows" ]; then \
37 mv dist/$$t/${NAME} dist/$$t/${NAME}.exe; \
38 zip -jq dist/${NAME}_$$t.zip dist/$$t/*; \
39 cd dist; \
40 sha256sum ${NAME}_$$t.zip >> checksums.txt; \
41 cd ..; \
42 else \
43 cp cmd/minify/bash_completion dist/$$t/.; \
44 cd dist/$$t; \
45 tar -cf - * | gzip -9 > ../${NAME}_$$t.tar.gz; \
46 cd ..; \
47 sha256sum ${NAME}_$$t.tar.gz >> checksums.txt; \
48 cd ..; \
49 fi; \
50 rm -rf dist/$$t; \
51 done
52
53clean:
54 echo "Cleaning dist/"
55 rm -rf dist
56
57.PHONY: install release clean
58.SILENT: install release clean
diff --git a/vendor/github.com/tdewolff/minify/v2/README.md b/vendor/github.com/tdewolff/minify/v2/README.md
new file mode 100644
index 0000000..a65ffee
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/README.md
@@ -0,0 +1,735 @@
1# Minify <a name="minify"></a> [![API reference](https://img.shields.io/badge/godoc-reference-5272B4)](https://pkg.go.dev/github.com/tdewolff/minify/v2?tab=doc) [![Go Report Card](https://goreportcard.com/badge/github.com/tdewolff/minify)](https://goreportcard.com/report/github.com/tdewolff/minify) [![codecov](https://codecov.io/gh/tdewolff/minify/branch/master/graph/badge.svg?token=Cr7r2EKPj2)](https://codecov.io/gh/tdewolff/minify) [![Donate](https://img.shields.io/badge/patreon-donate-DFB317)](https://www.patreon.com/tdewolff)
2
3**[Online demo](https://go.tacodewolff.nl/minify)** if you need to minify files *now*.
4
5**[Binaries](https://github.com/tdewolff/minify/releases) of CLI for various platforms.** See [CLI](https://github.com/tdewolff/minify/tree/master/cmd/minify) for more installation instructions.
6
7**[Python bindings](https://pypi.org/project/tdewolff-minify/)** install with `pip install tdewolff-minify`
8
9**[JavaScript bindings](https://www.npmjs.com/package/@tdewolff/minify)** install with `npm i @tdewolff/minify`
10
11**[.NET bindings](https://github.com/JKamsker/NMinify)** install with `Install-Package NMinify` or `dotnet add package NMinify`, thanks to Jonas Kamsker for the port
12
13---
14
15*Did you know that the shortest valid piece of HTML5 is `<!doctype html><title>x</title>`? See for yourself at the [W3C Validator](http://validator.w3.org/)!*
16
17Minify is a minifier package written in [Go][1]. It provides HTML5, CSS3, JS, JSON, SVG and XML minifiers and an interface to implement any other minifier. Minification is the process of removing bytes from a file (such as whitespace) without changing its output and therefore shrinking its size and speeding up transmission over the internet and possibly parsing. The implemented minifiers are designed for high performance.
18
19The core functionality associates mimetypes with minification functions, allowing embedded resources (like CSS or JS within HTML files) to be minified as well. Users can add new implementations that are triggered based on a mimetype (or pattern), or redirect to an external command (like ClosureCompiler, UglifyCSS, ...).
20
21### Sponsors
22
23[![SiteGround](https://www.siteground.com/img/downloads/siteground-logo-black-transparent-vector.svg)](https://www.siteground.com/)
24
25Please see https://www.patreon.com/tdewolff for ways to contribute, otherwise please contact me directly!
26
27#### Table of Contents
28
29- [Minify](#minify)
30 - [Prologue](#prologue)
31 - [Installation](#installation)
32 - [API stability](#api-stability)
33 - [Testing](#testing)
34 - [Performance](#performance)
35 - [HTML](#html)
36 - [Whitespace removal](#whitespace-removal)
37 - [CSS](#css)
38 - [JS](#js)
39 - [Comparison with other tools](#comparison-with-other-tools)
40 - [Compression ratio (lower is better)](#compression-ratio-lower-is-better)
41 - [Time (lower is better)](#time-lower-is-better)
42 - [JSON](#json)
43 - [SVG](#svg)
44 - [XML](#xml)
45 - [Usage](#usage)
46 - [New](#new)
47 - [From reader](#from-reader)
48 - [From bytes](#from-bytes)
49 - [From string](#from-string)
50 - [To reader](#to-reader)
51 - [To writer](#to-writer)
52 - [Middleware](#middleware)
53 - [Custom minifier](#custom-minifier)
54 - [Mediatypes](#mediatypes)
55 - [Examples](#examples)
56 - [Common minifiers](#common-minifiers)
57 - [External minifiers](#external-minifiers)
58 - [Closure Compiler](#closure-compiler)
59 - [UglifyJS](#uglifyjs)
60 - [esbuild](#esbuild)
61 - [Custom minifier](#custom-minifier-example)
62 - [ResponseWriter](#responsewriter)
63 - [Templates](#templates)
64 - [FAQ](#faq)
65 - [License](#license)
66
67### Roadmap
68
69- [ ] Use ASM/SSE to further speed-up core parts of the parsers/minifiers
70- [x] Improve JS minifiers by shortening variables and proper semicolon omission
71- [ ] Speed-up SVG minifier, it is very slow
72- [x] Proper parser error reporting and line number + column information
73- [ ] Generation of source maps (uncertain, might slow down parsers too much if it cannot run separately nicely)
74- [ ] Create a cmd to pack webfiles (much like webpack), ie. merging CSS and JS files, inlining small external files, minification and gzipping. This would work on HTML files.
75
76## Prologue
77Minifiers or bindings to minifiers exist in almost all programming languages. Some implementations are merely using several regular expressions to trim whitespace and comments (even though regex for parsing HTML/XML is ill-advised, for a good read see [Regular Expressions: Now You Have Two Problems](http://blog.codinghorror.com/regular-expressions-now-you-have-two-problems/)). Some implementations are much more profound, such as the [YUI Compressor](http://yui.github.io/yuicompressor/) and [Google Closure Compiler](https://github.com/google/closure-compiler) for JS. As most existing implementations either use JavaScript, use regexes, and don't focus on performance, they are pretty slow.
78
79This minifier proves to be that fast and extensive minifier that can handle HTML and any other filetype it may contain (CSS, JS, ...). It is usually orders of magnitude faster than existing minifiers.
80
81## Installation
82Make sure you have [Git](https://git-scm.com/) and [Go](https://golang.org/dl/) (1.13 or higher) installed, run
83```
84mkdir Project
85cd Project
86go mod init
87go get -u github.com/tdewolff/minify/v2
88```
89
90Then add the following imports to be able to use the various minifiers
91``` go
92import (
93 "github.com/tdewolff/minify/v2"
94 "github.com/tdewolff/minify/v2/css"
95 "github.com/tdewolff/minify/v2/html"
96 "github.com/tdewolff/minify/v2/js"
97 "github.com/tdewolff/minify/v2/json"
98 "github.com/tdewolff/minify/v2/svg"
99 "github.com/tdewolff/minify/v2/xml"
100)
101```
102
103You can optionally run `go mod tidy` to clean up the `go.mod` and `go.sum` files.
104
105See [CLI tool](https://github.com/tdewolff/minify/tree/master/cmd/minify) for installation instructions of the binary.
106
107### Docker
108
109If you want to use Docker, please see https://hub.docker.com/r/tdewolff/minify.
110
111```bash
112$ docker run -it tdewolff/minify --help
113```
114
115## API stability
116There is no guarantee for absolute stability, but I take issues and bugs seriously and don't take API changes lightly. The library will be maintained in a compatible way unless vital bugs prevent me from doing so. There has been one API change after v1 which added options support and I took the opportunity to push through some more API clean up as well. There are no plans whatsoever for future API changes.
117
118## Testing
119For all subpackages and the imported `parse` package, test coverage of 100% is pursued. Besides full coverage, the minifiers are [fuzz tested](https://github.com/tdewolff/fuzz) using [github.com/dvyukov/go-fuzz](http://www.github.com/dvyukov/go-fuzz), see [the wiki](https://github.com/tdewolff/minify/wiki) for the most important bugs found by fuzz testing. These tests ensure that everything works as intended and that the code does not crash (whatever the input). If you still encounter a bug, please file a [bug report](https://github.com/tdewolff/minify/issues)!
120
121## Performance
122The benchmarks directory contains a number of standardized samples used to compare performance between changes. To give an indication of the speed of this library, I've ran the tests on my Thinkpad T460 (i5-6300U quad-core 2.4GHz running Arch Linux) using Go 1.15.
123
124```
125name time/op
126CSS/sample_bootstrap.css-4 2.70ms ± 0%
127CSS/sample_gumby.css-4 3.57ms ± 0%
128CSS/sample_fontawesome.css-4 767µs ± 0%
129CSS/sample_normalize.css-4 85.5µs ± 0%
130HTML/sample_amazon.html-4 15.2ms ± 0%
131HTML/sample_bbc.html-4 3.90ms ± 0%
132HTML/sample_blogpost.html-4 420µs ± 0%
133HTML/sample_es6.html-4 15.6ms ± 0%
134HTML/sample_stackoverflow.html-4 3.73ms ± 0%
135HTML/sample_wikipedia.html-4 6.60ms ± 0%
136JS/sample_ace.js-4 28.7ms ± 0%
137JS/sample_dot.js-4 357µs ± 0%
138JS/sample_jquery.js-4 10.0ms ± 0%
139JS/sample_jqueryui.js-4 20.4ms ± 0%
140JS/sample_moment.js-4 3.47ms ± 0%
141JSON/sample_large.json-4 3.25ms ± 0%
142JSON/sample_testsuite.json-4 1.74ms ± 0%
143JSON/sample_twitter.json-4 24.2µs ± 0%
144SVG/sample_arctic.svg-4 34.7ms ± 0%
145SVG/sample_gopher.svg-4 307µs ± 0%
146SVG/sample_usa.svg-4 57.4ms ± 0%
147SVG/sample_car.svg-4 18.0ms ± 0%
148SVG/sample_tiger.svg-4 5.61ms ± 0%
149XML/sample_books.xml-4 54.7µs ± 0%
150XML/sample_catalog.xml-4 33.0µs ± 0%
151XML/sample_omg.xml-4 7.17ms ± 0%
152
153name speed
154CSS/sample_bootstrap.css-4 50.7MB/s ± 0%
155CSS/sample_gumby.css-4 52.1MB/s ± 0%
156CSS/sample_fontawesome.css-4 61.2MB/s ± 0%
157CSS/sample_normalize.css-4 70.8MB/s ± 0%
158HTML/sample_amazon.html-4 31.1MB/s ± 0%
159HTML/sample_bbc.html-4 29.5MB/s ± 0%
160HTML/sample_blogpost.html-4 49.8MB/s ± 0%
161HTML/sample_es6.html-4 65.6MB/s ± 0%
162HTML/sample_stackoverflow.html-4 55.0MB/s ± 0%
163HTML/sample_wikipedia.html-4 67.5MB/s ± 0%
164JS/sample_ace.js-4 22.4MB/s ± 0%
165JS/sample_dot.js-4 14.5MB/s ± 0%
166JS/sample_jquery.js-4 24.8MB/s ± 0%
167JS/sample_jqueryui.js-4 23.0MB/s ± 0%
168JS/sample_moment.js-4 28.6MB/s ± 0%
169JSON/sample_large.json-4 234MB/s ± 0%
170JSON/sample_testsuite.json-4 394MB/s ± 0%
171JSON/sample_twitter.json-4 63.0MB/s ± 0%
172SVG/sample_arctic.svg-4 42.4MB/s ± 0%
173SVG/sample_gopher.svg-4 19.0MB/s ± 0%
174SVG/sample_usa.svg-4 17.8MB/s ± 0%
175SVG/sample_car.svg-4 29.3MB/s ± 0%
176SVG/sample_tiger.svg-4 12.2MB/s ± 0%
177XML/sample_books.xml-4 81.0MB/s ± 0%
178XML/sample_catalog.xml-4 58.6MB/s ± 0%
179XML/sample_omg.xml-4 159MB/s ± 0%
180```
181
182## HTML
183
184HTML (with JS and CSS) minification typically shaves off about 10%.
185
186The HTML5 minifier uses these minifications:
187
188- strip unnecessary whitespace and otherwise collapse it to one space (or newline if it originally contained a newline)
189- strip superfluous quotes, or uses single/double quotes whichever requires fewer escapes
190- strip default attribute values and attribute boolean values
191- strip some empty attributes
192- strip unrequired tags (`html`, `head`, `body`, ...)
193- strip unrequired end tags (`tr`, `td`, `li`, ... and often `p`)
194- strip default protocols (`http:`, `https:` and `javascript:`)
195- strip all comments (including conditional comments, old IE versions are not supported anymore by Microsoft)
196- shorten `doctype` and `meta` charset
197- lowercase tags, attributes and some values to enhance gzip compression
198
199Options:
200
201- `KeepConditionalComments` preserve all IE conditional comments such as `<!--[if IE 6]><![endif]-->` and `<![if IE 6]><![endif]>`, see https://msdn.microsoft.com/en-us/library/ms537512(v=vs.85).aspx#syntax
202- `KeepDefaultAttrVals` preserve default attribute values such as `<script type="application/javascript">`
203- `KeepDocumentTags` preserve `html`, `head` and `body` tags
204- `KeepEndTags` preserve all end tags
205- `KeepQuotes` preserve quotes around attribute values
206- `KeepWhitespace` preserve whitespace between inline tags but still collapse multiple whitespace characters into one
207
208After recent benchmarking and profiling it became really fast and minifies pages in the 10ms range, making it viable for on-the-fly minification.
209
210However, be careful when doing on-the-fly minification. Minification typically trims off 10% and does this at worst around about 20MB/s. This means users have to download slower than 2MB/s to make on-the-fly minification worthwhile. This may or may not apply in your situation. Rather use caching!
211
212### Whitespace removal
213The whitespace removal mechanism collapses all sequences of whitespace (spaces, newlines, tabs) to a single space. If the sequence contained a newline or carriage return it will collapse into a newline character instead. It trims all text parts (in between tags) depending on whether it was preceded by a space from a previous piece of text and whether it is followed up by a block element or an inline element. In the former case we can omit spaces while for inline elements whitespace has significance.
214
215Make sure your HTML doesn't depend on whitespace between `block` elements that have been changed to `inline` or `inline-block` elements using CSS. Your layout *should not* depend on those whitespaces as the minifier will remove them. An example is a menu consisting of multiple `<li>` that have `display:inline-block` applied and have whitespace in between them. It is bad practise to rely on whitespace for element positioning anyways!
216
217## CSS
218
219Minification typically shaves off about 10%-15%. This CSS minifier will _not_ do structural changes to your stylesheets. Although this could result in smaller files, the complexity is quite high and the risk of breaking website is high too.
220
221The CSS minifier will only use safe minifications:
222
223- remove comments and unnecessary whitespace (but keep `/*! ... */` which usually contains the license)
224- remove trailing semicolons
225- optimize `margin`, `padding` and `border-width` number of sides
226- shorten numbers by removing unnecessary `+` and zeros and rewriting with/without exponent
227- remove dimension and percentage for zero values
228- remove quotes for URLs
229- remove quotes for font families and make lowercase
230- rewrite hex colors to/from color names, or to three digit hex
231- rewrite `rgb(`, `rgba(`, `hsl(` and `hsla(` colors to hex or name
232- use four digit hex for alpha values (`transparent` &#8594; `#0000`)
233- replace `normal` and `bold` by numbers for `font-weight` and `font`
234- replace `none` &#8594; `0` for `border`, `background` and `outline`
235- lowercase all identifiers except classes, IDs and URLs to enhance gzip compression
236- shorten MS alpha function
237- rewrite data URIs with base64 or ASCII whichever is shorter
238- calls minifier for data URI mediatypes, thus you can compress embedded SVG files if you have that minifier attached
239- shorten aggregate declarations such as `background` and `font`
240
241It does purposely not use the following techniques:
242
243- (partially) merge rulesets
244- (partially) split rulesets
245- collapse multiple declarations when main declaration is defined within a ruleset (don't put `font-weight` within an already existing `font`, too complex)
246- remove overwritten properties in ruleset (this not always overwrites it, for example with `!important`)
247- rewrite properties into one ruleset if possible (like `margin-top`, `margin-right`, `margin-bottom` and `margin-left` &#8594; `margin`)
248- put nested ID selector at the front (`body > div#elem p` &#8594; `#elem p`)
249- rewrite attribute selectors for IDs and classes (`div[id=a]` &#8594; `div#a`)
250- put space after pseudo-selectors (IE6 is old, move on!)
251
252There are a couple of comparison tables online, such as [CSS Minifier Comparison](http://www.codenothing.com/benchmarks/css-compressor-3.0/full.html), [CSS minifiers comparison](http://www.phpied.com/css-minifiers-comparison/) and [CleanCSS tests](http://goalsmashers.github.io/css-minification-benchmark/). Comparing speed between each, this minifier will usually be between 10x-300x faster than existing implementations, and even rank among the top for minification ratios. It falls short with the purposely not implemented and often unsafe techniques.
253
254Options:
255
256- `KeepCSS2` prohibits using CSS3 syntax (such as exponents in numbers, or `rgba(` &#8594; `rgb(`), might be incomplete
257- `Precision` number of significant digits to preserve for numbers, `0` means no trimming
258
259## JS
260
261The JS minifier typically shaves off about 35% -- 65% of filesize depening on the file, which is a compression close to many other minifiers. Common speeds of PHP and JS implementations are about 100-300kB/s (see [Uglify2](http://lisperator.net/uglifyjs/), [Adventures in PHP web asset minimization](https://www.happyassassin.net/2014/12/29/adventures-in-php-web-asset-minimization/)). This implementation is orders of magnitude faster at around ~25MB/s.
262
263The following features are implemented:
264
265- remove superfluous whitespace
266- remove superfluous semicolons
267- shorten `true`, `false`, and `undefined` to `!0`, `!1` and `void 0`
268- rename variables and functions to shorter names (not in global scope)
269- move `var` declarations to the top of the global/function scope (if more than one)
270- collapse if/else statements to expressions
271- minify conditional expressions to simpler ones
272- merge sequential expression statements to one, including into `return` and `throw`
273- remove superfluous grouping in expressions
274- shorten or remove string escapes
275- convert object key or index expression from string to identifier or decimal
276- merge concatenated strings
277- rewrite numbers (binary, octal, decimal, hexadecimal) to shorter representations
278
279Options:
280
281- `KeepVarNames` keeps variable names as they are and omits shortening variable names
282- `Precision` number of significant digits to preserve for numbers, `0` means no trimming
283- `Version` ECMAScript version to use for output, `0` is the latest
284
285### Comparison with other tools
286
287Performance is measured with `time [command]` ran 10 times and selecting the fastest one, on a Thinkpad T460 (i5-6300U quad-core 2.4GHz running Arch Linux) using Go 1.15.
288
289- [minify](https://github.com/tdewolff/minify): `minify -o script.min.js script.js`
290- [esbuild](https://github.com/evanw/esbuild): `esbuild --minify --outfile=script.min.js script.js`
291- [terser](https://github.com/terser/terser): `terser script.js --compress --mangle -o script.min.js`
292- [UglifyJS](https://github.com/Skalman/UglifyJS-online): `uglifyjs --compress --mangle -o script.min.js script.js`
293- [Closure Compiler](https://github.com/google/closure-compiler): `closure-compiler -O SIMPLE --js script.js --js_output_file script.min.js --language_in ECMASCRIPT_NEXT -W QUIET --jscomp_off=checkVars` optimization level `SIMPLE` instead of `ADVANCED` to make similar assumptions as do the other tools (do not rename/assume anything of global level variables)
294
295#### Compression ratio (lower is better)
296All tools give very similar results, although UglifyJS compresses slightly better.
297
298| Tool | ace.js | dot.js | jquery.js | jqueryui.js | moment.js |
299| --- | --- | --- | --- | --- | --- |
300| **minify** | 53.7% | 64.8% | 34.2% | 51.3% | 34.8% |
301| esbuild | 53.8% | 66.3% | 34.4% | 53.1% | 34.8% |
302| terser | 53.2% | 65.2% | 34.2% | 51.8% | 34.7% |
303| UglifyJS | 53.1% | 64.7% | 33.8% | 50.7% | 34.2% |
304| Closure Compiler | 53.4% | 64.0% | 35.7% | 53.6% | 34.3% |
305
306#### Time (lower is better)
307Most tools are extremely slow, with `minify` and `esbuild` being orders of magnitudes faster.
308
309| Tool | ace.js | dot.js | jquery.js | jqueryui.js | moment.js |
310| --- | --- | --- | --- | --- | --- |
311| **minify** | 49ms | 5ms | 22ms | 35ms | 13ms |
312| esbuild | 64ms | 9ms | 31ms | 51ms | 17ms |
313| terser | 2900s | 180ms | 1400ms | 2200ms | 730ms |
314| UglifyJS | 3900ms | 210ms | 2000ms | 3100ms | 910ms |
315| Closure Compiler | 6100ms | 2500ms | 4400ms | 5300ms | 3500ms |
316
317## JSON
318
319Minification typically shaves off about 15% of filesize for common indented JSON such as generated by [JSON Generator](http://www.json-generator.com/).
320
321The JSON minifier only removes whitespace, which is the only thing that can be left out, and minifies numbers (`1000` => `1e3`).
322
323Options:
324
325- `Precision` number of significant digits to preserve for numbers, `0` means no trimming
326- `KeepNumbers` do not minify numbers if set to `true`, by default numbers will be minified
327
328## SVG
329
330The SVG minifier uses these minifications:
331
332- trim and collapse whitespace between all tags
333- strip comments, empty `doctype`, XML prelude, `metadata`
334- strip SVG version
335- strip CDATA sections wherever possible
336- collapse tags with no content to a void tag
337- minify style tag and attributes with the CSS minifier
338- minify colors
339- shorten lengths and numbers and remove default `px` unit
340- shorten `path` data
341- use relative or absolute positions in path data whichever is shorter
342
343TODO:
344- convert attributes to style attribute whenever shorter
345- merge path data? (same style and no intersection -- the latter is difficult)
346
347Options:
348
349- `Precision` number of significant digits to preserve for numbers, `0` means no trimming
350
351## XML
352
353The XML minifier uses these minifications:
354
355- strip unnecessary whitespace and otherwise collapse it to one space (or newline if it originally contained a newline)
356- strip comments
357- collapse tags with no content to a void tag
358- strip CDATA sections wherever possible
359
360Options:
361
362- `KeepWhitespace` preserve whitespace between inline tags but still collapse multiple whitespace characters into one
363
364## Usage
365Any input stream is being buffered by the minification functions. This is how the underlying buffer package inherently works to ensure high performance. The output stream however is not buffered. It is wise to preallocate a buffer as big as the input to which the output is written, or otherwise use `bufio` to buffer to a streaming writer.
366
367### New
368Retrieve a minifier struct which holds a map of mediatype &#8594; minifier functions.
369``` go
370m := minify.New()
371```
372
373The following loads all provided minifiers.
374``` go
375m := minify.New()
376m.AddFunc("text/css", css.Minify)
377m.AddFunc("text/html", html.Minify)
378m.AddFunc("image/svg+xml", svg.Minify)
379m.AddFuncRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), js.Minify)
380m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify)
381m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify)
382```
383
384You can set options to several minifiers.
385``` go
386m.Add("text/html", &html.Minifier{
387 KeepDefaultAttrVals: true,
388 KeepWhitespace: true,
389})
390```
391
392### From reader
393Minify from an `io.Reader` to an `io.Writer` for a specific mediatype.
394``` go
395if err := m.Minify(mediatype, w, r); err != nil {
396 panic(err)
397}
398```
399
400### From bytes
401Minify from and to a `[]byte` for a specific mediatype.
402``` go
403b, err = m.Bytes(mediatype, b)
404if err != nil {
405 panic(err)
406}
407```
408
409### From string
410Minify from and to a `string` for a specific mediatype.
411``` go
412s, err = m.String(mediatype, s)
413if err != nil {
414 panic(err)
415}
416```
417
418### To reader
419Get a minifying reader for a specific mediatype.
420``` go
421mr := m.Reader(mediatype, r)
422if _, err := mr.Read(b); err != nil {
423 panic(err)
424}
425```
426
427### To writer
428Get a minifying writer for a specific mediatype. Must be explicitly closed because it uses an `io.Pipe` underneath.
429``` go
430mw := m.Writer(mediatype, w)
431if mw.Write([]byte("input")); err != nil {
432 panic(err)
433}
434if err := mw.Close(); err != nil {
435 panic(err)
436}
437```
438
439### Middleware
440Minify resources on the fly using middleware. It passes a wrapped response writer to the handler that removes the Content-Length header. The minifier is chosen based on the Content-Type header or, if the header is empty, by the request URI file extension. This is on-the-fly processing, you should preferably cache the results though!
441``` go
442fs := http.FileServer(http.Dir("www/"))
443http.Handle("/", m.Middleware(fs))
444```
445
446### Custom minifier
447Add a minifier for a specific mimetype.
448``` go
449type CustomMinifier struct {
450 KeepLineBreaks bool
451}
452
453func (c *CustomMinifier) Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
454 // ...
455 return nil
456}
457
458m.Add(mimetype, &CustomMinifier{KeepLineBreaks: true})
459// or
460m.AddRegexp(regexp.MustCompile("/x-custom$"), &CustomMinifier{KeepLineBreaks: true})
461```
462
463Add a minify function for a specific mimetype.
464``` go
465m.AddFunc(mimetype, func(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
466 // ...
467 return nil
468})
469m.AddFuncRegexp(regexp.MustCompile("/x-custom$"), func(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
470 // ...
471 return nil
472})
473```
474
475Add a command `cmd` with arguments `args` for a specific mimetype.
476``` go
477m.AddCmd(mimetype, exec.Command(cmd, args...))
478m.AddCmdRegexp(regexp.MustCompile("/x-custom$"), exec.Command(cmd, args...))
479```
480
481### Mediatypes
482Using the `params map[string]string` argument one can pass parameters to the minifier such as seen in mediatypes (`type/subtype; key1=val2; key2=val2`). Examples are the encoding or charset of the data. Calling `Minify` will split the mimetype and parameters for the minifiers for you, but `MinifyMimetype` can be used if you already have them split up.
483
484Minifiers can also be added using a regular expression. For example a minifier with `image/.*` will match any image mime.
485
486## Examples
487### Common minifiers
488Basic example that minifies from stdin to stdout and loads the default HTML, CSS and JS minifiers. Optionally, one can enable `java -jar build/compiler.jar` to run for JS (for example the [ClosureCompiler](https://code.google.com/p/closure-compiler/)). Note that reading the file into a buffer first and writing to a pre-allocated buffer would be faster (but would disable streaming).
489``` go
490package main
491
492import (
493 "log"
494 "os"
495 "os/exec"
496
497 "github.com/tdewolff/minify/v2"
498 "github.com/tdewolff/minify/v2/css"
499 "github.com/tdewolff/minify/v2/html"
500 "github.com/tdewolff/minify/v2/js"
501 "github.com/tdewolff/minify/v2/json"
502 "github.com/tdewolff/minify/v2/svg"
503 "github.com/tdewolff/minify/v2/xml"
504)
505
506func main() {
507 m := minify.New()
508 m.AddFunc("text/css", css.Minify)
509 m.AddFunc("text/html", html.Minify)
510 m.AddFunc("image/svg+xml", svg.Minify)
511 m.AddFuncRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), js.Minify)
512 m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify)
513 m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify)
514
515 if err := m.Minify("text/html", os.Stdout, os.Stdin); err != nil {
516 panic(err)
517 }
518}
519```
520
521### External minifiers
522Below are some examples of using common external minifiers.
523
524#### Closure Compiler
525See [Closure Compiler Application](https://developers.google.com/closure/compiler/docs/gettingstarted_app). Not tested.
526
527``` go
528m.AddCmdRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"),
529 exec.Command("java", "-jar", "build/compiler.jar"))
530```
531
532### UglifyJS
533See [UglifyJS](https://github.com/mishoo/UglifyJS2).
534
535``` go
536m.AddCmdRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"),
537 exec.Command("uglifyjs"))
538```
539
540### esbuild
541See [esbuild](https://github.com/evanw/esbuild).
542
543``` go
544m.AddCmdRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"),
545 exec.Command("esbuild", "$in.js", "--minify", "--outfile=$out.js"))
546```
547
548### <a name="custom-minifier-example"></a> Custom minifier
549Custom minifier showing an example that implements the minifier function interface. Within a custom minifier, it is possible to call any minifier function (through `m minify.Minifier`) recursively when dealing with embedded resources.
550``` go
551package main
552
553import (
554 "bufio"
555 "fmt"
556 "io"
557 "log"
558 "strings"
559
560 "github.com/tdewolff/minify/v2"
561)
562
563func main() {
564 m := minify.New()
565 m.AddFunc("text/plain", func(m *minify.M, w io.Writer, r io.Reader, _ map[string]string) error {
566 // remove newlines and spaces
567 rb := bufio.NewReader(r)
568 for {
569 line, err := rb.ReadString('\n')
570 if err != nil && err != io.EOF {
571 return err
572 }
573 if _, errws := io.WriteString(w, strings.Replace(line, " ", "", -1)); errws != nil {
574 return errws
575 }
576 if err == io.EOF {
577 break
578 }
579 }
580 return nil
581 })
582
583 in := "Because my coffee was too cold, I heated it in the microwave."
584 out, err := m.String("text/plain", in)
585 if err != nil {
586 panic(err)
587 }
588 fmt.Println(out)
589 // Output: Becausemycoffeewastoocold,Iheateditinthemicrowave.
590}
591```
592
593### ResponseWriter
594#### Middleware
595``` go
596func main() {
597 m := minify.New()
598 m.AddFunc("text/css", css.Minify)
599 m.AddFunc("text/html", html.Minify)
600 m.AddFunc("image/svg+xml", svg.Minify)
601 m.AddFuncRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), js.Minify)
602 m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify)
603 m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify)
604
605 fs := http.FileServer(http.Dir("www/"))
606 http.Handle("/", m.MiddlewareWithError(fs))
607}
608
609func handleError(w http.ResponseWriter, r *http.Request, err error) {
610 http.Error(w, err.Error(), http.StatusInternalServerError)
611}
612```
613
614In order to properly handle minify errors, it is necessary to close the response writer since all writes are concurrently handled. There is no need to check errors on writes since they will be returned on closing.
615
616```go
617func main() {
618 m := minify.New()
619 m.AddFunc("text/html", html.Minify)
620 m.AddFuncRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), js.Minify)
621
622 input := `<script>const i = 1_000_</script>` // Faulty JS
623 req := httptest.NewRequest(http.MethodGet, "/", nil)
624 rec := httptest.NewRecorder()
625 m.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
626 w.Header().Set("Content-Type", "text/html")
627 _, _ = w.Write([]byte(input))
628
629 if err = w.(io.Closer).Close(); err != nil {
630 panic(err)
631 }
632 })).ServeHTTP(rec, req)
633}
634```
635
636#### ResponseWriter
637``` go
638func Serve(w http.ResponseWriter, r *http.Request) {
639 mw := m.ResponseWriter(w, r)
640 defer mw.Close()
641 w = mw
642
643 http.ServeFile(w, r, path.Join("www", r.URL.Path))
644}
645```
646
647#### Custom response writer
648ResponseWriter example which returns a ResponseWriter that minifies the content and then writes to the original ResponseWriter. Any write after applying this filter will be minified.
649``` go
650type MinifyResponseWriter struct {
651 http.ResponseWriter
652 io.WriteCloser
653}
654
655func (m MinifyResponseWriter) Write(b []byte) (int, error) {
656 return m.WriteCloser.Write(b)
657}
658
659// MinifyResponseWriter must be closed explicitly by calling site.
660func MinifyFilter(mediatype string, res http.ResponseWriter) MinifyResponseWriter {
661 m := minify.New()
662 // add minfiers
663
664 mw := m.Writer(mediatype, res)
665 return MinifyResponseWriter{res, mw}
666}
667```
668
669``` go
670// Usage
671func(w http.ResponseWriter, req *http.Request) {
672 w = MinifyFilter("text/html", w)
673 if _, err := io.WriteString(w, "<p class="message"> This HTTP response will be minified. </p>"); err != nil {
674 panic(err)
675 }
676 if err := w.Close(); err != nil {
677 panic(err)
678 }
679 // Output: <p class=message>This HTTP response will be minified.
680}
681```
682
683### Templates
684
685Here's an example of a replacement for `template.ParseFiles` from `template/html`, which automatically minifies each template before parsing it.
686
687Be aware that minifying templates will work in most cases but not all. Because the HTML minifier only works for valid HTML5, your template must be valid HTML5 of itself. Template tags are parsed as regular text by the minifier.
688
689``` go
690func compileTemplates(filenames ...string) (*template.Template, error) {
691 m := minify.New()
692 m.AddFunc("text/html", html.Minify)
693
694 var tmpl *template.Template
695 for _, filename := range filenames {
696 name := filepath.Base(filename)
697 if tmpl == nil {
698 tmpl = template.New(name)
699 } else {
700 tmpl = tmpl.New(name)
701 }
702
703 b, err := ioutil.ReadFile(filename)
704 if err != nil {
705 return nil, err
706 }
707
708 mb, err := m.Bytes("text/html", b)
709 if err != nil {
710 return nil, err
711 }
712 tmpl.Parse(string(mb))
713 }
714 return tmpl, nil
715}
716```
717
718Example usage:
719
720``` go
721templates := template.Must(compileTemplates("view.html", "home.html"))
722```
723
724## FAQ
725### Newlines remain in minified output
726While you might expect the minified output to be on a single line for it to be fully minified, this is not true. In many cases, using a literal newline doesn't affect the file size, and in some cases it may even reduce the file size.
727
728A typical example is HTML. Whitespace is significant in HTML, meaning that spaces and newlines between or around tags may affect how they are displayed. There is no distinction between a space or a newline and they may be interchanged without affecting the displayed HTML. Remember that a space (0x20) and a newline (0x0A) are both one byte long, so that there is no difference in file size when interchanging them. This minifier removes unnecessary whitespace by replacing stretches of spaces and newlines by a single whitespace character. Specifically, if the stretch of white space characters contains a newline, it will replace it by a newline and otherwise by a space. This doesn't affect the file size, but may help somewhat for debugging or file transmission objectives.
729
730Another example is JavaScript. Single or double quoted string literals may not contain newline characters but instead need to escape them as `\n`. These are two bytes instead of a single newline byte. Using template literals it is allowed to have literal newline characters and we can use that fact to shave-off one byte! The result is that the minified output contains newlines instead of escaped newline characters, which makes the final file size smaller. Of course, changing from single or double quotes to template literals depends on other factors as well, and this minifier makes a calculation whether the template literal results in a shorter file size or not before converting a string literal.
731
732## License
733Released under the [MIT license](LICENSE.md).
734
735[1]: http://golang.org/ "Go Language"
diff --git a/vendor/github.com/tdewolff/minify/v2/common.go b/vendor/github.com/tdewolff/minify/v2/common.go
new file mode 100644
index 0000000..3773a9b
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/common.go
@@ -0,0 +1,524 @@
1package minify
2
3import (
4 "bytes"
5 "encoding/base64"
6
7 "github.com/tdewolff/parse/v2"
8 "github.com/tdewolff/parse/v2/strconv"
9)
10
11var (
12 textMimeBytes = []byte("text/plain")
13 charsetASCIIBytes = []byte("charset=us-ascii")
14 dataBytes = []byte("data:")
15 base64Bytes = []byte(";base64")
16)
17
18// Epsilon is the closest number to zero that is not considered to be zero.
19var Epsilon = 0.00001
20
21// Mediatype minifies a given mediatype by removing all whitespace and lowercasing all parts except strings (which may be case sensitive).
22func Mediatype(b []byte) []byte {
23 j := 0
24 inString := false
25 start, lastString := 0, 0
26 for i, c := range b {
27 if !inString && parse.IsWhitespace(c) {
28 if start != 0 {
29 j += copy(b[j:], b[start:i])
30 } else {
31 j += i
32 }
33 start = i + 1
34 } else if c == '"' {
35 inString = !inString
36 if inString {
37 if i-lastString < 1024 { // ToLower may otherwise slow down minification greatly
38 parse.ToLower(b[lastString:i])
39 }
40 } else {
41 lastString = j + (i + 1 - start)
42 }
43 }
44 }
45 if start != 0 {
46 j += copy(b[j:], b[start:])
47 parse.ToLower(b[lastString:j])
48 return b[:j]
49 }
50 parse.ToLower(b[lastString:])
51 return b
52}
53
54// DataURI minifies a data URI and calls a minifier by the specified mediatype. Specifications: https://www.ietf.org/rfc/rfc2397.txt.
55func DataURI(m *M, dataURI []byte) []byte {
56 origData := parse.Copy(dataURI)
57 mediatype, data, err := parse.DataURI(dataURI)
58 if err != nil {
59 return dataURI
60 }
61
62 data, _ = m.Bytes(string(mediatype), data)
63 base64Len := len(";base64") + base64.StdEncoding.EncodedLen(len(data))
64 asciiLen := len(data)
65 for _, c := range data {
66 if parse.DataURIEncodingTable[c] {
67 asciiLen += 2
68 }
69 if asciiLen > base64Len {
70 break
71 }
72 }
73 if len(origData) < base64Len && len(origData) < asciiLen {
74 return origData
75 }
76 if base64Len < asciiLen {
77 encoded := make([]byte, base64Len-len(";base64"))
78 base64.StdEncoding.Encode(encoded, data)
79 data = encoded
80 mediatype = append(mediatype, base64Bytes...)
81 } else {
82 data = parse.EncodeURL(data, parse.DataURIEncodingTable)
83 }
84 if len("text/plain") <= len(mediatype) && parse.EqualFold(mediatype[:len("text/plain")], textMimeBytes) {
85 mediatype = mediatype[len("text/plain"):]
86 }
87 for i := 0; i+len(";charset=us-ascii") <= len(mediatype); i++ {
88 // must start with semicolon and be followed by end of mediatype or semicolon
89 if mediatype[i] == ';' && parse.EqualFold(mediatype[i+1:i+len(";charset=us-ascii")], charsetASCIIBytes) && (i+len(";charset=us-ascii") >= len(mediatype) || mediatype[i+len(";charset=us-ascii")] == ';') {
90 mediatype = append(mediatype[:i], mediatype[i+len(";charset=us-ascii"):]...)
91 break
92 }
93 }
94 return append(append(append(dataBytes, mediatype...), ','), data...)
95}
96
97// MaxInt is the maximum value of int.
98const MaxInt = int(^uint(0) >> 1)
99
100// MinInt is the minimum value of int.
101const MinInt = -MaxInt - 1
102
103// Decimal minifies a given byte slice containing a decimal and removes superfluous characters. It differs from Number in that it does not parse exponents.
104// It does not parse or output exponents. prec is the number of significant digits. When prec is zero it will keep all digits. Only digits after the dot can be removed to reach the number of significant digits. Very large number may thus have more significant digits.
105func Decimal(num []byte, prec int) []byte {
106 if len(num) <= 1 {
107 return num
108 }
109
110 // omit first + and register mantissa start and end, whether it's negative and the exponent
111 neg := false
112 start := 0
113 dot := -1
114 end := len(num)
115 if 0 < end && (num[0] == '+' || num[0] == '-') {
116 if num[0] == '-' {
117 neg = true
118 }
119 start++
120 }
121 for i, c := range num[start:] {
122 if c == '.' {
123 dot = start + i
124 break
125 }
126 }
127 if dot == -1 {
128 dot = end
129 }
130
131 // trim leading zeros but leave at least one digit
132 for start < end-1 && num[start] == '0' {
133 start++
134 }
135 // trim trailing zeros
136 i := end - 1
137 for ; dot < i; i-- {
138 if num[i] != '0' {
139 end = i + 1
140 break
141 }
142 }
143 if i == dot {
144 end = dot
145 if start == end {
146 num[start] = '0'
147 return num[start : start+1]
148 }
149 } else if start == end-1 && num[start] == '0' {
150 return num[start:end]
151 }
152
153 // apply precision
154 if 0 < prec && dot <= start+prec {
155 precEnd := start + prec + 1 // include dot
156 if dot == start { // for numbers like .012
157 digit := start + 1
158 for digit < end && num[digit] == '0' {
159 digit++
160 }
161 precEnd = digit + prec
162 }
163 if precEnd < end {
164 end = precEnd
165
166 // process either an increase from a lesser significant decimal (>= 5)
167 // or remove trailing zeros after the dot, or both
168 i := end - 1
169 inc := '5' <= num[end]
170 for ; start < i; i-- {
171 if i == dot {
172 // no-op
173 } else if inc && num[i] != '9' {
174 num[i]++
175 inc = false
176 break
177 } else if inc && i < dot { // end inc for integer
178 num[i] = '0'
179 } else if !inc && (i < dot || num[i] != '0') {
180 break
181 }
182 }
183 if i < dot {
184 end = dot
185 } else {
186 end = i + 1
187 }
188
189 if inc {
190 if dot == start && end == start+1 {
191 num[start] = '1'
192 } else if num[start] == '9' {
193 num[start] = '1'
194 num[start+1] = '0'
195 end++
196 } else {
197 num[start]++
198 }
199 }
200 }
201 }
202
203 if neg {
204 start--
205 num[start] = '-'
206 }
207 return num[start:end]
208}
209
210// Number minifies a given byte slice containing a number and removes superfluous characters.
211func Number(num []byte, prec int) []byte {
212 if len(num) <= 1 {
213 return num
214 }
215
216 // omit first + and register mantissa start and end, whether it's negative and the exponent
217 neg := false
218 start := 0
219 dot := -1
220 end := len(num)
221 origExp := 0
222 if num[0] == '+' || num[0] == '-' {
223 if num[0] == '-' {
224 neg = true
225 }
226 start++
227 }
228 for i, c := range num[start:] {
229 if c == '.' {
230 dot = start + i
231 } else if c == 'e' || c == 'E' {
232 end = start + i
233 i += start + 1
234 if i < len(num) && num[i] == '+' {
235 i++
236 }
237 if tmpOrigExp, n := strconv.ParseInt(num[i:]); 0 < n && int64(MinInt) <= tmpOrigExp && tmpOrigExp <= int64(MaxInt) {
238 // range checks for when int is 32 bit
239 origExp = int(tmpOrigExp)
240 } else {
241 return num
242 }
243 break
244 }
245 }
246 if dot == -1 {
247 dot = end
248 }
249
250 // trim leading zeros but leave at least one digit
251 for start < end-1 && num[start] == '0' {
252 start++
253 }
254 // trim trailing zeros
255 i := end - 1
256 for ; dot < i; i-- {
257 if num[i] != '0' {
258 end = i + 1
259 break
260 }
261 }
262 if i == dot {
263 end = dot
264 if start == end {
265 num[start] = '0'
266 return num[start : start+1]
267 }
268 } else if start == end-1 && num[start] == '0' {
269 return num[start:end]
270 }
271
272 // apply precision
273 if 0 < prec { //&& (dot <= start+prec || start+prec+1 < dot || 0 < origExp) { // don't minify 9 to 10, but do 999 to 1e3 and 99e1 to 1e3
274 precEnd := start + prec
275 if dot == start { // for numbers like .012
276 digit := start + 1
277 for digit < end && num[digit] == '0' {
278 digit++
279 }
280 precEnd = digit + prec
281 } else if dot < precEnd { // for numbers where precision will include the dot
282 precEnd++
283 }
284 if precEnd < end && (dot < end || 1 < dot-precEnd+origExp) { // do not minify 9=>10 or 99=>100 or 9e1=>1e2 (but 90), but 999=>1e3 and 99e1=>1e3
285 end = precEnd
286 inc := '5' <= num[end]
287 if dot == end {
288 inc = end+1 < len(num) && '5' <= num[end+1]
289 }
290 if precEnd < dot {
291 origExp += dot - precEnd
292 dot = precEnd
293 }
294 // process either an increase from a lesser significant decimal (>= 5)
295 // and remove trailing zeros
296 i := end - 1
297 for ; start < i; i-- {
298 if i == dot {
299 // no-op
300 } else if inc && num[i] != '9' {
301 num[i]++
302 inc = false
303 break
304 } else if !inc && num[i] != '0' {
305 break
306 }
307 }
308 end = i + 1
309 if end < dot {
310 origExp += dot - end
311 dot = end
312 }
313 if inc { // single digit left
314 if dot == start {
315 num[start] = '1'
316 dot = start + 1
317 } else if num[start] == '9' {
318 num[start] = '1'
319 origExp++
320 } else {
321 num[start]++
322 }
323 }
324 }
325 }
326
327 // n is the number of significant digits
328 // normExp would be the exponent if it were normalised (0.1 <= f < 1)
329 n := 0
330 normExp := 0
331 if dot == start {
332 for i = dot + 1; i < end; i++ {
333 if num[i] != '0' {
334 n = end - i
335 normExp = dot - i + 1
336 break
337 }
338 }
339 } else if dot == end {
340 normExp = end - start
341 for i = end - 1; start <= i; i-- {
342 if num[i] != '0' {
343 n = i + 1 - start
344 end = i + 1
345 break
346 }
347 }
348 } else {
349 n = end - start - 1
350 normExp = dot - start
351 }
352
353 if origExp < 0 && (normExp < MinInt-origExp || normExp-n < MinInt-origExp) || 0 < origExp && (MaxInt-origExp < normExp || MaxInt-origExp < normExp-n) {
354 return num // exponent overflow
355 }
356 normExp += origExp
357
358 // intExp would be the exponent if it were an integer
359 intExp := normExp - n
360 lenIntExp := strconv.LenInt(int64(intExp))
361 lenNormExp := strconv.LenInt(int64(normExp))
362
363 // there are three cases to consider when printing the number
364 // case 1: without decimals and with a positive exponent (large numbers: 5e4)
365 // case 2: with decimals and with a negative exponent (small numbers with many digits: .123456e-4)
366 // case 3: with decimals and without an exponent (around zero: 5.6)
367 // case 4: without decimals and with a negative exponent (small numbers: 123456e-9)
368 if n <= normExp {
369 // case 1: print number with positive exponent
370 if dot < end {
371 // remove dot, either from the front or copy the smallest part
372 if dot == start {
373 start = end - n
374 } else if dot-start < end-dot-1 {
375 copy(num[start+1:], num[start:dot])
376 start++
377 } else {
378 copy(num[dot:], num[dot+1:end])
379 end--
380 }
381 }
382 if n+3 <= normExp {
383 num[end] = 'e'
384 end++
385 for i := end + lenIntExp - 1; end <= i; i-- {
386 num[i] = byte(intExp%10) + '0'
387 intExp /= 10
388 }
389 end += lenIntExp
390 } else if n+2 == normExp {
391 num[end] = '0'
392 num[end+1] = '0'
393 end += 2
394 } else if n+1 == normExp {
395 num[end] = '0'
396 end++
397 }
398 } else if normExp < -3 && lenNormExp < lenIntExp && dot < end {
399 // case 2: print normalized number (0.1 <= f < 1)
400 zeroes := -normExp + origExp
401 if 0 < zeroes {
402 copy(num[start+1:], num[start+1+zeroes:end])
403 end -= zeroes
404 } else if zeroes < 0 {
405 copy(num[start+1:], num[start:dot])
406 num[start] = '.'
407 }
408 num[end] = 'e'
409 num[end+1] = '-'
410 end += 2
411 for i := end + lenNormExp - 1; end <= i; i-- {
412 num[i] = -byte(normExp%10) + '0'
413 normExp /= 10
414 }
415 end += lenNormExp
416 } else if -lenIntExp-1 <= normExp {
417 // case 3: print number without exponent
418 zeroes := -normExp
419 if 0 < zeroes {
420 // dot placed at the front and negative exponent, adding zeroes
421 newDot := end - n - zeroes - 1
422 if newDot != dot {
423 d := start - newDot
424 if 0 < d {
425 if dot < end {
426 // copy original digits after the dot towards the end
427 copy(num[dot+1+d:], num[dot+1:end])
428 if start < dot {
429 // copy original digits before the dot towards the end
430 copy(num[start+d+1:], num[start:dot])
431 }
432 } else if start < dot {
433 // copy original digits before the dot towards the end
434 copy(num[start+d:], num[start:dot])
435 }
436 newDot = start
437 end += d
438 } else {
439 start += -d
440 }
441 num[newDot] = '.'
442 for i := 0; i < zeroes; i++ {
443 num[newDot+1+i] = '0'
444 }
445 }
446 } else {
447 // dot placed in the middle of the number
448 if dot == start {
449 // when there are zeroes after the dot
450 dot = end - n - 1
451 start = dot
452 } else if end <= dot {
453 // when input has no dot in it
454 dot = end
455 end++
456 }
457 newDot := start + normExp
458 // move digits between dot and newDot towards the end
459 if dot < newDot {
460 copy(num[dot:], num[dot+1:newDot+1])
461 } else if newDot < dot {
462 copy(num[newDot+1:], num[newDot:dot])
463 }
464 num[newDot] = '.'
465 }
466 } else {
467 // case 4: print number with negative exponent
468 // find new end, considering moving numbers to the front, removing the dot and increasing the length of the exponent
469 newEnd := end
470 if dot == start {
471 newEnd = start + n
472 } else {
473 newEnd--
474 }
475 newEnd += 2 + lenIntExp
476
477 exp := intExp
478 lenExp := lenIntExp
479 if newEnd < len(num) {
480 // it saves space to convert the decimal to an integer and decrease the exponent
481 if dot < end {
482 if dot == start {
483 copy(num[start:], num[end-n:end])
484 end = start + n
485 } else {
486 copy(num[dot:], num[dot+1:end])
487 end--
488 }
489 }
490 } else {
491 // it does not save space and will panic, so we revert to the original representation
492 exp = origExp
493 lenExp = 1
494 if origExp <= -10 || 10 <= origExp {
495 lenExp = strconv.LenInt(int64(origExp))
496 }
497 }
498 num[end] = 'e'
499 num[end+1] = '-'
500 end += 2
501 for i := end + lenExp - 1; end <= i; i-- {
502 num[i] = -byte(exp%10) + '0'
503 exp /= 10
504 }
505 end += lenExp
506 }
507
508 if neg {
509 start--
510 num[start] = '-'
511 }
512 return num[start:end]
513}
514
515func UpdateErrorPosition(err error, input *parse.Input, offset int) error {
516 if perr, ok := err.(*parse.Error); ok {
517 r := bytes.NewBuffer(input.Bytes())
518 line, column, _ := parse.Position(r, offset)
519 perr.Line += line - 1
520 perr.Column += column - 1
521 return perr
522 }
523 return err
524}
diff --git a/vendor/github.com/tdewolff/minify/v2/css/css.go b/vendor/github.com/tdewolff/minify/v2/css/css.go
new file mode 100644
index 0000000..4929609
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/css/css.go
@@ -0,0 +1,1549 @@
1// Package css minifies CSS3 following the specifications at http://www.w3.org/TR/css-syntax-3/.
2package css
3
4import (
5 "bytes"
6 "fmt"
7 "io"
8 "math"
9 "sort"
10 "strconv"
11 "strings"
12
13 "github.com/tdewolff/minify/v2"
14 "github.com/tdewolff/parse/v2"
15 "github.com/tdewolff/parse/v2/css"
16 strconvParse "github.com/tdewolff/parse/v2/strconv"
17)
18
19var (
20 spaceBytes = []byte(" ")
21 colonBytes = []byte(":")
22 semicolonBytes = []byte(";")
23 commaBytes = []byte(",")
24 leftBracketBytes = []byte("{")
25 rightBracketBytes = []byte("}")
26 rightParenBytes = []byte(")")
27 urlBytes = []byte("url(")
28 zeroBytes = []byte("0")
29 oneBytes = []byte("1")
30 transparentBytes = []byte("transparent")
31 blackBytes = []byte("#0000")
32 initialBytes = []byte("initial")
33 noneBytes = []byte("none")
34 autoBytes = []byte("auto")
35 leftBytes = []byte("left")
36 topBytes = []byte("top")
37 n400Bytes = []byte("400")
38 n700Bytes = []byte("700")
39 n50pBytes = []byte("50%")
40 n100pBytes = []byte("100%")
41 repeatXBytes = []byte("repeat-x")
42 repeatYBytes = []byte("repeat-y")
43 importantBytes = []byte("!important")
44 dataSchemeBytes = []byte("data:")
45)
46
47type cssMinifier struct {
48 m *minify.M
49 w io.Writer
50 p *css.Parser
51 o *Minifier
52
53 tokenBuffer []Token
54 tokensLevel int
55}
56
57////////////////////////////////////////////////////////////////
58
59// Minifier is a CSS minifier.
60type Minifier struct {
61 KeepCSS2 bool
62 Precision int // number of significant digits
63 newPrecision int // precision for new numbers
64}
65
66// Minify minifies CSS data, it reads from r and writes to w.
67func Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
68 return (&Minifier{}).Minify(m, w, r, params)
69}
70
71// Token is a parsed token with extra information for functions.
72type Token struct {
73 css.TokenType
74 Data []byte
75 Args []Token // only filled for functions
76 Fun, Ident Hash // only filled for functions and identifiers respectively
77}
78
79func (t Token) String() string {
80 if len(t.Args) == 0 {
81 return t.TokenType.String() + "(" + string(t.Data) + ")"
82 }
83 return fmt.Sprint(t.Args)
84}
85
86// Equal returns true if both tokens are equal.
87func (t Token) Equal(t2 Token) bool {
88 if t.TokenType == t2.TokenType && bytes.Equal(t.Data, t2.Data) && len(t.Args) == len(t2.Args) {
89 for i := 0; i < len(t.Args); i++ {
90 if t.Args[i].TokenType != t2.Args[i].TokenType || !bytes.Equal(t.Args[i].Data, t2.Args[i].Data) {
91 return false
92 }
93 }
94 return true
95 }
96 return false
97}
98
99// IsZero return true if a dimension, percentage, or number token is zero.
100func (t Token) IsZero() bool {
101 // as each number is already minified, starting with a zero means it is zero
102 return (t.TokenType == css.DimensionToken || t.TokenType == css.PercentageToken || t.TokenType == css.NumberToken) && t.Data[0] == '0'
103}
104
105// IsLength returns true if the token is a length.
106func (t Token) IsLength() bool {
107 if t.TokenType == css.DimensionToken {
108 return true
109 } else if t.TokenType == css.NumberToken && t.Data[0] == '0' {
110 return true
111 } else if t.TokenType == css.FunctionToken {
112 fun := ToHash(t.Data[:len(t.Data)-1])
113 if fun == Calc || fun == Min || fun == Max || fun == Clamp || fun == Attr || fun == Var || fun == Env {
114 return true
115 }
116 }
117 return false
118}
119
120// IsLengthPercentage returns true if the token is a length or percentage token.
121func (t Token) IsLengthPercentage() bool {
122 return t.TokenType == css.PercentageToken || t.IsLength()
123}
124
125////////////////////////////////////////////////////////////////
126
127// Minify minifies CSS data, it reads from r and writes to w.
128func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
129 o.newPrecision = o.Precision
130 if o.newPrecision <= 0 || 15 < o.newPrecision {
131 o.newPrecision = 15 // minimum number of digits a double can represent exactly
132 }
133
134 z := parse.NewInput(r)
135 defer z.Restore()
136
137 isInline := params != nil && params["inline"] == "1"
138 c := &cssMinifier{
139 m: m,
140 w: w,
141 p: css.NewParser(z, isInline),
142 o: o,
143 }
144 c.minifyGrammar()
145
146 if _, err := w.Write(nil); err != nil {
147 return err
148 }
149 if c.p.Err() == io.EOF {
150 return nil
151 }
152 return c.p.Err()
153}
154
155func (c *cssMinifier) minifyGrammar() {
156 semicolonQueued := false
157 for {
158 gt, _, data := c.p.Next()
159 switch gt {
160 case css.ErrorGrammar:
161 if c.p.HasParseError() {
162 if semicolonQueued {
163 c.w.Write(semicolonBytes)
164 }
165
166 // write out the offending declaration (but save the semicolon)
167 vals := c.p.Values()
168 if len(vals) > 0 && vals[len(vals)-1].TokenType == css.SemicolonToken {
169 vals = vals[:len(vals)-1]
170 semicolonQueued = true
171 }
172 for _, val := range vals {
173 c.w.Write(val.Data)
174 }
175 continue
176 }
177 return
178 case css.EndAtRuleGrammar, css.EndRulesetGrammar:
179 c.w.Write(rightBracketBytes)
180 semicolonQueued = false
181 continue
182 }
183
184 if semicolonQueued {
185 c.w.Write(semicolonBytes)
186 semicolonQueued = false
187 }
188
189 switch gt {
190 case css.AtRuleGrammar:
191 c.w.Write(data)
192 values := c.p.Values()
193 if ToHash(data[1:]) == Import && len(values) == 2 && values[1].TokenType == css.URLToken && 4 < len(values[1].Data) && values[1].Data[len(values[1].Data)-1] == ')' {
194 url := values[1].Data
195 if url[4] != '"' && url[4] != '\'' {
196 a := 4
197 for parse.IsWhitespace(url[a]) || parse.IsNewline(url[a]) {
198 a++
199 }
200 b := len(url) - 2
201 for a < b && (parse.IsWhitespace(url[b]) || parse.IsNewline(url[b])) {
202 b--
203 }
204 if a == b {
205 url = url[:2]
206 } else {
207 url = url[a-1 : b+2]
208 }
209 url[0] = '"'
210 url[len(url)-1] = '"'
211 } else {
212 url = url[4 : len(url)-1]
213 }
214 values[1].Data = url
215 }
216 for _, val := range values {
217 c.w.Write(val.Data)
218 }
219 semicolonQueued = true
220 case css.BeginAtRuleGrammar:
221 c.w.Write(data)
222 for _, val := range c.p.Values() {
223 c.w.Write(val.Data)
224 }
225 c.w.Write(leftBracketBytes)
226 case css.QualifiedRuleGrammar:
227 c.minifySelectors(data, c.p.Values())
228 c.w.Write(commaBytes)
229 case css.BeginRulesetGrammar:
230 c.minifySelectors(data, c.p.Values())
231 c.w.Write(leftBracketBytes)
232 case css.DeclarationGrammar:
233 c.minifyDeclaration(data, c.p.Values())
234 semicolonQueued = true
235 case css.CustomPropertyGrammar:
236 c.w.Write(data)
237 c.w.Write(colonBytes)
238 value := parse.TrimWhitespace(c.p.Values()[0].Data)
239 if len(c.p.Values()[0].Data) != 0 && len(value) == 0 {
240 value = spaceBytes
241 }
242 c.w.Write(value)
243 semicolonQueued = true
244 case css.CommentGrammar:
245 if len(data) > 5 && data[1] == '*' && data[2] == '!' {
246 c.w.Write(data[:3])
247 comment := parse.TrimWhitespace(parse.ReplaceMultipleWhitespace(data[3 : len(data)-2]))
248 c.w.Write(comment)
249 c.w.Write(data[len(data)-2:])
250 }
251 default:
252 c.w.Write(data)
253 }
254 }
255}
256
257func (c *cssMinifier) minifySelectors(property []byte, values []css.Token) {
258 inAttr := false
259 isClass := false
260 for _, val := range c.p.Values() {
261 if !inAttr {
262 if val.TokenType == css.IdentToken {
263 if !isClass {
264 parse.ToLower(val.Data)
265 }
266 isClass = false
267 } else if val.TokenType == css.DelimToken && val.Data[0] == '.' {
268 isClass = true
269 } else if val.TokenType == css.LeftBracketToken {
270 inAttr = true
271 }
272 } else {
273 if val.TokenType == css.StringToken && len(val.Data) > 2 {
274 s := val.Data[1 : len(val.Data)-1]
275 if css.IsIdent(s) {
276 c.w.Write(s)
277 continue
278 }
279 } else if val.TokenType == css.RightBracketToken {
280 inAttr = false
281 } else if val.TokenType == css.IdentToken && len(val.Data) == 1 && (val.Data[0] == 'i' || val.Data[0] == 'I') {
282 c.w.Write(spaceBytes)
283 }
284 }
285 c.w.Write(val.Data)
286 }
287}
288
289func (c *cssMinifier) parseFunction(values []css.Token) ([]Token, int) {
290 i := 1
291 level := 0
292 args := []Token{}
293 for ; i < len(values); i++ {
294 tt := values[i].TokenType
295 data := values[i].Data
296 if tt == css.LeftParenthesisToken {
297 level++
298 } else if tt == css.RightParenthesisToken {
299 if level == 0 {
300 i++
301 break
302 }
303 level--
304 }
305 if tt == css.FunctionToken {
306 subArgs, di := c.parseFunction(values[i:])
307 h := ToHash(parse.ToLower(parse.Copy(data[:len(data)-1]))) // TODO: use ToHashFold
308 args = append(args, Token{tt, data, subArgs, h, 0})
309 i += di - 1
310 } else {
311 var h Hash
312 if tt == css.IdentToken {
313 h = ToHash(parse.ToLower(parse.Copy(data))) // TODO: use ToHashFold
314 }
315 args = append(args, Token{tt, data, nil, 0, h})
316 }
317 }
318 return args, i
319}
320
321func (c *cssMinifier) parseDeclaration(values []css.Token) []Token {
322 // Check if this is a simple list of values separated by whitespace or commas, otherwise we'll not be processing
323 prevSep := true
324 tokens := c.tokenBuffer[:0]
325 for i := 0; i < len(values); i++ {
326 tt := values[i].TokenType
327 data := values[i].Data
328 if tt == css.LeftParenthesisToken || tt == css.LeftBraceToken || tt == css.LeftBracketToken ||
329 tt == css.RightParenthesisToken || tt == css.RightBraceToken || tt == css.RightBracketToken {
330 return nil
331 }
332
333 if !prevSep && tt != css.WhitespaceToken && tt != css.CommaToken && (tt != css.DelimToken || values[i].Data[0] != '/') {
334 return nil
335 }
336
337 if tt == css.WhitespaceToken || tt == css.CommaToken || tt == css.DelimToken && values[i].Data[0] == '/' {
338 if tt != css.WhitespaceToken {
339 tokens = append(tokens, Token{tt, data, nil, 0, 0})
340 }
341 prevSep = true
342 } else if tt == css.FunctionToken {
343 args, di := c.parseFunction(values[i:])
344 h := ToHash(parse.ToLower(parse.Copy(data[:len(data)-1]))) // TODO: use ToHashFold
345 tokens = append(tokens, Token{tt, data, args, h, 0})
346 prevSep = true
347 i += di - 1
348 } else {
349 var h Hash
350 if tt == css.IdentToken {
351 h = ToHash(parse.ToLower(parse.Copy(data))) // TODO: use ToHashFold
352 }
353 tokens = append(tokens, Token{tt, data, nil, 0, h})
354 prevSep = tt == css.URLToken
355 }
356 }
357 c.tokenBuffer = tokens // update buffer size for memory reuse
358 return tokens
359}
360
361func (c *cssMinifier) minifyDeclaration(property []byte, components []css.Token) {
362 c.w.Write(property)
363 c.w.Write(colonBytes)
364
365 if len(components) == 0 {
366 return
367 }
368
369 // Strip !important from the component list, this will be added later separately
370 important := false
371 if len(components) > 2 && components[len(components)-2].TokenType == css.DelimToken && components[len(components)-2].Data[0] == '!' && ToHash(components[len(components)-1].Data) == Important {
372 components = components[:len(components)-2]
373 important = true
374 }
375
376 prop := ToHash(property)
377 values := c.parseDeclaration(components)
378
379 // Do not process complex values (eg. containing blocks or is not alternated between whitespace/commas and flat values
380 if values == nil {
381 if prop == Filter && len(components) == 11 {
382 if bytes.Equal(components[0].Data, []byte("progid")) &&
383 components[1].TokenType == css.ColonToken &&
384 bytes.Equal(components[2].Data, []byte("DXImageTransform")) &&
385 components[3].Data[0] == '.' &&
386 bytes.Equal(components[4].Data, []byte("Microsoft")) &&
387 components[5].Data[0] == '.' &&
388 bytes.Equal(components[6].Data, []byte("Alpha(")) &&
389 bytes.Equal(parse.ToLower(components[7].Data), []byte("opacity")) &&
390 components[8].Data[0] == '=' &&
391 components[10].Data[0] == ')' {
392 components = components[6:]
393 components[0].Data = []byte("alpha(")
394 }
395 }
396
397 for _, component := range components {
398 c.w.Write(component.Data)
399 }
400 if important {
401 c.w.Write(importantBytes)
402 }
403 return
404 }
405
406 values = c.minifyTokens(prop, 0, values)
407 if len(values) > 0 {
408 values = c.minifyProperty(prop, values)
409 }
410 c.writeDeclaration(values, important)
411}
412
413func (c *cssMinifier) writeFunction(args []Token) {
414 for _, arg := range args {
415 c.w.Write(arg.Data)
416 if arg.TokenType == css.FunctionToken {
417 c.writeFunction(arg.Args)
418 c.w.Write(rightParenBytes)
419 }
420 }
421}
422
423func (c *cssMinifier) writeDeclaration(values []Token, important bool) {
424 prevSep := true
425 for _, value := range values {
426 if !prevSep && value.TokenType != css.CommaToken && (value.TokenType != css.DelimToken || value.Data[0] != '/') {
427 c.w.Write(spaceBytes)
428 }
429
430 c.w.Write(value.Data)
431 if value.TokenType == css.FunctionToken {
432 c.writeFunction(value.Args)
433 c.w.Write(rightParenBytes)
434 }
435
436 if value.TokenType == css.CommaToken || value.TokenType == css.DelimToken && value.Data[0] == '/' || value.TokenType == css.FunctionToken || value.TokenType == css.URLToken {
437 prevSep = true
438 } else {
439 prevSep = false
440 }
441 }
442
443 if important {
444 c.w.Write(importantBytes)
445 }
446}
447
448func (c *cssMinifier) minifyTokens(prop Hash, fun Hash, values []Token) []Token {
449 if 100 < c.tokensLevel+1 {
450 return values
451 }
452 c.tokensLevel++
453
454 for i, value := range values {
455 tt := value.TokenType
456 switch tt {
457 case css.NumberToken:
458 if prop == Z_Index || prop == Counter_Increment || prop == Counter_Reset || prop == Orphans || prop == Widows {
459 break // integers
460 }
461 if c.o.KeepCSS2 {
462 values[i].Data = minify.Decimal(values[i].Data, c.o.Precision) // don't use exponents
463 } else {
464 values[i].Data = minify.Number(values[i].Data, c.o.Precision)
465 }
466 case css.PercentageToken:
467 n := len(values[i].Data) - 1
468 if c.o.KeepCSS2 {
469 values[i].Data = minify.Decimal(values[i].Data[:n], c.o.Precision) // don't use exponents
470 } else {
471 values[i].Data = minify.Number(values[i].Data[:n], c.o.Precision)
472 }
473 values[i].Data = append(values[i].Data, '%')
474 case css.DimensionToken:
475 var dim []byte
476 values[i], dim = c.minifyDimension(values[i])
477 if 1 < len(values[i].Data) && values[i].Data[0] == '0' && optionalZeroDimension[string(dim)] && prop != Flex && fun == 0 {
478 // cut dimension for zero value, TODO: don't hardcode check for Flex and remove the dimension in minifyDimension
479 values[i].Data = values[i].Data[:1]
480 }
481 case css.StringToken:
482 values[i].Data = removeMarkupNewlines(values[i].Data)
483 case css.URLToken:
484 if 10 < len(values[i].Data) {
485 uri := parse.TrimWhitespace(values[i].Data[4 : len(values[i].Data)-1])
486 delim := byte('"')
487 if 1 < len(uri) && (uri[0] == '\'' || uri[0] == '"') {
488 delim = uri[0]
489 uri = removeMarkupNewlines(uri)
490 uri = uri[1 : len(uri)-1]
491 }
492 if 4 < len(uri) && parse.EqualFold(uri[:5], dataSchemeBytes) {
493 uri = minify.DataURI(c.m, uri)
494 }
495 if css.IsURLUnquoted(uri) {
496 values[i].Data = append(append(urlBytes, uri...), ')')
497 } else {
498 values[i].Data = append(append(append(urlBytes, delim), uri...), delim, ')')
499 }
500 }
501 case css.FunctionToken:
502 values[i].Args = c.minifyTokens(prop, values[i].Fun, values[i].Args)
503
504 fun := values[i].Fun
505 args := values[i].Args
506 if fun == Rgb || fun == Rgba || fun == Hsl || fun == Hsla {
507 valid := true
508 vals := []float64{}
509 for i, arg := range args {
510 numeric := arg.TokenType == css.NumberToken || arg.TokenType == css.PercentageToken
511 separator := arg.TokenType == css.CommaToken || i != 5 && arg.TokenType == css.WhitespaceToken || i == 5 && arg.TokenType == css.DelimToken && arg.Data[0] == '/'
512 if i%2 == 0 && !numeric || i%2 == 1 && !separator {
513 valid = false
514 break
515 } else if numeric {
516 var d float64
517 if arg.TokenType == css.PercentageToken {
518 var err error
519 d, err = strconv.ParseFloat(string(arg.Data[:len(arg.Data)-1]), 32) // can overflow
520 if err != nil {
521 valid = false
522 break
523 }
524 d /= 100.0
525 if d < minify.Epsilon {
526 d = 0.0
527 } else if 1.0-minify.Epsilon < d {
528 d = 1.0
529 }
530 } else {
531 var err error
532 d, err = strconv.ParseFloat(string(arg.Data), 32) // can overflow
533 if err != nil {
534 valid = false
535 break
536 }
537 }
538 vals = append(vals, d)
539 }
540 }
541 if !valid {
542 break
543 }
544
545 a := 1.0
546 if len(vals) == 4 {
547 if vals[0] < minify.Epsilon && vals[1] < minify.Epsilon && vals[2] < minify.Epsilon && vals[3] < minify.Epsilon {
548 values[i] = Token{css.IdentToken, transparentBytes, nil, 0, Transparent}
549 break
550 } else if 1.0-minify.Epsilon < vals[3] {
551 vals = vals[:3]
552 values[i].Args = values[i].Args[:len(values[i].Args)-2]
553 if fun == Rgba || fun == Hsla {
554 values[i].Data = values[i].Data[:len(values[i].Data)-1]
555 values[i].Data[len(values[i].Data)-1] = '('
556 }
557 } else {
558 a = vals[3]
559 }
560 }
561
562 if a == 1.0 && (len(vals) == 3 || len(vals) == 4) { // only minify color if fully opaque
563 if fun == Rgb || fun == Rgba {
564 for j := 0; j < 3; j++ {
565 if args[j*2].TokenType == css.NumberToken {
566 vals[j] /= 255.0
567 if vals[j] < minify.Epsilon {
568 vals[j] = 0.0
569 } else if 1.0-minify.Epsilon < vals[j] {
570 vals[j] = 1.0
571 }
572 }
573 }
574 values[i] = rgbToToken(vals[0], vals[1], vals[2])
575 break
576 } else if fun == Hsl || fun == Hsla && args[0].TokenType == css.NumberToken && args[2].TokenType == css.PercentageToken && args[4].TokenType == css.PercentageToken {
577 vals[0] /= 360.0
578 _, vals[0] = math.Modf(vals[0])
579 if vals[0] < 0.0 {
580 vals[0] = 1.0 + vals[0]
581 }
582 r, g, b := css.HSL2RGB(vals[0], vals[1], vals[2])
583 values[i] = rgbToToken(r, g, b)
584 break
585 }
586 } else if len(vals) == 4 {
587 args[6] = minifyNumberPercentage(args[6])
588 }
589
590 if 3 <= len(vals) && (fun == Rgb || fun == Rgba) {
591 // 0%, 20%, 40%, 60%, 80% and 100% can be represented exactly as, 51, 102, 153, 204, and 255 respectively
592 removePercentage := true
593 for j := 0; j < 3; j++ {
594 if args[j*2].TokenType != css.PercentageToken || 2.0*minify.Epsilon <= math.Mod(vals[j]+minify.Epsilon, 0.2) {
595 removePercentage = false
596 break
597 }
598 }
599 if removePercentage {
600 for j := 0; j < 3; j++ {
601 args[j*2].TokenType = css.NumberToken
602 if vals[j] < minify.Epsilon {
603 args[j*2].Data = zeroBytes
604 } else if math.Abs(vals[j]-0.2) < minify.Epsilon {
605 args[j*2].Data = []byte("51")
606 } else if math.Abs(vals[j]-0.4) < minify.Epsilon {
607 args[j*2].Data = []byte("102")
608 } else if math.Abs(vals[j]-0.6) < minify.Epsilon {
609 args[j*2].Data = []byte("153")
610 } else if math.Abs(vals[j]-0.8) < minify.Epsilon {
611 args[j*2].Data = []byte("204")
612 } else if math.Abs(vals[j]-1.0) < minify.Epsilon {
613 args[j*2].Data = []byte("255")
614 }
615 }
616 }
617 }
618 }
619 }
620 }
621 c.tokensLevel--
622 return values
623}
624
625func (c *cssMinifier) minifyProperty(prop Hash, values []Token) []Token {
626 // limit maximum to prevent slow recursions (e.g. for background's append)
627 if 100 < len(values) {
628 return values
629 }
630
631 switch prop {
632 case Font:
633 if len(values) > 1 { // must contain atleast font-size and font-family
634 // the font-families are separated by commas and are at the end of font
635 // get index for last token before font family names
636 i := len(values) - 1
637 for j, value := range values[2:] {
638 if value.TokenType == css.CommaToken {
639 i = 2 + j - 1 // identifier before first comma is a font-family
640 break
641 }
642 }
643 i--
644
645 // advance i while still at font-families when they contain spaces but no quotes
646 for ; i > 0; i-- { // i cannot be 0, font-family must be prepended by font-size
647 if values[i-1].TokenType == css.DelimToken && values[i-1].Data[0] == '/' {
648 break
649 } else if values[i].TokenType != css.IdentToken && values[i].TokenType != css.StringToken {
650 break
651 } else if h := values[i].Ident; h == Xx_Small || h == X_Small || h == Small || h == Medium || h == Large || h == X_Large || h == Xx_Large || h == Smaller || h == Larger || h == Inherit || h == Initial || h == Unset {
652 // inherit, initial and unset are followed by an IdentToken/StringToken, so must be for font-size
653 break
654 }
655 }
656
657 // font-family minified in place
658 values = append(values[:i+1], c.minifyProperty(Font_Family, values[i+1:])...)
659
660 // fix for IE9, IE10, IE11: font name starting with `-` is not recognized
661 if values[i+1].Data[0] == '-' {
662 v := make([]byte, len(values[i+1].Data)+2)
663 v[0] = '\''
664 copy(v[1:], values[i+1].Data)
665 v[len(v)-1] = '\''
666 values[i+1].Data = v
667 }
668
669 if i > 0 {
670 // line-height
671 if i > 1 && values[i-1].TokenType == css.DelimToken && values[i-1].Data[0] == '/' {
672 if values[i].Ident == Normal {
673 values = append(values[:i-1], values[i+1:]...)
674 }
675 i -= 2
676 }
677
678 // font-size
679 i--
680
681 for ; i > -1; i-- {
682 if values[i].Ident == Normal {
683 values = append(values[:i], values[i+1:]...)
684 } else if values[i].Ident == Bold {
685 values[i].TokenType = css.NumberToken
686 values[i].Data = n700Bytes
687 } else if values[i].TokenType == css.NumberToken && bytes.Equal(values[i].Data, n400Bytes) {
688 values = append(values[:i], values[i+1:]...)
689 }
690 }
691 }
692 }
693 case Font_Family:
694 for i, value := range values {
695 if value.TokenType == css.StringToken && 2 < len(value.Data) {
696 unquote := true
697 parse.ToLower(value.Data)
698 s := value.Data[1 : len(value.Data)-1]
699 if 0 < len(s) {
700 for _, split := range bytes.Split(s, spaceBytes) {
701 // if len is zero, it contains two consecutive spaces
702 if len(split) == 0 || !css.IsIdent(split) {
703 unquote = false
704 break
705 }
706 }
707 }
708 if unquote {
709 values[i].Data = s
710 }
711 }
712 }
713 case Font_Weight:
714 if values[0].Ident == Normal {
715 values[0].TokenType = css.NumberToken
716 values[0].Data = n400Bytes
717 } else if values[0].Ident == Bold {
718 values[0].TokenType = css.NumberToken
719 values[0].Data = n700Bytes
720 }
721 case Url:
722 for i := 0; i < len(values); i++ {
723 if values[i].TokenType == css.FunctionToken && len(values[i].Args) == 1 {
724 fun := values[i].Fun
725 data := values[i].Args[0].Data
726 if fun == Local && (data[0] == '\'' || data[0] == '"') {
727 if css.IsURLUnquoted(data[1 : len(data)-1]) {
728 data = data[1 : len(data)-1]
729 }
730 values[i].Args[0].Data = data
731 }
732 }
733 }
734 case Margin, Padding, Border_Width:
735 switch len(values) {
736 case 2:
737 if values[0].Equal(values[1]) {
738 values = values[:1]
739 }
740 case 3:
741 if values[0].Equal(values[1]) && values[0].Equal(values[2]) {
742 values = values[:1]
743 } else if values[0].Equal(values[2]) {
744 values = values[:2]
745 }
746 case 4:
747 if values[0].Equal(values[1]) && values[0].Equal(values[2]) && values[0].Equal(values[3]) {
748 values = values[:1]
749 } else if values[0].Equal(values[2]) && values[1].Equal(values[3]) {
750 values = values[:2]
751 } else if values[1].Equal(values[3]) {
752 values = values[:3]
753 }
754 }
755 case Border, Border_Bottom, Border_Left, Border_Right, Border_Top:
756 for i := 0; i < len(values); i++ {
757 if values[i].Ident == None || values[i].Ident == Currentcolor || values[i].Ident == Medium {
758 values = append(values[:i], values[i+1:]...)
759 i--
760 } else {
761 values[i] = minifyColor(values[i])
762 }
763 }
764 if len(values) == 0 {
765 values = []Token{{css.IdentToken, noneBytes, nil, 0, None}}
766 }
767 case Outline:
768 for i := 0; i < len(values); i++ {
769 if values[i].Ident == Invert || values[i].Ident == None || values[i].Ident == Medium {
770 values = append(values[:i], values[i+1:]...)
771 i--
772 } else {
773 values[i] = minifyColor(values[i])
774 }
775 }
776 if len(values) == 0 {
777 values = []Token{{css.IdentToken, noneBytes, nil, 0, None}}
778 }
779 case Background:
780 start := 0
781 for end := 0; end <= len(values); end++ { // loop over comma-separated lists
782 if end != len(values) && values[end].TokenType != css.CommaToken {
783 continue
784 } else if start == end {
785 start++
786 continue
787 }
788
789 // minify background-size and lowercase all identifiers
790 for i := start; i < end; i++ {
791 if values[i].TokenType == css.DelimToken && values[i].Data[0] == '/' {
792 // background-size consists of either [<length-percentage> | auto | cover | contain] or [<length-percentage> | auto]{2}
793 // we can only minify the latter
794 if i+1 < end && (values[i+1].TokenType == css.NumberToken || values[i+1].IsLengthPercentage() || values[i+1].Ident == Auto) {
795 if i+2 < end && (values[i+2].TokenType == css.NumberToken || values[i+2].IsLengthPercentage() || values[i+2].Ident == Auto) {
796 sizeValues := c.minifyProperty(Background_Size, values[i+1:i+3])
797 if len(sizeValues) == 1 && sizeValues[0].Ident == Auto {
798 // remove background-size if it is '/ auto' after minifying the property
799 values = append(values[:i], values[i+3:]...)
800 end -= 3
801 i--
802 } else {
803 values = append(values[:i+1], append(sizeValues, values[i+3:]...)...)
804 end -= 2 - len(sizeValues)
805 i += len(sizeValues) - 1
806 }
807 } else if values[i+1].Ident == Auto {
808 // remove background-size if it is '/ auto'
809 values = append(values[:i], values[i+2:]...)
810 end -= 2
811 i--
812 }
813 }
814 }
815 }
816
817 // minify all other values
818 iPaddingBox := -1 // position of background-origin that is padding-box
819 for i := start; i < end; i++ {
820 h := values[i].Ident
821 values[i] = minifyColor(values[i])
822 if values[i].TokenType == css.IdentToken {
823 if i+1 < end && values[i+1].TokenType == css.IdentToken && (h == Space || h == Round || h == Repeat || h == No_Repeat) {
824 if h2 := values[i+1].Ident; h2 == Space || h2 == Round || h2 == Repeat || h2 == No_Repeat {
825 repeatValues := c.minifyProperty(Background_Repeat, values[i:i+2])
826 if len(repeatValues) == 1 && repeatValues[0].Ident == Repeat {
827 values = append(values[:i], values[i+2:]...)
828 end -= 2
829 i--
830 } else {
831 values = append(values[:i], append(repeatValues, values[i+2:]...)...)
832 end -= 2 - len(repeatValues)
833 i += len(repeatValues) - 1
834 }
835 continue
836 }
837 } else if h == None || h == Scroll || h == Transparent {
838 values = append(values[:i], values[i+1:]...)
839 end--
840 i--
841 continue
842 } else if h == Border_Box || h == Padding_Box {
843 if iPaddingBox == -1 && h == Padding_Box { // background-origin
844 iPaddingBox = i
845 } else if iPaddingBox != -1 && h == Border_Box { // background-clip
846 values = append(values[:i], values[i+1:]...)
847 values = append(values[:iPaddingBox], values[iPaddingBox+1:]...)
848 end -= 2
849 i -= 2
850 }
851 continue
852 }
853 } else if values[i].TokenType == css.HashToken && bytes.Equal(values[i].Data, blackBytes) {
854 values = append(values[:i], values[i+1:]...)
855 end--
856 i--
857 continue
858 }
859
860 // further minify background-position and background-size combination
861 if values[i].TokenType == css.NumberToken || values[i].IsLengthPercentage() || h == Left || h == Right || h == Top || h == Bottom || h == Center {
862 j := i + 1
863 for ; j < len(values); j++ {
864 if h := values[j].Ident; h == Left || h == Right || h == Top || h == Bottom || h == Center {
865 continue
866 } else if values[j].TokenType == css.NumberToken || values[j].IsLengthPercentage() {
867 continue
868 }
869 break
870 }
871
872 positionValues := c.minifyProperty(Background_Position, values[i:j])
873 hasSize := j < len(values) && values[j].TokenType == css.DelimToken && values[j].Data[0] == '/'
874 if !hasSize && len(positionValues) == 2 && positionValues[0].IsZero() && positionValues[1].IsZero() {
875 if end-start == 2 {
876 values[i] = Token{css.NumberToken, zeroBytes, nil, 0, 0}
877 values[i+1] = Token{css.NumberToken, zeroBytes, nil, 0, 0}
878 i++
879 } else {
880 values = append(values[:i], values[j:]...)
881 end -= j - i
882 i--
883 }
884 } else {
885 if len(positionValues) == j-i {
886 for k, positionValue := range positionValues {
887 values[i+k] = positionValue
888 }
889 } else {
890 values = append(values[:i], append(positionValues, values[j:]...)...)
891 end -= j - i - len(positionValues)
892 }
893 i += len(positionValues) - 1
894 }
895 }
896 }
897
898 if end-start == 0 {
899 values = append(values[:start], append([]Token{{css.NumberToken, zeroBytes, nil, 0, 0}, {css.NumberToken, zeroBytes, nil, 0, 0}}, values[end:]...)...)
900 end += 2
901 }
902 start = end + 1
903 }
904 case Background_Size:
905 start := 0
906 for end := 0; end <= len(values); end++ { // loop over comma-separated lists
907 if end != len(values) && values[end].TokenType != css.CommaToken {
908 continue
909 } else if start == end {
910 start++
911 continue
912 }
913
914 if end-start == 2 && values[start+1].Ident == Auto {
915 values = append(values[:start+1], values[start+2:]...)
916 end--
917 }
918 start = end + 1
919 }
920 case Background_Repeat:
921 start := 0
922 for end := 0; end <= len(values); end++ { // loop over comma-separated lists
923 if end != len(values) && values[end].TokenType != css.CommaToken {
924 continue
925 } else if start == end {
926 start++
927 continue
928 }
929
930 if end-start == 2 && values[start].TokenType == css.IdentToken && values[start+1].TokenType == css.IdentToken {
931 if values[start].Ident == values[start+1].Ident {
932 values = append(values[:start+1], values[start+2:]...)
933 end--
934 } else if values[start].Ident == Repeat && values[start+1].Ident == No_Repeat {
935 values[start].Data = repeatXBytes
936 values[start].Ident = Repeat_X
937 values = append(values[:start+1], values[start+2:]...)
938 end--
939 } else if values[start].Ident == No_Repeat && values[start+1].Ident == Repeat {
940 values[start].Data = repeatYBytes
941 values[start].Ident = Repeat_Y
942 values = append(values[:start+1], values[start+2:]...)
943 end--
944 }
945 }
946 start = end + 1
947 }
948 case Background_Position:
949 start := 0
950 for end := 0; end <= len(values); end++ { // loop over comma-separated lists
951 if end != len(values) && values[end].TokenType != css.CommaToken {
952 continue
953 } else if start == end {
954 start++
955 continue
956 }
957
958 if end-start == 3 || end-start == 4 {
959 // remove zero offsets
960 for _, i := range []int{end - start - 1, start + 1} {
961 if 2 < end-start && values[i].IsZero() {
962 values = append(values[:i], values[i+1:]...)
963 end--
964 }
965 }
966
967 j := start + 1 // position of second set of horizontal/vertical values
968 if 2 < end-start && values[start+2].TokenType == css.IdentToken {
969 j = start + 2
970 }
971
972 b := make([]byte, 0, 4)
973 offsets := make([]Token, 2)
974 for _, i := range []int{j, start} {
975 if i+1 < end && i+1 != j {
976 if values[i+1].TokenType == css.PercentageToken {
977 // change right or bottom with percentage offset to left or top respectively
978 if values[i].Ident == Right || values[i].Ident == Bottom {
979 n, _ := strconvParse.ParseInt(values[i+1].Data[:len(values[i+1].Data)-1])
980 b = strconv.AppendInt(b[:0], 100-n, 10)
981 b = append(b, '%')
982 values[i+1].Data = b
983 if values[i].Ident == Right {
984 values[i].Data = leftBytes
985 values[i].Ident = Left
986 } else {
987 values[i].Data = topBytes
988 values[i].Ident = Top
989 }
990 }
991 }
992 if values[i].Ident == Left {
993 offsets[0] = values[i+1]
994 } else if values[i].Ident == Top {
995 offsets[1] = values[i+1]
996 }
997 } else if values[i].Ident == Left {
998 offsets[0] = Token{css.NumberToken, zeroBytes, nil, 0, 0}
999 } else if values[i].Ident == Top {
1000 offsets[1] = Token{css.NumberToken, zeroBytes, nil, 0, 0}
1001 } else if values[i].Ident == Right {
1002 offsets[0] = Token{css.PercentageToken, n100pBytes, nil, 0, 0}
1003 values[i].Ident = Left
1004 } else if values[i].Ident == Bottom {
1005 offsets[1] = Token{css.PercentageToken, n100pBytes, nil, 0, 0}
1006 values[i].Ident = Top
1007 }
1008 }
1009
1010 if values[start].Ident == Center || values[j].Ident == Center {
1011 if values[start].Ident == Left || values[j].Ident == Left {
1012 offsets = offsets[:1]
1013 } else if values[start].Ident == Top || values[j].Ident == Top {
1014 offsets[0] = Token{css.NumberToken, n50pBytes, nil, 0, 0}
1015 }
1016 }
1017
1018 if offsets[0].Data != nil && (len(offsets) == 1 || offsets[1].Data != nil) {
1019 values = append(append(values[:start], offsets...), values[end:]...)
1020 end -= end - start - len(offsets)
1021 }
1022 }
1023 // removing zero offsets in the previous loop might make it eligible for the next loop
1024 if end-start == 1 || end-start == 2 {
1025 if end-start == 1 && (values[start].Ident == Top || values[start].Ident == Bottom) {
1026 // we can't make this smaller, and converting to a number will break it
1027 // (https://github.com/tdewolff/minify/issues/221#issuecomment-415419918)
1028 break
1029 }
1030
1031 if end-start == 2 && (values[start].Ident == Top || values[start].Ident == Bottom || values[start+1].Ident == Left || values[start+1].Ident == Right) {
1032 // if it's a vertical position keyword, swap it with the next element
1033 // since otherwise converted number positions won't be valid anymore
1034 // (https://github.com/tdewolff/minify/issues/221#issue-353067229)
1035 values[start], values[start+1] = values[start+1], values[start]
1036 }
1037
1038 // transform keywords to lengths|percentages
1039 for i := start; i < end; i++ {
1040 if values[i].TokenType == css.IdentToken {
1041 if values[i].Ident == Left || values[i].Ident == Top {
1042 values[i].TokenType = css.NumberToken
1043 values[i].Data = zeroBytes
1044 values[i].Ident = 0
1045 } else if values[i].Ident == Right || values[i].Ident == Bottom {
1046 values[i].TokenType = css.PercentageToken
1047 values[i].Data = n100pBytes
1048 values[i].Ident = 0
1049 } else if values[i].Ident == Center {
1050 if i == start {
1051 values[i].TokenType = css.PercentageToken
1052 values[i].Data = n50pBytes
1053 values[i].Ident = 0
1054 } else {
1055 values = append(values[:start+1], values[start+2:]...)
1056 end--
1057 }
1058 }
1059 } else if i == start+1 && values[i].TokenType == css.PercentageToken && bytes.Equal(values[i].Data, n50pBytes) {
1060 values = append(values[:start+1], values[start+2:]...)
1061 end--
1062 } else if values[i].TokenType == css.PercentageToken && values[i].Data[0] == '0' {
1063 values[i].TokenType = css.NumberToken
1064 values[i].Data = zeroBytes
1065 values[i].Ident = 0
1066 }
1067 }
1068 }
1069 start = end + 1
1070 }
1071 case Box_Shadow:
1072 start := 0
1073 for end := 0; end <= len(values); end++ { // loop over comma-separated lists
1074 if end != len(values) && values[end].TokenType != css.CommaToken {
1075 continue
1076 } else if start == end {
1077 start++
1078 continue
1079 }
1080
1081 if end-start == 1 && values[start].Ident == Initial {
1082 values[start].Ident = None
1083 values[start].Data = noneBytes
1084 } else {
1085 numbers := []int{}
1086 for i := start; i < end; i++ {
1087 if values[i].IsLength() {
1088 numbers = append(numbers, i)
1089 }
1090 }
1091 if len(numbers) == 4 && values[numbers[3]].IsZero() {
1092 values = append(values[:numbers[3]], values[numbers[3]+1:]...)
1093 numbers = numbers[:3]
1094 end--
1095 }
1096 if len(numbers) == 3 && values[numbers[2]].IsZero() {
1097 values = append(values[:numbers[2]], values[numbers[2]+1:]...)
1098 end--
1099 }
1100 }
1101 start = end + 1
1102 }
1103 case Ms_Filter:
1104 alpha := []byte("progid:DXImageTransform.Microsoft.Alpha(Opacity=")
1105 if values[0].TokenType == css.StringToken && 2 < len(values[0].Data) && bytes.HasPrefix(values[0].Data[1:len(values[0].Data)-1], alpha) {
1106 values[0].Data = append(append([]byte{values[0].Data[0]}, []byte("alpha(opacity=")...), values[0].Data[1+len(alpha):]...)
1107 }
1108 case Color:
1109 values[0] = minifyColor(values[0])
1110 case Background_Color:
1111 values[0] = minifyColor(values[0])
1112 if !c.o.KeepCSS2 {
1113 if values[0].Ident == Transparent {
1114 values[0].Data = initialBytes
1115 values[0].Ident = Initial
1116 }
1117 }
1118 case Border_Color:
1119 sameValues := true
1120 for i := range values {
1121 if values[i].Ident == Currentcolor {
1122 values[i].Data = initialBytes
1123 values[i].Ident = Initial
1124 } else {
1125 values[i] = minifyColor(values[i])
1126 }
1127 if 0 < i && sameValues && !bytes.Equal(values[0].Data, values[i].Data) {
1128 sameValues = false
1129 }
1130 }
1131 if sameValues {
1132 values = values[:1]
1133 }
1134 case Border_Left_Color, Border_Right_Color, Border_Top_Color, Border_Bottom_Color, Text_Decoration_Color, Text_Emphasis_Color:
1135 if values[0].Ident == Currentcolor {
1136 values[0].Data = initialBytes
1137 values[0].Ident = Initial
1138 } else {
1139 values[0] = minifyColor(values[0])
1140 }
1141 case Caret_Color, Outline_Color, Fill, Stroke:
1142 values[0] = minifyColor(values[0])
1143 case Column_Rule:
1144 for i := 0; i < len(values); i++ {
1145 if values[i].Ident == Currentcolor || values[i].Ident == None || values[i].Ident == Medium {
1146 values = append(values[:i], values[i+1:]...)
1147 i--
1148 } else {
1149 values[i] = minifyColor(values[i])
1150 }
1151 }
1152 if len(values) == 0 {
1153 values = []Token{{css.IdentToken, noneBytes, nil, 0, None}}
1154 }
1155 case Text_Shadow:
1156 // TODO: minify better (can be comma separated list)
1157 for i := 0; i < len(values); i++ {
1158 values[i] = minifyColor(values[i])
1159 }
1160 case Text_Decoration:
1161 for i := 0; i < len(values); i++ {
1162 if values[i].Ident == Currentcolor || values[i].Ident == None || values[i].Ident == Solid {
1163 values = append(values[:i], values[i+1:]...)
1164 i--
1165 } else {
1166 values[i] = minifyColor(values[i])
1167 }
1168 }
1169 if len(values) == 0 {
1170 values = []Token{{css.IdentToken, noneBytes, nil, 0, None}}
1171 }
1172 case Text_Emphasis:
1173 for i := 0; i < len(values); i++ {
1174 if values[i].Ident == Currentcolor || values[i].Ident == None {
1175 values = append(values[:i], values[i+1:]...)
1176 i--
1177 } else {
1178 values[i] = minifyColor(values[i])
1179 }
1180 }
1181 if len(values) == 0 {
1182 values = []Token{{css.IdentToken, noneBytes, nil, 0, None}}
1183 }
1184 case Flex:
1185 if len(values) == 2 && values[0].TokenType == css.NumberToken {
1186 if values[1].TokenType != css.NumberToken && values[1].IsZero() {
1187 values = values[:1] // remove <flex-basis> if it is zero
1188 }
1189 } else if len(values) == 3 && values[0].TokenType == css.NumberToken && values[1].TokenType == css.NumberToken {
1190 if len(values[0].Data) == 1 && len(values[1].Data) == 1 {
1191 if values[2].Ident == Auto {
1192 if values[0].Data[0] == '0' && values[1].Data[0] == '1' {
1193 values = values[:1]
1194 values[0].TokenType = css.IdentToken
1195 values[0].Data = initialBytes
1196 values[0].Ident = Initial
1197 } else if values[0].Data[0] == '1' && values[1].Data[0] == '1' {
1198 values = values[:1]
1199 values[0].TokenType = css.IdentToken
1200 values[0].Data = autoBytes
1201 values[0].Ident = Auto
1202 } else if values[0].Data[0] == '0' && values[1].Data[0] == '0' {
1203 values = values[:1]
1204 values[0].TokenType = css.IdentToken
1205 values[0].Data = noneBytes
1206 values[0].Ident = None
1207 }
1208 } else if values[1].Data[0] == '1' && values[2].IsZero() {
1209 values = values[:1] // remove <flex-shrink> and <flex-basis> if they are 1 and 0 respectively
1210 } else if values[2].IsZero() {
1211 values = values[:2] // remove auto to write 2-value syntax of <flex-grow> <flex-shrink>
1212 } else {
1213 values[2] = minifyLengthPercentage(values[2])
1214 }
1215 }
1216 }
1217 case Flex_Basis:
1218 if values[0].Ident == Initial {
1219 values[0].Data = autoBytes
1220 values[0].Ident = Auto
1221 } else {
1222 values[0] = minifyLengthPercentage(values[0])
1223 }
1224 case Order, Flex_Grow:
1225 if values[0].Ident == Initial {
1226 values[0].TokenType = css.NumberToken
1227 values[0].Data = zeroBytes
1228 values[0].Ident = 0
1229 }
1230 case Flex_Shrink:
1231 if values[0].Ident == Initial {
1232 values[0].TokenType = css.NumberToken
1233 values[0].Data = oneBytes
1234 values[0].Ident = 0
1235 }
1236 case Unicode_Range:
1237 ranges := [][2]int{}
1238 for _, value := range values {
1239 if value.TokenType == css.CommaToken {
1240 continue
1241 } else if value.TokenType != css.UnicodeRangeToken {
1242 return values
1243 }
1244
1245 i := 2
1246 iWildcard := 0
1247 start := 0
1248 for i < len(value.Data) && value.Data[i] != '-' {
1249 start *= 16
1250 if '0' <= value.Data[i] && value.Data[i] <= '9' {
1251 start += int(value.Data[i] - '0')
1252 } else if 'a' <= value.Data[i]|32 && value.Data[i]|32 <= 'f' {
1253 start += int(value.Data[i]|32-'a') + 10
1254 } else if iWildcard == 0 && value.Data[i] == '?' {
1255 iWildcard = i
1256 }
1257 i++
1258 }
1259 end := start
1260 if iWildcard != 0 {
1261 end = start + int(math.Pow(16.0, float64(len(value.Data)-iWildcard))) - 1
1262 } else if i < len(value.Data) && value.Data[i] == '-' {
1263 i++
1264 end = 0
1265 for i < len(value.Data) {
1266 end *= 16
1267 if '0' <= value.Data[i] && value.Data[i] <= '9' {
1268 end += int(value.Data[i] - '0')
1269 } else if 'a' <= value.Data[i]|32 && value.Data[i]|32 <= 'f' {
1270 end += int(value.Data[i]|32-'a') + 10
1271 }
1272 i++
1273 }
1274 if end <= start {
1275 end = start
1276 }
1277 }
1278 ranges = append(ranges, [2]int{start, end})
1279 }
1280
1281 // sort and remove overlapping ranges
1282 sort.Slice(ranges, func(i, j int) bool { return ranges[i][0] < ranges[j][0] })
1283 for i := 0; i < len(ranges)-1; i++ {
1284 if ranges[i+1][1] <= ranges[i][1] {
1285 // next range is fully contained in the current range
1286 ranges = append(ranges[:i+1], ranges[i+2:]...)
1287 } else if ranges[i+1][0] <= ranges[i][1]+1 {
1288 // next range is partially covering the current range
1289 ranges[i][1] = ranges[i+1][1]
1290 ranges = append(ranges[:i+1], ranges[i+2:]...)
1291 }
1292 }
1293
1294 values = values[:0]
1295 for i, ran := range ranges {
1296 if i != 0 {
1297 values = append(values, Token{css.CommaToken, commaBytes, nil, 0, None})
1298 }
1299 if ran[0] == ran[1] {
1300 urange := []byte(fmt.Sprintf("U+%X", ran[0]))
1301 values = append(values, Token{css.UnicodeRangeToken, urange, nil, 0, None})
1302 } else if ran[0] == 0 && ran[1] == 0x10FFFF {
1303 values = append(values, Token{css.IdentToken, initialBytes, nil, 0, None})
1304 } else {
1305 k := 0
1306 for k < 6 && (ran[0]>>(k*4))&0xF == 0 && (ran[1]>>(k*4))&0xF == 0xF {
1307 k++
1308 }
1309 wildcards := k
1310 for k < 6 {
1311 if (ran[0]>>(k*4))&0xF != (ran[1]>>(k*4))&0xF {
1312 wildcards = 0
1313 break
1314 }
1315 k++
1316 }
1317 var urange []byte
1318 if wildcards != 0 {
1319 if ran[0]>>(wildcards*4) == 0 {
1320 urange = []byte(fmt.Sprintf("U+%s", strings.Repeat("?", wildcards)))
1321 } else {
1322 urange = []byte(fmt.Sprintf("U+%X%s", ran[0]>>(wildcards*4), strings.Repeat("?", wildcards)))
1323 }
1324 } else {
1325 urange = []byte(fmt.Sprintf("U+%X-%X", ran[0], ran[1]))
1326 }
1327 values = append(values, Token{css.UnicodeRangeToken, urange, nil, 0, None})
1328 }
1329 }
1330 }
1331 return values
1332}
1333
1334func minifyColor(value Token) Token {
1335 data := value.Data
1336 if value.TokenType == css.IdentToken {
1337 if hexValue, ok := ShortenColorName[value.Ident]; ok {
1338 value.TokenType = css.HashToken
1339 value.Data = hexValue
1340 }
1341 } else if value.TokenType == css.HashToken {
1342 parse.ToLower(data[1:])
1343 if len(data) == 9 && data[7] == data[8] {
1344 if data[7] == 'f' {
1345 data = data[:7]
1346 } else if data[7] == '0' {
1347 data = blackBytes
1348 }
1349 }
1350 if ident, ok := ShortenColorHex[string(data)]; ok {
1351 value.TokenType = css.IdentToken
1352 data = ident
1353 } else if len(data) == 7 && data[1] == data[2] && data[3] == data[4] && data[5] == data[6] {
1354 value.TokenType = css.HashToken
1355 data[2] = data[3]
1356 data[3] = data[5]
1357 data = data[:4]
1358 } else if len(data) == 9 && data[1] == data[2] && data[3] == data[4] && data[5] == data[6] && data[7] == data[8] {
1359 // from working draft Color Module Level 4
1360 value.TokenType = css.HashToken
1361 data[2] = data[3]
1362 data[3] = data[5]
1363 data[4] = data[7]
1364 data = data[:5]
1365 }
1366 value.Data = data
1367 }
1368 return value
1369}
1370
1371func minifyNumberPercentage(value Token) Token {
1372 // assumes input already minified
1373 if value.TokenType == css.PercentageToken && len(value.Data) == 3 && value.Data[len(value.Data)-2] == '0' {
1374 value.Data[1] = value.Data[0]
1375 value.Data[0] = '.'
1376 value.Data = value.Data[:2]
1377 value.TokenType = css.NumberToken
1378 } else if value.TokenType == css.NumberToken && 2 < len(value.Data) && value.Data[0] == '.' && value.Data[1] == '0' {
1379 if value.Data[2] == '0' {
1380 value.Data[0] = '.'
1381 copy(value.Data[1:], value.Data[3:])
1382 value.Data[len(value.Data)-2] = '%'
1383 value.Data = value.Data[:len(value.Data)-1]
1384 value.TokenType = css.PercentageToken
1385 } else if len(value.Data) == 3 {
1386 value.Data[0] = value.Data[2]
1387 value.Data[1] = '%'
1388 value.Data = value.Data[:2]
1389 value.TokenType = css.PercentageToken
1390 }
1391 }
1392 return value
1393}
1394
1395func minifyLengthPercentage(value Token) Token {
1396 if value.TokenType != css.NumberToken && value.IsZero() {
1397 value.TokenType = css.NumberToken
1398 value.Data = value.Data[:1] // remove dimension for zero value
1399 }
1400 return value
1401}
1402
1403func (c *cssMinifier) minifyDimension(value Token) (Token, []byte) {
1404 // TODO: add check for zero value
1405 var dim []byte
1406 if value.TokenType == css.DimensionToken {
1407 n := len(value.Data)
1408 for 0 < n {
1409 lower := 'a' <= value.Data[n-1] && value.Data[n-1] <= 'z'
1410 upper := 'A' <= value.Data[n-1] && value.Data[n-1] <= 'Z'
1411 if !lower && !upper {
1412 break
1413 } else if upper {
1414 value.Data[n-1] = value.Data[n-1] + ('a' - 'A')
1415 }
1416 n--
1417 }
1418
1419 num := value.Data[:n]
1420 if c.o.KeepCSS2 {
1421 num = minify.Decimal(num, c.o.Precision) // don't use exponents
1422 } else {
1423 num = minify.Number(num, c.o.Precision)
1424 }
1425 dim = value.Data[n:]
1426 value.Data = append(num, dim...)
1427 }
1428 return value, dim
1429
1430 // TODO: optimize
1431 //if value.TokenType == css.DimensionToken {
1432 // // TODO: reverse; parse dim not number
1433 // n := parse.Number(value.Data)
1434 // num := value.Data[:n]
1435 // dim = value.Data[n:]
1436 // parse.ToLower(dim)
1437
1438 // if c.o.KeepCSS2 {
1439 // num = minify.Decimal(num, c.o.Precision) // don't use exponents
1440 // } else {
1441 // num = minify.Number(num, c.o.Precision)
1442 // }
1443
1444 // // change dimension to compress number
1445 // h := ToHash(dim)
1446 // if h == Px || h == Pt || h == Pc || h == In || h == Mm || h == Cm || h == Q || h == Deg || h == Grad || h == Rad || h == Turn || h == S || h == Ms || h == Hz || h == Khz || h == Dpi || h == Dpcm || h == Dppx {
1447 // d, _ := strconv.ParseFloat(string(num), 64) // can never fail
1448 // var dimensions []Hash
1449 // var multipliers []float64
1450 // switch h {
1451 // case Px:
1452 // //dimensions = []Hash{In, Cm, Pc, Mm, Pt, Q}
1453 // //multipliers = []float64{0.010416666666666667, 0.026458333333333333, 0.0625, 0.26458333333333333, 0.75, 1.0583333333333333}
1454 // dimensions = []Hash{In, Pc, Pt}
1455 // multipliers = []float64{0.010416666666666667, 0.0625, 0.75}
1456 // case Pt:
1457 // //dimensions = []Hash{In, Cm, Pc, Mm, Px, Q}
1458 // //multipliers = []float64{0.013888888888888889, 0.035277777777777778, 0.083333333333333333, 0.35277777777777778, 1.3333333333333333, 1.4111111111111111}
1459 // dimensions = []Hash{In, Pc, Px}
1460 // multipliers = []float64{0.013888888888888889, 0.083333333333333333, 1.3333333333333333}
1461 // case Pc:
1462 // //dimensions = []Hash{In, Cm, Mm, Pt, Px, Q}
1463 // //multipliers = []float64{0.16666666666666667, 0.42333333333333333, 4.2333333333333333, 12.0, 16.0, 16.933333333333333}
1464 // dimensions = []Hash{In, Pt, Px}
1465 // multipliers = []float64{0.16666666666666667, 12.0, 16.0}
1466 // case In:
1467 // //dimensions = []Hash{Cm, Pc, Mm, Pt, Px, Q}
1468 // //multipliers = []float64{2.54, 6.0, 25.4, 72.0, 96.0, 101.6}
1469 // dimensions = []Hash{Pc, Pt, Px}
1470 // multipliers = []float64{6.0, 72.0, 96.0}
1471 // case Cm:
1472 // //dimensions = []Hash{In, Pc, Mm, Pt, Px, Q}
1473 // //multipliers = []float64{0.39370078740157480, 2.3622047244094488, 10.0, 28.346456692913386, 37.795275590551181, 40.0}
1474 // dimensions = []Hash{Mm, Q}
1475 // multipliers = []float64{10.0, 40.0}
1476 // case Mm:
1477 // //dimensions = []Hash{In, Cm, Pc, Pt, Px, Q}
1478 // //multipliers = []float64{0.039370078740157480, 0.1, 0.23622047244094488, 2.8346456692913386, 3.7795275590551181, 4.0}
1479 // dimensions = []Hash{Cm, Q}
1480 // multipliers = []float64{0.1, 4.0}
1481 // case Q:
1482 // //dimensions = []Hash{In, Cm, Pc, Pt, Px} // Q to mm is never smaller
1483 // //multipliers = []float64{0.0098425196850393701, 0.025, 0.059055118110236220, 0.70866141732283465, 0.94488188976377953}
1484 // dimensions = []Hash{Cm} // Q to mm is never smaller
1485 // multipliers = []float64{0.025}
1486 // case Deg:
1487 // //dimensions = []Hash{Turn, Rad, Grad}
1488 // //multipliers = []float64{0.0027777777777777778, 0.017453292519943296, 1.1111111111111111}
1489 // dimensions = []Hash{Turn, Grad}
1490 // multipliers = []float64{0.0027777777777777778, 1.1111111111111111}
1491 // case Grad:
1492 // //dimensions = []Hash{Turn, Rad, Deg}
1493 // //multipliers = []float64{0.0025, 0.015707963267948966, 0.9}
1494 // dimensions = []Hash{Turn, Deg}
1495 // multipliers = []float64{0.0025, 0.9}
1496 // case Turn:
1497 // //dimensions = []Hash{Rad, Deg, Grad}
1498 // //multipliers = []float64{6.2831853071795865, 360.0, 400.0}
1499 // dimensions = []Hash{Deg, Grad}
1500 // multipliers = []float64{360.0, 400.0}
1501 // case Rad:
1502 // //dimensions = []Hash{Turn, Deg, Grad}
1503 // //multipliers = []float64{0.15915494309189534, 57.295779513082321, 63.661977236758134}
1504 // case S:
1505 // dimensions = []Hash{Ms}
1506 // multipliers = []float64{1000.0}
1507 // case Ms:
1508 // dimensions = []Hash{S}
1509 // multipliers = []float64{0.001}
1510 // case Hz:
1511 // dimensions = []Hash{Khz}
1512 // multipliers = []float64{0.001}
1513 // case Khz:
1514 // dimensions = []Hash{Hz}
1515 // multipliers = []float64{1000.0}
1516 // case Dpi:
1517 // dimensions = []Hash{Dppx, Dpcm}
1518 // multipliers = []float64{0.010416666666666667, 0.39370078740157480}
1519 // case Dpcm:
1520 // //dimensions = []Hash{Dppx, Dpi}
1521 // //multipliers = []float64{0.026458333333333333, 2.54}
1522 // dimensions = []Hash{Dpi}
1523 // multipliers = []float64{2.54}
1524 // case Dppx:
1525 // //dimensions = []Hash{Dpcm, Dpi}
1526 // //multipliers = []float64{37.795275590551181, 96.0}
1527 // dimensions = []Hash{Dpi}
1528 // multipliers = []float64{96.0}
1529 // }
1530 // for i := range dimensions {
1531 // if dimensions[i] != h { //&& (d < 1.0) == (multipliers[i] > 1.0) {
1532 // b, _ := strconvParse.AppendFloat([]byte{}, d*multipliers[i], -1)
1533 // if c.o.KeepCSS2 {
1534 // b = minify.Decimal(b, c.o.newPrecision) // don't use exponents
1535 // } else {
1536 // b = minify.Number(b, c.o.newPrecision)
1537 // }
1538 // newDim := []byte(dimensions[i].String())
1539 // if len(b)+len(newDim) < len(num)+len(dim) {
1540 // num = b
1541 // dim = newDim
1542 // }
1543 // }
1544 // }
1545 // }
1546 // value.Data = append(num, dim...)
1547 //}
1548 //return value, dim
1549}
diff --git a/vendor/github.com/tdewolff/minify/v2/css/hash.go b/vendor/github.com/tdewolff/minify/v2/css/hash.go
new file mode 100644
index 0000000..98692c8
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/css/hash.go
@@ -0,0 +1,1392 @@
1package css
2
3// uses github.com/tdewolff/hasher
4//go:generate hasher -type=Hash -file=hash.go
5
6// Hash defines perfect hashes for a predefined list of strings
7type Hash uint32
8
9// Identifiers for the hashes associated with the text in the comments.
10const (
11 Ms_Filter Hash = 0xa // -ms-filter
12 Accelerator Hash = 0x3760b // accelerator
13 Aliceblue Hash = 0x7a209 // aliceblue
14 Align_Content Hash = 0xd980d // align-content
15 Align_Items Hash = 0x7ef0b // align-items
16 Align_Self Hash = 0x8cb0a // align-self
17 All Hash = 0x69103 // all
18 Alpha Hash = 0x37205 // alpha
19 Animation Hash = 0xca09 // animation
20 Animation_Delay Hash = 0x2050f // animation-delay
21 Animation_Direction Hash = 0x8e913 // animation-direction
22 Animation_Duration Hash = 0x35d12 // animation-duration
23 Animation_Fill_Mode Hash = 0x66c13 // animation-fill-mode
24 Animation_Iteration_Count Hash = 0xd4919 // animation-iteration-count
25 Animation_Name Hash = 0xca0e // animation-name
26 Animation_Play_State Hash = 0xfc14 // animation-play-state
27 Animation_Timing_Function Hash = 0x14119 // animation-timing-function
28 Antiquewhite Hash = 0x6490c // antiquewhite
29 Aquamarine Hash = 0x9ec0a // aquamarine
30 Attr Hash = 0x59804 // attr
31 Auto Hash = 0x44504 // auto
32 Azimuth Hash = 0x15a07 // azimuth
33 Background Hash = 0x2b0a // background
34 Background_Attachment Hash = 0x2b15 // background-attachment
35 Background_Clip Hash = 0xb6e0f // background-clip
36 Background_Color Hash = 0x21710 // background-color
37 Background_Image Hash = 0x5ad10 // background-image
38 Background_Origin Hash = 0x17111 // background-origin
39 Background_Position Hash = 0x18e13 // background-position
40 Background_Position_X Hash = 0x18e15 // background-position-x
41 Background_Position_Y Hash = 0x1a315 // background-position-y
42 Background_Repeat Hash = 0x1b811 // background-repeat
43 Background_Size Hash = 0x1cb0f // background-size
44 Behavior Hash = 0x1da08 // behavior
45 Black Hash = 0x1e205 // black
46 Blanchedalmond Hash = 0x1e70e // blanchedalmond
47 Blueviolet Hash = 0x7a70a // blueviolet
48 Bold Hash = 0x1fc04 // bold
49 Border Hash = 0x22706 // border
50 Border_Bottom Hash = 0x2270d // border-bottom
51 Border_Bottom_Color Hash = 0x22713 // border-bottom-color
52 Border_Bottom_Style Hash = 0x23a13 // border-bottom-style
53 Border_Bottom_Width Hash = 0x25d13 // border-bottom-width
54 Border_Box Hash = 0x27e0a // border-box
55 Border_Collapse Hash = 0x2b60f // border-collapse
56 Border_Color Hash = 0x2d30c // border-color
57 Border_Left Hash = 0x2df0b // border-left
58 Border_Left_Color Hash = 0x2df11 // border-left-color
59 Border_Left_Style Hash = 0x2f011 // border-left-style
60 Border_Left_Width Hash = 0x30111 // border-left-width
61 Border_Right Hash = 0x3120c // border-right
62 Border_Right_Color Hash = 0x31212 // border-right-color
63 Border_Right_Style Hash = 0x32412 // border-right-style
64 Border_Right_Width Hash = 0x33612 // border-right-width
65 Border_Spacing Hash = 0x3480e // border-spacing
66 Border_Style Hash = 0x3ab0c // border-style
67 Border_Top Hash = 0x3b70a // border-top
68 Border_Top_Color Hash = 0x3b710 // border-top-color
69 Border_Top_Style Hash = 0x3c710 // border-top-style
70 Border_Top_Width Hash = 0x3d710 // border-top-width
71 Border_Width Hash = 0x3e70c // border-width
72 Bottom Hash = 0x22e06 // bottom
73 Box_Shadow Hash = 0x2850a // box-shadow
74 Burlywood Hash = 0x3f309 // burlywood
75 Cadetblue Hash = 0x9c609 // cadetblue
76 Calc Hash = 0x9c304 // calc
77 Caption_Side Hash = 0x40f0c // caption-side
78 Caret_Color Hash = 0x4240b // caret-color
79 Center Hash = 0xdb06 // center
80 Charset Hash = 0x62f07 // charset
81 Chartreuse Hash = 0x42f0a // chartreuse
82 Chocolate Hash = 0x43909 // chocolate
83 Clamp Hash = 0x44e05 // clamp
84 Clear Hash = 0x45d05 // clear
85 Clip Hash = 0xb7904 // clip
86 Cm Hash = 0x53802 // cm
87 Color Hash = 0x2505 // color
88 Column_Count Hash = 0x4620c // column-count
89 Column_Gap Hash = 0x6a30a // column-gap
90 Column_Rule Hash = 0x4880b // column-rule
91 Column_Rule_Color Hash = 0x48811 // column-rule-color
92 Column_Rule_Style Hash = 0x49911 // column-rule-style
93 Column_Rule_Width Hash = 0x4aa11 // column-rule-width
94 Column_Width Hash = 0x4bb0c // column-width
95 Columns Hash = 0x74607 // columns
96 Content Hash = 0x5607 // content
97 Cornflowerblue Hash = 0x4c70e // cornflowerblue
98 Cornsilk Hash = 0x4d508 // cornsilk
99 Counter_Increment Hash = 0xd5d11 // counter-increment
100 Counter_Reset Hash = 0x4690d // counter-reset
101 Cue Hash = 0x4dd03 // cue
102 Cue_After Hash = 0x4dd09 // cue-after
103 Cue_Before Hash = 0x4e60a // cue-before
104 Currentcolor Hash = 0x5010c // currentcolor
105 Cursive Hash = 0x50d07 // cursive
106 Cursor Hash = 0x51406 // cursor
107 Darkblue Hash = 0x1f408 // darkblue
108 Darkcyan Hash = 0x1ff08 // darkcyan
109 Darkgoldenrod Hash = 0x3fb0d // darkgoldenrod
110 Darkgray Hash = 0x40708 // darkgray
111 Darkgreen Hash = 0x75c09 // darkgreen
112 Darkkhaki Hash = 0xa1409 // darkkhaki
113 Darkmagenta Hash = 0xce90b // darkmagenta
114 Darkolivegreen Hash = 0x6d90e // darkolivegreen
115 Darkorange Hash = 0x7500a // darkorange
116 Darkorchid Hash = 0xa0b0a // darkorchid
117 Darksalmon Hash = 0xa990a // darksalmon
118 Darkseagreen Hash = 0xb110c // darkseagreen
119 Darkslateblue Hash = 0xc1c0d // darkslateblue
120 Darkslategray Hash = 0xbfa0d // darkslategray
121 Darkturquoise Hash = 0xcaa0d // darkturquoise
122 Darkviolet Hash = 0x51a0a // darkviolet
123 Deeppink Hash = 0x67d08 // deeppink
124 Deepskyblue Hash = 0x4190b // deepskyblue
125 Default Hash = 0xa2207 // default
126 Deg Hash = 0x70103 // deg
127 Direction Hash = 0x8d909 // direction
128 Display Hash = 0xcce07 // display
129 Document Hash = 0x52408 // document
130 Dodgerblue Hash = 0x52c0a // dodgerblue
131 Dpcm Hash = 0x53604 // dpcm
132 Dpi Hash = 0x54f03 // dpi
133 Dppx Hash = 0x55b04 // dppx
134 Elevation Hash = 0x6d09 // elevation
135 Empty_Cells Hash = 0x3910b // empty-cells
136 Env Hash = 0x4f503 // env
137 Fantasy Hash = 0x3a407 // fantasy
138 Fill Hash = 0x67604 // fill
139 Filter Hash = 0x406 // filter
140 Firebrick Hash = 0x83509 // firebrick
141 Flex Hash = 0x55f04 // flex
142 Flex_Basis Hash = 0x89d0a // flex-basis
143 Flex_Direction Hash = 0x8d40e // flex-direction
144 Flex_Flow Hash = 0xc8709 // flex-flow
145 Flex_Grow Hash = 0x55f09 // flex-grow
146 Flex_Shrink Hash = 0x5680b // flex-shrink
147 Flex_Wrap Hash = 0x57309 // flex-wrap
148 Float Hash = 0x59505 // float
149 Floralwhite Hash = 0x5bd0b // floralwhite
150 Font Hash = 0x25404 // font
151 Font_Face Hash = 0x25409 // font-face
152 Font_Family Hash = 0x5ee0b // font-family
153 Font_Size Hash = 0x5f909 // font-size
154 Font_Size_Adjust Hash = 0x5f910 // font-size-adjust
155 Font_Stretch Hash = 0x6250c // font-stretch
156 Font_Style Hash = 0x6360a // font-style
157 Font_Variant Hash = 0x6400c // font-variant
158 Font_Weight Hash = 0x65b0b // font-weight
159 Forestgreen Hash = 0x4ec0b // forestgreen
160 Fuchsia Hash = 0x66607 // fuchsia
161 Function Hash = 0x15208 // function
162 Gainsboro Hash = 0xec09 // gainsboro
163 Ghostwhite Hash = 0x2990a // ghostwhite
164 Goldenrod Hash = 0x3ff09 // goldenrod
165 Grad Hash = 0x1004 // grad
166 Greenyellow Hash = 0x7600b // greenyellow
167 Grid Hash = 0x35504 // grid
168 Grid_Area Hash = 0x35509 // grid-area
169 Grid_Auto_Columns Hash = 0x7bb11 // grid-auto-columns
170 Grid_Auto_Flow Hash = 0x81c0e // grid-auto-flow
171 Grid_Auto_Rows Hash = 0x8640e // grid-auto-rows
172 Grid_Column Hash = 0x69e0b // grid-column
173 Grid_Column_End Hash = 0xcdb0f // grid-column-end
174 Grid_Column_Gap Hash = 0x69e0f // grid-column-gap
175 Grid_Column_Start Hash = 0x6bd11 // grid-column-start
176 Grid_Row Hash = 0x6ce08 // grid-row
177 Grid_Row_End Hash = 0x6ce0c // grid-row-end
178 Grid_Row_Gap Hash = 0x6e70c // grid-row-gap
179 Grid_Row_Start Hash = 0x7030e // grid-row-start
180 Grid_Template Hash = 0x7110d // grid-template
181 Grid_Template_Areas Hash = 0x71113 // grid-template-areas
182 Grid_Template_Columns Hash = 0x73815 // grid-template-columns
183 Grid_Template_Rows Hash = 0x77012 // grid-template-rows
184 Height Hash = 0x9306 // height
185 Honeydew Hash = 0x16008 // honeydew
186 Hsl Hash = 0x26f03 // hsl
187 Hsla Hash = 0x26f04 // hsla
188 Hz Hash = 0x68502 // hz
189 Ime_Mode Hash = 0xa1c08 // ime-mode
190 Import Hash = 0x78d06 // import
191 Important Hash = 0x78d09 // important
192 In Hash = 0x4402 // in
193 Include_Source Hash = 0x1800e // include-source
194 Indianred Hash = 0xb0909 // indianred
195 Inherit Hash = 0x79607 // inherit
196 Initial Hash = 0x79d07 // initial
197 Invert Hash = 0x7e406 // invert
198 Justify_Content Hash = 0x4e0f // justify-content
199 Justify_Items Hash = 0x6050d // justify-items
200 Justify_Self Hash = 0x82a0c // justify-self
201 Keyframes Hash = 0x5cb09 // keyframes
202 Khz Hash = 0x68403 // khz
203 Large Hash = 0xa905 // large
204 Larger Hash = 0xa906 // larger
205 Lavender Hash = 0x27108 // lavender
206 Lavenderblush Hash = 0x2710d // lavenderblush
207 Lawngreen Hash = 0x2ca09 // lawngreen
208 Layer_Background_Color Hash = 0x21116 // layer-background-color
209 Layer_Background_Image Hash = 0x5a716 // layer-background-image
210 Layout_Flow Hash = 0xcf80b // layout-flow
211 Layout_Grid Hash = 0x8050b // layout-grid
212 Layout_Grid_Char Hash = 0x80510 // layout-grid-char
213 Layout_Grid_Char_Spacing Hash = 0x80518 // layout-grid-char-spacing
214 Layout_Grid_Line Hash = 0x83e10 // layout-grid-line
215 Layout_Grid_Mode Hash = 0x85410 // layout-grid-mode
216 Layout_Grid_Type Hash = 0x88710 // layout-grid-type
217 Left Hash = 0x2e604 // left
218 Lemonchiffon Hash = 0x24b0c // lemonchiffon
219 Letter_Spacing Hash = 0x7ae0e // letter-spacing
220 Lightblue Hash = 0x8ba09 // lightblue
221 Lightcoral Hash = 0x8c30a // lightcoral
222 Lightcyan Hash = 0x8e209 // lightcyan
223 Lightgoldenrodyellow Hash = 0x8fc14 // lightgoldenrodyellow
224 Lightgray Hash = 0x91009 // lightgray
225 Lightgreen Hash = 0x9190a // lightgreen
226 Lightpink Hash = 0x92309 // lightpink
227 Lightsalmon Hash = 0x92c0b // lightsalmon
228 Lightseagreen Hash = 0x9370d // lightseagreen
229 Lightskyblue Hash = 0x9440c // lightskyblue
230 Lightslateblue Hash = 0x9500e // lightslateblue
231 Lightsteelblue Hash = 0x95e0e // lightsteelblue
232 Lightyellow Hash = 0x96c0b // lightyellow
233 Limegreen Hash = 0x97709 // limegreen
234 Line_Break Hash = 0x84a0a // line-break
235 Line_Height Hash = 0x8e0b // line-height
236 Linear_Gradient Hash = 0x9800f // linear-gradient
237 List_Style Hash = 0x98f0a // list-style
238 List_Style_Image Hash = 0x98f10 // list-style-image
239 List_Style_Position Hash = 0x99f13 // list-style-position
240 List_Style_Type Hash = 0x9b20f // list-style-type
241 Local Hash = 0x9c105 // local
242 Magenta Hash = 0xced07 // magenta
243 Margin Hash = 0x53906 // margin
244 Margin_Bottom Hash = 0xdb10d // margin-bottom
245 Margin_Left Hash = 0xdbd0b // margin-left
246 Margin_Right Hash = 0xb890c // margin-right
247 Margin_Top Hash = 0x5390a // margin-top
248 Marker_Offset Hash = 0xad00d // marker-offset
249 Marks Hash = 0xaee05 // marks
250 Mask Hash = 0x9cf04 // mask
251 Max Hash = 0x9d303 // max
252 Max_Height Hash = 0x9d30a // max-height
253 Max_Width Hash = 0x9dd09 // max-width
254 Media Hash = 0xd4505 // media
255 Medium Hash = 0x9e606 // medium
256 Mediumaquamarine Hash = 0x9e610 // mediumaquamarine
257 Mediumblue Hash = 0x9f60a // mediumblue
258 Mediumorchid Hash = 0xa000c // mediumorchid
259 Mediumpurple Hash = 0xa420c // mediumpurple
260 Mediumseagreen Hash = 0xa4e0e // mediumseagreen
261 Mediumslateblue Hash = 0xa5c0f // mediumslateblue
262 Mediumspringgreen Hash = 0xa6b11 // mediumspringgreen
263 Mediumturquoise Hash = 0xa7c0f // mediumturquoise
264 Mediumvioletred Hash = 0xa8b0f // mediumvioletred
265 Midnightblue Hash = 0xaa90c // midnightblue
266 Min Hash = 0x14d03 // min
267 Min_Height Hash = 0xab50a // min-height
268 Min_Width Hash = 0xabf09 // min-width
269 Mintcream Hash = 0xac809 // mintcream
270 Mistyrose Hash = 0xae409 // mistyrose
271 Mm Hash = 0xaed02 // mm
272 Moccasin Hash = 0xb0308 // moccasin
273 Monospace Hash = 0xaa009 // monospace
274 Ms Hash = 0x102 // ms
275 Namespace Hash = 0xd409 // namespace
276 Navajowhite Hash = 0x750b // navajowhite
277 No_Repeat Hash = 0xbf09 // no-repeat
278 None Hash = 0x38e04 // none
279 Normal Hash = 0x36e06 // normal
280 Offset Hash = 0xad706 // offset
281 Offset_Anchor Hash = 0xad70d // offset-anchor
282 Offset_Distance Hash = 0xb1d0f // offset-distance
283 Offset_Path Hash = 0xb2c0b // offset-path
284 Offset_Position Hash = 0xb370f // offset-position
285 Offset_Rotate Hash = 0xb460d // offset-rotate
286 Olivedrab Hash = 0xb6609 // olivedrab
287 Orangered Hash = 0x75409 // orangered
288 Order Hash = 0x22805 // order
289 Orphans Hash = 0x37f07 // orphans
290 Outline Hash = 0xba707 // outline
291 Outline_Color Hash = 0xba70d // outline-color
292 Outline_Style Hash = 0xbb40d // outline-style
293 Outline_Width Hash = 0xbc10d // outline-width
294 Overflow Hash = 0x9d08 // overflow
295 Overflow_X Hash = 0x9d0a // overflow-x
296 Overflow_Y Hash = 0xbce0a // overflow-y
297 Padding Hash = 0x45207 // padding
298 Padding_Bottom Hash = 0xb7c0e // padding-bottom
299 Padding_Box Hash = 0x4520b // padding-box
300 Padding_Left Hash = 0xd0a0c // padding-left
301 Padding_Right Hash = 0x5420d // padding-right
302 Padding_Top Hash = 0x57b0b // padding-top
303 Page Hash = 0x58504 // page
304 Page_Break_After Hash = 0x58510 // page-break-after
305 Page_Break_Before Hash = 0x6ac11 // page-break-before
306 Page_Break_Inside Hash = 0x6f211 // page-break-inside
307 Palegoldenrod Hash = 0xc100d // palegoldenrod
308 Palegreen Hash = 0xbd809 // palegreen
309 Paleturquoise Hash = 0xbe10d // paleturquoise
310 Palevioletred Hash = 0xbee0d // palevioletred
311 Papayawhip Hash = 0xc070a // papayawhip
312 Pause Hash = 0xc2905 // pause
313 Pause_After Hash = 0xc290b // pause-after
314 Pause_Before Hash = 0xc340c // pause-before
315 Pc Hash = 0x53702 // pc
316 Peachpuff Hash = 0x89509 // peachpuff
317 Pitch Hash = 0x55005 // pitch
318 Pitch_Range Hash = 0x5500b // pitch-range
319 Place_Content Hash = 0xc400d // place-content
320 Place_Items Hash = 0xc4d0b // place-items
321 Place_Self Hash = 0xc7e0a // place-self
322 Play_During Hash = 0xcd10b // play-during
323 Position Hash = 0x13908 // position
324 Powderblue Hash = 0xc9b0a // powderblue
325 Progid Hash = 0xca506 // progid
326 Pt Hash = 0x39302 // pt
327 Px Hash = 0x55d02 // px
328 Q Hash = 0x64d01 // q
329 Quotes Hash = 0xcb706 // quotes
330 Rad Hash = 0x903 // rad
331 Radial_Gradient Hash = 0x90f // radial-gradient
332 Repeat Hash = 0xc206 // repeat
333 Repeat_X Hash = 0x1c308 // repeat-x
334 Repeat_Y Hash = 0xc208 // repeat-y
335 Rgb Hash = 0x2903 // rgb
336 Rgba Hash = 0x2904 // rgba
337 Richness Hash = 0xae08 // richness
338 Right Hash = 0x31905 // right
339 Rosybrown Hash = 0xf309 // rosybrown
340 Round Hash = 0x3005 // round
341 Row_Gap Hash = 0x6ec07 // row-gap
342 Royalblue Hash = 0x69509 // royalblue
343 Ruby_Align Hash = 0xd930a // ruby-align
344 Ruby_Overhang Hash = 0xe00d // ruby-overhang
345 Ruby_Position Hash = 0x1340d // ruby-position
346 S Hash = 0x201 // s
347 Saddlebrown Hash = 0xb50b // saddlebrown
348 Sandybrown Hash = 0x3850a // sandybrown
349 Sans_Serif Hash = 0x39b0a // sans-serif
350 Scroll Hash = 0x12006 // scroll
351 Scrollbar_3d_Light_Color Hash = 0xd7c18 // scrollbar-3d-light-color
352 Scrollbar_Arrow_Color Hash = 0x12015 // scrollbar-arrow-color
353 Scrollbar_Base_Color Hash = 0x8a614 // scrollbar-base-color
354 Scrollbar_Dark_Shadow_Color Hash = 0x5d31b // scrollbar-dark-shadow-color
355 Scrollbar_Face_Color Hash = 0x61114 // scrollbar-face-color
356 Scrollbar_Highlight_Color Hash = 0x7cb19 // scrollbar-highlight-color
357 Scrollbar_Shadow_Color Hash = 0x87116 // scrollbar-shadow-color
358 Scrollbar_Track_Color Hash = 0x72315 // scrollbar-track-color
359 Seagreen Hash = 0x93c08 // seagreen
360 Seashell Hash = 0x2c308 // seashell
361 Serif Hash = 0x3a005 // serif
362 Size Hash = 0x1d604 // size
363 Slateblue Hash = 0x95509 // slateblue
364 Slategray Hash = 0xbfe09 // slategray
365 Small Hash = 0x68f05 // small
366 Smaller Hash = 0x68f07 // smaller
367 Solid Hash = 0x74c05 // solid
368 Space Hash = 0x6905 // space
369 Speak Hash = 0x78105 // speak
370 Speak_Header Hash = 0x7810c // speak-header
371 Speak_Numeral Hash = 0x7f90d // speak-numeral
372 Speak_Punctuation Hash = 0xaf211 // speak-punctuation
373 Speech_Rate Hash = 0xc570b // speech-rate
374 Springgreen Hash = 0xa710b // springgreen
375 Steelblue Hash = 0x96309 // steelblue
376 Stress Hash = 0x11b06 // stress
377 Stroke Hash = 0xc7806 // stroke
378 Supports Hash = 0xcbc08 // supports
379 Table_Layout Hash = 0xcf20c // table-layout
380 Text_Align Hash = 0x10e0a // text-align
381 Text_Align_Last Hash = 0x10e0f // text-align-last
382 Text_Autospace Hash = 0x4400e // text-autospace
383 Text_Decoration Hash = 0x7e0f // text-decoration
384 Text_Decoration_Color Hash = 0x2a115 // text-decoration-color
385 Text_Decoration_Line Hash = 0x7e14 // text-decoration-line
386 Text_Decoration_Style Hash = 0xb5115 // text-decoration-style
387 Text_Decoration_Thickness Hash = 0xc6019 // text-decoration-thickness
388 Text_Emphasis Hash = 0x170d // text-emphasis
389 Text_Emphasis_Color Hash = 0x1713 // text-emphasis-color
390 Text_Indent Hash = 0x3f0b // text-indent
391 Text_Justify Hash = 0x490c // text-justify
392 Text_Kashida_Space Hash = 0x5c12 // text-kashida-space
393 Text_Overflow Hash = 0x980d // text-overflow
394 Text_Shadow Hash = 0xd6d0b // text-shadow
395 Text_Transform Hash = 0xda40e // text-transform
396 Text_Underline_Position Hash = 0xdc717 // text-underline-position
397 Top Hash = 0x3be03 // top
398 Transition Hash = 0x4750a // transition
399 Transition_Delay Hash = 0x59a10 // transition-delay
400 Transition_Duration Hash = 0xb9413 // transition-duration
401 Transition_Property Hash = 0x47513 // transition-property
402 Transition_Timing_Function Hash = 0xa281a // transition-timing-function
403 Transparent Hash = 0xd150b // transparent
404 Turn Hash = 0xd1f04 // turn
405 Turquoise Hash = 0xa8209 // turquoise
406 Unicode_Bidi Hash = 0xcc40c // unicode-bidi
407 Unicode_Range Hash = 0xd230d // unicode-range
408 Unset Hash = 0xd3005 // unset
409 Url Hash = 0x3f403 // url
410 Var Hash = 0x64503 // var
411 Vertical_Align Hash = 0x7e60e // vertical-align
412 Visibility Hash = 0x4f70a // visibility
413 Voice_Family Hash = 0xd350c // voice-family
414 Volume Hash = 0xd4106 // volume
415 White Hash = 0x7b05 // white
416 White_Space Hash = 0x6500b // white-space
417 Whitesmoke Hash = 0x5c30a // whitesmoke
418 Widows Hash = 0xd7706 // widows
419 Width Hash = 0x26b05 // width
420 Word_Break Hash = 0x1670a // word-break
421 Word_Spacing Hash = 0x28e0c // word-spacing
422 Word_Wrap Hash = 0xd0209 // word-wrap
423 Writing_Mode Hash = 0xc8f0c // writing-mode
424 X_Large Hash = 0xa707 // x-large
425 X_Small Hash = 0x68d07 // x-small
426 Xx_Large Hash = 0xa608 // xx-large
427 Xx_Small Hash = 0x68c08 // xx-small
428 Yellow Hash = 0x76506 // yellow
429 Yellowgreen Hash = 0x7650b // yellowgreen
430 Z_Index Hash = 0x68607 // z-index
431)
432
433//var HashMap = map[string]Hash{
434// "-ms-filter": Ms_Filter,
435// "accelerator": Accelerator,
436// "aliceblue": Aliceblue,
437// "align-content": Align_Content,
438// "align-items": Align_Items,
439// "align-self": Align_Self,
440// "all": All,
441// "alpha": Alpha,
442// "animation": Animation,
443// "animation-delay": Animation_Delay,
444// "animation-direction": Animation_Direction,
445// "animation-duration": Animation_Duration,
446// "animation-fill-mode": Animation_Fill_Mode,
447// "animation-iteration-count": Animation_Iteration_Count,
448// "animation-name": Animation_Name,
449// "animation-play-state": Animation_Play_State,
450// "animation-timing-function": Animation_Timing_Function,
451// "antiquewhite": Antiquewhite,
452// "aquamarine": Aquamarine,
453// "attr": Attr,
454// "auto": Auto,
455// "azimuth": Azimuth,
456// "background": Background,
457// "background-attachment": Background_Attachment,
458// "background-clip": Background_Clip,
459// "background-color": Background_Color,
460// "background-image": Background_Image,
461// "background-origin": Background_Origin,
462// "background-position": Background_Position,
463// "background-position-x": Background_Position_X,
464// "background-position-y": Background_Position_Y,
465// "background-repeat": Background_Repeat,
466// "background-size": Background_Size,
467// "behavior": Behavior,
468// "black": Black,
469// "blanchedalmond": Blanchedalmond,
470// "blueviolet": Blueviolet,
471// "bold": Bold,
472// "border": Border,
473// "border-bottom": Border_Bottom,
474// "border-bottom-color": Border_Bottom_Color,
475// "border-bottom-style": Border_Bottom_Style,
476// "border-bottom-width": Border_Bottom_Width,
477// "border-box": Border_Box,
478// "border-collapse": Border_Collapse,
479// "border-color": Border_Color,
480// "border-left": Border_Left,
481// "border-left-color": Border_Left_Color,
482// "border-left-style": Border_Left_Style,
483// "border-left-width": Border_Left_Width,
484// "border-right": Border_Right,
485// "border-right-color": Border_Right_Color,
486// "border-right-style": Border_Right_Style,
487// "border-right-width": Border_Right_Width,
488// "border-spacing": Border_Spacing,
489// "border-style": Border_Style,
490// "border-top": Border_Top,
491// "border-top-color": Border_Top_Color,
492// "border-top-style": Border_Top_Style,
493// "border-top-width": Border_Top_Width,
494// "border-width": Border_Width,
495// "bottom": Bottom,
496// "box-shadow": Box_Shadow,
497// "burlywood": Burlywood,
498// "cadetblue": Cadetblue,
499// "calc": Calc,
500// "caption-side": Caption_Side,
501// "caret-color": Caret_Color,
502// "center": Center,
503// "charset": Charset,
504// "chartreuse": Chartreuse,
505// "chocolate": Chocolate,
506// "clamp": Clamp,
507// "clear": Clear,
508// "clip": Clip,
509// "cm": Cm,
510// "color": Color,
511// "column-count": Column_Count,
512// "column-gap": Column_Gap,
513// "column-rule": Column_Rule,
514// "column-rule-color": Column_Rule_Color,
515// "column-rule-style": Column_Rule_Style,
516// "column-rule-width": Column_Rule_Width,
517// "column-width": Column_Width,
518// "columns": Columns,
519// "content": Content,
520// "cornflowerblue": Cornflowerblue,
521// "cornsilk": Cornsilk,
522// "counter-increment": Counter_Increment,
523// "counter-reset": Counter_Reset,
524// "cue": Cue,
525// "cue-after": Cue_After,
526// "cue-before": Cue_Before,
527// "currentcolor": Currentcolor,
528// "cursive": Cursive,
529// "cursor": Cursor,
530// "darkblue": Darkblue,
531// "darkcyan": Darkcyan,
532// "darkgoldenrod": Darkgoldenrod,
533// "darkgray": Darkgray,
534// "darkgreen": Darkgreen,
535// "darkkhaki": Darkkhaki,
536// "darkmagenta": Darkmagenta,
537// "darkolivegreen": Darkolivegreen,
538// "darkorange": Darkorange,
539// "darkorchid": Darkorchid,
540// "darksalmon": Darksalmon,
541// "darkseagreen": Darkseagreen,
542// "darkslateblue": Darkslateblue,
543// "darkslategray": Darkslategray,
544// "darkturquoise": Darkturquoise,
545// "darkviolet": Darkviolet,
546// "deeppink": Deeppink,
547// "deepskyblue": Deepskyblue,
548// "default": Default,
549// "deg": Deg,
550// "direction": Direction,
551// "display": Display,
552// "document": Document,
553// "dodgerblue": Dodgerblue,
554// "dpcm": Dpcm,
555// "dpi": Dpi,
556// "dppx": Dppx,
557// "elevation": Elevation,
558// "empty-cells": Empty_Cells,
559// "env": Env,
560// "fantasy": Fantasy,
561// "fill": Fill,
562// "filter": Filter,
563// "firebrick": Firebrick,
564// "flex": Flex,
565// "flex-basis": Flex_Basis,
566// "flex-direction": Flex_Direction,
567// "flex-flow": Flex_Flow,
568// "flex-grow": Flex_Grow,
569// "flex-shrink": Flex_Shrink,
570// "flex-wrap": Flex_Wrap,
571// "float": Float,
572// "floralwhite": Floralwhite,
573// "font": Font,
574// "font-face": Font_Face,
575// "font-family": Font_Family,
576// "font-size": Font_Size,
577// "font-size-adjust": Font_Size_Adjust,
578// "font-stretch": Font_Stretch,
579// "font-style": Font_Style,
580// "font-variant": Font_Variant,
581// "font-weight": Font_Weight,
582// "forestgreen": Forestgreen,
583// "fuchsia": Fuchsia,
584// "function": Function,
585// "gainsboro": Gainsboro,
586// "ghostwhite": Ghostwhite,
587// "goldenrod": Goldenrod,
588// "grad": Grad,
589// "greenyellow": Greenyellow,
590// "grid": Grid,
591// "grid-area": Grid_Area,
592// "grid-auto-columns": Grid_Auto_Columns,
593// "grid-auto-flow": Grid_Auto_Flow,
594// "grid-auto-rows": Grid_Auto_Rows,
595// "grid-column": Grid_Column,
596// "grid-column-end": Grid_Column_End,
597// "grid-column-gap": Grid_Column_Gap,
598// "grid-column-start": Grid_Column_Start,
599// "grid-row": Grid_Row,
600// "grid-row-end": Grid_Row_End,
601// "grid-row-gap": Grid_Row_Gap,
602// "grid-row-start": Grid_Row_Start,
603// "grid-template": Grid_Template,
604// "grid-template-areas": Grid_Template_Areas,
605// "grid-template-columns": Grid_Template_Columns,
606// "grid-template-rows": Grid_Template_Rows,
607// "height": Height,
608// "honeydew": Honeydew,
609// "hsl": Hsl,
610// "hsla": Hsla,
611// "hz": Hz,
612// "ime-mode": Ime_Mode,
613// "import": Import,
614// "important": Important,
615// "in": In,
616// "include-source": Include_Source,
617// "indianred": Indianred,
618// "inherit": Inherit,
619// "initial": Initial,
620// "invert": Invert,
621// "justify-content": Justify_Content,
622// "justify-items": Justify_Items,
623// "justify-self": Justify_Self,
624// "keyframes": Keyframes,
625// "khz": Khz,
626// "large": Large,
627// "larger": Larger,
628// "lavender": Lavender,
629// "lavenderblush": Lavenderblush,
630// "lawngreen": Lawngreen,
631// "layer-background-color": Layer_Background_Color,
632// "layer-background-image": Layer_Background_Image,
633// "layout-flow": Layout_Flow,
634// "layout-grid": Layout_Grid,
635// "layout-grid-char": Layout_Grid_Char,
636// "layout-grid-char-spacing": Layout_Grid_Char_Spacing,
637// "layout-grid-line": Layout_Grid_Line,
638// "layout-grid-mode": Layout_Grid_Mode,
639// "layout-grid-type": Layout_Grid_Type,
640// "left": Left,
641// "lemonchiffon": Lemonchiffon,
642// "letter-spacing": Letter_Spacing,
643// "lightblue": Lightblue,
644// "lightcoral": Lightcoral,
645// "lightcyan": Lightcyan,
646// "lightgoldenrodyellow": Lightgoldenrodyellow,
647// "lightgray": Lightgray,
648// "lightgreen": Lightgreen,
649// "lightpink": Lightpink,
650// "lightsalmon": Lightsalmon,
651// "lightseagreen": Lightseagreen,
652// "lightskyblue": Lightskyblue,
653// "lightslateblue": Lightslateblue,
654// "lightsteelblue": Lightsteelblue,
655// "lightyellow": Lightyellow,
656// "limegreen": Limegreen,
657// "line-break": Line_Break,
658// "line-height": Line_Height,
659// "linear-gradient": Linear_Gradient,
660// "list-style": List_Style,
661// "list-style-image": List_Style_Image,
662// "list-style-position": List_Style_Position,
663// "list-style-type": List_Style_Type,
664// "local": Local,
665// "magenta": Magenta,
666// "margin": Margin,
667// "margin-bottom": Margin_Bottom,
668// "margin-left": Margin_Left,
669// "margin-right": Margin_Right,
670// "margin-top": Margin_Top,
671// "marker-offset": Marker_Offset,
672// "marks": Marks,
673// "mask": Mask,
674// "max": Max,
675// "max-height": Max_Height,
676// "max-width": Max_Width,
677// "media": Media,
678// "medium": Medium,
679// "mediumaquamarine": Mediumaquamarine,
680// "mediumblue": Mediumblue,
681// "mediumorchid": Mediumorchid,
682// "mediumpurple": Mediumpurple,
683// "mediumseagreen": Mediumseagreen,
684// "mediumslateblue": Mediumslateblue,
685// "mediumspringgreen": Mediumspringgreen,
686// "mediumturquoise": Mediumturquoise,
687// "mediumvioletred": Mediumvioletred,
688// "midnightblue": Midnightblue,
689// "min": Min,
690// "min-height": Min_Height,
691// "min-width": Min_Width,
692// "mintcream": Mintcream,
693// "mistyrose": Mistyrose,
694// "mm": Mm,
695// "moccasin": Moccasin,
696// "monospace": Monospace,
697// "ms": Ms,
698// "namespace": Namespace,
699// "navajowhite": Navajowhite,
700// "no-repeat": No_Repeat,
701// "none": None,
702// "normal": Normal,
703// "offset": Offset,
704// "offset-anchor": Offset_Anchor,
705// "offset-distance": Offset_Distance,
706// "offset-path": Offset_Path,
707// "offset-position": Offset_Position,
708// "offset-rotate": Offset_Rotate,
709// "olivedrab": Olivedrab,
710// "orangered": Orangered,
711// "order": Order,
712// "orphans": Orphans,
713// "outline": Outline,
714// "outline-color": Outline_Color,
715// "outline-style": Outline_Style,
716// "outline-width": Outline_Width,
717// "overflow": Overflow,
718// "overflow-x": Overflow_X,
719// "overflow-y": Overflow_Y,
720// "padding": Padding,
721// "padding-bottom": Padding_Bottom,
722// "padding-box": Padding_Box,
723// "padding-left": Padding_Left,
724// "padding-right": Padding_Right,
725// "padding-top": Padding_Top,
726// "page": Page,
727// "page-break-after": Page_Break_After,
728// "page-break-before": Page_Break_Before,
729// "page-break-inside": Page_Break_Inside,
730// "palegoldenrod": Palegoldenrod,
731// "palegreen": Palegreen,
732// "paleturquoise": Paleturquoise,
733// "palevioletred": Palevioletred,
734// "papayawhip": Papayawhip,
735// "pause": Pause,
736// "pause-after": Pause_After,
737// "pause-before": Pause_Before,
738// "pc": Pc,
739// "peachpuff": Peachpuff,
740// "pitch": Pitch,
741// "pitch-range": Pitch_Range,
742// "place-content": Place_Content,
743// "place-items": Place_Items,
744// "place-self": Place_Self,
745// "play-during": Play_During,
746// "position": Position,
747// "powderblue": Powderblue,
748// "progid": Progid,
749// "pt": Pt,
750// "px": Px,
751// "q": Q,
752// "quotes": Quotes,
753// "rad": Rad,
754// "radial-gradient": Radial_Gradient,
755// "repeat": Repeat,
756// "repeat-x": Repeat_X,
757// "repeat-y": Repeat_Y,
758// "rgb": Rgb,
759// "rgba": Rgba,
760// "richness": Richness,
761// "right": Right,
762// "rosybrown": Rosybrown,
763// "round": Round,
764// "row-gap": Row_Gap,
765// "royalblue": Royalblue,
766// "ruby-align": Ruby_Align,
767// "ruby-overhang": Ruby_Overhang,
768// "ruby-position": Ruby_Position,
769// "s": S,
770// "saddlebrown": Saddlebrown,
771// "sandybrown": Sandybrown,
772// "sans-serif": Sans_Serif,
773// "scroll": Scroll,
774// "scrollbar-3d-light-color": Scrollbar_3d_Light_Color,
775// "scrollbar-arrow-color": Scrollbar_Arrow_Color,
776// "scrollbar-base-color": Scrollbar_Base_Color,
777// "scrollbar-dark-shadow-color": Scrollbar_Dark_Shadow_Color,
778// "scrollbar-face-color": Scrollbar_Face_Color,
779// "scrollbar-highlight-color": Scrollbar_Highlight_Color,
780// "scrollbar-shadow-color": Scrollbar_Shadow_Color,
781// "scrollbar-track-color": Scrollbar_Track_Color,
782// "seagreen": Seagreen,
783// "seashell": Seashell,
784// "serif": Serif,
785// "size": Size,
786// "slateblue": Slateblue,
787// "slategray": Slategray,
788// "small": Small,
789// "smaller": Smaller,
790// "solid": Solid,
791// "space": Space,
792// "speak": Speak,
793// "speak-header": Speak_Header,
794// "speak-numeral": Speak_Numeral,
795// "speak-punctuation": Speak_Punctuation,
796// "speech-rate": Speech_Rate,
797// "springgreen": Springgreen,
798// "steelblue": Steelblue,
799// "stress": Stress,
800// "stroke": Stroke,
801// "supports": Supports,
802// "table-layout": Table_Layout,
803// "text-align": Text_Align,
804// "text-align-last": Text_Align_Last,
805// "text-autospace": Text_Autospace,
806// "text-decoration": Text_Decoration,
807// "text-decoration-color": Text_Decoration_Color,
808// "text-decoration-line": Text_Decoration_Line,
809// "text-decoration-style": Text_Decoration_Style,
810// "text-decoration-thickness": Text_Decoration_Thickness,
811// "text-emphasis": Text_Emphasis,
812// "text-emphasis-color": Text_Emphasis_Color,
813// "text-indent": Text_Indent,
814// "text-justify": Text_Justify,
815// "text-kashida-space": Text_Kashida_Space,
816// "text-overflow": Text_Overflow,
817// "text-shadow": Text_Shadow,
818// "text-transform": Text_Transform,
819// "text-underline-position": Text_Underline_Position,
820// "top": Top,
821// "transition": Transition,
822// "transition-delay": Transition_Delay,
823// "transition-duration": Transition_Duration,
824// "transition-property": Transition_Property,
825// "transition-timing-function": Transition_Timing_Function,
826// "transparent": Transparent,
827// "turn": Turn,
828// "turquoise": Turquoise,
829// "unicode-bidi": Unicode_Bidi,
830// "unicode-range": UnicodeRange,
831// "unset": Unset,
832// "url": Url,
833// "var": Var,
834// "vertical-align": Vertical_Align,
835// "visibility": Visibility,
836// "voice-family": Voice_Family,
837// "volume": Volume,
838// "white": White,
839// "white-space": White_Space,
840// "whitesmoke": Whitesmoke,
841// "widows": Widows,
842// "width": Width,
843// "word-break": Word_Break,
844// "word-spacing": Word_Spacing,
845// "word-wrap": Word_Wrap,
846// "writing-mode": Writing_Mode,
847// "x-large": X_Large,
848// "x-small": X_Small,
849// "xx-large": Xx_Large,
850// "xx-small": Xx_Small,
851// "yellow": Yellow,
852// "yellowgreen": Yellowgreen,
853// "z-index": Z_Index,
854//}
855
856// String returns the text associated with the hash.
857func (i Hash) String() string {
858 return string(i.Bytes())
859}
860
861// Bytes returns the text associated with the hash.
862func (i Hash) Bytes() []byte {
863 start := uint32(i >> 8)
864 n := uint32(i & 0xff)
865 if start+n > uint32(len(_Hash_text)) {
866 return []byte{}
867 }
868 return _Hash_text[start : start+n]
869}
870
871// ToHash returns a hash Hash for a given []byte. Hash is a uint32 that is associated with the text in []byte. It returns zero if no match found.
872func ToHash(s []byte) Hash {
873 if len(s) == 0 || len(s) > _Hash_maxLen {
874 return 0
875 }
876 //if 3 < len(s) {
877 // return HashMap[string(s)]
878 //}
879 h := uint32(_Hash_hash0)
880 for i := 0; i < len(s); i++ {
881 h ^= uint32(s[i])
882 h *= 16777619
883 }
884 if i := _Hash_table[h&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) {
885 t := _Hash_text[i>>8 : i>>8+i&0xff]
886 for i := 0; i < len(s); i++ {
887 if t[i] != s[i] {
888 goto NEXT
889 }
890 }
891 return i
892 }
893NEXT:
894 if i := _Hash_table[(h>>16)&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) {
895 t := _Hash_text[i>>8 : i>>8+i&0xff]
896 for i := 0; i < len(s); i++ {
897 if t[i] != s[i] {
898 return 0
899 }
900 }
901 return i
902 }
903 return 0
904}
905
906const _Hash_hash0 = 0x9acb0442
907const _Hash_maxLen = 27
908
909var _Hash_text = []byte("" +
910 "-ms-filteradial-gradientext-emphasis-colorgbackground-attach" +
911 "mentext-indentext-justify-contentext-kashida-spacelevationav" +
912 "ajowhitext-decoration-line-heightext-overflow-xx-largerichne" +
913 "ssaddlebrowno-repeat-yanimation-namespacenteruby-overhangain" +
914 "sborosybrownanimation-play-statext-align-lastresscrollbar-ar" +
915 "row-coloruby-positionanimation-timing-functionazimuthoneydew" +
916 "ord-breakbackground-originclude-sourcebackground-position-xb" +
917 "ackground-position-ybackground-repeat-xbackground-sizebehavi" +
918 "orblackblanchedalmondarkblueboldarkcyanimation-delayer-backg" +
919 "round-colorborder-bottom-colorborder-bottom-stylemonchiffont" +
920 "-faceborder-bottom-widthslavenderblushborder-box-shadoword-s" +
921 "pacinghostwhitext-decoration-colorborder-collapseashellawngr" +
922 "eenborder-colorborder-left-colorborder-left-styleborder-left" +
923 "-widthborder-right-colorborder-right-styleborder-right-width" +
924 "border-spacingrid-areanimation-durationormalphacceleratorpha" +
925 "nsandybrownonempty-cellsans-serifantasyborder-styleborder-to" +
926 "p-colorborder-top-styleborder-top-widthborder-widthburlywood" +
927 "arkgoldenrodarkgraycaption-sideepskybluecaret-colorchartreus" +
928 "echocolatext-autospaceclampadding-boxclearcolumn-counter-res" +
929 "etransition-propertycolumn-rule-colorcolumn-rule-stylecolumn" +
930 "-rule-widthcolumn-widthcornflowerbluecornsilkcue-aftercue-be" +
931 "forestgreenvisibilitycurrentcolorcursivecursordarkvioletdocu" +
932 "mentdodgerbluedpcmargin-topadding-rightdpitch-rangedppxflex-" +
933 "growflex-shrinkflex-wrapadding-topage-break-afterfloattransi" +
934 "tion-delayer-background-imagefloralwhitesmokeyframescrollbar" +
935 "-dark-shadow-colorfont-familyfont-size-adjustify-itemscrollb" +
936 "ar-face-colorfont-stretcharsetfont-stylefont-variantiquewhit" +
937 "e-spacefont-weightfuchsianimation-fill-modeeppinkhz-indexx-s" +
938 "malleroyalbluegrid-column-gapage-break-beforegrid-column-sta" +
939 "rtgrid-row-endarkolivegreengrid-row-gapage-break-insidegrid-" +
940 "row-startgrid-template-areascrollbar-track-colorgrid-templat" +
941 "e-columnsolidarkorangeredarkgreenyellowgreengrid-template-ro" +
942 "wspeak-headerimportantinheritinitialicebluevioletter-spacing" +
943 "rid-auto-columnscrollbar-highlight-colorinvertical-align-ite" +
944 "mspeak-numeralayout-grid-char-spacingrid-auto-flowjustify-se" +
945 "lfirebricklayout-grid-line-breaklayout-grid-modegrid-auto-ro" +
946 "wscrollbar-shadow-colorlayout-grid-typeachpufflex-basiscroll" +
947 "bar-base-colorlightbluelightcoralign-selflex-directionlightc" +
948 "yanimation-directionlightgoldenrodyellowlightgraylightgreenl" +
949 "ightpinklightsalmonlightseagreenlightskybluelightslateblueli" +
950 "ghtsteelbluelightyellowlimegreenlinear-gradientlist-style-im" +
951 "agelist-style-positionlist-style-typelocalcadetbluemaskmax-h" +
952 "eightmax-widthmediumaquamarinemediumbluemediumorchidarkorchi" +
953 "darkkhakime-modefaultransition-timing-functionmediumpurpleme" +
954 "diumseagreenmediumslatebluemediumspringgreenmediumturquoisem" +
955 "ediumvioletredarksalmonospacemidnightbluemin-heightmin-width" +
956 "mintcreamarker-offset-anchormistyrosemmarkspeak-punctuationm" +
957 "occasindianredarkseagreenoffset-distanceoffset-pathoffset-po" +
958 "sitionoffset-rotatext-decoration-styleolivedrabackground-cli" +
959 "padding-bottomargin-rightransition-durationoutline-coloroutl" +
960 "ine-styleoutline-widthoverflow-ypalegreenpaleturquoisepalevi" +
961 "oletredarkslategraypapayawhipalegoldenrodarkslatebluepause-a" +
962 "fterpause-beforeplace-contentplace-itemspeech-ratext-decorat" +
963 "ion-thicknesstrokeplace-selflex-flowriting-modepowderbluepro" +
964 "gidarkturquoisequotesupportsunicode-bidisplay-duringrid-colu" +
965 "mn-endarkmagentable-layout-floword-wrapadding-leftransparent" +
966 "urnunicode-rangeunsetvoice-familyvolumedianimation-iteration" +
967 "-counter-incrementext-shadowidowscrollbar-3d-light-coloruby-" +
968 "align-contentext-transformargin-bottomargin-leftext-underlin" +
969 "e-position")
970
971var _Hash_table = [1 << 10]Hash{
972 0x3: 0xc290b, // pause-after
973 0x6: 0xd5d11, // counter-increment
974 0x8: 0xcce07, // display
975 0x9: 0x51a0a, // darkviolet
976 0xb: 0xbf09, // no-repeat
977 0xd: 0x4402, // in
978 0x14: 0x6f211, // page-break-inside
979 0x15: 0x6250c, // font-stretch
980 0x19: 0x5f910, // font-size-adjust
981 0x1a: 0x47513, // transition-property
982 0x1c: 0x78105, // speak
983 0x1f: 0x82a0c, // justify-self
984 0x20: 0x61114, // scrollbar-face-color
985 0x24: 0x2b60f, // border-collapse
986 0x25: 0x68607, // z-index
987 0x27: 0xd980d, // align-content
988 0x2a: 0x99f13, // list-style-position
989 0x2b: 0xcdb0f, // grid-column-end
990 0x2c: 0x14119, // animation-timing-function
991 0x30: 0xb0909, // indianred
992 0x34: 0x97709, // limegreen
993 0x35: 0xbc10d, // outline-width
994 0x3f: 0x15a07, // azimuth
995 0x40: 0x1e70e, // blanchedalmond
996 0x41: 0x84a0a, // line-break
997 0x42: 0x7a209, // aliceblue
998 0x43: 0xf309, // rosybrown
999 0x46: 0xa7c0f, // mediumturquoise
1000 0x49: 0xd7706, // widows
1001 0x4b: 0xb370f, // offset-position
1002 0x4d: 0xd150b, // transparent
1003 0x4e: 0x79d07, // initial
1004 0x52: 0x1cb0f, // background-size
1005 0x55: 0x2505, // color
1006 0x56: 0x59a10, // transition-delay
1007 0x5a: 0x750b, // navajowhite
1008 0x5b: 0x7110d, // grid-template
1009 0x5c: 0x3b710, // border-top-color
1010 0x62: 0xbce0a, // overflow-y
1011 0x64: 0x9370d, // lightseagreen
1012 0x6c: 0x10e0f, // text-align-last
1013 0x6f: 0x8050b, // layout-grid
1014 0x70: 0xca09, // animation
1015 0x71: 0x1da08, // behavior
1016 0x72: 0x5390a, // margin-top
1017 0x74: 0x3ab0c, // border-style
1018 0x78: 0x5d31b, // scrollbar-dark-shadow-color
1019 0x79: 0x69103, // all
1020 0x7a: 0x3f0b, // text-indent
1021 0x7b: 0xbe10d, // paleturquoise
1022 0x7e: 0x58510, // page-break-after
1023 0x80: 0x5420d, // padding-right
1024 0x84: 0x7e60e, // vertical-align
1025 0x85: 0x50d07, // cursive
1026 0x8a: 0x7030e, // grid-row-start
1027 0x8c: 0xae08, // richness
1028 0x8e: 0x3b70a, // border-top
1029 0x94: 0x35509, // grid-area
1030 0x95: 0x85410, // layout-grid-mode
1031 0x96: 0xaee05, // marks
1032 0x97: 0x64d01, // q
1033 0x98: 0x78d09, // important
1034 0x9c: 0x406, // filter
1035 0x9d: 0xa8b0f, // mediumvioletred
1036 0xa5: 0xc570b, // speech-rate
1037 0xa8: 0x53702, // pc
1038 0xab: 0x90f, // radial-gradient
1039 0xae: 0x11b06, // stress
1040 0xb4: 0x6050d, // justify-items
1041 0xb7: 0x9500e, // lightslateblue
1042 0xba: 0x35504, // grid
1043 0xbb: 0xb0308, // moccasin
1044 0xbe: 0xd0209, // word-wrap
1045 0xc0: 0x6d90e, // darkolivegreen
1046 0xc5: 0xc6019, // text-decoration-thickness
1047 0xc7: 0xdb06, // center
1048 0xc8: 0x2a115, // text-decoration-color
1049 0xcb: 0xabf09, // min-width
1050 0xce: 0x5ee0b, // font-family
1051 0xd1: 0xa1c08, // ime-mode
1052 0xd3: 0x3d710, // border-top-width
1053 0xd4: 0x53906, // margin
1054 0xd9: 0x4880b, // column-rule
1055 0xda: 0x98f0a, // list-style
1056 0xdf: 0x6ce0c, // grid-row-end
1057 0xe4: 0x2050f, // animation-delay
1058 0xe8: 0x4aa11, // column-rule-width
1059 0xec: 0x57309, // flex-wrap
1060 0xed: 0xced07, // magenta
1061 0xee: 0x88710, // layout-grid-type
1062 0xef: 0x4520b, // padding-box
1063 0xf0: 0x7e14, // text-decoration-line
1064 0xf2: 0x4dd09, // cue-after
1065 0xf4: 0x8640e, // grid-auto-rows
1066 0xf5: 0x7650b, // yellowgreen
1067 0xf8: 0x89509, // peachpuff
1068 0xf9: 0x74607, // columns
1069 0xfa: 0x22805, // order
1070 0xfb: 0x3120c, // border-right
1071 0x100: 0x1800e, // include-source
1072 0x104: 0xc2905, // pause
1073 0x105: 0x1fc04, // bold
1074 0x106: 0xcc40c, // unicode-bidi
1075 0x108: 0x67604, // fill
1076 0x109: 0x75c09, // darkgreen
1077 0x10b: 0x45d05, // clear
1078 0x10c: 0x67d08, // deeppink
1079 0x110: 0x8e913, // animation-direction
1080 0x112: 0x1b811, // background-repeat
1081 0x117: 0xca506, // progid
1082 0x11d: 0x8a614, // scrollbar-base-color
1083 0x11e: 0xa, // -ms-filter
1084 0x11f: 0x2ca09, // lawngreen
1085 0x120: 0x51406, // cursor
1086 0x121: 0x44e05, // clamp
1087 0x123: 0x48811, // column-rule-color
1088 0x128: 0x40f0c, // caption-side
1089 0x12a: 0xc9b0a, // powderblue
1090 0x12b: 0xdc717, // text-underline-position
1091 0x12d: 0x72315, // scrollbar-track-color
1092 0x131: 0x81c0e, // grid-auto-flow
1093 0x132: 0x7810c, // speak-header
1094 0x133: 0x25409, // font-face
1095 0x136: 0xa710b, // springgreen
1096 0x13a: 0xc7e0a, // place-self
1097 0x13d: 0xc206, // repeat
1098 0x13e: 0x9800f, // linear-gradient
1099 0x142: 0x5010c, // currentcolor
1100 0x145: 0xad706, // offset
1101 0x14a: 0x69e0f, // grid-column-gap
1102 0x14c: 0x6905, // space
1103 0x14e: 0x39b0a, // sans-serif
1104 0x14f: 0x6360a, // font-style
1105 0x153: 0x66607, // fuchsia
1106 0x154: 0xb7904, // clip
1107 0x155: 0xae409, // mistyrose
1108 0x158: 0x9d08, // overflow
1109 0x15d: 0xc7806, // stroke
1110 0x162: 0x80510, // layout-grid-char
1111 0x163: 0xa420c, // mediumpurple
1112 0x165: 0x4f503, // env
1113 0x168: 0x4690d, // counter-reset
1114 0x16b: 0x5cb09, // keyframes
1115 0x16f: 0x7b05, // white
1116 0x172: 0x1004, // grad
1117 0x174: 0xdb10d, // margin-bottom
1118 0x175: 0x31212, // border-right-color
1119 0x177: 0x25404, // font
1120 0x178: 0xc100d, // palegoldenrod
1121 0x179: 0x73815, // grid-template-columns
1122 0x17a: 0x7e0f, // text-decoration
1123 0x17e: 0x89d0a, // flex-basis
1124 0x186: 0x7ef0b, // align-items
1125 0x189: 0x4bb0c, // column-width
1126 0x18a: 0x3c710, // border-top-style
1127 0x18b: 0x1d604, // size
1128 0x18c: 0xd4505, // media
1129 0x191: 0xb7c0e, // padding-bottom
1130 0x194: 0x2df11, // border-left-color
1131 0x195: 0x7a70a, // blueviolet
1132 0x198: 0x92c0b, // lightsalmon
1133 0x19d: 0x27108, // lavender
1134 0x19e: 0x5a716, // layer-background-image
1135 0x1a0: 0x6500b, // white-space
1136 0x1a3: 0xe00d, // ruby-overhang
1137 0x1a4: 0x24b0c, // lemonchiffon
1138 0x1a5: 0x3be03, // top
1139 0x1a9: 0x2c308, // seashell
1140 0x1aa: 0x7ae0e, // letter-spacing
1141 0x1ac: 0x2b0a, // background
1142 0x1af: 0x64503, // var
1143 0x1b0: 0xaed02, // mm
1144 0x1b6: 0x12015, // scrollbar-arrow-color
1145 0x1b8: 0xda40e, // text-transform
1146 0x1b9: 0x65b0b, // font-weight
1147 0x1ba: 0x53802, // cm
1148 0x1bb: 0x12006, // scroll
1149 0x1c0: 0x21710, // background-color
1150 0x1c1: 0x2710d, // lavenderblush
1151 0x1c6: 0xb5115, // text-decoration-style
1152 0x1c9: 0x79607, // inherit
1153 0x1cf: 0x2e604, // left
1154 0x1d0: 0x6490c, // antiquewhite
1155 0x1d4: 0xb6609, // olivedrab
1156 0x1da: 0x2990a, // ghostwhite
1157 0x1dd: 0x91009, // lightgray
1158 0x1e2: 0x26f04, // hsla
1159 0x1e3: 0x26f03, // hsl
1160 0x1e4: 0xbd809, // palegreen
1161 0x1e5: 0x4190b, // deepskyblue
1162 0x1e8: 0xac809, // mintcream
1163 0x1ea: 0x7e406, // invert
1164 0x1eb: 0x6400c, // font-variant
1165 0x1ec: 0x8fc14, // lightgoldenrodyellow
1166 0x1ee: 0x62f07, // charset
1167 0x1ef: 0xc8f0c, // writing-mode
1168 0x1f0: 0x5c30a, // whitesmoke
1169 0x1f5: 0x9d0a, // overflow-x
1170 0x1f6: 0xaa90c, // midnightblue
1171 0x1f7: 0xcb706, // quotes
1172 0x1f8: 0x22706, // border
1173 0x1fa: 0x42f0a, // chartreuse
1174 0x1fc: 0xba707, // outline
1175 0x1fd: 0xa281a, // transition-timing-function
1176 0x1fe: 0xcbc08, // supports
1177 0x204: 0x1670a, // word-break
1178 0x205: 0xaa009, // monospace
1179 0x206: 0x2850a, // box-shadow
1180 0x209: 0x5680b, // flex-shrink
1181 0x20f: 0xd0a0c, // padding-left
1182 0x214: 0xc4d0b, // place-items
1183 0x216: 0xc070a, // papayawhip
1184 0x217: 0x17111, // background-origin
1185 0x218: 0x52408, // document
1186 0x219: 0x52c0a, // dodgerblue
1187 0x21c: 0x9440c, // lightskyblue
1188 0x21e: 0x6bd11, // grid-column-start
1189 0x221: 0x30111, // border-left-width
1190 0x224: 0x68c08, // xx-small
1191 0x226: 0x1f408, // darkblue
1192 0x229: 0x25d13, // border-bottom-width
1193 0x22a: 0x98f10, // list-style-image
1194 0x22d: 0x44504, // auto
1195 0x230: 0x1e205, // black
1196 0x231: 0xaf211, // speak-punctuation
1197 0x232: 0x13908, // position
1198 0x234: 0xc340c, // pause-before
1199 0x236: 0x95e0e, // lightsteelblue
1200 0x23a: 0xcd10b, // play-during
1201 0x23f: 0x83509, // firebrick
1202 0x249: 0x6ce08, // grid-row
1203 0x24a: 0x55d02, // px
1204 0x24c: 0x1a315, // background-position-y
1205 0x251: 0xd1f04, // turn
1206 0x256: 0xba70d, // outline-color
1207 0x257: 0x9c304, // calc
1208 0x258: 0xd4919, // animation-iteration-count
1209 0x259: 0xad70d, // offset-anchor
1210 0x25b: 0xa4e0e, // mediumseagreen
1211 0x25e: 0x4620c, // column-count
1212 0x263: 0x10e0a, // text-align
1213 0x266: 0x66c13, // animation-fill-mode
1214 0x267: 0x32412, // border-right-style
1215 0x268: 0xa707, // x-large
1216 0x269: 0x8d40e, // flex-direction
1217 0x26a: 0x4f70a, // visibility
1218 0x26f: 0xb2c0b, // offset-path
1219 0x270: 0x27e0a, // border-box
1220 0x276: 0x70103, // deg
1221 0x278: 0x1713, // text-emphasis-color
1222 0x27f: 0xc1c0d, // darkslateblue
1223 0x283: 0x55f09, // flex-grow
1224 0x285: 0x8e209, // lightcyan
1225 0x28a: 0x102, // ms
1226 0x28d: 0xa906, // larger
1227 0x28e: 0xa990a, // darksalmon
1228 0x292: 0x2f011, // border-left-style
1229 0x293: 0xa8209, // turquoise
1230 0x294: 0x3a407, // fantasy
1231 0x296: 0xec09, // gainsboro
1232 0x297: 0x201, // s
1233 0x298: 0x23a13, // border-bottom-style
1234 0x299: 0xce90b, // darkmagenta
1235 0x29b: 0xb50b, // saddlebrown
1236 0x2a0: 0x59505, // float
1237 0x2a3: 0x6ec07, // row-gap
1238 0x2a5: 0xd4106, // volume
1239 0x2a6: 0xab50a, // min-height
1240 0x2a7: 0x77012, // grid-template-rows
1241 0x2a9: 0x3760b, // accelerator
1242 0x2b0: 0x68f05, // small
1243 0x2b1: 0x59804, // attr
1244 0x2b2: 0x28e0c, // word-spacing
1245 0x2b3: 0x35d12, // animation-duration
1246 0x2b5: 0x4dd03, // cue
1247 0x2b6: 0x95509, // slateblue
1248 0x2b8: 0x38e04, // none
1249 0x2b9: 0x6a30a, // column-gap
1250 0x2ba: 0x4e0f, // justify-content
1251 0x2bb: 0x5607, // content
1252 0x2bd: 0x54f03, // dpi
1253 0x2be: 0x87116, // scrollbar-shadow-color
1254 0x2bf: 0x78d06, // import
1255 0x2c0: 0xc8709, // flex-flow
1256 0x2c1: 0x69509, // royalblue
1257 0x2c3: 0x9c609, // cadetblue
1258 0x2c4: 0x490c, // text-justify
1259 0x2cb: 0x8c30a, // lightcoral
1260 0x2cf: 0xb890c, // margin-right
1261 0x2d2: 0x76506, // yellow
1262 0x2d3: 0x26b05, // width
1263 0x2d6: 0x14d03, // min
1264 0x2da: 0x1340d, // ruby-position
1265 0x2dc: 0x40708, // darkgray
1266 0x2e2: 0x69e0b, // grid-column
1267 0x2e4: 0xa1409, // darkkhaki
1268 0x2e5: 0xc400d, // place-content
1269 0x2e7: 0xbee0d, // palevioletred
1270 0x2ea: 0x5bd0b, // floralwhite
1271 0x2eb: 0xc208, // repeat-y
1272 0x2ee: 0x980d, // text-overflow
1273 0x2f1: 0xca0e, // animation-name
1274 0x2fb: 0x7cb19, // scrollbar-highlight-color
1275 0x2fe: 0x5500b, // pitch-range
1276 0x302: 0x3005, // round
1277 0x305: 0x4c70e, // cornflowerblue
1278 0x307: 0x7f90d, // speak-numeral
1279 0x308: 0x9e606, // medium
1280 0x30a: 0x170d, // text-emphasis
1281 0x30d: 0x9dd09, // max-width
1282 0x311: 0x36e06, // normal
1283 0x312: 0x68403, // khz
1284 0x315: 0x2903, // rgb
1285 0x316: 0x8ba09, // lightblue
1286 0x317: 0x8d909, // direction
1287 0x31a: 0xd350c, // voice-family
1288 0x31c: 0x3480e, // border-spacing
1289 0x321: 0x6d09, // elevation
1290 0x323: 0x1c308, // repeat-x
1291 0x324: 0x83e10, // layout-grid-line
1292 0x326: 0xa000c, // mediumorchid
1293 0x32b: 0xa6b11, // mediumspringgreen
1294 0x32d: 0xa905, // large
1295 0x32e: 0xd930a, // ruby-align
1296 0x330: 0xbfa0d, // darkslategray
1297 0x332: 0x5c12, // text-kashida-space
1298 0x334: 0xbb40d, // outline-style
1299 0x336: 0x3a005, // serif
1300 0x337: 0x4240b, // caret-color
1301 0x33a: 0x37205, // alpha
1302 0x33c: 0x71113, // grid-template-areas
1303 0x33d: 0x49911, // column-rule-style
1304 0x33f: 0xcf80b, // layout-flow
1305 0x340: 0x31905, // right
1306 0x341: 0x3e70c, // border-width
1307 0x343: 0xb6e0f, // background-clip
1308 0x344: 0xd230d, // unicode-range
1309 0x345: 0x74c05, // solid
1310 0x346: 0x2df0b, // border-left
1311 0x348: 0x9ec0a, // aquamarine
1312 0x349: 0x3850a, // sandybrown
1313 0x34a: 0x16008, // honeydew
1314 0x34b: 0x75409, // orangered
1315 0x34c: 0xb110c, // darkseagreen
1316 0x34d: 0x37f07, // orphans
1317 0x34e: 0x6e70c, // grid-row-gap
1318 0x351: 0x22e06, // bottom
1319 0x359: 0x9c105, // local
1320 0x35c: 0x8cb0a, // align-self
1321 0x35e: 0x33612, // border-right-width
1322 0x360: 0x2b15, // background-attachment
1323 0x364: 0x9190a, // lightgreen
1324 0x366: 0x39302, // pt
1325 0x368: 0x4400e, // text-autospace
1326 0x36b: 0x3f403, // url
1327 0x36c: 0x68502, // hz
1328 0x371: 0x9306, // height
1329 0x372: 0x5ad10, // background-image
1330 0x377: 0x903, // rad
1331 0x37c: 0x21116, // layer-background-color
1332 0x37d: 0x1ff08, // darkcyan
1333 0x382: 0x18e13, // background-position
1334 0x384: 0x9d303, // max
1335 0x38c: 0xa608, // xx-large
1336 0x38d: 0x3f309, // burlywood
1337 0x38f: 0xd7c18, // scrollbar-3d-light-color
1338 0x390: 0x3ff09, // goldenrod
1339 0x392: 0x92309, // lightpink
1340 0x393: 0x8e0b, // line-height
1341 0x396: 0x22713, // border-bottom-color
1342 0x398: 0x80518, // layout-grid-char-spacing
1343 0x39c: 0x2904, // rgba
1344 0x3a1: 0x9f60a, // mediumblue
1345 0x3a3: 0x9d30a, // max-height
1346 0x3a4: 0x7bb11, // grid-auto-columns
1347 0x3a5: 0xa0b0a, // darkorchid
1348 0x3a9: 0x7600b, // greenyellow
1349 0x3ae: 0x96c0b, // lightyellow
1350 0x3b1: 0x4750a, // transition
1351 0x3b3: 0x4e60a, // cue-before
1352 0x3b6: 0x15208, // function
1353 0x3b9: 0x96309, // steelblue
1354 0x3be: 0xa5c0f, // mediumslateblue
1355 0x3bf: 0xcaa0d, // darkturquoise
1356 0x3c0: 0x43909, // chocolate
1357 0x3c3: 0x5f909, // font-size
1358 0x3c5: 0x55f04, // flex
1359 0x3c7: 0xd3005, // unset
1360 0x3c8: 0xd6d0b, // text-shadow
1361 0x3ca: 0x4ec0b, // forestgreen
1362 0x3cc: 0xbfe09, // slategray
1363 0x3cd: 0x6ac11, // page-break-before
1364 0x3ce: 0x55b04, // dppx
1365 0x3d0: 0x2270d, // border-bottom
1366 0x3d3: 0xb1d0f, // offset-distance
1367 0x3d4: 0x3fb0d, // darkgoldenrod
1368 0x3d6: 0x53604, // dpcm
1369 0x3d8: 0x7500a, // darkorange
1370 0x3dc: 0xb9413, // transition-duration
1371 0x3de: 0x2d30c, // border-color
1372 0x3df: 0x18e15, // background-position-x
1373 0x3e0: 0x55005, // pitch
1374 0x3e2: 0xdbd0b, // margin-left
1375 0x3e3: 0x58504, // page
1376 0x3e5: 0x57b0b, // padding-top
1377 0x3e7: 0xb460d, // offset-rotate
1378 0x3e8: 0x93c08, // seagreen
1379 0x3e9: 0x4d508, // cornsilk
1380 0x3ea: 0x68f07, // smaller
1381 0x3ec: 0xcf20c, // table-layout
1382 0x3ed: 0xfc14, // animation-play-state
1383 0x3ef: 0xa2207, // default
1384 0x3f0: 0x68d07, // x-small
1385 0x3f3: 0x9e610, // mediumaquamarine
1386 0x3f4: 0xad00d, // marker-offset
1387 0x3f9: 0xd409, // namespace
1388 0x3fa: 0x9cf04, // mask
1389 0x3fb: 0x45207, // padding
1390 0x3fd: 0x9b20f, // list-style-type
1391 0x3ff: 0x3910b, // empty-cells
1392}
diff --git a/vendor/github.com/tdewolff/minify/v2/css/table.go b/vendor/github.com/tdewolff/minify/v2/css/table.go
new file mode 100644
index 0000000..b7ecb84
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/css/table.go
@@ -0,0 +1,198 @@
1package css
2
3var optionalZeroDimension = map[string]bool{
4 "px": true,
5 "mm": true,
6 "q": true,
7 "cm": true,
8 "in": true,
9 "pt": true,
10 "pc": true,
11 "ch": true,
12 "em": true,
13 "ex": true,
14 "rem": true,
15 "vh": true,
16 "vw": true,
17 "vmin": true,
18 "vmax": true,
19 "deg": true,
20 "grad": true,
21 "rad": true,
22 "turn": true,
23}
24
25// Uses http://www.w3.org/TR/2010/PR-css3-color-20101028/ for colors
26
27// ShortenColorHex maps a color hexcode to its shorter name
28var ShortenColorHex = map[string][]byte{
29 "#000080": []byte("navy"),
30 "#008000": []byte("green"),
31 "#008080": []byte("teal"),
32 "#4b0082": []byte("indigo"),
33 "#800000": []byte("maroon"),
34 "#800080": []byte("purple"),
35 "#808000": []byte("olive"),
36 "#808080": []byte("gray"),
37 "#a0522d": []byte("sienna"),
38 "#a52a2a": []byte("brown"),
39 "#c0c0c0": []byte("silver"),
40 "#cd853f": []byte("peru"),
41 "#d2b48c": []byte("tan"),
42 "#da70d6": []byte("orchid"),
43 "#dda0dd": []byte("plum"),
44 "#ee82ee": []byte("violet"),
45 "#f0e68c": []byte("khaki"),
46 "#f0ffff": []byte("azure"),
47 "#f5deb3": []byte("wheat"),
48 "#f5f5dc": []byte("beige"),
49 "#fa8072": []byte("salmon"),
50 "#faf0e6": []byte("linen"),
51 "#ff6347": []byte("tomato"),
52 "#ff7f50": []byte("coral"),
53 "#ffa500": []byte("orange"),
54 "#ffc0cb": []byte("pink"),
55 "#ffd700": []byte("gold"),
56 "#ffe4c4": []byte("bisque"),
57 "#fffafa": []byte("snow"),
58 "#fffff0": []byte("ivory"),
59 "#ff0000": []byte("red"),
60 "#f00": []byte("red"),
61}
62
63// ShortenColorName maps a color name to its shorter hexcode
64var ShortenColorName = map[Hash][]byte{
65 Black: []byte("#000"),
66 Darkblue: []byte("#00008b"),
67 Mediumblue: []byte("#0000cd"),
68 Darkgreen: []byte("#006400"),
69 Darkcyan: []byte("#008b8b"),
70 Deepskyblue: []byte("#00bfff"),
71 Darkturquoise: []byte("#00ced1"),
72 Mediumspringgreen: []byte("#00fa9a"),
73 Springgreen: []byte("#00ff7f"),
74 Midnightblue: []byte("#191970"),
75 Dodgerblue: []byte("#1e90ff"),
76 Lightseagreen: []byte("#20b2aa"),
77 Forestgreen: []byte("#228b22"),
78 Seagreen: []byte("#2e8b57"),
79 Darkslategray: []byte("#2f4f4f"),
80 Limegreen: []byte("#32cd32"),
81 Mediumseagreen: []byte("#3cb371"),
82 Turquoise: []byte("#40e0d0"),
83 Royalblue: []byte("#4169e1"),
84 Steelblue: []byte("#4682b4"),
85 Darkslateblue: []byte("#483d8b"),
86 Mediumturquoise: []byte("#48d1cc"),
87 Darkolivegreen: []byte("#556b2f"),
88 Cadetblue: []byte("#5f9ea0"),
89 Cornflowerblue: []byte("#6495ed"),
90 Mediumaquamarine: []byte("#66cdaa"),
91 Slateblue: []byte("#6a5acd"),
92 Olivedrab: []byte("#6b8e23"),
93 Slategray: []byte("#708090"),
94 Lightslateblue: []byte("#789"),
95 Mediumslateblue: []byte("#7b68ee"),
96 Lawngreen: []byte("#7cfc00"),
97 Chartreuse: []byte("#7fff00"),
98 Aquamarine: []byte("#7fffd4"),
99 Lightskyblue: []byte("#87cefa"),
100 Blueviolet: []byte("#8a2be2"),
101 Darkmagenta: []byte("#8b008b"),
102 Saddlebrown: []byte("#8b4513"),
103 Darkseagreen: []byte("#8fbc8f"),
104 Lightgreen: []byte("#90ee90"),
105 Mediumpurple: []byte("#9370db"),
106 Darkviolet: []byte("#9400d3"),
107 Palegreen: []byte("#98fb98"),
108 Darkorchid: []byte("#9932cc"),
109 Yellowgreen: []byte("#9acd32"),
110 Darkgray: []byte("#a9a9a9"),
111 Lightblue: []byte("#add8e6"),
112 Greenyellow: []byte("#adff2f"),
113 Paleturquoise: []byte("#afeeee"),
114 Lightsteelblue: []byte("#b0c4de"),
115 Powderblue: []byte("#b0e0e6"),
116 Firebrick: []byte("#b22222"),
117 Darkgoldenrod: []byte("#b8860b"),
118 Mediumorchid: []byte("#ba55d3"),
119 Rosybrown: []byte("#bc8f8f"),
120 Darkkhaki: []byte("#bdb76b"),
121 Mediumvioletred: []byte("#c71585"),
122 Indianred: []byte("#cd5c5c"),
123 Chocolate: []byte("#d2691e"),
124 Lightgray: []byte("#d3d3d3"),
125 Goldenrod: []byte("#daa520"),
126 Palevioletred: []byte("#db7093"),
127 Gainsboro: []byte("#dcdcdc"),
128 Burlywood: []byte("#deb887"),
129 Lightcyan: []byte("#e0ffff"),
130 Lavender: []byte("#e6e6fa"),
131 Darksalmon: []byte("#e9967a"),
132 Palegoldenrod: []byte("#eee8aa"),
133 Lightcoral: []byte("#f08080"),
134 Aliceblue: []byte("#f0f8ff"),
135 Honeydew: []byte("#f0fff0"),
136 Sandybrown: []byte("#f4a460"),
137 Whitesmoke: []byte("#f5f5f5"),
138 Mintcream: []byte("#f5fffa"),
139 Ghostwhite: []byte("#f8f8ff"),
140 Antiquewhite: []byte("#faebd7"),
141 Lightgoldenrodyellow: []byte("#fafad2"),
142 Fuchsia: []byte("#f0f"),
143 Magenta: []byte("#f0f"),
144 Deeppink: []byte("#ff1493"),
145 Orangered: []byte("#ff4500"),
146 Darkorange: []byte("#ff8c00"),
147 Lightsalmon: []byte("#ffa07a"),
148 Lightpink: []byte("#ffb6c1"),
149 Peachpuff: []byte("#ffdab9"),
150 Navajowhite: []byte("#ffdead"),
151 Moccasin: []byte("#ffe4b5"),
152 Mistyrose: []byte("#ffe4e1"),
153 Blanchedalmond: []byte("#ffebcd"),
154 Papayawhip: []byte("#ffefd5"),
155 Lavenderblush: []byte("#fff0f5"),
156 Seashell: []byte("#fff5ee"),
157 Cornsilk: []byte("#fff8dc"),
158 Lemonchiffon: []byte("#fffacd"),
159 Floralwhite: []byte("#fffaf0"),
160 Yellow: []byte("#ff0"),
161 Lightyellow: []byte("#ffffe0"),
162 White: []byte("#fff"),
163}
164
165// PropertyOverrides is a map of which properties are overridden by the given property.
166var PropertyOverrides = map[Hash][]Hash{
167 Background: {Background, Background_Image, Background_Position, Background_Size, Background_Repeat, Background_Origin, Background_Clip, Background_Attachment, Background_Color},
168 Font: {Font, Font_Style, Font_Variant, Font_Weight, Font_Stretch, Font_Size, Font_Family, Line_Height},
169 Border: {Border, Border_Width, Border_Top_Width, Border_Right_Width, Border_Bottom_Width, Border_Left_Width, Border_Style, Border_Top_Style, Border_Right_Style, Border_Bottom_Style, Border_Left_Style, Border_Color, Border_Top_Color, Border_Right_Color, Border_Bottom_Color, Border_Left_Color},
170 Border_Width: {Border_Width, Border_Top_Width, Border_Right_Width, Border_Bottom_Width, Border_Left_Width},
171 Border_Style: {Border_Style, Border_Top_Style, Border_Right_Style, Border_Bottom_Style, Border_Left_Style},
172 Border_Color: {Border_Color, Border_Top_Color, Border_Right_Color, Border_Bottom_Color, Border_Left_Color},
173 Border_Top: {Border_Top, Border_Top_Width, Border_Top_Style, Border_Top_Color},
174 Border_Right: {Border_Right, Border_Right_Width, Border_Right_Style, Border_Right_Color},
175 Border_Bottom: {Border_Bottom, Border_Bottom_Width, Border_Bottom_Style, Border_Bottom_Color},
176 Border_Left: {Border_Left, Border_Left_Width, Border_Left_Style, Border_Left_Color},
177 Margin: {Margin, Margin_Top, Margin_Right, Margin_Bottom, Margin_Left},
178 Padding: {Padding, Padding_Top, Padding_Right, Padding_Bottom, Padding_Left},
179 Column_Rule: {Column_Rule, Column_Rule_Width, Column_Rule_Style, Column_Rule_Color},
180 Animation: {Animation, Animation_Name, Animation_Duration, Animation_Timing_Function, Animation_Delay, Animation_Iteration_Count, Animation_Direction, Animation_Fill_Mode, Animation_Play_State},
181 Columns: {Columns, Column_Width, Column_Count},
182 Flex: {Flex, Flex_Basis, Flex_Grow, Flex_Shrink},
183 Flex_Flow: {Flex_Flow, Flex_Direction, Flex_Wrap},
184 Grid: {Grid, Grid_Template_Rows, Grid_Template_Columns, Grid_Template_Areas, Grid_Auto_Rows, Grid_Auto_Columns, Grid_Auto_Flow, Grid_Column_Gap, Grid_Row_Gap, Column_Gap, Row_Gap},
185 Grid_Area: {Grid_Area, Grid_Row_Start, Grid_Column_Start, Grid_Row_End, Grid_Column_End},
186 Grid_Row: {Grid_Row, Grid_Row_Start, Grid_Row_End},
187 Grid_Column: {Grid_Column, Grid_Column_Start, Grid_Column_End},
188 Grid_Template: {Grid_Template, Grid_Template_Rows, Grid_Template_Columns, Grid_Template_Areas},
189 List_Style: {List_Style, List_Style_Image, List_Style_Position, List_Style_Type},
190 Offset: {Offset, Offset_Position, Offset_Path, Offset_Distance, Offset_Anchor, Offset_Rotate},
191 Outline: {Outline, Outline_Width, Outline_Style, Outline_Color},
192 Overflow: {Overflow, Overflow_X, Overflow_Y},
193 Place_Content: {Place_Content, Align_Content, Justify_Content},
194 Place_Items: {Place_Items, Align_Items, Justify_Items},
195 Place_Self: {Place_Self, Align_Self, Justify_Self},
196 Text_Decoration: {Text_Decoration, Text_Decoration_Color, Text_Decoration_Color, Text_Decoration_Line, Text_Decoration_Thickness},
197 Transition: {Transition, Transition_Property, Transition_Duration, Transition_Timing_Function, Transition_Delay},
198}
diff --git a/vendor/github.com/tdewolff/minify/v2/css/util.go b/vendor/github.com/tdewolff/minify/v2/css/util.go
new file mode 100644
index 0000000..7325aca
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/css/util.go
@@ -0,0 +1,55 @@
1package css
2
3import (
4 "encoding/hex"
5
6 "github.com/tdewolff/parse/v2"
7 "github.com/tdewolff/parse/v2/css"
8)
9
10func removeMarkupNewlines(data []byte) []byte {
11 // remove any \\\r\n \\\r \\\n
12 for i := 1; i < len(data)-2; i++ {
13 if data[i] == '\\' && (data[i+1] == '\n' || data[i+1] == '\r') {
14 // encountered first replacee, now start to move bytes to the front
15 j := i + 2
16 if data[i+1] == '\r' && len(data) > i+2 && data[i+2] == '\n' {
17 j++
18 }
19 for ; j < len(data); j++ {
20 if data[j] == '\\' && len(data) > j+1 && (data[j+1] == '\n' || data[j+1] == '\r') {
21 if data[j+1] == '\r' && len(data) > j+2 && data[j+2] == '\n' {
22 j++
23 }
24 j++
25 } else {
26 data[i] = data[j]
27 i++
28 }
29 }
30 data = data[:i]
31 break
32 }
33 }
34 return data
35}
36
37func rgbToToken(r, g, b float64) Token {
38 // r, g, b are in interval [0.0, 1.0]
39 rgb := []byte{byte((r * 255.0) + 0.5), byte((g * 255.0) + 0.5), byte((b * 255.0) + 0.5)}
40
41 val := make([]byte, 7)
42 val[0] = '#'
43 hex.Encode(val[1:], rgb)
44 parse.ToLower(val)
45 if s, ok := ShortenColorHex[string(val[:7])]; ok {
46 return Token{css.IdentToken, s, nil, 0, 0}
47 } else if val[1] == val[2] && val[3] == val[4] && val[5] == val[6] {
48 val[2] = val[3]
49 val[3] = val[5]
50 val = val[:4]
51 } else {
52 val = val[:7]
53 }
54 return Token{css.HashToken, val, nil, 0, 0}
55}
diff --git a/vendor/github.com/tdewolff/minify/v2/html/buffer.go b/vendor/github.com/tdewolff/minify/v2/html/buffer.go
new file mode 100644
index 0000000..f58367b
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/html/buffer.go
@@ -0,0 +1,137 @@
1package html
2
3import (
4 "github.com/tdewolff/parse/v2"
5 "github.com/tdewolff/parse/v2/html"
6)
7
8// Token is a single token unit with an attribute value (if given) and hash of the data.
9type Token struct {
10 html.TokenType
11 Hash Hash
12 Data []byte
13 Text []byte
14 AttrVal []byte
15 Traits traits
16 Offset int
17}
18
19// TokenBuffer is a buffer that allows for token look-ahead.
20type TokenBuffer struct {
21 r *parse.Input
22 l *html.Lexer
23
24 buf []Token
25 pos int
26
27 attrBuffer []*Token
28}
29
30// NewTokenBuffer returns a new TokenBuffer.
31func NewTokenBuffer(r *parse.Input, l *html.Lexer) *TokenBuffer {
32 return &TokenBuffer{
33 r: r,
34 l: l,
35 buf: make([]Token, 0, 8),
36 }
37}
38
39func (z *TokenBuffer) read(t *Token) {
40 t.Offset = z.r.Offset()
41 t.TokenType, t.Data = z.l.Next()
42 t.Text = z.l.Text()
43 if t.TokenType == html.AttributeToken {
44 t.Offset += 1 + len(t.Text) + 1
45 t.AttrVal = z.l.AttrVal()
46 if len(t.AttrVal) > 1 && (t.AttrVal[0] == '"' || t.AttrVal[0] == '\'') {
47 t.Offset++
48 t.AttrVal = t.AttrVal[1 : len(t.AttrVal)-1] // quotes will be readded in attribute loop if necessary
49 }
50 t.Hash = ToHash(t.Text)
51 t.Traits = attrMap[t.Hash]
52 } else if t.TokenType == html.StartTagToken || t.TokenType == html.EndTagToken {
53 t.AttrVal = nil
54 t.Hash = ToHash(t.Text)
55 t.Traits = tagMap[t.Hash] // zero if not exist
56 } else {
57 t.AttrVal = nil
58 t.Hash = 0
59 t.Traits = 0
60 }
61}
62
63// Peek returns the ith element and possibly does an allocation.
64// Peeking past an error will panic.
65func (z *TokenBuffer) Peek(pos int) *Token {
66 pos += z.pos
67 if pos >= len(z.buf) {
68 if len(z.buf) > 0 && z.buf[len(z.buf)-1].TokenType == html.ErrorToken {
69 return &z.buf[len(z.buf)-1]
70 }
71
72 c := cap(z.buf)
73 d := len(z.buf) - z.pos
74 p := pos - z.pos + 1 // required peek length
75 var buf []Token
76 if 2*p > c {
77 buf = make([]Token, 0, 2*c+p)
78 } else {
79 buf = z.buf
80 }
81 copy(buf[:d], z.buf[z.pos:])
82
83 buf = buf[:p]
84 pos -= z.pos
85 for i := d; i < p; i++ {
86 z.read(&buf[i])
87 if buf[i].TokenType == html.ErrorToken {
88 buf = buf[:i+1]
89 pos = i
90 break
91 }
92 }
93 z.pos, z.buf = 0, buf
94 }
95 return &z.buf[pos]
96}
97
98// Shift returns the first element and advances position.
99func (z *TokenBuffer) Shift() *Token {
100 if z.pos >= len(z.buf) {
101 t := &z.buf[:1][0]
102 z.read(t)
103 return t
104 }
105 t := &z.buf[z.pos]
106 z.pos++
107 return t
108}
109
110// Attributes extracts the gives attribute hashes from a tag.
111// It returns in the same order pointers to the requested token data or nil.
112func (z *TokenBuffer) Attributes(hashes ...Hash) []*Token {
113 n := 0
114 for {
115 if t := z.Peek(n); t.TokenType != html.AttributeToken {
116 break
117 }
118 n++
119 }
120 if len(hashes) > cap(z.attrBuffer) {
121 z.attrBuffer = make([]*Token, len(hashes))
122 } else {
123 z.attrBuffer = z.attrBuffer[:len(hashes)]
124 for i := range z.attrBuffer {
125 z.attrBuffer[i] = nil
126 }
127 }
128 for i := z.pos; i < z.pos+n; i++ {
129 attr := &z.buf[i]
130 for j, hash := range hashes {
131 if hash == attr.Hash {
132 z.attrBuffer[j] = attr
133 }
134 }
135 }
136 return z.attrBuffer
137}
diff --git a/vendor/github.com/tdewolff/minify/v2/html/hash.go b/vendor/github.com/tdewolff/minify/v2/html/hash.go
new file mode 100644
index 0000000..3b91cbb
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/html/hash.go
@@ -0,0 +1,543 @@
1package html
2
3// generated by hasher -type=Hash -file=hash.go; DO NOT EDIT, except for adding more constants to the list and rerun go generate
4
5// uses github.com/tdewolff/hasher
6//go:generate hasher -type=Hash -file=hash.go
7
8// Hash defines perfect hashes for a predefined list of strings
9type Hash uint32
10
11// Unique hash definitions to be used instead of strings
12const (
13 A Hash = 0x1 // a
14 Abbr Hash = 0x37a04 // abbr
15 About Hash = 0x5 // about
16 Accept Hash = 0x1106 // accept
17 Accept_Charset Hash = 0x110e // accept-charset
18 Action Hash = 0x23f06 // action
19 Address Hash = 0x5a07 // address
20 Align Hash = 0x32705 // align
21 Alink Hash = 0x7005 // alink
22 Allowfullscreen Hash = 0x2ad0f // allowfullscreen
23 Amp_Boilerplate Hash = 0x610f // amp-boilerplate
24 Area Hash = 0x1e304 // area
25 Article Hash = 0x2707 // article
26 Aside Hash = 0xb405 // aside
27 Async Hash = 0xac05 // async
28 Audio Hash = 0xd105 // audio
29 Autofocus Hash = 0xe409 // autofocus
30 Autoplay Hash = 0x10808 // autoplay
31 Axis Hash = 0x11004 // axis
32 B Hash = 0x101 // b
33 Background Hash = 0x300a // background
34 Base Hash = 0x19604 // base
35 Bb Hash = 0x37b02 // bb
36 Bdi Hash = 0x7503 // bdi
37 Bdo Hash = 0x31f03 // bdo
38 Bgcolor Hash = 0x12607 // bgcolor
39 Blockquote Hash = 0x13e0a // blockquote
40 Body Hash = 0xd04 // body
41 Br Hash = 0x37c02 // br
42 Button Hash = 0x14806 // button
43 Canvas Hash = 0xb006 // canvas
44 Caption Hash = 0x21f07 // caption
45 Charset Hash = 0x1807 // charset
46 Checked Hash = 0x1b307 // checked
47 Cite Hash = 0xfb04 // cite
48 Class Hash = 0x15905 // class
49 Classid Hash = 0x15907 // classid
50 Clear Hash = 0x2b05 // clear
51 Code Hash = 0x19204 // code
52 Codebase Hash = 0x19208 // codebase
53 Codetype Hash = 0x1a408 // codetype
54 Col Hash = 0x12803 // col
55 Colgroup Hash = 0x1bb08 // colgroup
56 Color Hash = 0x12805 // color
57 Cols Hash = 0x1cf04 // cols
58 Colspan Hash = 0x1cf07 // colspan
59 Compact Hash = 0x1ec07 // compact
60 Content Hash = 0x28407 // content
61 Controls Hash = 0x20108 // controls
62 Data Hash = 0x1f04 // data
63 Datalist Hash = 0x1f08 // datalist
64 Datatype Hash = 0x4d08 // datatype
65 Dd Hash = 0x5b02 // dd
66 Declare Hash = 0xb707 // declare
67 Default Hash = 0x7f07 // default
68 DefaultChecked Hash = 0x1730e // defaultChecked
69 DefaultMuted Hash = 0x7f0c // defaultMuted
70 DefaultSelected Hash = 0x8a0f // defaultSelected
71 Defer Hash = 0x9805 // defer
72 Del Hash = 0x10503 // del
73 Details Hash = 0x15f07 // details
74 Dfn Hash = 0x16c03 // dfn
75 Dialog Hash = 0xa606 // dialog
76 Dir Hash = 0x7603 // dir
77 Disabled Hash = 0x18008 // disabled
78 Div Hash = 0x18703 // div
79 Dl Hash = 0x1b902 // dl
80 Dt Hash = 0x23102 // dt
81 Em Hash = 0x4302 // em
82 Embed Hash = 0x4905 // embed
83 Enabled Hash = 0x26c07 // enabled
84 Enctype Hash = 0x1fa07 // enctype
85 Face Hash = 0x5604 // face
86 Fieldset Hash = 0x21408 // fieldset
87 Figcaption Hash = 0x21c0a // figcaption
88 Figure Hash = 0x22606 // figure
89 Footer Hash = 0xdb06 // footer
90 For Hash = 0x23b03 // for
91 Form Hash = 0x23b04 // form
92 Formaction Hash = 0x23b0a // formaction
93 Formnovalidate Hash = 0x2450e // formnovalidate
94 Frame Hash = 0x28c05 // frame
95 Frameborder Hash = 0x28c0b // frameborder
96 H1 Hash = 0x2e002 // h1
97 H2 Hash = 0x25302 // h2
98 H3 Hash = 0x25502 // h3
99 H4 Hash = 0x25702 // h4
100 H5 Hash = 0x25902 // h5
101 H6 Hash = 0x25b02 // h6
102 Head Hash = 0x2d204 // head
103 Header Hash = 0x2d206 // header
104 Hgroup Hash = 0x25d06 // hgroup
105 Hidden Hash = 0x26806 // hidden
106 Hr Hash = 0x32d02 // hr
107 Href Hash = 0x32d04 // href
108 Hreflang Hash = 0x32d08 // hreflang
109 Html Hash = 0x27304 // html
110 Http_Equiv Hash = 0x2770a // http-equiv
111 I Hash = 0x2401 // i
112 Icon Hash = 0x28304 // icon
113 Id Hash = 0xb602 // id
114 Iframe Hash = 0x28b06 // iframe
115 Img Hash = 0x29703 // img
116 Inert Hash = 0xf605 // inert
117 Inlist Hash = 0x29a06 // inlist
118 Input Hash = 0x2a405 // input
119 Ins Hash = 0x2a903 // ins
120 Ismap Hash = 0x11205 // ismap
121 Itemscope Hash = 0xfc09 // itemscope
122 Kbd Hash = 0x7403 // kbd
123 Keygen Hash = 0x1f606 // keygen
124 Label Hash = 0xbe05 // label
125 Lang Hash = 0x33104 // lang
126 Language Hash = 0x33108 // language
127 Legend Hash = 0x2c506 // legend
128 Li Hash = 0x2302 // li
129 Link Hash = 0x7104 // link
130 Longdesc Hash = 0xc208 // longdesc
131 Main Hash = 0xf404 // main
132 Manifest Hash = 0x2bc08 // manifest
133 Map Hash = 0xee03 // map
134 Mark Hash = 0x2cb04 // mark
135 Math Hash = 0x2cf04 // math
136 Max Hash = 0x2d803 // max
137 Maxlength Hash = 0x2d809 // maxlength
138 Media Hash = 0xa405 // media
139 Menu Hash = 0x12204 // menu
140 Meta Hash = 0x2e204 // meta
141 Meter Hash = 0x2f705 // meter
142 Method Hash = 0x2fc06 // method
143 Multiple Hash = 0x30208 // multiple
144 Muted Hash = 0x30a05 // muted
145 Name Hash = 0xa204 // name
146 Nav Hash = 0x32403 // nav
147 Nohref Hash = 0x32b06 // nohref
148 Noresize Hash = 0x13608 // noresize
149 Noscript Hash = 0x14d08 // noscript
150 Noshade Hash = 0x16e07 // noshade
151 Novalidate Hash = 0x2490a // novalidate
152 Nowrap Hash = 0x1d506 // nowrap
153 Object Hash = 0xd506 // object
154 Ol Hash = 0xcb02 // ol
155 Open Hash = 0x32104 // open
156 Optgroup Hash = 0x35608 // optgroup
157 Option Hash = 0x30f06 // option
158 Output Hash = 0x206 // output
159 P Hash = 0x501 // p
160 Param Hash = 0xf005 // param
161 Pauseonexit Hash = 0x1160b // pauseonexit
162 Picture Hash = 0x1c207 // picture
163 Plaintext Hash = 0x1da09 // plaintext
164 Poster Hash = 0x26206 // poster
165 Pre Hash = 0x35d03 // pre
166 Prefix Hash = 0x35d06 // prefix
167 Profile Hash = 0x36407 // profile
168 Progress Hash = 0x34208 // progress
169 Property Hash = 0x31508 // property
170 Q Hash = 0x14301 // q
171 Rb Hash = 0x2f02 // rb
172 Readonly Hash = 0x1e408 // readonly
173 Rel Hash = 0xbc03 // rel
174 Required Hash = 0x22a08 // required
175 Resource Hash = 0x1c708 // resource
176 Rev Hash = 0x7803 // rev
177 Reversed Hash = 0x7808 // reversed
178 Rows Hash = 0x9c04 // rows
179 Rowspan Hash = 0x9c07 // rowspan
180 Rp Hash = 0x6a02 // rp
181 Rt Hash = 0x2802 // rt
182 Rtc Hash = 0xf903 // rtc
183 Ruby Hash = 0xe004 // ruby
184 Rules Hash = 0x12c05 // rules
185 S Hash = 0x1c01 // s
186 Samp Hash = 0x6004 // samp
187 Scope Hash = 0x10005 // scope
188 Scoped Hash = 0x10006 // scoped
189 Script Hash = 0x14f06 // script
190 Scrolling Hash = 0xc809 // scrolling
191 Seamless Hash = 0x19808 // seamless
192 Section Hash = 0x13007 // section
193 Select Hash = 0x16506 // select
194 Selected Hash = 0x16508 // selected
195 Shape Hash = 0x19f05 // shape
196 Size Hash = 0x13a04 // size
197 Slot Hash = 0x20804 // slot
198 Small Hash = 0x2ab05 // small
199 Sortable Hash = 0x2ef08 // sortable
200 Source Hash = 0x1c906 // source
201 Span Hash = 0x9f04 // span
202 Src Hash = 0x34903 // src
203 Srcset Hash = 0x34906 // srcset
204 Start Hash = 0x2505 // start
205 Strong Hash = 0x29e06 // strong
206 Style Hash = 0x2c205 // style
207 Sub Hash = 0x31d03 // sub
208 Summary Hash = 0x33907 // summary
209 Sup Hash = 0x34003 // sup
210 Svg Hash = 0x34f03 // svg
211 Tabindex Hash = 0x2e408 // tabindex
212 Table Hash = 0x2f205 // table
213 Target Hash = 0x706 // target
214 Tbody Hash = 0xc05 // tbody
215 Td Hash = 0x1e02 // td
216 Template Hash = 0x4208 // template
217 Text Hash = 0x1df04 // text
218 Textarea Hash = 0x1df08 // textarea
219 Tfoot Hash = 0xda05 // tfoot
220 Th Hash = 0x2d102 // th
221 Thead Hash = 0x2d105 // thead
222 Time Hash = 0x12004 // time
223 Title Hash = 0x15405 // title
224 Tr Hash = 0x1f202 // tr
225 Track Hash = 0x1f205 // track
226 Translate Hash = 0x20b09 // translate
227 Truespeed Hash = 0x23209 // truespeed
228 Type Hash = 0x5104 // type
229 Typemustmatch Hash = 0x1a80d // typemustmatch
230 Typeof Hash = 0x5106 // typeof
231 U Hash = 0x301 // u
232 Ul Hash = 0x8302 // ul
233 Undeterminate Hash = 0x370d // undeterminate
234 Usemap Hash = 0xeb06 // usemap
235 Valign Hash = 0x32606 // valign
236 Value Hash = 0x18905 // value
237 Valuetype Hash = 0x18909 // valuetype
238 Var Hash = 0x28003 // var
239 Video Hash = 0x35205 // video
240 Visible Hash = 0x36b07 // visible
241 Vlink Hash = 0x37205 // vlink
242 Vocab Hash = 0x37705 // vocab
243 Wbr Hash = 0x37e03 // wbr
244 Xmlns Hash = 0x2eb05 // xmlns
245 Xmp Hash = 0x36203 // xmp
246)
247
248// String returns the hash' name.
249func (i Hash) String() string {
250 start := uint32(i >> 8)
251 n := uint32(i & 0xff)
252 if start+n > uint32(len(_Hash_text)) {
253 return ""
254 }
255 return _Hash_text[start : start+n]
256}
257
258// ToHash returns the hash whose name is s. It returns zero if there is no
259// such hash. It is case sensitive.
260func ToHash(s []byte) Hash {
261 if len(s) == 0 || len(s) > _Hash_maxLen {
262 return 0
263 }
264 h := uint32(_Hash_hash0)
265 for i := 0; i < len(s); i++ {
266 h ^= uint32(s[i])
267 h *= 16777619
268 }
269 if i := _Hash_table[h&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) {
270 t := _Hash_text[i>>8 : i>>8+i&0xff]
271 for i := 0; i < len(s); i++ {
272 if t[i] != s[i] {
273 goto NEXT
274 }
275 }
276 return i
277 }
278NEXT:
279 if i := _Hash_table[(h>>16)&uint32(len(_Hash_table)-1)]; int(i&0xff) == len(s) {
280 t := _Hash_text[i>>8 : i>>8+i&0xff]
281 for i := 0; i < len(s); i++ {
282 if t[i] != s[i] {
283 return 0
284 }
285 }
286 return i
287 }
288 return 0
289}
290
291const _Hash_hash0 = 0x9acb0442
292const _Hash_maxLen = 15
293const _Hash_text = "aboutputargetbodyaccept-charsetdatalistarticlearbackgroundet" +
294 "erminatemplatembedatatypeofaceaddressamp-boilerplatealinkbdi" +
295 "reversedefaultMutedefaultSelectedeferowspanamedialogasyncanv" +
296 "asideclarelabelongdescrollingaudiobjectfooterubyautofocusema" +
297 "paramainertcitemscopedelautoplayaxismapauseonexitimenubgcolo" +
298 "rulesectionoresizeblockquotebuttonoscriptitleclassidetailsel" +
299 "ectedfnoshadefaultCheckedisabledivaluetypecodebaseamlesshape" +
300 "codetypemustmatcheckedlcolgroupicturesourcecolspanowraplaint" +
301 "extareadonlycompactrackeygenctypecontrolslotranslatefieldset" +
302 "figcaptionfigurequiredtruespeedformactionformnovalidateh2h3h" +
303 "4h5h6hgrouposterhiddenabledhtmlhttp-equivaricontentiframebor" +
304 "derimginlistronginputinsmallowfullscreenmanifestylegendmarkm" +
305 "atheadermaxlength1metabindexmlnsortablemetermethodmultiplemu" +
306 "tedoptionpropertysubdopenavalignohreflanguagesummarysuprogre" +
307 "ssrcsetsvgvideoptgrouprefixmprofilevisiblevlinkvocabbrwbr"
308
309var _Hash_table = [1 << 9]Hash{
310 0x0: 0x1df08, // textarea
311 0x4: 0x32d02, // hr
312 0x8: 0x1c207, // picture
313 0xb: 0x18905, // value
314 0xf: 0x2e408, // tabindex
315 0x12: 0x15905, // class
316 0x15: 0x37e03, // wbr
317 0x18: 0x1a80d, // typemustmatch
318 0x1a: 0x1b902, // dl
319 0x1d: 0xf903, // rtc
320 0x1e: 0x25702, // h4
321 0x22: 0x2ef08, // sortable
322 0x24: 0x4208, // template
323 0x25: 0x28c0b, // frameborder
324 0x28: 0x37a04, // abbr
325 0x29: 0x28b06, // iframe
326 0x2a: 0x610f, // amp-boilerplate
327 0x2c: 0x1e408, // readonly
328 0x30: 0x23f06, // action
329 0x33: 0x28c05, // frame
330 0x35: 0x12c05, // rules
331 0x36: 0x30208, // multiple
332 0x38: 0x31f03, // bdo
333 0x39: 0x1d506, // nowrap
334 0x3e: 0x21408, // fieldset
335 0x3f: 0x7503, // bdi
336 0x46: 0x7f0c, // defaultMuted
337 0x49: 0x35205, // video
338 0x4c: 0x19808, // seamless
339 0x4d: 0x13608, // noresize
340 0x4f: 0xb602, // id
341 0x51: 0x25d06, // hgroup
342 0x52: 0x23102, // dt
343 0x55: 0x12805, // color
344 0x56: 0x34003, // sup
345 0x59: 0x370d, // undeterminate
346 0x5a: 0x35608, // optgroup
347 0x5b: 0x2d206, // header
348 0x5c: 0xb405, // aside
349 0x5f: 0x10005, // scope
350 0x60: 0x101, // b
351 0x61: 0xcb02, // ol
352 0x64: 0x32b06, // nohref
353 0x65: 0x1da09, // plaintext
354 0x66: 0x20804, // slot
355 0x67: 0x11004, // axis
356 0x68: 0x12803, // col
357 0x69: 0x32606, // valign
358 0x6c: 0x2d105, // thead
359 0x70: 0x34906, // srcset
360 0x71: 0x26806, // hidden
361 0x76: 0x1bb08, // colgroup
362 0x78: 0x34f03, // svg
363 0x7b: 0x2cb04, // mark
364 0x7e: 0x33104, // lang
365 0x81: 0x1cf04, // cols
366 0x86: 0x5a07, // address
367 0x8b: 0xf404, // main
368 0x8c: 0x4302, // em
369 0x8f: 0x32d08, // hreflang
370 0x93: 0x1b307, // checked
371 0x94: 0x25902, // h5
372 0x95: 0x301, // u
373 0x96: 0x32705, // align
374 0x97: 0x14301, // q
375 0x99: 0xd506, // object
376 0x9b: 0x28407, // content
377 0x9d: 0xc809, // scrolling
378 0x9f: 0x36407, // profile
379 0xa0: 0x34903, // src
380 0xa1: 0xda05, // tfoot
381 0xa3: 0x2f705, // meter
382 0xa4: 0x37705, // vocab
383 0xa6: 0xd04, // body
384 0xa8: 0x19204, // code
385 0xac: 0x20108, // controls
386 0xb0: 0x2ab05, // small
387 0xb1: 0x18008, // disabled
388 0xb5: 0x5604, // face
389 0xb6: 0x501, // p
390 0xb9: 0x2302, // li
391 0xbb: 0xe409, // autofocus
392 0xbf: 0x27304, // html
393 0xc2: 0x4d08, // datatype
394 0xc6: 0x35d06, // prefix
395 0xcb: 0x35d03, // pre
396 0xcc: 0x1106, // accept
397 0xd1: 0x23b03, // for
398 0xd5: 0x29e06, // strong
399 0xd6: 0x9c07, // rowspan
400 0xd7: 0x25502, // h3
401 0xd8: 0x2cf04, // math
402 0xde: 0x16e07, // noshade
403 0xdf: 0x19f05, // shape
404 0xe1: 0x10006, // scoped
405 0xe3: 0x706, // target
406 0xe6: 0x21c0a, // figcaption
407 0xe9: 0x1df04, // text
408 0xea: 0x1c708, // resource
409 0xec: 0xee03, // map
410 0xf0: 0x29a06, // inlist
411 0xf1: 0x16506, // select
412 0xf2: 0x1f606, // keygen
413 0xf3: 0x5106, // typeof
414 0xf6: 0xb006, // canvas
415 0xf7: 0x30f06, // option
416 0xf8: 0xbe05, // label
417 0xf9: 0xbc03, // rel
418 0xfb: 0x1f04, // data
419 0xfd: 0x6004, // samp
420 0x100: 0x110e, // accept-charset
421 0x101: 0xeb06, // usemap
422 0x103: 0x2bc08, // manifest
423 0x109: 0xa204, // name
424 0x10a: 0x14806, // button
425 0x10b: 0x2b05, // clear
426 0x10e: 0x33907, // summary
427 0x10f: 0x2e204, // meta
428 0x110: 0x33108, // language
429 0x112: 0x300a, // background
430 0x113: 0x2707, // article
431 0x116: 0x23b0a, // formaction
432 0x119: 0x1, // a
433 0x11b: 0x5, // about
434 0x11c: 0xfc09, // itemscope
435 0x11e: 0x14d08, // noscript
436 0x11f: 0x15907, // classid
437 0x120: 0x36203, // xmp
438 0x121: 0x19604, // base
439 0x123: 0x1c01, // s
440 0x124: 0x36b07, // visible
441 0x126: 0x37b02, // bb
442 0x127: 0x9c04, // rows
443 0x12d: 0x2450e, // formnovalidate
444 0x131: 0x1f205, // track
445 0x135: 0x18703, // div
446 0x136: 0xac05, // async
447 0x137: 0x31508, // property
448 0x13a: 0x16c03, // dfn
449 0x13e: 0xf605, // inert
450 0x142: 0x10503, // del
451 0x144: 0x25302, // h2
452 0x147: 0x2c205, // style
453 0x149: 0x29703, // img
454 0x14a: 0xc05, // tbody
455 0x14b: 0x7603, // dir
456 0x14c: 0x2eb05, // xmlns
457 0x14e: 0x1f08, // datalist
458 0x14f: 0x32d04, // href
459 0x150: 0x1f202, // tr
460 0x151: 0x13e0a, // blockquote
461 0x152: 0x18909, // valuetype
462 0x155: 0xdb06, // footer
463 0x157: 0x14f06, // script
464 0x158: 0x1cf07, // colspan
465 0x15d: 0x1730e, // defaultChecked
466 0x15f: 0x2490a, // novalidate
467 0x164: 0x1a408, // codetype
468 0x165: 0x2c506, // legend
469 0x16b: 0x1160b, // pauseonexit
470 0x16c: 0x21f07, // caption
471 0x16f: 0x26c07, // enabled
472 0x173: 0x26206, // poster
473 0x175: 0x30a05, // muted
474 0x176: 0x11205, // ismap
475 0x178: 0x2a903, // ins
476 0x17a: 0xe004, // ruby
477 0x17b: 0x37c02, // br
478 0x17c: 0x8a0f, // defaultSelected
479 0x17d: 0x7403, // kbd
480 0x17f: 0x1c906, // source
481 0x182: 0x9f04, // span
482 0x184: 0x2d803, // max
483 0x18a: 0x5b02, // dd
484 0x18b: 0x13a04, // size
485 0x18c: 0xa405, // media
486 0x18d: 0x19208, // codebase
487 0x18f: 0x4905, // embed
488 0x192: 0x5104, // type
489 0x193: 0xf005, // param
490 0x194: 0x25b02, // h6
491 0x197: 0x28304, // icon
492 0x198: 0x12607, // bgcolor
493 0x199: 0x2ad0f, // allowfullscreen
494 0x19a: 0x12004, // time
495 0x19b: 0x7803, // rev
496 0x19d: 0x34208, // progress
497 0x19e: 0x22606, // figure
498 0x1a0: 0x6a02, // rp
499 0x1a2: 0xa606, // dialog
500 0x1a4: 0x2802, // rt
501 0x1a7: 0x1e304, // area
502 0x1a8: 0x7808, // reversed
503 0x1aa: 0x32104, // open
504 0x1ac: 0x2d204, // head
505 0x1ad: 0x7005, // alink
506 0x1af: 0x28003, // var
507 0x1b0: 0x15f07, // details
508 0x1b1: 0x2401, // i
509 0x1b3: 0x1e02, // td
510 0x1b4: 0xb707, // declare
511 0x1b5: 0x8302, // ul
512 0x1ba: 0x2fc06, // method
513 0x1bd: 0x13007, // section
514 0x1be: 0x22a08, // required
515 0x1c2: 0x9805, // defer
516 0x1c3: 0x37205, // vlink
517 0x1c4: 0x15405, // title
518 0x1c5: 0x2770a, // http-equiv
519 0x1c6: 0x1fa07, // enctype
520 0x1c7: 0x1ec07, // compact
521 0x1c8: 0x2d809, // maxlength
522 0x1c9: 0x16508, // selected
523 0x1cc: 0xd105, // audio
524 0x1cd: 0xc208, // longdesc
525 0x1d1: 0xfb04, // cite
526 0x1da: 0x2505, // start
527 0x1de: 0x2d102, // th
528 0x1df: 0x10808, // autoplay
529 0x1e2: 0x7104, // link
530 0x1e3: 0x206, // output
531 0x1e5: 0x12204, // menu
532 0x1e6: 0x2a405, // input
533 0x1eb: 0x32403, // nav
534 0x1ec: 0x31d03, // sub
535 0x1ee: 0x1807, // charset
536 0x1ef: 0x7f07, // default
537 0x1f3: 0x2f205, // table
538 0x1f4: 0x23b04, // form
539 0x1f5: 0x23209, // truespeed
540 0x1f6: 0x2f02, // rb
541 0x1fb: 0x20b09, // translate
542 0x1fd: 0x2e002, // h1
543}
diff --git a/vendor/github.com/tdewolff/minify/v2/html/html.go b/vendor/github.com/tdewolff/minify/v2/html/html.go
new file mode 100644
index 0000000..616a9ba
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/html/html.go
@@ -0,0 +1,514 @@
1// Package html minifies HTML5 following the specifications at http://www.w3.org/TR/html5/syntax.html.
2package html
3
4import (
5 "bytes"
6 "io"
7
8 "github.com/tdewolff/minify/v2"
9 "github.com/tdewolff/parse/v2"
10 "github.com/tdewolff/parse/v2/buffer"
11 "github.com/tdewolff/parse/v2/html"
12)
13
14var (
15 gtBytes = []byte(">")
16 isBytes = []byte("=")
17 spaceBytes = []byte(" ")
18 doctypeBytes = []byte("<!doctype html>")
19 jsMimeBytes = []byte("application/javascript")
20 cssMimeBytes = []byte("text/css")
21 htmlMimeBytes = []byte("text/html")
22 svgMimeBytes = []byte("image/svg+xml")
23 formMimeBytes = []byte("application/x-www-form-urlencoded")
24 mathMimeBytes = []byte("application/mathml+xml")
25 dataSchemeBytes = []byte("data:")
26 jsSchemeBytes = []byte("javascript:")
27 httpBytes = []byte("http")
28 radioBytes = []byte("radio")
29 onBytes = []byte("on")
30 textBytes = []byte("text")
31 noneBytes = []byte("none")
32 submitBytes = []byte("submit")
33 allBytes = []byte("all")
34 rectBytes = []byte("rect")
35 dataBytes = []byte("data")
36 getBytes = []byte("get")
37 autoBytes = []byte("auto")
38 oneBytes = []byte("one")
39 inlineParams = map[string]string{"inline": "1"}
40)
41
42////////////////////////////////////////////////////////////////
43
44// Minifier is an HTML minifier.
45type Minifier struct {
46 KeepComments bool
47 KeepConditionalComments bool
48 KeepDefaultAttrVals bool
49 KeepDocumentTags bool
50 KeepEndTags bool
51 KeepQuotes bool
52 KeepWhitespace bool
53}
54
55// Minify minifies HTML data, it reads from r and writes to w.
56func Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
57 return (&Minifier{}).Minify(m, w, r, params)
58}
59
60// Minify minifies HTML data, it reads from r and writes to w.
61func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]string) error {
62 var rawTagHash Hash
63 var rawTagMediatype []byte
64
65 omitSpace := true // if true the next leading space is omitted
66 inPre := false
67
68 attrMinifyBuffer := buffer.NewWriter(make([]byte, 0, 64))
69 attrByteBuffer := make([]byte, 0, 64)
70
71 z := parse.NewInput(r)
72 defer z.Restore()
73
74 l := html.NewLexer(z)
75 tb := NewTokenBuffer(z, l)
76 for {
77 t := *tb.Shift()
78 switch t.TokenType {
79 case html.ErrorToken:
80 if _, err := w.Write(nil); err != nil {
81 return err
82 }
83 if l.Err() == io.EOF {
84 return nil
85 }
86 return l.Err()
87 case html.DoctypeToken:
88 w.Write(doctypeBytes)
89 case html.CommentToken:
90 if o.KeepComments {
91 w.Write(t.Data)
92 } else if o.KeepConditionalComments && 6 < len(t.Text) && (bytes.HasPrefix(t.Text, []byte("[if ")) || bytes.HasSuffix(t.Text, []byte("[endif]")) || bytes.HasSuffix(t.Text, []byte("[endif]--"))) {
93 // [if ...] is always 7 or more characters, [endif] is only encountered for downlevel-revealed
94 // see https://msdn.microsoft.com/en-us/library/ms537512(v=vs.85).aspx#syntax
95 if bytes.HasPrefix(t.Data, []byte("<!--[if ")) && bytes.HasSuffix(t.Data, []byte("<![endif]-->")) { // downlevel-hidden
96 begin := bytes.IndexByte(t.Data, '>') + 1
97 end := len(t.Data) - len("<![endif]-->")
98 w.Write(t.Data[:begin])
99 if err := o.Minify(m, w, buffer.NewReader(t.Data[begin:end]), nil); err != nil {
100 return minify.UpdateErrorPosition(err, z, t.Offset)
101 }
102 w.Write(t.Data[end:])
103 } else {
104 w.Write(t.Data) // downlevel-revealed or short downlevel-hidden
105 }
106 } else if 1 < len(t.Text) && t.Text[0] == '#' {
107 // SSI tags
108 w.Write(t.Data)
109 }
110 case html.SvgToken:
111 if err := m.MinifyMimetype(svgMimeBytes, w, buffer.NewReader(t.Data), nil); err != nil {
112 if err != minify.ErrNotExist {
113 return minify.UpdateErrorPosition(err, z, t.Offset)
114 }
115 w.Write(t.Data)
116 }
117 case html.MathToken:
118 if err := m.MinifyMimetype(mathMimeBytes, w, buffer.NewReader(t.Data), nil); err != nil {
119 if err != minify.ErrNotExist {
120 return minify.UpdateErrorPosition(err, z, t.Offset)
121 }
122 w.Write(t.Data)
123 }
124 case html.TextToken:
125 // CSS and JS minifiers for inline code
126 if rawTagHash != 0 {
127 if rawTagHash == Style || rawTagHash == Script || rawTagHash == Iframe {
128 var mimetype []byte
129 var params map[string]string
130 if rawTagHash == Iframe {
131 mimetype = htmlMimeBytes
132 } else if len(rawTagMediatype) > 0 {
133 mimetype, params = parse.Mediatype(rawTagMediatype)
134 } else if rawTagHash == Script {
135 mimetype = jsMimeBytes
136 } else if rawTagHash == Style {
137 mimetype = cssMimeBytes
138 }
139 if err := m.MinifyMimetype(mimetype, w, buffer.NewReader(t.Data), params); err != nil {
140 if err != minify.ErrNotExist {
141 return minify.UpdateErrorPosition(err, z, t.Offset)
142 }
143 w.Write(t.Data)
144 }
145 } else {
146 w.Write(t.Data)
147 }
148 } else if inPre {
149 w.Write(t.Data)
150 } else {
151 t.Data = parse.ReplaceMultipleWhitespaceAndEntities(t.Data, EntitiesMap, TextRevEntitiesMap)
152
153 // whitespace removal; trim left
154 if omitSpace && parse.IsWhitespace(t.Data[0]) {
155 t.Data = t.Data[1:]
156 }
157
158 // whitespace removal; trim right
159 omitSpace = false
160 if len(t.Data) == 0 {
161 omitSpace = true
162 } else if parse.IsWhitespace(t.Data[len(t.Data)-1]) {
163 omitSpace = true
164 i := 0
165 for {
166 next := tb.Peek(i)
167 // trim if EOF, text token with leading whitespace or block token
168 if next.TokenType == html.ErrorToken {
169 t.Data = t.Data[:len(t.Data)-1]
170 omitSpace = false
171 break
172 } else if next.TokenType == html.TextToken {
173 // this only happens when a comment, doctype or phrasing end tag (only for !o.KeepWhitespace) was in between
174 // remove if the text token starts with a whitespace
175 if len(next.Data) > 0 && parse.IsWhitespace(next.Data[0]) {
176 t.Data = t.Data[:len(t.Data)-1]
177 omitSpace = false
178 }
179 break
180 } else if next.TokenType == html.StartTagToken || next.TokenType == html.EndTagToken {
181 if o.KeepWhitespace {
182 break
183 }
184 // remove when followed up by a block tag
185 if next.Traits&nonPhrasingTag != 0 {
186 t.Data = t.Data[:len(t.Data)-1]
187 omitSpace = false
188 break
189 } else if next.TokenType == html.StartTagToken {
190 break
191 }
192 }
193 i++
194 }
195 }
196
197 w.Write(t.Data)
198 }
199 case html.StartTagToken, html.EndTagToken:
200 rawTagHash = 0
201 hasAttributes := false
202 if t.TokenType == html.StartTagToken {
203 if next := tb.Peek(0); next.TokenType == html.AttributeToken {
204 hasAttributes = true
205 }
206 if t.Traits&rawTag != 0 {
207 // ignore empty script and style tags
208 if !hasAttributes && (t.Hash == Script || t.Hash == Style) {
209 if next := tb.Peek(1); next.TokenType == html.EndTagToken {
210 tb.Shift()
211 tb.Shift()
212 break
213 }
214 }
215 rawTagHash = t.Hash
216 rawTagMediatype = nil
217
218 // do not minify content of <style amp-boilerplate>
219 if hasAttributes && t.Hash == Style {
220 if attrs := tb.Attributes(Amp_Boilerplate); attrs[0] != nil {
221 rawTagHash = 0
222 }
223 }
224 }
225 } else if t.Hash == Template {
226 omitSpace = true // EndTagToken
227 }
228
229 if t.Hash == Pre {
230 inPre = t.TokenType == html.StartTagToken
231 }
232
233 // remove superfluous tags, except for html, head and body tags when KeepDocumentTags is set
234 if !hasAttributes && (!o.KeepDocumentTags && (t.Hash == Html || t.Hash == Head || t.Hash == Body) || t.Hash == Colgroup) {
235 break
236 } else if t.TokenType == html.EndTagToken {
237 omitEndTag := false
238 if !o.KeepEndTags {
239 if t.Hash == Thead || t.Hash == Tbody || t.Hash == Tfoot || t.Hash == Tr || t.Hash == Th ||
240 t.Hash == Td || t.Hash == Option || t.Hash == Dd || t.Hash == Dt || t.Hash == Li ||
241 t.Hash == Rb || t.Hash == Rt || t.Hash == Rtc || t.Hash == Rp {
242 omitEndTag = true // omit end tags
243 } else if t.Hash == P {
244 i := 0
245 for {
246 next := tb.Peek(i)
247 i++
248 // continue if text token is empty or whitespace
249 if next.TokenType == html.TextToken && parse.IsAllWhitespace(next.Data) {
250 continue
251 }
252 if next.TokenType == html.ErrorToken || next.TokenType == html.EndTagToken && next.Traits&keepPTag == 0 || next.TokenType == html.StartTagToken && next.Traits&omitPTag != 0 {
253 omitEndTag = true // omit p end tag
254 }
255 break
256 }
257 } else if t.Hash == Optgroup {
258 i := 0
259 for {
260 next := tb.Peek(i)
261 i++
262 // continue if text token
263 if next.TokenType == html.TextToken {
264 continue
265 }
266 if next.TokenType == html.ErrorToken || next.Hash != Option {
267 omitEndTag = true // omit optgroup end tag
268 }
269 break
270 }
271 }
272 }
273
274 if t.Traits&nonPhrasingTag != 0 {
275 omitSpace = true // omit spaces after block elements
276 } else if o.KeepWhitespace || t.Traits&objectTag != 0 {
277 omitSpace = false
278 }
279
280 if !omitEndTag {
281 if len(t.Data) > 3+len(t.Text) {
282 t.Data[2+len(t.Text)] = '>'
283 t.Data = t.Data[:3+len(t.Text)]
284 }
285 w.Write(t.Data)
286 }
287
288 // skip text in select and optgroup tags
289 if t.Hash == Option || t.Hash == Optgroup {
290 if next := tb.Peek(0); next.TokenType == html.TextToken {
291 tb.Shift()
292 }
293 }
294 break
295 }
296
297 if o.KeepWhitespace || t.Traits&objectTag != 0 {
298 omitSpace = false
299 } else if t.Traits&nonPhrasingTag != 0 {
300 omitSpace = true // omit spaces after block elements
301 }
302
303 w.Write(t.Data)
304
305 if hasAttributes {
306 if t.Hash == Meta {
307 attrs := tb.Attributes(Content, Http_Equiv, Charset, Name)
308 if content := attrs[0]; content != nil {
309 if httpEquiv := attrs[1]; httpEquiv != nil {
310 httpEquiv.AttrVal = parse.TrimWhitespace(httpEquiv.AttrVal)
311 if charset := attrs[2]; charset == nil && parse.EqualFold(httpEquiv.AttrVal, []byte("content-type")) {
312 content.AttrVal = minify.Mediatype(content.AttrVal)
313 if bytes.Equal(content.AttrVal, []byte("text/html;charset=utf-8")) {
314 httpEquiv.Text = nil
315 content.Text = []byte("charset")
316 content.Hash = Charset
317 content.AttrVal = []byte("utf-8")
318 }
319 }
320 }
321 if name := attrs[3]; name != nil {
322 name.AttrVal = parse.TrimWhitespace(name.AttrVal)
323 if parse.EqualFold(name.AttrVal, []byte("keywords")) {
324 content.AttrVal = bytes.ReplaceAll(content.AttrVal, []byte(", "), []byte(","))
325 } else if parse.EqualFold(name.AttrVal, []byte("viewport")) {
326 content.AttrVal = bytes.ReplaceAll(content.AttrVal, []byte(" "), []byte(""))
327 for i := 0; i < len(content.AttrVal); i++ {
328 if content.AttrVal[i] == '=' && i+2 < len(content.AttrVal) {
329 i++
330 if n := parse.Number(content.AttrVal[i:]); n > 0 {
331 minNum := minify.Number(content.AttrVal[i:i+n], -1)
332 if len(minNum) < n {
333 copy(content.AttrVal[i:i+len(minNum)], minNum)
334 copy(content.AttrVal[i+len(minNum):], content.AttrVal[i+n:])
335 content.AttrVal = content.AttrVal[:len(content.AttrVal)+len(minNum)-n]
336 }
337 i += len(minNum)
338 }
339 i-- // mitigate for-loop increase
340 }
341 }
342 }
343 }
344 }
345 } else if t.Hash == Script {
346 attrs := tb.Attributes(Src, Charset)
347 if attrs[0] != nil && attrs[1] != nil {
348 attrs[1].Text = nil
349 }
350 } else if t.Hash == Input {
351 attrs := tb.Attributes(Type, Value)
352 if t, value := attrs[0], attrs[1]; t != nil && value != nil {
353 isRadio := parse.EqualFold(t.AttrVal, radioBytes)
354 if !isRadio && len(value.AttrVal) == 0 {
355 value.Text = nil
356 } else if isRadio && parse.EqualFold(value.AttrVal, onBytes) {
357 value.Text = nil
358 }
359 }
360 } else if t.Hash == A {
361 attrs := tb.Attributes(Id, Name)
362 if id, name := attrs[0], attrs[1]; id != nil && name != nil {
363 if bytes.Equal(id.AttrVal, name.AttrVal) {
364 name.Text = nil
365 }
366 }
367 }
368
369 // write attributes
370 for {
371 attr := *tb.Shift()
372 if attr.TokenType != html.AttributeToken {
373 break
374 } else if attr.Text == nil {
375 continue // removed attribute
376 }
377
378 val := attr.AttrVal
379 if attr.Traits&trimAttr != 0 {
380 val = parse.ReplaceMultipleWhitespaceAndEntities(val, EntitiesMap, nil)
381 val = parse.TrimWhitespace(val)
382 } else {
383 val = parse.ReplaceEntities(val, EntitiesMap, nil)
384 }
385 if t.Traits != 0 {
386 if len(val) == 0 && (attr.Hash == Class ||
387 attr.Hash == Dir ||
388 attr.Hash == Id ||
389 attr.Hash == Name ||
390 attr.Hash == Action && t.Hash == Form) {
391 continue // omit empty attribute values
392 }
393 if attr.Traits&caselessAttr != 0 {
394 val = parse.ToLower(val)
395 }
396 if rawTagHash != 0 && attr.Hash == Type {
397 rawTagMediatype = parse.Copy(val)
398 }
399
400 if attr.Hash == Enctype || attr.Hash == Codetype || attr.Hash == Accept || attr.Hash == Type && (t.Hash == A || t.Hash == Link || t.Hash == Embed || t.Hash == Object || t.Hash == Source || t.Hash == Script || t.Hash == Style) {
401 val = minify.Mediatype(val)
402 }
403
404 // default attribute values can be omitted
405 if !o.KeepDefaultAttrVals && (attr.Hash == Type && (t.Hash == Script && jsMimetypes[string(val)] ||
406 t.Hash == Style && bytes.Equal(val, cssMimeBytes) ||
407 t.Hash == Link && bytes.Equal(val, cssMimeBytes) ||
408 t.Hash == Input && bytes.Equal(val, textBytes) ||
409 t.Hash == Button && bytes.Equal(val, submitBytes)) ||
410 attr.Hash == Language && t.Hash == Script ||
411 attr.Hash == Method && bytes.Equal(val, getBytes) ||
412 attr.Hash == Enctype && bytes.Equal(val, formMimeBytes) ||
413 attr.Hash == Colspan && bytes.Equal(val, oneBytes) ||
414 attr.Hash == Rowspan && bytes.Equal(val, oneBytes) ||
415 attr.Hash == Shape && bytes.Equal(val, rectBytes) ||
416 attr.Hash == Span && bytes.Equal(val, oneBytes) ||
417 attr.Hash == Clear && bytes.Equal(val, noneBytes) ||
418 attr.Hash == Frameborder && bytes.Equal(val, oneBytes) ||
419 attr.Hash == Scrolling && bytes.Equal(val, autoBytes) ||
420 attr.Hash == Valuetype && bytes.Equal(val, dataBytes) ||
421 attr.Hash == Media && t.Hash == Style && bytes.Equal(val, allBytes)) {
422 continue
423 }
424
425 if attr.Hash == Style {
426 // CSS minifier for attribute inline code
427 val = parse.TrimWhitespace(val)
428 attrMinifyBuffer.Reset()
429 if err := m.MinifyMimetype(cssMimeBytes, attrMinifyBuffer, buffer.NewReader(val), inlineParams); err == nil {
430 val = attrMinifyBuffer.Bytes()
431 } else if err != minify.ErrNotExist {
432 return minify.UpdateErrorPosition(err, z, attr.Offset)
433 }
434 if len(val) == 0 {
435 continue
436 }
437 } else if len(attr.Text) > 2 && attr.Text[0] == 'o' && attr.Text[1] == 'n' {
438 // JS minifier for attribute inline code
439 val = parse.TrimWhitespace(val)
440 if len(val) >= 11 && parse.EqualFold(val[:11], jsSchemeBytes) {
441 val = val[11:]
442 }
443 attrMinifyBuffer.Reset()
444 if err := m.MinifyMimetype(jsMimeBytes, attrMinifyBuffer, buffer.NewReader(val), nil); err == nil {
445 val = attrMinifyBuffer.Bytes()
446 } else if err != minify.ErrNotExist {
447 return minify.UpdateErrorPosition(err, z, attr.Offset)
448 }
449 if len(val) == 0 {
450 continue
451 }
452 } else if attr.Traits&urlAttr != 0 { // anchors are already handled
453 val = parse.TrimWhitespace(val)
454 if 5 < len(val) {
455 if parse.EqualFold(val[:4], httpBytes) {
456 if val[4] == ':' {
457 if m.URL != nil && m.URL.Scheme == "http" {
458 val = val[5:]
459 } else {
460 parse.ToLower(val[:4])
461 }
462 } else if (val[4] == 's' || val[4] == 'S') && val[5] == ':' {
463 if m.URL != nil && m.URL.Scheme == "https" {
464 val = val[6:]
465 } else {
466 parse.ToLower(val[:5])
467 }
468 }
469 } else if parse.EqualFold(val[:5], dataSchemeBytes) {
470 val = minify.DataURI(m, val)
471 }
472 }
473 }
474 }
475
476 w.Write(spaceBytes)
477 w.Write(attr.Text)
478 if len(val) > 0 && attr.Traits&booleanAttr == 0 {
479 w.Write(isBytes)
480
481 // use double quotes for RDFa attributes
482 isXML := attr.Hash == Vocab || attr.Hash == Typeof || attr.Hash == Property || attr.Hash == Resource || attr.Hash == Prefix || attr.Hash == Content || attr.Hash == About || attr.Hash == Rev || attr.Hash == Datatype || attr.Hash == Inlist
483
484 // no quotes if possible, else prefer single or double depending on which occurs more often in value
485 var quote byte
486
487 if 0 < len(attr.Data) && (attr.Data[len(attr.Data)-1] == '\'' || attr.Data[len(attr.Data)-1] == '"') {
488 quote = attr.Data[len(attr.Data)-1]
489 }
490 val = html.EscapeAttrVal(&attrByteBuffer, val, quote, o.KeepQuotes, isXML)
491 w.Write(val)
492 }
493 }
494 } else {
495 _ = tb.Shift() // StartTagClose
496 }
497 w.Write(gtBytes)
498
499 // skip text in select and optgroup tags
500 if t.Hash == Select || t.Hash == Optgroup {
501 if next := tb.Peek(0); next.TokenType == html.TextToken {
502 tb.Shift()
503 }
504 }
505
506 // keep space after phrasing tags (<i>, <span>, ...) FontAwesome etc.
507 if t.TokenType == html.StartTagToken && t.Traits&nonPhrasingTag == 0 {
508 if next := tb.Peek(0); next.Hash == t.Hash && next.TokenType == html.EndTagToken {
509 omitSpace = false
510 }
511 }
512 }
513 }
514}
diff --git a/vendor/github.com/tdewolff/minify/v2/html/table.go b/vendor/github.com/tdewolff/minify/v2/html/table.go
new file mode 100644
index 0000000..22239fc
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/html/table.go
@@ -0,0 +1,1346 @@
1package html
2
3type traits uint16
4
5const (
6 normalTag traits = 1 << iota
7 rawTag // raw tags need special processing for their content
8 nonPhrasingTag // non-phrasing elements are unaffected by whitespace, remove spaces around these tags
9 objectTag // content tags with a few exclusions, keep spaces after these open/close tags
10 omitPTag // omit p end tag if it is followed by this start tag
11 keepPTag // keep p end tag if it is followed by this end tag
12)
13
14const (
15 booleanAttr traits = 1 << iota
16 caselessAttr
17 urlAttr
18 trimAttr
19)
20
21var tagMap = map[Hash]traits{
22 A: keepPTag,
23 Abbr: normalTag,
24 Address: nonPhrasingTag | omitPTag,
25 Area: normalTag,
26 Article: nonPhrasingTag | omitPTag,
27 Aside: nonPhrasingTag | omitPTag,
28 Audio: keepPTag,
29 B: normalTag,
30 Base: normalTag,
31 Bb: normalTag,
32 Bdi: normalTag,
33 Bdo: normalTag,
34 Blockquote: nonPhrasingTag | omitPTag,
35 Body: nonPhrasingTag,
36 Br: nonPhrasingTag,
37 Button: objectTag,
38 Canvas: objectTag | keepPTag,
39 Caption: nonPhrasingTag,
40 Cite: normalTag,
41 Code: normalTag,
42 Col: nonPhrasingTag,
43 Colgroup: nonPhrasingTag,
44 Data: normalTag,
45 Datalist: normalTag,
46 Dd: nonPhrasingTag,
47 Del: keepPTag,
48 Details: omitPTag,
49 Dfn: normalTag,
50 Dialog: normalTag,
51 Div: nonPhrasingTag | omitPTag,
52 Dl: nonPhrasingTag | omitPTag,
53 Dt: nonPhrasingTag,
54 Em: normalTag,
55 Embed: nonPhrasingTag,
56 Fieldset: nonPhrasingTag | omitPTag,
57 Figcaption: nonPhrasingTag | omitPTag,
58 Figure: nonPhrasingTag | omitPTag,
59 Footer: nonPhrasingTag | omitPTag,
60 Form: nonPhrasingTag | omitPTag,
61 H1: nonPhrasingTag | omitPTag,
62 H2: nonPhrasingTag | omitPTag,
63 H3: nonPhrasingTag | omitPTag,
64 H4: nonPhrasingTag | omitPTag,
65 H5: nonPhrasingTag | omitPTag,
66 H6: nonPhrasingTag | omitPTag,
67 Head: nonPhrasingTag,
68 Header: nonPhrasingTag | omitPTag,
69 Hgroup: nonPhrasingTag,
70 Hr: nonPhrasingTag | omitPTag,
71 Html: nonPhrasingTag,
72 I: normalTag,
73 Iframe: rawTag | objectTag,
74 Img: objectTag,
75 Input: objectTag,
76 Ins: keepPTag,
77 Kbd: normalTag,
78 Label: normalTag,
79 Legend: normalTag,
80 Li: nonPhrasingTag,
81 Link: normalTag,
82 Main: nonPhrasingTag | omitPTag,
83 Map: keepPTag,
84 Mark: normalTag,
85 Math: rawTag,
86 Menu: omitPTag,
87 Meta: nonPhrasingTag,
88 Meter: objectTag,
89 Nav: nonPhrasingTag | omitPTag,
90 Noscript: nonPhrasingTag | keepPTag,
91 Object: objectTag,
92 Ol: nonPhrasingTag | omitPTag,
93 Optgroup: normalTag,
94 Option: normalTag,
95 Output: nonPhrasingTag,
96 P: nonPhrasingTag | omitPTag,
97 Param: normalTag,
98 Picture: normalTag,
99 Pre: nonPhrasingTag | omitPTag,
100 Progress: objectTag,
101 Q: objectTag,
102 Rp: normalTag,
103 Rt: normalTag,
104 Ruby: normalTag,
105 S: normalTag,
106 Samp: normalTag,
107 Script: rawTag,
108 Section: nonPhrasingTag | omitPTag,
109 Select: objectTag,
110 Slot: normalTag,
111 Small: normalTag,
112 Source: normalTag,
113 Span: normalTag,
114 Strong: normalTag,
115 Style: rawTag | nonPhrasingTag,
116 Sub: normalTag,
117 Summary: normalTag,
118 Sup: normalTag,
119 Svg: rawTag | objectTag,
120 Table: nonPhrasingTag | omitPTag,
121 Tbody: nonPhrasingTag,
122 Td: nonPhrasingTag,
123 Template: normalTag,
124 Textarea: rawTag | objectTag,
125 Tfoot: nonPhrasingTag,
126 Th: nonPhrasingTag,
127 Thead: nonPhrasingTag,
128 Time: normalTag,
129 Title: nonPhrasingTag,
130 Tr: nonPhrasingTag,
131 Track: normalTag,
132 U: normalTag,
133 Ul: nonPhrasingTag | omitPTag,
134 Var: normalTag,
135 Video: objectTag | keepPTag,
136 Wbr: normalTag,
137}
138
139var attrMap = map[Hash]traits{
140 Accept: trimAttr,
141 Accept_Charset: caselessAttr,
142 Action: urlAttr,
143 Align: caselessAttr,
144 Alink: caselessAttr,
145 Allowfullscreen: booleanAttr,
146 Async: booleanAttr,
147 Autofocus: booleanAttr,
148 Autoplay: booleanAttr,
149 Axis: caselessAttr,
150 Background: urlAttr,
151 Bgcolor: caselessAttr,
152 Charset: caselessAttr,
153 Checked: booleanAttr,
154 Cite: urlAttr,
155 Class: trimAttr,
156 Classid: urlAttr,
157 Clear: caselessAttr,
158 Codebase: urlAttr,
159 Codetype: trimAttr,
160 Color: caselessAttr,
161 Cols: trimAttr,
162 Colspan: trimAttr,
163 Compact: booleanAttr,
164 Controls: booleanAttr,
165 Data: urlAttr,
166 Declare: booleanAttr,
167 Default: booleanAttr,
168 DefaultChecked: booleanAttr,
169 DefaultMuted: booleanAttr,
170 DefaultSelected: booleanAttr,
171 Defer: booleanAttr,
172 Dir: caselessAttr,
173 Disabled: booleanAttr,
174 Enabled: booleanAttr,
175 Enctype: trimAttr,
176 Face: caselessAttr,
177 Formaction: urlAttr,
178 Formnovalidate: booleanAttr,
179 Frame: caselessAttr,
180 Hidden: booleanAttr,
181 Href: urlAttr,
182 Hreflang: caselessAttr,
183 Http_Equiv: caselessAttr,
184 Icon: urlAttr,
185 Inert: booleanAttr,
186 Ismap: booleanAttr,
187 Itemscope: booleanAttr,
188 Lang: trimAttr,
189 Language: caselessAttr,
190 Link: caselessAttr,
191 Longdesc: urlAttr,
192 Manifest: urlAttr,
193 Maxlength: trimAttr,
194 Media: caselessAttr | trimAttr,
195 Method: caselessAttr,
196 Multiple: booleanAttr,
197 Muted: booleanAttr,
198 Nohref: booleanAttr,
199 Noresize: booleanAttr,
200 Noshade: booleanAttr,
201 Novalidate: booleanAttr,
202 Nowrap: booleanAttr,
203 Open: booleanAttr,
204 Pauseonexit: booleanAttr,
205 Poster: urlAttr,
206 Profile: urlAttr,
207 Readonly: booleanAttr,
208 Rel: caselessAttr | trimAttr,
209 Required: booleanAttr,
210 Rev: caselessAttr,
211 Reversed: booleanAttr,
212 Rows: trimAttr,
213 Rowspan: trimAttr,
214 Rules: caselessAttr,
215 Scope: caselessAttr,
216 Scoped: booleanAttr,
217 Scrolling: caselessAttr,
218 Seamless: booleanAttr,
219 Selected: booleanAttr,
220 Shape: caselessAttr,
221 Size: trimAttr,
222 Sortable: booleanAttr,
223 Span: trimAttr,
224 Src: urlAttr,
225 Srcset: trimAttr,
226 Tabindex: trimAttr,
227 Target: caselessAttr,
228 Text: caselessAttr,
229 Translate: caselessAttr,
230 Truespeed: booleanAttr,
231 Type: trimAttr,
232 Typemustmatch: booleanAttr,
233 Undeterminate: booleanAttr,
234 Usemap: urlAttr,
235 Valign: caselessAttr,
236 Valuetype: caselessAttr,
237 Vlink: caselessAttr,
238 Visible: booleanAttr,
239 Xmlns: urlAttr,
240}
241
242var jsMimetypes = map[string]bool{
243 "text/javascript": true,
244 "application/javascript": true,
245}
246
247// EntitiesMap are all named character entities.
248var EntitiesMap = map[string][]byte{
249 "AElig": []byte("&#198;"),
250 "AMP": []byte("&"),
251 "Aacute": []byte("&#193;"),
252 "Abreve": []byte("&#258;"),
253 "Acirc": []byte("&#194;"),
254 "Agrave": []byte("&#192;"),
255 "Alpha": []byte("&#913;"),
256 "Amacr": []byte("&#256;"),
257 "Aogon": []byte("&#260;"),
258 "ApplyFunction": []byte("&af;"),
259 "Aring": []byte("&#197;"),
260 "Assign": []byte("&#8788;"),
261 "Atilde": []byte("&#195;"),
262 "Backslash": []byte("&#8726;"),
263 "Barwed": []byte("&#8966;"),
264 "Because": []byte("&#8757;"),
265 "Bernoullis": []byte("&Bscr;"),
266 "Breve": []byte("&#728;"),
267 "Bumpeq": []byte("&bump;"),
268 "Cacute": []byte("&#262;"),
269 "CapitalDifferentialD": []byte("&DD;"),
270 "Cayleys": []byte("&Cfr;"),
271 "Ccaron": []byte("&#268;"),
272 "Ccedil": []byte("&#199;"),
273 "Ccirc": []byte("&#264;"),
274 "Cconint": []byte("&#8752;"),
275 "Cedilla": []byte("&#184;"),
276 "CenterDot": []byte("&#183;"),
277 "CircleDot": []byte("&odot;"),
278 "CircleMinus": []byte("&#8854;"),
279 "CirclePlus": []byte("&#8853;"),
280 "CircleTimes": []byte("&#8855;"),
281 "ClockwiseContourIntegral": []byte("&#8754;"),
282 "CloseCurlyDoubleQuote": []byte("&#8221;"),
283 "CloseCurlyQuote": []byte("&#8217;"),
284 "Congruent": []byte("&#8801;"),
285 "Conint": []byte("&#8751;"),
286 "ContourIntegral": []byte("&oint;"),
287 "Coproduct": []byte("&#8720;"),
288 "CounterClockwiseContourIntegral": []byte("&#8755;"),
289 "CupCap": []byte("&#8781;"),
290 "DDotrahd": []byte("&#10513;"),
291 "Dagger": []byte("&#8225;"),
292 "Dcaron": []byte("&#270;"),
293 "Delta": []byte("&#916;"),
294 "DiacriticalAcute": []byte("&#180;"),
295 "DiacriticalDot": []byte("&dot;"),
296 "DiacriticalDoubleAcute": []byte("&#733;"),
297 "DiacriticalGrave": []byte("`"),
298 "DiacriticalTilde": []byte("&#732;"),
299 "Diamond": []byte("&diam;"),
300 "DifferentialD": []byte("&dd;"),
301 "DotDot": []byte("&#8412;"),
302 "DotEqual": []byte("&#8784;"),
303 "DoubleContourIntegral": []byte("&#8751;"),
304 "DoubleDot": []byte("&Dot;"),
305 "DoubleDownArrow": []byte("&dArr;"),
306 "DoubleLeftArrow": []byte("&lArr;"),
307 "DoubleLeftRightArrow": []byte("&iff;"),
308 "DoubleLeftTee": []byte("&Dashv;"),
309 "DoubleLongLeftArrow": []byte("&xlArr;"),
310 "DoubleLongLeftRightArrow": []byte("&xhArr;"),
311 "DoubleLongRightArrow": []byte("&xrArr;"),
312 "DoubleRightArrow": []byte("&rArr;"),
313 "DoubleRightTee": []byte("&#8872;"),
314 "DoubleUpArrow": []byte("&uArr;"),
315 "DoubleUpDownArrow": []byte("&vArr;"),
316 "DoubleVerticalBar": []byte("&par;"),
317 "DownArrow": []byte("&darr;"),
318 "DownArrowBar": []byte("&#10515;"),
319 "DownArrowUpArrow": []byte("&#8693;"),
320 "DownBreve": []byte("&#785;"),
321 "DownLeftRightVector": []byte("&#10576;"),
322 "DownLeftTeeVector": []byte("&#10590;"),
323 "DownLeftVector": []byte("&#8637;"),
324 "DownLeftVectorBar": []byte("&#10582;"),
325 "DownRightTeeVector": []byte("&#10591;"),
326 "DownRightVector": []byte("&#8641;"),
327 "DownRightVectorBar": []byte("&#10583;"),
328 "DownTee": []byte("&top;"),
329 "DownTeeArrow": []byte("&#8615;"),
330 "Downarrow": []byte("&dArr;"),
331 "Dstrok": []byte("&#272;"),
332 "Eacute": []byte("&#201;"),
333 "Ecaron": []byte("&#282;"),
334 "Ecirc": []byte("&#202;"),
335 "Egrave": []byte("&#200;"),
336 "Element": []byte("&in;"),
337 "Emacr": []byte("&#274;"),
338 "EmptySmallSquare": []byte("&#9723;"),
339 "EmptyVerySmallSquare": []byte("&#9643;"),
340 "Eogon": []byte("&#280;"),
341 "Epsilon": []byte("&#917;"),
342 "EqualTilde": []byte("&esim;"),
343 "Equilibrium": []byte("&#8652;"),
344 "Exists": []byte("&#8707;"),
345 "ExponentialE": []byte("&ee;"),
346 "FilledSmallSquare": []byte("&#9724;"),
347 "FilledVerySmallSquare": []byte("&squf;"),
348 "ForAll": []byte("&#8704;"),
349 "Fouriertrf": []byte("&Fscr;"),
350 "GT": []byte(">"),
351 "Gamma": []byte("&#915;"),
352 "Gammad": []byte("&#988;"),
353 "Gbreve": []byte("&#286;"),
354 "Gcedil": []byte("&#290;"),
355 "Gcirc": []byte("&#284;"),
356 "GreaterEqual": []byte("&ge;"),
357 "GreaterEqualLess": []byte("&gel;"),
358 "GreaterFullEqual": []byte("&gE;"),
359 "GreaterGreater": []byte("&#10914;"),
360 "GreaterLess": []byte("&gl;"),
361 "GreaterSlantEqual": []byte("&ges;"),
362 "GreaterTilde": []byte("&gsim;"),
363 "HARDcy": []byte("&#1066;"),
364 "Hacek": []byte("&#711;"),
365 "Hat": []byte("^"),
366 "Hcirc": []byte("&#292;"),
367 "HilbertSpace": []byte("&Hscr;"),
368 "HorizontalLine": []byte("&boxh;"),
369 "Hstrok": []byte("&#294;"),
370 "HumpDownHump": []byte("&bump;"),
371 "HumpEqual": []byte("&#8783;"),
372 "IJlig": []byte("&#306;"),
373 "Iacute": []byte("&#205;"),
374 "Icirc": []byte("&#206;"),
375 "Ifr": []byte("&Im;"),
376 "Igrave": []byte("&#204;"),
377 "Imacr": []byte("&#298;"),
378 "ImaginaryI": []byte("&ii;"),
379 "Implies": []byte("&rArr;"),
380 "Integral": []byte("&int;"),
381 "Intersection": []byte("&xcap;"),
382 "InvisibleComma": []byte("&ic;"),
383 "InvisibleTimes": []byte("&it;"),
384 "Iogon": []byte("&#302;"),
385 "Itilde": []byte("&#296;"),
386 "Jcirc": []byte("&#308;"),
387 "Jsercy": []byte("&#1032;"),
388 "Kappa": []byte("&#922;"),
389 "Kcedil": []byte("&#310;"),
390 "LT": []byte("<"),
391 "Lacute": []byte("&#313;"),
392 "Lambda": []byte("&#923;"),
393 "Laplacetrf": []byte("&Lscr;"),
394 "Lcaron": []byte("&#317;"),
395 "Lcedil": []byte("&#315;"),
396 "LeftAngleBracket": []byte("&lang;"),
397 "LeftArrow": []byte("&larr;"),
398 "LeftArrowBar": []byte("&#8676;"),
399 "LeftArrowRightArrow": []byte("&#8646;"),
400 "LeftCeiling": []byte("&#8968;"),
401 "LeftDoubleBracket": []byte("&lobrk;"),
402 "LeftDownTeeVector": []byte("&#10593;"),
403 "LeftDownVector": []byte("&#8643;"),
404 "LeftDownVectorBar": []byte("&#10585;"),
405 "LeftFloor": []byte("&#8970;"),
406 "LeftRightArrow": []byte("&harr;"),
407 "LeftRightVector": []byte("&#10574;"),
408 "LeftTee": []byte("&#8867;"),
409 "LeftTeeArrow": []byte("&#8612;"),
410 "LeftTeeVector": []byte("&#10586;"),
411 "LeftTriangle": []byte("&#8882;"),
412 "LeftTriangleBar": []byte("&#10703;"),
413 "LeftTriangleEqual": []byte("&#8884;"),
414 "LeftUpDownVector": []byte("&#10577;"),
415 "LeftUpTeeVector": []byte("&#10592;"),
416 "LeftUpVector": []byte("&#8639;"),
417 "LeftUpVectorBar": []byte("&#10584;"),
418 "LeftVector": []byte("&#8636;"),
419 "LeftVectorBar": []byte("&#10578;"),
420 "Leftarrow": []byte("&lArr;"),
421 "Leftrightarrow": []byte("&iff;"),
422 "LessEqualGreater": []byte("&leg;"),
423 "LessFullEqual": []byte("&lE;"),
424 "LessGreater": []byte("&lg;"),
425 "LessLess": []byte("&#10913;"),
426 "LessSlantEqual": []byte("&les;"),
427 "LessTilde": []byte("&lsim;"),
428 "Lleftarrow": []byte("&#8666;"),
429 "Lmidot": []byte("&#319;"),
430 "LongLeftArrow": []byte("&xlarr;"),
431 "LongLeftRightArrow": []byte("&xharr;"),
432 "LongRightArrow": []byte("&xrarr;"),
433 "Longleftarrow": []byte("&xlArr;"),
434 "Longleftrightarrow": []byte("&xhArr;"),
435 "Longrightarrow": []byte("&xrArr;"),
436 "LowerLeftArrow": []byte("&#8601;"),
437 "LowerRightArrow": []byte("&#8600;"),
438 "Lstrok": []byte("&#321;"),
439 "MediumSpace": []byte("&#8287;"),
440 "Mellintrf": []byte("&Mscr;"),
441 "MinusPlus": []byte("&mp;"),
442 "Nacute": []byte("&#323;"),
443 "Ncaron": []byte("&#327;"),
444 "Ncedil": []byte("&#325;"),
445 "NegativeMediumSpace": []byte("&#8203;"),
446 "NegativeThickSpace": []byte("&#8203;"),
447 "NegativeThinSpace": []byte("&#8203;"),
448 "NegativeVeryThinSpace": []byte("&#8203;"),
449 "NestedGreaterGreater": []byte("&Gt;"),
450 "NestedLessLess": []byte("&Lt;"),
451 "NewLine": []byte("\n"),
452 "NoBreak": []byte("&#8288;"),
453 "NonBreakingSpace": []byte("&#160;"),
454 "NotCongruent": []byte("&#8802;"),
455 "NotCupCap": []byte("&#8813;"),
456 "NotDoubleVerticalBar": []byte("&npar;"),
457 "NotElement": []byte("&#8713;"),
458 "NotEqual": []byte("&ne;"),
459 "NotExists": []byte("&#8708;"),
460 "NotGreater": []byte("&ngt;"),
461 "NotGreaterEqual": []byte("&nge;"),
462 "NotGreaterLess": []byte("&ntgl;"),
463 "NotGreaterTilde": []byte("&#8821;"),
464 "NotLeftTriangle": []byte("&#8938;"),
465 "NotLeftTriangleEqual": []byte("&#8940;"),
466 "NotLess": []byte("&nlt;"),
467 "NotLessEqual": []byte("&nle;"),
468 "NotLessGreater": []byte("&ntlg;"),
469 "NotLessTilde": []byte("&#8820;"),
470 "NotPrecedes": []byte("&npr;"),
471 "NotPrecedesSlantEqual": []byte("&#8928;"),
472 "NotReverseElement": []byte("&#8716;"),
473 "NotRightTriangle": []byte("&#8939;"),
474 "NotRightTriangleEqual": []byte("&#8941;"),
475 "NotSquareSubsetEqual": []byte("&#8930;"),
476 "NotSquareSupersetEqual": []byte("&#8931;"),
477 "NotSubsetEqual": []byte("&#8840;"),
478 "NotSucceeds": []byte("&nsc;"),
479 "NotSucceedsSlantEqual": []byte("&#8929;"),
480 "NotSupersetEqual": []byte("&#8841;"),
481 "NotTilde": []byte("&nsim;"),
482 "NotTildeEqual": []byte("&#8772;"),
483 "NotTildeFullEqual": []byte("&#8775;"),
484 "NotTildeTilde": []byte("&nap;"),
485 "NotVerticalBar": []byte("&nmid;"),
486 "Ntilde": []byte("&#209;"),
487 "OElig": []byte("&#338;"),
488 "Oacute": []byte("&#211;"),
489 "Ocirc": []byte("&#212;"),
490 "Odblac": []byte("&#336;"),
491 "Ograve": []byte("&#210;"),
492 "Omacr": []byte("&#332;"),
493 "Omega": []byte("&ohm;"),
494 "Omicron": []byte("&#927;"),
495 "OpenCurlyDoubleQuote": []byte("&#8220;"),
496 "OpenCurlyQuote": []byte("&#8216;"),
497 "Oslash": []byte("&#216;"),
498 "Otilde": []byte("&#213;"),
499 "OverBar": []byte("&#8254;"),
500 "OverBrace": []byte("&#9182;"),
501 "OverBracket": []byte("&tbrk;"),
502 "OverParenthesis": []byte("&#9180;"),
503 "PartialD": []byte("&part;"),
504 "PlusMinus": []byte("&pm;"),
505 "Poincareplane": []byte("&Hfr;"),
506 "Precedes": []byte("&pr;"),
507 "PrecedesEqual": []byte("&pre;"),
508 "PrecedesSlantEqual": []byte("&#8828;"),
509 "PrecedesTilde": []byte("&#8830;"),
510 "Product": []byte("&prod;"),
511 "Proportion": []byte("&#8759;"),
512 "Proportional": []byte("&prop;"),
513 "QUOT": []byte("\""),
514 "Racute": []byte("&#340;"),
515 "Rcaron": []byte("&#344;"),
516 "Rcedil": []byte("&#342;"),
517 "ReverseElement": []byte("&ni;"),
518 "ReverseEquilibrium": []byte("&#8651;"),
519 "ReverseUpEquilibrium": []byte("&duhar;"),
520 "Rfr": []byte("&Re;"),
521 "RightAngleBracket": []byte("&rang;"),
522 "RightArrow": []byte("&rarr;"),
523 "RightArrowBar": []byte("&#8677;"),
524 "RightArrowLeftArrow": []byte("&#8644;"),
525 "RightCeiling": []byte("&#8969;"),
526 "RightDoubleBracket": []byte("&robrk;"),
527 "RightDownTeeVector": []byte("&#10589;"),
528 "RightDownVector": []byte("&#8642;"),
529 "RightDownVectorBar": []byte("&#10581;"),
530 "RightFloor": []byte("&#8971;"),
531 "RightTee": []byte("&#8866;"),
532 "RightTeeArrow": []byte("&map;"),
533 "RightTeeVector": []byte("&#10587;"),
534 "RightTriangle": []byte("&#8883;"),
535 "RightTriangleBar": []byte("&#10704;"),
536 "RightTriangleEqual": []byte("&#8885;"),
537 "RightUpDownVector": []byte("&#10575;"),
538 "RightUpTeeVector": []byte("&#10588;"),
539 "RightUpVector": []byte("&#8638;"),
540 "RightUpVectorBar": []byte("&#10580;"),
541 "RightVector": []byte("&#8640;"),
542 "RightVectorBar": []byte("&#10579;"),
543 "Rightarrow": []byte("&rArr;"),
544 "RoundImplies": []byte("&#10608;"),
545 "Rrightarrow": []byte("&#8667;"),
546 "RuleDelayed": []byte("&#10740;"),
547 "SHCHcy": []byte("&#1065;"),
548 "SOFTcy": []byte("&#1068;"),
549 "Sacute": []byte("&#346;"),
550 "Scaron": []byte("&#352;"),
551 "Scedil": []byte("&#350;"),
552 "Scirc": []byte("&#348;"),
553 "ShortDownArrow": []byte("&darr;"),
554 "ShortLeftArrow": []byte("&larr;"),
555 "ShortRightArrow": []byte("&rarr;"),
556 "ShortUpArrow": []byte("&uarr;"),
557 "Sigma": []byte("&#931;"),
558 "SmallCircle": []byte("&#8728;"),
559 "Square": []byte("&squ;"),
560 "SquareIntersection": []byte("&#8851;"),
561 "SquareSubset": []byte("&#8847;"),
562 "SquareSubsetEqual": []byte("&#8849;"),
563 "SquareSuperset": []byte("&#8848;"),
564 "SquareSupersetEqual": []byte("&#8850;"),
565 "SquareUnion": []byte("&#8852;"),
566 "Subset": []byte("&Sub;"),
567 "SubsetEqual": []byte("&sube;"),
568 "Succeeds": []byte("&sc;"),
569 "SucceedsEqual": []byte("&sce;"),
570 "SucceedsSlantEqual": []byte("&#8829;"),
571 "SucceedsTilde": []byte("&#8831;"),
572 "SuchThat": []byte("&ni;"),
573 "Superset": []byte("&sup;"),
574 "SupersetEqual": []byte("&supe;"),
575 "Supset": []byte("&Sup;"),
576 "THORN": []byte("&#222;"),
577 "Tab": []byte(" "),
578 "Tcaron": []byte("&#356;"),
579 "Tcedil": []byte("&#354;"),
580 "Therefore": []byte("&#8756;"),
581 "Theta": []byte("&#920;"),
582 "ThinSpace": []byte("&#8201;"),
583 "Tilde": []byte("&sim;"),
584 "TildeEqual": []byte("&sime;"),
585 "TildeFullEqual": []byte("&cong;"),
586 "TildeTilde": []byte("&ap;"),
587 "TripleDot": []byte("&tdot;"),
588 "Tstrok": []byte("&#358;"),
589 "Uacute": []byte("&#218;"),
590 "Uarrocir": []byte("&#10569;"),
591 "Ubreve": []byte("&#364;"),
592 "Ucirc": []byte("&#219;"),
593 "Udblac": []byte("&#368;"),
594 "Ugrave": []byte("&#217;"),
595 "Umacr": []byte("&#362;"),
596 "UnderBar": []byte("_"),
597 "UnderBrace": []byte("&#9183;"),
598 "UnderBracket": []byte("&bbrk;"),
599 "UnderParenthesis": []byte("&#9181;"),
600 "Union": []byte("&xcup;"),
601 "UnionPlus": []byte("&#8846;"),
602 "Uogon": []byte("&#370;"),
603 "UpArrow": []byte("&uarr;"),
604 "UpArrowBar": []byte("&#10514;"),
605 "UpArrowDownArrow": []byte("&#8645;"),
606 "UpDownArrow": []byte("&varr;"),
607 "UpEquilibrium": []byte("&udhar;"),
608 "UpTee": []byte("&bot;"),
609 "UpTeeArrow": []byte("&#8613;"),
610 "Uparrow": []byte("&uArr;"),
611 "Updownarrow": []byte("&vArr;"),
612 "UpperLeftArrow": []byte("&#8598;"),
613 "UpperRightArrow": []byte("&#8599;"),
614 "Upsilon": []byte("&#933;"),
615 "Uring": []byte("&#366;"),
616 "Utilde": []byte("&#360;"),
617 "Verbar": []byte("&Vert;"),
618 "VerticalBar": []byte("&mid;"),
619 "VerticalLine": []byte("|"),
620 "VerticalSeparator": []byte("&#10072;"),
621 "VerticalTilde": []byte("&wr;"),
622 "VeryThinSpace": []byte("&#8202;"),
623 "Vvdash": []byte("&#8874;"),
624 "Wcirc": []byte("&#372;"),
625 "Yacute": []byte("&#221;"),
626 "Ycirc": []byte("&#374;"),
627 "Zacute": []byte("&#377;"),
628 "Zcaron": []byte("&#381;"),
629 "ZeroWidthSpace": []byte("&#8203;"),
630 "aacute": []byte("&#225;"),
631 "abreve": []byte("&#259;"),
632 "acirc": []byte("&#226;"),
633 "acute": []byte("&#180;"),
634 "aelig": []byte("&#230;"),
635 "agrave": []byte("&#224;"),
636 "alefsym": []byte("&#8501;"),
637 "alpha": []byte("&#945;"),
638 "amacr": []byte("&#257;"),
639 "amp": []byte("&"),
640 "andslope": []byte("&#10840;"),
641 "angle": []byte("&ang;"),
642 "angmsd": []byte("&#8737;"),
643 "angmsdaa": []byte("&#10664;"),
644 "angmsdab": []byte("&#10665;"),
645 "angmsdac": []byte("&#10666;"),
646 "angmsdad": []byte("&#10667;"),
647 "angmsdae": []byte("&#10668;"),
648 "angmsdaf": []byte("&#10669;"),
649 "angmsdag": []byte("&#10670;"),
650 "angmsdah": []byte("&#10671;"),
651 "angrtvb": []byte("&#8894;"),
652 "angrtvbd": []byte("&#10653;"),
653 "angsph": []byte("&#8738;"),
654 "angst": []byte("&#197;"),
655 "angzarr": []byte("&#9084;"),
656 "aogon": []byte("&#261;"),
657 "apos": []byte("'"),
658 "approx": []byte("&ap;"),
659 "approxeq": []byte("&ape;"),
660 "aring": []byte("&#229;"),
661 "ast": []byte("*"),
662 "asymp": []byte("&ap;"),
663 "asympeq": []byte("&#8781;"),
664 "atilde": []byte("&#227;"),
665 "awconint": []byte("&#8755;"),
666 "backcong": []byte("&#8780;"),
667 "backepsilon": []byte("&#1014;"),
668 "backprime": []byte("&#8245;"),
669 "backsim": []byte("&bsim;"),
670 "backsimeq": []byte("&#8909;"),
671 "barvee": []byte("&#8893;"),
672 "barwed": []byte("&#8965;"),
673 "barwedge": []byte("&#8965;"),
674 "bbrktbrk": []byte("&#9142;"),
675 "becaus": []byte("&#8757;"),
676 "because": []byte("&#8757;"),
677 "bemptyv": []byte("&#10672;"),
678 "bernou": []byte("&Bscr;"),
679 "between": []byte("&#8812;"),
680 "bigcap": []byte("&xcap;"),
681 "bigcirc": []byte("&#9711;"),
682 "bigcup": []byte("&xcup;"),
683 "bigodot": []byte("&xodot;"),
684 "bigoplus": []byte("&#10753;"),
685 "bigotimes": []byte("&#10754;"),
686 "bigsqcup": []byte("&#10758;"),
687 "bigstar": []byte("&#9733;"),
688 "bigtriangledown": []byte("&#9661;"),
689 "bigtriangleup": []byte("&#9651;"),
690 "biguplus": []byte("&#10756;"),
691 "bigvee": []byte("&Vee;"),
692 "bigwedge": []byte("&#8896;"),
693 "bkarow": []byte("&rbarr;"),
694 "blacklozenge": []byte("&lozf;"),
695 "blacksquare": []byte("&squf;"),
696 "blacktriangle": []byte("&#9652;"),
697 "blacktriangledown": []byte("&#9662;"),
698 "blacktriangleleft": []byte("&#9666;"),
699 "blacktriangleright": []byte("&#9656;"),
700 "bottom": []byte("&bot;"),
701 "bowtie": []byte("&#8904;"),
702 "boxminus": []byte("&#8863;"),
703 "boxplus": []byte("&#8862;"),
704 "boxtimes": []byte("&#8864;"),
705 "bprime": []byte("&#8245;"),
706 "breve": []byte("&#728;"),
707 "brvbar": []byte("&#166;"),
708 "bsol": []byte("\\"),
709 "bsolhsub": []byte("&#10184;"),
710 "bullet": []byte("&bull;"),
711 "bumpeq": []byte("&#8783;"),
712 "cacute": []byte("&#263;"),
713 "capbrcup": []byte("&#10825;"),
714 "caron": []byte("&#711;"),
715 "ccaron": []byte("&#269;"),
716 "ccedil": []byte("&#231;"),
717 "ccirc": []byte("&#265;"),
718 "ccupssm": []byte("&#10832;"),
719 "cedil": []byte("&#184;"),
720 "cemptyv": []byte("&#10674;"),
721 "centerdot": []byte("&#183;"),
722 "checkmark": []byte("&check;"),
723 "circeq": []byte("&cire;"),
724 "circlearrowleft": []byte("&#8634;"),
725 "circlearrowright": []byte("&#8635;"),
726 "circledR": []byte("&REG;"),
727 "circledS": []byte("&oS;"),
728 "circledast": []byte("&oast;"),
729 "circledcirc": []byte("&ocir;"),
730 "circleddash": []byte("&#8861;"),
731 "cirfnint": []byte("&#10768;"),
732 "cirscir": []byte("&#10690;"),
733 "clubsuit": []byte("&#9827;"),
734 "colon": []byte(":"),
735 "colone": []byte("&#8788;"),
736 "coloneq": []byte("&#8788;"),
737 "comma": []byte(","),
738 "commat": []byte("@"),
739 "compfn": []byte("&#8728;"),
740 "complement": []byte("&comp;"),
741 "complexes": []byte("&Copf;"),
742 "congdot": []byte("&#10861;"),
743 "conint": []byte("&oint;"),
744 "coprod": []byte("&#8720;"),
745 "copysr": []byte("&#8471;"),
746 "cudarrl": []byte("&#10552;"),
747 "cudarrr": []byte("&#10549;"),
748 "cularr": []byte("&#8630;"),
749 "cularrp": []byte("&#10557;"),
750 "cupbrcap": []byte("&#10824;"),
751 "cupdot": []byte("&#8845;"),
752 "curarr": []byte("&#8631;"),
753 "curarrm": []byte("&#10556;"),
754 "curlyeqprec": []byte("&#8926;"),
755 "curlyeqsucc": []byte("&#8927;"),
756 "curlyvee": []byte("&#8910;"),
757 "curlywedge": []byte("&#8911;"),
758 "curren": []byte("&#164;"),
759 "curvearrowleft": []byte("&#8630;"),
760 "curvearrowright": []byte("&#8631;"),
761 "cwconint": []byte("&#8754;"),
762 "cylcty": []byte("&#9005;"),
763 "dagger": []byte("&#8224;"),
764 "daleth": []byte("&#8504;"),
765 "dbkarow": []byte("&rBarr;"),
766 "dblac": []byte("&#733;"),
767 "dcaron": []byte("&#271;"),
768 "ddagger": []byte("&#8225;"),
769 "ddotseq": []byte("&eDDot;"),
770 "delta": []byte("&#948;"),
771 "demptyv": []byte("&#10673;"),
772 "diamond": []byte("&diam;"),
773 "diamondsuit": []byte("&#9830;"),
774 "digamma": []byte("&#989;"),
775 "divide": []byte("&div;"),
776 "divideontimes": []byte("&#8903;"),
777 "divonx": []byte("&#8903;"),
778 "dlcorn": []byte("&#8990;"),
779 "dlcrop": []byte("&#8973;"),
780 "dollar": []byte("$"),
781 "doteqdot": []byte("&eDot;"),
782 "dotminus": []byte("&#8760;"),
783 "dotplus": []byte("&#8724;"),
784 "dotsquare": []byte("&#8865;"),
785 "doublebarwedge": []byte("&#8966;"),
786 "downarrow": []byte("&darr;"),
787 "downdownarrows": []byte("&#8650;"),
788 "downharpoonleft": []byte("&#8643;"),
789 "downharpoonright": []byte("&#8642;"),
790 "drbkarow": []byte("&RBarr;"),
791 "drcorn": []byte("&#8991;"),
792 "drcrop": []byte("&#8972;"),
793 "dstrok": []byte("&#273;"),
794 "dwangle": []byte("&#10662;"),
795 "dzigrarr": []byte("&#10239;"),
796 "eacute": []byte("&#233;"),
797 "ecaron": []byte("&#283;"),
798 "ecirc": []byte("&#234;"),
799 "ecolon": []byte("&#8789;"),
800 "egrave": []byte("&#232;"),
801 "elinters": []byte("&#9191;"),
802 "emacr": []byte("&#275;"),
803 "emptyset": []byte("&#8709;"),
804 "emptyv": []byte("&#8709;"),
805 "emsp13": []byte("&#8196;"),
806 "emsp14": []byte("&#8197;"),
807 "eogon": []byte("&#281;"),
808 "epsilon": []byte("&#949;"),
809 "eqcirc": []byte("&ecir;"),
810 "eqcolon": []byte("&#8789;"),
811 "eqsim": []byte("&esim;"),
812 "eqslantgtr": []byte("&egs;"),
813 "eqslantless": []byte("&els;"),
814 "equals": []byte("="),
815 "equest": []byte("&#8799;"),
816 "equivDD": []byte("&#10872;"),
817 "eqvparsl": []byte("&#10725;"),
818 "excl": []byte("!"),
819 "expectation": []byte("&Escr;"),
820 "exponentiale": []byte("&ee;"),
821 "fallingdotseq": []byte("&#8786;"),
822 "female": []byte("&#9792;"),
823 "forall": []byte("&#8704;"),
824 "fpartint": []byte("&#10765;"),
825 "frac12": []byte("&#189;"),
826 "frac13": []byte("&#8531;"),
827 "frac14": []byte("&#188;"),
828 "frac15": []byte("&#8533;"),
829 "frac16": []byte("&#8537;"),
830 "frac18": []byte("&#8539;"),
831 "frac23": []byte("&#8532;"),
832 "frac25": []byte("&#8534;"),
833 "frac34": []byte("&#190;"),
834 "frac35": []byte("&#8535;"),
835 "frac38": []byte("&#8540;"),
836 "frac45": []byte("&#8536;"),
837 "frac56": []byte("&#8538;"),
838 "frac58": []byte("&#8541;"),
839 "frac78": []byte("&#8542;"),
840 "gacute": []byte("&#501;"),
841 "gamma": []byte("&#947;"),
842 "gammad": []byte("&#989;"),
843 "gbreve": []byte("&#287;"),
844 "gcirc": []byte("&#285;"),
845 "geq": []byte("&ge;"),
846 "geqq": []byte("&gE;"),
847 "geqslant": []byte("&ges;"),
848 "gesdoto": []byte("&#10882;"),
849 "gesdotol": []byte("&#10884;"),
850 "ggg": []byte("&Gg;"),
851 "gnapprox": []byte("&gnap;"),
852 "gneq": []byte("&gne;"),
853 "gneqq": []byte("&gnE;"),
854 "grave": []byte("`"),
855 "gt": []byte(">"),
856 "gtquest": []byte("&#10876;"),
857 "gtrapprox": []byte("&gap;"),
858 "gtrdot": []byte("&#8919;"),
859 "gtreqless": []byte("&gel;"),
860 "gtreqqless": []byte("&gEl;"),
861 "gtrless": []byte("&gl;"),
862 "gtrsim": []byte("&gsim;"),
863 "hArr": []byte("&iff;"),
864 "hairsp": []byte("&#8202;"),
865 "hamilt": []byte("&Hscr;"),
866 "hardcy": []byte("&#1098;"),
867 "harrcir": []byte("&#10568;"),
868 "hcirc": []byte("&#293;"),
869 "hearts": []byte("&#9829;"),
870 "heartsuit": []byte("&#9829;"),
871 "hellip": []byte("&mldr;"),
872 "hercon": []byte("&#8889;"),
873 "hksearow": []byte("&#10533;"),
874 "hkswarow": []byte("&#10534;"),
875 "homtht": []byte("&#8763;"),
876 "hookleftarrow": []byte("&#8617;"),
877 "hookrightarrow": []byte("&#8618;"),
878 "horbar": []byte("&#8213;"),
879 "hslash": []byte("&hbar;"),
880 "hstrok": []byte("&#295;"),
881 "hybull": []byte("&#8259;"),
882 "hyphen": []byte("&dash;"),
883 "iacute": []byte("&#237;"),
884 "icirc": []byte("&#238;"),
885 "iexcl": []byte("&#161;"),
886 "igrave": []byte("&#236;"),
887 "iiiint": []byte("&qint;"),
888 "iiint": []byte("&tint;"),
889 "ijlig": []byte("&#307;"),
890 "imacr": []byte("&#299;"),
891 "image": []byte("&Im;"),
892 "imagline": []byte("&Iscr;"),
893 "imagpart": []byte("&Im;"),
894 "imath": []byte("&#305;"),
895 "imped": []byte("&#437;"),
896 "incare": []byte("&#8453;"),
897 "infintie": []byte("&#10717;"),
898 "inodot": []byte("&#305;"),
899 "intcal": []byte("&#8890;"),
900 "integers": []byte("&Zopf;"),
901 "intercal": []byte("&#8890;"),
902 "intlarhk": []byte("&#10775;"),
903 "intprod": []byte("&iprod;"),
904 "iogon": []byte("&#303;"),
905 "iquest": []byte("&#191;"),
906 "isin": []byte("&in;"),
907 "isindot": []byte("&#8949;"),
908 "isinsv": []byte("&#8947;"),
909 "isinv": []byte("&in;"),
910 "itilde": []byte("&#297;"),
911 "jcirc": []byte("&#309;"),
912 "jmath": []byte("&#567;"),
913 "jsercy": []byte("&#1112;"),
914 "kappa": []byte("&#954;"),
915 "kappav": []byte("&#1008;"),
916 "kcedil": []byte("&#311;"),
917 "kgreen": []byte("&#312;"),
918 "lacute": []byte("&#314;"),
919 "laemptyv": []byte("&#10676;"),
920 "lagran": []byte("&Lscr;"),
921 "lambda": []byte("&#955;"),
922 "langle": []byte("&lang;"),
923 "laquo": []byte("&#171;"),
924 "larrbfs": []byte("&#10527;"),
925 "larrhk": []byte("&#8617;"),
926 "larrlp": []byte("&#8619;"),
927 "larrsim": []byte("&#10611;"),
928 "larrtl": []byte("&#8610;"),
929 "lbrace": []byte("{"),
930 "lbrack": []byte("["),
931 "lbrksld": []byte("&#10639;"),
932 "lbrkslu": []byte("&#10637;"),
933 "lcaron": []byte("&#318;"),
934 "lcedil": []byte("&#316;"),
935 "lcub": []byte("{"),
936 "ldquor": []byte("&#8222;"),
937 "ldrdhar": []byte("&#10599;"),
938 "ldrushar": []byte("&#10571;"),
939 "leftarrow": []byte("&larr;"),
940 "leftarrowtail": []byte("&#8610;"),
941 "leftharpoondown": []byte("&#8637;"),
942 "leftharpoonup": []byte("&#8636;"),
943 "leftleftarrows": []byte("&#8647;"),
944 "leftrightarrow": []byte("&harr;"),
945 "leftrightarrows": []byte("&#8646;"),
946 "leftrightharpoons": []byte("&#8651;"),
947 "leftrightsquigarrow": []byte("&#8621;"),
948 "leftthreetimes": []byte("&#8907;"),
949 "leq": []byte("&le;"),
950 "leqq": []byte("&lE;"),
951 "leqslant": []byte("&les;"),
952 "lesdoto": []byte("&#10881;"),
953 "lesdotor": []byte("&#10883;"),
954 "lessapprox": []byte("&lap;"),
955 "lessdot": []byte("&#8918;"),
956 "lesseqgtr": []byte("&leg;"),
957 "lesseqqgtr": []byte("&lEg;"),
958 "lessgtr": []byte("&lg;"),
959 "lesssim": []byte("&lsim;"),
960 "lfloor": []byte("&#8970;"),
961 "llcorner": []byte("&#8990;"),
962 "lmidot": []byte("&#320;"),
963 "lmoust": []byte("&#9136;"),
964 "lmoustache": []byte("&#9136;"),
965 "lnapprox": []byte("&lnap;"),
966 "lneq": []byte("&lne;"),
967 "lneqq": []byte("&lnE;"),
968 "longleftarrow": []byte("&xlarr;"),
969 "longleftrightarrow": []byte("&xharr;"),
970 "longmapsto": []byte("&xmap;"),
971 "longrightarrow": []byte("&xrarr;"),
972 "looparrowleft": []byte("&#8619;"),
973 "looparrowright": []byte("&#8620;"),
974 "lotimes": []byte("&#10804;"),
975 "lowast": []byte("&#8727;"),
976 "lowbar": []byte("_"),
977 "lozenge": []byte("&loz;"),
978 "lpar": []byte("("),
979 "lrcorner": []byte("&#8991;"),
980 "lsaquo": []byte("&#8249;"),
981 "lsqb": []byte("["),
982 "lsquor": []byte("&#8218;"),
983 "lstrok": []byte("&#322;"),
984 "lt": []byte("<"),
985 "lthree": []byte("&#8907;"),
986 "ltimes": []byte("&#8905;"),
987 "ltquest": []byte("&#10875;"),
988 "lurdshar": []byte("&#10570;"),
989 "luruhar": []byte("&#10598;"),
990 "maltese": []byte("&malt;"),
991 "mapsto": []byte("&map;"),
992 "mapstodown": []byte("&#8615;"),
993 "mapstoleft": []byte("&#8612;"),
994 "mapstoup": []byte("&#8613;"),
995 "marker": []byte("&#9646;"),
996 "measuredangle": []byte("&#8737;"),
997 "micro": []byte("&#181;"),
998 "midast": []byte("*"),
999 "middot": []byte("&#183;"),
1000 "minusb": []byte("&#8863;"),
1001 "minusd": []byte("&#8760;"),
1002 "minusdu": []byte("&#10794;"),
1003 "mnplus": []byte("&mp;"),
1004 "models": []byte("&#8871;"),
1005 "mstpos": []byte("&ac;"),
1006 "multimap": []byte("&#8888;"),
1007 "nLeftarrow": []byte("&#8653;"),
1008 "nLeftrightarrow": []byte("&#8654;"),
1009 "nRightarrow": []byte("&#8655;"),
1010 "nVDash": []byte("&#8879;"),
1011 "nVdash": []byte("&#8878;"),
1012 "nabla": []byte("&Del;"),
1013 "nacute": []byte("&#324;"),
1014 "napos": []byte("&#329;"),
1015 "napprox": []byte("&nap;"),
1016 "natural": []byte("&#9838;"),
1017 "naturals": []byte("&Nopf;"),
1018 "ncaron": []byte("&#328;"),
1019 "ncedil": []byte("&#326;"),
1020 "nearrow": []byte("&#8599;"),
1021 "nequiv": []byte("&#8802;"),
1022 "nesear": []byte("&toea;"),
1023 "nexist": []byte("&#8708;"),
1024 "nexists": []byte("&#8708;"),
1025 "ngeq": []byte("&nge;"),
1026 "ngtr": []byte("&ngt;"),
1027 "niv": []byte("&ni;"),
1028 "nleftarrow": []byte("&#8602;"),
1029 "nleftrightarrow": []byte("&#8622;"),
1030 "nleq": []byte("&nle;"),
1031 "nless": []byte("&nlt;"),
1032 "nltrie": []byte("&#8940;"),
1033 "notinva": []byte("&#8713;"),
1034 "notinvb": []byte("&#8951;"),
1035 "notinvc": []byte("&#8950;"),
1036 "notniva": []byte("&#8716;"),
1037 "notnivb": []byte("&#8958;"),
1038 "notnivc": []byte("&#8957;"),
1039 "nparallel": []byte("&npar;"),
1040 "npolint": []byte("&#10772;"),
1041 "nprcue": []byte("&#8928;"),
1042 "nprec": []byte("&npr;"),
1043 "nrightarrow": []byte("&#8603;"),
1044 "nrtrie": []byte("&#8941;"),
1045 "nsccue": []byte("&#8929;"),
1046 "nshortmid": []byte("&nmid;"),
1047 "nshortparallel": []byte("&npar;"),
1048 "nsimeq": []byte("&#8772;"),
1049 "nsmid": []byte("&nmid;"),
1050 "nspar": []byte("&npar;"),
1051 "nsqsube": []byte("&#8930;"),
1052 "nsqsupe": []byte("&#8931;"),
1053 "nsubseteq": []byte("&#8840;"),
1054 "nsucc": []byte("&nsc;"),
1055 "nsupseteq": []byte("&#8841;"),
1056 "ntilde": []byte("&#241;"),
1057 "ntriangleleft": []byte("&#8938;"),
1058 "ntrianglelefteq": []byte("&#8940;"),
1059 "ntriangleright": []byte("&#8939;"),
1060 "ntrianglerighteq": []byte("&#8941;"),
1061 "num": []byte("#"),
1062 "numero": []byte("&#8470;"),
1063 "nvDash": []byte("&#8877;"),
1064 "nvdash": []byte("&#8876;"),
1065 "nvinfin": []byte("&#10718;"),
1066 "nwarrow": []byte("&#8598;"),
1067 "oacute": []byte("&#243;"),
1068 "ocirc": []byte("&#244;"),
1069 "odblac": []byte("&#337;"),
1070 "oelig": []byte("&#339;"),
1071 "ograve": []byte("&#242;"),
1072 "olcross": []byte("&#10683;"),
1073 "omacr": []byte("&#333;"),
1074 "omega": []byte("&#969;"),
1075 "omicron": []byte("&#959;"),
1076 "ominus": []byte("&#8854;"),
1077 "order": []byte("&oscr;"),
1078 "orderof": []byte("&oscr;"),
1079 "origof": []byte("&#8886;"),
1080 "orslope": []byte("&#10839;"),
1081 "oslash": []byte("&#248;"),
1082 "otilde": []byte("&#245;"),
1083 "otimes": []byte("&#8855;"),
1084 "otimesas": []byte("&#10806;"),
1085 "parallel": []byte("&par;"),
1086 "percnt": []byte("%"),
1087 "period": []byte("."),
1088 "permil": []byte("&#8240;"),
1089 "perp": []byte("&bot;"),
1090 "pertenk": []byte("&#8241;"),
1091 "phmmat": []byte("&Mscr;"),
1092 "pitchfork": []byte("&fork;"),
1093 "planck": []byte("&hbar;"),
1094 "planckh": []byte("&#8462;"),
1095 "plankv": []byte("&hbar;"),
1096 "plus": []byte("+"),
1097 "plusacir": []byte("&#10787;"),
1098 "pluscir": []byte("&#10786;"),
1099 "plusdo": []byte("&#8724;"),
1100 "plusmn": []byte("&pm;"),
1101 "plussim": []byte("&#10790;"),
1102 "plustwo": []byte("&#10791;"),
1103 "pointint": []byte("&#10773;"),
1104 "pound": []byte("&#163;"),
1105 "prec": []byte("&pr;"),
1106 "precapprox": []byte("&prap;"),
1107 "preccurlyeq": []byte("&#8828;"),
1108 "preceq": []byte("&pre;"),
1109 "precnapprox": []byte("&prnap;"),
1110 "precneqq": []byte("&prnE;"),
1111 "precnsim": []byte("&#8936;"),
1112 "precsim": []byte("&#8830;"),
1113 "primes": []byte("&Popf;"),
1114 "prnsim": []byte("&#8936;"),
1115 "profalar": []byte("&#9006;"),
1116 "profline": []byte("&#8978;"),
1117 "profsurf": []byte("&#8979;"),
1118 "propto": []byte("&prop;"),
1119 "prurel": []byte("&#8880;"),
1120 "puncsp": []byte("&#8200;"),
1121 "qprime": []byte("&#8279;"),
1122 "quaternions": []byte("&Hopf;"),
1123 "quatint": []byte("&#10774;"),
1124 "quest": []byte("?"),
1125 "questeq": []byte("&#8799;"),
1126 "quot": []byte("\""),
1127 "racute": []byte("&#341;"),
1128 "radic": []byte("&Sqrt;"),
1129 "raemptyv": []byte("&#10675;"),
1130 "rangle": []byte("&rang;"),
1131 "raquo": []byte("&#187;"),
1132 "rarrbfs": []byte("&#10528;"),
1133 "rarrhk": []byte("&#8618;"),
1134 "rarrlp": []byte("&#8620;"),
1135 "rarrsim": []byte("&#10612;"),
1136 "rarrtl": []byte("&#8611;"),
1137 "rationals": []byte("&Qopf;"),
1138 "rbrace": []byte("}"),
1139 "rbrack": []byte("]"),
1140 "rbrksld": []byte("&#10638;"),
1141 "rbrkslu": []byte("&#10640;"),
1142 "rcaron": []byte("&#345;"),
1143 "rcedil": []byte("&#343;"),
1144 "rcub": []byte("}"),
1145 "rdldhar": []byte("&#10601;"),
1146 "rdquor": []byte("&#8221;"),
1147 "real": []byte("&Re;"),
1148 "realine": []byte("&Rscr;"),
1149 "realpart": []byte("&Re;"),
1150 "reals": []byte("&Ropf;"),
1151 "rfloor": []byte("&#8971;"),
1152 "rightarrow": []byte("&rarr;"),
1153 "rightarrowtail": []byte("&#8611;"),
1154 "rightharpoondown": []byte("&#8641;"),
1155 "rightharpoonup": []byte("&#8640;"),
1156 "rightleftarrows": []byte("&#8644;"),
1157 "rightleftharpoons": []byte("&#8652;"),
1158 "rightrightarrows": []byte("&#8649;"),
1159 "rightsquigarrow": []byte("&#8605;"),
1160 "rightthreetimes": []byte("&#8908;"),
1161 "risingdotseq": []byte("&#8787;"),
1162 "rmoust": []byte("&#9137;"),
1163 "rmoustache": []byte("&#9137;"),
1164 "rotimes": []byte("&#10805;"),
1165 "rpar": []byte(")"),
1166 "rppolint": []byte("&#10770;"),
1167 "rsaquo": []byte("&#8250;"),
1168 "rsqb": []byte("]"),
1169 "rsquor": []byte("&#8217;"),
1170 "rthree": []byte("&#8908;"),
1171 "rtimes": []byte("&#8906;"),
1172 "rtriltri": []byte("&#10702;"),
1173 "ruluhar": []byte("&#10600;"),
1174 "sacute": []byte("&#347;"),
1175 "scaron": []byte("&#353;"),
1176 "scedil": []byte("&#351;"),
1177 "scirc": []byte("&#349;"),
1178 "scnsim": []byte("&#8937;"),
1179 "scpolint": []byte("&#10771;"),
1180 "searrow": []byte("&#8600;"),
1181 "semi": []byte(";"),
1182 "seswar": []byte("&tosa;"),
1183 "setminus": []byte("&#8726;"),
1184 "sfrown": []byte("&#8994;"),
1185 "shchcy": []byte("&#1097;"),
1186 "shortmid": []byte("&mid;"),
1187 "shortparallel": []byte("&par;"),
1188 "sigma": []byte("&#963;"),
1189 "sigmaf": []byte("&#962;"),
1190 "sigmav": []byte("&#962;"),
1191 "simeq": []byte("&sime;"),
1192 "simplus": []byte("&#10788;"),
1193 "simrarr": []byte("&#10610;"),
1194 "slarr": []byte("&larr;"),
1195 "smallsetminus": []byte("&#8726;"),
1196 "smeparsl": []byte("&#10724;"),
1197 "smid": []byte("&mid;"),
1198 "softcy": []byte("&#1100;"),
1199 "sol": []byte("/"),
1200 "solbar": []byte("&#9023;"),
1201 "spades": []byte("&#9824;"),
1202 "spadesuit": []byte("&#9824;"),
1203 "spar": []byte("&par;"),
1204 "sqsube": []byte("&#8849;"),
1205 "sqsubset": []byte("&#8847;"),
1206 "sqsubseteq": []byte("&#8849;"),
1207 "sqsupe": []byte("&#8850;"),
1208 "sqsupset": []byte("&#8848;"),
1209 "sqsupseteq": []byte("&#8850;"),
1210 "square": []byte("&squ;"),
1211 "squarf": []byte("&squf;"),
1212 "srarr": []byte("&rarr;"),
1213 "ssetmn": []byte("&#8726;"),
1214 "ssmile": []byte("&#8995;"),
1215 "sstarf": []byte("&Star;"),
1216 "straightepsilon": []byte("&#1013;"),
1217 "straightphi": []byte("&#981;"),
1218 "strns": []byte("&#175;"),
1219 "subedot": []byte("&#10947;"),
1220 "submult": []byte("&#10945;"),
1221 "subplus": []byte("&#10943;"),
1222 "subrarr": []byte("&#10617;"),
1223 "subset": []byte("&sub;"),
1224 "subseteq": []byte("&sube;"),
1225 "subseteqq": []byte("&subE;"),
1226 "subsetneq": []byte("&#8842;"),
1227 "subsetneqq": []byte("&subnE;"),
1228 "succ": []byte("&sc;"),
1229 "succapprox": []byte("&scap;"),
1230 "succcurlyeq": []byte("&#8829;"),
1231 "succeq": []byte("&sce;"),
1232 "succnapprox": []byte("&scnap;"),
1233 "succneqq": []byte("&scnE;"),
1234 "succnsim": []byte("&#8937;"),
1235 "succsim": []byte("&#8831;"),
1236 "supdsub": []byte("&#10968;"),
1237 "supedot": []byte("&#10948;"),
1238 "suphsol": []byte("&#10185;"),
1239 "suphsub": []byte("&#10967;"),
1240 "suplarr": []byte("&#10619;"),
1241 "supmult": []byte("&#10946;"),
1242 "supplus": []byte("&#10944;"),
1243 "supset": []byte("&sup;"),
1244 "supseteq": []byte("&supe;"),
1245 "supseteqq": []byte("&supE;"),
1246 "supsetneq": []byte("&#8843;"),
1247 "supsetneqq": []byte("&supnE;"),
1248 "swarrow": []byte("&#8601;"),
1249 "szlig": []byte("&#223;"),
1250 "target": []byte("&#8982;"),
1251 "tcaron": []byte("&#357;"),
1252 "tcedil": []byte("&#355;"),
1253 "telrec": []byte("&#8981;"),
1254 "there4": []byte("&#8756;"),
1255 "therefore": []byte("&#8756;"),
1256 "theta": []byte("&#952;"),
1257 "thetasym": []byte("&#977;"),
1258 "thetav": []byte("&#977;"),
1259 "thickapprox": []byte("&ap;"),
1260 "thicksim": []byte("&sim;"),
1261 "thinsp": []byte("&#8201;"),
1262 "thkap": []byte("&ap;"),
1263 "thksim": []byte("&sim;"),
1264 "thorn": []byte("&#254;"),
1265 "tilde": []byte("&#732;"),
1266 "times": []byte("&#215;"),
1267 "timesb": []byte("&#8864;"),
1268 "timesbar": []byte("&#10801;"),
1269 "topbot": []byte("&#9014;"),
1270 "topfork": []byte("&#10970;"),
1271 "tprime": []byte("&#8244;"),
1272 "triangle": []byte("&utri;"),
1273 "triangledown": []byte("&dtri;"),
1274 "triangleleft": []byte("&ltri;"),
1275 "trianglelefteq": []byte("&#8884;"),
1276 "triangleq": []byte("&trie;"),
1277 "triangleright": []byte("&rtri;"),
1278 "trianglerighteq": []byte("&#8885;"),
1279 "tridot": []byte("&#9708;"),
1280 "triminus": []byte("&#10810;"),
1281 "triplus": []byte("&#10809;"),
1282 "tritime": []byte("&#10811;"),
1283 "trpezium": []byte("&#9186;"),
1284 "tstrok": []byte("&#359;"),
1285 "twoheadleftarrow": []byte("&Larr;"),
1286 "twoheadrightarrow": []byte("&Rarr;"),
1287 "uacute": []byte("&#250;"),
1288 "ubreve": []byte("&#365;"),
1289 "ucirc": []byte("&#251;"),
1290 "udblac": []byte("&#369;"),
1291 "ugrave": []byte("&#249;"),
1292 "ulcorn": []byte("&#8988;"),
1293 "ulcorner": []byte("&#8988;"),
1294 "ulcrop": []byte("&#8975;"),
1295 "umacr": []byte("&#363;"),
1296 "uogon": []byte("&#371;"),
1297 "uparrow": []byte("&uarr;"),
1298 "updownarrow": []byte("&varr;"),
1299 "upharpoonleft": []byte("&#8639;"),
1300 "upharpoonright": []byte("&#8638;"),
1301 "upsih": []byte("&#978;"),
1302 "upsilon": []byte("&#965;"),
1303 "upuparrows": []byte("&#8648;"),
1304 "urcorn": []byte("&#8989;"),
1305 "urcorner": []byte("&#8989;"),
1306 "urcrop": []byte("&#8974;"),
1307 "uring": []byte("&#367;"),
1308 "utilde": []byte("&#361;"),
1309 "uwangle": []byte("&#10663;"),
1310 "varepsilon": []byte("&#1013;"),
1311 "varkappa": []byte("&#1008;"),
1312 "varnothing": []byte("&#8709;"),
1313 "varphi": []byte("&#981;"),
1314 "varpi": []byte("&piv;"),
1315 "varpropto": []byte("&prop;"),
1316 "varrho": []byte("&rhov;"),
1317 "varsigma": []byte("&#962;"),
1318 "vartheta": []byte("&#977;"),
1319 "vartriangleleft": []byte("&#8882;"),
1320 "vartriangleright": []byte("&#8883;"),
1321 "vee": []byte("&or;"),
1322 "veebar": []byte("&#8891;"),
1323 "vellip": []byte("&#8942;"),
1324 "verbar": []byte("|"),
1325 "vert": []byte("|"),
1326 "vprop": []byte("&prop;"),
1327 "vzigzag": []byte("&#10650;"),
1328 "wcirc": []byte("&#373;"),
1329 "wedge": []byte("&and;"),
1330 "wedgeq": []byte("&#8793;"),
1331 "weierp": []byte("&wp;"),
1332 "wreath": []byte("&wr;"),
1333 "xvee": []byte("&Vee;"),
1334 "xwedge": []byte("&#8896;"),
1335 "yacute": []byte("&#253;"),
1336 "ycirc": []byte("&#375;"),
1337 "zacute": []byte("&#378;"),
1338 "zcaron": []byte("&#382;"),
1339 "zeetrf": []byte("&Zfr;"),
1340 "zigrarr": []byte("&#8669;"),
1341}
1342
1343// TextRevEntitiesMap is a map of escapes.
1344var TextRevEntitiesMap = map[byte][]byte{
1345 '<': []byte("&lt;"),
1346}
diff --git a/vendor/github.com/tdewolff/minify/v2/js/js.go b/vendor/github.com/tdewolff/minify/v2/js/js.go
new file mode 100644
index 0000000..1f6cefe
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/js/js.go
@@ -0,0 +1,1277 @@
1// Package js minifies ECMAScript 2021 following the language specification at https://tc39.es/ecma262/.
2package js
3
4import (
5 "bytes"
6 "io"
7
8 "github.com/tdewolff/minify/v2"
9 "github.com/tdewolff/parse/v2"
10 "github.com/tdewolff/parse/v2/js"
11)
12
13type blockType int
14
15const (
16 defaultBlock blockType = iota
17 functionBlock
18 iterationBlock
19)
20
21// Minifier is a JS minifier.
22type Minifier struct {
23 Precision int // number of significant digits
24 KeepVarNames bool
25 useAlphabetVarNames bool
26 Version int
27}
28
29func (o *Minifier) minVersion(version int) bool {
30 return o.Version == 0 || version <= o.Version
31}
32
33// Minify minifies JS data, it reads from r and writes to w.
34func Minify(m *minify.M, w io.Writer, r io.Reader, params map[string]string) error {
35 return (&Minifier{}).Minify(m, w, r, params)
36}
37
38// Minify minifies JS data, it reads from r and writes to w.
39func (o *Minifier) Minify(_ *minify.M, w io.Writer, r io.Reader, _ map[string]string) error {
40 z := parse.NewInput(r)
41 ast, err := js.Parse(z, js.Options{WhileToFor: true})
42 if err != nil {
43 return err
44 }
45
46 // license comments
47 for _, comment := range ast.Comments {
48 if 3 < len(comment) && comment[2] == '!' {
49 w.Write(comment)
50 if comment[1] == '/' {
51 w.Write(newlineBytes)
52 }
53 } else if 2 < len(comment) && comment[0] == '#' && comment[1] == '!' {
54 w.Write(comment)
55 }
56 }
57
58 m := &jsMinifier{
59 o: o,
60 w: w,
61 renamer: newRenamer(!o.KeepVarNames, !o.useAlphabetVarNames),
62 }
63 m.hoistVars(&ast.BlockStmt)
64 ast.List = optimizeStmtList(ast.List, functionBlock)
65 for _, item := range ast.List {
66 m.writeSemicolon()
67 m.minifyStmt(item)
68 }
69
70 if _, err := w.Write(nil); err != nil {
71 return err
72 }
73 return nil
74}
75
76type expectExpr int
77
78const (
79 expectAny expectExpr = iota
80 expectExprStmt // in statement
81 expectExprBody // in arrow function body
82)
83
84type jsMinifier struct {
85 o *Minifier
86 w io.Writer
87
88 prev []byte
89 needsSemicolon bool // write a semicolon if required
90 needsSpace bool // write a space if next token is an identifier
91 expectExpr expectExpr // avoid ambiguous syntax such as an expression starting with function
92 groupedStmt bool // avoid ambiguous syntax by grouping the expression statement
93 inFor bool
94 spaceBefore byte
95
96 renamer *renamer
97}
98
99func (m *jsMinifier) write(b []byte) {
100 // 0 < len(b)
101 if m.needsSpace && js.IsIdentifierContinue(b) || m.spaceBefore == b[0] {
102 m.w.Write(spaceBytes)
103 }
104 m.w.Write(b)
105 m.prev = b
106 m.needsSpace = false
107 m.expectExpr = expectAny
108 m.spaceBefore = 0
109}
110
111func (m *jsMinifier) writeSpaceAfterIdent() {
112 // space after identifier and after regular expression (to prevent confusion with its tag)
113 if js.IsIdentifierEnd(m.prev) || 1 < len(m.prev) && m.prev[0] == '/' {
114 m.w.Write(spaceBytes)
115 }
116}
117
118func (m *jsMinifier) writeSpaceBeforeIdent() {
119 m.needsSpace = true
120}
121
122func (m *jsMinifier) writeSpaceBefore(c byte) {
123 m.spaceBefore = c
124}
125
126func (m *jsMinifier) requireSemicolon() {
127 m.needsSemicolon = true
128}
129
130func (m *jsMinifier) writeSemicolon() {
131 if m.needsSemicolon {
132 m.w.Write(semicolonBytes)
133 m.needsSemicolon = false
134 m.needsSpace = false
135 }
136}
137
138func (m *jsMinifier) minifyStmt(i js.IStmt) {
139 switch stmt := i.(type) {
140 case *js.ExprStmt:
141 m.expectExpr = expectExprStmt
142 m.minifyExpr(stmt.Value, js.OpExpr)
143 if m.groupedStmt {
144 m.write(closeParenBytes)
145 m.groupedStmt = false
146 }
147 m.requireSemicolon()
148 case *js.VarDecl:
149 m.minifyVarDecl(stmt, false)
150 m.requireSemicolon()
151 case *js.IfStmt:
152 hasIf := !isEmptyStmt(stmt.Body)
153 hasElse := !isEmptyStmt(stmt.Else)
154 if !hasIf && !hasElse {
155 break
156 }
157
158 m.write(ifOpenBytes)
159 m.minifyExpr(stmt.Cond, js.OpExpr)
160 m.write(closeParenBytes)
161
162 if !hasIf && hasElse {
163 m.requireSemicolon()
164 } else if hasIf {
165 if hasElse && endsInIf(stmt.Body) {
166 // prevent: if(a){if(b)c}else d; => if(a)if(b)c;else d;
167 m.write(openBraceBytes)
168 m.minifyStmt(stmt.Body)
169 m.write(closeBraceBytes)
170 m.needsSemicolon = false
171 } else {
172 m.minifyStmt(stmt.Body)
173 }
174 }
175 if hasElse {
176 m.writeSemicolon()
177 m.write(elseBytes)
178 m.writeSpaceBeforeIdent()
179 m.minifyStmt(stmt.Else)
180 }
181 case *js.BlockStmt:
182 m.renamer.renameScope(stmt.Scope)
183 m.minifyBlockStmt(stmt)
184 case *js.ReturnStmt:
185 m.write(returnBytes)
186 m.writeSpaceBeforeIdent()
187 m.minifyExpr(stmt.Value, js.OpExpr)
188 m.requireSemicolon()
189 case *js.LabelledStmt:
190 m.write(stmt.Label)
191 m.write(colonBytes)
192 m.minifyStmtOrBlock(stmt.Value, defaultBlock)
193 case *js.BranchStmt:
194 m.write(stmt.Type.Bytes())
195 if stmt.Label != nil {
196 m.write(spaceBytes)
197 m.write(stmt.Label)
198 }
199 m.requireSemicolon()
200 case *js.WithStmt:
201 m.write(withOpenBytes)
202 m.minifyExpr(stmt.Cond, js.OpExpr)
203 m.write(closeParenBytes)
204 m.minifyStmtOrBlock(stmt.Body, defaultBlock)
205 case *js.DoWhileStmt:
206 m.write(doBytes)
207 m.writeSpaceBeforeIdent()
208 m.minifyStmtOrBlock(stmt.Body, iterationBlock)
209 m.writeSemicolon()
210 m.write(whileOpenBytes)
211 m.minifyExpr(stmt.Cond, js.OpExpr)
212 m.write(closeParenBytes)
213 case *js.WhileStmt:
214 m.write(whileOpenBytes)
215 m.minifyExpr(stmt.Cond, js.OpExpr)
216 m.write(closeParenBytes)
217 m.minifyStmtOrBlock(stmt.Body, iterationBlock)
218 case *js.ForStmt:
219 stmt.Body.List = optimizeStmtList(stmt.Body.List, iterationBlock)
220 m.renamer.renameScope(stmt.Body.Scope)
221 m.write(forOpenBytes)
222 m.inFor = true
223 if decl, ok := stmt.Init.(*js.VarDecl); ok {
224 m.minifyVarDecl(decl, true)
225 } else {
226 m.minifyExpr(stmt.Init, js.OpLHS)
227 }
228 m.inFor = false
229 m.write(semicolonBytes)
230 m.minifyExpr(stmt.Cond, js.OpExpr)
231 m.write(semicolonBytes)
232 m.minifyExpr(stmt.Post, js.OpExpr)
233 m.write(closeParenBytes)
234 m.minifyBlockAsStmt(stmt.Body)
235 case *js.ForInStmt:
236 stmt.Body.List = optimizeStmtList(stmt.Body.List, iterationBlock)
237 m.renamer.renameScope(stmt.Body.Scope)
238 m.write(forOpenBytes)
239 m.inFor = true
240 if decl, ok := stmt.Init.(*js.VarDecl); ok {
241 m.minifyVarDecl(decl, false)
242 } else {
243 m.minifyExpr(stmt.Init, js.OpLHS)
244 }
245 m.inFor = false
246 m.writeSpaceAfterIdent()
247 m.write(inBytes)
248 m.writeSpaceBeforeIdent()
249 m.minifyExpr(stmt.Value, js.OpExpr)
250 m.write(closeParenBytes)
251 m.minifyBlockAsStmt(stmt.Body)
252 case *js.ForOfStmt:
253 stmt.Body.List = optimizeStmtList(stmt.Body.List, iterationBlock)
254 m.renamer.renameScope(stmt.Body.Scope)
255 if stmt.Await {
256 m.write(forAwaitOpenBytes)
257 } else {
258 m.write(forOpenBytes)
259 }
260 m.inFor = true
261 if decl, ok := stmt.Init.(*js.VarDecl); ok {
262 m.minifyVarDecl(decl, false)
263 } else {
264 m.minifyExpr(stmt.Init, js.OpLHS)
265 }
266 m.inFor = false
267 m.writeSpaceAfterIdent()
268 m.write(ofBytes)
269 m.writeSpaceBeforeIdent()
270 m.minifyExpr(stmt.Value, js.OpAssign)
271 m.write(closeParenBytes)
272 m.minifyBlockAsStmt(stmt.Body)
273 case *js.SwitchStmt:
274 m.write(switchOpenBytes)
275 m.minifyExpr(stmt.Init, js.OpExpr)
276 m.write(closeParenOpenBracketBytes)
277 m.needsSemicolon = false
278 for i, _ := range stmt.List {
279 stmt.List[i].List = optimizeStmtList(stmt.List[i].List, defaultBlock)
280 }
281 m.renamer.renameScope(stmt.Scope)
282 for _, clause := range stmt.List {
283 m.writeSemicolon()
284 m.write(clause.TokenType.Bytes())
285 if clause.Cond != nil {
286 m.writeSpaceBeforeIdent()
287 m.minifyExpr(clause.Cond, js.OpExpr)
288 }
289 m.write(colonBytes)
290 for _, item := range clause.List {
291 m.writeSemicolon()
292 m.minifyStmt(item)
293 }
294 }
295 m.write(closeBraceBytes)
296 m.needsSemicolon = false
297 case *js.ThrowStmt:
298 m.write(throwBytes)
299 m.writeSpaceBeforeIdent()
300 m.minifyExpr(stmt.Value, js.OpExpr)
301 m.requireSemicolon()
302 case *js.TryStmt:
303 m.write(tryBytes)
304 stmt.Body.List = optimizeStmtList(stmt.Body.List, defaultBlock)
305 m.renamer.renameScope(stmt.Body.Scope)
306 m.minifyBlockStmt(stmt.Body)
307 if stmt.Catch != nil {
308 m.write(catchBytes)
309 stmt.Catch.List = optimizeStmtList(stmt.Catch.List, defaultBlock)
310 if v, ok := stmt.Binding.(*js.Var); ok && v.Uses == 1 && m.o.minVersion(2019) {
311 stmt.Catch.Scope.Declared = stmt.Catch.Scope.Declared[1:]
312 stmt.Binding = nil
313 }
314 m.renamer.renameScope(stmt.Catch.Scope)
315 if stmt.Binding != nil {
316 m.write(openParenBytes)
317 m.minifyBinding(stmt.Binding)
318 m.write(closeParenBytes)
319 }
320 m.minifyBlockStmt(stmt.Catch)
321 }
322 if stmt.Finally != nil {
323 m.write(finallyBytes)
324 stmt.Finally.List = optimizeStmtList(stmt.Finally.List, defaultBlock)
325 m.renamer.renameScope(stmt.Finally.Scope)
326 m.minifyBlockStmt(stmt.Finally)
327 }
328 case *js.FuncDecl:
329 m.minifyFuncDecl(stmt, false)
330 case *js.ClassDecl:
331 m.minifyClassDecl(stmt)
332 case *js.DebuggerStmt:
333 m.write(debuggerBytes)
334 m.requireSemicolon()
335 case *js.EmptyStmt:
336 case *js.ImportStmt:
337 m.write(importBytes)
338 if stmt.Default != nil {
339 m.write(spaceBytes)
340 m.write(stmt.Default)
341 if len(stmt.List) != 0 {
342 m.write(commaBytes)
343 } else if stmt.Default != nil || len(stmt.List) != 0 {
344 m.write(spaceBytes)
345 }
346 }
347 if len(stmt.List) == 1 && len(stmt.List[0].Name) == 1 && stmt.List[0].Name[0] == '*' {
348 m.writeSpaceBeforeIdent()
349 m.minifyAlias(stmt.List[0])
350 if stmt.Default != nil || len(stmt.List) != 0 {
351 m.write(spaceBytes)
352 }
353 } else if 0 < len(stmt.List) {
354 m.write(openBraceBytes)
355 for i, item := range stmt.List {
356 if i != 0 {
357 m.write(commaBytes)
358 }
359 m.minifyAlias(item)
360 }
361 m.write(closeBraceBytes)
362 }
363 if stmt.Default != nil || len(stmt.List) != 0 {
364 m.write(fromBytes)
365 }
366 m.write(minifyString(stmt.Module, false))
367 m.requireSemicolon()
368 case *js.ExportStmt:
369 m.write(exportBytes)
370 if stmt.Decl != nil {
371 if stmt.Default {
372 m.write(spaceDefaultBytes)
373 m.writeSpaceBeforeIdent()
374 m.minifyExpr(stmt.Decl, js.OpAssign)
375 _, isHoistable := stmt.Decl.(*js.FuncDecl)
376 _, isClass := stmt.Decl.(*js.ClassDecl)
377 if !isHoistable && !isClass {
378 m.requireSemicolon()
379 }
380 } else {
381 m.writeSpaceBeforeIdent()
382 m.minifyStmt(stmt.Decl.(js.IStmt)) // can only be variable, function, or class decl
383 }
384 } else {
385 if len(stmt.List) == 1 && (len(stmt.List[0].Name) == 1 && stmt.List[0].Name[0] == '*' || stmt.List[0].Name == nil && len(stmt.List[0].Binding) == 1 && stmt.List[0].Binding[0] == '*') {
386 m.writeSpaceBeforeIdent()
387 m.minifyAlias(stmt.List[0])
388 if stmt.Module != nil && stmt.List[0].Name != nil {
389 m.write(spaceBytes)
390 }
391 } else if 0 < len(stmt.List) {
392 m.write(openBraceBytes)
393 for i, item := range stmt.List {
394 if i != 0 {
395 m.write(commaBytes)
396 }
397 m.minifyAlias(item)
398 }
399 m.write(closeBraceBytes)
400 }
401 if stmt.Module != nil {
402 m.write(fromBytes)
403 m.write(minifyString(stmt.Module, false))
404 }
405 m.requireSemicolon()
406 }
407 case *js.DirectivePrologueStmt:
408 stmt.Value[0] = '"'
409 stmt.Value[len(stmt.Value)-1] = '"'
410 m.write(stmt.Value)
411 m.requireSemicolon()
412 }
413}
414
415func (m *jsMinifier) minifyBlockStmt(stmt *js.BlockStmt) {
416 m.write(openBraceBytes)
417 m.needsSemicolon = false
418 for _, item := range stmt.List {
419 m.writeSemicolon()
420 m.minifyStmt(item)
421 }
422 m.write(closeBraceBytes)
423 m.needsSemicolon = false
424}
425
426func (m *jsMinifier) minifyBlockAsStmt(blockStmt *js.BlockStmt) {
427 // minify block when statement is expected, i.e. semicolon if empty or remove braces for single statement
428 // assume we already renamed the scope
429 hasLexicalVars := false
430 for _, v := range blockStmt.Scope.Declared[blockStmt.Scope.NumForDecls:] {
431 if v.Decl == js.LexicalDecl {
432 hasLexicalVars = true
433 break
434 }
435 }
436 if 1 < len(blockStmt.List) || hasLexicalVars {
437 m.minifyBlockStmt(blockStmt)
438 } else if len(blockStmt.List) == 1 {
439 m.minifyStmt(blockStmt.List[0])
440 } else {
441 m.write(semicolonBytes)
442 m.needsSemicolon = false
443 }
444}
445
446func (m *jsMinifier) minifyStmtOrBlock(i js.IStmt, blockType blockType) {
447 // minify stmt or a block
448 if blockStmt, ok := i.(*js.BlockStmt); ok {
449 blockStmt.List = optimizeStmtList(blockStmt.List, blockType)
450 m.renamer.renameScope(blockStmt.Scope)
451 m.minifyBlockAsStmt(blockStmt)
452 } else {
453 // optimizeStmtList can in some cases expand one stmt to two shorter stmts
454 list := optimizeStmtList([]js.IStmt{i}, blockType)
455 if len(list) == 1 {
456 m.minifyStmt(list[0])
457 } else if len(list) == 0 {
458 m.write(semicolonBytes)
459 m.needsSemicolon = false
460 } else {
461 m.minifyBlockStmt(&js.BlockStmt{List: list, Scope: js.Scope{}})
462 }
463 }
464}
465
466func (m *jsMinifier) minifyAlias(alias js.Alias) {
467 if alias.Name != nil {
468 if alias.Name[0] == '"' || alias.Name[0] == '\'' {
469 m.write(minifyString(alias.Name, false))
470 } else {
471 m.write(alias.Name)
472 }
473 if !bytes.Equal(alias.Name, starBytes) {
474 m.write(spaceBytes)
475 }
476 m.write(asSpaceBytes)
477 }
478 if alias.Binding != nil {
479 if alias.Binding[0] == '"' || alias.Binding[0] == '\'' {
480 m.write(minifyString(alias.Binding, false))
481 } else {
482 m.write(alias.Binding)
483 }
484 }
485}
486
487func (m *jsMinifier) minifyParams(params js.Params, removeUnused bool) {
488 // remove unused parameters from the end
489 j := len(params.List)
490 if removeUnused && params.Rest == nil {
491 for ; 0 < j; j-- {
492 if v, ok := params.List[j-1].Binding.(*js.Var); !ok || ok && 1 < v.Uses {
493 break
494 }
495 }
496 }
497
498 m.write(openParenBytes)
499 for i, item := range params.List[:j] {
500 if i != 0 {
501 m.write(commaBytes)
502 }
503 m.minifyBindingElement(item)
504 }
505 if params.Rest != nil {
506 if len(params.List) != 0 {
507 m.write(commaBytes)
508 }
509 m.write(ellipsisBytes)
510 m.minifyBinding(params.Rest)
511 }
512 m.write(closeParenBytes)
513}
514
515func (m *jsMinifier) minifyArguments(args js.Args) {
516 m.write(openParenBytes)
517 for i, item := range args.List {
518 if i != 0 {
519 m.write(commaBytes)
520 }
521 if item.Rest {
522 m.write(ellipsisBytes)
523 }
524 m.minifyExpr(item.Value, js.OpAssign)
525 }
526 m.write(closeParenBytes)
527}
528
529func (m *jsMinifier) minifyVarDecl(decl *js.VarDecl, onlyDefines bool) {
530 if len(decl.List) == 0 {
531 return
532 } else if decl.TokenType == js.ErrorToken {
533 // remove 'var' when hoisting variables
534 first := true
535 for _, item := range decl.List {
536 if item.Default != nil || !onlyDefines {
537 if !first {
538 m.write(commaBytes)
539 }
540 m.minifyBindingElement(item)
541 first = false
542 }
543 }
544 } else {
545 if decl.TokenType == js.VarToken && len(decl.List) <= 10000 {
546 // move single var decls forward and order for GZIP optimization
547 start := 0
548 if _, ok := decl.List[0].Binding.(*js.Var); !ok {
549 start++
550 }
551 for i := 0; i < len(decl.List); i++ {
552 item := decl.List[i]
553 if v, ok := item.Binding.(*js.Var); ok && item.Default == nil && len(v.Data) == 1 {
554 for j := start; j < len(decl.List); j++ {
555 if v2, ok := decl.List[j].Binding.(*js.Var); ok && decl.List[j].Default == nil && len(v2.Data) == 1 {
556 if m.renamer.identOrder[v2.Data[0]] < m.renamer.identOrder[v.Data[0]] {
557 continue
558 } else if m.renamer.identOrder[v2.Data[0]] == m.renamer.identOrder[v.Data[0]] {
559 break
560 }
561 }
562 decl.List = append(decl.List[:i], decl.List[i+1:]...)
563 decl.List = append(decl.List[:j], append([]js.BindingElement{item}, decl.List[j:]...)...)
564 break
565 }
566 }
567 }
568 }
569
570 m.write(decl.TokenType.Bytes())
571 m.writeSpaceBeforeIdent()
572 for i, item := range decl.List {
573 if i != 0 {
574 m.write(commaBytes)
575 }
576 m.minifyBindingElement(item)
577 }
578 }
579}
580
581func (m *jsMinifier) minifyFuncDecl(decl *js.FuncDecl, inExpr bool) {
582 parentRename := m.renamer.rename
583 m.renamer.rename = !decl.Body.Scope.HasWith && !m.o.KeepVarNames
584 m.hoistVars(&decl.Body)
585 decl.Body.List = optimizeStmtList(decl.Body.List, functionBlock)
586
587 if decl.Async {
588 m.write(asyncSpaceBytes)
589 }
590 m.write(functionBytes)
591 if decl.Generator {
592 m.write(starBytes)
593 }
594
595 // TODO: remove function name, really necessary?
596 //if decl.Name != nil && decl.Name.Uses == 1 {
597 // scope := decl.Body.Scope
598 // for i, vorig := range scope.Declared {
599 // if decl.Name == vorig {
600 // scope.Declared = append(scope.Declared[:i], scope.Declared[i+1:]...)
601 // }
602 // }
603 //}
604
605 if inExpr {
606 m.renamer.renameScope(decl.Body.Scope)
607 }
608 if decl.Name != nil && (!inExpr || 1 < decl.Name.Uses) {
609 if !decl.Generator {
610 m.write(spaceBytes)
611 }
612 m.write(decl.Name.Data)
613 }
614 if !inExpr {
615 m.renamer.renameScope(decl.Body.Scope)
616 }
617
618 m.minifyParams(decl.Params, true)
619 m.minifyBlockStmt(&decl.Body)
620 m.renamer.rename = parentRename
621}
622
623func (m *jsMinifier) minifyMethodDecl(decl *js.MethodDecl) {
624 parentRename := m.renamer.rename
625 m.renamer.rename = !decl.Body.Scope.HasWith && !m.o.KeepVarNames
626 m.hoistVars(&decl.Body)
627 decl.Body.List = optimizeStmtList(decl.Body.List, functionBlock)
628
629 if decl.Static {
630 m.write(staticBytes)
631 m.writeSpaceBeforeIdent()
632 }
633 if decl.Async {
634 m.write(asyncBytes)
635 if decl.Generator {
636 m.write(starBytes)
637 } else {
638 m.writeSpaceBeforeIdent()
639 }
640 } else if decl.Generator {
641 m.write(starBytes)
642 } else if decl.Get {
643 m.write(getBytes)
644 m.writeSpaceBeforeIdent()
645 } else if decl.Set {
646 m.write(setBytes)
647 m.writeSpaceBeforeIdent()
648 }
649 m.minifyPropertyName(decl.Name)
650 m.renamer.renameScope(decl.Body.Scope)
651 m.minifyParams(decl.Params, !decl.Set)
652 m.minifyBlockStmt(&decl.Body)
653 m.renamer.rename = parentRename
654}
655
656func (m *jsMinifier) minifyArrowFunc(decl *js.ArrowFunc) {
657 parentRename := m.renamer.rename
658 m.renamer.rename = !decl.Body.Scope.HasWith && !m.o.KeepVarNames
659 m.hoistVars(&decl.Body)
660 decl.Body.List = optimizeStmtList(decl.Body.List, functionBlock)
661
662 m.renamer.renameScope(decl.Body.Scope)
663 if decl.Async {
664 m.write(asyncBytes)
665 }
666 removeParens := false
667 if decl.Params.Rest == nil && len(decl.Params.List) == 1 && decl.Params.List[0].Default == nil {
668 if decl.Params.List[0].Binding == nil {
669 removeParens = true
670 } else if _, ok := decl.Params.List[0].Binding.(*js.Var); ok {
671 removeParens = true
672 }
673 }
674 if removeParens {
675 if decl.Async && decl.Params.List[0].Binding != nil {
676 // add space after async in: async a => ...
677 m.write(spaceBytes)
678 }
679 m.minifyBindingElement(decl.Params.List[0])
680 } else {
681 parentInFor := m.inFor
682 m.inFor = false
683 m.minifyParams(decl.Params, true)
684 m.inFor = parentInFor
685 }
686 m.write(arrowBytes)
687 removeBraces := false
688 if 0 < len(decl.Body.List) {
689 returnStmt, isReturn := decl.Body.List[len(decl.Body.List)-1].(*js.ReturnStmt)
690 if isReturn && returnStmt.Value != nil {
691 // merge expression statements to final return statement, remove function body braces
692 var list []js.IExpr
693 removeBraces = true
694 for _, item := range decl.Body.List[:len(decl.Body.List)-1] {
695 if expr, isExpr := item.(*js.ExprStmt); isExpr {
696 list = append(list, expr.Value)
697 } else {
698 removeBraces = false
699 break
700 }
701 }
702 if removeBraces {
703 list = append(list, returnStmt.Value)
704 expr := list[0]
705 if 0 < len(list) {
706 if 1 < len(list) {
707 expr = &js.CommaExpr{list}
708 }
709 expr = &js.GroupExpr{X: expr}
710 }
711 m.expectExpr = expectExprBody
712 m.minifyExpr(expr, js.OpAssign)
713 if m.groupedStmt {
714 m.write(closeParenBytes)
715 m.groupedStmt = false
716 }
717 }
718 } else if isReturn && returnStmt.Value == nil {
719 // remove empty return
720 decl.Body.List = decl.Body.List[:len(decl.Body.List)-1]
721 }
722 }
723 if !removeBraces {
724 m.minifyBlockStmt(&decl.Body)
725 }
726 m.renamer.rename = parentRename
727}
728
729func (m *jsMinifier) minifyClassDecl(decl *js.ClassDecl) {
730 m.write(classBytes)
731 if decl.Name != nil {
732 m.write(spaceBytes)
733 m.write(decl.Name.Data)
734 }
735 if decl.Extends != nil {
736 m.write(spaceExtendsBytes)
737 m.writeSpaceBeforeIdent()
738 m.minifyExpr(decl.Extends, js.OpLHS)
739 }
740 m.write(openBraceBytes)
741 m.needsSemicolon = false
742 for _, item := range decl.List {
743 m.writeSemicolon()
744 if item.StaticBlock != nil {
745 m.write(staticBytes)
746 m.minifyBlockStmt(item.StaticBlock)
747 } else if item.Method != nil {
748 m.minifyMethodDecl(item.Method)
749 } else {
750 if item.Static {
751 m.write(staticBytes)
752 if !item.Name.IsComputed() && item.Name.Literal.TokenType == js.IdentifierToken {
753 m.write(spaceBytes)
754 }
755 }
756 m.minifyPropertyName(item.Name)
757 if item.Init != nil {
758 m.write(equalBytes)
759 m.minifyExpr(item.Init, js.OpAssign)
760 }
761 m.requireSemicolon()
762 }
763 }
764 m.write(closeBraceBytes)
765 m.needsSemicolon = false
766}
767
768func (m *jsMinifier) minifyPropertyName(name js.PropertyName) {
769 if name.IsComputed() {
770 m.write(openBracketBytes)
771 m.minifyExpr(name.Computed, js.OpAssign)
772 m.write(closeBracketBytes)
773 } else if name.Literal.TokenType == js.StringToken {
774 m.write(minifyString(name.Literal.Data, false))
775 } else {
776 m.write(name.Literal.Data)
777 }
778}
779
780func (m *jsMinifier) minifyProperty(property js.Property) {
781 // property.Name is always set in ObjectLiteral
782 if property.Spread {
783 m.write(ellipsisBytes)
784 } else if v, ok := property.Value.(*js.Var); property.Name != nil && (!ok || !property.Name.IsIdent(v.Name())) {
785 // add 'old-name:' before BindingName as the latter will be renamed
786 m.minifyPropertyName(*property.Name)
787 m.write(colonBytes)
788 }
789 m.minifyExpr(property.Value, js.OpAssign)
790 if property.Init != nil {
791 m.write(equalBytes)
792 m.minifyExpr(property.Init, js.OpAssign)
793 }
794}
795
796func (m *jsMinifier) minifyBindingElement(element js.BindingElement) {
797 if element.Binding != nil {
798 parentInFor := m.inFor
799 m.inFor = false
800 m.minifyBinding(element.Binding)
801 m.inFor = parentInFor
802 if element.Default != nil {
803 m.write(equalBytes)
804 m.minifyExpr(element.Default, js.OpAssign)
805 }
806 }
807}
808
809func (m *jsMinifier) minifyBinding(ibinding js.IBinding) {
810 switch binding := ibinding.(type) {
811 case *js.Var:
812 m.write(binding.Data)
813 case *js.BindingArray:
814 m.write(openBracketBytes)
815 for i, item := range binding.List {
816 if i != 0 {
817 m.write(commaBytes)
818 }
819 m.minifyBindingElement(item)
820 }
821 if binding.Rest != nil {
822 if 0 < len(binding.List) {
823 m.write(commaBytes)
824 }
825 m.write(ellipsisBytes)
826 m.minifyBinding(binding.Rest)
827 }
828 m.write(closeBracketBytes)
829 case *js.BindingObject:
830 m.write(openBraceBytes)
831 for i, item := range binding.List {
832 if i != 0 {
833 m.write(commaBytes)
834 }
835 // item.Key is always set
836 if item.Key.IsComputed() {
837 m.minifyPropertyName(*item.Key)
838 m.write(colonBytes)
839 } else if v, ok := item.Value.Binding.(*js.Var); !ok || !item.Key.IsIdent(v.Data) {
840 // add 'old-name:' before BindingName as the latter will be renamed
841 m.minifyPropertyName(*item.Key)
842 m.write(colonBytes)
843 }
844 m.minifyBindingElement(item.Value)
845 }
846 if binding.Rest != nil {
847 if 0 < len(binding.List) {
848 m.write(commaBytes)
849 }
850 m.write(ellipsisBytes)
851 m.write(binding.Rest.Data)
852 }
853 m.write(closeBraceBytes)
854 }
855}
856
857func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
858 if cond, ok := i.(*js.CondExpr); ok {
859 i = m.optimizeCondExpr(cond, prec)
860 } else if unary, ok := i.(*js.UnaryExpr); ok {
861 i = optimizeUnaryExpr(unary, prec)
862 }
863
864 switch expr := i.(type) {
865 case *js.Var:
866 for expr.Link != nil {
867 expr = expr.Link
868 }
869 data := expr.Data
870 if bytes.Equal(data, undefinedBytes) { // TODO: only if not defined
871 if js.OpUnary < prec {
872 m.write(groupedVoidZeroBytes)
873 } else {
874 m.write(voidZeroBytes)
875 }
876 } else if bytes.Equal(data, infinityBytes) { // TODO: only if not defined
877 if js.OpMul < prec {
878 m.write(groupedOneDivZeroBytes)
879 } else {
880 m.write(oneDivZeroBytes)
881 }
882 } else {
883 m.write(data)
884 }
885 case *js.LiteralExpr:
886 if expr.TokenType == js.DecimalToken {
887 m.write(decimalNumber(expr.Data, m.o.Precision))
888 } else if expr.TokenType == js.BinaryToken {
889 m.write(binaryNumber(expr.Data, m.o.Precision))
890 } else if expr.TokenType == js.OctalToken {
891 m.write(octalNumber(expr.Data, m.o.Precision))
892 } else if expr.TokenType == js.HexadecimalToken {
893 m.write(hexadecimalNumber(expr.Data, m.o.Precision))
894 } else if expr.TokenType == js.TrueToken {
895 if js.OpUnary < prec {
896 m.write(groupedNotZeroBytes)
897 } else {
898 m.write(notZeroBytes)
899 }
900 } else if expr.TokenType == js.FalseToken {
901 if js.OpUnary < prec {
902 m.write(groupedNotOneBytes)
903 } else {
904 m.write(notOneBytes)
905 }
906 } else if expr.TokenType == js.StringToken {
907 m.write(minifyString(expr.Data, true))
908 } else if expr.TokenType == js.RegExpToken {
909 // </script>/ => < /script>/
910 if 0 < len(m.prev) && m.prev[len(m.prev)-1] == '<' && bytes.HasPrefix(expr.Data, regExpScriptBytes) {
911 m.write(spaceBytes)
912 }
913 m.write(minifyRegExp(expr.Data))
914 } else {
915 m.write(expr.Data)
916 }
917 case *js.BinaryExpr:
918 mergeBinaryExpr(expr)
919 if expr.X == nil {
920 m.minifyExpr(expr.Y, prec)
921 break
922 }
923
924 precLeft := binaryLeftPrecMap[expr.Op]
925 // convert (a,b)&&c into a,b&&c but not a=(b,c)&&d into a=(b,c&&d)
926 if prec <= js.OpExpr {
927 if group, ok := expr.X.(*js.GroupExpr); ok {
928 if comma, ok := group.X.(*js.CommaExpr); ok && js.OpAnd <= exprPrec(comma.List[len(comma.List)-1]) {
929 expr.X = group.X
930 precLeft = js.OpExpr
931 }
932 }
933 }
934 if expr.Op == js.InstanceofToken || expr.Op == js.InToken {
935 group := expr.Op == js.InToken && m.inFor
936 if group {
937 m.write(openParenBytes)
938 }
939 m.minifyExpr(expr.X, precLeft)
940 m.writeSpaceAfterIdent()
941 m.write(expr.Op.Bytes())
942 m.writeSpaceBeforeIdent()
943 m.minifyExpr(expr.Y, binaryRightPrecMap[expr.Op])
944 if group {
945 m.write(closeParenBytes)
946 }
947 } else {
948 // TODO: has effect on GZIP?
949 //if expr.Op == js.EqEqToken || expr.Op == js.NotEqToken || expr.Op == js.EqEqEqToken || expr.Op == js.NotEqEqToken {
950 // // switch a==const for const==a, such as typeof a=="undefined" for "undefined"==typeof a (GZIP improvement)
951 // if _, ok := expr.Y.(*js.LiteralExpr); ok {
952 // expr.X, expr.Y = expr.Y, expr.X
953 // }
954 //}
955
956 if v, not, ok := isUndefinedOrNullVar(expr); ok {
957 // change a===null||a===undefined to a==null
958 op := js.EqEqToken
959 if not {
960 op = js.NotEqToken
961 }
962 expr = &js.BinaryExpr{op, v, &js.LiteralExpr{js.NullToken, nullBytes}}
963 }
964
965 m.minifyExpr(expr.X, precLeft)
966 if expr.Op == js.GtToken && m.prev[len(m.prev)-1] == '-' {
967 // 0 < len(m.prev) always
968 m.write(spaceBytes)
969 } else if expr.Op == js.EqEqEqToken || expr.Op == js.NotEqEqToken {
970 if left, ok := expr.X.(*js.UnaryExpr); ok && left.Op == js.TypeofToken {
971 if right, ok := expr.Y.(*js.LiteralExpr); ok && right.TokenType == js.StringToken {
972 if expr.Op == js.EqEqEqToken {
973 expr.Op = js.EqEqToken
974 } else {
975 expr.Op = js.NotEqToken
976 }
977 }
978 } else if right, ok := expr.Y.(*js.UnaryExpr); ok && right.Op == js.TypeofToken {
979 if left, ok := expr.X.(*js.LiteralExpr); ok && left.TokenType == js.StringToken {
980 if expr.Op == js.EqEqEqToken {
981 expr.Op = js.EqEqToken
982 } else {
983 expr.Op = js.NotEqToken
984 }
985 }
986 }
987 }
988 m.write(expr.Op.Bytes())
989 if expr.Op == js.AddToken {
990 // +++ => + ++
991 m.writeSpaceBefore('+')
992 } else if expr.Op == js.SubToken {
993 // --- => - --
994 m.writeSpaceBefore('-')
995 } else if expr.Op == js.DivToken {
996 // // => / /
997 m.writeSpaceBefore('/')
998 }
999 m.minifyExpr(expr.Y, binaryRightPrecMap[expr.Op])
1000 }
1001 case *js.UnaryExpr:
1002 if expr.Op == js.PostIncrToken || expr.Op == js.PostDecrToken {
1003 m.minifyExpr(expr.X, unaryPrecMap[expr.Op])
1004 m.write(expr.Op.Bytes())
1005 } else {
1006 isLtNot := expr.Op == js.NotToken && 0 < len(m.prev) && m.prev[len(m.prev)-1] == '<'
1007 m.write(expr.Op.Bytes())
1008 if expr.Op == js.DeleteToken || expr.Op == js.VoidToken || expr.Op == js.TypeofToken || expr.Op == js.AwaitToken {
1009 m.writeSpaceBeforeIdent()
1010 } else if expr.Op == js.PosToken {
1011 // +++ => + ++
1012 m.writeSpaceBefore('+')
1013 } else if expr.Op == js.NegToken || isLtNot {
1014 // --- => - --
1015 // <!-- => <! --
1016 m.writeSpaceBefore('-')
1017 } else if expr.Op == js.NotToken {
1018 if lit, ok := expr.X.(*js.LiteralExpr); ok && (lit.TokenType == js.StringToken || lit.TokenType == js.RegExpToken) {
1019 // !"string" => !1
1020 m.write(oneBytes)
1021 break
1022 } else if ok && lit.TokenType == js.DecimalToken {
1023 // !123 => !1 (except for !0)
1024 if num := minify.Number(lit.Data, m.o.Precision); len(num) == 1 && num[0] == '0' {
1025 m.write(zeroBytes)
1026 } else {
1027 m.write(oneBytes)
1028 }
1029 break
1030 }
1031 }
1032 m.minifyExpr(expr.X, unaryPrecMap[expr.Op])
1033 }
1034 case *js.DotExpr:
1035 if group, ok := expr.X.(*js.GroupExpr); ok {
1036 if lit, ok := group.X.(*js.LiteralExpr); ok && lit.TokenType == js.DecimalToken {
1037 num := minify.Number(lit.Data, m.o.Precision)
1038 isInt := true
1039 for _, c := range num {
1040 if c == '.' || c == 'e' || c == 'E' {
1041 isInt = false
1042 break
1043 }
1044 }
1045 if isInt {
1046 m.write(num)
1047 m.write(dotBytes)
1048 } else {
1049 m.write(num)
1050 }
1051 m.write(dotBytes)
1052 m.write(expr.Y.Data)
1053 break
1054 }
1055 }
1056 if prec < js.OpMember {
1057 m.minifyExpr(expr.X, js.OpCall)
1058 } else {
1059 m.minifyExpr(expr.X, js.OpMember)
1060 }
1061 if expr.Optional {
1062 m.write(questionBytes)
1063 } else if last := m.prev[len(m.prev)-1]; '0' <= last && last <= '9' {
1064 // 0 < len(m.prev) always
1065 isInteger := true
1066 for _, c := range m.prev[:len(m.prev)-1] {
1067 if c < '0' || '9' < c {
1068 isInteger = false
1069 break
1070 }
1071 }
1072 if isInteger {
1073 // prevent previous integer
1074 m.write(dotBytes)
1075 }
1076 }
1077 m.write(dotBytes)
1078 m.write(expr.Y.Data)
1079 case *js.GroupExpr:
1080 if cond, ok := expr.X.(*js.CondExpr); ok {
1081 expr.X = m.optimizeCondExpr(cond, js.OpExpr)
1082 }
1083 precInside := exprPrec(expr.X)
1084 if prec <= precInside || precInside == js.OpCoalesce && prec == js.OpBitOr {
1085 m.minifyExpr(expr.X, prec)
1086 } else {
1087 parentInFor := m.inFor
1088 m.inFor = false
1089 m.write(openParenBytes)
1090 m.minifyExpr(expr.X, js.OpExpr)
1091 m.write(closeParenBytes)
1092 m.inFor = parentInFor
1093 }
1094 case *js.ArrayExpr:
1095 parentInFor := m.inFor
1096 m.inFor = false
1097 m.write(openBracketBytes)
1098 for i, item := range expr.List {
1099 if i != 0 {
1100 m.write(commaBytes)
1101 }
1102 if item.Spread {
1103 m.write(ellipsisBytes)
1104 }
1105 m.minifyExpr(item.Value, js.OpAssign)
1106 }
1107 if 0 < len(expr.List) && expr.List[len(expr.List)-1].Value == nil {
1108 m.write(commaBytes)
1109 }
1110 m.write(closeBracketBytes)
1111 m.inFor = parentInFor
1112 case *js.ObjectExpr:
1113 parentInFor := m.inFor
1114 m.inFor = false
1115 groupedStmt := m.expectExpr != expectAny
1116 if groupedStmt {
1117 m.write(openParenBracketBytes)
1118 } else {
1119 m.write(openBraceBytes)
1120 }
1121 for i, item := range expr.List {
1122 if i != 0 {
1123 m.write(commaBytes)
1124 }
1125 m.minifyProperty(item)
1126 }
1127 m.write(closeBraceBytes)
1128 if groupedStmt {
1129 m.groupedStmt = true
1130 }
1131 m.inFor = parentInFor
1132 case *js.TemplateExpr:
1133 if expr.Tag != nil {
1134 if prec < js.OpMember {
1135 m.minifyExpr(expr.Tag, js.OpCall)
1136 } else {
1137 m.minifyExpr(expr.Tag, js.OpMember)
1138 }
1139 if expr.Optional {
1140 m.write(optChainBytes)
1141 }
1142 }
1143 parentInFor := m.inFor
1144 m.inFor = false
1145 for _, item := range expr.List {
1146 m.write(replaceEscapes(item.Value, '`', 1, 2))
1147 m.minifyExpr(item.Expr, js.OpExpr)
1148 }
1149 m.write(replaceEscapes(expr.Tail, '`', 1, 1))
1150 m.inFor = parentInFor
1151 case *js.NewExpr:
1152 if expr.Args == nil && js.OpLHS < prec && prec != js.OpNew {
1153 m.write(openNewBytes)
1154 m.writeSpaceBeforeIdent()
1155 m.minifyExpr(expr.X, js.OpNew)
1156 m.write(closeParenBytes)
1157 } else {
1158 m.write(newBytes)
1159 m.writeSpaceBeforeIdent()
1160 if expr.Args != nil {
1161 m.minifyExpr(expr.X, js.OpMember)
1162 m.minifyArguments(*expr.Args)
1163 } else {
1164 m.minifyExpr(expr.X, js.OpNew)
1165 }
1166 }
1167 case *js.NewTargetExpr:
1168 m.write(newTargetBytes)
1169 m.writeSpaceBeforeIdent()
1170 case *js.ImportMetaExpr:
1171 if m.expectExpr == expectExprStmt {
1172 m.write(openParenBytes)
1173 m.groupedStmt = true
1174 }
1175 m.write(importMetaBytes)
1176 m.writeSpaceBeforeIdent()
1177 case *js.YieldExpr:
1178 m.write(yieldBytes)
1179 m.writeSpaceBeforeIdent()
1180 if expr.X != nil {
1181 if expr.Generator {
1182 m.write(starBytes)
1183 m.minifyExpr(expr.X, js.OpAssign)
1184 } else if v, ok := expr.X.(*js.Var); !ok || !bytes.Equal(v.Name(), undefinedBytes) { // TODO: only if not defined
1185 m.minifyExpr(expr.X, js.OpAssign)
1186 }
1187 }
1188 case *js.CallExpr:
1189 m.minifyExpr(expr.X, js.OpCall)
1190 parentInFor := m.inFor
1191 m.inFor = false
1192 if expr.Optional {
1193 m.write(optChainBytes)
1194 }
1195 m.minifyArguments(expr.Args)
1196 m.inFor = parentInFor
1197 case *js.IndexExpr:
1198 if m.expectExpr == expectExprStmt {
1199 if v, ok := expr.X.(*js.Var); ok && bytes.Equal(v.Name(), letBytes) {
1200 m.write(notBytes)
1201 }
1202 }
1203 if prec < js.OpMember {
1204 m.minifyExpr(expr.X, js.OpCall)
1205 } else {
1206 m.minifyExpr(expr.X, js.OpMember)
1207 }
1208 if expr.Optional {
1209 m.write(optChainBytes)
1210 }
1211 if lit, ok := expr.Y.(*js.LiteralExpr); ok && lit.TokenType == js.StringToken && 2 < len(lit.Data) {
1212 if isIdent := js.AsIdentifierName(lit.Data[1 : len(lit.Data)-1]); isIdent {
1213 m.write(dotBytes)
1214 m.write(lit.Data[1 : len(lit.Data)-1])
1215 break
1216 } else if isNum := js.AsDecimalLiteral(lit.Data[1 : len(lit.Data)-1]); isNum {
1217 m.write(openBracketBytes)
1218 m.write(minify.Number(lit.Data[1:len(lit.Data)-1], 0))
1219 m.write(closeBracketBytes)
1220 break
1221 }
1222 }
1223 parentInFor := m.inFor
1224 m.inFor = false
1225 m.write(openBracketBytes)
1226 m.minifyExpr(expr.Y, js.OpExpr)
1227 m.write(closeBracketBytes)
1228 m.inFor = parentInFor
1229 case *js.CondExpr:
1230 m.minifyExpr(expr.Cond, js.OpCoalesce)
1231 m.write(questionBytes)
1232 m.minifyExpr(expr.X, js.OpAssign)
1233 m.write(colonBytes)
1234 m.minifyExpr(expr.Y, js.OpAssign)
1235 case *js.VarDecl:
1236 m.minifyVarDecl(expr, true) // happens in for statement or when vars were hoisted
1237 case *js.FuncDecl:
1238 grouped := m.expectExpr == expectExprStmt && prec != js.OpExpr
1239 if grouped {
1240 m.write(openParenBytes)
1241 } else if m.expectExpr == expectExprStmt {
1242 m.write(notBytes)
1243 }
1244 parentInFor, parentGroupedStmt := m.inFor, m.groupedStmt
1245 m.inFor, m.groupedStmt = false, false
1246 m.minifyFuncDecl(expr, true)
1247 m.inFor, m.groupedStmt = parentInFor, parentGroupedStmt
1248 if grouped {
1249 m.write(closeParenBytes)
1250 }
1251 case *js.ArrowFunc:
1252 parentGroupedStmt := m.groupedStmt
1253 m.groupedStmt = false
1254 m.minifyArrowFunc(expr)
1255 m.groupedStmt = parentGroupedStmt
1256 case *js.MethodDecl:
1257 parentGroupedStmt := m.groupedStmt
1258 m.groupedStmt = false
1259 m.minifyMethodDecl(expr) // only happens in object literal
1260 m.groupedStmt = parentGroupedStmt
1261 case *js.ClassDecl:
1262 if m.expectExpr == expectExprStmt {
1263 m.write(notBytes)
1264 }
1265 parentInFor, parentGroupedStmt := m.inFor, m.groupedStmt
1266 m.inFor, m.groupedStmt = false, false
1267 m.minifyClassDecl(expr)
1268 m.inFor, m.groupedStmt = parentInFor, parentGroupedStmt
1269 case *js.CommaExpr:
1270 for i, item := range expr.List {
1271 if i != 0 {
1272 m.write(commaBytes)
1273 }
1274 m.minifyExpr(item, js.OpAssign)
1275 }
1276 }
1277}
diff --git a/vendor/github.com/tdewolff/minify/v2/js/stmtlist.go b/vendor/github.com/tdewolff/minify/v2/js/stmtlist.go
new file mode 100644
index 0000000..a1d3e2e
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/js/stmtlist.go
@@ -0,0 +1,341 @@
1package js
2
3import (
4 "github.com/tdewolff/parse/v2/js"
5)
6
7func optimizeStmt(i js.IStmt) js.IStmt {
8 // convert if/else into expression statement, and optimize blocks
9 if ifStmt, ok := i.(*js.IfStmt); ok {
10 hasIf := !isEmptyStmt(ifStmt.Body)
11 hasElse := !isEmptyStmt(ifStmt.Else)
12 if unaryExpr, ok := ifStmt.Cond.(*js.UnaryExpr); ok && unaryExpr.Op == js.NotToken && hasElse {
13 ifStmt.Cond = unaryExpr.X
14 ifStmt.Body, ifStmt.Else = ifStmt.Else, ifStmt.Body
15 hasIf, hasElse = hasElse, hasIf
16 }
17 if !hasIf && !hasElse {
18 return &js.ExprStmt{Value: ifStmt.Cond}
19 } else if hasIf && !hasElse {
20 ifStmt.Body = optimizeStmt(ifStmt.Body)
21 if X, isExprBody := ifStmt.Body.(*js.ExprStmt); isExprBody {
22 if unaryExpr, ok := ifStmt.Cond.(*js.UnaryExpr); ok && unaryExpr.Op == js.NotToken {
23 left := groupExpr(unaryExpr.X, binaryLeftPrecMap[js.OrToken])
24 right := groupExpr(X.Value, binaryRightPrecMap[js.OrToken])
25 return &js.ExprStmt{&js.BinaryExpr{js.OrToken, left, right}}
26 }
27 left := groupExpr(ifStmt.Cond, binaryLeftPrecMap[js.AndToken])
28 right := groupExpr(X.Value, binaryRightPrecMap[js.AndToken])
29 return &js.ExprStmt{&js.BinaryExpr{js.AndToken, left, right}}
30 } else if X, isIfStmt := ifStmt.Body.(*js.IfStmt); isIfStmt && isEmptyStmt(X.Else) {
31 left := groupExpr(ifStmt.Cond, binaryLeftPrecMap[js.AndToken])
32 right := groupExpr(X.Cond, binaryRightPrecMap[js.AndToken])
33 ifStmt.Cond = &js.BinaryExpr{js.AndToken, left, right}
34 ifStmt.Body = X.Body
35 return ifStmt
36 }
37 } else if !hasIf && hasElse {
38 ifStmt.Else = optimizeStmt(ifStmt.Else)
39 if X, isExprElse := ifStmt.Else.(*js.ExprStmt); isExprElse {
40 left := groupExpr(ifStmt.Cond, binaryLeftPrecMap[js.OrToken])
41 right := groupExpr(X.Value, binaryRightPrecMap[js.OrToken])
42 return &js.ExprStmt{&js.BinaryExpr{js.OrToken, left, right}}
43 }
44 } else if hasIf && hasElse {
45 ifStmt.Body = optimizeStmt(ifStmt.Body)
46 ifStmt.Else = optimizeStmt(ifStmt.Else)
47 XExpr, isExprBody := ifStmt.Body.(*js.ExprStmt)
48 YExpr, isExprElse := ifStmt.Else.(*js.ExprStmt)
49 if isExprBody && isExprElse {
50 return &js.ExprStmt{condExpr(ifStmt.Cond, XExpr.Value, YExpr.Value)}
51 }
52 XReturn, isReturnBody := ifStmt.Body.(*js.ReturnStmt)
53 YReturn, isReturnElse := ifStmt.Else.(*js.ReturnStmt)
54 if isReturnBody && isReturnElse {
55 if XReturn.Value == nil && YReturn.Value == nil {
56 return &js.ReturnStmt{commaExpr(ifStmt.Cond, &js.UnaryExpr{
57 Op: js.VoidToken,
58 X: &js.LiteralExpr{js.NumericToken, zeroBytes},
59 })}
60 } else if XReturn.Value != nil && YReturn.Value != nil {
61 return &js.ReturnStmt{condExpr(ifStmt.Cond, XReturn.Value, YReturn.Value)}
62 }
63 return ifStmt
64 }
65 XThrow, isThrowBody := ifStmt.Body.(*js.ThrowStmt)
66 YThrow, isThrowElse := ifStmt.Else.(*js.ThrowStmt)
67 if isThrowBody && isThrowElse {
68 return &js.ThrowStmt{condExpr(ifStmt.Cond, XThrow.Value, YThrow.Value)}
69 }
70 }
71 } else if decl, ok := i.(*js.VarDecl); ok {
72 // TODO: remove function name in var name=function name(){}
73 //for _, item := range decl.List {
74 // if v, ok := item.Binding.(*js.Var); ok && item.Default != nil {
75 // if fun, ok := item.Default.(*js.FuncDecl); ok && fun.Name != nil && bytes.Equal(v.Data, fun.Name.Data) {
76 // scope := fun.Body.Scope
77 // for i, vorig := range scope.Declared {
78 // if fun.Name == vorig {
79 // scope.Declared = append(scope.Declared[:i], scope.Declared[i+1:]...)
80 // }
81 // }
82 // scope.AddUndeclared(v)
83 // v.Uses += fun.Name.Uses - 1
84 // fun.Name.Link = v
85 // fun.Name = nil
86 // }
87 // }
88 //}
89
90 if decl.TokenType == js.ErrorToken {
91 // convert hoisted var declaration to expression or empty (if there are no defines) statement
92 for _, item := range decl.List {
93 if item.Default != nil {
94 return &js.ExprStmt{Value: decl}
95 }
96 }
97 return &js.EmptyStmt{}
98 }
99 // TODO: remove unused declarations
100 //for i := 0; i < len(decl.List); i++ {
101 // if v, ok := decl.List[i].Binding.(*js.Var); ok && v.Uses < 2 {
102 // decl.List = append(decl.List[:i], decl.List[i+1:]...)
103 // i--
104 // }
105 //}
106 //if len(decl.List) == 0 {
107 // return &js.EmptyStmt{}
108 //}
109 return decl
110 } else if blockStmt, ok := i.(*js.BlockStmt); ok {
111 // merge body and remove braces if it is not a lexical declaration
112 blockStmt.List = optimizeStmtList(blockStmt.List, defaultBlock)
113 if len(blockStmt.List) == 1 {
114 if _, ok := blockStmt.List[0].(*js.ClassDecl); ok {
115 return &js.EmptyStmt{}
116 } else if varDecl, ok := blockStmt.List[0].(*js.VarDecl); ok && varDecl.TokenType != js.VarToken {
117 // remove let or const declaration in otherwise empty scope, but keep assignments
118 exprs := []js.IExpr{}
119 for _, item := range varDecl.List {
120 if item.Default != nil && hasSideEffects(item.Default) {
121 exprs = append(exprs, item.Default)
122 }
123 }
124 if len(exprs) == 0 {
125 return &js.EmptyStmt{}
126 } else if len(exprs) == 1 {
127 return &js.ExprStmt{exprs[0]}
128 }
129 return &js.ExprStmt{&js.CommaExpr{exprs}}
130 }
131 return optimizeStmt(blockStmt.List[0])
132 } else if len(blockStmt.List) == 0 {
133 return &js.EmptyStmt{}
134 }
135 return blockStmt
136 }
137 return i
138}
139
140func optimizeStmtList(list []js.IStmt, blockType blockType) []js.IStmt {
141 // merge expression statements as well as if/else statements followed by flow control statements
142 if len(list) == 0 {
143 return list
144 }
145 j := 0 // write index
146 for i := 0; i < len(list); i++ { // read index
147 if ifStmt, ok := list[i].(*js.IfStmt); ok && !isEmptyStmt(ifStmt.Else) {
148 // if(!a)b;else c => if(a)c; else b
149 if unary, ok := ifStmt.Cond.(*js.UnaryExpr); ok && unary.Op == js.NotToken && isFlowStmt(lastStmt(ifStmt.Else)) {
150 ifStmt.Cond = unary.X
151 ifStmt.Body, ifStmt.Else = ifStmt.Else, ifStmt.Body
152 }
153 if isFlowStmt(lastStmt(ifStmt.Body)) {
154 // if body ends in flow statement (return, throw, break, continue), we can remove the else statement and put its body in the current scope
155 if blockStmt, ok := ifStmt.Else.(*js.BlockStmt); ok {
156 blockStmt.Scope.Unscope()
157 list = append(list[:i+1], append(blockStmt.List, list[i+1:]...)...)
158 } else {
159 list = append(list[:i+1], append([]js.IStmt{ifStmt.Else}, list[i+1:]...)...)
160 }
161 ifStmt.Else = nil
162 }
163 }
164
165 list[i] = optimizeStmt(list[i])
166
167 if _, ok := list[i].(*js.EmptyStmt); ok {
168 k := i + 1
169 for ; k < len(list); k++ {
170 if _, ok := list[k].(*js.EmptyStmt); !ok {
171 break
172 }
173 }
174 list = append(list[:i], list[k:]...)
175 i--
176 continue
177 }
178
179 if 0 < i {
180 // merge expression statements with expression, return, and throw statements
181 if left, ok := list[i-1].(*js.ExprStmt); ok {
182 if right, ok := list[i].(*js.ExprStmt); ok {
183 right.Value = commaExpr(left.Value, right.Value)
184 j--
185 } else if returnStmt, ok := list[i].(*js.ReturnStmt); ok && returnStmt.Value != nil {
186 returnStmt.Value = commaExpr(left.Value, returnStmt.Value)
187 j--
188 } else if throwStmt, ok := list[i].(*js.ThrowStmt); ok {
189 throwStmt.Value = commaExpr(left.Value, throwStmt.Value)
190 j--
191 } else if forStmt, ok := list[i].(*js.ForStmt); ok {
192 if varDecl, ok := forStmt.Init.(*js.VarDecl); ok && len(varDecl.List) == 0 || forStmt.Init == nil {
193 // TODO: only merge statements that don't have 'in' or 'of' keywords (slow to check?)
194 forStmt.Init = left.Value
195 j--
196 }
197 } else if whileStmt, ok := list[i].(*js.WhileStmt); ok {
198 // TODO: only merge statements that don't have 'in' or 'of' keywords (slow to check?)
199 var body *js.BlockStmt
200 if blockStmt, ok := whileStmt.Body.(*js.BlockStmt); ok {
201 body = blockStmt
202 } else {
203 body = &js.BlockStmt{}
204 body.List = []js.IStmt{whileStmt.Body}
205 }
206 list[i] = &js.ForStmt{Init: left.Value, Cond: whileStmt.Cond, Post: nil, Body: body}
207 j--
208 } else if switchStmt, ok := list[i].(*js.SwitchStmt); ok {
209 switchStmt.Init = commaExpr(left.Value, switchStmt.Init)
210 j--
211 } else if withStmt, ok := list[i].(*js.WithStmt); ok {
212 withStmt.Cond = commaExpr(left.Value, withStmt.Cond)
213 j--
214 } else if ifStmt, ok := list[i].(*js.IfStmt); ok {
215 ifStmt.Cond = commaExpr(left.Value, ifStmt.Cond)
216 j--
217 } else if varDecl, ok := list[i].(*js.VarDecl); ok && varDecl.TokenType == js.VarToken {
218 if merge := mergeVarDeclExprStmt(varDecl, left, true); merge {
219 j--
220 }
221 }
222 } else if left, ok := list[i-1].(*js.VarDecl); ok {
223 if right, ok := list[i].(*js.VarDecl); ok && left.TokenType == right.TokenType {
224 // merge const and let declarations, or non-hoisted var declarations
225 right.List = append(left.List, right.List...)
226 j--
227
228 // remove from vardecls list of scope
229 scope := left.Scope.Func
230 for i, decl := range scope.VarDecls {
231 if left == decl {
232 scope.VarDecls = append(scope.VarDecls[:i], scope.VarDecls[i+1:]...)
233 break
234 }
235 }
236 } else if left.TokenType == js.VarToken {
237 if exprStmt, ok := list[i].(*js.ExprStmt); ok {
238 // pull in assignments to variables into the declaration, e.g. var a;a=5 => var a=5
239 if merge := mergeVarDeclExprStmt(left, exprStmt, false); merge {
240 list[i] = list[i-1]
241 j--
242 }
243 } else if forStmt, ok := list[i].(*js.ForStmt); ok {
244 // TODO: only merge statements that don't have 'in' or 'of' keywords (slow to check?)
245 if forStmt.Init == nil {
246 forStmt.Init = left
247 j--
248 } else if decl, ok := forStmt.Init.(*js.VarDecl); ok && decl.TokenType == js.ErrorToken && !hasDefines(decl) {
249 forStmt.Init = left
250 j--
251 } else if ok && (decl.TokenType == js.VarToken || decl.TokenType == js.ErrorToken) {
252 // this is the second VarDecl, so we are hoisting var declarations, which means the forInit variables are already in 'left'
253 mergeVarDecls(left, decl, false)
254 decl.TokenType = js.VarToken
255 forStmt.Init = left
256 j--
257 }
258 } else if whileStmt, ok := list[i].(*js.WhileStmt); ok {
259 // TODO: only merge statements that don't have 'in' or 'of' keywords (slow to check?)
260 var body *js.BlockStmt
261 if blockStmt, ok := whileStmt.Body.(*js.BlockStmt); ok {
262 body = blockStmt
263 } else {
264 body = &js.BlockStmt{}
265 body.List = []js.IStmt{whileStmt.Body}
266 }
267 list[i] = &js.ForStmt{Init: left, Cond: whileStmt.Cond, Post: nil, Body: body}
268 j--
269 }
270 }
271 }
272 }
273 list[j] = list[i]
274
275 // merge if/else with return/throw when followed by return/throw
276 MergeIfReturnThrow:
277 if 0 < j {
278 // separate from expression merging in case of: if(a)return b;b=c;return d
279 if ifStmt, ok := list[j-1].(*js.IfStmt); ok && isEmptyStmt(ifStmt.Body) != isEmptyStmt(ifStmt.Else) {
280 // either the if body is empty or the else body is empty. In case where both bodies have return/throw, we already rewrote that if statement to an return/throw statement
281 if returnStmt, ok := list[j].(*js.ReturnStmt); ok {
282 if returnStmt.Value == nil {
283 if left, ok := ifStmt.Body.(*js.ReturnStmt); ok && left.Value == nil {
284 list[j-1] = &js.ExprStmt{Value: ifStmt.Cond}
285 } else if left, ok := ifStmt.Else.(*js.ReturnStmt); ok && left.Value == nil {
286 list[j-1] = &js.ExprStmt{Value: ifStmt.Cond}
287 }
288 } else {
289 if left, ok := ifStmt.Body.(*js.ReturnStmt); ok && left.Value != nil {
290 returnStmt.Value = condExpr(ifStmt.Cond, left.Value, returnStmt.Value)
291 list[j-1] = returnStmt
292 j--
293 goto MergeIfReturnThrow
294 } else if left, ok := ifStmt.Else.(*js.ReturnStmt); ok && left.Value != nil {
295 returnStmt.Value = condExpr(ifStmt.Cond, returnStmt.Value, left.Value)
296 list[j-1] = returnStmt
297 j--
298 goto MergeIfReturnThrow
299 }
300 }
301 } else if throwStmt, ok := list[j].(*js.ThrowStmt); ok {
302 if left, ok := ifStmt.Body.(*js.ThrowStmt); ok {
303 throwStmt.Value = condExpr(ifStmt.Cond, left.Value, throwStmt.Value)
304 list[j-1] = throwStmt
305 j--
306 goto MergeIfReturnThrow
307 } else if left, ok := ifStmt.Else.(*js.ThrowStmt); ok {
308 throwStmt.Value = condExpr(ifStmt.Cond, throwStmt.Value, left.Value)
309 list[j-1] = throwStmt
310 j--
311 goto MergeIfReturnThrow
312 }
313 }
314 }
315 }
316 j++
317 }
318
319 // remove superfluous return or continue
320 if 0 < j {
321 if blockType == functionBlock {
322 if returnStmt, ok := list[j-1].(*js.ReturnStmt); ok {
323 if returnStmt.Value == nil || isUndefined(returnStmt.Value) {
324 j--
325 } else if commaExpr, ok := returnStmt.Value.(*js.CommaExpr); ok && isUndefined(commaExpr.List[len(commaExpr.List)-1]) {
326 // rewrite function f(){return a,void 0} => function f(){a}
327 if len(commaExpr.List) == 2 {
328 list[j-1] = &js.ExprStmt{Value: commaExpr.List[0]}
329 } else {
330 commaExpr.List = commaExpr.List[:len(commaExpr.List)-1]
331 }
332 }
333 }
334 } else if blockType == iterationBlock {
335 if branchStmt, ok := list[j-1].(*js.BranchStmt); ok && branchStmt.Type == js.ContinueToken && branchStmt.Label == nil {
336 j--
337 }
338 }
339 }
340 return list[:j]
341}
diff --git a/vendor/github.com/tdewolff/minify/v2/js/util.go b/vendor/github.com/tdewolff/minify/v2/js/util.go
new file mode 100644
index 0000000..6883d93
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/js/util.go
@@ -0,0 +1,1361 @@
1package js
2
3import (
4 "bytes"
5 "encoding/hex"
6 stdStrconv "strconv"
7 "unicode/utf8"
8
9 "github.com/tdewolff/minify/v2"
10 "github.com/tdewolff/parse/v2/js"
11 "github.com/tdewolff/parse/v2/strconv"
12)
13
14var (
15 spaceBytes = []byte(" ")
16 newlineBytes = []byte("\n")
17 starBytes = []byte("*")
18 colonBytes = []byte(":")
19 semicolonBytes = []byte(";")
20 commaBytes = []byte(",")
21 dotBytes = []byte(".")
22 ellipsisBytes = []byte("...")
23 openBraceBytes = []byte("{")
24 closeBraceBytes = []byte("}")
25 openParenBytes = []byte("(")
26 closeParenBytes = []byte(")")
27 openBracketBytes = []byte("[")
28 closeBracketBytes = []byte("]")
29 openParenBracketBytes = []byte("({")
30 closeParenOpenBracketBytes = []byte("){")
31 notBytes = []byte("!")
32 questionBytes = []byte("?")
33 equalBytes = []byte("=")
34 optChainBytes = []byte("?.")
35 arrowBytes = []byte("=>")
36 zeroBytes = []byte("0")
37 oneBytes = []byte("1")
38 letBytes = []byte("let")
39 getBytes = []byte("get")
40 setBytes = []byte("set")
41 asyncBytes = []byte("async")
42 functionBytes = []byte("function")
43 staticBytes = []byte("static")
44 ifOpenBytes = []byte("if(")
45 elseBytes = []byte("else")
46 withOpenBytes = []byte("with(")
47 doBytes = []byte("do")
48 whileOpenBytes = []byte("while(")
49 forOpenBytes = []byte("for(")
50 forAwaitOpenBytes = []byte("for await(")
51 inBytes = []byte("in")
52 ofBytes = []byte("of")
53 switchOpenBytes = []byte("switch(")
54 throwBytes = []byte("throw")
55 tryBytes = []byte("try")
56 catchBytes = []byte("catch")
57 finallyBytes = []byte("finally")
58 importBytes = []byte("import")
59 exportBytes = []byte("export")
60 fromBytes = []byte("from")
61 returnBytes = []byte("return")
62 classBytes = []byte("class")
63 asSpaceBytes = []byte("as ")
64 asyncSpaceBytes = []byte("async ")
65 spaceDefaultBytes = []byte(" default")
66 spaceExtendsBytes = []byte(" extends")
67 yieldBytes = []byte("yield")
68 newBytes = []byte("new")
69 openNewBytes = []byte("(new")
70 newTargetBytes = []byte("new.target")
71 importMetaBytes = []byte("import.meta")
72 nanBytes = []byte("NaN")
73 undefinedBytes = []byte("undefined")
74 infinityBytes = []byte("Infinity")
75 nullBytes = []byte("null")
76 voidZeroBytes = []byte("void 0")
77 groupedVoidZeroBytes = []byte("(void 0)")
78 oneDivZeroBytes = []byte("1/0")
79 groupedOneDivZeroBytes = []byte("(1/0)")
80 notZeroBytes = []byte("!0")
81 groupedNotZeroBytes = []byte("(!0)")
82 notOneBytes = []byte("!1")
83 groupedNotOneBytes = []byte("(!1)")
84 debuggerBytes = []byte("debugger")
85 regExpScriptBytes = []byte("/script>")
86)
87
88func isEmptyStmt(stmt js.IStmt) bool {
89 if stmt == nil {
90 return true
91 } else if _, ok := stmt.(*js.EmptyStmt); ok {
92 return true
93 } else if decl, ok := stmt.(*js.VarDecl); ok && decl.TokenType == js.ErrorToken {
94 for _, item := range decl.List {
95 if item.Default != nil {
96 return false
97 }
98 }
99 return true
100 } else if block, ok := stmt.(*js.BlockStmt); ok {
101 for _, item := range block.List {
102 if ok := isEmptyStmt(item); !ok {
103 return false
104 }
105 }
106 return true
107 }
108 return false
109}
110
111func isFlowStmt(stmt js.IStmt) bool {
112 if _, ok := stmt.(*js.ReturnStmt); ok {
113 return true
114 } else if _, ok := stmt.(*js.ThrowStmt); ok {
115 return true
116 } else if _, ok := stmt.(*js.BranchStmt); ok {
117 return true
118 }
119 return false
120}
121
122func lastStmt(stmt js.IStmt) js.IStmt {
123 if block, ok := stmt.(*js.BlockStmt); ok && 0 < len(block.List) {
124 return lastStmt(block.List[len(block.List)-1])
125 }
126 return stmt
127}
128
129func endsInIf(istmt js.IStmt) bool {
130 switch stmt := istmt.(type) {
131 case *js.IfStmt:
132 if stmt.Else == nil {
133 _, ok := optimizeStmt(stmt).(*js.IfStmt)
134 return ok
135 }
136 return endsInIf(stmt.Else)
137 case *js.BlockStmt:
138 if 0 < len(stmt.List) {
139 return endsInIf(stmt.List[len(stmt.List)-1])
140 }
141 case *js.LabelledStmt:
142 return endsInIf(stmt.Value)
143 case *js.WithStmt:
144 return endsInIf(stmt.Body)
145 case *js.WhileStmt:
146 return endsInIf(stmt.Body)
147 case *js.ForStmt:
148 return endsInIf(stmt.Body)
149 case *js.ForInStmt:
150 return endsInIf(stmt.Body)
151 case *js.ForOfStmt:
152 return endsInIf(stmt.Body)
153 }
154 return false
155}
156
157// precedence maps for the precedence inside the operation
158var unaryPrecMap = map[js.TokenType]js.OpPrec{
159 js.PostIncrToken: js.OpLHS,
160 js.PostDecrToken: js.OpLHS,
161 js.PreIncrToken: js.OpUnary,
162 js.PreDecrToken: js.OpUnary,
163 js.NotToken: js.OpUnary,
164 js.BitNotToken: js.OpUnary,
165 js.TypeofToken: js.OpUnary,
166 js.VoidToken: js.OpUnary,
167 js.DeleteToken: js.OpUnary,
168 js.PosToken: js.OpUnary,
169 js.NegToken: js.OpUnary,
170 js.AwaitToken: js.OpUnary,
171}
172
173var binaryLeftPrecMap = map[js.TokenType]js.OpPrec{
174 js.EqToken: js.OpLHS,
175 js.MulEqToken: js.OpLHS,
176 js.DivEqToken: js.OpLHS,
177 js.ModEqToken: js.OpLHS,
178 js.ExpEqToken: js.OpLHS,
179 js.AddEqToken: js.OpLHS,
180 js.SubEqToken: js.OpLHS,
181 js.LtLtEqToken: js.OpLHS,
182 js.GtGtEqToken: js.OpLHS,
183 js.GtGtGtEqToken: js.OpLHS,
184 js.BitAndEqToken: js.OpLHS,
185 js.BitXorEqToken: js.OpLHS,
186 js.BitOrEqToken: js.OpLHS,
187 js.ExpToken: js.OpUpdate,
188 js.MulToken: js.OpMul,
189 js.DivToken: js.OpMul,
190 js.ModToken: js.OpMul,
191 js.AddToken: js.OpAdd,
192 js.SubToken: js.OpAdd,
193 js.LtLtToken: js.OpShift,
194 js.GtGtToken: js.OpShift,
195 js.GtGtGtToken: js.OpShift,
196 js.LtToken: js.OpCompare,
197 js.LtEqToken: js.OpCompare,
198 js.GtToken: js.OpCompare,
199 js.GtEqToken: js.OpCompare,
200 js.InToken: js.OpCompare,
201 js.InstanceofToken: js.OpCompare,
202 js.EqEqToken: js.OpEquals,
203 js.NotEqToken: js.OpEquals,
204 js.EqEqEqToken: js.OpEquals,
205 js.NotEqEqToken: js.OpEquals,
206 js.BitAndToken: js.OpBitAnd,
207 js.BitXorToken: js.OpBitXor,
208 js.BitOrToken: js.OpBitOr,
209 js.AndToken: js.OpAnd,
210 js.OrToken: js.OpOr,
211 js.NullishToken: js.OpBitOr, // or OpCoalesce
212 js.CommaToken: js.OpExpr,
213}
214
215var binaryRightPrecMap = map[js.TokenType]js.OpPrec{
216 js.EqToken: js.OpAssign,
217 js.MulEqToken: js.OpAssign,
218 js.DivEqToken: js.OpAssign,
219 js.ModEqToken: js.OpAssign,
220 js.ExpEqToken: js.OpAssign,
221 js.AddEqToken: js.OpAssign,
222 js.SubEqToken: js.OpAssign,
223 js.LtLtEqToken: js.OpAssign,
224 js.GtGtEqToken: js.OpAssign,
225 js.GtGtGtEqToken: js.OpAssign,
226 js.BitAndEqToken: js.OpAssign,
227 js.BitXorEqToken: js.OpAssign,
228 js.BitOrEqToken: js.OpAssign,
229 js.ExpToken: js.OpExp,
230 js.MulToken: js.OpExp,
231 js.DivToken: js.OpExp,
232 js.ModToken: js.OpExp,
233 js.AddToken: js.OpMul,
234 js.SubToken: js.OpMul,
235 js.LtLtToken: js.OpAdd,
236 js.GtGtToken: js.OpAdd,
237 js.GtGtGtToken: js.OpAdd,
238 js.LtToken: js.OpShift,
239 js.LtEqToken: js.OpShift,
240 js.GtToken: js.OpShift,
241 js.GtEqToken: js.OpShift,
242 js.InToken: js.OpShift,
243 js.InstanceofToken: js.OpShift,
244 js.EqEqToken: js.OpCompare,
245 js.NotEqToken: js.OpCompare,
246 js.EqEqEqToken: js.OpCompare,
247 js.NotEqEqToken: js.OpCompare,
248 js.BitAndToken: js.OpEquals,
249 js.BitXorToken: js.OpBitAnd,
250 js.BitOrToken: js.OpBitXor,
251 js.AndToken: js.OpAnd, // changes order in AST but not in execution
252 js.OrToken: js.OpOr, // changes order in AST but not in execution
253 js.NullishToken: js.OpBitOr, // or OpCoalesce
254 js.CommaToken: js.OpAssign,
255}
256
257// precedence maps of the operation itself
258var unaryOpPrecMap = map[js.TokenType]js.OpPrec{
259 js.PostIncrToken: js.OpUpdate,
260 js.PostDecrToken: js.OpUpdate,
261 js.PreIncrToken: js.OpUpdate,
262 js.PreDecrToken: js.OpUpdate,
263 js.NotToken: js.OpUnary,
264 js.BitNotToken: js.OpUnary,
265 js.TypeofToken: js.OpUnary,
266 js.VoidToken: js.OpUnary,
267 js.DeleteToken: js.OpUnary,
268 js.PosToken: js.OpUnary,
269 js.NegToken: js.OpUnary,
270 js.AwaitToken: js.OpUnary,
271}
272
273var binaryOpPrecMap = map[js.TokenType]js.OpPrec{
274 js.EqToken: js.OpAssign,
275 js.MulEqToken: js.OpAssign,
276 js.DivEqToken: js.OpAssign,
277 js.ModEqToken: js.OpAssign,
278 js.ExpEqToken: js.OpAssign,
279 js.AddEqToken: js.OpAssign,
280 js.SubEqToken: js.OpAssign,
281 js.LtLtEqToken: js.OpAssign,
282 js.GtGtEqToken: js.OpAssign,
283 js.GtGtGtEqToken: js.OpAssign,
284 js.BitAndEqToken: js.OpAssign,
285 js.BitXorEqToken: js.OpAssign,
286 js.BitOrEqToken: js.OpAssign,
287 js.ExpToken: js.OpExp,
288 js.MulToken: js.OpMul,
289 js.DivToken: js.OpMul,
290 js.ModToken: js.OpMul,
291 js.AddToken: js.OpAdd,
292 js.SubToken: js.OpAdd,
293 js.LtLtToken: js.OpShift,
294 js.GtGtToken: js.OpShift,
295 js.GtGtGtToken: js.OpShift,
296 js.LtToken: js.OpCompare,
297 js.LtEqToken: js.OpCompare,
298 js.GtToken: js.OpCompare,
299 js.GtEqToken: js.OpCompare,
300 js.InToken: js.OpCompare,
301 js.InstanceofToken: js.OpCompare,
302 js.EqEqToken: js.OpEquals,
303 js.NotEqToken: js.OpEquals,
304 js.EqEqEqToken: js.OpEquals,
305 js.NotEqEqToken: js.OpEquals,
306 js.BitAndToken: js.OpBitAnd,
307 js.BitXorToken: js.OpBitXor,
308 js.BitOrToken: js.OpBitOr,
309 js.AndToken: js.OpAnd,
310 js.OrToken: js.OpOr,
311 js.NullishToken: js.OpCoalesce,
312 js.CommaToken: js.OpExpr,
313}
314
315func exprPrec(i js.IExpr) js.OpPrec {
316 switch expr := i.(type) {
317 case *js.Var, *js.LiteralExpr, *js.ArrayExpr, *js.ObjectExpr, *js.FuncDecl, *js.ClassDecl:
318 return js.OpPrimary
319 case *js.UnaryExpr:
320 return unaryOpPrecMap[expr.Op]
321 case *js.BinaryExpr:
322 return binaryOpPrecMap[expr.Op]
323 case *js.NewExpr:
324 if expr.Args == nil {
325 return js.OpNew
326 }
327 return js.OpMember
328 case *js.TemplateExpr:
329 if expr.Tag == nil {
330 return js.OpPrimary
331 }
332 return expr.Prec
333 case *js.DotExpr:
334 return expr.Prec
335 case *js.IndexExpr:
336 return expr.Prec
337 case *js.NewTargetExpr, *js.ImportMetaExpr:
338 return js.OpMember
339 case *js.CallExpr:
340 return js.OpCall
341 case *js.CondExpr, *js.YieldExpr, *js.ArrowFunc:
342 return js.OpAssign
343 case *js.GroupExpr:
344 return exprPrec(expr.X)
345 }
346 return js.OpExpr // CommaExpr
347}
348
349func hasSideEffects(i js.IExpr) bool {
350 // assume that variable usage and that the index operator themselves have no side effects
351 switch expr := i.(type) {
352 case *js.Var, *js.LiteralExpr, *js.FuncDecl, *js.ClassDecl, *js.ArrowFunc, *js.NewTargetExpr, *js.ImportMetaExpr:
353 return false
354 case *js.NewExpr, *js.CallExpr, *js.YieldExpr:
355 return true
356 case *js.GroupExpr:
357 return hasSideEffects(expr.X)
358 case *js.DotExpr:
359 return hasSideEffects(expr.X)
360 case *js.IndexExpr:
361 return hasSideEffects(expr.X) || hasSideEffects(expr.Y)
362 case *js.CondExpr:
363 return hasSideEffects(expr.Cond) || hasSideEffects(expr.X) || hasSideEffects(expr.Y)
364 case *js.CommaExpr:
365 for _, item := range expr.List {
366 if hasSideEffects(item) {
367 return true
368 }
369 }
370 case *js.ArrayExpr:
371 for _, item := range expr.List {
372 if hasSideEffects(item.Value) {
373 return true
374 }
375 }
376 return false
377 case *js.ObjectExpr:
378 for _, item := range expr.List {
379 if hasSideEffects(item.Value) || item.Init != nil && hasSideEffects(item.Init) || item.Name != nil && item.Name.IsComputed() && hasSideEffects(item.Name.Computed) {
380 return true
381 }
382 }
383 return false
384 case *js.TemplateExpr:
385 if hasSideEffects(expr.Tag) {
386 return true
387 }
388 for _, item := range expr.List {
389 if hasSideEffects(item.Expr) {
390 return true
391 }
392 }
393 return false
394 case *js.UnaryExpr:
395 if expr.Op == js.DeleteToken || expr.Op == js.PreIncrToken || expr.Op == js.PreDecrToken || expr.Op == js.PostIncrToken || expr.Op == js.PostDecrToken {
396 return true
397 }
398 return hasSideEffects(expr.X)
399 case *js.BinaryExpr:
400 return binaryOpPrecMap[expr.Op] == js.OpAssign
401 }
402 return true
403}
404
405// TODO: use in more cases
406func groupExpr(i js.IExpr, prec js.OpPrec) js.IExpr {
407 precInside := exprPrec(i)
408 if _, ok := i.(*js.GroupExpr); !ok && precInside < prec && (precInside != js.OpCoalesce || prec != js.OpBitOr) {
409 return &js.GroupExpr{X: i}
410 }
411 return i
412}
413
414// TODO: use in more cases
415func condExpr(cond, x, y js.IExpr) js.IExpr {
416 if comma, ok := cond.(*js.CommaExpr); ok {
417 comma.List[len(comma.List)-1] = &js.CondExpr{
418 Cond: groupExpr(comma.List[len(comma.List)-1], js.OpCoalesce),
419 X: groupExpr(x, js.OpAssign),
420 Y: groupExpr(y, js.OpAssign),
421 }
422 return comma
423 }
424 return &js.CondExpr{
425 Cond: groupExpr(cond, js.OpCoalesce),
426 X: groupExpr(x, js.OpAssign),
427 Y: groupExpr(y, js.OpAssign),
428 }
429}
430
431func commaExpr(x, y js.IExpr) js.IExpr {
432 comma, ok := x.(*js.CommaExpr)
433 if !ok {
434 comma = &js.CommaExpr{List: []js.IExpr{x}}
435 }
436 if comma2, ok := y.(*js.CommaExpr); ok {
437 comma.List = append(comma.List, comma2.List...)
438 } else {
439 comma.List = append(comma.List, y)
440 }
441 return comma
442}
443
444func innerExpr(i js.IExpr) js.IExpr {
445 for {
446 if group, ok := i.(*js.GroupExpr); ok {
447 i = group.X
448 } else {
449 return i
450 }
451 }
452}
453
454func finalExpr(i js.IExpr) js.IExpr {
455 i = innerExpr(i)
456 if comma, ok := i.(*js.CommaExpr); ok {
457 i = comma.List[len(comma.List)-1]
458 }
459 if binary, ok := i.(*js.BinaryExpr); ok && binary.Op == js.EqToken {
460 i = binary.X // return first
461 }
462 return i
463}
464
465func isTrue(i js.IExpr) bool {
466 i = innerExpr(i)
467 if lit, ok := i.(*js.LiteralExpr); ok && lit.TokenType == js.TrueToken {
468 return true
469 } else if unary, ok := i.(*js.UnaryExpr); ok && unary.Op == js.NotToken {
470 ret, _ := isFalsy(unary.X)
471 return ret
472 }
473 return false
474}
475
476func isFalse(i js.IExpr) bool {
477 i = innerExpr(i)
478 if lit, ok := i.(*js.LiteralExpr); ok {
479 return lit.TokenType == js.FalseToken
480 } else if unary, ok := i.(*js.UnaryExpr); ok && unary.Op == js.NotToken {
481 ret, _ := isTruthy(unary.X)
482 return ret
483 }
484 return false
485}
486
487func isEqualExpr(a, b js.IExpr) bool {
488 a = innerExpr(a)
489 b = innerExpr(b)
490 if left, ok := a.(*js.Var); ok {
491 if right, ok := b.(*js.Var); ok {
492 return bytes.Equal(left.Name(), right.Name())
493 }
494 }
495 // TODO: use reflect.DeepEqual?
496 return false
497}
498
499func toNullishExpr(condExpr *js.CondExpr) (js.IExpr, bool) {
500 if v, not, ok := isUndefinedOrNullVar(condExpr.Cond); ok {
501 left, right := condExpr.X, condExpr.Y
502 if not {
503 left, right = right, left
504 }
505 if isEqualExpr(v, right) {
506 // convert conditional expression to nullish: a==null?b:a => a??b
507 return &js.BinaryExpr{js.NullishToken, groupExpr(right, binaryLeftPrecMap[js.NullishToken]), groupExpr(left, binaryRightPrecMap[js.NullishToken])}, true
508 } else if isUndefined(left) {
509 // convert conditional expression to optional expr: a==null?undefined:a.b => a?.b
510 expr := right
511 var parent js.IExpr
512 for {
513 prevExpr := expr
514 if callExpr, ok := expr.(*js.CallExpr); ok {
515 expr = callExpr.X
516 } else if dotExpr, ok := expr.(*js.DotExpr); ok {
517 expr = dotExpr.X
518 } else if indexExpr, ok := expr.(*js.IndexExpr); ok {
519 expr = indexExpr.X
520 } else if templateExpr, ok := expr.(*js.TemplateExpr); ok {
521 expr = templateExpr.Tag
522 } else {
523 break
524 }
525 parent = prevExpr
526 }
527 if parent != nil && isEqualExpr(v, expr) {
528 if callExpr, ok := parent.(*js.CallExpr); ok {
529 callExpr.Optional = true
530 } else if dotExpr, ok := parent.(*js.DotExpr); ok {
531 dotExpr.Optional = true
532 } else if indexExpr, ok := parent.(*js.IndexExpr); ok {
533 indexExpr.Optional = true
534 } else if templateExpr, ok := parent.(*js.TemplateExpr); ok {
535 templateExpr.Optional = true
536 }
537 return right, true
538 }
539 }
540 }
541 return nil, false
542}
543
544func isUndefinedOrNullVar(i js.IExpr) (*js.Var, bool, bool) {
545 i = innerExpr(i)
546 if binary, ok := i.(*js.BinaryExpr); ok && (binary.Op == js.OrToken || binary.Op == js.AndToken) {
547 eqEqOp := js.EqEqToken
548 eqEqEqOp := js.EqEqEqToken
549 if binary.Op == js.AndToken {
550 eqEqOp = js.NotEqToken
551 eqEqEqOp = js.NotEqEqToken
552 }
553
554 left, isBinaryX := innerExpr(binary.X).(*js.BinaryExpr)
555 right, isBinaryY := innerExpr(binary.Y).(*js.BinaryExpr)
556 if isBinaryX && isBinaryY && (left.Op == eqEqOp || left.Op == eqEqEqOp) && (right.Op == eqEqOp || right.Op == eqEqEqOp) {
557 var leftVar, rightVar *js.Var
558 if v, ok := left.X.(*js.Var); ok && isUndefinedOrNull(left.Y) {
559 leftVar = v
560 } else if v, ok := left.Y.(*js.Var); ok && isUndefinedOrNull(left.X) {
561 leftVar = v
562 }
563 if v, ok := right.X.(*js.Var); ok && isUndefinedOrNull(right.Y) {
564 rightVar = v
565 } else if v, ok := right.Y.(*js.Var); ok && isUndefinedOrNull(right.X) {
566 rightVar = v
567 }
568 if leftVar != nil && leftVar == rightVar {
569 return leftVar, binary.Op == js.AndToken, true
570 }
571 }
572 } else if ok && (binary.Op == js.EqEqToken || binary.Op == js.NotEqToken) {
573 var variable *js.Var
574 if v, ok := binary.X.(*js.Var); ok && isUndefinedOrNull(binary.Y) {
575 variable = v
576 } else if v, ok := binary.Y.(*js.Var); ok && isUndefinedOrNull(binary.X) {
577 variable = v
578 }
579 if variable != nil {
580 return variable, binary.Op == js.NotEqToken, true
581 }
582 }
583 return nil, false, false
584}
585
586func isUndefinedOrNull(i js.IExpr) bool {
587 i = innerExpr(i)
588 if lit, ok := i.(*js.LiteralExpr); ok {
589 return lit.TokenType == js.NullToken
590 }
591 return isUndefined(i)
592}
593
594func isUndefined(i js.IExpr) bool {
595 i = innerExpr(i)
596 if v, ok := i.(*js.Var); ok {
597 if bytes.Equal(v.Name(), undefinedBytes) { // TODO: only if not defined
598 return true
599 }
600 } else if unary, ok := i.(*js.UnaryExpr); ok && unary.Op == js.VoidToken {
601 return !hasSideEffects(unary.X)
602 }
603 return false
604}
605
606// returns whether truthy and whether it could be coerced to a boolean (i.e. when returns (false,true) this means it is falsy)
607func isTruthy(i js.IExpr) (bool, bool) {
608 if falsy, ok := isFalsy(i); ok {
609 return !falsy, true
610 }
611 return false, false
612}
613
614// returns whether falsy and whether it could be coerced to a boolean (i.e. when returns (false,true) this means it is truthy)
615func isFalsy(i js.IExpr) (bool, bool) {
616 negated := false
617 group, isGroup := i.(*js.GroupExpr)
618 unary, isUnary := i.(*js.UnaryExpr)
619 for isGroup || isUnary && unary.Op == js.NotToken {
620 if isGroup {
621 i = group.X
622 } else {
623 i = unary.X
624 negated = !negated
625 }
626 group, isGroup = i.(*js.GroupExpr)
627 unary, isUnary = i.(*js.UnaryExpr)
628 }
629 if lit, ok := i.(*js.LiteralExpr); ok {
630 tt := lit.TokenType
631 d := lit.Data
632 if tt == js.FalseToken || tt == js.NullToken || tt == js.StringToken && len(lit.Data) == 0 {
633 return !negated, true // falsy
634 } else if tt == js.TrueToken || tt == js.StringToken {
635 return negated, true // truthy
636 } else if tt == js.DecimalToken || tt == js.BinaryToken || tt == js.OctalToken || tt == js.HexadecimalToken || tt == js.BigIntToken {
637 for _, c := range d {
638 if c == 'e' || c == 'E' || c == 'n' {
639 break
640 } else if c != '0' && c != '.' && c != 'x' && c != 'X' && c != 'b' && c != 'B' && c != 'o' && c != 'O' {
641 return negated, true // truthy
642 }
643 }
644 return !negated, true // falsy
645 }
646 } else if isUndefined(i) {
647 return !negated, true // falsy
648 } else if v, ok := i.(*js.Var); ok && bytes.Equal(v.Name(), nanBytes) {
649 return !negated, true // falsy
650 }
651 return false, false // unknown
652}
653
654func isBooleanExpr(expr js.IExpr) bool {
655 if unaryExpr, ok := expr.(*js.UnaryExpr); ok {
656 return unaryExpr.Op == js.NotToken
657 } else if binaryExpr, ok := expr.(*js.BinaryExpr); ok {
658 op := binaryOpPrecMap[binaryExpr.Op]
659 if op == js.OpAnd || op == js.OpOr {
660 return isBooleanExpr(binaryExpr.X) && isBooleanExpr(binaryExpr.Y)
661 }
662 return op == js.OpCompare || op == js.OpEquals
663 } else if litExpr, ok := expr.(*js.LiteralExpr); ok {
664 return litExpr.TokenType == js.TrueToken || litExpr.TokenType == js.FalseToken
665 } else if groupExpr, ok := expr.(*js.GroupExpr); ok {
666 return isBooleanExpr(groupExpr.X)
667 }
668 return false
669}
670
671func invertBooleanOp(op js.TokenType) js.TokenType {
672 if op == js.EqEqToken {
673 return js.NotEqToken
674 } else if op == js.NotEqToken {
675 return js.EqEqToken
676 } else if op == js.EqEqEqToken {
677 return js.NotEqEqToken
678 } else if op == js.NotEqEqToken {
679 return js.EqEqEqToken
680 }
681 return js.ErrorToken
682}
683
684func optimizeBooleanExpr(expr js.IExpr, invert bool, prec js.OpPrec) js.IExpr {
685 if invert {
686 // unary !(boolean) has already been handled
687 if binaryExpr, ok := expr.(*js.BinaryExpr); ok && binaryOpPrecMap[binaryExpr.Op] == js.OpEquals {
688 binaryExpr.Op = invertBooleanOp(binaryExpr.Op)
689 return expr
690 } else {
691 return optimizeUnaryExpr(&js.UnaryExpr{js.NotToken, groupExpr(expr, js.OpUnary)}, prec)
692 }
693 } else if isBooleanExpr(expr) {
694 return groupExpr(expr, prec)
695 } else {
696 return &js.UnaryExpr{js.NotToken, &js.UnaryExpr{js.NotToken, groupExpr(expr, js.OpUnary)}}
697 }
698}
699
700func optimizeUnaryExpr(expr *js.UnaryExpr, prec js.OpPrec) js.IExpr {
701 if expr.Op == js.NotToken {
702 invert := true
703 var expr2 js.IExpr = expr.X
704 for {
705 if unary, ok := expr2.(*js.UnaryExpr); ok && unary.Op == js.NotToken {
706 invert = !invert
707 expr2 = unary.X
708 } else if group, ok := expr2.(*js.GroupExpr); ok {
709 expr2 = group.X
710 } else {
711 break
712 }
713 }
714 if !invert && isBooleanExpr(expr2) {
715 return groupExpr(expr2, prec)
716 } else if binary, ok := expr2.(*js.BinaryExpr); ok && invert {
717 if binaryOpPrecMap[binary.Op] == js.OpEquals {
718 binary.Op = invertBooleanOp(binary.Op)
719 return groupExpr(binary, prec)
720 } else if binary.Op == js.AndToken || binary.Op == js.OrToken {
721 op := js.AndToken
722 if binary.Op == js.AndToken {
723 op = js.OrToken
724 }
725 precInside := binaryOpPrecMap[op]
726 needsGroup := precInside < prec && (precInside != js.OpCoalesce || prec != js.OpBitOr)
727
728 // rewrite !(a||b) to !a&&!b
729 // rewrite !(a==0||b==0) to a!=0&&b!=0
730 score := 3 // savings if rewritten (group parentheses and not-token)
731 if needsGroup {
732 score -= 2
733 }
734 score -= 2 // add two not-tokens for left and right
735
736 // == and === can become != and !==
737 var isEqX, isEqY bool
738 if binaryExpr, ok := binary.X.(*js.BinaryExpr); ok && binaryOpPrecMap[binaryExpr.Op] == js.OpEquals {
739 score += 1
740 isEqX = true
741 }
742 if binaryExpr, ok := binary.Y.(*js.BinaryExpr); ok && binaryOpPrecMap[binaryExpr.Op] == js.OpEquals {
743 score += 1
744 isEqY = true
745 }
746
747 // add group if it wasn't already there
748 var needsGroupX, needsGroupY bool
749 if !isEqX && binaryLeftPrecMap[binary.Op] <= exprPrec(binary.X) && exprPrec(binary.X) < js.OpUnary {
750 score -= 2
751 needsGroupX = true
752 }
753 if !isEqY && binaryRightPrecMap[binary.Op] <= exprPrec(binary.Y) && exprPrec(binary.Y) < js.OpUnary {
754 score -= 2
755 needsGroupY = true
756 }
757
758 // remove group
759 if op == js.OrToken {
760 if exprPrec(binary.X) == js.OpOr {
761 score += 2
762 }
763 if exprPrec(binary.Y) == js.OpAnd {
764 score += 2
765 }
766 }
767
768 if 0 < score {
769 binary.Op = op
770 if isEqX {
771 binary.X.(*js.BinaryExpr).Op = invertBooleanOp(binary.X.(*js.BinaryExpr).Op)
772 }
773 if isEqY {
774 binary.Y.(*js.BinaryExpr).Op = invertBooleanOp(binary.Y.(*js.BinaryExpr).Op)
775 }
776 if needsGroupX {
777 binary.X = &js.GroupExpr{binary.X}
778 }
779 if needsGroupY {
780 binary.Y = &js.GroupExpr{binary.Y}
781 }
782 if !isEqX {
783 binary.X = &js.UnaryExpr{js.NotToken, binary.X}
784 }
785 if !isEqY {
786 binary.Y = &js.UnaryExpr{js.NotToken, binary.Y}
787 }
788 if needsGroup {
789 return &js.GroupExpr{binary}
790 }
791 return binary
792 }
793 }
794 }
795 }
796 return expr
797}
798
799func (m *jsMinifier) optimizeCondExpr(expr *js.CondExpr, prec js.OpPrec) js.IExpr {
800 // remove double negative !! in condition, or switch cases for single negative !
801 if unary1, ok := expr.Cond.(*js.UnaryExpr); ok && unary1.Op == js.NotToken {
802 if unary2, ok := unary1.X.(*js.UnaryExpr); ok && unary2.Op == js.NotToken {
803 if isBooleanExpr(unary2.X) {
804 expr.Cond = unary2.X
805 }
806 } else {
807 expr.Cond = unary1.X
808 expr.X, expr.Y = expr.Y, expr.X
809 }
810 }
811
812 finalCond := finalExpr(expr.Cond)
813 if truthy, ok := isTruthy(expr.Cond); truthy && ok {
814 // if condition is truthy
815 return expr.X
816 } else if !truthy && ok {
817 // if condition is falsy
818 return expr.Y
819 } else if isEqualExpr(finalCond, expr.X) && (exprPrec(finalCond) < js.OpAssign || binaryLeftPrecMap[js.OrToken] <= exprPrec(finalCond)) && (exprPrec(expr.Y) < js.OpAssign || binaryRightPrecMap[js.OrToken] <= exprPrec(expr.Y)) {
820 // if condition is equal to true body
821 // for higher prec we need to add group parenthesis, and for lower prec we have parenthesis anyways. This only is shorter if len(expr.X) >= 3. isEqualExpr only checks for literal variables, which is a name will be minified to a one or two character name.
822 return &js.BinaryExpr{js.OrToken, groupExpr(expr.Cond, binaryLeftPrecMap[js.OrToken]), expr.Y}
823 } else if isEqualExpr(finalCond, expr.Y) && (exprPrec(finalCond) < js.OpAssign || binaryLeftPrecMap[js.AndToken] <= exprPrec(finalCond)) && (exprPrec(expr.X) < js.OpAssign || binaryRightPrecMap[js.AndToken] <= exprPrec(expr.X)) {
824 // if condition is equal to false body
825 // for higher prec we need to add group parenthesis, and for lower prec we have parenthesis anyways. This only is shorter if len(expr.X) >= 3. isEqualExpr only checks for literal variables, which is a name will be minified to a one or two character name.
826 return &js.BinaryExpr{js.AndToken, groupExpr(expr.Cond, binaryLeftPrecMap[js.AndToken]), expr.X}
827 } else if isEqualExpr(expr.X, expr.Y) {
828 // if true and false bodies are equal
829 return groupExpr(&js.CommaExpr{[]js.IExpr{expr.Cond, expr.X}}, prec)
830 } else if nullishExpr, ok := toNullishExpr(expr); ok && m.o.minVersion(2020) {
831 // no need to check whether left/right need to add groups, as the space saving is always more
832 return nullishExpr
833 } else {
834 callX, isCallX := expr.X.(*js.CallExpr)
835 callY, isCallY := expr.Y.(*js.CallExpr)
836 if isCallX && isCallY && len(callX.Args.List) == 1 && len(callY.Args.List) == 1 && !callX.Args.List[0].Rest && !callY.Args.List[0].Rest && isEqualExpr(callX.X, callY.X) {
837 expr.X = callX.Args.List[0].Value
838 expr.Y = callY.Args.List[0].Value
839 return &js.CallExpr{callX.X, js.Args{[]js.Arg{{expr, false}}}, false} // recompress the conditional expression inside
840 }
841
842 // shorten when true and false bodies are true and false
843 trueX, falseX := isTrue(expr.X), isFalse(expr.X)
844 trueY, falseY := isTrue(expr.Y), isFalse(expr.Y)
845 if trueX && falseY || falseX && trueY {
846 return optimizeBooleanExpr(expr.Cond, falseX, prec)
847 } else if trueX || trueY {
848 // trueX != trueY
849 cond := optimizeBooleanExpr(expr.Cond, trueY, binaryLeftPrecMap[js.OrToken])
850 if trueY {
851 return &js.BinaryExpr{js.OrToken, cond, groupExpr(expr.X, binaryRightPrecMap[js.OrToken])}
852 } else {
853 return &js.BinaryExpr{js.OrToken, cond, groupExpr(expr.Y, binaryRightPrecMap[js.OrToken])}
854 }
855 } else if falseX || falseY {
856 // falseX != falseY
857 cond := optimizeBooleanExpr(expr.Cond, falseX, binaryLeftPrecMap[js.AndToken])
858 if falseX {
859 return &js.BinaryExpr{js.AndToken, cond, groupExpr(expr.Y, binaryRightPrecMap[js.AndToken])}
860 } else {
861 return &js.BinaryExpr{js.AndToken, cond, groupExpr(expr.X, binaryRightPrecMap[js.AndToken])}
862 }
863 } else if condExpr, ok := expr.X.(*js.CondExpr); ok && isEqualExpr(expr.Y, condExpr.Y) {
864 // nested conditional expression with same false bodies
865 return &js.CondExpr{&js.BinaryExpr{js.AndToken, groupExpr(expr.Cond, binaryLeftPrecMap[js.AndToken]), groupExpr(condExpr.Cond, binaryRightPrecMap[js.AndToken])}, condExpr.X, expr.Y}
866 } else if prec <= js.OpExpr {
867 // regular conditional expression
868 // convert (a,b)?c:d => a,b?c:d
869 if group, ok := expr.Cond.(*js.GroupExpr); ok {
870 if comma, ok := group.X.(*js.CommaExpr); ok && js.OpCoalesce <= exprPrec(comma.List[len(comma.List)-1]) {
871 expr.Cond = comma.List[len(comma.List)-1]
872 comma.List[len(comma.List)-1] = expr
873 return comma // recompress the conditional expression inside
874 }
875 }
876 }
877 }
878 return expr
879}
880
881func isHexDigit(b byte) bool {
882 return '0' <= b && b <= '9' || 'a' <= b && b <= 'f' || 'A' <= b && b <= 'F'
883}
884
885func mergeBinaryExpr(expr *js.BinaryExpr) {
886 // merge string concatenations which may be intertwined with other additions
887 var ok bool
888 for expr.Op == js.AddToken {
889 if lit, ok := expr.Y.(*js.LiteralExpr); ok && lit.TokenType == js.StringToken {
890 left := expr
891 strings := []*js.LiteralExpr{lit}
892 n := len(lit.Data) - 2
893 for left.Op == js.AddToken {
894 if 50 < len(strings) {
895 return // limit recursion
896 }
897 if lit, ok := left.X.(*js.LiteralExpr); ok && lit.TokenType == js.StringToken {
898 strings = append(strings, lit)
899 n += len(lit.Data) - 2
900 left.X = nil
901 } else if newLeft, ok := left.X.(*js.BinaryExpr); ok {
902 if lit, ok := newLeft.Y.(*js.LiteralExpr); ok && lit.TokenType == js.StringToken {
903 strings = append(strings, lit)
904 n += len(lit.Data) - 2
905 left = newLeft
906 continue
907 }
908 }
909 break
910 }
911
912 if 1 < len(strings) {
913 // unescaped quotes will be repaired in minifyString later on
914 b := make([]byte, 0, n+2)
915 b = append(b, strings[len(strings)-1].Data[:len(strings[len(strings)-1].Data)-1]...)
916 for i := len(strings) - 2; 0 < i; i-- {
917 b = append(b, strings[i].Data[1:len(strings[i].Data)-1]...)
918 }
919 b = append(b, strings[0].Data[1:]...)
920 b[len(b)-1] = b[0]
921
922 expr.X = left.X
923 expr.Y.(*js.LiteralExpr).Data = b
924 }
925 }
926 if expr, ok = expr.X.(*js.BinaryExpr); !ok {
927 break
928 }
929 }
930}
931
932func minifyString(b []byte, allowTemplate bool) []byte {
933 if len(b) < 3 {
934 return []byte("\"\"")
935 }
936
937 // switch quotes if more optimal
938 singleQuotes := 0
939 doubleQuotes := 0
940 backtickQuotes := 0
941 newlines := 0
942 dollarSigns := 0
943 notEscapes := false
944 for i := 1; i < len(b)-1; i++ {
945 if b[i] == '\'' {
946 singleQuotes++
947 } else if b[i] == '"' {
948 doubleQuotes++
949 } else if b[i] == '`' {
950 backtickQuotes++
951 } else if b[i] == '$' {
952 dollarSigns++
953 } else if b[i] == '\\' && i+1 < len(b) {
954 if b[i+1] == 'n' || b[i+1] == 'r' {
955 newlines++
956 } else if '1' <= b[i+1] && b[i+1] <= '9' || b[i+1] == '0' && i+2 < len(b) && '0' <= b[i+2] && b[i+2] <= '9' {
957 notEscapes = true
958 }
959 }
960 }
961 quote := byte('"') // default to " for better GZIP compression
962 quotes := singleQuotes
963 if doubleQuotes < singleQuotes {
964 quote = byte('"')
965 quotes = doubleQuotes
966 } else if singleQuotes < doubleQuotes {
967 quote = byte('\'')
968 }
969 if allowTemplate && !notEscapes && backtickQuotes+dollarSigns < quotes+newlines {
970 quote = byte('`')
971 }
972 b[0] = quote
973 b[len(b)-1] = quote
974
975 // strip unnecessary escapes
976 return replaceEscapes(b, quote, 1, 1)
977}
978
979func replaceEscapes(b []byte, quote byte, prefix, suffix int) []byte {
980 // strip unnecessary escapes
981 j := 0
982 start := 0
983 for i := prefix; i < len(b)-suffix; i++ {
984 if c := b[i]; c == '\\' {
985 c = b[i+1]
986 if c == quote || c == '\\' || quote != '`' && (c == 'n' || c == 'r') || c == '0' && (i+2 == len(b)-1 || b[i+2] < '0' || '7' < b[i+2]) {
987 // keep escape sequence
988 i++
989 continue
990 }
991 n := 1 // number of characters to skip
992 if c == '\n' || c == '\r' || c == 0xE2 && i+3 < len(b)-1 && b[i+2] == 0x80 && (b[i+3] == 0xA8 || b[i+3] == 0xA9) {
993 // line continuations
994 if c == 0xE2 {
995 n = 4
996 } else if c == '\r' && i+2 < len(b)-1 && b[i+2] == '\n' {
997 n = 3
998 } else {
999 n = 2
1000 }
1001 } else if c == 'x' {
1002 if i+3 < len(b)-1 && isHexDigit(b[i+2]) && b[i+2] < '8' && isHexDigit(b[i+3]) && (!(b[i+2] == '0' && b[i+3] == '0') || i+3 == len(b) || b[i+3] != '\\' && (b[i+3] < '0' && '7' < b[i+3])) {
1003 // don't convert \x00 to \0 if it may be an octal number
1004 // hexadecimal escapes
1005 _, _ = hex.Decode(b[i+3:i+4:i+4], b[i+2:i+4])
1006 n = 3
1007 if b[i+3] == '\\' || b[i+3] == quote || b[i+3] == '\n' || b[i+3] == '\r' || b[i+3] == 0 {
1008 if b[i+3] == '\n' {
1009 b[i+3] = 'n'
1010 } else if b[i+3] == '\r' {
1011 b[i+3] = 'r'
1012 }
1013 n--
1014 b[i+2] = '\\'
1015 }
1016 } else {
1017 i++
1018 continue
1019 }
1020 } else if c == 'u' && i+2 < len(b) {
1021 l := i + 2
1022 if b[i+2] == '{' {
1023 l++
1024 }
1025 r := l
1026 for ; r < len(b) && (b[i+2] == '{' || r < l+4); r++ {
1027 if b[r] < '0' || '9' < b[r] && b[r] < 'A' || 'F' < b[r] && b[r] < 'a' || 'f' < b[r] {
1028 break
1029 }
1030 }
1031 if b[i+2] == '{' && 6 < r-l || b[i+2] != '{' && r-l != 4 {
1032 i++
1033 continue
1034 }
1035 num, err := stdStrconv.ParseInt(string(b[l:r]), 16, 32)
1036 if err != nil || 0x10FFFF <= num {
1037 i++
1038 continue
1039 }
1040
1041 if num == 0 {
1042 // don't convert NULL to literal NULL (gives JS parsing problems)
1043 if r == len(b) || b[r] != '\\' && (b[r] < '0' && '7' < b[r]) {
1044 b[r-2] = '\\'
1045 n = r - l
1046 } else {
1047 // don't convert NULL to \0 (may be an octal number)
1048 b[r-4] = '\\'
1049 b[r-3] = 'x'
1050 n = r - l - 2
1051 }
1052 } else {
1053 // decode unicode character to UTF-8 and put at the end of the escape sequence
1054 // then skip the first part of the escape sequence until the decoded character
1055 n = 2 + r - l
1056 if b[i+2] == '{' {
1057 n += 2
1058 }
1059 m := utf8.RuneLen(rune(num))
1060 if m == -1 {
1061 i++
1062 continue
1063 }
1064 utf8.EncodeRune(b[i+n-m:], rune(num))
1065 n -= m
1066 }
1067 } else if '0' <= c && c <= '7' {
1068 // octal escapes (legacy), \0 already handled
1069 num := c - '0'
1070 if i+2 < len(b)-1 && '0' <= b[i+2] && b[i+2] <= '7' {
1071 num = num*8 + b[i+2] - '0'
1072 n++
1073 if num < 32 && i+3 < len(b)-1 && '0' <= b[i+3] && b[i+3] <= '7' {
1074 num = num*8 + b[i+3] - '0'
1075 n++
1076 }
1077 }
1078 b[i+n] = num
1079 if num == 0 || num == '\\' || num == quote || num == '\n' || num == '\r' {
1080 if num == 0 {
1081 b[i+n] = '0'
1082 } else if num == '\n' {
1083 b[i+n] = 'n'
1084 } else if num == '\r' {
1085 b[i+n] = 'r'
1086 }
1087 n--
1088 b[i+n] = '\\'
1089 }
1090 } else if c == 'n' {
1091 b[i+1] = '\n' // only for template literals
1092 } else if c == 'r' {
1093 b[i+1] = '\r' // only for template literals
1094 } else if c == 't' {
1095 b[i+1] = '\t'
1096 } else if c == 'f' {
1097 b[i+1] = '\f'
1098 } else if c == 'v' {
1099 b[i+1] = '\v'
1100 } else if c == 'b' {
1101 b[i+1] = '\b'
1102 }
1103 // remove unnecessary escape character, anything but 0x00, 0x0A, 0x0D, \, ' or "
1104 if start != 0 {
1105 j += copy(b[j:], b[start:i])
1106 } else {
1107 j = i
1108 }
1109 start = i + n
1110 i += n - 1
1111 } else if c == quote || c == '$' && quote == '`' && (i+1 < len(b) && b[i+1] == '{' || i+2 < len(b) && b[i+1] == '\\' && b[i+2] == '{') {
1112 // may not be escaped properly when changing quotes
1113 if j < start {
1114 // avoid append
1115 j += copy(b[j:], b[start:i])
1116 b[j] = '\\'
1117 j++
1118 start = i
1119 } else {
1120 b = append(append(b[:i], '\\'), b[i:]...)
1121 i++
1122 b[i] = c // was overwritten above
1123 }
1124 } else if c == '<' && 9 <= len(b)-1-i {
1125 if b[i+1] == '\\' && 10 <= len(b)-1-i && bytes.Equal(b[i+2:i+10], []byte("/script>")) {
1126 i += 9
1127 } else if bytes.Equal(b[i+1:i+9], []byte("/script>")) {
1128 i++
1129 if j < start {
1130 // avoid append
1131 j += copy(b[j:], b[start:i])
1132 b[j] = '\\'
1133 j++
1134 start = i
1135 } else {
1136 b = append(append(b[:i], '\\'), b[i:]...)
1137 i++
1138 b[i] = '/' // was overwritten above
1139 }
1140 }
1141 }
1142 }
1143 if start != 0 {
1144 j += copy(b[j:], b[start:])
1145 return b[:j]
1146 }
1147 return b
1148}
1149
1150var regexpEscapeTable = [256]bool{
1151 // ASCII
1152 false, false, false, false, false, false, false, false,
1153 false, false, false, false, false, false, false, false,
1154 false, false, false, false, false, false, false, false,
1155 false, false, false, false, false, false, false, false,
1156
1157 false, false, false, false, true, false, false, false, // $
1158 true, true, true, true, false, false, true, true, // (, ), *, +, ., /
1159 true, true, true, true, true, true, true, true, // 0, 1, 2, 3, 4, 5, 6, 7
1160 true, true, false, false, false, false, false, true, // 8, 9, ?
1161
1162 false, false, true, false, true, false, false, false, // B, D
1163 false, false, false, false, false, false, false, false,
1164 true, false, false, true, false, false, false, true, // P, S, W
1165 false, false, false, true, true, true, true, false, // [, \, ], ^
1166
1167 false, false, true, true, true, false, true, false, // b, c, d, f
1168 false, false, false, true, false, false, true, false, // k, n
1169 true, false, true, true, true, true, true, true, // p, r, s, t, u, v, w
1170 true, false, false, true, true, true, false, false, // x, {, |, }
1171
1172 // non-ASCII
1173 false, false, false, false, false, false, false, false,
1174 false, false, false, false, false, false, false, false,
1175 false, false, false, false, false, false, false, false,
1176 false, false, false, false, false, false, false, false,
1177
1178 false, false, false, false, false, false, false, false,
1179 false, false, false, false, false, false, false, false,
1180 false, false, false, false, false, false, false, false,
1181 false, false, false, false, false, false, false, false,
1182
1183 false, false, false, false, false, false, false, false,
1184 false, false, false, false, false, false, false, false,
1185 false, false, false, false, false, false, false, false,
1186 false, false, false, false, false, false, false, false,
1187
1188 false, false, false, false, false, false, false, false,
1189 false, false, false, false, false, false, false, false,
1190 false, false, false, false, false, false, false, false,
1191 false, false, false, false, false, false, false, false,
1192}
1193
1194var regexpClassEscapeTable = [256]bool{
1195 // ASCII
1196 false, false, false, false, false, false, false, false,
1197 false, false, false, false, false, false, false, false,
1198 false, false, false, false, false, false, false, false,
1199 false, false, false, false, false, false, false, false,
1200
1201 false, false, false, false, false, false, false, false,
1202 false, false, false, false, false, false, false, false,
1203 true, true, true, true, true, true, true, true, // 0, 1, 2, 3, 4, 5, 6, 7
1204 true, true, false, false, false, false, false, false, // 8, 9
1205
1206 false, false, false, false, true, false, false, false, // D
1207 false, false, false, false, false, false, false, false,
1208 true, false, false, true, false, false, false, true, // P, S, W
1209 false, false, false, false, true, true, false, false, // \, ]
1210
1211 false, false, true, true, true, false, true, false, // b, c, d, f
1212 false, false, false, false, false, false, true, false, // n
1213 true, false, true, true, true, true, true, true, // p, r, s, t, u, v, w
1214 true, false, false, false, false, false, false, false, // x
1215
1216 // non-ASCII
1217 false, false, false, false, false, false, false, false,
1218 false, false, false, false, false, false, false, false,
1219 false, false, false, false, false, false, false, false,
1220 false, false, false, false, false, false, false, false,
1221
1222 false, false, false, false, false, false, false, false,
1223 false, false, false, false, false, false, false, false,
1224 false, false, false, false, false, false, false, false,
1225 false, false, false, false, false, false, false, false,
1226
1227 false, false, false, false, false, false, false, false,
1228 false, false, false, false, false, false, false, false,
1229 false, false, false, false, false, false, false, false,
1230 false, false, false, false, false, false, false, false,
1231
1232 false, false, false, false, false, false, false, false,
1233 false, false, false, false, false, false, false, false,
1234 false, false, false, false, false, false, false, false,
1235 false, false, false, false, false, false, false, false,
1236}
1237
1238func minifyRegExp(b []byte) []byte {
1239 inClass := false
1240 afterDash := 0
1241 iClass := 0
1242 for i := 1; i < len(b)-1; i++ {
1243 if inClass {
1244 afterDash++
1245 }
1246 if b[i] == '\\' {
1247 c := b[i+1]
1248 escape := true
1249 if inClass {
1250 escape = regexpClassEscapeTable[c] || c == '-' && 2 < afterDash && i+2 < len(b) && b[i+2] != ']' || c == '^' && i == iClass+1
1251 } else {
1252 escape = regexpEscapeTable[c]
1253 }
1254 if !escape {
1255 b = append(b[:i], b[i+1:]...)
1256 if inClass && 2 < afterDash && c == '-' {
1257 afterDash = 0
1258 } else if inClass && c == '^' {
1259 afterDash = 1
1260 }
1261 } else {
1262 i++
1263 }
1264 } else if b[i] == '[' {
1265 if b[i+1] == '^' {
1266 i++
1267 }
1268 afterDash = 1
1269 inClass = true
1270 iClass = i
1271 } else if inClass && b[i] == ']' {
1272 inClass = false
1273 } else if b[i] == '/' {
1274 break
1275 } else if inClass && 2 < afterDash && b[i] == '-' {
1276 afterDash = 0
1277 }
1278 }
1279 return b
1280}
1281
1282func removeUnderscores(b []byte) []byte {
1283 for i := 0; i < len(b); i++ {
1284 if b[i] == '_' {
1285 b = append(b[:i], b[i+1:]...)
1286 i--
1287 }
1288 }
1289 return b
1290}
1291
1292func decimalNumber(b []byte, prec int) []byte {
1293 b = removeUnderscores(b)
1294 return minify.Number(b, prec)
1295}
1296
1297func binaryNumber(b []byte, prec int) []byte {
1298 b = removeUnderscores(b)
1299 if len(b) <= 2 || 65 < len(b) {
1300 return b
1301 }
1302 var n int64
1303 for _, c := range b[2:] {
1304 n *= 2
1305 n += int64(c - '0')
1306 }
1307 i := strconv.LenInt(n) - 1
1308 b = b[:i+1]
1309 for 0 <= i {
1310 b[i] = byte('0' + n%10)
1311 n /= 10
1312 i--
1313 }
1314 return minify.Number(b, prec)
1315}
1316
1317func octalNumber(b []byte, prec int) []byte {
1318 b = removeUnderscores(b)
1319 if len(b) <= 2 || 23 < len(b) {
1320 return b
1321 }
1322 var n int64
1323 for _, c := range b[2:] {
1324 n *= 8
1325 n += int64(c - '0')
1326 }
1327 i := strconv.LenInt(n) - 1
1328 b = b[:i+1]
1329 for 0 <= i {
1330 b[i] = byte('0' + n%10)
1331 n /= 10
1332 i--
1333 }
1334 return minify.Number(b, prec)
1335}
1336
1337func hexadecimalNumber(b []byte, prec int) []byte {
1338 b = removeUnderscores(b)
1339 if len(b) <= 2 || 12 < len(b) || len(b) == 12 && ('D' < b[2] && b[2] <= 'F' || 'd' < b[2]) {
1340 return b
1341 }
1342 var n int64
1343 for _, c := range b[2:] {
1344 n *= 16
1345 if c <= '9' {
1346 n += int64(c - '0')
1347 } else if c <= 'F' {
1348 n += 10 + int64(c-'A')
1349 } else {
1350 n += 10 + int64(c-'a')
1351 }
1352 }
1353 i := strconv.LenInt(n) - 1
1354 b = b[:i+1]
1355 for 0 <= i {
1356 b[i] = byte('0' + n%10)
1357 n /= 10
1358 i--
1359 }
1360 return minify.Number(b, prec)
1361}
diff --git a/vendor/github.com/tdewolff/minify/v2/js/vars.go b/vendor/github.com/tdewolff/minify/v2/js/vars.go
new file mode 100644
index 0000000..81457c3
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/js/vars.go
@@ -0,0 +1,443 @@
1package js
2
3import (
4 "bytes"
5 "sort"
6
7 "github.com/tdewolff/parse/v2/js"
8)
9
10const identStartLen = 54
11const identContinueLen = 64
12
13type renamer struct {
14 identStart []byte
15 identContinue []byte
16 identOrder map[byte]int
17 reserved map[string]struct{}
18 rename bool
19}
20
21func newRenamer(rename, useCharFreq bool) *renamer {
22 reserved := make(map[string]struct{}, len(js.Keywords))
23 for name := range js.Keywords {
24 reserved[name] = struct{}{}
25 }
26 identStart := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$")
27 identContinue := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$0123456789")
28 if useCharFreq {
29 // sorted based on character frequency of a collection of JS samples
30 identStart = []byte("etnsoiarclduhmfpgvbjy_wOxCEkASMFTzDNLRPHIBV$WUKqYGXQZJ")
31 identContinue = []byte("etnsoiarcldu14023hm8f6pg57v9bjy_wOxCEkASMFTzDNLRPHIBV$WUKqYGXQZJ")
32 }
33 if len(identStart) != identStartLen || len(identContinue) != identContinueLen {
34 panic("bad identStart or identContinue lengths")
35 }
36 identOrder := map[byte]int{}
37 for i, c := range identStart {
38 identOrder[c] = i
39 }
40 return &renamer{
41 identStart: identStart,
42 identContinue: identContinue,
43 identOrder: identOrder,
44 reserved: reserved,
45 rename: rename,
46 }
47}
48
49func (r *renamer) renameScope(scope js.Scope) {
50 if !r.rename {
51 return
52 }
53
54 i := 0
55 // keep function argument declaration order to improve GZIP compression
56 sort.Sort(js.VarsByUses(scope.Declared[scope.NumFuncArgs:]))
57 for _, v := range scope.Declared {
58 v.Data = r.getName(v.Data, i)
59 i++
60 for r.isReserved(v.Data, scope.Undeclared) {
61 v.Data = r.getName(v.Data, i)
62 i++
63 }
64 }
65}
66
67func (r *renamer) isReserved(name []byte, undeclared js.VarArray) bool {
68 if 1 < len(name) { // there are no keywords or known globals that are one character long
69 if _, ok := r.reserved[string(name)]; ok {
70 return true
71 }
72 }
73 for _, v := range undeclared {
74 for v.Link != nil {
75 v = v.Link
76 }
77 if bytes.Equal(v.Data, name) {
78 return true
79 }
80 }
81 return false
82}
83
84func (r *renamer) getIndex(name []byte) int {
85 index := 0
86NameLoop:
87 for i := len(name) - 1; 0 <= i; i-- {
88 chars := r.identContinue
89 if i == 0 {
90 chars = r.identStart
91 index *= identStartLen
92 } else {
93 index *= identContinueLen
94 }
95 for j, c := range chars {
96 if name[i] == c {
97 index += j
98 continue NameLoop
99 }
100 }
101 return -1
102 }
103 for n := 0; n < len(name)-1; n++ {
104 offset := identStartLen
105 for i := 0; i < n; i++ {
106 offset *= identContinueLen
107 }
108 index += offset
109 }
110 return index
111}
112
113func (r *renamer) getName(name []byte, index int) []byte {
114 // Generate new names for variables where the last character is (a-zA-Z$_) and others are (a-zA-Z).
115 // Thus we can have 54 one-character names and 52*54=2808 two-character names for every branch leaf.
116 // That is sufficient for virtually all input.
117
118 // one character
119 if index < identStartLen {
120 name[0] = r.identStart[index]
121 return name[:1]
122 }
123 index -= identStartLen
124
125 // two characters or more
126 n := 2
127 for {
128 offset := identStartLen
129 for i := 0; i < n-1; i++ {
130 offset *= identContinueLen
131 }
132 if index < offset {
133 break
134 }
135 index -= offset
136 n++
137 }
138
139 if cap(name) < n {
140 name = make([]byte, n)
141 } else {
142 name = name[:n]
143 }
144 name[0] = r.identStart[index%identStartLen]
145 index /= identStartLen
146 for i := 1; i < n; i++ {
147 name[i] = r.identContinue[index%identContinueLen]
148 index /= identContinueLen
149 }
150 return name
151}
152
153////////////////////////////////////////////////////////////////
154
155func hasDefines(v *js.VarDecl) bool {
156 for _, item := range v.List {
157 if item.Default != nil {
158 return true
159 }
160 }
161 return false
162}
163
164func bindingVars(ibinding js.IBinding) (vs []*js.Var) {
165 switch binding := ibinding.(type) {
166 case *js.Var:
167 vs = append(vs, binding)
168 case *js.BindingArray:
169 for _, item := range binding.List {
170 if item.Binding != nil {
171 vs = append(vs, bindingVars(item.Binding)...)
172 }
173 }
174 if binding.Rest != nil {
175 vs = append(vs, bindingVars(binding.Rest)...)
176 }
177 case *js.BindingObject:
178 for _, item := range binding.List {
179 if item.Value.Binding != nil {
180 vs = append(vs, bindingVars(item.Value.Binding)...)
181 }
182 }
183 if binding.Rest != nil {
184 vs = append(vs, binding.Rest)
185 }
186 }
187 return
188}
189
190func addDefinition(decl *js.VarDecl, binding js.IBinding, value js.IExpr, forward bool) {
191 // see if not already defined in variable declaration list
192 // if forward is set, binding=value comes before decl, otherwise the reverse holds true
193 vars := bindingVars(binding)
194
195 // remove variables in destination
196RemoveVarsLoop:
197 for _, vbind := range vars {
198 for i, item := range decl.List {
199 if v, ok := item.Binding.(*js.Var); ok && item.Default == nil && v == vbind {
200 v.Uses--
201 decl.List = append(decl.List[:i], decl.List[i+1:]...)
202 continue RemoveVarsLoop
203 }
204 }
205
206 if value != nil {
207 // variable declaration must be somewhere else, find and remove it
208 for _, decl2 := range decl.Scope.Func.VarDecls {
209 for i, item := range decl2.List {
210 if v, ok := item.Binding.(*js.Var); ok && item.Default == nil && v == vbind {
211 v.Uses--
212 decl2.List = append(decl2.List[:i], decl2.List[i+1:]...)
213 continue RemoveVarsLoop
214 }
215 }
216 }
217 }
218 }
219
220 // add declaration to destination
221 item := js.BindingElement{Binding: binding, Default: value}
222 if forward {
223 decl.List = append([]js.BindingElement{item}, decl.List...)
224 } else {
225 decl.List = append(decl.List, item)
226 }
227}
228
229func mergeVarDecls(dst, src *js.VarDecl, forward bool) {
230 // Merge var declarations by moving declarations from src to dst. If forward is set, src comes first and dst after, otherwise the order is reverse.
231 if forward {
232 // reverse order so we can iterate from beginning to end, sometimes addDefinition may remove another declaration in the src list
233 n := len(src.List) - 1
234 for j := 0; j < len(src.List)/2; j++ {
235 src.List[j], src.List[n-j] = src.List[n-j], src.List[j]
236 }
237 }
238 for j := 0; j < len(src.List); j++ {
239 addDefinition(dst, src.List[j].Binding, src.List[j].Default, forward)
240 }
241 src.List = src.List[:0]
242}
243
244func mergeVarDeclExprStmt(decl *js.VarDecl, exprStmt *js.ExprStmt, forward bool) bool {
245 // Merge var declarations with an assignment expression. If forward is set than expr comes first and decl after, otherwise the order is reverse.
246 if decl2, ok := exprStmt.Value.(*js.VarDecl); ok {
247 // this happens when a variable declarations is converted to an expression due to hoisting
248 mergeVarDecls(decl, decl2, forward)
249 return true
250 } else if commaExpr, ok := exprStmt.Value.(*js.CommaExpr); ok {
251 n := 0
252 for i := 0; i < len(commaExpr.List); i++ {
253 item := commaExpr.List[i]
254 if forward {
255 item = commaExpr.List[len(commaExpr.List)-i-1]
256 }
257 if src, ok := item.(*js.VarDecl); ok {
258 // this happens when a variable declarations is converted to an expression due to hoisting
259 mergeVarDecls(decl, src, forward)
260 n++
261 continue
262 } else if binaryExpr, ok := item.(*js.BinaryExpr); ok && binaryExpr.Op == js.EqToken {
263 if v, ok := binaryExpr.X.(*js.Var); ok && v.Decl == js.VariableDecl {
264 addDefinition(decl, v, binaryExpr.Y, forward)
265 n++
266 continue
267 }
268 }
269 break
270 }
271 merge := n == len(commaExpr.List)
272 if !forward {
273 commaExpr.List = commaExpr.List[n:]
274 } else {
275 commaExpr.List = commaExpr.List[:len(commaExpr.List)-n]
276 }
277 return merge
278 } else if binaryExpr, ok := exprStmt.Value.(*js.BinaryExpr); ok && binaryExpr.Op == js.EqToken {
279 if v, ok := binaryExpr.X.(*js.Var); ok && v.Decl == js.VariableDecl {
280 addDefinition(decl, v, binaryExpr.Y, forward)
281 return true
282 }
283 }
284 return false
285}
286
287func (m *jsMinifier) countHoistLength(ibinding js.IBinding) int {
288 if !m.o.KeepVarNames {
289 return len(bindingVars(ibinding)) * 2 // assume that var name will be of length one, +1 for the comma
290 }
291
292 n := 0
293 for _, v := range bindingVars(ibinding) {
294 n += len(v.Data) + 1 // +1 for the comma when added to other declaration
295 }
296 return n
297}
298
299func (m *jsMinifier) hoistVars(body *js.BlockStmt) {
300 // Hoist all variable declarations in the current module/function scope to the top.
301 // If the first statement is a var declaration, expand it. Otherwise prepend a new var declaration.
302 // Except for the first var declaration, all others are converted to expressions. This is possible because an ArrayBindingPattern and ObjectBindingPattern can be converted to an ArrayLiteral or ObjectLiteral respectively, as they are supersets of the BindingPatterns.
303 if 1 < len(body.Scope.VarDecls) {
304 // Select which variable declarations will be hoisted (convert to expression) and which not
305 best := 0
306 score := make([]int, len(body.Scope.VarDecls)) // savings if hoisted
307 hoist := make([]bool, len(body.Scope.VarDecls))
308 for i, varDecl := range body.Scope.VarDecls {
309 hoist[i] = true
310 score[i] = 4 // "var "
311 if !varDecl.InForInOf {
312 n := 0
313 nArrays := 0
314 nObjects := 0
315 hasDefinitions := false
316 for j, item := range varDecl.List {
317 if item.Default != nil {
318 if _, ok := item.Binding.(*js.BindingObject); ok {
319 if j != 0 && nArrays == 0 && nObjects == 0 {
320 varDecl.List[0], varDecl.List[j] = varDecl.List[j], varDecl.List[0]
321 }
322 nObjects++
323 } else if _, ok := item.Binding.(*js.BindingArray); ok {
324 if j != 0 && nArrays == 0 && nObjects == 0 {
325 varDecl.List[0], varDecl.List[j] = varDecl.List[j], varDecl.List[0]
326 }
327 nArrays++
328 }
329 score[i] -= m.countHoistLength(item.Binding) // var names and commas
330 hasDefinitions = true
331 n++
332 }
333 }
334 if !hasDefinitions {
335 score[i] = 5 - 1 // 1 for a comma
336 if varDecl.InFor {
337 score[i]-- // semicolon can be reused
338 }
339 }
340 if nObjects != 0 && !varDecl.InFor && nObjects == n {
341 score[i] -= 2 // required parenthesis around braces
342 }
343 if nArrays != 0 || nObjects != 0 {
344 score[i]-- // space after var disappears
345 }
346 if score[i] < score[best] || body.Scope.VarDecls[best].InForInOf {
347 // select var decl with the least savings if hoisted
348 best = i
349 }
350 if score[i] < 0 {
351 hoist[i] = false
352 }
353 }
354 }
355 if body.Scope.VarDecls[best].InForInOf {
356 // no savings possible
357 return
358 }
359
360 decl := body.Scope.VarDecls[best]
361 if 10000 < len(decl.List) {
362 return
363 }
364 hoist[best] = false
365
366 // get original declarations
367 orig := []*js.Var{}
368 for _, item := range decl.List {
369 orig = append(orig, bindingVars(item.Binding)...)
370 }
371
372 // hoist other variable declarations in this function scope but don't initialize yet
373 j := 0
374 for i, varDecl := range body.Scope.VarDecls {
375 if hoist[i] {
376 varDecl.TokenType = js.ErrorToken
377 for _, item := range varDecl.List {
378 refs := bindingVars(item.Binding)
379 bindingElements := make([]js.BindingElement, 0, len(refs))
380 DeclaredLoop:
381 for _, ref := range refs {
382 for _, v := range orig {
383 if ref == v {
384 continue DeclaredLoop
385 }
386 }
387 bindingElements = append(bindingElements, js.BindingElement{Binding: ref, Default: nil})
388 orig = append(orig, ref)
389
390 s := decl.Scope
391 for s != nil && s != s.Func {
392 s.AddUndeclared(ref)
393 s = s.Parent
394 }
395 if item.Default != nil {
396 ref.Uses++
397 }
398 }
399 if i < best {
400 // prepend
401 decl.List = append(decl.List[:j], append(bindingElements, decl.List[j:]...)...)
402 j += len(bindingElements)
403 } else {
404 // append
405 decl.List = append(decl.List, bindingElements...)
406 }
407 }
408 }
409 }
410
411 // rearrange to put array/object first
412 var prevRefs []*js.Var
413 BeginArrayObject:
414 for i, item := range decl.List {
415 refs := bindingVars(item.Binding)
416 if _, ok := item.Binding.(*js.Var); !ok {
417 if i != 0 {
418 interferes := false
419 if item.Default != nil {
420 InterferenceLoop:
421 for _, ref := range refs {
422 for _, v := range prevRefs {
423 if ref == v {
424 interferes = true
425 break InterferenceLoop
426 }
427 }
428 }
429 }
430 if !interferes {
431 decl.List[0], decl.List[i] = decl.List[i], decl.List[0]
432 break BeginArrayObject
433 }
434 } else {
435 break BeginArrayObject
436 }
437 }
438 if item.Default != nil {
439 prevRefs = append(prevRefs, refs...)
440 }
441 }
442 }
443}
diff --git a/vendor/github.com/tdewolff/minify/v2/minify.go b/vendor/github.com/tdewolff/minify/v2/minify.go
new file mode 100644
index 0000000..8e2e3fd
--- /dev/null
+++ b/vendor/github.com/tdewolff/minify/v2/minify.go
@@ -0,0 +1,371 @@
1// Package minify relates MIME type to minifiers. Several minifiers are provided in the subpackages.
2package minify
3
4import (
5 "bytes"
6 "errors"
7 "fmt"
8 "io"
9 "io/ioutil"
10 "log"
11 "mime"
12 "net/http"
13 "net/url"
14 "os"
15 "os/exec"
16 "path"
17 "regexp"
18 "strings"
19 "sync"
20
21 "github.com/tdewolff/parse/v2"
22 "github.com/tdewolff/parse/v2/buffer"
23)
24
25// Warning is used to report usage warnings such as using a deprecated feature
26var Warning = log.New(os.Stderr, "WARNING: ", 0)
27
28// ErrNotExist is returned when no minifier exists for a given mimetype.
29var ErrNotExist = errors.New("minifier does not exist for mimetype")
30
31// ErrClosedWriter is returned when writing to a closed writer.
32var ErrClosedWriter = errors.New("write on closed writer")
33
34////////////////////////////////////////////////////////////////
35
36// MinifierFunc is a function that implements Minifer.
37type MinifierFunc func(*M, io.Writer, io.Reader, map[string]string) error
38
39// Minify calls f(m, w, r, params)
40func (f MinifierFunc) Minify(m *M, w io.Writer, r io.Reader, params map[string]string) error {
41 return f(m, w, r, params)
42}
43
44// Minifier is the interface for minifiers.
45// The *M parameter is used for minifying embedded resources, such as JS within HTML.
46type Minifier interface {
47 Minify(*M, io.Writer, io.Reader, map[string]string) error
48}
49
50////////////////////////////////////////////////////////////////
51
52type patternMinifier struct {
53 pattern *regexp.Regexp
54 Minifier
55}
56
57type cmdMinifier struct {
58 cmd *exec.Cmd
59}
60
61var cmdArgExtension = regexp.MustCompile(`^\.[0-9a-zA-Z]+`)
62
63func (c *cmdMinifier) Minify(_ *M, w io.Writer, r io.Reader, _ map[string]string) error {
64 cmd := &exec.Cmd{}
65 *cmd = *c.cmd // concurrency safety
66
67 var in, out *os.File
68 for i, arg := range cmd.Args {
69 if j := strings.Index(arg, "$in"); j != -1 {
70 var err error
71 ext := cmdArgExtension.FindString(arg[j+3:])
72 if in, err = ioutil.TempFile("", "minify-in-*"+ext); err != nil {
73 return err
74 }
75 cmd.Args[i] = arg[:j] + in.Name() + arg[j+3+len(ext):]
76 } else if j := strings.Index(arg, "$out"); j != -1 {
77 var err error
78 ext := cmdArgExtension.FindString(arg[j+4:])
79 if out, err = ioutil.TempFile("", "minify-out-*"+ext); err != nil {
80 return err
81 }
82 cmd.Args[i] = arg[:j] + out.Name() + arg[j+4+len(ext):]
83 }
84 }
85
86 if in == nil {
87 cmd.Stdin = r
88 } else if _, err := io.Copy(in, r); err != nil {
89 return err
90 }
91 if out == nil {
92 cmd.Stdout = w
93 } else {
94 defer io.Copy(w, out)
95 }
96 stderr := &bytes.Buffer{}
97 cmd.Stderr = stderr
98
99 err := cmd.Run()
100 if _, ok := err.(*exec.ExitError); ok {
101 if stderr.Len() != 0 {
102 err = fmt.Errorf("%s", stderr.String())
103 }
104 err = fmt.Errorf("command %s failed: %w", cmd.Path, err)
105 }
106 return err
107}
108
109////////////////////////////////////////////////////////////////
110
111// M holds a map of mimetype => function to allow recursive minifier calls of the minifier functions.
112type M struct {
113 mutex sync.RWMutex
114 literal map[string]Minifier
115 pattern []patternMinifier
116
117 URL *url.URL
118}
119
120// New returns a new M.
121func New() *M {
122 return &M{
123 sync.RWMutex{},
124 map[string]Minifier{},
125 []patternMinifier{},
126 nil,
127 }
128}
129
130// Add adds a minifier to the mimetype => function map (unsafe for concurrent use).
131func (m *M) Add(mimetype string, minifier Minifier) {
132 m.mutex.Lock()
133 m.literal[mimetype] = minifier
134 m.mutex.Unlock()
135}
136
137// AddFunc adds a minify function to the mimetype => function map (unsafe for concurrent use).
138func (m *M) AddFunc(mimetype string, minifier MinifierFunc) {
139 m.mutex.Lock()
140 m.literal[mimetype] = minifier
141 m.mutex.Unlock()
142}
143
144// AddRegexp adds a minifier to the mimetype => function map (unsafe for concurrent use).
145func (m *M) AddRegexp(pattern *regexp.Regexp, minifier Minifier) {
146 m.mutex.Lock()
147 m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
148 m.mutex.Unlock()
149}
150
151// AddFuncRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use).
152func (m *M) AddFuncRegexp(pattern *regexp.Regexp, minifier MinifierFunc) {
153 m.mutex.Lock()
154 m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
155 m.mutex.Unlock()
156}
157
158// AddCmd adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
159// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype.
160func (m *M) AddCmd(mimetype string, cmd *exec.Cmd) {
161 m.mutex.Lock()
162 m.literal[mimetype] = &cmdMinifier{cmd}
163 m.mutex.Unlock()
164}
165
166// AddCmdRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
167// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype regular expression.
168func (m *M) AddCmdRegexp(pattern *regexp.Regexp, cmd *exec.Cmd) {
169 m.mutex.Lock()
170 m.pattern = append(m.pattern, patternMinifier{pattern, &cmdMinifier{cmd}})
171 m.mutex.Unlock()
172}
173
174// Match returns the pattern and minifier that gets matched with the mediatype.
175// It returns nil when no matching minifier exists.
176// It has the same matching algorithm as Minify.
177func (m *M) Match(mediatype string) (string, map[string]string, MinifierFunc) {
178 m.mutex.RLock()
179 defer m.mutex.RUnlock()
180
181 mimetype, params := parse.Mediatype([]byte(mediatype))
182 if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
183 return string(mimetype), params, minifier.Minify
184 }
185
186 for _, minifier := range m.pattern {
187 if minifier.pattern.Match(mimetype) {
188 return minifier.pattern.String(), params, minifier.Minify
189 }
190 }
191 return string(mimetype), params, nil
192}
193
194// Minify minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
195// An error is returned when no such mimetype exists (ErrNotExist) or when an error occurred in the minifier function.
196// Mediatype may take the form of 'text/plain', 'text/*', '*/*' or 'text/plain; charset=UTF-8; version=2.0'.
197func (m *M) Minify(mediatype string, w io.Writer, r io.Reader) error {
198 mimetype, params := parse.Mediatype([]byte(mediatype))
199 return m.MinifyMimetype(mimetype, w, r, params)
200}
201
202// MinifyMimetype minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
203// It is a lower level version of Minify and requires the mediatype to be split up into mimetype and parameters.
204// It is mostly used internally by minifiers because it is faster (no need to convert a byte-slice to string and vice versa).
205func (m *M) MinifyMimetype(mimetype []byte, w io.Writer, r io.Reader, params map[string]string) error {
206 m.mutex.RLock()
207 defer m.mutex.RUnlock()
208
209 if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
210 return minifier.Minify(m, w, r, params)
211 }
212 for _, minifier := range m.pattern {
213 if minifier.pattern.Match(mimetype) {
214 return minifier.Minify(m, w, r, params)
215 }
216 }
217 return ErrNotExist
218}
219
220// Bytes minifies an array of bytes (safe for concurrent use). When an error occurs it return the original array and the error.
221// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
222func (m *M) Bytes(mediatype string, v []byte) ([]byte, error) {
223 out := buffer.NewWriter(make([]byte, 0, len(v)))
224 if err := m.Minify(mediatype, out, buffer.NewReader(v)); err != nil {
225 return v, err
226 }
227 return out.Bytes(), nil
228}
229
230// String minifies a string (safe for concurrent use). When an error occurs it return the original string and the error.
231// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
232func (m *M) String(mediatype string, v string) (string, error) {
233 out := buffer.NewWriter(make([]byte, 0, len(v)))
234 if err := m.Minify(mediatype, out, buffer.NewReader([]byte(v))); err != nil {
235 return v, err
236 }
237 return string(out.Bytes()), nil
238}
239
240// Reader wraps a Reader interface and minifies the stream.
241// Errors from the minifier are returned by the reader.
242func (m *M) Reader(mediatype string, r io.Reader) io.Reader {
243 pr, pw := io.Pipe()
244 go func() {
245 if err := m.Minify(mediatype, pw, r); err != nil {
246 pw.CloseWithError(err)
247 } else {
248 pw.Close()
249 }
250 }()
251 return pr
252}
253
254// writer makes sure that errors from the minifier are passed down through Close (can be blocking).
255type writer struct {
256 pw *io.PipeWriter
257 wg sync.WaitGroup
258 err error
259 closed bool
260}
261
262// Write intercepts any writes to the writer.
263func (w *writer) Write(b []byte) (int, error) {
264 if w.closed {
265 return 0, ErrClosedWriter
266 }
267 n, err := w.pw.Write(b)
268 if w.err != nil {
269 err = w.err
270 }
271 return n, err
272}
273
274// Close must be called when writing has finished. It returns the error from the minifier.
275func (w *writer) Close() error {
276 if !w.closed {
277 w.pw.Close()
278 w.wg.Wait()
279 w.closed = true
280 }
281 return w.err
282}
283
284// Writer wraps a Writer interface and minifies the stream.
285// Errors from the minifier are returned by Close on the writer.
286// The writer must be closed explicitly.
287func (m *M) Writer(mediatype string, w io.Writer) io.WriteCloser {
288 pr, pw := io.Pipe()
289 mw := &writer{pw, sync.WaitGroup{}, nil, false}
290 mw.wg.Add(1)
291 go func() {
292 defer mw.wg.Done()
293
294 if err := m.Minify(mediatype, w, pr); err != nil {
295 mw.err = err
296 }
297 pr.Close()
298 }()
299 return mw
300}
301
302// responseWriter wraps an http.ResponseWriter and makes sure that errors from the minifier are passed down through Close (can be blocking).
303// All writes to the response writer are intercepted and minified on the fly.
304// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
305type responseWriter struct {
306 http.ResponseWriter
307
308 writer *writer
309 m *M
310 mediatype string
311}
312
313// WriteHeader intercepts any header writes and removes the Content-Length header.
314func (w *responseWriter) WriteHeader(status int) {
315 w.ResponseWriter.Header().Del("Content-Length")
316 w.ResponseWriter.WriteHeader(status)
317}
318
319// Write intercepts any writes to the response writer.
320// The first write will extract the Content-Type as the mediatype. Otherwise it falls back to the RequestURI extension.
321func (w *responseWriter) Write(b []byte) (int, error) {
322 if w.writer == nil {
323 // first write
324 if mediatype := w.ResponseWriter.Header().Get("Content-Type"); mediatype != "" {
325 w.mediatype = mediatype
326 }
327 w.writer = w.m.Writer(w.mediatype, w.ResponseWriter).(*writer)
328 }
329 return w.writer.Write(b)
330}
331
332// Close must be called when writing has finished. It returns the error from the minifier.
333func (w *responseWriter) Close() error {
334 if w.writer != nil {
335 return w.writer.Close()
336 }
337 return nil
338}
339
340// ResponseWriter minifies any writes to the http.ResponseWriter.
341// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
342// Minification might be slower than just sending the original file! Caching is advised.
343func (m *M) ResponseWriter(w http.ResponseWriter, r *http.Request) *responseWriter {
344 mediatype := mime.TypeByExtension(path.Ext(r.RequestURI))
345 return &responseWriter{w, nil, m, mediatype}
346}
347
348// Middleware provides a middleware function that minifies content on the fly by intercepting writes to http.ResponseWriter.
349// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
350// Minification might be slower than just sending the original file! Caching is advised.
351func (m *M) Middleware(next http.Handler) http.Handler {
352 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
353 mw := m.ResponseWriter(w, r)
354 next.ServeHTTP(mw, r)
355 mw.Close()
356 })
357}
358
359// MiddlewareWithError provides a middleware function that minifies content on the fly by intercepting writes to http.ResponseWriter. The error function allows handling minification errors.
360// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
361// Minification might be slower than just sending the original file! Caching is advised.
362func (m *M) MiddlewareWithError(next http.Handler, errorFunc func(w http.ResponseWriter, r *http.Request, err error)) http.Handler {
363 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
364 mw := m.ResponseWriter(w, r)
365 next.ServeHTTP(mw, r)
366 if err := mw.Close(); err != nil {
367 errorFunc(w, r, err)
368 return
369 }
370 })
371}