jaroslav@1189: /**
jaroslav@1189: * HTML via Java(tm) Language Bindings
jaroslav@1189: * Copyright (C) 2013 Jaroslav Tulach
jaroslav@1189: *
jaroslav@1189: * This program is free software: you can redistribute it and/or modify
jaroslav@1189: * it under the terms of the GNU General Public License as published by
jaroslav@1189: * the Free Software Foundation, version 2 of the License.
jaroslav@1189: *
jaroslav@1189: * This program is distributed in the hope that it will be useful,
jaroslav@1189: * but WITHOUT ANY WARRANTY; without even the implied warranty of
jaroslav@1189: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
jaroslav@1189: * GNU General Public License for more details. apidesign.org
jaroslav@1189: * designates this particular file as subject to the
jaroslav@1189: * "Classpath" exception as provided by apidesign.org
jaroslav@1189: * in the License file that accompanied this code.
jaroslav@1189: *
jaroslav@1189: * You should have received a copy of the GNU General Public License
jaroslav@1189: * along with this program. Look for COPYING file in the top folder.
jaroslav@1189: * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
jaroslav@1189: */
jaroslav@1189: package org.apidesign.html.ko2brwsr;
jaroslav@1189:
jaroslav@1189: import java.lang.reflect.Method;
jaroslav@1189: import java.util.List;
jaroslav@1189: import org.apidesign.bck2brwsr.core.ExtraJavaScript;
jaroslav@1189: import org.apidesign.bck2brwsr.core.JavaScriptBody;
jaroslav@1189:
jaroslav@1194: /** Provides binding between models and bck2brwsr VM.
jaroslav@1189: *
jaroslav@1189: * @author Jaroslav Tulach
jaroslav@1189: */
jaroslav@1189: @ExtraJavaScript(resource = "/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js")
jaroslav@1194: final class Knockout {
jaroslav@1189: /** used by tests */
jaroslav@1189: static Knockout next;
jaroslav@1189: private final Object model;
jaroslav@1189:
jaroslav@1189: Knockout(Object model) {
jaroslav@1189: this.model = model == null ? this : model;
jaroslav@1189: }
jaroslav@1189:
jaroslav@1189: public static Knockout applyBindings(
jaroslav@1189: Object model, String[] propsGettersAndSetters,
jaroslav@1189: String[] methodsAndSignatures
jaroslav@1189: ) {
jaroslav@1189: applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures);
jaroslav@1189: return new Knockout(model);
jaroslav@1189: }
jaroslav@1189: public static Knockout applyBindings(
jaroslav@1189: Class modelClass, M model, String[] propsGettersAndSetters,
jaroslav@1189: String[] methodsAndSignatures
jaroslav@1189: ) {
jaroslav@1189: Knockout bindings = next;
jaroslav@1189: next = null;
jaroslav@1189: if (bindings == null) {
jaroslav@1189: bindings = new Knockout(null);
jaroslav@1189: }
jaroslav@1189: applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures);
jaroslav@1189: applyBindings(bindings);
jaroslav@1189: return bindings;
jaroslav@1189: }
jaroslav@1189:
jaroslav@1189: public void valueHasMutated(String prop) {
jaroslav@1189: valueHasMutated(model, prop);
jaroslav@1189: }
jaroslav@1189: @JavaScriptBody(args = { "self", "prop" }, body =
jaroslav@1189: "var p = self[prop]; if (p) p.valueHasMutated();"
jaroslav@1189: )
jaroslav@1189: public static void valueHasMutated(Object self, String prop) {
jaroslav@1189: }
jaroslav@1189:
jaroslav@1189:
jaroslav@1189: @JavaScriptBody(args = { "id", "ev" }, body = "ko.utils.triggerEvent(window.document.getElementById(id), ev.substring(2));")
jaroslav@1189: public static void triggerEvent(String id, String ev) {
jaroslav@1189: }
jaroslav@1189:
jaroslav@1189: @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive", "array" }, body =
jaroslav@1189: "var bnd = {\n"
jaroslav@1189: + " 'read': function() {\n"
jaroslav@1189: + " var v = model[getter]();\n"
jaroslav@1216: + " if (array) v = v.koArray(); else if (v !== null) v = v.valueOf();\n"
jaroslav@1189: + " return v;\n"
jaroslav@1189: + " },\n"
jaroslav@1189: + " 'owner': bindings\n"
jaroslav@1189: + "};\n"
jaroslav@1189: + "if (setter != null) {\n"
jaroslav@1189: + " bnd['write'] = function(val) {\n"
jaroslav@1216: + " var v = val === null ? null : val.valueOf();"
jaroslav@1216: + " model[setter](v);\n"
jaroslav@1189: + " };\n"
jaroslav@1189: + "}\n"
jaroslav@1189: + "bindings[prop] = ko['computed'](bnd);"
jaroslav@1189: )
jaroslav@1189: static void bind(
jaroslav@1189: Object bindings, Object model, String prop, String getter, String setter, boolean primitive, boolean array
jaroslav@1189: ) {
jaroslav@1189: }
jaroslav@1189:
jaroslav@1189: @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body =
jaroslav@1189: "bindings[prop] = function(data, ev) { model[sig](data, ev); };"
jaroslav@1189: )
jaroslav@1189: static void expose(
jaroslav@1189: Object bindings, Object model, String prop, String sig
jaroslav@1189: ) {
jaroslav@1189: }
jaroslav@1189:
jaroslav@1189: @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
jaroslav@1189: static void applyBindings(Object bindings) {}
jaroslav@1189:
jaroslav@1189: private static void applyImpl(
jaroslav@1189: String[] propsGettersAndSetters,
jaroslav@1189: Class> modelClass,
jaroslav@1189: Object bindings,
jaroslav@1189: Object model,
jaroslav@1189: String[] methodsAndSignatures
jaroslav@1189: ) throws IllegalStateException, SecurityException {
jaroslav@1189: for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
jaroslav@1189: try {
jaroslav@1189: Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
jaroslav@1189: bind(bindings, model, propsGettersAndSetters[i],
jaroslav@1189: propsGettersAndSetters[i + 1],
jaroslav@1189: propsGettersAndSetters[i + 2],
jaroslav@1189: getter.getReturnType().isPrimitive(),
jaroslav@1189: List.class.isAssignableFrom(getter.getReturnType()));
jaroslav@1189: } catch (NoSuchMethodException ex) {
jaroslav@1189: throw new IllegalStateException(ex.getMessage());
jaroslav@1189: }
jaroslav@1189: }
jaroslav@1189: for (int i = 0; i < methodsAndSignatures.length; i += 2) {
jaroslav@1189: expose(
jaroslav@1189: bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]);
jaroslav@1189: }
jaroslav@1189: }
jaroslav@1189: }