initial commit

This commit is contained in:
MrLetsplay 2025-01-18 20:43:31 +01:00
commit c6eec9364f
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
35 changed files with 1466 additions and 0 deletions

40
.classpath Normal file
View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target/

23
.project Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>NoJS</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.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,3 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/test/java=UTF-8

View File

@ -0,0 +1,8 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=11

View File

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

67
pom.xml Normal file
View File

@ -0,0 +1,67 @@
<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>me.mrletsplay</groupId>
<artifactId>NoJS</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>11</release>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<source>11</source>
<encoding>UTF-8</encoding>
<sourcepath>src/main/java</sourcepath>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<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.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,109 @@
package me.mrletsplay.nojs;
import java.io.IOException;
import java.io.InputStream;
import org.slf4j.helpers.NOPLogger;
import me.mrletsplay.nojs._test.DynPage;
import me.mrletsplay.nojs._test.LinksPage;
import me.mrletsplay.nojs._test.TestPage;
import me.mrletsplay.nojs.action.Action;
import me.mrletsplay.nojs.action.ActionData;
import me.mrletsplay.nojs.component.impl.Button;
import me.mrletsplay.nojs.component.impl.Button.Style;
import me.mrletsplay.nojs.component.impl.Form;
import me.mrletsplay.nojs.component.impl.Link;
import me.mrletsplay.nojs.component.impl.TextInput;
import me.mrletsplay.nojs.page.StaticPage;
import me.mrletsplay.simplehttpserver.dom.html.HtmlDocument;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
import me.mrletsplay.simplehttpserver.dom.html.element.HtmlButton;
import me.mrletsplay.simplehttpserver.http.HttpRequestMethod;
import me.mrletsplay.simplehttpserver.http.HttpStatusCodes;
import me.mrletsplay.simplehttpserver.http.header.DefaultClientContentTypes;
import me.mrletsplay.simplehttpserver.http.request.HttpRequestContext;
import me.mrletsplay.simplehttpserver.http.request.urlencoded.UrlEncoded;
import me.mrletsplay.simplehttpserver.http.response.HtmlResponse;
import me.mrletsplay.simplehttpserver.http.response.TextResponse;
import me.mrletsplay.simplehttpserver.http.server.HttpServer;
import me.mrletsplay.simplehttpserver.http.util.MimeType;
public class NoJS {
public static void main(String[] args) {
HttpServer httpServer = new HttpServer(HttpServer.newConfigurationBuilder()
.hostBindAll()
.port(6969)
.poolSize(10)
.ioWorkers(2)
.debugMode(true)
.logger(NOPLogger.NOP_LOGGER)
.create());
httpServer.getDocumentProvider().register(HttpRequestMethod.GET, "/", () -> {
HttpRequestContext ctx = HttpRequestContext.getCurrentContext();
HtmlDocument doc = new HtmlDocument();
doc.setTitle("Test");
doc.setIcon("/amogus.png");
doc.setDescription("Just testing");
HtmlElement form = HtmlElement.of("form");
form.setAttribute("method", "post");
HtmlElement input = HtmlElement.of("input");
input.setAttribute("name", "texttest");
form.appendChild(input);
HtmlButton button = HtmlElement.button();
button.setText("Click me!");
form.appendChild(button);
doc.getBodyNode().appendChild(form);
ctx.respond(HttpStatusCodes.OK_200, new HtmlResponse(doc));
});
// new TestEndpoint().register(httpServer.getDocumentProvider());
StaticPage test = new StaticPage()
.add(new Button().text("Click me!").action(new Action("poopy").extraData(new ActionData().put("test", "abc"))).style(Style.LINK))
.add(new Link().text("Click me!").href("#"))
.add(new Form()
.add(new TextInput("username").placeholder("Username"))
.add(new TextInput("password").placeholder("Password"))
.add(new Button().text("Login"))
.add(new Button().text("Can't click here").enabled(false))
.action(new Action("beans"))
);
test.register(httpServer.getDocumentProvider(), "/test");
new TestPage().register(httpServer.getDocumentProvider(), "/test2");
new DynPage().register(httpServer.getDocumentProvider(), "/dyn");
new LinksPage().register(httpServer.getDocumentProvider(), "/links");
httpServer.getDocumentProvider().register(HttpRequestMethod.POST, "/", () -> {
HttpRequestContext ctx = HttpRequestContext.getCurrentContext();
UrlEncoded data = ctx.getClientHeader().getPostData().getParsedAs(DefaultClientContentTypes.URLENCODED);
ctx.respond(HttpStatusCodes.OK_200, new TextResponse(data.toString()));
});
httpServer.getDocumentProvider().register(HttpRequestMethod.GET, "/base.css", () -> {
try(InputStream in = NoJS.class.getResourceAsStream("/style/base.css")) {
HttpRequestContext ctx = HttpRequestContext.getCurrentContext();
ctx.getServerHeader().setContent(MimeType.CSS, in.readAllBytes());
} catch(IOException ex) {
throw new RuntimeException(ex);
}
});
// httpServer.getDocumentProvider().registerPattern(HttpRequestMethod.GET, "/{path...?}", () -> {
// HttpRequestContext ctx = HttpRequestContext.getCurrentContext();
// ctx.respond(HttpStatusCodes.OK_200, new TextResponse(ctx.getPathParameters().get("path")));
// });
httpServer.start();
}
}

