10 Commits

57 changed files with 1664 additions and 3553 deletions

7
.idea/discord.xml generated
View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
</component>
</project>

6
.idea/misc.xml generated
View File

@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="ASK" />
<option name="description" value="" />
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
@@ -8,7 +12,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="25" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="23" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

2
.idea/vcs.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -1 +0,0 @@
Please read the license here: https://open-autonomous-connection.org/license.html

View File

@@ -1,5 +1,23 @@
# Open Autonomous Connection WebClient
> [!IMPORTANT]
> This is the classic version!
> Classic version is not longer maintained or supported please switch the Branch to master
> The new WebClient has a Backwards compatibility and is supporting the classic Version
This is the Protocol for our Open Autonomous Connection project.<br />
This is the WebClient for our Open Autonomous Connection project.<br />
Feel free to join our Discord.
<br />
## License Notice
This project (OAC) is licensed under the [Open Autonomous Public License (OAPL)](https://open-autonomous-connection.org/license.html).
**Third-party components:**
- *UnlegitLibrary* is authored by the same copyright holder and is used here under a special agreement:
While [UnlegitLibrary](https://repo.unlegitdqrk.dev/UnlegitDqrk/unlegitlibrary/) is generally distributed under the [GNU GPLv3](https://repo.unlegitdqrk.dev/UnlegitDqrk/unlegitlibrary/src/branch/master/LICENSE),
it is additionally licensed under OAPL **exclusively for the OAC project**.
Therefore, within OAC, the OAPL terms apply to UnlegitLibrary as well.
# Bugs/Problems
# In progress
# TODO

199
pom.xml
View File

@@ -1,23 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.openautonomousconnection</groupId>
<artifactId>WebClient</artifactId>
<version>1.0.0-BETA.1.0</version>
<version>1.0-CLASSIC</version>
<organization>
<name>Open Autonomous Connection</name>
<url>https://open-autonomous-connection.org/</url>
</organization>
<url>https://open-autonomous-connection.org/</url>
<description>The default WebClient</description>
<description>The default DNS-Server</description>
<properties>
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.10.0</junit.version>
</properties>
<developers>
@@ -45,16 +46,54 @@
<issueManagement>
<system>Issue Tracker</system>
<url>https://repo.open-autonomous-connection.org/open-autonomous-connection/WebClient/issues</url>
<url>https://repo.open-autonomous-connection.org/open-autonomous-connection/DNSServer/issues</url>
</issueManagement>
<licenses>
<license>
<name>Open Autonomous Public License (OAPL)</name>
<url>https://open-autonomous-connection.org/license.html</url>
<name>Open Autonomous Public License</name>
<url>https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL/</url>
<distribution>repo</distribution>
</license>
</licenses>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<executions>
<execution>
<!-- Default configuration for running with: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>
org.openautonomousconnection.browser/org.openautonomousconnection.browser.ViewApplication
</mainClass>
<launcher>app</launcher>
<jlinkZipName>app</jlinkZipName>
<jlinkImageName>app</jlinkImageName>
<noManPages>true</noManPages>
<stripDebug>true</stripDebug>
<noHeaderFiles>true</noHeaderFiles>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>oac</id>
@@ -68,18 +107,8 @@
<dependencies>
<dependency>
<groupId>org.openautonomousconnection</groupId>
<artifactId>OACSwing</artifactId>
<version>1.0.0-BETA.1.1</version>
</dependency>
<dependency>
<groupId>org.openautonomousconnection</groupId>
<artifactId>LuaScript</artifactId>
<version>1.0.0-BETA.1.1</version>
</dependency>
<dependency>
<groupId>org.openautonomousconnection</groupId>
<artifactId>InfoNameLib</artifactId>
<version>1.0.0-BETA.1.3</version>
<artifactId>protocol</artifactId>
<version>1.0.0-CLASSIC</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
@@ -89,61 +118,99 @@
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<version>26-ea+22</version>
<scope>compile</scope>
<artifactId>javafx-controls</artifactId>
<version>21</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>26-ea+22</version>
<scope>compile</scope>
<version>21</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>21</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>26-ea+22</version>
<scope>compile</scope>
<version>21</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>21</version>
</dependency>
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>11.1.2</version>
</dependency>
<dependency>
<groupId>com.dlsc.formsfx</groupId>
<artifactId>formsfx-core</artifactId>
<version>11.6.0</version>
<exclusions>
<exclusion>
<groupId>org.openjfx</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.synedra</groupId>
<artifactId>validatorfx</artifactId>
<version>0.4.0</version>
<exclusions>
<exclusion>
<groupId>org.openjfx</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.kordamp.ikonli</groupId>
<artifactId>ikonli-javafx</artifactId>
<version>12.3.1</version>
</dependency>
<dependency>
<groupId>org.kordamp.bootstrapfx</groupId>
<artifactId>bootstrapfx-core</artifactId>
<version>0.4.0</version>
</dependency>
<dependency>
<groupId>eu.hansolo</groupId>
<artifactId>tilesfx</artifactId>
<version>11.48</version>
<exclusions>
<exclusion>
<groupId>org.openjfx</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.almasb</groupId>
<artifactId>fxgl</artifactId>
<version>17.3</version>
<exclusions>
<exclusion>
<groupId>org.openjfx</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
<configuration>
<failOnError>false</failOnError>
<failOnWarnings>false</failOnWarnings>
<doclint>none</doclint>
<locale>en_US</locale>
<encoding>UTF-8</encoding>
<docencoding>UTF-8</docencoding>
<charset>UTF-8</charset>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,22 @@
module me.openautonomousconnection.browser {
requires javafx.controls;
requires javafx.fxml;
requires javafx.web;
requires org.controlsfx.controls;
requires com.dlsc.formsfx;
requires net.synedra.validatorfx;
requires org.kordamp.ikonli.javafx;
requires org.kordamp.bootstrapfx.core;
requires eu.hansolo.tilesfx;
requires com.almasb.fxgl.all;
requires java.desktop;
requires java.sql;
requires protocol;
requires unlegitlibrary;
exports org.openautonomousconnection.browser;
opens org.openautonomousconnection.browser to javafx.fxml;
exports org.openautonomousconnection.browser.controller;
opens org.openautonomousconnection.browser.controller to javafx.fxml;
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.browser;
import org.openautonomousconnection.browser.controller.Browser;
import org.openautonomousconnection.protocol.domain.Domain;
import org.openautonomousconnection.protocol.side.ProtocolClient;
import org.openautonomousconnection.protocol.utils.SiteType;
public class Client extends ProtocolClient {
@Override
public void handleHTMLContent(SiteType siteType, Domain domain, String htmlContent) {
Browser.getInstance().loadHtml(siteType, domain, htmlContent);
}
@Override
public void handleMessage(String message) {
MessageDialog.show(message);
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.browser;
import dev.unlegitdqrk.unlegitlibrary.file.ConfigurationManager;
import org.openautonomousconnection.protocol.utils.APIInformation;
import java.io.File;
import java.io.IOException;
public class Config {
private static File configFile = new File("./config.txt");
private static ConfigurationManager config;
private static APIInformation apiInformation;
public static void init() throws IOException {
if (!configFile.exists()) configFile.createNewFile();
config = new ConfigurationManager(configFile);
config.loadProperties();
if (!config.isSet("dns.host")) config.set("dns.host", "45.155.173.50");
if (!config.isSet("dns.port")) config.set("dns.port", 9382);
if (!config.isSet("api.username")) config.set("api.username", "");
if (!config.isSet("api.application")) config.set("api.application", "");
if (!config.isSet("api.key")) config.set("api.key", "");
config.saveProperties();
apiInformation = new APIInformation(getAPIUsername(), getAPIApplication(), getAPIKey());
}
public static APIInformation getApiInformation() {
return apiInformation;
}
public static String getDNSHost() {
return config.getString("dns.host");
}
public static void setDNSHost(String host) throws IOException {
config.set("dns.host", host);
config.saveProperties();
}
public static int getDNSPort() {
return config.getInt("dns.port");
}
public static void setDNSPort(int port) throws IOException {
config.set("dns.port", port);
config.saveProperties();
}
public static String getAPIUsername() {
return config.getString("api.username");
}
public static void setAPIUsername(String username) throws IOException {
config.set("api.username", username);
config.saveProperties();
}
public static String getAPIApplication() {
return config.getString("api.application");
}
public static void setAPIApplication(String application) throws IOException {
config.set("api.application", application);
config.saveProperties();
}
public static String getAPIKey() {
return config.getString("api.key");
}
public static void setAPIKey(String key) throws IOException {
config.set("api.key", key);
config.saveProperties();
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.browser;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.openautonomousconnection.browser.controller.Browser;
import org.openautonomousconnection.browser.history.HistoryManager;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.ProtocolSettings;
import org.openautonomousconnection.protocol.ProtocolVersion;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
public class Main extends Application {
public static ProtocolBridge protocolBridge;
private static final Thread shutdownThread = new Thread(shutdown());
private static Runnable shutdown() {
return () -> {
try {
if (protocolBridge == null) return;
protocolBridge.getProtocolClient().disconnectClient();
} catch (IOException exception) {
exception.printStackTrace();
}
};
}
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
try {
Config.init();
} catch (IOException exception) {
exception.printStackTrace();
return;
}
HistoryManager.loadHistory();
String host = Config.getDNSHost();
int port = Config.getDNSPort();
final ProtocolSettings protocolSettings = new ProtocolSettings();
protocolSettings.host = host;
protocolSettings.port = port;
protocolBridge = new ProtocolBridge(ProtocolVersion.PV_1_0_0, protocolSettings, new Client());
protocolBridge.getProtocolClient().setProtocolBridge(protocolBridge);
Runtime.getRuntime().addShutdownHook(shutdownThread);
try {
protocolBridge.getProtocolClient().startClient();
} catch (Exception exception) {
exception.printStackTrace();
Platform.runLater(() -> {
try {
Parent root2 = FXMLLoader.load(MessageDialog.class.getResource("MessageDialog.fxml"));
Stage stage2 = new Stage();
stage2.setTitle("Open Autonomous Connection - DNS Message Dialog");
stage2.setScene(new Scene(root2));
stage2.setResizable(false);
stage2.requestFocus();
stage2.show();
MessageDialog.getInstance().txtServer.setText("Failed to connect to DNS-Server! Please try again later.\n" + exception.getMessage());
} catch (IOException exception2) {
exception2.printStackTrace();
return;
}
});
launch(args);
return;
}
}
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Browser.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Open Autonomous Connection");
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(event -> shutdownThread.start());
Browser.getInstance().setStage(stage);
Platform.runLater(() -> {
try {
Parent root2 = FXMLLoader.load(MessageDialog.class.getResource("MessageDialog.fxml"));
Stage stage2 = new Stage();
stage2.setTitle("Open Autonomous Connection - DNS Message Dialog");
stage2.setScene(new Scene(root2));
stage2.setResizable(false);
stage2.requestFocus();
stage2.show();
MessageDialog.getInstance().txtServer.setText("Please close this window now!");
stage2.close();
} catch (IOException exception) {
exception.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.browser;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class MessageDialog implements Initializable {
private static MessageDialog instance;
public Label txtServer;
public static MessageDialog getInstance() {
return instance;
}
public static void show(String text) {
Platform.runLater(() -> {
try {
Parent root = FXMLLoader.load(MessageDialog.class.getResource("MessageDialog.fxml"));
Stage stage = new Stage();
stage.setTitle("Open Autonomous Connection - DNS Message Dialog");
stage.setScene(new Scene(root));
stage.setResizable(false);
stage.requestFocus();
stage.show();
MessageDialog.getInstance().txtServer.setText(text);
} catch (IOException exception) {
exception.printStackTrace();
}
});
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
instance = this;
}
}

View File

@@ -0,0 +1,318 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.browser.controller;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.openautonomousconnection.browser.Main;
import org.openautonomousconnection.browser.MessageDialog;
import org.openautonomousconnection.browser.history.HistoryManager;
import org.openautonomousconnection.protocol.domain.Domain;
import org.openautonomousconnection.protocol.domain.LocalDomain;
import org.openautonomousconnection.protocol.domain.RequestDomain;
import org.openautonomousconnection.protocol.utils.DomainUtils;
import org.openautonomousconnection.protocol.utils.SiteType;
import org.openautonomousconnection.protocol.utils.WebsitesContent;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import java.awt.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ResourceBundle;
public class Browser implements Initializable {
public static Browser instance;
public Button btnHome;
public Button btnBackward;
public Button btnReload;
public Button btnGo;
public Button btnForward;
public Button btnSettings;
public TextField domainInput;
public Button btnHistory;
public WebView webView;
public static Browser getInstance() {
return instance;
}
public void onHistoryClick(ActionEvent actionEvent) {
Platform.runLater(() -> {
try {
Parent root = FXMLLoader.load(getClass().getResource("History.fxml"));
Stage stage = new Stage();
stage.setTitle("Open Autonomous Connection - Settings");
stage.setScene(new Scene(root));
stage.setResizable(false);
stage.requestFocus();
stage.show();
} catch (IOException exception) {
exception.printStackTrace();
}
});
}
public void onForwardClick(ActionEvent actionEvent) {
try {
navigate(HistoryManager.navigateForward());
} catch (URISyntaxException | IOException | ClassNotFoundException exception) {
exception.printStackTrace();
}
}
public void onBackwardClick(ActionEvent actionEvent) {
try {
navigate(HistoryManager.navigateBack());
} catch (URISyntaxException | IOException | ClassNotFoundException exception) {
exception.printStackTrace();
}
}
public void onHomeClick(ActionEvent actionEvent) {
try {
navigate(SiteType.PUBLIC.name + "://browser-start.root/");
} catch (IOException | URISyntaxException | ClassNotFoundException exception) {
exception.printStackTrace();
}
}
public void onReloadClick(ActionEvent actionEvent) {
try {
navigate(domainInput.getText());
} catch (URISyntaxException | IOException | ClassNotFoundException exception) {
exception.printStackTrace();
}
}
public void onGoClick(ActionEvent actionEvent) {
try {
navigate(domainInput.getText());
HistoryManager.addHistoryItem(domainInput.getText());
} catch (URISyntaxException | IOException | ClassNotFoundException exception) {
exception.printStackTrace();
}
}
public void navigate(String url) throws IOException, URISyntaxException, ClassNotFoundException {
if (url == null) return;
if (url.isEmpty()) return;
if (url.isBlank()) return;
if (url.startsWith(SiteType.LOCAL.name)) {
loadLocalDomain(url);
return;
}
if (url.startsWith("http://")) url = url.substring("http://".length());
if (url.startsWith("https://")) url = url.substring("https://".length());
if (url.startsWith("www.")) url = url.substring("www.".length());
if (!url.startsWith(SiteType.PUBLIC.name)) url = SiteType.PUBLIC.name + "://" + url;
String tld = DomainUtils.getTopLevelDomain(url);
String name = DomainUtils.getDomainName(url);
String path = DomainUtils.getPath(url);
// TODO: Navigate
Main.protocolBridge.getProtocolClient().resolveSite(new RequestDomain(name, tld, path));
}
private void loadLocalDomain(String url) throws IOException, URISyntaxException, ClassNotFoundException {
if (url == null) return;
if (url.isEmpty()) return;
if (url.isBlank()) return;
if (url.startsWith(SiteType.PUBLIC.name) || url.startsWith("http://") || url.startsWith("Https://") || url.startsWith("www.")) {
navigate(url);
return;
}
if (!url.startsWith(SiteType.LOCAL.name)) url = SiteType.LOCAL.name + "://" + url;
File file = new File(url.substring((SiteType.LOCAL.name + "://").length()));
if (!file.exists()) {
loadHtml(SiteType.PROTOCOL, new LocalDomain("file-not-found", "html", ""), WebsitesContent.FILE_NOT_FOUND);
return;
}
loadFile(file);
}
public void onSettingsClick(ActionEvent actionEvent) {
Platform.runLater(() -> {
try {
Parent root = FXMLLoader.load(getClass().getResource("Settings.fxml"));
Stage stage = new Stage();
stage.setTitle("Open Autonomous Connection - Settings");
stage.setScene(new Scene(root));
stage.setResizable(false);
stage.requestFocus();
stage.show();
} catch (IOException exception) {
exception.printStackTrace();
}
});
}
private Stage stage;
public final void setStage(Stage stage) {
this.stage = stage;
stage.setTitle("Open Autonomous Connection - " + getTitle(webView.getEngine()));
}
private String getTitle(WebEngine webEngine) {
Document doc = webEngine.getDocument();
if (doc == null) return domainInput.getText();
NodeList heads = doc.getElementsByTagName("head");
String titleText = webEngine.getLocation() ; // use location if page does not define a title
if (heads.getLength() > 0) {
Element head = (Element)heads.item(0);
NodeList titles = head.getElementsByTagName("title");
if (titles.getLength() > 0) {
Node title = titles.item(0);
titleText = title.getTextContent();
}
}
return titleText ;
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
instance = this;
btnGo.fire();
try {
URL oracle = new URL("https://raw.githubusercontent.com/Open-Autonomous-Connection/browser/master/src/resources/version.txt");
BufferedReader in = new BufferedReader(new InputStreamReader(oracle.openStream()));
String version = "";
String inputLine;
while ((inputLine = in.readLine()) != null) version += inputLine;
if (!version.equalsIgnoreCase(Files.readString(Path.of(Main.class.getResource("../../../version.txt").toURI())))) {
System.out.println();
System.out.println("===============================================");
System.out.println("IMPORTANT: A NEW VERSION IS PUBLISHED ON GITHUB");
System.out.println("===============================================");
System.out.println();
MessageDialog.show("A new version is published on GitHub:\nhttps://github.com/Open-Autonomous-Connection/");
}
} catch (IOException | URISyntaxException exception) {
System.out.println();
System.out.println("===============================================");
System.out.println("IMPORTANT: VERSION CHECK COULD NOT COMPLETED! VISIT OUR GITHUB");
System.out.println("https://github.com/Open-Autonomous-Connection");
System.out.println("===============================================");
System.out.println();
MessageDialog.show("Version check could not completed! Visit our GitHub:\nhttps://github.com/Open-Autonomous-Connection/");
}
webView.getEngine().getLoadWorker().stateProperty().addListener((observableValue, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
EventListener eventListener = evt -> {
String type = evt.getType();
if (type.equalsIgnoreCase("click")) {
String href = ((Element) evt.getTarget()).getAttribute("href");
try {
if (href.startsWith("http") || href.startsWith("https://")) {
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) Desktop.getDesktop().browse(new URI(href));
else MessageDialog.show("Failed to load Website " + href);
return;
}
if (href.startsWith(SiteType.PUBLIC.name + "://")) navigate(href);
else {
String base = "oac://" + DomainUtils.getDomainName(domainInput.getText()) + "." + DomainUtils.getTopLevelDomain(domainInput.getText()) + "/";
navigate(base + (href.startsWith("/") ? href.substring("/".length()) : href));
}
} catch (IOException | URISyntaxException | ClassNotFoundException exception) {
loadHtml(SiteType.PROTOCOL, new LocalDomain("error-occurred", "html", ""), WebsitesContent.ERROR_OCCURRED(exception.getMessage()));
}
}
};
Document doc = webView.getEngine().getDocument();
NodeList nodeList = doc.getElementsByTagName("a");
for (int i = 0; i < nodeList.getLength(); i++) {
((EventTarget) nodeList.item(i)).addEventListener("click", eventListener, false);
//((EventTarget) nodeList.item(i)).addEventListener(EVENT_TYPE_MOUSEOVER, listener, false);
//((EventTarget) nodeList.item(i)).addEventListener(EVENT_TYPE_MOUSEOVER, listener, false);
}
}
});
// TODO: Crash on header redirect. Fixing later
// webView.getEngine().locationProperty().addListener((obs, oldLocation, newLocation) -> {
// try {
// if (newLocation.startsWith("http") || newLocation.startsWith("https://")) {
// if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) Desktop.getDesktop().browse(new URI(newLocation));
// else MessageDialog.show("Failed to load Website " + newLocation);
//
// return;
// }
//
// if (newLocation.startsWith(SiteType.PUBLIC.name + "://")) navigate(newLocation);
// else {
// String base = "oac://" + DomainUtils.getDomainName(domainInput.getText()) + "." + DomainUtils.getTopLevelDomain(domainInput.getText()) + "/";
// navigate(base + (newLocation.startsWith("/") ? newLocation.substring("/".length()) : newLocation));
// }
// } catch (IOException | URISyntaxException | ClassNotFoundException exception) {
// loadHtml(SiteType.PROTOCOL, new LocalDomain("error-occurred", "html", ""), WebsitesContent.ERROR_OCCURRED(exception.getMessage()));
// }
// });
}
public void loadFile(File file) {
domainInput.setText(SiteType.LOCAL.name + "://" + file.getAbsolutePath());
Platform.runLater(() -> {
webView.getEngine().load(file.toURI().toString());
if (stage != null) stage.setTitle("Open Autonomous Connection - " + getTitle(webView.getEngine()));
});
}
public void loadHtml(SiteType siteType, Domain domain, String htmlContent) {
domainInput.setText(siteType.name + "://" + domain.name + "." + domain.topLevelDomain + "/" + domain.getPath());
Platform.runLater(() -> {
webView.getEngine().loadContent(htmlContent);
if (stage != null) stage.setTitle("Open Autonomous Connection - " + getTitle(webView.getEngine()));
});
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.browser.controller;
import javafx.event.ActionEvent;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import org.openautonomousconnection.browser.Config;
import org.openautonomousconnection.browser.MessageDialog;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class Settings implements Initializable {
public TextField hostInput;
public TextField portInput;
public Button btnSave;
public TextField usernameInput;
public TextField applicationInput;
public TextField apiKeyInput;
public void onSaveClick(ActionEvent actionEvent) {
try {
Integer.parseInt(portInput.getText());
} catch (NumberFormatException ignored) {
MessageDialog.show("Please enter a valid port number");
return;
}
try {
Config.setDNSHost(hostInput.getText());
Config.setDNSPort(Integer.parseInt(portInput.getText()));
Config.setAPIApplication(applicationInput.getText());
Config.setAPIUsername(usernameInput.getText());
Config.setAPIKey(apiKeyInput.getText());
} catch (IOException exception) {
MessageDialog.show("Failed to save settings:\n" + exception.getMessage());
return;
}
MessageDialog.show("Browser need a restart");
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
hostInput.setText(Config.getDNSHost());
portInput.setText(String.valueOf(Config.getDNSPort()));
usernameInput.setText(Config.getAPIUsername());
applicationInput.setText(Config.getAPIApplication());
apiKeyInput.setText(Config.getAPIKey());
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.browser.history;
import java.io.Serializable;
import java.text.SimpleDateFormat;
public class HistoryItem implements Serializable {
private String url;
private SimpleDateFormat dateFormat;
public HistoryItem(String url, SimpleDateFormat dateFormat) {
this.url = url;
this.dateFormat = dateFormat;
}
public final String getUrl() {
return url;
}
public final SimpleDateFormat getDateFormat() {
return dateFormat;
}
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
*
* You are unauthorized to remove this copyright.
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
* See LICENSE-File if exists
*/
package org.openautonomousconnection.browser.history;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
public class HistoryManager {
private static final File historyFile = new File("history.txt");
private static List<HistoryItem> historyItems = new ArrayList<>();
private static int currentPosition = -1;
public static List<HistoryItem> getHistoryItems() {
return historyItems;
}
public static void addHistoryItem(String url) {
if (currentPosition < historyItems.size() - 1)
historyItems.subList(currentPosition + 1, historyItems.size()).clear();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
historyItems.add(new HistoryItem(url, dateFormat));
currentPosition++;
saveHistory();
}
public static void saveHistory() {
if (!historyFile.exists()) {
try {
historyFile.createNewFile();
} catch (IOException exception) {
exception.printStackTrace();
return;
}
}
if (historyFile.length() == 0) return;
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(historyFile))) {
oos.writeObject(historyItems);
} catch (IOException exception) {
exception.printStackTrace();
}
}
public static void loadHistory() {
if (!historyFile.exists()) {
try {
historyFile.createNewFile();
} catch (IOException exception) {
exception.printStackTrace();
return;
}
}
if (historyFile.length() == 0) return;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(historyFile))) {
historyItems = (List<HistoryItem>) ois.readObject();
currentPosition = historyItems.size() - 1;
} catch (IOException | ClassNotFoundException exception) {
exception.printStackTrace();
}
}
public static String navigateBack() {
if (currentPosition > 0) {
currentPosition--;
return historyItems.get(currentPosition).getUrl();
}
return null;
}
public static String navigateForward() {
if (currentPosition < historyItems.size() - 1) {
currentPosition++;
return historyItems.get(currentPosition).getUrl();
}
return null;
}
public static void clear() {
historyItems.clear();
currentPosition = -1;
}
}

View File

@@ -1,99 +0,0 @@
package org.openautonomousconnection.webclient;
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
import org.openautonomousconnection.infonamelib.LibClientImpl;
import org.openautonomousconnection.infonamelib.OacWebUrlInstaller;
import org.openautonomousconnection.oacswing.component.OACOptionPane;
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolINSServerEvent;
import java.awt.*;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Protocol client implementation for the WebClient.
*/
public class ClientImpl extends ProtocolClient {
private final LibImpl libImpl = new LibImpl();
private final AtomicBoolean connectedInitialized = new AtomicBoolean(false);
private final Component dialogParent;
private final Runnable onServerReady;
public ClientImpl(Component dialogParent, Runnable onServerReady) {
this.dialogParent = dialogParent;
this.onServerReady = Objects.requireNonNull(onServerReady, "onServerReady");
}
@Override
public boolean trustINS(String caFingerprint) {
Object[] options = {"Continue", "Cancel"};
int result = OACOptionPane.showOptionDialog(
dialogParent,
"You never connected to this INS before!\n" +
"Fingerprint: " + caFingerprint + "\nDo you want to connect?",
"INS Connection",
OACOptionPane.YES_NO_OPTION,
OACOptionPane.INFORMATION_MESSAGE,
null,
options,
options[0]
);
return result == 0;
}
@Override
public boolean trustNewINSFingerprint(String oldCAFingerprint, String newCAFingerprint) {
Object[] options = {"Continue", "Cancel"};
int result = OACOptionPane.showOptionDialog(
dialogParent,
"The fingerprint does not match with the saved fingerprint!\n" +
"Saved Fingerprint: " + oldCAFingerprint + "\n" +
"New Fingerprint: " + newCAFingerprint + "\n" +
"Do you want to connect?",
"INS Connection",
OACOptionPane.YES_NO_OPTION,
OACOptionPane.INFORMATION_MESSAGE,
null,
options,
options[0]
);
return result == 0;
}
@Listener
public void onConnected(ConnectedToProtocolINSServerEvent event) {
try {
buildServerConnection(null, getProtocolBridge().getProtocolValues().ssl);
OacWebUrlInstaller.installOnce(getProtocolBridge().getProtocolValues().eventManager, this, libImpl);
if (connectedInitialized.compareAndSet(false, true)) {
onServerReady.run();
}
} catch (Exception e) {
getProtocolBridge().getLogger().exception("Failed to build Server connection", e);
OACOptionPane.showMessageDialog(
dialogParent,
"Failed to to build Server connection:\n" + e.getMessage(),
"Server Connection",
OACOptionPane.ERROR_MESSAGE
);
}
}
private class LibImpl extends LibClientImpl {
@Override
public void serverConnectionFailed(Exception exception) {
getProtocolBridge().getLogger().exception("Failed to connect to server", exception);
OACOptionPane.showMessageDialog(
dialogParent,
"Failed to connect to Server:\n" + exception.getMessage(),
"Server Connection",
OACOptionPane.ERROR_MESSAGE
);
}
}
}

View File

@@ -1,34 +0,0 @@
package org.openautonomousconnection.webclient;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Initializes the JavaFX Toolkit exactly once for Swing embedding.
*/
public final class FxBootstrap {
private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false);
private FxBootstrap() {
// Utility class
}
/**
* Ensures JavaFX Toolkit is initialized.
* Must be called before any Platform.runLater() usage.
*/
public static void ensureInitialized() {
if (!INITIALIZED.compareAndSet(false, true)) {
return;
}
// Creating a JFXPanel initializes the JavaFX toolkit in Swing apps.
new JFXPanel();
// Keep JavaFX runtime alive even if last window closes.
Platform.setImplicitExit(false);
}
}

View File

@@ -1,58 +0,0 @@
package org.openautonomousconnection.webclient;
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
import org.openautonomousconnection.oacswing.component.design.Design;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import org.openautonomousconnection.webclient.settings.AppSettings;
import org.openautonomousconnection.webclient.settings.SettingsManager;
import org.openautonomousconnection.webclient.ui.BrowserUI;
import javax.swing.*;
import java.io.IOException;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
/**
* Application entry point.
*/
public class Main {
private static BrowserUI ui;
private static AppSettings settings;
private static AddonLoader addonLoader;
public static BrowserUI getUi() {
return ui;
}
public static AppSettings getSettings() {
return settings;
}
private static void installDefaultCookieManager() {
if (CookieHandler.getDefault() != null) return;
CookieManager cm = new CookieManager();
cm.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(cm);
}
public static void main(String[] args) throws IOException {
settings = SettingsManager.load();
FxBootstrap.ensureInitialized();
installDefaultCookieManager();
DesignManager.setGlobalDesign(Design.DARK);
SwingUtilities.invokeLater(() -> {
ui = new BrowserUI(settings);
ui.setSize(1200, 800);
ui.setLocationRelativeTo(null);
ui.setVisible(true);
ui.openNewTab(settings.getStartPageUrl());
});
}
}

View File

@@ -1,40 +0,0 @@
package org.openautonomousconnection.webclient.lua;
import org.openautonomousconnection.webclient.ClientImpl;
import java.util.Objects;
public class WebLogger {
private final String host;
private final ClientImpl client;
public WebLogger(String host, ClientImpl client) {
this.host = host;
this.client = Objects.requireNonNull(client, "client");
}
public void log(String string) {
client.getProtocolBridge().getLogger().log(host + ": " + string);
}
public void info(String info) {
client.getProtocolBridge().getLogger().info(host + ": " + info);
}
public void warn(String warn) {
client.getProtocolBridge().getLogger().warn(host + ": " + warn);
}
public void error(String error) {
client.getProtocolBridge().getLogger().error(host + ": " + error);
}
public void exception(String infoLine, Exception exception) {
client.getProtocolBridge().getLogger().exception(host + ": " + infoLine, exception);
}
public void debug(String debug) {
client.getProtocolBridge().getLogger().debug(host + ": " + debug);
}
}

View File

@@ -1,37 +0,0 @@
package org.openautonomousconnection.webclient.lua.hosts;
import org.openautonomousconnection.luascript.hosts.ConsoleHost;
import org.openautonomousconnection.webclient.lua.WebLogger;
public class ConsoleHostImpl implements ConsoleHost {
private final WebLogger logger;
public ConsoleHostImpl(WebLogger logger) {
this.logger = logger;
}
@Override
public void info(String message) {
logger.info(message);
}
@Override
public void log(String message) {
logger.log(message);
}
@Override
public void warn(String message) {
logger.warn(message);
}
@Override
public void error(String message) {
logger.error(message);
}
@Override
public void exception(String message) {
logger.exception("", new RuntimeException(message));
}
}

View File

@@ -1,312 +0,0 @@
package org.openautonomousconnection.webclient.lua.hosts;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import org.openautonomousconnection.luascript.fx.FxDomHost;
import org.openautonomousconnection.luascript.fx.FxThreadBridge;
import org.openautonomousconnection.luascript.hosts.UiHost;
import org.w3c.dom.Element;
import java.util.Objects;
/**
* UiHost implementation for JavaFX WebView (no JavaScript).
*
* <p>Operations are implemented via W3C DOM attributes/text, and best-effort behavior for
* value/style/class using standard attributes.</p>
*/
public final class UiHostImpl implements UiHost {
private final WebEngine engine;
private final WebView view;
private final FxDomHost dom;
/**
* Creates a new UI host.
*
* @param engine web engine
* @param view web view
* @param dom dom host
*/
public UiHostImpl(WebEngine engine, WebView view, FxDomHost dom) {
this.engine = Objects.requireNonNull(engine, "engine");
this.view = view;
this.dom = Objects.requireNonNull(dom, "dom");
}
private static boolean hasClassToken(String classAttr, String cls) {
String[] parts = classAttr.trim().split("\\s+");
for (String p : parts) {
if (p.equals(cls)) return true;
}
return false;
}
private static String removeCssProp(String style, String propLower) {
if (style == null || style.isBlank()) return "";
StringBuilder sb = new StringBuilder();
for (String part : style.split(";")) {
String p = part.trim();
if (p.isEmpty()) continue;
int idx = p.indexOf(':');
if (idx <= 0) continue;
String k = p.substring(0, idx).trim().toLowerCase();
if (k.equals(propLower)) continue;
if (!sb.isEmpty()) sb.append(';');
sb.append(p);
}
String out = sb.toString().trim();
if (!out.isEmpty() && !out.endsWith(";")) out += ";";
return out;
}
@Override
public void alert(String message) {
// No JS: use simple JavaFX dialog-less fallback (log-style). You can replace with real Dialogs later.
// Keeping it deterministic and non-blocking for now.
System.out.println("[ui.alert] " + (message == null ? "" : message));
}
@Override
public boolean confirm(String message) {
// No JS: deterministic default (false). Replace with JavaFX dialogs if you want UI interaction.
System.out.println("[ui.confirm] " + (message == null ? "" : message));
return false;
}
@Override
public String prompt(String message, String defaultValue) {
// No JS: deterministic default.
System.out.println("[ui.prompt] " + (message == null ? "" : message));
return defaultValue;
}
@Override
public void setText(String elementId, String text) {
dom.setTextContent(elementId, text);
}
@Override
public String getText(String elementId) {
return dom.getTextContent(elementId);
}
@Override
public void setHtml(String elementId, String html) {
// Without JS, safest is to set textContent (prevents HTML parsing).
// If you need real HTML injection, we must extend DomHost with fragment parsing (not in current API).
dom.setTextContent(elementId, html);
}
@Override
public String getHtml(String elementId) {
// Without JS, best-effort: return textContent.
return dom.getTextContent(elementId);
}
@Override
public void setValue(String elementId, String value) {
// Input/textarea value is commonly reflected as attribute "value".
dom.setAttribute(elementId, "value", value == null ? "" : value);
}
@Override
public String getValue(String elementId) {
String v = dom.getAttribute(elementId, "value");
return v == null ? "" : v;
}
@Override
public void setEnabled(String elementId, boolean enabled) {
if (enabled) dom.removeAttribute(elementId, "disabled");
else dom.setAttribute(elementId, "disabled", "disabled");
}
@Override
public void setVisible(String elementId, boolean visible) {
// Best-effort via style attribute
String style = dom.getAttribute(elementId, "style");
style = style == null ? "" : style;
style = removeCssProp(style, "display");
if (!visible) {
style = style.trim();
if (!style.isEmpty() && !style.endsWith(";")) style += ";";
style += "display:none;";
}
dom.setAttribute(elementId, "style", style);
}
@Override
public void addClass(String elementId, String className) {
String cls = Objects.requireNonNull(className, "className").trim();
if (cls.isEmpty()) return;
FxThreadBridge.runAndWait(() -> {
Element el = dom.byId(elementId);
String c = el.getAttribute("class");
c = (c == null) ? "" : c.trim();
if (c.isEmpty()) {
el.setAttribute("class", cls);
return;
}
if (!hasClassToken(c, cls)) {
el.setAttribute("class", c + " " + cls);
}
});
}
@Override
public void removeClass(String elementId, String className) {
String cls = Objects.requireNonNull(className, "className").trim();
if (cls.isEmpty()) return;
FxThreadBridge.runAndWait(() -> {
Element el = dom.byId(elementId);
String c = el.getAttribute("class");
c = (c == null) ? "" : c.trim();
if (c.isEmpty()) return;
String[] parts = c.split("\\s+");
StringBuilder sb = new StringBuilder();
for (String p : parts) {
if (p.equals(cls)) continue;
if (!sb.isEmpty()) sb.append(' ');
sb.append(p);
}
el.setAttribute("class", sb.toString());
});
}
@Override
public boolean toggleClass(String elementId, String className) {
if (hasClass(elementId, className)) {
removeClass(elementId, className);
return false;
}
addClass(elementId, className);
return true;
}
@Override
public boolean hasClass(String elementId, String className) {
String cls = Objects.requireNonNull(className, "className").trim();
if (cls.isEmpty()) return false;
return FxThreadBridge.callAndWait(() -> {
Element el = dom.byId(elementId);
String c = el.getAttribute("class");
c = (c == null) ? "" : c.trim();
return !c.isEmpty() && hasClassToken(c, cls);
});
}
@Override
public void setStyle(String elementId, String property, String value) {
String prop = Objects.requireNonNull(property, "property").trim().toLowerCase();
if (prop.isEmpty()) return;
String style = dom.getAttribute(elementId, "style");
style = style == null ? "" : style;
style = removeCssProp(style, prop);
String v = value == null ? "" : value.trim();
if (!v.isEmpty()) {
style = style.trim();
if (!style.isEmpty() && !style.endsWith(";")) style += ";";
style += prop + ":" + v + ";";
}
dom.setAttribute(elementId, "style", style);
}
@Override
public String getStyle(String elementId, String property) {
// Best-effort parsing from style attribute.
String prop = Objects.requireNonNull(property, "property").trim().toLowerCase();
if (prop.isEmpty()) return "";
String style = dom.getAttribute(elementId, "style");
style = style == null ? "" : style;
for (String part : style.split(";")) {
String p = part.trim();
if (p.isEmpty()) continue;
int idx = p.indexOf(':');
if (idx <= 0) continue;
String k = p.substring(0, idx).trim().toLowerCase();
if (k.equals(prop)) return p.substring(idx + 1).trim();
}
return "";
}
@Override
public void setAttribute(String elementId, String name, String value) {
dom.setAttribute(elementId, name, value);
}
@Override
public String getAttribute(String elementId, String name) {
return dom.getAttribute(elementId, name);
}
@Override
public void removeAttribute(String elementId, String name) {
dom.removeAttribute(elementId, name);
}
@Override
public void focus(String elementId) {
engine.setJavaScriptEnabled(true);
engine.executeScript(
"document.getElementById('" + elementId + "').focus();"
);
engine.setJavaScriptEnabled(false);
}
@Override
public void blur(String elementId) {
engine.setJavaScriptEnabled(true);
engine.executeScript(
"document.getElementById('" + elementId + "').blur();"
);
engine.setJavaScriptEnabled(false);
}
@Override
public void scrollIntoView(String elementId) {
engine.setJavaScriptEnabled(true);
engine.executeScript(
"document.getElementById('" + elementId + "').scrollIntoView();"
);
engine.setJavaScriptEnabled(false);
}
@Override
public int viewportWidth() {
return FxThreadBridge.callAndWait(() ->
(int) Math.round(view.getWidth())
);
}
@Override
public int viewportHeight() {
return FxThreadBridge.callAndWait(() ->
(int) Math.round(view.getHeight())
);
}
@Override
public long nowMillis() {
return System.currentTimeMillis();
}
}

View File

@@ -1,192 +0,0 @@
package org.openautonomousconnection.webclient.settings;
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* In-memory settings model.
*
* <p>Persisted by {@link SettingsManager}.</p>
*/
public final class AppSettings {
private final List<InsEndpoint> insEndpoints = new ArrayList<>();
private final List<String> favorites = new ArrayList<>();
private String startPageUrl = "web://info.oac/";
private boolean sslEnabled = true;
private boolean luaEnabled = true;
private boolean historyEnabled = true;
private InsEndpoint selectedIns;
private LuaExecutionPolicy luaPolicy = LuaExecutionPolicy.uiDefault();
/**
* Creates settings with defaults.
*/
public AppSettings() {
// Defaults: include the current INSList defaults as initial endpoint.
insEndpoints.add(new InsEndpoint("open-autonomous-connection.org", 1026));
selectedIns = insEndpoints.get(0);
}
/**
* Returns the configured start page URL.
*
* @return start page URL
*/
public String getStartPageUrl() {
return startPageUrl;
}
/**
* Sets the start page URL.
*
* @param startPageUrl URL (non-null, non-blank)
*/
public void setStartPageUrl(String startPageUrl) {
String s = Objects.requireNonNull(startPageUrl, "startPageUrl").trim();
if (s.isEmpty()) throw new IllegalArgumentException("startPageUrl must not be blank");
this.startPageUrl = s;
}
/**
* Returns whether SSL is enabled for protocol connections.
*
* @return true if enabled
*/
public boolean isSslEnabled() {
return sslEnabled;
}
/**
* Enables/disables SSL.
*
* @param sslEnabled enabled
*/
public void setSslEnabled(boolean sslEnabled) {
this.sslEnabled = sslEnabled;
}
/**
* Returns whether Lua runtime is enabled in WebView.
*
* @return true if enabled
*/
public boolean isLuaEnabled() {
return luaEnabled;
}
/**
* Enables/disables Lua.
*
* @param luaEnabled enabled
*/
public void setLuaEnabled(boolean luaEnabled) {
this.luaEnabled = luaEnabled;
}
/**
* Returns whether history is enabled.
*
* @return true if enabled
*/
public boolean isHistoryEnabled() {
return historyEnabled;
}
/**
* Enables/disables history tracking.
*
* @param historyEnabled enabled
*/
public void setHistoryEnabled(boolean historyEnabled) {
this.historyEnabled = historyEnabled;
}
/**
* Returns a mutable INS endpoint list.
*
* @return list (mutable)
*/
public List<InsEndpoint> getInsEndpointsMutable() {
return insEndpoints;
}
/**
* Returns an immutable view of INS endpoints.
*
* @return endpoints
*/
public List<InsEndpoint> getInsEndpoints() {
return Collections.unmodifiableList(insEndpoints);
}
/**
* Returns currently selected INS.
*
* @return selected endpoint
*/
public InsEndpoint getSelectedIns() {
return selectedIns;
}
/**
* Sets selected INS endpoint.
*
* @param selectedIns endpoint (must exist in list or will be added)
*/
public void setSelectedIns(InsEndpoint selectedIns) {
Objects.requireNonNull(selectedIns, "selectedIns");
if (!insEndpoints.contains(selectedIns)) {
insEndpoints.add(selectedIns);
}
this.selectedIns = selectedIns;
}
/**
* Returns a mutable favorites list.
*
* @return favorites (mutable)
*/
public List<String> getFavoritesMutable() {
return favorites;
}
/**
* Returns immutable favorites.
*
* @return favorites
*/
public List<String> getFavorites() {
return Collections.unmodifiableList(favorites);
}
/**
* Returns Lua execution policy.
*
* @return policy
*/
public LuaExecutionPolicy getLuaPolicy() {
return luaPolicy;
}
/**
* Sets Lua execution policy.
*
* @param luaPolicy policy (non-null)
*/
public void setLuaPolicy(LuaExecutionPolicy luaPolicy) {
this.luaPolicy = Objects.requireNonNull(luaPolicy, "luaPolicy");
}
/**
* Resets the Lua policy back to ui default.
*/
public void resetLuaPolicyToUiDefault() {
this.luaPolicy = new LuaExecutionPolicy(Duration.ofMillis(50L), 200_000L, 5_000);
}
}

View File

@@ -1,138 +0,0 @@
package org.openautonomousconnection.webclient.settings;
import javafx.concurrent.Worker;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import org.luaj.vm2.Globals;
import org.openautonomousconnection.luascript.fx.FxDomHost;
import org.openautonomousconnection.luascript.fx.FxEventHost;
import org.openautonomousconnection.luascript.fx.FxWebViewResourceHost;
import org.openautonomousconnection.luascript.hosts.HostServices;
import org.openautonomousconnection.luascript.runtime.LuaRuntime;
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
import org.openautonomousconnection.luascript.utils.LuaGlobalsFactory;
import org.openautonomousconnection.webclient.lua.WebLogger;
import org.openautonomousconnection.webclient.lua.hosts.ConsoleHostImpl;
import org.openautonomousconnection.webclient.lua.hosts.UiHostImpl;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* JavaFX WebView integration entry point for LuaScript (no JavaScript).
*
* <p>Hard rule: every HTML script tag is treated as Lua.</p>
*/
public final class FxEngine implements AutoCloseable {
private final WebEngine engine;
private final WebView webView;
private final LuaExecutionPolicy policy;
private final WebLogger logger;
private final AtomicBoolean bootstrapped = new AtomicBoolean(false);
private LuaRuntime runtime;
/**
* Creates an integration engine with default UI execution policy.
*
* @param engine web engine
* @param webView web view
* @param logger web logger
*/
public FxEngine(WebEngine engine, WebView webView, WebLogger logger) {
this(engine, webView, LuaExecutionPolicy.uiDefault(), logger);
}
/**
* Creates an integration engine with a custom execution policy.
*
* @param engine web engine
* @param webView web view
* @param policy execution policy
* @param logger web logger
*/
public FxEngine(WebEngine engine, WebView webView, LuaExecutionPolicy policy, WebLogger logger) {
this.engine = Objects.requireNonNull(engine, "engine");
this.webView = Objects.requireNonNull(webView, "webView");
this.policy = Objects.requireNonNull(policy, "policy");
this.logger = Objects.requireNonNull(logger, "logger");
}
/**
* Installs a load hook that bootstraps Lua when a page finished loading.
*/
public void install() {
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
bootstrapped.set(false);
bootstrap();
} else if (newState == Worker.State.CANCELLED || newState == Worker.State.FAILED) {
bootstrapped.set(false);
closeRuntimeQuietly();
}
});
}
/**
* Bootstraps Lua for the currently loaded document.
*/
public void bootstrap() {
if (!bootstrapped.compareAndSet(false, true)) return;
closeRuntimeQuietly();
FxDomHost dom = new FxDomHost(engine);
dom.ensureAllElementsHaveId();
Globals globals = LuaGlobalsFactory.create(
new LuaGlobalsFactory.Options()
.enableDebug(false)
.sandbox(true)
);
ConsoleHostImpl console = new ConsoleHostImpl(logger);
UiHostImpl uiHost = new UiHostImpl(engine, webView, dom);
FxWebViewResourceHost resourceHost = new FxWebViewResourceHost(engine);
LuaRuntime rt = new LuaRuntime(globals, new HostServices.Default(uiHost, dom, null, resourceHost, console), policy);
FxEventHost eventHost = new FxEventHost(dom, rt.eventRouter());
HostServices services = new HostServices.Default(uiHost, dom, eventHost, resourceHost, console);
rt.close();
rt = new LuaRuntime(globals, services, policy);
rt.installStdTables(true);
rt.bootstrapFromDom();
this.runtime = rt;
}
/**
* Returns active runtime or null if not bootstrapped.
*
* @return runtime or null
*/
public LuaRuntime runtimeOrNull() {
return runtime;
}
@Override
public void close() {
closeRuntimeQuietly();
}
private void closeRuntimeQuietly() {
LuaRuntime rt = this.runtime;
this.runtime = null;
if (rt != null) {
try {
rt.close();
} catch (Exception ignored) {
// Best-effort shutdown.
}
}
}
}

View File

@@ -1,82 +0,0 @@
package org.openautonomousconnection.webclient.settings;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* In-memory history tracker (URL + timestamp).
*
* <p>Persistence can be added later; for now it follows settings toggle and supports clearing.</p>
*/
public final class HistoryManager {
private final List<Entry> entries = new ArrayList<>();
private volatile boolean enabled = true;
/**
* Returns whether history is enabled.
*
* @return enabled
*/
public boolean isEnabled() {
return enabled;
}
/**
* Sets whether history is enabled.
*
* @param enabled enabled
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
if (!enabled) {
clear();
}
}
/**
* Adds a URL to history if enabled.
*
* @param url url (non-null, non-blank)
*/
public void add(String url) {
if (!enabled) return;
String s = Objects.requireNonNull(url, "url").trim();
if (s.isEmpty()) return;
// De-dup consecutive duplicates
if (!entries.isEmpty()) {
Entry last = entries.get(entries.size() - 1);
if (last.url().equals(s)) return;
}
entries.add(new Entry(s, Instant.now()));
}
/**
* Clears history.
*/
public void clear() {
entries.clear();
}
/**
* Returns immutable entries.
*
* @return entries
*/
public List<Entry> entries() {
return Collections.unmodifiableList(entries);
}
/**
* Single history entry.
*
* @param url visited URL
* @param visitedAt timestamp
*/
public record Entry(String url, Instant visitedAt) {
}
}

View File

@@ -1,125 +0,0 @@
package org.openautonomousconnection.webclient.settings;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* Persists browsing history to disk.
*
* <p>Format per line: {@code <epochMillis>\t<url>}</p>
*/
public final class HistoryStore {
private static final String FILE_NAME = "history.log";
private final File file;
/**
* Creates a history store in the default user settings directory.
*/
public HistoryStore() {
this(historyFile());
}
/**
* Creates a history store for a specific file.
*
* @param file history file
*/
public HistoryStore(File file) {
this.file = Objects.requireNonNull(file, "file");
File dir = file.getParentFile();
if (dir != null && !dir.isDirectory()) {
//noinspection ResultOfMethodCallIgnored
dir.mkdirs();
}
}
/**
* Returns the default history file location.
*
* @return file
*/
public static File historyFile() {
return new File(FILE_NAME);
}
/**
* Loads history entries from disk.
*
* @return entries (immutable)
*/
public List<Entry> load() {
if (!file.isFile()) return List.of();
List<Entry> out = new ArrayList<>();
try (BufferedReader br = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
String line;
while ((line = br.readLine()) != null) {
int tab = line.indexOf('\t');
if (tab <= 0) continue;
String tsS = line.substring(0, tab).trim();
String url = line.substring(tab + 1).trim();
if (url.isEmpty()) continue;
long ms;
try {
ms = Long.parseLong(tsS);
} catch (Exception ignored) {
continue;
}
out.add(new Entry(url, Instant.ofEpochMilli(ms)));
}
} catch (Exception ignored) {
return List.of();
}
return Collections.unmodifiableList(out);
}
/**
* Appends a single entry to disk (best-effort).
*
* @param url visited URL
* @param at timestamp
*/
public void append(String url, Instant at) {
Objects.requireNonNull(url, "url");
Objects.requireNonNull(at, "at");
String u = url.trim();
if (u.isEmpty()) return;
try (Writer w = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file, true), StandardCharsets.UTF_8))) {
w.write(Long.toString(at.toEpochMilli()));
w.write('\t');
w.write(u.replace('\n', ' ').replace('\r', ' '));
w.write('\n');
} catch (Exception ignored) {
// Best-effort
}
}
/**
* Clears history file (best-effort).
*/
public void clear() {
try {
Files.deleteIfExists(file.toPath());
} catch (Exception ignored) {
// Best-effort
}
}
/**
* History entry.
*
* @param url visited URL
* @param visitedAt timestamp
*/
public record Entry(String url, Instant visitedAt) {
}
}

View File

@@ -1,59 +0,0 @@
package org.openautonomousconnection.webclient.settings;
import java.util.Objects;
/**
* Represents an INS endpoint (host + TCP port).
*/
public record InsEndpoint(String host, int port) {
/**
* Creates an INS endpoint.
*
* @param host endpoint host (non-null, non-blank)
* @param port tcp port (1..65535)
*/
public InsEndpoint(String host, int port) {
String h = Objects.requireNonNull(host, "host").trim();
if (h.isEmpty()) throw new IllegalArgumentException("host must not be blank");
if (port < 1 || port > 65535) throw new IllegalArgumentException("port out of range: " + port);
this.host = h;
this.port = port;
}
/**
* Returns the host.
*
* @return host
*/
@Override
public String host() {
return host;
}
/**
* Returns the tcp port.
*
* @return port
*/
@Override
public int port() {
return port;
}
@Override
public String toString() {
return host + ":" + port;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof InsEndpoint other)) return false;
return host.equalsIgnoreCase(other.host) && port == other.port;
}
@Override
public int hashCode() {
return host.toLowerCase().hashCode() * 31 + port;
}
}

