79 Commits

Author SHA1 Message Date
Scott Embler
55abbec0ba Adding extension project to support MathML syntax through j2html. (#223)
- Based on PR #220 by matthew-mccall.
2022-12-26 15:36:54 -05:00
Scott Embler
94c82398ad Maven multi module support (#222)
* Restructuring to a Maven multi-module project.

- Renamed directories to match artifact ids.
- Added parent POM.
- Added developers to parent POM.
- Added dependency management and plugin management.  No expectation that the build or release process is fully functional.
- Extracted versions into properties like other projects.

* Fixed file paths for workflow and documentation.

- Added junit to j2html-codegen module.

* Temporarily setting packaging for j2html-codegen to pom, to allow workflow to complete.

* Removed copied configuration of maven-jar-plugin from parent POM.

* Integrating code generation into the main build process.

- j2html-codegen is now supplying a Maven plugin that can read a model file and generate corresponding attribute and tag classes as part of the project build.
- j2html classes that would conflict with the generated classes have been removed.

* Targeting LTS versions only.

- JDK 9 & 10 support ended  in 2018
2022-12-26 09:51:00 -05: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
David
26ba4c3cae [maven-release-plugin] prepare release j2html-1.5.0 2021-06-13 23:02:14 +02:00
David
dc676b767f bump release plugin 2021-06-13 23:00:16 +02:00
David
07dcbb3ec2 specify pom location for release plugin 2021-06-13 22:11:29 +02:00
Scott Embler
b18459c66f Bifunction each (#191)
Adding TagCreator.each() method to provide access to the collection index.
2021-06-13 16:03:04 -04:00
David
aa8fb7c763 format java code in website project 2021-06-12 22:55:23 +02:00
David
c4f2429ec1 website readme 2021-06-12 22:45:00 +02:00
Scott Embler
a5aaeff22f Merge pull request #190 from obecker/remove-generated
Change package for generated tags
2021-06-12 16:30:49 -04:00
David
f4a179160c move website into repo 2021-06-12 22:19:38 +02:00
Oliver Becker
787035f6b1 Change package for generated tags
- move all generated tag classes from j2html.tags.specialized.generated to j2html.tags.specialized
  since they are now only generated tags and in the long run there should be no difference for a user of j2html whether the classes have been generated or not
2021-06-12 22:13:52 +02:00
Scott Embler
8b08968a6b Merge pull request #187 from obecker/htmltag
Remove manual tags, use only generated tags
2021-06-12 15:14:01 -04:00
Oliver Becker
56f61e421f Remove manual tags, use only generated tags
- change html, head, body from manual to generated tags
- restore compatibility with j2html 1.4.0
- fix https://github.com/tipsy/j2html/issues/185
2021-06-12 20:49:02 +02:00
Scott Embler
afd00597f7 Adding Automatic-Module-Name to manifest to support JDK 9+. (#183)
* Adding Automatic-Module-Name to manifest to support JDK 9+.

- Using reverse-DNS naming convention for module.
2021-06-08 12:23:20 -04:00
Scott Embler
8cc0b7c2c7 Merge pull request #184 from obecker/contributing-fix
Adjust CONTRIBUTING.md to the latest code changes
2021-06-07 18:25:58 -04:00
Oliver Becker
89f0194be0 Adjust CONTRIBUTING.md to the latest code changes
IInstance.get() has been renamed to self() in https://github.com/tipsy/j2html/pull/180
2021-06-07 17:43:35 +02:00
Scott Embler
1079a01750 Merge pull request #182 from obecker/each-fix
Remove calls to render() during tree construction.
2021-06-05 13:42:53 -04:00
Oliver Becker
f89957eeab Fix implementation flaw that called render() during tree construction
* Enables the new render capabilities also for content that was constructed with
  - TagCreator.each(Map, Function) and
  - TagCreator.each(Map, BiFunction)
* Adds convenience method TagCreator.each(DomContent...)
2021-06-04 15:57:16 +02:00
Scott Embler
c07facd490 Merge pull request #181 from sembler/fix_style_and_script_escaping
Treating text as unescaped when using TagCreator methods for style and script elements.
2021-06-03 10:24:45 -04:00
Scott.Embler
1d43a25457 Treating text as unescaped when using TagCreator methods for style and script elements.
- Replicating PR #152, but avoiding conflicts and using more current conventions.
2021-06-02 13:32:37 -04:00
sagesmith-wf
2ecd734239 Make join method work as expected (#168)
- Avoids appending a delimiter to the end of the joined text.
- Add test to verify changes.
2021-06-02 10:11:44 -04:00
Scott Embler
8b2669cab1 Merge pull request #180 from obecker/polish-code
Minor code improvements
2021-05-21 19:39:02 -04:00
Oliver Becker
8c0d03e200 Minor code improvements
* add compiler flag -Xlint:all, resolve all warnings about unchecked or raw generics
* rename IInstance.get() to IInstance.self() because it better reflects its intention
* move MainTag from manual to generated tags
* remove unnecessary type casts in TagCreator
2021-05-21 12:36:51 +02:00
Scott Embler
15702ac0d7 Extracted rendering functionality into implementations of HtmlBuilder. (#179)
* Extracted rendering functionality into implementations of HtmlBuilder.

- Two HtmlBuilders are implemented, FlatHtml and IndentedHtml.  Each offers the equivalent output as ContainerTag.render() and ContainerTag.renderFormatted.
- Existing implementations of DomContent/Renderable were updated to support HtmlBuilder where possible.
- Attribute was altered to support the use of TagBuilder.
- Config was altered to allow instances to be created, which can be passed into factory methods for the HtmlBuilders. Config.defaults() can be used for library defaults, while Config.global() can be used for current static configuration.
- Retained compatibility with previous library version as much as possible. See RenderingCompatabilityTest.

* Fixing unchecked or unsafe operations

* Removing unnecessary @param from javadoc.

* Improving casting of HtmlBuilders in implementations of DomContent (tags, text, etc.).

* Preventing NPEs when text is null.

Co-authored-by: Oliver Becker <ob@obqo.de>
2021-05-20 10:47:06 -04:00
Scott Embler
fd41cccfe2 Merge pull request #176 from obecker/intellij
Add .idea folder to .gitignore for developers using IntelliJ IDEA
2021-04-25 10:11:57 -04:00
Oliver Becker
31a565235e Add .idea folder to .gitignore for developers using IntelliJ IDEA 2021-04-25 10:32:23 +02:00
Oliver Becker
da76ee0d2b Fix indent of tags without tag name when using renderFormatted() (#174)
https://github.com/tipsy/j2html/issues/173

Off topic: Set execute flag on mvnw
2021-04-24 21:52:50 +02:00
Scott Embler
4a4c6314d8 Minor restructuring of Tag internals, increased argument validation, and tests to clarify current behavior. (#175)
Co-authored-by: Scott.Embler <scott.embler@noaa.gov>
2021-04-24 11:37:00 +02:00
pointbazaar
be626b6b96 completely separate the generated interfaces from manual ones and completely separate the manual tags from the generated ones. Also wipe the directories for code generation before code generation (#167) 2020-10-20 10:52:38 +02:00
dependabot[bot]
226cfb9a28 Bump junit from 4.11 to 4.13.1 in /code_gen (#166)
Bumps [junit](https://github.com/junit-team/junit4) from 4.11 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.11.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.11...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-17 10:25:29 +02:00
pointbazaar
acf6bff72a Split j2html in 2 projects, "code_gen" and "library" (Issue #161) (#165)
* split the project in 2, to make it easier to iterate on the code generation without breaking the build. This could also help reduce the compiled size of the library.

* update CONTRIBUTING.md

* try to fix github workflow
2020-10-17 10:23:57 +02:00
dependabot[bot]
07427c1433 Bump junit from 4.12 to 4.13.1 (#164)
Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-13 15:10:36 +02:00
lambdaupb
7d98d4f2bd Escape Attribute on render instead of in constructor. Fixes #160 (#162)
Also initialize EscapeUtil StringBuilder with input length to avoid growing copying.
2020-09-24 08:50:01 +02:00
lambdaupb
94ad6e29a3 Use one Appendable instance that is passed down for all rendering (#158) 2020-09-23 20:53:32 +02:00
pointbazaar
9338efc7cd Add CONTRIBUTING.md (depends on PR156) (#157)
* start on CONTRIBUTING.md

* Update CONTRIBUTING.md

* Update CONTRIBUTING.md
2020-08-31 16:24:44 +02:00
pointbazaar
312cabe69c Implementation of Attributes-per-Tag via Interfaces (#156) 2020-08-31 16:24:00 +02:00
pointbazaar
f091000bbe first iteration of the attribute per tag POC (#154)
* create HtmlTag, BodyTag, HeadTag. hopefully without too large diffs this time.

* add some forgotten variants

* remove 'final' on some methods changed during the last 2 commits, to make it more consistent with the rest of the code

* remove <head>,<body>,<html> from TagCreatorCodeGenerator
2020-08-16 10:39:13 +02:00
David
2ae40f9e6c Update README.md 2020-08-15 17:38:58 +02:00
David
dd2079a136 add github ci workflow 2020-08-15 17:35:47 +02:00
David
b0cf8e8c6f add maven wrapper 2020-08-15 17:34:50 +02:00
David
793dcca03c remove travis file 2020-08-15 17:32:54 +02:00
pointbazaar
379ddb82bf Remove test dependency lombok
* lombok was almost never used. also gave me errors on 'mvn install' on ubuntu 20.04 https://github.com/rzwitserloot/lombok/issues/1651 . unless there are plans to use it more extensively, maybe it is best to remove it, to clean up the dependencies and enable me (and maybe others) to build without errors.

* fix Employee.java without lombok
2020-08-15 10:31:25 +02:00
Arne Zelasko
8316035854 add step attribute (#150) 2020-05-19 19:10:21 +02:00
b-gyula
1fcecb961e #145 override toString calling render() allowing inline usage: (#146)
```
String content = "Some content already generated";
content += tr(th("Name"),th("Desc")) + "some other content";
```
2020-02-06 12:28:01 +01:00
Jamie Mansfield
5c9d62cdf3 bug-fix: Children of non-formatted tags (#139)
Resolves GH-138.
2019-09-14 00:29:55 +02:00
David
c31badd3e4 Update .travis.yml 2019-09-13 23:46:39 +02:00
labkey-matthewb
629038303c remove private constructor from Attr (#134) 2019-06-04 20:49:35 +02:00
mbellew
0f30bc9d9a generalize addTo() to enable other attr helpers besides ShortForm (#133)
* small changes to make for easier experimentation

* generalize addTo() to enable other attr helpers besides ShortForm
2019-05-31 23:28:56 +02:00
mbellew
ae65127dcc small changes to make for easier experimentation (#132) 2019-05-31 23:20:10 +02:00
David
dae578a523 Bump version in readme 2019-01-24 14:49:40 +01:00
David
b89435ca7c Cleanup after tests 2019-01-24 14:49:13 +01:00
David
3deb6b4ddb [maven-release-plugin] prepare for next development iteration 2019-01-24 14:43:12 +01:00
David
023922a7db [maven-release-plugin] prepare release j2html-1.4.0 2019-01-24 14:42:55 +01:00
David
00c0461669 Auto format project 2019-01-24 14:24:21 +01:00
Robin Karlsson
cd6e0084ef Add Stream<DomContent> variants of each and with (#118) 2018-05-20 21:45:36 +02:00
Moandji Ezana
764b5d7759 Add TagCreator::each(Map, BiFunction) (#115) 2018-05-07 00:18:36 +02:00
David
d9c4963ff0 bump version 2018-05-01 12:29:02 +02:00
David
3ec15d28a5 woops 2018-05-01 12:28:13 +02:00
Moandji Ezana
f1680464d1 Do not indent textarea contents (#113)
* Do not indent textarea contents.

Fixes #102

* Restore import style

* Handle <pre> and TagCreator::each
2018-05-01 02:06:45 +02:00
Matthias
f497b5c8b5 support osgi meta data (#112) 2018-04-17 23:54:31 +02:00
Matthias
f212895eb2 Let each() take Map in addition to Collection (#110)
* add support for maps for 'each' method.

* add missing imports.
2018-04-16 20:11:37 +02:00
Rupert Madden-Abbott
62cafb9b31 Allow nulls in join() (fix #109)
* Discard nulls in join.

* Add test.
2018-04-16 20:07:11 +02:00
David
af7c986dd6 reorder asserts in each/fitler test 2018-02-05 01:36:46 +01:00
David
3aaab8b0fe unignore tests 2018-02-05 01:34:45 +01:00
David
cf9558b6ab fix formatting 2018-02-05 01:14:29 +01:00
Paul N. Baker
f87f9d8647 Overloaded method of iff for java8 Optional<>'s. (Includes test) (#103)
Using an optional as a condition is really tricky. Even if the optional is not present, the right value of ifValue must be evaluated eagerly in order to be passed to the method. Passing a lambda function for ifFunction allows the value to be evaluated lazily. This avoids the need to manually map and "elseGet" a null value to preserve current functionality.
2018-01-17 22:10:21 +01:00
194 changed files with 139748 additions and 3676 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
text eol=crlf

28
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Test all JDKs on all OSes
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
java_version: [1.8, 11, 17]
os: [windows-latest, macOS-latest, ubuntu-latest]
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Set up JDK ${{ matrix.java_version }}
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java_version }}
- name: Make Maven Wrapper executable
if: contains(matrix.os, 'win') == false
run: chmod +x ./mvnw
- name: Build with Maven
run: ./mvnw verify --file pom.xml --batch-mode
env:
MAVEN_OPTS: -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn

94
.gitignore vendored
View File

@@ -1,101 +1,15 @@
# Created by https://www.gitignore.io
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Java ###
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
### Eclipse ###
.classpath
.project
.settings/
buildNumber.properties
### IntelliJ ###
*.iml
.idea/

117
.mvn/wrapper/MavenWrapperDownloader.java vendored Normal file
View File

@@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* 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.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

2
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

View File

@@ -1,8 +0,0 @@
language: java
jdk:
- oraclejdk8
sudo: false
addons:
apt:
packages:
- oracle-java8-installer

153
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,153 @@
# Contributing
This Project is currently accepting Contributions.
Have a look at the Issues for Inspiration.
## Requirements
- JDK >= 1.8 (see pom.xml)
- Maven
## Project Goals
As stated in the **README**, the Goal of this Project is to enjoy typesafe, performant HTML generation in Java.
For preventing performance regressions, there are Tests.
## Coding Style
There is a `.editorconfig` and a `eclipse_formatting_profile.xml`
What sticks out is that end of line is CRLF `'\r\n'` for this Project.
This means that if you're on Linux `'\n'`, you have to configure git to handle this
correctly so that you have the correct EOL in your working directory,
and the EOL is also correct in the repository itself.
For this purpose, j2html has a `.gitattributes` file.
[Guide to configuring EOL with git](https://docs.github.com/en/github/using-git/configuring-git-to-handle-line-endings)
If you are on Windows, there should be no Problems.
### Reformatting of generated Java Code
As this Projects makes use of Code-Generation techniques in order to generate a more typesafe API without too much manual Work,
there is the `j2html-codegen/` directory which contains everything needed to generate the code.
For simplicity (and also to avoid extra dependencies), they do not format the code correctly.
So if you change the Code-Generation Code, you may need to reformat the generated files to fit the Coding Style.
## Contribution Workflow
The workflow (most of the time) consists of:
- Comment (on Issue or PR) to find out what needs Work
- Get Feedback on your Ideas
- Fork this repo
- Open a PR
- Adjust the PR until it is merged or discarded
## Project Architecture
### j2html/src/main/java/j2html/TagCreator.java
This is **the** central class in J2HTML. It provides the methods
for users of J2HTML to generate all HTML Tags.
It contains methods like
```
public static HtmlTag html(DomContent... dc) {
return new HtmlTag().with(dc);
}
```
which can be used in Projects using this dependency as
```
html(
head(
script("https://example.com/my/js/files.js")
),
body(
div(
h1("Hello World")
).withClasses("container")
)
)
```
### How are the different HTML Tags implemented?
Each HTML Tag has it's own class, which makes it possible for each Tag to have
the correct Attributes and Methods to set those Attributes.
The classes are located in `j2html/src/main/java/j2html/tags/specialized` and follow the naming convention `tag_name + 'Tag.java'`, e.g. `BodyTag.java`.
Notice that the first letter of the Tag is in uppercase.
Each Tag-specific class `implements` interfaces which correspond to the Attributes that can be set on these Tags.
For Reference which Tags support which Attributes, see [HTML Attribute Reference](https://www.w3schools.com/tags/ref_attributes.asp).
For Example, `ButtonTag` might implement `IType<ButtonTag>` which says it can have an Attribute `type`, which may later show up like `<button type="submit"></button>`.
### How are the Attributes of HTML Tags implemented?
Each Attribute has it's own interface in `src/main/java/j2html/tags/attributes/` and follows the naming convention `"I" + attribute_name + '.java'`, e.g. `IAccept.java`. Notice that the first letter of the Attribute is in uppercase.
Dissecting `IAccept.java`:
```
public interface IAccept<T extends Tag> extends IInstance<T> {
default T withAccept(final String accept_) {
return self().attr("accept", accept_);
}
default T withCondAccept(final boolean enable, final String accept_) {
if (enable) {
self().attr("accept", accept_);
}
return self();
}
}
```
As you can see, **IAccept** extends `IInstance<T>` which provides only the `self()` Method to access an instance of type `T`.
All attribute-specific interfaces extend `IInstance<T>`.
```
public interface IInstance<T> {
default T self() { return (T) this; }
}
```
`IInstance<T>` is cheating the type system because `self()` returns an instance of type `T`, but the implementing class
technically does not have to supply it's own type as the type argument. But by convention, in this Project, the implementing class
always supplies it's own type as the type argument.
But in `default` methods in interfaces there is AFAIK no way to obtain the type of the class that is implementing the interface.
If you find a way, that would be a great PR.
### Special classes/interfaces besides TagCreator.java
There are 3 classes which contain code-generating methods in `j2html-codegen/src/main/java/j2html_codegen/generators/`:
- `AttributeInterfaceCodeGenerator.java` (generating the interfaces for the attributes)
- `SpecializedTagClassCodeGenerator.java` (generating the classes for the tags)
- `TagCreatorCodeGenerator.java` (generating some contents of `TagCreator.java`)
### Other special classes / interfaces in J2HTML
- **Tag.java** is the base class for every tag and extends DomContent
- **EmptyTag.java** is the base class for all Tags which have no contents
- **ContainerTag.java** is the base class for all Tags which can contain other tags
- **DomContent.java**
### How is the Code generation for the Attribute specific Interfaces parameterized?
Attributes differ in their 'type' . Some of them can be set with numbers (which are converted into strings in the html).
Others can only be set or not set, others still have 3 states: set, unset, and not present.
To model these propertise, a single Attribute can be described by an instance of **AttrD.java**.
`j2html/src/main/java/j2html/tags/generators/AttributesList.java` contains the different Attributes, their properties,
and the Tags they can be set on. It is the starting point for adding new Attributes and customizing their properties.

View File

@@ -1,4 +1,4 @@
![](https://img.shields.io/travis/tipsy/j2html.svg)
[![Workflow](https://github.com/tipsy/j2html/workflows/Test%20all%20JDKs%20on%20all%20OSes/badge.svg)](https://github.com/tipsy/j2html/actions)
![](https://img.shields.io/github/license/tipsy/j2html.svg)
![](https://img.shields.io/maven-central/v/com.j2html/j2html.svg)
@@ -13,12 +13,12 @@ The project webpage is [j2html.com](http://j2html.com).
<dependency>
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>1.2.2</version>
<version>1.6.0</version>
</dependency>
```
### Or the gradle dependency
```
compile 'com.j2html:j2html:1.2.2'
compile 'com.j2html:j2html:1.6.0'
```
### Import TagCreator and start building HTML

19
docs/404.html Normal file

File diff suppressed because one or more lines are too long

1
docs/CNAME Normal file
View File

@@ -0,0 +1 @@
j2html.com

3
docs/README Normal file
View File

@@ -0,0 +1,3 @@
# j2html.com
Don't edit these files manually, they are generated from `j2html/website`.

121
docs/css/prism.css Normal file
View File

@@ -0,0 +1,121 @@
/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript+java */
/**
* okaidia theme for JavaScript, CSS and HTML
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
* @author ocodia
*/
code[class*="language-"],
pre[class*="language-"] {
color: #f8f8f2;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #272822;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #f8f8f2;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #f92672;
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #f8f8f2;
}
.token.atrule,
.token.attr-value,
.token.function {
color: #e6db74;
}
.token.keyword {
color: #66d9ef;
}
.token.regex,
.token.important {
color: #fd971f;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

24
docs/download.html Normal file

File diff suppressed because one or more lines are too long

5
docs/download_html.bat Normal file
View File

@@ -0,0 +1,5 @@
curl "http://localhost:8888/" > index.html
curl "http://localhost:8888/download.html" > download.html
curl "http://localhost:8888/examples.html" > examples.html
curl "http://localhost:8888/news.html" > news.html
curl "http://localhost:8888/404.html" > 404.html

426
docs/examples.html Normal file

File diff suppressed because one or more lines are too long

32
docs/img/favicon.svg Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="512px" height="512px" viewBox="0 0 512 512" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
<desc>Created with Lunacy</desc>
<defs>
<path d="M0 0L512 0L512 512L0 512L0 0Z" id="path_1" />
<rect width="512" height="512" id="artboard_1" />
<clipPath id="clip_1">
<use xlink:href="#artboard_1" clip-rule="evenodd" />
</clipPath>
<clipPath id="mask_1">
<use xlink:href="#path_1" />
</clipPath>
</defs>
<g id="Artboard-Copy-3" clip-path="url(#clip_1)">
<g id="HTML5_Badge">
<path d="M0 0L512 0L512 512L0 512L0 0Z" id="Background" fill="none" stroke="none" />
<g clip-path="url(#mask_1)">
<path d="M41 460L0 0L451 0L410 460L225 512" transform="translate(30 0)" id="Shape" fill="#1272BF" stroke="none" />
<path d="M0 435L149 394L184 0L0 0" transform="translate(256 37)" id="Shape" fill="#FFFFFF" fill-opacity="0.21176471" stroke="none" />
</g>
</g>
<g id="coffee-icon-12" transform="translate(122.26813 68.45154)">
<g id="Group">
<path d="M109.76 63.5233C72.71 61.7926 37.5831 55.1903 18.2249 46.4086C5.14845 40.4472 -1.64617 32.627 0.340938 25.8965C3.22545 16.2815 18.7377 7.94844 45.8521 1.41021L51.7493 0L56.2363 4.23062C58.7362 6.53823 61.8771 9.16634 63.2232 10.1278C64.6975 11.0893 65.9154 12.4355 66.1718 13.4611C66.3641 14.4226 67.582 16.7302 68.864 18.5891C74.1202 26.2811 84.5686 31.1527 101.812 33.8449C112.452 35.5756 153.028 35.5756 164.117 33.909C181.424 31.2809 192.193 26.3452 197.449 18.5891C198.731 16.7302 199.949 14.4226 200.141 13.4611C200.398 12.4996 201.551 11.0893 202.897 10.1919C204.244 9.35865 207.384 6.66643 209.884 4.35882L214.564 1.95618e-05L220.461 1.41023C251.742 8.78175 269.177 19.9993 265.972 30.7681C264.818 34.55 261.677 38.2678 256.87 41.4728C233.025 57.3056 171.937 66.4719 109.76 63.5233L109.76 63.5233Z" transform="translate(0 276.47668)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M83.9789 173.473C64.877 172.256 51.8006 168.217 47.9546 162.32C47.3777 161.423 46.7367 159.435 46.6084 157.897C46.2879 155.397 45.9033 154.82 43.2752 152.961C38.5959 149.564 29.8142 140.334 25.0708 133.731C20.0069 126.68 12.1225 111.04 9.30215 102.45C3.14853 83.9255 0.199922 64.5031 0.00761933 41.8116C-0.0564799 29.1198 0.199922 27.8378 4.11003 22.838C6.73814 19.633 14.1738 14.6332 20.5197 12.0051C54.3005 -2.09699 127.952 -4.14819 171.732 7.83854C187.565 12.1974 198.334 18.6074 202.436 26.1712C204.038 29.0557 204.359 30.4659 204.615 35.8503L204.622 35.9837C204.936 41.9426 204.946 42.1293 206.282 41.7475C206.987 41.4911 210.833 41.2988 214.679 41.2988C225.384 41.3629 232.371 43.991 238.012 50.1447C250.96 64.2467 246.729 91.297 228.525 110.271C217.371 121.873 203.333 129.116 186.924 131.744L180.193 132.834L177.95 135.718C172.822 142.128 167.373 147.769 162.886 151.423C158.591 154.884 158.079 155.525 157.822 157.961C157.374 161.358 156.092 163.41 152.694 165.846C143.72 172.32 115.452 175.461 83.9789 173.473ZM179.103 35.2734C179.103 41.1706 159.104 46.9396 130.708 49.119C121.285 49.8241 97.4399 50.401 89.6838 50.0805C57.2491 48.6703 33.0833 44.1192 26.7374 38.1579C23.5965 35.2093 25.4554 31.9402 32.0577 28.9274C40.2625 25.1455 55.0056 22.0687 74.2997 20.2098C85.1967 19.1842 119.362 19.1842 130.067 20.2739C160.322 23.2225 179.103 28.9915 179.103 35.2734ZM48.5956 75.2078C49.9416 100.591 56.0312 124.565 65.3898 141.295C66.6077 143.539 67.5051 145.333 67.3128 145.333C66.159 145.333 50.9673 133.09 47.6981 129.501C36.737 117.514 28.8527 101.168 24.3657 81.2332C22.9555 74.695 20.5838 59.4391 20.9684 58.9904C21.0966 58.9263 23.8529 59.8878 27.1861 61.1698C30.5193 62.3877 36.4165 64.1185 40.3266 65.0159C44.1726 65.9133 47.5058 66.6824 47.7622 66.8107C47.9545 67.003 48.3391 70.7208 48.5956 75.2078ZM201.731 105.399C221.41 95.2071 231.089 77.4513 222.884 66.5543C220.256 63.0929 216.474 61.8109 210.064 62.1314C207.115 62.2596 204.423 62.7083 204.039 63.0929C203.654 63.4775 202.757 67.5158 202.116 72.0669C200.577 82.7075 198.205 92.9636 195.129 102.13C194.955 102.641 194.789 103.12 194.634 103.57C193.539 106.749 192.961 108.429 193.474 108.889C194.05 109.405 196 108.388 200.133 106.231C200.633 105.97 201.165 105.693 201.731 105.399Z" transform="translate(30.973198 125.50244)" id="Shape" fill="#FFFFFF" fill-rule="evenodd" stroke="none" />
<path d="M1.14148 89.3819C4.64379 86.2444 7.12459 81.9395 8.43796 76.759C9.75133 71.5055 9.6054 66.5439 7.63534 50.4917C4.93565 29.113 10.335 17.4386 27.7007 6.93166C32.2975 4.23196 34.0486 3.28342 39.375 1.09447L41.9288 0L38.6454 2.77266C32.8812 7.66131 29.8896 11.3095 27.3358 16.563C23.5417 24.2243 22.885 30.2074 24.3443 43.633C25.7306 56.7666 25.8036 61.2175 24.4902 66.6169C22.4472 75.2997 15.5885 82.961 5.00862 88.3604C-0.536715 91.1331 -0.901539 91.206 1.14148 89.3819L1.14148 89.3819Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 109.50348 7.280884)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M2.2619 64.5395C7.44241 59.1401 8.53688 53.3759 6.63979 41.6286C4.15899 25.8682 5.54532 19.1554 12.9148 12.0048C16.0523 8.86735 19.1168 6.82434 24.6621 4.1976C34.2935 -0.472146 36.8472 -1.20179 32.6882 1.78976C27.9455 5.29208 24.5891 8.94032 22.4002 13.0993C20.5031 16.7476 20.4301 17.1853 20.722 26.3789C21.3057 46.7361 21.2328 49.5817 19.8464 52.1355C16.8549 57.6808 9.77728 63.2991 2.18894 66.1447L0 66.9473L2.26191 64.5395L2.2619 64.5395Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 67.97293 38.71814)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M2.43573 63.7984C7.25141 58.326 7.98106 53.5833 5.86508 40.8875C3.01946 23.5948 6.30288 14.7661 18.1961 7.32363C22.72 4.55097 33.2269 -0.410636 33.7376 0.0271519C33.8836 0.173082 32.2054 1.70534 30.0164 3.38353C25.2737 7.10474 22.2092 11.3367 20.458 16.5902C19.5095 19.6547 19.3635 21.7707 19.7284 27.0971C21.1877 47.3813 21.1877 47.4543 19.4365 51.4674C17.3935 56.356 12.3589 61.0257 5.64618 64.3821C2.80055 65.8414 0.319755 67.0089 0.0278784 67.0089C-0.191007 67.0089 0.903465 65.5496 2.43571 63.7984L2.43573 63.7984Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 160.54498 22.258545)" id="Shape" fill="#FFFFFF" stroke="none" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

33
docs/img/logo.svg Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="1720px" height="512px" viewBox="0 0 1720 512" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
<desc>Created with Lunacy</desc>
<defs>
<path d="M0 0L512 0L512 512L0 512L0 0Z" id="path_1" />
<rect width="1720" height="512" id="artboard_1" />
<clipPath id="clip_1">
<use xlink:href="#artboard_1" clip-rule="evenodd" />
</clipPath>
<clipPath id="mask_1">
<use xlink:href="#path_1" />
</clipPath>
</defs>
<g id="Artboard-Copy-2" clip-path="url(#clip_1)">
<g id="HTML5_Badge">
<path d="M0 0L512 0L512 512L0 512L0 0Z" id="Background" fill="none" stroke="none" />
<g clip-path="url(#mask_1)">
<path d="M41 460L0 0L451 0L410 460L225 512" transform="translate(30 0)" id="Shape" fill="#1272BF" stroke="none" />
<path d="M0 435L149 394L184 0L0 0" transform="translate(256 37)" id="Shape" fill="#FFFFFF" fill-opacity="0.21176471" stroke="none" />
</g>
</g>
<g id="coffee-icon-12" transform="translate(122.26813 68.45154)">
<g id="Group">
<path d="M109.76 63.5233C72.71 61.7926 37.5831 55.1903 18.2249 46.4086C5.14845 40.4472 -1.64617 32.627 0.340938 25.8965C3.22545 16.2815 18.7377 7.94844 45.8521 1.41021L51.7493 0L56.2363 4.23062C58.7362 6.53823 61.8771 9.16634 63.2232 10.1278C64.6975 11.0893 65.9154 12.4355 66.1718 13.4611C66.3641 14.4226 67.582 16.7302 68.864 18.5891C74.1202 26.2811 84.5686 31.1527 101.812 33.8449C112.452 35.5756 153.028 35.5756 164.117 33.909C181.424 31.2809 192.193 26.3452 197.449 18.5891C198.731 16.7302 199.949 14.4226 200.141 13.4611C200.398 12.4996 201.551 11.0893 202.897 10.1919C204.244 9.35865 207.384 6.66643 209.884 4.35882L214.564 1.95618e-05L220.461 1.41023C251.742 8.78175 269.177 19.9993 265.972 30.7681C264.818 34.55 261.677 38.2678 256.87 41.4728C233.025 57.3056 171.937 66.4719 109.76 63.5233L109.76 63.5233Z" transform="translate(0 276.47668)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M83.9789 173.473C64.877 172.256 51.8006 168.217 47.9546 162.32C47.3777 161.423 46.7367 159.435 46.6084 157.897C46.2879 155.397 45.9033 154.82 43.2752 152.961C38.5959 149.564 29.8142 140.334 25.0708 133.731C20.0069 126.68 12.1225 111.04 9.30215 102.45C3.14853 83.9255 0.199922 64.5031 0.00761933 41.8116C-0.0564799 29.1198 0.199922 27.8378 4.11003 22.838C6.73814 19.633 14.1738 14.6332 20.5197 12.0051C54.3005 -2.09699 127.952 -4.14819 171.732 7.83854C187.565 12.1974 198.334 18.6074 202.436 26.1712C204.038 29.0557 204.359 30.4659 204.615 35.8503L204.622 35.9837C204.936 41.9426 204.946 42.1293 206.282 41.7475C206.987 41.4911 210.833 41.2988 214.679 41.2988C225.384 41.3629 232.371 43.991 238.012 50.1447C250.96 64.2467 246.729 91.297 228.525 110.271C217.371 121.873 203.333 129.116 186.924 131.744L180.193 132.834L177.95 135.718C172.822 142.128 167.373 147.769 162.886 151.423C158.591 154.884 158.079 155.525 157.822 157.961C157.374 161.358 156.092 163.41 152.694 165.846C143.72 172.32 115.452 175.461 83.9789 173.473ZM179.103 35.2734C179.103 41.1706 159.104 46.9396 130.708 49.119C121.285 49.8241 97.4399 50.401 89.6838 50.0805C57.2491 48.6703 33.0833 44.1192 26.7374 38.1579C23.5965 35.2093 25.4554 31.9402 32.0577 28.9274C40.2625 25.1455 55.0056 22.0687 74.2997 20.2098C85.1967 19.1842 119.362 19.1842 130.067 20.2739C160.322 23.2225 179.103 28.9915 179.103 35.2734ZM48.5956 75.2078C49.9416 100.591 56.0312 124.565 65.3898 141.295C66.6077 143.539 67.5051 145.333 67.3128 145.333C66.159 145.333 50.9673 133.09 47.6981 129.501C36.737 117.514 28.8527 101.168 24.3657 81.2332C22.9555 74.695 20.5838 59.4391 20.9684 58.9904C21.0966 58.9263 23.8529 59.8878 27.1861 61.1698C30.5193 62.3877 36.4165 64.1185 40.3266 65.0159C44.1726 65.9133 47.5058 66.6824 47.7622 66.8107C47.9545 67.003 48.3391 70.7208 48.5956 75.2078ZM201.731 105.399C221.41 95.2071 231.089 77.4513 222.884 66.5543C220.256 63.0929 216.474 61.8109 210.064 62.1314C207.115 62.2596 204.423 62.7083 204.039 63.0929C203.654 63.4775 202.757 67.5158 202.116 72.0669C200.577 82.7075 198.205 92.9636 195.129 102.13C194.955 102.641 194.789 103.12 194.634 103.57C193.539 106.749 192.961 108.429 193.474 108.889C194.05 109.405 196 108.388 200.133 106.231C200.633 105.97 201.165 105.693 201.731 105.399Z" transform="translate(30.973198 125.50244)" id="Shape" fill="#FFFFFF" fill-rule="evenodd" stroke="none" />
<path d="M1.14148 89.3819C4.64379 86.2444 7.12459 81.9395 8.43796 76.759C9.75133 71.5055 9.6054 66.5439 7.63534 50.4917C4.93565 29.113 10.335 17.4386 27.7007 6.93166C32.2975 4.23196 34.0486 3.28342 39.375 1.09447L41.9288 0L38.6454 2.77266C32.8812 7.66131 29.8896 11.3095 27.3358 16.563C23.5417 24.2243 22.885 30.2074 24.3443 43.633C25.7306 56.7666 25.8036 61.2175 24.4902 66.6169C22.4472 75.2997 15.5885 82.961 5.00862 88.3604C-0.536715 91.1331 -0.901539 91.206 1.14148 89.3819L1.14148 89.3819Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 109.50348 7.280884)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M2.2619 64.5395C7.44241 59.1401 8.53688 53.3759 6.63979 41.6286C4.15899 25.8682 5.54532 19.1554 12.9148 12.0048C16.0523 8.86735 19.1168 6.82434 24.6621 4.1976C34.2935 -0.472146 36.8472 -1.20179 32.6882 1.78976C27.9455 5.29208 24.5891 8.94032 22.4002 13.0993C20.5031 16.7476 20.4301 17.1853 20.722 26.3789C21.3057 46.7361 21.2328 49.5817 19.8464 52.1355C16.8549 57.6808 9.77728 63.2991 2.18894 66.1447L0 66.9473L2.26191 64.5395L2.2619 64.5395Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 67.97293 38.71814)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M2.43573 63.7984C7.25141 58.326 7.98106 53.5833 5.86508 40.8875C3.01946 23.5948 6.30288 14.7661 18.1961 7.32363C22.72 4.55097 33.2269 -0.410636 33.7376 0.0271519C33.8836 0.173082 32.2054 1.70534 30.0164 3.38353C25.2737 7.10474 22.2092 11.3367 20.458 16.5902C19.5095 19.6547 19.3635 21.7707 19.7284 27.0971C21.1877 47.3813 21.1877 47.4543 19.4365 51.4674C17.3935 56.356 12.3589 61.0257 5.64618 64.3821C2.80055 65.8414 0.319755 67.0089 0.0278784 67.0089C-0.191007 67.0089 0.903465 65.5496 2.43571 63.7984L2.43573 63.7984Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 160.54498 22.258545)" id="Shape" fill="#FFFFFF" stroke="none" />
</g>
</g>
<path d="M364 10.8L376.4 0L438 0L422.8 125.2C428.667 115.067 436.267 106.867 445.6 100.6C454.933 94.3334 465.867 91.2 478.4 91.2Q502.8 91.2 519 106.4C529.8 116.533 533.867 132.267 531.2 153.6L521.2 236L544.4 236L540.8 265.2L528.8 275.2L467.2 275.2L479.6 173.6C481.733 158.133 480.667 147.067 476.4 140.4C472.133 133.733 465.067 130.4 455.2 130.4C443.733 130.4 435.067 134 429.2 141.2Q420.4 152 417.2 169.6L404.4 275.2L354.8 275.2L383.6 39.2L360.4 39.2L364 10.8ZM1052 0L1039.6 10.8L1036 39.2L1060.8 39.2L1032 275.2L1094 275.2L1106 265.2L1109.6 236L1086.4 236L1115.2 0L1052 0ZM79.2 62.8L82.0851 35.1857Q82.1542 34.5246 82.2534 33.8673Q82.3527 33.2101 82.482 32.5581Q82.6113 31.9061 82.7704 31.2607Q82.9294 30.6154 83.1178 29.9779Q83.3063 29.3405 83.5237 28.7124Q83.7411 28.0843 83.9872 27.4668Q84.2331 26.8493 84.5072 26.2437Q84.7812 25.6381 85.0827 25.0458Q85.3842 24.4534 85.7125 23.8754Q86.0408 23.2975 86.3953 22.7352Q86.7497 22.1729 87.1295 21.6274Q87.5094 21.0819 87.9138 20.5545Q88.3183 20.027 88.7464 19.5186Q89.1746 19.0101 89.6256 18.5219Q90.0767 18.0336 90.5496 17.5666Q91.0225 17.0995 91.5163 16.6546Q92.0101 16.2097 92.5238 15.7878Q93.0375 15.366 93.57 14.9682Q94.1025 14.5703 94.6526 14.1973Q95.2028 13.8243 95.7694 13.4768Q96.3361 13.1294 96.9181 12.8083Q97.5 12.4872 98.0961 12.1931Q98.6922 11.899 99.3012 11.6325Q99.9101 11.366 100.531 11.1277Q101.151 10.8894 101.782 10.6798Q102.413 10.4702 103.052 10.2897Q103.692 10.1092 104.339 9.95819Q104.987 9.80719 105.64 9.68601Q106.294 9.56483 106.952 9.47372Q107.611 9.38261 108.273 9.32177Q108.934 9.26092 109.598 9.23047Q110.262 9.20001 110.927 9.20001L132.8 9.20001L129.915 36.8144Q129.846 37.4754 129.747 38.1327Q129.647 38.7899 129.518 39.4419Q129.389 40.0939 129.23 40.7393Q129.071 41.3847 128.882 42.0221Q128.694 42.6595 128.476 43.2876Q128.259 43.9157 128.013 44.5332Q127.767 45.1507 127.493 45.7563Q127.219 46.3619 126.917 46.9543Q126.616 47.5466 126.288 48.1246Q125.959 48.7025 125.605 49.2648Q125.25 49.8271 124.87 50.3726Q124.491 50.9181 124.086 51.4456Q123.682 51.9731 123.254 52.4815Q122.825 52.9899 122.374 53.4781Q121.923 53.9664 121.45 54.4334Q120.978 54.9005 120.484 55.3454Q119.99 55.7904 119.476 56.2122Q118.963 56.634 118.43 57.0319Q117.898 57.4297 117.347 57.8027Q116.797 58.1758 116.231 58.5232Q115.664 58.8706 115.082 59.1917Q114.5 59.5128 113.904 59.8069Q113.308 60.101 112.699 60.3675Q112.09 60.634 111.469 60.8723Q110.849 61.1106 110.218 61.3202Q109.587 61.5298 108.948 61.7103Q108.308 61.8908 107.661 62.0418Q107.013 62.1928 106.36 62.314Q105.706 62.4352 105.048 62.5263Q104.389 62.6174 103.727 62.6782Q103.066 62.7391 102.402 62.7695Q101.738 62.8 101.073 62.8L79.2 62.8ZM578.8 94.8L565.2 107.2L561.6 134L585.6 134L574.8 221.2C572.667 239.867 576.867 254.2 587.4 264.2C597.933 274.2 612.8 279.2 632 279.2C649.867 279.2 663.733 274.4 673.6 264.8C683.467 255.2 690.933 240 696 219.2L661.2 207.6Q656 228.4 649.8 234.2C645.667 238.067 641.467 240 637.2 240Q632.752 240 629.894 237.868Q623.241 232.907 625.2 216.4L635.2 134L676.8 134L690.4 121.2L694 94.8L640 94.8L646.8 40.0001L610.8 40.0001L595.2 56L590.4 94.8L578.8 94.8ZM158.4 139.6L198.4 144L207.6 114.4C211.867 112.267 217.2 110.733 223.6 109.8C230 108.867 235.467 108.4 240 108.4C248.8 108.4 256.333 110 262.6 113.2C268.867 116.4 272 122 272 130C272 140.933 265.933 151.733 253.8 162.4Q235.6 178.4 206.2 195.4C186.6 206.733 165.467 219.2 142.8 232.8L143.2 275.2L321.2 275.2L330 204.4L288.8 200.4L284.4 229.6L206.8 229.6C225.2 220.267 243.867 210.667 262.8 200.8C281.733 190.933 297.6 179.4 310.4 166.2Q329.6 146.4 329.6 118.4Q329.6 96 313.2 79.4C302.267 68.3333 282.533 62.8 254 62.8Q240 62.8 218.4 65.8Q196.8 68.8 173.6 78.4L158.4 139.6ZM724 94.8L779.6 94.8L781.2 129.6C787.333 118.133 795.2 108.867 804.8 101.8C814.4 94.7334 826 91.2 839.6 91.2C852.667 91.2 863.6 94.4667 872.4 101C881.2 107.533 886.533 117.6 888.4 131.2Q897.6 113.6 912.2 102.4C921.933 94.9333 933.733 91.2 947.6 91.2C963.867 91.2 976.733 96.2667 986.2 106.4C995.667 116.533 999.067 132.267 996.4 153.6L986.4 236L1009.6 236L1006 265.2L994 275.2L932.4 275.2L944.8 173.6C946.667 158.133 945.733 147.067 942 140.4C938.267 133.733 931.467 130.4 921.6 130.4Q903.6 130.4 896 142.4C890.933 150.4 887.6 160.667 886 173.2L873.6 275.2L824.4 275.2L836.8 173.6C838.667 158.133 837.733 147.067 834 140.4C830.267 133.733 823.467 130.4 813.6 130.4Q795.6 130.4 788 142.4C782.933 150.4 779.6 160.667 778 173.2L765.6 275.2L716 275.2L733.2 134L708 134L711.6 105.6L724 94.8ZM57.2 94.8L44.8 105.6L41.2 134L70.4 134L54 268.8Q48.8 312 35.4 319.2C26.4667 324 15.3333 323.867 2 318.8L0 352Q17.2 364 40 364C58.9333 364 73.3333 354.933 83.2 336.8C93.0667 318.667 99.7333 296 103.2 268.8L124.8 94.8L57.2 94.8Z" transform="translate(592 103.83917)" id="j2html" fill="#222222" fill-rule="evenodd" stroke="none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

33
docs/index.html Normal file

File diff suppressed because one or more lines are too long

7
docs/js/prism.js Normal file
View File

@@ -0,0 +1,7 @@
/* 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));;
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/("|')(\\\n|\\?.)*?\1/,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":{pattern:/[a-z0-9_]+\(/i,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|~|\^|%/,ignore:/&(lt|gt|amp);/i,punctuation:/[{}[\];(),.:]/};;
Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|-?Infinity)\b/,"function":/(?!\d)[a-z0-9_$]+(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/<script[\w\W]*?>[\w\W]*?<\/script>/i,inside:{tag:{pattern:/<script[\w\W]*?>|<\/script>/i,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript},alias:"language-javascript"}});;
Prism.languages.java=Prism.languages.extend("clike",{keyword:/\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/,number:/\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+[e]?[\d]*[df]\b|\b\d*\.?\d+\b/i,operator:{pattern:/(^|[^\.])(?:\+=|\+\+?|-=|--?|!=?|<{1,2}=?|>{1,3}=?|==?|&=|&&?|\|=|\|\|?|\?|\*=?|\/=?|%=?|\^=?|:|~)/m,lookbehind:!0}});;

19
docs/news.html Normal file

File diff suppressed because one or more lines are too long

101
j2html-codegen/.gitignore vendored Normal file
View File

@@ -0,0 +1,101 @@
# Created by https://www.gitignore.io
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Java ###
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
### Eclipse ###
.classpath
.project
.settings/
buildNumber.properties

86
j2html-codegen/pom.xml Normal file
View File

@@ -0,0 +1,86 @@
<?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>
<parent>
<groupId>com.j2html</groupId>
<artifactId>j2html-parent</artifactId>
<version>1.6.1-SNAPSHOT</version>
</parent>
<name>j2html-codegen</name>
<artifactId>j2html-codegen-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.8.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<!--
Note: The use of version properties below is to avoid
redefining these versions when using this maven plugin
in other projects. Using the more concise dependency-
management strategy for versioning doesn't work when
another project tries to detect this plugin's dependency
versions.
-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>${javapoet.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.4</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -0,0 +1,33 @@
package com.j2html.codegen;
import com.j2html.codegen.generators.SpecializedTagClassCodeGenerator;
import com.j2html.codegen.generators.AttributeInterfaceCodeGenerator;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
public final class App
{
public static void main( String[] args )
{
final Path relPath = Paths.get("../library/src/main/java/j2html/");
final Path absPath = relPath.toAbsolutePath();
System.out.println("writing in "+absPath);
//decide if the files should be
//deleted or generated
final boolean delete = false;
try {
AttributeInterfaceCodeGenerator.generate(absPath, delete);
SpecializedTagClassCodeGenerator.generate(absPath, delete);
//TagCreatorCodeGenerator.print();
} catch (IOException e) {
e.printStackTrace();
}
//don't forget to auto-reformat the generated code.
}
}

View File

@@ -0,0 +1,50 @@
package com.j2html.codegen;
import com.j2html.codegen.model.AttrD;
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.containerTags;
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.emptyTags;
import static com.j2html.codegen.model.AttributesList.attributesDescriptive;
import static com.j2html.codegen.model.AttributesList.getCustomAttributesForHtmlTag;
import static java.lang.System.*;
public class Export {
public static void main(String[] args){
for (final String tag : emptyTags()) {
out.print("EMPTY-ELEMENT[");
out.print(tag);
out.print("]");
out.println();
}
for (final String tag : containerTags()) {
out.print("ELEMENT[");
out.print(tag);
out.print("]");
out.println();
}
out.println();
for(AttrD attr : attributesDescriptive()){
if(attr.hasArgument){
out.print("STRING");
}else{
out.print("BOOLEAN");
}
out.print("[");
out.print(attr.attr);
out.print("]");
out.println();
for(String tag : attr.tags){
out.print("ATTRIBUTE[");
out.print(tag);
out.print(":");
out.print(attr.attr);
out.print("]");
out.println();
}
out.println();
}
}
}

View File

@@ -0,0 +1,199 @@
package com.j2html.codegen;
import com.j2html.codegen.Model.Node;
import com.squareup.javapoet.*;
import javax.lang.model.element.Modifier;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import static com.j2html.codegen.Model.Metadata.ON_OFF;
import static com.j2html.codegen.Model.Metadata.SELF_CLOSING;
public class Generator {
public static final ClassName INSTANCE = ClassName.get("j2html.tags", "IInstance");
public static final ClassName TAG = ClassName.get("j2html.tags", "Tag");
public static final ClassName EMPTY_TAG = ClassName.get("j2html.tags", "EmptyTag");
public static final ClassName CONTAINER_TAG = ClassName.get("j2html.tags", "ContainerTag");
public static void main(String... args) throws IOException {
Path path = Paths.get("j2html-codegen", "src", "test", "resources", "html.model");
String definitions = new String(Files.readAllBytes(path));
Model model = new Model();
Parser.parse(definitions, model);
Path dir = Paths.get("/j2html/generated-source");
Files.createDirectories(dir);
generate(dir, "j2html.tags.attributes", "j2html.tags.specialized", model);
}
public static void generate(Path root, String attributePkg, String elementPkg, Model model) throws IOException {
Map<String, JavaFile> attributes = generateAttributePackage(attributePkg, model);
for (JavaFile file : attributes.values()) {
file.writeTo(root);
}
Map<String, JavaFile> elements = generateElementPackage(elementPkg, model, attributes);
for (JavaFile file : elements.values()) {
file.writeTo(root);
}
}
private static Map<String, JavaFile> generateElementPackage(String pkg, Model model, Map<String, JavaFile> attributes) {
Map<String, JavaFile> files = new HashMap<>();
// Convert all elements into classes.
for (Node element : model.elements()) {
ClassName className = ClassName.get(pkg, capitalize(element.name) + "Tag");
TypeSpec.Builder type = defineElementClass(element, className);
// Assign attributes to this element.
for (Node attribute : element.children) {
JavaFile file = attributes.get(attribute.name);
type.addSuperinterface(
ParameterizedTypeName.get(
ClassName.get(file.packageName, file.typeSpec.name),
className
)
);
}
files.put(
element.name,
JavaFile.builder(pkg, type.build())
.skipJavaLangImports(true)
.build()
);
}
return files;
}
private static Map<String, JavaFile> generateAttributePackage(String pkg, Model model) {
Map<String, JavaFile> files = new HashMap<>();
// Convert all attributes into classes.
for (Node attribute : model.attributes()) {
TypeSpec.Builder type = defineAttributeClass(pkg, attribute);
if (attribute.type.equals(Node.Type.STRING)) {
defineStringAttributeMethods(attribute, type);
} else if (attribute.type.equals(Node.Type.BOOLEAN) && !attribute.is(ON_OFF)) {
defineBooleanAttributeMethods(attribute, type);
} else if (attribute.type.equals(Node.Type.BOOLEAN) && attribute.is(ON_OFF)) {
defineOnOffAttributeMethods(attribute, type);
}
files.put(
attribute.name,
JavaFile.builder(pkg, type.build())
.skipJavaLangImports(true)
.build()
);
}
return files;
}
private static TypeSpec.Builder defineElementClass(Node element, ClassName className) {
MethodSpec constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addStatement("super(\"" + element.name + "\")")
.build();
TypeSpec.Builder type = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC)
.superclass(
ParameterizedTypeName.get(element.is(SELF_CLOSING) ? EMPTY_TAG : CONTAINER_TAG, className)
)
.addMethod(constructor);
return type;
}
private static TypeSpec.Builder defineAttributeClass(String pkg, Node attribute) {
ClassName name = ClassName.get(pkg, "I" + capitalize(attribute.name));
return TypeSpec.interfaceBuilder(name)
.addSuperinterface(ParameterizedTypeName.get(INSTANCE, TypeVariableName.get("T")))
.addTypeVariable(TypeVariableName.get("T", ParameterizedTypeName.get(TAG, TypeVariableName.get("T"))))
.addModifiers(Modifier.PUBLIC);
}
private static void defineBooleanAttributeMethods(Node attribute, TypeSpec.Builder type) {
MethodSpec with = MethodSpec.methodBuilder(methodName("is", attribute.name))
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
.addStatement("return self().attr(\"" + attribute.name + "\")")
.returns(TypeVariableName.get("T"))
.build();
MethodSpec withCond = MethodSpec.methodBuilder(methodName("withCond", attribute.name))
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
.addParameter(TypeName.BOOLEAN, "enable", Modifier.FINAL)
.addStatement("return enable ? self().attr(\"" + attribute.name + "\") : self()")
.returns(TypeVariableName.get("T"))
.build();
type.addMethod(with);
type.addMethod(withCond);
}
private static void defineOnOffAttributeMethods(Node attribute, TypeSpec.Builder type) {
MethodSpec with = MethodSpec.methodBuilder(methodName("is", attribute.name))
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
.addStatement("return self().attr(\"" + attribute.name + "\", \"on\")")
.returns(TypeVariableName.get("T"))
.build();
MethodSpec withCond = MethodSpec.methodBuilder(methodName("withCond", attribute.name))
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
.addParameter(TypeName.BOOLEAN, "enable", Modifier.FINAL)
.addStatement("return enable ? self().attr(\"" + attribute.name + "\", \"on\") : self()")
.returns(TypeVariableName.get("T"))
.build();
type.addMethod(with);
type.addMethod(withCond);
}
private static void defineStringAttributeMethods(Node attribute, TypeSpec.Builder type) {
MethodSpec with = MethodSpec.methodBuilder(methodName("with", attribute.name))
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
.addParameter(String.class, parameter(attribute), Modifier.FINAL)
.addStatement("return self().attr(\"" + attribute.name + "\", " + parameter(attribute) + ")")
.returns(TypeVariableName.get("T"))
.build();
MethodSpec withCond = MethodSpec.methodBuilder(methodName("withCond", attribute.name))
.addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
.addParameter(TypeName.BOOLEAN, "enable", Modifier.FINAL)
.addParameter(String.class, parameter(attribute), Modifier.FINAL)
.addStatement("return enable ? self().attr(\"" + attribute.name + "\", " + parameter(attribute) + ") : self()")
.returns(TypeVariableName.get("T"))
.build();
type.addMethod(with);
type.addMethod(withCond);
}
private static String parameter(Node attribute) {
return attribute.name + "_";
}
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

@@ -0,0 +1,16 @@
package com.j2html.codegen;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public final class GeneratorUtil {
public static final void deleteAllFilesInDir(final Path dir) throws IOException {
for(final File file : dir.toFile().listFiles()){
System.out.println("deleting " + file.toPath());
Files.delete(file.toPath());
}
}
}

View File

@@ -0,0 +1,65 @@
package com.j2html.codegen;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
@Mojo(name = "generate-source-files", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public class J2htmlCodeGeneratorMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", required = true, readonly = true)
MavenProject project;
@Parameter(property = "modelFile", required = true)
String modelFile;
@Parameter(property = "attributePackage", required = true)
String attributePackage;
@Parameter(property = "tagPackage", required = true)
String tagPackage;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().debug("Model File: " + modelFile);
getLog().debug("Attribute Package: " + attributePackage);
getLog().debug("Tag Package: " + tagPackage);
String outputDirectory = project.getBuild().getDirectory() + "/generated-sources/j2html-codegen";
project.addCompileSourceRoot(outputDirectory);
getLog().debug("Generating J2Html sources in: " + outputDirectory);
String definitions;
try {
definitions = new String(Files.readAllBytes(Paths.get(modelFile)));
} catch (IOException e) {
throw new MojoFailureException("Unable to locate model file: " + modelFile, e);
}
Model model = new Model();
try {
Parser.parse(definitions, model);
}catch (RuntimeException e){
throw new MojoFailureException("Unable to parse model file.", e);
}
try {
Generator.generate(
Paths.get(outputDirectory).toAbsolutePath(),
attributePackage,
tagPackage,
model
);
} catch (IOException e) {
throw new MojoFailureException("Failed to generate source files.", e);
}
}
}

View File

@@ -0,0 +1,171 @@
package com.j2html.codegen;
import java.util.*;
import static com.j2html.codegen.Model.Metadata.ON_OFF;
import static com.j2html.codegen.Model.Metadata.SELF_CLOSING;
import static com.j2html.codegen.Model.Node.Type.*;
public class Model implements Parser.Listener {
private Map<String, Node> elements;
private Map<String, Node> attributes;
public Model() {
elements = new LinkedHashMap<>();
attributes = new LinkedHashMap<>();
}
public Collection<Node> elements(){
return elements.values();
}
public Collection<Node> attributes(){
return attributes.values();
}
public Node addElement(String name) {
return add(ELEMENT, name, elements);
}
public Node addBooleanAttribute(String name) {
return add(BOOLEAN, name, attributes);
}
public Node addStringAttribute(String name) {
return add(STRING, name, attributes);
}
public Node element(String name) {
if (!elements.containsKey(name)) {
throw new NodeDoesNotExist(name);
}
return elements.get(name);
}
public Node attribute(String name) {
if (!attributes.containsKey(name)) {
throw new NodeDoesNotExist(name);
}
return attributes.get(name);
}
private Node add(Node.Type type, String name, Map<String, Node> nodes) {
if (nodes.containsKey(name)) {
throw new NodeAlreadyExists(name);
}
Node node = new Node(type, name);
nodes.put(name, node);
return node;
}
@Override
public void lineCommented(int line, String txt) {
// Ignore.
}
@Override
public void elementDefined(int line, String name) {
attempt(() -> addElement(name), line);
}
@Override
public void emptyElementDefined(int line, String name) {
attempt(() -> addElement(name).annotate(SELF_CLOSING), line);
}
@Override
public void booleanDefined(int line, String name) {
attempt(() -> addBooleanAttribute(name), line);
}
@Override
public void onOffDefined(int line, String name) {
attempt(() -> addBooleanAttribute(name).annotate(ON_OFF), line);
}
@Override
public void stringDefined(int line, String name) {
attempt(() -> addStringAttribute(name), line);
}
@Override
public void attributeDefined(int line, String element, String name) {
attempt(() -> element(element).addChild(attribute(name)), line);
}
@Override
public void invalidLine(int line, String txt) {
throw new RuntimeException("Invalid line [" + line + "]: " + txt);
}
@FunctionalInterface
private interface Unsafe {
void call() throws RuntimeException;
}
private void attempt(Unsafe operation, int line) {
try {
operation.call();
} catch (RuntimeException e) {
throw new InvalidModel(e, line);
}
}
public static class Node {
enum Type {
ELEMENT,
BOOLEAN,
STRING
}
public final Type type;
public final String name;
public final List<Metadata> metadata;
public final List<Node> children;
private Node(Type type, String name) {
this.type = type;
this.name = name;
this.metadata = new ArrayList<>();
this.children = new ArrayList<>();
}
public void annotate(Metadata meta) {
metadata.add(meta);
}
public void addChild(Node node) {
children.add(node);
}
public boolean is(Metadata annotation){
return metadata.contains(annotation);
}
}
public enum Metadata {
SELF_CLOSING,
ON_OFF,
OBSOLETE
}
public static class InvalidModel extends RuntimeException {
public InvalidModel(Exception cause, int line) {
super(cause.getMessage() + ". At line " + line, cause);
}
}
public static class NodeAlreadyExists extends RuntimeException {
public NodeAlreadyExists(String name) {
super("Node already exists: " + name);
}
}
public static class NodeDoesNotExist extends RuntimeException {
public NodeDoesNotExist(String name) {
super("Node does not exist: " + name);
}
}
}

View File

@@ -0,0 +1,91 @@
package com.j2html.codegen;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Parser {
private static final Pattern EMPTY_LINE_PATTERN = Pattern.compile("\\s*");
private static final Pattern COMMENT_PATTERN = Pattern.compile("#.*");
private static final Pattern NODE_PATTERN = Pattern.compile("(?<type>ELEMENT|EMPTY-ELEMENT|BOOLEAN|ONOFF|STRING)\\[(?<name>\\S+)\\]");
private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile("ATTRIBUTE\\[(?<element>\\S+):(?<name>\\S+)\\]");
public interface Listener {
void lineCommented(int line, String txt);
void elementDefined(int line, String name);
void emptyElementDefined(int line, String name);
void booleanDefined(int line, String name);
void onOffDefined(int line, String name);
void stringDefined(int line, String name);
void attributeDefined(int line, String element, String name);
void invalidLine(int line, String txt);
}
public static void parse(String txt, Listener listener) {
String[] lines = txt.split("[\r\n]+");
for (int i = 0; i < lines.length; i++) {
int number = i + 1;
String line = lines[i];
if (match(EMPTY_LINE_PATTERN, line)) continue;
if (match(COMMENT_PATTERN, line, matcher -> {
listener.lineCommented(number, line);
})) continue;
if (match(NODE_PATTERN, line, matcher -> {
String type = matcher.group("type");
String name = matcher.group("name");
switch (type) {
case "ELEMENT":
listener.elementDefined(number, name);
break;
case "EMPTY-ELEMENT":
listener.emptyElementDefined(number, name);
break;
case "BOOLEAN":
listener.booleanDefined(number, name);
break;
case "ONOFF":
listener.onOffDefined(number, name);
break;
case "STRING":
listener.stringDefined(number, name);
break;
}
})) continue;
if (match(ATTRIBUTE_PATTERN, line, matcher -> {
listener.attributeDefined(
number,
matcher.group("element"),
matcher.group("name")
);
})) continue;
listener.invalidLine(number, line);
}
}
private static boolean match(Pattern pattern, String txt) {
return pattern.matcher(txt).matches();
}
private static boolean match(Pattern pattern, String txt, Consumer<Matcher> onMatch) {
Matcher matcher = pattern.matcher(txt);
if (matcher.matches()) {
onMatch.accept(matcher);
return true;
}
return false;
}
}

View File

@@ -0,0 +1,185 @@
package com.j2html.codegen.generators;
import com.j2html.codegen.GeneratorUtil;
import com.j2html.codegen.model.AttrD;
import com.j2html.codegen.model.AttributesList;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public final class AttributeInterfaceCodeGenerator {
private static final String relPath = "tags/attributes/";
public static void generate(final Path absPath, final boolean delete) throws IOException {
//delete all files in the directory for fresh generation
final Path dir = Paths.get(absPath.toString(),relPath);
GeneratorUtil.deleteAllFilesInDir(dir);
for (final AttrD attr : AttributesList.attributesDescriptive()) {
final Path path = makePath(attr.attr, absPath);
final String interfaceName = interfaceNameFromAttribute(attr.attr)+"<T extends Tag<T>>";
/*
IFormAction<T extends Tag<T>> extends IInstance<T>
default T withFormAction(String formAction){
return self().attr("formaction", formAction);
}
*/
final String interfaceStr = getInterfaceTemplate(
interfaceName,
Optional.of("IInstance<T>"),
Arrays.asList("j2html.tags.Tag","j2html.tags.IInstance"),
interfaceNameFromAttribute(attr.attr).substring(1),
attr
);
if (!delete) {
System.out.println("writing to "+path);
Files.write(path, interfaceStr.getBytes());
}
}
}
private static String getPackage(){
return "package j2html.tags.attributes;\n";
}
private static String makeReturnTypeAndMethodName(final String name){
return "default "+ "T "+name;
}
private static String getInterfaceTemplate(
final String interfaceName,
final Optional<String> optExtends,
final List<String> imports,
final String interfaceNameSimple,
final AttrD attrD
){
final StringBuilder sb = new StringBuilder();
sb.append(getPackage());
sb.append("\n");
for(String importName : imports){
sb.append("import ").append(importName).append(";\n");
}
sb.append("\n");
sb.append("public interface ")
.append(interfaceName);
optExtends.ifPresent(ext -> sb.append(" extends ").append(ext).append(" "));
sb.append(" {\n");
//interface contents
/*
IFormAction<T extends Tag> extends IInstance<T>
default T withFormAction(String formAction){
return self().attr("formaction", formAction);
}
*/
//IMPORTANT: '_' added as suffix to mitigate problems
//where attributes are java keywords. Just to make it consistent and avoid special cases.
final String attrName = interfaceNameSimple.toLowerCase();
final String paramName = attrName+"_";
//depending on if the attribute has an argument or not,
//generate methods according to the convention in Tag.java
// arg -> with$ATTR(arg), withCond$ATTR(condition, arg)
// no arg -> is$ATTR(), withCond$ATTR(condition)
//append the 'with$ATTR' method
writeAttributeMethod(interfaceNameSimple, attrD, sb, attrName, paramName);
writeAttributeMethodCond(interfaceNameSimple, attrD, sb, attrName, paramName);
sb.append("}\n");
return sb.toString();
}
private static void addAttributeNoArg(final StringBuilder sb, final String attrName){
//generate the code to add an attribute without an argument
//there are some special attributes
//which do take an argument, but where the argument
//is boolean (meaning on/off, yes/no and the like)
sb.append("self().attr(\"");
if (attrName.equals("autocomplete")){
sb.append(attrName).append("\",\"on\"");
} else {
sb.append(attrName).append("\"");
}
sb.append(");\n");
}
private static void writeAttributeMethodCond(String interfaceNameSimple, AttrD attrD, StringBuilder sb, String attrName, String paramName) {
sb.append(makeReturnTypeAndMethodName("withCond"+interfaceNameSimple));
if(attrD.hasArgument){
//add a variant where you can specify the argument
sb.append("(final boolean enable, final String ").append(paramName).append(") {");
sb.append("if (enable){\n");
sb.append("self().attr(\"").append(attrName).append("\", ").append(paramName).append(");\n");
sb.append("}\n");
sb.append("return self();\n");
}else{
//add a variant where you can toggle the attribute
sb.append("(final boolean enable) {");
sb.append("if (enable){\n");
addAttributeNoArg(sb, attrName);
sb.append("}\n");
sb.append("return self();\n");
}
sb.append("}\n");
}
private static void writeAttributeMethod(String interfaceNameSimple, AttrD attrD, StringBuilder sb, String attrName, String paramName) {
sb.append(makeReturnTypeAndMethodName(
((attrD.hasArgument)?"with":"is")+interfaceNameSimple)
);
if(attrD.hasArgument){
//add a variant where you can specify the argument
sb.append("(final String ").append(paramName).append(") {")
.append("return self().attr(\"").append(attrName).append("\", ").append(paramName).append(");\n");
}else{
//add a variant where you can toggle the attribute
sb.append("() {");
addAttributeNoArg(sb, attrName);
sb.append("return self();\n");
}
sb.append("}\n");
}
public static String interfaceNameFromAttribute(String attribute){
String res = attribute.substring(0,1).toUpperCase()+attribute.substring(1);
return "I" + res;
}
private static Path makePath(String tagLowerCase, final Path absPath){
final String filename = interfaceNameFromAttribute(tagLowerCase)+".java";
return Paths.get(absPath.toString(),relPath,filename);
}
}

View File

@@ -0,0 +1,166 @@
package com.j2html.codegen.generators;
import com.j2html.codegen.GeneratorUtil;
import com.j2html.codegen.model.AttributesList;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.containerTags;
import static com.j2html.codegen.generators.TagCreatorCodeGenerator.emptyTags;
public final class SpecializedTagClassCodeGenerator {
private static final String relPath = "tags/specialized";
public static void generate(final Path absPath, final boolean delete) throws IOException {
//delete all files in the directory for fresh generation
final Path dir = Paths.get(absPath.toString(),relPath);
GeneratorUtil.deleteAllFilesInDir(dir);
//the delete argument serves to give the possibility
//to delete the classes that were written before
System.out.println("// EmptyTags, generated in " + SpecializedTagClassCodeGenerator.class);
for (final String tag : emptyTags()) {
final String className = classNameFromTag(tag);
final Path path = makePath(absPath,tag);
final List<String> interfaceNames = getInterfaceNamesForTag(tag);
final String classString =
getClassTemplate(
className,
Optional.of("EmptyTag<"+className+">"),
Arrays.asList(
"j2html.tags.EmptyTag",
"j2html.tags.attributes.*"
),
tag,
interfaceNames
);
/*
public InputTag() {
super("input");
}
*/
if(!delete){
System.out.println("writing to "+path);
Files.write(path, classString.getBytes());
}
}
System.out.println("// ContainerTags, generated in " + SpecializedTagClassCodeGenerator.class);
for (final String tag : containerTags()) {
final Path path = makePath(absPath, tag);
final String className = classNameFromTag(tag);
final List<String> interfaceNames = getInterfaceNamesForTag(tag);
final String classString =
getClassTemplate(
className,
Optional.of("ContainerTag<"+className+">"),
Arrays.asList(
"j2html.tags.ContainerTag",
"j2html.tags.attributes.*"
),
tag,
interfaceNames
);
if(delete){
if(Files.exists(path)) {
System.out.println("deleting " + path);
Files.delete(path);
}
}else {
System.out.println("writing to "+path);
Files.write(path, classString.getBytes());
}
}
}
public static String classNameFromTag(String tageNameLowerCase){
String res = tageNameLowerCase.substring(0,1).toUpperCase()+tageNameLowerCase.substring(1);
return res + "Tag";
}
private static Path makePath(final Path absPath, String tagLowerCase){
final String filename = classNameFromTag(tagLowerCase)+".java";
return Paths.get(absPath.toString(),relPath,filename);
}
private static String getPackage(){
return "package j2html.tags.specialized;\n";
}
private static String getClassTemplate(
final String className,
final Optional<String> optExtends,
final List<String> imports,
final String tag,
final List<String> interfaces
){
final StringBuilder sb = new StringBuilder();
sb.append(getPackage());
sb.append("\n");
for(String importName : imports){
sb.append("import ").append(importName).append(";\n");
}
sb.append("\n");
sb.append("public final class ")
.append(className)
.append(" ");
optExtends.ifPresent(ext -> sb.append("extends ").append(ext).append(" "));
//add the 'implements' clause
if(!interfaces.isEmpty()) {
sb.append("\n");
sb.append("implements ");
final List<String> genericInterfaceNames
= interfaces.stream().map(iName -> iName+"<"+className+">")
.collect(Collectors.toList());
sb.append(
String.join(",", genericInterfaceNames)
);
}
sb.append(" {\n");
//class contents
sb.append("public ")
.append(className)
.append("() {")
.append("super(\"").append(tag).append("\");")
.append("}\n");
sb.append("}\n");
return sb.toString();
}
private static List<String> getInterfaceNamesForTag(final String tagNameLowercase){
return AttributesList.getCustomAttributesForHtmlTag(tagNameLowercase)
.stream()
.map(
AttributeInterfaceCodeGenerator::interfaceNameFromAttribute
).collect(Collectors.toList());
}
}

View File

@@ -1,38 +1,56 @@
package j2html.tags;
package com.j2html.codegen.generators;
import java.util.Arrays;
import java.util.List;
class TagCreatorCodeGenerator {
public final class TagCreatorCodeGenerator {
public static void print() {
public static void main(String[] args) {
System.out.println("// EmptyTags, generated in " + TagCreatorCodeGenerator.class);
for (String tag : emptyTags()) {
String emptyA1 = "public static EmptyTag " + tag + "()";
String emptyA2 = "{ return new EmptyTag(\"" + tag + "\"); }";
final String className = SpecializedTagClassCodeGenerator.classNameFromTag(tag);
final String publicstaticTypeMethod = "public static "+className+" "+tag+" ";
final String castReturn = " return ("+className+") ";
final String construct = " new "+className+"()";
String emptyA1 = publicstaticTypeMethod + "()";
String emptyA2 = "{ return "+construct+"; }";
// Attr shorthands
String emptyB1 = "public static EmptyTag " + tag + "(Attr.ShortForm shortAttr)";
String emptyB2 = "{ return Attr.addTo(new EmptyTag(\"" + tag + "\"), shortAttr); }";
String emptyB1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr)";
String emptyB2 = "{ "+castReturn+" Attr.addTo("+construct+", shortAttr); }";
// Print
System.out.println(String.format("%-80s%1s", emptyA1, emptyA2));
System.out.println(String.format("%-80s%1s", emptyB1, emptyB2));
System.out.println("");
System.out.println();
}
System.out.println("// ContainerTags, generated in " + TagCreatorCodeGenerator.class);
for (String tag : containerTags()) {
String containerA1 = "public static ContainerTag " + tag + "()";
String containerA2 = "{ return new ContainerTag(\"" + tag + "\"); }";
String containerB1 = "public static ContainerTag " + tag + "(String text)";
String containerB2 = "{ return new ContainerTag(\"" + tag + "\").withText(text); }";
String containerC1 = "public static ContainerTag " + tag + "(DomContent... dc)";
String containerC2 = "{ return new ContainerTag(\"" + tag + "\").with(dc); }";
final String className = SpecializedTagClassCodeGenerator.classNameFromTag(tag);
final String publicstaticTypeMethod = "public static "+className+" "+tag+" ";
final String castReturn = " return ("+className+") ";
final String construct = " new "+className+"()";
String containerA1 = publicstaticTypeMethod+ "()";
String containerA2 = "{ "+castReturn + construct + "; }";
String containerB1 = publicstaticTypeMethod + "(String text)";
String containerB2 = "{ "+castReturn + construct + ".withText(text); }";
String containerC1 = publicstaticTypeMethod + "(DomContent... dc)";
String containerC2 = "{ "+castReturn + construct+".with(dc); }";
// Attr shorthands
String containerD1 = "public static ContainerTag " + tag + "(Attr.ShortForm shortAttr)";
String containerD2 = "{ return Attr.addTo(new ContainerTag(\"" + tag + "\"), shortAttr); }";
String containerE1 = "public static ContainerTag " + tag + "(Attr.ShortForm shortAttr, String text)";
String containerE2 = "{ return Attr.addTo(new ContainerTag(\"" + tag + "\").withText(text), shortAttr); }";
String containerF1 = "public static ContainerTag " + tag + "(Attr.ShortForm shortAttr, DomContent... dc)";
String containerF2 = "{ return Attr.addTo(new ContainerTag(\"" + tag + "\").with(dc), shortAttr); }";
String containerD1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr)";
String containerD2 = "{ "+castReturn+" Attr.addTo("+construct+", shortAttr); }";
String containerE1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr, String text)";
String containerE2 = "{ "+castReturn+" Attr.addTo("+construct+".withText(text), shortAttr); }";
String containerF1 = publicstaticTypeMethod + "(Attr.ShortForm shortAttr, DomContent... dc)";
String containerF2 = "{ "+castReturn+" Attr.addTo("+construct+".with(dc), shortAttr); }";
// Print
System.out.println(String.format("%-80s%1s", containerA1, containerA2));
System.out.println(String.format("%-80s%1s", containerB1, containerB2));
@@ -40,12 +58,12 @@ class TagCreatorCodeGenerator {
System.out.println(String.format("%-80s%1s", containerD1, containerD2));
System.out.println(String.format("%-80s%1s", containerE1, containerE2));
System.out.println(String.format("%-80s%1s", containerF1, containerF2));
System.out.println("");
System.out.println();
}
}
// This is a method that contains all ContainerTags, there is nothing below it
private static List<String> emptyTags() {
public static List<String> emptyTags() {
return Arrays.asList(
"area",
"base",
@@ -66,7 +84,7 @@ class TagCreatorCodeGenerator {
);
}
private static List<String> containerTags() {
public static List<String> containerTags() {
return Arrays.asList(
"a",
"abbr",
@@ -85,6 +103,7 @@ class TagCreatorCodeGenerator {
"cite",
"code",
"colgroup",
"data",
"datalist",
"dd",
"del",
@@ -130,6 +149,7 @@ class TagCreatorCodeGenerator {
"option",
"output",
"p",
"picture",
"pre",
"progress",
"q",
@@ -141,6 +161,7 @@ class TagCreatorCodeGenerator {
"script",
"section",
"select",
"slot",
"small",
"span",
"strong",
@@ -151,6 +172,7 @@ class TagCreatorCodeGenerator {
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",

View File

@@ -0,0 +1,94 @@
package com.j2html.codegen.generators;
import com.j2html.codegen.wattsi.AttributeDefinition;
import com.j2html.codegen.wattsi.ElementDefinition;
import com.j2html.codegen.wattsi.WattsiSource;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
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

@@ -0,0 +1,24 @@
package com.j2html.codegen.model;
public final class AttrD {
//attribute descriptor
public final String attr;
public final boolean hasArgument;
//the html tags that this attribute can be used on
public final String[] tags;
public AttrD(final String attr, boolean hasArgument){
this.attr = attr;
this.hasArgument = hasArgument;
this.tags = new String[]{};
}
public AttrD(final String attr, boolean hasArgument, final String... tags) {
this.attr = attr;
this.hasArgument = hasArgument;
this.tags = tags;
}
}

View File

@@ -0,0 +1,182 @@
package com.j2html.codegen.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public final class AttributesList {
//https://www.w3schools.com/tags/ref_attributes.asp
public static List<String> getCustomAttributesForHtmlTag(final String tagLowercase){
final List<String> attrs = new ArrayList<>();
for(AttrD attrD : attributesDescriptive()){
if(
Arrays.asList(attrD.tags).contains(tagLowercase)
){
attrs.add(attrD.attr);
}
}
return attrs;
}
public static List<AttrD> attributesDescriptive() {
return Arrays.asList(
new AttrD("accept", true, "input"),
//new AttrD("accept-charset","form"), //contains dashes, TODO
//new AttrD("accesskey"), //global attribute
new AttrD("action", true, "form"),
//"align", not supported in HTML5
new AttrD("alt", true, "area","img","input"),
new AttrD("async", false, "script"),
new AttrD("autocomplete", false, "form","input"),
new AttrD("autofocus", false, "button","input","select","textarea"),
new AttrD("autoplay", false, "audio","video"),
//"bgcolor", not supported in HTMTL5
//"border", not supported in HTML5
new AttrD("charset", true, "meta","script"),
new AttrD("checked", false, "input"),
new AttrD("cite", true, "blockquote","del","ins","q"),
//"class" already implemented in Tag.java // global attribute
new AttrD("cols", true, "textarea"),
new AttrD("colspan", true, "td","th"),
new AttrD("content", true, "meta"),
//"contenteditable" global attribute, should be in Tag.java
new AttrD("controls", false, "audio","video"),
new AttrD("coords", true, "area"),
new AttrD("data", true, "object"),
new AttrD("datetime", true, "del","ins","time"),
new AttrD("default", false, "track"),
new AttrD("defer", false, "script"),
//new AttrD("dir"), //global attribute
new AttrD("dirname", true, "input","textarea"),
new AttrD("disabled",false, "button","fieldset","input","optgroup","option","select","textarea"),
new AttrD("download",false, "a","area"),
//new AttrD("draggable") global attribute, should be in Tag.java
new AttrD("enctype", true, "form"),
new AttrD("for", true, "label","output"),
new AttrD("form", true, "button","fieldset","input","label","meter","object","output","select","textarea"),
new AttrD("formaction", true, "button","input"),
new AttrD("headers", true, "td","th"),
new AttrD("height", true, "canvas","embed","iframe","img","input","object","video"),
//new AttrD("hidden"), global attribute
new AttrD("high", true, "meter"),
new AttrD("href", true, "a","area","base","link"),
new AttrD("hreflang", true, "a","area","link"),
//"http-equiv", //TODO: '-' is problematic in code generation
//"id" global attribute, should be in Tag.java
new AttrD("ismap", false, "img"),
new AttrD("kind", true, "track"),
new AttrD("label", true, "track","option","optgroup"),
//"lang" global attribute, should be in Tag.java
new AttrD("list", true, "input"),
new AttrD("loop", false, "audio","video"),
new AttrD("low", true, "meter"),
new AttrD("max", true, "input","meter","progress"),
new AttrD("maxlength", true, "input","textarea"),
new AttrD("media", true, "a","area","link","source","style"),
new AttrD("method", true, "form"),
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","slot","textarea"),
new AttrD("novalidate", false, "form"),
new AttrD("onabort", true, "audio","embed","img","object","video"),
new AttrD("onafterprint", true, "body"),
new AttrD("onbeforeprint", true, "body"),
new AttrD("onbeforeunload", true, "body"),
//new AttrD("onblur"), global attribute
new AttrD("oncanplay", true, "audio","embed","object","video"),
new AttrD("oncanplaythrough", true, "audio","video"),
/* a bunch of event attributes that are on all visible elements (so should be in Tag.java)
"onchange",
"onclick",
"oncontextmenu",
"oncopy",
*/
new AttrD("oncuechange", true, "track"),
/*
"oncut",
...
"ondrop",
*/
new AttrD("ondurationchange", true, "audio","video"),
new AttrD("onemptied", true, "audio","video"),
new AttrD("onended", true, "audio","video"),
new AttrD("onerror", true, "audio","body","embed","img","object","script","style","video"),
//new AttrD("onfocus"),// global attribute
new AttrD("onhashchange", true, "body"),
// ... a bunch of event attributes visible on all elements
new AttrD("onload", true, "body","iframe","img","input","link","script","style"),
new AttrD("onloadeddata", true, "audio","video"),
new AttrD("onloadedmetadata", true, "audio","video"),
new AttrD("onloadstart", true, "audio","video"),
// ... a bunch of event attributes visible on all elements
new AttrD("onoffline", true, "body"),
new AttrD("ononline", true, "body"),
new AttrD("onpagehide", true, "body"),
new AttrD("onpageshow", true, "body"),
//new AttrD("onpaste"),// global attribute
new AttrD("onpause", true, "audio","video"),
new AttrD("onplay", true, "audio","video"),
new AttrD("onplaying", true, "audio","video"),
new AttrD("onpopstate", true, "body"),
new AttrD("onprogress", true, "audio","video"),
new AttrD("onratechange", true, "audio","video"),
new AttrD("onreset", true, "form"),
new AttrD("onresize", true, "body"),
//new AttrD("onscroll"), //global attribute
new AttrD("onsearch", true, "input"),
new AttrD("onseeked", true, "audio","video"),
new AttrD("onseeking", true, "audio","video"),
//new AttrD("onselect"), //global attribute
new AttrD("onstalled", true, "audio","video"),
new AttrD("onstorage", true, "body"),
new AttrD("onsubmit", true, "form"),
new AttrD("onsuspend", true, "audio","video"),
new AttrD("ontimeupdate", true, "audio","video"),
new AttrD("ontoggle", true, "details"),
new AttrD("onunload", true, "body"),
new AttrD("onvolumechanged", true, "audio","video"),
new AttrD("onwaiting", true, "audio","video"),
//new AttrD("onwheel"), //global attribute
new AttrD("open", false, "details"),
new AttrD("optimum", true, "meter"),
new AttrD("pattern", true, "input"),
new AttrD("placeholder", true, "input","textarea"),
new AttrD("poster", true, "video"),
new AttrD("preload", true, "audio","video"),
new AttrD("readonly", false, "input","textarea"),
new AttrD("rel", true, "a","area","form","link"),
new AttrD("required", false, "input","select","textarea"),
new AttrD("reversed", false, "ol"),
new AttrD("rows", true, "textarea"),
new AttrD("rowspan", true, "td","th"),
new AttrD("sandbox", false, "iframe"),
new AttrD("scope", true, "th"),
new AttrD("selected", false, "option"),
new AttrD("shape", true, "area"),
new AttrD("size", true, "input","select"),
new AttrD("sizes", true, "img","link","source"),
new AttrD("span", true, "col","colgroup"),
//new AttrD("spellcheck"), //global attribute
new AttrD("src", true, "audio","embed","iframe","img","input","script","source","track","video"),
new AttrD("srcdoc", true, "iframe"),
new AttrD("srclang", true, "track"),
new AttrD("srcset", true, "img","source"),
new AttrD("start", true, "ol"),
new AttrD("step", true, "input"),
//new AttrD("style"), //global attribute
//new AttrD("tabindex"), //global attribute
new AttrD("target", true, "a","area","base","form"),
//new AttrD("title"), //global attribute
//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","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 com.j2html.codegen.wattsi;
public interface AttributeDefinition {
String name();
boolean appliesTo(ElementDefinition element);
boolean isObsolete();
}

View File

@@ -0,0 +1,11 @@
package com.j2html.codegen.wattsi;
public interface ElementDefinition {
String name();
boolean isSelfClosing();
boolean isObsolete();
}

View File

@@ -0,0 +1,209 @@
package com.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 isSelfClosing() {
return false;
}
@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,16 @@
package com.j2html.codegen;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class AppTest
{
@Test
public void shouldAnswerWithTrue()
{
//dummy, just to conform to the default mvn
//directory layout
assertTrue( true );
}
}

View File

@@ -0,0 +1,77 @@
package com.j2html.codegen;
import com.j2html.codegen.generators.TagCreatorCodeGenerator;
import com.j2html.codegen.wattsi.ElementDefinition;
import com.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);
}
}

View File

@@ -0,0 +1,69 @@
package com.j2html.codegen;
import org.junit.Test;
import org.mockito.InOrder;
import java.util.function.Consumer;
import static org.mockito.Mockito.*;
public class ParserTest {
private void verifyParsing(String txt, Consumer<Parser.Listener> checks) {
Parser.Listener listener = mock(Parser.Listener.class);
Parser.parse(txt, listener);
checks.accept(listener);
}
@Test
public void an_empty_input_has_no_events() {
verifyParsing("", listener -> {
verifyNoInteractions(listener);
});
}
@Test
public void whitespace_has_no_events() {
verifyParsing(" \t\t\t\t", listener -> {
verifyNoInteractions(listener);
});
}
@Test
public void commented_lines_are_signaled() {
verifyParsing("#Comment 1.\n# Comment B?", listener -> {
InOrder order = inOrder(listener);
order.verify(listener).lineCommented(1, "#Comment 1.");
order.verify(listener).lineCommented(2, "# Comment B?");
});
}
@Test
public void node_definitions_are_signaled() {
verifyParsing("ELEMENT[a]\nEMPTY-ELEMENT[b]\nBOOLEAN[c]\nONOFF[d]\nSTRING[e]", listener -> {
InOrder order = inOrder(listener);
order.verify(listener).elementDefined(1, "a");
order.verify(listener).emptyElementDefined(2, "b");
order.verify(listener).booleanDefined(3, "c");
order.verify(listener).onOffDefined(4, "d");
order.verify(listener).stringDefined(5, "e");
});
}
@Test
public void attribute_definitions_are_signaled() {
verifyParsing("ATTRIBUTE[a:b]", listener -> {
InOrder order = inOrder(listener);
order.verify(listener).attributeDefined(1, "a", "b");
});
}
@Test
public void invalid_lines_are_signaled() {
verifyParsing("lol, I dunno!\nIt Broke...", listener -> {
InOrder order = inOrder(listener);
order.verify(listener).invalidLine(1, "lol, I dunno!");
order.verify(listener).invalidLine(2, "It Broke...");
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,622 @@
EMPTY-ELEMENT[area]
EMPTY-ELEMENT[base]
EMPTY-ELEMENT[br]
EMPTY-ELEMENT[col]
EMPTY-ELEMENT[embed]
EMPTY-ELEMENT[hr]
EMPTY-ELEMENT[img]
EMPTY-ELEMENT[input]
EMPTY-ELEMENT[keygen]
EMPTY-ELEMENT[link]
EMPTY-ELEMENT[meta]
EMPTY-ELEMENT[param]
EMPTY-ELEMENT[source]
EMPTY-ELEMENT[track]
EMPTY-ELEMENT[wbr]
ELEMENT[a]
ELEMENT[abbr]
ELEMENT[address]
ELEMENT[article]
ELEMENT[aside]
ELEMENT[audio]
ELEMENT[b]
ELEMENT[bdi]
ELEMENT[bdo]
ELEMENT[blockquote]
ELEMENT[body]
ELEMENT[button]
ELEMENT[canvas]
ELEMENT[caption]
ELEMENT[cite]
ELEMENT[code]
ELEMENT[colgroup]
ELEMENT[data]
ELEMENT[datalist]
ELEMENT[dd]
ELEMENT[del]
ELEMENT[details]
ELEMENT[dfn]
ELEMENT[dialog]
ELEMENT[div]
ELEMENT[dl]
ELEMENT[dt]
ELEMENT[em]
ELEMENT[fieldset]
ELEMENT[figcaption]
ELEMENT[figure]
ELEMENT[footer]
ELEMENT[form]
ELEMENT[h1]
ELEMENT[h2]
ELEMENT[h3]
ELEMENT[h4]
ELEMENT[h5]
ELEMENT[h6]
ELEMENT[head]
ELEMENT[header]
ELEMENT[html]
ELEMENT[i]
ELEMENT[iframe]
ELEMENT[ins]
ELEMENT[kbd]
ELEMENT[label]
ELEMENT[legend]
ELEMENT[li]
ELEMENT[main]
ELEMENT[map]
ELEMENT[mark]
ELEMENT[menu]
ELEMENT[menuitem]
ELEMENT[meter]
ELEMENT[nav]
ELEMENT[noscript]
ELEMENT[object]
ELEMENT[ol]
ELEMENT[optgroup]
ELEMENT[option]
ELEMENT[output]
ELEMENT[p]
ELEMENT[picture]
ELEMENT[pre]
ELEMENT[progress]
ELEMENT[q]
ELEMENT[rp]
ELEMENT[rt]
ELEMENT[ruby]
ELEMENT[s]
ELEMENT[samp]
ELEMENT[script]
ELEMENT[section]
ELEMENT[select]
ELEMENT[slot]
ELEMENT[small]
ELEMENT[span]
ELEMENT[strong]
ELEMENT[style]
ELEMENT[sub]
ELEMENT[summary]
ELEMENT[sup]
ELEMENT[table]
ELEMENT[tbody]
ELEMENT[td]
ELEMENT[template]
ELEMENT[textarea]
ELEMENT[tfoot]
ELEMENT[th]
ELEMENT[thead]
ELEMENT[time]
ELEMENT[title]
ELEMENT[tr]
ELEMENT[u]
ELEMENT[ul]
ELEMENT[var]
ELEMENT[video]
STRING[accept]
ATTRIBUTE[input:accept]
STRING[action]
ATTRIBUTE[form:action]
STRING[alt]
ATTRIBUTE[area:alt]
ATTRIBUTE[img:alt]
ATTRIBUTE[input:alt]
BOOLEAN[async]
ATTRIBUTE[script:async]
ONOFF[autocomplete]
ATTRIBUTE[form:autocomplete]
ATTRIBUTE[input:autocomplete]
BOOLEAN[autofocus]
ATTRIBUTE[button:autofocus]
ATTRIBUTE[input:autofocus]
ATTRIBUTE[select:autofocus]
ATTRIBUTE[textarea:autofocus]
BOOLEAN[autoplay]
ATTRIBUTE[audio:autoplay]
ATTRIBUTE[video:autoplay]
STRING[charset]
ATTRIBUTE[meta:charset]
ATTRIBUTE[script:charset]
BOOLEAN[checked]
ATTRIBUTE[input:checked]
STRING[cite]
ATTRIBUTE[blockquote:cite]
ATTRIBUTE[del:cite]
ATTRIBUTE[ins:cite]
ATTRIBUTE[q:cite]
STRING[cols]
ATTRIBUTE[textarea:cols]
STRING[colspan]
ATTRIBUTE[td:colspan]
ATTRIBUTE[th:colspan]
STRING[content]
ATTRIBUTE[meta:content]
BOOLEAN[controls]
ATTRIBUTE[audio:controls]
ATTRIBUTE[video:controls]
STRING[coords]
ATTRIBUTE[area:coords]
STRING[data]
ATTRIBUTE[object:data]
STRING[datetime]
ATTRIBUTE[del:datetime]
ATTRIBUTE[ins:datetime]
ATTRIBUTE[time:datetime]
BOOLEAN[default]
ATTRIBUTE[track:default]
BOOLEAN[defer]
ATTRIBUTE[script:defer]
STRING[dirname]
ATTRIBUTE[input:dirname]
ATTRIBUTE[textarea:dirname]
BOOLEAN[disabled]
ATTRIBUTE[button:disabled]
ATTRIBUTE[fieldset:disabled]
ATTRIBUTE[input:disabled]
ATTRIBUTE[optgroup:disabled]
ATTRIBUTE[option:disabled]
ATTRIBUTE[select:disabled]
ATTRIBUTE[textarea:disabled]
BOOLEAN[download]
ATTRIBUTE[a:download]
ATTRIBUTE[area:download]
STRING[enctype]
ATTRIBUTE[form:enctype]
STRING[for]
ATTRIBUTE[label:for]
ATTRIBUTE[output:for]
STRING[form]
ATTRIBUTE[button:form]
ATTRIBUTE[fieldset:form]
ATTRIBUTE[input:form]
ATTRIBUTE[label:form]
ATTRIBUTE[meter:form]
ATTRIBUTE[object:form]
ATTRIBUTE[output:form]
ATTRIBUTE[select:form]
ATTRIBUTE[textarea:form]
STRING[formaction]
ATTRIBUTE[button:formaction]
ATTRIBUTE[input:formaction]
STRING[headers]
ATTRIBUTE[td:headers]
ATTRIBUTE[th:headers]
STRING[height]
ATTRIBUTE[canvas:height]
ATTRIBUTE[embed:height]
ATTRIBUTE[iframe:height]
ATTRIBUTE[img:height]
ATTRIBUTE[input:height]
ATTRIBUTE[object:height]
ATTRIBUTE[video:height]
STRING[high]
ATTRIBUTE[meter:high]
STRING[href]
ATTRIBUTE[a:href]
ATTRIBUTE[area:href]
ATTRIBUTE[base:href]
ATTRIBUTE[link:href]
STRING[hreflang]
ATTRIBUTE[a:hreflang]
ATTRIBUTE[area:hreflang]
ATTRIBUTE[link:hreflang]
BOOLEAN[ismap]
ATTRIBUTE[img:ismap]
STRING[kind]
ATTRIBUTE[track:kind]
STRING[label]
ATTRIBUTE[track:label]
ATTRIBUTE[option:label]
ATTRIBUTE[optgroup:label]
STRING[list]
ATTRIBUTE[input:list]
BOOLEAN[loop]
ATTRIBUTE[audio:loop]
ATTRIBUTE[video:loop]
STRING[low]
ATTRIBUTE[meter:low]
STRING[max]
ATTRIBUTE[input:max]
ATTRIBUTE[meter:max]
ATTRIBUTE[progress:max]
STRING[maxlength]
ATTRIBUTE[input:maxlength]
ATTRIBUTE[textarea:maxlength]
STRING[media]
ATTRIBUTE[a:media]
ATTRIBUTE[area:media]
ATTRIBUTE[link:media]
ATTRIBUTE[source:media]
ATTRIBUTE[style:media]
STRING[method]
ATTRIBUTE[form:method]
STRING[min]
ATTRIBUTE[input:min]
ATTRIBUTE[meter:min]
BOOLEAN[multiple]
ATTRIBUTE[input:multiple]
ATTRIBUTE[select:multiple]
BOOLEAN[muted]
ATTRIBUTE[video:muted]
ATTRIBUTE[audio:muted]
STRING[name]
ATTRIBUTE[button:name]
ATTRIBUTE[fieldset:name]
ATTRIBUTE[form:name]
ATTRIBUTE[iframe:name]
ATTRIBUTE[input:name]
ATTRIBUTE[map:name]
ATTRIBUTE[meta:name]
ATTRIBUTE[object:name]
ATTRIBUTE[output:name]
ATTRIBUTE[param:name]
ATTRIBUTE[select:name]
ATTRIBUTE[slot:name]
ATTRIBUTE[textarea:name]
BOOLEAN[novalidate]
ATTRIBUTE[form:novalidate]
STRING[onabort]
ATTRIBUTE[audio:onabort]
ATTRIBUTE[embed:onabort]
ATTRIBUTE[img:onabort]
ATTRIBUTE[object:onabort]
ATTRIBUTE[video:onabort]
STRING[onafterprint]
ATTRIBUTE[body:onafterprint]
STRING[onbeforeprint]
ATTRIBUTE[body:onbeforeprint]
STRING[onbeforeunload]
ATTRIBUTE[body:onbeforeunload]
STRING[oncanplay]
ATTRIBUTE[audio:oncanplay]
ATTRIBUTE[embed:oncanplay]
ATTRIBUTE[object:oncanplay]
ATTRIBUTE[video:oncanplay]
STRING[oncanplaythrough]
ATTRIBUTE[audio:oncanplaythrough]
ATTRIBUTE[video:oncanplaythrough]
STRING[oncuechange]
ATTRIBUTE[track:oncuechange]
STRING[ondurationchange]
ATTRIBUTE[audio:ondurationchange]
ATTRIBUTE[video:ondurationchange]
STRING[onemptied]
ATTRIBUTE[audio:onemptied]
ATTRIBUTE[video:onemptied]
STRING[onended]
ATTRIBUTE[audio:onended]
ATTRIBUTE[video:onended]
STRING[onerror]
ATTRIBUTE[audio:onerror]
ATTRIBUTE[body:onerror]
ATTRIBUTE[embed:onerror]
ATTRIBUTE[img:onerror]
ATTRIBUTE[object:onerror]
ATTRIBUTE[script:onerror]
ATTRIBUTE[style:onerror]
ATTRIBUTE[video:onerror]
STRING[onhashchange]
ATTRIBUTE[body:onhashchange]
STRING[onload]
ATTRIBUTE[body:onload]
ATTRIBUTE[iframe:onload]
ATTRIBUTE[img:onload]
ATTRIBUTE[input:onload]
ATTRIBUTE[link:onload]
ATTRIBUTE[script:onload]
ATTRIBUTE[style:onload]
STRING[onloadeddata]
ATTRIBUTE[audio:onloadeddata]
ATTRIBUTE[video:onloadeddata]
STRING[onloadedmetadata]
ATTRIBUTE[audio:onloadedmetadata]
ATTRIBUTE[video:onloadedmetadata]
STRING[onloadstart]
ATTRIBUTE[audio:onloadstart]
ATTRIBUTE[video:onloadstart]
STRING[onoffline]
ATTRIBUTE[body:onoffline]
STRING[ononline]
ATTRIBUTE[body:ononline]
STRING[onpagehide]
ATTRIBUTE[body:onpagehide]
STRING[onpageshow]
ATTRIBUTE[body:onpageshow]
STRING[onpause]
ATTRIBUTE[audio:onpause]
ATTRIBUTE[video:onpause]
STRING[onplay]
ATTRIBUTE[audio:onplay]
ATTRIBUTE[video:onplay]
STRING[onplaying]
ATTRIBUTE[audio:onplaying]
ATTRIBUTE[video:onplaying]
STRING[onpopstate]
ATTRIBUTE[body:onpopstate]
STRING[onprogress]
ATTRIBUTE[audio:onprogress]
ATTRIBUTE[video:onprogress]
STRING[onratechange]
ATTRIBUTE[audio:onratechange]
ATTRIBUTE[video:onratechange]
STRING[onreset]
ATTRIBUTE[form:onreset]
STRING[onresize]
ATTRIBUTE[body:onresize]
STRING[onsearch]
ATTRIBUTE[input:onsearch]
STRING[onseeked]
ATTRIBUTE[audio:onseeked]
ATTRIBUTE[video:onseeked]
STRING[onseeking]
ATTRIBUTE[audio:onseeking]
ATTRIBUTE[video:onseeking]
STRING[onstalled]
ATTRIBUTE[audio:onstalled]
ATTRIBUTE[video:onstalled]
STRING[onstorage]
ATTRIBUTE[body:onstorage]
STRING[onsubmit]
ATTRIBUTE[form:onsubmit]
STRING[onsuspend]
ATTRIBUTE[audio:onsuspend]
ATTRIBUTE[video:onsuspend]
STRING[ontimeupdate]
ATTRIBUTE[audio:ontimeupdate]
ATTRIBUTE[video:ontimeupdate]
STRING[ontoggle]
ATTRIBUTE[details:ontoggle]
STRING[onunload]
ATTRIBUTE[body:onunload]
STRING[onvolumechanged]
ATTRIBUTE[audio:onvolumechanged]
ATTRIBUTE[video:onvolumechanged]
STRING[onwaiting]
ATTRIBUTE[audio:onwaiting]
ATTRIBUTE[video:onwaiting]
BOOLEAN[open]
ATTRIBUTE[details:open]
STRING[optimum]
ATTRIBUTE[meter:optimum]
STRING[pattern]
ATTRIBUTE[input:pattern]
STRING[placeholder]
ATTRIBUTE[input:placeholder]
ATTRIBUTE[textarea:placeholder]
STRING[poster]
ATTRIBUTE[video:poster]
STRING[preload]
ATTRIBUTE[audio:preload]
ATTRIBUTE[video:preload]
BOOLEAN[readonly]
ATTRIBUTE[input:readonly]
ATTRIBUTE[textarea:readonly]
STRING[rel]
ATTRIBUTE[a:rel]
ATTRIBUTE[area:rel]
ATTRIBUTE[form:rel]
ATTRIBUTE[link:rel]
BOOLEAN[required]
ATTRIBUTE[input:required]
ATTRIBUTE[select:required]
ATTRIBUTE[textarea:required]
BOOLEAN[reversed]
ATTRIBUTE[ol:reversed]
STRING[rows]
ATTRIBUTE[textarea:rows]
STRING[rowspan]
ATTRIBUTE[td:rowspan]
ATTRIBUTE[th:rowspan]
BOOLEAN[sandbox]
ATTRIBUTE[iframe:sandbox]
STRING[scope]
ATTRIBUTE[th:scope]
BOOLEAN[selected]
ATTRIBUTE[option:selected]
STRING[shape]
ATTRIBUTE[area:shape]
STRING[size]
ATTRIBUTE[input:size]
ATTRIBUTE[select:size]
STRING[sizes]
ATTRIBUTE[img:sizes]
ATTRIBUTE[link:sizes]
ATTRIBUTE[source:sizes]
STRING[span]
ATTRIBUTE[col:span]
ATTRIBUTE[colgroup:span]
STRING[src]
ATTRIBUTE[audio:src]
ATTRIBUTE[embed:src]
ATTRIBUTE[iframe:src]
ATTRIBUTE[img:src]
ATTRIBUTE[input:src]
ATTRIBUTE[script:src]
ATTRIBUTE[source:src]
ATTRIBUTE[track:src]
ATTRIBUTE[video:src]
STRING[srcdoc]
ATTRIBUTE[iframe:srcdoc]
STRING[srclang]
ATTRIBUTE[track:srclang]
STRING[srcset]
ATTRIBUTE[img:srcset]
ATTRIBUTE[source:srcset]
STRING[start]
ATTRIBUTE[ol:start]
STRING[step]
ATTRIBUTE[input:step]
STRING[target]
ATTRIBUTE[a:target]
ATTRIBUTE[area:target]
ATTRIBUTE[base:target]
ATTRIBUTE[form:target]
STRING[type]
ATTRIBUTE[a:type]
ATTRIBUTE[button:type]
ATTRIBUTE[embed:type]
ATTRIBUTE[input:type]
ATTRIBUTE[link:type]
ATTRIBUTE[menu:type]
ATTRIBUTE[object:type]
ATTRIBUTE[script:type]
ATTRIBUTE[source:type]
ATTRIBUTE[style:type]
STRING[usemap]
ATTRIBUTE[img:usemap]
ATTRIBUTE[object:usemap]
STRING[value]
ATTRIBUTE[button:value]
ATTRIBUTE[data:value]
ATTRIBUTE[input:value]
ATTRIBUTE[li:value]
ATTRIBUTE[option:value]
ATTRIBUTE[meter:value]
ATTRIBUTE[progress:value]
ATTRIBUTE[param:value]
STRING[width]
ATTRIBUTE[canvas:width]
ATTRIBUTE[embed:width]
ATTRIBUTE[iframe:width]
ATTRIBUTE[img:width]
ATTRIBUTE[input:width]
ATTRIBUTE[object:width]
ATTRIBUTE[video:width]
STRING[wrap]
ATTRIBUTE[textarea:wrap]

101
j2html-ext-mathml/.gitignore vendored Normal file
View File

@@ -0,0 +1,101 @@
# Created by https://www.gitignore.io
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Java ###
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
### Eclipse ###
.classpath
.project
.settings/
buildNumber.properties

130
j2html-ext-mathml/pom.xml Normal file
View File

@@ -0,0 +1,130 @@
<?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>
<parent>
<groupId>com.j2html</groupId>
<artifactId>j2html-parent</artifactId>
<version>1.6.1-SNAPSHOT</version>
</parent>
<name>j2html-ext-mathml</name>
<artifactId>j2html-ext-mathml</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>1.6.1-SNAPSHOT</version>
</dependency>
</dependencies>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>com.j2html</groupId>
<artifactId>j2html-codegen-maven-plugin</artifactId>
<version>1.6.1-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>generate-source-files</goal>
</goals>
</execution>
</executions>
<configuration>
<modelFile>${project.basedir}/src/main/models/mathml.model</modelFile>
<attributePackage>com.j2html.mathml.attributes</attributePackage>
<tagPackage>com.j2html.mathml.tags</tagPackage>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<executions>
<execution>
<id>default</id>
<goals>
<goal>perform</goal>
</goals>
<configuration>
<pomFileName>pom.xml</pomFileName>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Automatic-Module-Name>com.j2html</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
</plugin>
<!--plugin>
<groupId>org.revapi</groupId>
<artifactId>revapi-maven-plugin</artifactId>
<configuration>
<oldArtifacts>
<artifact>com.j2html:j2html-ext-mathml:1.6.0</artifact>
</oldArtifacts>
<analysisConfiguration>
<revapi.differences>
<differences>
</differences>
</revapi.differences>
</analysisConfiguration>
</configuration>
</plugin-->
</plugins>
</build>
</project>

View File

@@ -0,0 +1,247 @@
package com.j2html.mathml;
import com.j2html.mathml.tags.*;
import j2html.tags.DomContent;
public class MathML {
public static AnnotationTag annotation(String text) {
return new AnnotationTag().withText(text);
}
public static AnnotationTag annotation(DomContent... dc) {
return new AnnotationTag().with(dc);
}
public static MactionTag maction(String text) {
return new MactionTag().withText(text);
}
public static MactionTag maction(DomContent... dc) {
return new MactionTag().with(dc);
}
public static MathTag math(String text) {
return new MathTag().withText(text);
}
public static MathTag math(DomContent... dc) {
return new MathTag().with(dc);
}
public static MerrorTag merror(String text) {
return new MerrorTag().withText(text);
}
public static MerrorTag merror(DomContent... dc) {
return new MerrorTag().with(dc);
}
public static MfracTag mfrac(String text) {
return new MfracTag().withText(text);
}
public static MfracTag mfrac(DomContent... dc) {
return new MfracTag().with(dc);
}
public static MiTag mi(String text) {
return new MiTag().withText(text);
}
public static MiTag mi(DomContent... dc) {
return new MiTag().with(dc);
}
public static MmultiscriptsTag mmultiscripts(String text) {
return new MmultiscriptsTag().withText(text);
}
public static MmultiscriptsTag mmultiscripts(DomContent... dc) {
return new MmultiscriptsTag().with(dc);
}
public static MnTag mn(String text) {
return new MnTag().withText(text);
}
public static MnTag mn(DomContent... dc) {
return new MnTag().with(dc);
}
public static MoTag mo(String text) {
return new MoTag().withText(text);
}
public static MoTag mo(DomContent... dc) {
return new MoTag().with(dc);
}
public static MoverTag mover(String text) {
return new MoverTag().withText(text);
}
public static MoverTag mover(DomContent... dc) {
return new MoverTag().with(dc);
}
public static MpaddedTag mpadded(String text) {
return new MpaddedTag().withText(text);
}
public static MpaddedTag mpadded(DomContent... dc) {
return new MpaddedTag().with(dc);
}
public static MphantomTag mphantom(String text) {
return new MphantomTag().withText(text);
}
public static MphantomTag mphantom(DomContent... dc) {
return new MphantomTag().with(dc);
}
public static MprescriptsTag mprescripts(String text) {
return new MprescriptsTag().withText(text);
}
public static MprescriptsTag mprescripts(DomContent... dc) {
return new MprescriptsTag().with(dc);
}
public static MrootTag mroot(String text) {
return new MrootTag().withText(text);
}
public static MrootTag mroot(DomContent... dc) {
return new MrootTag().with(dc);
}
public static MrowTag mrow(String text) {
return new MrowTag().withText(text);
}
public static MrowTag mrow(DomContent... dc) {
return new MrowTag().with(dc);
}
public static MsTag ms(String text) {
return new MsTag().withText(text);
}
public static MsTag ms(DomContent... dc) {
return new MsTag().with(dc);
}
public static MspaceTag mspace(String text) {
return new MspaceTag().withText(text);
}
public static MspaceTag mspace(DomContent... dc) {
return new MspaceTag().with(dc);
}
public static MsqrtTag msqrt(String text) {
return new MsqrtTag().withText(text);
}
public static MsqrtTag msqrt(DomContent... dc) {
return new MsqrtTag().with(dc);
}
public static MstyleTag mstyle(String text) {
return new MstyleTag().withText(text);
}
public static MstyleTag mstyle(DomContent... dc) {
return new MstyleTag().with(dc);
}
public static MsubTag msub(String text) {
return new MsubTag().withText(text);
}
public static MsubTag msub(DomContent... dc) {
return new MsubTag().with(dc);
}
public static MsubsupTag msubsup(String text) {
return new MsubsupTag().withText(text);
}
public static MsubsupTag msubsup(DomContent... dc) {
return new MsubsupTag().with(dc);
}
public static MsupTag msup(String text) {
return new MsupTag().withText(text);
}
public static MsupTag msup(DomContent... dc) {
return new MsupTag().with(dc);
}
public static MtableTag mtable(String text) {
return new MtableTag().withText(text);
}
public static MtableTag mtable(DomContent... dc) {
return new MtableTag().with(dc);
}
public static MtdTag mtd(String text) {
return new MtdTag().withText(text);
}
public static MtdTag mtd(DomContent... dc) {
return new MtdTag().with(dc);
}
public static MtextTag mtext(String text) {
return new MtextTag().withText(text);
}
public static MtextTag mtext(DomContent... dc) {
return new MtextTag().with(dc);
}
public static MtrTag mtr(String text) {
return new MtrTag().withText(text);
}
public static MtrTag mtr(DomContent... dc) {
return new MtrTag().with(dc);
}
public static MunderTag munder(String text) {
return new MunderTag().withText(text);
}
public static MunderTag munder(DomContent... dc) {
return new MunderTag().with(dc);
}
public static MunderoverTag munderover(String text) {
return new MunderoverTag().withText(text);
}
public static MunderoverTag munderover(DomContent... dc) {
return new MunderoverTag().with(dc);
}
public static NoneTag none(String text) {
return new NoneTag().withText(text);
}
public static NoneTag none(DomContent... dc) {
return new NoneTag().with(dc);
}
public static SemanticsTag semantics(String text) {
return new SemanticsTag().withText(text);
}
public static SemanticsTag semantics(DomContent... dc) {
return new SemanticsTag().with(dc);
}
}

View File

@@ -0,0 +1,254 @@
ELEMENT[annotation]
ELEMENT[maction]
ELEMENT[math]
ELEMENT[merror]
ELEMENT[mfrac]
ELEMENT[mi]
ELEMENT[mmultiscripts]
ELEMENT[mn]
ELEMENT[mo]
ELEMENT[mover]
ELEMENT[mpadded]
ELEMENT[mphantom]
ELEMENT[mprescripts]
ELEMENT[mroot]
ELEMENT[mrow]
ELEMENT[ms]
ELEMENT[mspace]
ELEMENT[msqrt]
ELEMENT[mstyle]
ELEMENT[msub]
ELEMENT[msubsup]
ELEMENT[msup]
ELEMENT[mtable]
ELEMENT[mtd]
ELEMENT[mtext]
ELEMENT[mtr]
ELEMENT[munder]
ELEMENT[munderover]
ELEMENT[none]
ELEMENT[semantics]
BOOLEAN[displaystyle]
ATTRIBUTE[annotation:displaystyle]
ATTRIBUTE[maction:displaystyle]
ATTRIBUTE[math:displaystyle]
ATTRIBUTE[merror:displaystyle]
ATTRIBUTE[mfrac:displaystyle]
ATTRIBUTE[mi:displaystyle]
ATTRIBUTE[mmultiscripts:displaystyle]
ATTRIBUTE[mn:displaystyle]
ATTRIBUTE[mo:displaystyle]
ATTRIBUTE[mover:displaystyle]
ATTRIBUTE[mpadded:displaystyle]
ATTRIBUTE[mphantom:displaystyle]
ATTRIBUTE[mprescripts:displaystyle]
ATTRIBUTE[mroot:displaystyle]
ATTRIBUTE[mrow:displaystyle]
ATTRIBUTE[ms:displaystyle]
ATTRIBUTE[mspace:displaystyle]
ATTRIBUTE[msqrt:displaystyle]
ATTRIBUTE[mstyle:displaystyle]
ATTRIBUTE[msub:displaystyle]
ATTRIBUTE[msubsup:displaystyle]
ATTRIBUTE[msup:displaystyle]
ATTRIBUTE[mtable:displaystyle]
ATTRIBUTE[mtd:displaystyle]
ATTRIBUTE[mtext:displaystyle]
ATTRIBUTE[mtr:displaystyle]
ATTRIBUTE[munder:displaystyle]
ATTRIBUTE[munderover:displaystyle]
ATTRIBUTE[none:displaystyle]
ATTRIBUTE[semantics:displaystyle]
BOOLEAN[mathbackground]
ATTRIBUTE[annotation:mathbackground]
ATTRIBUTE[maction:mathbackground]
ATTRIBUTE[math:mathbackground]
ATTRIBUTE[merror:mathbackground]
ATTRIBUTE[mfrac:mathbackground]
ATTRIBUTE[mi:mathbackground]
ATTRIBUTE[mmultiscripts:mathbackground]
ATTRIBUTE[mn:mathbackground]
ATTRIBUTE[mo:mathbackground]
ATTRIBUTE[mover:mathbackground]
ATTRIBUTE[mpadded:mathbackground]
ATTRIBUTE[mphantom:mathbackground]
ATTRIBUTE[mprescripts:mathbackground]
ATTRIBUTE[mroot:mathbackground]
ATTRIBUTE[mrow:mathbackground]
ATTRIBUTE[ms:mathbackground]
ATTRIBUTE[mspace:mathbackground]
ATTRIBUTE[msqrt:mathbackground]
ATTRIBUTE[mstyle:mathbackground]
ATTRIBUTE[msub:mathbackground]
ATTRIBUTE[msubsup:mathbackground]
ATTRIBUTE[msup:mathbackground]
ATTRIBUTE[mtable:mathbackground]
ATTRIBUTE[mtd:mathbackground]
ATTRIBUTE[mtext:mathbackground]
ATTRIBUTE[mtr:mathbackground]
ATTRIBUTE[munder:mathbackground]
ATTRIBUTE[munderover:mathbackground]
ATTRIBUTE[none:mathbackground]
ATTRIBUTE[semantics:mathbackground]
BOOLEAN[mathcolor]
ATTRIBUTE[annotation:mathcolor]
ATTRIBUTE[maction:mathcolor]
ATTRIBUTE[math:mathcolor]
ATTRIBUTE[merror:mathcolor]
ATTRIBUTE[mfrac:mathcolor]
ATTRIBUTE[mi:mathcolor]
ATTRIBUTE[mmultiscripts:mathcolor]
ATTRIBUTE[mn:mathcolor]
ATTRIBUTE[mo:mathcolor]
ATTRIBUTE[mover:mathcolor]
ATTRIBUTE[mpadded:mathcolor]
ATTRIBUTE[mphantom:mathcolor]
ATTRIBUTE[mprescripts:mathcolor]
ATTRIBUTE[mroot:mathcolor]
ATTRIBUTE[mrow:mathcolor]
ATTRIBUTE[ms:mathcolor]
ATTRIBUTE[mspace:mathcolor]
ATTRIBUTE[msqrt:mathcolor]
ATTRIBUTE[mstyle:mathcolor]
ATTRIBUTE[msub:mathcolor]
ATTRIBUTE[msubsup:mathcolor]
ATTRIBUTE[msup:mathcolor]
ATTRIBUTE[mtable:mathcolor]
ATTRIBUTE[mtd:mathcolor]
ATTRIBUTE[mtext:mathcolor]
ATTRIBUTE[mtr:mathcolor]
ATTRIBUTE[munder:mathcolor]
ATTRIBUTE[munderover:mathcolor]
ATTRIBUTE[none:mathcolor]
ATTRIBUTE[semantics:mathcolor]
BOOLEAN[mathsize]
ATTRIBUTE[annotation:mathsize]
ATTRIBUTE[maction:mathsize]
ATTRIBUTE[math:mathsize]
ATTRIBUTE[merror:mathsize]
ATTRIBUTE[mfrac:mathsize]
ATTRIBUTE[mi:mathsize]
ATTRIBUTE[mmultiscripts:mathsize]
ATTRIBUTE[mn:mathsize]
ATTRIBUTE[mo:mathsize]
ATTRIBUTE[mover:mathsize]
ATTRIBUTE[mpadded:mathsize]
ATTRIBUTE[mphantom:mathsize]
ATTRIBUTE[mprescripts:mathsize]
ATTRIBUTE[mroot:mathsize]
ATTRIBUTE[mrow:mathsize]
ATTRIBUTE[ms:mathsize]
ATTRIBUTE[mspace:mathsize]
ATTRIBUTE[msqrt:mathsize]
ATTRIBUTE[mstyle:mathsize]
ATTRIBUTE[msub:mathsize]
ATTRIBUTE[msubsup:mathsize]
ATTRIBUTE[msup:mathsize]
ATTRIBUTE[mtable:mathsize]
ATTRIBUTE[mtd:mathsize]
ATTRIBUTE[mtext:mathsize]
ATTRIBUTE[mtr:mathsize]
ATTRIBUTE[munder:mathsize]
ATTRIBUTE[munderover:mathsize]
ATTRIBUTE[none:mathsize]
ATTRIBUTE[semantics:mathsize]
BOOLEAN[mathvariant]
ATTRIBUTE[annotation:mathvariant]
ATTRIBUTE[maction:mathvariant]
ATTRIBUTE[math:mathvariant]
ATTRIBUTE[merror:mathvariant]
ATTRIBUTE[mfrac:mathvariant]
ATTRIBUTE[mi:mathvariant]
ATTRIBUTE[mmultiscripts:mathvariant]
ATTRIBUTE[mn:mathvariant]
ATTRIBUTE[mo:mathvariant]
ATTRIBUTE[mover:mathvariant]
ATTRIBUTE[mpadded:mathvariant]
ATTRIBUTE[mphantom:mathvariant]
ATTRIBUTE[mprescripts:mathvariant]
ATTRIBUTE[mroot:mathvariant]
ATTRIBUTE[mrow:mathvariant]
ATTRIBUTE[ms:mathvariant]
ATTRIBUTE[mspace:mathvariant]
ATTRIBUTE[msqrt:mathvariant]
ATTRIBUTE[mstyle:mathvariant]
ATTRIBUTE[msub:mathvariant]
ATTRIBUTE[msubsup:mathvariant]
ATTRIBUTE[msup:mathvariant]
ATTRIBUTE[mtable:mathvariant]
ATTRIBUTE[mtd:mathvariant]
ATTRIBUTE[mtext:mathvariant]
ATTRIBUTE[mtr:mathvariant]
ATTRIBUTE[munder:mathvariant]
ATTRIBUTE[munderover:mathvariant]
ATTRIBUTE[none:mathvariant]
ATTRIBUTE[semantics:mathvariant]
BOOLEAN[nonce]
ATTRIBUTE[annotation:nonce]
ATTRIBUTE[maction:nonce]
ATTRIBUTE[math:nonce]
ATTRIBUTE[merror:nonce]
ATTRIBUTE[mfrac:nonce]
ATTRIBUTE[mi:nonce]
ATTRIBUTE[mmultiscripts:nonce]
ATTRIBUTE[mn:nonce]
ATTRIBUTE[mo:nonce]
ATTRIBUTE[mover:nonce]
ATTRIBUTE[mpadded:nonce]
ATTRIBUTE[mphantom:nonce]
ATTRIBUTE[mprescripts:nonce]
ATTRIBUTE[mroot:nonce]
ATTRIBUTE[mrow:nonce]
ATTRIBUTE[ms:nonce]
ATTRIBUTE[mspace:nonce]
ATTRIBUTE[msqrt:nonce]
ATTRIBUTE[mstyle:nonce]
ATTRIBUTE[msub:nonce]
ATTRIBUTE[msubsup:nonce]
ATTRIBUTE[msup:nonce]
ATTRIBUTE[mtable:nonce]
ATTRIBUTE[mtd:nonce]
ATTRIBUTE[mtext:nonce]
ATTRIBUTE[mtr:nonce]
ATTRIBUTE[munder:nonce]
ATTRIBUTE[munderover:nonce]
ATTRIBUTE[none:nonce]
ATTRIBUTE[semantics:nonce]
BOOLEAN[scriptlevel]
ATTRIBUTE[annotation:scriptlevel]
ATTRIBUTE[maction:scriptlevel]
ATTRIBUTE[math:scriptlevel]
ATTRIBUTE[merror:scriptlevel]
ATTRIBUTE[mfrac:scriptlevel]
ATTRIBUTE[mi:scriptlevel]
ATTRIBUTE[mmultiscripts:scriptlevel]
ATTRIBUTE[mn:scriptlevel]
ATTRIBUTE[mo:scriptlevel]
ATTRIBUTE[mover:scriptlevel]
ATTRIBUTE[mpadded:scriptlevel]
ATTRIBUTE[mphantom:scriptlevel]
ATTRIBUTE[mprescripts:scriptlevel]
ATTRIBUTE[mroot:scriptlevel]
ATTRIBUTE[mrow:scriptlevel]
ATTRIBUTE[ms:scriptlevel]
ATTRIBUTE[mspace:scriptlevel]
ATTRIBUTE[msqrt:scriptlevel]
ATTRIBUTE[mstyle:scriptlevel]
ATTRIBUTE[msub:scriptlevel]
ATTRIBUTE[msubsup:scriptlevel]
ATTRIBUTE[msup:scriptlevel]
ATTRIBUTE[mtable:scriptlevel]
ATTRIBUTE[mtd:scriptlevel]
ATTRIBUTE[mtext:scriptlevel]
ATTRIBUTE[mtr:scriptlevel]
ATTRIBUTE[munder:scriptlevel]
ATTRIBUTE[munderover:scriptlevel]
ATTRIBUTE[none:scriptlevel]
ATTRIBUTE[semantics:scriptlevel]

View File

@@ -0,0 +1,42 @@
import org.junit.Test;
import static com.j2html.mathml.MathML.*;
import static org.junit.Assert.assertEquals;
public class MathMLTests {
@Test
public void all_tags_render_containers(){
assertEquals("<annotation></annotation>", annotation().render());
assertEquals("<maction></maction>", maction().render());
assertEquals("<math></math>", math().render());
assertEquals("<merror></merror>", merror().render());
assertEquals("<mfrac></mfrac>", mfrac().render());
assertEquals("<mi></mi>", mi().render());
assertEquals("<mmultiscripts></mmultiscripts>", mmultiscripts().render());
assertEquals("<mn></mn>", mn().render());
assertEquals("<mo></mo>", mo().render());
assertEquals("<mover></mover>", mover().render());
assertEquals("<mpadded></mpadded>", mpadded().render());
assertEquals("<mphantom></mphantom>", mphantom().render());
assertEquals("<mprescripts></mprescripts>", mprescripts().render());
assertEquals("<mroot></mroot>", mroot().render());
assertEquals("<mrow></mrow>", mrow().render());
assertEquals("<ms></ms>", ms().render());
assertEquals("<mspace></mspace>", mspace().render());
assertEquals("<msqrt></msqrt>", msqrt().render());
assertEquals("<mstyle></mstyle>", mstyle().render());
assertEquals("<msub></msub>", msub().render());
assertEquals("<msubsup></msubsup>", msubsup().render());
assertEquals("<msup></msup>", msup().render());
assertEquals("<mtable></mtable>", mtable().render());
assertEquals("<mtd></mtd>", mtd().render());
assertEquals("<mtext></mtext>", mtext().render());
assertEquals("<mtr></mtr>", mtr().render());
assertEquals("<munder></munder>", munder().render());
assertEquals("<munderover></munderover>", munderover().render());
assertEquals("<none></none>", none().render());
assertEquals("<semantics></semantics>", semantics().render());
}
}

96
j2html-website/.gitignore vendored Normal file
View File

@@ -0,0 +1,96 @@
# Created by https://www.gitignore.io
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Java ###
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties

21
j2html-website/README.md Normal file
View File

@@ -0,0 +1,21 @@
# j2html.com source code
## Running
Run this as any maven project.
Website is available on http://localhost:8888/.
## Deploying
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
curl "http://localhost:8888/news.html" > news.html
curl "http://localhost:8888/404.html" > 404.html
```
Changes will be deployed automatically once they have been merged.

Binary file not shown.

Binary file not shown.

83
j2html-website/pom.xml Normal file
View File

@@ -0,0 +1,83 @@
<?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>
<parent>
<groupId>com.j2html</groupId>
<artifactId>j2html-parent</artifactId>
<version>1.6.1-SNAPSHOT</version>
</parent>
<artifactId>j2html-website</artifactId>
<dependencies>
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version>4.0.0.ALPHA2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<finalName>j2html-website</finalName>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>app.Main</mainClass>
<classpathPrefix>dependency-jars/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<!-- This tells Maven to include all dependencies -->
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>app.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,22 @@
package app;
import app.controllers.PageController;
import io.javalin.Javalin;
import io.javalin.http.staticfiles.Location;
public class Main {
public static void main(String[] args) {
Javalin app = Javalin.create(config ->
config.addStaticFiles("website/src/main/resources/public", Location.EXTERNAL)
).start(8888);
app.get("/", PageController::serveIndex);
app.get("/download.html", PageController::serveDownload);
app.get("/examples.html", PageController::serveExamples);
app.get("/news.html", PageController::serveNews);
app.get("/404.html", PageController::serve404);
}
}

View File

@@ -0,0 +1,31 @@
package app.controllers;
import app.views.pages.DownloadView;
import app.views.pages.ExamplesView;
import app.views.pages.IndexView;
import app.views.pages.NewsView;
import app.views.pages.PageNotFoundView;
import io.javalin.http.Context;
public class PageController {
public static void serveIndex(Context ctx) {
ctx.html(IndexView.render());
}
public static void serveDownload(Context ctx) {
ctx.html(DownloadView.render());
}
public static void serveExamples(Context ctx) {
ctx.html(ExamplesView.render());
}
public static void serveNews(Context ctx) {
ctx.html(NewsView.render());
}
public static void serve404(Context ctx) {
ctx.html(PageNotFoundView.render());
}
}

View File

@@ -0,0 +1,24 @@
package app.debug;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Employee {
public final int id;
public final String name;
public final String title;
Employee(int id, String name, String title) {
this.id = id;
this.name = name;
this.title = title;
}
public static List<Employee> employees = new ArrayList<Employee>(Arrays.asList(
new Employee(1, "David", "Creator of Bad Libraries"),
new Employee(2, "Christian", "Fanboi of Jenkins"),
new Employee(3, "Paul", "Hater of Lambda Expressions")
));
}

View File

@@ -0,0 +1,96 @@
package app.views;
import j2html.tags.DomContent;
import static j2html.TagCreator.a;
import static j2html.TagCreator.attrs;
import static j2html.TagCreator.body;
import static j2html.TagCreator.br;
import static j2html.TagCreator.div;
import static j2html.TagCreator.document;
import static j2html.TagCreator.fileAsString;
import static j2html.TagCreator.footer;
import static j2html.TagCreator.h1;
import static j2html.TagCreator.head;
import static j2html.TagCreator.header;
import static j2html.TagCreator.html;
import static j2html.TagCreator.img;
import static j2html.TagCreator.join;
import static j2html.TagCreator.li;
import static j2html.TagCreator.link;
import static j2html.TagCreator.main;
import static j2html.TagCreator.meta;
import static j2html.TagCreator.nav;
import static j2html.TagCreator.p;
import static j2html.TagCreator.scriptWithInlineFile;
import static j2html.TagCreator.scriptWithInlineFile_min;
import static j2html.TagCreator.span;
import static j2html.TagCreator.styleWithInlineFile_min;
import static j2html.TagCreator.title;
import static j2html.TagCreator.ul;
public class MainView {
public static String render(String title, String heading, DomContent... tags) {
return document(
html(
head(
meta().withCharset("UTF-8"),
meta().withName("viewport").withContent("width=device-width, initial-scale=1"),
meta().withName("description").withContent("j2html - Fast and fluent Java HTML builder. Build type-safe HTML 5 with Java 8 expression!"),
title(title + " - Java HTML builder"),
link().withRel("icon").withHref("/img/favicon.svg"),
link().withRel("stylesheet").withHref("https://fonts.googleapis.com/css2?family=Lato&family=Patua+One&display=swap"),
styleWithInlineFile_min("/public/css/prism.css"),
styleWithInlineFile_min("/public/css/main.css")
),
body(
fileAsString("/html/googleTagManager.html"),
fileAsString("/html/githubBanner.html"),
header(attrs(".top-header"),
nav(attrs(".width-limit"),
a().withId("logo").withHref("/").with(
span(img().withSrc("/img/logo.svg").withAlt("j2html logo"))
),
ul(
li(a("Home").withHref("/")),
li(a("Download").withHref("/download.html")),
li(a("Examples").withHref("/examples.html")),
li(a("News").withHref("/news.html"))
)
)
),
header(attrs(".banner"),
h1(attrs(".width-limit"), heading)
),
main(attrs(".width-limit"),
tags //content from other template
),
div(attrs("#javalin-suggestion"), join(
span("").withClass("close"),
"Want a simple and modern web framework?",
br(),
"Try our other project:",
a("https://javalin.io").withHref("https://javalin.io?from=j2html")
)),
footer(join(
"This page was created using",
a("j2html").withHref("https://github.com/tipsy/j2html").withTarget("_blank"),
"and",
a("Javalin").withHref("https://javalin.io/").withTarget("_blank"),
". Webpage source on",
a("Github").withHref("https://github.com/j2html/j2html-webpage").withTarget("_blank"),
".",
br(),
p(attrs(".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.")
)),
scriptWithInlineFile("/public/js/prism.js"),
scriptWithInlineFile_min("/public/js/codeCompare.js"),
scriptWithInlineFile_min("/public/js/javalinSuggestion.js")
)
)
);
}
}

View File

@@ -0,0 +1,52 @@
package app.views;
import j2html.tags.ContainerTag;
import j2html.tags.Text;
import static j2html.TagCreator.a;
import static j2html.TagCreator.attrs;
import static j2html.TagCreator.code;
import static j2html.TagCreator.div;
import static j2html.TagCreator.fileAsEscapedString;
import static j2html.TagCreator.join;
import static j2html.TagCreator.li;
import static j2html.TagCreator.p;
import static j2html.TagCreator.pre;
import static j2html.TagCreator.ul;
public class Partials {
public static ContainerTag codeSnippet(String language, Text snippet) {
return pre(
code().withClass("language-" + language).with(
snippet
)
);
}
public static ContainerTag javaComparison(String filename) {
return div().withClasses("code-compare", "nowith").with(
ul(
li("version 1.0.0 +").withClass("nowith-switch active"),
li("earlier versions").withClass("with-switch")
),
pre(attrs(".nowith-pre"),
code(attrs(".language-java"),
fileAsEscapedString("/codeExamples/" + filename + "_new.java")
)
),
pre(attrs(".with-pre"),
code(attrs(".language-java"),
fileAsEscapedString("/codeExamples/" + filename + ".java")
)
)
);
}
public static ContainerTag mavenLink(String version) {
return p(join(
"j2html", version, "is available for download on",
a("Maven Central").withHref("http://search.maven.org/#artifactdetails%7Ccom.j2html%7Cj2html%7C" + version + "%7Cjar").withTarget("_blank"),
"."
));
}
}

View File

@@ -0,0 +1,43 @@
package app.views.pages;
import app.views.MainView;
import static app.views.Partials.codeSnippet;
import static j2html.TagCreator.*;
public class DownloadView {
public static String render() {
return MainView.render(
"Download j2html",
"Maven and GitHub",
section(attrs("#download"),
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",
a("GitHub").withHref("https://github.com/tipsy/j2html").withTarget("_blank"),
", make changes, and create pull requests! We will go through pull requests every sunday, so don't be shy."
))
)
);
}
}

View File

@@ -0,0 +1,121 @@
package app.views.pages;
import app.views.MainView;
import java.util.Arrays;
import java.util.List;
import static app.views.Partials.codeSnippet;
import static app.views.Partials.javaComparison;
import static j2html.TagCreator.a;
import static j2html.TagCreator.attrs;
import static j2html.TagCreator.each;
import static j2html.TagCreator.em;
import static j2html.TagCreator.fileAsEscapedString;
import static j2html.TagCreator.h2;
import static j2html.TagCreator.p;
import static j2html.TagCreator.section;
import static j2html.TagCreator.table;
import static j2html.TagCreator.tbody;
import static j2html.TagCreator.td;
import static j2html.TagCreator.text;
import static j2html.TagCreator.tr;
public class ExamplesView {
private static List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 9, 10);
public static String render() {
return MainView.render(
"Examples of how to use j2html",
"Reclaim control of your HTML",
section(attrs("#examples"),
h2(attrs("#basic-example"), "Basic example"),
p("Creating a basic HTML structure in j2html is pretty similar to plain HTML. This Java code:"),
javaComparison("basic"),
p("Becomes this HTML:"),
codeSnippet("markup", fileAsEscapedString("/codeExamples/basic.html")),
p(
text(
"It's literally impossible to forget to close a div, mistype an attribute name, or forget an attribute quote! " +
"Remember to include the Java wrapping code though, j2html is not a template language, all files are .java. " +
"To see how the wrapping code could look, check out the "
),
a("getting started example").withHref("/"),
text(".")
),
h2(attrs("#core-concepts"), "Core concepts"),
codeSnippet("java", fileAsEscapedString("/codeExamples/coreConcepts.java")),
h2(attrs("#loops"), "Loops, each() and filter()"),
p("Using Java 8's lambda syntax, you can write loops (via streams) inside your HTML-builder:"),
javaComparison("forLoopLambda"),
p("j2html also offers a custom each method, which is slightly more powerful:"),
javaComparison("each"),
p("If you need to filter your collection, j2html has a built in filter function too:"),
javaComparison("filter"),
p(
"Since this is pure Java, all the Employee methods (getName, getImgPath, getTitle) are available to you, " +
"and you get autocomplete suggestions and compile time errors."
),
p("Given three random employees, all the above approaches would give the same HTML:"),
codeSnippet("markup", fileAsEscapedString("/codeExamples/forLoop.html")),
h2(attrs("#2d-table-example"), "Two dimensional table example"),
javaComparison("2dTable"),
p("The code above is generating this table:"),
table(attrs("#table-example"),
tbody(
each(numbers, i -> tr(
each(numbers, j -> td(
String.valueOf(i * j)
))
))
)
),
h2(attrs("#partials"), "Partials"),
p("You can create partials for elements you use a lot:"),
javaComparison("partial"),
p("The equivalent HTML would be:"),
codeSnippet("markup", fileAsEscapedString("/codeExamples/partial.html")),
p("You can then use these partials, for example in a registration form:"),
javaComparison("view"),
p("Pretty clean, right? The rendered HTML is more verbose:"),
codeSnippet("markup", fileAsEscapedString("/codeExamples/view.html")),
p(
text("Imagine if you wanted labels in addition. The Java snippet would look almost identical: You could create a partial called"),
em(" passwordAndLabel() "),
text(
"and nothing but the method name would change. The resulting HTML however, would be twice or thrice as big, " +
"depending on whether or not you wrapped the input and label in another tag."
)
),
h2(attrs("#dynamic-views"), "Dynamic views"),
p(
"Once you've set up partials, you can call them from wherever, which greatly reduces potential errors. " +
"Let's say we want to include the form from the partials-example in our webpage. " +
"We want a header above and a footer below. A lot of templating languages make you do this: "
),
codeSnippet("java", fileAsEscapedString("/codeExamples/otherTemplates.vm")),
p(
"This is a pain to work with. You have no idea what the header and footer expects, and you have no way to affect how they treat your content. " +
"You can easily break the site by forgetting to close divs, or by forgetting to include either the header or the footer in one of your views. " +
"In j2html you can specify the context in which a view is rendered, and supply the rendering method with type safe parameters! " +
"If we want to insert our form in a header/footer frame, we simply create a MainView and make it take our view as an argument:"
),
javaComparison("main"),
p("Which will result in the rendered HTML:"),
codeSnippet("markup", fileAsEscapedString("/codeExamples/main.html")),
p(
"We would now get a compilation error if we forgot to include a title, and there is 0 chance of forgetting either header or footer, mistyping paths" +
", forgetting to close divs, or anything else."
)
)
);
}
}

View File

@@ -0,0 +1,64 @@
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.h3;
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;
public class IndexView {
public static String render() {
return MainView.render(
"Fast and fluent Java HTML5 builder",
"Fast and fluent Java HTML5 builder",
section(attrs("#index"),
h2("Getting started with j2html"),
p("Import TagCreator to get started. j2html's syntax is fluent and closely matched to HTML:"),
codeSnippet("java", fileAsEscapedString("/codeExamples/minimal.java")),
p("The Java code above becomes the HTML below:"),
codeSnippet("markup", fileAsEscapedString("/codeExamples/minimal.html")),
p(
text("Check out some more "),
a("examples").withHref("/examples.html"),
text(".")
),
h2("Intended use"),
h3("Consider using j2html if:"),
ul(
li("You love type safety. You love catching errors at compile time instead of waiting for some poor user to notice that something is wrong"),
li("You like to dynamically reuse your view code"),
li(
text("You think template engines are too slow. This index page was rendered 100 000 times in less than a second on an i5-4670. That's about a thousand times faster than Apache '"),
a("Velocity").withHref("http://velocity.apache.org/").withTarget("_blank"),
text("' (hah!)")
)
),
h3("Be careful about using j2html if:"),
ul(
li("You don't know Java and HTML well"),
li("You're creating a classic \"website\" that has a lot of static HTML (if it's all generated then it's fine)"),
li("Your application has a lot of text and you don't use language files / a database (string concatenation would get very annoying)"),
li("You use a CSS framework which relies on a lot of copy pasting HTML from docs. You'll have to translate these snippets to j2html")
),
h2("Why did you make this library?"),
p(
"First: j2html is a Java HTML builder. It's not a template engine, and it doesn't want to compete with template engines. "
+ "We were looking for a good way to create HTML for a complex login solution which had many different forms "
+ "(with different configurations, depending on user state and user actions, etc.), but very little actual HTML per page. "
+ "The result was j2html. We decided to release the Java HTML builder we made, since it seems better "
+ "than all the other Java HTML builders we found while researching the subject. Hopefully someone will find it useful!"
)
)
);
}
}

View File

@@ -0,0 +1,191 @@
package app.views.pages;
import app.views.MainView;
import j2html.TagCreator;
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.*;
public class NewsView {
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, el -> TagCreator.li(el))
));
}
public static String render() {
return MainView.render(
"News about j2html",
"News and releases",
section(attrs("#news"),
newsPost(
"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",
false,
join("Added support for", code("Map"), "in", code("each()")),
join("Added osgi metadata"),
join("Added support for", code("Optional"), "in", code("iff()")),
join("Fixed bugs in ", code("renderFormatted()"))
),
newsPost(
"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",
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",
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 -> {...}")),
join("Added option to configure JS-minification via", code("Config.jsMinifier = string -> {...}")),
join("Added option to close empty tags via", code("Config.closeEmptyTags = true")),
join(code("attr()"), "can now take take any object, not just strings")
),
newsPost(
"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",
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>")),
join("Added a", code("join()"), "method to more easily join sentences with inline HTML, like:", br(), code("join(\"Added a\", code(\"join()\"), \"method to more ea...")),
join("Added a", code("document()"), "method that takes a", code("html()"), " tag and renders a HTML declaration followed by the html content"),
join("Added support for Java 7 and Java 6. Some functionality (each/filter) will not work on these versions, but everything else should.")
),
newsPost(
"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)"),
join("Static files can now be fetched from anywhere, not just classpath")
),
newsPost(
"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")
),
// ---------------------------------------------------------------------------------------------
h2("j2html 0.7 released!").withId("j2html-0.7-released"),
mavenLink("0.7"),
p("Changes:"),
ul(
li("New file-api (avilable from TagCreator class):").with(
dl(
dt("fileAsString(String path)"),
dd("returns a file's content as a String (useful for including static HTML such as Google Analytics code)"),
dt("fileAsEscapedString(String path)"),
dd("returns fileAsString(path) escaped (useful for including the code-examples on this website)"),
dt("styleWithInlineFile(String path)"),
dd("returns <style>fileAsString(path)</style>"),
dt("styleWithInlineFile_min(String path)"),
dd("returns <style>fileAsString(path)</style>, minimized using CSSMin"),
dt("scriptWithInlineFile(String path)"),
dd("returns <script>fileAsString(path)</script>"),
dt("scriptWithInlineFile_min(String path)"),
dd("returns <script>fileAsString(path)</script>, minimized using Google Closure")
)
),
li("Java 7 support (j2html previously only worked with Java 8)"),
li("Use apache stringutils for escaping")
),
// ---------------------------------------------------------------------------------------------
h2("j2html 0.5.0 is on Maven! (Late May 2015)").withId("j2html-on-maven"),
mavenLink("0.5.0"),
p("We've finished the herculean task of releasing on Maven Central.")
)
);
}
}

View File

@@ -0,0 +1,20 @@
package app.views.pages;
import app.views.MainView;
import static j2html.TagCreator.attrs;
import static j2html.TagCreator.h2;
import static j2html.TagCreator.p;
import static j2html.TagCreator.section;
public class PageNotFoundView {
public static String render() {
return MainView.render(
"Page not found",
"Page not found",
section(attrs("#not-found"),
h2("Use the menu to get back on track!"),
p(attrs(".error-code"), "Error code 404")
)
);
}
}

View File

@@ -0,0 +1,11 @@
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 9, 10);
...
table().withId("table-example").with(
tbody().with(
each(numbers, i -> tr().with(
each(numbers, j -> td().with(
String.valueOf(i * j)
))
))
)
)

View File

@@ -0,0 +1,11 @@
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 9, 10);
...
table(attr("#table-example"),
tbody(
each(numbers, i -> tr(
each(numbers, j -> td(
String.valueOf(i * j)
))
))
)
)

View File

@@ -0,0 +1,11 @@
<html>
<head>
<title>Title</title>
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<main id="main" class="content">
<h1>Heading!</h1>
</main>
</body>
<html>

View File

@@ -0,0 +1,11 @@
html().with(
head().with(
title("Title"),
link().withRel("stylesheet").withHref("/css/main.css")
),
body().with(
main().withId("main").withClass("content").with(
h1("Heading!")
)
)
)

View File

@@ -0,0 +1,11 @@
html(
head(
title("Title"),
link().withRel("stylesheet").withHref("/css/main.css")
),
body(
main(attrs("#main.content"),
h1("Heading!")
)
)
)

View File

@@ -0,0 +1,38 @@
TagCreator.class // Static utility class for creating all tags
import static j2html.TagCreator.*; // Use static star import
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:
p(join("This paragraph has", b("bold"), "and", i("italic"), "text."))
TagCreator.iff(boolean condition, T ifValue) // If-expression for use in method calls
div().withClasses("menu-element", iff(isActive, "active"))
TagCreator.iffElse(boolean condition, T ifValue, T elseValue) // If/else-expression for use in method calls
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

@@ -0,0 +1,15 @@
// each() lets you iterate through a collection and returns the generated HTML
// as a DomContent object, meaning you can add siblings, which is not possible
// using the stream api in the previous example
body().with(
div().withId("employees").with(
p("Some sibling element"),
each(employees, employee ->
div().withClass("employee").with(
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
)
)
)

View File

@@ -0,0 +1,15 @@
// each() lets you iterate through a collection and returns the generated HTML
// as a DomContent object, meaning you can add siblings, which is not possible
// using the stream api in the previous example
body(
div(attrs("#employees"),
p("Some sibling element"),
each(employees, employee ->
div(attrs(".employee"),
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
)
)
)

View File

@@ -0,0 +1,15 @@
// filter() is meant to be used with each(). It just calls the normal
// stream().filter() method, but hides all the boilerplate Java code
// to keep your j2html code neat
body().with(
div().withId("employees").with(
p("Some sibling element"),
each(filter(employees, employee -> employee != null), employee ->
div().withClass("employee").with(
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
)
)
)

View File

@@ -0,0 +1,15 @@
// filter() is meant to be used with each(). It just calls the normal
// stream().filter() method, but hides all the boilerplate Java code
// to keep your j2html code neat
body(
div(attrs("#employees"),
p("Some sibling element"),
each(filter(employees, employee -> employee != null), employee ->
div(attrs(".employee"),
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
)
)
)

View File

@@ -0,0 +1,19 @@
<body>
<div id="employees">
<div class="employee">
<h2>David</h2>
<img src="/img/david.png">
<p>Creator of Bad Libraries</p>
</div>
<div class="employee">
<h2>Christian</h2>
<img src="/img/christian.png">
<p>Fanboi of Jenkins</p>
</div>
<div class="employee">
<h2>Paul</h2>
<img src="/img/paul.png">
<p>Hater of Lambda Expressions</p>
</div>
</div>
</body>

View File

@@ -0,0 +1,11 @@
body().with(
div().withId("employees").with(
employees.stream().map(employee ->
div().withClass("employee").with(
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
).toArray(ContainerTag[]::new)
)
)

View File

@@ -0,0 +1,11 @@
body(
div(attrs("#employees"),
employees.stream().map(employee ->
div(attrs(".employee"),
h2(employee.getName()),
img().withSrc(employee.getImgPath()),
p(employee.getTitle())
)
).toArray(ContainerTag[]::new)
)
)

View File

@@ -0,0 +1,22 @@
<html>
<head>
<title>Signup page</title>
</head>
<body>
<header>
...
</header>
<main>
<h1>Please sign up</h1>
<form method="post">
<input type="email" id="email" name="email" placeholder="Email address" required>
<input type="password" id="choosePassword" name="choosePassword" placeholder="Choose password" required>
<input type="password" id="repeatPassword" name="repeatPassword" placeholder="Repeat password" required>
<button type="submit">Sign up</button>
</form>
</main>
<footer>
...
</footer>
</body>
</html>

View File

@@ -0,0 +1,32 @@
public class MainView {
public static String render(String pageTitle, Tag... tags) {
return document().render() +
html().with(
head().with(
title(pageTitle)
),
body().with(
header().with(
...
),
main().with(
tags //the view from the partials example
),
footer().with(
...
)
)
).render();
}
}
MainView.render(
"Signup page",
h1("Please sign up"),
form().withMethod("post").with(
emailInput("Email address"),
choosePasswordInput("Choose Password"),
repeatPasswordInput("Repeat Password"),
submitButton("Sign up")
)
);

View File

@@ -0,0 +1,33 @@
public class MainView {
public static String render(String pageTitle, Tag... tags) {
return document(
html(
head(
title(pageTitle)
),
body(
header(
...
),
main(
tags //the view from the partials example
),
footer(
...
)
)
)
);
}
}
MainView.render(
"Signup page",
h1("Please sign up"),
form().withMethod("post").with(
emailInput("Email address"),
choosePasswordInput("Choose Password"),
repeatPasswordInput("Repeat Password"),
submitButton("Sign up")
)
);

View File

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

View File

@@ -0,0 +1,4 @@
<body>
<h1>Hello, World!</h1>
<img src="/img/hello.png">
</body>

View File

@@ -0,0 +1,10 @@
import static j2html.TagCreator.*;
public class Main {
public static void main(String[] args) {
body(
h1("Hello, World!"),
img().withSrc("/img/hello.png")
).render();
}
}

View File

@@ -0,0 +1,7 @@
#include("/path/to/header")
#setTitle("Signup page")
<h1>Please sign up</h1>
<form>
...
</form>
#include("/path/to/footer")

View File

@@ -0,0 +1,33 @@
<input
type="password"
id="enterPassword"
name="enterPassword"
placeholder="Enter password"
required
>
<input
type="password"
id="choosePassword"
name="choosePassword"
placeholder="Choose password"
required
>
<input
type="password"
id="repeatPassword"
name="repeatPassword"
placeholder="Repeat password"
required
>
<input
type="email"
id="email"
name="email"
placeholder="Email"
required
>
<button type="submit">Text</button>

View File

@@ -0,0 +1,33 @@
public static Tag enterPasswordInput(String placeholder) {
return passwordInput("enterPassword", placeholder);
}
public static Tag choosePasswordInput(String placeholder) {
return passwordInput("choosePassword", placeholder);
}
public static Tag repeatPasswordInput(String placeholder) {
return passwordInput("repeatPassword", placeholder);
}
public static Tag passwordInput(String identifier, String placeholder) {
return input()
.withType("password")
.withId(identifier)
.withName(identifier)
.withPlaceholder(placeholder)
.isRequired();
}
public static Tag emailInput(String placeholder) {
return input()
.withType("email")
.withId("email")
.withName("email")
.withPlaceholder(placeholder)
.isRequired();
}
public static Tag submitButton(String text) {
return button().withType("submit").withText(text);
}

View File

@@ -0,0 +1,33 @@
public static Tag enterPasswordInput(String placeholder) {
return passwordInput("enterPassword", placeholder);
}
public static Tag choosePasswordInput(String placeholder) {
return passwordInput("choosePassword", placeholder);
}
public static Tag repeatPasswordInput(String placeholder) {
return passwordInput("repeatPassword", placeholder);
}
public static Tag passwordInput(String identifier, String placeholder) {
return input()
.withType("password")
.withId(identifier)
.withName(identifier)
.withPlaceholder(placeholder)
.isRequired();
}
public static Tag emailInput(String placeholder) {
return input()
.withType("email")
.withId("email")
.withName("email")
.withPlaceholder(placeholder)
.isRequired();
}
public static Tag submitButton(String text) {
return button(text).withType("submit");
}

View File

@@ -0,0 +1,7 @@
<h1>Please sign up</h1>
<form method="post">
<input type="email" id="email" name="email" placeholder="Email address" required>
<input type="password" id="choosePassword" name="choosePassword" placeholder="Choose password" required>
<input type="password" id="repeatPassword" name="repeatPassword" placeholder="Repeat password" required>
<button type="submit">Sign up</button>
</form>

View File

@@ -0,0 +1,7 @@
h1("Please sign up"),
form().withMethod("post").with(
emailInput("Email address"),
choosePasswordInput("Choose Password"),
repeatPasswordInput("Repeat Password"),
submitButton("Sign up")
)

View File

@@ -0,0 +1,7 @@
h1("Please sign up"),
form().withMethod("post").with(
emailInput("Email address"),
choosePasswordInput("Choose Password"),
repeatPasswordInput("Repeat Password"),
submitButton("Sign up")
)

View File

@@ -0,0 +1,3 @@
<a id="fork-me" href="https://github.com/tipsy/j2html" target="_blank">
<img style="position:fixed;top:0;right:0;border:0;z-index:99999" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJUAAACVCAMAAABmfEh9AAAAllBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPEhwAAAAFBgkAAAARFR8fIiwRFB4YHCUQEx0RFB4SFiERFB8QEx3s7e4eIStPUlpKTFUvMjwOEhs8P0hfYmlXWmE5PUUZHSfd3uAnKjTAwcRnanB2eH5CRU6TlZqxs7bP0NKFh4yUlpqipKiUlpujpKiipKdzB4/DAAAAFXRSTlMAAwgBDREiKBYbL5wdQBjb3ZzclY3cWcIvAAAGZ0lEQVR42u2c3W7bMAyF02bL2q5bu7WLqDhW7Si24p902/u/3EglljxXQX1jixclsIv0QvhAHvAQZLJFF8vl1dX19afn/Ub9ej+2L5ufd6v725vPn66vr66ulsvFBOGoPj/LLJHvY8kkf/j2ZTUtlqe6ed6+JCOytX/ZPCLW/aRYnur2aZeo3QisjXogrO+TYjmq1ROqZgQWlvpxaiyisljf71dP+yQfJflk8mwtfbK+/NhnmxGSx1L/nBhraZP1ySbr7sc2O4xrEIMiTpqsu6+7zWGU5PNpi0hUfaxRkl9jqR9nxZLjJJ8d5s3W/mWU5Dfq56xY4z1x3iJuxnrinFg/xnrizJLfJZeLuMboJK9mbxDyIpPjkpvD48ySz4NQLpwnzopFY2CYqfzti7ibXfLZIQQlSoBjT1sRJL8bUgmCaiyWHwOjemKXqVIWYDwWljqmJ56h6jSVfyR+YOGJlCpbvipNhXBUbgycVfKqR3UuX0JUGNE8kcZA2VEphMLyZdIYabGieeJX74mYKyNEmiYaQCuLFdMTcy8sIUSuwWT6lYpI4SUfwRM9Vk1lbLQYYO3m90TVyxa1hgqqVEmHFdkTCaLRWQVa5rrAdMla+WzNPwYeXLaUBihyVBfUUhaAVh1zDNx1yZJllpLk20IWzqjjeaJXfK5J8ghVuQYRzxNdtpoOKt0674kjeVqNdFjqBAUatLJUMT3x0GtbCFXAqymVK2GkMZA80WH9gaqFLbVSGwxWI1ZbRhSFEAGsaKsRy7GFVlgsZT8yWI1YqkKtiUrpo+sPzhPjrEYsFv4jKGiMw/JjYCxPpCCotoDSYnFYjVgqRV3eQNNhRfdEwtoSVK51A+2wiBsVbww0BAWZBC08VuTVCAkroXQl8DdFLD6eaMCmK9+bTLzxxIdonqgIqixpGJTRPdGNgYIknwG0GZT4kcW5gLBQ8g0UugRzVhaLMVCIFF5lAXq7tsHhXGCxdCNlUboJItYYSEXMlOvxJTRK9AabqOeCg+ywslbYSExyIovuiQRxnpuPAICDDY/VSId1hCKrQHVtnsVqZJ1AIdMSTJqc2zyL1YiBhBr9voLOfTisRkqoMg2mAizksEHk8TwRxU5QhdGwdZKPey6wA7PZViQurOW6i/irESEMQckGc+UjqidaAtEiVAGtEJ7KST6KJ67dDuLYdfrWyp7BamTdnqFkDRj2vMJgNbI2lDFKmW6zGtqe5OOcC7z7EFSxRbhae6r1PoviiV71BhpJh6iyPCk++oXMUmm0HwyB8bvlcC74dXJqZLLZSkD/3yDirUYkNGmaN3maJsJkwmJFXo2c+kPdApi0hDaV557K4FyATFAilM6roupafTRPlJ3eZXGCMgAVZuuo4nnifuM9UQmCkgWQY1fQSPpjrNWIz5Y6QVVlhVBaifieaLFKldZ01SeoPBVSRfXEXYclEqgt1On0oxHrwmrEUU05BjptQZNQprStZf/uIwee6F6bYwwsa0nly7UmKHHxWyOeap4x0ICmFg9WWvSX4DcpHdWUReydC2StTpe7WuZdkw+sRgYPTrcacZonKF2i2nOkCntij2omTxQERWovpNsmvfl1weC56TzR33wwS9S5ZFoqGrjK3hioHk5Yg9em9ESnLZUa6lwlNFIc7br5/3PB7c3gsUk90WurAWnNOq3cDrzvibfDxyYdAx1WDcZ0UKStwbngPvDcHOeCGk5QUGSOynviKvDaHGOgqBHKb2wGP6i5Cz43xwldpd3GJnAuWIRj+hO6MABJKvu58p4YemtiT/RYr0b3Tyte8oGX5pK8qAGg9WL3sV1cjum/NVKabjk5iMvPTbwa6e//IlBdWo14qAhU75wLkCkC1bs/qIlFFfZEBlSBMZADVcgTGVCFJM+A6o3kk5wDVWg1woAq4IkcqEKeyIAq4Ik7BlSBMZADVWgMZEAV8EQOVCFPZEAV8EQOVAFP5EAV8kQGVIHVCAeqt5LfSAZUAU/kQBX6JiUDqtBqhAFVwBM5UIU8kQHVW8krDlShcwEDqoAncqAaYqEncqAaYpEnMqD6D2v15QnHQA5Ufaz71eqJxsAFg/BY35HriXbIHMJhEdftM+6QWUSHRVw3+L+VLXhEh4VcGM8LJnHGQjCKBZcgLOKysWATiEXBjAqxiMvGglEsMfhRWS4bC3bBk+ojPuIjPiIc/wAQkAE6ZqHsywAAAABJRU5ErkJggg==" alt="Fork me on GitHub">
</a>

View File

@@ -0,0 +1,7 @@
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-W4XBCK"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-W4XBCK');</script>

View File

@@ -0,0 +1,328 @@
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
}
html {
position: relative;
min-height: 100%;
font-family: arial, sans-serif;
overflow-y: scroll;
background: #fafafa;
}
body {
height: 100%;
margin-bottom: 50px;
}
h1, h2, h3, nav a {
font-family: 'Patua One', cursive;
font-weight: 400;
}
h1, h2, h3, h4 {
margin-top: 1.5em;
margin-bottom: 0.2em;
}
h1 + p, h2 + p, h3 + p {
margin-top: 0;
}
h2, h3 {
color: rgba(0, 0, 0, 0.7);
}
pre + p {
margin-top: 2em;
}
#news h2:not(:first-of-type) {
margin-top: 2em;
}
a {
text-decoration: none;
color: #1272BF;
}
p,
li {
font-family: "Lato", Arial, sans-serif;
line-height: 1.5;
color: rgba(0, 0, 0, 0.7);
font-size: 17px;
}
li {
margin: 5px 0;
}
dt {
font-family: monospace;
font-weight: 700;
margin-top: 10px;
font-size: 14px;
}
dd {
margin: 0;
font-size: 13px;
}
.width-limit {
padding: 0 20px;
max-width: 940px;
margin: 0 auto;
}
#github-stars {
position: absolute;
right: 27px;
top: 10px;
width: 100px;
height: 30px;
}
#logo {
font-size: 20px;
font-style: italic;
}
#logo img {
display: block;
height: 50px;
}
.top-header {
background: #fff;
}
.top-header nav {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
}
.top-header ul {
margin: 0;
padding: 0;
list-style: none;
}
.top-header li {
display: inline-block;
margin: 0 10px;
}
.top-header ul li a {
display: inline-block;
padding: 8px;
font-size: 22px;
}
.banner {
background: #1272BF;
margin: 0;
color: white;
padding: 0 25px;
height: 100px;
line-height: 100px;
border-top: 3px solid #0d4d80;
border-bottom: 3px solid #0d4d80;
}
.banner h1 {
font-size: 40px;
text-shadow: 4px 4px 0 rgba(0, 0, 0, 0.3);
}
p code,
li code {
font-size: 85%;
font-family: monospace;
background: rgba(30, 177, 204, 0.1);
border: 1px solid rgba(26, 142, 167, 0.49);
padding: 1px 3px;
border-radius: 3px;
}
footer {
font-size: 13px;
position: absolute;
left: 0;
bottom: 0;
height: 40px;
width: 100%;
line-height: 40px;
text-align: center;
background: #f5f5f5;
border-top: 1px solid #ddd;
padding: 0 20px;
overflow: hidden;
}
.lols {
text-align: center;
font-size: 9px;
margin: 0;
color: #777;
}
#table-example {
text-align: center;
width: 100%;
background: rgb(255, 255, 255);
border-spacing: 5px 20px;
font-weight: 700;
border: 1px solid #ddd;
border-radius: 5px;
}
@media screen and (max-width: 1024px) {
#fork-me {
display: none;
}
.top-header {
border-bottom: 1px solid #ccc;
}
}
@media screen and (max-width: 768px) {
.banner {
display: none;
}
.top-header nav ul li {
margin: 0 5px;
}
.top-header nav ul li a {
font-size: 16px;
}
#logo {
font-size: 0;
}
}
@media screen and (max-width: 460px) {
#logo {
display: none;
}
.top-header nav ul li {
margin: 0 2px;
}
.top-header nav ul li a {
font-size: 14px;
}
}
/* Prism */
.code-compare ul {
background: #272822;
border-radius: 5px 5px 0 0;
list-style: none;
margin: 0;
border-bottom: 1px solid #464644;
padding: 0;
height: 28px;
}
.code-compare ul li {
color: #999;
display: inline-block;
margin: 0;
cursor: pointer;
height: 28px;
line-height: 28px;
font-size: 13px;
padding: 0 10px;
}
.code-compare ul li + li {
border-left: 1px solid #464644;
}
.code-compare pre {
display: none;
margin-top: 0;
border-radius: 0 0 5px 5px;
}
.code-compare.with .with-switch,
.code-compare.nowith .nowith-switch {
color: #eee;
}
.code-compare.with .with-pre,
.code-compare.nowith .nowith-pre {
display: block;
}
pre {
background: #272822;
padding: 15px !important;
border-radius: 5px;
}
pre::-webkit-scrollbar {
width: 5px;
height: 5px;
}
pre::-webkit-scrollbar-thumb {
background: #777;
border-radius: 5px;
}
pre::-webkit-scrollbar-thumb:hover {
background: #999;
}
pre::-webkit-scrollbar-track {
background: #333;
border-radius: 5px;
}
pre::-webkit-scrollbar-button {
width: 8px;
height: 8px;
}
#javalin-suggestion {
line-height: 2;
position: fixed;
bottom: 30px;
right: 30px;
padding: 20px 40px 20px 20px;
background: #fff;
box-shadow: 4px 4px 0 0px rgba(0, 0, 0, .2);
z-index: 1;
display: none;
font-size: 17px;
max-width: calc(100% - 60px);
border: 2px solid #009ebe;
}
#javalin-suggestion .close {
cursor: pointer;
position: absolute;
top: 11px;
right: 15px;
}
#javalin-suggestion a {
font-weight: 700;
}

View File

@@ -0,0 +1,121 @@
/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript+java */
/**
* okaidia theme for JavaScript, CSS and HTML
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
* @author ocodia
*/
code[class*="language-"],
pre[class*="language-"] {
color: #f8f8f2;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #272822;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #f8f8f2;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #f92672;
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #f8f8f2;
}
.token.atrule,
.token.attr-value,
.token.function {
color: #e6db74;
}
.token.keyword {
color: #66d9ef;
}
.token.regex,
.token.important {
color: #fd971f;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="512px" height="512px" viewBox="0 0 512 512" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
<desc>Created with Lunacy</desc>
<defs>
<path d="M0 0L512 0L512 512L0 512L0 0Z" id="path_1" />
<rect width="512" height="512" id="artboard_1" />
<clipPath id="clip_1">
<use xlink:href="#artboard_1" clip-rule="evenodd" />
</clipPath>
<clipPath id="mask_1">
<use xlink:href="#path_1" />
</clipPath>
</defs>
<g id="Artboard-Copy-3" clip-path="url(#clip_1)">
<g id="HTML5_Badge">
<path d="M0 0L512 0L512 512L0 512L0 0Z" id="Background" fill="none" stroke="none" />
<g clip-path="url(#mask_1)">
<path d="M41 460L0 0L451 0L410 460L225 512" transform="translate(30 0)" id="Shape" fill="#1272BF" stroke="none" />
<path d="M0 435L149 394L184 0L0 0" transform="translate(256 37)" id="Shape" fill="#FFFFFF" fill-opacity="0.21176471" stroke="none" />
</g>
</g>
<g id="coffee-icon-12" transform="translate(122.26813 68.45154)">
<g id="Group">
<path d="M109.76 63.5233C72.71 61.7926 37.5831 55.1903 18.2249 46.4086C5.14845 40.4472 -1.64617 32.627 0.340938 25.8965C3.22545 16.2815 18.7377 7.94844 45.8521 1.41021L51.7493 0L56.2363 4.23062C58.7362 6.53823 61.8771 9.16634 63.2232 10.1278C64.6975 11.0893 65.9154 12.4355 66.1718 13.4611C66.3641 14.4226 67.582 16.7302 68.864 18.5891C74.1202 26.2811 84.5686 31.1527 101.812 33.8449C112.452 35.5756 153.028 35.5756 164.117 33.909C181.424 31.2809 192.193 26.3452 197.449 18.5891C198.731 16.7302 199.949 14.4226 200.141 13.4611C200.398 12.4996 201.551 11.0893 202.897 10.1919C204.244 9.35865 207.384 6.66643 209.884 4.35882L214.564 1.95618e-05L220.461 1.41023C251.742 8.78175 269.177 19.9993 265.972 30.7681C264.818 34.55 261.677 38.2678 256.87 41.4728C233.025 57.3056 171.937 66.4719 109.76 63.5233L109.76 63.5233Z" transform="translate(0 276.47668)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M83.9789 173.473C64.877 172.256 51.8006 168.217 47.9546 162.32C47.3777 161.423 46.7367 159.435 46.6084 157.897C46.2879 155.397 45.9033 154.82 43.2752 152.961C38.5959 149.564 29.8142 140.334 25.0708 133.731C20.0069 126.68 12.1225 111.04 9.30215 102.45C3.14853 83.9255 0.199922 64.5031 0.00761933 41.8116C-0.0564799 29.1198 0.199922 27.8378 4.11003 22.838C6.73814 19.633 14.1738 14.6332 20.5197 12.0051C54.3005 -2.09699 127.952 -4.14819 171.732 7.83854C187.565 12.1974 198.334 18.6074 202.436 26.1712C204.038 29.0557 204.359 30.4659 204.615 35.8503L204.622 35.9837C204.936 41.9426 204.946 42.1293 206.282 41.7475C206.987 41.4911 210.833 41.2988 214.679 41.2988C225.384 41.3629 232.371 43.991 238.012 50.1447C250.96 64.2467 246.729 91.297 228.525 110.271C217.371 121.873 203.333 129.116 186.924 131.744L180.193 132.834L177.95 135.718C172.822 142.128 167.373 147.769 162.886 151.423C158.591 154.884 158.079 155.525 157.822 157.961C157.374 161.358 156.092 163.41 152.694 165.846C143.72 172.32 115.452 175.461 83.9789 173.473ZM179.103 35.2734C179.103 41.1706 159.104 46.9396 130.708 49.119C121.285 49.8241 97.4399 50.401 89.6838 50.0805C57.2491 48.6703 33.0833 44.1192 26.7374 38.1579C23.5965 35.2093 25.4554 31.9402 32.0577 28.9274C40.2625 25.1455 55.0056 22.0687 74.2997 20.2098C85.1967 19.1842 119.362 19.1842 130.067 20.2739C160.322 23.2225 179.103 28.9915 179.103 35.2734ZM48.5956 75.2078C49.9416 100.591 56.0312 124.565 65.3898 141.295C66.6077 143.539 67.5051 145.333 67.3128 145.333C66.159 145.333 50.9673 133.09 47.6981 129.501C36.737 117.514 28.8527 101.168 24.3657 81.2332C22.9555 74.695 20.5838 59.4391 20.9684 58.9904C21.0966 58.9263 23.8529 59.8878 27.1861 61.1698C30.5193 62.3877 36.4165 64.1185 40.3266 65.0159C44.1726 65.9133 47.5058 66.6824 47.7622 66.8107C47.9545 67.003 48.3391 70.7208 48.5956 75.2078ZM201.731 105.399C221.41 95.2071 231.089 77.4513 222.884 66.5543C220.256 63.0929 216.474 61.8109 210.064 62.1314C207.115 62.2596 204.423 62.7083 204.039 63.0929C203.654 63.4775 202.757 67.5158 202.116 72.0669C200.577 82.7075 198.205 92.9636 195.129 102.13C194.955 102.641 194.789 103.12 194.634 103.57C193.539 106.749 192.961 108.429 193.474 108.889C194.05 109.405 196 108.388 200.133 106.231C200.633 105.97 201.165 105.693 201.731 105.399Z" transform="translate(30.973198 125.50244)" id="Shape" fill="#FFFFFF" fill-rule="evenodd" stroke="none" />
<path d="M1.14148 89.3819C4.64379 86.2444 7.12459 81.9395 8.43796 76.759C9.75133 71.5055 9.6054 66.5439 7.63534 50.4917C4.93565 29.113 10.335 17.4386 27.7007 6.93166C32.2975 4.23196 34.0486 3.28342 39.375 1.09447L41.9288 0L38.6454 2.77266C32.8812 7.66131 29.8896 11.3095 27.3358 16.563C23.5417 24.2243 22.885 30.2074 24.3443 43.633C25.7306 56.7666 25.8036 61.2175 24.4902 66.6169C22.4472 75.2997 15.5885 82.961 5.00862 88.3604C-0.536715 91.1331 -0.901539 91.206 1.14148 89.3819L1.14148 89.3819Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 109.50348 7.280884)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M2.2619 64.5395C7.44241 59.1401 8.53688 53.3759 6.63979 41.6286C4.15899 25.8682 5.54532 19.1554 12.9148 12.0048C16.0523 8.86735 19.1168 6.82434 24.6621 4.1976C34.2935 -0.472146 36.8472 -1.20179 32.6882 1.78976C27.9455 5.29208 24.5891 8.94032 22.4002 13.0993C20.5031 16.7476 20.4301 17.1853 20.722 26.3789C21.3057 46.7361 21.2328 49.5817 19.8464 52.1355C16.8549 57.6808 9.77728 63.2991 2.18894 66.1447L0 66.9473L2.26191 64.5395L2.2619 64.5395Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 67.97293 38.71814)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M2.43573 63.7984C7.25141 58.326 7.98106 53.5833 5.86508 40.8875C3.01946 23.5948 6.30288 14.7661 18.1961 7.32363C22.72 4.55097 33.2269 -0.410636 33.7376 0.0271519C33.8836 0.173082 32.2054 1.70534 30.0164 3.38353C25.2737 7.10474 22.2092 11.3367 20.458 16.5902C19.5095 19.6547 19.3635 21.7707 19.7284 27.0971C21.1877 47.3813 21.1877 47.4543 19.4365 51.4674C17.3935 56.356 12.3589 61.0257 5.64618 64.3821C2.80055 65.8414 0.319755 67.0089 0.0278784 67.0089C-0.191007 67.0089 0.903465 65.5496 2.43571 63.7984L2.43573 63.7984Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 160.54498 22.258545)" id="Shape" fill="#FFFFFF" stroke="none" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="1720px" height="512px" viewBox="0 0 1720 512" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
<desc>Created with Lunacy</desc>
<defs>
<path d="M0 0L512 0L512 512L0 512L0 0Z" id="path_1" />
<rect width="1720" height="512" id="artboard_1" />
<clipPath id="clip_1">
<use xlink:href="#artboard_1" clip-rule="evenodd" />
</clipPath>
<clipPath id="mask_1">
<use xlink:href="#path_1" />
</clipPath>
</defs>
<g id="Artboard-Copy-2" clip-path="url(#clip_1)">
<g id="HTML5_Badge">
<path d="M0 0L512 0L512 512L0 512L0 0Z" id="Background" fill="none" stroke="none" />
<g clip-path="url(#mask_1)">
<path d="M41 460L0 0L451 0L410 460L225 512" transform="translate(30 0)" id="Shape" fill="#1272BF" stroke="none" />
<path d="M0 435L149 394L184 0L0 0" transform="translate(256 37)" id="Shape" fill="#FFFFFF" fill-opacity="0.21176471" stroke="none" />
</g>
</g>
<g id="coffee-icon-12" transform="translate(122.26813 68.45154)">
<g id="Group">
<path d="M109.76 63.5233C72.71 61.7926 37.5831 55.1903 18.2249 46.4086C5.14845 40.4472 -1.64617 32.627 0.340938 25.8965C3.22545 16.2815 18.7377 7.94844 45.8521 1.41021L51.7493 0L56.2363 4.23062C58.7362 6.53823 61.8771 9.16634 63.2232 10.1278C64.6975 11.0893 65.9154 12.4355 66.1718 13.4611C66.3641 14.4226 67.582 16.7302 68.864 18.5891C74.1202 26.2811 84.5686 31.1527 101.812 33.8449C112.452 35.5756 153.028 35.5756 164.117 33.909C181.424 31.2809 192.193 26.3452 197.449 18.5891C198.731 16.7302 199.949 14.4226 200.141 13.4611C200.398 12.4996 201.551 11.0893 202.897 10.1919C204.244 9.35865 207.384 6.66643 209.884 4.35882L214.564 1.95618e-05L220.461 1.41023C251.742 8.78175 269.177 19.9993 265.972 30.7681C264.818 34.55 261.677 38.2678 256.87 41.4728C233.025 57.3056 171.937 66.4719 109.76 63.5233L109.76 63.5233Z" transform="translate(0 276.47668)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M83.9789 173.473C64.877 172.256 51.8006 168.217 47.9546 162.32C47.3777 161.423 46.7367 159.435 46.6084 157.897C46.2879 155.397 45.9033 154.82 43.2752 152.961C38.5959 149.564 29.8142 140.334 25.0708 133.731C20.0069 126.68 12.1225 111.04 9.30215 102.45C3.14853 83.9255 0.199922 64.5031 0.00761933 41.8116C-0.0564799 29.1198 0.199922 27.8378 4.11003 22.838C6.73814 19.633 14.1738 14.6332 20.5197 12.0051C54.3005 -2.09699 127.952 -4.14819 171.732 7.83854C187.565 12.1974 198.334 18.6074 202.436 26.1712C204.038 29.0557 204.359 30.4659 204.615 35.8503L204.622 35.9837C204.936 41.9426 204.946 42.1293 206.282 41.7475C206.987 41.4911 210.833 41.2988 214.679 41.2988C225.384 41.3629 232.371 43.991 238.012 50.1447C250.96 64.2467 246.729 91.297 228.525 110.271C217.371 121.873 203.333 129.116 186.924 131.744L180.193 132.834L177.95 135.718C172.822 142.128 167.373 147.769 162.886 151.423C158.591 154.884 158.079 155.525 157.822 157.961C157.374 161.358 156.092 163.41 152.694 165.846C143.72 172.32 115.452 175.461 83.9789 173.473ZM179.103 35.2734C179.103 41.1706 159.104 46.9396 130.708 49.119C121.285 49.8241 97.4399 50.401 89.6838 50.0805C57.2491 48.6703 33.0833 44.1192 26.7374 38.1579C23.5965 35.2093 25.4554 31.9402 32.0577 28.9274C40.2625 25.1455 55.0056 22.0687 74.2997 20.2098C85.1967 19.1842 119.362 19.1842 130.067 20.2739C160.322 23.2225 179.103 28.9915 179.103 35.2734ZM48.5956 75.2078C49.9416 100.591 56.0312 124.565 65.3898 141.295C66.6077 143.539 67.5051 145.333 67.3128 145.333C66.159 145.333 50.9673 133.09 47.6981 129.501C36.737 117.514 28.8527 101.168 24.3657 81.2332C22.9555 74.695 20.5838 59.4391 20.9684 58.9904C21.0966 58.9263 23.8529 59.8878 27.1861 61.1698C30.5193 62.3877 36.4165 64.1185 40.3266 65.0159C44.1726 65.9133 47.5058 66.6824 47.7622 66.8107C47.9545 67.003 48.3391 70.7208 48.5956 75.2078ZM201.731 105.399C221.41 95.2071 231.089 77.4513 222.884 66.5543C220.256 63.0929 216.474 61.8109 210.064 62.1314C207.115 62.2596 204.423 62.7083 204.039 63.0929C203.654 63.4775 202.757 67.5158 202.116 72.0669C200.577 82.7075 198.205 92.9636 195.129 102.13C194.955 102.641 194.789 103.12 194.634 103.57C193.539 106.749 192.961 108.429 193.474 108.889C194.05 109.405 196 108.388 200.133 106.231C200.633 105.97 201.165 105.693 201.731 105.399Z" transform="translate(30.973198 125.50244)" id="Shape" fill="#FFFFFF" fill-rule="evenodd" stroke="none" />
<path d="M1.14148 89.3819C4.64379 86.2444 7.12459 81.9395 8.43796 76.759C9.75133 71.5055 9.6054 66.5439 7.63534 50.4917C4.93565 29.113 10.335 17.4386 27.7007 6.93166C32.2975 4.23196 34.0486 3.28342 39.375 1.09447L41.9288 0L38.6454 2.77266C32.8812 7.66131 29.8896 11.3095 27.3358 16.563C23.5417 24.2243 22.885 30.2074 24.3443 43.633C25.7306 56.7666 25.8036 61.2175 24.4902 66.6169C22.4472 75.2997 15.5885 82.961 5.00862 88.3604C-0.536715 91.1331 -0.901539 91.206 1.14148 89.3819L1.14148 89.3819Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 109.50348 7.280884)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M2.2619 64.5395C7.44241 59.1401 8.53688 53.3759 6.63979 41.6286C4.15899 25.8682 5.54532 19.1554 12.9148 12.0048C16.0523 8.86735 19.1168 6.82434 24.6621 4.1976C34.2935 -0.472146 36.8472 -1.20179 32.6882 1.78976C27.9455 5.29208 24.5891 8.94032 22.4002 13.0993C20.5031 16.7476 20.4301 17.1853 20.722 26.3789C21.3057 46.7361 21.2328 49.5817 19.8464 52.1355C16.8549 57.6808 9.77728 63.2991 2.18894 66.1447L0 66.9473L2.26191 64.5395L2.2619 64.5395Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 67.97293 38.71814)" id="Shape" fill="#FFFFFF" stroke="none" />
<path d="M2.43573 63.7984C7.25141 58.326 7.98106 53.5833 5.86508 40.8875C3.01946 23.5948 6.30288 14.7661 18.1961 7.32363C22.72 4.55097 33.2269 -0.410636 33.7376 0.0271519C33.8836 0.173082 32.2054 1.70534 30.0164 3.38353C25.2737 7.10474 22.2092 11.3367 20.458 16.5902C19.5095 19.6547 19.3635 21.7707 19.7284 27.0971C21.1877 47.3813 21.1877 47.4543 19.4365 51.4674C17.3935 56.356 12.3589 61.0257 5.64618 64.3821C2.80055 65.8414 0.319755 67.0089 0.0278784 67.0089C-0.191007 67.0089 0.903465 65.5496 2.43571 63.7984L2.43573 63.7984Z" transform="matrix(0.9848077 -0.17364818 0.17364818 0.9848077 160.54498 22.258545)" id="Shape" fill="#FFFFFF" stroke="none" />
</g>
</g>
<path d="M364 10.8L376.4 0L438 0L422.8 125.2C428.667 115.067 436.267 106.867 445.6 100.6C454.933 94.3334 465.867 91.2 478.4 91.2Q502.8 91.2 519 106.4C529.8 116.533 533.867 132.267 531.2 153.6L521.2 236L544.4 236L540.8 265.2L528.8 275.2L467.2 275.2L479.6 173.6C481.733 158.133 480.667 147.067 476.4 140.4C472.133 133.733 465.067 130.4 455.2 130.4C443.733 130.4 435.067 134 429.2 141.2Q420.4 152 417.2 169.6L404.4 275.2L354.8 275.2L383.6 39.2L360.4 39.2L364 10.8ZM1052 0L1039.6 10.8L1036 39.2L1060.8 39.2L1032 275.2L1094 275.2L1106 265.2L1109.6 236L1086.4 236L1115.2 0L1052 0ZM79.2 62.8L82.0851 35.1857Q82.1542 34.5246 82.2534 33.8673Q82.3527 33.2101 82.482 32.5581Q82.6113 31.9061 82.7704 31.2607Q82.9294 30.6154 83.1178 29.9779Q83.3063 29.3405 83.5237 28.7124Q83.7411 28.0843 83.9872 27.4668Q84.2331 26.8493 84.5072 26.2437Q84.7812 25.6381 85.0827 25.0458Q85.3842 24.4534 85.7125 23.8754Q86.0408 23.2975 86.3953 22.7352Q86.7497 22.1729 87.1295 21.6274Q87.5094 21.0819 87.9138 20.5545Q88.3183 20.027 88.7464 19.5186Q89.1746 19.0101 89.6256 18.5219Q90.0767 18.0336 90.5496 17.5666Q91.0225 17.0995 91.5163 16.6546Q92.0101 16.2097 92.5238 15.7878Q93.0375 15.366 93.57 14.9682Q94.1025 14.5703 94.6526 14.1973Q95.2028 13.8243 95.7694 13.4768Q96.3361 13.1294 96.9181 12.8083Q97.5 12.4872 98.0961 12.1931Q98.6922 11.899 99.3012 11.6325Q99.9101 11.366 100.531 11.1277Q101.151 10.8894 101.782 10.6798Q102.413 10.4702 103.052 10.2897Q103.692 10.1092 104.339 9.95819Q104.987 9.80719 105.64 9.68601Q106.294 9.56483 106.952 9.47372Q107.611 9.38261 108.273 9.32177Q108.934 9.26092 109.598 9.23047Q110.262 9.20001 110.927 9.20001L132.8 9.20001L129.915 36.8144Q129.846 37.4754 129.747 38.1327Q129.647 38.7899 129.518 39.4419Q129.389 40.0939 129.23 40.7393Q129.071 41.3847 128.882 42.0221Q128.694 42.6595 128.476 43.2876Q128.259 43.9157 128.013 44.5332Q127.767 45.1507 127.493 45.7563Q127.219 46.3619 126.917 46.9543Q126.616 47.5466 126.288 48.1246Q125.959 48.7025 125.605 49.2648Q125.25 49.8271 124.87 50.3726Q124.491 50.9181 124.086 51.4456Q123.682 51.9731 123.254 52.4815Q122.825 52.9899 122.374 53.4781Q121.923 53.9664 121.45 54.4334Q120.978 54.9005 120.484 55.3454Q119.99 55.7904 119.476 56.2122Q118.963 56.634 118.43 57.0319Q117.898 57.4297 117.347 57.8027Q116.797 58.1758 116.231 58.5232Q115.664 58.8706 115.082 59.1917Q114.5 59.5128 113.904 59.8069Q113.308 60.101 112.699 60.3675Q112.09 60.634 111.469 60.8723Q110.849 61.1106 110.218 61.3202Q109.587 61.5298 108.948 61.7103Q108.308 61.8908 107.661 62.0418Q107.013 62.1928 106.36 62.314Q105.706 62.4352 105.048 62.5263Q104.389 62.6174 103.727 62.6782Q103.066 62.7391 102.402 62.7695Q101.738 62.8 101.073 62.8L79.2 62.8ZM578.8 94.8L565.2 107.2L561.6 134L585.6 134L574.8 221.2C572.667 239.867 576.867 254.2 587.4 264.2C597.933 274.2 612.8 279.2 632 279.2C649.867 279.2 663.733 274.4 673.6 264.8C683.467 255.2 690.933 240 696 219.2L661.2 207.6Q656 228.4 649.8 234.2C645.667 238.067 641.467 240 637.2 240Q632.752 240 629.894 237.868Q623.241 232.907 625.2 216.4L635.2 134L676.8 134L690.4 121.2L694 94.8L640 94.8L646.8 40.0001L610.8 40.0001L595.2 56L590.4 94.8L578.8 94.8ZM158.4 139.6L198.4 144L207.6 114.4C211.867 112.267 217.2 110.733 223.6 109.8C230 108.867 235.467 108.4 240 108.4C248.8 108.4 256.333 110 262.6 113.2C268.867 116.4 272 122 272 130C272 140.933 265.933 151.733 253.8 162.4Q235.6 178.4 206.2 195.4C186.6 206.733 165.467 219.2 142.8 232.8L143.2 275.2L321.2 275.2L330 204.4L288.8 200.4L284.4 229.6L206.8 229.6C225.2 220.267 243.867 210.667 262.8 200.8C281.733 190.933 297.6 179.4 310.4 166.2Q329.6 146.4 329.6 118.4Q329.6 96 313.2 79.4C302.267 68.3333 282.533 62.8 254 62.8Q240 62.8 218.4 65.8Q196.8 68.8 173.6 78.4L158.4 139.6ZM724 94.8L779.6 94.8L781.2 129.6C787.333 118.133 795.2 108.867 804.8 101.8C814.4 94.7334 826 91.2 839.6 91.2C852.667 91.2 863.6 94.4667 872.4 101C881.2 107.533 886.533 117.6 888.4 131.2Q897.6 113.6 912.2 102.4C921.933 94.9333 933.733 91.2 947.6 91.2C963.867 91.2 976.733 96.2667 986.2 106.4C995.667 116.533 999.067 132.267 996.4 153.6L986.4 236L1009.6 236L1006 265.2L994 275.2L932.4 275.2L944.8 173.6C946.667 158.133 945.733 147.067 942 140.4C938.267 133.733 931.467 130.4 921.6 130.4Q903.6 130.4 896 142.4C890.933 150.4 887.6 160.667 886 173.2L873.6 275.2L824.4 275.2L836.8 173.6C838.667 158.133 837.733 147.067 834 140.4C830.267 133.733 823.467 130.4 813.6 130.4Q795.6 130.4 788 142.4C782.933 150.4 779.6 160.667 778 173.2L765.6 275.2L716 275.2L733.2 134L708 134L711.6 105.6L724 94.8ZM57.2 94.8L44.8 105.6L41.2 134L70.4 134L54 268.8Q48.8 312 35.4 319.2C26.4667 324 15.3333 323.867 2 318.8L0 352Q17.2 364 40 364C58.9333 364 73.3333 354.933 83.2 336.8C93.0667 318.667 99.7333 296 103.2 268.8L124.8 94.8L57.2 94.8Z" transform="translate(592 103.83917)" id="j2html" fill="#222222" fill-rule="evenodd" stroke="none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show More