1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Sun Mar 31 06:46:25 2013 +0200
1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Sun Mar 31 12:01:38 2013 +0200
1.3 @@ -30,45 +30,40 @@
1.4 public class Knockout {
1.5 /** used by tests */
1.6 static Knockout next;
1.7 + private final Object model;
1.8
1.9 - Knockout() {
1.10 + Knockout(Object model) {
1.11 + this.model = model == null ? this : model;
1.12 }
1.13
1.14 public static <M> Knockout applyBindings(
1.15 + Object model, String[] propsGettersAndSetters,
1.16 + String[] methodsAndSignatures
1.17 + ) {
1.18 + applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures);
1.19 + return new Knockout(model);
1.20 + }
1.21 + public static <M> Knockout applyBindings(
1.22 Class<M> modelClass, M model, String[] propsGettersAndSetters,
1.23 String[] methodsAndSignatures
1.24 ) {
1.25 Knockout bindings = next;
1.26 next = null;
1.27 if (bindings == null) {
1.28 - bindings = new Knockout();
1.29 + bindings = new Knockout(null);
1.30 }
1.31 - for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
1.32 - try {
1.33 - Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
1.34 - bind(bindings, model, propsGettersAndSetters[i],
1.35 - propsGettersAndSetters[i + 1],
1.36 - propsGettersAndSetters[i + 2],
1.37 - getter.getReturnType().isPrimitive(),
1.38 - List.class.isAssignableFrom(getter.getReturnType())
1.39 - );
1.40 - } catch (NoSuchMethodException ex) {
1.41 - throw new IllegalStateException(ex.getMessage());
1.42 - }
1.43 - }
1.44 - for (int i = 0; i < methodsAndSignatures.length; i += 2) {
1.45 - expose(
1.46 - bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]
1.47 - );
1.48 - }
1.49 + applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures);
1.50 applyBindings(bindings);
1.51 return bindings;
1.52 }
1.53
1.54 - @JavaScriptBody(args = { "prop" }, body =
1.55 - "this[prop].valueHasMutated();"
1.56 + public void valueHasMutated(String prop) {
1.57 + valueHasMutated(model, prop);
1.58 + }
1.59 + @JavaScriptBody(args = { "self", "prop" }, body =
1.60 + "self[prop].valueHasMutated();"
1.61 )
1.62 - public void valueHasMutated(String prop) {
1.63 + public void valueHasMutated(Object self, String prop) {
1.64 }
1.65
1.66
1.67 @@ -107,4 +102,29 @@
1.68
1.69 @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
1.70 private static void applyBindings(Object bindings) {}
1.71 +
1.72 + private static void applyImpl(
1.73 + String[] propsGettersAndSetters,
1.74 + Class<?> modelClass,
1.75 + Object bindings,
1.76 + Object model,
1.77 + String[] methodsAndSignatures
1.78 + ) throws IllegalStateException, SecurityException {
1.79 + for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
1.80 + try {
1.81 + Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
1.82 + bind(bindings, model, propsGettersAndSetters[i],
1.83 + propsGettersAndSetters[i + 1],
1.84 + propsGettersAndSetters[i + 2],
1.85 + getter.getReturnType().isPrimitive(),
1.86 + List.class.isAssignableFrom(getter.getReturnType()));
1.87 + } catch (NoSuchMethodException ex) {
1.88 + throw new IllegalStateException(ex.getMessage());
1.89 + }
1.90 + }
1.91 + for (int i = 0; i < methodsAndSignatures.length; i += 2) {
1.92 + expose(
1.93 + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]);
1.94 + }
1.95 + }
1.96 }
2.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Sun Mar 31 06:46:25 2013 +0200
2.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Sun Mar 31 12:01:38 2013 +0200
2.3 @@ -135,16 +135,13 @@
2.4 w.append(" private org.apidesign.bck2brwsr.htmlpage.Knockout ko;\n");
2.5 w.append(body.toString());
2.6 w.append(" private static Class<" + e.getSimpleName() + "> modelFor() { return null; }\n");
2.7 - for (int i = 0; i < propsGetSet.size(); i += 4) {
2.8 - w.append(" @JavaScriptOnly(name=\"" + propsGetSet.get(i) + "\",\n");
2.9 - w.append(" value=\"function() { ");
2.10 - final String setter = propsGetSet.get(i + 2);
2.11 - if (setter != null) {
2.12 - w.append("if (arguments.length == 1) this." + setter + "(arguments[0]); ");
2.13 - }
2.14 - w.append("return this." + propsGetSet.get(i + 1) + "();}\")\n");
2.15 - w.append(" private static native void __accessor" + i + "();");
2.16 - }
2.17 + w.append(" public ").append(className).append("() {\n");
2.18 + w.append(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(this, ");
2.19 + writeStringArray(propsGetSet, w);
2.20 + w.append(", ");
2.21 + writeStringArray(Collections.<String>emptyList(), w);
2.22 + w.append(" );\n");
2.23 + w.append(" };\n");
2.24 w.append("}\n");
2.25 } finally {
2.26 w.close();
2.27 @@ -219,25 +216,10 @@
2.28 w.write("public " + className + " applyBindings() {\n");
2.29 w.write(" ko = org.apidesign.bck2brwsr.htmlpage.Knockout.applyBindings(");
2.30 w.write(className + ".class, this, ");
2.31 - w.write("new String[] {\n");
2.32 - String sep = "";
2.33 - for (String n : propsGetSet) {
2.34 - w.write(sep);
2.35 - if (n == null) {
2.36 - w.write(" null");
2.37 - } else {
2.38 - w.write(" \"" + n + "\"");
2.39 - }
2.40 - sep = ",\n";
2.41 - }
2.42 - w.write("\n }, new String[] {\n");
2.43 - sep = "";
2.44 - for (String n : functions) {
2.45 - w.write(sep);
2.46 - w.write(n);
2.47 - sep = ",\n";
2.48 - }
2.49 - w.write("\n });\n return this;\n}\n");
2.50 + writeStringArray(propsGetSet, w);
2.51 + w.append(", ");
2.52 + writeStringArray(functions, w);
2.53 + w.write(");\n return this;\n}\n");
2.54
2.55 w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
2.56 w.write(" org.apidesign.bck2brwsr.htmlpage.Knockout.triggerEvent(e.getId(), ev.getElementPropertyName());\n");
2.57 @@ -681,8 +663,8 @@
2.58 body.append(");\n");
2.59 body.append("}\n");
2.60
2.61 - functions.add('\"' + n + '\"');
2.62 - functions.add('\"' + n + "__VLjava_lang_Object_2Ljava_lang_Object_2" + '\"');
2.63 + functions.add(n);
2.64 + functions.add(n + "__VLjava_lang_Object_2Ljava_lang_Object_2");
2.65 }
2.66 return true;
2.67 }
2.68 @@ -762,4 +744,19 @@
2.69 }
2.70 return models.values().contains(e.getSimpleName().toString());
2.71 }
2.72 +
2.73 + private void writeStringArray(List<String> strings, Writer w) throws IOException {
2.74 + w.write("new String[] {\n");
2.75 + String sep = "";
2.76 + for (String n : strings) {
2.77 + w.write(sep);
2.78 + if (n == null) {
2.79 + w.write(" null");
2.80 + } else {
2.81 + w.write(" \"" + n + "\"");
2.82 + }
2.83 + sep = ",\n";
2.84 + }
2.85 + w.write("\n }");
2.86 + }
2.87 }
3.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Sun Mar 31 06:46:25 2013 +0200
3.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Sun Mar 31 12:01:38 2013 +0200
3.3 @@ -101,7 +101,7 @@
3.4
3.5 @HtmlFragment(
3.6 "<ul id='ul' data-bind='foreach: people'>\n"
3.7 - + " <li data-bind='text: $data.firstName(), click: $root.removePerson'></li>\n"
3.8 + + " <li data-bind='text: $data.firstName, click: $root.removePerson'></li>\n"
3.9 + "</ul>\n"
3.10 )
3.11 @BrwsrTest public void displayContentOfArrayOfPeople() {
3.12 @@ -132,6 +132,11 @@
3.13
3.14 String txt = childText("ul", 0);
3.15 assert "first".equals(txt) : "Expecting 'first': " + txt;
3.16 +
3.17 + first.setFirstName("changed");
3.18 +
3.19 + txt = childText("ul", 0);
3.20 + assert "changed".equals(txt) : "Expecting 'changed': " + txt;
3.21 }
3.22
3.23 @OnFunction
4.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Sun Mar 31 06:46:25 2013 +0200
4.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Sun Mar 31 12:01:38 2013 +0200
4.3 @@ -197,6 +197,10 @@
4.4 static class MockKnockout extends Knockout {
4.5 List<String> mutated = new ArrayList<>();
4.6
4.7 + MockKnockout() {
4.8 + super(null);
4.9 + }
4.10 +
4.11 @Override
4.12 public void valueHasMutated(String prop) {
4.13 mutated.add(prop);