View File

@@ -1,190 +0,0 @@
package org.openautonomousconnection.webclient.settings;
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
/**
* Loads/saves {@link AppSettings} to a simple properties file.
*
* <p>Location: {@code settings.properties}</p>
*/
public final class SettingsManager {
private static final String FILE_NAME = "settings.properties";
private SettingsManager() {
// Utility class
}
/**
* Returns the settings file location.
*
* @return settings file
*/
public static File settingsFile() {
return new File(FILE_NAME);
}
/**
* Loads settings from disk. If missing, returns defaults.
*
* @return settings
*/
public static AppSettings load() {
AppSettings s = new AppSettings();
File f = settingsFile();
if (!f.isFile()) return s;
Properties p = new Properties();
try (InputStream in = Files.newInputStream(f.toPath())) {
p.load(new InputStreamReader(in, StandardCharsets.UTF_8));
} catch (Exception ignored) {
return s;
}
trySetString(p, "startPageUrl", s::setStartPageUrl);
s.setSslEnabled(parseBool(p.getProperty("sslEnabled"), s.isSslEnabled()));
s.setLuaEnabled(parseBool(p.getProperty("luaEnabled"), s.isLuaEnabled()));
s.setHistoryEnabled(parseBool(p.getProperty("historyEnabled"), s.isHistoryEnabled()));
// INS endpoints
List<InsEndpoint> endpoints = new ArrayList<>();
int count = parseInt(p.getProperty("ins.count"), 0);
for (int i = 0; i < count; i++) {
String host = p.getProperty("ins." + i + ".host");
int port = parseInt(p.getProperty("ins." + i + ".port"), -1);
if (host == null || host.isBlank() || port < 1 || port > 65535) continue;
endpoints.add(new InsEndpoint(host.trim(), port));
}
if (!endpoints.isEmpty()) {
s.getInsEndpointsMutable().clear();
s.getInsEndpointsMutable().addAll(endpoints);
}
String selHost = p.getProperty("ins.selected.host");
int selPort = parseInt(p.getProperty("ins.selected.port"), -1);
if (selHost != null && !selHost.isBlank() && selPort >= 1 && selPort <= 65535) {
s.setSelectedIns(new InsEndpoint(selHost.trim(), selPort));
} else {
// Keep default selection, but ensure it exists in list.
s.setSelectedIns(s.getSelectedIns());
}
// Favorites
int favCount = parseInt(p.getProperty("favorites.count"), 0);
s.getFavoritesMutable().clear();
for (int i = 0; i < favCount; i++) {
String url = p.getProperty("favorites." + i);
if (url != null && !url.isBlank()) s.getFavoritesMutable().add(url.trim());
}
// Lua policy
long timeoutMs = parseLong(p.getProperty("lua.timeoutMs"), 50L);
long instr = parseLong(p.getProperty("lua.instructionLimit"), 200_000L);
int hook = parseInt(p.getProperty("lua.hookStep"), 5_000);
try {
s.setLuaPolicy(new LuaExecutionPolicy(Duration.ofMillis(timeoutMs), instr, hook));
} catch (Exception ignored) {
s.resetLuaPolicyToUiDefault();
}
return s;
}
/**
* Saves settings to disk (best-effort).
*
* @param s settings
*/
public static void save(AppSettings s) {
Objects.requireNonNull(s, "s");
File f = settingsFile();
File dir = f.getParentFile();
if (dir != null && !dir.isDirectory()) {
//noinspection ResultOfMethodCallIgnored
dir.mkdirs();
}
Properties p = new Properties();
p.setProperty("startPageUrl", s.getStartPageUrl());
p.setProperty("sslEnabled", Boolean.toString(s.isSslEnabled()));
p.setProperty("luaEnabled", Boolean.toString(s.isLuaEnabled()));
p.setProperty("historyEnabled", Boolean.toString(s.isHistoryEnabled()));
List<InsEndpoint> endpoints = s.getInsEndpoints();
p.setProperty("ins.count", Integer.toString(endpoints.size()));
for (int i = 0; i < endpoints.size(); i++) {
InsEndpoint ep = endpoints.get(i);
p.setProperty("ins." + i + ".host", ep.host());
p.setProperty("ins." + i + ".port", Integer.toString(ep.port()));
}
InsEndpoint sel = s.getSelectedIns();
if (sel != null) {
p.setProperty("ins.selected.host", sel.host());
p.setProperty("ins.selected.port", Integer.toString(sel.port()));
}
List<String> fav = s.getFavorites();
p.setProperty("favorites.count", Integer.toString(fav.size()));
for (int i = 0; i < fav.size(); i++) {
p.setProperty("favorites." + i, fav.get(i));
}
p.setProperty("lua.timeoutMs", Long.toString(s.getLuaPolicy().timeout().toMillis()));
p.setProperty("lua.instructionLimit", Long.toString(s.getLuaPolicy().instructionLimit()));
p.setProperty("lua.hookStep", Integer.toString(s.getLuaPolicy().hookStep()));
try (OutputStream out = Files.newOutputStream(f.toPath())) {
p.store(new OutputStreamWriter(out, StandardCharsets.UTF_8), "OAC WebClient Settings");
} catch (Exception ignored) {
// Best-effort persistence
}
}
private static void trySetString(Properties p, String key, java.util.function.Consumer<String> setter) {
String v = p.getProperty(key);
if (v != null && !v.isBlank()) {
try {
setter.accept(v.trim());
} catch (Exception ignored) {
// Ignore malformed value
}
}
}
private static boolean parseBool(String v, boolean def) {
if (v == null) return def;
String s = v.trim().toLowerCase();
if (s.equals("true")) return true;
if (s.equals("false")) return false;
return def;
}
private static int parseInt(String v, int def) {
if (v == null) return def;
try {
return Integer.parseInt(v.trim());
} catch (Exception e) {
return def;
}
}
private static long parseLong(String v, long def) {
if (v == null) return def;
try {
return Long.parseLong(v.trim());
} catch (Exception e) {
return def;
}
}
}