View File

@ -0,0 +1,37 @@
package me.mrletsplay.nojs._test;
import java.util.Date;
import java.util.List;
import me.mrletsplay.nojs.action.Action;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.nojs.component.impl.Button;
import me.mrletsplay.nojs.component.impl.Form;
import me.mrletsplay.nojs.component.impl.Group;
import me.mrletsplay.nojs.component.impl.Text;
import me.mrletsplay.nojs.page.Page;
import me.mrletsplay.nojs.page.layout.GridLayout;
import me.mrletsplay.nojs.page.layout.InsetLayout;
import me.mrletsplay.nojs.page.layout.Layout;
public class DynPage implements Page {
@Override
public List<Component> getComponents() {
return List.of(
new Text().text(new Date().toString()),
new Form()
.action(Action.empty())
.layout(new InsetLayout(GridLayout.ofColumns(1), "100px"))
.add(new Button().text("Refresh")),
new Group()
.add(new Text().text("Your name is"))
);
}
@Override
public Layout getLayout() {
return GridLayout.ofColumns(1);
}
}

View File

@ -0,0 +1,76 @@
package me.mrletsplay.nojs._test;
import java.util.ArrayList;
import java.util.List;
import me.mrletsplay.nojs.action.Action;
import me.mrletsplay.nojs.action.ActionEvent;
import me.mrletsplay.nojs.action.ActionHandler;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.nojs.component.impl.Button;
import me.mrletsplay.nojs.component.impl.Form;
import me.mrletsplay.nojs.component.impl.Group;
import me.mrletsplay.nojs.component.impl.Link;
import me.mrletsplay.nojs.component.impl.Message;
import me.mrletsplay.nojs.component.impl.Text;
import me.mrletsplay.nojs.component.impl.TextInput;
import me.mrletsplay.nojs.page.Page;
import me.mrletsplay.nojs.page.layout.GridLayout;
import me.mrletsplay.nojs.page.layout.Layout;
public class LinksPage implements Page {
private List<String> links = new ArrayList<>();
private String error;
@Override
public List<Component> getComponents() {
Group linksGroup = new Group()
.layout(GridLayout.ofColumns(1)
.gap("2px"));
for(String s : links) {
linksGroup.add(new Link().href(s).text(s));
}
if(links.isEmpty()) {
linksGroup.add(new Text().text("No links added yet"));
}
if(error != null) {
linksGroup.add(new Message().text(error));
error = null;
}
return List.of(
linksGroup,
new Form()
.add(new Text().text("Add a new link:"))
.add(new Group()
.add(new TextInput("link").placeholder("Insert link here"))
.add(new Button().text("Add Link"))
.layout(new GridLayout().columns("1fr", "auto").gap("4px"))
)
.layout(GridLayout.ofColumns(1).gap("4px"))
.action(new Action("addLink"))
);
}
@Override
public Layout getLayout() {
return GridLayout.ofColumns(1)
.gap("10px");
}
@ActionHandler("addLink")
public void addLink(ActionEvent event) {
String link = event.getData("link");
if(link == null || link.isBlank()) {
error = "Invalid link";
return;
}
links.add(event.getData("link"));
}
}

