14 Commits

Author SHA1 Message Date
dependabot[bot]
4b99683147 Bump jsoup from 1.14.3 to 1.15.3 in /code_gen
Bumps [jsoup](https://github.com/jhy/jsoup) from 1.14.3 to 1.15.3.
- [Release notes](https://github.com/jhy/jsoup/releases)
- [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES)
- [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.14.3...jsoup-1.15.3)

---
updated-dependencies:
- dependency-name: org.jsoup:jsoup
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-01 23:55:56 +00:00
Scott Embler
38c08d4724 Updating README.md and website documents to match with release 1.6.0. (#214) 2022-06-30 17:49:14 -04:00
David
1c164a0773 [maven-release-plugin] prepare for next development iteration 2022-06-30 20:40:14 +02:00
David
dcc7258c8e [maven-release-plugin] prepare release j2html-1.6.0 2022-06-30 20:40:09 +02:00
Scott Embler
930ade9354 Upgrading several maven plugins. (#213)
- The maven-enforcer-plugin had an API incompatibility that might be fixed with a version upgrade.
- Several other plugins were out-of-date, and worth upgrading.
- Some plugins were not upgraded out of concern that they might break the build.
2022-06-30 08:12:09 +02:00
Scott Embler
d1c404d5db Web component support (#211)
* Foundational components for reading/processing Wattsi files into classes.

* Introducing tests for compliance with wattsi definitions.

- Several elements have been found to be missing from the code generator.
- A long-term strategy for dealing with obsolete elements will be needed.  Should we remove and change the API?  Or should we deprecate and leave in-place?
- Found one element that was accidentally added to the code generator but never used.  Hopefully.
- Fixes will be applied in following commits.

* Added newer elements/tags and cleaned up invalid Tag class.

- Added DataTag.
- Added SlotTag and TemplateTag to support web components.
- Added global attributes to support web components.
- Removed GenerateTag.  Accidentally introduced by string replacement in code generator.

* Upgrading revapi and maven plugin. Configured revapi to ignore removal of GenerateTag.

- The GenerateTag class was created accidentally.  It does not have any representation in the HTML standard is should not be provided in this library.
2022-06-30 02:13:46 +02:00
Scott Embler
c2177d0584 Updated readme file with latest version number. (#206) 2022-02-02 13:34:48 -05:00
Scott Embler
5eb202b0a9 Updated html files for website. (#205) 2022-01-24 10:57:48 -05:00
Scott Embler
4bda3635ab Website update 1.5.0 release (#204)
* Updated new items to include 1.5.0.

- Adding short upgrade guide for incompatibilities between 1.4.0 and 1.5.0.
2022-01-23 22:04:46 -05:00
David (javalin.io)
f083b2cc9c Update and rename README to README.md 2022-01-23 22:31:21 +01:00
Scott Embler
9ad0e428c1 Fixes #200. Adds support for the picture tag. (#201) 2021-10-03 18:43:33 -04:00
Scott Embler
83866be83f Resolves #194. Introduces Revapi into the build process. (#195)
- Switching to verify goal in main.yml.
2021-06-17 10:17:50 -04:00
David
5135cffc3f Bump website to 1.5.0 2021-06-13 23:10:30 +02:00
David
09e08f3465 [maven-release-plugin] prepare for next development iteration 2021-06-13 23:02:20 +02:00
32 changed files with 129297 additions and 142 deletions

View File

@@ -23,6 +23,6 @@ jobs:
if: contains(matrix.os, 'win') == false
run: chmod +x ./mvnw
- name: Build with Maven
run: ./mvnw package --file library/pom.xml --batch-mode
run: ./mvnw verify --file library/pom.xml --batch-mode
env:
MAVEN_OPTS: -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn

View File

@@ -13,12 +13,12 @@ The project webpage is [j2html.com](http://j2html.com).
<dependency>
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>1.4.0</version>
<version>1.6.0</version>
</dependency>
```
### Or the gradle dependency
```
compile 'com.j2html:j2html:1.4.0'
compile 'com.j2html:j2html:1.6.0'
```
### Import TagCreator and start building HTML

View File

@@ -1,74 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.j2html</groupId>
<artifactId>j2htmlcodegen</artifactId>
<version>1.0-SNAPSHOT</version>
<groupId>com.j2html</groupId>
<artifactId>j2htmlcodegen</artifactId>
<version>1.0-SNAPSHOT</version>
<name>j2htmlcodegen</name>
<url>https://j2html.com/</url>
<name>j2htmlcodegen</name>
<url>https://j2html.com/</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -63,7 +63,7 @@ public final class TagCreatorCodeGenerator {
}
// This is a method that contains all ContainerTags, there is nothing below it
static List<String> emptyTags() {
public static List<String> emptyTags() {
return Arrays.asList(
"area",
"base",
@@ -84,7 +84,7 @@ public final class TagCreatorCodeGenerator {
);
}
static List<String> containerTags() {
public static List<String> containerTags() {
return Arrays.asList(
"a",
"abbr",
@@ -103,6 +103,7 @@ public final class TagCreatorCodeGenerator {
"cite",
"code",
"colgroup",
"data",
"datalist",
"dd",
"del",
@@ -134,7 +135,6 @@ public final class TagCreatorCodeGenerator {
"label",
"legend",
"li",
"generate",
"main",
"map",
"mark",
@@ -149,6 +149,7 @@ public final class TagCreatorCodeGenerator {
"option",
"output",
"p",
"picture",
"pre",
"progress",
"q",
@@ -160,6 +161,7 @@ public final class TagCreatorCodeGenerator {
"script",
"section",
"select",
"slot",
"small",
"span",
"strong",
@@ -170,6 +172,7 @@ public final class TagCreatorCodeGenerator {
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",

View File

@@ -0,0 +1,94 @@
package j2html_codegen.generators;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import j2html_codegen.wattsi.AttributeDefinition;
import j2html_codegen.wattsi.ElementDefinition;
import j2html_codegen.wattsi.WattsiSource;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import javax.lang.model.element.Modifier;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class WattsiGenerator {
public static void main(String... args) throws IOException {
Path source = Paths.get(args[0]);
Document doc = Jsoup.parse(source.toFile(), "UTF-8", "https://html.spec.whatwg.org/");
WattsiSource wattsi = new WattsiSource(doc);
List<ElementDefinition> elements = wattsi.elementDefinitions();
List<AttributeDefinition> attributes = wattsi.attributeDefinitions();
// for (ElementDefinition element : elements) {
// System.out.println((element.isObsolete() ? "!" : "") + element.name());
// for (AttributeDefinition attribute : attributes) {
// if (attribute.appliesTo(element)) {
// System.out.println(" " + (attribute.isObsolete() ? "!" : "") + attribute.name());
// }
// }
// System.out.println();
// }
for (ElementDefinition element : elements) {
ClassName className = ClassName.get(
"com.j2html",
capitalize(element.name()) + "Tag"
);
TypeSpec.Builder type = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC);
if (element.isObsolete()) {
type.addAnnotation(Deprecated.class);
}
for (AttributeDefinition attribute : attributes) {
if (attribute.appliesTo(element)) {
String name = methodName("with", attribute.name().split("-"));
MethodSpec.Builder setter = MethodSpec.methodBuilder(name)
.addModifiers(Modifier.PUBLIC)
.returns(className)
.addStatement("return this");
if(attribute.isObsolete()){
setter.addAnnotation(Deprecated.class);
}
type.addMethod(setter.build());
}
}
// System.out.println(type.build().toString());
}
System.out.println(doc.select("dfn"));
}
private static String methodName(String prefix, String... words){
String[] tmp = new String[words.length + 1];
tmp[0] = prefix;
for(int i = 0; i < words.length; i++){
tmp[i+1] = words[i];
}
return methodName(tmp);
}
private static String methodName(String... words){
String[] camelCase = new String[words.length];
camelCase[0] = words[0];
for(int i = 1; i < words.length; i++){
camelCase[i] = capitalize(words[i]);
}
return String.join("", camelCase);
}
private static String capitalize(String word){
return word.substring(0,1).toUpperCase() + word.substring(1).toLowerCase();
}
}

View File

@@ -80,7 +80,7 @@ public final class AttributesList {
new AttrD("min", true, "input","meter"),
new AttrD("multiple", false, "input","select"),
new AttrD("muted", false, "video","audio"),
new AttrD("name", true, "button","fieldset","form","iframe","input","map","meta","object","output","param","select","textarea"),
new AttrD("name", true, "button","fieldset","form","iframe","input","map","meta","object","output","param","select","slot","textarea"),
new AttrD("novalidate", false, "form"),
new AttrD("onabort", true, "audio","embed","img","object","video"),
new AttrD("onafterprint", true, "body"),
@@ -174,7 +174,7 @@ public final class AttributesList {
//new AttrD("translate"),// global attribute
new AttrD("type", true, "a","button","embed","input","link","menu","object","script","source","style"),
new AttrD("usemap", true, "img","object"),
new AttrD("value", true, "button","input","li","option","meter","progress","param"),
new AttrD("value", true, "button","data","input","li","option","meter","progress","param"),
new AttrD("width", true, "canvas","embed","iframe","img","input","object","video"),
new AttrD("wrap", true, "textarea")
);

View File

@@ -0,0 +1,10 @@
package j2html_codegen.wattsi;
public interface AttributeDefinition {
String name();
boolean appliesTo(ElementDefinition element);
boolean isObsolete();
}

View File

@@ -0,0 +1,9 @@
package j2html_codegen.wattsi;
public interface ElementDefinition {
String name();
boolean isObsolete();
}

View File

@@ -0,0 +1,204 @@
package j2html_codegen.wattsi;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
public class WattsiSource {
private final Document doc;
private final Set<Reference> obsolete = new HashSet<>();
public WattsiSource(Document doc) {
this.doc = doc;
// Find where obsolete elements are defined or referenced.
Elements obsoleteElements = doc.select("p:contains(Elements in the following list are entirely obsolete) + dl");
// Convert definitions into references to record obsolete elements.
obsoleteElements.select("dt > dfn[element]")
.stream()
.map(WattsiElement::new)
.map(WattsiElement::reference)
.forEach(obsolete::add);
// Extract references to record obsolete elements.
obsoleteElements.select("dt > code")
.stream()
.map(Element::childNodes)
.map(Reference::from)
.forEach(obsolete::add);
// Find where obsolete attributes are defined or referenced.
Elements obsoleteAttributes = doc.select("p:contains(The following attributes are obsolete) + dl");
// Convert definitions into references to record obsolete attributes.
obsoleteAttributes.select("dt > dfn[element-attr]").stream()
.map(WattsiAttribute::new)
.map(WattsiAttribute::reference)
.forEach(obsolete::add);
// System.out.println(obsoleteAttributes.select("dt"));
// obsoleteAttributes.select("dt > code").stream()
// .map(Element::childNodes)
// .map(Reference::from)
// .forEach(System.err::println);
// System.out.println(
// doc.select("dfn[obsolete]")
// );
}
public List<ElementDefinition> elementDefinitions() {
return doc.select("dfn[element]").stream()
.map(WattsiElement::new)
.collect(toList());
}
public List<AttributeDefinition> attributeDefinitions() {
return doc.select("dfn[element-attr]").stream()
.map(WattsiAttribute::new)
.collect(toList());
}
public class WattsiElement implements ElementDefinition {
private final Element dfn;
WattsiElement(Element dfn) {
if (!"dfn".equals(dfn.tagName())) {
throw new IllegalArgumentException("Element cannot be defined from: " + dfn);
}
if (!dfn.hasAttr("element")) {
throw new IllegalArgumentException("Does not define an element: " + dfn);
}
if (dfn.childrenSize() != 1) {
throw new IllegalArgumentException("Element cannot have multiple definitions: " + dfn);
}
this.dfn = dfn;
}
private Reference reference() {
return Reference.from(dfn.childNodes());
}
@Override
public String name() {
if (dfn.hasAttr("data-x")) {
return dfn.attr("data-x");
}
return Reference.from(dfn.childNodes()).key;
}
@Override
public boolean isObsolete() {
return obsolete.contains(reference());
}
}
public class WattsiAttribute implements AttributeDefinition {
private final Element dfn;
WattsiAttribute(Element dfn) {
if (!"dfn".equals(dfn.tagName())) {
throw new IllegalArgumentException("Attribute cannot be defined from: " + dfn);
}
if (!dfn.hasAttr("element-attr")) {
throw new IllegalArgumentException("Does not define an attribute: " + dfn);
}
if (dfn.childrenSize() != 1) {
throw new IllegalArgumentException("Attribute cannot have multiple definitions: " + dfn);
}
this.dfn = dfn;
}
private Reference reference() {
return Reference.from(dfn.childNodes());
}
@Override
public String name() {
return reference().text;
}
private List<String> targets() {
if (dfn.hasAttr("for")) {
return Arrays.asList(dfn.attr("for").trim().split(","));
}
return new ArrayList<>();
}
@Override
public boolean appliesTo(ElementDefinition element) {
return targets().contains(element.name());
}
@Override
public boolean isObsolete() {
return obsolete.contains(reference());
}
}
private static class Reference {
private final String key;
private final String text;
Reference(String key, String text) {
this.key = key;
this.text = text;
}
@Override
public String toString() {
return key + "[" + text + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Reference reference = (Reference) o;
return key.equals(reference.key);
}
@Override
public int hashCode() {
return Objects.hash(key);
}
public static Reference from(List<Node> nodes) {
if (nodes.stream().allMatch(n -> n instanceof TextNode)) {
String txt = nodes.stream()
.map(n -> (TextNode) n)
.map(TextNode::text)
.collect(Collectors.joining(" "));
return new Reference(txt, txt);
}
for (Node node : nodes) {
if (node instanceof Element) {
Element element = (Element) node;
if (element.is("code") || element.is("span")) {
if (element.hasAttr("data-x")) {
return new Reference(element.attr("data-x").toLowerCase(), element.text());
} else {
return new Reference(element.text().toLowerCase(), element.text());
}
}
}
}
return null;
}
}
}

View File

@@ -0,0 +1,77 @@
package j2html_codegen;
import j2html_codegen.generators.TagCreatorCodeGenerator;
import j2html_codegen.wattsi.ElementDefinition;
import j2html_codegen.wattsi.WattsiSource;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static org.junit.Assert.assertEquals;
public class CodeGeneratorComplianceTests {
private WattsiSource specification;
@Before
public void setUp() throws IOException {
Path source = Paths.get("src","test","resources","2022-01.wattsi");
Document doc = Jsoup.parse(source.toFile(), "UTF-8", "https://html.spec.whatwg.org/");
specification = new WattsiSource(doc);
}
private Set<String> generatedElements(){
Set<String> elements = new HashSet<>();
elements.addAll(TagCreatorCodeGenerator.emptyTags());
elements.addAll(TagCreatorCodeGenerator.containerTags());
return elements;
}
private Set<String> specifiedElements(WattsiSource source){
Set<String> elements = new HashSet<>();
for(ElementDefinition element : source.elementDefinitions()){
elements.add(element.name());
}
return elements;
}
@Test
@Ignore
// TODO restore this test once a policy has been determined for obsolete elements.
public void all_wattsi_elements_are_defined_in_the_code_generator() {
Set<String> generated = generatedElements();
List<String> undefined = specification.elementDefinitions().stream()
.filter(element -> !element.isObsolete())
.filter(element -> !generated.contains(element.name()))
.map(ElementDefinition::name)
.collect(toList());
assertEquals("HTML elements are missing", emptyList(), undefined);
// Currently missing (and mostly deprecated):
// hgroup
}
@Test
public void only_wattsi_elements_are_defined_in_the_code_generator(){
Set<String> specified = specifiedElements(specification);
List<String> invalid = generatedElements().stream()
.filter(element -> !specified.contains(element))
.collect(toList());
assertEquals("HTML elements are invalid", emptyList(), invalid);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,9 +11,9 @@
<header class="top-header"><nav class="width-limit"><a id="logo" href="/"><span><img src="/img/logo.svg" alt="j2html logo"></span></a><ul><li><a href="/">Home</a></li><li><a href="/download.html">Download</a></li><li><a href="/examples.html">Examples</a></li><li><a href="/news.html">News</a></li></ul></nav></header><header class="banner"><h1 class="width-limit">Maven and GitHub</h1></header><main class="width-limit"><section id="download"><h2>Maven dependency</h2><p>To experience the joy of generating HTML with a Java HTML builder, add the j2html dependency to your POM:</p><pre><code class="language-markup">&lt;dependency&gt;
&lt;groupId&gt;com.j2html&lt;/groupId&gt;
&lt;artifactId&gt;j2html&lt;/artifactId&gt;
&lt;version&gt;1.4.0&lt;/version&gt;
&lt;version&gt;1.6.0&lt;/version&gt;
&lt;/dependency&gt;
</code></pre><h2>Clone the repo on GitHub</h2><p>Please clone and/or fork the repo on <a href="https://github.com/tipsy/j2html" target="_blank">GitHub</a>, make changes, and create pull requests! We will go through pull requests every sunday, so don't be shy.</p></section></main><div id="javalin-suggestion"><span class="close"></span> Want a simple and modern web framework? <br> Try our other project: <a href="https://javalin.io?from=j2html">https://javalin.io</a></div><footer>This page was created using <a href="https://github.com/tipsy/j2html" target="_blank">j2html</a> and <a href="https://javalin.io/" target="_blank">Javalin</a>. Webpage source on <a href="https://github.com/j2html/j2html-webpage" target="_blank">Github</a>. <br> <p class="lols">A static page generator or a template engine would be better suited than a HTML builder for creating this page, but we had to do it.</p></footer><script>/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript+java */
</code></pre><a id="upgrade"></a><h2>Steps for upgrading</h2><p>From 1.4.0 to 1.5.0</p><ul><li>Change return types from <code>Tag</code>, <code>ContainerTag</code> or <code>EmptyTag</code> to the specific tag being returned.</li><li>Change missing method calls on tags, such as <code>withRole(&quot;value&quot;)</code> to <code>.attr(&quot;role&quot;, &quot;value&quot;)</code>.</li><li>Method parameters of <code>Tag</code>, <code>ContainerTag</code> or <code>EmptyTag</code> should have a wildcard (&lt;?&gt;) added, or be changed to a specific tag.</li><li>Replace ambiguous method references like <code>each(list, TagCreator::li)</code> with lambdas such as <code>each(list, str -&gt; li(str))</code>.</li></ul><h2>Clone the repo on GitHub</h2><p>Please clone and/or fork the repo on <a href="https://github.com/tipsy/j2html" target="_blank">GitHub</a>, make changes, and create pull requests! We will go through pull requests every sunday, so don't be shy.</p></section></main><div id="javalin-suggestion"><span class="close"></span> Want a simple and modern web framework? <br> Try our other project: <a href="https://javalin.io?from=j2html">https://javalin.io</a></div><footer>This page was created using <a href="https://github.com/tipsy/j2html" target="_blank">j2html</a> and <a href="https://javalin.io/" target="_blank">Javalin</a>. Webpage source on <a href="https://github.com/j2html/j2html-webpage" target="_blank">Github</a>. <br> <p class="lols">A static page generator or a template engine would be better suited than a HTML builder for creating this page, but we had to do it.</p></footer><script>/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript+java */
self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var a={};for(var r in e)e.hasOwnProperty(r)&&(a[r]=t.util.clone(e[r]));return a;case"Array":return e.map(function(e){return t.util.clone(e)})}return e}},languages:{extend:function(e,n){var a=t.util.clone(t.languages[e]);for(var r in n)a[r]=n[r];return a},insertBefore:function(e,n,a,r){r=r||t.languages;var i=r[e];if(2==arguments.length){a=arguments[1];for(var l in a)a.hasOwnProperty(l)&&(i[l]=a[l]);return i}var s={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var l in a)a.hasOwnProperty(l)&&(s[l]=a[l]);s[o]=i[o]}return t.languages.DFS(t.languages,function(t,n){n===r[e]&&t!=e&&(this[t]=s)}),r[e]=s},DFS:function(e,n,a){for(var r in e)e.hasOwnProperty(r)&&(n.call(e,r,e[r],a||r),"Object"===t.util.type(e[r])?t.languages.DFS(e[r],n):"Array"===t.util.type(e[r])&&t.languages.DFS(e[r],n,r))}},highlightAll:function(e,n){for(var a,r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'),i=0;a=r[i++];)t.highlightElement(a,e===!0,n)},highlightElement:function(a,r,i){for(var l,s,o=a;o&&!e.test(o.className);)o=o.parentNode;if(o&&(l=(o.className.match(e)||[,""])[1],s=t.languages[l]),s){a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,o=a.parentNode,/pre/i.test(o.nodeName)&&(o.className=o.className.replace(e,"").replace(/\s+/g," ")+" language-"+l);var u=a.textContent;if(u){u=u.replace(/^(?:\r?\n|\r)/,"");var g={element:a,language:l,grammar:s,code:u};if(t.hooks.run("before-highlight",g),r&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){g.highlightedCode=n.stringify(JSON.parse(e.data),l),t.hooks.run("before-insert",g),g.element.innerHTML=g.highlightedCode,i&&i.call(g.element),t.hooks.run("after-highlight",g)},c.postMessage(JSON.stringify({language:g.language,code:g.code}))}else g.highlightedCode=t.highlight(g.code,g.grammar,g.language),t.hooks.run("before-insert",g),g.element.innerHTML=g.highlightedCode,i&&i.call(a),t.hooks.run("after-highlight",g)}}},highlight:function(e,a,r){var i=t.tokenize(e,a);return n.stringify(t.util.encode(i),r)},tokenize:function(e,n){var a=t.Token,r=[e],i=n.rest;if(i){for(var l in i)n[l]=i[l];delete n.rest}e:for(var l in n)if(n.hasOwnProperty(l)&&n[l]){var s=n[l];s="Array"===t.util.type(s)?s:[s];for(var o=0;o<s.length;++o){var u=s[o],g=u.inside,c=!!u.lookbehind,f=0,h=u.alias;u=u.pattern||u;for(var p=0;p<r.length;p++){var d=r[p];if(r.length>e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),N=[p,1];b&&N.push(b);var O=new a(l,g?t.tokenize(m,g):m,h);N.push(O),w&&N.push(w),Array.prototype.splice.apply(r,N)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+"</"+i.tag+">"},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);;
Prism.languages.markup={comment:/<!--[\w\W]*?-->/,prolog:/<\?.+?\?>/,doctype:/<!DOCTYPE.+?>/,cdata:/<!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/i,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/=|>|"/}},punctuation:/\/?>/,"attr-name":{pattern:/[\w:-]+/,inside:{namespace:/^[\w-]+?:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(t){"entity"===t.type&&(t.attributes.title=t.content.replace(/&amp;/,"&"))});;
Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{punctuation:/[;:]/}},url:/url\((?:(["'])(\\\n|\\?.)*?\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*(?=\s*\{)/,string:/("|')(\\\n|\\?.)*?\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,punctuation:/[\{\};:]/,"function":/[-a-z0-9]+(?=\()/i},Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/<style[\w\W]*?>[\w\W]*?<\/style>/i,inside:{tag:{pattern:/<style[\w\W]*?>|<\/style>/i,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css},alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag));;

View File

@@ -45,8 +45,12 @@
import static j2html.TagCreator.*; // Use static star import
Config.class // Static config class which holds all configuration
Config.closeEmptyTags = true // all options are public and static, assign using &#x27;=&#x27; operator
Config.class // Holds all configuration. Offers global configuration or customizable instances
Config.closeEmptyTags = true // Global options are public, static and mutable.
Config.global() // Copy all static Config fields into an instance. Instances are immutable
Config.defaults() // A Config with defaults that are independent of global options
Config.global().withEmptyTagsClosed(true) // A Config that is different from the global options
Config.defaults().withEmptyTagsClosed(true) // A Config that is different from the default options
TagCreator.join() // Method for joining small snippets, like:
@@ -64,6 +68,17 @@ div().withClasses(&quot;menu-element&quot;, iffElse(isActive, &quot;active&quot;
Tag.class // Is extended by ContainerTag (ex &lt;div&gt;&lt;/div&gt; and EmptyTag (ex &lt;br&gt;)
Tag.attr(String attribute, Object value) // Set an attribute on the tag
Tag.withXyz(String value) // Calls attr with predefined attribute (ex .withId, .withClass, etc.)
Tag.render(HtmlBuilder builder) // Render HTML using the given builder.
Tag.render() // Shortcut for rendering flat HTML into a string using global Config.
ContainerTag.renderFormatted() // Shortcut for rendering indented HTML into a string using global Config.
HtmlBuilder.class // Interface for composing HTML. Implemented by FlatHtml and IndentedHtml
FlatHtml.into(Appendable) // Render into a stream, file, etc. without indentation or line breaks
FlatHtml.into(Appendable appendable, Config config) // Customize rendering of flat html
IndentedHtml.into(Appendable) // Render human-readable HTML into an stream, file, etc.
IndentedHtml.into(Appendable appendable, Config config) // Customize rendering of intended html
ul(li(&quot;one&quot;), li(&quot;two&quot;)).render(IndentedHtml.inMemory()).toString() // Similar to renderFormatted()
ul(li(&quot;one&quot;), li(&quot;two&quot;)).render(IndentedHtml.into(filewriter)) // Write HTML into a file
</code></pre><h2 id="loops">Loops, each() and filter()</h2><p>Using Java 8&#x27;s lambda syntax, you can write loops (via streams) inside your HTML-builder:</p><div class="code-compare nowith"><ul><li class="nowith-switch active">version 1.0.0 +</li><li class="with-switch">earlier versions</li></ul><pre class="nowith-pre"><code class="language-java">body(
div(attrs(&quot;#employees&quot;),
employees.stream().map(employee -&gt;

File diff suppressed because one or more lines are too long

View File

@@ -10,7 +10,7 @@
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>1.5.0</version>
<version>1.6.1-SNAPSHOT</version>
<name>j2html</name>
<description>Java to HTML builder with a fluent API</description>
@@ -27,7 +27,7 @@
<connection>scm:git:git@github.com:tipsy/j2html.git</connection>
<developerConnection>scm:git:git@github.com:tipsy/j2html.git</developerConnection>
<url>https://github.com/tipsy/j2html.git</url>
<tag>j2html-1.5.0</tag>
<tag>HEAD</tag>
</scm>
<developers>
<developer>
@@ -98,7 +98,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -111,7 +111,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4</version>
<version>3.1.0</version>
<executions>
<execution>
<id>enforce-java</id>
@@ -131,14 +131,14 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<version>3.4.0</version>
<configuration>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<version>3.2.2</version>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
@@ -154,7 +154,7 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.5.0</version>
<version>5.1.6</version>
<executions>
<execution>
<id>bundle-manifest</id>
@@ -165,6 +165,44 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.revapi</groupId>
<artifactId>revapi-maven-plugin</artifactId>
<version>0.14.6</version>
<dependencies>
<dependency>
<groupId>org.revapi</groupId>
<artifactId>revapi-java</artifactId>
<version>0.26.1</version>
</dependency>
</dependencies>
<configuration>
<oldArtifacts>
<artifact>com.j2html:j2html:1.6.0</artifact>
</oldArtifacts>
<analysisConfiguration>
<revapi.differences>
<differences>
<item>
<ignore>true</ignore>
<code>java.class.removed</code>
<old>class j2html.tags.specialized.GenerateTag</old>
<justification>
This class should never have been used. It was introduced
accidentally by find+replace in the code generator class.
</justification>
</item>
</differences>
</revapi.differences>
</analysisConfiguration>
</configuration>
<executions>
<execution>
<id>check</id>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
@@ -182,7 +220,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<version>3.0.1</version>
<executions>
<execution>
<id>sign-artifacts</id>

View File

@@ -461,6 +461,13 @@ public class TagCreator {
public static ColgroupTag colgroup (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new ColgroupTag().withText(text), shortAttr); }
public static ColgroupTag colgroup (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new ColgroupTag().with(dc), shortAttr); }
public static DataTag data () { return new DataTag(); }
public static DataTag data (String text) { return new DataTag().withText(text); }
public static DataTag data (DomContent... dc) { return new DataTag().with(dc); }
public static DataTag data (Attr.ShortForm shortAttr) { return Attr.addTo( new DataTag(), shortAttr); }
public static DataTag data (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new DataTag().withText(text), shortAttr); }
public static DataTag data (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new DataTag().with(dc), shortAttr); }
public static DatalistTag datalist () { return new DatalistTag(); }
public static DatalistTag datalist (String text) { return new DatalistTag().withText(text); }
public static DatalistTag datalist (DomContent... dc) { return new DatalistTag().with(dc); }
@@ -762,6 +769,13 @@ public class TagCreator {
public static PTag p (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new PTag().withText(text), shortAttr); }
public static PTag p (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new PTag().with(dc), shortAttr); }
public static PictureTag picture () { return new PictureTag(); }
public static PictureTag picture (String text) { return new PictureTag().withText(text); }
public static PictureTag picture (DomContent... dc) { return new PictureTag().with(dc); }
public static PictureTag picture (Attr.ShortForm shortAttr) { return Attr.addTo( new PictureTag(), shortAttr); }
public static PictureTag picture (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new PictureTag().withText(text), shortAttr); }
public static PictureTag picture (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new PictureTag().with(dc), shortAttr); }
public static PreTag pre () { return new PreTag(); }
public static PreTag pre (String text) { return new PreTag().withText(text); }
public static PreTag pre (DomContent... dc) { return new PreTag().with(dc); }
@@ -839,6 +853,13 @@ public class TagCreator {
public static SelectTag select (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new SelectTag().withText(text), shortAttr); }
public static SelectTag select (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new SelectTag().with(dc), shortAttr); }
public static SlotTag slot () { return new SlotTag(); }
public static SlotTag slot (String text) { return new SlotTag().withText(text); }
public static SlotTag slot (DomContent... dc) { return new SlotTag().with(dc); }
public static SlotTag slot (Attr.ShortForm shortAttr) { return Attr.addTo( new SlotTag(), shortAttr); }
public static SlotTag slot (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new SlotTag().withText(text), shortAttr); }
public static SlotTag slot (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new SlotTag().with(dc), shortAttr); }
public static SmallTag small () { return new SmallTag(); }
public static SmallTag small (String text) { return new SmallTag().withText(text); }
public static SmallTag small (DomContent... dc) { return new SmallTag().with(dc); }
@@ -909,6 +930,13 @@ public class TagCreator {
public static TdTag td (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new TdTag().withText(text), shortAttr); }
public static TdTag td (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new TdTag().with(dc), shortAttr); }
public static TemplateTag template () { return new TemplateTag(); }
public static TemplateTag template (String text) { return new TemplateTag().withText(text); }
public static TemplateTag template (DomContent... dc) { return new TemplateTag().with(dc); }
public static TemplateTag template (Attr.ShortForm shortAttr) { return Attr.addTo( new TemplateTag(), shortAttr); }
public static TemplateTag template (Attr.ShortForm shortAttr, String text) { return Attr.addTo( new TemplateTag().withText(text), shortAttr); }
public static TemplateTag template (Attr.ShortForm shortAttr, DomContent... dc) { return Attr.addTo( new TemplateTag().with(dc), shortAttr); }
public static TextareaTag textarea () { return new TextareaTag(); }
public static TextareaTag textarea (String text) { return new TextareaTag().withText(text); }
public static TextareaTag textarea (DomContent... dc) { return new TextareaTag().with(dc); }

View File

@@ -53,6 +53,7 @@ public abstract class Attr {
public static final String HTTP_EQUIV = "http-equiv";
public static final String ICON = "icon";
public static final String ID = "id";
public static final String IS = "is";
public static final String ISMAP = "ismap";
public static final String ITEMPROP = "itemprop";
public static final String KEYTYPE = "keytype";
@@ -96,6 +97,7 @@ public abstract class Attr {
public static final String SHAPE = "shape";
public static final String SIZE = "size";
public static final String SIZES = "sizes";
public static final String SLOT = "slot";
public static final String SPAN = "span";
public static final String SPELLCHECK = "spellcheck";
public static final String SRC = "src";

View File

@@ -162,8 +162,12 @@ public abstract class Tag<T extends Tag<T>> extends DomContent implements IInsta
public T withId(String id) { return attr(Attr.ID, id); }
public T withIs(String element){ return attr(Attr.IS, element); }
public T withLang(String lang) { return attr(Attr.LANG, lang); }
public T withSlot(String name){ return attr(Attr.SLOT, name); }
public T isSpellcheck(){ return attr(Attr.SPELLCHECK, "true"); }
public T withStyle(String style) { return attr(Attr.STYLE, style); }
@@ -191,8 +195,12 @@ public abstract class Tag<T extends Tag<T>> extends DomContent implements IInsta
public T withCondId(boolean condition, String id) { return condAttr(condition, Attr.ID, id); }
public T withCondIs(boolean condition, String element){ return condAttr(condition, Attr.IS, element); }
public T withCondLang(boolean condition, String lang) { return condAttr(condition, Attr.LANG, lang); }
public T withCondSlot(boolean condition, String name){ return condAttr(condition, Attr.SLOT, name); }
public T withCondSpellcheck(boolean condition){ return attr(Attr.SPELLCHECK, (condition)?"true":"false"); }
public T withCondStyle(boolean condition, String style) { return condAttr(condition, Attr.STYLE, style); }

View File

@@ -0,0 +1,11 @@
package j2html.tags.specialized;
import j2html.tags.ContainerTag;
import j2html.tags.attributes.IValue;
public final class DataTag extends ContainerTag<DataTag>
implements IValue<DataTag> {
public DataTag() {
super("data");
}
}

View File

@@ -1,9 +0,0 @@
package j2html.tags.specialized;
import j2html.tags.ContainerTag;
public final class GenerateTag extends ContainerTag<GenerateTag> {
public GenerateTag() {
super("generate");
}
}

View File

@@ -0,0 +1,9 @@
package j2html.tags.specialized;
import j2html.tags.ContainerTag;
public final class PictureTag extends ContainerTag<PictureTag> {
public PictureTag() {
super("picture");
}
}

View File

@@ -0,0 +1,11 @@
package j2html.tags.specialized;
import j2html.tags.ContainerTag;
import j2html.tags.attributes.IName;
public final class SlotTag extends ContainerTag<SlotTag>
implements IName<SlotTag> {
public SlotTag() {
super("slot");
}
}

View File

@@ -0,0 +1,9 @@
package j2html.tags.specialized;
import j2html.tags.ContainerTag;
public final class TemplateTag extends ContainerTag<TemplateTag> {
public TemplateTag() {
super("template");
}
}

View File

@@ -288,13 +288,13 @@ public class JSMin {
out.flush();
}
private class UnterminatedCommentException extends Exception {
public class UnterminatedCommentException extends Exception {
}
private class UnterminatedStringLiteralException extends Exception {
public class UnterminatedStringLiteralException extends Exception {
}
private class UnterminatedRegExpLiteralException extends Exception {
public class UnterminatedRegExpLiteralException extends Exception {
}
}

View File

@@ -222,6 +222,7 @@ public class TagCreatorTest {
assertThat(cite("Text").render(), is("<cite>Text</cite>"));
assertThat(code().render(), is("<code></code>"));
assertThat(colgroup().render(), is("<colgroup></colgroup>"));
assertThat(data().render(), is("<data></data>"));
assertThat(datalist().render(), is("<datalist></datalist>"));
assertThat(dd().render(), is("<dd></dd>"));
assertThat(dd("Text").render(), is("<dd>Text</dd>"));
@@ -287,6 +288,7 @@ public class TagCreatorTest {
assertThat(output().render(), is("<output></output>"));
assertThat(p().render(), is("<p></p>"));
assertThat(p("Text").render(), is("<p>Text</p>"));
assertThat(picture().render(), is("<picture></picture>"));
assertThat(pre().render(), is("<pre></pre>"));
assertThat(progress().render(), is("<progress></progress>"));
assertThat(q().render(), is("<q></q>"));
@@ -300,6 +302,8 @@ public class TagCreatorTest {
assertThat(script().render(), is("<script></script>"));
assertThat(section().render(), is("<section></section>"));
assertThat(select().render(), is("<select></select>"));
assertThat(slot().render(), is("<slot></slot>"));
assertThat(slot("Text").render(), is("<slot>Text</slot>"));
assertThat(small().render(), is("<small></small>"));
assertThat(small("Text").render(), is("<small>Text</small>"));
assertThat(span().render(), is("<span></span>"));
@@ -317,6 +321,7 @@ public class TagCreatorTest {
assertThat(tbody().render(), is("<tbody></tbody>"));
assertThat(td().render(), is("<td></td>"));
assertThat(td("Text").render(), is("<td>Text</td>"));
assertThat(template("Text").render(), is("<template>Text</template>"));
assertThat(textarea().render(), is("<textarea></textarea>"));
assertThat(tfoot().render(), is("<tfoot></tfoot>"));
assertThat(th().render(), is("<th></th>"));

View File

@@ -10,7 +10,7 @@ Website is available on http://localhost:8888/.
In order to "deploy" the website, the static files in the `j2html/docs` must be updated.
You can either download the files manually from your browser, or use curl:
```
```sh
curl "http://localhost:8888/" > index.html
curl "http://localhost:8888/download.html" > download.html
curl "http://localhost:8888/examples.html" > examples.html

View File

@@ -21,7 +21,7 @@
<dependency>
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>1.4.0</version>
<version>1.6.0</version>
</dependency>
</dependencies>
@@ -45,7 +45,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>

View File

@@ -7,9 +7,8 @@ import io.javalin.http.staticfiles.Location;
public class Main {
public static void main(String[] args) {
Javalin app = Javalin.create(config ->
config.addStaticFiles("src/main/resources/public", Location.EXTERNAL)
config.addStaticFiles("website/src/main/resources/public", Location.EXTERNAL)
).start(8888);
app.get("/", PageController::serveIndex);

View File

@@ -2,13 +2,7 @@ package app.views.pages;
import app.views.MainView;
import static app.views.Partials.codeSnippet;
import static j2html.TagCreator.a;
import static j2html.TagCreator.attrs;
import static j2html.TagCreator.fileAsEscapedString;
import static j2html.TagCreator.h2;
import static j2html.TagCreator.join;
import static j2html.TagCreator.p;
import static j2html.TagCreator.section;
import static j2html.TagCreator.*;
public class DownloadView {
public static String render() {
@@ -19,6 +13,23 @@ public class DownloadView {
h2("Maven dependency"),
p("To experience the joy of generating HTML with a Java HTML builder, add the j2html dependency to your POM:"),
codeSnippet("markup", fileAsEscapedString("/codeExamples/mavenDep.xml")),
a().withId("upgrade"),
h2("Steps for upgrading"),
p("From 1.4.0 to 1.5.0"),
ul(
li(
join("Change return types from", code("Tag"), ",", code("ContainerTag"), "or", code("EmptyTag"), "to the specific tag being returned.")
),
li(
join("Change missing method calls on tags, such as", code("withRole(\"value\")"), "to", code(".attr(\"role\", \"value\")"), ".")
),
li(
join("Method parameters of", code("Tag"), ",", code("ContainerTag"), "or", code("EmptyTag"), "should have a wildcard (&lt;?&gt;) added, or be changed to a specific tag.")
),
li(
join("Replace ambiguous method references like ", code("each(list, TagCreator::li)"), " with lambdas such as", code("each(list, str -> li(str))"), ".")
)
),
h2("Clone the repo on GitHub"),
p(join(
"Please clone and/or fork the repo on",

View File

@@ -2,39 +2,35 @@ package app.views.pages;
import app.views.MainView;
import j2html.TagCreator;
import j2html.tags.ContainerTag;
import j2html.tags.DomContent;
import j2html.tags.specialized.ArticleTag;
import java.util.Arrays;
import java.util.List;
import static app.views.Partials.mavenLink;
import static j2html.TagCreator.a;
import static j2html.TagCreator.article;
import static j2html.TagCreator.attrs;
import static j2html.TagCreator.br;
import static j2html.TagCreator.code;
import static j2html.TagCreator.dd;
import static j2html.TagCreator.dl;
import static j2html.TagCreator.dt;
import static j2html.TagCreator.each;
import static j2html.TagCreator.em;
import static j2html.TagCreator.h2;
import static j2html.TagCreator.join;
import static j2html.TagCreator.li;
import static j2html.TagCreator.p;
import static j2html.TagCreator.section;
import static j2html.TagCreator.text;
import static j2html.TagCreator.ul;
import static j2html.TagCreator.*;
public class NewsView {
private static ContainerTag newsPost(String title, String version, DomContent... listItems) {
private static ArticleTag newsPost(
String title,
String version,
boolean breaking,
DomContent... listItems
) {
List<DomContent> list = Arrays.asList(listItems);
return article(
h2(title).withId("j2html-" + version + "-released"),
mavenLink(version),
iff(breaking,
p(
a("This version contains breaking changes. Please view the upgrade guide.").withHref("download.html#upgrade")
)
),
p("Changes:"),
ul(
each(list, TagCreator::li)
each(list, el -> TagCreator.li(el))
));
}
@@ -45,13 +41,45 @@ public class NewsView {
section(attrs("#news"),
newsPost(
"j2html 1.4.0 adds a couple of convenient methods (Jan 2019)", "1.4.0",
"j2html 1.6.0 adds support for web components and newer HTML elements. (Jun 2022)",
"1.6.0",
false,
join("Added", code("TagCreator::data"), "to provide machine-readable translations."),
join("Added", code("TagCreator::picture"), "to provide alternative versions of an image for different display/device scenarios."),
join("Added", code("TagCreator::slot"), "to provide placeholders for web components."),
join("Added", code("TagCreator::template"), "to provide templating of content fragments for web components."),
join("Added global attributes used for web components.")
),
newsPost(
"j2html 1.5.0 enhances factory methods and class types, overhauls HTML rendering, introduces support for Java modules and fixes several bugs (Jun 2021)",
"1.5.0",
true,
join("Added", code("Automatic-Module-Name"), "to manifest to support JDK 9+"),
join("Added", code("TagCreator::each(DomContent...)")),
join("Added", code("TagCreator::each(Collection, BiFunction)")),
join("Added", code("HtmlBuilder"), "interface, with implementations for ", code("FlatHtml"), "and", code("IndentedHtml")),
join("Added", code("Config.defaults()"), "for customizable configuration instances and", code("Config.global()"), "for (backwards-compatible) static global configuration"),
join("Deprecated old rendering methods. Libraries that use custom Tag implementations should switch to using ", code("HtmlBuilder")),
join("Fixed the use of", code("rawHtml()"), "when constructing the Tag tree"),
join("Fixed text escaping for", code("style()"), "and", code("script()")),
join("Fixed formatting of children for", code("textarea()"), "and", code("pre()")),
join("Fixed appending an unnecessary delimiter to the end of text with", code("join()")),
join("Thanks to", a("Oliver Becker").withHref("https://github.com/obecker"), "for many code improvements")
),
newsPost(
"j2html 1.4.0 adds a couple of convenient methods (Jan 2019)",
"1.4.0",
false,
join("Added", code("TagCreator::each(Map, BiFunction)")),
join("Added", code("Stream<DomContent>"), "variants of", code("each"), "and", code("with"))
),
newsPost(
"j2html 1.3.0 has a couple of features and fixes (May 2018)", "1.3.0",
"j2html 1.3.0 has a couple of features and fixes (May 2018)",
"1.3.0",
false,
join("Added support for", code("Map"), "in", code("each()")),
join("Added osgi metadata"),
join("Added support for", code("Optional"), "in", code("iff()")),
@@ -59,19 +87,25 @@ public class NewsView {
),
newsPost(
"j2html 1.2.2 has performance improvements (Dec 2017)", "1.2.2",
"j2html 1.2.2 has performance improvements (Dec 2017)",
"1.2.2",
false,
join("There have been some massive performance improvements. Big thanks to", a("kicktipp").withHref("https://github.com/kicktipp"), ".")
),
newsPost(
"j2html 1.2.1 fixes some bugs (Nov 2017)", "1.2.1",
"j2html 1.2.1 fixes some bugs (Nov 2017)",
"1.2.1",
false,
join("Fix", em("\"bad closing tag (<!DOCTYPE html/>) when closeEmptyTags is true\""), "bug"),
join("Fix", em("\"can't load static resources from jar\""), "bug"),
join("Fix", em("\"CSSMin stripping last character of CSS rule if rule doesn't end in semi-colon\""), "bug")
),
newsPost(
"j2html 1.2.0 already? (Sep 2017)", "1.2.0",
"j2html 1.2.0 already? (Sep 2017)",
"1.2.0",
false,
join("Added option to render formatted HTML, ex", code("body(...).renderFormatted()")),
join("Added option to configure HTML-formatting-indent via", code("Config.indenter = (int, string) -> {...}")),
join("Added option to configure CSS-minification via", code("Config.cssMinifier = string -> {...}")),
@@ -81,12 +115,16 @@ public class NewsView {
),
newsPost(
"j2html 1.1.0 is out! (Aug 2017)", "1.1.0",
"j2html 1.1.0 is out! (Aug 2017)",
"1.1.0",
false,
join("Added a option to customize TextEscaper via", code("Config.textEscaper = text -> {}"))
),
newsPost(
"j2html 1.0.0 is here! (May 2017)", "1.0.0",
"j2html 1.0.0 is here! (May 2017)",
"1.0.0",
false,
join("v1 is officially done. We will be doing", a("semantic versioning").withHref("http://semver.org/").withTarget("_blank"), "from now on."),
join("All tag methods (", code("div()"), ",", code("p()"), "etc ) can now accept an arbitrary number of", code("DomContent"), "as arguments, eliminating the need for", code("with()"), "in most cases."),
join("Added a shorthand-attribute overloads to all TagCreator methods:", br(), code("div(attrs(\"#id.class\")"), "becomes", code("<div id=\"id\" class=\"class\"></div>")),
@@ -96,7 +134,9 @@ public class NewsView {
),
newsPost(
"j2html 0.99 released! (Apr 2017)", "0.99",
"j2html 0.99 released! (Apr 2017)",
"0.99",
false,
join("Added generic", code("iff()"), "/", code("iffElse()"), "methods for performing if's in method calls."),
join("Added", code("withClasses()"), "to add multiple classes to element. Works well with", code("iff()"), "."),
join("HTML-escaping is now a lot faster (and a lot faster than StringUtils)"),
@@ -104,7 +144,9 @@ public class NewsView {
),
newsPost(
"j2html 0.88 released! (Jan 2017)", "0.88",
"j2html 0.88 released! (Jan 2017)",
"0.88",
false,
text("Closure and StringUtils dependencies removed in favor of custom implementations. Most users seem interested in a very lightweight library."),
code("unsafeHtml()"), text(" is now "), code("rawHtml()"),
text("Added "), code(".equals()"), text(" to Tag-class. Two Tags are equal if they render the same HTML")

View File

@@ -2,8 +2,12 @@ TagCreator.class // Static utility class for creating all tags
import static j2html.TagCreator.*; // Use static star import
Config.class // Static config class which holds all configuration
Config.closeEmptyTags = true // all options are public and static, assign using '=' operator
Config.class // Holds all configuration. Offers global configuration or customizable instances
Config.closeEmptyTags = true // Global options are public, static and mutable.
Config.global() // Copy all static Config fields into an instance. Instances are immutable
Config.defaults() // A Config with defaults that are independent of global options
Config.global().withEmptyTagsClosed(true) // A Config that is different from the global options
Config.defaults().withEmptyTagsClosed(true) // A Config that is different from the default options
TagCreator.join() // Method for joining small snippets, like:
@@ -21,3 +25,14 @@ div().withClasses("menu-element", iffElse(isActive, "active", "not-active"))
Tag.class // Is extended by ContainerTag (ex <div></div> and EmptyTag (ex <br>)
Tag.attr(String attribute, Object value) // Set an attribute on the tag
Tag.withXyz(String value) // Calls attr with predefined attribute (ex .withId, .withClass, etc.)
Tag.render(HtmlBuilder builder) // Render HTML using the given builder.
Tag.render() // Shortcut for rendering flat HTML into a string using global Config.
ContainerTag.renderFormatted() // Shortcut for rendering indented HTML into a string using global Config.
HtmlBuilder.class // Interface for composing HTML. Implemented by FlatHtml and IndentedHtml
FlatHtml.into(Appendable) // Render into a stream, file, etc. without indentation or line breaks
FlatHtml.into(Appendable appendable, Config config) // Customize rendering of flat html
IndentedHtml.into(Appendable) // Render human-readable HTML into an stream, file, etc.
IndentedHtml.into(Appendable appendable, Config config) // Customize rendering of intended html
ul(li("one"), li("two")).render(IndentedHtml.inMemory()).toString() // Similar to renderFormatted()
ul(li("one"), li("two")).render(IndentedHtml.into(filewriter)) // Write HTML into a file

View File

@@ -1,5 +1,5 @@
<dependency>
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>1.4.0</version>
<version>1.6.0</version>
</dependency>