View File

@@ -1,136 +0,0 @@
package org.openautonomousconnection.webclient.ui;
import org.openautonomousconnection.oacswing.component.OACButton;
import org.openautonomousconnection.oacswing.component.OACPanel;
import org.openautonomousconnection.oacswing.component.OACTextField;
import javax.swing.border.EmptyBorder;
import java.awt.*;
public final class BrowserDesign extends OACPanel {
private static final int HEIGHT_NAV = 44;
private static final int RADIUS = 16;
private final OACButton backButton = iconButton("«");
private final OACButton forwardButton = iconButton("»");
private final OACButton reloadButton = iconButton("");
private final OACTextField addressField = new OACTextField();
private final OACButton goButton = pillButton("\uD83D\uDD0D");
private final OACButton starButton = iconButton("");
private final OACButton menuButton = iconButton("");
private final FavChipBar favoritesBar = new FavChipBar();
public BrowserDesign() {
super(new BorderLayout(0, 0));
setOpaque(false);
GlassPanel card = new GlassPanel(new BorderLayout(0, 0), RADIUS,
Color.decode("#131e34"),
Color.decode("#0f172a"),
Color.decode("#2a3756"));
card.setBorder(new EmptyBorder(8, 10, 8, 10));
card.add(buildNavRow(), BorderLayout.NORTH);
card.add(favoritesBar, BorderLayout.CENTER);
add(card, BorderLayout.CENTER);
setBorder(new EmptyBorder(8, 10, 8, 10));
addressField.setBorder(new EmptyBorder(6, 10, 6, 10));
addressField.setPreferredSize(new Dimension(1, 32));
backButton.setToolTipText("Back");
forwardButton.setToolTipText("Forward");
reloadButton.setToolTipText("Reload");
goButton.setToolTipText("Open URL");
starButton.setToolTipText("Add to favorites");
menuButton.setToolTipText("Menu");
}
private static OACButton iconButton(String text) {
OACButton b = new OACButton(text);
b.setMargin(new Insets(3, 10, 3, 10));
b.setFocusable(false);
b.setPreferredSize(new Dimension(44, 32));
return b;
}
private static OACButton pillButton(String text) {
OACButton b = new OACButton(text);
b.setMargin(new Insets(3, 14, 3, 14));
b.setFocusable(false);
b.setPreferredSize(new Dimension(64, 32));
return b;
}
private Component buildNavRow() {
OACPanel row = new OACPanel(new BorderLayout(10, 0));
row.setOpaque(false);
row.setBorder(new EmptyBorder(2, 2, 8, 2));
row.setPreferredSize(new Dimension(1, HEIGHT_NAV));
OACPanel left = new OACPanel(new FlowLayout(FlowLayout.LEFT, 6, 0));
left.setOpaque(false);
left.add(backButton);
left.add(forwardButton);
left.add(reloadButton);
GlassPanel addressPill = new GlassPanel(new BorderLayout(8, 0), 14,
Color.decode("#0f172a"), Color.decode("#0b1220"), Color.decode("#2a3756"));
addressPill.setBorder(new EmptyBorder(2, 8, 2, 8));
addressPill.add(addressField, BorderLayout.CENTER);
OACPanel right = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
right.setOpaque(false);
right.add(goButton);
right.add(starButton);
right.add(menuButton);
row.add(left, BorderLayout.WEST);
row.add(addressPill, BorderLayout.CENTER);
row.add(right, BorderLayout.EAST);
return row;
}
/**
* Returns the favorites bar.
*
* @return favorites bar
*/
public FavChipBar favoritesBar() {
return favoritesBar;
}
public OACButton backButton() {
return backButton;
}
public OACButton forwardButton() {
return forwardButton;
}
public OACButton reloadButton() {
return reloadButton;
}
public OACTextField addressField() {
return addressField;
}
public OACButton goButton() {
return goButton;
}
public OACButton starButton() {
return starButton;
}
public OACButton menuButton() {
return menuButton;
}
}

