1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Tue Mar 19 11:51:08 2013 +0100
1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Thu Apr 11 20:44:46 2013 +0200
1.3 @@ -21,6 +21,7 @@
1.4 import java.io.IOException;
1.5 import java.io.InputStreamReader;
1.6 import java.lang.reflect.Method;
1.7 +import java.util.List;
1.8 import java.util.logging.Level;
1.9 import java.util.logging.Logger;
1.10 import javafx.scene.web.WebEngine;
1.11 @@ -37,7 +38,8 @@
1.12 private static final Logger LOG = Logger.getLogger(Knockout.class.getName());
1.13 /** used by tests */
1.14 static Knockout next;
1.15 -
1.16 + private final Object model;
1.17 +
1.18 static {
1.19 BufferedReader r = new BufferedReader(new InputStreamReader(Knockout.class.getResourceAsStream("knockout-2.2.1.js")));
1.20 StringBuilder sb = new StringBuilder();
1.21 @@ -58,42 +60,34 @@
1.22 }
1.23
1.24
1.25 - private final Object bindings;
1.26 -
1.27 - Knockout(Object bindings) {
1.28 - this.bindings = bindings;
1.29 + Knockout(Object model) {
1.30 + this.model = model == null ? this : model;
1.31 }
1.32
1.33 public static <M> Knockout applyBindings(
1.34 - Class<M> modelClass, M model, String[] propsGettersAndSetters
1.35 + Object model, String[] propsGettersAndSetters,
1.36 + String[] methodsAndSignatures
1.37 + ) {
1.38 + applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures);
1.39 + return new Knockout(model);
1.40 + }
1.41 + public static <M> Knockout applyBindings(
1.42 + Class<M> modelClass, M model, String[] propsGettersAndSetters,
1.43 + String[] methodsAndSignatures
1.44 ) {
1.45 Object bindings = next;
1.46 next = null;
1.47 if (bindings == null) {
1.48 bindings = web().executeScript("new Object()");
1.49 }
1.50 - for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
1.51 - try {
1.52 - Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
1.53 - bind(bindings, model, propsGettersAndSetters[i],
1.54 - propsGettersAndSetters[i + 1],
1.55 - propsGettersAndSetters[i + 2],
1.56 - getter.getReturnType().isPrimitive()
1.57 - );
1.58 - } catch (NoSuchMethodException ex) {
1.59 - throw new IllegalStateException(ex.getMessage());
1.60 - }
1.61 - }
1.62 + applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures);
1.63 applyBindings(bindings);
1.64 return new Knockout(bindings);
1.65 }
1.66
1.67 - @JavaScriptBody(args = { "prop" }, body =
1.68 - "this[prop].valueHasMutated();"
1.69 - )
1.70 public void valueHasMutated(String prop) {
1.71 LOG.log(Level.FINE, "property mutated: {0}", prop);
1.72 - JSObject koProp = (JSObject) ((JSObject)bindings).getMember(prop);
1.73 + JSObject koProp = (JSObject) ((JSObject)model).getMember(prop);
1.74 koProp.call("valueHasMutated");
1.75 }
1.76
1.77 @@ -102,23 +96,24 @@
1.78 public static void triggerEvent(String id, String ev) {
1.79 }
1.80
1.81 - @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive" }, body =
1.82 + @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive", "array" }, body =
1.83 "var bnd = {\n"
1.84 - + " read: function() {\n"
1.85 + + " 'read': function() {\n"
1.86 + " var v = model[getter]();\n"
1.87 + + " if (array) v = v.koArray();\n"
1.88 + " return v;\n"
1.89 + " },\n"
1.90 - + " owner: bindings\n"
1.91 + + " 'owner': bindings\n"
1.92 + "};\n"
1.93 + "if (setter != null) {\n"
1.94 - + " bnd.write = function(val) {\n"
1.95 + + " bnd['write'] = function(val) {\n"
1.96 + " model[setter](primitive ? new Number(val) : val);\n"
1.97 + " };\n"
1.98 + "}\n"
1.99 - + "bindings[prop] = ko.computed(bnd);"
1.100 + + "bindings[prop] = ko['computed'](bnd);"
1.101 )
1.102 private static void bind(
1.103 - Object bindings, Object model, String prop, String getter, String setter, boolean primitive
1.104 + Object bindings, Object model, String prop, String getter, String setter, boolean primitive, boolean array
1.105 ) {
1.106 WebEngine e = web();
1.107 JSObject bnd = (JSObject) e.executeScript("var x = {}; x.bnd = "
1.108 @@ -153,6 +148,13 @@
1.109 int under = mangled.indexOf("__");
1.110 return mangled.substring(0, under);
1.111 }
1.112 + @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body =
1.113 + "bindings[prop] = function(data, ev) { model[sig](data, ev); };"
1.114 + )
1.115 + private static void expose(
1.116 + Object bindings, Object model, String prop, String sig
1.117 + ) {
1.118 + }
1.119
1.120 @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
1.121 private static void applyBindings(Object bindings) {
1.122 @@ -164,4 +166,29 @@
1.123 return (WebEngine) System.getProperties().get("webEngine");
1.124 }
1.125
1.126 +
1.127 + private static void applyImpl(
1.128 + String[] propsGettersAndSetters,
1.129 + Class<?> modelClass,
1.130 + Object bindings,
1.131 + Object model,
1.132 + String[] methodsAndSignatures
1.133 + ) throws IllegalStateException, SecurityException {
1.134 + for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
1.135 + try {
1.136 + Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
1.137 + bind(bindings, model, propsGettersAndSetters[i],
1.138 + propsGettersAndSetters[i + 1],
1.139 + propsGettersAndSetters[i + 2],
1.140 + getter.getReturnType().isPrimitive(),
1.141 + List.class.isAssignableFrom(getter.getReturnType()));
1.142 + } catch (NoSuchMethodException ex) {
1.143 + throw new IllegalStateException(ex.getMessage());
1.144 + }
1.145 + }
1.146 + for (int i = 0; i < methodsAndSignatures.length; i += 2) {
1.147 + expose(
1.148 + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]);
1.149 + }
1.150 + }
1.151 }