View File

@ -0,0 +1,30 @@
package me.mrletsplay.nojs._test;
import me.mrletsplay.nojs.action.Action;
import me.mrletsplay.nojs.action.ActionEvent;
import me.mrletsplay.nojs.action.ActionHandler;
import me.mrletsplay.nojs.component.impl.Button;
import me.mrletsplay.nojs.component.impl.Form;
import me.mrletsplay.nojs.component.impl.Link;
import me.mrletsplay.nojs.component.impl.TextInput;
import me.mrletsplay.nojs.page.StaticPage;
import me.mrletsplay.nojs.page.layout.GridLayout;
public class TestPage extends StaticPage {
public TestPage() {
add(new Form()
.add(new TextInput("beans"))
.add(new Button())
.add(new Link().href("/test").text("Link to other page"))
.action(new Action("submit"))
.layout(GridLayout.ofSize(2, 2))
);
}
@ActionHandler("submit")
public void onSubmit(ActionEvent event) {
System.out.println("Got event " + event.getAction());
}
}

View File

@ -0,0 +1,54 @@
package me.mrletsplay.nojs.action;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public class Action {
public static final String ACTION_NAME = "_action";
private String name;
private ActionData extraData;
public Action(String name) {
Objects.requireNonNull(name, "name");
this.name = name;
this.extraData = ActionData.EMPTY_DATA;
}
public String getName() {
return name;
}
public Action extraData(ActionData extraData) {
this.extraData = extraData != null ? extraData : ActionData.EMPTY_DATA;
return this;
}
public ActionData getExtraData() {
return extraData;
}
public List<HtmlElement> createFormElements() {
List<HtmlElement> elements = new ArrayList<>();
HtmlElement action = HtmlElement.of("input");
action.setAttribute("name", ACTION_NAME);
action.setAttribute("value", name);
action.setAttribute("hidden");
elements.add(action);
elements.addAll(extraData.createFormElements());
return elements;
}
public static Action empty() {
return new Action("");
}
}

View File

@ -0,0 +1,49 @@
package me.mrletsplay.nojs.action;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public class ActionData {
public static final ActionData EMPTY_DATA = new ActionData(Collections.emptyMap());
private Map<String, String> data;
private ActionData(Map<String, String> data) {
this.data = data;
}
public ActionData() {
this.data = new HashMap<>();
}
public ActionData put(String key, String value) {
Objects.requireNonNull(key, "key");
Objects.requireNonNull(value, "value");
data.put(key, value);
return this;
}
public List<HtmlElement> createFormElements() {
if(data.isEmpty()) return Collections.emptyList();
List<HtmlElement> elements = new ArrayList<>();
for(Map.Entry<String, String> d : data.entrySet()) {
HtmlElement e = HtmlElement.of("input");
e.setAttribute("name", d.getKey());
e.setAttribute("value", d.getValue());
e.setAttribute("hidden");
elements.add(e);
}
return elements;
}
}

View File

@ -0,0 +1,21 @@
package me.mrletsplay.nojs.action;
import me.mrletsplay.simplehttpserver.http.request.urlencoded.UrlEncoded;
public class ActionEvent {
private UrlEncoded data;
public ActionEvent(UrlEncoded data) {
this.data = data;
}
public String getAction() {
return data.getFirst(Action.ACTION_NAME);
}
public String getData(String name) {
return data.getFirst(name);
}
}