View File

@@ -1,380 +0,0 @@
package org.openautonomousconnection.webclient.ui;
import javafx.application.Platform;
import javafx.concurrent.Worker;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebHistory;
import javafx.scene.web.WebView;
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
import org.openautonomousconnection.oacswing.component.OACPanel;
import org.openautonomousconnection.webclient.ClientImpl;
import org.openautonomousconnection.webclient.lua.WebLogger;
import org.openautonomousconnection.webclient.settings.FxEngine;
import org.w3c.dom.Document;
import javax.swing.*;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.awt.*;
import java.io.StringWriter;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
/**
* Logical browser tab: stable UI key + embedded JavaFX WebView.
*
* <p>This class merges the previous {@code BrowserTab} + {@code TabView} into one component.</p>
*/
public final class BrowserTab extends OACPanel {
private final String key;
private final AtomicBoolean initialized = new AtomicBoolean(false);
private final Consumer<String> onLocationChanged;
private final WebLogger webLogger;
private final ClientImpl protocolClient;
private final boolean luaEnabled;
private final LuaExecutionPolicy luaPolicy;
private volatile Runnable openInNewTab;
private JFXPanel fxPanel;
private WebView webView;
private WebEngine engine;
private volatile FxEngine luaEngine;
/**
* Creates a browser tab.
*
* @param key stable UI key (must match CardLayout key and titlebar tab title)
* @param initialUrl initial URL (used for logger context)
* @param onLocationChange callback invoked on URL changes
* @param luaEnabled whether Lua is enabled for this tab
* @param luaPolicy execution policy for Lua
*/
public BrowserTab(String key, String initialUrl, Consumer<String> onLocationChange, boolean luaEnabled, LuaExecutionPolicy luaPolicy, ClientImpl protocolClient) {
super();
this.key = Objects.requireNonNull(key, "key");
this.onLocationChanged = Objects.requireNonNull(onLocationChange, "onLocationChange");
this.protocolClient = Objects.requireNonNull(protocolClient, "protocolClient");
this.webLogger = new WebLogger(Objects.requireNonNull(initialUrl, "initialUrl"), protocolClient);
this.luaEnabled = luaEnabled;
this.luaPolicy = Objects.requireNonNull(luaPolicy, "luaPolicy");
setLayout(new BorderLayout());
}
private static String serializeDom(Document doc) throws Exception {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.setOutputProperty(OutputKeys.METHOD, "html");
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
t.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter sw = new StringWriter(64 * 1024);
t.transform(new DOMSource(doc), new StreamResult(sw));
return sw.toString();
}
/**
* Returns the stable tab key.
*
* @return key
*/
public String getKey() {
return key;
}
public ClientImpl getProtocolClient() {
return protocolClient;
}
/**
* Sets callback for opening current page in a new tab.
*
* @param callback callback
*/
public void setOpenInNewTabCallback(Runnable callback) {
this.openInNewTab = callback;
}
@Override
public void addNotify() {
super.addNotify();
if (!initialized.compareAndSet(false, true)) {
return;
}
fxPanel = new JFXPanel();
add(fxPanel, BorderLayout.CENTER);
Platform.runLater(() -> {
webView = new WebView();
webView.setContextMenuEnabled(false);
engine = webView.getEngine();
engine.setJavaScriptEnabled(false);
engine.locationProperty().addListener((obs, oldV, newV) -> fireLocationChanged(newV));
engine.getLoadWorker().stateProperty().addListener((obs, oldS, newS) -> {
if (newS == Worker.State.RUNNING || newS == Worker.State.SUCCEEDED) {
fireLocationChanged(engine.getLocation());
}
});
installCustomContextMenu();
if (luaEnabled) {
luaEngine = new FxEngine(engine, webView, luaPolicy, webLogger);
luaEngine.install();
} else {
luaEngine = null;
}
fxPanel.setScene(new Scene(webView));
});
}
/**
* Loads a URL.
*
* @param url URL
*/
public void loadUrl(String url) {
String target = Objects.requireNonNull(url, "url").trim();
if (target.isEmpty()) return;
Platform.runLater(() -> {
WebEngine e = engine;
if (e != null) e.load(target);
});
}
/**
* Reloads the current page.
*/
public void reload() {
String current = getEngineLocation();
if (current == null || current.isBlank()) return;
Platform.runLater(() -> {
WebEngine e = engine;
if (e != null) e.reload();
});
}
/**
* Goes back in history if possible.
*/
public void goBack() {
if (peekHistoryTarget(-1) == null) return;
Platform.runLater(() -> {
WebEngine e = engine;
if (e == null) return;
WebHistory h = e.getHistory();
if (h.getCurrentIndex() > 0) h.go(-1);
});
}
/**
* Goes forward in history if possible.
*/
public void goForward() {
if (peekHistoryTarget(+1) == null) return;
Platform.runLater(() -> {
WebEngine e = engine;
if (e == null) return;
WebHistory h = e.getHistory();
if (h.getCurrentIndex() < h.getEntries().size() - 1) h.go(1);
});
}
/**
* Returns current location if known.
*
* @return URL or null
*/
public String getLocationUrl() {
return getEngineLocation();
}
/**
* Returns current engine location.
*
* @return url or null
*/
public String getEngineLocation() {
WebEngine e = engine;
return e != null ? e.getLocation() : null;
}
/**
* Applies HTML live to the current tab (no saving).
*
* @param html html text
*/
public void applyHtml(String html) {
String content = html == null ? "" : html;
Platform.runLater(() -> {
WebEngine e = engine;
if (e != null) e.loadContent(content, "text/html");
});
}
/**
* Returns the current rendered HTML (DOM serialization).
*
* @return current html (never null)
*/
public String getCurrentHtml() {
CompletableFuture<String> fut = new CompletableFuture<>();
Platform.runLater(() -> {
try {
WebEngine e = engine;
if (e == null) {
fut.complete("");
return;
}
Document doc = e.getDocument();
if (doc == null) {
fut.complete("");
return;
}
fut.complete(serializeDom(doc));
} catch (Throwable t) {
fut.completeExceptionally(t);
}
});
try {
return fut.join();
} catch (Throwable t) {
return "";
}
}
/**
* Releases resources.
*/
public void dispose() {
FxEngine le = luaEngine;
luaEngine = null;
if (le != null) {
try {
le.close();
} catch (Exception ignored) {
// Intentionally ignored
}
}
Platform.runLater(() -> {
WebEngine e = engine;
if (e != null) {
try {
e.load(null);
} catch (Exception ignored) {
// Intentionally ignored
}
}
engine = null;
webView = null;
if (fxPanel != null) {
fxPanel.setScene(null);
}
});
disconnectProtocolQuietly();
}
private void fireLocationChanged(String location) {
if (location == null) return;
String s = location.trim();
if (s.isEmpty()) return;
try {
onLocationChanged.accept(s);
} catch (Exception ignored) {
// Must not break FX thread
}
}
private void installCustomContextMenu() {
final ContextMenu menu = new ContextMenu();
MenuItem back = new MenuItem("Back");
back.setOnAction(e -> SwingUtilities.invokeLater(this::goBack));
MenuItem forward = new MenuItem("Forward");
forward.setOnAction(e -> SwingUtilities.invokeLater(this::goForward));
MenuItem reload = new MenuItem("Reload");
reload.setOnAction(e -> SwingUtilities.invokeLater(this::reload));
MenuItem copyLink = new MenuItem("Copy Link");
copyLink.setOnAction(e -> {
WebEngine e2 = engine;
String loc = e2 != null ? e2.getLocation() : null;
if (loc == null) return;
ClipboardContent cc = new ClipboardContent();
cc.putString(loc);
Clipboard.getSystemClipboard().setContent(cc);
});
MenuItem openNewTab = new MenuItem("Open in New Tab");
openNewTab.setOnAction(e -> {
Runnable r = this.openInNewTab;
if (r != null) SwingUtilities.invokeLater(r);
});
menu.getItems().addAll(back, forward, reload, copyLink, openNewTab);
webView.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, ev -> {
menu.hide();
menu.show(webView, ev.getScreenX(), ev.getScreenY());
ev.consume();
});
}
private String peekHistoryTarget(int delta) {
WebEngine e = engine;
if (e == null) return null;
WebHistory h = e.getHistory();
int idx = h.getCurrentIndex() + delta;
if (idx < 0 || idx >= h.getEntries().size()) return null;
return h.getEntries().get(idx).getUrl();
}
private void disconnectProtocolQuietly() {
try {
if (protocolClient.getClientServerConnection() != null) {
protocolClient.getClientServerConnection().disconnect();
}
} catch (Exception ignored) {
// Best-effort shutdown.
}
try {
if (protocolClient.getClientINSConnection() != null) {
protocolClient.getClientINSConnection().disconnect();
}
} catch (Exception ignored) {
// Best-effort shutdown.
}
}
}

