1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Fri Mar 22 17:03:32 2013 +0100
1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ConvertTypes.java Mon Mar 25 11:50:36 2013 +0100
1.3 @@ -43,7 +43,8 @@
1.4 }
1.5
1.6 @JavaScriptBody(args = { "object", "property" },
1.7 - body = "var p = object[property]; return p ? p : null;"
1.8 + body = "if (property === null) return object;\n"
1.9 + + "var p = object[property]; return p ? p : null;"
1.10 )
1.11 private static Object getProperty(Object object, String property) {
1.12 return null;
2.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Fri Mar 22 17:03:32 2013 +0100
2.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Mon Mar 25 11:50:36 2013 +0100
2.3 @@ -29,12 +29,13 @@
2.4 public class Knockout {
2.5 /** used by tests */
2.6 static Knockout next;
2.7 -
2.8 +
2.9 Knockout() {
2.10 }
2.11
2.12 public static <M> Knockout applyBindings(
2.13 - Class<M> modelClass, M model, String[] propsGettersAndSetters
2.14 + Class<M> modelClass, M model, String[] propsGettersAndSetters,
2.15 + String[] methodsAndSignatures
2.16 ) {
2.17 Knockout bindings = next;
2.18 next = null;
2.19 @@ -53,6 +54,11 @@
2.20 throw new IllegalStateException(ex.getMessage());
2.21 }
2.22 }
2.23 + for (int i = 0; i < methodsAndSignatures.length; i += 2) {
2.24 + expose(
2.25 + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]
2.26 + );
2.27 + }
2.28 applyBindings(bindings);
2.29 return bindings;
2.30 }
2.31 @@ -87,6 +93,14 @@
2.32 Object bindings, Object model, String prop, String getter, String setter, boolean primitive
2.33 ) {
2.34 }
2.35 +
2.36 + @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body =
2.37 + "bindings[prop] = function(data, ev) { model[sig](data, ev); };"
2.38 + )
2.39 + private static void expose(
2.40 + Object bindings, Object model, String prop, String sig
2.41 + ) {
2.42 + }
2.43
2.44 @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
2.45 private static void applyBindings(Object bindings) {}
3.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Fri Mar 22 17:03:32 2013 +0100
3.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Mon Mar 25 11:50:36 2013 +0100
3.3 @@ -55,6 +55,7 @@
3.4 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
3.5 import org.apidesign.bck2brwsr.htmlpage.api.Model;
3.6 import org.apidesign.bck2brwsr.htmlpage.api.On;
3.7 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
3.8 import org.apidesign.bck2brwsr.htmlpage.api.Page;
3.9 import org.apidesign.bck2brwsr.htmlpage.api.Property;
3.10 import org.openide.util.lookup.ServiceProvider;
3.11 @@ -68,6 +69,7 @@
3.12 @SupportedAnnotationTypes({
3.13 "org.apidesign.bck2brwsr.htmlpage.api.Model",
3.14 "org.apidesign.bck2brwsr.htmlpage.api.Page",
3.15 + "org.apidesign.bck2brwsr.htmlpage.api.OnFunction",
3.16 "org.apidesign.bck2brwsr.htmlpage.api.On"
3.17 })
3.18 public final class PageProcessor extends AbstractProcessor {
3.19 @@ -164,6 +166,7 @@
3.20 try {
3.21 StringWriter body = new StringWriter();
3.22 List<String> propsGetSet = new ArrayList<>();
3.23 + List<String> functions = new ArrayList<>();
3.24 Map<String, Collection<String>> propsDeps = new HashMap<>();
3.25 if (!generateComputedProperties(body, p.properties(), e.getEnclosedElements(), propsGetSet, propsDeps)) {
3.26 ok = false;
3.27 @@ -171,6 +174,9 @@
3.28 if (!generateProperties(e, body, p.properties(), propsGetSet, propsDeps)) {
3.29 ok = false;
3.30 }
3.31 + if (!generateFunctions(e, body, className, e.getEnclosedElements(), functions)) {
3.32 + ok = false;
3.33 + }
3.34
3.35 FileObject java = processingEnv.getFiler().createSourceFile(pkg + '.' + className, e);
3.36 w = new OutputStreamWriter(java.openOutputStream());
3.37 @@ -208,6 +214,13 @@
3.38 }
3.39 sep = ",\n";
3.40 }
3.41 + w.write("\n }, new String[] {\n");
3.42 + sep = "";
3.43 + for (String n : functions) {
3.44 + w.write(sep);
3.45 + w.write(n);
3.46 + sep = ",\n";
3.47 + }
3.48 w.write("\n });\n return this;\n}\n");
3.49
3.50 w.write("public void triggerEvent(Element e, OnEvent ev) {\n");
3.51 @@ -275,46 +288,7 @@
3.52 continue;
3.53 }
3.54 ExecutableElement ee = (ExecutableElement)method;
3.55 - StringBuilder params = new StringBuilder();
3.56 - {
3.57 - boolean first = true;
3.58 - for (VariableElement ve : ee.getParameters()) {
3.59 - if (!first) {
3.60 - params.append(", ");
3.61 - }
3.62 - first = false;
3.63 - if (ve.asType() == stringType) {
3.64 - if (ve.getSimpleName().contentEquals("id")) {
3.65 - params.append('"').append(id).append('"');
3.66 - continue;
3.67 - }
3.68 - params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString(ev, \"");
3.69 - params.append(ve.getSimpleName().toString());
3.70 - params.append("\")");
3.71 - continue;
3.72 - }
3.73 - if (processingEnv.getTypeUtils().getPrimitiveType(TypeKind.DOUBLE) == ve.asType()) {
3.74 - params.append("org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble(ev, \"");
3.75 - params.append(ve.getSimpleName().toString());
3.76 - params.append("\")");
3.77 - continue;
3.78 - }
3.79 - String rn = ve.asType().toString();
3.80 - int last = rn.lastIndexOf('.');
3.81 - if (last >= 0) {
3.82 - rn = rn.substring(last + 1);
3.83 - }
3.84 - if (rn.equals(className)) {
3.85 - params.append(className).append(".this");
3.86 - continue;
3.87 - }
3.88 - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
3.89 - "@On method can only accept String named 'id' or " + className + " arguments",
3.90 - ee
3.91 - );
3.92 - return false;
3.93 - }
3.94 - }
3.95 + CharSequence params = wrapParams(ee, id, className, "ev", null);
3.96 if (!ee.getModifiers().contains(Modifier.STATIC)) {
3.97 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee);
3.98 ok = false;
3.99 @@ -659,4 +633,105 @@
3.100 e = e.getEnclosingElement();
3.101 }
3.102 }
3.103 +
3.104 + private boolean generateFunctions(
3.105 + Element clazz, StringWriter body, String className,
3.106 + List<? extends Element> enclosedElements, List<String> functions
3.107 + ) {
3.108 + for (Element m : enclosedElements) {
3.109 + if (m.getKind() != ElementKind.METHOD) {
3.110 + continue;
3.111 + }
3.112 + ExecutableElement e = (ExecutableElement)m;
3.113 + OnFunction onF = e.getAnnotation(OnFunction.class);
3.114 + if (onF == null) {
3.115 + continue;
3.116 + }
3.117 + if (!e.getModifiers().contains(Modifier.STATIC)) {
3.118 + processingEnv.getMessager().printMessage(
3.119 + Diagnostic.Kind.ERROR, "@OnFunction method needs to be static", e
3.120 + );
3.121 + return false;
3.122 + }
3.123 + if (e.getModifiers().contains(Modifier.PRIVATE)) {
3.124 + processingEnv.getMessager().printMessage(
3.125 + Diagnostic.Kind.ERROR, "@OnFunction method cannot be private", e
3.126 + );
3.127 + return false;
3.128 + }
3.129 + if (e.getReturnType().getKind() != TypeKind.VOID) {
3.130 + processingEnv.getMessager().printMessage(
3.131 + Diagnostic.Kind.ERROR, "@OnFunction method should return void", e
3.132 + );
3.133 + return false;
3.134 + }
3.135 + String n = e.getSimpleName().toString();
3.136 + body.append("private void ").append(n).append("(Object data, Object ev) {\n");
3.137 + body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("(");
3.138 + body.append(wrapParams(e, null, className, "ev", "data"));
3.139 + body.append(");\n");
3.140 + body.append("}\n");
3.141 +
3.142 + functions.add('\"' + n + '\"');
3.143 + functions.add('\"' + n + "__VLjava_lang_Object_2Ljava_lang_Object_2" + '\"');
3.144 + }
3.145 + return true;
3.146 + }
3.147 +
3.148 + private CharSequence wrapParams(
3.149 + ExecutableElement ee, String id, String className, String evName, String dataName
3.150 + ) {
3.151 + TypeMirror stringType = processingEnv.getElementUtils().getTypeElement("java.lang.String").asType();
3.152 + StringBuilder params = new StringBuilder();
3.153 + boolean first = true;
3.154 + for (VariableElement ve : ee.getParameters()) {
3.155 + if (!first) {
3.156 + params.append(", ");
3.157 + }
3.158 + first = false;
3.159 + String toCall = null;
3.160 + if (ve.asType() == stringType) {
3.161 + if (ve.getSimpleName().contentEquals("id")) {
3.162 + params.append('"').append(id).append('"');
3.163 + continue;
3.164 + }
3.165 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toString";
3.166 + }
3.167 + if (ve.asType().getKind() == TypeKind.DOUBLE) {
3.168 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toDouble";
3.169 + }
3.170 + if (ve.asType().getKind() == TypeKind.INT) {
3.171 + toCall = "org.apidesign.bck2brwsr.htmlpage.ConvertTypes.toInt";
3.172 + }
3.173 +
3.174 + if (toCall != null) {
3.175 + params.append(toCall).append('(');
3.176 + if (dataName != null && ve.getSimpleName().contentEquals("data")) {
3.177 + params.append(dataName);
3.178 + params.append(", null");
3.179 + } else {
3.180 + params.append(evName);
3.181 + params.append(", \"");
3.182 + params.append(ve.getSimpleName().toString());
3.183 + params.append("\"");
3.184 + }
3.185 + params.append(")");
3.186 + continue;
3.187 + }
3.188 + String rn = ve.asType().toString();
3.189 + int last = rn.lastIndexOf('.');
3.190 + if (last >= 0) {
3.191 + rn = rn.substring(last + 1);
3.192 + }
3.193 + if (rn.equals(className)) {
3.194 + params.append(className).append(".this");
3.195 + continue;
3.196 + }
3.197 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
3.198 + "@On method can only accept String named 'id' or " + className + " arguments",
3.199 + ee
3.200 + );
3.201 + }
3.202 + return params;
3.203 + }
3.204 }
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnFunction.java Mon Mar 25 11:50:36 2013 +0100
4.3 @@ -0,0 +1,34 @@
4.4 +/**
4.5 + * Back 2 Browser Bytecode Translator
4.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
4.7 + *
4.8 + * This program is free software: you can redistribute it and/or modify
4.9 + * it under the terms of the GNU General Public License as published by
4.10 + * the Free Software Foundation, version 2 of the License.
4.11 + *
4.12 + * This program is distributed in the hope that it will be useful,
4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.15 + * GNU General Public License for more details.
4.16 + *
4.17 + * You should have received a copy of the GNU General Public License
4.18 + * along with this program. Look for COPYING file in the top folder.
4.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
4.20 + */
4.21 +package org.apidesign.bck2brwsr.htmlpage.api;
4.22 +
4.23 +import java.lang.annotation.ElementType;
4.24 +import java.lang.annotation.Retention;
4.25 +import java.lang.annotation.RetentionPolicy;
4.26 +import java.lang.annotation.Target;
4.27 +
4.28 +/** Methods in class annotated by {@link Model} or {@link Page} can be
4.29 + * annotated by this annotation to signal that they should be available
4.30 + * as functions to users of the model classes.
4.31 + *
4.32 + * @author Jaroslav Tulach <jtulach@netbeans.org>
4.33 + */
4.34 +@Target(ElementType.METHOD)
4.35 +@Retention(RetentionPolicy.SOURCE)
4.36 +public @interface OnFunction {
4.37 +}
5.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Fri Mar 22 17:03:32 2013 +0100
5.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/KnockoutTest.java Mon Mar 25 11:50:36 2013 +0100
5.3 @@ -21,6 +21,7 @@
5.4 import org.apidesign.bck2brwsr.core.JavaScriptBody;
5.5 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
5.6 import org.apidesign.bck2brwsr.htmlpage.api.OnEvent;
5.7 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
5.8 import org.apidesign.bck2brwsr.htmlpage.api.Page;
5.9 import org.apidesign.bck2brwsr.htmlpage.api.Property;
5.10 import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
5.11 @@ -34,7 +35,8 @@
5.12 */
5.13 @Page(xhtml="Knockout.xhtml", className="KnockoutModel", properties={
5.14 @Property(name="name", type=String.class),
5.15 - @Property(name="results", type=String.class, array = true)
5.16 + @Property(name="results", type=String.class, array = true),
5.17 + @Property(name="callbackCount", type=int.class)
5.18 })
5.19 public class KnockoutTest {
5.20
5.21 @@ -55,7 +57,7 @@
5.22
5.23 @HtmlFragment(
5.24 "<ul id='ul' data-bind='foreach: results'>\n"
5.25 - + " <li><b data-bind='text: $data'></b></li>\n"
5.26 + + " <li data-bind='text: $data, click: $root.call'/>\n"
5.27 + "</ul>\n"
5.28 )
5.29 @BrwsrTest public void displayContentOfArray() {
5.30 @@ -66,10 +68,15 @@
5.31 int cnt = countChildren("ul");
5.32 assert cnt == 1 : "One child, but was " + cnt;
5.33
5.34 - m.getResults().add("hello");
5.35 + m.getResults().add("Hi");
5.36
5.37 cnt = countChildren("ul");
5.38 assert cnt == 2 : "Two children now, but was " + cnt;
5.39 +
5.40 + triggerChildClick("ul", 1);
5.41 +
5.42 + assert 1 == m.getCallbackCount() : "One callback " + m.getCallbackCount();
5.43 + assert "Hi".equals(m.getName()) : "We got callback from 2nd child " + m.getName();
5.44 }
5.45
5.46 @HtmlFragment(
5.47 @@ -91,6 +98,12 @@
5.48 assert cnt == 2 : "Two children now, but was " + cnt;
5.49 }
5.50
5.51 + @OnFunction
5.52 + static void call(KnockoutModel m, String data) {
5.53 + m.setName(data);
5.54 + m.setCallbackCount(m.getCallbackCount() + 1);
5.55 + }
5.56 +
5.57 @ComputedProperty
5.58 static String helloMessage(String name) {
5.59 return "Hello " + name + "!";
5.60 @@ -112,4 +125,12 @@
5.61 + "return e.children.length;\n "
5.62 )
5.63 private static native int countChildren(String id);
5.64 +
5.65 + @JavaScriptBody(args = { "id", "pos" }, body =
5.66 + "var e = window.document.getElementById(id);\n "
5.67 + + "var ev = window.document.createEvent('MouseEvents');\n "
5.68 + + "ev.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);\n "
5.69 + + "e.children[pos].dispatchEvent(ev);\n "
5.70 + )
5.71 + private static native void triggerChildClick(String id, int pos);
5.72 }
6.1 --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Fri Mar 22 17:03:32 2013 +0100
6.2 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ModelTest.java Mon Mar 25 11:50:36 2013 +0100
6.3 @@ -23,6 +23,7 @@
6.4 import java.util.List;
6.5 import java.util.ListIterator;
6.6 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
6.7 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
6.8 import org.apidesign.bck2brwsr.htmlpage.api.Page;
6.9 import org.apidesign.bck2brwsr.htmlpage.api.Property;
6.10 import static org.testng.Assert.*;
6.11 @@ -168,6 +169,10 @@
6.12 }
6.13 }
6.14
6.15 + @OnFunction
6.16 + static void doSomething() {
6.17 + }
6.18 +
6.19 @ComputedProperty
6.20 static int powerValue(int value) {
6.21 return value * value;
7.1 --- a/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calc.java Fri Mar 22 17:03:32 2013 +0100
7.2 +++ b/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calc.java Mon Mar 25 11:50:36 2013 +0100
7.3 @@ -17,9 +17,11 @@
7.4 */
7.5 package org.apidesign.bck2brwsr.demo.calc.staticcompilation;
7.6
7.7 +import java.util.List;
7.8 import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty;
7.9 import org.apidesign.bck2brwsr.htmlpage.api.On;
7.10 import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*;
7.11 +import org.apidesign.bck2brwsr.htmlpage.api.OnFunction;
7.12 import org.apidesign.bck2brwsr.htmlpage.api.Page;
7.13 import org.apidesign.bck2brwsr.htmlpage.api.Property;
7.14
7.15 @@ -38,7 +40,7 @@
7.16 })
7.17 public class Calc {
7.18 static {
7.19 - new Calculator().applyBindings();
7.20 + new Calculator().applyBindings().setOperation("plus");
7.21 }
7.22
7.23 @On(event = CLICK, id="clear")
7.24 @@ -76,6 +78,16 @@
7.25 c.setMemory(0);
7.26 }
7.27
7.28 + @OnFunction
7.29 + static void recoverMemory(Calculator c, double data) {
7.30 + c.setDisplay(data);
7.31 + }
7.32 +
7.33 + @OnFunction
7.34 + static void removeMemory(Calculator c, double data) {
7.35 + c.getHistory().remove(data);
7.36 + }
7.37 +
7.38 private static double compute(String op, double memory, double display) {
7.39 switch (op) {
7.40 case "plus": return memory + display;
7.41 @@ -112,4 +124,9 @@
7.42 }
7.43 return "Attempt to compute " + memory + " " + operation + " " + display + " = " + compute(operation, memory, display);
7.44 }
7.45 +
7.46 + @ComputedProperty
7.47 + static boolean emptyHistory(List<?> history) {
7.48 + return history.isEmpty();
7.49 + }
7.50 }
8.1 --- a/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Fri Mar 22 17:03:32 2013 +0100
8.2 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Mon Mar 25 11:50:36 2013 +0100
8.3 @@ -79,8 +79,13 @@
8.4
8.5 <h4>Previous Results</h4>
8.6
8.7 + <div data-bind="if: emptyHistory">No results yet.</div>
8.8 <ul data-bind="foreach: history">
8.9 - <li data-bind="text: $data"/>
8.10 + <li>
8.11 + <span data-bind="text: $data"/> -
8.12 + <a href="#" data-bind="click: $root.recoverMemory">Use</a>
8.13 + <a href="#" data-bind="click: $root.removeMemory">Remove</a>
8.14 + </li>
8.15 </ul>
8.16
8.17 <div data-bind="text: displayPreview"></div>