You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jt...@apache.org on 2017/09/03 12:48:36 UTC

[03/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusFX.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusFX.java b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusFX.java
new file mode 100644
index 0000000..1bc5613
--- /dev/null
+++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusFX.java
@@ -0,0 +1,125 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.wstyrus;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javafx.application.Platform;
+import org.netbeans.html.boot.impl.FnContext;
+import org.netbeans.html.boot.spi.Fn;
+import org.testng.ITest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class TyrusFX implements ITest, Runnable {
+    private final Fn.Presenter p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+    private int count;
+
+    TyrusFX(Fn.Presenter p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            Platform.runLater(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        try {
+            FnContext.currentPresenter(p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                if (count++ < 10000) {
+                    notify = false;
+                    try {
+                        Thread.sleep(100);
+                    } catch (Exception ex1) {
+                        // ignore and continue
+                    }
+                    Platform.runLater(this);
+                    return;
+                }
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            FnContext.currentPresenter(null);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java
new file mode 100644
index 0000000..9f20e57
--- /dev/null
+++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java
@@ -0,0 +1,219 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.wstyrus;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.BrowserBuilder;
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.spi.WSTransfer;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.netbeans.html.boot.impl.FnContext;
+import org.netbeans.html.ko4j.KO4J;
+import org.openide.util.lookup.ServiceProvider;
+import org.testng.Assert;
+import static org.testng.Assert.*;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = KnockoutTCK.class)
+public final class TyrusKnockoutTest extends KnockoutTCK {
+    private static Class<?> browserClass;
+    private static Fn.Presenter browserContext;
+    
+    public TyrusKnockoutTest() {
+    }
+    
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        Class[] arr = testClasses();
+        for (int i = 0; i < arr.length; i++) {
+            assertEquals(
+                arr[i].getClassLoader(),
+                TyrusKnockoutTest.class.getClassLoader(),
+                "All classes loaded by the same classloader"
+            );
+        }
+        
+        URI uri = TyrusDynamicHTTP.initServer();
+    
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(TyrusKnockoutTest.class).
+            loadPage(uri.toString()).
+            invoke("initialized");
+        
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                bb.showAndWait();
+            }
+        });
+        
+        ClassLoader l = getClassLoader();
+        List<Object> res = new ArrayList<Object>();
+        for (int i = 0; i < arr.length; i++) {
+            Class<?> c = Class.forName(arr[i].getName(), true, l);
+            Class<? extends Annotation> koTest = 
+                c.getClassLoader().loadClass(KOTest.class.getName()).
+                asSubclass(Annotation.class);
+            for (Method m : c.getMethods()) {
+                if (m.getAnnotation(koTest) != null) {
+                    res.add(new TyrusFX(browserContext, m));
+                }
+            }
+        }
+        return res.toArray();
+    }
+
+    static synchronized ClassLoader getClassLoader() throws InterruptedException {
+        while (browserClass == null) {
+            TyrusKnockoutTest.class.wait();
+        }
+        return browserClass.getClassLoader();
+    }
+    
+    public static synchronized void initialized(Class<?> browserCls) throws Exception {
+        browserClass = browserCls;
+        browserContext = Fn.activePresenter();
+        TyrusKnockoutTest.class.notifyAll();
+    }
+    
+    public static void initialized() throws Exception {
+        Assert.assertSame(
+            TyrusKnockoutTest.class.getClassLoader(),
+            ClassLoader.getSystemClassLoader(),
+            "No special classloaders"
+        );
+        TyrusKnockoutTest.initialized(TyrusKnockoutTest.class);
+    }
+    
+    @Override
+    public BrwsrCtx createContext() {
+        KO4J ko = new KO4J(browserContext);
+        TyrusContext tc = new TyrusContext();
+        Contexts.Builder cb = Contexts.newBuilder().
+            register(Technology.class, ko.knockout(), 10).
+            register(Transfer.class, tc, 10).
+            register(WSTransfer.class, tc, 10).
+            register(Executor.class, (Executor)browserContext, 10).
+            register(Fn.Presenter.class, (Fn.Presenter)browserContext, 10);
+                
+        return cb.build();
+    }
+
+    @Override
+    public Object createJSON(Map<String, Object> values) {
+        JSONObject json = new JSONObject();
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            try {
+                json.put(entry.getKey(), entry.getValue());
+            } catch (JSONException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+        return json;
+    }
+
+    @Override
+    @JavaScriptBody(args = { "s", "args" }, body = ""
+        + "var f = new Function(s); "
+        + "return f.apply(null, args);"
+    )
+    public native Object executeScript(String script, Object[] arguments);
+
+    @JavaScriptBody(args = {  }, body = 
+          "var h;"
+        + "if (!!window && !!window.location && !!window.location.href)\n"
+        + "  h = window.location.href;\n"
+        + "else "
+        + "  h = null;"
+        + "return h;\n"
+    )
+    private static native String findBaseURL();
+    
+    @Override
+    public URI prepareURL(String content, String mimeType, String[] parameters) {
+        try {
+            final URL baseURL = new URL(findBaseURL());
+            StringBuilder sb = new StringBuilder();
+            sb.append("/dynamic?mimeType=").append(mimeType);
+            for (int i = 0; i < parameters.length; i++) {
+                sb.append("&param" + i).append("=").append(parameters[i]);
+            }
+            String mangle = content.replace("\n", "%0a")
+                .replace("\"", "\\\"").replace(" ", "%20");
+            sb.append("&content=").append(mangle);
+
+            URL query = new URL(baseURL, sb.toString());
+            URLConnection c = query.openConnection();
+            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
+            URI connectTo = new URI(br.readLine());
+            return connectTo;
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/test/resources/org/netbeans/html/wstyrus/test.html
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/test/resources/org/netbeans/html/wstyrus/test.html b/ko-ws-tyrus/src/test/resources/org/netbeans/html/wstyrus/test.html
new file mode 100644
index 0000000..f4e58fe
--- /dev/null
+++ b/ko-ws-tyrus/src/test/resources/org/netbeans/html/wstyrus/test.html
@@ -0,0 +1,56 @@
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Tyrus WebSockets Execution Harness</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <h1>Tyrus WebSockets Execution Harness</h1>
+    </body>
+    <script></script>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/pom.xml
----------------------------------------------------------------------
diff --git a/ko4j/pom.xml b/ko4j/pom.xml
new file mode 100644
index 0000000..5ba42a3
--- /dev/null
+++ b/ko4j/pom.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 Oracle and/or its affiliates. All rights reserved.
+
+    Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+    Other names may be trademarks of their respective owners.
+
+    The contents of this file are subject to the terms of either the GNU
+    General Public License Version 2 only ("GPL") or the Common
+    Development and Distribution License("CDDL") (collectively, the
+    "License"). You may not use this file except in compliance with the
+    License. You can obtain a copy of the License at
+    http://www.netbeans.org/cddl-gplv2.html
+    or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+    specific language governing permissions and limitations under the
+    License.  When distributing the software, include this License Header
+    Notice in each file and include the License file at
+    nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+    particular file as subject to the "Classpath" exception as provided
+    by Oracle in the GPL Version 2 section of the License file that
+    accompanied this code. If applicable, add the following below the
+    License Header, with the fields enclosed by brackets [] replaced by
+    your own identifying information:
+    "Portions Copyrighted [year] [name of copyright owner]"
+
+    Contributor(s):
+
+    The Original Software is NetBeans. The Initial Developer of the Original
+    Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+
+    If you wish your version of this file to be governed by only the CDDL
+    or only the GPL Version 2, indicate your decision by adding
+    "[Contributor] elects to include this software in this distribution
+    under the [CDDL or GPL Version 2] license." If you do not indicate a
+    single choice of license, a recipient has the option to distribute
+    your version of this file under either the CDDL, the GPL Version 2 or
+    to extend the choice of license to its licensees as provided above.
+    However, if you add GPL Version 2 code and therefore, elected the GPL
+    Version 2 license, then the option applies only if the new code is
+    made subject to such option by the copyright holder.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>ko4j</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>Knockout.js for Java</name>
+  <url>http://maven.apache.org</url>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <bundleSymbolicName>org.netbeans.html.ko4j</bundleSymbolicName>
+    <netbeans.compile.on.save>none</netbeans.compile.on.save> 
+  </properties>
+  <build>
+      <plugins>
+          <plugin>
+              <groupId>org.apache.felix</groupId>
+              <artifactId>maven-bundle-plugin</artifactId>
+              <configuration>
+                  <instructions>
+                      <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
+                      <Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.netbeans.html.context.spi.Contexts$Provider</Provide-Capability>
+                  </instructions>
+              </configuration>
+          </plugin>
+          <plugin>
+              <groupId>org.netbeans.html</groupId>
+              <artifactId>html4j-maven-plugin</artifactId>
+          </plugin>
+          <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-javadoc-plugin</artifactId>
+              <configuration>
+                  <skip>false</skip>
+              </configuration>
+          </plugin>
+          <plugin>
+              <inherited>false</inherited>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-dependency-plugin</artifactId>
+              <version>2.10</version>
+              <executions>
+                  <execution>
+                      <id>knockout.js</id>
+                      <goals>
+                          <goal>unpack</goal>
+                      </goals>
+                      <phase>generate-resources</phase>
+                      <configuration>
+                          <artifactItems>
+                              <artifactItem>
+                                  <groupId>org.netbeans.html</groupId>
+                                  <artifactId>ko4j</artifactId>
+                                  <version>1.3</version>
+                                  <type>jar</type>
+                                  <overWrite>false</overWrite>
+                                  <outputDirectory>${project.build.directory}/classes</outputDirectory>
+                                  <includes>**/*.js</includes>
+                              </artifactItem>
+                          </artifactItems>
+                          <artifact>
+                          </artifact>
+                      </configuration>
+                  </execution>
+              </executions>
+          </plugin>
+      </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.netbeans.html</groupId>
+      <artifactId>net.java.html.json</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.json.tck</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.netbeans.html</groupId>
+      <artifactId>net.java.html.boot</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.boot.fx</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-http-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-websockets-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+        <groupId>org.glassfish.grizzly</groupId>
+        <artifactId>grizzly-http-servlet</artifactId>
+        <version>${grizzly.version}</version>
+        <scope>test</scope>
+    </dependency>    
+    <dependency>
+        <groupId>javax.servlet</groupId>
+        <artifactId>javax.servlet-api</artifactId>
+        <scope>test</scope>
+    </dependency>
+  </dependencies>
+    <description>Binds net.java.html.json APIs together with knockout.js</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java
new file mode 100644
index 0000000..8aa98c5
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java
@@ -0,0 +1,149 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.util.logging.Logger;
+import net.java.html.json.Model;
+import net.java.html.json.OnReceive;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.context.spi.Contexts.Provider;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.spi.WSTransfer;
+import org.openide.util.lookup.ServiceProvider;
+
+/** Support for <a href="http://knockoutjs.com">knockout.js</a>
+ * and its Java binding via {@link Model model classes}.
+ * Registers {@link Provider}, so {@link java.util.ServiceLoader} can find it.
+ * The provider registers following technologies:
+ * <ul>
+ * <li><b>ko4j</b> - bindings for <a href="http://knockoutjs.com">knockout.js</a>
+ *   and the classes generated by the {@link Model} annotation.
+ * </li>
+ * <li><b>xhr</b> - <a href="http://www.w3.org/TR/XMLHttpRequest/">XMLHttpRequest</a>
+ *   based implementation for <em>REST</em> calls 
+ *   (<b>GET</b>, <b>PUT</b>, <b>POST</b>, <b>DELETE</b> methods) 
+ *   for {@link OnReceive} annotation.
+ * </li>
+ * <li><b>websocket</b> - 
+ *   native browser <a href="http://www.w3.org/TR/websockets/">websockets</a>
+ *   based implementation for {@link OnReceive} annotation and its <b>WebSocket</b>
+ *   subprotocol.
+ * </li>
+ * </ul>
+ *
+ * @author Jaroslav Tulach
+ * @since 0.7
+ */
+@ServiceProvider(service = Provider.class)
+public final class KO4J implements Provider {
+    static final Logger LOG = Logger.getLogger(KOSockets.class.getName());
+    private KOTech ko4j;
+    private KOTransfer trans;
+    private KOSockets socks;
+    
+    public KO4J() {
+        this(null);
+    }
+
+    @Deprecated
+    public KO4J(Fn.Presenter presenter) {
+    }
+    
+    /** Return instance of the knockout.js for Java technology.
+     * @return non-null instance
+     */
+    public Technology knockout() {
+        if (ko4j == null) {
+            ko4j = new KOTech();
+        }
+        return ko4j;
+    }
+    
+    /** Browser based implementation of transfer interface. Uses
+     * browser method to convert string to JSON.
+     * 
+     * @return non-null instance
+     */
+    public Transfer transfer() {
+        if (trans == null) {
+            trans = new KOTransfer();
+        }
+        return trans;
+    }
+    
+    /** Returns browser based implementation of websocket transfer.
+     * If available (for example JavaFX WebView on JDK7 does not have
+     * this implementation).
+     * 
+     * @return an instance or <code>null</code>, if there is no
+     *   <code>WebSocket</code> object in the browser
+     */
+    public WSTransfer<?> websockets() {
+        if (!KOSockets.areWebSocketsSupported()) {
+            return null;
+        }
+        if (socks == null) {
+            socks = new KOSockets();
+        }
+        return socks;
+    }
+
+    /** Registers technologies at position 100:
+     * <ul>
+     *   <li>{@link #knockout()}</li>
+     *   <li>{@link #transfer()}</li>
+     *   <li>{@link #websockets()()} - if browser supports web sockets</li>
+     * </ul>
+     * @param context the context to register to
+     * @param requestor the class requesting the registration
+     */
+    @Override
+    public void fillContext(Contexts.Builder context, Class<?> requestor) {
+        context.register(Technology.class, knockout(), 100);
+        context.register(Transfer.class, transfer(), 100);
+        context.register(WSTransfer.class, websockets(), 100);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java
new file mode 100644
index 0000000..bb7c49e
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java
@@ -0,0 +1,81 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.WSTransfer;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ *
+ * @author Jaroslav Tulach
+ */
+@Contexts.Id("websocket")
+final class KOSockets
+implements WSTransfer<LoadWS> {
+    KOSockets() {
+    }
+    
+    @Override
+    public LoadWS open(String url, JSONCall onReply) {
+        return new LoadWS(onReply, url);
+    }
+
+    @Override
+    public void send(LoadWS socket, JSONCall data) {
+        socket.send(data);
+    }
+
+    @Override
+    public void close(LoadWS socket) {
+        socket.close();
+    }
+
+    @JavaScriptBody(args = {}, body = "if (window['WebSocket']) return true; else return false;")
+    static final boolean areWebSocketsSupported() {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java
new file mode 100644
index 0000000..b598f82
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java
@@ -0,0 +1,176 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.PropertyBinding;
+import org.netbeans.html.json.spi.Technology;
+import static org.netbeans.html.ko4j.KO4J.LOG;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ * <p>
+ *
+ * @author Jaroslav Tulach
+ */
+@Contexts.Id("ko4j")
+final class KOTech
+implements Technology.BatchCopy<Object>, Technology.ValueMutated<Object>, Technology.ApplyId<Object> {
+    private Object[] jsObjects;
+    private int jsIndex;
+
+    public KOTech() {
+    }
+    
+    @Override
+    public Object wrapModel(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr) {
+        return createKO(model, copyFrom, propArr, funcArr, null);
+    }
+
+    final Object createKO(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr, Knockout[] ko) {
+        String[] propNames = new String[propArr.length];
+        Number[] propInfo = new Number[propArr.length];
+        Object[] propValues = new Object[propArr.length];
+        for (int i = 0; i < propNames.length; i++) {
+            propNames[i] = propArr[i].getPropertyName();
+            int info =
+                (propArr[i].isReadOnly() ? 1 : 0) +
+                (propArr[i].isConstant()? 2 : 0);
+            propInfo[i] = info;
+            Object value = propArr[i].getValue();
+            if (value instanceof Enum) {
+                value = value.toString();
+            }
+            propValues[i] = value;
+        }
+        String[] funcNames = new String[funcArr.length];
+        for (int i = 0; i < funcNames.length; i++) {
+            funcNames[i] = funcArr[i].getFunctionName();
+        }
+        Object ret = getJSObject();
+        Knockout newKO = new Knockout(model, ret, propArr, funcArr);
+        if (ko != null) {
+            ko[0] = newKO;
+        }
+        newKO.wrapModel(
+            ret, copyFrom,
+            propNames, propInfo, propValues, funcNames
+        );
+        return ret;
+    }
+    
+    private Object getJSObject() {
+        int len = 64;
+        if (jsObjects != null && jsIndex < (len = jsObjects.length)) {
+            Object ret = jsObjects[jsIndex];
+            jsObjects[jsIndex] = null;
+            jsIndex++;
+            return ret;
+        }
+        jsObjects = Knockout.allocJS(len * 2);
+        jsIndex = 1;
+        Object ret = jsObjects[0];
+        jsObjects[0] = null;
+        return ret;
+    }
+    
+    @Override
+    public Object wrapModel(Object model) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void bind(PropertyBinding b, Object model, Object data) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void valueHasMutated(Object data, String propertyName) {
+        Knockout.cleanUp();
+        Knockout.valueHasMutated(data, propertyName, null, null);
+    }
+    
+    @Override
+    public void valueHasMutated(Object data, String propertyName, Object oldValue, Object newValue) {
+        Knockout.cleanUp();
+        if (newValue instanceof Enum) {
+            newValue = newValue.toString();
+        }
+        Knockout.valueHasMutated(data, propertyName, oldValue, newValue);
+    }
+
+    @Override
+    public void expose(FunctionBinding fb, Object model, Object d) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void applyBindings(Object data) {
+        applyBindings(null, data);
+    }
+    @Override
+    public void applyBindings(String id, Object data) {
+        Object ko = Knockout.applyBindings(id, data);
+        if (ko instanceof Knockout) {
+            ((Knockout)ko).hold();
+        }
+    }
+
+    @Override
+    public Object wrapArray(Object[] arr) {
+        return arr;
+    }
+    
+    @Override
+    public void runSafe(final Runnable r) {
+        LOG.warning("Technology.runSafe has been deprecated. Use BrwsrCtx.execute!");
+        r.run();
+    }    
+
+    @Override
+    public <M> M toModel(Class<M> modelClass, Object data) {
+        return modelClass.cast(Knockout.toModel(data));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java
new file mode 100644
index 0000000..a7ed847
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java
@@ -0,0 +1,164 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.Transfer;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ * <p>
+ *
+ * @author Jaroslav Tulach
+ */
+@Contexts.Id("xhr")
+final class KOTransfer
+implements Transfer {
+    KOTransfer() {
+    }
+
+    @Override
+    public void extract(Object obj, String[] props, Object[] values) {
+        if (obj instanceof JSObjToStr) {
+            obj = ((JSObjToStr)obj).obj;
+        }
+        LoadJSON.extractJSON(obj, props, values);
+    }
+
+    @Override
+    public void loadJSON(final JSONCall call) {
+        if (call.isJSONP()) {
+            String me = LoadJSON.createJSONP(call);
+            LoadJSON.loadJSONP(call.composeURL(me), me);
+        } else {
+            String data = null;
+            if (call.isDoOutput()) {
+                try {
+                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                    call.writeData(bos);
+                    data = new String(bos.toByteArray(), "UTF-8");
+                } catch (IOException ex) {
+                    call.notifyError(ex);
+                }
+            }
+            List<String> headerPairs = new ArrayList<String>();
+            String h = call.getHeaders();
+            if (h != null) {
+                int pos = 0;
+                while (pos < h.length()) {
+                    int tagEnd = h.indexOf(':', pos);
+                    if (tagEnd == -1) {
+                        break;
+                    }
+                    int r = h.indexOf('\r', tagEnd);
+                    int n = h.indexOf('\n', tagEnd);
+                    if (r == -1) {
+                        r = h.length();
+                    }
+                    if (n == -1) {
+                        n = h.length();
+                    }
+                    headerPairs.add(h.substring(pos, tagEnd).trim());
+                    headerPairs.add(h.substring(tagEnd + 1, Math.min(r, n)).trim());
+                    pos = Math.max(r, n);
+                }
+            }
+            LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data, headerPairs.toArray());
+        }
+    }
+
+    @Override
+    public Object toJSON(InputStream is) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        InputStreamReader r = new InputStreamReader(is);
+        for (;;) {
+            int ch = r.read();
+            if (ch == -1) {
+                break;
+            }
+            sb.append((char)ch);
+        }
+        return LoadJSON.parse(sb.toString());
+    }
+
+    static void notifySuccess(Object done, Object str, Object data) {
+        Object notifyObj;
+        if (data instanceof Object[]) {
+            Object[] arr = (Object[]) data;
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = new JSObjToStr(str, arr[i]);
+            }
+            notifyObj = arr;
+        } else {
+            notifyObj = new JSObjToStr(str, data);
+        }
+        ((JSONCall)done).notifySuccess(notifyObj);
+    }
+
+    static void notifyError(Object done, Object msg) {
+        ((JSONCall)done).notifyError(new Exception(msg.toString()));
+    }
+
+    private static final class JSObjToStr {
+        final String str;
+        final Object obj;
+
+        public JSObjToStr(Object str, Object obj) {
+            this.str = str == null ? "" : str.toString();
+            this.obj = obj;
+        }
+
+        @Override
+        public String toString() {
+            return str;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
new file mode 100644
index 0000000..3479068
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
@@ -0,0 +1,277 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.js.JavaScriptResource;
+import net.java.html.json.Model;
+import org.netbeans.html.json.spi.FunctionBinding;
+import org.netbeans.html.json.spi.PropertyBinding;
+
+/** This is an implementation package - just
+ * include its JAR on classpath and use official {@link Context} API
+ * to access the functionality.
+ * <p>
+ * Provides binding between {@link Model models} and knockout.js running
+ * inside a JavaFX WebView. 
+ *
+ * @author Jaroslav Tulach
+ */
+@JavaScriptResource("knockout-3.4.0.js")
+final class Knockout extends WeakReference<Object> {
+    private static final ReferenceQueue<Object> QUEUE = new ReferenceQueue();
+    private static final Set<Knockout> active = Collections.synchronizedSet(new HashSet<Knockout>());
+
+    @JavaScriptBody(args = {"object", "property"}, body = 
+        "var ret;\n" + 
+        "if (property === null) ret = object;\n" + 
+        "else if (object === null) ret = null;\n" + 
+        "else ret = object[property];\n" + 
+        "return ret ? ko.utils.unwrapObservable(ret) : null;"
+    )
+    static Object getProperty(Object object, String property) {
+        return null;
+    }
+    
+    private PropertyBinding[] props;
+    private FunctionBinding[] funcs;
+    private Object js;
+    private Object strong;
+
+    public Knockout(Object model, Object js, PropertyBinding[] props, FunctionBinding[] funcs) {
+        super(model, QUEUE);
+        this.js = js;
+        this.props = new PropertyBinding[props.length];
+        for (int i = 0; i < props.length; i++) {
+            this.props[i] = props[i].weak();
+        }
+        this.funcs = new FunctionBinding[funcs.length];
+        for (int i = 0; i < funcs.length; i++) {
+            this.funcs[i] = funcs[i].weak();
+        }
+        active.add(this);
+    }
+    
+    static void cleanUp() {
+        for (;;) {
+            Knockout ko = (Knockout)QUEUE.poll();
+            if (ko == null) {
+                return;
+            }
+            active.remove(ko);
+            clean(ko.js);
+            ko.js = null;
+            ko.props = null;
+            ko.funcs = null;
+        }
+    }
+    
+    final void hold() {
+        strong = get();
+    }
+    
+    final Object getValue(int index) {
+        return props[index].getValue();
+    }
+    
+    final void setValue(int index, Object v) {
+        if (v instanceof Knockout) {
+            v = ((Knockout)v).get();
+        }
+        props[index].setValue(v);
+    }
+    
+    final void call(int index, Object data, Object ev) {
+        funcs[index].call(data, ev);
+    }
+    
+    @JavaScriptBody(args = { "model", "prop", "oldValue", "newValue" }, 
+        wait4js = false,
+        body =
+          "if (model) {\n"
+        + "  var koProp = model[prop];\n"
+        + "  if (koProp) {\n"
+        + "    var koFire = koProp['valueHasMutated'];\n"
+        + "    if (koFire) {\n"
+        + "      if (oldValue !== null || newValue !== null) {\n"
+        + "        koFire(newValue);\n"
+        + "      } else {\n"
+        + "        koFire();\n"
+        + "      }\n"
+        + "    }\n"
+        + "  }\n"
+        + "}\n"
+    )
+    native static void valueHasMutated(
+        Object model, String prop, Object oldValue, Object newValue
+    );
+
+    @JavaScriptBody(args = { "id", "bindings" }, body = 
+        "var d = window['document'];\n" +
+        "var e = id ? d['getElementById'](id) : d['body'];\n" +
+        "ko['cleanNode'](e);\n" +
+        "ko['applyBindings'](bindings, e);\n" +
+        "return bindings['ko4j'];\n"
+    )
+    native static Object applyBindings(String id, Object bindings);
+    
+    @JavaScriptBody(args = { "cnt" }, body = 
+        "var arr = new Array(cnt);\n" +
+        "for (var i = 0; i < cnt; i++) arr[i] = new Object();\n" +
+        "return arr;\n"
+    )
+    native static Object[] allocJS(int cnt);
+    
+    @JavaScriptBody(
+        javacall = true,
+        keepAlive = false,
+        wait4js = false,
+        args = { "ret", "copyFrom", "propNames", "propInfo", "propValues", "funcNames" },
+        body = 
+          "Object.defineProperty(ret, 'ko4j', { value : this });\n"
+        + "function normalValue(r) {\n"
+        + "  if (r) try { var br = r.valueOf(); } catch (err) {}\n"
+        + "  return br === undefined ? r: br;\n"
+        + "}\n"
+        + "function koComputed(index, name, readOnly, value) {\n"
+        + "  var orig = copyFrom ? copyFrom[name] : null;\n"
+        + "  if (!ko['isObservable'](orig)) {\n"
+        + "    orig = null;\n"
+        + "    var trigger = ko['observable']()['extend']({'notify':'always'});\n"
+        + "  } else {\n"
+        + "    var trigger = orig;\n"
+        + "  }\n"
+        + "  function realGetter() {\n"
+        + "    var self = ret['ko4j'];\n"
+        + "    try {\n"
+        + "      var v = self ? self.@org.netbeans.html.ko4j.Knockout::getValue(I)(index) : null;\n"
+        + "      return v;\n"
+        + "    } catch (e) {\n"
+        + "      alert(\"Cannot call getValue on \" + self + \" prop: \" + name + \" error: \" + e);\n"
+        + "    }\n"
+        + "  }\n"
+        + "  var activeGetter = orig ? orig : function() { return value; };\n"
+        + "  var bnd = {\n"
+        + "    'read': function() {\n"
+        + "      trigger();\n"
+        + "      if (orig) {\n"
+        + "        var r = orig();\n"
+        + "      } else {\n"
+        + "        var r = activeGetter();\n"
+        + "        activeGetter = realGetter;\n"
+        + "      }\n"
+        + "      return normalValue(r);;\n"
+        + "    },\n"
+        + "    'owner': ret\n"
+        + "  };\n"
+        + "  if (!readOnly) {\n"
+        + "    function write(val) {\n"
+        + "      if (orig) orig(val);\n"
+        + "      var self = ret['ko4j'];\n"
+        + "      if (!self) return;\n"
+        + "      var model = val ? val['ko4j'] : null;\n"
+        + "      self.@org.netbeans.html.ko4j.Knockout::setValue(ILjava/lang/Object;)(index, model ? model : val);\n"
+        + "    };\n"
+        + "    bnd['write'] = write;\n"
+        + "    if (orig) {\n"
+        + "      write(orig());\n"
+        + "      orig.subscribe(write);\n"
+        + "    }\n"
+        + "  };\n"
+        + "  var cmpt = ko['computed'](bnd);\n"
+        + "  cmpt['valueHasMutated'] = function(val) {\n"
+        + "    if (arguments.length === 1) activeGetter = function() { return val; };\n"
+        + "    trigger(val);\n"
+        + "  };\n"
+        + "  ret[name] = cmpt;\n"
+        + "}\n"
+        + "for (var i = 0; i < propNames.length; i++) {\n"
+        + "  if ((propInfo[i] & 2) !== 0) {\n"
+        + "    ret[propNames[i]] = normalValue(propValues[i]);\n"
+        + "  } else {\n"
+        + "    koComputed(i, propNames[i], (propInfo[i] & 1) !== 0, propValues[i]);\n"
+        + "  }\n"
+        + "}\n"
+        + "function koExpose(index, name) {\n"
+        + "  ret[name] = function(data, ev) {\n"
+        + "    var self = ret['ko4j'];\n"
+        + "    if (!self) return;\n"
+        + "    self.@org.netbeans.html.ko4j.Knockout::call(ILjava/lang/Object;Ljava/lang/Object;)(index, data, ev);\n"
+        + "  };\n"
+        + "}\n"
+        + "for (var i = 0; i < funcNames.length; i++) {\n"
+        + "  koExpose(i, funcNames[i]);\n"
+        + "}\n"
+        )
+    native void wrapModel(
+        Object ret, Object copyFrom,
+        String[] propNames, Number[] propInfo,
+        Object propValues,
+        String[] funcNames
+    );
+    
+    @JavaScriptBody(args = { "js" }, wait4js = false, body = 
+        "delete js['ko4j'];\n" +
+        "for (var p in js) {\n" +
+        "  delete js[p];\n" +
+        "};\n" +
+        "\n"
+    )
+    private static native void clean(Object js);
+    
+    @JavaScriptBody(args = { "o" }, body = "return o['ko4j'] ? o['ko4j'] : o;")
+    private static native Object toModelImpl(Object wrapper);
+    static Object toModel(Object wrapper) {
+        Object o = toModelImpl(wrapper);
+        if (o instanceof Knockout) {
+            return ((Knockout)o).get();
+        } else {
+            return o;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java
new file mode 100644
index 0000000..6957596
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java
@@ -0,0 +1,138 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.json.spi.JSONCall;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class LoadJSON {
+    private LoadJSON() {}
+
+    static String createJSONP(JSONCall whenDone) {
+        int h = whenDone.hashCode();
+        String name;
+        for (;;) {
+            name = "jsonp" + Integer.toHexString(h);
+            if (defineIfUnused(name, whenDone)) {
+                return name;
+            }
+            h++;
+        }
+    }
+
+    @JavaScriptBody(args = {"name", "done"}, javacall = true, body
+        = "if (window[name]) return false;\n "
+        + "window[name] = function(data) {\n "
+        + "  delete window[name];\n"
+        + "  var el = window.document.getElementById(name);\n"
+        + "  el.parentNode.removeChild(el);\n"
+        + "  done.@org.netbeans.html.json.spi.JSONCall::notifySuccess(Ljava/lang/Object;)(data);\n"
+        + "};\n"
+        + "return true;\n"
+    )
+    private static boolean defineIfUnused(String name, JSONCall done) {
+        return true;
+    }
+
+    @JavaScriptBody(args = {"s"}, body = "return eval('(' + s + ')');")
+    static Object parse(String s) {
+        return s;
+    }
+
+    @JavaScriptBody(args = {"url", "done", "method", "data", "hp"}, javacall = true, body = ""
+        + "var request = new XMLHttpRequest();\n"
+        + "if (!method) method = 'GET';\n"
+        + "request.open(method, url, true);\n"
+        + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
+        + "for (var i = 0; i < hp.length; i += 2) {\n"
+        + "  var h = hp[i];\n"
+        + "  var v = hp[i + 1];\n"
+        + "  request.setRequestHeader(h, v);\n"
+        + "}\n"
+        + "request.onreadystatechange = function() {\n"
+        + "  if (request.readyState !== 4) return;\n"
+        + "  var r = request.response || request.responseText;\n"
+        + "  try {\n"
+        + "    var str = r;\n"
+        + "    if (request.status !== 0)\n"
+        + "      if (request.status < 100 || request.status >= 400) throw request.status + ': ' + request.statusText;"
+        + "    try { r = eval('(' + r + ')'); } catch (ignore) { }"
+        + "    @org.netbeans.html.ko4j.KOTransfer::notifySuccess(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)(done, str, r);\n"
+        + "  } catch (error) {;\n"
+        + "    @org.netbeans.html.ko4j.KOTransfer::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, error);\n"
+        + "  }\n"
+        + "};\n"
+        + "request.onerror = function (e) {\n"
+        + "  @org.netbeans.html.ko4j.KOTransfer::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, e.type + ' status ' + request.status);\n"
+        + "};\n"
+        + "if (data) request.send(data);\n"
+        + "else request.send();\n"
+    )
+    static void loadJSON(
+        String url, JSONCall done, String method, String data, Object[] headerPairs
+    ) {
+    }
+
+    @JavaScriptBody(args = {"url", "jsonp"}, body
+        = "var scrpt = window.document.createElement('script');\n "
+        + "scrpt.setAttribute('src', url);\n "
+        + "scrpt.setAttribute('id', jsonp);\n "
+        + "scrpt.setAttribute('type', 'text/javascript');\n "
+        + "var body = document.getElementsByTagName('body')[0];\n "
+        + "body.appendChild(scrpt);\n"
+    )
+    static void loadJSONP(String url, String jsonp) {
+
+    }
+
+    static void extractJSON(Object jsonObject, String[] props, Object[] values) {
+        for (int i = 0; i < props.length; i++) {
+            values[i] = Knockout.getProperty(jsonObject, props[i]);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java
----------------------------------------------------------------------
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java
new file mode 100644
index 0000000..bfd260f
--- /dev/null
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java
@@ -0,0 +1,141 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import net.java.html.js.JavaScriptBody;
+import org.netbeans.html.json.spi.JSONCall;
+
+/** Communication with WebSockets via browser's WebSocket object.
+ *
+ * @author Jaroslav Tulach
+ */
+final class LoadWS {
+    private final Object ws;
+    private final JSONCall call;
+    LoadWS(JSONCall first, String url) {
+        call = first;
+        ws = initWebSocket(this, url);
+        if (ws == null) {
+            first.notifyError(new IllegalArgumentException("Wrong URL: " + url));
+        }
+    }
+    
+    void send(JSONCall call) {
+        push(call);
+    }
+    
+    private synchronized void push(JSONCall call) {
+        send(ws, call.getMessage());
+    }
+
+    void onOpen(Object ev) {
+        if (!call.isDoOutput()) {
+            call.notifySuccess(null);
+        }
+    }
+    
+    
+    @JavaScriptBody(args = { "data" }, body = "try {\n"
+        + "    return eval('(' + data + ')');\n"
+        + "  } catch (error) {;\n"
+        + "    return data;\n"
+        + "  }\n"
+    )
+    private static native Object toJSON(String data);
+    
+    void onMessage(Object ev, String data) {
+        Object json = toJSON(data);
+        call.notifySuccess(json);
+    }
+    
+    void onError(Object ev) {
+        call.notifyError(new Exception(ev.toString()));
+    }
+
+    void onClose(boolean wasClean, int code, String reason) {
+        call.notifyError(null);
+    }
+    
+    @JavaScriptBody(args = { "back", "url" }, javacall = true, body = ""
+        + "if (window.WebSocket) {\n"
+        + "  try {\n"
+        + "    var ws = new window.WebSocket(url);\n"
+        + "    ws.onopen = function(ev) {\n"
+        + "      back.@org.netbeans.html.ko4j.LoadWS::onOpen(Ljava/lang/Object;)(ev);\n"
+        + "    };\n"
+        + "    ws.onmessage = function(ev) {\n"
+        + "      back.@org.netbeans.html.ko4j.LoadWS::onMessage(Ljava/lang/Object;Ljava/lang/String;)(ev, ev.data);\n"
+        + "    };\n"
+        + "    ws.onerror = function(ev) {\n"
+        + "      back.@org.netbeans.html.ko4j.LoadWS::onError(Ljava/lang/Object;)(ev);\n"
+        + "    };\n"
+        + "    ws.onclose = function(ev) {\n"
+        + "      back.@org.netbeans.html.ko4j.LoadWS::onClose(ZILjava/lang/String;)(ev.wasClean, ev.code, ev.reason);\n"
+        + "    };\n"
+        + "    return ws;\n"
+        + "  } catch (ex) {\n"
+        + "    return null;\n"
+        + "  }\n"
+        + "} else {\n"
+        + "  return null;\n"
+        + "}\n"
+    )
+    private static Object initWebSocket(Object back, String url) {
+        return null;
+    }
+    
+
+    @JavaScriptBody(args = { "ws", "msg" }, body = ""
+        + "ws.send(msg);"
+    )
+    private void send(Object ws, String msg) {
+    }
+
+    @JavaScriptBody(args = { "ws" }, body = "ws.close();")
+    private static void close(Object ws) {
+    }
+
+    void close() {
+        close(ws);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java
----------------------------------------------------------------------
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java b/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java
new file mode 100644
index 0000000..fa7e673
--- /dev/null
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java
@@ -0,0 +1,261 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketAddOn;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class DynamicHTTP extends HttpHandler {
+    private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
+    private static int resourcesCount;
+    private static List<Resource> resources;
+    private static ServerConfiguration conf;
+    private static HttpServer server;
+
+    private DynamicHTTP() {
+    }
+
+    static URI initServer() throws Exception {
+        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
+        final WebSocketAddOn addon = new WebSocketAddOn();
+        for (NetworkListener listener : server.getListeners()) {
+            listener.registerAddOn(addon);
+        }
+        resources = new ArrayList<Resource>();
+
+        conf = server.getServerConfiguration();
+        final DynamicHTTP dh = new DynamicHTTP();
+
+        conf.addHttpHandler(dh, "/");
+
+        server.start();
+
+        return pageURL("http", server, "/test.html");
+    }
+
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/test.html".equals(request.getRequestURI())) {
+            response.setContentType("text/html");
+            final InputStream is = DynamicHTTP.class.getResourceAsStream("test.html");
+            copyStream(is, response.getOutputStream(), null);
+            return;
+        }
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List<String> params = new ArrayList<String>();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, "Error processing message " + text, ex);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko4j/src/test/java/org/netbeans/html/ko4j/InitializeKnockoutTest.java
----------------------------------------------------------------------
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/InitializeKnockoutTest.java b/ko4j/src/test/java/org/netbeans/html/ko4j/InitializeKnockoutTest.java
new file mode 100644
index 0000000..c1e2eaf
--- /dev/null
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/InitializeKnockoutTest.java
@@ -0,0 +1,156 @@
+/**
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Oracle. Portions Copyright 2013-2016 Oracle. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+package org.netbeans.html.ko4j;
+
+import java.net.URL;
+import java.util.concurrent.CountDownLatch;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import net.java.html.BrwsrCtx;
+import net.java.html.boot.fx.FXBrowsers;
+import net.java.html.js.JavaScriptBody;
+import net.java.html.json.Models;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertNotNull;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class InitializeKnockoutTest {
+    public InitializeKnockoutTest() {
+    }
+    
+    @BeforeClass
+    public void initFX() throws Throwable {
+        new Thread("initFX") {
+            @Override
+            public void run() {
+                if (Platform.isFxApplicationThread()) {
+                    new App().start(new Stage());
+                } else {
+                    try {
+                        App.launch(App.class);
+                    } catch (IllegalStateException ex) {
+                        Platform.runLater(this);
+                    }
+                }
+            }
+        }.start();
+        App.CDL.await();
+    }
+
+    @JavaScriptBody(args = {}, body = "return typeof ko !== 'undefined' ? ko : null;")
+    static native Object ko();
+    
+    @Test
+    public void brwsrCtxExecute() throws Throwable {
+        final CountDownLatch init = new CountDownLatch(1);
+        final BrwsrCtx[] ctx = { null };
+        FXBrowsers.runInBrowser(App.webView(), new Runnable() {
+            @Override
+            public void run() {
+                ctx[0] = BrwsrCtx.findDefault(InitializeKnockoutTest.class);
+                init.countDown();
+            }
+        });
+        init.await();
+
+        final CountDownLatch cdl = new CountDownLatch(1);
+        FXBrowsers.runInBrowser(App.webView(), new Runnable() {
+            @Override
+            public void run() {
+                assertNull(ko(), "Knockout isn't yet defined");
+                Models.toRaw(null);
+                assertNotNull(ko(), "After call to toRaw, ko is defined");
+
+                cdl.countDown();
+            }
+        });
+
+        cdl.await();
+    }
+
+    public static class App extends Application {
+        static final CountDownLatch CDL = new CountDownLatch(1);
+        private static BorderPane pane;
+
+        static WebView webView() {
+            try {
+                CDL.await();
+            } catch (InterruptedException ex) {
+                throw new IllegalStateException(ex);
+            }
+            return (WebView)System.getProperties().get("v1");
+        }
+
+        @Override
+        public void start(Stage stage) {
+            pane= new BorderPane();
+            Scene scene = new Scene(pane, 800, 600);
+            stage.setScene(scene);
+            final WebView w1 = new WebView();
+            System.getProperties().put("v1", w1);
+            pane.setCenter(w1);
+
+            
+            URL url = InitializeKnockoutTest.class.getResource("test.html");
+            assertNotNull(url);
+            FXBrowsers.load(w1, url, new Runnable() {
+                @Override
+                public void run() {
+                    CDL.countDown();
+                }
+            });
+
+        }
+        
+        
+    }
+}