View File

@@ -1,473 +0,0 @@
package org.openautonomousconnection.webclient.ui;
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
import org.openautonomousconnection.oacswing.component.*;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.ProtocolValues;
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
import org.openautonomousconnection.webclient.ClientImpl;
import org.openautonomousconnection.webclient.settings.AppSettings;
import org.openautonomousconnection.webclient.settings.HistoryStore;
import org.openautonomousconnection.webclient.settings.InsEndpoint;
import org.openautonomousconnection.webclient.settings.SettingsManager;
import org.openautonomousconnection.webclient.ui.menus.AboutDialog;
import org.openautonomousconnection.webclient.ui.menus.AddonsDialog;
import org.openautonomousconnection.webclient.ui.menus.SettingsDialog;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
/**
* Multi-tab browser UI.
*/
public class BrowserUI extends OACFrame {
private static final int TITLE_BAR_HEIGHT = 42;
private final AppSettings settings;
private final HistoryStore historyStore = new HistoryStore();
private final CardLayout cardLayout;
private final OACPanel pageHost;
private final Map<String, BrowserTab> tabsByKey = new LinkedHashMap<>();
private final Map<String, ProtocolBridge> protocolByKey = new LinkedHashMap<>();
private final Map<String, AddonLoader> addonLoaderByKey = new LinkedHashMap<>();
private final BrowserDesign browser;
private final PlusTabSupport plusTabSupport;
private int tabCounter = 0;
private int lastSelectedRealTab = -1;
private boolean handlingTabSwitch = false;
private boolean suppressPlusAutoOpen = false;
public BrowserUI(AppSettings settings) {
super("OAC Browser");
setBackground(DesignManager.getGlobalDesign().getElements().get(OACFrame.class).background().getColor());
this.settings = Objects.requireNonNull(settings, "settings");
OACPanel content = (OACPanel) getContentPane();
content.setLayout(new BorderLayout());
content.setBorder(BorderFactory.createEmptyBorder(TITLE_BAR_HEIGHT, 0, 0, 0));
browser = new BrowserDesign();
content.add(browser, BorderLayout.NORTH);
cardLayout = new CardLayout();
pageHost = new OACPanel(cardLayout);
content.add(pageHost, BorderLayout.CENTER);
plusTabSupport = new PlusTabSupport(getTitleBar().getTabs(), () -> openNewTab(settings.getStartPageUrl()));
plusTabSupport.ensurePlusTab();
getTitleBar().getTabs().addChangeListener(e -> onHeaderTabChanged());
browser.addressField().addActionListener(e -> navigateCurrent(browser.addressField().getText()));
browser.goButton().addActionListener(e -> navigateCurrent(browser.addressField().getText()));
browser.backButton().addActionListener(e -> {
BrowserTab tab = getCurrentTab();
if (tab != null) tab.goBack();
});
browser.forwardButton().addActionListener(e -> {
BrowserTab tab = getCurrentTab();
if (tab != null) tab.goForward();
});
browser.reloadButton().addActionListener(e -> {
BrowserTab tab = getCurrentTab();
if (tab != null) tab.reload();
});
browser.starButton().addActionListener(e -> addCurrentToFavorites());
browser.menuButton().addActionListener(e -> showAppMenu(browser.menuButton()));
browser.favoritesBar().setOnNavigate(this::navigateCurrent);
browser.favoritesBar().setOnEdit(this::openSettings);
browser.favoritesBar().setFavorites(settings.getFavorites());
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
SettingsManager.save(settings);
}
});
}
private static String normalizeUrl(String input) {
String s = input == null ? "" : input.trim();
if (s.isEmpty()) return "web://info.oac/";
if (s.startsWith("web://")) {
String rest = s.substring("web://".length());
if (!rest.contains("/")) return s + "/";
return s;
}
return "web://" + s + (s.contains("/") ? "" : "/");
}
private static String displayTitleFromUrl(String location) {
if (location == null) return "New Tab";
String s = location.trim();
if (s.isEmpty()) return "New Tab";
if (s.startsWith("web://")) s = s.substring("web://".length());
if (s.startsWith("https://")) s = s.substring("https://".length());
if (s.startsWith("http://")) s = s.substring("http://".length());
int slash = s.indexOf('/');
if (slash > 0) s = s.substring(0, slash);
if (s.isEmpty()) return "New Tab";
if (s.length() <= 24) return s;
return s.substring(0, 24) + "...";
}
private void showAppMenu(Component anchor) {
OACPopupMenu menu = new OACPopupMenu();
OACMenuItem settingsItem = new OACMenuItem("Settings");
settingsItem.addActionListener(e -> openSettings());
OACMenuItem addonsItem = new OACMenuItem("Addons");
addonsItem.addActionListener(e -> new AddonsDialog(this, settings).setVisible(true));
OACMenuItem aboutItem = new OACMenuItem("About");
aboutItem.addActionListener(e -> new AboutDialog(this, settings).setVisible(true));
OACMenu history = new OACMenu("History");
OACCheckBoxMenuItem historyEnabled = new OACCheckBoxMenuItem("Enable History", settings.isHistoryEnabled());
historyEnabled.addActionListener(e -> {
settings.setHistoryEnabled(historyEnabled.isSelected());
SettingsManager.save(settings);
if (!settings.isHistoryEnabled()) historyStore.clear();
});
OACMenuItem show = new OACMenuItem("Show History");
show.addActionListener(e -> showHistoryDialog());
OACMenuItem clear = new OACMenuItem("Clear History");
clear.addActionListener(e -> {
historyStore.clear();
OACOptionPane.showMessageDialog(this, "History cleared.", "History", OACOptionPane.INFORMATION_MESSAGE);
});
history.add(historyEnabled);
history.addSeparator();
history.add(show);
history.add(clear);
menu.add(settingsItem);
menu.addSeparator();
menu.add(history);
menu.addSeparator();
menu.add(addonsItem);
menu.add(aboutItem);
menu.show(anchor, 0, anchor.getHeight());
}
private void showHistoryDialog() {
java.util.List<HistoryStore.Entry> entries = historyStore.load();
OACTextArea area = new OACTextArea();
area.setEditable(false);
area.setLineWrap(true);
area.setWrapStyleWord(true);
StringBuilder sb = new StringBuilder(32_768);
for (int i = entries.size() - 1; i >= 0; i--) {
HistoryStore.Entry e = entries.get(i);
sb.append(e.visitedAt()).append(" ").append(e.url()).append('\n');
}
area.setText(sb.toString());
OACScrollPane sp = new OACScrollPane(area);
sp.setPreferredSize(new Dimension(860, 520));
OACOptionPane.showOptionDialog(this, sp, "History", OACOptionPane.DEFAULT_OPTION, OACOptionPane.INFORMATION_MESSAGE,
null, null, null);
}
private void openSettings() {
SettingsDialog dlg = new SettingsDialog(this, settings, () -> {
SettingsManager.save(settings);
browser.favoritesBar().setFavorites(settings.getFavorites());
if (!settings.isHistoryEnabled()) historyStore.clear();
});
dlg.setVisible(true);
}
private void navigateCurrent(String input) {
BrowserTab tab = getCurrentTab();
if (tab == null) return;
String normalized = normalizeUrl(input);
tab.loadUrl(normalized);
browser.addressField().setText(normalized);
updateTabPresentation(tab.getKey(), normalized);
onVisited(normalized);
}
public void openNewTab(String url) {
String key = nextTabKey();
String normalized = normalizeUrl(url);
AtomicReference<BrowserTab> tabRef = new AtomicReference<>();
ClientImpl client = new ClientImpl(this, () -> SwingUtilities.invokeLater(() -> {
BrowserTab readyTab = tabRef.get();
if (readyTab == null) return;
readyTab.loadUrl(normalized);
}));
BrowserTab tab = new BrowserTab(
key,
normalized,
newLocation -> SwingUtilities.invokeLater(() -> {
String selectedKey = getSelectedTabKey();
if (Objects.equals(selectedKey, key)) {
browser.addressField().setText(newLocation);
}
updateTabPresentation(key, newLocation);
onVisited(newLocation);
}),
settings.isLuaEnabled(),
settings.getLuaPolicy(),
client
);
tabRef.set(tab);
tab.setOpenInNewTabCallback(() -> openNewTab(
tab.getEngineLocation() == null ? settings.getStartPageUrl() : tab.getEngineLocation()
));
tabsByKey.put(key, tab);
pageHost.add(tab, key);
OACTabbedPane tabs = getTitleBar().getTabs();
suppressPlusAutoOpen = true;
try {
plusTabSupport.ensurePlusTab();
int insertIndex = findPlusIndex(tabs);
if (insertIndex < 0) insertIndex = tabs.getTabCount();
tabs.insertTab(key, null, new OACPanel(), null, insertIndex);
tabs.setTabComponentAt(insertIndex, new TabButton(displayTitleFromUrl(normalized), () -> closeTabByKey(key)));
tabs.setSelectedIndex(insertIndex);
} finally {
suppressPlusAutoOpen = false;
}
cardLayout.show(pageHost, key);
browser.addressField().setText(normalized);
connectTabClient(key, client);
}
private void connectTabClient(String key, ClientImpl client) {
try {
File logsFolder = new File("logs");
if (!logsFolder.exists()) logsFolder.mkdir();
ProtocolValues values = new ProtocolValues();
values.packetHandler = new PacketHandler();
values.eventManager = new EventManager();
values.ssl = settings.isSslEnabled();
AddonLoader addonLoader = new AddonLoader(values.eventManager,
new Logger(new File(logsFolder, "addons"), false, true));
ProtocolBridge bridge = new ProtocolBridge(
client,
values,
ProtocolVersion.PV_1_0_0_BETA,
new File(logsFolder, "client")
);
protocolByKey.put(key, bridge);
addonLoaderByKey.put(key, addonLoader);
client.buildINSConnection();
bridge.getProtocolValues().eventManager.registerListener(client);
InsEndpoint ep = Objects.requireNonNull(settings.getSelectedIns(), "selectedIns");
client.getClientINSConnection().connect(ep.host(), ep.port());
File addonsFolder = new File("addons");
if (!addonsFolder.exists()) addonsFolder.mkdir();
addonLoader.loadAddonsFromDirectory(addonsFolder);
} catch (Exception e) {
OACOptionPane.showMessageDialog(
this,
"Tab connection failed:\n" + e.getMessage(),
"Connection",
OACOptionPane.ERROR_MESSAGE
);
}
}
private int findPlusIndex(OACTabbedPane tabs) {
for (int i = 0; i < tabs.getTabCount(); i++) {
if ("+".equals(tabs.getTitleAt(i))) return i;
}
return -1;
}
private String nextTabKey() {
tabCounter++;
return "Tab-" + tabCounter;
}
private void onVisited(String url) {
if (!settings.isHistoryEnabled()) return;
if (url == null || url.isBlank()) return;
historyStore.append(url.trim(), Instant.now());
}
private void addCurrentToFavorites() {
BrowserTab tab = getCurrentTab();
if (tab == null) return;
String loc = tab.getEngineLocation();
if (loc == null || loc.isBlank()) return;
String u = loc.trim();
if (!settings.getFavoritesMutable().contains(u)) {
settings.getFavoritesMutable().add(u);
SettingsManager.save(settings);
browser.favoritesBar().setFavorites(settings.getFavorites());
}
}
private void closeCurrentTab() {
String key = getSelectedTabKey();
if (key == null) return;
closeTabByKey(key);
}
private void closeTabByKey(String key) {
int idx = findTabIndexByKey(key);
if (idx < 0) return;
BrowserTab removed = tabsByKey.remove(key);
if (removed != null) {
removed.dispose();
pageHost.remove(removed);
}
protocolByKey.remove(key);
addonLoaderByKey.remove(key);
OACTabbedPane tabs = getTitleBar().getTabs();
suppressPlusAutoOpen = true;
try {
tabs.removeTabAt(idx);
} finally {
suppressPlusAutoOpen = false;
}
if (tabsByKey.isEmpty()) {
openNewTab(settings.getStartPageUrl());
return;
}
plusTabSupport.ensurePlusTab();
int plusIdx = findPlusIndex(tabs);
if (plusIdx > 0) {
int target = Math.min(Math.max(0, idx), plusIdx - 1);
tabs.setSelectedIndex(target);
}
onHeaderTabChanged();
}
private int findTabIndexByKey(String key) {
OACTabbedPane tabs = getTitleBar().getTabs();
for (int i = 0; i < tabs.getTabCount(); i++) {
if (Objects.equals(tabs.getTitleAt(i), key)) return i;
}
return -1;
}
private void updateTabPresentation(String key, String location) {
int idx = findTabIndexByKey(key);
if (idx < 0) return;
Component component = getTitleBar().getTabs().getTabComponentAt(idx);
if (component instanceof TabButton tabButton) {
tabButton.setTitle(displayTitleFromUrl(location));
}
}
private void onHeaderTabChanged() {
if (handlingTabSwitch) return;
OACTabbedPane tabs = getTitleBar().getTabs();
int idx = tabs.getSelectedIndex();
if (idx < 0) return;
if (plusTabSupport.isPlusTab(idx)) {
if (suppressPlusAutoOpen) {
return;
}
if (tabsByKey.isEmpty()) {
return;
}
handlingTabSwitch = true;
try {
int fallback = lastSelectedRealTab >= 0
? Math.min(lastSelectedRealTab, Math.max(0, tabs.getTabCount() - 1))
: Math.max(0, tabs.getTabCount() - 2);
int next = plusTabSupport.handleIfPlusSelected(idx, fallback);
if (next >= 0 && next < tabs.getTabCount()) {
tabs.setSelectedIndex(next);
}
} finally {
handlingTabSwitch = false;
}
return;
}
lastSelectedRealTab = idx;
String key = tabs.getTitleAt(idx);
BrowserTab tab = tabsByKey.get(key);
if (tab == null) return;
cardLayout.show(pageHost, key);
String loc = tab.getLocationUrl();
if (loc != null && !loc.isBlank()) {
browser.addressField().setText(loc);
}
}
public BrowserTab getCurrentTab() {
String key = getSelectedTabKey();
if (key == null) return null;
return tabsByKey.get(key);
}
public AddonLoader getCurrentAddonLoader() {
String key = getSelectedTabKey();
if (key == null) return null;
return addonLoaderByKey.get(key);
}
private String getSelectedTabKey() {
int idx = getTitleBar().getTabs().getSelectedIndex();
if (idx < 0) return null;
if (plusTabSupport.isPlusTab(idx)) return null;
return getTitleBar().getTabs().getTitleAt(idx);
}
}

