# HG changeset patch # User Jaroslav Tulach # Date 1363623420 -3600 # Node ID c285a78302afd0c5a4fb05452512c151da1c24bb # Parent 859804c78010bded0fa46b1450c87582dfda723e Knockout bindings for primitive types supported in WebView diff -r 859804c78010 -r c285a78302af javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Thu Mar 14 09:22:28 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Mon Mar 18 17:17:00 2013 +0100 @@ -17,7 +17,14 @@ */ package org.apidesign.bck2brwsr.htmlpage; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.scene.web.WebEngine; +import netscape.javascript.JSObject; import org.apidesign.bck2brwsr.core.ExtraJavaScript; import org.apidesign.bck2brwsr.core.JavaScriptBody; @@ -27,19 +34,43 @@ */ @ExtraJavaScript(resource = "/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js") public class Knockout { + private static final Logger LOG = Logger.getLogger(Knockout.class.getName()); /** used by tests */ static Knockout next; - Knockout() { + static { + BufferedReader r = new BufferedReader(new InputStreamReader(Knockout.class.getResourceAsStream("knockout-2.2.1.js"))); + StringBuilder sb = new StringBuilder(); + for (;;) { + try { + String l = r.readLine(); + if (l == null) { + break; + } + sb.append(l).append('\n'); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + web().executeScript(sb.toString()); + Object ko = web().executeScript("ko"); + assert ko != null : "Knockout library successfully defined 'ko'"; + } + + + private final Object bindings; + + Knockout(Object bindings) { + this.bindings = bindings; } public static Knockout applyBindings( Class modelClass, M model, String[] propsGettersAndSetters ) { - Knockout bindings = next; + Object bindings = next; next = null; if (bindings == null) { - bindings = new Knockout(); + bindings = web().executeScript("new Object()"); } for (int i = 0; i < propsGettersAndSetters.length; i += 4) { try { @@ -54,13 +85,16 @@ } } applyBindings(bindings); - return bindings; + return new Knockout(bindings); } @JavaScriptBody(args = { "prop" }, body = "this[prop].valueHasMutated();" ) public void valueHasMutated(String prop) { + LOG.log(Level.INFO, "property mutated: {0}", prop); + JSObject koProp = (JSObject) ((JSObject)bindings).getMember(prop); + koProp.call("valueHasMutated"); } @@ -86,8 +120,48 @@ private static void bind( Object bindings, Object model, String prop, String getter, String setter, boolean primitive ) { + WebEngine e = web(); + JSObject bnd = (JSObject) e.executeScript("var x = {}; x.bnd = " + + "new Function('ko', 'bindings', 'model', 'prop', 'getter', 'setter', 'primitive', '" + + "var bnd = {" + + " read: function() {" + + " try {" + + " return model[getter]();" + + " } catch (e) {" + + " alert(\"Cannot call \" + getter + \" on \" + model + \" error: \" + e);" + + " }" + + " }," + + " owner: bindings" + + "};" + + "if (setter != null) {" + + " bnd.write = function(val) {" + + " model[setter](primitive ? new Number(val) : val);" + + " };" + + "};" + + "bindings[prop] = ko.computed(bnd);'" + + "); x;"); + + Object ko = e.executeScript("ko"); + bnd.call("bnd", ko, bindings, model, prop, strip(getter), strip(setter), primitive); + LOG.log(Level.INFO, "binding defined for {0}: {1}", new Object[]{prop, ((JSObject)bindings).getMember(prop)}); + } + + private static String strip(String mangled) { + if (mangled == null) { + return null; + } + int under = mangled.indexOf("__"); + return mangled.substring(0, under); } @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);") - private static void applyBindings(Object bindings) {} + private static void applyBindings(Object bindings) { + JSObject ko = (JSObject) web().executeScript("ko"); + ko.call("applyBindings", bindings); + } + + private static WebEngine web() { + return (WebEngine) System.getProperties().get("webEngine"); + } + } diff -r 859804c78010 -r c285a78302af javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Thu Mar 14 09:22:28 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Mon Mar 18 17:17:00 2013 +0100 @@ -98,7 +98,7 @@ try { w.append("package " + pkg + ";\n"); w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); - w.append("final class ").append(className).append(" {\n"); + w.append("public final class ").append(className).append(" {\n"); w.append(" private boolean locked;\n"); if (!initializeOnClick(className, (TypeElement) e, w, pp)) { return false; diff -r 859804c78010 -r c285a78302af javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Thu Mar 14 09:22:28 2013 +0100 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Mon Mar 18 17:17:00 2013 +0100 @@ -110,6 +110,10 @@ static class MockKnockout extends Knockout { List mutated = new ArrayList(); + + public MockKnockout() { + super(new Object()); + } @Override public void valueHasMutated(String prop) {