Fx ready
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>org.openautonomousconnection</groupId>
|
||||
<artifactId>LuaScript</artifactId>
|
||||
<version>1.0.0-BETA.1.4</version>
|
||||
<version>1.0.0-BETA.1.5</version>
|
||||
|
||||
<organization>
|
||||
<name>Open Autonomous Connection</name>
|
||||
|
||||
@@ -1,30 +1,41 @@
|
||||
package org.openautonomousconnection.luascript.hosts;
|
||||
|
||||
/**
|
||||
* Host capability for console logging.
|
||||
* Abstraction for script console output.
|
||||
*/
|
||||
public interface ConsoleHost {
|
||||
|
||||
/**
|
||||
* Info log.
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void info(String message);
|
||||
|
||||
/**
|
||||
* Standard log.
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void log(String message);
|
||||
|
||||
/**
|
||||
* Warning log.
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void warn(String message);
|
||||
|
||||
/**
|
||||
* Error-like stacktrace print.
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void error(String message);
|
||||
|
||||
/**
|
||||
* Exception-like print.
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void exception(String message);
|
||||
|
||||
@@ -4,79 +4,79 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Host capability that exposes a DOM-like API.
|
||||
* Abstraction over DOM access for scripting.
|
||||
*
|
||||
* <p>Element identity is the (stable) element id.</p>
|
||||
* <p>All element references are by stable element {@code id}.</p>
|
||||
*/
|
||||
public interface DomHost {
|
||||
|
||||
/**
|
||||
* Returns all element ids known to the renderer (must be stable).
|
||||
* Returns a list of all element ids in the document.
|
||||
*
|
||||
* @return list of element ids
|
||||
* @return list of ids (never null)
|
||||
*/
|
||||
List<String> getAllElementIds();
|
||||
|
||||
/**
|
||||
* Returns all attributes for the given element id.
|
||||
* Returns all attributes of the element.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @return attributes map (attributeName -> attributeValue)
|
||||
* @return attributes map (never null)
|
||||
*/
|
||||
Map<String, String> getAttributes(String elementId);
|
||||
|
||||
/**
|
||||
* Returns the tag name of the element (lowercase recommended), e.g. "script", "button".
|
||||
* Returns the element tag name, lowercase if possible.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @return tag name
|
||||
* @return tag name (never null)
|
||||
*/
|
||||
String getTagName(String elementId);
|
||||
|
||||
/**
|
||||
* Returns the text content of an element
|
||||
* Returns the element text content.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @return text content (never null)
|
||||
* @return text (never null)
|
||||
*/
|
||||
String getTextContent(String elementId);
|
||||
|
||||
/**
|
||||
* Sets the text content of an element.
|
||||
* Sets the element text content.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param text text
|
||||
* @param text new text
|
||||
*/
|
||||
void setTextContent(String elementId, String text);
|
||||
|
||||
/**
|
||||
* Gets a single attribute or null if missing.
|
||||
* Returns an attribute value or null if missing.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param name attribute name
|
||||
* @return value or null
|
||||
* @param name attribute name
|
||||
* @return value or null if missing
|
||||
*/
|
||||
String getAttribute(String elementId, String name);
|
||||
|
||||
/**
|
||||
* Sets an attribute (creates it if missing).
|
||||
* Sets an attribute value (empty string allowed).
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param name attribute name
|
||||
* @param value attribute value
|
||||
* @param name attribute name
|
||||
* @param value attribute value
|
||||
*/
|
||||
void setAttribute(String elementId, String name, String value);
|
||||
|
||||
/**
|
||||
* Removes an attribute.
|
||||
* Removes an attribute from the element.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param name attribute name
|
||||
* @param name attribute name
|
||||
*/
|
||||
void removeAttribute(String elementId, String name);
|
||||
|
||||
/**
|
||||
* Returns parent id or null.
|
||||
* Returns the parent element id or null if none.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @return parent id or null
|
||||
@@ -84,48 +84,48 @@ public interface DomHost {
|
||||
String getParentId(String elementId);
|
||||
|
||||
/**
|
||||
* Returns direct children ids.
|
||||
* Returns children element ids.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @return children ids
|
||||
* @return list of children ids (never null)
|
||||
*/
|
||||
List<String> getChildrenIds(String elementId);
|
||||
|
||||
/**
|
||||
* Creates a new element (detached) and returns its id.
|
||||
* Creates an element and makes it addressable immediately.
|
||||
*
|
||||
* @param tagName tag name
|
||||
* @param requestedId optional requested id, may be null/blank for auto id
|
||||
* @param tagName tag name
|
||||
* @param requestedId requested id or null
|
||||
* @return created element id
|
||||
*/
|
||||
String createElement(String tagName, String requestedId);
|
||||
|
||||
/**
|
||||
* Removes an element from the DOM.
|
||||
* Removes an element from the document.
|
||||
*
|
||||
* @param elementId element id
|
||||
*/
|
||||
void removeElement(String elementId);
|
||||
|
||||
/**
|
||||
* Moves/appends child under parent.
|
||||
* Appends a child element to a parent.
|
||||
*
|
||||
* @param parentId parent id
|
||||
* @param childId child id
|
||||
* @param childId child id
|
||||
*/
|
||||
void appendChild(String parentId, String childId);
|
||||
|
||||
/**
|
||||
* Inserts child before an existing direct child.
|
||||
* Inserts {@code childId} before {@code beforeChildId} within {@code parentId}.
|
||||
*
|
||||
* @param parentId parent id
|
||||
* @param childId child id
|
||||
* @param parentId parent id
|
||||
* @param childId child id
|
||||
* @param beforeChildId existing child id
|
||||
*/
|
||||
void insertBefore(String parentId, String childId, String beforeChildId);
|
||||
|
||||
/**
|
||||
* Checks if an element id exists.
|
||||
* Checks if an element with the given id exists.
|
||||
*
|
||||
* @param id element id
|
||||
* @return true if exists
|
||||
@@ -133,18 +133,18 @@ public interface DomHost {
|
||||
boolean exists(String id);
|
||||
|
||||
/**
|
||||
* Returns element ids by tag.
|
||||
* Queries elements by tag name and returns their ids.
|
||||
*
|
||||
* @param tagName tag
|
||||
* @return ids
|
||||
* @param tagName tag name
|
||||
* @return list of ids (never null)
|
||||
*/
|
||||
List<String> queryByTag(String tagName);
|
||||
|
||||
/**
|
||||
* Returns element ids by class.
|
||||
* Queries elements by class token and returns their ids.
|
||||
*
|
||||
* @param className class
|
||||
* @return ids
|
||||
* @param className class name token
|
||||
* @return list of ids (never null)
|
||||
*/
|
||||
List<String> queryByClass(String className);
|
||||
}
|
||||
@@ -1,37 +1,41 @@
|
||||
package org.openautonomousconnection.luascript.hosts;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Event subscription abstraction (implemented by the client UI layer).
|
||||
* Abstraction over DOM event subscription for scripting.
|
||||
*
|
||||
* <p>Implementations forward DOM events into a {@code LuaEventRouter}.</p>
|
||||
*/
|
||||
public interface EventHost {
|
||||
|
||||
/**
|
||||
* Subscribes to an element event.
|
||||
* Adds a listener for an element's event.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param eventName event name (e.g. click)
|
||||
* @param eventName normalized event name
|
||||
*/
|
||||
void addListener(String elementId, String eventName);
|
||||
|
||||
/**
|
||||
* Unsubscribes from an element event.
|
||||
* Removes a listener for an element's event.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param eventName event name
|
||||
* @param eventName normalized event name
|
||||
*/
|
||||
void removeListener(String elementId, String eventName);
|
||||
|
||||
/**
|
||||
* Subscribes to a global event (app/window scope).
|
||||
* Adds a global (document-level) listener.
|
||||
*
|
||||
* @param eventName event name
|
||||
* @param eventName normalized event name
|
||||
*/
|
||||
void addGlobalListener(String eventName);
|
||||
|
||||
/**
|
||||
* Unsubscribes from a global event.
|
||||
* Removes a global (document-level) listener.
|
||||
*
|
||||
* @param eventName event name
|
||||
* @param eventName normalized event name
|
||||
*/
|
||||
void removeGlobalListener(String eventName);
|
||||
}
|
||||
@@ -1,165 +1,145 @@
|
||||
package org.openautonomousconnection.luascript.hosts;
|
||||
|
||||
import javafx.scene.web.WebEngine;
|
||||
import org.openautonomousconnection.luascript.fx.FxDomHost;
|
||||
import org.openautonomousconnection.luascript.fx.FxEventHost;
|
||||
import org.openautonomousconnection.luascript.fx.FxUiHost;
|
||||
import org.openautonomousconnection.luascript.fx.FxWebViewResourceHost;
|
||||
import org.openautonomousconnection.luascript.runtime.LuaRuntime;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Service container holding optional host capabilities.
|
||||
* Container for host-side services exposed to Lua.
|
||||
*
|
||||
* <p>This avoids one huge "bridge" interface.</p>
|
||||
* <p>All services are optional to allow different embedding scenarios.</p>
|
||||
*/
|
||||
public final class HostServices {
|
||||
|
||||
private final ConsoleHost console;
|
||||
private final DomHost dom;
|
||||
private final EventHost events;
|
||||
private final ResourceHost resources;
|
||||
private final UiHost ui;
|
||||
|
||||
private HostServices(Builder b) {
|
||||
this.console = b.console;
|
||||
this.ui = b.ui;
|
||||
this.dom = b.dom;
|
||||
this.events = b.events;
|
||||
this.resources = b.resources;
|
||||
}
|
||||
public interface HostServices {
|
||||
|
||||
/**
|
||||
* Builds a JavaFX WebView preset of HostServices (DOM + events + resources + UI).
|
||||
* Returns an optional UI host.
|
||||
*
|
||||
* <p>Important: This method creates the {@link EventHost} using the provided {@link LuaRuntime}'s
|
||||
* {@link org.openautonomousconnection.luascript.runtime.LuaEventRouter}. Therefore, the runtime must already
|
||||
* be constructed before calling this method.</p>
|
||||
* @return ui host
|
||||
*/
|
||||
Optional<UiHost> ui();
|
||||
|
||||
/**
|
||||
* Returns an optional DOM host.
|
||||
*
|
||||
* <p>Note: You should call {@link FxDomHost#ensureAllElementsHaveId()} after the WebEngine finished loading.</p>
|
||||
* @return dom host
|
||||
*/
|
||||
Optional<DomHost> dom();
|
||||
|
||||
/**
|
||||
* Returns an optional event host.
|
||||
*
|
||||
* @param engine JavaFX WebEngine
|
||||
* @param runtime Lua runtime used for event routing
|
||||
* @param console optional console host (may be null)
|
||||
* @return HostServices preset for JavaFX WebView
|
||||
* @return event host
|
||||
*/
|
||||
public static HostServices fxPreset(WebEngine engine, LuaRuntime runtime, ConsoleHost console) {
|
||||
Objects.requireNonNull(engine, "engine");
|
||||
Objects.requireNonNull(runtime, "runtime");
|
||||
|
||||
FxDomHost dom = new FxDomHost(engine);
|
||||
FxWebViewResourceHost resources = new FxWebViewResourceHost(engine);
|
||||
FxUiHost ui = new FxUiHost(engine, dom);
|
||||
FxEventHost events = new FxEventHost(dom, runtime.eventRouter());
|
||||
|
||||
Builder b = builder().dom(dom).events(events).resources(resources).ui(ui);
|
||||
|
||||
if (console != null) {
|
||||
b.console(console);
|
||||
}
|
||||
|
||||
return b.build();
|
||||
}
|
||||
Optional<EventHost> events();
|
||||
|
||||
/**
|
||||
* @return builder
|
||||
* Returns an optional resource host.
|
||||
*
|
||||
* @return resource host
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
Optional<ResourceHost> resources();
|
||||
|
||||
/**
|
||||
* @return optional DomHost capability
|
||||
* Returns an optional console host.
|
||||
*
|
||||
* @return console host
|
||||
*/
|
||||
public Optional<DomHost> dom() {
|
||||
return Optional.ofNullable(dom);
|
||||
}
|
||||
Optional<ConsoleHost> console();
|
||||
|
||||
/**
|
||||
* @return optional EventHost capability
|
||||
* Simple immutable implementation.
|
||||
*/
|
||||
public Optional<EventHost> events() {
|
||||
return Optional.ofNullable(events);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return optional ResourceHost capability
|
||||
*/
|
||||
public Optional<ResourceHost> resources() {
|
||||
return Optional.ofNullable(resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return optional console host
|
||||
*/
|
||||
public Optional<ConsoleHost> console() {
|
||||
return Optional.ofNullable(console);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return optional ui host
|
||||
*/
|
||||
public Optional<UiHost> ui() {
|
||||
return Optional.ofNullable(ui);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for HostServices.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private ConsoleHost console;
|
||||
private DomHost dom;
|
||||
private EventHost events;
|
||||
private ResourceHost resources;
|
||||
private UiHost ui;
|
||||
|
||||
public Builder console(ConsoleHost console) {
|
||||
this.console = Objects.requireNonNull(console, "console");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder ui(UiHost ui) {
|
||||
this.ui = Objects.requireNonNull(ui, "ui");
|
||||
return this;
|
||||
}
|
||||
final class Default implements HostServices {
|
||||
private final UiHost ui;
|
||||
private final DomHost dom;
|
||||
private final EventHost events;
|
||||
private final ResourceHost resources;
|
||||
private final ConsoleHost console;
|
||||
|
||||
/**
|
||||
* Provides dom capability.
|
||||
* Creates a HostServices container.
|
||||
*
|
||||
* @param ui ui host
|
||||
* @param dom dom host
|
||||
* @return this
|
||||
*/
|
||||
public Builder dom(DomHost dom) {
|
||||
this.dom = Objects.requireNonNull(dom, "dom");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides event subscription capability.
|
||||
*
|
||||
* @param events event host
|
||||
* @return this
|
||||
* @param resources resource host
|
||||
* @param console console host
|
||||
*/
|
||||
public Builder events(EventHost events) {
|
||||
this.events = Objects.requireNonNull(events, "events");
|
||||
return this;
|
||||
public Default(UiHost ui, DomHost dom, EventHost events, ResourceHost resources, ConsoleHost console) {
|
||||
this.ui = ui;
|
||||
this.dom = dom;
|
||||
this.events = events;
|
||||
this.resources = resources;
|
||||
this.console = console;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UiHost> ui() {
|
||||
return Optional.ofNullable(ui);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DomHost> dom() {
|
||||
return Optional.ofNullable(dom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<EventHost> events() {
|
||||
return Optional.ofNullable(events);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ResourceHost> resources() {
|
||||
return Optional.ofNullable(resources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ConsoleHost> console() {
|
||||
return Optional.ofNullable(console);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stdout-based console host.
|
||||
*/
|
||||
final class StdoutConsole implements ConsoleHost {
|
||||
private final String prefix;
|
||||
|
||||
/**
|
||||
* Provides resource loading capability.
|
||||
* Creates a new stdout console with a prefix.
|
||||
*
|
||||
* @param resources resource host
|
||||
* @return this
|
||||
* @param prefix prefix (may be empty)
|
||||
*/
|
||||
public Builder resources(ResourceHost resources) {
|
||||
this.resources = Objects.requireNonNull(resources, "resources");
|
||||
return this;
|
||||
public StdoutConsole(String prefix) {
|
||||
this.prefix = Objects.requireNonNull(prefix, "prefix");
|
||||
}
|
||||
|
||||
public HostServices build() {
|
||||
return new HostServices(this);
|
||||
@Override
|
||||
public void info(String message) {
|
||||
System.out.println(prefix + "[info] " + safe(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String message) {
|
||||
System.out.println(prefix + "[log] " + safe(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String message) {
|
||||
System.out.println(prefix + "[warn] " + safe(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message) {
|
||||
System.err.println(prefix + "[error] " + safe(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exception(String message) {
|
||||
System.err.println(prefix + "[exception] " + safe(message));
|
||||
}
|
||||
|
||||
private static String safe(String s) {
|
||||
return s == null ? "" : s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package org.openautonomousconnection.luascript.hosts;
|
||||
|
||||
/**
|
||||
* Resource loading abstraction for LuaScript (e.g. script src).
|
||||
* Abstraction for loading external resources (e.g. {@code <script src="...">}).
|
||||
*/
|
||||
public interface ResourceHost {
|
||||
|
||||
/**
|
||||
* Reads text from a script source (file/url/virtual path).
|
||||
* Reads a UTF-8 text resource.
|
||||
*
|
||||
* @param src source identifier
|
||||
* @param src resource location (absolute or relative)
|
||||
* @return text content
|
||||
* @throws Exception on load failures
|
||||
* @throws Exception on IO or resolution error
|
||||
*/
|
||||
String readText(String src) throws Exception;
|
||||
}
|
||||
@@ -1,59 +1,215 @@
|
||||
package org.openautonomousconnection.luascript.hosts;
|
||||
|
||||
/**
|
||||
* Host capability for UI operations.
|
||||
* Abstraction for UI operations exposed to scripts.
|
||||
*/
|
||||
public interface UiHost {
|
||||
|
||||
/**
|
||||
* Displays an alert-like message.
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void alert(String message);
|
||||
|
||||
/**
|
||||
* Displays a confirm-like prompt.
|
||||
*
|
||||
* @param message message
|
||||
* @return true if accepted
|
||||
*/
|
||||
boolean confirm(String message);
|
||||
|
||||
/**
|
||||
* Displays a prompt-like query.
|
||||
*
|
||||
* @param message message
|
||||
* @param defaultValue default value
|
||||
* @return user response or default
|
||||
*/
|
||||
String prompt(String message, String defaultValue);
|
||||
|
||||
/**
|
||||
* Sets element text.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param text text
|
||||
*/
|
||||
void setText(String elementId, String text);
|
||||
|
||||
/**
|
||||
* Gets element text.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @return text (never null)
|
||||
*/
|
||||
String getText(String elementId);
|
||||
|
||||
/**
|
||||
* Sets element HTML (best-effort for non-JS hosts).
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param html html
|
||||
*/
|
||||
void setHtml(String elementId, String html);
|
||||
|
||||
/**
|
||||
* Gets element HTML (best-effort for non-JS hosts).
|
||||
*
|
||||
* @param elementId element id
|
||||
* @return html (never null)
|
||||
*/
|
||||
String getHtml(String elementId);
|
||||
|
||||
/**
|
||||
* Sets a form-like value.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param value value
|
||||
*/
|
||||
void setValue(String elementId, String value);
|
||||
|
||||
/**
|
||||
* Gets a form-like value.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @return value (never null)
|
||||
*/
|
||||
String getValue(String elementId);
|
||||
|
||||
/**
|
||||
* Enables/disables an element.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param enabled enabled
|
||||
*/
|
||||
void setEnabled(String elementId, boolean enabled);
|
||||
|
||||
/**
|
||||
* Shows/hides an element.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param visible visible
|
||||
*/
|
||||
void setVisible(String elementId, boolean visible);
|
||||
|
||||
/**
|
||||
* Adds a class token.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param className class token
|
||||
*/
|
||||
void addClass(String elementId, String className);
|
||||
|
||||
/**
|
||||
* Removes a class token.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param className class token
|
||||
*/
|
||||
void removeClass(String elementId, String className);
|
||||
|
||||
/**
|
||||
* Toggles a class token.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param className class token
|
||||
* @return true if class is present after toggle
|
||||
*/
|
||||
boolean toggleClass(String elementId, String className);
|
||||
|
||||
/**
|
||||
* Checks whether a class token is present.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param className class token
|
||||
* @return true if present
|
||||
*/
|
||||
boolean hasClass(String elementId, String className);
|
||||
|
||||
/**
|
||||
* Sets a CSS property via style attribute (best-effort).
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param property css property
|
||||
* @param value css value
|
||||
*/
|
||||
void setStyle(String elementId, String property, String value);
|
||||
|
||||
/**
|
||||
* Gets a CSS property via style attribute (best-effort).
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param property css property
|
||||
* @return css value or empty string
|
||||
*/
|
||||
String getStyle(String elementId, String property);
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param name attribute name
|
||||
* @param value value
|
||||
*/
|
||||
void setAttribute(String elementId, String name, String value);
|
||||
|
||||
/**
|
||||
* Gets an attribute value or null if missing.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param name attribute name
|
||||
* @return value or null
|
||||
*/
|
||||
String getAttribute(String elementId, String name);
|
||||
|
||||
/**
|
||||
* Removes an attribute.
|
||||
*
|
||||
* @param elementId element id
|
||||
* @param name attribute name
|
||||
*/
|
||||
void removeAttribute(String elementId, String name);
|
||||
|
||||
/**
|
||||
* Focuses an element (best-effort).
|
||||
*
|
||||
* @param elementId element id
|
||||
*/
|
||||
void focus(String elementId);
|
||||
|
||||
/**
|
||||
* Blurs an element (best-effort).
|
||||
*
|
||||
* @param elementId element id
|
||||
*/
|
||||
void blur(String elementId);
|
||||
|
||||
/**
|
||||
* Scrolls an element into view (best-effort).
|
||||
*
|
||||
* @param elementId element id
|
||||
*/
|
||||
void scrollIntoView(String elementId);
|
||||
|
||||
/**
|
||||
* Returns viewport width or -1 if unknown.
|
||||
*
|
||||
* @return width
|
||||
*/
|
||||
int viewportWidth();
|
||||
|
||||
/**
|
||||
* Returns viewport height or -1 if unknown.
|
||||
*
|
||||
* @return height
|
||||
*/
|
||||
int viewportHeight();
|
||||
|
||||
/**
|
||||
* Returns current host time in milliseconds.
|
||||
*
|
||||
* @return millis
|
||||
*/
|
||||
long nowMillis();
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package org.openautonomousconnection.luascript.runtime;
|
||||
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.scene.web.WebEngine;
|
||||
import org.luaj.vm2.Globals;
|
||||
import org.openautonomousconnection.luascript.fx.FxDomHost;
|
||||
import org.openautonomousconnection.luascript.fx.FxEventHost;
|
||||
import org.openautonomousconnection.luascript.fx.FxUiHost;
|
||||
import org.openautonomousconnection.luascript.fx.FxWebViewResourceHost;
|
||||
import org.openautonomousconnection.luascript.hosts.HostServices;
|
||||
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
|
||||
import org.openautonomousconnection.luascript.utils.LuaGlobalsFactory;
|
||||
|
||||
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 FxLuaScriptEngine implements AutoCloseable {
|
||||
|
||||
private final WebEngine engine;
|
||||
private final LuaExecutionPolicy policy;
|
||||
|
||||
private final AtomicBoolean bootstrapped = new AtomicBoolean(false);
|
||||
|
||||
private LuaRuntime runtime;
|
||||
|
||||
/**
|
||||
* Creates an integration engine with default UI execution policy.
|
||||
*
|
||||
* @param engine web engine
|
||||
*/
|
||||
public FxLuaScriptEngine(WebEngine engine) {
|
||||
this(engine, LuaExecutionPolicy.uiDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an integration engine with a custom execution policy.
|
||||
*
|
||||
* @param engine web engine
|
||||
* @param policy execution policy
|
||||
*/
|
||||
public FxLuaScriptEngine(WebEngine engine, LuaExecutionPolicy policy) {
|
||||
this.engine = Objects.requireNonNull(engine, "engine");
|
||||
this.policy = Objects.requireNonNull(policy, "policy");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
// DOM host must exist before event/UI tables, and must ensure stable ids.
|
||||
FxDomHost dom = new FxDomHost(engine);
|
||||
dom.ensureAllElementsHaveId();
|
||||
|
||||
// Create per-page globals; harden sandbox in production.
|
||||
Globals globals = LuaGlobalsFactory.create(
|
||||
new LuaGlobalsFactory.Options()
|
||||
.enableDebug(false)
|
||||
.sandbox(true)
|
||||
);
|
||||
|
||||
// Create runtime first (router lives inside it).
|
||||
HostServices.StdoutConsole console = new HostServices.StdoutConsole("[lua] ");
|
||||
FxUiHost uiHost = new FxUiHost(engine, dom);
|
||||
FxWebViewResourceHost resourceHost = new FxWebViewResourceHost(engine);
|
||||
|
||||
// runtime depends on services; events depends on runtime router.
|
||||
// We'll create eventHost after runtime, then build HostServices with it.
|
||||
LuaRuntime rt = new LuaRuntime(globals, new HostServices.Default(uiHost, dom, null, resourceHost, console), policy);
|
||||
|
||||
FxEventHost eventHost = new FxEventHost(dom, rt.eventRouter());
|
||||
|
||||
// Rebuild services including eventHost and reinstall tables.
|
||||
HostServices services = new HostServices.Default(uiHost, dom, eventHost, resourceHost, console);
|
||||
|
||||
// Replace runtime with correct services (clean and deterministic).
|
||||
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.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,10 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Bootstrap that:
|
||||
* 1) Executes ALL scripts found in DOM as Lua: <script> ... or <script src="...">
|
||||
* 1) Executes ALL scripts found in DOM as Lua: <script> ... or <script src="...">
|
||||
* 2) Scans DOM for on:* handlers and binds them automatically.
|
||||
*
|
||||
* <p>Hard client rule: JavaScript is not executed. Every HTML script tag is treated as Lua.</p>
|
||||
*/
|
||||
public final class LuaScriptBootstrap {
|
||||
|
||||
@@ -36,11 +38,11 @@ public final class LuaScriptBootstrap {
|
||||
EventHost eventHost = services.events().orElseThrow(() -> new IllegalStateException("EventHost not provided"));
|
||||
ResourceHost resources = services.resources().orElseThrow(() -> new IllegalStateException("ResourceHost not provided"));
|
||||
|
||||
executeAllScripts(globals, dom, resources);
|
||||
executeAllScriptsAsLua(globals, dom, resources);
|
||||
new LuaDomBinder(dom, eventHost, dispatcher).bindAll();
|
||||
}
|
||||
|
||||
private static void executeAllScripts(Globals globals, DomHost dom, ResourceHost resources) {
|
||||
private static void executeAllScriptsAsLua(Globals globals, DomHost dom, ResourceHost resources) {
|
||||
for (String elementId : dom.getAllElementIds()) {
|
||||
String tag = safeLower(dom.getTagName(elementId));
|
||||
if (!"script".equals(tag)) continue;
|
||||
|
||||
Reference in New Issue
Block a user