View File

@@ -1,102 +0,0 @@
package org.openautonomousconnection.webclient.ui;
import org.openautonomousconnection.oacswing.component.OACButton;
import org.openautonomousconnection.oacswing.component.OACPanel;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Favorites bar rendered as compact chips.
*/
public final class FavChipBar extends OACPanel {
private final List<String> favorites = new ArrayList<>();
private Consumer<String> onNavigate = u -> {
};
private Runnable onEdit = () -> {
};
/**
* Creates a chip-based favorites bar.
*/
public FavChipBar() {
super(new BorderLayout());
setOpaque(false);
setBorder(new EmptyBorder(4, 8, 6, 8));
rebuild();
}
private static String compactLabel(String url) {
String s = url;
if (s.startsWith("web://")) s = s.substring("web://".length());
if (s.endsWith("/")) s = s.substring(0, s.length() - 1);
if (s.isEmpty()) s = url;
if (s.length() <= 24) return s;
return s.substring(0, 24) + "...";
}
/**
* Sets navigation callback.
*
* @param onNavigate callback
*/
public void setOnNavigate(Consumer<String> onNavigate) {
this.onNavigate = Objects.requireNonNull(onNavigate, "onNavigate");
rebuild();
}
/**
* Sets edit callback.
*
* @param onEdit callback
*/
public void setOnEdit(Runnable onEdit) {
this.onEdit = Objects.requireNonNull(onEdit, "onEdit");
rebuild();
}
/**
* Replaces favorites list.
*
* @param items favorites
*/
public void setFavorites(List<String> items) {
favorites.clear();
if (items != null) favorites.addAll(items);
rebuild();
}
private void rebuild() {
removeAll();
OACPanel chips = new OACPanel(new FlowLayout(FlowLayout.LEFT, 6, 0));
chips.setOpaque(false);
for (String url : favorites) {
if (url == null || url.isBlank()) continue;
String u = url.trim();
OACButton chip = new OACButton(compactLabel("" + u));
chip.setToolTipText(u);
chip.setMargin(new Insets(3, 10, 3, 10));
chip.setFocusable(false);
chip.addActionListener(e -> onNavigate.accept(u));
chips.add(chip);
}
OACPanel right = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
right.setOpaque(false);
add(chips, BorderLayout.CENTER);
add(right, BorderLayout.EAST);
revalidate();
repaint();
}
}

View File

@@ -1,56 +0,0 @@
package org.openautonomousconnection.webclient.ui;
import org.openautonomousconnection.oacswing.component.OACPanel;
import java.awt.*;
/**
* A modern painted surface with subtle gradient and a hairline border.
*/
public class GlassPanel extends OACPanel {
private final int radius;
private final Color border;
private final Color top;
private final Color bottom;
/**
* Creates a glass-like panel.
*
* @param layout layout
* @param radius corner radius
* @param top gradient top color
* @param bottom gradient bottom color
* @param border border color
*/
public GlassPanel(LayoutManager layout, int radius, Color top, Color bottom, Color border) {
super(layout);
this.radius = radius;
this.top = top;
this.bottom = bottom;
this.border = border;
setOpaque(false);
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
try {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int w = getWidth();
int h = getHeight();
GradientPaint gp = new GradientPaint(0, 0, top, 0, h, bottom);
g2.setPaint(gp);
g2.fillRoundRect(0, 0, w - 1, h - 1, radius, radius);
g2.setColor(border);
g2.drawRoundRect(0, 0, w - 1, h - 1, radius, radius);
} finally {
g2.dispose();
}
super.paintComponent(g);
}
}

View File

@@ -1,77 +0,0 @@
package org.openautonomousconnection.webclient.ui;
import org.openautonomousconnection.oacswing.component.OACPanel;
import org.openautonomousconnection.oacswing.component.OACTabbedPane;
import java.util.Objects;
/**
* Manages a trailing "+" tab in a tabbed pane component.
*/
public final class PlusTabSupport {
private final OACTabbedPane tabs;
private final Runnable onNewTab;
private final OACPanel dummy = new OACPanel(); // never shown as content
/**
* Creates plus-tab support.
*
* @param tabs tabbed pane
* @param onNewTab callback when "+" is pressed/selected
*/
public PlusTabSupport(OACTabbedPane tabs, Runnable onNewTab) {
this.tabs = Objects.requireNonNull(tabs, "tabs");
this.onNewTab = Objects.requireNonNull(onNewTab, "onNewTab");
}
/**
* Ensures the "+" tab exists as the last tab.
*/
public void ensurePlusTab() {
int plusIdx = findPlusIndex();
if (plusIdx >= 0) return;
tabs.addTab("+", dummy);
}
/**
* Returns whether the index points to the "+" tab.
*
* @param index index
* @return true if plus tab
*/
public boolean isPlusTab(int index) {
int plusIdx = findPlusIndex();
return plusIdx >= 0 && index == plusIdx;
}
/**
* Handles selection change; if "+" selected, triggers new tab and returns previous index to reselect.
*
* @param selectedIndex currently selected index
* @param previousIndex previous index (fallback)
* @return index that should be selected after handling
*/
public int handleIfPlusSelected(int selectedIndex, int previousIndex) {
if (!isPlusTab(selectedIndex)) return selectedIndex;
onNewTab.run();
int plusIdx = findPlusIndex();
if (plusIdx < 0) return 0;
// Select last "real" tab if exists, else 0
int lastReal = Math.max(0, plusIdx - 1);
return lastReal;
}
private int findPlusIndex() {
for (int i = 0; i < tabs.getTabCount(); i++) {
String t = tabs.getTitleAt(i);
if ("+".equals(t)) return i;
}
return -1;
}
}

View File

@@ -1,77 +0,0 @@
package org.openautonomousconnection.webclient.ui;
import org.openautonomousconnection.oacswing.component.OACButton;
import org.openautonomousconnection.oacswing.component.OACLabel;
import org.openautonomousconnection.oacswing.component.OACPanel;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.util.Objects;
/**
* Custom tab header component (favicon + title + close button).
*/
public final class TabButton extends OACPanel {
private final OACLabel iconLabel = new OACLabel("");
private final OACLabel titleLabel = new OACLabel("");
private final OACButton closeButton = new OACButton("x");
/**
* Creates a tab button component.
*
* @param initialTitle initial title
* @param onClose close action
*/
public TabButton(String initialTitle, Runnable onClose) {
super(new BorderLayout(6, 0));
Objects.requireNonNull(onClose, "onClose");
setOpaque(false);
setBorder(new EmptyBorder(3, 10, 3, 6));
iconLabel.setPreferredSize(new Dimension(16, 16));
iconLabel.setMinimumSize(new Dimension(16, 16));
titleLabel.setText(safeTitle(initialTitle));
titleLabel.setBorder(new EmptyBorder(0, 2, 0, 2));
closeButton.setFocusable(false);
closeButton.setMargin(new Insets(1, 8, 1, 8));
closeButton.addActionListener(e -> onClose.run());
add(iconLabel, BorderLayout.WEST);
add(titleLabel, BorderLayout.CENTER);
add(closeButton, BorderLayout.EAST);
}
private static String safeTitle(String title) {
String s = title == null ? "" : title.trim();
if (s.isEmpty()) return "New Tab";
if (s.length() <= 22) return s;
return s.substring(0, 22) + "...";
}
/**
* Sets the tab title.
*
* @param title title
*/
public void setTitle(String title) {
titleLabel.setText(safeTitle(title));
revalidate();
repaint();
}
/**
* Sets the favicon icon (scaled by caller if needed).
*
* @param icon icon or null
*/
public void setFavicon(Icon icon) {
iconLabel.setIcon(icon);
revalidate();
repaint();
}
}

View File

@@ -1,134 +0,0 @@
package org.openautonomousconnection.webclient.ui.menus;
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
import org.openautonomousconnection.oacswing.component.*;
import org.openautonomousconnection.webclient.ClientImpl;
import org.openautonomousconnection.webclient.Main;
import org.openautonomousconnection.webclient.settings.AppSettings;
import org.openautonomousconnection.webclient.settings.InsEndpoint;
import org.openautonomousconnection.webclient.ui.BrowserTab;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.Objects;
/**
* About dialog showing basic client information and quick actions.
*/
public final class AboutDialog extends OACDialog {
/**
* Creates an about dialog.
*
* @param owner owner frame
* @param settings app settings
*/
public AboutDialog(Frame owner, AppSettings settings) {
super(owner, "About", true);
Objects.requireNonNull(settings, "settings");
buildUi(settings);
pack();
setMinimumSize(new Dimension(640, 420));
setLocationRelativeTo(owner);
}
private static String safeClientInsId() {
ClientImpl c = currentClient();
if (c == null) return "N/A";
if (c.getClientINSConnection() == null) return "N/A";
return String.valueOf(c.getClientINSConnection().getUniqueID());
}
/**
* Tries to resolve an additional "web server" id if available in your client.
*
* <p>Adjust this method once you confirm where your web-server id is exposed.</p>
*
* @return id or N/A
*/
private static String safeClientWebId() {
ClientImpl c = currentClient();
if (c == null) return "N/A";
if (c.getClientServerConnection() == null) return "N/A";
return String.valueOf(c.getClientServerConnection().getUniqueID());
}
private static ClientImpl currentClient() {
if (Main.getUi() == null) return null;
BrowserTab tab = Main.getUi().getCurrentTab();
if (tab == null) return null;
return tab.getProtocolClient();
}
private void buildUi(AppSettings settings) {
setLayout(new BorderLayout());
OACPanel root = new OACPanel(new BorderLayout(10, 10));
root.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
OACTextArea info = new OACTextArea();
info.setEditable(false);
info.setLineWrap(true);
info.setWrapStyleWord(true);
InsEndpoint selected = settings.getSelectedIns();
LuaExecutionPolicy pol = settings.getLuaPolicy();
String insId = safeClientInsId();
String webId = safeClientWebId();
info.setText(
"OAC WebClient\n\n" +
"This client embeds JavaFX WebView into Swing and executes Lua scripts (optional) instead of JavaScript.\n\n" +
"Selected INS: " + (selected == null ? "N/A" : selected) + "\n\n" +
"Client IDs:\n" +
" - INS Connection ID: " + insId + "\n" +
" - Web Connection ID: " + webId + "\n\n" +
"Lua Execution Policy:\n" +
" - timeoutMs: " + pol.timeout().toMillis() + "\n" +
" - instructionLimit: " + pol.instructionLimit() + "\n" +
" - hookStep: " + pol.hookStep() + "\n"
);
root.add(new OACScrollPane(info), BorderLayout.CENTER);
OACPanel actions = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
OACButton openCerts = new OACButton("Open Certificates Folder");
openCerts.addActionListener(e -> {
ClientImpl c = currentClient();
if (c == null) {
OACOptionPane.showMessageDialog(this, "No active tab client.", "Certificates", OACOptionPane.WARNING_MESSAGE);
return;
}
openDir(c.getFolderStructure().certificatesFolder.getAbsolutePath(), "Certificates");
});
OACButton openLicenses = new OACButton("Open Licenses Folder");
openLicenses.addActionListener(e -> openDir(new File("licenses").getAbsolutePath(), "Licenses"));
OACButton close = new OACButton("Close");
close.addActionListener(e -> dispose());
actions.add(openCerts);
actions.add(openLicenses);
actions.add(close);
root.add(actions, BorderLayout.SOUTH);
add(root, BorderLayout.CENTER);
}
private void openDir(String dir, String title) {
try {
File f = new File(dir);
Desktop.getDesktop().open(f.getAbsoluteFile());
} catch (Exception ex) {
OACOptionPane.showMessageDialog(this,
"Failed to open " + title + " folder:\n" + ex.getMessage(),
title,
OACOptionPane.ERROR_MESSAGE);
}
}
}

View File

@@ -1,257 +0,0 @@
package org.openautonomousconnection.webclient.ui.menus;
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
import dev.unlegitdqrk.unlegitlibrary.addon.events.AddonLoadedEvent;
import dev.unlegitdqrk.unlegitlibrary.addon.impl.Addon;
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
import org.openautonomousconnection.oacswing.component.*;
import org.openautonomousconnection.webclient.settings.AppSettings;
import org.openautonomousconnection.webclient.ui.BrowserUI;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.Objects;
/**
* Addons management dialog for the currently selected tab/client.
*/
public final class AddonsDialog extends OACDialog {
private final BrowserUI browserUi;
private final DefaultListModel<Addon> addonModel = new DefaultListModel<>();
private final OACList<Addon> addonList = new OACList<>(addonModel);
private final OACTextArea details = new OACTextArea();
private final AddonListener addonListener = new AddonListener();
private AddonLoader currentLoader;
public AddonsDialog(Frame owner, AppSettings settings) {
super(owner, "Addons", true);
Objects.requireNonNull(settings, "settings");
this.browserUi = owner instanceof BrowserUI bui ? bui : null;
buildUi();
bindLoader();
refreshAddons();
pack();
setMinimumSize(new Dimension(820, 520));
setLocationRelativeTo(owner);
}
private static String safe(ValueSupplier s) {
try {
String v = s.get();
return v == null || v.isBlank() ? "N/A" : v;
} catch (Exception ignored) {
return "N/A";
}
}
@Override
public void dispose() {
unbindLoader();
super.dispose();
}
private void buildUi() {
setLayout(new BorderLayout(10, 10));
OACPanel root = new OACPanel(new BorderLayout(10, 10));
root.setBorder(javax.swing.BorderFactory.createEmptyBorder(12, 12, 12, 12));
addonList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
addonList.addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) updateDetails();
});
details.setEditable(false);
details.setLineWrap(true);
details.setWrapStyleWord(true);
OACPanel center = new OACPanel(new GridLayout(1, 2, 10, 0));
center.add(new OACScrollPane(addonList));
center.add(new OACScrollPane(details));
root.add(center, BorderLayout.CENTER);
OACPanel buttons = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
OACButton refresh = new OACButton("Refresh");
refresh.addActionListener(e -> refreshAddons());
OACButton loadDir = new OACButton("Load Dir");
loadDir.addActionListener(e -> loadFromAddonsDirectory());
OACButton loadJar = new OACButton("Load Jar");
loadJar.addActionListener(e -> loadSingleJar());
OACButton enable = new OACButton("Enable");
enable.addActionListener(e -> setSelectedEnabled(true));
OACButton disable = new OACButton("Disable");
disable.addActionListener(e -> setSelectedEnabled(false));
OACButton close = new OACButton("Close");
close.addActionListener(e -> dispose());
buttons.add(new OACLabel("Current tab addons"));
buttons.add(refresh);
buttons.add(loadDir);
buttons.add(loadJar);
buttons.add(enable);
buttons.add(disable);
buttons.add(close);
root.add(buttons, BorderLayout.SOUTH);
add(root, BorderLayout.CENTER);
}
private void bindLoader() {
unbindLoader();
currentLoader = browserUi == null ? null : browserUi.getCurrentAddonLoader();
if (currentLoader != null) {
currentLoader.getEventManager().registerListener(addonListener);
}
}
private void unbindLoader() {
if (currentLoader != null) {
try {
currentLoader.getEventManager().unregisterListener(addonListener);
} catch (Exception ignored) {
// best effort
}
}
currentLoader = null;
}
private void refreshAddons() {
bindLoader();
addonModel.clear();
if (currentLoader == null) {
details.setText("No active tab or no addon loader for current tab.");
return;
}
for (Addon addon : currentLoader.getLoadedAddons()) {
addonModel.addElement(addon);
}
if (addonModel.size() > 0) {
addonList.setSelectedIndex(0);
}
updateDetails();
}
private void loadFromAddonsDirectory() {
if (currentLoader == null) {
OACOptionPane.showMessageDialog(this, "No addon loader for current tab.", "Addons", OACOptionPane.WARNING_MESSAGE);
return;
}
try {
File addonsFolder = new File("addons");
if (!addonsFolder.exists() && !addonsFolder.mkdirs()) {
throw new IllegalStateException("Cannot create addons directory.");
}
currentLoader.loadAddonsFromDirectory(addonsFolder);
refreshAddons();
} catch (Exception ex) {
OACOptionPane.showMessageDialog(this, "Failed to load from directory:\n" + ex.getMessage(), "Addons", OACOptionPane.ERROR_MESSAGE);
}
}
private void loadSingleJar() {
if (currentLoader == null) {
OACOptionPane.showMessageDialog(this, "No addon loader for current tab.", "Addons", OACOptionPane.WARNING_MESSAGE);
return;
}
FileDialog fd = new FileDialog(this, "Select addon jar", FileDialog.LOAD);
fd.setFile("*.jar");
fd.setVisible(true);
String file = fd.getFile();
String dir = fd.getDirectory();
if (file == null || dir == null) return;
try {
currentLoader.loadAddonFromJar(new File(dir, file));
refreshAddons();
} catch (Exception ex) {
OACOptionPane.showMessageDialog(this, "Failed to load jar:\n" + ex.getMessage(), "Addons", OACOptionPane.ERROR_MESSAGE);
}
}
private void setSelectedEnabled(boolean enabled) {
if (currentLoader == null) {
OACOptionPane.showMessageDialog(this, "No addon loader for current tab.", "Addons", OACOptionPane.WARNING_MESSAGE);
return;
}
Addon addon = addonList.getSelectedValue();
if (addon == null) return;
try {
if (enabled) {
currentLoader.enableAddon(addon);
} else {
currentLoader.disableAddon(addon);
}
addonList.repaint();
updateDetails();
} catch (Exception ex) {
OACOptionPane.showMessageDialog(this, "Operation failed:\n" + ex.getMessage(), "Addons", OACOptionPane.ERROR_MESSAGE);
}
}
private void updateDetails() {
Addon addon = addonList.getSelectedValue();
if (addon == null) {
details.setText(addonModel.isEmpty() ? "No addons loaded." : "Select an addon.");
return;
}
String name = safe(() -> addon.getAddonInfo().name());
String version = safe(() -> addon.getAddonInfo().version());
String author = safe(() -> addon.getAddonInfo().author());
details.setText(
"Name: " + name + "\n" +
"Version: " + version + "\n" +
"Author: " + author + "\n" +
"Enabled: " + addon.isEnabled() + "\n" +
"Class: " + addon.getClass().getName()
);
}
private boolean containsAddon(Addon addon) {
for (int i = 0; i < addonModel.size(); i++) {
if (addonModel.get(i) == addon) return true;
}
return false;
}
@FunctionalInterface
private interface ValueSupplier {
String get();
}
private final class AddonListener extends EventListener {
@Listener
public void onLoaded(AddonLoadedEvent event) {
EventQueue.invokeLater(() -> {
Addon a = event.getAddon();
if (a == null) return;
if (!containsAddon(a)) addonModel.addElement(a);
addonList.repaint();
if (addonList.getSelectedValue() == null && addonModel.size() > 0) {
addonList.setSelectedIndex(0);
}
updateDetails();
});
}
}
}

