Compare commits
23 Commits
j2html-0.5
...
j2html-0.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66b298efe4 | ||
|
|
ed44fc79e0 | ||
|
|
c926322fb6 | ||
|
|
d52f86dd39 | ||
|
|
5692033f90 | ||
|
|
8667fd31d7 | ||
|
|
9ef6d645d8 | ||
|
|
4a2f502e5d | ||
|
|
f54f0a53e4 | ||
|
|
04439d79de | ||
|
|
f2d6621570 | ||
|
|
4f05b26dc1 | ||
|
|
c2691df9c5 | ||
|
|
91b12a6da0 | ||
|
|
ccd76527bc | ||
|
|
ef43af72a3 | ||
|
|
5ad63ad1d6 | ||
|
|
e92b7ce527 | ||
|
|
7bbb3a34da | ||
|
|
7e3e827af5 | ||
|
|
0f5639660d | ||
|
|
aa64aca294 | ||
|
|
6f11845140 |
201
LICENSE
Normal file
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2015 David Åse
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
35
README.md
35
README.md
@@ -1,4 +1,37 @@
|
||||
# j2html
|
||||
Java to HTML generator. Enjoy typesafe HTML generation.
|
||||
|
||||
This project was inspired by [Gagawa](https://code.google.com/p/gagawa/)
|
||||
The project webpage is [j2html.com](http://j2html.com)
|
||||
|
||||
##Getting started
|
||||
###Add the maven dependency
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.j2html</groupId>
|
||||
<artifactId>j2html</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
###Import TagCreator and start building HTML
|
||||
```java
|
||||
import static j2html.TagCreator.*;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
body().with(
|
||||
h1("Heading!").withClass("example"),
|
||||
img().withSrc("img/hello.png")
|
||||
).render();
|
||||
}
|
||||
}
|
||||
```
|
||||
The above Java will result in the following HTML:
|
||||
```html
|
||||
<body>
|
||||
<h1 class="example">Heading!</h1>
|
||||
<img src="img/hello.png">
|
||||
</body>
|
||||
```
|
||||
|
||||
Find more examples at http://j2html.com/examples.html
|
||||
|
||||
27
pom.xml
27
pom.xml
@@ -10,7 +10,7 @@
|
||||
|
||||
<groupId>com.j2html</groupId>
|
||||
<artifactId>j2html</artifactId>
|
||||
<version>0.5.0</version>
|
||||
<version>0.7</version>
|
||||
|
||||
<name>j2html</name>
|
||||
<description>Java to HTML builder with a fluent API</description>
|
||||
@@ -39,16 +39,21 @@
|
||||
<url>https://github.com/tipsy/j2html/issues</url>
|
||||
</issueManagement>
|
||||
|
||||
<properties>
|
||||
<jdk.version>1.8</jdk.version>
|
||||
<junit.version>4.11</junit.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<version>4.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.javascript</groupId>
|
||||
<artifactId>closure-compiler</artifactId>
|
||||
<version>r2388</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -61,8 +66,8 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.3</version>
|
||||
<configuration>
|
||||
<source>${jdk.version}</source>
|
||||
<target>${jdk.version}</target>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<optimize>true</optimize>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@@ -79,7 +84,7 @@
|
||||
<configuration>
|
||||
<rules>
|
||||
<requireJavaVersion>
|
||||
<version>[${jdk.version},)</version>
|
||||
<version>[1.7,)</version>
|
||||
</requireJavaVersion>
|
||||
</rules>
|
||||
</configuration>
|
||||
@@ -127,4 +132,4 @@
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,170 +1,178 @@
|
||||
package j2html;
|
||||
|
||||
import j2html.tags.ContainerTag;
|
||||
import j2html.tags.EmptyTag;
|
||||
import j2html.tags.Text;
|
||||
import j2html.tags.UnescapedText;
|
||||
import j2html.tags.*;
|
||||
|
||||
public class TagCreator {
|
||||
|
||||
public static ContainerTag tag(String tagName) { return new ContainerTag(tagName); }
|
||||
public static EmptyTag emptyTag(String tagName) { return new EmptyTag(tagName); }
|
||||
//Special tags
|
||||
public static ContainerTag tag(String tagName) { return new ContainerTag(tagName); }
|
||||
public static EmptyTag emptyTag(String tagName) { return new EmptyTag(tagName); }
|
||||
|
||||
public static Text text(String text) { return new Text(text); }
|
||||
public static UnescapedText unsafeHtml(String html) { return new UnescapedText(html); }
|
||||
|
||||
public static Tag styleWithInlineFile(String path) { return InlineStaticResource.get(path, InlineStaticResource.TargetFormat.CSS); }
|
||||
public static Tag scriptWithInlineFile(String path) { return InlineStaticResource.get(path, InlineStaticResource.TargetFormat.JS); }
|
||||
public static Tag styleWithInlineFile_min(String path) { return InlineStaticResource.get(path, InlineStaticResource.TargetFormat.CSS_MIN); }
|
||||
public static Tag scriptWithInlineFile_min(String path) { return InlineStaticResource.get(path, InlineStaticResource.TargetFormat.JS_MIN); }
|
||||
|
||||
public static UnescapedText fileAsString(String path) { return new UnescapedText(InlineStaticResource.getFileAsString(path)); }
|
||||
public static Text fileAsEscapedString(String path) { return new Text(InlineStaticResource.getFileAsString(path)); }
|
||||
|
||||
public static Text text(String text) { return new Text(text); }
|
||||
public static UnescapedText unsafeHtml(String html) { return new UnescapedText(html); }
|
||||
|
||||
//EmptyTags
|
||||
public static EmptyTag area() { return new EmptyTag("area"); }
|
||||
public static EmptyTag base() { return new EmptyTag("base"); }
|
||||
public static EmptyTag br() { return new EmptyTag("br"); }
|
||||
public static EmptyTag col() { return new EmptyTag("col"); }
|
||||
public static EmptyTag document() { return new EmptyTag("!DOCTYPE html"); }
|
||||
public static EmptyTag embed() { return new EmptyTag("embed"); }
|
||||
public static EmptyTag hr() { return new EmptyTag("hr"); }
|
||||
public static EmptyTag img() { return new EmptyTag("img"); }
|
||||
public static EmptyTag input() { return new EmptyTag("input"); }
|
||||
public static EmptyTag keygen() { return new EmptyTag("keygen"); }
|
||||
public static EmptyTag link() { return new EmptyTag("link"); }
|
||||
public static EmptyTag meta() { return new EmptyTag("meta"); }
|
||||
public static EmptyTag param() { return new EmptyTag("param"); }
|
||||
public static EmptyTag source() { return new EmptyTag("source"); }
|
||||
public static EmptyTag track() { return new EmptyTag("track"); }
|
||||
public static EmptyTag wbr() { return new EmptyTag("wbr"); }
|
||||
public static EmptyTag area() { return new EmptyTag("area"); }
|
||||
public static EmptyTag base() { return new EmptyTag("base"); }
|
||||
public static EmptyTag br() { return new EmptyTag("br"); }
|
||||
public static EmptyTag col() { return new EmptyTag("col"); }
|
||||
public static EmptyTag document() { return new EmptyTag("!DOCTYPE html"); }
|
||||
public static EmptyTag embed() { return new EmptyTag("embed"); }
|
||||
public static EmptyTag hr() { return new EmptyTag("hr"); }
|
||||
public static EmptyTag img() { return new EmptyTag("img"); }
|
||||
public static EmptyTag input() { return new EmptyTag("input"); }
|
||||
public static EmptyTag keygen() { return new EmptyTag("keygen"); }
|
||||
public static EmptyTag link() { return new EmptyTag("link"); }
|
||||
public static EmptyTag meta() { return new EmptyTag("meta"); }
|
||||
public static EmptyTag param() { return new EmptyTag("param"); }
|
||||
public static EmptyTag source() { return new EmptyTag("source"); }
|
||||
public static EmptyTag track() { return new EmptyTag("track"); }
|
||||
public static EmptyTag wbr() { return new EmptyTag("wbr"); }
|
||||
|
||||
|
||||
//ContainerTags
|
||||
public static ContainerTag a() { return new ContainerTag("a"); }
|
||||
public static ContainerTag a(String text) { return new ContainerTag("a").withText(text); }
|
||||
public static ContainerTag abbr() { return new ContainerTag("abbr"); }
|
||||
public static ContainerTag address() { return new ContainerTag("address"); }
|
||||
public static ContainerTag article() { return new ContainerTag("article"); }
|
||||
public static ContainerTag aside() { return new ContainerTag("aside"); }
|
||||
public static ContainerTag audio() { return new ContainerTag("audio"); }
|
||||
public static ContainerTag b() { return new ContainerTag("b"); }
|
||||
public static ContainerTag b(String text) { return new ContainerTag("b").withText(text); }
|
||||
public static ContainerTag bdi() { return new ContainerTag("bdi"); }
|
||||
public static ContainerTag bdi(String text) { return new ContainerTag("bdi").withText(text); }
|
||||
public static ContainerTag bdo() { return new ContainerTag("bdo"); }
|
||||
public static ContainerTag bdo(String text) { return new ContainerTag("bdo").withText(text); }
|
||||
public static ContainerTag blockquote() { return new ContainerTag("blockquote"); }
|
||||
public static ContainerTag blockquote(String text) { return new ContainerTag("blockquote").withText(text); }
|
||||
public static ContainerTag body() { return new ContainerTag("body"); }
|
||||
public static ContainerTag button() { return new ContainerTag("button"); }
|
||||
public static ContainerTag button(String text) { return new ContainerTag("button").withText(text); }
|
||||
public static ContainerTag canvas() { return new ContainerTag("canvas"); }
|
||||
public static ContainerTag caption() { return new ContainerTag("caption"); }
|
||||
public static ContainerTag caption(String text) { return new ContainerTag("caption").withText(text); }
|
||||
public static ContainerTag cite() { return new ContainerTag("cite"); }
|
||||
public static ContainerTag cite(String text) { return new ContainerTag("cite").withText(text); }
|
||||
public static ContainerTag code() { return new ContainerTag("code"); }
|
||||
public static ContainerTag colgroup() { return new ContainerTag("colgroup"); }
|
||||
public static ContainerTag datalist() { return new ContainerTag("datalist"); }
|
||||
public static ContainerTag dd() { return new ContainerTag("dd"); }
|
||||
public static ContainerTag dd(String text) { return new ContainerTag("dd").withText(text); }
|
||||
public static ContainerTag del() { return new ContainerTag("del"); }
|
||||
public static ContainerTag del(String text) { return new ContainerTag("del").withText(text); }
|
||||
public static ContainerTag details() { return new ContainerTag("details"); }
|
||||
public static ContainerTag dfn() { return new ContainerTag("dfn"); }
|
||||
public static ContainerTag dfn(String text) { return new ContainerTag("dfn").withText(text); }
|
||||
public static ContainerTag dialog() { return new ContainerTag("dialog"); }
|
||||
public static ContainerTag dialog(String text) { return new ContainerTag("dialog").withText(text); }
|
||||
public static ContainerTag div() { return new ContainerTag("div"); }
|
||||
public static ContainerTag dl() { return new ContainerTag("dl"); }
|
||||
public static ContainerTag dt() { return new ContainerTag("dt"); }
|
||||
public static ContainerTag dt(String text) { return new ContainerTag("dt").withText(text); }
|
||||
public static ContainerTag em() { return new ContainerTag("em"); }
|
||||
public static ContainerTag em(String text) { return new ContainerTag("em").withText(text); }
|
||||
public static ContainerTag fieldset() { return new ContainerTag("fieldset"); }
|
||||
public static ContainerTag figcaption() { return new ContainerTag("figcaption"); }
|
||||
public static ContainerTag figcaption(String text) { return new ContainerTag("figcaption").withText(text); }
|
||||
public static ContainerTag figure() { return new ContainerTag("figure"); }
|
||||
public static ContainerTag footer() { return new ContainerTag("footer"); }
|
||||
public static ContainerTag form() { return new ContainerTag("form"); }
|
||||
public static ContainerTag h1() { return new ContainerTag("h1"); }
|
||||
public static ContainerTag h1(String text) { return new ContainerTag("h1").withText(text); }
|
||||
public static ContainerTag h2() { return new ContainerTag("h2"); }
|
||||
public static ContainerTag h2(String text) { return new ContainerTag("h2").withText(text); }
|
||||
public static ContainerTag h3() { return new ContainerTag("h3"); }
|
||||
public static ContainerTag h3(String text) { return new ContainerTag("h3").withText(text); }
|
||||
public static ContainerTag h4() { return new ContainerTag("h4"); }
|
||||
public static ContainerTag h4(String text) { return new ContainerTag("h4").withText(text); }
|
||||
public static ContainerTag h5() { return new ContainerTag("h5"); }
|
||||
public static ContainerTag h5(String text) { return new ContainerTag("h5").withText(text); }
|
||||
public static ContainerTag h6() { return new ContainerTag("h6"); }
|
||||
public static ContainerTag h6(String text) { return new ContainerTag("h6").withText(text); }
|
||||
public static ContainerTag head() { return new ContainerTag("head"); }
|
||||
public static ContainerTag header() { return new ContainerTag("header"); }
|
||||
public static ContainerTag html() { return new ContainerTag("html"); }
|
||||
public static ContainerTag i() { return new ContainerTag("i"); }
|
||||
public static ContainerTag i(String text) { return new ContainerTag("i").withText(text); }
|
||||
public static ContainerTag iframe() { return new ContainerTag("iframe"); }
|
||||
public static ContainerTag ins() { return new ContainerTag("ins"); }
|
||||
public static ContainerTag ins(String text) { return new ContainerTag("ins").withText(text); }
|
||||
public static ContainerTag kbd() { return new ContainerTag("kbd"); }
|
||||
public static ContainerTag label() { return new ContainerTag("label"); }
|
||||
public static ContainerTag label(String text) { return new ContainerTag("label").withText(text); }
|
||||
public static ContainerTag legend() { return new ContainerTag("legend"); }
|
||||
public static ContainerTag legend(String text) { return new ContainerTag("legend").withText(text); }
|
||||
public static ContainerTag li() { return new ContainerTag("li"); }
|
||||
public static ContainerTag li(String text) { return new ContainerTag("li").withText(text); }
|
||||
public static ContainerTag main() { return new ContainerTag("main"); }
|
||||
public static ContainerTag map() { return new ContainerTag("map"); }
|
||||
public static ContainerTag mark() { return new ContainerTag("mark"); }
|
||||
public static ContainerTag menu() { return new ContainerTag("menu"); }
|
||||
public static ContainerTag menuitem() { return new ContainerTag("menuitem"); }
|
||||
public static ContainerTag meter() { return new ContainerTag("meter"); }
|
||||
public static ContainerTag nav() { return new ContainerTag("nav"); }
|
||||
public static ContainerTag noscript() { return new ContainerTag("noscript"); }
|
||||
public static ContainerTag object() { return new ContainerTag("object"); }
|
||||
public static ContainerTag ol() { return new ContainerTag("ol"); }
|
||||
public static ContainerTag optgroup() { return new ContainerTag("optgroup"); }
|
||||
public static ContainerTag option() { return new ContainerTag("option"); }
|
||||
public static ContainerTag option(String text) { return new ContainerTag("option").withText(text); }
|
||||
public static ContainerTag output() { return new ContainerTag("output"); }
|
||||
public static ContainerTag p() { return new ContainerTag("p"); }
|
||||
public static ContainerTag p(String text) { return new ContainerTag("p").withText(text); }
|
||||
public static ContainerTag pre() { return new ContainerTag("pre"); }
|
||||
public static ContainerTag progress() { return new ContainerTag("progress"); }
|
||||
public static ContainerTag q() { return new ContainerTag("q"); }
|
||||
public static ContainerTag q(String text) { return new ContainerTag("q").withText(text); }
|
||||
public static ContainerTag rp() { return new ContainerTag("rp"); }
|
||||
public static ContainerTag rt() { return new ContainerTag("rt"); }
|
||||
public static ContainerTag ruby() { return new ContainerTag("ruby"); }
|
||||
public static ContainerTag s() { return new ContainerTag("s"); }
|
||||
public static ContainerTag s(String text) { return new ContainerTag("s").withText(text); }
|
||||
public static ContainerTag samp() { return new ContainerTag("samp"); }
|
||||
public static ContainerTag script() { return new ContainerTag("script"); }
|
||||
public static ContainerTag section() { return new ContainerTag("section"); }
|
||||
public static ContainerTag select() { return new ContainerTag("select"); }
|
||||
public static ContainerTag small() { return new ContainerTag("small"); }
|
||||
public static ContainerTag small(String text) { return new ContainerTag("small").withText(text); }
|
||||
public static ContainerTag span() { return new ContainerTag("span"); }
|
||||
public static ContainerTag span(String text) { return new ContainerTag("span").withText(text); }
|
||||
public static ContainerTag strong() { return new ContainerTag("strong"); }
|
||||
public static ContainerTag strong(String text) { return new ContainerTag("strong").withText(text); }
|
||||
public static ContainerTag style() { return new ContainerTag("style"); }
|
||||
public static ContainerTag sub() { return new ContainerTag("sub"); }
|
||||
public static ContainerTag sub(String text) { return new ContainerTag("sub").withText(text); }
|
||||
public static ContainerTag summary() { return new ContainerTag("summary"); }
|
||||
public static ContainerTag summary(String text) { return new ContainerTag("summary").withText(text); }
|
||||
public static ContainerTag sup() { return new ContainerTag("sup"); }
|
||||
public static ContainerTag sup(String text) { return new ContainerTag("sup").withText(text); }
|
||||
public static ContainerTag table() { return new ContainerTag("table"); }
|
||||
public static ContainerTag tbody() { return new ContainerTag("tbody"); }
|
||||
public static ContainerTag td() { return new ContainerTag("td"); }
|
||||
public static ContainerTag td(String text) { return new ContainerTag("td").withText(text); }
|
||||
public static ContainerTag textarea() { return new ContainerTag("textarea"); }
|
||||
public static ContainerTag tfoot() { return new ContainerTag("tfoot"); }
|
||||
public static ContainerTag th() { return new ContainerTag("th"); }
|
||||
public static ContainerTag th(String text) { return new ContainerTag("th").withText(text); }
|
||||
public static ContainerTag thead() { return new ContainerTag("thead"); }
|
||||
public static ContainerTag time() { return new ContainerTag("time"); }
|
||||
public static ContainerTag title() { return new ContainerTag("title"); }
|
||||
public static ContainerTag title(String text) { return new ContainerTag("title").withText(text); }
|
||||
public static ContainerTag tr() { return new ContainerTag("tr"); }
|
||||
public static ContainerTag u() { return new ContainerTag("u"); }
|
||||
public static ContainerTag u(String text) { return new ContainerTag("u").withText(text); }
|
||||
public static ContainerTag ul() { return new ContainerTag("ul"); }
|
||||
public static ContainerTag var() { return new ContainerTag("var"); }
|
||||
public static ContainerTag video() { return new ContainerTag("video"); }
|
||||
public static ContainerTag a() { return new ContainerTag("a"); }
|
||||
public static ContainerTag a(String text) { return new ContainerTag("a").withText(text); }
|
||||
public static ContainerTag abbr() { return new ContainerTag("abbr"); }
|
||||
public static ContainerTag address() { return new ContainerTag("address"); }
|
||||
public static ContainerTag article() { return new ContainerTag("article"); }
|
||||
public static ContainerTag aside() { return new ContainerTag("aside"); }
|
||||
public static ContainerTag audio() { return new ContainerTag("audio"); }
|
||||
public static ContainerTag b() { return new ContainerTag("b"); }
|
||||
public static ContainerTag b(String text) { return new ContainerTag("b").withText(text); }
|
||||
public static ContainerTag bdi() { return new ContainerTag("bdi"); }
|
||||
public static ContainerTag bdi(String text) { return new ContainerTag("bdi").withText(text); }
|
||||
public static ContainerTag bdo() { return new ContainerTag("bdo"); }
|
||||
public static ContainerTag bdo(String text) { return new ContainerTag("bdo").withText(text); }
|
||||
public static ContainerTag blockquote() { return new ContainerTag("blockquote"); }
|
||||
public static ContainerTag blockquote(String text) { return new ContainerTag("blockquote").withText(text); }
|
||||
public static ContainerTag body() { return new ContainerTag("body"); }
|
||||
public static ContainerTag button() { return new ContainerTag("button"); }
|
||||
public static ContainerTag button(String text) { return new ContainerTag("button").withText(text); }
|
||||
public static ContainerTag canvas() { return new ContainerTag("canvas"); }
|
||||
public static ContainerTag caption() { return new ContainerTag("caption"); }
|
||||
public static ContainerTag caption(String text) { return new ContainerTag("caption").withText(text); }
|
||||
public static ContainerTag cite() { return new ContainerTag("cite"); }
|
||||
public static ContainerTag cite(String text) { return new ContainerTag("cite").withText(text); }
|
||||
public static ContainerTag code() { return new ContainerTag("code"); }
|
||||
public static ContainerTag colgroup() { return new ContainerTag("colgroup"); }
|
||||
public static ContainerTag datalist() { return new ContainerTag("datalist"); }
|
||||
public static ContainerTag dd() { return new ContainerTag("dd"); }
|
||||
public static ContainerTag dd(String text) { return new ContainerTag("dd").withText(text); }
|
||||
public static ContainerTag del() { return new ContainerTag("del"); }
|
||||
public static ContainerTag del(String text) { return new ContainerTag("del").withText(text); }
|
||||
public static ContainerTag details() { return new ContainerTag("details"); }
|
||||
public static ContainerTag dfn() { return new ContainerTag("dfn"); }
|
||||
public static ContainerTag dfn(String text) { return new ContainerTag("dfn").withText(text); }
|
||||
public static ContainerTag dialog() { return new ContainerTag("dialog"); }
|
||||
public static ContainerTag dialog(String text) { return new ContainerTag("dialog").withText(text); }
|
||||
public static ContainerTag div() { return new ContainerTag("div"); }
|
||||
public static ContainerTag dl() { return new ContainerTag("dl"); }
|
||||
public static ContainerTag dt() { return new ContainerTag("dt"); }
|
||||
public static ContainerTag dt(String text) { return new ContainerTag("dt").withText(text); }
|
||||
public static ContainerTag em() { return new ContainerTag("em"); }
|
||||
public static ContainerTag em(String text) { return new ContainerTag("em").withText(text); }
|
||||
public static ContainerTag fieldset() { return new ContainerTag("fieldset"); }
|
||||
public static ContainerTag figcaption() { return new ContainerTag("figcaption"); }
|
||||
public static ContainerTag figcaption(String text) { return new ContainerTag("figcaption").withText(text); }
|
||||
public static ContainerTag figure() { return new ContainerTag("figure"); }
|
||||
public static ContainerTag footer() { return new ContainerTag("footer"); }
|
||||
public static ContainerTag form() { return new ContainerTag("form"); }
|
||||
public static ContainerTag h1() { return new ContainerTag("h1"); }
|
||||
public static ContainerTag h1(String text) { return new ContainerTag("h1").withText(text); }
|
||||
public static ContainerTag h2() { return new ContainerTag("h2"); }
|
||||
public static ContainerTag h2(String text) { return new ContainerTag("h2").withText(text); }
|
||||
public static ContainerTag h3() { return new ContainerTag("h3"); }
|
||||
public static ContainerTag h3(String text) { return new ContainerTag("h3").withText(text); }
|
||||
public static ContainerTag h4() { return new ContainerTag("h4"); }
|
||||
public static ContainerTag h4(String text) { return new ContainerTag("h4").withText(text); }
|
||||
public static ContainerTag h5() { return new ContainerTag("h5"); }
|
||||
public static ContainerTag h5(String text) { return new ContainerTag("h5").withText(text); }
|
||||
public static ContainerTag h6() { return new ContainerTag("h6"); }
|
||||
public static ContainerTag h6(String text) { return new ContainerTag("h6").withText(text); }
|
||||
public static ContainerTag head() { return new ContainerTag("head"); }
|
||||
public static ContainerTag header() { return new ContainerTag("header"); }
|
||||
public static ContainerTag html() { return new ContainerTag("html"); }
|
||||
public static ContainerTag i() { return new ContainerTag("i"); }
|
||||
public static ContainerTag i(String text) { return new ContainerTag("i").withText(text); }
|
||||
public static ContainerTag iframe() { return new ContainerTag("iframe"); }
|
||||
public static ContainerTag ins() { return new ContainerTag("ins"); }
|
||||
public static ContainerTag ins(String text) { return new ContainerTag("ins").withText(text); }
|
||||
public static ContainerTag kbd() { return new ContainerTag("kbd"); }
|
||||
public static ContainerTag label() { return new ContainerTag("label"); }
|
||||
public static ContainerTag label(String text) { return new ContainerTag("label").withText(text); }
|
||||
public static ContainerTag legend() { return new ContainerTag("legend"); }
|
||||
public static ContainerTag legend(String text) { return new ContainerTag("legend").withText(text); }
|
||||
public static ContainerTag li() { return new ContainerTag("li"); }
|
||||
public static ContainerTag li(String text) { return new ContainerTag("li").withText(text); }
|
||||
public static ContainerTag main() { return new ContainerTag("main"); }
|
||||
public static ContainerTag map() { return new ContainerTag("map"); }
|
||||
public static ContainerTag mark() { return new ContainerTag("mark"); }
|
||||
public static ContainerTag menu() { return new ContainerTag("menu"); }
|
||||
public static ContainerTag menuitem() { return new ContainerTag("menuitem"); }
|
||||
public static ContainerTag meter() { return new ContainerTag("meter"); }
|
||||
public static ContainerTag nav() { return new ContainerTag("nav"); }
|
||||
public static ContainerTag noscript() { return new ContainerTag("noscript"); }
|
||||
public static ContainerTag object() { return new ContainerTag("object"); }
|
||||
public static ContainerTag ol() { return new ContainerTag("ol"); }
|
||||
public static ContainerTag optgroup() { return new ContainerTag("optgroup"); }
|
||||
public static ContainerTag option() { return new ContainerTag("option"); }
|
||||
public static ContainerTag option(String text) { return new ContainerTag("option").withText(text); }
|
||||
public static ContainerTag output() { return new ContainerTag("output"); }
|
||||
public static ContainerTag p() { return new ContainerTag("p"); }
|
||||
public static ContainerTag p(String text) { return new ContainerTag("p").withText(text); }
|
||||
public static ContainerTag pre() { return new ContainerTag("pre"); }
|
||||
public static ContainerTag progress() { return new ContainerTag("progress"); }
|
||||
public static ContainerTag q() { return new ContainerTag("q"); }
|
||||
public static ContainerTag q(String text) { return new ContainerTag("q").withText(text); }
|
||||
public static ContainerTag rp() { return new ContainerTag("rp"); }
|
||||
public static ContainerTag rt() { return new ContainerTag("rt"); }
|
||||
public static ContainerTag ruby() { return new ContainerTag("ruby"); }
|
||||
public static ContainerTag s() { return new ContainerTag("s"); }
|
||||
public static ContainerTag s(String text) { return new ContainerTag("s").withText(text); }
|
||||
public static ContainerTag samp() { return new ContainerTag("samp"); }
|
||||
public static ContainerTag script() { return new ContainerTag("script"); }
|
||||
public static ContainerTag section() { return new ContainerTag("section"); }
|
||||
public static ContainerTag select() { return new ContainerTag("select"); }
|
||||
public static ContainerTag small() { return new ContainerTag("small"); }
|
||||
public static ContainerTag small(String text) { return new ContainerTag("small").withText(text); }
|
||||
public static ContainerTag span() { return new ContainerTag("span"); }
|
||||
public static ContainerTag span(String text) { return new ContainerTag("span").withText(text); }
|
||||
public static ContainerTag strong() { return new ContainerTag("strong"); }
|
||||
public static ContainerTag strong(String text) { return new ContainerTag("strong").withText(text); }
|
||||
public static ContainerTag style() { return new ContainerTag("style"); }
|
||||
public static ContainerTag sub() { return new ContainerTag("sub"); }
|
||||
public static ContainerTag sub(String text) { return new ContainerTag("sub").withText(text); }
|
||||
public static ContainerTag summary() { return new ContainerTag("summary"); }
|
||||
public static ContainerTag summary(String text) { return new ContainerTag("summary").withText(text); }
|
||||
public static ContainerTag sup() { return new ContainerTag("sup"); }
|
||||
public static ContainerTag sup(String text) { return new ContainerTag("sup").withText(text); }
|
||||
public static ContainerTag table() { return new ContainerTag("table"); }
|
||||
public static ContainerTag tbody() { return new ContainerTag("tbody"); }
|
||||
public static ContainerTag td() { return new ContainerTag("td"); }
|
||||
public static ContainerTag td(String text) { return new ContainerTag("td").withText(text); }
|
||||
public static ContainerTag textarea() { return new ContainerTag("textarea"); }
|
||||
public static ContainerTag tfoot() { return new ContainerTag("tfoot"); }
|
||||
public static ContainerTag th() { return new ContainerTag("th"); }
|
||||
public static ContainerTag th(String text) { return new ContainerTag("th").withText(text); }
|
||||
public static ContainerTag thead() { return new ContainerTag("thead"); }
|
||||
public static ContainerTag time() { return new ContainerTag("time"); }
|
||||
public static ContainerTag title() { return new ContainerTag("title"); }
|
||||
public static ContainerTag title(String text) { return new ContainerTag("title").withText(text); }
|
||||
public static ContainerTag tr() { return new ContainerTag("tr"); }
|
||||
public static ContainerTag u() { return new ContainerTag("u"); }
|
||||
public static ContainerTag u(String text) { return new ContainerTag("u").withText(text); }
|
||||
public static ContainerTag ul() { return new ContainerTag("ul"); }
|
||||
public static ContainerTag var() { return new ContainerTag("var"); }
|
||||
public static ContainerTag video() { return new ContainerTag("video"); }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package j2html.attributes;
|
||||
|
||||
import static j2html.utils.SimpleHtmlEscaper.escape;
|
||||
import static org.apache.commons.lang3.StringEscapeUtils.*;
|
||||
|
||||
public class Attribute {
|
||||
private String name;
|
||||
@@ -8,7 +8,7 @@ public class Attribute {
|
||||
|
||||
public Attribute(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = escape(value);
|
||||
this.value = escapeHtml4(value);
|
||||
}
|
||||
|
||||
public Attribute(String name) {
|
||||
|
||||
@@ -45,7 +45,9 @@ public class ContainerTag extends Tag {
|
||||
*/
|
||||
public ContainerTag with(List<Tag> children) {
|
||||
if (children != null) {
|
||||
children.forEach(this::with);
|
||||
for (Tag child : children) {
|
||||
this.with(child);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -114,17 +116,6 @@ public class ContainerTag extends Tag {
|
||||
*/
|
||||
@Override
|
||||
public String render() {
|
||||
//very slow
|
||||
// return renderOpenTag() + children.stream().map(Tag::render).collect(Collectors.joining()) + renderCloseTag();
|
||||
|
||||
//pretty fast
|
||||
// StringBuilder rendered = new StringBuilder(renderOpenTag());
|
||||
// children.forEach(rendered::append);
|
||||
// rendered.append(renderCloseTag());
|
||||
// return rendered.toString();
|
||||
|
||||
|
||||
//fastest
|
||||
StringBuilder rendered = new StringBuilder(renderOpenTag());
|
||||
if (children != null && children.size() > 0) {
|
||||
for (Tag child : children) {
|
||||
|
||||
43
src/main/java/j2html/tags/InlineStaticResource.java
Normal file
43
src/main/java/j2html/tags/InlineStaticResource.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package j2html.tags;
|
||||
|
||||
import j2html.utils.*;
|
||||
import java.nio.file.*;
|
||||
import static j2html.TagCreator.*;
|
||||
|
||||
public class InlineStaticResource {
|
||||
|
||||
public enum TargetFormat { CSS_MIN, CSS, JS_MIN, JS }
|
||||
|
||||
public static ContainerTag get(String path, TargetFormat format) {
|
||||
ContainerTag errorAlert = script().with(unsafeHtml("alert('Unable to read file. File: \"" + path + "\", Type: \"" + format + "\"')"));
|
||||
String fileString = getFileAsString(path);
|
||||
if(fileString != null) {
|
||||
switch(format) {
|
||||
case CSS_MIN : return style().with(unsafeHtml(compressCss(fileString)));
|
||||
case JS_MIN : return script().with(unsafeHtml(compressJs(fileString, path)));
|
||||
case CSS : return style().with(unsafeHtml(fileString));
|
||||
case JS : return script().with(unsafeHtml(fileString));
|
||||
default : return errorAlert;
|
||||
}
|
||||
}
|
||||
return errorAlert;
|
||||
}
|
||||
|
||||
public static String getFileAsString(String path) {
|
||||
try {
|
||||
return new String(Files.readAllBytes(Paths.get(InlineStaticResource.class.getResource(path).toURI())), "UTF-8");
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String compressCss(String code) {
|
||||
return CSSMin.compress(code);
|
||||
}
|
||||
|
||||
private static String compressJs(String code, String debugPath) {
|
||||
return JSMin.compressJs(code, debugPath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,8 +48,6 @@ public abstract class Tag {
|
||||
}
|
||||
|
||||
public String renderOpenTag() {
|
||||
// final String tagAttributes = attributes.stream().map(Attribute::render).collect(Collectors.joining());
|
||||
// return "<" + tag + tagAttributes + ">"; //too slow
|
||||
String tagAttributes = "";
|
||||
for (Attribute attribute : attributes) {
|
||||
tagAttributes += attribute.render();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package j2html.tags;
|
||||
|
||||
|
||||
import static j2html.utils.SimpleHtmlEscaper.escape;
|
||||
import static org.apache.commons.lang3.StringEscapeUtils.*;
|
||||
|
||||
public class Text extends Tag {
|
||||
|
||||
@@ -11,7 +10,7 @@ public class Text extends Tag {
|
||||
|
||||
@Override
|
||||
public String render() {
|
||||
return escape(tag);
|
||||
return escapeHtml4(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
675
src/main/java/j2html/utils/CSSMin.java
Normal file
675
src/main/java/j2html/utils/CSSMin.java
Normal file
@@ -0,0 +1,675 @@
|
||||
/**
|
||||
* * CSSMin Copyright License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2011-2014, Barry van Oudtshoorn
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of Barryvan nor the names of its
|
||||
* contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior
|
||||
* written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* CSSMin takes in well-formed, human-readable CSS and reduces its size substantially.
|
||||
* It removes unnecessary whitespace and comments, and orders the contents of CSS
|
||||
* selectors alphabetically to enhance GZIP compression.
|
||||
*
|
||||
* Originally by Barry van Oudtshoorn, with bug reports, fixes, and contributions by
|
||||
* <ul>
|
||||
* <li>Kevin de Groote</li>
|
||||
* <li>Pedro Pinheiro</li>
|
||||
* <li>Asier Lostalé</li>
|
||||
* <li>David Åse</li>
|
||||
* </ul>
|
||||
* Some code is based on the YUI CssCompressor code, by Julien Lecomte.
|
||||
*
|
||||
* @author Barry van Oudtshoorn
|
||||
* Repo: https://github.com/barryvan/CSSMin
|
||||
*/
|
||||
|
||||
package j2html.utils;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
public class CSSMin {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(CSSMin.class.getName());
|
||||
|
||||
protected static boolean debugLogging = false;
|
||||
|
||||
/**
|
||||
* Minify CSS from a reader to a printstream.
|
||||
*
|
||||
* @param input Where to read the CSS from
|
||||
*/
|
||||
public static String compress(String input) {
|
||||
try {
|
||||
int k,
|
||||
j, // Number of open braces
|
||||
n; // Current position in stream
|
||||
char curr;
|
||||
|
||||
BufferedReader br = new BufferedReader(new StringReader(input));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (debugLogging) {
|
||||
LOG.info("Reading file into StringBuffer...");
|
||||
}
|
||||
String s;
|
||||
while ((s = br.readLine()) != null) {
|
||||
if (s.trim().equals("")) continue;
|
||||
sb.append(s);
|
||||
}
|
||||
|
||||
if (debugLogging) {
|
||||
LOG.info("Removing comments...");
|
||||
}
|
||||
// Find the start of the comment
|
||||
n = 0;
|
||||
while ((n = sb.indexOf("/*", n)) != -1) {
|
||||
if (sb.charAt(n + 2) == '*') { // Retain special comments
|
||||
n += 2;
|
||||
continue;
|
||||
}
|
||||
k = sb.indexOf("*/", n + 2);
|
||||
if (k == -1) {
|
||||
throw new UnterminatedCommentException();
|
||||
}
|
||||
sb.delete(n, k + 2);
|
||||
}
|
||||
if (debugLogging) {
|
||||
LOG.info(sb.toString());
|
||||
LOG.info("\n\n");
|
||||
}
|
||||
|
||||
if (debugLogging) {
|
||||
LOG.info("Parsing and processing selectors...");
|
||||
}
|
||||
Vector<Selector> selectors = new Vector<>();
|
||||
n = 0;
|
||||
j = 0;
|
||||
for (int i = 0; i < sb.length(); i++) {
|
||||
curr = sb.charAt(i);
|
||||
if (j < 0) {
|
||||
throw new UnbalancedBracesException();
|
||||
}
|
||||
if (curr == '{') {
|
||||
j++;
|
||||
} else if (curr == '}') {
|
||||
j--;
|
||||
if (j == 0) {
|
||||
try {
|
||||
selectors.addElement(new Selector(sb.substring(n, i + 1)));
|
||||
} catch (UnterminatedSelectorException usex) {
|
||||
LOG.warning("Unterminated selector: " + usex.getMessage());
|
||||
} catch (EmptySelectorBodyException ebex) {
|
||||
if (debugLogging) {
|
||||
LOG.warning("Empty selector body: " + ebex.getMessage());
|
||||
}
|
||||
}
|
||||
n = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
for (Selector selector : selectors) {
|
||||
result.append(selector);
|
||||
}
|
||||
|
||||
if (debugLogging) {
|
||||
LOG.info("Process completed successfully.");
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
|
||||
} catch (UnterminatedCommentException ucex) {
|
||||
LOG.warning("Unterminated comment.");
|
||||
} catch (UnbalancedBracesException ubex) {
|
||||
LOG.warning("Unbalanced braces.");
|
||||
} catch (Exception ex) {
|
||||
LOG.warning(ex.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class Selector {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(Selector.class.getName());
|
||||
|
||||
private Property[] properties = null;
|
||||
private Vector<Selector> subSelectors = null;
|
||||
private String selector;
|
||||
|
||||
/**
|
||||
* Creates a new Selector using the supplied strings.
|
||||
*
|
||||
* @param selector The selector; for example, "div { border: solid 1px red; color: blue; }"
|
||||
* @throws IncompleteSelectorException, UnterminatedSelectorException, EmptySelectorBodyException If the selector is incomplete and cannot be parsed.
|
||||
*/
|
||||
public Selector(String selector) throws IncompleteSelectorException, UnterminatedSelectorException, EmptySelectorBodyException {
|
||||
String[] parts = selector.split("\\{"); // We have to escape the { with a \ for the regex, which itself requires escaping for the string. Sigh.
|
||||
if (parts.length < 2) {
|
||||
throw new IncompleteSelectorException(selector);
|
||||
}
|
||||
|
||||
this.selector = parts[0].trim();
|
||||
|
||||
// Simplify combinators
|
||||
this.selector = this.selector.replaceAll("\\s?(\\+|~|,|=|~=|\\^=|\\$=|\\*=|\\|=|>)\\s?", "$1");
|
||||
|
||||
|
||||
// We're dealing with a nested property, eg @-webkit-keyframes
|
||||
if (parts.length > 2) {
|
||||
this.subSelectors = new Vector<>();
|
||||
parts = selector.split("(\\s*\\{\\s*)|(\\s*\\}\\s*)");
|
||||
for (int i = 1; i < parts.length; i += 2) {
|
||||
parts[i] = parts[i].trim();
|
||||
parts[i + 1] = parts[i + 1].trim();
|
||||
if (!(parts[i].equals("") || (parts[i + 1].equals("")))) {
|
||||
this.subSelectors.addElement(new Selector(parts[i] + "{" + parts[i + 1] + "}"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String contents = parts[parts.length - 1].trim();
|
||||
if (CSSMin.debugLogging) {
|
||||
LOG.info("Parsing selector: " + this.selector);
|
||||
LOG.info("\t" + contents);
|
||||
}
|
||||
if (contents.charAt(contents.length() - 1) != '}') { // Ensure that we have a leading and trailing brace.
|
||||
throw new UnterminatedSelectorException(selector);
|
||||
}
|
||||
if (contents.length() == 1) {
|
||||
throw new EmptySelectorBodyException(selector);
|
||||
}
|
||||
contents = contents.substring(0, contents.length() - 2);
|
||||
|
||||
this.properties = new Property[0];
|
||||
this.properties = parseProperties(contents).toArray(this.properties);
|
||||
sortProperties(this.properties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints out this selector and its contents nicely, with the contents sorted alphabetically.
|
||||
*
|
||||
* @return A string representing this selector, minified.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.selector).append("{");
|
||||
if (this.subSelectors != null) {
|
||||
for (Selector s : this.subSelectors) {
|
||||
sb.append(s.toString());
|
||||
}
|
||||
}
|
||||
if (this.properties != null) {
|
||||
for (Property p : this.properties) {
|
||||
sb.append(p.toString());
|
||||
}
|
||||
}
|
||||
if (sb.charAt(sb.length() - 1) == ';') {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses out the properties of a selector's body.
|
||||
*
|
||||
* @param contents The body; for example, "border: solid 1px red; color: blue;"
|
||||
* @return An array of properties parsed from this selector.
|
||||
*/
|
||||
private ArrayList<Property> parseProperties(String contents) {
|
||||
ArrayList<String> parts = new ArrayList<>();
|
||||
boolean bInsideString = false,
|
||||
bInsideURL = false;
|
||||
int j = 0;
|
||||
String substr;
|
||||
for (int i = 0; i < contents.length(); i++) {
|
||||
if (bInsideString) { // If we're inside a string
|
||||
bInsideString = !(contents.charAt(i) == '"');
|
||||
} else if (bInsideURL) { // If we're inside a URL
|
||||
bInsideURL = !(contents.charAt(i) == ')');
|
||||
} else if (contents.charAt(i) == '"') {
|
||||
bInsideString = true;
|
||||
} else if (contents.charAt(i) == '(') {
|
||||
if ((i - 3) > 0 && "url".equals(contents.substring(i - 3, i))) bInsideURL = true;
|
||||
} else if (contents.charAt(i) == ';') {
|
||||
substr = contents.substring(j, i);
|
||||
if (!(substr.trim().equals(""))) parts.add(substr);
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
substr = contents.substring(j, contents.length());
|
||||
if (!(substr.trim().equals(""))) parts.add(substr);
|
||||
|
||||
ArrayList<Property> results = new ArrayList<>();
|
||||
for (String part : parts) {
|
||||
try {
|
||||
results.add(new Property(part));
|
||||
} catch (IncompletePropertyException ipex) {
|
||||
LOG.warning("Incomplete property in selector \"" + this.selector + "\": \"" + ipex.getMessage() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the properties array to enhance gzipping.
|
||||
*
|
||||
* @param properties The array to be sorted.
|
||||
*/
|
||||
private void sortProperties(Property[] properties) {
|
||||
Arrays.sort(properties);
|
||||
}
|
||||
}
|
||||
|
||||
class Property implements Comparable<Property> {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(Property.class.getName());
|
||||
|
||||
protected String property;
|
||||
protected Part[] parts;
|
||||
|
||||
/**
|
||||
* Creates a new Property using the supplied strings. Parses out the values of the property selector.
|
||||
*
|
||||
* @param property The property; for example, "border: solid 1px red;" or "-moz-box-shadow: 3px 3px 3px rgba(255, 255, 0, 0.5);".
|
||||
* @throws IncompletePropertyException If the property is incomplete and cannot be parsed.
|
||||
*/
|
||||
public Property(String property) throws IncompletePropertyException {
|
||||
try {
|
||||
// Parse the property.
|
||||
ArrayList<String> parts = new ArrayList<>();
|
||||
boolean bCanSplit = true;
|
||||
int j = 0;
|
||||
String substr;
|
||||
if (CSSMin.debugLogging) {
|
||||
LOG.info("\t\tExamining property: " + property);
|
||||
}
|
||||
for (int i = 0; i < property.length(); i++) {
|
||||
if (!bCanSplit) { // If we're inside a string
|
||||
bCanSplit = (property.charAt(i) == '"');
|
||||
} else if (property.charAt(i) == '"') {
|
||||
bCanSplit = false;
|
||||
} else if (property.charAt(i) == ':' && parts.size() < 1) {
|
||||
substr = property.substring(j, i);
|
||||
if (!(substr.trim().equals(""))) parts.add(substr);
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
substr = property.substring(j, property.length());
|
||||
if (!(substr.trim().equals(""))) parts.add(substr);
|
||||
if (parts.size() < 2) {
|
||||
throw new IncompletePropertyException(property);
|
||||
}
|
||||
|
||||
String prop = parts.get(0).trim();
|
||||
if (!(prop.length() > 2 && prop.substring(0, 2).equals("--"))) {
|
||||
prop = prop.toLowerCase();
|
||||
}
|
||||
this.property = prop;
|
||||
|
||||
this.parts = parseValues(simplifyColours(parts.get(1).trim().replaceAll(", ", ",")));
|
||||
|
||||
} catch (PatternSyntaxException e) {
|
||||
// Invalid regular expression used.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints out this property nicely.
|
||||
*
|
||||
* @return A string representing this property, minified.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.property).append(":");
|
||||
for (Part p : this.parts) {
|
||||
sb.append(p.toString()).append(",");
|
||||
}
|
||||
sb.deleteCharAt(sb.length() - 1); // Delete the trailing comma.
|
||||
sb.append(";");
|
||||
if (CSSMin.debugLogging) {
|
||||
LOG.info(sb.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this property with another.
|
||||
*/
|
||||
public int compareTo(Property other) {
|
||||
// We can't just use String.compareTo(), because we need to sort properties that have hack prefixes last -- eg, *display should come after display.
|
||||
String thisProp = this.property;
|
||||
String thatProp = other.property;
|
||||
|
||||
if (thisProp.charAt(0) == '-') {
|
||||
thisProp = thisProp.substring(1);
|
||||
thisProp = thisProp.substring(thisProp.indexOf('-') + 1);
|
||||
} else if (thisProp.charAt(0) < 65) {
|
||||
thisProp = thisProp.substring(1);
|
||||
}
|
||||
|
||||
if (thatProp.charAt(0) == '-') {
|
||||
thatProp = thatProp.substring(1);
|
||||
thatProp = thatProp.substring(thatProp.indexOf('-') + 1);
|
||||
} else if (thatProp.charAt(0) < 65) {
|
||||
thatProp = thatProp.substring(1);
|
||||
}
|
||||
|
||||
return thisProp.compareTo(thatProp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the values out of a property.
|
||||
*
|
||||
* @param contents The property to parse
|
||||
* @return An array of Parts
|
||||
*/
|
||||
private Part[] parseValues(String contents) {
|
||||
String[] parts = contents.split(",");
|
||||
Part[] results = new Part[parts.length];
|
||||
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
try {
|
||||
results[i] = new Part(parts[i], property);
|
||||
} catch (Exception e) {
|
||||
LOG.warning(e.getMessage());
|
||||
results[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private String simplifyColours(String contents) {
|
||||
// This replacement, although it results in a smaller uncompressed file,
|
||||
// actually makes the gzipped file bigger -- people tend to use rgba(0,0,0,0.x)
|
||||
// quite a lot, which means that rgba(0,0,0,0) has its first eight or so characters
|
||||
// compressed really efficiently; much more so than "transparent".
|
||||
//contents = contents.replaceAll("rgba\\(0,0,0,0\\)", "transparent");
|
||||
|
||||
return simplifyRGBColours(contents);
|
||||
}
|
||||
|
||||
// Convert rgb(51,102,153) to #336699 (this code largely based on YUI code)
|
||||
private String simplifyRGBColours(String contents) {
|
||||
StringBuffer newContents = new StringBuffer();
|
||||
StringBuffer hexColour;
|
||||
String[] rgbColours;
|
||||
int colourValue;
|
||||
|
||||
Pattern pattern = Pattern.compile("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)");
|
||||
Matcher matcher = pattern.matcher(contents);
|
||||
|
||||
while (matcher.find()) {
|
||||
hexColour = new StringBuffer("#");
|
||||
rgbColours = matcher.group(1).split(",");
|
||||
for (String rgbColour : rgbColours) {
|
||||
colourValue = Integer.parseInt(rgbColour);
|
||||
if (colourValue < 16) {
|
||||
hexColour.append("0");
|
||||
}
|
||||
hexColour.append(Integer.toHexString(colourValue));
|
||||
}
|
||||
matcher.appendReplacement(newContents, hexColour.toString());
|
||||
}
|
||||
matcher.appendTail(newContents);
|
||||
|
||||
return newContents.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class Part {
|
||||
String contents;
|
||||
String property;
|
||||
|
||||
/**
|
||||
* Create a new property by parsing the given string.
|
||||
*
|
||||
* @param contents The string to parse.
|
||||
* @throws Exception If the part cannot be parsed.
|
||||
*/
|
||||
public Part(String contents, String property) throws Exception {
|
||||
// Many of these regular expressions are adapted from those used in the YUI CSS Compressor.
|
||||
|
||||
// For simpler regexes.
|
||||
this.contents = " " + contents;
|
||||
this.property = property;
|
||||
|
||||
simplify();
|
||||
}
|
||||
|
||||
private void simplify() {
|
||||
|
||||
// !important doesn't need to be spaced
|
||||
this.contents = this.contents.replaceAll(" !important", "!important");
|
||||
|
||||
// Replace 0in, 0cm, etc. with just 0
|
||||
this.contents = this.contents.replaceAll("(\\s)(0)(px|em|%|in|cm|mm|pc|pt|ex)", "$1$2");
|
||||
|
||||
this.contents = this.contents.trim();
|
||||
|
||||
// Simplify multiple zeroes
|
||||
if (this.contents.equals("0 0 0 0")) this.contents = "0";
|
||||
if (this.contents.equals("0 0 0")) this.contents = "0";
|
||||
if (this.contents.equals("0 0")) this.contents = "0";
|
||||
|
||||
// Simplify multiple-parameter properties
|
||||
simplifyParameters();
|
||||
|
||||
// Simplify font weights
|
||||
simplifyFontWeights();
|
||||
|
||||
// Strip unnecessary quotes from url() and single-word parts, and make as much lowercase as possible.
|
||||
simplifyQuotesAndCaps();
|
||||
|
||||
// Simplify colours
|
||||
simplifyColourNames();
|
||||
simplifyHexColours();
|
||||
}
|
||||
|
||||
private void simplifyParameters() {
|
||||
if (this.property.equals("background-size")) return;
|
||||
|
||||
StringBuilder newContents = new StringBuilder();
|
||||
|
||||
String[] params = this.contents.split(" ");
|
||||
if (params.length == 4) {
|
||||
// We can drop off the fourth item if the second and fourth items match
|
||||
// ie turn 3px 0 3px 0 into 3px 0 3px
|
||||
if (params[1].equalsIgnoreCase(params[3])) {
|
||||
params = Arrays.copyOf(params, 3);
|
||||
}
|
||||
}
|
||||
if (params.length == 3) {
|
||||
// We can drop off the third item if the first and third items match
|
||||
// ie turn 3px 0 3px into 3px 0
|
||||
if (params[0].equalsIgnoreCase(params[2])) {
|
||||
params = Arrays.copyOf(params, 2);
|
||||
}
|
||||
}
|
||||
if (params.length == 2) {
|
||||
// We can drop off the second item if the first and second items match
|
||||
// ie turn 3px 3px into 3px
|
||||
if (params[0].equalsIgnoreCase(params[1])) {
|
||||
params = Arrays.copyOf(params, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (String param : params) {
|
||||
newContents.append(param).append(" ");
|
||||
}
|
||||
newContents.deleteCharAt(newContents.length() - 1); // Delete the trailing space
|
||||
|
||||
this.contents = newContents.toString();
|
||||
}
|
||||
|
||||
private void simplifyFontWeights() {
|
||||
if (!this.property.equals("font-weight")) return;
|
||||
|
||||
String lcContents = this.contents.toLowerCase();
|
||||
|
||||
for (int i = 0; i < Constants.fontWeightNames.length; i++) {
|
||||
if (lcContents.equals(Constants.fontWeightNames[i])) {
|
||||
this.contents = Constants.fontWeightValues[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void simplifyQuotesAndCaps() {
|
||||
// Strip quotes from URLs
|
||||
if ((this.contents.length() > 4) && (this.contents.substring(0, 4).equalsIgnoreCase("url("))) {
|
||||
this.contents = this.contents.replaceAll("(?i)url\\(('|\")?(.*?)\\1\\)", "url($2)");
|
||||
} else if ((this.contents.length() > 4) && (this.contents.substring(0, 4).equalsIgnoreCase("var("))) {
|
||||
this.contents = this.contents.replaceAll("\\s", "");
|
||||
} else {
|
||||
String[] words = this.contents.split("\\s");
|
||||
if (words.length == 1) {
|
||||
if (!this.property.equalsIgnoreCase("animation-name")) {
|
||||
this.contents = this.contents.toLowerCase();
|
||||
}
|
||||
this.contents = this.contents.replaceAll("('|\")?(.*?)\1", "$2");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void simplifyColourNames() {
|
||||
String lcContents = this.contents.toLowerCase();
|
||||
|
||||
for (int i = 0; i < Constants.htmlColourNames.length; i++) {
|
||||
if (lcContents.equals(Constants.htmlColourNames[i])) {
|
||||
if (Constants.htmlColourValues[i].length() < Constants.htmlColourNames[i].length()) {
|
||||
this.contents = Constants.htmlColourValues[i];
|
||||
}
|
||||
break;
|
||||
} else if (lcContents.equals(Constants.htmlColourValues[i])) {
|
||||
if (Constants.htmlColourNames[i].length() < Constants.htmlColourValues[i].length()) {
|
||||
this.contents = Constants.htmlColourNames[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void simplifyHexColours() {
|
||||
StringBuffer newContents = new StringBuffer();
|
||||
|
||||
Pattern pattern = Pattern.compile("#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])");
|
||||
Matcher matcher = pattern.matcher(this.contents);
|
||||
|
||||
while (matcher.find()) {
|
||||
if (matcher.group(1).equalsIgnoreCase(matcher.group(2)) && matcher.group(3).equalsIgnoreCase(matcher.group(4)) && matcher.group(5).equalsIgnoreCase(matcher.group(6))) {
|
||||
matcher.appendReplacement(newContents, "#" + matcher.group(1).toLowerCase() + matcher.group(3).toLowerCase() + matcher.group(5).toLowerCase());
|
||||
} else {
|
||||
matcher.appendReplacement(newContents, matcher.group().toLowerCase());
|
||||
}
|
||||
}
|
||||
matcher.appendTail(newContents);
|
||||
|
||||
this.contents = newContents.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns itself.
|
||||
*
|
||||
* @return this part's string representation.
|
||||
*/
|
||||
public String toString() {
|
||||
return this.contents;
|
||||
}
|
||||
}
|
||||
|
||||
class UnterminatedCommentException extends Exception {}
|
||||
|
||||
class UnbalancedBracesException extends Exception {}
|
||||
|
||||
class IncompletePropertyException extends Exception {
|
||||
String message = null;
|
||||
|
||||
public IncompletePropertyException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
|
||||
class EmptySelectorBodyException extends Exception {
|
||||
String message = null;
|
||||
|
||||
public EmptySelectorBodyException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
|
||||
class UnterminatedSelectorException extends Exception {
|
||||
String message = null;
|
||||
|
||||
public UnterminatedSelectorException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
|
||||
class IncompleteSelectorException extends Exception {
|
||||
String message = null;
|
||||
|
||||
public IncompleteSelectorException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
|
||||
class Constants {
|
||||
static final String[] htmlColourNames = { "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "green", "greenyellow", "honeydew", "hotpink", "indianred ", "indigo ", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgrey", "lightgreen", "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen" };
|
||||
static final String[] htmlColourValues = { "#f0f8ff", "#faebd7", "#00ffff", "#7fffd4", "#f0ffff", "#f5f5dc", "#ffe4c4", "#000", "#ffebcd", "#00f", "#8a2be2", "#a52a2a", "#deb887", "#5f9ea0", "#7fff00", "#d2691e", "#ff7f50", "#6495ed", "#fff8dc", "#dc143c", "#0ff", "#00008b", "#008b8b", "#b8860b", "#a9a9a9", "#006400", "#bdb76b", "#8b008b", "#556b2f", "#ff8c00", "#9932cc", "#8b0000", "#e9967a", "#8fbc8f", "#483d8b", "#2f4f4f", "#00ced1", "#9400d3", "#ff1493", "#00bfff", "#696969", "#1e90ff", "#b22222", "#fffaf0", "#228b22", "#f0f", "#dcdcdc", "#f8f8ff", "#ffd700", "#daa520", "#808080", "#008000", "#adff2f", "#f0fff0", "#ff69b4", "#cd5c5c", "#4b0082", "#fffff0", "#f0e68c", "#e6e6fa", "#fff0f5", "#7cfc00", "#fffacd", "#add8e6", "#f08080", "#e0ffff", "#fafad2", "#d3d3d3", "#90ee90", "#ffb6c1", "#ffa07a", "#20b2aa", "#87cefa", "#789", "#b0c4de", "#ffffe0", "#0f0", "#32cd32", "#faf0e6", "#f0f", "#800000", "#66cdaa", "#0000cd", "#ba55d3", "#9370d8", "#3cb371", "#7b68ee", "#00fa9a", "#48d1cc", "#c71585", "#191970", "#f5fffa", "#ffe4e1", "#ffe4b5", "#ffdead", "#000080", "#fdf5e6", "#808000", "#6b8e23", "#ffa500", "#ff4500", "#da70d6", "#eee8aa", "#98fb98", "#afeeee", "#d87093", "#ffefd5", "#ffdab9", "#cd853f", "#ffc0cb", "#dda0dd", "#b0e0e6", "#800080", "#f00", "#bc8f8f", "#4169e1", "#8b4513", "#fa8072", "#f4a460", "#2e8b57", "#fff5ee", "#a0522d", "#c0c0c0", "#87ceeb", "#6a5acd", "#708090", "#fffafa", "#00ff7f", "#4682b4", "#d2b48c", "#008080", "#d8bfd8", "#ff6347", "#40e0d0", "#ee82ee", "#f5deb3", "#fff", "#f5f5f5", "#ff0", "#9acd32" };
|
||||
static final String[] fontWeightNames = { "normal", "bold", "bolder", "lighter" };
|
||||
static final String[] fontWeightValues = { "400", "700", "900", "100" };
|
||||
}
|
||||
18
src/main/java/j2html/utils/JSMin.java
Normal file
18
src/main/java/j2html/utils/JSMin.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package j2html.utils;
|
||||
|
||||
import com.google.javascript.jscomp.*;
|
||||
import com.google.javascript.jscomp.Compiler;
|
||||
|
||||
public class JSMin {
|
||||
|
||||
public static String compressJs(String code, String sourcePath) {
|
||||
com.google.javascript.jscomp.Compiler compiler = new Compiler();
|
||||
CompilerOptions options = new CompilerOptions();
|
||||
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
|
||||
SourceFile extern = SourceFile.fromCode("externs.js", "");
|
||||
SourceFile input = SourceFile.fromCode(sourcePath, code);
|
||||
compiler.compile(extern, input, options);
|
||||
return compiler.toSource();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package j2html.utils;
|
||||
|
||||
public class SimpleHtmlEscaper {
|
||||
|
||||
/**
|
||||
* Simple approach from http://stackoverflow.com/a/25228492/4893160 ... good enough ?
|
||||
* Apache StringEscapeUtils is extremely slow compared (probably because it's a lot safer!)
|
||||
* @param s the string to be escaped
|
||||
* @return the escaped string
|
||||
*/
|
||||
public static String escape(String s) {
|
||||
if("".equals(s)){ return ""; }
|
||||
if(s == null) { return null; }
|
||||
StringBuilder out = new StringBuilder(Math.max(16, s.length()));
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c > 127 || c == '"' || c == '<' || c == '>' || c == '&') {
|
||||
out.append("&#");
|
||||
out.append((int) c);
|
||||
out.append(';');
|
||||
} else {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package j2html.attributes;
|
||||
import j2html.tags.ContainerTag;
|
||||
import org.junit.Test;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class AttributeTest {
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package j2html.tags;
|
||||
import org.junit.Test;
|
||||
|
||||
import static j2html.TagCreator.*;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ComplexRenderTest {
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package j2html.tags;
|
||||
import org.junit.Test;
|
||||
|
||||
import static j2html.TagCreator.*;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ConvenienceMethodTest {
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package j2html.tags;
|
||||
import org.junit.Test;
|
||||
|
||||
import static j2html.TagCreator.*;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class TagCreatorTest {
|
||||
|
||||
@@ -14,8 +14,13 @@ public class TagCreatorTest {
|
||||
assertEquals(tag("tagname").render(), "<tagname></tagname>");
|
||||
assertEquals(emptyTag("tagname").render(), "<tagname>");
|
||||
assertEquals(text("text").render(), "text");
|
||||
assertEquals(text("<script>").render(), "<script>");
|
||||
assertEquals(text("<script> and \"</script>\"").render(), "<script> and "</script>"");
|
||||
assertEquals(unsafeHtml("<script>").render(), "<script>");
|
||||
assertEquals(styleWithInlineFile_min("/test.css").render(), "<style>body{background:#daa520;margin-bottom:10px;margin-left:10px;margin-right:10px;margin-top:10px}</style>");
|
||||
assertEquals(scriptWithInlineFile_min("/test.js").render(), "<script>(function(){console.log(15)})();</script>");
|
||||
assertEquals(fileAsString("/test.html").render(), "<body>\r\n"+" Any content\r\n"+"</body>\r\n");
|
||||
assertEquals(fileAsEscapedString("/test.html").render(), "<body>\r\n"+" Any content\r\n"+"</body>\r\n");
|
||||
assertEquals(fileAsString("/test.java").render(), "public class AnyContent{}\r\n");
|
||||
|
||||
//EmptyTags
|
||||
assertEquals(document().render(), "<!DOCTYPE html>");
|
||||
@@ -170,4 +175,4 @@ public class TagCreatorTest {
|
||||
assertEquals(video().render(), "<video></video>");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,4 @@ public class TagTest {
|
||||
assertEquals(testTag.renderCloseTag(), "</a>");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
package performancetester;
|
||||
|
||||
import j2html.tags.ContainerTag;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
import static j2html.TagCreator.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class RenderSpeed {
|
||||
|
||||
/**
|
||||
* This test class is only used during development of j2html, to see if changes to data-types and rendering
|
||||
* techniques have a positive or negative impact
|
||||
*/
|
||||
|
||||
public int iterations = 100000;
|
||||
public int precision = 10;
|
||||
|
||||
@Test
|
||||
public void testFullPageRenderSpeed() {
|
||||
ContainerTag complexTestTag = html().with(body().with(header(), main().with(p("Main stuff...")), footer().condWith(1 == 1, p("Conditional with!"))));
|
||||
String expectedResult = "<html><body><header></header><main><p>Main stuff...</p></main><footer><p>Conditional with!</p></footer></body></html>";
|
||||
assertEquals(complexTestTag.render(), (expectedResult));
|
||||
String methodName = new Object(){}.getClass().getEnclosingMethod().getName();
|
||||
measureAvgMethodTime(this::fullPageRenderTest, iterations, precision, methodName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttributeRenderSpeed() {
|
||||
String methodName = new Object(){}.getClass().getEnclosingMethod().getName();
|
||||
measureAvgMethodTime(this::attributeRenderTest, iterations, precision, methodName);
|
||||
}
|
||||
|
||||
public void attributeRenderTest(int i) {
|
||||
tag("div " + i)
|
||||
.attr("just", "adding")
|
||||
.attr("some", "pretty")
|
||||
.attr("random", "attributes")
|
||||
.attr("to", "this")
|
||||
.attr("div", "here").render();
|
||||
}
|
||||
|
||||
public String fullPageRenderTest(int i) {
|
||||
return html().with(
|
||||
head().with(
|
||||
title().withText("Test " + i)
|
||||
),
|
||||
body().with(
|
||||
header().with(
|
||||
h1("Test Header")
|
||||
),
|
||||
main().with(
|
||||
h2("Test Form"),
|
||||
div().with(
|
||||
input().withType("email").withName("email").withPlaceholder("Email"),
|
||||
input().withType("password").withName("password").withPlaceholder("Password"),
|
||||
input().withType("password").withName("repeatPassword").withPlaceholder("Repeat password"),
|
||||
input().withType("password").withName("repeatPasswordAgain").withPlaceholder("Repeat password again"),
|
||||
button().withType("submit").withText("Login")
|
||||
),
|
||||
div().withText(
|
||||
"<script>alert(1)</script>"
|
||||
)
|
||||
),
|
||||
footer().withText("Test Footer"),
|
||||
script().withSrc("/testScript.js")
|
||||
)
|
||||
).render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Please tell me if there is a proper way to do this :D
|
||||
*
|
||||
* @param method the method to measure
|
||||
* @param iterations the number of times to run the method
|
||||
* @param precision the number of times to run the timer
|
||||
* @param methodName the name of the method (only used for output)
|
||||
*/
|
||||
private void measureAvgMethodTime(IntConsumer method, int iterations, int precision, String methodName) {
|
||||
double averageTime = LongStream.range(0, precision).map(l -> measureMethodTime(method, iterations)).average().getAsDouble();
|
||||
System.out.println("\n" + methodName + ": " + averageTime + "ms was avg runtime per " + String.format("%,d", iterations) + " iterations (ran " + precision + " times) \n");
|
||||
}
|
||||
|
||||
private long measureMethodTime(IntConsumer method, int iterations) {
|
||||
long startTime = System.nanoTime();
|
||||
IntStream.range(0, iterations).parallel().forEach(method);
|
||||
long endTime = System.nanoTime();
|
||||
System.out.print(TimeUnit.NANOSECONDS.toMillis(endTime - startTime) + " ");
|
||||
return TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
|
||||
}
|
||||
|
||||
}
|
||||
7
src/test/resources/test.css
Normal file
7
src/test/resources/test.css
Normal file
@@ -0,0 +1,7 @@
|
||||
body {
|
||||
background: goldenrod;
|
||||
margin-top: 10px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
3
src/test/resources/test.html
Normal file
3
src/test/resources/test.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<body>
|
||||
Any content
|
||||
</body>
|
||||
1
src/test/resources/test.java
Normal file
1
src/test/resources/test.java
Normal file
@@ -0,0 +1 @@
|
||||
public class AnyContent{}
|
||||
6
src/test/resources/test.js
Normal file
6
src/test/resources/test.js
Normal file
@@ -0,0 +1,6 @@
|
||||
(function () {
|
||||
var test = 5;
|
||||
var tast = 10;
|
||||
var testTast = test + tast;
|
||||
console.log(testTast);
|
||||
})();
|
||||
Reference in New Issue
Block a user