ko4j registers implementation of Transfer and WSTransfer based on XHR and WebSocket from a browser. The Java implementations of these interfaces has been moved to ko-ws-tyrus module. JavaScript arrays passed as parameters to Java callback methods need to be wrapped. UniversalKO
authorJaroslav Tulach <jaroslav.tulach@netbeans.org>
Thu, 09 Jan 2014 20:39:23 +0100
branchUniversalKO
changeset 4466dce58c06f58
parent 445 491f5e97da6e
child 447 a673941d1d93
ko4j registers implementation of Transfer and WSTransfer based on XHR and WebSocket from a browser. The Java implementations of these interfaces has been moved to ko-ws-tyrus module. JavaScript arrays passed as parameters to Java callback methods need to be wrapped.
boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
boot/src/main/java/org/apidesign/html/boot/spi/Fn.java
boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java
json-tck/src/main/java/net/java/html/js/tests/Bodies.java
json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java
json-tck/src/main/java/net/java/html/js/tests/Sum.java
ko-ws-tyrus/pom.xml
ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java
ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java
ko4j/pom.xml
ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java
ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java
ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java
ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java
ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java
     1.1 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java	Thu Jan 09 19:12:55 2014 +0100
     1.2 +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java	Thu Jan 09 20:39:23 2014 +0100
     1.3 @@ -61,7 +61,7 @@
     1.4   * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     1.5   */
     1.6  public abstract class AbstractFXPresenter 
     1.7 -implements Fn.Presenter, Fn.ToJavaScript, Executor {
     1.8 +implements Fn.Presenter, Fn.ToJavaScript, Fn.FromJavaScript, Executor {
     1.9      static final Logger LOG = Logger.getLogger(FXPresenter.class.getName());
    1.10      protected static int cnt;
    1.11      protected List<String> scripts;
    1.12 @@ -220,6 +220,11 @@
    1.13      }
    1.14  
    1.15      @Override
    1.16 +    public Object toJava(Object jsArray) {
    1.17 +        return checkArray(jsArray);
    1.18 +    }
    1.19 +    
    1.20 +    @Override
    1.21      public Object toJavaScript(Object toReturn) {
    1.22          if (toReturn instanceof Object[]) {
    1.23              return convertArrays((Object[])toReturn);
     2.1 --- a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java	Thu Jan 09 19:12:55 2014 +0100
     2.2 +++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java	Thu Jan 09 20:39:23 2014 +0100
     2.3 @@ -265,4 +265,24 @@
     2.4           */
     2.5          public Object toJavaScript(Object toReturn);
     2.6      }
     2.7 +    
     2.8 +    /** Additional interface to be implemented by {@link Presenter}s that
     2.9 +     * need to convert JavaScript object (usually array) to Java object 
    2.10 +     * when calling back from JavaScript to Java.
    2.11 +     * <p>
    2.12 +     * <em>Note:</em> The implementation based on <em>JavaFX</em>
    2.13 +     * <code>WebView</code> uses this interface to convert JavaScript arrays to
    2.14 +     * Java ones.
    2.15 +      * 
    2.16 +     * @since 0.7
    2.17 +     */
    2.18 +    public interface FromJavaScript {
    2.19 +        /** Convert a JavaScript object into suitable Java representation
    2.20 +         * before a Java method is called with this object as an argument.
    2.21 +         * 
    2.22 +         * @param js the JavaScript object
    2.23 +         * @return replacement object for 
    2.24 +         */
    2.25 +        public Object toJava(Object js);
    2.26 +    }
    2.27  }
     3.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java	Thu Jan 09 19:12:55 2014 +0100
     3.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java	Thu Jan 09 20:39:23 2014 +0100
     3.3 @@ -298,13 +298,26 @@
     3.4                  }
     3.5                  
     3.6                  int cnt = 0;
     3.7 +                StringBuilder convert = new StringBuilder();
     3.8                  for (VariableElement ve : m.getParameters()) {
     3.9                      source.append(sep);
    3.10 -                    source.append(ve.asType());
    3.11 -                    source.append(" arg").append(++cnt);
    3.12 +                    ++cnt;
    3.13 +                    final TypeMirror t = ve.asType();
    3.14 +                    if (!t.getKind().isPrimitive()) {
    3.15 +                        source.append("Object");
    3.16 +                        convert.append("    if (p instanceof org.apidesign.html.boot.spi.Fn.FromJavaScript) {\n");
    3.17 +                        convert.append("      arg").append(cnt).
    3.18 +                            append(" = ((org.apidesign.html.boot.spi.Fn.FromJavaScript)p).toJava(arg").append(cnt).
    3.19 +                            append(");\n");
    3.20 +                        convert.append("    }\n");
    3.21 +                    } else {
    3.22 +                        source.append(t);
    3.23 +                    }
    3.24 +                    source.append(" arg").append(cnt);
    3.25                      sep = ", ";
    3.26                  }
    3.27                  source.append(") throws Throwable {\n");
    3.28 +                source.append(convert);
    3.29                  if (processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_7) >= 0) {
    3.30                      source.append("    try (java.io.Closeable a = org.apidesign.html.boot.spi.Fn.activate(p)) { \n");
    3.31                  } else {
    3.32 @@ -326,7 +339,8 @@
    3.33                  sep = "";
    3.34                  for (VariableElement ve : m.getParameters()) {
    3.35                      source.append(sep);
    3.36 -                    source.append("arg").append(++cnt);
    3.37 +                    source.append("(").append(ve.asType());
    3.38 +                    source.append(")arg").append(++cnt);
    3.39                      sep = ", ";
    3.40                  }
    3.41                  source.append(");\n");
     4.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java	Thu Jan 09 19:12:55 2014 +0100
     4.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java	Thu Jan 09 20:39:23 2014 +0100
     4.3 @@ -88,6 +88,9 @@
     4.4          if (name.equals(Fn.ToJavaScript.class.getName())) {
     4.5              return Fn.ToJavaScript.class;
     4.6          }
     4.7 +        if (name.equals(Fn.FromJavaScript.class.getName())) {
     4.8 +            return Fn.FromJavaScript.class;
     4.9 +        }
    4.10          if (name.equals(FnUtils.class.getName())) {
    4.11              return FnUtils.class;
    4.12          }
     5.1 --- a/json-tck/src/main/java/net/java/html/js/tests/Bodies.java	Thu Jan 09 19:12:55 2014 +0100
     5.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/Bodies.java	Thu Jan 09 20:39:23 2014 +0100
     5.3 @@ -99,4 +99,9 @@
     5.4      
     5.5      @JavaScriptBody(args = {}, body = "return true;")
     5.6      public static native boolean truth();
     5.7 +    
     5.8 +    @JavaScriptBody(args = { "s" }, javacall = true, body = 
     5.9 +        "return s.@net.java.html.js.tests.Sum::sum([Ljava/lang/Object;)([1, 2, 3]);"
    5.10 +    )
    5.11 +    public static native int sumArr(Sum s);
    5.12  }
     6.1 --- a/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java	Thu Jan 09 19:12:55 2014 +0100
     6.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java	Thu Jan 09 20:39:23 2014 +0100
     6.3 @@ -267,6 +267,11 @@
     6.4          assert new Factorial().factorial(6) == 720;
     6.5      }
     6.6      
     6.7 +    @KOTest public void sumArray() {
     6.8 +        int r = Bodies.sumArr(new Sum());
     6.9 +        assert r == 6 : "Sum is six: " + r;
    6.10 +    }
    6.11 +    
    6.12      private static class R implements Runnable {
    6.13          int cnt;
    6.14          private final Thread initThread;
     7.1 --- a/json-tck/src/main/java/net/java/html/js/tests/Sum.java	Thu Jan 09 19:12:55 2014 +0100
     7.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/Sum.java	Thu Jan 09 20:39:23 2014 +0100
     7.3 @@ -50,4 +50,14 @@
     7.4      public int sum(int a, int b) {
     7.5          return a + b;
     7.6      }
     7.7 +    
     7.8 +    public int sum(Object[] arr) {
     7.9 +        int s = 0;
    7.10 +        for (int i = 0; i < arr.length; i++) {
    7.11 +            if (arr[i] instanceof Number) {
    7.12 +                s += ((Number)arr[i]).intValue();
    7.13 +            }
    7.14 +        }
    7.15 +        return s;
    7.16 +    }
    7.17  }
     8.1 --- a/ko-ws-tyrus/pom.xml	Thu Jan 09 19:12:55 2014 +0100
     8.2 +++ b/ko-ws-tyrus/pom.xml	Thu Jan 09 20:39:23 2014 +0100
     8.3 @@ -94,7 +94,6 @@
     8.4        <groupId>${project.groupId}</groupId>
     8.5        <artifactId>net.java.html.boot</artifactId>
     8.6        <version>${project.version}</version>
     8.7 -      <scope>test</scope>
     8.8        <type>jar</type>
     8.9      </dependency>
    8.10      <dependency>
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java	Thu Jan 09 20:39:23 2014 +0100
     9.3 @@ -0,0 +1,248 @@
     9.4 +/**
     9.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     9.6 + *
     9.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
     9.8 + *
     9.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    9.10 + * Other names may be trademarks of their respective owners.
    9.11 + *
    9.12 + * The contents of this file are subject to the terms of either the GNU
    9.13 + * General Public License Version 2 only ("GPL") or the Common
    9.14 + * Development and Distribution License("CDDL") (collectively, the
    9.15 + * "License"). You may not use this file except in compliance with the
    9.16 + * License. You can obtain a copy of the License at
    9.17 + * http://www.netbeans.org/cddl-gplv2.html
    9.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    9.19 + * specific language governing permissions and limitations under the
    9.20 + * License.  When distributing the software, include this License Header
    9.21 + * Notice in each file and include the License file at
    9.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    9.23 + * particular file as subject to the "Classpath" exception as provided
    9.24 + * by Oracle in the GPL Version 2 section of the License file that
    9.25 + * accompanied this code. If applicable, add the following below the
    9.26 + * License Header, with the fields enclosed by brackets [] replaced by
    9.27 + * your own identifying information:
    9.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    9.29 + *
    9.30 + * Contributor(s):
    9.31 + *
    9.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    9.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
    9.34 + *
    9.35 + * If you wish your version of this file to be governed by only the CDDL
    9.36 + * or only the GPL Version 2, indicate your decision by adding
    9.37 + * "[Contributor] elects to include this software in this distribution
    9.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    9.39 + * single choice of license, a recipient has the option to distribute
    9.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    9.41 + * to extend the choice of license to its licensees as provided above.
    9.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    9.43 + * Version 2 license, then the option applies only if the new code is
    9.44 + * made subject to such option by the copyright holder.
    9.45 + */
    9.46 +package org.netbeans.html.wstyrus;
    9.47 +
    9.48 +import java.io.IOException;
    9.49 +import java.io.InputStream;
    9.50 +import java.io.InputStreamReader;
    9.51 +import java.io.OutputStream;
    9.52 +import java.io.PushbackInputStream;
    9.53 +import java.io.Reader;
    9.54 +import java.net.HttpURLConnection;
    9.55 +import java.net.MalformedURLException;
    9.56 +import java.net.URL;
    9.57 +import java.net.URLConnection;
    9.58 +import java.util.Iterator;
    9.59 +import java.util.concurrent.Executor;
    9.60 +import java.util.concurrent.Executors;
    9.61 +import java.util.concurrent.ThreadFactory;
    9.62 +import java.util.logging.Level;
    9.63 +import java.util.logging.Logger;
    9.64 +import net.java.html.js.JavaScriptBody;
    9.65 +import org.apidesign.html.json.spi.JSONCall;
    9.66 +import org.json.JSONArray;
    9.67 +import org.json.JSONException;
    9.68 +import org.json.JSONObject;
    9.69 +import org.json.JSONTokener;
    9.70 +
    9.71 +/** This is an implementation package - just
    9.72 + * include its JAR on classpath and use official {@link Context} API
    9.73 + * to access the functionality.
    9.74 + *
    9.75 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    9.76 + */
    9.77 +final class LoadJSON implements Runnable {
    9.78 +    private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName());
    9.79 +    private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
    9.80 +        @Override
    9.81 +        public Thread newThread(Runnable runnable) {
    9.82 +            Thread thread = Executors.defaultThreadFactory().newThread(runnable);
    9.83 +            thread.setDaemon(true);
    9.84 +            return thread;
    9.85 +        }
    9.86 +    });
    9.87 +
    9.88 +    private final JSONCall call;
    9.89 +    private final URL base;
    9.90 +
    9.91 +
    9.92 +    private LoadJSON(JSONCall call) {
    9.93 +        this.call = call;
    9.94 +        this.base = null;
    9.95 +    }
    9.96 +
    9.97 +    public static void loadJSON(JSONCall call) {
    9.98 +        assert !"WebSocket".equals(call.getMethod());
    9.99 +        REQ.execute(new LoadJSON((call)));
   9.100 +    }
   9.101 +
   9.102 +    @Override
   9.103 +    public void run() {
   9.104 +        final String url;
   9.105 +        Throwable error = null;
   9.106 +        Object json = null;
   9.107 +        
   9.108 +        if (call.isJSONP()) {
   9.109 +            url = call.composeURL("dummy");
   9.110 +        } else {
   9.111 +            url = call.composeURL(null);
   9.112 +        }
   9.113 +        try {
   9.114 +            final URL u = new URL(base, url.replace(" ", "%20"));
   9.115 +            URLConnection conn = u.openConnection();
   9.116 +            if (conn instanceof HttpURLConnection) {
   9.117 +                HttpURLConnection huc = (HttpURLConnection) conn;
   9.118 +                if (call.getMethod() != null) {
   9.119 +                    huc.setRequestMethod(call.getMethod());
   9.120 +                }
   9.121 +                if (call.isDoOutput()) {
   9.122 +                    huc.setDoOutput(true);
   9.123 +                    final OutputStream os = huc.getOutputStream();
   9.124 +                    call.writeData(os);
   9.125 +                    os.flush();
   9.126 +                }
   9.127 +            }
   9.128 +            final PushbackInputStream is = new PushbackInputStream(
   9.129 +                conn.getInputStream(), 1
   9.130 +            );
   9.131 +            boolean array = false;
   9.132 +            boolean string = false;
   9.133 +            if (call.isJSONP()) {
   9.134 +                for (;;) {
   9.135 +                    int ch = is.read();
   9.136 +                    if (ch == -1) {
   9.137 +                        break;
   9.138 +                    }
   9.139 +                    if (ch == '[') {
   9.140 +                        is.unread(ch);
   9.141 +                        array = true;
   9.142 +                        break;
   9.143 +                    }
   9.144 +                    if (ch == '{') {
   9.145 +                        is.unread(ch);
   9.146 +                        break;
   9.147 +                    }
   9.148 +                }
   9.149 +            } else {
   9.150 +                int ch = is.read();
   9.151 +                if (ch == -1) {
   9.152 +                    string = true;
   9.153 +                } else {
   9.154 +                    array = ch == '[';
   9.155 +                    is.unread(ch);
   9.156 +                    if (!array && ch != '{') {
   9.157 +                        string = true;
   9.158 +                    }
   9.159 +                }
   9.160 +            }
   9.161 +            try {
   9.162 +                if (string) {
   9.163 +                    throw new JSONException("");
   9.164 +                }
   9.165 +                Reader r = new InputStreamReader(is, "UTF-8");
   9.166 +
   9.167 +                JSONTokener tok = new JSONTokener(r);
   9.168 +                Object obj;
   9.169 +                obj = array ? new JSONArray(tok) : new JSONObject(tok);
   9.170 +                json = convertToArray(obj);
   9.171 +            } catch (JSONException ex) {
   9.172 +                Reader r = new InputStreamReader(is, "UTF-8");
   9.173 +                StringBuilder sb = new StringBuilder();
   9.174 +                for (;;) {
   9.175 +                    int ch = r.read();
   9.176 +                    if (ch == -1) {
   9.177 +                        break;
   9.178 +                    }
   9.179 +                    sb.append((char)ch);
   9.180 +                }
   9.181 +                json = sb.toString();
   9.182 +            }
   9.183 +        } catch (IOException ex) {
   9.184 +            error = ex;
   9.185 +        } finally {
   9.186 +            if (error != null) {
   9.187 +                call.notifyError(error);
   9.188 +            } else {
   9.189 +                call.notifySuccess(json);
   9.190 +            }
   9.191 +        }
   9.192 +    }
   9.193 +
   9.194 +    static Object convertToArray(Object o) throws JSONException {
   9.195 +        if (o instanceof JSONArray) {
   9.196 +            JSONArray ja = (JSONArray)o;
   9.197 +            Object[] arr = new Object[ja.length()];
   9.198 +            for (int i = 0; i < arr.length; i++) {
   9.199 +                arr[i] = convertToArray(ja.get(i));
   9.200 +            }
   9.201 +            return arr;
   9.202 +        } else if (o instanceof JSONObject) {
   9.203 +            JSONObject obj = (JSONObject)o;
   9.204 +            Iterator it = obj.keys();
   9.205 +            while (it.hasNext()) {
   9.206 +                String key = (String)it.next();
   9.207 +                obj.put(key, convertToArray(obj.get(key)));
   9.208 +            }
   9.209 +            return obj;
   9.210 +        } else {
   9.211 +            return o;
   9.212 +        }
   9.213 +    }
   9.214 +    
   9.215 +    public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
   9.216 +        if (jsonObject instanceof JSONObject) {
   9.217 +            JSONObject obj = (JSONObject)jsonObject;
   9.218 +            for (int i = 0; i < props.length; i++) {
   9.219 +                try {
   9.220 +                    values[i] = obj.has(props[i]) ? obj.get(props[i]) : null;
   9.221 +                } catch (JSONException ex) {
   9.222 +                    LoadJSON.LOG.log(Level.SEVERE, "Can't read " + props[i] + " from " + jsonObject, ex);
   9.223 +                }
   9.224 +            }
   9.225 +            return;
   9.226 +        }
   9.227 +        for (int i = 0; i < props.length; i++) {
   9.228 +            values[i] = getProperty(jsonObject, props[i]);
   9.229 +        }
   9.230 +    }
   9.231 +    
   9.232 +    @JavaScriptBody(args = {"object", "property"},
   9.233 +            body
   9.234 +            = "if (property === null) return object;\n"
   9.235 +            + "if (object === null) return null;\n"
   9.236 +            + "var p = object[property]; return p ? p : null;"
   9.237 +    )
   9.238 +    private static Object getProperty(Object object, String property) {
   9.239 +        return null;
   9.240 +    }
   9.241 +    
   9.242 +    public static Object parse(InputStream is) throws IOException {
   9.243 +        try {
   9.244 +            InputStreamReader r = new InputStreamReader(is, "UTF-8");
   9.245 +            JSONTokener t = new JSONTokener(r);
   9.246 +            return new JSONObject(t);
   9.247 +        } catch (JSONException ex) {
   9.248 +            throw new IOException(ex);
   9.249 +        }
   9.250 +    }
   9.251 +}
    10.1 --- a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java	Thu Jan 09 19:12:55 2014 +0100
    10.2 +++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java	Thu Jan 09 20:39:23 2014 +0100
    10.3 @@ -43,6 +43,7 @@
    10.4  package org.netbeans.html.wstyrus;
    10.5  
    10.6  import java.io.IOException;
    10.7 +import java.io.InputStream;
    10.8  import java.net.URI;
    10.9  import java.net.URISyntaxException;
   10.10  import java.util.Iterator;
   10.11 @@ -58,6 +59,8 @@
   10.12  import net.java.html.json.OnReceive;
   10.13  import org.apidesign.html.context.spi.Contexts;
   10.14  import org.apidesign.html.json.spi.JSONCall;
   10.15 +import org.apidesign.html.json.spi.Technology;
   10.16 +import org.apidesign.html.json.spi.Transfer;
   10.17  import org.apidesign.html.json.spi.WSTransfer;
   10.18  import org.netbeans.html.wstyrus.TyrusContext.Comm;
   10.19  import org.json.JSONArray;
   10.20 @@ -82,12 +85,14 @@
   10.21   * @author Jaroslav Tulach <jtulach@netbeans.org>
   10.22   */
   10.23  @ServiceProvider(service = Contexts.Provider.class)
   10.24 -public final class TyrusContext implements Contexts.Provider, WSTransfer<Comm> {
   10.25 +public final class TyrusContext 
   10.26 +implements Contexts.Provider, WSTransfer<Comm>, Transfer {
   10.27      @Override
   10.28      public void fillContext(Contexts.Builder context, Class<?> requestor) {
   10.29          // default WebSocket transfer implementation is registered
   10.30          // in ko-fx module with 100, provide this one as a fallback only
   10.31          context.register(WSTransfer.class, this, 1000);
   10.32 +        context.register(Transfer.class, this, 1000);
   10.33      }
   10.34  
   10.35      @Override
   10.36 @@ -115,6 +120,21 @@
   10.37              socket.callback.notifyError(ex);
   10.38          }
   10.39      }
   10.40 +
   10.41 +    @Override
   10.42 +    public void extract(Object obj, String[] props, Object[] values) {
   10.43 +        LoadJSON.extractJSON(obj, props, values);
   10.44 +    }
   10.45 +
   10.46 +    @Override
   10.47 +    public Object toJSON(InputStream is) throws IOException {
   10.48 +        return LoadJSON.parse(is);
   10.49 +    }
   10.50 +
   10.51 +    @Override
   10.52 +    public void loadJSON(JSONCall call) {
   10.53 +        LoadJSON.loadJSON(call);
   10.54 +    }
   10.55      
   10.56      /** Implementation class in an implementation. Represents a {@link ClientEndpoint} of the
   10.57       * WebSocket channel. You are unlikely to get on hold of it.
    11.1 --- a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java	Thu Jan 09 19:12:55 2014 +0100
    11.2 +++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java	Thu Jan 09 20:39:23 2014 +0100
    11.3 @@ -150,7 +150,7 @@
    11.4          TyrusContext tc = new TyrusContext();
    11.5          Contexts.Builder cb = Contexts.newBuilder().
    11.6              register(Technology.class, ko.knockout(), 10).
    11.7 -            register(Transfer.class, ko.transferViaOrgJSON(), 10).
    11.8 +            register(Transfer.class, tc, 10).
    11.9              register(WSTransfer.class, tc, 10);
   11.10          return cb.build();
   11.11      }
    12.1 --- a/ko4j/pom.xml	Thu Jan 09 19:12:55 2014 +0100
    12.2 +++ b/ko4j/pom.xml	Thu Jan 09 20:39:23 2014 +0100
    12.3 @@ -49,10 +49,6 @@
    12.4          <systemPath>${jfxrt.jar}</systemPath>
    12.5      </dependency>
    12.6      <dependency>
    12.7 -        <groupId>de.twentyeleven.skysail</groupId>
    12.8 -        <artifactId>org.json-osgi</artifactId>
    12.9 -    </dependency>
   12.10 -    <dependency>
   12.11        <groupId>org.netbeans.html</groupId>
   12.12        <artifactId>net.java.html.json</artifactId>
   12.13        <version>${project.version}</version>
    13.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java	Thu Jan 09 19:12:55 2014 +0100
    13.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java	Thu Jan 09 20:39:23 2014 +0100
    13.3 @@ -42,9 +42,11 @@
    13.4   */
    13.5  package org.netbeans.html.ko4j;
    13.6  
    13.7 +import java.io.ByteArrayOutputStream;
    13.8  import java.io.Closeable;
    13.9  import java.io.IOException;
   13.10  import java.io.InputStream;
   13.11 +import java.io.InputStreamReader;
   13.12  import java.util.concurrent.Executor;
   13.13  import java.util.logging.Logger;
   13.14  import net.java.html.js.JavaScriptBody;
   13.15 @@ -73,7 +75,7 @@
   13.16          this.browserContext = browserContext;
   13.17      }
   13.18      
   13.19 -    @JavaScriptBody(args = {}, body = "return true;")
   13.20 +    @JavaScriptBody(args = {}, body = "if (window) return true; else return false;")
   13.21      private static boolean isJavaScriptEnabledJs() {
   13.22          return false;
   13.23      }
   13.24 @@ -148,7 +150,22 @@
   13.25  
   13.26      @Override
   13.27      public void loadJSON(final JSONCall call) {
   13.28 -        LoadJSON.loadJSON(call);
   13.29 +        if (call.isJSONP()) {
   13.30 +            String me = LoadJSON.createJSONP(call);
   13.31 +            LoadJSON.loadJSONP(call.composeURL(me), me);
   13.32 +        } else {
   13.33 +            String data = null;
   13.34 +            if (call.isDoOutput()) {
   13.35 +                try {
   13.36 +                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
   13.37 +                    call.writeData(bos);
   13.38 +                    data = new String(bos.toByteArray(), "UTF-8");
   13.39 +                } catch (IOException ex) {
   13.40 +                    call.notifyError(ex);
   13.41 +                }
   13.42 +            }
   13.43 +            LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data);
   13.44 +        }
   13.45      }
   13.46  
   13.47      @Override
   13.48 @@ -158,7 +175,16 @@
   13.49  
   13.50      @Override
   13.51      public Object toJSON(InputStream is) throws IOException {
   13.52 -        return LoadJSON.parse(is);
   13.53 +        StringBuilder sb = new StringBuilder();
   13.54 +        InputStreamReader r = new InputStreamReader(is);
   13.55 +        for (;;) {
   13.56 +            int ch = r.read();
   13.57 +            if (ch == -1) {
   13.58 +                break;
   13.59 +            }
   13.60 +            sb.append((char)ch);
   13.61 +        }
   13.62 +        return LoadJSON.parse(sb.toString());
   13.63      }
   13.64  
   13.65      @Override
    14.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java	Thu Jan 09 19:12:55 2014 +0100
    14.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java	Thu Jan 09 20:39:23 2014 +0100
    14.3 @@ -84,34 +84,31 @@
    14.4          return getKO();
    14.5      }
    14.6      
    14.7 -    /** Java based implementation of transfer interface. Requires
    14.8 -     * org.json libraries on classpath. Use: <pre>
    14.9 -&lt;dependency&gt;
   14.10 -    &lt;groupId>de.twentyeleven.skysail&lt;/groupId&gt;
   14.11 -    &lt;artifactId>org.json-osgi&lt;/artifactId&gt;
   14.12 -&lt;/dependency&gt;
   14.13 -     * </pre>
   14.14 -     * @return instance of the technology or <code>null</code>, 
   14.15 -     *   if <code>org.json</code> interfaces are not around
   14.16 +    /** Browser based implementation of transfer interface. Uses
   14.17 +     * browser method to convert string to JSON.
   14.18 +     * 
   14.19 +     * @return non-null instance
   14.20       */
   14.21 -    public Transfer transferViaOrgJSON() {
   14.22 +    public Transfer transfer() {
   14.23          return getKO();
   14.24      }
   14.25      
   14.26      /** Returns browser based implementation of websocket transfer.
   14.27 +     * If available (for example JavaFX WebView on JDK7 does not have
   14.28 +     * this implementation).
   14.29       * 
   14.30       * @return an instance or <code>null</code>, if there is no
   14.31       *   <code>WebSocket</code> object in the browser
   14.32       */
   14.33 -    public WSTransfer<?> websocketsViaBrowser() {
   14.34 +    public WSTransfer<?> websockets() {
   14.35          return getKO().areWebSocketsSupported() ? getKO() : null;
   14.36      }
   14.37  
   14.38      /** Registers technologies at position 100:
   14.39       * <ul>
   14.40       *   <li>{@link #knockout()}</li>
   14.41 -     *   <li>{@link #transferViaOrgJSON()} - if <code>org.json</code> libraries are around</li>
   14.42 -     *   <li>{@link #websocketsViaBrowser()()} - if browser supports web sockets</li>
   14.43 +     *   <li>{@link #transfer()}</li>
   14.44 +     *   <li>{@link #websockets()()} - if browser supports web sockets</li>
   14.45       * </ul>
   14.46       * @param context the context to register to
   14.47       * @param requestor the class requesting the registration
   14.48 @@ -120,9 +117,9 @@
   14.49      public void fillContext(Contexts.Builder context, Class<?> requestor) {
   14.50          if (FXContext.isJavaScriptEnabled()) {
   14.51              context.register(Technology.class, knockout(), 100);
   14.52 -            context.register(Transfer.class, transferViaOrgJSON(), 100);
   14.53 +            context.register(Transfer.class, transfer(), 100);
   14.54              if (c.areWebSocketsSupported()) {
   14.55 -                context.register(WSTransfer.class, websocketsViaBrowser(), 100);
   14.56 +                context.register(WSTransfer.class, websockets(), 100);
   14.57              }
   14.58          }
   14.59      }
    15.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java	Thu Jan 09 19:12:55 2014 +0100
    15.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java	Thu Jan 09 20:39:23 2014 +0100
    15.3 @@ -42,226 +42,167 @@
    15.4   */
    15.5  package org.netbeans.html.ko4j;
    15.6  
    15.7 +import java.io.ByteArrayOutputStream;
    15.8  import java.io.IOException;
    15.9  import java.io.InputStream;
   15.10  import java.io.InputStreamReader;
   15.11 -import java.io.OutputStream;
   15.12 -import java.io.PushbackInputStream;
   15.13 -import java.io.Reader;
   15.14 -import java.net.HttpURLConnection;
   15.15 -import java.net.MalformedURLException;
   15.16 -import java.net.URL;
   15.17 -import java.net.URLConnection;
   15.18 -import java.util.Iterator;
   15.19 -import java.util.concurrent.Executor;
   15.20 -import java.util.concurrent.Executors;
   15.21 -import java.util.concurrent.ThreadFactory;
   15.22 -import java.util.logging.Level;
   15.23 -import java.util.logging.Logger;
   15.24 -import javafx.application.Platform;
   15.25  import net.java.html.js.JavaScriptBody;
   15.26 -import netscape.javascript.JSObject;
   15.27  import org.apidesign.html.json.spi.JSONCall;
   15.28 -import org.json.JSONArray;
   15.29 -import org.json.JSONException;
   15.30 -import org.json.JSONObject;
   15.31 -import org.json.JSONTokener;
   15.32 +import org.apidesign.html.json.spi.Transfer;
   15.33 +import org.apidesign.html.json.spi.WSTransfer;
   15.34  
   15.35 -/** This is an implementation package - just
   15.36 - * include its JAR on classpath and use official {@link Context} API
   15.37 - * to access the functionality.
   15.38 +/**
   15.39   *
   15.40   * @author Jaroslav Tulach <jtulach@netbeans.org>
   15.41   */
   15.42 -final class LoadJSON implements Runnable {
   15.43 -    private static final Logger LOG = FXContext.LOG;
   15.44 -    private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
   15.45 -        @Override
   15.46 -        public Thread newThread(Runnable runnable) {
   15.47 -            Thread thread = Executors.defaultThreadFactory().newThread(runnable);
   15.48 -            thread.setDaemon(true);
   15.49 -            return thread;
   15.50 -        }
   15.51 -    });
   15.52 -
   15.53 -    private final JSONCall call;
   15.54 -    private final URL base;
   15.55 -    private Throwable error;
   15.56 -    private Object json;
   15.57 -
   15.58 -
   15.59 -    private LoadJSON(JSONCall call) {
   15.60 -        this.call = call;
   15.61 -        URL b;
   15.62 -        try {
   15.63 -            b = new URL(findBaseURL());
   15.64 -        } catch (MalformedURLException ex) {
   15.65 -            LOG.log(Level.SEVERE, "Can't find base url for " + call.composeURL("dummy"), ex);
   15.66 -            b = null;
   15.67 -        }
   15.68 -        this.base = b;
   15.69 -    }
   15.70 -
   15.71 -    public static void loadJSON(JSONCall call) {
   15.72 -        assert !"WebSocket".equals(call.getMethod());
   15.73 -        REQ.execute(new LoadJSON((call)));
   15.74 +final class LoadJSON implements Transfer, WSTransfer<LoadWS> {
   15.75 +    private LoadJSON() {}
   15.76 +    
   15.77 +    @Override
   15.78 +    public void extract(Object obj, String[] props, Object[] values) {
   15.79 +        extractJSON(obj, props, values);
   15.80      }
   15.81  
   15.82      @Override
   15.83 -    public void run() {
   15.84 -        if (Platform.isFxApplicationThread()) {
   15.85 -            if (error != null) {
   15.86 -                call.notifyError(error);
   15.87 -            } else {
   15.88 -                call.notifySuccess(json);
   15.89 -            }
   15.90 -            return;
   15.91 -        }
   15.92 -        final String url;
   15.93 +    public void loadJSON(final JSONCall call) {
   15.94          if (call.isJSONP()) {
   15.95 -            url = call.composeURL("dummy");
   15.96 +            String me = createJSONP(call);
   15.97 +            loadJSONP(call.composeURL(me), me);
   15.98          } else {
   15.99 -            url = call.composeURL(null);
  15.100 -        }
  15.101 -        try {
  15.102 -            final URL u = new URL(base, url.replace(" ", "%20"));
  15.103 -            URLConnection conn = u.openConnection();
  15.104 -            if (conn instanceof HttpURLConnection) {
  15.105 -                HttpURLConnection huc = (HttpURLConnection) conn;
  15.106 -                if (call.getMethod() != null) {
  15.107 -                    huc.setRequestMethod(call.getMethod());
  15.108 -                }
  15.109 -                if (call.isDoOutput()) {
  15.110 -                    huc.setDoOutput(true);
  15.111 -                    final OutputStream os = huc.getOutputStream();
  15.112 -                    call.writeData(os);
  15.113 -                    os.flush();
  15.114 +            String data = null;
  15.115 +            if (call.isDoOutput()) {
  15.116 +                try {
  15.117 +                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
  15.118 +                    call.writeData(bos);
  15.119 +                    data = new String(bos.toByteArray(), "UTF-8");
  15.120 +                } catch (IOException ex) {
  15.121 +                    call.notifyError(ex);
  15.122                  }
  15.123              }
  15.124 -            final PushbackInputStream is = new PushbackInputStream(
  15.125 -                conn.getInputStream(), 1
  15.126 -            );
  15.127 -            boolean array = false;
  15.128 -            boolean string = false;
  15.129 -            if (call.isJSONP()) {
  15.130 -                for (;;) {
  15.131 -                    int ch = is.read();
  15.132 -                    if (ch == -1) {
  15.133 -                        break;
  15.134 -                    }
  15.135 -                    if (ch == '[') {
  15.136 -                        is.unread(ch);
  15.137 -                        array = true;
  15.138 -                        break;
  15.139 -                    }
  15.140 -                    if (ch == '{') {
  15.141 -                        is.unread(ch);
  15.142 -                        break;
  15.143 -                    }
  15.144 -                }
  15.145 -            } else {
  15.146 -                int ch = is.read();
  15.147 -                if (ch == -1) {
  15.148 -                    string = true;
  15.149 -                } else {
  15.150 -                    array = ch == '[';
  15.151 -                    is.unread(ch);
  15.152 -                    if (!array && ch != '{') {
  15.153 -                        string = true;
  15.154 -                    }
  15.155 -                }
  15.156 -            }
  15.157 -            try {
  15.158 -                if (string) {
  15.159 -                    throw new JSONException("");
  15.160 -                }
  15.161 -                Reader r = new InputStreamReader(is, "UTF-8");
  15.162 -
  15.163 -                JSONTokener tok = new JSONTokener(r);
  15.164 -                Object obj;
  15.165 -                obj = array ? new JSONArray(tok) : new JSONObject(tok);
  15.166 -                json = convertToArray(obj);
  15.167 -            } catch (JSONException ex) {
  15.168 -                Reader r = new InputStreamReader(is, "UTF-8");
  15.169 -                StringBuilder sb = new StringBuilder();
  15.170 -                for (;;) {
  15.171 -                    int ch = r.read();
  15.172 -                    if (ch == -1) {
  15.173 -                        break;
  15.174 -                    }
  15.175 -                    sb.append((char)ch);
  15.176 -                }
  15.177 -                json = sb.toString();
  15.178 -            }
  15.179 -        } catch (IOException ex) {
  15.180 -            error = ex;
  15.181 -        } finally {
  15.182 -            Platform.runLater(this);
  15.183 +            loadJSON(call.composeURL(null), call, call.getMethod(), data);
  15.184          }
  15.185      }
  15.186  
  15.187 -    static Object convertToArray(Object o) throws JSONException {
  15.188 -        if (o instanceof JSONArray) {
  15.189 -            JSONArray ja = (JSONArray)o;
  15.190 -            Object[] arr = new Object[ja.length()];
  15.191 -            for (int i = 0; i < arr.length; i++) {
  15.192 -                arr[i] = convertToArray(ja.get(i));
  15.193 +    @Override
  15.194 +    public Object toJSON(InputStream is) throws IOException {
  15.195 +        StringBuilder sb = new StringBuilder();
  15.196 +        InputStreamReader r = new InputStreamReader(is);
  15.197 +        for (;;) {
  15.198 +            int ch = r.read();
  15.199 +            if (ch == -1) {
  15.200 +                break;
  15.201              }
  15.202 -            return arr;
  15.203 -        } else if (o instanceof JSONObject) {
  15.204 -            JSONObject obj = (JSONObject)o;
  15.205 -            Iterator it = obj.keys();
  15.206 -            while (it.hasNext()) {
  15.207 -                String key = (String)it.next();
  15.208 -                obj.put(key, convertToArray(obj.get(key)));
  15.209 +            sb.append((char)ch);
  15.210 +        }
  15.211 +        return parse(sb.toString());
  15.212 +    }
  15.213 +
  15.214 +    @Override
  15.215 +    public LoadWS open(String url, JSONCall callback) {
  15.216 +        return new LoadWS(callback, url);
  15.217 +    }
  15.218 +
  15.219 +    @Override
  15.220 +    public void send(LoadWS socket, JSONCall data) {
  15.221 +        socket.send(data);
  15.222 +    }
  15.223 +
  15.224 +    @Override
  15.225 +    public void close(LoadWS socket) {
  15.226 +        socket.close();
  15.227 +    }
  15.228 +    
  15.229 +    //
  15.230 +    // implementations
  15.231 +    //
  15.232 +    
  15.233 +    @JavaScriptBody(args = {"object", "property"},
  15.234 +        body
  15.235 +        = "if (property === null) return object;\n"
  15.236 +        + "if (object === null) return null;\n"
  15.237 +        + "var p = object[property]; return p ? p : null;"
  15.238 +    )
  15.239 +    private static Object getProperty(Object object, String property) {
  15.240 +        return null;
  15.241 +    }
  15.242 +
  15.243 +    static String createJSONP(JSONCall whenDone) {
  15.244 +        int h = whenDone.hashCode();
  15.245 +        String name;
  15.246 +        for (;;) {
  15.247 +            name = "jsonp" + Integer.toHexString(h);
  15.248 +            if (defineIfUnused(name, whenDone)) {
  15.249 +                return name;
  15.250              }
  15.251 -            return obj;
  15.252 -        } else {
  15.253 -            return o;
  15.254 +            h++;
  15.255 +        }
  15.256 +    }
  15.257 +
  15.258 +    @JavaScriptBody(args = {"name", "done"}, javacall = true, body
  15.259 +        = "if (window[name]) return false;\n "
  15.260 +        + "window[name] = function(data) {\n "
  15.261 +        + "  delete window[name];\n"
  15.262 +        + "  var el = window.document.getElementById(name);\n"
  15.263 +        + "  el.parentNode.removeChild(el);\n"
  15.264 +        + "  done.@org.apidesign.html.json.spi.JSONCall::notifySuccess(Ljava/lang/Object;)(data);\n"
  15.265 +        + "};\n"
  15.266 +        + "return true;\n"
  15.267 +    )
  15.268 +    private static boolean defineIfUnused(String name, JSONCall done) {
  15.269 +        return true;
  15.270 +    }
  15.271 +
  15.272 +    @JavaScriptBody(args = {"s"}, body = "return eval('(' + s + ')');")
  15.273 +    static Object parse(String s) {
  15.274 +        return s;
  15.275 +    }
  15.276 +
  15.277 +    @JavaScriptBody(args = {"url", "done", "method", "data"}, javacall = true, body = ""
  15.278 +        + "var request = new XMLHttpRequest();\n"
  15.279 +        + "if (!method) method = 'GET';\n"
  15.280 +        + "request.open(method, url, true);\n"
  15.281 +        + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
  15.282 +        + "request.onreadystatechange = function() {\n"
  15.283 +        + "  if (this.readyState!==4) return;\n"
  15.284 +        + "  try {\n"
  15.285 +        + "    var r = this.response;\n"
  15.286 +        + "    try { r = eval('(' + this.response + ')'); } catch (ignore) { }"
  15.287 +        + "    done.@org.apidesign.html.json.spi.JSONCall::notifySuccess(Ljava/lang/Object;)(r);\n"
  15.288 +        + "  } catch (error) {;\n"
  15.289 +        + "    @org.netbeans.html.ko4j.LoadJSON::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, this.response);\n"
  15.290 +        + "  }\n"
  15.291 +        + "};\n"
  15.292 +        + "request.onerror = function (e) {console.log('eeeor:' + Object.getOwnPropertyNames(e));\n"
  15.293 +        + "  @org.netbeans.html.ko4j.LoadJSON::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, e);\n"
  15.294 +        + "}\n"
  15.295 +        + "if (data) request.send(data);"
  15.296 +        + "else request.send();"
  15.297 +    )
  15.298 +    static void loadJSON(
  15.299 +        String url, JSONCall done, String method, String data
  15.300 +    ) {
  15.301 +    }
  15.302 +    
  15.303 +    static void notifyError(Object done, Object msg) {
  15.304 +        ((JSONCall)done).notifyError(new Exception(msg.toString()));
  15.305 +    }
  15.306 +
  15.307 +    @JavaScriptBody(args = {"url", "jsonp"}, body
  15.308 +        = "var scrpt = window.document.createElement('script');\n "
  15.309 +        + "scrpt.setAttribute('src', url);\n "
  15.310 +        + "scrpt.setAttribute('id', jsonp);\n "
  15.311 +        + "scrpt.setAttribute('type', 'text/javascript');\n "
  15.312 +        + "var body = document.getElementsByTagName('body')[0];\n "
  15.313 +        + "body.appendChild(scrpt);\n"
  15.314 +    )
  15.315 +    static void loadJSONP(String url, String jsonp) {
  15.316 +
  15.317 +    }
  15.318 +
  15.319 +    static void extractJSON(Object jsonObject, String[] props, Object[] values) {
  15.320 +        for (int i = 0; i < props.length; i++) {
  15.321 +            values[i] = getProperty(jsonObject, props[i]);
  15.322          }
  15.323      }
  15.324      
  15.325 -    public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
  15.326 -        if (jsonObject instanceof JSONObject) {
  15.327 -            JSONObject obj = (JSONObject)jsonObject;
  15.328 -            for (int i = 0; i < props.length; i++) {
  15.329 -                try {
  15.330 -                    values[i] = obj.has(props[i]) ? obj.get(props[i]) : null;
  15.331 -                } catch (JSONException ex) {
  15.332 -                    LoadJSON.LOG.log(Level.SEVERE, "Can't read " + props[i] + " from " + jsonObject, ex);
  15.333 -                }
  15.334 -            }
  15.335 -        }
  15.336 -        if (jsonObject instanceof JSObject) {
  15.337 -            JSObject obj = (JSObject)jsonObject;
  15.338 -            for (int i = 0; i < props.length; i++) {
  15.339 -                Object val = obj.getMember(props[i]);
  15.340 -                values[i] = isDefined(val) ? val : null;
  15.341 -            }
  15.342 -        }
  15.343 -    }
  15.344 -    
  15.345 -    public static Object parse(InputStream is) throws IOException {
  15.346 -        try {
  15.347 -            InputStreamReader r = new InputStreamReader(is, "UTF-8");
  15.348 -            JSONTokener t = new JSONTokener(r);
  15.349 -            return new JSONObject(t);
  15.350 -        } catch (JSONException ex) {
  15.351 -            throw new IOException(ex);
  15.352 -        }
  15.353 -    }
  15.354 -
  15.355 -    @JavaScriptBody(args = {  }, body = 
  15.356 -          "var h;"
  15.357 -        + "if (!!window && !!window.location && !!window.location.href)\n"
  15.358 -        + "  h = window.location.href;\n"
  15.359 -        + "else "
  15.360 -        + "  h = null;"
  15.361 -        + "return h;\n"
  15.362 -    )
  15.363 -    private static native String findBaseURL();
  15.364 -    
  15.365 -    private static boolean isDefined(Object val) {
  15.366 -        return !"undefined".equals(val);
  15.367 -    }
  15.368  }
    16.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java	Thu Jan 09 19:12:55 2014 +0100
    16.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java	Thu Jan 09 20:39:23 2014 +0100
    16.3 @@ -44,12 +44,8 @@
    16.4  
    16.5  import net.java.html.js.JavaScriptBody;
    16.6  import org.apidesign.html.json.spi.JSONCall;
    16.7 -import org.json.JSONArray;
    16.8 -import org.json.JSONException;
    16.9 -import org.json.JSONObject;
   16.10 -import org.json.JSONTokener;
   16.11  
   16.12 -/** Communication with WebSockets for WebView 1.8.
   16.13 +/** Communication with WebSockets via browser's WebSocket object.
   16.14   *
   16.15   * @author Jaroslav Tulach <jtulach@netbeans.org>
   16.16   */
   16.17 @@ -57,7 +53,6 @@
   16.18      private static final boolean SUPPORTED = isWebSocket();
   16.19      private final Object ws;
   16.20      private final JSONCall call;
   16.21 -
   16.22      LoadWS(JSONCall first, String url) {
   16.23          call = first;
   16.24          ws = initWebSocket(this, url);
   16.25 @@ -84,18 +79,17 @@
   16.26          }
   16.27      }
   16.28      
   16.29 +    
   16.30 +    @JavaScriptBody(args = { "data" }, body = "try {\n"
   16.31 +        + "    return eval('(' + data + ')');\n"
   16.32 +        + "  } catch (error) {;\n"
   16.33 +        + "    return data;\n"
   16.34 +        + "  }\n"
   16.35 +    )
   16.36 +    private static native Object toJSON(String data);
   16.37 +    
   16.38      void onMessage(Object ev, String data) {
   16.39 -        Object json;
   16.40 -        try {
   16.41 -            data = data.trim();
   16.42 -            
   16.43 -            JSONTokener tok = new JSONTokener(data);
   16.44 -            Object obj;
   16.45 -            obj = data.startsWith("[") ? new JSONArray(tok) : new JSONObject(tok);
   16.46 -            json = LoadJSON.convertToArray(obj);
   16.47 -        } catch (JSONException ex) {
   16.48 -            json = data;
   16.49 -        }
   16.50 +        Object json = toJSON(data);
   16.51          call.notifySuccess(json);
   16.52      }
   16.53      
   16.54 @@ -113,20 +107,28 @@
   16.55      }
   16.56  
   16.57      @JavaScriptBody(args = { "back", "url" }, javacall = true, body = ""
   16.58 -        + "if (window.WebSocket) {"
   16.59 -        + "  try {"
   16.60 -        + "    var ws = new window.WebSocket(url);"
   16.61 -        + "    ws.onopen = function(ev) { back.@org.netbeans.html.ko4j.LoadWS::onOpen(Ljava/lang/Object;)(ev); };"
   16.62 -        + "    ws.onmessage = function(ev) { back.@org.netbeans.html.ko4j.LoadWS::onMessage(Ljava/lang/Object;Ljava/lang/String;)(ev, ev.data); };"
   16.63 -        + "    ws.onerror = function(ev) { back.@org.netbeans.html.ko4j.LoadWS::onError(Ljava/lang/Object;)(ev); };"
   16.64 -        + "    ws.onclose = function(ev) { back.@org.netbeans.html.ko4j.LoadWS::onClose(ZILjava/lang/String;)(ev.wasClean, ev.code, ev.reason); };"
   16.65 -        + "    return ws;"
   16.66 -        + "  } catch (ex) {"
   16.67 -        + "    return null;"
   16.68 -        + "  }"
   16.69 -        + "} else {"
   16.70 -        + "  return null;"
   16.71 -        + "}"
   16.72 +        + "if (window.WebSocket) {\n"
   16.73 +        + "  try {\n"
   16.74 +        + "    var ws = new window.WebSocket(url);\n"
   16.75 +        + "    ws.onopen = function(ev) {\n"
   16.76 +        + "      back.@org.netbeans.html.ko4j.LoadWS::onOpen(Ljava/lang/Object;)(ev);\n"
   16.77 +        + "    };\n"
   16.78 +        + "    ws.onmessage = function(ev) {\n"
   16.79 +        + "      back.@org.netbeans.html.ko4j.LoadWS::onMessage(Ljava/lang/Object;Ljava/lang/String;)(ev, ev.data);\n"
   16.80 +        + "    };\n"
   16.81 +        + "    ws.onerror = function(ev) {\n"
   16.82 +        + "      back.@org.netbeans.html.ko4j.LoadWS::onError(Ljava/lang/Object;)(ev);\n"
   16.83 +        + "    };\n"
   16.84 +        + "    ws.onclose = function(ev) {\n"
   16.85 +        + "      back.@org.netbeans.html.ko4j.LoadWS::onClose(ZILjava/lang/String;)(ev.wasClean, ev.code, ev.reason);\n"
   16.86 +        + "    };\n"
   16.87 +        + "    return ws;\n"
   16.88 +        + "  } catch (ex) {\n"
   16.89 +        + "    return null;\n"
   16.90 +        + "  }\n"
   16.91 +        + "} else {\n"
   16.92 +        + "  return null;\n"
   16.93 +        + "}\n"
   16.94      )
   16.95      private static Object initWebSocket(Object back, String url) {
   16.96          return null;
    17.1 --- a/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java	Thu Jan 09 19:12:55 2014 +0100
    17.2 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java	Thu Jan 09 20:39:23 2014 +0100
    17.3 @@ -66,8 +66,6 @@
    17.4  import org.apidesign.html.json.spi.WSTransfer;
    17.5  import org.apidesign.html.json.tck.KOTest;
    17.6  import org.apidesign.html.json.tck.KnockoutTCK;
    17.7 -import org.json.JSONException;
    17.8 -import org.json.JSONObject;
    17.9  import org.openide.util.lookup.ServiceProvider;
   17.10  import org.testng.annotations.Factory;
   17.11  import static org.testng.Assert.*;
   17.12 @@ -163,16 +161,17 @@
   17.13  
   17.14      @Override
   17.15      public Object createJSON(Map<String, Object> values) {
   17.16 -        JSONObject json = new JSONObject();
   17.17 +        Object json = createJSON();
   17.18          for (Map.Entry<String, Object> entry : values.entrySet()) {
   17.19 -            try {
   17.20 -                json.put(entry.getKey(), entry.getValue());
   17.21 -            } catch (JSONException ex) {
   17.22 -                throw new IllegalStateException(ex);
   17.23 -            }
   17.24 +            setProperty(json, entry.getKey(), entry.getValue());
   17.25          }
   17.26          return json;
   17.27      }
   17.28 +    
   17.29 +    @JavaScriptBody(args = {}, body = "return new Object();")
   17.30 +    private static native Object createJSON();
   17.31 +    @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
   17.32 +    private static native void setProperty(Object json, String key, Object value);
   17.33  
   17.34      @Override
   17.35      @JavaScriptBody(args = { "s", "args" }, body = ""