initial commit
This commit is contained in:
commit
43771ff9d6
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/.settings
|
||||||
|
/bin
|
||||||
|
/.classpath
|
||||||
|
/TEST
|
||||||
|
/target/
|
||||||
|
/files/
|
23
.project
Normal file
23
.project
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>MdBlog</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
64
pom.xml
Normal file
64
pom.xml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>MDTest</groupId>
|
||||||
|
<artifactId>MDTest</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<build>
|
||||||
|
<sourceDirectory>src/main/java</sourceDirectory>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<release>17</release>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<commonmark.version>0.21.0</commonmark.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>Graphite-Official</id>
|
||||||
|
<url>https://maven.graphite-official.com/releases</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.mrletsplay</groupId>
|
||||||
|
<artifactId>SimpleHTTPServer</artifactId>
|
||||||
|
<version>2.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.commonmark</groupId>
|
||||||
|
<artifactId>commonmark</artifactId>
|
||||||
|
<version>${commonmark.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.commonmark</groupId>
|
||||||
|
<artifactId>commonmark-ext-gfm-tables</artifactId>
|
||||||
|
<version>${commonmark.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.commonmark</groupId>
|
||||||
|
<artifactId>commonmark-ext-gfm-strikethrough</artifactId>
|
||||||
|
<version>${commonmark.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.commonmark</groupId>
|
||||||
|
<artifactId>commonmark-ext-ins</artifactId>
|
||||||
|
<version>${commonmark.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.commonmark</groupId>
|
||||||
|
<artifactId>commonmark-ext-task-list-items</artifactId>
|
||||||
|
<version>${commonmark.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
137
src/main/java/me/mrletsplay/mdblog/MdBlog.java
Normal file
137
src/main/java/me/mrletsplay/mdblog/MdBlog.java
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package me.mrletsplay.mdblog;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardWatchEventKinds;
|
||||||
|
import java.nio.file.WatchKey;
|
||||||
|
import java.nio.file.WatchService;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import me.mrletsplay.mdblog.blog.Post;
|
||||||
|
import me.mrletsplay.simplehttpserver.http.HttpRequestMethod;
|
||||||
|
import me.mrletsplay.simplehttpserver.http.document.FileDocument;
|
||||||
|
import me.mrletsplay.simplehttpserver.http.request.HttpRequestContext;
|
||||||
|
import me.mrletsplay.simplehttpserver.http.server.HttpServer;
|
||||||
|
|
||||||
|
public class MdBlog {
|
||||||
|
|
||||||
|
private static final Path
|
||||||
|
FILES_PATH = Path.of("files"),
|
||||||
|
POSTS_PATH = FILES_PATH.resolve("posts");
|
||||||
|
|
||||||
|
private static HttpServer server;
|
||||||
|
private static WatchService watchService;
|
||||||
|
private static List<WatchKey> watchedDirectories;
|
||||||
|
private static Map<String, Post> posts;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
server = new HttpServer(HttpServer.newConfigurationBuilder()
|
||||||
|
.hostBindAll()
|
||||||
|
.port(3706)
|
||||||
|
.create());
|
||||||
|
|
||||||
|
server.getDocumentProvider().registerPattern(HttpRequestMethod.GET, "/posts/{path...}", () -> {
|
||||||
|
HttpRequestContext ctx = HttpRequestContext.getCurrentContext();
|
||||||
|
String path = ctx.getPathParameters().get("path");
|
||||||
|
Post post = posts.get(path);
|
||||||
|
if(post != null) {
|
||||||
|
post.getContent().createContent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path resolved = POSTS_PATH.resolve(path).normalize();
|
||||||
|
if(!resolved.startsWith(POSTS_PATH) || resolved.getFileName().toString().endsWith(".md")) {
|
||||||
|
server.getDocumentProvider().getNotFoundDocument().createContent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Files.isRegularFile(resolved)) {
|
||||||
|
server.getDocumentProvider().getNotFoundDocument().createContent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new FileDocument(resolved).createContent();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
extractAndRegister("style/base.css");
|
||||||
|
extractAndRegister("style/post.css");
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
Files.createDirectories(FILES_PATH);
|
||||||
|
Files.createDirectories(POSTS_PATH);
|
||||||
|
|
||||||
|
watchedDirectories = new ArrayList<>();
|
||||||
|
watchService = POSTS_PATH.getFileSystem().newWatchService();
|
||||||
|
posts = new HashMap<>();
|
||||||
|
updateBlogs();
|
||||||
|
watchFolders();
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
try {
|
||||||
|
WatchKey key = watchService.take();
|
||||||
|
key.pollEvents();
|
||||||
|
updateBlogs();
|
||||||
|
watchFolders();
|
||||||
|
if(!key.reset()) {
|
||||||
|
key.cancel();
|
||||||
|
watchedDirectories.remove(key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void extractAndRegister(String path) throws IOException {
|
||||||
|
Path filePath = FILES_PATH.resolve(path);
|
||||||
|
if(!Files.exists(filePath)) {
|
||||||
|
Files.createDirectories(filePath.getParent());
|
||||||
|
Files.write(filePath, MdBlog.class.getResourceAsStream("/" + path).readAllBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
server.getDocumentProvider().register(HttpRequestMethod.GET, "/" + path, new FileDocument(filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateBlogs() throws IOException {
|
||||||
|
Iterator<Post> it = posts.values().iterator();
|
||||||
|
while(it.hasNext()) {
|
||||||
|
Post p = it.next();
|
||||||
|
if(!p.update()) it.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.walk(POSTS_PATH)
|
||||||
|
.filter(Files::isRegularFile)
|
||||||
|
.filter(f -> f.getFileName().toString().endsWith(".md"))
|
||||||
|
.filter(f -> posts.values().stream().noneMatch(p -> p.getFilePath().equals(f)))
|
||||||
|
.forEach(f -> {
|
||||||
|
try {
|
||||||
|
String path = POSTS_PATH.relativize(f).toString();
|
||||||
|
path = path.substring(0, path.length() - ".md".length());
|
||||||
|
posts.put(path, new Post(f));
|
||||||
|
} catch (IOException e) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void watchFolders() throws IOException {
|
||||||
|
Files.walk(POSTS_PATH)
|
||||||
|
.filter(Files::isDirectory)
|
||||||
|
.filter(d -> watchedDirectories.stream().noneMatch(w -> w.watchable().equals(d)))
|
||||||
|
.forEach(d -> {
|
||||||
|
try {
|
||||||
|
System.out.println("Watching " + d);
|
||||||
|
watchedDirectories.add(d.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.OVERFLOW));
|
||||||
|
} catch (IOException e) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
84
src/main/java/me/mrletsplay/mdblog/blog/Post.java
Normal file
84
src/main/java/me/mrletsplay/mdblog/blog/Post.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package me.mrletsplay.mdblog.blog;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import me.mrletsplay.mdblog.markdown.MdParser;
|
||||||
|
import me.mrletsplay.mdblog.markdown.MdRenderer;
|
||||||
|
import me.mrletsplay.mrcore.misc.ByteUtils;
|
||||||
|
import me.mrletsplay.simplehttpserver.dom.html.HtmlDocument;
|
||||||
|
|
||||||
|
public class Post {
|
||||||
|
|
||||||
|
private static final MessageDigest MD_5;
|
||||||
|
private static final MdRenderer RENDERER = new MdRenderer();
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
MD_5 = MessageDigest.getInstance("MD5");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path filePath;
|
||||||
|
private String checksum;
|
||||||
|
private PostMetadata metadata;
|
||||||
|
private HtmlDocument content;
|
||||||
|
|
||||||
|
public Post(Path filePath) throws IOException {
|
||||||
|
this.filePath = filePath;
|
||||||
|
this.checksum = checksum(filePath);
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getFilePath() {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostMetadata getMetadata() {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlDocument getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load() throws IOException {
|
||||||
|
String postData = Files.readString(filePath);
|
||||||
|
String[] spl = postData.split("\n---\n", 2);
|
||||||
|
if(spl.length != 2) throw new IOException("Invalid post file");
|
||||||
|
|
||||||
|
this.metadata = PostMetadata.load(spl[0]);
|
||||||
|
|
||||||
|
HtmlDocument document = new HtmlDocument();
|
||||||
|
document.getBodyNode().appendChild(RENDERER.render(MdParser.parse(spl[1])));
|
||||||
|
document.setTitle(metadata.title());
|
||||||
|
document.setDescription(metadata.author());
|
||||||
|
document.addStyleSheet("/style/base.css");
|
||||||
|
document.addStyleSheet("/style/post.css");
|
||||||
|
this.content = document;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean update() {
|
||||||
|
if(!Files.exists(filePath)) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String newChecksum = checksum(filePath);
|
||||||
|
if(checksum.equals(newChecksum)) return true;
|
||||||
|
this.checksum = newChecksum;
|
||||||
|
load();
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String checksum(Path filePath) throws IOException {
|
||||||
|
return ByteUtils.bytesToHex(MD_5.digest(Files.readAllBytes(filePath)));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
43
src/main/java/me/mrletsplay/mdblog/blog/PostMetadata.java
Normal file
43
src/main/java/me/mrletsplay/mdblog/blog/PostMetadata.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package me.mrletsplay.mdblog.blog;
|
||||||
|
|
||||||
|
import java.time.DateTimeException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public record PostMetadata(Instant date, String title, String author, Set<String> tags) {
|
||||||
|
|
||||||
|
public static PostMetadata load(String metadataString) {
|
||||||
|
Instant date = Instant.EPOCH;
|
||||||
|
String title = "Untitled Post";
|
||||||
|
String author = "Unknown Author";
|
||||||
|
Set<String> tags = Collections.emptySet();
|
||||||
|
for(String line : metadataString.split("\n")) {
|
||||||
|
if(line.isEmpty()) continue;
|
||||||
|
String[] spl = line.split(":", 2);
|
||||||
|
if(spl.length != 2) {
|
||||||
|
System.err.println("Invalid metadata line: " + line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = spl[0].toLowerCase().trim();
|
||||||
|
String value = spl[1].trim();
|
||||||
|
|
||||||
|
switch(key) {
|
||||||
|
case "date" -> {
|
||||||
|
try {
|
||||||
|
date = Instant.parse(value);
|
||||||
|
}catch(DateTimeException e) {}
|
||||||
|
}
|
||||||
|
case "title" -> title = value;
|
||||||
|
case "author" -> author = value;
|
||||||
|
case "tags" -> tags = Arrays.stream(value.split(",")).map(String::trim).collect(Collectors.toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PostMetadata(date, title, author, tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
src/main/java/me/mrletsplay/mdblog/markdown/MdException.java
Normal file
23
src/main/java/me/mrletsplay/mdblog/markdown/MdException.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package me.mrletsplay.mdblog.markdown;
|
||||||
|
|
||||||
|
public class MdException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2453345940664448784L;
|
||||||
|
|
||||||
|
public MdException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MdException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MdException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MdException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
src/main/java/me/mrletsplay/mdblog/markdown/MdParser.java
Normal file
22
src/main/java/me/mrletsplay/mdblog/markdown/MdParser.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package me.mrletsplay.mdblog.markdown;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
|
||||||
|
import org.commonmark.ext.gfm.tables.TablesExtension;
|
||||||
|
import org.commonmark.ext.ins.InsExtension;
|
||||||
|
import org.commonmark.ext.task.list.items.TaskListItemsExtension;
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
import org.commonmark.parser.Parser;
|
||||||
|
|
||||||
|
public class MdParser {
|
||||||
|
|
||||||
|
private static Parser parser = new Parser.Builder()
|
||||||
|
.extensions(Arrays.asList(TablesExtension.create(), StrikethroughExtension.create(), InsExtension.create(), TaskListItemsExtension.create()))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public static Node parse(String text) {
|
||||||
|
Node n = parser.parse(text);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package me.mrletsplay.mdblog.markdown;
|
||||||
|
|
||||||
|
public class MdRenderContext {
|
||||||
|
|
||||||
|
private MdRenderer renderer;
|
||||||
|
|
||||||
|
public MdRenderContext(MdRenderer renderer) {
|
||||||
|
this.renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MdRenderer getRenderer() {
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
208
src/main/java/me/mrletsplay/mdblog/markdown/MdRenderer.java
Normal file
208
src/main/java/me/mrletsplay/mdblog/markdown/MdRenderer.java
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
package me.mrletsplay.mdblog.markdown;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
|
||||||
|
import org.commonmark.ext.gfm.tables.TableBlock;
|
||||||
|
import org.commonmark.ext.gfm.tables.TableBody;
|
||||||
|
import org.commonmark.ext.gfm.tables.TableCell;
|
||||||
|
import org.commonmark.ext.gfm.tables.TableHead;
|
||||||
|
import org.commonmark.ext.gfm.tables.TableRow;
|
||||||
|
import org.commonmark.ext.ins.Ins;
|
||||||
|
import org.commonmark.ext.task.list.items.TaskListItemMarker;
|
||||||
|
import org.commonmark.node.BlockQuote;
|
||||||
|
import org.commonmark.node.BulletList;
|
||||||
|
import org.commonmark.node.Code;
|
||||||
|
import org.commonmark.node.Document;
|
||||||
|
import org.commonmark.node.Emphasis;
|
||||||
|
import org.commonmark.node.FencedCodeBlock;
|
||||||
|
import org.commonmark.node.HardLineBreak;
|
||||||
|
import org.commonmark.node.Heading;
|
||||||
|
import org.commonmark.node.HtmlBlock;
|
||||||
|
import org.commonmark.node.HtmlInline;
|
||||||
|
import org.commonmark.node.Image;
|
||||||
|
import org.commonmark.node.IndentedCodeBlock;
|
||||||
|
import org.commonmark.node.Link;
|
||||||
|
import org.commonmark.node.LinkReferenceDefinition;
|
||||||
|
import org.commonmark.node.ListItem;
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
import org.commonmark.node.OrderedList;
|
||||||
|
import org.commonmark.node.Paragraph;
|
||||||
|
import org.commonmark.node.SoftLineBreak;
|
||||||
|
import org.commonmark.node.StrongEmphasis;
|
||||||
|
import org.commonmark.node.Text;
|
||||||
|
import org.commonmark.node.ThematicBreak;
|
||||||
|
|
||||||
|
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
|
||||||
|
|
||||||
|
public class MdRenderer {
|
||||||
|
|
||||||
|
public HtmlElement render(Node node) {
|
||||||
|
MdRenderContext ctx = new MdRenderContext(this);
|
||||||
|
|
||||||
|
HtmlElement element = renderSingleNode(ctx, node);
|
||||||
|
if(element == null) return null;
|
||||||
|
Node ch = node.getFirstChild();
|
||||||
|
if(ch == null) return element;
|
||||||
|
do {
|
||||||
|
HtmlElement chEl = render(ch);
|
||||||
|
if(chEl == null) continue;
|
||||||
|
element.appendChild(chEl);
|
||||||
|
}while((ch = ch.getNext()) != null);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HtmlElement renderSingleNode(MdRenderContext ctx, Node node) {
|
||||||
|
try {
|
||||||
|
Method m = MdRenderer.class.getDeclaredMethod("render", MdRenderContext.class, node.getClass());
|
||||||
|
return (HtmlElement) m.invoke(this, ctx, node);
|
||||||
|
}catch(NoSuchMethodException e) {
|
||||||
|
System.err.println("Warning: No render() method defined for " + node.getClass().getName());
|
||||||
|
return null;
|
||||||
|
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Document node) {
|
||||||
|
HtmlElement el = new HtmlElement("div");
|
||||||
|
el.setAttribute("md-document");
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Heading node) {
|
||||||
|
return new HtmlElement("h" + Math.min(6, node.getLevel()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Paragraph node) {
|
||||||
|
return new HtmlElement("p");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, BlockQuote node) {
|
||||||
|
return new HtmlElement("blockquote");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, BulletList node) {
|
||||||
|
return new HtmlElement("ul");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, FencedCodeBlock node) {
|
||||||
|
HtmlElement el = new HtmlElement("pre");
|
||||||
|
HtmlElement code = new HtmlElement("code");
|
||||||
|
code.setText(node.getLiteral());
|
||||||
|
el.appendChild(code);
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, HtmlBlock node) {
|
||||||
|
return new RawHtmlElement(node.getLiteral());
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, ThematicBreak node) {
|
||||||
|
HtmlElement el = new HtmlElement("hr");
|
||||||
|
el.setSelfClosing(true);
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, IndentedCodeBlock node) {
|
||||||
|
HtmlElement el = new HtmlElement("pre");
|
||||||
|
HtmlElement code = new HtmlElement("code");
|
||||||
|
code.setText(node.getLiteral());
|
||||||
|
el.appendChild(code);
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Link node) {
|
||||||
|
HtmlElement el = new HtmlElement("a");
|
||||||
|
el.setAttribute("href", node.getDestination());
|
||||||
|
el.setAttribute("title", node.getTitle());
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, ListItem node) {
|
||||||
|
return new HtmlElement("li");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, OrderedList node) {
|
||||||
|
HtmlElement el = new HtmlElement("ol");
|
||||||
|
el.setAttribute("start", String.valueOf(node.getStartNumber()));
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Image node) {
|
||||||
|
return HtmlElement.img(node.getDestination(), node.getTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Emphasis node) {
|
||||||
|
return new HtmlElement("em");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, StrongEmphasis node) {
|
||||||
|
return new HtmlElement("strong");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Text node) {
|
||||||
|
HtmlElement el = new HtmlElement("span");
|
||||||
|
el.setText(node.getLiteral());
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Code node) {
|
||||||
|
HtmlElement el = new HtmlElement("code");
|
||||||
|
el.setText(node.getLiteral());
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, HtmlInline node) {
|
||||||
|
return new RawHtmlElement(node.getLiteral());
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, SoftLineBreak node) {
|
||||||
|
return new RawHtmlElement(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, HardLineBreak node) {
|
||||||
|
return HtmlElement.br();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, LinkReferenceDefinition node) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Strikethrough node) {
|
||||||
|
return new HtmlElement("del");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, TableBlock node) {
|
||||||
|
return new HtmlElement("table");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, TableHead node) {
|
||||||
|
return new HtmlElement("thead");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, TableBody node) {
|
||||||
|
return new HtmlElement("tbody");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, TableRow node) {
|
||||||
|
return new HtmlElement("tr");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, TableCell node) {
|
||||||
|
return new HtmlElement("td");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, Ins node) {
|
||||||
|
return new HtmlElement("ins");
|
||||||
|
}
|
||||||
|
|
||||||
|
public HtmlElement render(MdRenderContext ctx, TaskListItemMarker node) {
|
||||||
|
HtmlElement el = new HtmlElement("input");
|
||||||
|
el.setAttribute("type", "checkbox");
|
||||||
|
el.setAttribute("disabled");
|
||||||
|
if(node.isChecked()) el.setAttribute("checked");
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package me.mrletsplay.mdblog.markdown;
|
||||||
|
|
||||||
|
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
|
||||||
|
|
||||||
|
public class RawHtmlElement extends HtmlElement {
|
||||||
|
|
||||||
|
private String raw;
|
||||||
|
|
||||||
|
public RawHtmlElement(String raw) {
|
||||||
|
super("raw");
|
||||||
|
this.raw = raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
7
src/main/resources/style/base.css
Normal file
7
src/main/resources/style/base.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
body {
|
||||||
|
filter: invert();
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
background-color: red;
|
||||||
|
}
|
3
src/main/resources/style/post.css
Normal file
3
src/main/resources/style/post.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
h2 {
|
||||||
|
background-color: orange;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user