View File

@ -0,0 +1,14 @@
package me.mrletsplay.nojs.action;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionHandler {
public String value();
}

View File

@ -0,0 +1,29 @@
package me.mrletsplay.nojs.action;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public interface ActionHandlers {
public default void onAction(ActionEvent event) {
if(event.getAction() == null) return;
for(Method m : getClass().getDeclaredMethods()) {
ActionHandler handler = m.getAnnotation(ActionHandler.class);
if(handler == null) continue;
if(!handler.value().equals(event.getAction())) continue;
if(m.getParameterCount() != 1 || m.getParameters()[0].getType() != ActionEvent.class) {
System.err.println("Invalid action handler found: " + m.getName() + ". Must have one parameter of type ActionEvent");
continue;
}
try {
m.invoke(this, event);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e); // TODO: exception
}
}
}
}

View File

@ -0,0 +1,9 @@
package me.mrletsplay.nojs.component;
import me.mrletsplay.nojs.action.Action;
public interface ActionableComponent extends Component {
public ActionableComponent action(Action action);
}

View File

@ -0,0 +1,9 @@
package me.mrletsplay.nojs.component;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public interface Component {
public HtmlElement toHtml();
}

View File

@ -0,0 +1,11 @@
package me.mrletsplay.nojs.component;
import me.mrletsplay.nojs.page.layout.Layout;
public interface ContainerComponent extends Component {
public ContainerComponent add(Component component);
public ContainerComponent layout(Layout layout);
}

View File

@ -0,0 +1,76 @@
package me.mrletsplay.nojs.component.impl;
import me.mrletsplay.nojs.action.Action;
import me.mrletsplay.nojs.component.ActionableComponent;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
import me.mrletsplay.simplehttpserver.dom.html.element.HtmlButton;
public class Button implements ActionableComponent {
private String text;
private Style style;
private boolean enabled;
private Action action;
public Button() {
this.text = "Button";
this.style = Style.NORMAL;
this.enabled = true;
}
public Button text(String text) {
this.text = text != null ? text : "Button";
return this;
}
public Button style(Style style) {
this.style = style != null ? style : Style.NORMAL;
return this;
}
public Button enabled(boolean enabled) {
this.enabled = enabled;
return this;
}
@Override
public Button action(Action action) {
this.action = action;
return this;
}
@Override
public HtmlElement toHtml() {
HtmlButton button = HtmlElement.button();
button.addClass("button");
if(style != null) {
button.setAttribute("data-style", style.name().toLowerCase());
}
if(!enabled) {
button.setAttribute("disabled");
}
button.setText(text);
if(action != null) {
button.setAttribute("data-action");
HtmlElement form = Form.createElement(action, null);
form.appendChild(button);
return form;
}
return button;
}
public static enum Style { // TODO
NORMAL,
LINK,
;
}
}

View File

@ -0,0 +1,73 @@
package me.mrletsplay.nojs.component.impl;
import java.util.ArrayList;
import java.util.List;
import me.mrletsplay.nojs.action.Action;
import me.mrletsplay.nojs.component.ActionableComponent;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.nojs.component.ContainerComponent;
import me.mrletsplay.nojs.page.layout.Layout;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public class Form implements ActionableComponent, ContainerComponent {
private Action action;
private List<Component> components;
private Layout layout;
public Form() {
this.components = new ArrayList<>();
}
@Override
public Form add(Component component) {
components.add(component);
return this;
}
@Override
public Form layout(Layout layout) {
this.layout = layout;
return this;
}
@Override
public Form action(Action action) {
this.action = action;
return this;
}
@Override
public HtmlElement toHtml() {
HtmlElement form = createElement(action, layout);
for(Component c : components) {
// TODO: check for nested forms
form.appendChild(c.toHtml());
}
return form;
}
public static HtmlElement createElement(Action action, Layout layout) {
HtmlElement form = HtmlElement.of("form");
form.addClass("form");
form.setAttribute("action", "");
form.setAttribute("method", "post");
if(layout != null) {
layout.apply(form);
}
if(action != null) {
for(HtmlElement e : action.createFormElements()) {
form.appendChild(e);
}
}
return form;
}
}

