#249319: targetId and an ability to apply bindings to specific element on the HTML page
authorJaroslav Tulach <jtulach@netbeans.org>
Fri, 19 Dec 2014 10:57:23 +0100
changeset 920117b732d42d0
parent 919 d06b9f7976d1
child 921 7bf1ed2963cb
#249319: targetId and an ability to apply bindings to specific element on the HTML page
json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java
json-tck/src/main/java/net/java/html/json/tests/JSONTest.java
json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java
json-tck/src/main/java/net/java/html/json/tests/MinesTest.java
json-tck/src/main/java/net/java/html/json/tests/PairModel.java
json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java
json/src/main/java/net/java/html/json/Model.java
json/src/main/java/net/java/html/json/Models.java
json/src/main/java/org/netbeans/html/json/impl/Bindings.java
json/src/main/java/org/netbeans/html/json/impl/JSON.java
json/src/main/java/org/netbeans/html/json/impl/JSONList.java
json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
json/src/main/java/org/netbeans/html/json/spi/Proto.java
json/src/main/java/org/netbeans/html/json/spi/Technology.java
json/src/test/java/net/java/html/json/MapModelTest.java
json/src/test/java/net/java/html/json/ModelTest.java
json/src/test/java/net/java/html/json/PersonImpl.java
json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java
json/src/test/java/org/netbeans/html/json/impl/EmployerTest.java
json/src/test/java/org/netbeans/html/json/impl/ToDoTest.java
ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java
ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
ko4j/src/test/java/org/netbeans/html/ko4j/LessCallbacksCheck.java
src/main/javadoc/overview.html
     1.1 --- a/json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java	Thu Dec 18 09:22:47 2014 +0100
     1.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/GCKnockoutTest.java	Fri Dec 19 10:57:23 2014 +0100
     1.3 @@ -71,7 +71,7 @@
     1.4          try {
     1.5              GC m = Models.bind(new GC(), ctx);
     1.6              m.getAll().add(new Fullname("Jarda", "Tulach"));
     1.7 -            m.applyBindings();
     1.8 +            Models.applyBindings(m);
     1.9  
    1.10              int cnt = Utils.countChildren(GCKnockoutTest.class, "ul");
    1.11              assert cnt == 1 : "One child, but was " + cnt;
     2.1 --- a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Thu Dec 18 09:22:47 2014 +0100
     2.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Fri Dec 19 10:57:23 2014 +0100
     2.3 @@ -57,7 +57,7 @@
     2.4   *
     2.5   * @author Jaroslav Tulach
     2.6   */
     2.7 -@Model(className = "JSONik", properties = {
     2.8 +@Model(className = "JSONik", targetId = "", properties = {
     2.9      @Property(name = "fetched", type = Person.class),
    2.10      @Property(name = "fetchedCount", type = int.class),
    2.11      @Property(name = "fetchedResponse", type = String.class),
     3.1 --- a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Thu Dec 18 09:22:47 2014 +0100
     3.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Fri Dec 19 10:57:23 2014 +0100
     3.3 @@ -58,7 +58,7 @@
     3.4   *
     3.5   * @author Jaroslav Tulach
     3.6   */
     3.7 -@Model(className="KnockoutModel", properties={
     3.8 +@Model(className="KnockoutModel", targetId = "", properties={
     3.9      @Property(name="name", type=String.class),
    3.10      @Property(name="results", type=String.class, array = true),
    3.11      @Property(name="numbers", type=int.class, array = true),
     4.1 --- a/json-tck/src/main/java/net/java/html/json/tests/MinesTest.java	Thu Dec 18 09:22:47 2014 +0100
     4.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/MinesTest.java	Fri Dec 19 10:57:23 2014 +0100
     4.3 @@ -57,7 +57,7 @@
     4.4  
     4.5  /** Tests model of a mine field and its behavior in the browser.
     4.6   */
     4.7 -@Model(className = "Mines", properties = {
     4.8 +@Model(className = "Mines", targetId = "", properties = {
     4.9      @Property(name = "state", type = MinesTest.GameState.class),
    4.10      @Property(name = "rows", type = Row.class, array = true),
    4.11  })
     5.1 --- a/json-tck/src/main/java/net/java/html/json/tests/PairModel.java	Thu Dec 18 09:22:47 2014 +0100
     5.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/PairModel.java	Fri Dec 19 10:57:23 2014 +0100
     5.3 @@ -54,7 +54,7 @@
     5.4   *
     5.5   * @author Jaroslav Tulach
     5.6   */
     5.7 -@Model(className = "Pair", properties = {
     5.8 +@Model(className = "Pair", targetId = "", properties = {
     5.9      @Property(name = "firstName", type = String.class),
    5.10      @Property(name = "lastName", type = String.class),
    5.11      @Property(name = "next", type = Pair.class)
     6.1 --- a/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java	Thu Dec 18 09:22:47 2014 +0100
     6.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java	Fri Dec 19 10:57:23 2014 +0100
     6.3 @@ -53,7 +53,7 @@
     6.4   *
     6.5   * @author Jaroslav Tulach
     6.6   */
     6.7 -@Model(className = "WebSocketik", properties = {
     6.8 +@Model(className = "WebSocketik", targetId="", properties = {
     6.9      @Property(name = "fetched", type = Person.class),
    6.10      @Property(name = "fetchedCount", type = int.class),
    6.11      @Property(name = "open", type = int.class),
     7.1 --- a/json/src/main/java/net/java/html/json/Model.java	Thu Dec 18 09:22:47 2014 +0100
     7.2 +++ b/json/src/main/java/net/java/html/json/Model.java	Fri Dec 19 10:57:23 2014 +0100
     7.3 @@ -48,6 +48,7 @@
     7.4  import java.lang.annotation.Target;
     7.5  import java.net.URL;
     7.6  import java.util.List;
     7.7 +import org.netbeans.html.json.spi.Technology;
     7.8  
     7.9  /** Defines a model class that represents a single 
    7.10   * <a target="_blank" href="http://en.wikipedia.org/wiki/JSON">JSON</a>-like object
    7.11 @@ -122,7 +123,7 @@
    7.12   * <p>
    7.13   * In case you are using <a target="_blank" href="http://knockoutjs.com/">Knockout technology</a>
    7.14   * for Java then you can associate such model object with surrounding HTML page by
    7.15 - * calling: <code>p.applyBindings();</code>. The page can then use regular
    7.16 + * calling: <code>p.applyBindings();</code> (in case you specify {@link #tar. The page can then use regular
    7.17   * <a target="_blank" href="http://knockoutjs.com/">Knockout</a> bindings to reference your
    7.18   * model and create dynamic connection between your model in Java and 
    7.19   * live DOM structure in the browser:
    7.20 @@ -202,4 +203,22 @@
    7.21       * @return array of property definitions
    7.22       */
    7.23      Property[] properties();
    7.24 +    
    7.25 +    /** The id of an element to bind this model too. If this
    7.26 +     * property is specified an <code>applyBindings</code> method
    7.27 +     * in the model class is going to be generated which later calls
    7.28 +     * {@link Models#applyBindings(java.lang.Object, java.lang.String)}
    7.29 +     * with appropriate <code>targetId</code>. If the <code>targetId</code>
    7.30 +     * is specified as empty string, <code>null</code> value is passed
    7.31 +     * to {@link Models#applyBindings(java.lang.Object, java.lang.String)} method.
    7.32 +     * If the <code>targetId</code> is not specified at all, no public
    7.33 +     * <code>applyBindings</code> method is generated at all (a change compared
    7.34 +     * to previous versions of this API).
    7.35 +     * 
    7.36 +     * @return an empty string (means apply globally), or ID of a (usually DOM)
    7.37 +     *    element to apply this model after calling its generated
    7.38 +     *    <code>applyBindings()</code> method to
    7.39 +     * @since 1.1
    7.40 +     */
    7.41 +    String targetId() default "";
    7.42  }
     8.1 --- a/json/src/main/java/net/java/html/json/Models.java	Thu Dec 18 09:22:47 2014 +0100
     8.2 +++ b/json/src/main/java/net/java/html/json/Models.java	Fri Dec 19 10:57:23 2014 +0100
     8.3 @@ -150,11 +150,8 @@
     8.4          return JSON.find(model);
     8.5      }
     8.6      
     8.7 -    /** Apply bindings of a model class. Each model class has an
     8.8 -     * apply bindings method that "activates" it. In <em>ko4j</em> mode,
     8.9 -     * it binds the model values to the currently active page. One 
    8.10 -     * can call the generated method directly, or use this static 
    8.11 -     * helper method to perform the activation.
    8.12 +    /** Apply bindings of a model class to overall page. In <em>ko4j</em> mode,
    8.13 +     * it binds the model values to the currently active page. 
    8.14       * 
    8.15       * @param model instance of a {@link Model class}
    8.16       * @throws IllegalArgumentException if the <code>model</code> is not
    8.17 @@ -164,6 +161,22 @@
    8.18       * @since 0.7
    8.19       */
    8.20      public static void applyBindings(Object model) {
    8.21 -        JSON.applyBindings(model);
    8.22 +        JSON.applyBindings(model, null);
    8.23 +    }
    8.24 +    
    8.25 +    
    8.26 +    /** Apply bindings of a model class. In <em>ko4j</em> mode,
    8.27 +     * it binds the model values to an element on currently active page.
    8.28 +     * 
    8.29 +     * @param model instance of a {@link Model class}
    8.30 +     * @param targetId the id of the element to apply the binding to 
    8.31 +     * @throws IllegalArgumentException if the <code>model</code> is not
    8.32 +     * instance of a class generated by {@link Model model annotation}
    8.33 +     * processor.
    8.34 +     * 
    8.35 +     * @since 1.1
    8.36 +     */
    8.37 +    public static void applyBindings(Object model, String targetId) {
    8.38 +        JSON.applyBindings(model, targetId);
    8.39      }
    8.40  }
     9.1 --- a/json/src/main/java/org/netbeans/html/json/impl/Bindings.java	Thu Dec 18 09:22:47 2014 +0100
     9.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/Bindings.java	Fri Dec 19 10:57:23 2014 +0100
     9.3 @@ -42,6 +42,8 @@
     9.4   */
     9.5  package org.netbeans.html.json.impl;
     9.6  
     9.7 +import java.util.logging.Level;
     9.8 +import java.util.logging.Logger;
     9.9  import net.java.html.BrwsrCtx;
    9.10  import org.netbeans.html.json.spi.FunctionBinding;
    9.11  import org.netbeans.html.json.spi.PropertyBinding;
    9.12 @@ -53,6 +55,8 @@
    9.13   * @author Jaroslav Tulach
    9.14   */
    9.15  public final class Bindings<Data> {
    9.16 +    private static final Logger LOG = Logger.getLogger(Bindings.class.getName()); 
    9.17 +    
    9.18      private Data data;
    9.19      private final Technology<Data> bp;
    9.20  
    9.21 @@ -105,7 +109,18 @@
    9.22          }
    9.23      }
    9.24      
    9.25 -    public void applyBindings() {
    9.26 +    public void applyBindings(String id) {
    9.27 +        if (bp instanceof Technology.ApplyId) {
    9.28 +            Technology.ApplyId<Data> ai = (Technology.ApplyId<Data>) bp;
    9.29 +            ai.applyBindings(id, data);
    9.30 +            return;
    9.31 +        }
    9.32 +        if (id != null) {
    9.33 +            LOG.log(Level.WARNING, 
    9.34 +                "Technology {0} does not implement ApplyId extension. Can't apply to {1}. Applying globally.", 
    9.35 +                new Object[]{bp, id}
    9.36 +            );
    9.37 +        }
    9.38          bp.applyBindings(data);
    9.39      }
    9.40  
    10.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Thu Dec 18 09:22:47 2014 +0100
    10.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Fri Dec 19 10:57:23 2014 +0100
    10.3 @@ -296,12 +296,12 @@
    10.4          return find(object, null);
    10.5      }
    10.6      
    10.7 -    public static void applyBindings(Object object) {
    10.8 +    public static void applyBindings(Object object, String id) {
    10.9          final Proto proto = findProto(object);
   10.10          if (proto == null) {
   10.11              throw new IllegalArgumentException("Not a model: " + object.getClass());
   10.12          }
   10.13 -        proto.applyBindings();
   10.14 +        proto.applyBindings(id);
   10.15      }
   10.16      
   10.17      public static void loadJSON(
    11.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSONList.java	Thu Dec 18 09:22:47 2014 +0100
    11.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSONList.java	Fri Dec 19 10:57:23 2014 +0100
    11.3 @@ -179,7 +179,7 @@
    11.4              public void run() {
    11.5                  Bindings m = PropertyBindingAccessor.getBindings(proto, false);
    11.6                  if (m != null) {
    11.7 -                    m.valueHasMutated(name, null, null);
    11.8 +                    m.valueHasMutated(name, null, JSONList.this);
    11.9                      for (String dependant : deps) {
   11.10                          m.valueHasMutated(dependant, null, null);
   11.11                      }
    12.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Thu Dec 18 09:22:47 2014 +0100
    12.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Fri Dec 19 10:57:23 2014 +0100
    12.3 @@ -499,17 +499,31 @@
    12.4                  w.append("  }\n");
    12.5                  writeToString(props, w);
    12.6                  writeClone(className, props, w);
    12.7 -                w.write("  /** Activates this model instance in the current {@link \n"
    12.8 -                    + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. \n"
    12.9 -                    + "In case of using Knockout technology, this means to \n"
   12.10 -                    + "bind JSON like data in this model instance with Knockout tags in \n"
   12.11 -                    + "the surrounding HTML page.\n"
   12.12 -                    + "*/\n"
   12.13 -                );
   12.14 -                w.write("  public " + className + " applyBindings() {\n");
   12.15 -                w.write("    proto.applyBindings();\n");
   12.16 -                w.write("    return this;\n");
   12.17 -                w.write("  }\n");
   12.18 +                String targetId = findTargetId(e);
   12.19 +                if (targetId != null) {
   12.20 +                    w.write("  /** Activates this model instance in the current {@link \n"
   12.21 +                        + "net.java.html.json.Models#bind(java.lang.Object, net.java.html.BrwsrCtx) browser context}. \n"
   12.22 +                        + "In case of using Knockout technology, this means to \n"
   12.23 +                        + "bind JSON like data in this model instance with Knockout tags in \n"
   12.24 +                        + "the surrounding HTML page.\n"
   12.25 +                    );
   12.26 +                    if (targetId != null) {
   12.27 +                        w.write("This method binds to element '" + targetId + "' on the page\n");
   12.28 +                    }
   12.29 +                    w.write(""
   12.30 +                        + "@return <code>this</code> object\n"
   12.31 +                        + "*/\n"
   12.32 +                    );
   12.33 +                    w.write("  public " + className + " applyBindings() {\n");
   12.34 +                    w.write("    proto.applyBindings();\n");
   12.35 +    //                w.write("    proto.applyBindings(id);\n");
   12.36 +                    w.write("    return this;\n");
   12.37 +                    w.write("  }\n");
   12.38 +                } else {
   12.39 +                    w.write("  private " + className + " applyBindings() {\n");
   12.40 +                    w.write("    throw new IllegalStateException(\"Please specify targetId=\\\"\\\" in your @Model annotation\");\n");
   12.41 +                    w.write("  }\n");
   12.42 +                }
   12.43                  w.write("  public boolean equals(Object o) {\n");
   12.44                  w.write("    if (o == this) return true;\n");
   12.45                  w.write("    if (!(o instanceof " + className + ")) return false;\n");
   12.46 @@ -1757,6 +1771,21 @@
   12.47          }
   12.48      }
   12.49  
   12.50 +    private static String findTargetId(Element e) {
   12.51 +        for (AnnotationMirror m : e.getAnnotationMirrors()) {
   12.52 +            if (m.getAnnotationType().toString().equals(Model.class.getName())) {
   12.53 +                for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entrySet : m.getElementValues().entrySet()) {
   12.54 +                    ExecutableElement key = entrySet.getKey();
   12.55 +                    AnnotationValue value = entrySet.getValue();
   12.56 +                    if ("targetId()".equals(key.toString())) {
   12.57 +                        return value.toString();
   12.58 +                    }
   12.59 +                }
   12.60 +            }
   12.61 +        }
   12.62 +        return null;
   12.63 +    }
   12.64 +
   12.65      private static class Prprt {
   12.66          private final Element e;
   12.67          private final AnnotationMirror tm;
    13.1 --- a/json/src/main/java/org/netbeans/html/json/spi/Proto.java	Thu Dec 18 09:22:47 2014 +0100
    13.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/Proto.java	Fri Dec 19 10:57:23 2014 +0100
    13.3 @@ -193,7 +193,21 @@
    13.4       * of the current model to the <em>body</em> element of the page.
    13.5       */
    13.6      public void applyBindings() {
    13.7 -        initBindings().applyBindings();
    13.8 +        initBindings().applyBindings(null);
    13.9 +    }
   13.10 +    
   13.11 +    /** Initializes the associated model to the specified element's subtree.
   13.12 +     * The technology is taken from the current {@link #getContext() context} and
   13.13 +     * in case of <em>knockout.js</em> applies given bindings 
   13.14 +     * of the current model to the element of the page with 'id' attribute
   13.15 +     * set to the specified <code>id</code> value.
   13.16 +     * 
   13.17 +     * @param id the id of element to apply the binding to
   13.18 +     * @since 1.1
   13.19 +     * @see Technology.ApplyId
   13.20 +     */
   13.21 +    public void applyBindings(String id) {
   13.22 +        initBindings().applyBindings(id);
   13.23      }
   13.24      
   13.25      /** Invokes the provided runnable in the {@link #getContext() context}
    14.1 --- a/json/src/main/java/org/netbeans/html/json/spi/Technology.java	Thu Dec 18 09:22:47 2014 +0100
    14.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/Technology.java	Fri Dec 19 10:57:23 2014 +0100
    14.3 @@ -173,4 +173,22 @@
    14.4           */
    14.5          public void valueHasMutated(D data, String propertyName, Object oldValue, Object newValue);
    14.6      }
    14.7 +    
    14.8 +    /** Apply technology bindings at selected subtree of the HTML page.
    14.9 +     * Can be accessed via {@link Proto#applyBindings(java.lang.String)} or
   14.10 +     * via method <code>applyBindings(String)</code> generated when one
   14.11 +     * is using the {@link Model} annotation.
   14.12 +     * 
   14.13 +     * @param <D> the internal data for the technology
   14.14 +     * @since 1.1
   14.15 +     */
   14.16 +    public static interface ApplyId<D> extends Technology<D> {
   14.17 +        /** Applies given data to current context (usually an element on an 
   14.18 +         * HTML page).
   14.19 +         * 
   14.20 +         * @param id the id of an element to apply the data to
   14.21 +         * @param data the data to apply
   14.22 +         */
   14.23 +        public void applyBindings(String id, D data);
   14.24 +    }
   14.25  }
    15.1 --- a/json/src/test/java/net/java/html/json/MapModelTest.java	Thu Dec 18 09:22:47 2014 +0100
    15.2 +++ b/json/src/test/java/net/java/html/json/MapModelTest.java	Fri Dec 19 10:57:23 2014 +0100
    15.3 @@ -74,8 +74,20 @@
    15.4              register(Transfer.class, t, 1).build();
    15.5      }
    15.6      
    15.7 +    @Test public void isThereNoApplyBinding() throws Exception {
    15.8 +        try {
    15.9 +            Person.class.getMethod("applyBindings");
   15.10 +        } catch (NoSuchMethodException ex) {
   15.11 +            // OK
   15.12 +            return;
   15.13 +        }
   15.14 +        fail("There should be no applyBindings() method");
   15.15 +    }
   15.16 +    
   15.17      @Test public void isThereABinding() throws Exception {
   15.18 -        Person p = Models.bind(new Person(), c).applyBindings();
   15.19 +        Person p = Models.bind(new Person(), c);
   15.20 +        Models.applyBindings(p);
   15.21 +        assertNull(t.appliedId, "Applied globally");
   15.22          p.setFirstName("Jarda");
   15.23          
   15.24          Map m = (Map)Models.toRaw(p);
   15.25 @@ -94,6 +106,11 @@
   15.26          assertEquals(o.changes, 2, "Snd change");
   15.27      }
   15.28      
   15.29 +    @Test public void applyLocally() throws Exception {
   15.30 +        Person p = Models.bind(new Person(), c);
   15.31 +        Models.applyBindings(p, "local");
   15.32 +        assertEquals(t.appliedId, "local", "Applied locally");
   15.33 +    }
   15.34      
   15.35      @Test public void dontNotifySameProperty() throws Exception {
   15.36          Person p = Models.bind(new Person(), c);
   15.37 @@ -293,7 +310,9 @@
   15.38      }
   15.39      
   15.40      static final class MapTechnology 
   15.41 -    implements Technology<Map<String,One>>, Transfer {
   15.42 +    implements Technology.ApplyId<Map<String,One>>, Transfer {
   15.43 +        private Map<String, One> appliedData;
   15.44 +        private String appliedId;
   15.45  
   15.46          @Override
   15.47          public Map<String, One> wrapModel(Object model) {
   15.48 @@ -329,6 +348,7 @@
   15.49  
   15.50          @Override
   15.51          public void applyBindings(Map<String, One> data) {
   15.52 +            throw new UnsupportedOperationException("Never called!");
   15.53          }
   15.54  
   15.55          @Override
   15.56 @@ -370,5 +390,11 @@
   15.57          public void runSafe(Runnable r) {
   15.58              throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
   15.59          }
   15.60 +
   15.61 +        @Override
   15.62 +        public void applyBindings(String id, Map<String, One> data) {
   15.63 +            this.appliedId = id;
   15.64 +            this.appliedData = data;
   15.65 +        }
   15.66      }
   15.67  }
    16.1 --- a/json/src/test/java/net/java/html/json/ModelTest.java	Thu Dec 18 09:22:47 2014 +0100
    16.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java	Fri Dec 19 10:57:23 2014 +0100
    16.3 @@ -60,7 +60,7 @@
    16.4   *
    16.5   * @author Jaroslav Tulach
    16.6   */
    16.7 -@Model(className = "Modelik", properties = {
    16.8 +@Model(className = "Modelik", targetId = "", properties = {
    16.9      @Property(name = "value", type = int.class),
   16.10      @Property(name = "count", type = int.class),
   16.11      @Property(name = "unrelated", type = long.class),
    17.1 --- a/json/src/test/java/net/java/html/json/PersonImpl.java	Thu Dec 18 09:22:47 2014 +0100
    17.2 +++ b/json/src/test/java/net/java/html/json/PersonImpl.java	Fri Dec 19 10:57:23 2014 +0100
    17.3 @@ -92,7 +92,7 @@
    17.4          }
    17.5      }
    17.6      
    17.7 -    @Model(className = "People", properties = {
    17.8 +    @Model(className = "People", targetId="myPeople", properties = {
    17.9          @Property(array = true, name = "info", type = Person.class),
   17.10          @Property(array = true, name = "nicknames", type = String.class),
   17.11          @Property(array = true, name = "age", type = int.class),
    18.1 --- a/json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java	Thu Dec 18 09:22:47 2014 +0100
    18.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java	Fri Dec 19 10:57:23 2014 +0100
    18.3 @@ -77,7 +77,7 @@
    18.4              register(Transfer.class, t, 1).build();
    18.5      }
    18.6      
    18.7 -    @Model(className = "MyX", properties = {
    18.8 +    @Model(className = "MyX", targetId = "anythingX", properties = {
    18.9          @Property(name = "one", type = MyY.class),
   18.10          @Property(name = "all", type = MyY.class, array = true)
   18.11      })
   18.12 @@ -253,7 +253,8 @@
   18.13      @Test public void firstChangeInArrayNotifiedTransitively() throws Exception {
   18.14          MyOverall p = Models.bind(
   18.15              new MyOverall(new MyX(new MyY("Ahoj", 0), new MyY("Hi", 333), new MyY("Hello", 999))
   18.16 -        ), c).applyBindings();
   18.17 +        ), c);
   18.18 +        Models.applyBindings(p);
   18.19          
   18.20          Map m = (Map)Models.toRaw(p);
   18.21          Object v = m.get("valueAccross");
    19.1 --- a/json/src/test/java/org/netbeans/html/json/impl/EmployerTest.java	Thu Dec 18 09:22:47 2014 +0100
    19.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/EmployerTest.java	Fri Dec 19 10:57:23 2014 +0100
    19.3 @@ -60,6 +60,6 @@
    19.4      @Test public void preLoadsTheClass() {
    19.5          Employer em = Models.fromRaw(BrwsrCtx.EMPTY, Employer.class, this);
    19.6          Assert.assertNotNull(em, "Class loaded");
    19.7 -        em.applyBindings();
    19.8 +        Models.applyBindings(em);
    19.9      }
   19.10  }
    20.1 --- a/json/src/test/java/org/netbeans/html/json/impl/ToDoTest.java	Thu Dec 18 09:22:47 2014 +0100
    20.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/ToDoTest.java	Fri Dec 19 10:57:23 2014 +0100
    20.3 @@ -105,7 +105,8 @@
    20.4                      new Todo("First", false),
    20.5                      new Todo("2nd", true),
    20.6                      new Todo("Third", false)
    20.7 -                ), c).applyBindings();
    20.8 +                ), c);
    20.9 +        Models.applyBindings(ui);
   20.10  
   20.11          Map m = (Map) Models.toRaw(ui);
   20.12          Object v = m.get("remaining");
    21.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java	Thu Dec 18 09:22:47 2014 +0100
    21.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java	Fri Dec 19 10:57:23 2014 +0100
    21.3 @@ -57,7 +57,7 @@
    21.4   */
    21.5  @Contexts.Id("ko4j")
    21.6  final class KOTech
    21.7 -implements Technology.BatchInit<Object>, Technology.ValueMutated<Object> {
    21.8 +implements Technology.BatchInit<Object>, Technology.ValueMutated<Object>, Technology.ApplyId<Object> {
    21.9      private Object[] jsObjects;
   21.10      private int jsIndex;
   21.11  
   21.12 @@ -131,7 +131,11 @@
   21.13  
   21.14      @Override
   21.15      public void applyBindings(Object data) {
   21.16 -        Object ko = Knockout.applyBindings(data);
   21.17 +        applyBindings(null, data);
   21.18 +    }
   21.19 +    @Override
   21.20 +    public void applyBindings(String id, Object data) {
   21.21 +        Object ko = Knockout.applyBindings(id, data);
   21.22          if (ko instanceof Knockout) {
   21.23              ((Knockout)ko).hold();
   21.24          }
    22.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java	Thu Dec 18 09:22:47 2014 +0100
    22.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java	Fri Dec 19 10:57:23 2014 +0100
    22.3 @@ -131,12 +131,14 @@
    22.4          Object model, String prop, Object oldValue, Object newValue
    22.5      );
    22.6  
    22.7 -    @JavaScriptBody(args = { "bindings" }, body = 
    22.8 -        "ko['cleanNode'](window['document']['body']);\n" +
    22.9 -        "ko['applyBindings'](bindings);\n" +
   22.10 +    @JavaScriptBody(args = { "id", "bindings" }, body = 
   22.11 +        "var d = window['document'];\n" +
   22.12 +        "var e = id ? d['getElementById'](id) : d['body'];\n" +
   22.13 +        "ko['cleanNode'](e);\n" +
   22.14 +        "ko['applyBindings'](bindings, e);\n" +
   22.15          "return bindings['ko4j'];\n"
   22.16      )
   22.17 -    native static Object applyBindings(Object bindings);
   22.18 +    native static Object applyBindings(String id, Object bindings);
   22.19      
   22.20      @JavaScriptBody(args = { "cnt" }, body = 
   22.21          "var arr = new Array(cnt);\n" +
    23.1 --- a/ko4j/src/test/java/org/netbeans/html/ko4j/LessCallbacksCheck.java	Thu Dec 18 09:22:47 2014 +0100
    23.2 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/LessCallbacksCheck.java	Fri Dec 19 10:57:23 2014 +0100
    23.3 @@ -53,7 +53,7 @@
    23.4   *
    23.5   * @author Jaroslav Tulach
    23.6   */
    23.7 -@Model(className = "LessCalls", properties = {
    23.8 +@Model(className = "LessCalls", targetId = "", properties = {
    23.9      @Property(name = "value", type = int.class)
   23.10  })
   23.11  public class LessCallbacksCheck {
    24.1 --- a/src/main/javadoc/overview.html	Thu Dec 18 09:22:47 2014 +0100
    24.2 +++ b/src/main/javadoc/overview.html	Fri Dec 19 10:57:23 2014 +0100
    24.3 @@ -94,6 +94,27 @@
    24.4              {@link org.netbeans.html.json.spi.Transfer Java based JSON} and 
    24.5              {@link org.netbeans.html.json.spi.WSTransfer WebSocket} implementations
    24.6              under the name <b>tyrus</b>.
    24.7 +        </p>
    24.8 +        <p>
    24.9 +            A particular DOM subtree
   24.10 +            that a <a target="_blank" href="http://knockoutjs.com">knockout.js</a> model gets
   24.11 +            applied to can be selected by using 
   24.12 +            {@link net.java.html.json.Models#applyBindings(java.lang.Object,%20java.lang.String)
   24.13 +            Models.applyBindings(m, id)} with an id of an HTML element.
   24.14 +            There is new {@link net.java.html.json.Model#targetId()} attribute 
   24.15 +            which controls behavior of the generated <code>applyBindings</code> method. 
   24.16 +            If <em>specified and non-empty</em>, then the generated method
   24.17 +            will call {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)}
   24.18 +            with <code>this</code> and the provided {@link net.java.html.json.Model#targetId() target id}.
   24.19 +            If <em>specified, but left empty</em>, then the generated method
   24.20 +            calls {@link net.java.html.json.Models#applyBindings(java.lang.Object)}.
   24.21 +            <em>If unspecified</em>, the method will <b>not</b> be generated at all
   24.22 +            (a change with respect to older versions). However one can
   24.23 +            still use {@link net.java.html.json.Models#applyBindings(java.lang.Object)}
   24.24 +            or {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)}
   24.25 +            to perform the association of any model with the page element.
   24.26 +        </ul>
   24.27 +        <p>
   24.28              Memory model when using Knockout bindings has been improved
   24.29              (required additions of two new methods:
   24.30              {@link org.netbeans.html.json.spi.PropertyBinding#weak()} and