package is.L42.connected.withHtml; import java.awt.Dimension; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import javafx.concurrent.Worker; import javafx.embed.swing.JFXPanel; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javax.swing.JFrame; import javax.swing.SwingUtilities; import netscape.javascript.JSObject; import javafx.application.Platform; import java.awt.event.WindowAdapter; import platformSpecific.javaTranslation.Resources; @SuppressWarnings("serial") public class Frame extends JFrame{ private static final HashMap<String,Frame> windows=new HashMap<>(); public static void load(String wName,String html,int x,int y){ Frame f=windows.get(wName); if (f!=null){f.dispose();} f=Frame.createNew(wName,html,x,y);windows.put(wName,f); } private static Frame createNew(String wName,String html,int x, int y) { FutureTask<Frame> future = new FutureTask<>(()-> { final Frame frame = new Frame(Frame.extractTitle(html)); JFXPanel jfxPanel = new JFXPanel(); frame.createHtmlContent(jfxPanel,html); frame.getContentPane().add(jfxPanel); frame.setMinimumSize(new Dimension(x, y)); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.addWindowListener( new WindowAdapter(){ public void windowClosing(WindowEvent e){ Frame.close(wName); }}); frame.setVisible(true); return frame; }); SwingUtilities.invokeLater(future); try {return future.get();} catch (ExecutionException e) {throw propagateException(e.getCause());} catch (InterruptedException e) {throw propagateException(e);} } private static String extractTitle(String html) { String htmlUP=html.toUpperCase(); int start=htmlUP.indexOf("<TITLE>");//TODO: better parsing? int end=htmlUP.indexOf("</TITLE>"); return html.substring(start+("<TITLE>".length()),end); } private Void initWeb(CountDownLatch latch, JFXPanel jfxPanel,String html){ Group root = new Group(); Scene scene = new Scene(root); WebView browser = new WebView(); this.webEngine = browser.getEngine(); this.webEngine.getLoadWorker().stateProperty().addListener( (ov, oldState,newState)->{ if (newState == Worker.State.SUCCEEDED) {latch.countDown();} }); this.webEngine.loadContent(html); this.webEngine.setOnAlert(event->{ Alert alert = new Alert(AlertType.INFORMATION); alert.setTitle("Information Dialog"); alert.setHeaderText(null); alert.setContentText(event.getData()); alert.showAndWait(); //alert.setOnCloseRequest(e->{ alert.close(); }); }); root.getChildren().add(browser); jfxPanel.setScene(scene); return null; } private void createHtmlContent(JFXPanel jfxPanel,String html) { CountDownLatch latch = new CountDownLatch(1); FutureTask<Void> future=new FutureTask<Void>(()->initWeb(latch,jfxPanel,html)); Platform.runLater(future); try {future.get();} catch (ExecutionException e) {throw propagateException(e.getCause());} catch (InterruptedException e) {throw propagateException(e);} try {latch.await();} catch (InterruptedException e) {throw propagateException(e);} future=new FutureTask<Void>(()->{ Object o=this.webEngine.executeScript( "window.event42=function(s){ if(event42.eventCollector){event42.eventCollector.add(s);return 'Event '+s+' added '+event42.eventCollector.toString();} return 'Event '+s+' not added';}"); assert o instanceof JSObject : o.toString(); JSObject jsobj = (JSObject)o; jsobj.setMember("eventCollector",this.events); return null; }); Platform.runLater(future); try {future.get();} catch (ExecutionException e) {throw propagateException(e.getCause());} catch (InterruptedException e) {throw propagateException(e);} } public static void close(String wName){ Frame f=windows.get(wName); if(f!=null){ System.out.println(wName+" is disposed"); f.dispose(); windows.remove(wName); f.events.isDisposed=true; } } public static String executeJs(String wName, String command){ Frame f=windows.get(wName); if(f!=null){return f.executeJs(command);} throw new Resources.Error("wName not active:"+wName); } private Frame(String title){super(title);} private WebEngine webEngine; private String executeJs(String command) { FutureTask<String> future = new FutureTask<>(()->executeJsFX(command)); Platform.runLater(future); try {return future.get();} catch (ExecutionException e) {throw propagateException(e.getCause());} catch (InterruptedException e) {throw propagateException(e);} } private String executeJsFX(String command) { try{ Object res=webEngine.executeScript(command); if(res ==null){return "";}//TODO: get a 42 error if(res instanceof String){return (String)res;} if(res instanceof Integer){return res.toString();} if(res instanceof Double){return res.toString();} if(res instanceof Boolean){return res.toString();} //if(res instanceof netscape.javascript.JSObject){return "";} return ""; } catch(netscape.javascript.JSException jsExc){ throw new Resources.Error("JavascriptError:"+jsExc); } } private static Error propagateException(Throwable t){ if (t instanceof RuntimeException){throw (RuntimeException)t;} if (t instanceof Error){throw (Error)t;} if (t instanceof InterruptedException){Thread.currentThread().interrupt();} throw new Error(t); } public static class Events{ private boolean isDisposed=false; private final List<String>data=new ArrayList<>(); public synchronized void add(String s){data.add(s);} public synchronized String toString(){return data.toString();} public synchronized String get(){ while(!isDisposed && data.isEmpty()){ try{this.wait(100);} catch (InterruptedException e) {propagateException(e);} } if(isDisposed){throw new Resources.Error("Requested EventQueue is disposed");} return data.remove(0); } } private final Events events=new Events(); public static String getEventString(String wName){ Frame f=windows.get(wName); if (f!=null){return f.getEventString();} throw new Resources.Error("wName not active:"+wName); } public String getEventString(){return events.get();} }