View File

@ -0,0 +1,47 @@
package me.mrletsplay.nojs.component.impl;
import java.util.ArrayList;
import java.util.List;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.nojs.component.ContainerComponent;
import me.mrletsplay.nojs.page.layout.Layout;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public class Group implements ContainerComponent {
private List<Component> components;
private Layout layout;
public Group() {
this.components = new ArrayList<>();
}
@Override
public Group add(Component component) {
components.add(component);
return this;
}
@Override
public Group layout(Layout layout) {
this.layout = layout;
return this;
}
@Override
public HtmlElement toHtml() {
HtmlElement div = HtmlElement.of("div");
div.addClass("group");
if(layout != null) {
layout.apply(div);
}
for(Component c : components) {
div.appendChild(c.toHtml());
}
return div;
}
}

View File

@ -0,0 +1,35 @@
package me.mrletsplay.nojs.component.impl;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public class Link implements Component {
private String text;
private String href;
public Link() {
this.text = "Link";
this.href = "#";
}
public Link text(String text) {
this.text = text != null ? text : "Link";
return this;
}
public Link href(String href) {
this.href = href != null ? href : "#";
return this;
}
@Override
public HtmlElement toHtml() {
HtmlElement link = HtmlElement.of("a");
link.addClass("link");
link.setText(text);
link.setAttribute("href", href);
return link;
}
}

View File

@ -0,0 +1,49 @@
package me.mrletsplay.nojs.component.impl;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import me.mrletsplay.nojs.NoJS;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
import me.mrletsplay.simplehttpserver.dom.html.element.HtmlRaw;
public class Message implements Component {
private String text;
public Message() {
this.text = "Message";
}
public Message text(String text) {
this.text = text != null ? text : "Message";
return this;
}
@Override
public HtmlElement toHtml() {
HtmlElement message = HtmlElement.of("div");
message.addClass("message");
HtmlElement textSpan = HtmlElement.of("span");
textSpan.setText(text);
message.appendChild(textSpan);
HtmlElement checkLabel = HtmlElement.of("label");
message.appendChild(checkLabel);
HtmlElement check = HtmlElement.of("input");
check.setAttribute("type", "checkbox");
check.setAttribute("autocomplete", "off"); // Clear on reload
checkLabel.appendChild(check);
try(InputStream in = NoJS.class.getResourceAsStream("/icon/x.svg")) {
checkLabel.appendChild(new HtmlRaw(new String(in.readAllBytes(), StandardCharsets.UTF_8)));
}catch(IOException ex) {}
return message;
}
}

View File

@ -0,0 +1,42 @@
package me.mrletsplay.nojs.component.impl;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public class Text implements Component {
private Type type;
private String text;
public Text() {
this.type = Type.PLAIN;
this.text = "Text";
}
public Text type(Type type) {
this.type = type == null ? Type.PLAIN : type;
return this;
}
public Text text(String text) {
this.text = text != null ? text : "Text";
return this;
}
@Override
public HtmlElement toHtml() {
HtmlElement text = HtmlElement.of(type == Type.PLAIN ? "span" : "p");
text.addClass("text");
text.setText(this.text);
return text;
}
public static enum Type {
PLAIN,
PARAGRAPH,
;
}
}

View File

