This project is a simple ZK addon based on ZK CE (community edition) that wraps various browser built-in JavaScript API in Java.
- Geolocation API
- Clipboard API
- Notification API (not yet supported)
- Web Speech API (not yet supported)
- MediaDevices (not yet supported)
- Drag and Drop API (not yet supported)
Require Maven installed.
- Run
mvn jetty:run - Navigate to
http://localhost:8080/browser-kit/
3 main steps:
- Initialize the helper in a controller's lifecycle
- Listen to the corresponding event
- Call its methods in an event listener
see *.java and *.zul under src/test/
<dependency>
<groupId>org.zkoss.zkforge</groupId>
<artifactId>browser-kit</artifactId>
<version>2.0.0</version>
</dependency>Include ZK CE repository
<repository>
<id>ZK CE</id>
<name>ZK CE Repository</name>
<url>https://mavensync.zkoss.org/maven2</url>
</repository>Both ClipboardHelper and GeolocationHelper implement a unified, stateless design pattern that provides seamless integration with ZK applications.
Both helpers implement a consistent stateless architecture with auto-initialization and singleton AuService pattern for optimal resource management and developer experience.
- All public methods are static for simplified usage
- No instance management required - just call the methods directly
- Single AuService instance shared across all desktops
- Desktop-level initialization tracking prevents duplicate setup
- Efficient resource utilization with proper cleanup support
Browser API → JavaScript Helper → zAu.send(desktop event)
→ AuService → Desktop Event → Controller(Composer/ViewModel) Listeners
The ClipboardHelper provides static access to the browser's Clipboard API for reading and writing clipboard content.
- Secure context: This feature is available only in secure contexts (HTTPS)
- Init
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
ClipboardHelper.init();
}- Listening for results
@Listen(ClipboardEvent.EVENT_NAME + " = #root")
public void handleClipboard(ClipboardEvent event) {
ClipboardResult result = event.getClipboardResult();
if (result.isSuccess()) {
processClipboardContent(result.getText());
}
}- Call operation methods
// Writing to clipboard
ClipboardHelper.writeText("Hello World");
// Reading from clipboard (results via events)
ClipboardHelper.readText();For scenarios with multiple forms or components requesting clipboard access simultaneously, use the targeted clipboard API to deliver events directly to the requesting component instead of broadcasting to all components.
When multiple forms on a page need clipboard access:
- Broadcast approach (original): All components receive all clipboard events → requires manual filtering and flag management
- Targeted approach (new): Each component receives only ITS own clipboard events → no filtering needed, no cross-form interference
The clipboard operations follow a unified delegation pattern:
- Primary methods (
readTextTo(),readImageTo(),writeTextTo()): Accept a target component (ornullfor broadcast) - Broadcast methods (
readText(),readImage(),writeText()): Delegate to primary methods withnulltarget - Single source of truth: All operations go through the targeted methods
This design eliminates code duplication and provides a clean API:
// Broadcast - delegates to readTextTo(null)
ClipboardHelper.readText();
// Targeted - primary implementation
ClipboardHelper.readTextTo(Component targetComponent);
// Same pattern for images and writes
ClipboardHelper.readImage(); // → readImageTo(null)
ClipboardHelper.readImageTo(Component);
ClipboardHelper.writeText(String); // → writeTextTo(null, String)
ClipboardHelper.writeTextTo(Component, String);public class FormComposer extends SelectorComposer<Component> {
@Wire
private Textbox pastingTarget;
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
ClipboardHelper.init();
// Listen for clipboard events - events are automatically targeted to this component
pastingTarget.addEventListener(ClipboardEvent.EVENT_NAME, (ClipboardEvent event) -> {
if (event.isSuccess() && event.isTextResult()) {
// Safe to process - event is guaranteed for this component only
pastingTarget.setValue(event.getClipboardText().getText());
}
});
}
@Listen("onClick = #read")
public void read() {
// Use targeted API - delivers result only to THIS component
ClipboardHelper.readTextTo(pastingTarget);
}
}| Feature | Broadcast API | Targeted API |
|---|---|---|
| Event Delivery | All root components | Target component only |
| Multiple Forms | Events broadcast to all | No interference |
| Code Complexity | Flags/IDs needed for filtering | No filtering required |
| Concurrent Requests | Potential race conditions | Naturally concurrent-safe |
| Use Case | Global clipboard monitoring | Form-specific operations |
| How to Use | readText() |
readTextTo(self) or readTextTo(null) for broadcast |
See multiple-requester.zul and FormComposer.java for a complete working example with 3 forms requesting clipboard simultaneously.
The GeolocationHelper provides static access to the browser's Geolocation API for requesting user location.
- Secure context: This feature is available only in secure contexts (HTTPS)
// Request current position
GeolocationHelper.getCurrentPosition();
// Listen for results
@Listen(GeolocationEvent.EVENT_NAME + " = #root")
public void handleGeolocation(GeolocationEvent event) {
if (event.isSuccess()) {
GeoLocationPosition position = event.getGeoLocationPosition();
processLocation(position);
} else {
handleLocationError(event.getGeoLocationPositionError());
}
}- Simplified API: No instance management - direct static method calls
- Desktop-level Events: Multiple composers can listen independently
- Resource Efficiency: Singleton AuService pattern minimizes overhead
- Clean Lifecycle: Optional
dispose()method for explicit cleanup
Init
flowchart TB
subgraph Client[Browser]
JS[JavaScript Helper]
end
subgraph Server[Server]
Java[Static Helper Method]
Init[Auto-initialization]
AU[Singleton AuService]
end
Java -->|1. Calls| Init
Init -->|2. Checks| Init
Init -->|3. Registers| AU
Init -->|4. Loads| JS
style Client fill:#f0f8ff,color:black
style Server fill:#e8f5e9,color:black
style Java fill:#ffd1dc,stroke:#c2185b,color:black
style Init fill:#e0f7fa,stroke:#006064,color:black
style AU fill:#e8f5e9,stroke:#2e7d32,color:black
style JS fill:#fff3e0,stroke:#ef6c00,color:black
Calling
flowchart TB
subgraph Client[Browser]
API[Browser API]
JS[JavaScript Helper]
end
subgraph Server[Server]
Java[Static Helper Method]
AU[Singleton AuService]
Controller[Java Controller]
end
Java -->|1. Executes| API
API -->|2. Returns| JS
JS -->|3. Sends Event| AU
AU -->|4. Posts Event| Controller
style Client fill:#f0f8ff,color:black
style Server fill:#e8f5e9,color:black
style Java fill:#ffd1dc,stroke:#c2185b,color:black
style AU fill:#e8f5e9,stroke:#2e7d32,color:black
style JS fill:#fff3e0,stroke:#ef6c00,color:black
style API fill:#e3f2fd,stroke:#1565c0,color:black
style Controller fill:#ffecb3,stroke:#ff6f00,color:black
- ClipboardHelper: see ClipboardComposer.java
- GeolocationHelper: see LocationComposer.java
Note: Both helpers require user permissions and are subject to browser support and security restrictions.
- change version in
pom.xmlto official version - run release
- set tag in GitHub as the version (e.g.
v1.0.0) - Publish to ZK Maven CE repository with jekins3/PBFUM
- a simple project, no need freshly version
- Source Code - Apache License 2.0