View File

@@ -1,417 +0,0 @@
package org.openautonomousconnection.webclient.ui.menus;
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
import org.openautonomousconnection.oacswing.component.*;
import org.openautonomousconnection.webclient.settings.AppSettings;
import org.openautonomousconnection.webclient.settings.InsEndpoint;
import javax.swing.*;
import java.awt.*;
import java.time.Duration;
import java.util.List;
import java.util.Objects;
/**
* Settings dialog for WebClient.
*/
public final class SettingsDialog extends OACDialog {
private final AppSettings settings;
private final Runnable onSaved;
private final OACTextField startPageField = new OACTextField();
private final OACCheckBox sslEnabled = new OACCheckBox("Enable SSL");
private final OACCheckBox luaEnabled = new OACCheckBox("Enable Lua");
private final OACCheckBox historyEnabled = new OACCheckBox("Enable History");
private final DefaultListModel<InsEndpoint> insModel = new DefaultListModel<>();
private final OACList<InsEndpoint> insList = new OACList<>(insModel);
private final OACTextField insHost = new OACTextField();
private final OACTextField insPort = new OACTextField();
private final DefaultListModel<String> favModel = new DefaultListModel<>();
private final OACList<String> favList = new OACList<>(favModel);
private final OACTextField favUrl = new OACTextField();
private final OACTextField luaTimeoutMs = new OACTextField();
private final OACTextField luaInstructionLimit = new OACTextField();
private final OACTextField luaHookStep = new OACTextField();
/**
* Creates settings dialog.
*
* @param owner owner frame
* @param settings settings (mutable)
* @param onSaved callback invoked after saving
*/
public SettingsDialog(Frame owner, AppSettings settings, Runnable onSaved) {
super(owner, "Settings", true);
this.settings = Objects.requireNonNull(settings, "settings");
this.onSaved = Objects.requireNonNull(onSaved, "onSaved");
buildUi();
loadFromSettings();
pack();
setMinimumSize(new Dimension(760, 560));
setLocationRelativeTo(owner);
}
private void buildUi() {
setLayout(new BorderLayout());
OACTabbedPane tabs = new OACTabbedPane();
tabs.addTab("General", buildGeneral());
tabs.addTab("INS", buildIns());
tabs.addTab("Favorites", buildFavorites());
tabs.addTab("Lua Policy", buildLuaPolicy());
add(tabs, BorderLayout.CENTER);
OACPanel south = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 8, 8));
OACButton save = new OACButton("Save");
OACButton cancel = new OACButton("Cancel");
save.addActionListener(e -> onSave());
cancel.addActionListener(e -> dispose());
south.add(cancel);
south.add(save);
add(south, BorderLayout.SOUTH);
}
private JComponent buildGeneral() {
OACPanel p = new OACPanel(new GridBagLayout());
p.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(6, 6, 6, 6);
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
int y = 0;
c.gridx = 0;
c.gridy = y;
c.weightx = 0;
p.add(new OACLabel("Start Page"), c);
c.gridx = 1;
c.gridy = y;
c.weightx = 1;
p.add(startPageField, c);
y++;
c.gridx = 0;
c.gridy = y;
c.gridwidth = 2;
OACPanel checks = new OACPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
checks.add(sslEnabled);
checks.add(luaEnabled);
checks.add(historyEnabled);
p.add(checks, c);
return p;
}
private JComponent buildIns() {
OACPanel root = new OACPanel(new BorderLayout(10, 10));
root.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
insList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
root.add(new OACScrollPane(insList), BorderLayout.CENTER);
OACPanel editor = new OACPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(6, 6, 6, 6);
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
editor.add(new OACLabel("Host"), c);
c.gridx = 1;
c.gridy = 0;
c.weightx = 1;
editor.add(insHost, c);
c.gridx = 0;
c.gridy = 1;
c.weightx = 0;
editor.add(new OACLabel("Port"), c);
c.gridx = 1;
c.gridy = 1;
c.weightx = 1;
insPort.setText("1026");
insPort.setToolTipText("Default: 1026");
editor.add(insPort, c);
OACPanel buttons = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
OACButton add = new OACButton("Add");
OACButton remove = new OACButton("Remove");
OACButton select = new OACButton("Select");
add.addActionListener(e -> onAddIns());
remove.addActionListener(e -> onRemoveIns());
select.addActionListener(e -> onSelectIns());
buttons.add(add);
buttons.add(remove);
buttons.add(select);
OACPanel south = new OACPanel(new BorderLayout());
south.add(editor, BorderLayout.CENTER);
south.add(buttons, BorderLayout.SOUTH);
root.add(south, BorderLayout.SOUTH);
return root;
}
private JComponent buildFavorites() {
OACPanel root = new OACPanel(new BorderLayout(10, 10));
root.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
favList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
root.add(new OACScrollPane(favList), BorderLayout.CENTER);
OACPanel addRow = new OACPanel(new BorderLayout(8, 0));
addRow.add(new OACLabel("URL"), BorderLayout.WEST);
addRow.add(favUrl, BorderLayout.CENTER);
OACPanel buttons = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
OACButton add = new OACButton("Add");
OACButton remove = new OACButton("Remove");
add.addActionListener(e -> onAddFavorite());
remove.addActionListener(e -> onRemoveFavorite());
buttons.add(add);
buttons.add(remove);
OACPanel south = new OACPanel(new BorderLayout(8, 8));
south.add(addRow, BorderLayout.CENTER);
south.add(buttons, BorderLayout.SOUTH);
root.add(south, BorderLayout.SOUTH);
return root;
}
private JComponent buildLuaPolicy() {
OACPanel p = new OACPanel(new GridBagLayout());
p.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(6, 6, 6, 6);
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
int y = 0;
c.gridx = 0;
c.gridy = y;
c.weightx = 0;
p.add(new OACLabel("Timeout (ms)"), c);
c.gridx = 1;
c.gridy = y;
c.weightx = 1;
p.add(luaTimeoutMs, c);
y++;
c.gridx = 0;
c.gridy = y;
c.weightx = 0;
p.add(new OACLabel("Instruction Limit"), c);
c.gridx = 1;
c.gridy = y;
c.weightx = 1;
p.add(luaInstructionLimit, c);
y++;
c.gridx = 0;
c.gridy = y;
c.weightx = 0;
p.add(new OACLabel("Hook Step"), c);
c.gridx = 1;
c.gridy = y;
c.weightx = 1;
p.add(luaHookStep, c);
y++;
OACPanel buttons = new OACPanel(new FlowLayout(FlowLayout.RIGHT, 8, 0));
OACButton reset = new OACButton("Reset to uiDefault");
reset.addActionListener(e -> {
settings.resetLuaPolicyToUiDefault();
loadLuaPolicy();
});
buttons.add(reset);
c.gridx = 0;
c.gridy = y;
c.gridwidth = 2;
p.add(buttons, c);
return p;
}
private void loadFromSettings() {
startPageField.setText(settings.getStartPageUrl());
sslEnabled.setSelected(settings.isSslEnabled());
luaEnabled.setSelected(settings.isLuaEnabled());
historyEnabled.setSelected(settings.isHistoryEnabled());
insModel.clear();
for (InsEndpoint ep : settings.getInsEndpoints()) insModel.addElement(ep);
// Select current INS in list if present
InsEndpoint selected = settings.getSelectedIns();
if (selected != null) {
for (int i = 0; i < insModel.size(); i++) {
if (selected.equals(insModel.get(i))) {
insList.setSelectedIndex(i);
break;
}
}
}
favModel.clear();
for (String u : settings.getFavorites()) favModel.addElement(u);
loadLuaPolicy();
}
private void loadLuaPolicy() {
LuaExecutionPolicy pol = settings.getLuaPolicy();
luaTimeoutMs.setText(Long.toString(pol.timeout().toMillis()));
luaInstructionLimit.setText(Long.toString(pol.instructionLimit()));
luaHookStep.setText(Integer.toString(pol.hookStep()));
}
private void onAddIns() {
String host = insHost.getText() == null ? "" : insHost.getText().trim();
String portS = insPort.getText() == null ? "" : insPort.getText().trim();
if (host.isEmpty() || portS.isEmpty()) {
OACOptionPane.showMessageDialog(this, "Host/Port required.", "INS", OACOptionPane.WARNING_MESSAGE);
return;
}
int port;
try {
port = Integer.parseInt(portS);
} catch (Exception e) {
OACOptionPane.showMessageDialog(this, "Invalid port.", "INS", OACOptionPane.WARNING_MESSAGE);
return;
}
InsEndpoint ep;
try {
ep = new InsEndpoint(host, port);
} catch (Exception e) {
OACOptionPane.showMessageDialog(this, e.getMessage(), "INS", OACOptionPane.WARNING_MESSAGE);
return;
}
// Add if not exists
for (int i = 0; i < insModel.size(); i++) {
if (ep.equals(insModel.get(i))) {
insList.setSelectedIndex(i);
return;
}
}
insModel.addElement(ep);
insList.setSelectedIndex(insModel.size() - 1);
}
private void onRemoveIns() {
int idx = insList.getSelectedIndex();
if (idx < 0) return;
InsEndpoint ep = insModel.get(idx);
int confirm = OACOptionPane.showConfirmDialog(this, "Remove " + ep + "?", "INS", OACOptionPane.YES_NO_OPTION);
if (confirm != OACOptionPane.YES_OPTION) return;
insModel.remove(idx);
if (insModel.isEmpty()) {
// Always keep at least one
insModel.addElement(new InsEndpoint("open-autonomous-connection.org", 1026));
}
insList.setSelectedIndex(Math.min(idx, insModel.size() - 1));
}
private void onSelectIns() {
int idx = insList.getSelectedIndex();
if (idx < 0) return;
InsEndpoint ep = insModel.get(idx);
OACOptionPane.showMessageDialog(this,
"Selected INS will be used on next (re)connect:\n" + ep,
"INS", OACOptionPane.INFORMATION_MESSAGE);
}
private void onAddFavorite() {
String url = favUrl.getText() == null ? "" : favUrl.getText().trim();
if (url.isEmpty()) return;
for (int i = 0; i < favModel.size(); i++) {
if (url.equals(favModel.get(i))) return;
}
favModel.addElement(url);
favUrl.setText("");
}
private void onRemoveFavorite() {
int idx = favList.getSelectedIndex();
if (idx < 0) return;
favModel.remove(idx);
}
private void onSave() {
// Validate + apply
String start = startPageField.getText() == null ? "" : startPageField.getText().trim();
if (start.isEmpty()) {
OACOptionPane.showMessageDialog(this, "Start page required.", "Settings", OACOptionPane.WARNING_MESSAGE);
return;
}
try {
settings.setStartPageUrl(start);
settings.setSslEnabled(sslEnabled.isSelected());
settings.setLuaEnabled(luaEnabled.isSelected());
settings.setHistoryEnabled(historyEnabled.isSelected());
// INS list + selection
List<InsEndpoint> list = settings.getInsEndpointsMutable();
list.clear();
for (int i = 0; i < insModel.size(); i++) list.add(insModel.get(i));
int selIdx = insList.getSelectedIndex();
if (selIdx >= 0 && selIdx < insModel.size()) {
settings.setSelectedIns(insModel.get(selIdx));
} else if (!list.isEmpty()) {
settings.setSelectedIns(list.get(0));
}
// Favorites
List<String> favs = settings.getFavoritesMutable();
favs.clear();
for (int i = 0; i < favModel.size(); i++) {
String u = favModel.get(i);
if (u != null && !u.isBlank()) favs.add(u.trim());
}
// Lua policy
long timeoutMs = parseLongOrFail(luaTimeoutMs.getText(), "Timeout (ms)");
long instr = parseLongOrFail(luaInstructionLimit.getText(), "Instruction Limit");
int hook = (int) parseLongOrFail(luaHookStep.getText(), "Hook Step");
settings.setLuaPolicy(new LuaExecutionPolicy(Duration.ofMillis(timeoutMs), instr, hook));
} catch (Exception e) {
OACOptionPane.showMessageDialog(this, e.getMessage(), "Settings", OACOptionPane.ERROR_MESSAGE);
return;
}
onSaved.run();
dispose();
}
private long parseLongOrFail(String text, String field) {
String s = text == null ? "" : text.trim();
if (s.isEmpty()) throw new IllegalArgumentException(field + " is required.");
try {
return Long.parseLong(s);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid number for " + field + ".");
}
}
}

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: org.openautonomousconnection.browser.Main

View File

@@ -0,0 +1,15 @@
Name: Java™ libraries for 3D Graphics, Multimedia and Processing
Short Name: jogamp
URL: http://jogamp.org/
Version: 2.2.4 (master@78f641de80d1c37cd61e5300eeba369c6aa9b1a1)
License: Mixed (see LICENSE.txt files)
Description:
This library is used to provide OpenGL bindings. Download
jogamp-all-platforms.7z and copy files from the jar/ directory.
See http://jogamp.org/wiki/index.php/Downloading_and_installing_JOGL for
related documentation.
Local Modifications:
None.

View File

@@ -0,0 +1,133 @@
The GlueGen source code is mostly licensed under the New BSD 2-clause license,
however it contains other licensed material as well.
Below you find a detailed list of licenses used in this project.
+++
The content of folder 'make/lib' contains build-time only
Java binaries (JAR) to ease the build setup.
Each JAR file has it's corresponding LICENSE file containing the
source location and license text. None of these binaries are contained in any way
by the generated and deployed GlueGen binaries.
+++
L.1) The GlueGen source tree contains code from the JogAmp Community
which is covered by the Simplified BSD 2-clause license:
Copyright 2010 JogAmp Community. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of JogAmp Community.
You can address the JogAmp Community via:
Web http://jogamp.org/
Forum/Mailinglist http://jogamp.762907.n3.nabble.com/
Chatrooms
IRC irc.freenode.net #jogamp
Jabber conference.jabber.org room: jogamp (deprecated!)
Repository http://jogamp.org/git/
Email mediastream _at_ jogamp _dot_ org
L.2) The GlueGen source tree contains code from Sun Microsystems, Inc.
which is covered by the New BSD 3-clause license:
Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistribution of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistribution in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of Sun Microsystems, Inc. or the names of
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
This software is provided "AS IS," without a warranty of any kind. ALL
EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
You acknowledge that this software is not designed or intended for use
in the design, construction, operation or maintenance of any nuclear
facility.
L.3) The GlueGen source tree contains CGRAM http://www.antlr.org/grammar/cgram/,
a ANSI-C parser implementation using ANTLR, which is being used
in the compile time part only.
It is covered by the Original BSD 4-clause license:
Copyright (c) 1998-2000, Non, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions, and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
All advertising materials mentioning features or use of this
software must display the following acknowledgement:
This product includes software developed by Non, Inc. and
its contributors.
Neither name of the company nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