@ -0,0 +1,60 @@
package me.mrletsplay.nojs.component.impl;
import java.util.Objects;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public class TextInput implements Component {
private String name;
private String placeholder;
private String value;
private boolean hidden;
public TextInput(String name) {
Objects.requireNonNull(name, "name");
this.name = name;
this.placeholder = "";
this.value = "";
this.hidden = false;
}
public TextInput placeholder(String placeholder) {
this.placeholder = placeholder != null ? placeholder : "";
return this;
}
public TextInput value(String value) {
this.value = value != null ? value : "";
return this;
}
public TextInput hidden(boolean hidden) {
this.hidden = hidden;
return this;
}
@Override
public HtmlElement toHtml() {
HtmlElement input = HtmlElement.of("input");
input.addClass("input");
input.setAttribute("name", name);
if(!placeholder.isEmpty()) {
input.setAttribute("placeholder", placeholder);
}
if(!value.isEmpty()) {
input.setAttribute("value", value);
}
if(hidden) {
input.setAttribute("hidden");
}
return input;
}
}

View File

@ -0,0 +1,79 @@
package me.mrletsplay.nojs.page;
import java.util.List;
import me.mrletsplay.nojs.action.ActionEvent;
import me.mrletsplay.nojs.action.ActionHandlers;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.nojs.page.layout.Layout;
import me.mrletsplay.simplehttpserver.dom.css.CssElement;
import me.mrletsplay.simplehttpserver.dom.css.CssSelector;
import me.mrletsplay.simplehttpserver.dom.css.StyleSheet;
import me.mrletsplay.simplehttpserver.dom.html.HtmlDocument;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
import me.mrletsplay.simplehttpserver.http.HttpRequestMethod;
import me.mrletsplay.simplehttpserver.http.HttpStatusCodes;
import me.mrletsplay.simplehttpserver.http.document.DocumentProvider;
import me.mrletsplay.simplehttpserver.http.document.HttpDocument;
import me.mrletsplay.simplehttpserver.http.header.DefaultClientContentTypes;
import me.mrletsplay.simplehttpserver.http.request.HttpRequestContext;
import me.mrletsplay.simplehttpserver.http.request.urlencoded.UrlEncoded;
import me.mrletsplay.simplehttpserver.http.response.HtmlResponse;
public interface Page extends HttpDocument, ActionHandlers {
public List<Component> getComponents();
public default Layout getLayout() {
return null;
}
public default HtmlDocument toHtml() {
HtmlDocument doc = new HtmlDocument();
doc.addStyleSheet("/base.css");
StyleSheet sheet = new StyleSheet();
CssElement cssE = new CssElement(CssSelector.selectClass("ee"));
cssE.setProperty("text", "red");
sheet.addElement(cssE);
doc.getHeadNode().appendChild(HtmlElement.style(sheet));
Layout layout = getLayout();
if(layout != null) {
layout.apply(doc.getBodyNode());
}
for(Component c : getComponents()) {
doc.getBodyNode().appendChild(c.toHtml());
}
return doc;
}
@Override
public default void createContent() {
HttpRequestContext ctx = HttpRequestContext.getCurrentContext();
if(ctx.getClientHeader().getMethod() == HttpRequestMethod.POST) {
// TODO: error handling
UrlEncoded data = ctx.getClientHeader().getPostData().getParsedAs(DefaultClientContentTypes.URLENCODED);
System.out.println(data);
ActionEvent e = new ActionEvent(data);
onAction(e);
ctx.redirect(HttpStatusCodes.SEE_OTHER_303, ctx.getClientHeader().getPath().toString());
return;
}
ctx.respond(HttpStatusCodes.OK_200, new HtmlResponse(toHtml()));
}
public default void register(DocumentProvider provider, String path) {
provider.register(HttpRequestMethod.GET, path, this);
provider.register(HttpRequestMethod.POST, path, this);
}
}

View File

