diff --git a/submissions/WebCLBrowser/README.md b/submissions/WebCLBrowser/README.md new file mode 100644 index 00000000..3772cd14 --- /dev/null +++ b/submissions/WebCLBrowser/README.md @@ -0,0 +1,17 @@ +# CommandLine Web Browser +A simple command-line web browser, written in Java, using Lanterna and Jsoup.\ +Most web pages can be read in a simplistic state, though Cloudflare seems to block access to some pages. +The interface has a traditional browser toolbar at the top, with forward and back arrows, etc. +![example](https://www.wiicart.net/img/clbrowser.png) + +# How To Run +Download the JAR file locally, which can be found [here](https://github.com/Wiicart/CommandLineWebBrowser/releases), +[here](https://www.wiicart.net/clbrowser/clbrowser-1.0.jar), or in this directory. + - Example Command: `curl -L -O -A "Mozilla/5.0" https://www.wiicart.net/clbrowser/clbrowser-1.0.jar` + - On Linux or Mac, run `java -jar `. If using windows, use `javaw`. + +The program relies on the Lanterna, Jsoup and Jfiglet libraries, but they are shaded into the JAR, so no action is needed. + +# Why +I created this because when I'm ssh-ing into servers of mine, I usually have a browser open in the next window for references. +I figured a program like this could be useful for quickly glancing at a webpage without having to switch windows. \ No newline at end of file diff --git a/submissions/WebCLBrowser/clbrowser-1.0.jar b/submissions/WebCLBrowser/clbrowser-1.0.jar new file mode 100644 index 00000000..1c09eb89 Binary files /dev/null and b/submissions/WebCLBrowser/clbrowser-1.0.jar differ diff --git a/submissions/WebCLBrowser/pom.xml b/submissions/WebCLBrowser/pom.xml new file mode 100644 index 00000000..15ba5c4a --- /dev/null +++ b/submissions/WebCLBrowser/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + net.wiicart.webcli + CLIWebBrowser + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.3 + + + package + + shade + + + + + net.wiicart.webcli.Main + + + + + + + + + + + + com.googlecode.lanterna + lanterna + 3.1.2 + compile + + + org.jetbrains + annotations + 26.0.2 + compile + + + + org.jsoup + jsoup + 1.20.1 + compile + + + com.github.lalyos + jfiglet + 0.0.9 + compile + + + + \ No newline at end of file diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/CLIWebBrowser.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/CLIWebBrowser.java new file mode 100644 index 00000000..9c52b518 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/CLIWebBrowser.java @@ -0,0 +1,50 @@ +package net.wiicart.webcli; + +import com.googlecode.lanterna.TextColor; +import com.googlecode.lanterna.gui2.MultiWindowTextGUI; +import com.googlecode.lanterna.screen.Screen; +import com.googlecode.lanterna.terminal.DefaultTerminalFactory; +import com.googlecode.lanterna.terminal.Terminal; +import net.wiicart.webcli.screen.WebPageScreen; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public final class CLIWebBrowser { + + public CLIWebBrowser() { + try (Terminal terminal = createTerminalAndInit()){ + terminal.setBackgroundColor(TextColor.ANSI.RED); + Screen screen = createScreen(); + screen.startScreen(); + MultiWindowTextGUI gui = new MultiWindowTextGUI(screen); + WebPageScreen screen1 = new WebPageScreen(gui); + screen1.show(); + terminal.exitPrivateMode(); + terminal.flush(); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + + private @NotNull Terminal createTerminalAndInit() { + try { + DefaultTerminalFactory factory = new DefaultTerminalFactory(); + Terminal terminal = factory.createTerminal(); + terminal.enterPrivateMode(); + return terminal; + } catch(IOException e) { + throw new RuntimeException(e); + } + } + + private @NotNull Screen createScreen() { + try { + DefaultTerminalFactory factory = new DefaultTerminalFactory(); + return factory.createScreen(); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/Main.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/Main.java new file mode 100644 index 00000000..1796c330 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/Main.java @@ -0,0 +1,7 @@ +package net.wiicart.webcli; + +public class Main { + public static void main(String[] args) { + new CLIWebBrowser(); // initialize program + } +} \ No newline at end of file diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/AbstractScreen.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/AbstractScreen.java new file mode 100644 index 00000000..bb187189 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/AbstractScreen.java @@ -0,0 +1,48 @@ +package net.wiicart.webcli.screen; + +import org.jetbrains.annotations.MustBeInvokedByOverriders; + +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractScreen> { + + public abstract ScreenFutureRunner show(); + + //todo remove, uneccessarry + abstract boolean readyToExecute(); + + /** + * Class that manages Future Runnables to be executed once a Page has served its purpose. + * @param The type this was obtained from + */ + public static final class ScreenFutureRunner> { + + private final T screen; + + private final List runnables = new ArrayList<>(); + + ScreenFutureRunner(T screen) { + this.screen = screen; + } + + public final ScreenFutureRunner then(Runnable runnable) { + if (screen.readyToExecute()) { + runnable.run(); + } else { + runnables.add(runnable); + } + return this; + } + + + @MustBeInvokedByOverriders + void executeRunnables() { + for (Runnable runnable : runnables) { + runnable.run(); + } + runnables.clear(); + } + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/LoadingScreen.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/LoadingScreen.java new file mode 100644 index 00000000..1e6c9cdf --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/LoadingScreen.java @@ -0,0 +1,35 @@ +package net.wiicart.webcli.screen; + +import com.googlecode.lanterna.gui2.ProgressBar; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import org.jetbrains.annotations.NotNull; + +/** + * Represents a loading screen, used before a page is loaded. + */ +public final class LoadingScreen extends AbstractScreen { + + private final @NotNull WindowBasedTextGUI gui; + private final @NotNull ProgressBar progressBar; + + private ScreenFutureRunner future; + + public LoadingScreen(@NotNull WindowBasedTextGUI gui) { + this.gui = gui; + progressBar = new ProgressBar(); + future = new ScreenFutureRunner<>(this); + + + } + + @Override + public ScreenFutureRunner show() { + return future; + } + + @Override + boolean readyToExecute() { + return false; + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/URLEntryScreen.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/URLEntryScreen.java new file mode 100644 index 00000000..b2b19644 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/URLEntryScreen.java @@ -0,0 +1,77 @@ +package net.wiicart.webcli.screen; + +import com.googlecode.lanterna.TerminalSize; +import com.googlecode.lanterna.gui2.BasicWindow; +import com.googlecode.lanterna.gui2.Borders; +import com.googlecode.lanterna.gui2.Button; +import com.googlecode.lanterna.gui2.Direction; +import com.googlecode.lanterna.gui2.Label; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.TextBox; +import com.googlecode.lanterna.gui2.Window; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public final class URLEntryScreen extends AbstractScreen { + + private static final Set ENTRY_HINTS = Set.of(Window.Hint.CENTERED, Window.Hint.FIT_TERMINAL_WINDOW); + + private final WindowBasedTextGUI gui; + private final TextBox textBox = new TextBox(new TerminalSize(50, 1)); + private String entry; // The URL that was entered + private boolean execute = false; + + private final ScreenFutureRunner future; + + public URLEntryScreen(@NotNull WindowBasedTextGUI gui) { + this.gui = gui; + future = new ScreenFutureRunner<>(this); + initTextBox(); + // Do not call show() here + } + + private void initTextBox() { + textBox.withBorder(Borders.singleLine("Enter your URL:")); + } + + public ScreenFutureRunner show() { + Window window = createWindow(); + gui.addWindowAndWait(window); + execute = true; + return future; + } + + private @NotNull Panel createEntryPanel(Window window) { + Panel panel = new Panel(); + panel.setLayoutManager(new LinearLayout(Direction.VERTICAL)); + panel.setSize(new TerminalSize(25, 6)); + panel.addComponent(new Label("Enter a URL:")); + panel.addComponent(textBox); + panel.addComponent(new Button("Go", () -> { + entry = textBox.getText(); + future.executeRunnables(); + window.close(); + })); + return panel; + } + + private @NotNull Window createWindow() { + BasicWindow window = new BasicWindow("CLI Web Browser"); + window.setTitle("Web Browser"); + window.setComponent(createEntryPanel(window)); + window.setHints(ENTRY_HINTS); + return window; + } + + public String getEntry() { + return entry; + } + + @Override + boolean readyToExecute() { + return execute; + } +} \ No newline at end of file diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/WebPageScreen.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/WebPageScreen.java new file mode 100644 index 00000000..586413c6 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/WebPageScreen.java @@ -0,0 +1,229 @@ +package net.wiicart.webcli.screen; + +import com.googlecode.lanterna.SGR; +import com.googlecode.lanterna.TerminalSize; +import com.googlecode.lanterna.TextColor; +import com.googlecode.lanterna.graphics.SimpleTheme; +import com.googlecode.lanterna.gui2.BasicWindow; +import com.googlecode.lanterna.gui2.Direction; +import com.googlecode.lanterna.gui2.EmptySpace; +import com.googlecode.lanterna.gui2.Label; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.ProgressBar; +import com.googlecode.lanterna.gui2.TextBox; +import com.googlecode.lanterna.gui2.TextGUI; +import com.googlecode.lanterna.gui2.Window; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import com.googlecode.lanterna.gui2.WindowListenerAdapter; +import com.googlecode.lanterna.input.KeyStroke; +import net.wiicart.webcli.screen.helper.ToolBar; +import net.wiicart.webcli.screen.helper.UnreachablePage; +import net.wiicart.webcli.util.URLUtil; +import net.wiicart.webcli.web.renderer.primitivetext.PrimitiveTextBoxRenderer; +import net.wiicart.webcli.web.WebPage; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jsoup.Connection; +import org.jsoup.HttpStatusException; +import org.jsoup.Progress; + +import java.util.Set; + +public final class WebPageScreen extends AbstractScreen { + + private static final Set ENTRY_HINTS = Set.of(Window.Hint.CENTERED, Window.Hint.FIT_TERMINAL_WINDOW); + + private final WindowBasedTextGUI gui; + + private final ScreenFutureRunner future; + + private @NotNull Node currentNode = new Node(""); + + private final Panel content = new Panel(); + + private final Label title = loadTitle(); + + private final ToolBar toolBar = new ToolBar(this, "Title", "Enter a website address here"); + + private final EmptySpace emptySpace; + + private ProgressBar progress; + + private final Window window; + + public WebPageScreen(@NotNull WindowBasedTextGUI gui) { + this.gui = gui; + emptySpace = new EmptySpace(TextColor.ANSI.WHITE, new TerminalSize(getColumnCount(), 1)); + emptySpace.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Fill, LinearLayout.GrowPolicy.CanGrow)); + window = createWindow(); + future = new ScreenFutureRunner<>(this); + } + + private @NotNull Window createWindow() { + BasicWindow window1 = new BasicWindow("Web Browser"); + + Panel root = new Panel(); + root.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Fill)); + root.setLayoutManager(new LinearLayout(Direction.VERTICAL)); + root.addComponent(title); + Panel toolBarWrapper = new Panel(); + toolBarWrapper.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Center)); + toolBarWrapper.setSize(new TerminalSize(getColumnCount(), 1)); + toolBarWrapper.setTheme(new SimpleTheme(TextColor.ANSI.BLUE, TextColor.ANSI.WHITE_BRIGHT)); + toolBarWrapper.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Center, LinearLayout.GrowPolicy.CanGrow)); + toolBar.setPanelSize(getColumnCount()); + toolBarWrapper.addComponent(toolBar.getPanel()); + root.addComponent(toolBarWrapper); + root.addComponent(emptySpace); + + content.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Fill)); + root.addComponent(content); + + window1.setComponent(root); + window1.setHints(ENTRY_HINTS); + window1.addWindowListener(new WindowListenerAdapter() { + @Override + public void onResized(Window window, TerminalSize oldSize, TerminalSize newSize) { + emptySpace.setSize(new TerminalSize(newSize.getColumns(), 1)); + toolBar.setPanelSize(newSize.getColumns()); + } + }); + return window1; + } + + @Override + public ScreenFutureRunner show() { + loadTitle(); + goToAddress("https://www.wiicart.net/clbrowser", false); + gui.addWindowAndWait(window); + return future; + } + + @Override + boolean readyToExecute() { + return false; + } + + public void backwards() { + Node previous = currentNode.previous; + if(previous != null) { + currentNode = previous; + refresh(); + } + } + + public void forwards() { + Node next = currentNode.next; + if(next != null) { + currentNode = next; + refresh(); + } + } + + public void refresh() { + content.removeAllComponents(); + content.addComponent(PrimitiveTextBoxRenderer.generateFullBodyTextBox() + .addLine("Loading...")); + goToAddress(currentNode.address, false); + } + + public void goToAddress(String address, boolean updateNode) { + try { + if(updateNode) { + updateCurrentNode(address); + } + content.removeAllComponents(); + progress = new ProgressBar(0, 100, getColumnCount()); + progress.setValue(0); + content.addComponent(progress); + WebPage page = WebPage.fromAddress(address, generateProgressUpdate()); + title.setText(page.getTitle()); + toolBar.setAddress(URLUtil.simplify(address)); + content.removeAllComponents(); + page.applyContent(content); + } catch(HttpStatusException e) { + toErrorPage(e.getStatusCode(), e.getMessage()); + } catch(Exception e) { + toErrorPage(000, e.getMessage()); + } + } + + private void toErrorPage(int code, @Nullable String message) { + content.removeAllComponents(); + TextBox box = PrimitiveTextBoxRenderer.generateFullBodyTextBox(); + for(String string : UnreachablePage.withCode(code)) { + box.addLine(string); + } + + if(message != null) { + box.addLine(message); + } + + title.setText("Error " + code); + content.addComponent(box); + } + + // Moves the current node to a new node of the current webpage, + // updates the previous to reflect the new page. + private void updateCurrentNode(String address) { + Node newNode = new Node(address); + newNode.previous = currentNode; + currentNode.next = newNode; + currentNode = newNode; + } + + public void exit() { + window.close(); + } + + private @NotNull Label loadTitle() { + Label title1 = new Label("Blank Page"); + title1.setTheme(new SimpleTheme(TextColor.ANSI.BLUE, TextColor.ANSI.WHITE)); + title1.addStyle(SGR.BOLD); + title1.addStyle(SGR.FRAKTUR); + title1.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Center)); + title1.setSize(new TerminalSize(100, 100)); + return title1; + } + + + private static class Node { + + private final String address; + + private Node previous; + private Node next; + + public Node(String address) { + this.address = address; + } + } + + public int getColumnCount() { + return gui.getScreen().getTerminalSize().getColumns(); + } + + @SuppressWarnings("unused") + public int getRowCount() { + return gui.getScreen().getTerminalSize().getRows(); + } + + @Contract(pure = true) + private @NotNull Progress generateProgressUpdate() { + return (processed, total, percent, context) -> { + int processedPercent = Float.valueOf(percent).intValue(); + progress.setValue(processedPercent); + }; + } + + @SuppressWarnings("unused") + private static final class EscapeListener implements TextGUI.Listener { + + @Override + public boolean onUnhandledKeyStroke(TextGUI textGUI, KeyStroke keyStroke) { + return false; + } + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/helper/ToolBar.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/helper/ToolBar.java new file mode 100644 index 00000000..479bab28 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/helper/ToolBar.java @@ -0,0 +1,63 @@ +package net.wiicart.webcli.screen.helper; + +import com.googlecode.lanterna.TerminalPosition; +import com.googlecode.lanterna.TerminalSize; +import com.googlecode.lanterna.TextColor; +import com.googlecode.lanterna.graphics.SimpleTheme; +import com.googlecode.lanterna.gui2.Borders; +import com.googlecode.lanterna.gui2.Button; +import com.googlecode.lanterna.gui2.Direction; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.TextBox; +import net.wiicart.webcli.screen.WebPageScreen; +import org.jetbrains.annotations.NotNull; + +public final class ToolBar { + + private final @NotNull WebPageScreen screen; + + private final @NotNull String title; + + private final @NotNull Panel panel; + + private final @NotNull TextBox addressBox; + + public ToolBar(@NotNull WebPageScreen screen, @NotNull String title, @NotNull String address) { + this.screen = screen; + this.title = title; + addressBox = new TextBox(address); + addressBox.setSize(new TerminalSize(60, 1)); + addressBox.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Fill)); + panel = new Panel(); + initPanel(); + } + + private void initPanel() { + panel.setTheme(new SimpleTheme(TextColor.ANSI.BLUE, TextColor.ANSI.WHITE)); + panel.setLayoutManager(new LinearLayout(Direction.HORIZONTAL)); + panel.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Fill)); + panel.setPosition(new TerminalPosition(0, 0)); // ToolBar must stick to the top + + panel.withBorder(Borders.singleLine(title)); + panel.addComponent(new Button("←", screen::backwards)); + panel.addComponent(new Button("→", screen::forwards)); + panel.addComponent(new Button("⟳", screen::refresh)); + panel.addComponent(addressBox); + panel.addComponent(new Button("Go", () -> screen.goToAddress(addressBox.getText(), true))); + panel.addComponent(new Button("Exit", screen::exit)); + } + + public @NotNull Panel getPanel() { + return panel; + } + + public void setPanelSize(int columns) { + panel.setSize(new TerminalSize(columns, 1)); + } + + public void setAddress(@NotNull String address) { + addressBox.setText(address); + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/helper/UnreachablePage.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/helper/UnreachablePage.java new file mode 100644 index 00000000..7b271cbe --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/screen/helper/UnreachablePage.java @@ -0,0 +1,52 @@ +package net.wiicart.webcli.screen.helper; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public final class UnreachablePage { + + private UnreachablePage() {} + + private static final List LINES = new ArrayList<>(); + static { + LINES.add("$$$$$$$$\\ $$$$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$$\\ "); + LINES.add("$$ _____|$$ __$$\\ $$ __$$\\ $$ __$$\\ $$ __$$\\ "); + LINES.add("$$ | $$ | $$ |$$ | $$ |$$ / $$ |$$ | $$ |"); + LINES.add("$$$$$\\ $$$$$$$ |$$$$$$$ |$$ | $$ |$$$$$$$ |"); + LINES.add("$$ __| $$ __$$< $$ __$$< $$ | $$ |$$ __$$< "); + LINES.add("$$ | $$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ |"); + LINES.add("$$$$$$$$\\ $$ | $$ |$$ | $$ | $$$$$$ |$$ | $$ |"); + LINES.add("\\________|\\__| \\__|\\__| \\__| \\______/ \\__| \\__|"); + LINES.add(""); + LINES.add("Unable to reach page"); + LINES.add("status: {status}"); + } + + private static final List FOUR_ZERO_FOUR = new ArrayList<>(); + static { + FOUR_ZERO_FOUR.add("________________________________________________ "); + FOUR_ZERO_FOUR.add("| 0000 0000 000000000000 0000 0000 |\\ "); + FOUR_ZERO_FOUR.add("| 0000 0000 0000 0000 0000 0000 | \\ _ _ "); + FOUR_ZERO_FOUR.add("| 0000000000000000000 000000000000000000000 | \\ | \\ | \\ "); + FOUR_ZERO_FOUR.add("| 0000 0000 0000 0000 | |--| \\_| \\_____ "); + FOUR_ZERO_FOUR.add("| 0000 0000 0000 0000 | / \\____ _____\\"); + FOUR_ZERO_FOUR.add("| 0000 000000000000 0000 | / | / "); + FOUR_ZERO_FOUR.add("________________________________________________/ |_/ "); + FOUR_ZERO_FOUR.add(" 404 | Not Found "); + FOUR_ZERO_FOUR.add(" "); + } + + public static @NotNull List withCode(int code) { + if(code == 404) { + return FOUR_ZERO_FOUR; + } + + List list = new ArrayList<>(LINES); + String pos = list.get(10); + list.set(10, pos.replace("{status}", String.valueOf(code))); + return list; + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/util/URLUtil.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/util/URLUtil.java new file mode 100644 index 00000000..e5604ca9 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/util/URLUtil.java @@ -0,0 +1,41 @@ +package net.wiicart.webcli.util; + +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class URLUtil { + + private static final Pattern URL_WITH_HTTP_NO_PAGE = Pattern.compile( + "https?://([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,63}/?" + ); + + private URLUtil() {} + + // Normalizes a String url by adding https:// to the front if necessary, among other things. + public static @NotNull String normalizeURL(@NotNull String url) { + if (!url.startsWith("http://") && !url.startsWith("https://")) { + url = "https://" + url; + } + + Matcher matcher = URL_WITH_HTTP_NO_PAGE.matcher(url); + if(matcher.matches()) { + if(url.endsWith("/")) { + url = url + "index.html"; + } else { + url = url + "/index.html"; + } + } + return url; + } + + public static @NotNull String simplify(@NotNull String url) { + return url.replace("http://", "") + .replace("https://", "") + .replace("index.html/", "") + .replace("index.html", "") + .replace(".html", ""); + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/WebPage.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/WebPage.java new file mode 100644 index 00000000..007d10cf --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/WebPage.java @@ -0,0 +1,63 @@ +package net.wiicart.webcli.web; + +import com.googlecode.lanterna.gui2.Panel; +import net.wiicart.webcli.util.URLUtil; +import net.wiicart.webcli.web.renderer.primitivetext.PrimitiveTextBoxRenderer; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jsoup.Connection; +import org.jsoup.Jsoup; +import org.jsoup.Progress; +import org.jsoup.nodes.Document; + +import java.io.IOException; + +// https://www.baeldung.com/java-with-jsoup +@SuppressWarnings("unused") +public final class WebPage { + + private final @NotNull String title; + + private final @NotNull Document document; + + @Contract("_ -> new") + public static @NotNull WebPage fromAddress(@NotNull String address) throws IOException { + address = URLUtil.normalizeURL(address); + return new WebPage(address); + } + + @Contract("_, _ -> new") + public static @NotNull WebPage fromAddress(@NotNull String address, @NotNull Progress progress) throws IOException { + address = URLUtil.normalizeURL(address); + return new WebPage(address, progress); + } + + private WebPage(@NotNull String address) throws IOException { + document = Jsoup.connect(address) + .followRedirects(true) + .timeout(15000) + .get(); + + title = document.title(); + } + + private WebPage(@NotNull String address, Progress progress) throws IOException { + document = Jsoup.connect(address) + .onResponseProgress(progress) + .followRedirects(true) + .timeout(10000) + .get(); + + title = document.title(); + } + + public @NotNull String getTitle() { + return title; + } + + public void applyContent(@NotNull Panel panel) { + PrimitiveTextBoxRenderer renderer = new PrimitiveTextBoxRenderer(document); + renderer.applyContent(panel); + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/WebEngine.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/WebEngine.java new file mode 100644 index 00000000..0c1e0e66 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/WebEngine.java @@ -0,0 +1,8 @@ +package net.wiicart.webcli.web.engine; + +public final class WebEngine { + + public WebEngine() { + + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/tree/Tree.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/tree/Tree.java new file mode 100644 index 00000000..3d2c445e --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/tree/Tree.java @@ -0,0 +1,8 @@ +package net.wiicart.webcli.web.engine.tree; + +public final class Tree { + + private TreeNode root; + + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/tree/TreeBuilder.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/tree/TreeBuilder.java new file mode 100644 index 00000000..1f75b5f2 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/tree/TreeBuilder.java @@ -0,0 +1,4 @@ +package net.wiicart.webcli.web.engine.tree; + +public class TreeBuilder { +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/tree/TreeNode.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/tree/TreeNode.java new file mode 100644 index 00000000..5f30ee17 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/engine/tree/TreeNode.java @@ -0,0 +1,65 @@ +package net.wiicart.webcli.web.engine.tree; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Set; + +@SuppressWarnings("unused") +class TreeNode { + + private final @NotNull String type; // the tag type, like -> "body" + + private @Nullable String className; // The class of the element, if present + private @Nullable String id; // The id of the element, if present. + + private @NotNull Set children; + + + private @Nullable TreeNode parent; + + TreeNode(@NotNull String type) { + this.type = type; + className = null; + parent = null; + children = new HashSet<>(); + } + + boolean isLeaf() { + return children.isEmpty(); + } + + boolean isRoot() { + return parent == null; + } + + @NotNull String getType() { + return type; + } + + @Nullable String getClassName() { + return className; + } + + void setClassName(@Nullable String className) { + this.className = className; + } + + @Nullable TreeNode getParent() { + return parent; + } + + void setParent(@Nullable TreeNode parent) { + this.parent = parent; + } + + @NotNull Set getChildren() { + return children; + } + + void setChildren(@NotNull Set children) { + this.children = new HashSet<>(children); + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/Renderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/Renderer.java new file mode 100644 index 00000000..0649ccda --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/Renderer.java @@ -0,0 +1,8 @@ +package net.wiicart.webcli.web.renderer; + +import com.googlecode.lanterna.gui2.Panel; +import org.jetbrains.annotations.NotNull; + +public interface Renderer { + void applyContent(@NotNull Panel panel); +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitive/HeaderRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitive/HeaderRenderer.java new file mode 100644 index 00000000..8c1f82f7 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitive/HeaderRenderer.java @@ -0,0 +1,14 @@ +package net.wiicart.webcli.web.renderer.primitive; + +import com.googlecode.lanterna.gui2.Panel; +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Element; + +final class HeaderRenderer implements PrimitiveRenderer.ElementRenderer { + + @Override + public void applyContent(@NotNull Panel panel, @NotNull Element element) { + + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitive/PrimitiveRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitive/PrimitiveRenderer.java new file mode 100644 index 00000000..151f3610 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitive/PrimitiveRenderer.java @@ -0,0 +1,72 @@ +package net.wiicart.webcli.web.renderer.primitive; + +import com.googlecode.lanterna.SGR; +import com.googlecode.lanterna.TerminalSize; +import com.googlecode.lanterna.TextColor; +import com.googlecode.lanterna.graphics.SimpleTheme; +import com.googlecode.lanterna.gui2.EmptySpace; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.TextBox; +import net.wiicart.webcli.web.renderer.Renderer; +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +import java.util.Iterator; +import java.util.Locale; +import java.util.Set; + +@SuppressWarnings("unused") +public final class PrimitiveRenderer implements Renderer { + + private final Document document; + + private static final Set HEADERS = Set.of("h1", "h2", "h3", "h4", "h5", "h6"); + + @SuppressWarnings("unused") + public PrimitiveRenderer(@NotNull Document document) { + this.document = document; + } + + public void applyContent(@NotNull Panel panel) { + panel.addComponent(new EmptySpace(TextColor.ANSI.WHITE, new TerminalSize(500, 1))); + + Iterator it = document.body().stream().iterator(); + while (it.hasNext()) { + Element element = it.next(); + String text = element.ownText().strip().trim(); + if(text.isEmpty()) { + continue; + } + + if(HEADERS.contains(element.tagName().toLowerCase(Locale.ROOT))) { + panel.addComponent(createHeaderTextBox(text)); + } else { + panel.addComponent(createTextBox(text)); + } + System.out.println(text); + } + } + + private @NotNull TextBox createTextBox(String content) { + TextBox textBox = new TextBox(content, TextBox.Style.MULTI_LINE); + textBox.setTheme(new SimpleTheme(TextColor.ANSI.BLACK, TextColor.ANSI.WHITE)); + textBox.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Fill)); + textBox.setReadOnly(true); + return textBox; + } + + private @NotNull TextBox createHeaderTextBox(@NotNull String content) { + TextBox box = createTextBox(content); + box.setTheme(new SimpleTheme(TextColor.ANSI.BLACK, TextColor.ANSI.WHITE, SGR.BOLD)); + return createTextBox(content); + } + + protected interface ElementRenderer { + + void applyContent(@NotNull Panel panel, @NotNull Element element); + + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ASCIIHeaderRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ASCIIHeaderRenderer.java new file mode 100644 index 00000000..235d0aa7 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ASCIIHeaderRenderer.java @@ -0,0 +1,24 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import com.github.lalyos.jfiglet.FigletFont; +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Element; + +import java.util.List; + +final class ASCIIHeaderRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + @Override + public List getContent(@NotNull Element element) { + String text = element.ownText().strip().trim(); + if(text.isBlank()) { + return List.of(); + } + + try { + return List.of(FigletFont.convertOneLine(text)); + } catch (Exception e) { + return new SingleLineHeaderRenderer().getContent(element); + } + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/BreakRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/BreakRenderer.java new file mode 100644 index 00000000..e7a71586 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/BreakRenderer.java @@ -0,0 +1,19 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.jsoup.nodes.Element; + +import java.util.List; + +final class BreakRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + private static final String CONTENT = " ".repeat(PrimitiveTextBoxRenderer.ROW_SIZE); + + @Contract(value = "_ -> new", pure = true) + @Override + public @NotNull @Unmodifiable List getContent(@NotNull Element element) { + return List.of(CONTENT); + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ButtonRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ButtonRenderer.java new file mode 100644 index 00000000..1a38699d --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ButtonRenderer.java @@ -0,0 +1,38 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Element; + +import java.util.List; + +final class ButtonRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + private static final String FORMAT = "[BUTTON {data}]"; + + @Override + public List getContent(@NotNull Element element) { + StringBuilder builder = new StringBuilder(); + + String name = element.attr("name"); + if(!name.isEmpty()) { + builder.append("name=\"").append(name).append("\""); + } + + String type = element.attr("type"); + if(!type.isEmpty()) { + builder.append("type=\"").append(type).append("\""); + } + + String value = element.attr("text"); + if(!value.isEmpty()) { + builder.append("text=\"").append(value).append("\""); + } + + String text = element.attr("text"); + if(!text.isEmpty()) { + builder.append("text=\"").append(text).append("\""); + } + + return List.of(FORMAT.replace("{data}", builder.toString())); + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ImageRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ImageRenderer.java new file mode 100644 index 00000000..8512008d --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ImageRenderer.java @@ -0,0 +1,18 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Element; + +import java.util.List; + +final class ImageRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + private static final String FORMAT = "[IMAGE alt=\"{alt}\"]"; + + @Override + public List getContent(@NotNull Element element) { + String text = element.attr("alt"); + text = FORMAT.replace("{alt}", text); + return List.of(text); + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/LineRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/LineRenderer.java new file mode 100644 index 00000000..4619be08 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/LineRenderer.java @@ -0,0 +1,19 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.jsoup.nodes.Element; + +import java.util.List; + +final class LineRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + private static final String LINE = "_".repeat(PrimitiveTextBoxRenderer.ROW_SIZE); + + @Contract(value = "_ -> new", pure = true) + @Override + public @NotNull @Unmodifiable List getContent(@NotNull Element element) { + return List.of(LINE); + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/LinkRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/LinkRenderer.java new file mode 100644 index 00000000..44a92be0 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/LinkRenderer.java @@ -0,0 +1,20 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.jsoup.nodes.Element; + +import java.util.List; + +final class LinkRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + private static final String FORMAT = "[⚡LINK text=\"{text}\" link=\"{link}\"]"; + + @Override + public @NotNull @Unmodifiable List getContent(@NotNull Element element) { + String text = element.ownText().strip().trim(); + String link = element.attr("href").strip().trim(); + return List.of(FORMAT.replace("{text}", text).replace("{link}", link)); + } + +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ListItemRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ListItemRenderer.java new file mode 100644 index 00000000..e287aac3 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ListItemRenderer.java @@ -0,0 +1,19 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Element; + +import java.util.List; + +final class ListItemRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + @Override + public List getContent(@NotNull Element element) { + String text = element.ownText().strip().trim(); + if(text.isBlank()) { + return List.of(); + } + + return List.of(" •" + text); + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/PrimitiveTextBoxRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/PrimitiveTextBoxRenderer.java new file mode 100644 index 00000000..fcda463e --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/PrimitiveTextBoxRenderer.java @@ -0,0 +1,96 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import com.googlecode.lanterna.TerminalSize; +import com.googlecode.lanterna.TextColor; +import com.googlecode.lanterna.graphics.SimpleTheme; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.TextBox; +import net.wiicart.webcli.web.renderer.Renderer; +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; + +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +// Fully text-based renderer that renders all text in to a single TextBox. +public class PrimitiveTextBoxRenderer implements Renderer { + + static final int ROW_SIZE = 60; + + private final Document document; + + public PrimitiveTextBoxRenderer(@NotNull Document document) { + this.document = document; + } + + @Override + public void applyContent(@NotNull Panel panel) { + TextBox textBox = generateFullBodyTextBox(); + textBox.setTheme(new SimpleTheme(TextColor.ANSI.BLACK, TextColor.ANSI.WHITE_BRIGHT)); + Iterator it = document.body().stream().iterator(); + while (it.hasNext()) { + Element element = it.next(); + + ElementRenderer renderer = Type.getRenderer(element); + for(String string : renderer.getContent(element)) { + textBox.addLine(string); + } + } + panel.addComponent(textBox); + } + + public static @NotNull TextBox generateFullBodyTextBox() { + TextBox box = new TextBox(new TerminalSize(100, 100), TextBox.Style.MULTI_LINE); + box.setLayoutData(LinearLayout.createLayoutData(LinearLayout.Alignment.Fill)); + box.setSize(new TerminalSize(100, 100)); + box.setTheme(new SimpleTheme(TextColor.ANSI.WHITE, TextColor.ANSI.BLACK)); + box.setReadOnly(true); + return box; + } + + enum Type { + HEADER(Set.of("h1", "h2", "h3", "h4", "h5", "h6", "header"), new ASCIIHeaderRenderer()), + TEXT(Set.of("p", "span", "div", "textarea"), new TextRenderer()), + IMAGE(Set.of("img"), new ImageRenderer()), + LIST(Set.of("menu", "ul", "ol"), new TextRenderer()), + BREAK(Set.of("br"), new BreakRenderer()), + LINK(Set.of("a"), new LinkRenderer()), + LINE(Set.of("hr"), new LineRenderer()), + BUTTON(Set.of("button"), new ButtonRenderer()), + LIST_ITEM(Set.of("li"), new ListItemRenderer()), + SMALL_SCRIPT(Set.of("sup", "sub"), new ScriptRenderer()); + + private final Set tags; + private final ElementRenderer renderer; + + private static final UnimplementedElementRenderer UNIMPLEMENTED = new UnimplementedElementRenderer(); + + Type(@NotNull Set tags, @NotNull ElementRenderer renderer) { + this.tags = tags; + this.renderer = renderer; + } + + public static @NotNull ElementRenderer getRenderer(@NotNull Element element) { + for(Type type : Type.values()) { + if(type.tagMatches(element.tagName())) { + return type.renderer; + } + } + return UNIMPLEMENTED; + } + + public boolean tagMatches(@NotNull String tag) { + return tags.contains(tag.toLowerCase(Locale.ROOT)); + } + } + + interface ElementRenderer { + + List getContent(@NotNull Element element); + + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ScriptRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ScriptRenderer.java new file mode 100644 index 00000000..48895c03 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/ScriptRenderer.java @@ -0,0 +1,17 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Element; + +import java.util.List; + +final class ScriptRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + @Override + public List getContent(@NotNull Element element) { + String text = element.ownText().strip().trim(); + if(text.isBlank()) { + return List.of(); + } + return List.of(text); + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/SingleLineHeaderRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/SingleLineHeaderRenderer.java new file mode 100644 index 00000000..6b076011 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/SingleLineHeaderRenderer.java @@ -0,0 +1,41 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.jsoup.nodes.Element; + +import java.util.List; +import java.util.Map; + +final class SingleLineHeaderRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + private static final Map HEADERS = Map.of( + "header", "======", + "h1", "======", + "h2", "=====", + "h3", "====", + "h4", "===", + "h5", "==", + "h6", "=" + ); + + @Contract(pure = true) + @Override + public @NotNull @Unmodifiable List getContent(@NotNull Element element) { + String text = element.ownText().trim().strip(); + if(text.isEmpty()) { + return List.of(); + } + + String ends = HEADERS.get(element.tagName()); + if(ends == null) { + ends = "="; + } + + return List.of( + " ", + ends + text + ends + ); + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/SuperscriptRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/SuperscriptRenderer.java new file mode 100644 index 00000000..1753548e --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/SuperscriptRenderer.java @@ -0,0 +1,60 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.jsoup.nodes.Element; + +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +@SuppressWarnings("unused") // Terminals don't seem to support superscript, leaving for more testing later. +final class SuperscriptRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + private static final Map MAPPINGS = Map.ofEntries( + Map.entry("a", "ᵃ"), + Map.entry("b", "ᵇ"), + Map.entry("c", "ᶜ"), + Map.entry("d", "ᵈ"), + Map.entry("e", "ᵉ"), + Map.entry("f", "ᶠ"), + Map.entry("g", "ᵍ"), + Map.entry("h", "ʰ"), + Map.entry("i", "ᶦ"), + Map.entry("j", "ʲ"), + Map.entry("k", "ᵏ"), + Map.entry("l", "ˡ"), + Map.entry("m", "ᵐ"), + Map.entry("n", "ⁿ"), + Map.entry("o", "ᵒ"), + Map.entry("p", "ᵖ"), + Map.entry("q", "ᵠ"), + Map.entry("r", "ʳ"), + Map.entry("s", "ˢ"), + Map.entry("t", "ᵗ"), + Map.entry("u", "ᵘ"), + Map.entry("v", "ᵛ"), + Map.entry("w", "ʷ"), + Map.entry("x", "ˣ"), + Map.entry("y", "ʸ"), + Map.entry("z", "ᶻ") + ); + + + @Override + public @NotNull @Unmodifiable List getContent(@NotNull Element element) { + String text = element.ownText().strip().trim(); + if(text.isBlank()) { + return List.of(); + } + + StringBuilder builder = new StringBuilder(); + for(char ch : text.toCharArray()) { + String character = String.valueOf(ch); + String key = MAPPINGS.get(character.toLowerCase(Locale.ROOT)); + builder.append(Objects.requireNonNullElse(key, character)); + } + return List.of(builder.toString()); + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/TextRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/TextRenderer.java new file mode 100644 index 00000000..94578970 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/TextRenderer.java @@ -0,0 +1,52 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Element; + +import java.util.ArrayList; +import java.util.List; + +final class TextRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + private static final String START_AND_END = "+" + "-".repeat(PrimitiveTextBoxRenderer.ROW_SIZE - 2) + "+"; + + @Override + public @NotNull List getContent(@NotNull Element element) { + String text = element.ownText().strip().trim(); + + if(text.isEmpty()) { + return List.of(); + } + + List list = new ArrayList<>(); + list.add(START_AND_END); + + StringBuilder builder = new StringBuilder("|"); + String[] words = text.split(" "); + for(String word : words) { + if(builder.length() + word.length() < PrimitiveTextBoxRenderer.ROW_SIZE - 2) { + builder.append(word); + builder.append(" "); + } else { + complete(builder); + list.add(builder.toString()); + builder = new StringBuilder("|" + word + " "); + } + } + + if(!builder.isEmpty()) { + complete(builder); + list.add(builder.toString()); + } + + list.add(START_AND_END); + list.add(""); + + return list; + } + + private void complete(@NotNull StringBuilder builder) { + int length = builder.length(); + builder.append(" ".repeat(Math.max(0, PrimitiveTextBoxRenderer.ROW_SIZE - length - 1))).append("|"); + } +} diff --git a/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/UnimplementedElementRenderer.java b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/UnimplementedElementRenderer.java new file mode 100644 index 00000000..00c69e6b --- /dev/null +++ b/submissions/WebCLBrowser/src/main/java/net/wiicart/webcli/web/renderer/primitivetext/UnimplementedElementRenderer.java @@ -0,0 +1,21 @@ +package net.wiicart.webcli.web.renderer.primitivetext; + +import org.jetbrains.annotations.NotNull; +import org.jsoup.nodes.Element; + +import java.util.List; + +final class UnimplementedElementRenderer implements PrimitiveTextBoxRenderer.ElementRenderer { + + @Override + public List getContent(@NotNull Element element) { + String text = element.ownText().strip().trim(); + if(text.isEmpty()) { + return List.of(); + } + + String head = "+-----(UNIMPLEMENTED ELEMENT \"" + element.tagName() + "\")-----+"; + String footer = "+" + "-".repeat(head.length() - 2) + "+"; + return List.of(head, text, footer); + } +} diff --git a/submissions/WebCLBrowser/src/main/resources/tlds.txt b/submissions/WebCLBrowser/src/main/resources/tlds.txt new file mode 100644 index 00000000..e4b19a44 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/resources/tlds.txt @@ -0,0 +1,1440 @@ +AAA +AARP +ABB +ABBOTT +ABBVIE +ABC +ABLE +ABOGADO +ABUDHABI +AC +ACADEMY +ACCENTURE +ACCOUNTANT +ACCOUNTANTS +ACO +ACTOR +AD +ADS +ADULT +AE +AEG +AERO +AETNA +AF +AFL +AFRICA +AG +AGAKHAN +AGENCY +AI +AIG +AIRBUS +AIRFORCE +AIRTEL +AKDN +AL +ALIBABA +ALIPAY +ALLFINANZ +ALLSTATE +ALLY +ALSACE +ALSTOM +AM +AMAZON +AMERICANEXPRESS +AMERICANFAMILY +AMEX +AMFAM +AMICA +AMSTERDAM +ANALYTICS +ANDROID +ANQUAN +ANZ +AO +AOL +APARTMENTS +APP +APPLE +AQ +AQUARELLE +AR +ARAB +ARAMCO +ARCHI +ARMY +ARPA +ART +ARTE +AS +ASDA +ASIA +ASSOCIATES +AT +ATHLETA +ATTORNEY +AU +AUCTION +AUDI +AUDIBLE +AUDIO +AUSPOST +AUTHOR +AUTO +AUTOS +AW +AWS +AX +AXA +AZ +AZURE +BA +BABY +BAIDU +BANAMEX +BAND +BANK +BAR +BARCELONA +BARCLAYCARD +BARCLAYS +BAREFOOT +BARGAINS +BASEBALL +BASKETBALL +BAUHAUS +BAYERN +BB +BBC +BBT +BBVA +BCG +BCN +BD +BE +BEATS +BEAUTY +BEER +BERLIN +BEST +BESTBUY +BET +BF +BG +BH +BHARTI +BI +BIBLE +BID +BIKE +BING +BINGO +BIO +BIZ +BJ +BLACK +BLACKFRIDAY +BLOCKBUSTER +BLOG +BLOOMBERG +BLUE +BM +BMS +BMW +BN +BNPPARIBAS +BO +BOATS +BOEHRINGER +BOFA +BOM +BOND +BOO +BOOK +BOOKING +BOSCH +BOSTIK +BOSTON +BOT +BOUTIQUE +BOX +BR +BRADESCO +BRIDGESTONE +BROADWAY +BROKER +BROTHER +BRUSSELS +BS +BT +BUILD +BUILDERS +BUSINESS +BUY +BUZZ +BV +BW +BY +BZ +BZH +CA +CAB +CAFE +CAL +CALL +CALVINKLEIN +CAM +CAMERA +CAMP +CANON +CAPETOWN +CAPITAL +CAPITALONE +CAR +CARAVAN +CARDS +CARE +CAREER +CAREERS +CARS +CASA +CASE +CASH +CASINO +CAT +CATERING +CATHOLIC +CBA +CBN +CBRE +CC +CD +CENTER +CEO +CERN +CF +CFA +CFD +CG +CH +CHANEL +CHANNEL +CHARITY +CHASE +CHAT +CHEAP +CHINTAI +CHRISTMAS +CHROME +CHURCH +CI +CIPRIANI +CIRCLE +CISCO +CITADEL +CITI +CITIC +CITY +CK +CL +CLAIMS +CLEANING +CLICK +CLINIC +CLINIQUE +CLOTHING +CLOUD +CLUB +CLUBMED +CM +CN +CO +COACH +CODES +COFFEE +COLLEGE +COLOGNE +COM +COMMBANK +COMMUNITY +COMPANY +COMPARE +COMPUTER +COMSEC +CONDOS +CONSTRUCTION +CONSULTING +CONTACT +CONTRACTORS +COOKING +COOL +COOP +CORSICA +COUNTRY +COUPON +COUPONS +COURSES +CPA +CR +CREDIT +CREDITCARD +CREDITUNION +CRICKET +CROWN +CRS +CRUISE +CRUISES +CU +CUISINELLA +CV +CW +CX +CY +CYMRU +CYOU +CZ +DAD +DANCE +DATA +DATE +DATING +DATSUN +DAY +DCLK +DDS +DE +DEAL +DEALER +DEALS +DEGREE +DELIVERY +DELL +DELOITTE +DELTA +DEMOCRAT +DENTAL +DENTIST +DESI +DESIGN +DEV +DHL +DIAMONDS +DIET +DIGITAL +DIRECT +DIRECTORY +DISCOUNT +DISCOVER +DISH +DIY +DJ +DK +DM +DNP +DO +DOCS +DOCTOR +DOG +DOMAINS +DOT +DOWNLOAD +DRIVE +DTV +DUBAI +DUNLOP +DUPONT +DURBAN +DVAG +DVR +DZ +EARTH +EAT +EC +ECO +EDEKA +EDU +EDUCATION +EE +EG +EMAIL +EMERCK +ENERGY +ENGINEER +ENGINEERING +ENTERPRISES +EPSON +EQUIPMENT +ER +ERICSSON +ERNI +ES +ESQ +ESTATE +ET +EU +EUROVISION +EUS +EVENTS +EXCHANGE +EXPERT +EXPOSED +EXPRESS +EXTRASPACE +FAGE +FAIL +FAIRWINDS +FAITH +FAMILY +FAN +FANS +FARM +FARMERS +FASHION +FAST +FEDEX +FEEDBACK +FERRARI +FERRERO +FI +FIDELITY +FIDO +FILM +FINAL +FINANCE +FINANCIAL +FIRE +FIRESTONE +FIRMDALE +FISH +FISHING +FIT +FITNESS +FJ +FK +FLICKR +FLIGHTS +FLIR +FLORIST +FLOWERS +FLY +FM +FO +FOO +FOOD +FOOTBALL +FORD +FOREX +FORSALE +FORUM +FOUNDATION +FOX +FR +FREE +FRESENIUS +FRL +FROGANS +FRONTIER +FTR +FUJITSU +FUN +FUND +FURNITURE +FUTBOL +FYI +GA +GAL +GALLERY +GALLO +GALLUP +GAME +GAMES +GAP +GARDEN +GAY +GB +GBIZ +GD +GDN +GE +GEA +GENT +GENTING +GEORGE +GF +GG +GGEE +GH +GI +GIFT +GIFTS +GIVES +GIVING +GL +GLASS +GLE +GLOBAL +GLOBO +GM +GMAIL +GMBH +GMO +GMX +GN +GODADDY +GOLD +GOLDPOINT +GOLF +GOO +GOODYEAR +GOOG +GOOGLE +GOP +GOT +GOV +GP +GQ +GR +GRAINGER +GRAPHICS +GRATIS +GREEN +GRIPE +GROCERY +GROUP +GS +GT +GU +GUCCI +GUGE +GUIDE +GUITARS +GURU +GW +GY +HAIR +HAMBURG +HANGOUT +HAUS +HBO +HDFC +HDFCBANK +HEALTH +HEALTHCARE +HELP +HELSINKI +HERE +HERMES +HIPHOP +HISAMITSU +HITACHI +HIV +HK +HKT +HM +HN +HOCKEY +HOLDINGS +HOLIDAY +HOMEDEPOT +HOMEGOODS +HOMES +HOMESENSE +HONDA +HORSE +HOSPITAL +HOST +HOSTING +HOT +HOTELS +HOTMAIL +HOUSE +HOW +HR +HSBC +HT +HU +HUGHES +HYATT +HYUNDAI +IBM +ICBC +ICE +ICU +ID +IE +IEEE +IFM +IKANO +IL +IM +IMAMAT +IMDB +IMMO +IMMOBILIEN +IN +INC +INDUSTRIES +INFINITI +INFO +ING +INK +INSTITUTE +INSURANCE +INSURE +INT +INTERNATIONAL +INTUIT +INVESTMENTS +IO +IPIRANGA +IQ +IR +IRISH +IS +ISMAILI +IST +ISTANBUL +IT +ITAU +ITV +JAGUAR +JAVA +JCB +JE +JEEP +JETZT +JEWELRY +JIO +JLL +JM +JMP +JNJ +JO +JOBS +JOBURG +JOT +JOY +JP +JPMORGAN +JPRS +JUEGOS +JUNIPER +KAUFEN +KDDI +KE +KERRYHOTELS +KERRYPROPERTIES +KFH +KG +KH +KI +KIA +KIDS +KIM +KINDLE +KITCHEN +KIWI +KM +KN +KOELN +KOMATSU +KOSHER +KP +KPMG +KPN +KR +KRD +KRED +KUOKGROUP +KW +KY +KYOTO +KZ +LA +LACAIXA +LAMBORGHINI +LAMER +LAND +LANDROVER +LANXESS +LASALLE +LAT +LATINO +LATROBE +LAW +LAWYER +LB +LC +LDS +LEASE +LECLERC +LEFRAK +LEGAL +LEGO +LEXUS +LGBT +LI +LIDL +LIFE +LIFEINSURANCE +LIFESTYLE +LIGHTING +LIKE +LILLY +LIMITED +LIMO +LINCOLN +LINK +LIVE +LIVING +LK +LLC +LLP +LOAN +LOANS +LOCKER +LOCUS +LOL +LONDON +LOTTE +LOTTO +LOVE +LPL +LPLFINANCIAL +LR +LS +LT +LTD +LTDA +LU +LUNDBECK +LUXE +LUXURY +LV +LY +MA +MADRID +MAIF +MAISON +MAKEUP +MAN +MANAGEMENT +MANGO +MAP +MARKET +MARKETING +MARKETS +MARRIOTT +MARSHALLS +MATTEL +MBA +MC +MCKINSEY +MD +ME +MED +MEDIA +MEET +MELBOURNE +MEME +MEMORIAL +MEN +MENU +MERCKMSD +MG +MH +MIAMI +MICROSOFT +MIL +MINI +MINT +MIT +MITSUBISHI +MK +ML +MLB +MLS +MM +MMA +MN +MO +MOBI +MOBILE +MODA +MOE +MOI +MOM +MONASH +MONEY +MONSTER +MORMON +MORTGAGE +MOSCOW +MOTO +MOTORCYCLES +MOV +MOVIE +MP +MQ +MR +MS +MSD +MT +MTN +MTR +MU +MUSEUM +MUSIC +MV +MW +MX +MY +MZ +NA +NAB +NAGOYA +NAME +NAVY +NBA +NC +NE +NEC +NET +NETBANK +NETFLIX +NETWORK +NEUSTAR +NEW +NEWS +NEXT +NEXTDIRECT +NEXUS +NF +NFL +NG +NGO +NHK +NI +NICO +NIKE +NIKON +NINJA +NISSAN +NISSAY +NL +NO +NOKIA +NORTON +NOW +NOWRUZ +NOWTV +NP +NR +NRA +NRW +NTT +NU +NYC +NZ +OBI +OBSERVER +OFFICE +OKINAWA +OLAYAN +OLAYANGROUP +OLLO +OM +OMEGA +ONE +ONG +ONL +ONLINE +OOO +OPEN +ORACLE +ORANGE +ORG +ORGANIC +ORIGINS +OSAKA +OTSUKA +OTT +OVH +PA +PAGE +PANASONIC +PARIS +PARS +PARTNERS +PARTS +PARTY +PAY +PCCW +PE +PET +PF +PFIZER +PG +PH +PHARMACY +PHD +PHILIPS +PHONE +PHOTO +PHOTOGRAPHY +PHOTOS +PHYSIO +PICS +PICTET +PICTURES +PID +PIN +PING +PINK +PIONEER +PIZZA +PK +PL +PLACE +PLAY +PLAYSTATION +PLUMBING +PLUS +PM +PN +PNC +POHL +POKER +POLITIE +PORN +POST +PR +PRAXI +PRESS +PRIME +PRO +PROD +PRODUCTIONS +PROF +PROGRESSIVE +PROMO +PROPERTIES +PROPERTY +PROTECTION +PRU +PRUDENTIAL +PS +PT +PUB +PW +PWC +PY +QA +QPON +QUEBEC +QUEST +RACING +RADIO +RE +READ +REALESTATE +REALTOR +REALTY +RECIPES +RED +REDSTONE +REDUMBRELLA +REHAB +REISE +REISEN +REIT +RELIANCE +REN +RENT +RENTALS +REPAIR +REPORT +REPUBLICAN +REST +RESTAURANT +REVIEW +REVIEWS +REXROTH +RICH +RICHARDLI +RICOH +RIL +RIO +RIP +RO +ROCKS +RODEO +ROGERS +ROOM +RS +RSVP +RU +RUGBY +RUHR +RUN +RW +RWE +RYUKYU +SA +SAARLAND +SAFE +SAFETY +SAKURA +SALE +SALON +SAMSCLUB +SAMSUNG +SANDVIK +SANDVIKCOROMANT +SANOFI +SAP +SARL +SAS +SAVE +SAXO +SB +SBI +SBS +SC +SCB +SCHAEFFLER +SCHMIDT +SCHOLARSHIPS +SCHOOL +SCHULE +SCHWARZ +SCIENCE +SCOT +SD +SE +SEARCH +SEAT +SECURE +SECURITY +SEEK +SELECT +SENER +SERVICES +SEVEN +SEW +SEX +SEXY +SFR +SG +SH +SHANGRILA +SHARP +SHELL +SHIA +SHIKSHA +SHOES +SHOP +SHOPPING +SHOUJI +SHOW +SI +SILK +SINA +SINGLES +SITE +SJ +SK +SKI +SKIN +SKY +SKYPE +SL +SLING +SM +SMART +SMILE +SN +SNCF +SO +SOCCER +SOCIAL +SOFTBANK +SOFTWARE +SOHU +SOLAR +SOLUTIONS +SONG +SONY +SOY +SPA +SPACE +SPORT +SPOT +SR +SRL +SS +ST +STADA +STAPLES +STAR +STATEBANK +STATEFARM +STC +STCGROUP +STOCKHOLM +STORAGE +STORE +STREAM +STUDIO +STUDY +STYLE +SU +SUCKS +SUPPLIES +SUPPLY +SUPPORT +SURF +SURGERY +SUZUKI +SV +SWATCH +SWISS +SX +SY +SYDNEY +SYSTEMS +SZ +TAB +TAIPEI +TALK +TAOBAO +TARGET +TATAMOTORS +TATAR +TATTOO +TAX +TAXI +TC +TCI +TD +TDK +TEAM +TECH +TECHNOLOGY +TEL +TEMASEK +TENNIS +TEVA +TF +TG +TH +THD +THEATER +THEATRE +TIAA +TICKETS +TIENDA +TIPS +TIRES +TIROL +TJ +TJMAXX +TJX +TK +TKMAXX +TL +TM +TMALL +TN +TO +TODAY +TOKYO +TOOLS +TOP +TORAY +TOSHIBA +TOTAL +TOURS +TOWN +TOYOTA +TOYS +TR +TRADE +TRADING +TRAINING +TRAVEL +TRAVELERS +TRAVELERSINSURANCE +TRUST +TRV +TT +TUBE +TUI +TUNES +TUSHU +TV +TVS +TW +TZ +UA +UBANK +UBS +UG +UK +UNICOM +UNIVERSITY +UNO +UOL +UPS +US +UY +UZ +VA +VACATIONS +VANA +VANGUARD +VC +VE +VEGAS +VENTURES +VERISIGN +VERSICHERUNG +VET +VG +VI +VIAJES +VIDEO +VIG +VIKING +VILLAS +VIN +VIP +VIRGIN +VISA +VISION +VIVA +VIVO +VLAANDEREN +VN +VODKA +VOLVO +VOTE +VOTING +VOTO +VOYAGE +VU +WALES +WALMART +WALTER +WANG +WANGGOU +WATCH +WATCHES +WEATHER +WEATHERCHANNEL +WEBCAM +WEBER +WEBSITE +WED +WEDDING +WEIBO +WEIR +WF +WHOSWHO +WIEN +WIKI +WILLIAMHILL +WIN +WINDOWS +WINE +WINNERS +WME +WOLTERSKLUWER +WOODSIDE +WORK +WORKS +WORLD +WOW +WS +WTC +WTF +XBOX +XEROX +XIHUAN +XIN +XN--11B4C3D +XN--1CK2E1B +XN--1QQW23A +XN--2SCRJ9C +XN--30RR7Y +XN--3BST00M +XN--3DS443G +XN--3E0B707E +XN--3HCRJ9C +XN--3PXU8K +XN--42C2D9A +XN--45BR5CYL +XN--45BRJ9C +XN--45Q11C +XN--4DBRK0CE +XN--4GBRIM +XN--54B7FTA0CC +XN--55QW42G +XN--55QX5D +XN--5SU34J936BGSG +XN--5TZM5G +XN--6FRZ82G +XN--6QQ986B3XL +XN--80ADXHKS +XN--80AO21A +XN--80AQECDR1A +XN--80ASEHDB +XN--80ASWG +XN--8Y0A063A +XN--90A3AC +XN--90AE +XN--90AIS +XN--9DBQ2A +XN--9ET52U +XN--9KRT00A +XN--B4W605FERD +XN--BCK1B9A5DRE4C +XN--C1AVG +XN--C2BR7G +XN--CCK2B3B +XN--CCKWCXETD +XN--CG4BKI +XN--CLCHC0EA0B2G2A9GCD +XN--CZR694B +XN--CZRS0T +XN--CZRU2D +XN--D1ACJ3B +XN--D1ALF +XN--E1A4C +XN--ECKVDTC9D +XN--EFVY88H +XN--FCT429K +XN--FHBEI +XN--FIQ228C5HS +XN--FIQ64B +XN--FIQS8S +XN--FIQZ9S +XN--FJQ720A +XN--FLW351E +XN--FPCRJ9C3D +XN--FZC2C9E2C +XN--FZYS8D69UVGM +XN--G2XX48C +XN--GCKR3F0F +XN--GECRJ9C +XN--GK3AT1E +XN--H2BREG3EVE +XN--H2BRJ9C +XN--H2BRJ9C8C +XN--HXT814E +XN--I1B6B1A6A2E +XN--IMR513N +XN--IO0A7I +XN--J1AEF +XN--J1AMH +XN--J6W193G +XN--JLQ480N2RG +XN--JVR189M +XN--KCRX77D1X4A +XN--KPRW13D +XN--KPRY57D +XN--KPUT3I +XN--L1ACC +XN--LGBBAT1AD8J +XN--MGB9AWBF +XN--MGBA3A3EJT +XN--MGBA3A4F16A +XN--MGBA7C0BBN0A +XN--MGBAAM7A8H +XN--MGBAB2BD +XN--MGBAH1A3HJKRD +XN--MGBAI9AZGQP6J +XN--MGBAYH7GPA +XN--MGBBH1A +XN--MGBBH1A71E +XN--MGBC0A9AZCG +XN--MGBCA7DZDO +XN--MGBCPQ6GPA1A +XN--MGBERP4A5D4AR +XN--MGBGU82A +XN--MGBI4ECEXP +XN--MGBPL2FH +XN--MGBT3DHD +XN--MGBTX2B +XN--MGBX4CD0AB +XN--MIX891F +XN--MK1BU44C +XN--MXTQ1M +XN--NGBC5AZD +XN--NGBE9E0A +XN--NGBRX +XN--NODE +XN--NQV7F +XN--NQV7FS00EMA +XN--NYQY26A +XN--O3CW4H +XN--OGBPF8FL +XN--OTU796D +XN--P1ACF +XN--P1AI +XN--PGBS0DH +XN--PSSY2U +XN--Q7CE6A +XN--Q9JYB4C +XN--QCKA1PMC +XN--QXA6A +XN--QXAM +XN--RHQV96G +XN--ROVU88B +XN--RVC1E0AM3E +XN--S9BRJ9C +XN--SES554G +XN--T60B56A +XN--TCKWE +XN--TIQ49XQYJ +XN--UNUP4Y +XN--VERMGENSBERATER-CTB +XN--VERMGENSBERATUNG-PWB +XN--VHQUV +XN--VUQ861B +XN--W4R85EL8FHU5DNRA +XN--W4RS40L +XN--WGBH1C +XN--WGBL6A +XN--XHQ521B +XN--XKC2AL3HYE2A +XN--XKC2DL3A5EE0H +XN--Y9A3AQ +XN--YFRO4I67O +XN--YGBI2AMMX +XN--ZFR164B +XXX +XYZ +YACHTS +YAHOO +YAMAXUN +YANDEX +YE +YODOBASHI +YOGA +YOKOHAMA +YOU +YOUTUBE +YT +YUN +ZA +ZAPPOS +ZARA +ZERO +ZIP +ZM +ZONE +ZUERICH +ZW \ No newline at end of file diff --git a/submissions/WebCLBrowser/src/main/resources/unreachable/404.txt b/submissions/WebCLBrowser/src/main/resources/unreachable/404.txt new file mode 100644 index 00000000..6d1591b9 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/resources/unreachable/404.txt @@ -0,0 +1,26 @@ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡭⠥⠐⠒⠒⠒⠒⠂⠤⢤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⢀⣤⠖⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠲⣤⡀⠀⠀⠀⠀⠀ +⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢦⡀⠀⠀⠀ +⠀⠀⢠⠟⠀⠀⠀⠀⠀⣠⣤⣤⡀⠀⠀⠀⠀⠀⣤⣤⣄⠀⠀⠀⠀⠈⠻⡄⠀⠀ a8 ,a888a, a8 + ,d88 ,8P"' `"Y8, ,d88 + a8P88 ,8P Y8, a8P88 + ,d8" 88 88 88 ,d8" 88 + a8P' 88 88 88 a8P' 88 + ,d8" 88 88 88 ,d8" 88 + 888888888888 88 88 888888888888 + 88 `8b d8' 88 + 88 `8ba, ,ad8' 88 + 88 "Y888P" 88 + + +⠀⣠⠋⠀⠀⠀⠀⠀⠀⣿⣿⣿⣧⠀⠀⠀⠀⣼⣿⣿⣿⠀⠀⠀⠀⠀⠀⠹⣄⠀ +⠀⡏⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⡟⠀⠀⠀⠀⢻⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⢹⠀ +⢰⠀⠀⠀⠀⠀⠀⠀⠀⠙⠛⠛⠀⠀⠀⠀⠀⠀⠛⠛⠋⠀⠀⠀⠀⠀⠀⠀⠀⡆ +⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⣤⣤⣤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇ +⠸⠀⠀⠀⠀⠀⠀⠀⣠⣴⡿⠟⠛⠋⠉⠉⠙⠛⠻⢷⣦⣄⠀⠀⠀⠀⠀⠀⠀⠇ +⠀⣇⠀⠀⠀⠀⢀⣼⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⣷⡀⠀⠀⠀⠀⣸⠀ +⠀⠘⣆⠀⠒⠲⣾⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢘⡷⠖⠒⠀⣰⠃⠀ +⠀⠀⠘⣦⡀⠀⠈⠳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠺⠁⠀⠀⣴⠃⠀⠀ +⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠞⠁⠀⠀⠀ +⠀⠀⠀⠀⠀⠈⠛⠦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⠴⠛⠁⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠓⠒⠠⠤⠤⠤⠤⠄⠒⠚⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀ \ No newline at end of file diff --git a/submissions/WebCLBrowser/src/main/resources/unreachable/genericerror.text b/submissions/WebCLBrowser/src/main/resources/unreachable/genericerror.text new file mode 100644 index 00000000..acc32c53 --- /dev/null +++ b/submissions/WebCLBrowser/src/main/resources/unreachable/genericerror.text @@ -0,0 +1,32 @@ + + ___________________________________________________________________ + / \ + | ____________________________________________________________ | + | | | | + | | /$$$$$$$$ /$$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$$ | | + | | | $$_____/ | $$__ $$ | $$__ $$ /$$__ $$ | $$__ $$ | | + | | | $$ | $$ \ $$ | $$ \ $$ | $$ \ $$ | $$ \ $$ | | + | | | $$$$$ | $$$$$$$/ | $$$$$$$/ | $$ | $$ | $$$$$$$/ | | + | | | $$__/ | $$__ $$ | $$__ $$ | $$ | $$ | $$__ $$ | | + | | | $$ | $$ \ $$ | $$ \ $$ | $$ | $$ | $$ \ $$ | | + | | | $$$$$$$$ | $$ | $$ | $$ | $$ | $$$$$$/ | $$ | $$ | | + | | |________/ |__/ |__/ |__/ |__/ \______/ |__/ |__/ | | + | | | | + | | Unable to reach page: | | + | | status: 000 | | + | | | | + | |____________________________________________________________| | + | | + \____________________________________________________________________/ + \___________________________________/ + ___________________________________________ + _-' .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. --- `-_ + _-'.-.-. .---.-.-.-.-.-.-.-.-.-.-.-.-.-.-.--. .-.-.`-_ + _-'.-.-.-. .---.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-`__`. .-.-.-.`-_ + _-'.-.-.-.-. .-----.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-----. .-.-.-.-.`-_ + _-'.-.-.-.-.-. .---.-. .-------------------------. .-.---. .---.-.-.-.`-_ + :-------------------------------------------------------------------------: + `---._.-------------------------------------------------------------._.---' + +computer art credit: Roland Hangg +asciiart.eu \ No newline at end of file diff --git a/submissions/WebCLBrowser/src/main/resources/welcome.html b/submissions/WebCLBrowser/src/main/resources/welcome.html new file mode 100644 index 00000000..e69de29b