View File

@@ -0,0 +1,2 @@
- MISSING -
PLEASE DOWNLOAD ON AN OTHER PAGE!

View File

@@ -0,0 +1,430 @@
The JOGL source code is mostly licensed under the 'New BSD 2-Clause License',
however it contains other licensed material as well.
Other licensed material is compatible with the 'New BSD 2-Clause License',
if not stated otherwise.
'New BSD 2-Clause License' incompatible materials are optional, they are:
A.7) The JOGL source tree _may_ contain code from Oculus VR, Inc.
which is covered by it's own permissive Oculus VR Rift SDK Software License.
(Optional, see A.7 below for details)
Below you find a detailed list of licenses used in this project.
+++
The content of folder 'make/lib' contains build- and test-time only
Java binaries (JAR) to ease the build setup.
Each JAR file has it's corresponding LICENSE file containing the
source location and license text. None of these binaries are contained in any way
by the generated and deployed JOGL binaries.
+++
L.1) The JOGL source tree contains code from the JogAmp Community
which is covered by the Simplified BSD 2-clause license:
Copyright 2010 JogAmp Community. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of JogAmp Community.
You can address the JogAmp Community via:
Web http://jogamp.org/
Forum/Mailinglist http://forum.jogamp.org
Chatrooms
IRC irc.freenode.net #jogamp
Jabber conference.jabber.org room: jogamp (deprecated!)
Repository http://jogamp.org/git/
Email mediastream _at_ jogamp _dot_ org
L.2) The JOGL source tree contains code from Sun Microsystems, Inc.
which is covered by the New BSD 3-clause license:
Copyright (c) 2003-2009 Sun Microsystems, Inc. All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistribution of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistribution in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of Sun Microsystems, Inc. or the names of
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
This software is provided "AS IS," without a warranty of any kind. ALL
EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
You acknowledge that this software is not designed or intended for use
in the design, construction, operation or maintenance of any nuclear
facility.
L.3) The JOGL source tree contains code ported from the OpenGL sample
implementation by Silicon Graphics, Inc. This code is licensed under
the SGI Free Software License B, Version 2.0
License Applicability. Except to the extent portions of this file are
made subject to an alternative license as permitted in the SGI Free
Software License B, Version 2.0 (the "License"), the contents of this
file are subject only to the provisions of the License. You may not use
this file except in compliance with the License. You may obtain a copy
of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
http://oss.sgi.com/projects/FreeB
http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf
Or within this repository: doc/licenses/SGIFreeSWLicB.2.0.pdf
Note that, as provided in the License, the Software is distributed on an
"AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
L.4) The JOGL source tree contains code from the LWJGL project which is
similarly covered by the New BSD 3-clause license:
Copyright (c) 2002-2004 LWJGL Project
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of 'LWJGL' nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
L.5) The JOGL source tree also contains a Java port of Brian Paul's Tile
Rendering library, used with permission of the author under the
New BSD 3-clause license instead of the original LGPL:
Copyright (c) 1997-2005 Brian Paul. All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistribution of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistribution in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of Brian Paul or the names of contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
This software is provided "AS IS," without a warranty of any
kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
EXCLUDED. THE COPYRIGHT HOLDERS AND CONTRIBUTORS SHALL NOT BE
LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO
EVENT WILL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
INABILITY TO USE THIS SOFTWARE, EVEN IF THE COPYRIGHT HOLDERS OR
CONTRIBUTORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
A.1) The JOGL source tree also contains header files from Khronos,
reflecting OpenKODE, EGL, OpenGL ES1, OpenGL ES2 and OpenGL.
http://www.khronos.org/legal/license/
Files:
make/stub_includes/opengl/**
make/stub_includes/egl/**
make/stub_includes/khr/**
make/stub_includes/openmax/**
Copyright (c) 2007-2010 The Khronos Group Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and/or associated documentation files (the
"Materials"), to deal in the Materials without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Materials, and to
permit persons to whom the Materials are furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Materials.
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
A.2) The JOGL source tree contains code from The Apache Software Foundation
which is covered by the Apache License Version 2.0
Apache Harmony - Open Source Java SE
=====================================
<http://harmony.apache.org/>
Author: The Apache Software Foundation (http://www.apache.org/).
Copyright 2006, 2010 The Apache Software Foundation.
Apache License Version 2.0, January 2004
http://www.apache.org/licenses/LICENSE-2.0
Or within this repository: doc/licenses/Apache.LICENSE-2.0
Files:
src/jogamp/graph/geom/plane/AffineTransform.java
src/jogamp/graph/geom/plane/IllegalPathStateException.java
src/jogamp/graph/geom/plane/NoninvertibleTransformException.java
src/jogamp/graph/geom/plane/PathIterator.java
src/jogamp/graph/geom/plane/Path2D.java
src/jogamp/graph/math/plane/Crossing.java
src/org/apache/harmony/misc/HashCode.java
A.3) The JOGL source tree contains code from David Schweinsberg
which is covered by the Apache License Version 1.1 and Version 2.0
Typecast
========
Typecast is a font development environment for OpenType font technology.
<https://java.net/projects/typecast>
Author: David Schweinsberg
Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
Apache Licenses
http://www.apache.org/licenses/
Apache License Version 1.1
http://www.apache.org/licenses/LICENSE-1.1
Or within this repository: doc/licenses/Apache.LICENSE-1.1
Files:
src/jogl/classes/jogamp/graph/font/typecast/ot/*
src/jogl/classes/jogamp/graph/font/typecast/ot/table/*
Apache License Version 2.0
http://www.apache.org/licenses/LICENSE-2.0
Or within this repository: doc/licenses/Apache.LICENSE-2.0
src/jogl/classes/jogamp/graph/font/typecast/ot/*
src/jogl/classes/jogamp/graph/font/typecast/ot/mac/*
src/jogl/classes/jogamp/graph/font/typecast/ot/table/*
src/jogl/classes/jogamp/graph/font/typecast/tt/engine/*
A.4) The JOGL source tree contains fonts from Ubuntu
which is covered by the UBUNTU FONT LICENCE Version 1.0
Ubuntu Font Family
==================
The Ubuntu Font Family are libre fonts funded by Canonical Ltd on behalf of the Ubuntu project.
<http://font.ubuntu.com/>
Copyright 2010 Canonical Ltd.
Licensed under the Ubuntu Font Licence 1.0
Author: Canonical Ltd., Dalton Maag
UBUNTU FONT LICENCE
Version 1.0
http://font.ubuntu.com/ufl/ubuntu-font-licence-1.0.txt
Or within this repository: doc/licenses/ubuntu-font-licence-1.0.txt
Files:
src/jogamp/graph/font/fonts/ubuntu/*
A.5) The JOGL source tree also contains header files from NVIDIA,
reflecting Cg.
Files:
make/stub_includes/cg/CG/**
Copyright (c) 2002, NVIDIA Corporation
NVIDIA Corporation("NVIDIA") supplies this software to you in consideration
of your agreement to the following terms, and your use, installation,
modification or redistribution of this NVIDIA software constitutes
acceptance of these terms. If you do not agree with these terms, please do
not use, install, modify or redistribute this NVIDIA software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, NVIDIA grants you a personal, non-exclusive license,
under NVIDIA's copyrights in this original NVIDIA software (the "NVIDIA
Software"), to use, reproduce, modify and redistribute the NVIDIA
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the NVIDIA Software, you must retain the
copyright notice of NVIDIA, this notice and the following text and
disclaimers in all such redistributions of the NVIDIA Software. Neither the
name, trademarks, service marks nor logos of NVIDIA Corporation may be used
to endorse or promote products derived from the NVIDIA Software without
specific prior written permission from NVIDIA. Except as expressly stated
in this notice, no other rights or licenses express or implied, are granted
by NVIDIA herein, including but not limited to any patent rights that may be
infringed by your derivative works or by other works in which the NVIDIA
Software may be incorporated. No hardware is licensed hereunder.
THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR ITS USE AND OPERATION
EITHER ALONE OR IN COMBINATION WITH OTHER PRODUCTS.
IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL,
EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, LOST
PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY OUT OF THE USE,
REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE NVIDIA SOFTWARE,
HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF NVIDIA HAS BEEN ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
A.6) The JOGL source tree contains code from Hernan J. Gonzalez and Shawn Hartsock
which is covered by the Apache License Version 2.0
PNGJ
====
PNGJ: Java library for reading and writing PNG images.
Version 1.12 (3 Dec 2012)
<http://code.google.com/p/pngj/>
Author: Hernan J. Gonzalez and Shawn Hartsock
Copyright (C) 2004 The Apache Software Foundation. All rights reserved.
Apache Licenses
http://www.apache.org/licenses/
Apache License Version 2.0
http://www.apache.org/licenses/LICENSE-2.0
Or within this repository: doc/licenses/Apache.LICENSE-2.0
src/jogl/classes/jogamp/opengl/util/pngj/**
A.7) The JOGL source tree _may_ contain code from Oculus VR, Inc.
which is covered by it's own permissive Oculus VR Rift SDK Software License.
This code _can_ be included to produce a binding
and hence support for the Oculus VR Rift.
The code is included _and_ it's build artifacts will be released,
if the git sub-module oculusvr-sdk is included in the jogl source repository
as true for current official JogAmp builds and releases!
If using JogAmp JOGL builds with oculusvr-sdk support,
but the user prefers to _not_ use it for license or other reasons,
the user can simply remove the artifacts 'jar/atomics/oculusvr*jar'.
No other produced artifact is affected.
While the Oculus VR Rift SDK Software License is permissive,
it's differences to the New BSD license shall be mentioned, see below!
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Copyright © 2014 Oculus VR, Inc. All rights reserved.
Oculus VR, Inc. Software Development Kit License Agreement
Human-Readable Summary:
- You are Free to:
- Use, modify, and distribute the Oculus VR Rift SDK in source and binary
form with your applications/software.
- With the Following Restrictions:
- You can only distribute or re-distribute the source code to LibOVR in
whole, not in part.
- Modifications to the Oculus VR Rift SDK in source or binary form must
be shared with Oculus VR.
- If your applications cause health and safety issues, you may lose your
right to use the Oculus VR Rift SDK, including LibOVR.
- The Oculus VR Rift SDK may not be used to interface with unapproved commercial
virtual reality mobile or non-mobile products or hardware.
- This human-readable Summary is not a license. It is simply a convenient
reference for understanding the full Oculus VR Rift SDK License Agreement.
The Summary is written as a user-friendly interface to the full Oculus VR Rift
SDK License below. This Summary itself has no legal value, and its contents do
not appear in the actual license.
Full-length Legal Copy may be found at:
http://www.oculusvr.com/licenses/LICENSE-3.1
http://jogamp.org/git/?p=oculusvr-sdk.git;a=blob;f=LICENSE.txt;hb=HEAD
Or within this repository: oculusvr-sdk/LICENSE.txt

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.web.*?>
<!--
~ Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
~
~ You are unauthorized to remove this copyright.
~ You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
~ See LICENSE-File if exists
-->
<AnchorPane prefHeight="580.0" prefWidth="922.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.openautonomousconnection.browser.controller.Browser">
<children>
<Button fx:id="btnHome" mnemonicParsing="false" onAction="#onHomeClick" prefHeight="45.0" prefWidth="105.0" text="Home" AnchorPane.leftAnchor="15.0" AnchorPane.topAnchor="15.0" />
<Button fx:id="btnReload" mnemonicParsing="false" onAction="#onReloadClick" prefHeight="45.0" prefWidth="105.0" text="Reload" AnchorPane.leftAnchor="125.0" AnchorPane.topAnchor="15.0" />
<Button fx:id="btnGo" mnemonicParsing="false" onAction="#onGoClick" prefHeight="45.0" prefWidth="105.0" text="Go" AnchorPane.rightAnchor="230.0" AnchorPane.topAnchor="15.0" />
<Button fx:id="btnSettings" layoutX="707.0" layoutY="15.0" mnemonicParsing="false" onAction="#onSettingsClick" prefHeight="45.0" prefWidth="105.0" text="Settings" AnchorPane.rightAnchor="15.0" AnchorPane.topAnchor="15.0" />
<TextField fx:id="domainInput" prefHeight="45.0" promptText="oac://" text="oac://browser-start.root/" AnchorPane.leftAnchor="235.0" AnchorPane.rightAnchor="345.0" AnchorPane.topAnchor="15.0" />
<WebView fx:id="webView" layoutY="68.0" prefHeight="502.0" prefWidth="730.0" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="15.0" AnchorPane.rightAnchor="15.0" AnchorPane.topAnchor="68.0" />
</children>
</AnchorPane>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<!--
~ Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
~
~ You are unauthorized to remove this copyright.
~ You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
~ See LICENSE-File if exists
-->
<AnchorPane maxHeight="340.0" maxWidth="600.0" prefHeight="340.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.openautonomousconnection.browser.MessageDialog">
<children>
<Label alignment="CENTER" contentDisplay="CENTER" layoutX="90.0" layoutY="15.0" prefHeight="65.0" prefWidth="420.0" text="IMPORTANT MESSAGE" textAlignment="CENTER" textFill="RED">
<font>
<Font size="25.0" />
</font>
</Label>
<Label fx:id="txtServer" alignment="TOP_LEFT" layoutX="90.0" layoutY="80.0" prefHeight="230.0" prefWidth="420.0" text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Non diam phasellus vestibulum lorem sed. Vulputate sapien nec sagittis aliquam malesuada bibendum. Purus viverra accumsan in nisl. Arcu ac tortor dignissim convallis. Mattis vulputate enim nulla aliquet porttitor lacus luctus accumsan. Risus nec feugiat in fermentum posuere urna. Tristique et egestas quis ipsum suspendisse ultrices. Id consectetur purus ut faucibus pulvinar elementum. Nisi quis eleifend quam adipiscing vitae. Sagittis purus sit amet volutpat consequat mauris." wrapText="true">
<font>
<Font size="15.0" />
</font>
</Label>
</children>
</AnchorPane>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<!--
~ Copyright (C) 2024 Open Autonomous Connection - All Rights Reserved
~
~ You are unauthorized to remove this copyright.
~ You have to give Credits to the Author in your project and link this GitHub site: https://github.com/Open-Autonomous-Connection
~ See LICENSE-File if exists
-->
<AnchorPane prefHeight="255.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.openautonomousconnection.browser.controller.Settings">
<children>
<Label alignment="CENTER" contentDisplay="CENTER" layoutX="50.0" layoutY="15.0" prefHeight="55.0" prefWidth="260.0" text="DNS Settings" textAlignment="CENTER">
<font>
<Font size="25.0" />
</font>
</Label>
<Label layoutX="15.0" layoutY="85.0" prefHeight="25.0" prefWidth="35.0" text="Host">
<font>
<Font size="15.0" />
</font>
</Label>
<Label layoutX="15.0" layoutY="115.0" prefHeight="25.0" prefWidth="35.0" text="Port">
<font>
<Font size="15.0" />
</font>
</Label>
<TextField fx:id="hostInput" layoutX="95.0" layoutY="85.0" prefHeight="25.0" prefWidth="170.0" promptText="Host" text="82.197.95.202" />
<TextField fx:id="portInput" layoutX="95.0" layoutY="115.0" prefHeight="25.0" prefWidth="170.0" promptText="Port" text="9382" />
<Button fx:id="btnSave" layoutX="250.0" layoutY="180.0" mnemonicParsing="false" onAction="#onSaveClick" prefHeight="55.0" prefWidth="100.0" text="Save" />
<Label alignment="CENTER" contentDisplay="CENTER" layoutX="325.0" layoutY="15.0" prefHeight="55.0" prefWidth="260.0" text="API Settings" textAlignment="CENTER">
<font>
<Font size="25.0" />
</font>
</Label>
<Label layoutX="290.0" layoutY="85.0" prefHeight="25.0" prefWidth="100.0" text="Username">
<font>
<Font size="15.0" />
</font>
</Label>
<Label layoutX="290.0" layoutY="115.0" prefHeight="25.0" prefWidth="100.0" text="Application">
<font>
<Font size="15.0" />
</font>
</Label>
<TextField fx:id="usernameInput" editable="false" layoutX="390.0" layoutY="85.0" prefHeight="25.0" prefWidth="170.0" promptText="Username" />
<TextField fx:id="applicationInput" editable="false" layoutX="390.0" layoutY="115.0" prefHeight="25.0" prefWidth="170.0" promptText="Application" />
<Label layoutX="290.0" layoutY="145.0" prefHeight="25.0" prefWidth="100.0" text="API Key">
<font>
<Font size="15.0" />
</font>
</Label>
<TextField fx:id="apiKeyInput" editable="false" layoutX="390.0" layoutY="145.0" prefHeight="25.0" prefWidth="170.0" promptText="API Key" />
</children>
</AnchorPane>

View File

@@ -0,0 +1 @@
1.0