@ -0,0 +1,56 @@
package me.mrletsplay.nojs.page;
import java.util.ArrayList;
import java.util.List;
import me.mrletsplay.nojs.action.ActionEvent;
import me.mrletsplay.nojs.action.ActionHandlers;
import me.mrletsplay.nojs.component.Component;
import me.mrletsplay.nojs.page.layout.Layout;
public class StaticPage implements Page {
private List<Component> components;
private Layout layout;
private List<ActionHandlers> actionHandlers;
public StaticPage() {
this.components = new ArrayList<>();
this.actionHandlers = new ArrayList<>();
}
public StaticPage add(Component component) {
components.add(component);
return this;
}
public StaticPage layout(Layout layout) {
this.layout = layout;
return this;
}
public StaticPage addActionHandler(ActionHandlers handler) {
actionHandlers.add(handler);
return this;
}
@Override
public List<Component> getComponents() {
return components;
}
@Override
public Layout getLayout() {
return layout;
}
@Override
public void onAction(ActionEvent event) {
Page.super.onAction(event);
for(ActionHandlers handler : actionHandlers) {
handler.onAction(event);
}
}
}

View File

@ -0,0 +1,115 @@
package me.mrletsplay.nojs.page.layout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public class GridLayout implements Layout {
private List<String> columns;
private List<String> rows;
private String gap;
private Flow flow;
public GridLayout() {
this.columns = new ArrayList<>();
this.rows = new ArrayList<>();
this.flow = Flow.ROW;
}
public GridLayout columns(Collection<String> columns) {
this.columns = new ArrayList<>(columns == null ? Collections.emptyList() : columns);
return this;
}
public GridLayout rows(Collection<String> rows) {
this.rows = new ArrayList<>(rows == null ? Collections.emptyList() : rows);
return this;
}
public GridLayout columns(String... columns) {
return columns(columns == null ? null : Arrays.asList(columns));
}
public GridLayout rows(String... rows) {
return rows(rows == null ? null : Arrays.asList(rows));
}
public GridLayout gap(String gap) {
this.gap = gap;
return this;
}
public GridLayout flow(Flow flow) {
this.flow = flow;
return this;
}
@Override
public void apply(HtmlElement html) {
String css = "display:grid;";
if(!columns.isEmpty()) {
css += "grid-template-columns:" + String.join(" ", columns) + ";";
}
if(!rows.isEmpty()) {
css += "grid-template-rows:" + String.join(" ", rows) + ";";
}
if(gap != null) {
css += "gap:" + gap + ";";
}
if(flow != Flow.ROW) {
css += "grid-auto-flow:" + flow.name().toLowerCase() + ";";
}
html.setAttribute("style", css);
}
public static GridLayout ofColumns(int columns) {
List<String> colSizes = new ArrayList<>();
for(int i = 0; i < columns; i++) {
colSizes.add("auto");
}
return new GridLayout()
.columns(colSizes);
}
public static GridLayout ofRows(int rows) {
List<String> rowSizes = new ArrayList<>();
for(int i = 0; i < rows; i++) {
rowSizes.add("auto");
}
return new GridLayout()
.flow(Flow.COLUMN)
.rows(rowSizes);
}
public static GridLayout ofSize(int columns, int rows) {
List<String> colsAndRows = new ArrayList<>();
for(int i = 0; i < Math.max(columns, rows); i++) {
colsAndRows.add("auto");
}
return new GridLayout()
.columns(colsAndRows.subList(0, columns))
.rows(colsAndRows.subList(0, rows));
}
public static enum Flow {
ROW,
COLUMN,
;
}
}

View File

@ -0,0 +1,20 @@
package me.mrletsplay.nojs.page.layout;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public class InsetLayout implements Layout {
private Layout layout;
private String inset;
public InsetLayout(Layout layout, String inset) {
this.layout = layout;
this.inset = inset == null ? "0" : inset;
}
public void apply(HtmlElement html) {
if(layout != null) layout.apply(html);
html.appendAttribute("style", "margin:" + inset + ";");
}
}

View File

@ -0,0 +1,9 @@
package me.mrletsplay.nojs.page.layout;
import me.mrletsplay.simplehttpserver.dom.html.HtmlElement;
public interface Layout {
public void apply(HtmlElement html);
}

View File

@ -0,0 +1,28 @@
package me.mrletsplay.nojs.rest;
import me.mrletsplay.simplehttpserver.http.HttpRequestMethod;
import me.mrletsplay.simplehttpserver.http.HttpStatusCodes;
import me.mrletsplay.simplehttpserver.http.endpoint.Endpoint;
import me.mrletsplay.simplehttpserver.http.endpoint.EndpointCollection;
import me.mrletsplay.simplehttpserver.http.header.DefaultClientContentTypes;
import me.mrletsplay.simplehttpserver.http.request.HttpRequestContext;
import me.mrletsplay.simplehttpserver.http.request.urlencoded.UrlEncoded;
import me.mrletsplay.simplehttpserver.http.response.TextResponse;
public class TestEndpoint implements EndpointCollection {
@Endpoint(method = HttpRequestMethod.POST, path = "/test")
public void test() {
HttpRequestContext ctx = HttpRequestContext.getCurrentContext();
UrlEncoded encoded = ctx.getClientHeader().getPostData().getParsedAs(DefaultClientContentTypes.URLENCODED);
ctx.respond(HttpStatusCodes.OK_200, new TextResponse(encoded.getFirst("test")));
}
@Override
public String getBasePath() {
return "/api";
}
}

View File

@ -0,0 +1,23 @@
package me.mrletsplay.nojs.util;
public class ValidationException extends Exception {
private static final long serialVersionUID = 5956388648233218085L;
public ValidationException() {
super();
}
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
public ValidationException(String message) {
super(message);
}
public ValidationException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="31.999998"
height="31.999998"
viewBox="0 0 8.466666 8.466666"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<g
id="g7"
style="fill-opacity:1">
<rect
style="fill-opacity:1;stroke-width:0.184"
id="rect6"
width="1.0583333"
height="8.4666662"
x="5.4576707"
y="-4.2333331"
rx="0.26458332"
ry="0.26458332"
transform="rotate(45)" />
<rect
style="fill-opacity:1;stroke-width:0.184"
id="rect6-3"
width="1.0583333"
height="8.4666662"
x="-0.52916664"
y="1.7535037"
rx="0.26458332"
ry="0.26458332"
transform="matrix(-0.70710678,0.70710678,0.70710678,0.70710678,0,0)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1023 B

View File

@ -0,0 +1,119 @@
html {
--background: #222;
--foreground: #444;
--accent: #3584e4;
--text: #fff;
--link: var(--accent);
}
* {
font-family: system-ui;
color: var(--text);
}
body {
background-color: var(--background);
}
/*.button, .link, .input {
margin: 2px;
}*/
.button {
background: var(--accent);
color: var(--text);
border: none;
border-radius: 5px;
padding: 8px;
cursor: pointer;
font-weight: bold;
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.input {
background: #2c3444;
border: none;
color: var(--text);
border-radius: 5px;
padding: 8px;
}
.input:focus, .button:focus, .link:focus {
outline: 2px solid var(--accent);
}
.link, .button[data-style="link"] {
background: unset;
color: var(--link);
text-decoration: none;
padding: 0;
font-weight: normal;
font-size: 1em;
border-radius: 0;
}
.link:hover, .button[data-style="link"]:hover {
text-decoration: underline;
}
.message {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
position: fixed;
top: 0;
left: 0;
min-height: 30px;
background-color: orangered;
width: 100vw;
}
.message>label>input {
display: none;
}
.message {
padding-top: 5px;
padding-bottom: 5px;
}
.message>*:first-child {
margin-left: 10px;
}
.message>*:last-child {
margin-right: 10px;
}
.message>label {
position: relative;
/*background: var(--accent);*/
cursor: pointer;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
}
.message>label>svg {
fill: white;
width: 20px;
height: 20px;
}
/*.message>label:after {
display: block;
text-align: center;
content: "x";
width: 100%;
height: 100%;
}*/
.message:has(:checked) {
display: none;
}