1.1 --- a/.hgtags Wed Jan 08 13:18:34 2014 +0100
1.2 +++ b/.hgtags Tue Jan 14 14:18:50 2014 +0100
1.3 @@ -7,3 +7,4 @@
1.4 6656cca0a73a0f0c3d0a349f471a6c5bab6035a9 release-0.5
1.5 3e5e3c96f9f13bc1487c46e050a6d545c427d945 release-0.6
1.6 53634fd10e30d0fcfd8d05a2bdf2df36c50ee237 netbeans_base
1.7 +da6c0d295eed1c309ce108f70df158c5b4ef09aa release-0.7
2.1 --- a/boot-fx/pom.xml Wed Jan 08 13:18:34 2014 +0100
2.2 +++ b/boot-fx/pom.xml Tue Jan 14 14:18:50 2014 +0100
2.3 @@ -4,11 +4,11 @@
2.4 <parent>
2.5 <groupId>org.netbeans.html</groupId>
2.6 <artifactId>pom</artifactId>
2.7 - <version>0.7-SNAPSHOT</version>
2.8 + <version>0.8-SNAPSHOT</version>
2.9 </parent>
2.10 <groupId>org.netbeans.html</groupId>
2.11 <artifactId>net.java.html.boot.fx</artifactId>
2.12 - <version>0.7-SNAPSHOT</version>
2.13 + <version>0.8-SNAPSHOT</version>
2.14 <name>FX WebView Bootstrap</name>
2.15 <packaging>bundle</packaging>
2.16 <url>http://maven.apache.org</url>
2.17 @@ -34,8 +34,8 @@
2.18 <artifactId>maven-compiler-plugin</artifactId>
2.19 <version>2.3.2</version>
2.20 <configuration>
2.21 - <source>1.7</source>
2.22 - <target>1.7</target>
2.23 + <source>1.6</source>
2.24 + <target>1.6</target>
2.25 </configuration>
2.26 </plugin>
2.27 </plugins>
3.1 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java Wed Jan 08 13:18:34 2014 +0100
3.2 +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java Tue Jan 14 14:18:50 2014 +0100
3.3 @@ -46,8 +46,8 @@
3.4 import java.io.Reader;
3.5 import java.net.URL;
3.6 import java.util.ArrayList;
3.7 -import java.util.Arrays;
3.8 import java.util.List;
3.9 +import java.util.concurrent.Executor;
3.10 import java.util.logging.Level;
3.11 import java.util.logging.Logger;
3.12 import javafx.application.Platform;
3.13 @@ -60,7 +60,8 @@
3.14 *
3.15 * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
3.16 */
3.17 -public abstract class AbstractFXPresenter implements Fn.Presenter {
3.18 +public abstract class AbstractFXPresenter
3.19 +implements Fn.Presenter, Fn.ToJavaScript, Fn.FromJavaScript, Executor {
3.20 static final Logger LOG = Logger.getLogger(FXPresenter.class.getName());
3.21 protected static int cnt;
3.22 protected List<String> scripts;
3.23 @@ -69,6 +70,10 @@
3.24
3.25 @Override
3.26 public Fn defineFn(String code, String... names) {
3.27 + return defineJSFn(code, names);
3.28 + }
3.29 +
3.30 + final JSFn defineJSFn(String code, String... names) {
3.31 StringBuilder sb = new StringBuilder();
3.32 sb.append("(function() {");
3.33 sb.append(" return function(");
3.34 @@ -153,6 +158,88 @@
3.35 protected abstract void waitFinished();
3.36
3.37 protected abstract WebView findView(final URL resource);
3.38 +
3.39 + final JSObject convertArrays(Object[] arr) {
3.40 + for (int i = 0; i < arr.length; i++) {
3.41 + if (arr[i] instanceof Object[]) {
3.42 + arr[i] = convertArrays((Object[]) arr[i]);
3.43 + }
3.44 + }
3.45 + final JSObject wrapArr = (JSObject)wrapArrFn().call("array", arr); // NOI18N
3.46 + return wrapArr;
3.47 + }
3.48 +
3.49 + private JSObject wrapArrImpl;
3.50 + private final JSObject wrapArrFn() {
3.51 + if (wrapArrImpl == null) {
3.52 + try {
3.53 + wrapArrImpl = (JSObject)defineJSFn(" var k = {};"
3.54 + + " k.array= function() {"
3.55 + + " return Array.prototype.slice.call(arguments);"
3.56 + + " };"
3.57 + + " return k;"
3.58 + ).invokeImpl(null, false);
3.59 + } catch (Exception ex) {
3.60 + throw new IllegalStateException(ex);
3.61 + }
3.62 + }
3.63 + return wrapArrImpl;
3.64 + }
3.65 +
3.66 + final Object checkArray(Object val) {
3.67 + int length = ((Number) arraySizeFn().call("array", val, null)).intValue();
3.68 + if (length == -1) {
3.69 + return val;
3.70 + }
3.71 + Object[] arr = new Object[length];
3.72 + arraySizeFn().call("array", val, arr);
3.73 + return arr;
3.74 + }
3.75 + private JSObject arraySize;
3.76 + private final JSObject arraySizeFn() {
3.77 + if (arraySize == null) {
3.78 + try {
3.79 + arraySize = (JSObject)defineJSFn(" var k = {};"
3.80 + + " k.array = function(arr, to) {"
3.81 + + " if (to === null) {"
3.82 + + " if (Object.prototype.toString.call(arr) === '[object Array]') return arr.length;"
3.83 + + " else return -1;"
3.84 + + " } else {"
3.85 + + " var l = arr.length;"
3.86 + + " for (var i = 0; i < l; i++) to[i] = arr[i];"
3.87 + + " return l;"
3.88 + + " }"
3.89 + + " };"
3.90 + + " return k;"
3.91 + ).invokeImpl(null, false);
3.92 + } catch (Exception ex) {
3.93 + throw new IllegalStateException(ex);
3.94 + }
3.95 + }
3.96 + return arraySize;
3.97 + }
3.98 +
3.99 + @Override
3.100 + public Object toJava(Object jsArray) {
3.101 + return checkArray(jsArray);
3.102 + }
3.103 +
3.104 + @Override
3.105 + public Object toJavaScript(Object toReturn) {
3.106 + if (toReturn instanceof Object[]) {
3.107 + return convertArrays((Object[])toReturn);
3.108 + } else {
3.109 + return toReturn;
3.110 + }
3.111 + }
3.112 +
3.113 + @Override public void execute(Runnable r) {
3.114 + if (Platform.isFxApplicationThread()) {
3.115 + r.run();
3.116 + } else {
3.117 + Platform.runLater(r);
3.118 + }
3.119 + }
3.120
3.121 private static final class JSFn extends Fn {
3.122
3.123 @@ -168,15 +255,32 @@
3.124
3.125 @Override
3.126 public Object invoke(Object thiz, Object... args) throws Exception {
3.127 + return invokeImpl(thiz, true, args);
3.128 + }
3.129 +
3.130 + final Object invokeImpl(Object thiz, boolean arrayChecks, Object... args) throws Exception {
3.131 try {
3.132 if (LOG.isLoggable(Level.FINE)) {
3.133 LOG.log(Level.FINE, "calling {0} function #{1}", new Object[]{++call, id});
3.134 }
3.135 List<Object> all = new ArrayList<Object>(args.length + 1);
3.136 all.add(thiz == null ? fn : thiz);
3.137 - all.addAll(Arrays.asList(args));
3.138 + for (int i = 0; i < args.length; i++) {
3.139 + if (arrayChecks && args[i] instanceof Object[]) {
3.140 + Object[] arr = (Object[]) args[i];
3.141 + Object conv = ((AbstractFXPresenter)presenter()).convertArrays(arr);
3.142 + args[i] = conv;
3.143 + }
3.144 + all.add(args[i]);
3.145 + }
3.146 Object ret = fn.call("call", all.toArray()); // NOI18N
3.147 - return ret == fn ? null : ret;
3.148 + if (ret == fn) {
3.149 + return null;
3.150 + }
3.151 + if (!arrayChecks) {
3.152 + return ret;
3.153 + }
3.154 + return ((AbstractFXPresenter)presenter()).checkArray(ret);
3.155 } catch (Error t) {
3.156 t.printStackTrace();
3.157 throw t;
4.1 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java Wed Jan 08 13:18:34 2014 +0100
4.2 +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java Tue Jan 14 14:18:50 2014 +0100
4.3 @@ -169,6 +169,7 @@
4.4 @Override
4.5 public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) {
4.6 if (newState.equals(Worker.State.SUCCEEDED)) {
4.7 + FXConsole.register(view.getEngine());
4.8 onLoad.onPageLoad();
4.9 }
4.10 if (newState.equals(Worker.State.FAILED)) {
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXConsole.java Tue Jan 14 14:18:50 2014 +0100
5.3 @@ -0,0 +1,84 @@
5.4 +/**
5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5.6 + *
5.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
5.8 + *
5.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
5.10 + * Other names may be trademarks of their respective owners.
5.11 + *
5.12 + * The contents of this file are subject to the terms of either the GNU
5.13 + * General Public License Version 2 only ("GPL") or the Common
5.14 + * Development and Distribution License("CDDL") (collectively, the
5.15 + * "License"). You may not use this file except in compliance with the
5.16 + * License. You can obtain a copy of the License at
5.17 + * http://www.netbeans.org/cddl-gplv2.html
5.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
5.19 + * specific language governing permissions and limitations under the
5.20 + * License. When distributing the software, include this License Header
5.21 + * Notice in each file and include the License file at
5.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
5.23 + * particular file as subject to the "Classpath" exception as provided
5.24 + * by Oracle in the GPL Version 2 section of the License file that
5.25 + * accompanied this code. If applicable, add the following below the
5.26 + * License Header, with the fields enclosed by brackets [] replaced by
5.27 + * your own identifying information:
5.28 + * "Portions Copyrighted [year] [name of copyright owner]"
5.29 + *
5.30 + * Contributor(s):
5.31 + *
5.32 + * The Original Software is NetBeans. The Initial Developer of the Original
5.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
5.34 + *
5.35 + * If you wish your version of this file to be governed by only the CDDL
5.36 + * or only the GPL Version 2, indicate your decision by adding
5.37 + * "[Contributor] elects to include this software in this distribution
5.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
5.39 + * single choice of license, a recipient has the option to distribute
5.40 + * your version of this file under either the CDDL, the GPL Version 2 or
5.41 + * to extend the choice of license to its licensees as provided above.
5.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
5.43 + * Version 2 license, then the option applies only if the new code is
5.44 + * made subject to such option by the copyright holder.
5.45 + */
5.46 +package org.netbeans.html.boot.fx;
5.47 +
5.48 +import java.util.logging.Level;
5.49 +import java.util.logging.Logger;
5.50 +import javafx.scene.web.WebEngine;
5.51 +import netscape.javascript.JSObject;
5.52 +
5.53 +/** This is an implementation package - just
5.54 + * include its JAR on classpath and use official {@link Context} API
5.55 + * to access the functionality.
5.56 + * <p>
5.57 + * Redirects JavaScript's messages to Java's {@link Logger}.
5.58 + *
5.59 + * @author Jaroslav Tulach <jtulach@netbeans.org>
5.60 + */
5.61 +public final class FXConsole {
5.62 + static final Logger LOG = Logger.getLogger(FXConsole.class.getName());
5.63 +
5.64 + private FXConsole() {
5.65 + }
5.66 +
5.67 + static void register(WebEngine eng) {
5.68 + JSObject fn = (JSObject) eng.executeScript(""
5.69 + + "(function(attr, l, c) {"
5.70 + + " window.console[attr] = function(msg) { c.log(l, msg); };"
5.71 + + "})"
5.72 + );
5.73 + FXConsole c = new FXConsole();
5.74 + c.registerImpl(fn, "log", Level.INFO);
5.75 + c.registerImpl(fn, "info", Level.INFO);
5.76 + c.registerImpl(fn, "warn", Level.WARNING);
5.77 + c.registerImpl(fn, "error", Level.SEVERE);
5.78 + }
5.79 +
5.80 + private void registerImpl(JSObject eng, String attr, Level l) {
5.81 + eng.call("call", null, attr, l, this);
5.82 + }
5.83 +
5.84 + public void log(Level l, String msg) {
5.85 + LOG.log(l, msg);
5.86 + }
5.87 +}
6.1 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java Wed Jan 08 13:18:34 2014 +0100
6.2 +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java Tue Jan 14 14:18:50 2014 +0100
6.3 @@ -42,22 +42,12 @@
6.4 */
6.5 package org.netbeans.html.boot.fx;
6.6
6.7 -import java.io.BufferedReader;
6.8 import java.io.File;
6.9 -import java.io.Reader;
6.10 import java.lang.reflect.Method;
6.11 import java.net.URL;
6.12 import java.net.URLClassLoader;
6.13 -import java.util.ArrayList;
6.14 -import java.util.Arrays;
6.15 -import java.util.List;
6.16 -import java.util.logging.Level;
6.17 -import java.util.logging.Logger;
6.18 -import javafx.application.Platform;
6.19 -import javafx.scene.web.WebEngine;
6.20 import javafx.scene.web.WebView;
6.21 import net.java.html.boot.BrowserBuilder;
6.22 -import netscape.javascript.JSObject;
6.23 import org.apidesign.html.boot.spi.Fn;
6.24 import org.openide.util.lookup.ServiceProvider;
6.25
7.1 --- a/boot-fx/src/test/java/org/apidesign/html/boot/fx/FxJavaScriptTst.java Wed Jan 08 13:18:34 2014 +0100
7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
7.3 @@ -1,55 +0,0 @@
7.4 -/**
7.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
7.6 - *
7.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
7.8 - *
7.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7.10 - * Other names may be trademarks of their respective owners.
7.11 - *
7.12 - * The contents of this file are subject to the terms of either the GNU
7.13 - * General Public License Version 2 only ("GPL") or the Common
7.14 - * Development and Distribution License("CDDL") (collectively, the
7.15 - * "License"). You may not use this file except in compliance with the
7.16 - * License. You can obtain a copy of the License at
7.17 - * http://www.netbeans.org/cddl-gplv2.html
7.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
7.19 - * specific language governing permissions and limitations under the
7.20 - * License. When distributing the software, include this License Header
7.21 - * Notice in each file and include the License file at
7.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
7.23 - * particular file as subject to the "Classpath" exception as provided
7.24 - * by Oracle in the GPL Version 2 section of the License file that
7.25 - * accompanied this code. If applicable, add the following below the
7.26 - * License Header, with the fields enclosed by brackets [] replaced by
7.27 - * your own identifying information:
7.28 - * "Portions Copyrighted [year] [name of copyright owner]"
7.29 - *
7.30 - * Contributor(s):
7.31 - *
7.32 - * The Original Software is NetBeans. The Initial Developer of the Original
7.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
7.34 - *
7.35 - * If you wish your version of this file to be governed by only the CDDL
7.36 - * or only the GPL Version 2, indicate your decision by adding
7.37 - * "[Contributor] elects to include this software in this distribution
7.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
7.39 - * single choice of license, a recipient has the option to distribute
7.40 - * your version of this file under either the CDDL, the GPL Version 2 or
7.41 - * to extend the choice of license to its licensees as provided above.
7.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
7.43 - * Version 2 license, then the option applies only if the new code is
7.44 - * made subject to such option by the copyright holder.
7.45 - */
7.46 -package org.apidesign.html.boot.fx;
7.47 -
7.48 -import org.apidesign.html.json.tck.JavaScriptTCK;
7.49 -
7.50 -/**
7.51 - *
7.52 - * @author Jaroslav Tulach <jtulach@netbeans.org>
7.53 - */
7.54 -public final class FxJavaScriptTst extends JavaScriptTCK {
7.55 - public static Class[] tests() {
7.56 - return testClasses();
7.57 - }
7.58 -}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXJavaScriptTest.java Tue Jan 14 14:18:50 2014 +0100
8.3 @@ -0,0 +1,117 @@
8.4 +/**
8.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
8.6 + *
8.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
8.8 + *
8.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
8.10 + * Other names may be trademarks of their respective owners.
8.11 + *
8.12 + * The contents of this file are subject to the terms of either the GNU
8.13 + * General Public License Version 2 only ("GPL") or the Common
8.14 + * Development and Distribution License("CDDL") (collectively, the
8.15 + * "License"). You may not use this file except in compliance with the
8.16 + * License. You can obtain a copy of the License at
8.17 + * http://www.netbeans.org/cddl-gplv2.html
8.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
8.19 + * specific language governing permissions and limitations under the
8.20 + * License. When distributing the software, include this License Header
8.21 + * Notice in each file and include the License file at
8.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
8.23 + * particular file as subject to the "Classpath" exception as provided
8.24 + * by Oracle in the GPL Version 2 section of the License file that
8.25 + * accompanied this code. If applicable, add the following below the
8.26 + * License Header, with the fields enclosed by brackets [] replaced by
8.27 + * your own identifying information:
8.28 + * "Portions Copyrighted [year] [name of copyright owner]"
8.29 + *
8.30 + * Contributor(s):
8.31 + *
8.32 + * The Original Software is NetBeans. The Initial Developer of the Original
8.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
8.34 + *
8.35 + * If you wish your version of this file to be governed by only the CDDL
8.36 + * or only the GPL Version 2, indicate your decision by adding
8.37 + * "[Contributor] elects to include this software in this distribution
8.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
8.39 + * single choice of license, a recipient has the option to distribute
8.40 + * your version of this file under either the CDDL, the GPL Version 2 or
8.41 + * to extend the choice of license to its licensees as provided above.
8.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
8.43 + * Version 2 license, then the option applies only if the new code is
8.44 + * made subject to such option by the copyright holder.
8.45 + */
8.46 +package org.netbeans.html.boot.fx;
8.47 +
8.48 +import java.lang.annotation.Annotation;
8.49 +import java.lang.reflect.Method;
8.50 +import java.util.ArrayList;
8.51 +import java.util.List;
8.52 +import java.util.concurrent.Executors;
8.53 +import net.java.html.boot.BrowserBuilder;
8.54 +import org.apidesign.html.boot.spi.Fn;
8.55 +import org.apidesign.html.json.tck.KOTest;
8.56 +import org.netbeans.html.boot.impl.FnContext;
8.57 +import org.apidesign.html.boot.spi.Fn;
8.58 +import org.apidesign.html.json.tck.JavaScriptTCK;
8.59 +import org.apidesign.html.json.tck.KOTest;
8.60 +import org.testng.annotations.Factory;
8.61 +import org.testng.annotations.Test;
8.62 +
8.63 +/**
8.64 + *
8.65 + * @author Jaroslav Tulach <jtulach@netbeans.org>
8.66 + */
8.67 +public class FXJavaScriptTest {
8.68 + private static Class<?> browserClass;
8.69 + private static Fn.Presenter browserPresenter;
8.70 +
8.71 + public FXJavaScriptTest() {
8.72 + }
8.73 +
8.74 + @Factory public static Object[] compatibilityTests() throws Exception {
8.75 + final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(FXJavaScriptTest.class).
8.76 + loadPage("empty.html").
8.77 + invoke("initialized");
8.78 +
8.79 + Executors.newSingleThreadExecutor().submit(new Runnable() {
8.80 + @Override
8.81 + public void run() {
8.82 + bb.showAndWait();
8.83 + }
8.84 + });
8.85 +
8.86 + List<Object> res = new ArrayList<Object>();
8.87 + Class<? extends Annotation> test =
8.88 + loadClass().getClassLoader().loadClass(KOTest.class.getName()).
8.89 + asSubclass(Annotation.class);
8.90 +
8.91 + Class[] arr = (Class[]) loadClass().getDeclaredMethod("tests").invoke(null);
8.92 + for (Class c : arr) {
8.93 + for (Method m : c.getMethods()) {
8.94 + if (m.getAnnotation(test) != null) {
8.95 + res.add(new KOFx(browserPresenter, m));
8.96 + }
8.97 + }
8.98 + }
8.99 + return res.toArray();
8.100 + }
8.101 +
8.102 + static synchronized Class<?> loadClass() throws InterruptedException {
8.103 + while (browserClass == null) {
8.104 + FXJavaScriptTest.class.wait();
8.105 + }
8.106 + return browserClass;
8.107 + }
8.108 +
8.109 + public static synchronized void ready(Class<?> browserCls) throws Exception {
8.110 + browserClass = browserCls;
8.111 + browserPresenter = Fn.activePresenter();
8.112 + FXJavaScriptTest.class.notifyAll();
8.113 + }
8.114 +
8.115 + public static void initialized() throws Exception {
8.116 + Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(FXJavaScriptTest.class.getName());
8.117 + Method m = classpathClass.getMethod("ready", Class.class);
8.118 + m.invoke(null, FxJavaScriptTst.class);
8.119 + }
8.120 +}
9.1 --- a/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXPresenterTst.java Wed Jan 08 13:18:34 2014 +0100
9.2 +++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/FXPresenterTst.java Tue Jan 14 14:18:50 2014 +0100
9.3 @@ -42,6 +42,8 @@
9.4 */
9.5 package org.netbeans.html.boot.fx;
9.6
9.7 +import java.util.logging.Handler;
9.8 +import java.util.logging.LogRecord;
9.9 import net.java.html.js.JavaScriptBody;
9.10 import static org.testng.Assert.*;
9.11 import org.testng.annotations.Test;
9.12 @@ -57,9 +59,39 @@
9.13 assertEquals(run.cnt, 1, "Can call even private implementation classes");
9.14 }
9.15
9.16 + @Test public void checkConsoleLogging() {
9.17 + class H extends Handler {
9.18 + LogRecord record;
9.19 +
9.20 + @Override
9.21 + public void publish(LogRecord record) {
9.22 + assert this.record == null;
9.23 + this.record = record;
9.24 + }
9.25 +
9.26 + @Override
9.27 + public void flush() {
9.28 + }
9.29 +
9.30 + @Override
9.31 + public void close() throws SecurityException {
9.32 + }
9.33 + }
9.34 + H h = new H();
9.35 + FXConsole.LOG.addHandler(h);
9.36 +
9.37 + log("Ahoj");
9.38 +
9.39 + assert h.record != null : "Some log record obtained";
9.40 + assert "Ahoj".equals(h.record.getMessage()) : "It is our Ahoj: " + h.record.getMessage();
9.41 + }
9.42 +
9.43 @JavaScriptBody(args = { "r" }, javacall = true, body = "r.@java.lang.Runnable::run()();")
9.44 private static native void callback(Runnable r);
9.45
9.46 + @JavaScriptBody(args = { "msg" }, body = "console.log(msg);")
9.47 + private static native void log(String msg);
9.48 +
9.49 private static class R implements Runnable {
9.50 int cnt;
9.51
10.1 --- a/boot/pom.xml Wed Jan 08 13:18:34 2014 +0100
10.2 +++ b/boot/pom.xml Tue Jan 14 14:18:50 2014 +0100
10.3 @@ -4,11 +4,11 @@
10.4 <parent>
10.5 <groupId>org.netbeans.html</groupId>
10.6 <artifactId>pom</artifactId>
10.7 - <version>0.7-SNAPSHOT</version>
10.8 + <version>0.8-SNAPSHOT</version>
10.9 </parent>
10.10 <groupId>org.netbeans.html</groupId>
10.11 <artifactId>net.java.html.boot</artifactId>
10.12 - <version>0.7-SNAPSHOT</version>
10.13 + <version>0.8-SNAPSHOT</version>
10.14 <packaging>bundle</packaging>
10.15 <name>Browser Bootstrap</name>
10.16 <url>http://maven.apache.org</url>
11.1 --- a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java Wed Jan 08 13:18:34 2014 +0100
11.2 +++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java Tue Jan 14 14:18:50 2014 +0100
11.3 @@ -110,7 +110,7 @@
11.4 * {@link #loadPage(java.lang.String)}.
11.5 *
11.6 * @param context any instances that should be available to the builder -
11.7 - * implemenation dependant
11.8 + * implementation dependant
11.9 * @return new browser builder
11.10 */
11.11 public static BrowserBuilder newBrowser(Object... context) {
12.1 --- a/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Wed Jan 08 13:18:34 2014 +0100
12.2 +++ b/boot/src/main/java/org/apidesign/html/boot/spi/Fn.java Tue Jan 14 14:18:50 2014 +0100
12.3 @@ -51,6 +51,7 @@
12.4 import java.util.HashSet;
12.5 import java.util.Map;
12.6 import java.util.Set;
12.7 +import java.util.concurrent.Executor;
12.8 import net.java.html.js.JavaScriptBody;
12.9 import org.netbeans.html.boot.impl.FnContext;
12.10
12.11 @@ -87,7 +88,7 @@
12.12 * @return true, if proper presenter is used
12.13 */
12.14 public final boolean isValid() {
12.15 - return FnContext.currentPresenter(false) == presenter;
12.16 + return presenter != null && FnContext.currentPresenter(false) == presenter;
12.17 }
12.18
12.19 /** Helper method to check if the provided instance is valid function.
12.20 @@ -109,13 +110,29 @@
12.21 * @param code the body of the function (can reference <code>this</code> and <code>names</code> variables)
12.22 * @param names names of individual parameters
12.23 * @return the function object that can be {@link Fn#invoke(java.lang.Object, java.lang.Object...) invoked}
12.24 + * - can return <code>null</code> if there is {@link #activePresenter() no presenter}
12.25 * @since 0.7
12.26 */
12.27 public static Fn define(Class<?> caller, String code, String... names) {
12.28 - return FnContext.currentPresenter(false).defineFn(code, names);
12.29 + final Presenter p = FnContext.currentPresenter(false);
12.30 + return p == null ? null : p.defineFn(code, names);
12.31 }
12.32
12.33 private static final Map<String,Set<Presenter>> LOADED = new HashMap<String, Set<Presenter>>();
12.34 +
12.35 + /** Wraps function to ensure that the script represented by <code>resource</code>
12.36 + * gets loaded into the browser environment before the function <code>fn</code>
12.37 + * is executed.
12.38 + *
12.39 + * @param fn original function to call
12.40 + * @param caller the class who wishes to define/call the function
12.41 + * @param resource resources (accessible via {@link ClassLoader#getResource(java.lang.String)})
12.42 + * with a <em>JavaScript</em> that is supposed to loaded into the browser
12.43 + * environment
12.44 + * @return function that ensures the script is loaded and then delegates
12.45 + * to <code>fn</code>
12.46 + * @since 0.7
12.47 + */
12.48 public static Fn preload(final Fn fn, final Class<?> caller, final String resource) {
12.49 return new Fn() {
12.50 @Override
12.51 @@ -146,7 +163,7 @@
12.52 * @since 0.7
12.53 */
12.54 public static Presenter activePresenter() {
12.55 - return FnContext.currentPresenter(true);
12.56 + return FnContext.currentPresenter(false);
12.57 }
12.58
12.59 /** Activates given presenter. Used by the code generated by
12.60 @@ -175,11 +192,28 @@
12.61 * @throws Exception if something goes wrong, as exception may be thrown
12.62 */
12.63 public abstract Object invoke(Object thiz, Object... args) throws Exception;
12.64 +
12.65 + /** Provides the function implementation access to the presenter provided
12.66 + * in {@link #Fn(org.apidesign.html.boot.spi.Fn.Presenter) the constructor).
12.67 + *
12.68 + * @return presenter passed in in the constructor (may be, but should not be <code>null</code>)
12.69 + * @since 0.7
12.70 + */
12.71 + protected final Presenter presenter() {
12.72 + return presenter;
12.73 + }
12.74
12.75 /** The representation of a <em>presenter</em> - usually a browser window.
12.76 * Should be provided by a library included in the application and registered
12.77 * in <code>META-INF/services</code>, for example with
12.78 * <code>@ServiceProvider(service = Fn.Presenter.class)</code> annotation.
12.79 + * <p>
12.80 + * Since 0.7 a presenter may implement {@link Executor} interface, in case
12.81 + * it supports single threaded execution environment. The executor's
12.82 + * {@link Executor#execute(java.lang.Runnable)} method is then supposed
12.83 + * to invoke the runnable immediately (in case we are on the right thread
12.84 + * already) or return and asynchronously invoke the runnable later on the
12.85 + * right thread (if we are on wrong thread).
12.86 */
12.87 public interface Presenter {
12.88 /** Creates new function with given parameter names and provided body.
12.89 @@ -208,4 +242,49 @@
12.90 */
12.91 public void loadScript(Reader code) throws Exception;
12.92 }
12.93 +
12.94 + /** Additional interface to be implemented by {@link Presenter}s that
12.95 + * wish to control what objects are passed into the JavaScript virtual
12.96 + * machine.
12.97 + * <p>
12.98 + * If a JavaScript engine makes callback to Java method that returns
12.99 + * a value, the {@link #toJavaScript(java.lang.Object)} method is
12.100 + * consulted to convert the Java value to something reasonable inside
12.101 + * JavaScript VM.
12.102 + * <p>
12.103 + * <em>Note:</em> The implementation based on <em>JavaFX</em> <code>WebView</code>
12.104 + * uses this interface to convert Java arrays to JavaScript ones.
12.105 + *
12.106 + * @see Presenter
12.107 + * @since 0.7
12.108 + */
12.109 + public interface ToJavaScript {
12.110 + /** Convert a Java return value into some object suitable for
12.111 + * JavaScript virtual machine.
12.112 + *
12.113 + * @param toReturn the Java object to be returned
12.114 + * @return the replacement value to return instead
12.115 + */
12.116 + public Object toJavaScript(Object toReturn);
12.117 + }
12.118 +
12.119 + /** Additional interface to be implemented by {@link Presenter}s that
12.120 + * need to convert JavaScript object (usually array) to Java object
12.121 + * when calling back from JavaScript to Java.
12.122 + * <p>
12.123 + * <em>Note:</em> The implementation based on <em>JavaFX</em>
12.124 + * <code>WebView</code> uses this interface to convert JavaScript arrays to
12.125 + * Java ones.
12.126 + *
12.127 + * @since 0.7
12.128 + */
12.129 + public interface FromJavaScript {
12.130 + /** Convert a JavaScript object into suitable Java representation
12.131 + * before a Java method is called with this object as an argument.
12.132 + *
12.133 + * @param js the JavaScript object
12.134 + * @return replacement object for
12.135 + */
12.136 + public Object toJava(Object js);
12.137 + }
12.138 }
13.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java Wed Jan 08 13:18:34 2014 +0100
13.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java Tue Jan 14 14:18:50 2014 +0100
13.3 @@ -70,7 +70,7 @@
13.4 *
13.5 * @author Jaroslav Tulach <jtulach@netbeans.org>
13.6 */
13.7 -public final class FnUtils implements Fn.Presenter {
13.8 +public final class FnUtils {
13.9
13.10 private FnUtils() {
13.11 }
13.12 @@ -114,7 +114,7 @@
13.13 return true;
13.14 }
13.15 Class<?> clazz;
13.16 - Closeable c = Fn.activate(new FnUtils());
13.17 + Closeable c = Fn.activate(new TrueFn());
13.18 try {
13.19 try {
13.20 clazz = Class.forName(Test.class.getName(), true, l);
13.21 @@ -206,19 +206,7 @@
13.22 throw new IllegalStateException("Can't execute " + resource, ex);
13.23 }
13.24 }
13.25 -
13.26 - @Override
13.27 - public Fn defineFn(String code, String... names) {
13.28 - return new TrueFn();
13.29 - }
13.30 -
13.31 - @Override
13.32 - public void displayPage(URL page, Runnable onPageLoad) {
13.33 - }
13.34 -
13.35 - @Override
13.36 - public void loadScript(Reader code) throws Exception {
13.37 - }
13.38 +
13.39
13.40 private static final class FindInClass extends ClassVisitor {
13.41 private String name;
13.42 @@ -270,9 +258,7 @@
13.43
13.44 @Override
13.45 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
13.46 - if ("Lnet/java/html/js/JavaScriptBody;".equals(desc) // NOI18N
13.47 - || "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;".equals(desc) // NOI18N
13.48 - ) {
13.49 + if ("Lnet/java/html/js/JavaScriptBody;".equals(desc)) { // NOI18N
13.50 found++;
13.51 return new FindInAnno();
13.52 }
13.53 @@ -289,10 +275,10 @@
13.54 if (body == null) {
13.55 return;
13.56 }
13.57 - generateBody();
13.58 + generateBody(true);
13.59 }
13.60
13.61 - private boolean generateBody() {
13.62 + private boolean generateBody(boolean hasCode) {
13.63 if (bodyGenerated) {
13.64 return false;
13.65 }
13.66 @@ -332,6 +318,11 @@
13.67 "org/apidesign/html/boot/spi/Fn", "define",
13.68 "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/String;)Lorg/apidesign/html/boot/spi/Fn;"
13.69 );
13.70 + Label noPresenter = new Label();
13.71 + if (hasCode) {
13.72 + super.visitInsn(Opcodes.DUP);
13.73 + super.visitJumpInsn(Opcodes.IFNULL, noPresenter);
13.74 + }
13.75 if (resource != null) {
13.76 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
13.77 super.visitLdcInsn(resource);
13.78 @@ -423,7 +414,12 @@
13.79 @Override
13.80 public SignatureVisitor visitArrayType() {
13.81 if (nowReturn) {
13.82 - throw new IllegalStateException("Not supported yet");
13.83 + return new SignatureVisitor(Opcodes.ASM4) {
13.84 + @Override
13.85 + public void visitClassType(String name) {
13.86 + returnType = Type.getType("[" + Type.getObjectType(name).getDescriptor());
13.87 + }
13.88 + };
13.89 }
13.90 loadObject();
13.91 return new SignatureWriter();
13.92 @@ -492,6 +488,10 @@
13.93 );
13.94 super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
13.95 }
13.96 + if (hasCode) {
13.97 + super.visitLabel(noPresenter);
13.98 + super.visitCode();
13.99 + }
13.100 return true;
13.101 }
13.102
13.103 @@ -499,7 +499,7 @@
13.104 public void visitEnd() {
13.105 super.visitEnd();
13.106 if (body != null) {
13.107 - if (generateBody()) {
13.108 + if (generateBody(false)) {
13.109 // native method
13.110 super.visitMaxs(1, 0);
13.111 }
13.112 @@ -609,10 +609,23 @@
13.113 }
13.114 }
13.115
13.116 - private static final class TrueFn extends Fn {
13.117 + private static final class TrueFn extends Fn implements Fn.Presenter {
13.118 @Override
13.119 public Object invoke(Object thiz, Object... args) throws Exception {
13.120 return Boolean.TRUE;
13.121 }
13.122 +
13.123 + @Override
13.124 + public Fn defineFn(String code, String... names) {
13.125 + return this;
13.126 + }
13.127 +
13.128 + @Override
13.129 + public void displayPage(URL page, Runnable onPageLoad) {
13.130 + }
13.131 +
13.132 + @Override
13.133 + public void loadScript(Reader code) throws Exception {
13.134 + }
13.135 } // end of TrueFn
13.136 }
14.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java Wed Jan 08 13:18:34 2014 +0100
14.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java Tue Jan 14 14:18:50 2014 +0100
14.3 @@ -43,6 +43,9 @@
14.4 package org.netbeans.html.boot.impl;
14.5
14.6 import java.io.IOException;
14.7 +import java.io.OutputStream;
14.8 +import java.io.OutputStreamWriter;
14.9 +import java.io.PrintWriter;
14.10 import java.io.Writer;
14.11 import java.util.Collections;
14.12 import java.util.HashMap;
14.13 @@ -85,6 +88,8 @@
14.14 public final class JavaScriptProcesor extends AbstractProcessor {
14.15 private final Map<String,Map<String,ExecutableElement>> javacalls =
14.16 new HashMap<String,Map<String,ExecutableElement>>();
14.17 + private final Map<String,Set<TypeElement>> bodies =
14.18 + new HashMap<String, Set<TypeElement>>();
14.19
14.20 @Override
14.21 public Set<String> getSupportedAnnotationTypes() {
14.22 @@ -107,6 +112,17 @@
14.23 JavaScriptBody jsb = e.getAnnotation(JavaScriptBody.class);
14.24 if (jsb == null) {
14.25 continue;
14.26 + } else {
14.27 + Set<TypeElement> classes = this.bodies.get(findPkg(e));
14.28 + if (classes == null) {
14.29 + classes = new HashSet<TypeElement>();
14.30 + bodies.put(findPkg(e), classes);
14.31 + }
14.32 + Element t = e.getEnclosingElement();
14.33 + while (!t.getKind().isClass() && !t.getKind().isInterface()) {
14.34 + t = t.getEnclosingElement();
14.35 + }
14.36 + classes.add((TypeElement)t);
14.37 }
14.38 String[] arr = jsb.args();
14.39 if (params.size() != arr.length) {
14.40 @@ -151,6 +167,7 @@
14.41
14.42 if (roundEnv.processingOver()) {
14.43 generateCallbackClass(javacalls);
14.44 + generateJavaScriptBodyList(bodies);
14.45 javacalls.clear();
14.46 }
14.47 return true;
14.48 @@ -262,6 +279,33 @@
14.49 }
14.50 }
14.51
14.52 + private void generateJavaScriptBodyList(Map<String,Set<TypeElement>> bodies) {
14.53 + for (Map.Entry<String, Set<TypeElement>> entry : bodies.entrySet()) {
14.54 + String pkg = entry.getKey();
14.55 + Set<TypeElement> classes = entry.getValue();
14.56 +
14.57 + try {
14.58 + FileObject out = processingEnv.getFiler().createResource(
14.59 + StandardLocation.CLASS_OUTPUT, pkg, "net.java.html.js.classes",
14.60 + classes.iterator().next()
14.61 + );
14.62 + OutputStream os = out.openOutputStream();
14.63 + try {
14.64 + PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
14.65 + for (TypeElement type : classes) {
14.66 + w.println(processingEnv.getElementUtils().getBinaryName(type));
14.67 + }
14.68 + w.flush();
14.69 + w.close();
14.70 + } finally {
14.71 + os.close();
14.72 + }
14.73 + } catch (IOException x) {
14.74 + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
14.75 + }
14.76 + }
14.77 + }
14.78 +
14.79 private void generateCallbackClass(Map<String,Map<String, ExecutableElement>> process) {
14.80 for (Map.Entry<String, Map<String, ExecutableElement>> pkgEn : process.entrySet()) {
14.81 String pkgName = pkgEn.getKey();
14.82 @@ -298,13 +342,26 @@
14.83 }
14.84
14.85 int cnt = 0;
14.86 + StringBuilder convert = new StringBuilder();
14.87 for (VariableElement ve : m.getParameters()) {
14.88 source.append(sep);
14.89 - source.append(ve.asType());
14.90 - source.append(" arg").append(++cnt);
14.91 + ++cnt;
14.92 + final TypeMirror t = ve.asType();
14.93 + if (!t.getKind().isPrimitive()) {
14.94 + source.append("Object");
14.95 + convert.append(" if (p instanceof org.apidesign.html.boot.spi.Fn.FromJavaScript) {\n");
14.96 + convert.append(" arg").append(cnt).
14.97 + append(" = ((org.apidesign.html.boot.spi.Fn.FromJavaScript)p).toJava(arg").append(cnt).
14.98 + append(");\n");
14.99 + convert.append(" }\n");
14.100 + } else {
14.101 + source.append(t);
14.102 + }
14.103 + source.append(" arg").append(cnt);
14.104 sep = ", ";
14.105 }
14.106 source.append(") throws Throwable {\n");
14.107 + source.append(convert);
14.108 if (processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_7) >= 0) {
14.109 source.append(" try (java.io.Closeable a = org.apidesign.html.boot.spi.Fn.activate(p)) { \n");
14.110 } else {
14.111 @@ -312,7 +369,7 @@
14.112 }
14.113 source.append(" ");
14.114 if (m.getReturnType().getKind() != TypeKind.VOID) {
14.115 - source.append("return ");
14.116 + source.append("Object $ret = ");
14.117 }
14.118 if (isStatic) {
14.119 source.append(((TypeElement)m.getEnclosingElement()).getQualifiedName());
14.120 @@ -326,12 +383,18 @@
14.121 sep = "";
14.122 for (VariableElement ve : m.getParameters()) {
14.123 source.append(sep);
14.124 - source.append("arg").append(++cnt);
14.125 + source.append("(").append(ve.asType());
14.126 + source.append(")arg").append(++cnt);
14.127 sep = ", ";
14.128 }
14.129 source.append(");\n");
14.130 if (m.getReturnType().getKind() == TypeKind.VOID) {
14.131 source.append(" return null;\n");
14.132 + } else {
14.133 + source.append(" if (p instanceof org.apidesign.html.boot.spi.Fn.ToJavaScript) {\n");
14.134 + source.append(" $ret = ((org.apidesign.html.boot.spi.Fn.ToJavaScript)p).toJavaScript($ret);\n");
14.135 + source.append(" }\n");
14.136 + source.append(" return $ret;\n");
14.137 }
14.138 if (processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_7) >= 0) {
14.139 source.append(" }\n");
15.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java Wed Jan 08 13:18:34 2014 +0100
15.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsAgent.java Tue Jan 14 14:18:50 2014 +0100
15.3 @@ -59,7 +59,11 @@
15.4 @Override
15.5 public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
15.6 try {
15.7 - return FnUtils.transform(classfileBuffer, loader);
15.8 + if (JsPkgCache.process(loader, className)) {
15.9 + return FnUtils.transform(classfileBuffer, loader);
15.10 + } else {
15.11 + return classfileBuffer;
15.12 + }
15.13 } catch (Exception ex) {
15.14 return classfileBuffer;
15.15 }
16.1 --- a/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java Wed Jan 08 13:18:34 2014 +0100
16.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsClassLoader.java Tue Jan 14 14:18:50 2014 +0100
16.3 @@ -85,6 +85,12 @@
16.4 if (name.equals(Fn.Presenter.class.getName())) {
16.5 return Fn.Presenter.class;
16.6 }
16.7 + if (name.equals(Fn.ToJavaScript.class.getName())) {
16.8 + return Fn.ToJavaScript.class;
16.9 + }
16.10 + if (name.equals(Fn.FromJavaScript.class.getName())) {
16.11 + return Fn.FromJavaScript.class;
16.12 + }
16.13 if (name.equals(FnUtils.class.getName())) {
16.14 return FnUtils.class;
16.15 }
16.16 @@ -111,10 +117,10 @@
16.17 }
16.18 is.close();
16.19 is = null;
16.20 - arr = FnUtils.transform(arr, JsClassLoader.this);
16.21 - if (arr != null) {
16.22 - return defineClass(name, arr, 0, arr.length);
16.23 + if (JsPkgCache.process(this, name)) {
16.24 + arr = FnUtils.transform(arr, JsClassLoader.this);
16.25 }
16.26 + return defineClass(name, arr, 0, arr.length);
16.27 } catch (IOException ex) {
16.28 throw new ClassNotFoundException("Can't load " + name, ex);
16.29 } finally {
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
17.2 +++ b/boot/src/main/java/org/netbeans/html/boot/impl/JsPkgCache.java Tue Jan 14 14:18:50 2014 +0100
17.3 @@ -0,0 +1,132 @@
17.4 +/**
17.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
17.6 + *
17.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
17.8 + *
17.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
17.10 + * Other names may be trademarks of their respective owners.
17.11 + *
17.12 + * The contents of this file are subject to the terms of either the GNU
17.13 + * General Public License Version 2 only ("GPL") or the Common
17.14 + * Development and Distribution License("CDDL") (collectively, the
17.15 + * "License"). You may not use this file except in compliance with the
17.16 + * License. You can obtain a copy of the License at
17.17 + * http://www.netbeans.org/cddl-gplv2.html
17.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
17.19 + * specific language governing permissions and limitations under the
17.20 + * License. When distributing the software, include this License Header
17.21 + * Notice in each file and include the License file at
17.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
17.23 + * particular file as subject to the "Classpath" exception as provided
17.24 + * by Oracle in the GPL Version 2 section of the License file that
17.25 + * accompanied this code. If applicable, add the following below the
17.26 + * License Header, with the fields enclosed by brackets [] replaced by
17.27 + * your own identifying information:
17.28 + * "Portions Copyrighted [year] [name of copyright owner]"
17.29 + *
17.30 + * Contributor(s):
17.31 + *
17.32 + * The Original Software is NetBeans. The Initial Developer of the Original
17.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
17.34 + *
17.35 + * If you wish your version of this file to be governed by only the CDDL
17.36 + * or only the GPL Version 2, indicate your decision by adding
17.37 + * "[Contributor] elects to include this software in this distribution
17.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
17.39 + * single choice of license, a recipient has the option to distribute
17.40 + * your version of this file under either the CDDL, the GPL Version 2 or
17.41 + * to extend the choice of license to its licensees as provided above.
17.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
17.43 + * Version 2 license, then the option applies only if the new code is
17.44 + * made subject to such option by the copyright holder.
17.45 + */
17.46 +package org.netbeans.html.boot.impl;
17.47 +
17.48 +import java.io.BufferedReader;
17.49 +import java.io.IOException;
17.50 +import java.io.InputStream;
17.51 +import java.io.InputStreamReader;
17.52 +import java.net.URL;
17.53 +import java.util.Collections;
17.54 +import java.util.Enumeration;
17.55 +import java.util.Map;
17.56 +import java.util.Set;
17.57 +import java.util.TreeSet;
17.58 +import java.util.WeakHashMap;
17.59 +import java.util.logging.Level;
17.60 +import java.util.logging.Logger;
17.61 +
17.62 +/**
17.63 + *
17.64 + * @author Jaroslav Tulach <jtulach@netbeans.org>
17.65 + */
17.66 +final class JsPkgCache {
17.67 + private final Map<String,Set<String>> props = new WeakHashMap<String, Set<String>>();
17.68 + private static final Map<ClassLoader, JsPkgCache> CACHE = new WeakHashMap<ClassLoader, JsPkgCache>();
17.69 + private static final Set<String> NONE = Collections.emptySet();
17.70 +
17.71 + public static boolean process(ClassLoader l, String className) {
17.72 + if (className.equals("org.netbeans.html.boot.impl.Test")) {
17.73 + return true;
17.74 + }
17.75 + Set<String> p;
17.76 + JsPkgCache c;
17.77 + String pkgName;
17.78 + synchronized (CACHE) {
17.79 + c = CACHE.get(l);
17.80 + if (c == null) {
17.81 + c = new JsPkgCache();
17.82 + CACHE.put(l, c);
17.83 + }
17.84 + int lastDot = className.lastIndexOf('.');
17.85 + pkgName = className.substring(0, lastDot + 1).replace('.', '/');
17.86 + p = c.props.get(pkgName);
17.87 + if (p == NONE) {
17.88 + return false;
17.89 + } else if (p != null) {
17.90 + return p.contains(className);
17.91 + }
17.92 + }
17.93 + final String res = pkgName + "net.java.html.js.classes";
17.94 +
17.95 + Enumeration<URL> en;
17.96 + try {
17.97 + en = l.getResources(res);
17.98 + } catch (IOException ex) {
17.99 + en = null;
17.100 + }
17.101 + if (en == null || !en.hasMoreElements()) synchronized (CACHE) {
17.102 + c.props.put(pkgName, NONE);
17.103 + return false;
17.104 + }
17.105 +
17.106 + try {
17.107 + Set<String> arr = new TreeSet<String>();
17.108 + while (en.hasMoreElements()) {
17.109 + URL u = en.nextElement();
17.110 + BufferedReader r = new BufferedReader(
17.111 + new InputStreamReader(u.openStream(), "UTF-8")
17.112 + );
17.113 + for (;;) {
17.114 + String line = r.readLine();
17.115 + if (line == null) {
17.116 + break;
17.117 + }
17.118 + arr.add(line);
17.119 + }
17.120 + r.close();
17.121 + }
17.122 + p = arr;
17.123 + } catch (IOException ex) {
17.124 + LOG.log(Level.WARNING, "Can't read " + res, ex);
17.125 + p = NONE;
17.126 + }
17.127 +
17.128 + synchronized (CACHE) {
17.129 + c.props.put(pkgName, p);
17.130 + return p.contains(className);
17.131 + }
17.132 +
17.133 + }
17.134 + private static final Logger LOG = Logger.getLogger(JsPkgCache.class.getName());
17.135 +}
18.1 --- a/boot/src/test/java/org/netbeans/html/boot/impl/Compile.java Wed Jan 08 13:18:34 2014 +0100
18.2 +++ b/boot/src/test/java/org/netbeans/html/boot/impl/Compile.java Tue Jan 14 14:18:50 2014 +0100
18.3 @@ -55,6 +55,8 @@
18.4 import java.util.List;
18.5 import java.util.Locale;
18.6 import java.util.Map;
18.7 +import java.util.logging.Level;
18.8 +import java.util.logging.Logger;
18.9 import java.util.regex.Matcher;
18.10 import java.util.regex.Pattern;
18.11 import javax.tools.Diagnostic;
18.12 @@ -153,6 +155,16 @@
18.13
18.14 JavaFileManager jfm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) {
18.15 @Override
18.16 + public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
18.17 + try {
18.18 + return new VirtFO(new URI("mem://resource/" + relativeName), Kind.OTHER, relativeName);
18.19 + } catch (URISyntaxException ex) {
18.20 + throw new IllegalStateException(ex);
18.21 + }
18.22 + }
18.23 +
18.24 +
18.25 + @Override
18.26 public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
18.27 if (kind == Kind.CLASS) {
18.28 final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
19.1 --- a/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java Wed Jan 08 13:18:34 2014 +0100
19.2 +++ b/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java Tue Jan 14 14:18:50 2014 +0100
19.3 @@ -42,9 +42,11 @@
19.4 */
19.5 package org.netbeans.html.boot.impl;
19.6
19.7 +import java.io.Closeable;
19.8 import java.lang.reflect.InvocationTargetException;
19.9 import java.lang.reflect.Method;
19.10 import java.lang.reflect.Modifier;
19.11 +import org.apidesign.html.boot.spi.Fn;
19.12 import static org.testng.Assert.*;
19.13 import org.testng.annotations.BeforeMethod;
19.14 import org.testng.annotations.Test;
19.15 @@ -198,4 +200,37 @@
19.16 Method st = methodClass.getMethod("recordError", Object.class);
19.17 assertEquals(st.invoke(methodClass.newInstance(), "Hello"), "Hello", "The same parameter returned");
19.18 }
19.19 +
19.20 + @Test public void plusOrMul() throws Throwable {
19.21 + Method st = methodClass.getMethod("plusOrMul", int.class, int.class);
19.22 + assertNotNull(Fn.activePresenter(), "Is there a presenter?");
19.23 + Closeable c = Fn.activate(null);
19.24 + try {
19.25 + assertNull(Fn.activePresenter(), "No presenter now");
19.26 + assertEquals(st.invoke(null, 6, 7), 42, "Mul in Java");
19.27 + } finally {
19.28 + c.close();
19.29 + }
19.30 + assertNotNull(Fn.activePresenter(), "Is there a presenter again");
19.31 + assertEquals(st.invoke(null, 6, 7), 13, "Plus in JavaScript");
19.32 + c = Fn.activate(null);
19.33 + try {
19.34 + assertNull(Fn.activePresenter(), "No presenter again");
19.35 + assertEquals(st.invoke(null, 6, 7), 42, "Mul in Java");
19.36 + } finally {
19.37 + c.close();
19.38 + }
19.39 + assertNotNull(Fn.activePresenter(), "Is there a presenter again");
19.40 + assertEquals(st.invoke(null, 6, 7), 13, "Plus in JavaScript again");
19.41 + }
19.42 +
19.43 + @Test public void arrayInOut() throws Throwable {
19.44 + String[] arr = { "Ahoj" };
19.45 + Method st = methodClass.getMethod("arr", Object[].class);
19.46 + Object ret = st.invoke(null, (Object) arr);
19.47 + assertTrue(ret instanceof Object[], "Expecting array: " + ret);
19.48 + Object[] res = (Object[]) ret;
19.49 + assertEquals(res.length, 1, "One element");
19.50 + assertEquals(res[0], "Ahoj", "The right string");
19.51 + }
19.52 }
19.53 \ No newline at end of file
20.1 --- a/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderTest.java Wed Jan 08 13:18:34 2014 +0100
20.2 +++ b/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderTest.java Tue Jan 14 14:18:50 2014 +0100
20.3 @@ -49,6 +49,7 @@
20.4 import java.net.URLClassLoader;
20.5 import java.util.ArrayList;
20.6 import java.util.Arrays;
20.7 +import java.util.Collections;
20.8 import java.util.Enumeration;
20.9 import java.util.List;
20.10 import javax.script.Invocable;
20.11 @@ -123,7 +124,12 @@
20.12
20.13 @Override
20.14 protected Enumeration<URL> findResources(String name) {
20.15 - throw new UnsupportedOperationException();
20.16 + URL u = findResource(name);
20.17 + List<URL> arr = new ArrayList<URL>();
20.18 + if (u != null) {
20.19 + arr.add(u);
20.20 + }
20.21 + return Collections.enumeration(arr);
20.22 }
20.23
20.24 @Override
21.1 --- a/boot/src/test/java/org/netbeans/html/boot/impl/JsMethods.java Wed Jan 08 13:18:34 2014 +0100
21.2 +++ b/boot/src/test/java/org/netbeans/html/boot/impl/JsMethods.java Tue Jan 14 14:18:50 2014 +0100
21.3 @@ -106,6 +106,9 @@
21.4 @JavaScriptBody(args = { "v" }, javacall = true, body = "return @java.lang.Integer::parseInt(Ljava/lang/String;)(v);")
21.5 public static native int parseInt(String v);
21.6
21.7 + @JavaScriptBody(args = "arr", body = "return arr;")
21.8 + public static native Object[] arr(Object[] arr);
21.9 +
21.10 @JavaScriptBody(args = { "useA", "useB", "a", "b" }, body = "var l = 0;"
21.11 + "if (useA) l += a;\n"
21.12 + "if (useB) l += b;\n"
21.13 @@ -126,4 +129,9 @@
21.14 + "return this.@org.netbeans.html.boot.impl.JsMethods::getError()();"
21.15 )
21.16 public native Object recordError(Object err);
21.17 +
21.18 + @JavaScriptBody(args = { "x", "y" }, body = "return x + y;")
21.19 + public static int plusOrMul(int x, int y) {
21.20 + return x * y;
21.21 + }
21.22 }
22.1 --- a/context/pom.xml Wed Jan 08 13:18:34 2014 +0100
22.2 +++ b/context/pom.xml Tue Jan 14 14:18:50 2014 +0100
22.3 @@ -4,11 +4,11 @@
22.4 <parent>
22.5 <groupId>org.netbeans.html</groupId>
22.6 <artifactId>pom</artifactId>
22.7 - <version>0.7-SNAPSHOT</version>
22.8 + <version>0.8-SNAPSHOT</version>
22.9 </parent>
22.10 <groupId>org.netbeans.html</groupId>
22.11 <artifactId>net.java.html</artifactId>
22.12 - <version>0.7-SNAPSHOT</version>
22.13 + <version>0.8-SNAPSHOT</version>
22.14 <packaging>bundle</packaging>
22.15 <name>HTML Context</name>
22.16 <url>http://maven.apache.org</url>
23.1 --- a/equinox-agentclass-hook/pom.xml Wed Jan 08 13:18:34 2014 +0100
23.2 +++ b/equinox-agentclass-hook/pom.xml Tue Jan 14 14:18:50 2014 +0100
23.3 @@ -4,7 +4,7 @@
23.4 <parent>
23.5 <groupId>org.netbeans.html</groupId>
23.6 <artifactId>pom</artifactId>
23.7 - <version>0.7-SNAPSHOT</version>
23.8 + <version>0.8-SNAPSHOT</version>
23.9 </parent>
23.10 <name>AgentClass Hook for Equinox</name>
23.11 <artifactId>equinox-agentclass-hook</artifactId>
23.12 @@ -17,7 +17,7 @@
23.13 <configuration>
23.14 <instructions>
23.15 <Fragment-Host>org.eclipse.osgi;bundle-version="[3.8.0,4.0)"</Fragment-Host>
23.16 - <Import-Package></Import-Package>
23.17 + <Import-Package />
23.18 </instructions>
23.19 </configuration>
23.20 </plugin>
24.1 --- a/equinox-agentclass-hook/src/main/resources/hookconfigurators.properties Wed Jan 08 13:18:34 2014 +0100
24.2 +++ b/equinox-agentclass-hook/src/main/resources/hookconfigurators.properties Tue Jan 14 14:18:50 2014 +0100
24.3 @@ -41,4 +41,4 @@
24.4 # made subject to such option by the copyright holder.
24.5 #
24.6
24.7 -hook.configurators=org.netbeans.equinox.agentclass.AgentHook
24.8 +hook.configurators=org.netbeans.html.equinox.agentclass.AgentHook
25.1 --- a/geo/pom.xml Wed Jan 08 13:18:34 2014 +0100
25.2 +++ b/geo/pom.xml Tue Jan 14 14:18:50 2014 +0100
25.3 @@ -4,11 +4,11 @@
25.4 <parent>
25.5 <groupId>org.netbeans.html</groupId>
25.6 <artifactId>pom</artifactId>
25.7 - <version>0.7-SNAPSHOT</version>
25.8 + <version>0.8-SNAPSHOT</version>
25.9 </parent>
25.10 <groupId>org.netbeans.html</groupId>
25.11 <artifactId>net.java.html.geo</artifactId>
25.12 - <version>0.7-SNAPSHOT</version>
25.13 + <version>0.8-SNAPSHOT</version>
25.14 <packaging>bundle</packaging>
25.15 <name>Geolocation API</name>
25.16 <url>http://maven.apache.org</url>
26.1 --- a/html4j-maven-plugin/pom.xml Wed Jan 08 13:18:34 2014 +0100
26.2 +++ b/html4j-maven-plugin/pom.xml Tue Jan 14 14:18:50 2014 +0100
26.3 @@ -5,12 +5,12 @@
26.4 <parent>
26.5 <groupId>org.netbeans.html</groupId>
26.6 <artifactId>pom</artifactId>
26.7 - <version>0.7-SNAPSHOT</version>
26.8 + <version>0.8-SNAPSHOT</version>
26.9 </parent>
26.10 <packaging>maven-plugin</packaging>
26.11 <groupId>org.apidesign.html</groupId>
26.12 <artifactId>html4j-maven-plugin</artifactId>
26.13 - <version>0.7-SNAPSHOT</version>
26.14 + <version>0.8-SNAPSHOT</version>
26.15 <name>html4j-maven-plugin</name>
26.16 <url>http://maven.apache.org</url>
26.17 <properties>
27.1 --- a/json-tck/pom.xml Wed Jan 08 13:18:34 2014 +0100
27.2 +++ b/json-tck/pom.xml Tue Jan 14 14:18:50 2014 +0100
27.3 @@ -4,11 +4,11 @@
27.4 <parent>
27.5 <groupId>org.netbeans.html</groupId>
27.6 <artifactId>pom</artifactId>
27.7 - <version>0.7-SNAPSHOT</version>
27.8 + <version>0.8-SNAPSHOT</version>
27.9 </parent>
27.10 <groupId>org.netbeans.html</groupId>
27.11 <artifactId>net.java.html.json.tck</artifactId>
27.12 - <version>0.7-SNAPSHOT</version>
27.13 + <version>0.8-SNAPSHOT</version>
27.14 <packaging>bundle</packaging>
27.15 <name>HTML for Java TCK</name>
27.16 <url>http://maven.apache.org</url>
27.17 @@ -37,7 +37,7 @@
27.18 <dependency>
27.19 <groupId>org.netbeans.html</groupId>
27.20 <artifactId>net.java.html.json</artifactId>
27.21 - <version>0.7-SNAPSHOT</version>
27.22 + <version>0.8-SNAPSHOT</version>
27.23 <type>jar</type>
27.24 </dependency>
27.25 <dependency>
28.1 --- a/json-tck/src/main/java/net/java/html/js/tests/Bodies.java Wed Jan 08 13:18:34 2014 +0100
28.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/Bodies.java Tue Jan 14 14:18:50 2014 +0100
28.3 @@ -57,7 +57,10 @@
28.4 static native void callback(Runnable r);
28.5
28.6 @JavaScriptBody(args = {"c"}, javacall = true, body = "return c.@java.util.concurrent.Callable::call()();")
28.7 - static native Object callback(Callable<Boolean> c);
28.8 + static native Object callback(Callable<? extends Object> c);
28.9 +
28.10 + @JavaScriptBody(args = {"c", "v"}, javacall = true, body = "var arr = c.@java.util.concurrent.Callable::call()(); arr.push(v); return arr;")
28.11 + static native Object callbackAndPush(Callable<String[]> c, String v);
28.12
28.13 @JavaScriptBody(args = { "v" }, body = "return v;")
28.14 public static native Object id(Object v);
28.15 @@ -81,10 +84,24 @@
28.16
28.17 @JavaScriptBody(args = { "arr" }, body = "return arr.length;")
28.18 public static native int length(Object[] arr);
28.19 +
28.20 + @JavaScriptBody(args = { "o", "vo" }, body = "if (vo) o = o.valueOf(); return typeof o;")
28.21 + public static native String typeof(Object o, boolean useValueOf);
28.22
28.23 - @JavaScriptBody(args = { "arr", "i", "value" }, body = "arr[i] = value;")
28.24 - public static native void modify(String[] arr, int i, String value);
28.25 + @JavaScriptBody(args = { "b" }, body = "return typeof b;")
28.26 + public static native String typeof(boolean b);
28.27 +
28.28 + @JavaScriptBody(args = { "o" }, body = "return Array.isArray(o);")
28.29 + public static native boolean isArray(Object o);
28.30 +
28.31 + @JavaScriptBody(args = { "arr", "i", "value" }, body = "arr[i] = value; return arr[i];")
28.32 + public static native String modify(String[] arr, int i, String value);
28.33
28.34 @JavaScriptBody(args = {}, body = "return true;")
28.35 public static native boolean truth();
28.36 +
28.37 + @JavaScriptBody(args = { "s" }, javacall = true, body =
28.38 + "return s.@net.java.html.js.tests.Sum::sum([Ljava/lang/Object;)([1, 2, 3]);"
28.39 + )
28.40 + public static native int sumArr(Sum s);
28.41 }
29.1 --- a/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java Wed Jan 08 13:18:34 2014 +0100
29.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/JavaScriptBodyTest.java Tue Jan 14 14:18:50 2014 +0100
29.3 @@ -42,6 +42,7 @@
29.4 */
29.5 package net.java.html.js.tests;
29.6
29.7 +import java.util.Arrays;
29.8 import java.util.concurrent.Callable;
29.9 import org.apidesign.html.json.tck.KOTest;
29.10
29.11 @@ -73,6 +74,56 @@
29.12 Bodies.callback(run);
29.13 assert run.cnt == 1 : "Can call even private implementation classes: " + run.cnt;
29.14 }
29.15 +
29.16 + @KOTest public void typeOfCharacter() {
29.17 + String charType = Bodies.typeof('a', false);
29.18 + assert "number".equals(charType) : "Expecting number type: " + charType;
29.19 + }
29.20 + @KOTest public void typeOfBoolean() {
29.21 + String booleanType = Bodies.typeof(true, false);
29.22 + assert "boolean".equals(booleanType) : "Expecting boolean type: " + booleanType;
29.23 + }
29.24 +
29.25 + @KOTest public void typeOfPrimitiveBoolean() {
29.26 + String booleanType = Bodies.typeof(true);
29.27 + assert "boolean".equals(booleanType) || "number".equals(booleanType):
29.28 + "Expecting boolean or at least number type: " + booleanType;
29.29 + }
29.30 +
29.31 + @KOTest public void typeOfInteger() {
29.32 + String intType = Bodies.typeof(1, false);
29.33 + assert "number".equals(intType) : "Expecting number type: " + intType;
29.34 + }
29.35 +
29.36 + @KOTest public void typeOfString() {
29.37 + String strType = Bodies.typeof("Ahoj", false);
29.38 + assert "string".equals(strType) : "Expecting string type: " + strType;
29.39 + }
29.40 +
29.41 + @KOTest public void typeOfDouble() {
29.42 + String doubleType = Bodies.typeof(0.33, false);
29.43 + assert "number".equals(doubleType) : "Expecting number type: " + doubleType;
29.44 + }
29.45 +
29.46 + @KOTest public void typeOfBooleanValueOf() {
29.47 + String booleanType = Bodies.typeof(true, true);
29.48 + assert "boolean".equals(booleanType) : "Expecting boolean type: " + booleanType;
29.49 + }
29.50 +
29.51 + @KOTest public void typeOfIntegerValueOf() {
29.52 + String intType = Bodies.typeof(1, true);
29.53 + assert "number".equals(intType) : "Expecting number type: " + intType;
29.54 + }
29.55 +
29.56 + @KOTest public void typeOfStringValueOf() {
29.57 + String strType = Bodies.typeof("Ahoj", true);
29.58 + assert "string".equals(strType) : "Expecting string type: " + strType;
29.59 + }
29.60 +
29.61 + @KOTest public void typeOfDoubleValueOf() {
29.62 + String doubleType = Bodies.typeof(0.33, true);
29.63 + assert "number".equals(doubleType) : "Expecting number type: " + doubleType;
29.64 + }
29.65
29.66 @KOTest public void computeInARunnable() {
29.67 final int[] sum = new int[2];
29.68 @@ -130,31 +181,67 @@
29.69 assert res == 42 : "Expecting 42";
29.70 }
29.71
29.72 - @KOTest public void selectFromJavaArray() {
29.73 + @KOTest public void selectFromStringJavaArray() {
29.74 String[] arr = { "Ahoj", "World" };
29.75 Object res = Bodies.select(arr, 1);
29.76 assert "World".equals(res) : "Expecting World, but was: " + res;
29.77 }
29.78
29.79 + @KOTest public void selectFromObjectJavaArray() {
29.80 + Object[] arr = { new Object(), new Object() };
29.81 + Object res = Bodies.select(arr, 1);
29.82 + assert arr[1].equals(res) : "Expecting " + arr[1] + ", but was: " + res;
29.83 + }
29.84 +
29.85 @KOTest public void lengthOfJavaArray() {
29.86 String[] arr = { "Ahoj", "World" };
29.87 int res = Bodies.length(arr);
29.88 assert res == 2 : "Expecting 2, but was: " + res;
29.89 }
29.90
29.91 - @KOTest public void javaArrayInOut() {
29.92 + @KOTest public void isJavaArray() {
29.93 + String[] arr = { "Ahoj", "World" };
29.94 + boolean is = Bodies.isArray(arr);
29.95 + assert is: "Expecting it to be an array: " + is;
29.96 + }
29.97 +
29.98 + @KOTest public void javaArrayInOutIsCopied() {
29.99 String[] arr = { "Ahoj", "World" };
29.100 Object res = Bodies.id(arr);
29.101 - assert res == arr : "Expecting same array, but was: " + res;
29.102 + assert res != null : "Non-null is returned";
29.103 + assert res instanceof Object[] : "Returned an array: " + res;
29.104 + assert !(res instanceof String[]) : "Not returned a string array: " + res;
29.105 +
29.106 + Object[] ret = (Object[]) res;
29.107 + assert arr.length == ret.length : "Same length: " + ret.length;
29.108 + assert arr[0].equals(ret[0]) : "Same first elem";
29.109 + assert arr[1].equals(ret[1]) : "Same 2nd elem";
29.110 }
29.111
29.112 -// Modifying an array is a complex operation in the bridge:
29.113 -//
29.114 -// @KOTest public void modifyJavaArray() {
29.115 -// String[] arr = { "Ahoj", "World" };
29.116 -// Bodies.modify(arr, 0, "Hello");
29.117 -// assert "Hello".equals(arr[0]) : "Expecting World, but was: " + arr[0];
29.118 -// }
29.119 + @KOTest public void modifyJavaArrayHasNoEffect() {
29.120 + String[] arr = { "Ahoj", "World" };
29.121 + String value = Bodies.modify(arr, 0, "Hello");
29.122 + assert "Hello".equals(value) : "Inside JS the value is changed: " + value;
29.123 + assert "Ahoj".equals(arr[0]) : "From a Java point of view it remains: " + arr[0];
29.124 + }
29.125 +
29.126 + @KOTest
29.127 + public void callbackWithArray() {
29.128 + class A implements Callable<String[]> {
29.129 + @Override
29.130 + public String[] call() throws Exception {
29.131 + return new String[] { "Hello" };
29.132 + }
29.133 + }
29.134 + Callable<String[]> a = new A();
29.135 + Object b = Bodies.callbackAndPush(a, "World!");
29.136 + assert b instanceof Object[] : "Returns an array: " + b;
29.137 + Object[] arr = (Object[]) b;
29.138 + String str = Arrays.toString(arr);
29.139 + assert arr.length == 2 : "Size is two " + str;
29.140 + assert "Hello".equals(arr[0]) : "Hello expected: " + arr[0];
29.141 + assert "World!".equals(arr[1]) : "World! expected: " + arr[1];
29.142 + }
29.143
29.144 @KOTest public void truth() {
29.145 assert Bodies.truth() : "True is true";
29.146 @@ -180,6 +267,11 @@
29.147 assert new Factorial().factorial(6) == 720;
29.148 }
29.149
29.150 + @KOTest public void sumArray() {
29.151 + int r = Bodies.sumArr(new Sum());
29.152 + assert r == 6 : "Sum is six: " + r;
29.153 + }
29.154 +
29.155 private static class R implements Runnable {
29.156 int cnt;
29.157 private final Thread initThread;
30.1 --- a/json-tck/src/main/java/net/java/html/js/tests/Sum.java Wed Jan 08 13:18:34 2014 +0100
30.2 +++ b/json-tck/src/main/java/net/java/html/js/tests/Sum.java Tue Jan 14 14:18:50 2014 +0100
30.3 @@ -50,4 +50,14 @@
30.4 public int sum(int a, int b) {
30.5 return a + b;
30.6 }
30.7 +
30.8 + public int sum(Object[] arr) {
30.9 + int s = 0;
30.10 + for (int i = 0; i < arr.length; i++) {
30.11 + if (arr[i] instanceof Number) {
30.12 + s += ((Number)arr[i]).intValue();
30.13 + }
30.14 + }
30.15 + return s;
30.16 + }
30.17 }
31.1 --- a/json/pom.xml Wed Jan 08 13:18:34 2014 +0100
31.2 +++ b/json/pom.xml Tue Jan 14 14:18:50 2014 +0100
31.3 @@ -4,11 +4,11 @@
31.4 <parent>
31.5 <groupId>org.netbeans.html</groupId>
31.6 <artifactId>pom</artifactId>
31.7 - <version>0.7-SNAPSHOT</version>
31.8 + <version>0.8-SNAPSHOT</version>
31.9 </parent>
31.10 <groupId>org.netbeans.html</groupId>
31.11 <artifactId>net.java.html.json</artifactId>
31.12 - <version>0.7-SNAPSHOT</version>
31.13 + <version>0.8-SNAPSHOT</version>
31.14 <packaging>bundle</packaging>
31.15 <name>JSON Model in Java</name>
31.16 <url>http://maven.apache.org</url>
32.1 --- a/json/src/main/java/net/java/html/json/Models.java Wed Jan 08 13:18:34 2014 +0100
32.2 +++ b/json/src/main/java/net/java/html/json/Models.java Tue Jan 14 14:18:50 2014 +0100
32.3 @@ -106,15 +106,39 @@
32.4 return JSON.read(ctx, model, jsonObject);
32.5 }
32.6
32.7 -// /** Converts an existing {@link Model model} into its associated, raw
32.8 -// * JSON object. The object may, but does not have to, be the same instance
32.9 -// * as the model object.
32.10 -// *
32.11 -// * @param model the model object
32.12 -// * @return the raw JSON object associated with the model
32.13 -// * @since 0.7
32.14 -// */
32.15 -// public static Object toRaw(Object model) {
32.16 -// return JSON.toJSON(model);
32.17 -// }
32.18 + /** Converts an existing {@link Model model} into its associated, raw
32.19 + * JSON object. The object may, but does not have to, be the same instance
32.20 + * as the model object.
32.21 + *
32.22 + * @param model the model object (not <code>null</code>)
32.23 + * @return the raw JSON object associated with the model
32.24 + * @throws IllegalArgumentException if the <code>model</code> is
32.25 + * not instance of a class
32.26 + * generated by {@link Model model annotation} processor.
32.27 + * @since 0.7
32.28 + */
32.29 + public static Object toRaw(Object model) {
32.30 + final Class<? extends Object> type = model.getClass();
32.31 + if (!isModel(type)) {
32.32 + throw new IllegalStateException("Not a model " + type);
32.33 + }
32.34 + return JSON.find(model);
32.35 + }
32.36 +
32.37 + /** Apply bindings of a model class. Each model class has an
32.38 + * apply bindings method that "activates" it. In <em>ko4j</em> mode,
32.39 + * it binds the model values to the currently active page. One
32.40 + * can call the generated method directly, or use this static
32.41 + * helper method to perform the activation.
32.42 + *
32.43 + * @param model instance of a {@link Model class}
32.44 + * @throws IllegalArgumentException if the <code>model</code> is not
32.45 + * instance of a class generated by {@link Model model annotation}
32.46 + * processor.
32.47 + *
32.48 + * @since 0.7
32.49 + */
32.50 + public static void applyBindings(Object model) {
32.51 + JSON.applyBindings(model);
32.52 + }
32.53 }
33.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Proto.java Wed Jan 08 13:18:34 2014 +0100
33.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java Tue Jan 14 14:18:50 2014 +0100
33.3 @@ -532,6 +532,8 @@
33.4 }
33.5
33.6 /** Compares two objects that can be converted to integers.
33.7 + * @param a first value
33.8 + * @param b second value
33.9 * @return true if they are the same
33.10 */
33.11 public final boolean isSame(int a, int b) {
33.12 @@ -540,6 +542,8 @@
33.13
33.14 /** Compares two objects that can be converted to (floating point)
33.15 * numbers.
33.16 + * @param a first value
33.17 + * @param b second value
33.18 * @return true if they are the same
33.19 */
33.20 public final boolean isSame(double a, double b) {
33.21 @@ -548,6 +552,8 @@
33.22
33.23 /** Compares two objects for being the same - e.g. either <code>==</code>
33.24 * or <code>equals</code>.
33.25 + * @param a first value
33.26 + * @param b second value
33.27 * @return true if they are equals
33.28 */
33.29 public final boolean isSame(Object a, Object b) {
34.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java Wed Jan 08 13:18:34 2014 +0100
34.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java Tue Jan 14 14:18:50 2014 +0100
34.3 @@ -269,22 +269,35 @@
34.4 if (object instanceof Collection) {
34.5 return JSONList.koData((Collection<?>) object, model);
34.6 }
34.7 - Proto.Type<?> type = JSON.findType(object.getClass());
34.8 - if (type == null) {
34.9 - return null;
34.10 - }
34.11 - final Proto proto = PropertyBindingAccessor.protoFor(type, object);
34.12 + Proto proto = findProto(object);
34.13 if (proto == null) {
34.14 return null;
34.15 }
34.16 final Bindings b = PropertyBindingAccessor.getBindings(proto, true);
34.17 return b == null ? null : b.koData();
34.18 }
34.19 +
34.20 + private static Proto findProto(Object object) {
34.21 + Proto.Type<?> type = JSON.findType(object.getClass());
34.22 + if (type == null) {
34.23 + return null;
34.24 + }
34.25 + final Proto proto = PropertyBindingAccessor.protoFor(type, object);
34.26 + return proto;
34.27 + }
34.28
34.29 public static Object find(Object object) {
34.30 return find(object, null);
34.31 }
34.32
34.33 + public static void applyBindings(Object object) {
34.34 + final Proto proto = findProto(object);
34.35 + if (proto == null) {
34.36 + throw new IllegalArgumentException("Not a model: " + object.getClass());
34.37 + }
34.38 + proto.applyBindings();
34.39 + }
34.40 +
34.41 public static void loadJSON(
34.42 BrwsrCtx c, RcvrJSON callback,
34.43 String urlBefore, String urlAfter, String method,
35.1 --- a/json/src/test/java/net/java/html/json/MapModelTest.java Wed Jan 08 13:18:34 2014 +0100
35.2 +++ b/json/src/test/java/net/java/html/json/MapModelTest.java Tue Jan 14 14:18:50 2014 +0100
35.3 @@ -77,7 +77,7 @@
35.4 Person p = Models.bind(new Person(), c).applyBindings();
35.5 p.setFirstName("Jarda");
35.6
35.7 - Map m = (Map)JSON.find(p);
35.8 + Map m = (Map)Models.toRaw(p);
35.9 Object v = m.get("firstName");
35.10 assertNotNull(v, "Value should be in the map");
35.11 assertEquals(v.getClass(), One.class, "It is instance of One");
35.12 @@ -98,7 +98,7 @@
35.13 Person p = Models.bind(new Person(), c);
35.14 p.setFirstName("Jirka");
35.15
35.16 - Map m = (Map)JSON.find(p);
35.17 + Map m = (Map)Models.toRaw(p);
35.18 Object v = m.get("firstName");
35.19 assertNotNull(v, "Value should be in the map");
35.20 assertEquals(v.getClass(), One.class, "It is instance of One");
35.21 @@ -122,7 +122,7 @@
35.22 @Test public void derivedProperty() throws Exception {
35.23 Person p = Models.bind(new Person(), c);
35.24
35.25 - Map m = (Map)JSON.find(p);
35.26 + Map m = (Map)Models.toRaw(p);
35.27 Object v = m.get("fullName");
35.28 assertNotNull(v, "Value should be in the map");
35.29 assertEquals(v.getClass(), One.class, "It is instance of One");
35.30 @@ -135,7 +135,7 @@
35.31 p.setFirstName("Trans");
35.32 p.setSex(Sex.MALE);
35.33
35.34 - Map m = (Map)JSON.find(p);
35.35 + Map m = (Map)Models.toRaw(p);
35.36 Object o = m.get("changeSex");
35.37 assertNotNull(o, "Function registered in the model");
35.38 assertEquals(o.getClass(), One.class);
35.39 @@ -152,7 +152,7 @@
35.40 Person p = Models.bind(new Person(), c);
35.41 p.setFirstName("Trans");
35.42
35.43 - Map m = (Map)JSON.find(p);
35.44 + Map m = (Map)Models.toRaw(p);
35.45 Object o = m.get("changeSex");
35.46 assertNotNull(o, "Function registered in the model");
35.47 assertEquals(o.getClass(), One.class);
36.1 --- a/json/src/test/java/net/java/html/json/ModelTest.java Wed Jan 08 13:18:34 2014 +0100
36.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java Tue Jan 14 14:18:50 2014 +0100
36.3 @@ -119,7 +119,7 @@
36.4 }
36.5
36.6 @Test public void arrayChangesNotified() {
36.7 - model.applyBindings();
36.8 + Models.applyBindings(model);
36.9 model.getNames().add("Hello");
36.10
36.11 assertFalse(my.mutated.isEmpty(), "There was a change" + my.mutated);
37.1 --- a/json/src/test/java/net/java/html/json/TypesTest.java Wed Jan 08 13:18:34 2014 +0100
37.2 +++ b/json/src/test/java/net/java/html/json/TypesTest.java Tue Jan 14 14:18:50 2014 +0100
37.3 @@ -111,10 +111,10 @@
37.4 t.setFloatX(99f);
37.5 */
37.6
37.7 - Object json = JSON.find(t);
37.8 + Object json = Models.toRaw(t);
37.9
37.10 Types copy = Models.bind(new Types(), c);
37.11 - Map copyMap = (Map) JSON.find(copy);
37.12 + Map copyMap = (Map) Models.toRaw(copy);
37.13 One o = (One) copyMap.get("readFromEvent");
37.14 o.fb.call(null, json);
37.15
38.1 --- a/json/src/test/java/org/netbeans/html/json/impl/JSONListTest.java Wed Jan 08 13:18:34 2014 +0100
38.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/JSONListTest.java Tue Jan 14 14:18:50 2014 +0100
38.3 @@ -80,7 +80,7 @@
38.4 p.setLastName("2");
38.5 p.setSex(Sex.MALE);
38.6
38.7 - Object real = JSON.find(p);
38.8 + Object real = Models.toRaw(p);
38.9 assertEquals(this, real, "I am the right model");
38.10 }
38.11
39.1 --- a/ko-archetype-test/pom.xml Wed Jan 08 13:18:34 2014 +0100
39.2 +++ b/ko-archetype-test/pom.xml Tue Jan 14 14:18:50 2014 +0100
39.3 @@ -4,11 +4,11 @@
39.4 <parent>
39.5 <groupId>org.netbeans.html</groupId>
39.6 <artifactId>pom</artifactId>
39.7 - <version>0.7-SNAPSHOT</version>
39.8 + <version>0.8-SNAPSHOT</version>
39.9 </parent>
39.10 <groupId>org.netbeans.html</groupId>
39.11 <artifactId>ko-archetype-test</artifactId>
39.12 - <version>0.7-SNAPSHOT</version>
39.13 + <version>0.8-SNAPSHOT</version>
39.14 <name>Knockout 4 Java Archetype Test</name>
39.15 <url>http://maven.apache.org</url>
39.16 <description>Verifies the Knockout & net.java.html.json archetype behaves properly.</description>
39.17 @@ -34,7 +34,7 @@
39.18 </dependency>
39.19 <dependency>
39.20 <groupId>${project.groupId}</groupId>
39.21 - <artifactId>ko-fx</artifactId>
39.22 + <artifactId>ko4j</artifactId>
39.23 <version>${project.version}</version>
39.24 <scope>provided</scope>
39.25 </dependency>
40.1 --- a/ko-archetype/pom.xml Wed Jan 08 13:18:34 2014 +0100
40.2 +++ b/ko-archetype/pom.xml Tue Jan 14 14:18:50 2014 +0100
40.3 @@ -4,11 +4,11 @@
40.4 <parent>
40.5 <groupId>org.netbeans.html</groupId>
40.6 <artifactId>pom</artifactId>
40.7 - <version>0.7-SNAPSHOT</version>
40.8 + <version>0.8-SNAPSHOT</version>
40.9 </parent>
40.10 <groupId>org.apidesign.html</groupId>
40.11 <artifactId>knockout4j-archetype</artifactId>
40.12 - <version>0.7-SNAPSHOT</version>
40.13 + <version>0.8-SNAPSHOT</version>
40.14 <packaging>jar</packaging>
40.15 <name>Knockout 4 Java Maven Archetype</name>
40.16 <description>
40.17 @@ -57,7 +57,7 @@
40.18 <artifactId>maven-javadoc-plugin</artifactId>
40.19 <version>2.9</version>
40.20 <configuration>
40.21 - <subpackages></subpackages>
40.22 + <subpackages />
40.23 <skip>true</skip>
40.24 </configuration>
40.25 </plugin>
41.1 --- a/ko-archetype/src/main/resources/archetype-resources/pom.xml Wed Jan 08 13:18:34 2014 +0100
41.2 +++ b/ko-archetype/src/main/resources/archetype-resources/pom.xml Tue Jan 14 14:18:50 2014 +0100
41.3 @@ -112,7 +112,7 @@
41.4 </dependency>
41.5 <dependency>
41.6 <groupId>org.netbeans.html</groupId>
41.7 - <artifactId>ko-fx</artifactId>
41.8 + <artifactId>ko4j</artifactId>
41.9 <version>\${net.java.html.version}</version>
41.10 <scope>runtime</scope>
41.11 </dependency>
42.1 --- a/ko-fx/pom.xml Wed Jan 08 13:18:34 2014 +0100
42.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
42.3 @@ -1,115 +0,0 @@
42.4 -<?xml version="1.0"?>
42.5 -<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">
42.6 - <modelVersion>4.0.0</modelVersion>
42.7 - <parent>
42.8 - <groupId>org.netbeans.html</groupId>
42.9 - <artifactId>pom</artifactId>
42.10 - <version>0.7-SNAPSHOT</version>
42.11 - </parent>
42.12 - <groupId>org.netbeans.html</groupId>
42.13 - <artifactId>ko-fx</artifactId>
42.14 - <version>0.7-SNAPSHOT</version>
42.15 - <packaging>bundle</packaging>
42.16 - <name>Knockout.fx</name>
42.17 - <url>http://maven.apache.org</url>
42.18 - <properties>
42.19 - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
42.20 - <bundleSymbolicName>org.netbeans.html.ko-fx</bundleSymbolicName>
42.21 - </properties>
42.22 - <build>
42.23 - <plugins>
42.24 - <plugin>
42.25 - <groupId>org.apache.felix</groupId>
42.26 - <artifactId>maven-bundle-plugin</artifactId>
42.27 - </plugin>
42.28 - <plugin>
42.29 - <groupId>org.apache.maven.plugins</groupId>
42.30 - <artifactId>maven-javadoc-plugin</artifactId>
42.31 - <configuration>
42.32 - <skip>false</skip>
42.33 - </configuration>
42.34 - </plugin>
42.35 - <plugin>
42.36 - <groupId>org.apache.maven.plugins</groupId>
42.37 - <artifactId>maven-compiler-plugin</artifactId>
42.38 - <version>2.3.2</version>
42.39 - <configuration>
42.40 - <source>1.7</source>
42.41 - <target>1.7</target>
42.42 - </configuration>
42.43 - </plugin>
42.44 - </plugins>
42.45 - </build>
42.46 - <dependencies>
42.47 - <dependency>
42.48 - <groupId>com.oracle</groupId>
42.49 - <artifactId>javafx</artifactId>
42.50 - <version>2.2</version>
42.51 - <scope>system</scope>
42.52 - <systemPath>${jfxrt.jar}</systemPath>
42.53 - </dependency>
42.54 - <dependency>
42.55 - <groupId>de.twentyeleven.skysail</groupId>
42.56 - <artifactId>org.json-osgi</artifactId>
42.57 - </dependency>
42.58 - <dependency>
42.59 - <groupId>org.netbeans.html</groupId>
42.60 - <artifactId>net.java.html.json</artifactId>
42.61 - <version>${project.version}</version>
42.62 - </dependency>
42.63 - <dependency>
42.64 - <groupId>org.testng</groupId>
42.65 - <artifactId>testng</artifactId>
42.66 - <scope>test</scope>
42.67 - </dependency>
42.68 - <dependency>
42.69 - <groupId>${project.groupId}</groupId>
42.70 - <artifactId>net.java.html.json.tck</artifactId>
42.71 - <version>${project.version}</version>
42.72 - <scope>test</scope>
42.73 - </dependency>
42.74 - <dependency>
42.75 - <groupId>org.netbeans.api</groupId>
42.76 - <artifactId>org-openide-util-lookup</artifactId>
42.77 - <scope>provided</scope>
42.78 - </dependency>
42.79 - <dependency>
42.80 - <groupId>org.netbeans.html</groupId>
42.81 - <artifactId>net.java.html.boot</artifactId>
42.82 - <version>${project.version}</version>
42.83 - <type>jar</type>
42.84 - </dependency>
42.85 - <dependency>
42.86 - <groupId>${project.groupId}</groupId>
42.87 - <artifactId>net.java.html.boot.fx</artifactId>
42.88 - <version>${project.version}</version>
42.89 - <scope>test</scope>
42.90 - </dependency>
42.91 - <dependency>
42.92 - <groupId>org.glassfish.grizzly</groupId>
42.93 - <artifactId>grizzly-http-server</artifactId>
42.94 - <version>2.3.3</version>
42.95 - <scope>test</scope>
42.96 - </dependency>
42.97 - <dependency>
42.98 - <groupId>org.glassfish.grizzly</groupId>
42.99 - <artifactId>grizzly-websockets-server</artifactId>
42.100 - <version>2.3.3</version>
42.101 - <scope>test</scope>
42.102 - <type>jar</type>
42.103 - </dependency>
42.104 - <dependency>
42.105 - <groupId>org.glassfish.grizzly</groupId>
42.106 - <artifactId>grizzly-http-servlet</artifactId>
42.107 - <version>2.3.3</version>
42.108 - <scope>test</scope>
42.109 - </dependency>
42.110 - <dependency>
42.111 - <groupId>javax.servlet</groupId>
42.112 - <artifactId>javax.servlet-api</artifactId>
42.113 - <scope>test</scope>
42.114 - <version>3.1.0</version>
42.115 - </dependency>
42.116 - </dependencies>
42.117 - <description>Binds net.java.html.json APIs together with JavaFX WebView.</description>
42.118 -</project>
43.1 --- a/ko-fx/src/main/java/org/netbeans/html/kofx/Console.java Wed Jan 08 13:18:34 2014 +0100
43.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
43.3 @@ -1,81 +0,0 @@
43.4 -/**
43.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
43.6 - *
43.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
43.8 - *
43.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
43.10 - * Other names may be trademarks of their respective owners.
43.11 - *
43.12 - * The contents of this file are subject to the terms of either the GNU
43.13 - * General Public License Version 2 only ("GPL") or the Common
43.14 - * Development and Distribution License("CDDL") (collectively, the
43.15 - * "License"). You may not use this file except in compliance with the
43.16 - * License. You can obtain a copy of the License at
43.17 - * http://www.netbeans.org/cddl-gplv2.html
43.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
43.19 - * specific language governing permissions and limitations under the
43.20 - * License. When distributing the software, include this License Header
43.21 - * Notice in each file and include the License file at
43.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
43.23 - * particular file as subject to the "Classpath" exception as provided
43.24 - * by Oracle in the GPL Version 2 section of the License file that
43.25 - * accompanied this code. If applicable, add the following below the
43.26 - * License Header, with the fields enclosed by brackets [] replaced by
43.27 - * your own identifying information:
43.28 - * "Portions Copyrighted [year] [name of copyright owner]"
43.29 - *
43.30 - * Contributor(s):
43.31 - *
43.32 - * The Original Software is NetBeans. The Initial Developer of the Original
43.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
43.34 - *
43.35 - * If you wish your version of this file to be governed by only the CDDL
43.36 - * or only the GPL Version 2, indicate your decision by adding
43.37 - * "[Contributor] elects to include this software in this distribution
43.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
43.39 - * single choice of license, a recipient has the option to distribute
43.40 - * your version of this file under either the CDDL, the GPL Version 2 or
43.41 - * to extend the choice of license to its licensees as provided above.
43.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
43.43 - * Version 2 license, then the option applies only if the new code is
43.44 - * made subject to such option by the copyright holder.
43.45 - */
43.46 -package org.netbeans.html.kofx;
43.47 -
43.48 -import java.util.logging.Level;
43.49 -import java.util.logging.Logger;
43.50 -import net.java.html.js.JavaScriptBody;
43.51 -
43.52 -/** This is an implementation package - just
43.53 - * include its JAR on classpath and use official {@link Context} API
43.54 - * to access the functionality.
43.55 - * <p>
43.56 - * Redirects JavaScript's messages to Java's {@link Logger}.
43.57 - *
43.58 - * @author Jaroslav Tulach <jtulach@netbeans.org>
43.59 - */
43.60 -final class Console {
43.61 - private static final Logger LOG = Logger.getLogger(Console.class.getName());
43.62 -
43.63 - private Console() {
43.64 - }
43.65 -
43.66 - static void register() {
43.67 - registerImpl("log", Level.INFO);
43.68 - registerImpl("info", Level.INFO);
43.69 - registerImpl("warn", Level.WARNING);
43.70 - registerImpl("error", Level.SEVERE);
43.71 - }
43.72 -
43.73 - @JavaScriptBody(args = { "attr", "l" },
43.74 - javacall = true, body =
43.75 - " window.console[attr] = function(m) {\n"
43.76 - + " @org.netbeans.html.kofx.Console::log(Ljava/util/logging/Level;Ljava/lang/String;)(l, m);\n"
43.77 - + " };\n"
43.78 - )
43.79 - private static native void registerImpl(String attr, Level l);
43.80 -
43.81 - static void log(Level l, String msg) {
43.82 - LOG.log(l, msg);
43.83 - }
43.84 -}
44.1 --- a/ko-fx/src/main/java/org/netbeans/html/kofx/FXContext.java Wed Jan 08 13:18:34 2014 +0100
44.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
44.3 @@ -1,222 +0,0 @@
44.4 -/**
44.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
44.6 - *
44.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
44.8 - *
44.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
44.10 - * Other names may be trademarks of their respective owners.
44.11 - *
44.12 - * The contents of this file are subject to the terms of either the GNU
44.13 - * General Public License Version 2 only ("GPL") or the Common
44.14 - * Development and Distribution License("CDDL") (collectively, the
44.15 - * "License"). You may not use this file except in compliance with the
44.16 - * License. You can obtain a copy of the License at
44.17 - * http://www.netbeans.org/cddl-gplv2.html
44.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
44.19 - * specific language governing permissions and limitations under the
44.20 - * License. When distributing the software, include this License Header
44.21 - * Notice in each file and include the License file at
44.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
44.23 - * particular file as subject to the "Classpath" exception as provided
44.24 - * by Oracle in the GPL Version 2 section of the License file that
44.25 - * accompanied this code. If applicable, add the following below the
44.26 - * License Header, with the fields enclosed by brackets [] replaced by
44.27 - * your own identifying information:
44.28 - * "Portions Copyrighted [year] [name of copyright owner]"
44.29 - *
44.30 - * Contributor(s):
44.31 - *
44.32 - * The Original Software is NetBeans. The Initial Developer of the Original
44.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
44.34 - *
44.35 - * If you wish your version of this file to be governed by only the CDDL
44.36 - * or only the GPL Version 2, indicate your decision by adding
44.37 - * "[Contributor] elects to include this software in this distribution
44.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
44.39 - * single choice of license, a recipient has the option to distribute
44.40 - * your version of this file under either the CDDL, the GPL Version 2 or
44.41 - * to extend the choice of license to its licensees as provided above.
44.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
44.43 - * Version 2 license, then the option applies only if the new code is
44.44 - * made subject to such option by the copyright holder.
44.45 - */
44.46 -package org.netbeans.html.kofx;
44.47 -
44.48 -import java.io.Closeable;
44.49 -import java.io.IOException;
44.50 -import java.io.InputStream;
44.51 -import java.util.ServiceLoader;
44.52 -import java.util.logging.Logger;
44.53 -import javafx.application.Platform;
44.54 -import net.java.html.js.JavaScriptBody;
44.55 -import netscape.javascript.JSObject;
44.56 -import org.apidesign.html.boot.spi.Fn;
44.57 -import org.apidesign.html.context.spi.Contexts;
44.58 -import org.apidesign.html.json.spi.FunctionBinding;
44.59 -import org.apidesign.html.json.spi.JSONCall;
44.60 -import org.apidesign.html.json.spi.PropertyBinding;
44.61 -import org.apidesign.html.json.spi.Technology;
44.62 -import org.apidesign.html.json.spi.Transfer;
44.63 -import org.apidesign.html.json.spi.WSTransfer;
44.64 -import org.openide.util.lookup.ServiceProvider;
44.65 -
44.66 -/** This is an implementation package - just
44.67 - * include its JAR on classpath and use official {@link Context} API
44.68 - * to access the functionality.
44.69 - * <p>
44.70 - * Registers {@link ContextProvider}, so {@link ServiceLoader} can find it.
44.71 - *
44.72 - * @author Jaroslav Tulach <jtulach@netbeans.org>
44.73 - */
44.74 -public final class FXContext
44.75 -implements Technology.BatchInit<JSObject>, Transfer, WSTransfer<LoadWS> {
44.76 - static final Logger LOG = Logger.getLogger(FXContext.class.getName());
44.77 - private static Boolean javaScriptEnabled;
44.78 - private final Fn.Presenter browserContext;
44.79 -
44.80 - public FXContext(Fn.Presenter browserContext) {
44.81 - this.browserContext = browserContext;
44.82 - }
44.83 -
44.84 - @JavaScriptBody(args = {}, body = "return true;")
44.85 - private static boolean isJavaScriptEnabledJs() {
44.86 - return false;
44.87 - }
44.88 -
44.89 - static boolean isJavaScriptEnabled() {
44.90 - if (javaScriptEnabled != null) {
44.91 - return javaScriptEnabled;
44.92 - }
44.93 - return javaScriptEnabled = isJavaScriptEnabledJs();
44.94 - }
44.95 -
44.96 - final boolean areWebSocketsSupported() {
44.97 - return LoadWS.isSupported();
44.98 - }
44.99 -
44.100 -
44.101 - @Override
44.102 - public JSObject wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr) {
44.103 - String[] propNames = new String[propArr.length];
44.104 - boolean[] propReadOnly = new boolean[propArr.length];
44.105 - Object[] propValues = new Object[propArr.length];
44.106 - for (int i = 0; i < propNames.length; i++) {
44.107 - propNames[i] = propArr[i].getPropertyName();
44.108 - propReadOnly[i] = propArr[i].isReadOnly();
44.109 - propValues[i] = propArr[i].getValue();
44.110 - }
44.111 - String[] funcNames = new String[funcArr.length];
44.112 - for (int i = 0; i < funcNames.length; i++) {
44.113 - funcNames[i] = funcArr[i].getFunctionName();
44.114 - }
44.115 -
44.116 - return Knockout.wrapModel(model,
44.117 - propNames, propReadOnly, Knockout.toArray(propValues), propArr,
44.118 - funcNames, funcArr
44.119 - );
44.120 - }
44.121 -
44.122 - @Override
44.123 - public JSObject wrapModel(Object model) {
44.124 - throw new UnsupportedOperationException();
44.125 - }
44.126 -
44.127 - @Override
44.128 - public void bind(PropertyBinding b, Object model, JSObject data) {
44.129 - throw new UnsupportedOperationException();
44.130 - }
44.131 -
44.132 - @Override
44.133 - public void valueHasMutated(JSObject data, String propertyName) {
44.134 - Knockout.valueHasMutated(data, propertyName);
44.135 - }
44.136 -
44.137 - @Override
44.138 - public void expose(FunctionBinding fb, Object model, JSObject d) {
44.139 - throw new UnsupportedOperationException();
44.140 - }
44.141 -
44.142 - @Override
44.143 - public void applyBindings(JSObject data) {
44.144 - Knockout.applyBindings(data);
44.145 - }
44.146 -
44.147 - @Override
44.148 - public Object wrapArray(Object[] arr) {
44.149 - return Knockout.toArray(arr);
44.150 - }
44.151 -
44.152 - @Override
44.153 - public void extract(Object obj, String[] props, Object[] values) {
44.154 - LoadJSON.extractJSON(obj, props, values);
44.155 - }
44.156 -
44.157 - @Override
44.158 - public void loadJSON(final JSONCall call) {
44.159 - LoadJSON.loadJSON(call);
44.160 - }
44.161 -
44.162 - @Override
44.163 - public <M> M toModel(Class<M> modelClass, Object data) {
44.164 - if (data instanceof JSObject) {
44.165 - data = ((JSObject)data).getMember("ko-fx.model"); // NOI18N
44.166 - }
44.167 - return modelClass.cast(data);
44.168 - }
44.169 -
44.170 - @Override
44.171 - public Object toJSON(InputStream is) throws IOException {
44.172 - return LoadJSON.parse(is);
44.173 - }
44.174 -
44.175 - @Override
44.176 - public void runSafe(final Runnable r) {
44.177 - class Wrap implements Runnable {
44.178 - @Override public void run() {
44.179 - try (Closeable c = Fn.activate(browserContext)) {
44.180 - r.run();
44.181 - } catch (IOException ex) {
44.182 - // cannot be thrown
44.183 - }
44.184 - }
44.185 - }
44.186 - Wrap w = new Wrap();
44.187 -
44.188 - if (Platform.isFxApplicationThread()) {
44.189 - w.run();
44.190 - } else {
44.191 - Platform.runLater(w);
44.192 - }
44.193 - }
44.194 -
44.195 - @Override
44.196 - public LoadWS open(String url, JSONCall onReply) {
44.197 - return new LoadWS(onReply, url);
44.198 - }
44.199 -
44.200 - @Override
44.201 - public void send(LoadWS socket, JSONCall data) {
44.202 - socket.send(data);
44.203 - }
44.204 -
44.205 - @Override
44.206 - public void close(LoadWS socket) {
44.207 - socket.close();
44.208 - }
44.209 -
44.210 - @ServiceProvider(service = Contexts.Provider.class)
44.211 - public static final class Prvdr implements Contexts.Provider {
44.212 - @Override
44.213 - public void fillContext(Contexts.Builder context, Class<?> requestor) {
44.214 - if (isJavaScriptEnabled()) {
44.215 - FXContext c = new FXContext(Fn.activePresenter());
44.216 -
44.217 - context.register(Technology.class, c, 100);
44.218 - context.register(Transfer.class, c, 100);
44.219 - if (c.areWebSocketsSupported()) {
44.220 - context.register(WSTransfer.class, c, 100);
44.221 - }
44.222 - }
44.223 - }
44.224 - }
44.225 -}
45.1 --- a/ko-fx/src/main/java/org/netbeans/html/kofx/Knockout.java Wed Jan 08 13:18:34 2014 +0100
45.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
45.3 @@ -1,145 +0,0 @@
45.4 -/**
45.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
45.6 - *
45.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
45.8 - *
45.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
45.10 - * Other names may be trademarks of their respective owners.
45.11 - *
45.12 - * The contents of this file are subject to the terms of either the GNU
45.13 - * General Public License Version 2 only ("GPL") or the Common
45.14 - * Development and Distribution License("CDDL") (collectively, the
45.15 - * "License"). You may not use this file except in compliance with the
45.16 - * License. You can obtain a copy of the License at
45.17 - * http://www.netbeans.org/cddl-gplv2.html
45.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
45.19 - * specific language governing permissions and limitations under the
45.20 - * License. When distributing the software, include this License Header
45.21 - * Notice in each file and include the License file at
45.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
45.23 - * particular file as subject to the "Classpath" exception as provided
45.24 - * by Oracle in the GPL Version 2 section of the License file that
45.25 - * accompanied this code. If applicable, add the following below the
45.26 - * License Header, with the fields enclosed by brackets [] replaced by
45.27 - * your own identifying information:
45.28 - * "Portions Copyrighted [year] [name of copyright owner]"
45.29 - *
45.30 - * Contributor(s):
45.31 - *
45.32 - * The Original Software is NetBeans. The Initial Developer of the Original
45.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
45.34 - *
45.35 - * If you wish your version of this file to be governed by only the CDDL
45.36 - * or only the GPL Version 2, indicate your decision by adding
45.37 - * "[Contributor] elects to include this software in this distribution
45.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
45.39 - * single choice of license, a recipient has the option to distribute
45.40 - * your version of this file under either the CDDL, the GPL Version 2 or
45.41 - * to extend the choice of license to its licensees as provided above.
45.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
45.43 - * Version 2 license, then the option applies only if the new code is
45.44 - * made subject to such option by the copyright holder.
45.45 - */
45.46 -package org.netbeans.html.kofx;
45.47 -
45.48 -import net.java.html.js.JavaScriptBody;
45.49 -import net.java.html.js.JavaScriptResource;
45.50 -import net.java.html.json.Model;
45.51 -import netscape.javascript.JSObject;
45.52 -import org.apidesign.html.json.spi.FunctionBinding;
45.53 -import org.apidesign.html.json.spi.PropertyBinding;
45.54 -
45.55 -/** This is an implementation package - just
45.56 - * include its JAR on classpath and use official {@link Context} API
45.57 - * to access the functionality.
45.58 - * <p>
45.59 - * Provides binding between {@link Model models} and knockout.js running
45.60 - * inside a JavaFX WebView.
45.61 - *
45.62 - * @author Jaroslav Tulach <jtulach@netbeans.org>
45.63 - */
45.64 -@JavaScriptResource("knockout-2.2.1.js")
45.65 -final class Knockout {
45.66 - static final JSObject KObject;
45.67 - static {
45.68 - Console.register();
45.69 - KObject = (JSObject) kObj();
45.70 - }
45.71 -
45.72 - static Object toArray(Object[] arr) {
45.73 - return KObject.call("array", arr);
45.74 - }
45.75 -
45.76 - @JavaScriptBody(args = { "model", "prop" }, body =
45.77 - "if (model) {\n"
45.78 - + " var koProp = model[prop];\n"
45.79 - + " if (koProp && koProp['valueHasMutated']) {\n"
45.80 - + " koProp['valueHasMutated']();\n"
45.81 - + " }\n"
45.82 - + "}\n"
45.83 - )
45.84 - public native static void valueHasMutated(JSObject model, String prop);
45.85 -
45.86 - @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
45.87 - native static void applyBindings(Object bindings);
45.88 -
45.89 - @JavaScriptBody(args = {}, body =
45.90 - " var k = {};"
45.91 - + " k.array= function() {"
45.92 - + " return Array.prototype.slice.call(arguments);"
45.93 - + " };"
45.94 - + " return k;"
45.95 - )
45.96 - private static native Object kObj();
45.97 -
45.98 -
45.99 - @JavaScriptBody(
45.100 - javacall = true,
45.101 - args = {"model", "propNames", "propReadOnly", "propValues", "propArr", "funcNames", "funcArr"},
45.102 - body
45.103 - = "var ret = {};\n"
45.104 - + "ret['ko-fx.model'] = model;\n"
45.105 - + "function koComputed(name, readOnly, value, prop) {\n"
45.106 - + " function realGetter() {\n"
45.107 - + " try {"
45.108 - + " var v = prop.@org.apidesign.html.json.spi.PropertyBinding::getValue()();"
45.109 - + " return v;"
45.110 - + " } catch (e) {"
45.111 - + " alert(\"Cannot call getValue on \" + model + \" prop: \" + name + \" error: \" + e);"
45.112 - + " }"
45.113 - + " }\n"
45.114 - + " var activeGetter = function() { return value; };\n"
45.115 - + " var bnd = {"
45.116 - + " read: function() {"
45.117 - + " var r = activeGetter();"
45.118 - + " activeGetter = realGetter;"
45.119 - + " return r;"
45.120 - + " },"
45.121 - + " owner: ret\n"
45.122 - + " };\n"
45.123 - + " if (!readOnly) {\n"
45.124 - + " bnd.write = function(val) {\n"
45.125 - + " prop.@org.apidesign.html.json.spi.PropertyBinding::setValue(Ljava/lang/Object;)(val);\n"
45.126 - + " };"
45.127 - + " };"
45.128 - + " ret[name] = ko.computed(bnd);"
45.129 - + "}\n"
45.130 - + "for (var i = 0; i < propNames.length; i++) {\n"
45.131 - + " koComputed(propNames[i], propReadOnly[i], propValues[i], propArr[i]);\n"
45.132 - + "}\n"
45.133 - + "function koExpose(name, func) {\n"
45.134 - + " ret[name] = function(data, ev) {\n"
45.135 - + " func.@org.apidesign.html.json.spi.FunctionBinding::call(Ljava/lang/Object;Ljava/lang/Object;)(data, ev);\n"
45.136 - + " };\n"
45.137 - + "}\n"
45.138 - + "for (var i = 0; i < funcNames.length; i++) {\n"
45.139 - + " koExpose(funcNames[i], funcArr[i]);\n"
45.140 - + "}\n"
45.141 - + "return ret;\n"
45.142 - )
45.143 - static native JSObject wrapModel(
45.144 - Object model,
45.145 - String[] propNames, boolean[] propReadOnly, Object propValues, PropertyBinding[] propArr,
45.146 - String[] funcNames, FunctionBinding[] funcArr
45.147 - );
45.148 -}
46.1 --- a/ko-fx/src/main/java/org/netbeans/html/kofx/LoadJSON.java Wed Jan 08 13:18:34 2014 +0100
46.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
46.3 @@ -1,267 +0,0 @@
46.4 -/**
46.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
46.6 - *
46.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
46.8 - *
46.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
46.10 - * Other names may be trademarks of their respective owners.
46.11 - *
46.12 - * The contents of this file are subject to the terms of either the GNU
46.13 - * General Public License Version 2 only ("GPL") or the Common
46.14 - * Development and Distribution License("CDDL") (collectively, the
46.15 - * "License"). You may not use this file except in compliance with the
46.16 - * License. You can obtain a copy of the License at
46.17 - * http://www.netbeans.org/cddl-gplv2.html
46.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
46.19 - * specific language governing permissions and limitations under the
46.20 - * License. When distributing the software, include this License Header
46.21 - * Notice in each file and include the License file at
46.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
46.23 - * particular file as subject to the "Classpath" exception as provided
46.24 - * by Oracle in the GPL Version 2 section of the License file that
46.25 - * accompanied this code. If applicable, add the following below the
46.26 - * License Header, with the fields enclosed by brackets [] replaced by
46.27 - * your own identifying information:
46.28 - * "Portions Copyrighted [year] [name of copyright owner]"
46.29 - *
46.30 - * Contributor(s):
46.31 - *
46.32 - * The Original Software is NetBeans. The Initial Developer of the Original
46.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
46.34 - *
46.35 - * If you wish your version of this file to be governed by only the CDDL
46.36 - * or only the GPL Version 2, indicate your decision by adding
46.37 - * "[Contributor] elects to include this software in this distribution
46.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
46.39 - * single choice of license, a recipient has the option to distribute
46.40 - * your version of this file under either the CDDL, the GPL Version 2 or
46.41 - * to extend the choice of license to its licensees as provided above.
46.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
46.43 - * Version 2 license, then the option applies only if the new code is
46.44 - * made subject to such option by the copyright holder.
46.45 - */
46.46 -package org.netbeans.html.kofx;
46.47 -
46.48 -import java.io.IOException;
46.49 -import java.io.InputStream;
46.50 -import java.io.InputStreamReader;
46.51 -import java.io.OutputStream;
46.52 -import java.io.PushbackInputStream;
46.53 -import java.io.Reader;
46.54 -import java.net.HttpURLConnection;
46.55 -import java.net.MalformedURLException;
46.56 -import java.net.URL;
46.57 -import java.net.URLConnection;
46.58 -import java.util.Iterator;
46.59 -import java.util.concurrent.Executor;
46.60 -import java.util.concurrent.Executors;
46.61 -import java.util.concurrent.ThreadFactory;
46.62 -import java.util.logging.Level;
46.63 -import java.util.logging.Logger;
46.64 -import javafx.application.Platform;
46.65 -import net.java.html.js.JavaScriptBody;
46.66 -import netscape.javascript.JSObject;
46.67 -import org.apidesign.html.json.spi.JSONCall;
46.68 -import org.json.JSONArray;
46.69 -import org.json.JSONException;
46.70 -import org.json.JSONObject;
46.71 -import org.json.JSONTokener;
46.72 -
46.73 -/** This is an implementation package - just
46.74 - * include its JAR on classpath and use official {@link Context} API
46.75 - * to access the functionality.
46.76 - *
46.77 - * @author Jaroslav Tulach <jtulach@netbeans.org>
46.78 - */
46.79 -final class LoadJSON implements Runnable {
46.80 - private static final Logger LOG = FXContext.LOG;
46.81 - private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
46.82 - @Override
46.83 - public Thread newThread(Runnable runnable) {
46.84 - Thread thread = Executors.defaultThreadFactory().newThread(runnable);
46.85 - thread.setDaemon(true);
46.86 - return thread;
46.87 - }
46.88 - });
46.89 -
46.90 - private final JSONCall call;
46.91 - private final URL base;
46.92 - private Throwable error;
46.93 - private Object json;
46.94 -
46.95 -
46.96 - private LoadJSON(JSONCall call) {
46.97 - this.call = call;
46.98 - URL b;
46.99 - try {
46.100 - b = new URL(findBaseURL());
46.101 - } catch (MalformedURLException ex) {
46.102 - LOG.log(Level.SEVERE, "Can't find base url for " + call.composeURL("dummy"), ex);
46.103 - b = null;
46.104 - }
46.105 - this.base = b;
46.106 - }
46.107 -
46.108 - public static void loadJSON(JSONCall call) {
46.109 - assert !"WebSocket".equals(call.getMethod());
46.110 - REQ.execute(new LoadJSON((call)));
46.111 - }
46.112 -
46.113 - @Override
46.114 - public void run() {
46.115 - if (Platform.isFxApplicationThread()) {
46.116 - if (error != null) {
46.117 - call.notifyError(error);
46.118 - } else {
46.119 - call.notifySuccess(json);
46.120 - }
46.121 - return;
46.122 - }
46.123 - final String url;
46.124 - if (call.isJSONP()) {
46.125 - url = call.composeURL("dummy");
46.126 - } else {
46.127 - url = call.composeURL(null);
46.128 - }
46.129 - try {
46.130 - final URL u = new URL(base, url.replace(" ", "%20"));
46.131 - URLConnection conn = u.openConnection();
46.132 - if (conn instanceof HttpURLConnection) {
46.133 - HttpURLConnection huc = (HttpURLConnection) conn;
46.134 - if (call.getMethod() != null) {
46.135 - huc.setRequestMethod(call.getMethod());
46.136 - }
46.137 - if (call.isDoOutput()) {
46.138 - huc.setDoOutput(true);
46.139 - final OutputStream os = huc.getOutputStream();
46.140 - call.writeData(os);
46.141 - os.flush();
46.142 - }
46.143 - }
46.144 - final PushbackInputStream is = new PushbackInputStream(
46.145 - conn.getInputStream(), 1
46.146 - );
46.147 - boolean array = false;
46.148 - boolean string = false;
46.149 - if (call.isJSONP()) {
46.150 - for (;;) {
46.151 - int ch = is.read();
46.152 - if (ch == -1) {
46.153 - break;
46.154 - }
46.155 - if (ch == '[') {
46.156 - is.unread(ch);
46.157 - array = true;
46.158 - break;
46.159 - }
46.160 - if (ch == '{') {
46.161 - is.unread(ch);
46.162 - break;
46.163 - }
46.164 - }
46.165 - } else {
46.166 - int ch = is.read();
46.167 - if (ch == -1) {
46.168 - string = true;
46.169 - } else {
46.170 - array = ch == '[';
46.171 - is.unread(ch);
46.172 - if (!array && ch != '{') {
46.173 - string = true;
46.174 - }
46.175 - }
46.176 - }
46.177 - try {
46.178 - if (string) {
46.179 - throw new JSONException("");
46.180 - }
46.181 - Reader r = new InputStreamReader(is, "UTF-8");
46.182 -
46.183 - JSONTokener tok = new JSONTokener(r);
46.184 - Object obj;
46.185 - obj = array ? new JSONArray(tok) : new JSONObject(tok);
46.186 - json = convertToArray(obj);
46.187 - } catch (JSONException ex) {
46.188 - Reader r = new InputStreamReader(is, "UTF-8");
46.189 - StringBuilder sb = new StringBuilder();
46.190 - for (;;) {
46.191 - int ch = r.read();
46.192 - if (ch == -1) {
46.193 - break;
46.194 - }
46.195 - sb.append((char)ch);
46.196 - }
46.197 - json = sb.toString();
46.198 - }
46.199 - } catch (IOException ex) {
46.200 - error = ex;
46.201 - } finally {
46.202 - Platform.runLater(this);
46.203 - }
46.204 - }
46.205 -
46.206 - static Object convertToArray(Object o) throws JSONException {
46.207 - if (o instanceof JSONArray) {
46.208 - JSONArray ja = (JSONArray)o;
46.209 - Object[] arr = new Object[ja.length()];
46.210 - for (int i = 0; i < arr.length; i++) {
46.211 - arr[i] = convertToArray(ja.get(i));
46.212 - }
46.213 - return arr;
46.214 - } else if (o instanceof JSONObject) {
46.215 - JSONObject obj = (JSONObject)o;
46.216 - Iterator it = obj.keys();
46.217 - while (it.hasNext()) {
46.218 - String key = (String)it.next();
46.219 - obj.put(key, convertToArray(obj.get(key)));
46.220 - }
46.221 - return obj;
46.222 - } else {
46.223 - return o;
46.224 - }
46.225 - }
46.226 -
46.227 - public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
46.228 - if (jsonObject instanceof JSONObject) {
46.229 - JSONObject obj = (JSONObject)jsonObject;
46.230 - for (int i = 0; i < props.length; i++) {
46.231 - try {
46.232 - values[i] = obj.has(props[i]) ? obj.get(props[i]) : null;
46.233 - } catch (JSONException ex) {
46.234 - LoadJSON.LOG.log(Level.SEVERE, "Can't read " + props[i] + " from " + jsonObject, ex);
46.235 - }
46.236 - }
46.237 - }
46.238 - if (jsonObject instanceof JSObject) {
46.239 - JSObject obj = (JSObject)jsonObject;
46.240 - for (int i = 0; i < props.length; i++) {
46.241 - Object val = obj.getMember(props[i]);
46.242 - values[i] = isDefined(val) ? val : null;
46.243 - }
46.244 - }
46.245 - }
46.246 -
46.247 - public static Object parse(InputStream is) throws IOException {
46.248 - try {
46.249 - InputStreamReader r = new InputStreamReader(is, "UTF-8");
46.250 - JSONTokener t = new JSONTokener(r);
46.251 - return new JSONObject(t);
46.252 - } catch (JSONException ex) {
46.253 - throw new IOException(ex);
46.254 - }
46.255 - }
46.256 -
46.257 - @JavaScriptBody(args = { }, body =
46.258 - "var h;"
46.259 - + "if (!!window && !!window.location && !!window.location.href)\n"
46.260 - + " h = window.location.href;\n"
46.261 - + "else "
46.262 - + " h = null;"
46.263 - + "return h;\n"
46.264 - )
46.265 - private static native String findBaseURL();
46.266 -
46.267 - private static boolean isDefined(Object val) {
46.268 - return !"undefined".equals(val);
46.269 - }
46.270 -}
47.1 --- a/ko-fx/src/main/java/org/netbeans/html/kofx/LoadWS.java Wed Jan 08 13:18:34 2014 +0100
47.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
47.3 @@ -1,149 +0,0 @@
47.4 -/**
47.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
47.6 - *
47.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
47.8 - *
47.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
47.10 - * Other names may be trademarks of their respective owners.
47.11 - *
47.12 - * The contents of this file are subject to the terms of either the GNU
47.13 - * General Public License Version 2 only ("GPL") or the Common
47.14 - * Development and Distribution License("CDDL") (collectively, the
47.15 - * "License"). You may not use this file except in compliance with the
47.16 - * License. You can obtain a copy of the License at
47.17 - * http://www.netbeans.org/cddl-gplv2.html
47.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
47.19 - * specific language governing permissions and limitations under the
47.20 - * License. When distributing the software, include this License Header
47.21 - * Notice in each file and include the License file at
47.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
47.23 - * particular file as subject to the "Classpath" exception as provided
47.24 - * by Oracle in the GPL Version 2 section of the License file that
47.25 - * accompanied this code. If applicable, add the following below the
47.26 - * License Header, with the fields enclosed by brackets [] replaced by
47.27 - * your own identifying information:
47.28 - * "Portions Copyrighted [year] [name of copyright owner]"
47.29 - *
47.30 - * Contributor(s):
47.31 - *
47.32 - * The Original Software is NetBeans. The Initial Developer of the Original
47.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
47.34 - *
47.35 - * If you wish your version of this file to be governed by only the CDDL
47.36 - * or only the GPL Version 2, indicate your decision by adding
47.37 - * "[Contributor] elects to include this software in this distribution
47.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
47.39 - * single choice of license, a recipient has the option to distribute
47.40 - * your version of this file under either the CDDL, the GPL Version 2 or
47.41 - * to extend the choice of license to its licensees as provided above.
47.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
47.43 - * Version 2 license, then the option applies only if the new code is
47.44 - * made subject to such option by the copyright holder.
47.45 - */
47.46 -package org.netbeans.html.kofx;
47.47 -
47.48 -import net.java.html.js.JavaScriptBody;
47.49 -import org.apidesign.html.json.spi.JSONCall;
47.50 -import org.json.JSONArray;
47.51 -import org.json.JSONException;
47.52 -import org.json.JSONObject;
47.53 -import org.json.JSONTokener;
47.54 -
47.55 -/** Communication with WebSockets for WebView 1.8.
47.56 - *
47.57 - * @author Jaroslav Tulach <jtulach@netbeans.org>
47.58 - */
47.59 -final class LoadWS {
47.60 - private static final boolean SUPPORTED = isWebSocket();
47.61 - private final Object ws;
47.62 - private final JSONCall call;
47.63 -
47.64 - LoadWS(JSONCall first, String url) {
47.65 - call = first;
47.66 - ws = initWebSocket(this, url);
47.67 - if (ws == null) {
47.68 - first.notifyError(new IllegalArgumentException("Wrong URL: " + url));
47.69 - }
47.70 - }
47.71 -
47.72 - static boolean isSupported() {
47.73 - return SUPPORTED;
47.74 - }
47.75 -
47.76 - void send(JSONCall call) {
47.77 - push(call);
47.78 - }
47.79 -
47.80 - private synchronized void push(JSONCall call) {
47.81 - send(ws, call.getMessage());
47.82 - }
47.83 -
47.84 - void onOpen(Object ev) {
47.85 - if (!call.isDoOutput()) {
47.86 - call.notifySuccess(null);
47.87 - }
47.88 - }
47.89 -
47.90 - void onMessage(Object ev, String data) {
47.91 - Object json;
47.92 - try {
47.93 - data = data.trim();
47.94 -
47.95 - JSONTokener tok = new JSONTokener(data);
47.96 - Object obj;
47.97 - obj = data.startsWith("[") ? new JSONArray(tok) : new JSONObject(tok);
47.98 - json = LoadJSON.convertToArray(obj);
47.99 - } catch (JSONException ex) {
47.100 - json = data;
47.101 - }
47.102 - call.notifySuccess(json);
47.103 - }
47.104 -
47.105 - void onError(Object ev) {
47.106 - call.notifyError(new Exception(ev.toString()));
47.107 - }
47.108 -
47.109 - void onClose(boolean wasClean, int code, String reason) {
47.110 - call.notifyError(null);
47.111 - }
47.112 -
47.113 - @JavaScriptBody(args = {}, body = "if (window.WebSocket) return true; else return false;")
47.114 - private static boolean isWebSocket() {
47.115 - return false;
47.116 - }
47.117 -
47.118 - @JavaScriptBody(args = { "back", "url" }, javacall = true, body = ""
47.119 - + "if (window.WebSocket) {"
47.120 - + " try {"
47.121 - + " var ws = new window.WebSocket(url);"
47.122 - + " ws.onopen = function(ev) { back.@org.netbeans.html.kofx.LoadWS::onOpen(Ljava/lang/Object;)(ev); };"
47.123 - + " ws.onmessage = function(ev) { back.@org.netbeans.html.kofx.LoadWS::onMessage(Ljava/lang/Object;Ljava/lang/String;)(ev, ev.data); };"
47.124 - + " ws.onerror = function(ev) { back.@org.netbeans.html.kofx.LoadWS::onError(Ljava/lang/Object;)(ev); };"
47.125 - + " ws.onclose = function(ev) { back.@org.netbeans.html.kofx.LoadWS::onClose(ZILjava/lang/String;)(ev.wasClean, ev.code, ev.reason); };"
47.126 - + " return ws;"
47.127 - + " } catch (ex) {"
47.128 - + " return null;"
47.129 - + " }"
47.130 - + "} else {"
47.131 - + " return null;"
47.132 - + "}"
47.133 - )
47.134 - private static Object initWebSocket(Object back, String url) {
47.135 - return null;
47.136 - }
47.137 -
47.138 -
47.139 - @JavaScriptBody(args = { "ws", "msg" }, body = ""
47.140 - + "ws.send(msg);"
47.141 - )
47.142 - private void send(Object ws, String msg) {
47.143 - }
47.144 -
47.145 - @JavaScriptBody(args = { "ws" }, body = "ws.close();")
47.146 - private static void close(Object ws) {
47.147 - }
47.148 -
47.149 - void close() {
47.150 - close(ws);
47.151 - }
47.152 -}
48.1 --- a/ko-fx/src/main/resources/org/netbeans/html/kofx/knockout-2.2.1.js Wed Jan 08 13:18:34 2014 +0100
48.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
48.3 @@ -1,3594 +0,0 @@
48.4 -// Knockout JavaScript library v2.2.1
48.5 -// (c) Steven Sanderson - http://knockoutjs.com/
48.6 -// License: MIT (http://www.opensource.org/licenses/mit-license.php)
48.7 -
48.8 -(function(){
48.9 -var DEBUG=true;
48.10 -(function(window,document,navigator,jQuery,undefined){
48.11 -!function(factory) {
48.12 - // Support three module loading scenarios
48.13 - if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
48.14 - // [1] CommonJS/Node.js
48.15 - var target = module['exports'] || exports; // module.exports is for Node.js
48.16 - factory(target);
48.17 - } else if (typeof define === 'function' && define['amd']) {
48.18 - // [2] AMD anonymous module
48.19 - define(['exports'], factory);
48.20 - } else {
48.21 - // [3] No module loader (plain <script> tag) - put directly in global namespace
48.22 - factory(window['ko'] = {});
48.23 - }
48.24 -}(function(koExports){
48.25 -// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
48.26 -// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
48.27 -var ko = typeof koExports !== 'undefined' ? koExports : {};
48.28 -// Google Closure Compiler helpers (used only to make the minified file smaller)
48.29 -ko.exportSymbol = function(koPath, object) {
48.30 - var tokens = koPath.split(".");
48.31 -
48.32 - // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
48.33 - // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
48.34 - var target = ko;
48.35 -
48.36 - for (var i = 0; i < tokens.length - 1; i++)
48.37 - target = target[tokens[i]];
48.38 - target[tokens[tokens.length - 1]] = object;
48.39 -};
48.40 -ko.exportProperty = function(owner, publicName, object) {
48.41 - owner[publicName] = object;
48.42 -};
48.43 -ko.version = "2.2.1";
48.44 -
48.45 -ko.exportSymbol('version', ko.version);
48.46 -ko.utils = new (function () {
48.47 - var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
48.48 -
48.49 - // Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
48.50 - var knownEvents = {}, knownEventTypesByEventName = {};
48.51 - var keyEventTypeName = /Firefox\/2/i.test(navigator.userAgent) ? 'KeyboardEvent' : 'UIEvents';
48.52 - knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
48.53 - knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
48.54 - for (var eventType in knownEvents) {
48.55 - var knownEventsForType = knownEvents[eventType];
48.56 - if (knownEventsForType.length) {
48.57 - for (var i = 0, j = knownEventsForType.length; i < j; i++)
48.58 - knownEventTypesByEventName[knownEventsForType[i]] = eventType;
48.59 - }
48.60 - }
48.61 - var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
48.62 -
48.63 - // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
48.64 - // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
48.65 - // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
48.66 - // If there is a future need to detect specific versions of IE10+, we will amend this.
48.67 - var ieVersion = (function() {
48.68 - var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
48.69 -
48.70 - // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
48.71 - while (
48.72 - div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
48.73 - iElems[0]
48.74 - );
48.75 - return version > 4 ? version : undefined;
48.76 - }());
48.77 - var isIe6 = ieVersion === 6,
48.78 - isIe7 = ieVersion === 7;
48.79 -
48.80 - function isClickOnCheckableElement(element, eventType) {
48.81 - if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
48.82 - if (eventType.toLowerCase() != "click") return false;
48.83 - var inputType = element.type;
48.84 - return (inputType == "checkbox") || (inputType == "radio");
48.85 - }
48.86 -
48.87 - return {
48.88 - fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
48.89 -
48.90 - arrayForEach: function (array, action) {
48.91 - for (var i = 0, j = array.length; i < j; i++)
48.92 - action(array[i]);
48.93 - },
48.94 -
48.95 - arrayIndexOf: function (array, item) {
48.96 - if (typeof Array.prototype.indexOf == "function")
48.97 - return Array.prototype.indexOf.call(array, item);
48.98 - for (var i = 0, j = array.length; i < j; i++)
48.99 - if (array[i] === item)
48.100 - return i;
48.101 - return -1;
48.102 - },
48.103 -
48.104 - arrayFirst: function (array, predicate, predicateOwner) {
48.105 - for (var i = 0, j = array.length; i < j; i++)
48.106 - if (predicate.call(predicateOwner, array[i]))
48.107 - return array[i];
48.108 - return null;
48.109 - },
48.110 -
48.111 - arrayRemoveItem: function (array, itemToRemove) {
48.112 - var index = ko.utils.arrayIndexOf(array, itemToRemove);
48.113 - if (index >= 0)
48.114 - array.splice(index, 1);
48.115 - },
48.116 -
48.117 - arrayGetDistinctValues: function (array) {
48.118 - array = array || [];
48.119 - var result = [];
48.120 - for (var i = 0, j = array.length; i < j; i++) {
48.121 - if (ko.utils.arrayIndexOf(result, array[i]) < 0)
48.122 - result.push(array[i]);
48.123 - }
48.124 - return result;
48.125 - },
48.126 -
48.127 - arrayMap: function (array, mapping) {
48.128 - array = array || [];
48.129 - var result = [];
48.130 - for (var i = 0, j = array.length; i < j; i++)
48.131 - result.push(mapping(array[i]));
48.132 - return result;
48.133 - },
48.134 -
48.135 - arrayFilter: function (array, predicate) {
48.136 - array = array || [];
48.137 - var result = [];
48.138 - for (var i = 0, j = array.length; i < j; i++)
48.139 - if (predicate(array[i]))
48.140 - result.push(array[i]);
48.141 - return result;
48.142 - },
48.143 -
48.144 - arrayPushAll: function (array, valuesToPush) {
48.145 - if (valuesToPush instanceof Array)
48.146 - array.push.apply(array, valuesToPush);
48.147 - else
48.148 - for (var i = 0, j = valuesToPush.length; i < j; i++)
48.149 - array.push(valuesToPush[i]);
48.150 - return array;
48.151 - },
48.152 -
48.153 - extend: function (target, source) {
48.154 - if (source) {
48.155 - for(var prop in source) {
48.156 - if(source.hasOwnProperty(prop)) {
48.157 - target[prop] = source[prop];
48.158 - }
48.159 - }
48.160 - }
48.161 - return target;
48.162 - },
48.163 -
48.164 - emptyDomNode: function (domNode) {
48.165 - while (domNode.firstChild) {
48.166 - ko.removeNode(domNode.firstChild);
48.167 - }
48.168 - },
48.169 -
48.170 - moveCleanedNodesToContainerElement: function(nodes) {
48.171 - // Ensure it's a real array, as we're about to reparent the nodes and
48.172 - // we don't want the underlying collection to change while we're doing that.
48.173 - var nodesArray = ko.utils.makeArray(nodes);
48.174 -
48.175 - var container = document.createElement('div');
48.176 - for (var i = 0, j = nodesArray.length; i < j; i++) {
48.177 - container.appendChild(ko.cleanNode(nodesArray[i]));
48.178 - }
48.179 - return container;
48.180 - },
48.181 -
48.182 - cloneNodes: function (nodesArray, shouldCleanNodes) {
48.183 - for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
48.184 - var clonedNode = nodesArray[i].cloneNode(true);
48.185 - newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
48.186 - }
48.187 - return newNodesArray;
48.188 - },
48.189 -
48.190 - setDomNodeChildren: function (domNode, childNodes) {
48.191 - ko.utils.emptyDomNode(domNode);
48.192 - if (childNodes) {
48.193 - for (var i = 0, j = childNodes.length; i < j; i++)
48.194 - domNode.appendChild(childNodes[i]);
48.195 - }
48.196 - },
48.197 -
48.198 - replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
48.199 - var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
48.200 - if (nodesToReplaceArray.length > 0) {
48.201 - var insertionPoint = nodesToReplaceArray[0];
48.202 - var parent = insertionPoint.parentNode;
48.203 - for (var i = 0, j = newNodesArray.length; i < j; i++)
48.204 - parent.insertBefore(newNodesArray[i], insertionPoint);
48.205 - for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
48.206 - ko.removeNode(nodesToReplaceArray[i]);
48.207 - }
48.208 - }
48.209 - },
48.210 -
48.211 - setOptionNodeSelectionState: function (optionNode, isSelected) {
48.212 - // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
48.213 - if (ieVersion < 7)
48.214 - optionNode.setAttribute("selected", isSelected);
48.215 - else
48.216 - optionNode.selected = isSelected;
48.217 - },
48.218 -
48.219 - stringTrim: function (string) {
48.220 - return (string || "").replace(stringTrimRegex, "");
48.221 - },
48.222 -
48.223 - stringTokenize: function (string, delimiter) {
48.224 - var result = [];
48.225 - var tokens = (string || "").split(delimiter);
48.226 - for (var i = 0, j = tokens.length; i < j; i++) {
48.227 - var trimmed = ko.utils.stringTrim(tokens[i]);
48.228 - if (trimmed !== "")
48.229 - result.push(trimmed);
48.230 - }
48.231 - return result;
48.232 - },
48.233 -
48.234 - stringStartsWith: function (string, startsWith) {
48.235 - string = string || "";
48.236 - if (startsWith.length > string.length)
48.237 - return false;
48.238 - return string.substring(0, startsWith.length) === startsWith;
48.239 - },
48.240 -
48.241 - domNodeIsContainedBy: function (node, containedByNode) {
48.242 - if (containedByNode.compareDocumentPosition)
48.243 - return (containedByNode.compareDocumentPosition(node) & 16) == 16;
48.244 - while (node != null) {
48.245 - if (node == containedByNode)
48.246 - return true;
48.247 - node = node.parentNode;
48.248 - }
48.249 - return false;
48.250 - },
48.251 -
48.252 - domNodeIsAttachedToDocument: function (node) {
48.253 - return ko.utils.domNodeIsContainedBy(node, node.ownerDocument);
48.254 - },
48.255 -
48.256 - tagNameLower: function(element) {
48.257 - // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
48.258 - // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
48.259 - // we don't need to do the .toLowerCase() as it will always be lower case anyway.
48.260 - return element && element.tagName && element.tagName.toLowerCase();
48.261 - },
48.262 -
48.263 - registerEventHandler: function (element, eventType, handler) {
48.264 - var mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
48.265 - if (!mustUseAttachEvent && typeof jQuery != "undefined") {
48.266 - if (isClickOnCheckableElement(element, eventType)) {
48.267 - // For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
48.268 - // it toggles the element checked state *after* the click event handlers run, whereas native
48.269 - // click events toggle the checked state *before* the event handler.
48.270 - // Fix this by intecepting the handler and applying the correct checkedness before it runs.
48.271 - var originalHandler = handler;
48.272 - handler = function(event, eventData) {
48.273 - var jQuerySuppliedCheckedState = this.checked;
48.274 - if (eventData)
48.275 - this.checked = eventData.checkedStateBeforeEvent !== true;
48.276 - originalHandler.call(this, event);
48.277 - this.checked = jQuerySuppliedCheckedState; // Restore the state jQuery applied
48.278 - };
48.279 - }
48.280 - jQuery(element)['bind'](eventType, handler);
48.281 - } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
48.282 - element.addEventListener(eventType, handler, false);
48.283 - else if (typeof element.attachEvent != "undefined")
48.284 - element.attachEvent("on" + eventType, function (event) {
48.285 - handler.call(element, event);
48.286 - });
48.287 - else
48.288 - throw new Error("Browser doesn't support addEventListener or attachEvent");
48.289 - },
48.290 -
48.291 - triggerEvent: function (element, eventType) {
48.292 - if (!(element && element.nodeType))
48.293 - throw new Error("element must be a DOM node when calling triggerEvent");
48.294 -
48.295 - if (typeof jQuery != "undefined") {
48.296 - var eventData = [];
48.297 - if (isClickOnCheckableElement(element, eventType)) {
48.298 - // Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
48.299 - eventData.push({ checkedStateBeforeEvent: element.checked });
48.300 - }
48.301 - jQuery(element)['trigger'](eventType, eventData);
48.302 - } else if (typeof document.createEvent == "function") {
48.303 - if (typeof element.dispatchEvent == "function") {
48.304 - var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
48.305 - var event = document.createEvent(eventCategory);
48.306 - event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
48.307 - element.dispatchEvent(event);
48.308 - }
48.309 - else
48.310 - throw new Error("The supplied element doesn't support dispatchEvent");
48.311 - } else if (typeof element.fireEvent != "undefined") {
48.312 - // Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
48.313 - // so to make it consistent, we'll do it manually here
48.314 - if (isClickOnCheckableElement(element, eventType))
48.315 - element.checked = element.checked !== true;
48.316 - element.fireEvent("on" + eventType);
48.317 - }
48.318 - else
48.319 - throw new Error("Browser doesn't support triggering events");
48.320 - },
48.321 -
48.322 - unwrapObservable: function (value) {
48.323 - return ko.isObservable(value) ? value() : value;
48.324 - },
48.325 -
48.326 - peekObservable: function (value) {
48.327 - return ko.isObservable(value) ? value.peek() : value;
48.328 - },
48.329 -
48.330 - toggleDomNodeCssClass: function (node, classNames, shouldHaveClass) {
48.331 - if (classNames) {
48.332 - var cssClassNameRegex = /[\w-]+/g,
48.333 - currentClassNames = node.className.match(cssClassNameRegex) || [];
48.334 - ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
48.335 - var indexOfClass = ko.utils.arrayIndexOf(currentClassNames, className);
48.336 - if (indexOfClass >= 0) {
48.337 - if (!shouldHaveClass)
48.338 - currentClassNames.splice(indexOfClass, 1);
48.339 - } else {
48.340 - if (shouldHaveClass)
48.341 - currentClassNames.push(className);
48.342 - }
48.343 - });
48.344 - node.className = currentClassNames.join(" ");
48.345 - }
48.346 - },
48.347 -
48.348 - setTextContent: function(element, textContent) {
48.349 - var value = ko.utils.unwrapObservable(textContent);
48.350 - if ((value === null) || (value === undefined))
48.351 - value = "";
48.352 -
48.353 - if (element.nodeType === 3) {
48.354 - element.data = value;
48.355 - } else {
48.356 - // We need there to be exactly one child: a text node.
48.357 - // If there are no children, more than one, or if it's not a text node,
48.358 - // we'll clear everything and create a single text node.
48.359 - var innerTextNode = ko.virtualElements.firstChild(element);
48.360 - if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
48.361 - ko.virtualElements.setDomNodeChildren(element, [document.createTextNode(value)]);
48.362 - } else {
48.363 - innerTextNode.data = value;
48.364 - }
48.365 -
48.366 - ko.utils.forceRefresh(element);
48.367 - }
48.368 - },
48.369 -
48.370 - setElementName: function(element, name) {
48.371 - element.name = name;
48.372 -
48.373 - // Workaround IE 6/7 issue
48.374 - // - https://github.com/SteveSanderson/knockout/issues/197
48.375 - // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
48.376 - if (ieVersion <= 7) {
48.377 - try {
48.378 - element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
48.379 - }
48.380 - catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
48.381 - }
48.382 - },
48.383 -
48.384 - forceRefresh: function(node) {
48.385 - // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
48.386 - if (ieVersion >= 9) {
48.387 - // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
48.388 - var elem = node.nodeType == 1 ? node : node.parentNode;
48.389 - if (elem.style)
48.390 - elem.style.zoom = elem.style.zoom;
48.391 - }
48.392 - },
48.393 -
48.394 - ensureSelectElementIsRenderedCorrectly: function(selectElement) {
48.395 - // Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width.
48.396 - // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
48.397 - if (ieVersion >= 9) {
48.398 - var originalWidth = selectElement.style.width;
48.399 - selectElement.style.width = 0;
48.400 - selectElement.style.width = originalWidth;
48.401 - }
48.402 - },
48.403 -
48.404 - range: function (min, max) {
48.405 - min = ko.utils.unwrapObservable(min);
48.406 - max = ko.utils.unwrapObservable(max);
48.407 - var result = [];
48.408 - for (var i = min; i <= max; i++)
48.409 - result.push(i);
48.410 - return result;
48.411 - },
48.412 -
48.413 - makeArray: function(arrayLikeObject) {
48.414 - var result = [];
48.415 - for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
48.416 - result.push(arrayLikeObject[i]);
48.417 - };
48.418 - return result;
48.419 - },
48.420 -
48.421 - isIe6 : isIe6,
48.422 - isIe7 : isIe7,
48.423 - ieVersion : ieVersion,
48.424 -
48.425 - getFormFields: function(form, fieldName) {
48.426 - var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
48.427 - var isMatchingField = (typeof fieldName == 'string')
48.428 - ? function(field) { return field.name === fieldName }
48.429 - : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
48.430 - var matches = [];
48.431 - for (var i = fields.length - 1; i >= 0; i--) {
48.432 - if (isMatchingField(fields[i]))
48.433 - matches.push(fields[i]);
48.434 - };
48.435 - return matches;
48.436 - },
48.437 -
48.438 - parseJson: function (jsonString) {
48.439 - if (typeof jsonString == "string") {
48.440 - jsonString = ko.utils.stringTrim(jsonString);
48.441 - if (jsonString) {
48.442 - if (window.JSON && window.JSON.parse) // Use native parsing where available
48.443 - return window.JSON.parse(jsonString);
48.444 - return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
48.445 - }
48.446 - }
48.447 - return null;
48.448 - },
48.449 -
48.450 - stringifyJson: function (data, replacer, space) { // replacer and space are optional
48.451 - if ((typeof JSON == "undefined") || (typeof JSON.stringify == "undefined"))
48.452 - throw new Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
48.453 - return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
48.454 - },
48.455 -
48.456 - postJson: function (urlOrForm, data, options) {
48.457 - options = options || {};
48.458 - var params = options['params'] || {};
48.459 - var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
48.460 - var url = urlOrForm;
48.461 -
48.462 - // If we were given a form, use its 'action' URL and pick out any requested field values
48.463 - if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
48.464 - var originalForm = urlOrForm;
48.465 - url = originalForm.action;
48.466 - for (var i = includeFields.length - 1; i >= 0; i--) {
48.467 - var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
48.468 - for (var j = fields.length - 1; j >= 0; j--)
48.469 - params[fields[j].name] = fields[j].value;
48.470 - }
48.471 - }
48.472 -
48.473 - data = ko.utils.unwrapObservable(data);
48.474 - var form = document.createElement("form");
48.475 - form.style.display = "none";
48.476 - form.action = url;
48.477 - form.method = "post";
48.478 - for (var key in data) {
48.479 - var input = document.createElement("input");
48.480 - input.name = key;
48.481 - input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
48.482 - form.appendChild(input);
48.483 - }
48.484 - for (var key in params) {
48.485 - var input = document.createElement("input");
48.486 - input.name = key;
48.487 - input.value = params[key];
48.488 - form.appendChild(input);
48.489 - }
48.490 - document.body.appendChild(form);
48.491 - options['submitter'] ? options['submitter'](form) : form.submit();
48.492 - setTimeout(function () { form.parentNode.removeChild(form); }, 0);
48.493 - }
48.494 - }
48.495 -})();
48.496 -
48.497 -ko.exportSymbol('utils', ko.utils);
48.498 -ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
48.499 -ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
48.500 -ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
48.501 -ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
48.502 -ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
48.503 -ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
48.504 -ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
48.505 -ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
48.506 -ko.exportSymbol('utils.extend', ko.utils.extend);
48.507 -ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
48.508 -ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
48.509 -ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
48.510 -ko.exportSymbol('utils.postJson', ko.utils.postJson);
48.511 -ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
48.512 -ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
48.513 -ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
48.514 -ko.exportSymbol('utils.range', ko.utils.range);
48.515 -ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
48.516 -ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
48.517 -ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
48.518 -
48.519 -if (!Function.prototype['bind']) {
48.520 - // Function.prototype.bind is a standard part of ECMAScript 5th Edition (December 2009, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)
48.521 - // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
48.522 - Function.prototype['bind'] = function (object) {
48.523 - var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
48.524 - return function () {
48.525 - return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
48.526 - };
48.527 - };
48.528 -}
48.529 -
48.530 -ko.utils.domData = new (function () {
48.531 - var uniqueId = 0;
48.532 - var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
48.533 - var dataStore = {};
48.534 - return {
48.535 - get: function (node, key) {
48.536 - var allDataForNode = ko.utils.domData.getAll(node, false);
48.537 - return allDataForNode === undefined ? undefined : allDataForNode[key];
48.538 - },
48.539 - set: function (node, key, value) {
48.540 - if (value === undefined) {
48.541 - // Make sure we don't actually create a new domData key if we are actually deleting a value
48.542 - if (ko.utils.domData.getAll(node, false) === undefined)
48.543 - return;
48.544 - }
48.545 - var allDataForNode = ko.utils.domData.getAll(node, true);
48.546 - allDataForNode[key] = value;
48.547 - },
48.548 - getAll: function (node, createIfNotFound) {
48.549 - var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
48.550 - var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
48.551 - if (!hasExistingDataStore) {
48.552 - if (!createIfNotFound)
48.553 - return undefined;
48.554 - dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
48.555 - dataStore[dataStoreKey] = {};
48.556 - }
48.557 - return dataStore[dataStoreKey];
48.558 - },
48.559 - clear: function (node) {
48.560 - var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
48.561 - if (dataStoreKey) {
48.562 - delete dataStore[dataStoreKey];
48.563 - node[dataStoreKeyExpandoPropertyName] = null;
48.564 - return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
48.565 - }
48.566 - return false;
48.567 - }
48.568 - }
48.569 -})();
48.570 -
48.571 -ko.exportSymbol('utils.domData', ko.utils.domData);
48.572 -ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully
48.573 -
48.574 -ko.utils.domNodeDisposal = new (function () {
48.575 - var domDataKey = "__ko_domNodeDisposal__" + (new Date).getTime();
48.576 - var cleanableNodeTypes = { 1: true, 8: true, 9: true }; // Element, Comment, Document
48.577 - var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
48.578 -
48.579 - function getDisposeCallbacksCollection(node, createIfNotFound) {
48.580 - var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
48.581 - if ((allDisposeCallbacks === undefined) && createIfNotFound) {
48.582 - allDisposeCallbacks = [];
48.583 - ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
48.584 - }
48.585 - return allDisposeCallbacks;
48.586 - }
48.587 - function destroyCallbacksCollection(node) {
48.588 - ko.utils.domData.set(node, domDataKey, undefined);
48.589 - }
48.590 -
48.591 - function cleanSingleNode(node) {
48.592 - // Run all the dispose callbacks
48.593 - var callbacks = getDisposeCallbacksCollection(node, false);
48.594 - if (callbacks) {
48.595 - callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
48.596 - for (var i = 0; i < callbacks.length; i++)
48.597 - callbacks[i](node);
48.598 - }
48.599 -
48.600 - // Also erase the DOM data
48.601 - ko.utils.domData.clear(node);
48.602 -
48.603 - // Special support for jQuery here because it's so commonly used.
48.604 - // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
48.605 - // so notify it to tear down any resources associated with the node & descendants here.
48.606 - if ((typeof jQuery == "function") && (typeof jQuery['cleanData'] == "function"))
48.607 - jQuery['cleanData']([node]);
48.608 -
48.609 - // Also clear any immediate-child comment nodes, as these wouldn't have been found by
48.610 - // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
48.611 - if (cleanableNodeTypesWithDescendants[node.nodeType])
48.612 - cleanImmediateCommentTypeChildren(node);
48.613 - }
48.614 -
48.615 - function cleanImmediateCommentTypeChildren(nodeWithChildren) {
48.616 - var child, nextChild = nodeWithChildren.firstChild;
48.617 - while (child = nextChild) {
48.618 - nextChild = child.nextSibling;
48.619 - if (child.nodeType === 8)
48.620 - cleanSingleNode(child);
48.621 - }
48.622 - }
48.623 -
48.624 - return {
48.625 - addDisposeCallback : function(node, callback) {
48.626 - if (typeof callback != "function")
48.627 - throw new Error("Callback must be a function");
48.628 - getDisposeCallbacksCollection(node, true).push(callback);
48.629 - },
48.630 -
48.631 - removeDisposeCallback : function(node, callback) {
48.632 - var callbacksCollection = getDisposeCallbacksCollection(node, false);
48.633 - if (callbacksCollection) {
48.634 - ko.utils.arrayRemoveItem(callbacksCollection, callback);
48.635 - if (callbacksCollection.length == 0)
48.636 - destroyCallbacksCollection(node);
48.637 - }
48.638 - },
48.639 -
48.640 - cleanNode : function(node) {
48.641 - // First clean this node, where applicable
48.642 - if (cleanableNodeTypes[node.nodeType]) {
48.643 - cleanSingleNode(node);
48.644 -
48.645 - // ... then its descendants, where applicable
48.646 - if (cleanableNodeTypesWithDescendants[node.nodeType]) {
48.647 - // Clone the descendants list in case it changes during iteration
48.648 - var descendants = [];
48.649 - ko.utils.arrayPushAll(descendants, node.getElementsByTagName("*"));
48.650 - for (var i = 0, j = descendants.length; i < j; i++)
48.651 - cleanSingleNode(descendants[i]);
48.652 - }
48.653 - }
48.654 - return node;
48.655 - },
48.656 -
48.657 - removeNode : function(node) {
48.658 - ko.cleanNode(node);
48.659 - if (node.parentNode)
48.660 - node.parentNode.removeChild(node);
48.661 - }
48.662 - }
48.663 -})();
48.664 -ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
48.665 -ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
48.666 -ko.exportSymbol('cleanNode', ko.cleanNode);
48.667 -ko.exportSymbol('removeNode', ko.removeNode);
48.668 -ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
48.669 -ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
48.670 -ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
48.671 -(function () {
48.672 - var leadingCommentRegex = /^(\s*)<!--(.*?)-->/;
48.673 -
48.674 - function simpleHtmlParse(html) {
48.675 - // Based on jQuery's "clean" function, but only accounting for table-related elements.
48.676 - // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
48.677 -
48.678 - // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
48.679 - // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
48.680 - // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
48.681 - // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
48.682 -
48.683 - // Trim whitespace, otherwise indexOf won't work as expected
48.684 - var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
48.685 -
48.686 - // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
48.687 - var wrap = tags.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] ||
48.688 - !tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] ||
48.689 - (!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
48.690 - /* anything else */ [0, "", ""];
48.691 -
48.692 - // Go to html and back, then peel off extra wrappers
48.693 - // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
48.694 - var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
48.695 - if (typeof window['innerShiv'] == "function") {
48.696 - div.appendChild(window['innerShiv'](markup));
48.697 - } else {
48.698 - div.innerHTML = markup;
48.699 - }
48.700 -
48.701 - // Move to the right depth
48.702 - while (wrap[0]--)
48.703 - div = div.lastChild;
48.704 -
48.705 - return ko.utils.makeArray(div.lastChild.childNodes);
48.706 - }
48.707 -
48.708 - function jQueryHtmlParse(html) {
48.709 - // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
48.710 - if (jQuery['parseHTML']) {
48.711 - return jQuery['parseHTML'](html);
48.712 - } else {
48.713 - // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
48.714 - var elems = jQuery['clean']([html]);
48.715 -
48.716 - // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
48.717 - // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
48.718 - // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
48.719 - if (elems && elems[0]) {
48.720 - // Find the top-most parent element that's a direct child of a document fragment
48.721 - var elem = elems[0];
48.722 - while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
48.723 - elem = elem.parentNode;
48.724 - // ... then detach it
48.725 - if (elem.parentNode)
48.726 - elem.parentNode.removeChild(elem);
48.727 - }
48.728 -
48.729 - return elems;
48.730 - }
48.731 - }
48.732 -
48.733 - ko.utils.parseHtmlFragment = function(html) {
48.734 - return typeof jQuery != 'undefined' ? jQueryHtmlParse(html) // As below, benefit from jQuery's optimisations where possible
48.735 - : simpleHtmlParse(html); // ... otherwise, this simple logic will do in most common cases.
48.736 - };
48.737 -
48.738 - ko.utils.setHtml = function(node, html) {
48.739 - ko.utils.emptyDomNode(node);
48.740 -
48.741 - // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
48.742 - html = ko.utils.unwrapObservable(html);
48.743 -
48.744 - if ((html !== null) && (html !== undefined)) {
48.745 - if (typeof html != 'string')
48.746 - html = html.toString();
48.747 -
48.748 - // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
48.749 - // for example <tr> elements which are not normally allowed to exist on their own.
48.750 - // If you've referenced jQuery we'll use that rather than duplicating its code.
48.751 - if (typeof jQuery != 'undefined') {
48.752 - jQuery(node)['html'](html);
48.753 - } else {
48.754 - // ... otherwise, use KO's own parsing logic.
48.755 - var parsedNodes = ko.utils.parseHtmlFragment(html);
48.756 - for (var i = 0; i < parsedNodes.length; i++)
48.757 - node.appendChild(parsedNodes[i]);
48.758 - }
48.759 - }
48.760 - };
48.761 -})();
48.762 -
48.763 -ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
48.764 -ko.exportSymbol('utils.setHtml', ko.utils.setHtml);
48.765 -
48.766 -ko.memoization = (function () {
48.767 - var memos = {};
48.768 -
48.769 - function randomMax8HexChars() {
48.770 - return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
48.771 - }
48.772 - function generateRandomId() {
48.773 - return randomMax8HexChars() + randomMax8HexChars();
48.774 - }
48.775 - function findMemoNodes(rootNode, appendToArray) {
48.776 - if (!rootNode)
48.777 - return;
48.778 - if (rootNode.nodeType == 8) {
48.779 - var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
48.780 - if (memoId != null)
48.781 - appendToArray.push({ domNode: rootNode, memoId: memoId });
48.782 - } else if (rootNode.nodeType == 1) {
48.783 - for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
48.784 - findMemoNodes(childNodes[i], appendToArray);
48.785 - }
48.786 - }
48.787 -
48.788 - return {
48.789 - memoize: function (callback) {
48.790 - if (typeof callback != "function")
48.791 - throw new Error("You can only pass a function to ko.memoization.memoize()");
48.792 - var memoId = generateRandomId();
48.793 - memos[memoId] = callback;
48.794 - return "<!--[ko_memo:" + memoId + "]-->";
48.795 - },
48.796 -
48.797 - unmemoize: function (memoId, callbackParams) {
48.798 - var callback = memos[memoId];
48.799 - if (callback === undefined)
48.800 - throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
48.801 - try {
48.802 - callback.apply(null, callbackParams || []);
48.803 - return true;
48.804 - }
48.805 - finally { delete memos[memoId]; }
48.806 - },
48.807 -
48.808 - unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
48.809 - var memos = [];
48.810 - findMemoNodes(domNode, memos);
48.811 - for (var i = 0, j = memos.length; i < j; i++) {
48.812 - var node = memos[i].domNode;
48.813 - var combinedParams = [node];
48.814 - if (extraCallbackParamsArray)
48.815 - ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
48.816 - ko.memoization.unmemoize(memos[i].memoId, combinedParams);
48.817 - node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
48.818 - if (node.parentNode)
48.819 - node.parentNode.removeChild(node); // If possible, erase it totally (not always possible - someone else might just hold a reference to it then call unmemoizeDomNodeAndDescendants again)
48.820 - }
48.821 - },
48.822 -
48.823 - parseMemoText: function (memoText) {
48.824 - var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
48.825 - return match ? match[1] : null;
48.826 - }
48.827 - };
48.828 -})();
48.829 -
48.830 -ko.exportSymbol('memoization', ko.memoization);
48.831 -ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
48.832 -ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
48.833 -ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
48.834 -ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
48.835 -ko.extenders = {
48.836 - 'throttle': function(target, timeout) {
48.837 - // Throttling means two things:
48.838 -
48.839 - // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
48.840 - // notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
48.841 - target['throttleEvaluation'] = timeout;
48.842 -
48.843 - // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
48.844 - // so the target cannot change value synchronously or faster than a certain rate
48.845 - var writeTimeoutInstance = null;
48.846 - return ko.dependentObservable({
48.847 - 'read': target,
48.848 - 'write': function(value) {
48.849 - clearTimeout(writeTimeoutInstance);
48.850 - writeTimeoutInstance = setTimeout(function() {
48.851 - target(value);
48.852 - }, timeout);
48.853 - }
48.854 - });
48.855 - },
48.856 -
48.857 - 'notify': function(target, notifyWhen) {
48.858 - target["equalityComparer"] = notifyWhen == "always"
48.859 - ? function() { return false } // Treat all values as not equal
48.860 - : ko.observable["fn"]["equalityComparer"];
48.861 - return target;
48.862 - }
48.863 -};
48.864 -
48.865 -function applyExtenders(requestedExtenders) {
48.866 - var target = this;
48.867 - if (requestedExtenders) {
48.868 - for (var key in requestedExtenders) {
48.869 - var extenderHandler = ko.extenders[key];
48.870 - if (typeof extenderHandler == 'function') {
48.871 - target = extenderHandler(target, requestedExtenders[key]);
48.872 - }
48.873 - }
48.874 - }
48.875 - return target;
48.876 -}
48.877 -
48.878 -ko.exportSymbol('extenders', ko.extenders);
48.879 -
48.880 -ko.subscription = function (target, callback, disposeCallback) {
48.881 - this.target = target;
48.882 - this.callback = callback;
48.883 - this.disposeCallback = disposeCallback;
48.884 - ko.exportProperty(this, 'dispose', this.dispose);
48.885 -};
48.886 -ko.subscription.prototype.dispose = function () {
48.887 - this.isDisposed = true;
48.888 - this.disposeCallback();
48.889 -};
48.890 -
48.891 -ko.subscribable = function () {
48.892 - this._subscriptions = {};
48.893 -
48.894 - ko.utils.extend(this, ko.subscribable['fn']);
48.895 - ko.exportProperty(this, 'subscribe', this.subscribe);
48.896 - ko.exportProperty(this, 'extend', this.extend);
48.897 - ko.exportProperty(this, 'getSubscriptionsCount', this.getSubscriptionsCount);
48.898 -}
48.899 -
48.900 -var defaultEvent = "change";
48.901 -
48.902 -ko.subscribable['fn'] = {
48.903 - subscribe: function (callback, callbackTarget, event) {
48.904 - event = event || defaultEvent;
48.905 - var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
48.906 -
48.907 - var subscription = new ko.subscription(this, boundCallback, function () {
48.908 - ko.utils.arrayRemoveItem(this._subscriptions[event], subscription);
48.909 - }.bind(this));
48.910 -
48.911 - if (!this._subscriptions[event])
48.912 - this._subscriptions[event] = [];
48.913 - this._subscriptions[event].push(subscription);
48.914 - return subscription;
48.915 - },
48.916 -
48.917 - "notifySubscribers": function (valueToNotify, event) {
48.918 - event = event || defaultEvent;
48.919 - if (this._subscriptions[event]) {
48.920 - ko.dependencyDetection.ignore(function() {
48.921 - ko.utils.arrayForEach(this._subscriptions[event].slice(0), function (subscription) {
48.922 - // In case a subscription was disposed during the arrayForEach cycle, check
48.923 - // for isDisposed on each subscription before invoking its callback
48.924 - if (subscription && (subscription.isDisposed !== true))
48.925 - subscription.callback(valueToNotify);
48.926 - });
48.927 - }, this);
48.928 - }
48.929 - },
48.930 -
48.931 - getSubscriptionsCount: function () {
48.932 - var total = 0;
48.933 - for (var eventName in this._subscriptions) {
48.934 - if (this._subscriptions.hasOwnProperty(eventName))
48.935 - total += this._subscriptions[eventName].length;
48.936 - }
48.937 - return total;
48.938 - },
48.939 -
48.940 - extend: applyExtenders
48.941 -};
48.942 -
48.943 -
48.944 -ko.isSubscribable = function (instance) {
48.945 - return typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
48.946 -};
48.947 -
48.948 -ko.exportSymbol('subscribable', ko.subscribable);
48.949 -ko.exportSymbol('isSubscribable', ko.isSubscribable);
48.950 -
48.951 -ko.dependencyDetection = (function () {
48.952 - var _frames = [];
48.953 -
48.954 - return {
48.955 - begin: function (callback) {
48.956 - _frames.push({ callback: callback, distinctDependencies:[] });
48.957 - },
48.958 -
48.959 - end: function () {
48.960 - _frames.pop();
48.961 - },
48.962 -
48.963 - registerDependency: function (subscribable) {
48.964 - if (!ko.isSubscribable(subscribable))
48.965 - throw new Error("Only subscribable things can act as dependencies");
48.966 - if (_frames.length > 0) {
48.967 - var topFrame = _frames[_frames.length - 1];
48.968 - if (!topFrame || ko.utils.arrayIndexOf(topFrame.distinctDependencies, subscribable) >= 0)
48.969 - return;
48.970 - topFrame.distinctDependencies.push(subscribable);
48.971 - topFrame.callback(subscribable);
48.972 - }
48.973 - },
48.974 -
48.975 - ignore: function(callback, callbackTarget, callbackArgs) {
48.976 - try {
48.977 - _frames.push(null);
48.978 - return callback.apply(callbackTarget, callbackArgs || []);
48.979 - } finally {
48.980 - _frames.pop();
48.981 - }
48.982 - }
48.983 - };
48.984 -})();
48.985 -var primitiveTypes = { 'undefined':true, 'boolean':true, 'number':true, 'string':true };
48.986 -
48.987 -ko.observable = function (initialValue) {
48.988 - var _latestValue = initialValue;
48.989 -
48.990 - function observable() {
48.991 - if (arguments.length > 0) {
48.992 - // Write
48.993 -
48.994 - // Ignore writes if the value hasn't changed
48.995 - if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
48.996 - observable.valueWillMutate();
48.997 - _latestValue = arguments[0];
48.998 - if (DEBUG) observable._latestValue = _latestValue;
48.999 - observable.valueHasMutated();
48.1000 - }
48.1001 - return this; // Permits chained assignments
48.1002 - }
48.1003 - else {
48.1004 - // Read
48.1005 - ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
48.1006 - return _latestValue;
48.1007 - }
48.1008 - }
48.1009 - if (DEBUG) observable._latestValue = _latestValue;
48.1010 - ko.subscribable.call(observable);
48.1011 - observable.peek = function() { return _latestValue };
48.1012 - observable.valueHasMutated = function () { observable["notifySubscribers"](_latestValue); }
48.1013 - observable.valueWillMutate = function () { observable["notifySubscribers"](_latestValue, "beforeChange"); }
48.1014 - ko.utils.extend(observable, ko.observable['fn']);
48.1015 -
48.1016 - ko.exportProperty(observable, 'peek', observable.peek);
48.1017 - ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
48.1018 - ko.exportProperty(observable, "valueWillMutate", observable.valueWillMutate);
48.1019 -
48.1020 - return observable;
48.1021 -}
48.1022 -
48.1023 -ko.observable['fn'] = {
48.1024 - "equalityComparer": function valuesArePrimitiveAndEqual(a, b) {
48.1025 - var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
48.1026 - return oldValueIsPrimitive ? (a === b) : false;
48.1027 - }
48.1028 -};
48.1029 -
48.1030 -var protoProperty = ko.observable.protoProperty = "__ko_proto__";
48.1031 -ko.observable['fn'][protoProperty] = ko.observable;
48.1032 -
48.1033 -ko.hasPrototype = function(instance, prototype) {
48.1034 - if ((instance === null) || (instance === undefined) || (instance[protoProperty] === undefined)) return false;
48.1035 - if (instance[protoProperty] === prototype) return true;
48.1036 - return ko.hasPrototype(instance[protoProperty], prototype); // Walk the prototype chain
48.1037 -};
48.1038 -
48.1039 -ko.isObservable = function (instance) {
48.1040 - return ko.hasPrototype(instance, ko.observable);
48.1041 -}
48.1042 -ko.isWriteableObservable = function (instance) {
48.1043 - // Observable
48.1044 - if ((typeof instance == "function") && instance[protoProperty] === ko.observable)
48.1045 - return true;
48.1046 - // Writeable dependent observable
48.1047 - if ((typeof instance == "function") && (instance[protoProperty] === ko.dependentObservable) && (instance.hasWriteFunction))
48.1048 - return true;
48.1049 - // Anything else
48.1050 - return false;
48.1051 -}
48.1052 -
48.1053 -
48.1054 -ko.exportSymbol('observable', ko.observable);
48.1055 -ko.exportSymbol('isObservable', ko.isObservable);
48.1056 -ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
48.1057 -ko.observableArray = function (initialValues) {
48.1058 - if (arguments.length == 0) {
48.1059 - // Zero-parameter constructor initializes to empty array
48.1060 - initialValues = [];
48.1061 - }
48.1062 - if ((initialValues !== null) && (initialValues !== undefined) && !('length' in initialValues))
48.1063 - throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
48.1064 -
48.1065 - var result = ko.observable(initialValues);
48.1066 - ko.utils.extend(result, ko.observableArray['fn']);
48.1067 - return result;
48.1068 -}
48.1069 -
48.1070 -ko.observableArray['fn'] = {
48.1071 - 'remove': function (valueOrPredicate) {
48.1072 - var underlyingArray = this.peek();
48.1073 - var removedValues = [];
48.1074 - var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
48.1075 - for (var i = 0; i < underlyingArray.length; i++) {
48.1076 - var value = underlyingArray[i];
48.1077 - if (predicate(value)) {
48.1078 - if (removedValues.length === 0) {
48.1079 - this.valueWillMutate();
48.1080 - }
48.1081 - removedValues.push(value);
48.1082 - underlyingArray.splice(i, 1);
48.1083 - i--;
48.1084 - }
48.1085 - }
48.1086 - if (removedValues.length) {
48.1087 - this.valueHasMutated();
48.1088 - }
48.1089 - return removedValues;
48.1090 - },
48.1091 -
48.1092 - 'removeAll': function (arrayOfValues) {
48.1093 - // If you passed zero args, we remove everything
48.1094 - if (arrayOfValues === undefined) {
48.1095 - var underlyingArray = this.peek();
48.1096 - var allValues = underlyingArray.slice(0);
48.1097 - this.valueWillMutate();
48.1098 - underlyingArray.splice(0, underlyingArray.length);
48.1099 - this.valueHasMutated();
48.1100 - return allValues;
48.1101 - }
48.1102 - // If you passed an arg, we interpret it as an array of entries to remove
48.1103 - if (!arrayOfValues)
48.1104 - return [];
48.1105 - return this['remove'](function (value) {
48.1106 - return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
48.1107 - });
48.1108 - },
48.1109 -
48.1110 - 'destroy': function (valueOrPredicate) {
48.1111 - var underlyingArray = this.peek();
48.1112 - var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
48.1113 - this.valueWillMutate();
48.1114 - for (var i = underlyingArray.length - 1; i >= 0; i--) {
48.1115 - var value = underlyingArray[i];
48.1116 - if (predicate(value))
48.1117 - underlyingArray[i]["_destroy"] = true;
48.1118 - }
48.1119 - this.valueHasMutated();
48.1120 - },
48.1121 -
48.1122 - 'destroyAll': function (arrayOfValues) {
48.1123 - // If you passed zero args, we destroy everything
48.1124 - if (arrayOfValues === undefined)
48.1125 - return this['destroy'](function() { return true });
48.1126 -
48.1127 - // If you passed an arg, we interpret it as an array of entries to destroy
48.1128 - if (!arrayOfValues)
48.1129 - return [];
48.1130 - return this['destroy'](function (value) {
48.1131 - return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
48.1132 - });
48.1133 - },
48.1134 -
48.1135 - 'indexOf': function (item) {
48.1136 - var underlyingArray = this();
48.1137 - return ko.utils.arrayIndexOf(underlyingArray, item);
48.1138 - },
48.1139 -
48.1140 - 'replace': function(oldItem, newItem) {
48.1141 - var index = this['indexOf'](oldItem);
48.1142 - if (index >= 0) {
48.1143 - this.valueWillMutate();
48.1144 - this.peek()[index] = newItem;
48.1145 - this.valueHasMutated();
48.1146 - }
48.1147 - }
48.1148 -}
48.1149 -
48.1150 -// Populate ko.observableArray.fn with read/write functions from native arrays
48.1151 -// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
48.1152 -// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
48.1153 -ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
48.1154 - ko.observableArray['fn'][methodName] = function () {
48.1155 - // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
48.1156 - // (for consistency with mutating regular observables)
48.1157 - var underlyingArray = this.peek();
48.1158 - this.valueWillMutate();
48.1159 - var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
48.1160 - this.valueHasMutated();
48.1161 - return methodCallResult;
48.1162 - };
48.1163 -});
48.1164 -
48.1165 -// Populate ko.observableArray.fn with read-only functions from native arrays
48.1166 -ko.utils.arrayForEach(["slice"], function (methodName) {
48.1167 - ko.observableArray['fn'][methodName] = function () {
48.1168 - var underlyingArray = this();
48.1169 - return underlyingArray[methodName].apply(underlyingArray, arguments);
48.1170 - };
48.1171 -});
48.1172 -
48.1173 -ko.exportSymbol('observableArray', ko.observableArray);
48.1174 -ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
48.1175 - var _latestValue,
48.1176 - _hasBeenEvaluated = false,
48.1177 - _isBeingEvaluated = false,
48.1178 - readFunction = evaluatorFunctionOrOptions;
48.1179 -
48.1180 - if (readFunction && typeof readFunction == "object") {
48.1181 - // Single-parameter syntax - everything is on this "options" param
48.1182 - options = readFunction;
48.1183 - readFunction = options["read"];
48.1184 - } else {
48.1185 - // Multi-parameter syntax - construct the options according to the params passed
48.1186 - options = options || {};
48.1187 - if (!readFunction)
48.1188 - readFunction = options["read"];
48.1189 - }
48.1190 - if (typeof readFunction != "function")
48.1191 - throw new Error("Pass a function that returns the value of the ko.computed");
48.1192 -
48.1193 - function addSubscriptionToDependency(subscribable) {
48.1194 - _subscriptionsToDependencies.push(subscribable.subscribe(evaluatePossiblyAsync));
48.1195 - }
48.1196 -
48.1197 - function disposeAllSubscriptionsToDependencies() {
48.1198 - ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
48.1199 - subscription.dispose();
48.1200 - });
48.1201 - _subscriptionsToDependencies = [];
48.1202 - }
48.1203 -
48.1204 - function evaluatePossiblyAsync() {
48.1205 - var throttleEvaluationTimeout = dependentObservable['throttleEvaluation'];
48.1206 - if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
48.1207 - clearTimeout(evaluationTimeoutInstance);
48.1208 - evaluationTimeoutInstance = setTimeout(evaluateImmediate, throttleEvaluationTimeout);
48.1209 - } else
48.1210 - evaluateImmediate();
48.1211 - }
48.1212 -
48.1213 - function evaluateImmediate() {
48.1214 - if (_isBeingEvaluated) {
48.1215 - // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
48.1216 - // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
48.1217 - // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
48.1218 - // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
48.1219 - return;
48.1220 - }
48.1221 -
48.1222 - // Don't dispose on first evaluation, because the "disposeWhen" callback might
48.1223 - // e.g., dispose when the associated DOM element isn't in the doc, and it's not
48.1224 - // going to be in the doc until *after* the first evaluation
48.1225 - if (_hasBeenEvaluated && disposeWhen()) {
48.1226 - dispose();
48.1227 - return;
48.1228 - }
48.1229 -
48.1230 - _isBeingEvaluated = true;
48.1231 - try {
48.1232 - // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
48.1233 - // Then, during evaluation, we cross off any that are in fact still being used.
48.1234 - var disposalCandidates = ko.utils.arrayMap(_subscriptionsToDependencies, function(item) {return item.target;});
48.1235 -
48.1236 - ko.dependencyDetection.begin(function(subscribable) {
48.1237 - var inOld;
48.1238 - if ((inOld = ko.utils.arrayIndexOf(disposalCandidates, subscribable)) >= 0)
48.1239 - disposalCandidates[inOld] = undefined; // Don't want to dispose this subscription, as it's still being used
48.1240 - else
48.1241 - addSubscriptionToDependency(subscribable); // Brand new subscription - add it
48.1242 - });
48.1243 -
48.1244 - var newValue = readFunction.call(evaluatorFunctionTarget);
48.1245 -
48.1246 - // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
48.1247 - for (var i = disposalCandidates.length - 1; i >= 0; i--) {
48.1248 - if (disposalCandidates[i])
48.1249 - _subscriptionsToDependencies.splice(i, 1)[0].dispose();
48.1250 - }
48.1251 - _hasBeenEvaluated = true;
48.1252 -
48.1253 - dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
48.1254 - _latestValue = newValue;
48.1255 - if (DEBUG) dependentObservable._latestValue = _latestValue;
48.1256 - } finally {
48.1257 - ko.dependencyDetection.end();
48.1258 - }
48.1259 -
48.1260 - dependentObservable["notifySubscribers"](_latestValue);
48.1261 - _isBeingEvaluated = false;
48.1262 - if (!_subscriptionsToDependencies.length)
48.1263 - dispose();
48.1264 - }
48.1265 -
48.1266 - function dependentObservable() {
48.1267 - if (arguments.length > 0) {
48.1268 - if (typeof writeFunction === "function") {
48.1269 - // Writing a value
48.1270 - writeFunction.apply(evaluatorFunctionTarget, arguments);
48.1271 - } else {
48.1272 - throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
48.1273 - }
48.1274 - return this; // Permits chained assignments
48.1275 - } else {
48.1276 - // Reading the value
48.1277 - if (!_hasBeenEvaluated)
48.1278 - evaluateImmediate();
48.1279 - ko.dependencyDetection.registerDependency(dependentObservable);
48.1280 - return _latestValue;
48.1281 - }
48.1282 - }
48.1283 -
48.1284 - function peek() {
48.1285 - if (!_hasBeenEvaluated)
48.1286 - evaluateImmediate();
48.1287 - return _latestValue;
48.1288 - }
48.1289 -
48.1290 - function isActive() {
48.1291 - return !_hasBeenEvaluated || _subscriptionsToDependencies.length > 0;
48.1292 - }
48.1293 -
48.1294 - // By here, "options" is always non-null
48.1295 - var writeFunction = options["write"],
48.1296 - disposeWhenNodeIsRemoved = options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
48.1297 - disposeWhen = options["disposeWhen"] || options.disposeWhen || function() { return false; },
48.1298 - dispose = disposeAllSubscriptionsToDependencies,
48.1299 - _subscriptionsToDependencies = [],
48.1300 - evaluationTimeoutInstance = null;
48.1301 -
48.1302 - if (!evaluatorFunctionTarget)
48.1303 - evaluatorFunctionTarget = options["owner"];
48.1304 -
48.1305 - dependentObservable.peek = peek;
48.1306 - dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; };
48.1307 - dependentObservable.hasWriteFunction = typeof options["write"] === "function";
48.1308 - dependentObservable.dispose = function () { dispose(); };
48.1309 - dependentObservable.isActive = isActive;
48.1310 - dependentObservable.valueHasMutated = function() {
48.1311 - _hasBeenEvaluated = false;
48.1312 - evaluateImmediate();
48.1313 - };
48.1314 -
48.1315 - ko.subscribable.call(dependentObservable);
48.1316 - ko.utils.extend(dependentObservable, ko.dependentObservable['fn']);
48.1317 -
48.1318 - ko.exportProperty(dependentObservable, 'peek', dependentObservable.peek);
48.1319 - ko.exportProperty(dependentObservable, 'dispose', dependentObservable.dispose);
48.1320 - ko.exportProperty(dependentObservable, 'isActive', dependentObservable.isActive);
48.1321 - ko.exportProperty(dependentObservable, 'getDependenciesCount', dependentObservable.getDependenciesCount);
48.1322 -
48.1323 - // Evaluate, unless deferEvaluation is true
48.1324 - if (options['deferEvaluation'] !== true)
48.1325 - evaluateImmediate();
48.1326 -
48.1327 - // Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values.
48.1328 - // But skip if isActive is false (there will never be any dependencies to dispose).
48.1329 - // (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
48.1330 - // plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
48.1331 - if (disposeWhenNodeIsRemoved && isActive()) {
48.1332 - dispose = function() {
48.1333 - ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, arguments.callee);
48.1334 - disposeAllSubscriptionsToDependencies();
48.1335 - };
48.1336 - ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, dispose);
48.1337 - var existingDisposeWhenFunction = disposeWhen;
48.1338 - disposeWhen = function () {
48.1339 - return !ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) || existingDisposeWhenFunction();
48.1340 - }
48.1341 - }
48.1342 -
48.1343 - return dependentObservable;
48.1344 -};
48.1345 -
48.1346 -ko.isComputed = function(instance) {
48.1347 - return ko.hasPrototype(instance, ko.dependentObservable);
48.1348 -};
48.1349 -
48.1350 -var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
48.1351 -ko.dependentObservable[protoProp] = ko.observable;
48.1352 -
48.1353 -ko.dependentObservable['fn'] = {};
48.1354 -ko.dependentObservable['fn'][protoProp] = ko.dependentObservable;
48.1355 -
48.1356 -ko.exportSymbol('dependentObservable', ko.dependentObservable);
48.1357 -ko.exportSymbol('computed', ko.dependentObservable); // Make "ko.computed" an alias for "ko.dependentObservable"
48.1358 -ko.exportSymbol('isComputed', ko.isComputed);
48.1359 -
48.1360 -(function() {
48.1361 - var maxNestedObservableDepth = 10; // Escape the (unlikely) pathalogical case where an observable's current value is itself (or similar reference cycle)
48.1362 -
48.1363 - ko.toJS = function(rootObject) {
48.1364 - if (arguments.length == 0)
48.1365 - throw new Error("When calling ko.toJS, pass the object you want to convert.");
48.1366 -
48.1367 - // We just unwrap everything at every level in the object graph
48.1368 - return mapJsObjectGraph(rootObject, function(valueToMap) {
48.1369 - // Loop because an observable's value might in turn be another observable wrapper
48.1370 - for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
48.1371 - valueToMap = valueToMap();
48.1372 - return valueToMap;
48.1373 - });
48.1374 - };
48.1375 -
48.1376 - ko.toJSON = function(rootObject, replacer, space) { // replacer and space are optional
48.1377 - var plainJavaScriptObject = ko.toJS(rootObject);
48.1378 - return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
48.1379 - };
48.1380 -
48.1381 - function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
48.1382 - visitedObjects = visitedObjects || new objectLookup();
48.1383 -
48.1384 - rootObject = mapInputCallback(rootObject);
48.1385 - var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof Date));
48.1386 - if (!canHaveProperties)
48.1387 - return rootObject;
48.1388 -
48.1389 - var outputProperties = rootObject instanceof Array ? [] : {};
48.1390 - visitedObjects.save(rootObject, outputProperties);
48.1391 -
48.1392 - visitPropertiesOrArrayEntries(rootObject, function(indexer) {
48.1393 - var propertyValue = mapInputCallback(rootObject[indexer]);
48.1394 -
48.1395 - switch (typeof propertyValue) {
48.1396 - case "boolean":
48.1397 - case "number":
48.1398 - case "string":
48.1399 - case "function":
48.1400 - outputProperties[indexer] = propertyValue;
48.1401 - break;
48.1402 - case "object":
48.1403 - case "undefined":
48.1404 - var previouslyMappedValue = visitedObjects.get(propertyValue);
48.1405 - outputProperties[indexer] = (previouslyMappedValue !== undefined)
48.1406 - ? previouslyMappedValue
48.1407 - : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
48.1408 - break;
48.1409 - }
48.1410 - });
48.1411 -
48.1412 - return outputProperties;
48.1413 - }
48.1414 -
48.1415 - function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
48.1416 - if (rootObject instanceof Array) {
48.1417 - for (var i = 0; i < rootObject.length; i++)
48.1418 - visitorCallback(i);
48.1419 -
48.1420 - // For arrays, also respect toJSON property for custom mappings (fixes #278)
48.1421 - if (typeof rootObject['toJSON'] == 'function')
48.1422 - visitorCallback('toJSON');
48.1423 - } else {
48.1424 - for (var propertyName in rootObject)
48.1425 - visitorCallback(propertyName);
48.1426 - }
48.1427 - };
48.1428 -
48.1429 - function objectLookup() {
48.1430 - var keys = [];
48.1431 - var values = [];
48.1432 - this.save = function(key, value) {
48.1433 - var existingIndex = ko.utils.arrayIndexOf(keys, key);
48.1434 - if (existingIndex >= 0)
48.1435 - values[existingIndex] = value;
48.1436 - else {
48.1437 - keys.push(key);
48.1438 - values.push(value);
48.1439 - }
48.1440 - };
48.1441 - this.get = function(key) {
48.1442 - var existingIndex = ko.utils.arrayIndexOf(keys, key);
48.1443 - return (existingIndex >= 0) ? values[existingIndex] : undefined;
48.1444 - };
48.1445 - };
48.1446 -})();
48.1447 -
48.1448 -ko.exportSymbol('toJS', ko.toJS);
48.1449 -ko.exportSymbol('toJSON', ko.toJSON);
48.1450 -(function () {
48.1451 - var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';
48.1452 -
48.1453 - // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
48.1454 - // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
48.1455 - // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
48.1456 - ko.selectExtensions = {
48.1457 - readValue : function(element) {
48.1458 - switch (ko.utils.tagNameLower(element)) {
48.1459 - case 'option':
48.1460 - if (element[hasDomDataExpandoProperty] === true)
48.1461 - return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
48.1462 - return ko.utils.ieVersion <= 7
48.1463 - ? (element.getAttributeNode('value').specified ? element.value : element.text)
48.1464 - : element.value;
48.1465 - case 'select':
48.1466 - return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
48.1467 - default:
48.1468 - return element.value;
48.1469 - }
48.1470 - },
48.1471 -
48.1472 - writeValue: function(element, value) {
48.1473 - switch (ko.utils.tagNameLower(element)) {
48.1474 - case 'option':
48.1475 - switch(typeof value) {
48.1476 - case "string":
48.1477 - ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
48.1478 - if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
48.1479 - delete element[hasDomDataExpandoProperty];
48.1480 - }
48.1481 - element.value = value;
48.1482 - break;
48.1483 - default:
48.1484 - // Store arbitrary object using DomData
48.1485 - ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
48.1486 - element[hasDomDataExpandoProperty] = true;
48.1487 -
48.1488 - // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
48.1489 - element.value = typeof value === "number" ? value : "";
48.1490 - break;
48.1491 - }
48.1492 - break;
48.1493 - case 'select':
48.1494 - for (var i = element.options.length - 1; i >= 0; i--) {
48.1495 - if (ko.selectExtensions.readValue(element.options[i]) == value) {
48.1496 - element.selectedIndex = i;
48.1497 - break;
48.1498 - }
48.1499 - }
48.1500 - break;
48.1501 - default:
48.1502 - if ((value === null) || (value === undefined))
48.1503 - value = "";
48.1504 - element.value = value;
48.1505 - break;
48.1506 - }
48.1507 - }
48.1508 - };
48.1509 -})();
48.1510 -
48.1511 -ko.exportSymbol('selectExtensions', ko.selectExtensions);
48.1512 -ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
48.1513 -ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
48.1514 -ko.expressionRewriting = (function () {
48.1515 - var restoreCapturedTokensRegex = /\@ko_token_(\d+)\@/g;
48.1516 - var javaScriptReservedWords = ["true", "false"];
48.1517 -
48.1518 - // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
48.1519 - // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
48.1520 - var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;
48.1521 -
48.1522 - function restoreTokens(string, tokens) {
48.1523 - var prevValue = null;
48.1524 - while (string != prevValue) { // Keep restoring tokens until it no longer makes a difference (they may be nested)
48.1525 - prevValue = string;
48.1526 - string = string.replace(restoreCapturedTokensRegex, function (match, tokenIndex) {
48.1527 - return tokens[tokenIndex];
48.1528 - });
48.1529 - }
48.1530 - return string;
48.1531 - }
48.1532 -
48.1533 - function getWriteableValue(expression) {
48.1534 - if (ko.utils.arrayIndexOf(javaScriptReservedWords, ko.utils.stringTrim(expression).toLowerCase()) >= 0)
48.1535 - return false;
48.1536 - var match = expression.match(javaScriptAssignmentTarget);
48.1537 - return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
48.1538 - }
48.1539 -
48.1540 - function ensureQuoted(key) {
48.1541 - var trimmedKey = ko.utils.stringTrim(key);
48.1542 - switch (trimmedKey.length && trimmedKey.charAt(0)) {
48.1543 - case "'":
48.1544 - case '"':
48.1545 - return key;
48.1546 - default:
48.1547 - return "'" + trimmedKey + "'";
48.1548 - }
48.1549 - }
48.1550 -
48.1551 - return {
48.1552 - bindingRewriteValidators: [],
48.1553 -
48.1554 - parseObjectLiteral: function(objectLiteralString) {
48.1555 - // A full tokeniser+lexer would add too much weight to this library, so here's a simple parser
48.1556 - // that is sufficient just to split an object literal string into a set of top-level key-value pairs
48.1557 -
48.1558 - var str = ko.utils.stringTrim(objectLiteralString);
48.1559 - if (str.length < 3)
48.1560 - return [];
48.1561 - if (str.charAt(0) === "{")// Ignore any braces surrounding the whole object literal
48.1562 - str = str.substring(1, str.length - 1);
48.1563 -
48.1564 - // Pull out any string literals and regex literals
48.1565 - var tokens = [];
48.1566 - var tokenStart = null, tokenEndChar;
48.1567 - for (var position = 0; position < str.length; position++) {
48.1568 - var c = str.charAt(position);
48.1569 - if (tokenStart === null) {
48.1570 - switch (c) {
48.1571 - case '"':
48.1572 - case "'":
48.1573 - case "/":
48.1574 - tokenStart = position;
48.1575 - tokenEndChar = c;
48.1576 - break;
48.1577 - }
48.1578 - } else if ((c == tokenEndChar) && (str.charAt(position - 1) !== "\\")) {
48.1579 - var token = str.substring(tokenStart, position + 1);
48.1580 - tokens.push(token);
48.1581 - var replacement = "@ko_token_" + (tokens.length - 1) + "@";
48.1582 - str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
48.1583 - position -= (token.length - replacement.length);
48.1584 - tokenStart = null;
48.1585 - }
48.1586 - }
48.1587 -
48.1588 - // Next pull out balanced paren, brace, and bracket blocks
48.1589 - tokenStart = null;
48.1590 - tokenEndChar = null;
48.1591 - var tokenDepth = 0, tokenStartChar = null;
48.1592 - for (var position = 0; position < str.length; position++) {
48.1593 - var c = str.charAt(position);
48.1594 - if (tokenStart === null) {
48.1595 - switch (c) {
48.1596 - case "{": tokenStart = position; tokenStartChar = c;
48.1597 - tokenEndChar = "}";
48.1598 - break;
48.1599 - case "(": tokenStart = position; tokenStartChar = c;
48.1600 - tokenEndChar = ")";
48.1601 - break;
48.1602 - case "[": tokenStart = position; tokenStartChar = c;
48.1603 - tokenEndChar = "]";
48.1604 - break;
48.1605 - }
48.1606 - }
48.1607 -
48.1608 - if (c === tokenStartChar)
48.1609 - tokenDepth++;
48.1610 - else if (c === tokenEndChar) {
48.1611 - tokenDepth--;
48.1612 - if (tokenDepth === 0) {
48.1613 - var token = str.substring(tokenStart, position + 1);
48.1614 - tokens.push(token);
48.1615 - var replacement = "@ko_token_" + (tokens.length - 1) + "@";
48.1616 - str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
48.1617 - position -= (token.length - replacement.length);
48.1618 - tokenStart = null;
48.1619 - }
48.1620 - }
48.1621 - }
48.1622 -
48.1623 - // Now we can safely split on commas to get the key/value pairs
48.1624 - var result = [];
48.1625 - var keyValuePairs = str.split(",");
48.1626 - for (var i = 0, j = keyValuePairs.length; i < j; i++) {
48.1627 - var pair = keyValuePairs[i];
48.1628 - var colonPos = pair.indexOf(":");
48.1629 - if ((colonPos > 0) && (colonPos < pair.length - 1)) {
48.1630 - var key = pair.substring(0, colonPos);
48.1631 - var value = pair.substring(colonPos + 1);
48.1632 - result.push({ 'key': restoreTokens(key, tokens), 'value': restoreTokens(value, tokens) });
48.1633 - } else {
48.1634 - result.push({ 'unknown': restoreTokens(pair, tokens) });
48.1635 - }
48.1636 - }
48.1637 - return result;
48.1638 - },
48.1639 -
48.1640 - preProcessBindings: function (objectLiteralStringOrKeyValueArray) {
48.1641 - var keyValueArray = typeof objectLiteralStringOrKeyValueArray === "string"
48.1642 - ? ko.expressionRewriting.parseObjectLiteral(objectLiteralStringOrKeyValueArray)
48.1643 - : objectLiteralStringOrKeyValueArray;
48.1644 - var resultStrings = [], propertyAccessorResultStrings = [];
48.1645 -
48.1646 - var keyValueEntry;
48.1647 - for (var i = 0; keyValueEntry = keyValueArray[i]; i++) {
48.1648 - if (resultStrings.length > 0)
48.1649 - resultStrings.push(",");
48.1650 -
48.1651 - if (keyValueEntry['key']) {
48.1652 - var quotedKey = ensureQuoted(keyValueEntry['key']), val = keyValueEntry['value'];
48.1653 - resultStrings.push(quotedKey);
48.1654 - resultStrings.push(":");
48.1655 - resultStrings.push(val);
48.1656 -
48.1657 - if (val = getWriteableValue(ko.utils.stringTrim(val))) {
48.1658 - if (propertyAccessorResultStrings.length > 0)
48.1659 - propertyAccessorResultStrings.push(", ");
48.1660 - propertyAccessorResultStrings.push(quotedKey + " : function(__ko_value) { " + val + " = __ko_value; }");
48.1661 - }
48.1662 - } else if (keyValueEntry['unknown']) {
48.1663 - resultStrings.push(keyValueEntry['unknown']);
48.1664 - }
48.1665 - }
48.1666 -
48.1667 - var combinedResult = resultStrings.join("");
48.1668 - if (propertyAccessorResultStrings.length > 0) {
48.1669 - var allPropertyAccessors = propertyAccessorResultStrings.join("");
48.1670 - combinedResult = combinedResult + ", '_ko_property_writers' : { " + allPropertyAccessors + " } ";
48.1671 - }
48.1672 -
48.1673 - return combinedResult;
48.1674 - },
48.1675 -
48.1676 - keyValueArrayContainsKey: function(keyValueArray, key) {
48.1677 - for (var i = 0; i < keyValueArray.length; i++)
48.1678 - if (ko.utils.stringTrim(keyValueArray[i]['key']) == key)
48.1679 - return true;
48.1680 - return false;
48.1681 - },
48.1682 -
48.1683 - // Internal, private KO utility for updating model properties from within bindings
48.1684 - // property: If the property being updated is (or might be) an observable, pass it here
48.1685 - // If it turns out to be a writable observable, it will be written to directly
48.1686 - // allBindingsAccessor: All bindings in the current execution context.
48.1687 - // This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
48.1688 - // key: The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
48.1689 - // value: The value to be written
48.1690 - // checkIfDifferent: If true, and if the property being written is a writable observable, the value will only be written if
48.1691 - // it is !== existing value on that writable observable
48.1692 - writeValueToProperty: function(property, allBindingsAccessor, key, value, checkIfDifferent) {
48.1693 - if (!property || !ko.isWriteableObservable(property)) {
48.1694 - var propWriters = allBindingsAccessor()['_ko_property_writers'];
48.1695 - if (propWriters && propWriters[key])
48.1696 - propWriters[key](value);
48.1697 - } else if (!checkIfDifferent || property.peek() !== value) {
48.1698 - property(value);
48.1699 - }
48.1700 - }
48.1701 - };
48.1702 -})();
48.1703 -
48.1704 -ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
48.1705 -ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
48.1706 -ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
48.1707 -ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);
48.1708 -
48.1709 -// For backward compatibility, define the following aliases. (Previously, these function names were misleading because
48.1710 -// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
48.1711 -ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
48.1712 -ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);(function() {
48.1713 - // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
48.1714 - // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
48.1715 - // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
48.1716 - // of that virtual hierarchy
48.1717 - //
48.1718 - // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
48.1719 - // without having to scatter special cases all over the binding and templating code.
48.1720 -
48.1721 - // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
48.1722 - // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
48.1723 - // So, use node.text where available, and node.nodeValue elsewhere
48.1724 - var commentNodesHaveTextProperty = document.createComment("test").text === "<!--test-->";
48.1725 -
48.1726 - var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*-->$/ : /^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/;
48.1727 - var endCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
48.1728 - var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
48.1729 -
48.1730 - function isStartComment(node) {
48.1731 - return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
48.1732 - }
48.1733 -
48.1734 - function isEndComment(node) {
48.1735 - return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(endCommentRegex);
48.1736 - }
48.1737 -
48.1738 - function getVirtualChildren(startComment, allowUnbalanced) {
48.1739 - var currentNode = startComment;
48.1740 - var depth = 1;
48.1741 - var children = [];
48.1742 - while (currentNode = currentNode.nextSibling) {
48.1743 - if (isEndComment(currentNode)) {
48.1744 - depth--;
48.1745 - if (depth === 0)
48.1746 - return children;
48.1747 - }
48.1748 -
48.1749 - children.push(currentNode);
48.1750 -
48.1751 - if (isStartComment(currentNode))
48.1752 - depth++;
48.1753 - }
48.1754 - if (!allowUnbalanced)
48.1755 - throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
48.1756 - return null;
48.1757 - }
48.1758 -
48.1759 - function getMatchingEndComment(startComment, allowUnbalanced) {
48.1760 - var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
48.1761 - if (allVirtualChildren) {
48.1762 - if (allVirtualChildren.length > 0)
48.1763 - return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
48.1764 - return startComment.nextSibling;
48.1765 - } else
48.1766 - return null; // Must have no matching end comment, and allowUnbalanced is true
48.1767 - }
48.1768 -
48.1769 - function getUnbalancedChildTags(node) {
48.1770 - // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
48.1771 - // from <div>OK</div><!-- /ko --><!-- /ko -->, returns: <!-- /ko --><!-- /ko -->
48.1772 - var childNode = node.firstChild, captureRemaining = null;
48.1773 - if (childNode) {
48.1774 - do {
48.1775 - if (captureRemaining) // We already hit an unbalanced node and are now just scooping up all subsequent nodes
48.1776 - captureRemaining.push(childNode);
48.1777 - else if (isStartComment(childNode)) {
48.1778 - var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
48.1779 - if (matchingEndComment) // It's a balanced tag, so skip immediately to the end of this virtual set
48.1780 - childNode = matchingEndComment;
48.1781 - else
48.1782 - captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
48.1783 - } else if (isEndComment(childNode)) {
48.1784 - captureRemaining = [childNode]; // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
48.1785 - }
48.1786 - } while (childNode = childNode.nextSibling);
48.1787 - }
48.1788 - return captureRemaining;
48.1789 - }
48.1790 -
48.1791 - ko.virtualElements = {
48.1792 - allowedBindings: {},
48.1793 -
48.1794 - childNodes: function(node) {
48.1795 - return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
48.1796 - },
48.1797 -
48.1798 - emptyNode: function(node) {
48.1799 - if (!isStartComment(node))
48.1800 - ko.utils.emptyDomNode(node);
48.1801 - else {
48.1802 - var virtualChildren = ko.virtualElements.childNodes(node);
48.1803 - for (var i = 0, j = virtualChildren.length; i < j; i++)
48.1804 - ko.removeNode(virtualChildren[i]);
48.1805 - }
48.1806 - },
48.1807 -
48.1808 - setDomNodeChildren: function(node, childNodes) {
48.1809 - if (!isStartComment(node))
48.1810 - ko.utils.setDomNodeChildren(node, childNodes);
48.1811 - else {
48.1812 - ko.virtualElements.emptyNode(node);
48.1813 - var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
48.1814 - for (var i = 0, j = childNodes.length; i < j; i++)
48.1815 - endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
48.1816 - }
48.1817 - },
48.1818 -
48.1819 - prepend: function(containerNode, nodeToPrepend) {
48.1820 - if (!isStartComment(containerNode)) {
48.1821 - if (containerNode.firstChild)
48.1822 - containerNode.insertBefore(nodeToPrepend, containerNode.firstChild);
48.1823 - else
48.1824 - containerNode.appendChild(nodeToPrepend);
48.1825 - } else {
48.1826 - // Start comments must always have a parent and at least one following sibling (the end comment)
48.1827 - containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
48.1828 - }
48.1829 - },
48.1830 -
48.1831 - insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
48.1832 - if (!insertAfterNode) {
48.1833 - ko.virtualElements.prepend(containerNode, nodeToInsert);
48.1834 - } else if (!isStartComment(containerNode)) {
48.1835 - // Insert after insertion point
48.1836 - if (insertAfterNode.nextSibling)
48.1837 - containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
48.1838 - else
48.1839 - containerNode.appendChild(nodeToInsert);
48.1840 - } else {
48.1841 - // Children of start comments must always have a parent and at least one following sibling (the end comment)
48.1842 - containerNode.parentNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
48.1843 - }
48.1844 - },
48.1845 -
48.1846 - firstChild: function(node) {
48.1847 - if (!isStartComment(node))
48.1848 - return node.firstChild;
48.1849 - if (!node.nextSibling || isEndComment(node.nextSibling))
48.1850 - return null;
48.1851 - return node.nextSibling;
48.1852 - },
48.1853 -
48.1854 - nextSibling: function(node) {
48.1855 - if (isStartComment(node))
48.1856 - node = getMatchingEndComment(node);
48.1857 - if (node.nextSibling && isEndComment(node.nextSibling))
48.1858 - return null;
48.1859 - return node.nextSibling;
48.1860 - },
48.1861 -
48.1862 - virtualNodeBindingValue: function(node) {
48.1863 - var regexMatch = isStartComment(node);
48.1864 - return regexMatch ? regexMatch[1] : null;
48.1865 - },
48.1866 -
48.1867 - normaliseVirtualElementDomStructure: function(elementVerified) {
48.1868 - // Workaround for https://github.com/SteveSanderson/knockout/issues/155
48.1869 - // (IE <= 8 or IE 9 quirks mode parses your HTML weirdly, treating closing </li> tags as if they don't exist, thereby moving comment nodes
48.1870 - // that are direct descendants of <ul> into the preceding <li>)
48.1871 - if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
48.1872 - return;
48.1873 -
48.1874 - // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
48.1875 - // must be intended to appear *after* that child, so move them there.
48.1876 - var childNode = elementVerified.firstChild;
48.1877 - if (childNode) {
48.1878 - do {
48.1879 - if (childNode.nodeType === 1) {
48.1880 - var unbalancedTags = getUnbalancedChildTags(childNode);
48.1881 - if (unbalancedTags) {
48.1882 - // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
48.1883 - var nodeToInsertBefore = childNode.nextSibling;
48.1884 - for (var i = 0; i < unbalancedTags.length; i++) {
48.1885 - if (nodeToInsertBefore)
48.1886 - elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
48.1887 - else
48.1888 - elementVerified.appendChild(unbalancedTags[i]);
48.1889 - }
48.1890 - }
48.1891 - }
48.1892 - } while (childNode = childNode.nextSibling);
48.1893 - }
48.1894 - }
48.1895 - };
48.1896 -})();
48.1897 -ko.exportSymbol('virtualElements', ko.virtualElements);
48.1898 -ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
48.1899 -ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
48.1900 -//ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild); // firstChild is not minified
48.1901 -ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
48.1902 -//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling); // nextSibling is not minified
48.1903 -ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
48.1904 -ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
48.1905 -(function() {
48.1906 - var defaultBindingAttributeName = "data-bind";
48.1907 -
48.1908 - ko.bindingProvider = function() {
48.1909 - this.bindingCache = {};
48.1910 - };
48.1911 -
48.1912 - ko.utils.extend(ko.bindingProvider.prototype, {
48.1913 - 'nodeHasBindings': function(node) {
48.1914 - switch (node.nodeType) {
48.1915 - case 1: return node.getAttribute(defaultBindingAttributeName) != null; // Element
48.1916 - case 8: return ko.virtualElements.virtualNodeBindingValue(node) != null; // Comment node
48.1917 - default: return false;
48.1918 - }
48.1919 - },
48.1920 -
48.1921 - 'getBindings': function(node, bindingContext) {
48.1922 - var bindingsString = this['getBindingsString'](node, bindingContext);
48.1923 - return bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
48.1924 - },
48.1925 -
48.1926 - // The following function is only used internally by this default provider.
48.1927 - // It's not part of the interface definition for a general binding provider.
48.1928 - 'getBindingsString': function(node, bindingContext) {
48.1929 - switch (node.nodeType) {
48.1930 - case 1: return node.getAttribute(defaultBindingAttributeName); // Element
48.1931 - case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
48.1932 - default: return null;
48.1933 - }
48.1934 - },
48.1935 -
48.1936 - // The following function is only used internally by this default provider.
48.1937 - // It's not part of the interface definition for a general binding provider.
48.1938 - 'parseBindingsString': function(bindingsString, bindingContext, node) {
48.1939 - try {
48.1940 - var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache);
48.1941 - return bindingFunction(bindingContext, node);
48.1942 - } catch (ex) {
48.1943 - throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
48.1944 - }
48.1945 - }
48.1946 - });
48.1947 -
48.1948 - ko.bindingProvider['instance'] = new ko.bindingProvider();
48.1949 -
48.1950 - function createBindingsStringEvaluatorViaCache(bindingsString, cache) {
48.1951 - var cacheKey = bindingsString;
48.1952 - return cache[cacheKey]
48.1953 - || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString));
48.1954 - }
48.1955 -
48.1956 - function createBindingsStringEvaluator(bindingsString) {
48.1957 - // Build the source for a function that evaluates "expression"
48.1958 - // For each scope variable, add an extra level of "with" nesting
48.1959 - // Example result: with(sc1) { with(sc0) { return (expression) } }
48.1960 - var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString),
48.1961 - functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
48.1962 - return new Function("$context", "$element", functionBody);
48.1963 - }
48.1964 -})();
48.1965 -
48.1966 -ko.exportSymbol('bindingProvider', ko.bindingProvider);
48.1967 -(function () {
48.1968 - ko.bindingHandlers = {};
48.1969 -
48.1970 - ko.bindingContext = function(dataItem, parentBindingContext, dataItemAlias) {
48.1971 - if (parentBindingContext) {
48.1972 - ko.utils.extend(this, parentBindingContext); // Inherit $root and any custom properties
48.1973 - this['$parentContext'] = parentBindingContext;
48.1974 - this['$parent'] = parentBindingContext['$data'];
48.1975 - this['$parents'] = (parentBindingContext['$parents'] || []).slice(0);
48.1976 - this['$parents'].unshift(this['$parent']);
48.1977 - } else {
48.1978 - this['$parents'] = [];
48.1979 - this['$root'] = dataItem;
48.1980 - // Export 'ko' in the binding context so it will be available in bindings and templates
48.1981 - // even if 'ko' isn't exported as a global, such as when using an AMD loader.
48.1982 - // See https://github.com/SteveSanderson/knockout/issues/490
48.1983 - this['ko'] = ko;
48.1984 - }
48.1985 - this['$data'] = dataItem;
48.1986 - if (dataItemAlias)
48.1987 - this[dataItemAlias] = dataItem;
48.1988 - }
48.1989 - ko.bindingContext.prototype['createChildContext'] = function (dataItem, dataItemAlias) {
48.1990 - return new ko.bindingContext(dataItem, this, dataItemAlias);
48.1991 - };
48.1992 - ko.bindingContext.prototype['extend'] = function(properties) {
48.1993 - var clone = ko.utils.extend(new ko.bindingContext(), this);
48.1994 - return ko.utils.extend(clone, properties);
48.1995 - };
48.1996 -
48.1997 - function validateThatBindingIsAllowedForVirtualElements(bindingName) {
48.1998 - var validator = ko.virtualElements.allowedBindings[bindingName];
48.1999 - if (!validator)
48.2000 - throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
48.2001 - }
48.2002 -
48.2003 - function applyBindingsToDescendantsInternal (viewModel, elementOrVirtualElement, bindingContextsMayDifferFromDomParentElement) {
48.2004 - var currentChild, nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
48.2005 - while (currentChild = nextInQueue) {
48.2006 - // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
48.2007 - nextInQueue = ko.virtualElements.nextSibling(currentChild);
48.2008 - applyBindingsToNodeAndDescendantsInternal(viewModel, currentChild, bindingContextsMayDifferFromDomParentElement);
48.2009 - }
48.2010 - }
48.2011 -
48.2012 - function applyBindingsToNodeAndDescendantsInternal (viewModel, nodeVerified, bindingContextMayDifferFromDomParentElement) {
48.2013 - var shouldBindDescendants = true;
48.2014 -
48.2015 - // Perf optimisation: Apply bindings only if...
48.2016 - // (1) We need to store the binding context on this node (because it may differ from the DOM parent node's binding context)
48.2017 - // Note that we can't store binding contexts on non-elements (e.g., text nodes), as IE doesn't allow expando properties for those
48.2018 - // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
48.2019 - var isElement = (nodeVerified.nodeType === 1);
48.2020 - if (isElement) // Workaround IE <= 8 HTML parsing weirdness
48.2021 - ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);
48.2022 -
48.2023 - var shouldApplyBindings = (isElement && bindingContextMayDifferFromDomParentElement) // Case (1)
48.2024 - || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified); // Case (2)
48.2025 - if (shouldApplyBindings)
48.2026 - shouldBindDescendants = applyBindingsToNodeInternal(nodeVerified, null, viewModel, bindingContextMayDifferFromDomParentElement).shouldBindDescendants;
48.2027 -
48.2028 - if (shouldBindDescendants) {
48.2029 - // We're recursing automatically into (real or virtual) child nodes without changing binding contexts. So,
48.2030 - // * For children of a *real* element, the binding context is certainly the same as on their DOM .parentNode,
48.2031 - // hence bindingContextsMayDifferFromDomParentElement is false
48.2032 - // * For children of a *virtual* element, we can't be sure. Evaluating .parentNode on those children may
48.2033 - // skip over any number of intermediate virtual elements, any of which might define a custom binding context,
48.2034 - // hence bindingContextsMayDifferFromDomParentElement is true
48.2035 - applyBindingsToDescendantsInternal(viewModel, nodeVerified, /* bindingContextsMayDifferFromDomParentElement: */ !isElement);
48.2036 - }
48.2037 - }
48.2038 -
48.2039 - function applyBindingsToNodeInternal (node, bindings, viewModelOrBindingContext, bindingContextMayDifferFromDomParentElement) {
48.2040 - // Need to be sure that inits are only run once, and updates never run until all the inits have been run
48.2041 - var initPhase = 0; // 0 = before all inits, 1 = during inits, 2 = after all inits
48.2042 -
48.2043 - // Each time the dependentObservable is evaluated (after data changes),
48.2044 - // the binding attribute is reparsed so that it can pick out the correct
48.2045 - // model properties in the context of the changed data.
48.2046 - // DOM event callbacks need to be able to access this changed data,
48.2047 - // so we need a single parsedBindings variable (shared by all callbacks
48.2048 - // associated with this node's bindings) that all the closures can access.
48.2049 - var parsedBindings;
48.2050 - function makeValueAccessor(bindingKey) {
48.2051 - return function () { return parsedBindings[bindingKey] }
48.2052 - }
48.2053 - function parsedBindingsAccessor() {
48.2054 - return parsedBindings;
48.2055 - }
48.2056 -
48.2057 - var bindingHandlerThatControlsDescendantBindings;
48.2058 - ko.dependentObservable(
48.2059 - function () {
48.2060 - // Ensure we have a nonnull binding context to work with
48.2061 - var bindingContextInstance = viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
48.2062 - ? viewModelOrBindingContext
48.2063 - : new ko.bindingContext(ko.utils.unwrapObservable(viewModelOrBindingContext));
48.2064 - var viewModel = bindingContextInstance['$data'];
48.2065 -
48.2066 - // Optimization: Don't store the binding context on this node if it's definitely the same as on node.parentNode, because
48.2067 - // we can easily recover it just by scanning up the node's ancestors in the DOM
48.2068 - // (note: here, parent node means "real DOM parent" not "virtual parent", as there's no O(1) way to find the virtual parent)
48.2069 - if (bindingContextMayDifferFromDomParentElement)
48.2070 - ko.storedBindingContextForNode(node, bindingContextInstance);
48.2071 -
48.2072 - // Use evaluatedBindings if given, otherwise fall back on asking the bindings provider to give us some bindings
48.2073 - var evaluatedBindings = (typeof bindings == "function") ? bindings(bindingContextInstance, node) : bindings;
48.2074 - parsedBindings = evaluatedBindings || ko.bindingProvider['instance']['getBindings'](node, bindingContextInstance);
48.2075 -
48.2076 - if (parsedBindings) {
48.2077 - // First run all the inits, so bindings can register for notification on changes
48.2078 - if (initPhase === 0) {
48.2079 - initPhase = 1;
48.2080 - for (var bindingKey in parsedBindings) {
48.2081 - var binding = ko.bindingHandlers[bindingKey];
48.2082 - if (binding && node.nodeType === 8)
48.2083 - validateThatBindingIsAllowedForVirtualElements(bindingKey);
48.2084 -
48.2085 - if (binding && typeof binding["init"] == "function") {
48.2086 - var handlerInitFn = binding["init"];
48.2087 - var initResult = handlerInitFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
48.2088 -
48.2089 - // If this binding handler claims to control descendant bindings, make a note of this
48.2090 - if (initResult && initResult['controlsDescendantBindings']) {
48.2091 - if (bindingHandlerThatControlsDescendantBindings !== undefined)
48.2092 - throw new Error("Multiple bindings (" + bindingHandlerThatControlsDescendantBindings + " and " + bindingKey + ") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
48.2093 - bindingHandlerThatControlsDescendantBindings = bindingKey;
48.2094 - }
48.2095 - }
48.2096 - }
48.2097 - initPhase = 2;
48.2098 - }
48.2099 -
48.2100 - // ... then run all the updates, which might trigger changes even on the first evaluation
48.2101 - if (initPhase === 2) {
48.2102 - for (var bindingKey in parsedBindings) {
48.2103 - var binding = ko.bindingHandlers[bindingKey];
48.2104 - if (binding && typeof binding["update"] == "function") {
48.2105 - var handlerUpdateFn = binding["update"];
48.2106 - handlerUpdateFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
48.2107 - }
48.2108 - }
48.2109 - }
48.2110 - }
48.2111 - },
48.2112 - null,
48.2113 - { disposeWhenNodeIsRemoved : node }
48.2114 - );
48.2115 -
48.2116 - return {
48.2117 - shouldBindDescendants: bindingHandlerThatControlsDescendantBindings === undefined
48.2118 - };
48.2119 - };
48.2120 -
48.2121 - var storedBindingContextDomDataKey = "__ko_bindingContext__";
48.2122 - ko.storedBindingContextForNode = function (node, bindingContext) {
48.2123 - if (arguments.length == 2)
48.2124 - ko.utils.domData.set(node, storedBindingContextDomDataKey, bindingContext);
48.2125 - else
48.2126 - return ko.utils.domData.get(node, storedBindingContextDomDataKey);
48.2127 - }
48.2128 -
48.2129 - ko.applyBindingsToNode = function (node, bindings, viewModel) {
48.2130 - if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
48.2131 - ko.virtualElements.normaliseVirtualElementDomStructure(node);
48.2132 - return applyBindingsToNodeInternal(node, bindings, viewModel, true);
48.2133 - };
48.2134 -
48.2135 - ko.applyBindingsToDescendants = function(viewModel, rootNode) {
48.2136 - if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
48.2137 - applyBindingsToDescendantsInternal(viewModel, rootNode, true);
48.2138 - };
48.2139 -
48.2140 - ko.applyBindings = function (viewModel, rootNode) {
48.2141 - if (rootNode && (rootNode.nodeType !== 1) && (rootNode.nodeType !== 8))
48.2142 - throw new Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
48.2143 - rootNode = rootNode || window.document.body; // Make "rootNode" parameter optional
48.2144 -
48.2145 - applyBindingsToNodeAndDescendantsInternal(viewModel, rootNode, true);
48.2146 - };
48.2147 -
48.2148 - // Retrieving binding context from arbitrary nodes
48.2149 - ko.contextFor = function(node) {
48.2150 - // We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)
48.2151 - switch (node.nodeType) {
48.2152 - case 1:
48.2153 - case 8:
48.2154 - var context = ko.storedBindingContextForNode(node);
48.2155 - if (context) return context;
48.2156 - if (node.parentNode) return ko.contextFor(node.parentNode);
48.2157 - break;
48.2158 - }
48.2159 - return undefined;
48.2160 - };
48.2161 - ko.dataFor = function(node) {
48.2162 - var context = ko.contextFor(node);
48.2163 - return context ? context['$data'] : undefined;
48.2164 - };
48.2165 -
48.2166 - ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
48.2167 - ko.exportSymbol('applyBindings', ko.applyBindings);
48.2168 - ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
48.2169 - ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
48.2170 - ko.exportSymbol('contextFor', ko.contextFor);
48.2171 - ko.exportSymbol('dataFor', ko.dataFor);
48.2172 -})();
48.2173 -var attrHtmlToJavascriptMap = { 'class': 'className', 'for': 'htmlFor' };
48.2174 -ko.bindingHandlers['attr'] = {
48.2175 - 'update': function(element, valueAccessor, allBindingsAccessor) {
48.2176 - var value = ko.utils.unwrapObservable(valueAccessor()) || {};
48.2177 - for (var attrName in value) {
48.2178 - if (typeof attrName == "string") {
48.2179 - var attrValue = ko.utils.unwrapObservable(value[attrName]);
48.2180 -
48.2181 - // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
48.2182 - // when someProp is a "no value"-like value (strictly null, false, or undefined)
48.2183 - // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
48.2184 - var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
48.2185 - if (toRemove)
48.2186 - element.removeAttribute(attrName);
48.2187 -
48.2188 - // In IE <= 7 and IE8 Quirks Mode, you have to use the Javascript property name instead of the
48.2189 - // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
48.2190 - // but instead of figuring out the mode, we'll just set the attribute through the Javascript
48.2191 - // property for IE <= 8.
48.2192 - if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavascriptMap) {
48.2193 - attrName = attrHtmlToJavascriptMap[attrName];
48.2194 - if (toRemove)
48.2195 - element.removeAttribute(attrName);
48.2196 - else
48.2197 - element[attrName] = attrValue;
48.2198 - } else if (!toRemove) {
48.2199 - try {
48.2200 - element.setAttribute(attrName, attrValue.toString());
48.2201 - } catch (err) {
48.2202 - // ignore for now
48.2203 - if (console) {
48.2204 - console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err);
48.2205 - }
48.2206 - }
48.2207 - }
48.2208 -
48.2209 - // Treat "name" specially - although you can think of it as an attribute, it also needs
48.2210 - // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
48.2211 - // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
48.2212 - // entirely, and there's no strong reason to allow for such casing in HTML.
48.2213 - if (attrName === "name") {
48.2214 - ko.utils.setElementName(element, toRemove ? "" : attrValue.toString());
48.2215 - }
48.2216 - }
48.2217 - }
48.2218 - }
48.2219 -};
48.2220 -ko.bindingHandlers['checked'] = {
48.2221 - 'init': function (element, valueAccessor, allBindingsAccessor) {
48.2222 - var updateHandler = function() {
48.2223 - var valueToWrite;
48.2224 - if (element.type == "checkbox") {
48.2225 - valueToWrite = element.checked;
48.2226 - } else if ((element.type == "radio") && (element.checked)) {
48.2227 - valueToWrite = element.value;
48.2228 - } else {
48.2229 - return; // "checked" binding only responds to checkboxes and selected radio buttons
48.2230 - }
48.2231 -
48.2232 - var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue);
48.2233 - if ((element.type == "checkbox") && (unwrappedValue instanceof Array)) {
48.2234 - // For checkboxes bound to an array, we add/remove the checkbox value to that array
48.2235 - // This works for both observable and non-observable arrays
48.2236 - var existingEntryIndex = ko.utils.arrayIndexOf(unwrappedValue, element.value);
48.2237 - if (element.checked && (existingEntryIndex < 0))
48.2238 - modelValue.push(element.value);
48.2239 - else if ((!element.checked) && (existingEntryIndex >= 0))
48.2240 - modelValue.splice(existingEntryIndex, 1);
48.2241 - } else {
48.2242 - ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
48.2243 - }
48.2244 - };
48.2245 - ko.utils.registerEventHandler(element, "click", updateHandler);
48.2246 -
48.2247 - // IE 6 won't allow radio buttons to be selected unless they have a name
48.2248 - if ((element.type == "radio") && !element.name)
48.2249 - ko.bindingHandlers['uniqueName']['init'](element, function() { return true });
48.2250 - },
48.2251 - 'update': function (element, valueAccessor) {
48.2252 - var value = ko.utils.unwrapObservable(valueAccessor());
48.2253 -
48.2254 - if (element.type == "checkbox") {
48.2255 - if (value instanceof Array) {
48.2256 - // When bound to an array, the checkbox being checked represents its value being present in that array
48.2257 - element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
48.2258 - } else {
48.2259 - // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
48.2260 - element.checked = value;
48.2261 - }
48.2262 - } else if (element.type == "radio") {
48.2263 - element.checked = (element.value == value);
48.2264 - }
48.2265 - }
48.2266 -};
48.2267 -var classesWrittenByBindingKey = '__ko__cssValue';
48.2268 -ko.bindingHandlers['css'] = {
48.2269 - 'update': function (element, valueAccessor) {
48.2270 - var value = ko.utils.unwrapObservable(valueAccessor());
48.2271 - if (typeof value == "object") {
48.2272 - for (var className in value) {
48.2273 - var shouldHaveClass = ko.utils.unwrapObservable(value[className]);
48.2274 - ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
48.2275 - }
48.2276 - } else {
48.2277 - value = String(value || ''); // Make sure we don't try to store or set a non-string value
48.2278 - ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
48.2279 - element[classesWrittenByBindingKey] = value;
48.2280 - ko.utils.toggleDomNodeCssClass(element, value, true);
48.2281 - }
48.2282 - }
48.2283 -};
48.2284 -ko.bindingHandlers['enable'] = {
48.2285 - 'update': function (element, valueAccessor) {
48.2286 - var value = ko.utils.unwrapObservable(valueAccessor());
48.2287 - if (value && element.disabled)
48.2288 - element.removeAttribute("disabled");
48.2289 - else if ((!value) && (!element.disabled))
48.2290 - element.disabled = true;
48.2291 - }
48.2292 -};
48.2293 -
48.2294 -ko.bindingHandlers['disable'] = {
48.2295 - 'update': function (element, valueAccessor) {
48.2296 - ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
48.2297 - }
48.2298 -};
48.2299 -// For certain common events (currently just 'click'), allow a simplified data-binding syntax
48.2300 -// e.g. click:handler instead of the usual full-length event:{click:handler}
48.2301 -function makeEventHandlerShortcut(eventName) {
48.2302 - ko.bindingHandlers[eventName] = {
48.2303 - 'init': function(element, valueAccessor, allBindingsAccessor, viewModel) {
48.2304 - var newValueAccessor = function () {
48.2305 - var result = {};
48.2306 - result[eventName] = valueAccessor();
48.2307 - return result;
48.2308 - };
48.2309 - return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindingsAccessor, viewModel);
48.2310 - }
48.2311 - }
48.2312 -}
48.2313 -
48.2314 -ko.bindingHandlers['event'] = {
48.2315 - 'init' : function (element, valueAccessor, allBindingsAccessor, viewModel) {
48.2316 - var eventsToHandle = valueAccessor() || {};
48.2317 - for(var eventNameOutsideClosure in eventsToHandle) {
48.2318 - (function() {
48.2319 - var eventName = eventNameOutsideClosure; // Separate variable to be captured by event handler closure
48.2320 - if (typeof eventName == "string") {
48.2321 - ko.utils.registerEventHandler(element, eventName, function (event) {
48.2322 - var handlerReturnValue;
48.2323 - var handlerFunction = valueAccessor()[eventName];
48.2324 - if (!handlerFunction)
48.2325 - return;
48.2326 - var allBindings = allBindingsAccessor();
48.2327 -
48.2328 - try {
48.2329 - // Take all the event args, and prefix with the viewmodel
48.2330 - var argsForHandler = ko.utils.makeArray(arguments);
48.2331 - argsForHandler.unshift(viewModel);
48.2332 - handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
48.2333 - } finally {
48.2334 - if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
48.2335 - if (event.preventDefault)
48.2336 - event.preventDefault();
48.2337 - else
48.2338 - event.returnValue = false;
48.2339 - }
48.2340 - }
48.2341 -
48.2342 - var bubble = allBindings[eventName + 'Bubble'] !== false;
48.2343 - if (!bubble) {
48.2344 - event.cancelBubble = true;
48.2345 - if (event.stopPropagation)
48.2346 - event.stopPropagation();
48.2347 - }
48.2348 - });
48.2349 - }
48.2350 - })();
48.2351 - }
48.2352 - }
48.2353 -};
48.2354 -// "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
48.2355 -// "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
48.2356 -ko.bindingHandlers['foreach'] = {
48.2357 - makeTemplateValueAccessor: function(valueAccessor) {
48.2358 - return function() {
48.2359 - var modelValue = valueAccessor(),
48.2360 - unwrappedValue = ko.utils.peekObservable(modelValue); // Unwrap without setting a dependency here
48.2361 -
48.2362 - // If unwrappedValue is the array, pass in the wrapped value on its own
48.2363 - // The value will be unwrapped and tracked within the template binding
48.2364 - // (See https://github.com/SteveSanderson/knockout/issues/523)
48.2365 - if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
48.2366 - return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };
48.2367 -
48.2368 - // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
48.2369 - ko.utils.unwrapObservable(modelValue);
48.2370 - return {
48.2371 - 'foreach': unwrappedValue['data'],
48.2372 - 'as': unwrappedValue['as'],
48.2373 - 'includeDestroyed': unwrappedValue['includeDestroyed'],
48.2374 - 'afterAdd': unwrappedValue['afterAdd'],
48.2375 - 'beforeRemove': unwrappedValue['beforeRemove'],
48.2376 - 'afterRender': unwrappedValue['afterRender'],
48.2377 - 'beforeMove': unwrappedValue['beforeMove'],
48.2378 - 'afterMove': unwrappedValue['afterMove'],
48.2379 - 'templateEngine': ko.nativeTemplateEngine.instance
48.2380 - };
48.2381 - };
48.2382 - },
48.2383 - 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
48.2384 - return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
48.2385 - },
48.2386 - 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
48.2387 - return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindingsAccessor, viewModel, bindingContext);
48.2388 - }
48.2389 -};
48.2390 -ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
48.2391 -ko.virtualElements.allowedBindings['foreach'] = true;
48.2392 -var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
48.2393 -ko.bindingHandlers['hasfocus'] = {
48.2394 - 'init': function(element, valueAccessor, allBindingsAccessor) {
48.2395 - var handleElementFocusChange = function(isFocused) {
48.2396 - // Where possible, ignore which event was raised and determine focus state using activeElement,
48.2397 - // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
48.2398 - // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
48.2399 - // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
48.2400 - // from calling 'blur()' on the element when it loses focus.
48.2401 - // Discussion at https://github.com/SteveSanderson/knockout/pull/352
48.2402 - element[hasfocusUpdatingProperty] = true;
48.2403 - var ownerDoc = element.ownerDocument;
48.2404 - if ("activeElement" in ownerDoc) {
48.2405 - isFocused = (ownerDoc.activeElement === element);
48.2406 - }
48.2407 - var modelValue = valueAccessor();
48.2408 - ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'hasfocus', isFocused, true);
48.2409 - element[hasfocusUpdatingProperty] = false;
48.2410 - };
48.2411 - var handleElementFocusIn = handleElementFocusChange.bind(null, true);
48.2412 - var handleElementFocusOut = handleElementFocusChange.bind(null, false);
48.2413 -
48.2414 - ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
48.2415 - ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
48.2416 - ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
48.2417 - ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
48.2418 - },
48.2419 - 'update': function(element, valueAccessor) {
48.2420 - var value = ko.utils.unwrapObservable(valueAccessor());
48.2421 - if (!element[hasfocusUpdatingProperty]) {
48.2422 - value ? element.focus() : element.blur();
48.2423 - ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]); // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
48.2424 - }
48.2425 - }
48.2426 -};
48.2427 -ko.bindingHandlers['html'] = {
48.2428 - 'init': function() {
48.2429 - // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
48.2430 - return { 'controlsDescendantBindings': true };
48.2431 - },
48.2432 - 'update': function (element, valueAccessor) {
48.2433 - // setHtml will unwrap the value if needed
48.2434 - ko.utils.setHtml(element, valueAccessor());
48.2435 - }
48.2436 -};
48.2437 -var withIfDomDataKey = '__ko_withIfBindingData';
48.2438 -// Makes a binding like with or if
48.2439 -function makeWithIfBinding(bindingKey, isWith, isNot, makeContextCallback) {
48.2440 - ko.bindingHandlers[bindingKey] = {
48.2441 - 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
48.2442 - ko.utils.domData.set(element, withIfDomDataKey, {});
48.2443 - return { 'controlsDescendantBindings': true };
48.2444 - },
48.2445 - 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
48.2446 - var withIfData = ko.utils.domData.get(element, withIfDomDataKey),
48.2447 - dataValue = ko.utils.unwrapObservable(valueAccessor()),
48.2448 - shouldDisplay = !isNot !== !dataValue, // equivalent to isNot ? !dataValue : !!dataValue
48.2449 - isFirstRender = !withIfData.savedNodes,
48.2450 - needsRefresh = isFirstRender || isWith || (shouldDisplay !== withIfData.didDisplayOnLastUpdate);
48.2451 -
48.2452 - if (needsRefresh) {
48.2453 - if (isFirstRender) {
48.2454 - withIfData.savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
48.2455 - }
48.2456 -
48.2457 - if (shouldDisplay) {
48.2458 - if (!isFirstRender) {
48.2459 - ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(withIfData.savedNodes));
48.2460 - }
48.2461 - ko.applyBindingsToDescendants(makeContextCallback ? makeContextCallback(bindingContext, dataValue) : bindingContext, element);
48.2462 - } else {
48.2463 - ko.virtualElements.emptyNode(element);
48.2464 - }
48.2465 -
48.2466 - withIfData.didDisplayOnLastUpdate = shouldDisplay;
48.2467 - }
48.2468 - }
48.2469 - };
48.2470 - ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
48.2471 - ko.virtualElements.allowedBindings[bindingKey] = true;
48.2472 -}
48.2473 -
48.2474 -// Construct the actual binding handlers
48.2475 -makeWithIfBinding('if');
48.2476 -makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
48.2477 -makeWithIfBinding('with', true /* isWith */, false /* isNot */,
48.2478 - function(bindingContext, dataValue) {
48.2479 - return bindingContext['createChildContext'](dataValue);
48.2480 - }
48.2481 -);
48.2482 -function ensureDropdownSelectionIsConsistentWithModelValue(element, modelValue, preferModelValue) {
48.2483 - if (preferModelValue) {
48.2484 - if (modelValue !== ko.selectExtensions.readValue(element))
48.2485 - ko.selectExtensions.writeValue(element, modelValue);
48.2486 - }
48.2487 -
48.2488 - // No matter which direction we're syncing in, we want the end result to be equality between dropdown value and model value.
48.2489 - // If they aren't equal, either we prefer the dropdown value, or the model value couldn't be represented, so either way,
48.2490 - // change the model value to match the dropdown.
48.2491 - if (modelValue !== ko.selectExtensions.readValue(element))
48.2492 - ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
48.2493 -};
48.2494 -
48.2495 -ko.bindingHandlers['options'] = {
48.2496 - 'update': function (element, valueAccessor, allBindingsAccessor) {
48.2497 - if (ko.utils.tagNameLower(element) !== "select")
48.2498 - throw new Error("options binding applies only to SELECT elements");
48.2499 -
48.2500 - var selectWasPreviouslyEmpty = element.length == 0;
48.2501 - var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
48.2502 - return node.tagName && (ko.utils.tagNameLower(node) === "option") && node.selected;
48.2503 - }), function (node) {
48.2504 - return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
48.2505 - });
48.2506 - var previousScrollTop = element.scrollTop;
48.2507 -
48.2508 - var value = ko.utils.unwrapObservable(valueAccessor());
48.2509 - var selectedValue = element.value;
48.2510 -
48.2511 - // Remove all existing <option>s.
48.2512 - // Need to use .remove() rather than .removeChild() for <option>s otherwise IE behaves oddly (https://github.com/SteveSanderson/knockout/issues/134)
48.2513 - while (element.length > 0) {
48.2514 - ko.cleanNode(element.options[0]);
48.2515 - element.remove(0);
48.2516 - }
48.2517 -
48.2518 - if (value) {
48.2519 - var allBindings = allBindingsAccessor(),
48.2520 - includeDestroyed = allBindings['optionsIncludeDestroyed'];
48.2521 -
48.2522 - if (typeof value.length != "number")
48.2523 - value = [value];
48.2524 - if (allBindings['optionsCaption']) {
48.2525 - var option = document.createElement("option");
48.2526 - ko.utils.setHtml(option, allBindings['optionsCaption']);
48.2527 - ko.selectExtensions.writeValue(option, undefined);
48.2528 - element.appendChild(option);
48.2529 - }
48.2530 -
48.2531 - for (var i = 0, j = value.length; i < j; i++) {
48.2532 - // Skip destroyed items
48.2533 - var arrayEntry = value[i];
48.2534 - if (arrayEntry && arrayEntry['_destroy'] && !includeDestroyed)
48.2535 - continue;
48.2536 -
48.2537 - var option = document.createElement("option");
48.2538 -
48.2539 - function applyToObject(object, predicate, defaultValue) {
48.2540 - var predicateType = typeof predicate;
48.2541 - if (predicateType == "function") // Given a function; run it against the data value
48.2542 - return predicate(object);
48.2543 - else if (predicateType == "string") // Given a string; treat it as a property name on the data value
48.2544 - return object[predicate];
48.2545 - else // Given no optionsText arg; use the data value itself
48.2546 - return defaultValue;
48.2547 - }
48.2548 -
48.2549 - // Apply a value to the option element
48.2550 - var optionValue = applyToObject(arrayEntry, allBindings['optionsValue'], arrayEntry);
48.2551 - ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));
48.2552 -
48.2553 - // Apply some text to the option element
48.2554 - var optionText = applyToObject(arrayEntry, allBindings['optionsText'], optionValue);
48.2555 - ko.utils.setTextContent(option, optionText);
48.2556 -
48.2557 - element.appendChild(option);
48.2558 - }
48.2559 -
48.2560 - // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
48.2561 - // That's why we first added them without selection. Now it's time to set the selection.
48.2562 - var newOptions = element.getElementsByTagName("option");
48.2563 - var countSelectionsRetained = 0;
48.2564 - for (var i = 0, j = newOptions.length; i < j; i++) {
48.2565 - if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
48.2566 - ko.utils.setOptionNodeSelectionState(newOptions[i], true);
48.2567 - countSelectionsRetained++;
48.2568 - }
48.2569 - }
48.2570 -
48.2571 - element.scrollTop = previousScrollTop;
48.2572 -
48.2573 - if (selectWasPreviouslyEmpty && ('value' in allBindings)) {
48.2574 - // Ensure consistency between model value and selected option.
48.2575 - // If the dropdown is being populated for the first time here (or was otherwise previously empty),
48.2576 - // the dropdown selection state is meaningless, so we preserve the model value.
48.2577 - ensureDropdownSelectionIsConsistentWithModelValue(element, ko.utils.peekObservable(allBindings['value']), /* preferModelValue */ true);
48.2578 - }
48.2579 -
48.2580 - // Workaround for IE9 bug
48.2581 - ko.utils.ensureSelectElementIsRenderedCorrectly(element);
48.2582 - }
48.2583 - }
48.2584 -};
48.2585 -ko.bindingHandlers['options'].optionValueDomDataKey = '__ko.optionValueDomData__';
48.2586 -ko.bindingHandlers['selectedOptions'] = {
48.2587 - 'init': function (element, valueAccessor, allBindingsAccessor) {
48.2588 - ko.utils.registerEventHandler(element, "change", function () {
48.2589 - var value = valueAccessor(), valueToWrite = [];
48.2590 - ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
48.2591 - if (node.selected)
48.2592 - valueToWrite.push(ko.selectExtensions.readValue(node));
48.2593 - });
48.2594 - ko.expressionRewriting.writeValueToProperty(value, allBindingsAccessor, 'value', valueToWrite);
48.2595 - });
48.2596 - },
48.2597 - 'update': function (element, valueAccessor) {
48.2598 - if (ko.utils.tagNameLower(element) != "select")
48.2599 - throw new Error("values binding applies only to SELECT elements");
48.2600 -
48.2601 - var newValue = ko.utils.unwrapObservable(valueAccessor());
48.2602 - if (newValue && typeof newValue.length == "number") {
48.2603 - ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
48.2604 - var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
48.2605 - ko.utils.setOptionNodeSelectionState(node, isSelected);
48.2606 - });
48.2607 - }
48.2608 - }
48.2609 -};
48.2610 -ko.bindingHandlers['style'] = {
48.2611 - 'update': function (element, valueAccessor) {
48.2612 - var value = ko.utils.unwrapObservable(valueAccessor() || {});
48.2613 - for (var styleName in value) {
48.2614 - if (typeof styleName == "string") {
48.2615 - var styleValue = ko.utils.unwrapObservable(value[styleName]);
48.2616 - element.style[styleName] = styleValue || ""; // Empty string removes the value, whereas null/undefined have no effect
48.2617 - }
48.2618 - }
48.2619 - }
48.2620 -};
48.2621 -ko.bindingHandlers['submit'] = {
48.2622 - 'init': function (element, valueAccessor, allBindingsAccessor, viewModel) {
48.2623 - if (typeof valueAccessor() != "function")
48.2624 - throw new Error("The value for a submit binding must be a function");
48.2625 - ko.utils.registerEventHandler(element, "submit", function (event) {
48.2626 - var handlerReturnValue;
48.2627 - var value = valueAccessor();
48.2628 - try { handlerReturnValue = value.call(viewModel, element); }
48.2629 - finally {
48.2630 - if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
48.2631 - if (event.preventDefault)
48.2632 - event.preventDefault();
48.2633 - else
48.2634 - event.returnValue = false;
48.2635 - }
48.2636 - }
48.2637 - });
48.2638 - }
48.2639 -};
48.2640 -ko.bindingHandlers['text'] = {
48.2641 - 'update': function (element, valueAccessor) {
48.2642 - ko.utils.setTextContent(element, valueAccessor());
48.2643 - }
48.2644 -};
48.2645 -ko.virtualElements.allowedBindings['text'] = true;
48.2646 -ko.bindingHandlers['uniqueName'] = {
48.2647 - 'init': function (element, valueAccessor) {
48.2648 - if (valueAccessor()) {
48.2649 - var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
48.2650 - ko.utils.setElementName(element, name);
48.2651 - }
48.2652 - }
48.2653 -};
48.2654 -ko.bindingHandlers['uniqueName'].currentIndex = 0;
48.2655 -ko.bindingHandlers['value'] = {
48.2656 - 'init': function (element, valueAccessor, allBindingsAccessor) {
48.2657 - // Always catch "change" event; possibly other events too if asked
48.2658 - var eventsToCatch = ["change"];
48.2659 - var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
48.2660 - var propertyChangedFired = false;
48.2661 - if (requestedEventsToCatch) {
48.2662 - if (typeof requestedEventsToCatch == "string") // Allow both individual event names, and arrays of event names
48.2663 - requestedEventsToCatch = [requestedEventsToCatch];
48.2664 - ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
48.2665 - eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
48.2666 - }
48.2667 -
48.2668 - var valueUpdateHandler = function() {
48.2669 - propertyChangedFired = false;
48.2670 - var modelValue = valueAccessor();
48.2671 - var elementValue = ko.selectExtensions.readValue(element);
48.2672 - ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'value', elementValue);
48.2673 - }
48.2674 -
48.2675 - // Workaround for https://github.com/SteveSanderson/knockout/issues/122
48.2676 - // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
48.2677 - var ieAutoCompleteHackNeeded = ko.utils.ieVersion && element.tagName.toLowerCase() == "input" && element.type == "text"
48.2678 - && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
48.2679 - if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
48.2680 - ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
48.2681 - ko.utils.registerEventHandler(element, "blur", function() {
48.2682 - if (propertyChangedFired) {
48.2683 - valueUpdateHandler();
48.2684 - }
48.2685 - });
48.2686 - }
48.2687 -
48.2688 - ko.utils.arrayForEach(eventsToCatch, function(eventName) {
48.2689 - // The syntax "after<eventname>" means "run the handler asynchronously after the event"
48.2690 - // This is useful, for example, to catch "keydown" events after the browser has updated the control
48.2691 - // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
48.2692 - var handler = valueUpdateHandler;
48.2693 - if (ko.utils.stringStartsWith(eventName, "after")) {
48.2694 - handler = function() { setTimeout(valueUpdateHandler, 0) };
48.2695 - eventName = eventName.substring("after".length);
48.2696 - }
48.2697 - ko.utils.registerEventHandler(element, eventName, handler);
48.2698 - });
48.2699 - },
48.2700 - 'update': function (element, valueAccessor) {
48.2701 - var valueIsSelectOption = ko.utils.tagNameLower(element) === "select";
48.2702 - var newValue = ko.utils.unwrapObservable(valueAccessor());
48.2703 - var elementValue = ko.selectExtensions.readValue(element);
48.2704 - var valueHasChanged = (newValue != elementValue);
48.2705 -
48.2706 - // JavaScript's 0 == "" behavious is unfortunate here as it prevents writing 0 to an empty text box (loose equality suggests the values are the same).
48.2707 - // We don't want to do a strict equality comparison as that is more confusing for developers in certain cases, so we specifically special case 0 != "" here.
48.2708 - if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
48.2709 - valueHasChanged = true;
48.2710 -
48.2711 - if (valueHasChanged) {
48.2712 - var applyValueAction = function () { ko.selectExtensions.writeValue(element, newValue); };
48.2713 - applyValueAction();
48.2714 -
48.2715 - // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
48.2716 - // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
48.2717 - // to apply the value as well.
48.2718 - var alsoApplyAsynchronously = valueIsSelectOption;
48.2719 - if (alsoApplyAsynchronously)
48.2720 - setTimeout(applyValueAction, 0);
48.2721 - }
48.2722 -
48.2723 - // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
48.2724 - // because you're not allowed to have a model value that disagrees with a visible UI selection.
48.2725 - if (valueIsSelectOption && (element.length > 0))
48.2726 - ensureDropdownSelectionIsConsistentWithModelValue(element, newValue, /* preferModelValue */ false);
48.2727 - }
48.2728 -};
48.2729 -ko.bindingHandlers['visible'] = {
48.2730 - 'update': function (element, valueAccessor) {
48.2731 - var value = ko.utils.unwrapObservable(valueAccessor());
48.2732 - var isCurrentlyVisible = !(element.style.display == "none");
48.2733 - if (value && !isCurrentlyVisible)
48.2734 - element.style.display = "";
48.2735 - else if ((!value) && isCurrentlyVisible)
48.2736 - element.style.display = "none";
48.2737 - }
48.2738 -};
48.2739 -// 'click' is just a shorthand for the usual full-length event:{click:handler}
48.2740 -makeEventHandlerShortcut('click');
48.2741 -// If you want to make a custom template engine,
48.2742 -//
48.2743 -// [1] Inherit from this class (like ko.nativeTemplateEngine does)
48.2744 -// [2] Override 'renderTemplateSource', supplying a function with this signature:
48.2745 -//
48.2746 -// function (templateSource, bindingContext, options) {
48.2747 -// // - templateSource.text() is the text of the template you should render
48.2748 -// // - bindingContext.$data is the data you should pass into the template
48.2749 -// // - you might also want to make bindingContext.$parent, bindingContext.$parents,
48.2750 -// // and bindingContext.$root available in the template too
48.2751 -// // - options gives you access to any other properties set on "data-bind: { template: options }"
48.2752 -// //
48.2753 -// // Return value: an array of DOM nodes
48.2754 -// }
48.2755 -//
48.2756 -// [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
48.2757 -//
48.2758 -// function (script) {
48.2759 -// // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
48.2760 -// // For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
48.2761 -// }
48.2762 -//
48.2763 -// This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
48.2764 -// If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
48.2765 -// and then you don't need to override 'createJavaScriptEvaluatorBlock'.
48.2766 -
48.2767 -ko.templateEngine = function () { };
48.2768 -
48.2769 -ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
48.2770 - throw new Error("Override renderTemplateSource");
48.2771 -};
48.2772 -
48.2773 -ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
48.2774 - throw new Error("Override createJavaScriptEvaluatorBlock");
48.2775 -};
48.2776 -
48.2777 -ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {
48.2778 - // Named template
48.2779 - if (typeof template == "string") {
48.2780 - templateDocument = templateDocument || document;
48.2781 - var elem = templateDocument.getElementById(template);
48.2782 - if (!elem)
48.2783 - throw new Error("Cannot find template with ID " + template);
48.2784 - return new ko.templateSources.domElement(elem);
48.2785 - } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
48.2786 - // Anonymous template
48.2787 - return new ko.templateSources.anonymousTemplate(template);
48.2788 - } else
48.2789 - throw new Error("Unknown template type: " + template);
48.2790 -};
48.2791 -
48.2792 -ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
48.2793 - var templateSource = this['makeTemplateSource'](template, templateDocument);
48.2794 - return this['renderTemplateSource'](templateSource, bindingContext, options);
48.2795 -};
48.2796 -
48.2797 -ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
48.2798 - // Skip rewriting if requested
48.2799 - if (this['allowTemplateRewriting'] === false)
48.2800 - return true;
48.2801 - return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
48.2802 -};
48.2803 -
48.2804 -ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
48.2805 - var templateSource = this['makeTemplateSource'](template, templateDocument);
48.2806 - var rewritten = rewriterCallback(templateSource['text']());
48.2807 - templateSource['text'](rewritten);
48.2808 - templateSource['data']("isRewritten", true);
48.2809 -};
48.2810 -
48.2811 -ko.exportSymbol('templateEngine', ko.templateEngine);
48.2812 -
48.2813 -ko.templateRewriting = (function () {
48.2814 - var memoizeDataBindingAttributeSyntaxRegex = /(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi;
48.2815 - var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;
48.2816 -
48.2817 - function validateDataBindValuesForRewriting(keyValueArray) {
48.2818 - var allValidators = ko.expressionRewriting.bindingRewriteValidators;
48.2819 - for (var i = 0; i < keyValueArray.length; i++) {
48.2820 - var key = keyValueArray[i]['key'];
48.2821 - if (allValidators.hasOwnProperty(key)) {
48.2822 - var validator = allValidators[key];
48.2823 -
48.2824 - if (typeof validator === "function") {
48.2825 - var possibleErrorMessage = validator(keyValueArray[i]['value']);
48.2826 - if (possibleErrorMessage)
48.2827 - throw new Error(possibleErrorMessage);
48.2828 - } else if (!validator) {
48.2829 - throw new Error("This template engine does not support the '" + key + "' binding within its templates");
48.2830 - }
48.2831 - }
48.2832 - }
48.2833 - }
48.2834 -
48.2835 - function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, templateEngine) {
48.2836 - var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
48.2837 - validateDataBindValuesForRewriting(dataBindKeyValueArray);
48.2838 - var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray);
48.2839 -
48.2840 - // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
48.2841 - // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
48.2842 - // extra indirection.
48.2843 - var applyBindingsToNextSiblingScript =
48.2844 - "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()})";
48.2845 - return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
48.2846 - }
48.2847 -
48.2848 - return {
48.2849 - ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
48.2850 - if (!templateEngine['isTemplateRewritten'](template, templateDocument))
48.2851 - templateEngine['rewriteTemplate'](template, function (htmlString) {
48.2852 - return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
48.2853 - }, templateDocument);
48.2854 - },
48.2855 -
48.2856 - memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
48.2857 - return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
48.2858 - return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[6], /* tagToRetain: */ arguments[1], templateEngine);
48.2859 - }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {
48.2860 - return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", templateEngine);
48.2861 - });
48.2862 - },
48.2863 -
48.2864 - applyMemoizedBindingsToNextSibling: function (bindings) {
48.2865 - return ko.memoization.memoize(function (domNode, bindingContext) {
48.2866 - if (domNode.nextSibling)
48.2867 - ko.applyBindingsToNode(domNode.nextSibling, bindings, bindingContext);
48.2868 - });
48.2869 - }
48.2870 - }
48.2871 -})();
48.2872 -
48.2873 -
48.2874 -// Exported only because it has to be referenced by string lookup from within rewritten template
48.2875 -ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
48.2876 -(function() {
48.2877 - // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
48.2878 - // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
48.2879 - //
48.2880 - // Two are provided by default:
48.2881 - // 1. ko.templateSources.domElement - reads/writes the text content of an arbitrary DOM element
48.2882 - // 2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
48.2883 - // without reading/writing the actual element text content, since it will be overwritten
48.2884 - // with the rendered template output.
48.2885 - // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
48.2886 - // Template sources need to have the following functions:
48.2887 - // text() - returns the template text from your storage location
48.2888 - // text(value) - writes the supplied template text to your storage location
48.2889 - // data(key) - reads values stored using data(key, value) - see below
48.2890 - // data(key, value) - associates "value" with this template and the key "key". Is used to store information like "isRewritten".
48.2891 - //
48.2892 - // Optionally, template sources can also have the following functions:
48.2893 - // nodes() - returns a DOM element containing the nodes of this template, where available
48.2894 - // nodes(value) - writes the given DOM element to your storage location
48.2895 - // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
48.2896 - // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
48.2897 - //
48.2898 - // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
48.2899 - // using and overriding "makeTemplateSource" to return an instance of your custom template source.
48.2900 -
48.2901 - ko.templateSources = {};
48.2902 -
48.2903 - // ---- ko.templateSources.domElement -----
48.2904 -
48.2905 - ko.templateSources.domElement = function(element) {
48.2906 - this.domElement = element;
48.2907 - }
48.2908 -
48.2909 - ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
48.2910 - var tagNameLower = ko.utils.tagNameLower(this.domElement),
48.2911 - elemContentsProperty = tagNameLower === "script" ? "text"
48.2912 - : tagNameLower === "textarea" ? "value"
48.2913 - : "innerHTML";
48.2914 -
48.2915 - if (arguments.length == 0) {
48.2916 - return this.domElement[elemContentsProperty];
48.2917 - } else {
48.2918 - var valueToWrite = arguments[0];
48.2919 - if (elemContentsProperty === "innerHTML")
48.2920 - ko.utils.setHtml(this.domElement, valueToWrite);
48.2921 - else
48.2922 - this.domElement[elemContentsProperty] = valueToWrite;
48.2923 - }
48.2924 - };
48.2925 -
48.2926 - ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {
48.2927 - if (arguments.length === 1) {
48.2928 - return ko.utils.domData.get(this.domElement, "templateSourceData_" + key);
48.2929 - } else {
48.2930 - ko.utils.domData.set(this.domElement, "templateSourceData_" + key, arguments[1]);
48.2931 - }
48.2932 - };
48.2933 -
48.2934 - // ---- ko.templateSources.anonymousTemplate -----
48.2935 - // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
48.2936 - // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
48.2937 - // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.
48.2938 -
48.2939 - var anonymousTemplatesDomDataKey = "__ko_anon_template__";
48.2940 - ko.templateSources.anonymousTemplate = function(element) {
48.2941 - this.domElement = element;
48.2942 - }
48.2943 - ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
48.2944 - ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
48.2945 - if (arguments.length == 0) {
48.2946 - var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
48.2947 - if (templateData.textData === undefined && templateData.containerData)
48.2948 - templateData.textData = templateData.containerData.innerHTML;
48.2949 - return templateData.textData;
48.2950 - } else {
48.2951 - var valueToWrite = arguments[0];
48.2952 - ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {textData: valueToWrite});
48.2953 - }
48.2954 - };
48.2955 - ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
48.2956 - if (arguments.length == 0) {
48.2957 - var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
48.2958 - return templateData.containerData;
48.2959 - } else {
48.2960 - var valueToWrite = arguments[0];
48.2961 - ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {containerData: valueToWrite});
48.2962 - }
48.2963 - };
48.2964 -
48.2965 - ko.exportSymbol('templateSources', ko.templateSources);
48.2966 - ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
48.2967 - ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
48.2968 -})();
48.2969 -(function () {
48.2970 - var _templateEngine;
48.2971 - ko.setTemplateEngine = function (templateEngine) {
48.2972 - if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
48.2973 - throw new Error("templateEngine must inherit from ko.templateEngine");
48.2974 - _templateEngine = templateEngine;
48.2975 - }
48.2976 -
48.2977 - function invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, action) {
48.2978 - var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
48.2979 - while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
48.2980 - nextInQueue = ko.virtualElements.nextSibling(node);
48.2981 - if (node.nodeType === 1 || node.nodeType === 8)
48.2982 - action(node);
48.2983 - }
48.2984 - }
48.2985 -
48.2986 - function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
48.2987 - // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
48.2988 - // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
48.2989 - // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
48.2990 - // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
48.2991 - // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)
48.2992 -
48.2993 - if (continuousNodeArray.length) {
48.2994 - var firstNode = continuousNodeArray[0], lastNode = continuousNodeArray[continuousNodeArray.length - 1];
48.2995 -
48.2996 - // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
48.2997 - // whereas a regular applyBindings won't introduce new memoized nodes
48.2998 - invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
48.2999 - ko.applyBindings(bindingContext, node);
48.3000 - });
48.3001 - invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
48.3002 - ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
48.3003 - });
48.3004 - }
48.3005 - }
48.3006 -
48.3007 - function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
48.3008 - return nodeOrNodeArray.nodeType ? nodeOrNodeArray
48.3009 - : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
48.3010 - : null;
48.3011 - }
48.3012 -
48.3013 - function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
48.3014 - options = options || {};
48.3015 - var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
48.3016 - var templateDocument = firstTargetNode && firstTargetNode.ownerDocument;
48.3017 - var templateEngineToUse = (options['templateEngine'] || _templateEngine);
48.3018 - ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
48.3019 - var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);
48.3020 -
48.3021 - // Loosely check result is an array of DOM nodes
48.3022 - if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
48.3023 - throw new Error("Template engine must return an array of DOM nodes");
48.3024 -
48.3025 - var haveAddedNodesToParent = false;
48.3026 - switch (renderMode) {
48.3027 - case "replaceChildren":
48.3028 - ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
48.3029 - haveAddedNodesToParent = true;
48.3030 - break;
48.3031 - case "replaceNode":
48.3032 - ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
48.3033 - haveAddedNodesToParent = true;
48.3034 - break;
48.3035 - case "ignoreTargetNode": break;
48.3036 - default:
48.3037 - throw new Error("Unknown renderMode: " + renderMode);
48.3038 - }
48.3039 -
48.3040 - if (haveAddedNodesToParent) {
48.3041 - activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
48.3042 - if (options['afterRender'])
48.3043 - ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext['$data']]);
48.3044 - }
48.3045 -
48.3046 - return renderedNodesArray;
48.3047 - }
48.3048 -
48.3049 - ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
48.3050 - options = options || {};
48.3051 - if ((options['templateEngine'] || _templateEngine) == undefined)
48.3052 - throw new Error("Set a template engine before calling renderTemplate");
48.3053 - renderMode = renderMode || "replaceChildren";
48.3054 -
48.3055 - if (targetNodeOrNodeArray) {
48.3056 - var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
48.3057 -
48.3058 - var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
48.3059 - var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;
48.3060 -
48.3061 - return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
48.3062 - function () {
48.3063 - // Ensure we've got a proper binding context to work with
48.3064 - var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
48.3065 - ? dataOrBindingContext
48.3066 - : new ko.bindingContext(ko.utils.unwrapObservable(dataOrBindingContext));
48.3067 -
48.3068 - // Support selecting template as a function of the data being rendered
48.3069 - var templateName = typeof(template) == 'function' ? template(bindingContext['$data'], bindingContext) : template;
48.3070 -
48.3071 - var renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);
48.3072 - if (renderMode == "replaceNode") {
48.3073 - targetNodeOrNodeArray = renderedNodesArray;
48.3074 - firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
48.3075 - }
48.3076 - },
48.3077 - null,
48.3078 - { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
48.3079 - );
48.3080 - } else {
48.3081 - // We don't yet have a DOM node to evaluate, so use a memo and render the template later when there is a DOM node
48.3082 - return ko.memoization.memoize(function (domNode) {
48.3083 - ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
48.3084 - });
48.3085 - }
48.3086 - };
48.3087 -
48.3088 - ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
48.3089 - // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
48.3090 - // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
48.3091 - var arrayItemContext;
48.3092 -
48.3093 - // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
48.3094 - var executeTemplateForArrayItem = function (arrayValue, index) {
48.3095 - // Support selecting template as a function of the data being rendered
48.3096 - arrayItemContext = parentBindingContext['createChildContext'](ko.utils.unwrapObservable(arrayValue), options['as']);
48.3097 - arrayItemContext['$index'] = index;
48.3098 - var templateName = typeof(template) == 'function' ? template(arrayValue, arrayItemContext) : template;
48.3099 - return executeTemplate(null, "ignoreTargetNode", templateName, arrayItemContext, options);
48.3100 - }
48.3101 -
48.3102 - // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
48.3103 - var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {
48.3104 - activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
48.3105 - if (options['afterRender'])
48.3106 - options['afterRender'](addedNodesArray, arrayValue);
48.3107 - };
48.3108 -
48.3109 - return ko.dependentObservable(function () {
48.3110 - var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
48.3111 - if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
48.3112 - unwrappedArray = [unwrappedArray];
48.3113 -
48.3114 - // Filter out any entries marked as destroyed
48.3115 - var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
48.3116 - return options['includeDestroyed'] || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
48.3117 - });
48.3118 -
48.3119 - // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
48.3120 - // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
48.3121 - ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, filteredArray, executeTemplateForArrayItem, options, activateBindingsCallback]);
48.3122 -
48.3123 - }, null, { disposeWhenNodeIsRemoved: targetNode });
48.3124 - };
48.3125 -
48.3126 - var templateComputedDomDataKey = '__ko__templateComputedDomDataKey__';
48.3127 - function disposeOldComputedAndStoreNewOne(element, newComputed) {
48.3128 - var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
48.3129 - if (oldComputed && (typeof(oldComputed.dispose) == 'function'))
48.3130 - oldComputed.dispose();
48.3131 - ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && newComputed.isActive()) ? newComputed : undefined);
48.3132 - }
48.3133 -
48.3134 - ko.bindingHandlers['template'] = {
48.3135 - 'init': function(element, valueAccessor) {
48.3136 - // Support anonymous templates
48.3137 - var bindingValue = ko.utils.unwrapObservable(valueAccessor());
48.3138 - if ((typeof bindingValue != "string") && (!bindingValue['name']) && (element.nodeType == 1 || element.nodeType == 8)) {
48.3139 - // It's an anonymous template - store the element contents, then clear the element
48.3140 - var templateNodes = element.nodeType == 1 ? element.childNodes : ko.virtualElements.childNodes(element),
48.3141 - container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
48.3142 - new ko.templateSources.anonymousTemplate(element)['nodes'](container);
48.3143 - }
48.3144 - return { 'controlsDescendantBindings': true };
48.3145 - },
48.3146 - 'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
48.3147 - var templateName = ko.utils.unwrapObservable(valueAccessor()),
48.3148 - options = {},
48.3149 - shouldDisplay = true,
48.3150 - dataValue,
48.3151 - templateComputed = null;
48.3152 -
48.3153 - if (typeof templateName != "string") {
48.3154 - options = templateName;
48.3155 - templateName = options['name'];
48.3156 -
48.3157 - // Support "if"/"ifnot" conditions
48.3158 - if ('if' in options)
48.3159 - shouldDisplay = ko.utils.unwrapObservable(options['if']);
48.3160 - if (shouldDisplay && 'ifnot' in options)
48.3161 - shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);
48.3162 -
48.3163 - dataValue = ko.utils.unwrapObservable(options['data']);
48.3164 - }
48.3165 -
48.3166 - if ('foreach' in options) {
48.3167 - // Render once for each data point (treating data set as empty if shouldDisplay==false)
48.3168 - var dataArray = (shouldDisplay && options['foreach']) || [];
48.3169 - templateComputed = ko.renderTemplateForEach(templateName || element, dataArray, options, element, bindingContext);
48.3170 - } else if (!shouldDisplay) {
48.3171 - ko.virtualElements.emptyNode(element);
48.3172 - } else {
48.3173 - // Render once for this single data point (or use the viewModel if no data was provided)
48.3174 - var innerBindingContext = ('data' in options) ?
48.3175 - bindingContext['createChildContext'](dataValue, options['as']) : // Given an explitit 'data' value, we create a child binding context for it
48.3176 - bindingContext; // Given no explicit 'data' value, we retain the same binding context
48.3177 - templateComputed = ko.renderTemplate(templateName || element, innerBindingContext, options, element);
48.3178 - }
48.3179 -
48.3180 - // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
48.3181 - disposeOldComputedAndStoreNewOne(element, templateComputed);
48.3182 - }
48.3183 - };
48.3184 -
48.3185 - // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
48.3186 - ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {
48.3187 - var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);
48.3188 -
48.3189 - if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
48.3190 - return null; // It looks like a string literal, not an object literal, so treat it as a named template (which is allowed for rewriting)
48.3191 -
48.3192 - if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
48.3193 - return null; // Named templates can be rewritten, so return "no error"
48.3194 - return "This template engine does not support anonymous templates nested within its templates";
48.3195 - };
48.3196 -
48.3197 - ko.virtualElements.allowedBindings['template'] = true;
48.3198 -})();
48.3199 -
48.3200 -ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
48.3201 -ko.exportSymbol('renderTemplate', ko.renderTemplate);
48.3202 -
48.3203 -ko.utils.compareArrays = (function () {
48.3204 - var statusNotInOld = 'added', statusNotInNew = 'deleted';
48.3205 -
48.3206 - // Simple calculation based on Levenshtein distance.
48.3207 - function compareArrays(oldArray, newArray, dontLimitMoves) {
48.3208 - oldArray = oldArray || [];
48.3209 - newArray = newArray || [];
48.3210 -
48.3211 - if (oldArray.length <= newArray.length)
48.3212 - return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, dontLimitMoves);
48.3213 - else
48.3214 - return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, dontLimitMoves);
48.3215 - }
48.3216 -
48.3217 - function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, dontLimitMoves) {
48.3218 - var myMin = Math.min,
48.3219 - myMax = Math.max,
48.3220 - editDistanceMatrix = [],
48.3221 - smlIndex, smlIndexMax = smlArray.length,
48.3222 - bigIndex, bigIndexMax = bigArray.length,
48.3223 - compareRange = (bigIndexMax - smlIndexMax) || 1,
48.3224 - maxDistance = smlIndexMax + bigIndexMax + 1,
48.3225 - thisRow, lastRow,
48.3226 - bigIndexMaxForRow, bigIndexMinForRow;
48.3227 -
48.3228 - for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
48.3229 - lastRow = thisRow;
48.3230 - editDistanceMatrix.push(thisRow = []);
48.3231 - bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
48.3232 - bigIndexMinForRow = myMax(0, smlIndex - 1);
48.3233 - for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
48.3234 - if (!bigIndex)
48.3235 - thisRow[bigIndex] = smlIndex + 1;
48.3236 - else if (!smlIndex) // Top row - transform empty array into new array via additions
48.3237 - thisRow[bigIndex] = bigIndex + 1;
48.3238 - else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
48.3239 - thisRow[bigIndex] = lastRow[bigIndex - 1]; // copy value (no edit)
48.3240 - else {
48.3241 - var northDistance = lastRow[bigIndex] || maxDistance; // not in big (deletion)
48.3242 - var westDistance = thisRow[bigIndex - 1] || maxDistance; // not in small (addition)
48.3243 - thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
48.3244 - }
48.3245 - }
48.3246 - }
48.3247 -
48.3248 - var editScript = [], meMinusOne, notInSml = [], notInBig = [];
48.3249 - for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
48.3250 - meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
48.3251 - if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
48.3252 - notInSml.push(editScript[editScript.length] = { // added
48.3253 - 'status': statusNotInSml,
48.3254 - 'value': bigArray[--bigIndex],
48.3255 - 'index': bigIndex });
48.3256 - } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
48.3257 - notInBig.push(editScript[editScript.length] = { // deleted
48.3258 - 'status': statusNotInBig,
48.3259 - 'value': smlArray[--smlIndex],
48.3260 - 'index': smlIndex });
48.3261 - } else {
48.3262 - editScript.push({
48.3263 - 'status': "retained",
48.3264 - 'value': bigArray[--bigIndex] });
48.3265 - --smlIndex;
48.3266 - }
48.3267 - }
48.3268 -
48.3269 - if (notInSml.length && notInBig.length) {
48.3270 - // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
48.3271 - // smlIndexMax keeps the time complexity of this algorithm linear.
48.3272 - var limitFailedCompares = smlIndexMax * 10, failedCompares,
48.3273 - a, d, notInSmlItem, notInBigItem;
48.3274 - // Go through the items that have been added and deleted and try to find matches between them.
48.3275 - for (failedCompares = a = 0; (dontLimitMoves || failedCompares < limitFailedCompares) && (notInSmlItem = notInSml[a]); a++) {
48.3276 - for (d = 0; notInBigItem = notInBig[d]; d++) {
48.3277 - if (notInSmlItem['value'] === notInBigItem['value']) {
48.3278 - notInSmlItem['moved'] = notInBigItem['index'];
48.3279 - notInBigItem['moved'] = notInSmlItem['index'];
48.3280 - notInBig.splice(d,1); // This item is marked as moved; so remove it from notInBig list
48.3281 - failedCompares = d = 0; // Reset failed compares count because we're checking for consecutive failures
48.3282 - break;
48.3283 - }
48.3284 - }
48.3285 - failedCompares += d;
48.3286 - }
48.3287 - }
48.3288 - return editScript.reverse();
48.3289 - }
48.3290 -
48.3291 - return compareArrays;
48.3292 -})();
48.3293 -
48.3294 -ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
48.3295 -
48.3296 -(function () {
48.3297 - // Objective:
48.3298 - // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
48.3299 - // map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
48.3300 - // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
48.3301 - // so that its children is again the concatenation of the mappings of the array elements, but don't re-map any array elements that we
48.3302 - // previously mapped - retain those nodes, and just insert/delete other ones
48.3303 -
48.3304 - // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
48.3305 - // You can use this, for example, to activate bindings on those nodes.
48.3306 -
48.3307 - function fixUpNodesToBeMovedOrRemoved(contiguousNodeArray) {
48.3308 - // Before moving, deleting, or replacing a set of nodes that were previously outputted by the "map" function, we have to reconcile
48.3309 - // them against what is in the DOM right now. It may be that some of the nodes have already been removed from the document,
48.3310 - // or that new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
48.3311 - // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
48.3312 - // So, this function translates the old "map" output array into its best guess of what set of current DOM nodes should be removed.
48.3313 - //
48.3314 - // Rules:
48.3315 - // [A] Any leading nodes that aren't in the document any more should be ignored
48.3316 - // These most likely correspond to memoization nodes that were already removed during binding
48.3317 - // See https://github.com/SteveSanderson/knockout/pull/440
48.3318 - // [B] We want to output a contiguous series of nodes that are still in the document. So, ignore any nodes that
48.3319 - // have already been removed, and include any nodes that have been inserted among the previous collection
48.3320 -
48.3321 - // Rule [A]
48.3322 - while (contiguousNodeArray.length && !ko.utils.domNodeIsAttachedToDocument(contiguousNodeArray[0]))
48.3323 - contiguousNodeArray.splice(0, 1);
48.3324 -
48.3325 - // Rule [B]
48.3326 - if (contiguousNodeArray.length > 1) {
48.3327 - // Build up the actual new contiguous node set
48.3328 - var current = contiguousNodeArray[0], last = contiguousNodeArray[contiguousNodeArray.length - 1], newContiguousSet = [current];
48.3329 - while (current !== last) {
48.3330 - current = current.nextSibling;
48.3331 - if (!current) // Won't happen, except if the developer has manually removed some DOM elements (then we're in an undefined scenario)
48.3332 - return;
48.3333 - newContiguousSet.push(current);
48.3334 - }
48.3335 -
48.3336 - // ... then mutate the input array to match this.
48.3337 - // (The following line replaces the contents of contiguousNodeArray with newContiguousSet)
48.3338 - Array.prototype.splice.apply(contiguousNodeArray, [0, contiguousNodeArray.length].concat(newContiguousSet));
48.3339 - }
48.3340 - return contiguousNodeArray;
48.3341 - }
48.3342 -
48.3343 - function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
48.3344 - // Map this array value inside a dependentObservable so we re-map when any dependency changes
48.3345 - var mappedNodes = [];
48.3346 - var dependentObservable = ko.dependentObservable(function() {
48.3347 - var newMappedNodes = mapping(valueToMap, index) || [];
48.3348 -
48.3349 - // On subsequent evaluations, just replace the previously-inserted DOM nodes
48.3350 - if (mappedNodes.length > 0) {
48.3351 - ko.utils.replaceDomNodes(fixUpNodesToBeMovedOrRemoved(mappedNodes), newMappedNodes);
48.3352 - if (callbackAfterAddingNodes)
48.3353 - ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
48.3354 - }
48.3355 -
48.3356 - // Replace the contents of the mappedNodes array, thereby updating the record
48.3357 - // of which nodes would be deleted if valueToMap was itself later removed
48.3358 - mappedNodes.splice(0, mappedNodes.length);
48.3359 - ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
48.3360 - }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return (mappedNodes.length == 0) || !ko.utils.domNodeIsAttachedToDocument(mappedNodes[0]) } });
48.3361 - return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
48.3362 - }
48.3363 -
48.3364 - var lastMappingResultDomDataKey = "setDomNodeChildrenFromArrayMapping_lastMappingResult";
48.3365 -
48.3366 - ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes) {
48.3367 - // Compare the provided array against the previous one
48.3368 - array = array || [];
48.3369 - options = options || {};
48.3370 - var isFirstExecution = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) === undefined;
48.3371 - var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) || [];
48.3372 - var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; });
48.3373 - var editScript = ko.utils.compareArrays(lastArray, array);
48.3374 -
48.3375 - // Build the new mapping result
48.3376 - var newMappingResult = [];
48.3377 - var lastMappingResultIndex = 0;
48.3378 - var newMappingResultIndex = 0;
48.3379 -
48.3380 - var nodesToDelete = [];
48.3381 - var itemsToProcess = [];
48.3382 - var itemsForBeforeRemoveCallbacks = [];
48.3383 - var itemsForMoveCallbacks = [];
48.3384 - var itemsForAfterAddCallbacks = [];
48.3385 - var mapData;
48.3386 -
48.3387 - function itemMovedOrRetained(editScriptIndex, oldPosition) {
48.3388 - mapData = lastMappingResult[oldPosition];
48.3389 - if (newMappingResultIndex !== oldPosition)
48.3390 - itemsForMoveCallbacks[editScriptIndex] = mapData;
48.3391 - // Since updating the index might change the nodes, do so before calling fixUpNodesToBeMovedOrRemoved
48.3392 - mapData.indexObservable(newMappingResultIndex++);
48.3393 - fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes);
48.3394 - newMappingResult.push(mapData);
48.3395 - itemsToProcess.push(mapData);
48.3396 - }
48.3397 -
48.3398 - function callCallback(callback, items) {
48.3399 - if (callback) {
48.3400 - for (var i = 0, n = items.length; i < n; i++) {
48.3401 - if (items[i]) {
48.3402 - ko.utils.arrayForEach(items[i].mappedNodes, function(node) {
48.3403 - callback(node, i, items[i].arrayEntry);
48.3404 - });
48.3405 - }
48.3406 - }
48.3407 - }
48.3408 - }
48.3409 -
48.3410 - for (var i = 0, editScriptItem, movedIndex; editScriptItem = editScript[i]; i++) {
48.3411 - movedIndex = editScriptItem['moved'];
48.3412 - switch (editScriptItem['status']) {
48.3413 - case "deleted":
48.3414 - if (movedIndex === undefined) {
48.3415 - mapData = lastMappingResult[lastMappingResultIndex];
48.3416 -
48.3417 - // Stop tracking changes to the mapping for these nodes
48.3418 - if (mapData.dependentObservable)
48.3419 - mapData.dependentObservable.dispose();
48.3420 -
48.3421 - // Queue these nodes for later removal
48.3422 - nodesToDelete.push.apply(nodesToDelete, fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes));
48.3423 - if (options['beforeRemove']) {
48.3424 - itemsForBeforeRemoveCallbacks[i] = mapData;
48.3425 - itemsToProcess.push(mapData);
48.3426 - }
48.3427 - }
48.3428 - lastMappingResultIndex++;
48.3429 - break;
48.3430 -
48.3431 - case "retained":
48.3432 - itemMovedOrRetained(i, lastMappingResultIndex++);
48.3433 - break;
48.3434 -
48.3435 - case "added":
48.3436 - if (movedIndex !== undefined) {
48.3437 - itemMovedOrRetained(i, movedIndex);
48.3438 - } else {
48.3439 - mapData = { arrayEntry: editScriptItem['value'], indexObservable: ko.observable(newMappingResultIndex++) };
48.3440 - newMappingResult.push(mapData);
48.3441 - itemsToProcess.push(mapData);
48.3442 - if (!isFirstExecution)
48.3443 - itemsForAfterAddCallbacks[i] = mapData;
48.3444 - }
48.3445 - break;
48.3446 - }
48.3447 - }
48.3448 -
48.3449 - // Call beforeMove first before any changes have been made to the DOM
48.3450 - callCallback(options['beforeMove'], itemsForMoveCallbacks);
48.3451 -
48.3452 - // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
48.3453 - ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);
48.3454 -
48.3455 - // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
48.3456 - for (var i = 0, nextNode = ko.virtualElements.firstChild(domNode), lastNode, node; mapData = itemsToProcess[i]; i++) {
48.3457 - // Get nodes for newly added items
48.3458 - if (!mapData.mappedNodes)
48.3459 - ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));
48.3460 -
48.3461 - // Put nodes in the right place if they aren't there already
48.3462 - for (var j = 0; node = mapData.mappedNodes[j]; nextNode = node.nextSibling, lastNode = node, j++) {
48.3463 - if (node !== nextNode)
48.3464 - ko.virtualElements.insertAfter(domNode, node, lastNode);
48.3465 - }
48.3466 -
48.3467 - // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
48.3468 - if (!mapData.initialized && callbackAfterAddingNodes) {
48.3469 - callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
48.3470 - mapData.initialized = true;
48.3471 - }
48.3472 - }
48.3473 -
48.3474 - // If there's a beforeRemove callback, call it after reordering.
48.3475 - // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
48.3476 - // some sort of animation, which is why we first reorder the nodes that will be removed. If the
48.3477 - // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
48.3478 - // Perhaps we'll make that change in the future if this scenario becomes more common.
48.3479 - callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);
48.3480 -
48.3481 - // Finally call afterMove and afterAdd callbacks
48.3482 - callCallback(options['afterMove'], itemsForMoveCallbacks);
48.3483 - callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
48.3484 -
48.3485 - // Store a copy of the array items we just considered so we can difference it next time
48.3486 - ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
48.3487 - }
48.3488 -})();
48.3489 -
48.3490 -ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
48.3491 -ko.nativeTemplateEngine = function () {
48.3492 - this['allowTemplateRewriting'] = false;
48.3493 -}
48.3494 -
48.3495 -ko.nativeTemplateEngine.prototype = new ko.templateEngine();
48.3496 -ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
48.3497 - var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
48.3498 - templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
48.3499 - templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
48.3500 -
48.3501 - if (templateNodes) {
48.3502 - return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
48.3503 - } else {
48.3504 - var templateText = templateSource['text']();
48.3505 - return ko.utils.parseHtmlFragment(templateText);
48.3506 - }
48.3507 -};
48.3508 -
48.3509 -ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
48.3510 -ko.setTemplateEngine(ko.nativeTemplateEngine.instance);
48.3511 -
48.3512 -ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
48.3513 -(function() {
48.3514 - ko.jqueryTmplTemplateEngine = function () {
48.3515 - // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
48.3516 - // doesn't expose a version number, so we have to infer it.
48.3517 - // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
48.3518 - // which KO internally refers to as version "2", so older versions are no longer detected.
48.3519 - var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
48.3520 - if ((typeof(jQuery) == "undefined") || !(jQuery['tmpl']))
48.3521 - return 0;
48.3522 - // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
48.3523 - try {
48.3524 - if (jQuery['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
48.3525 - // Since 1.0.0pre, custom tags should append markup to an array called "__"
48.3526 - return 2; // Final version of jquery.tmpl
48.3527 - }
48.3528 - } catch(ex) { /* Apparently not the version we were looking for */ }
48.3529 -
48.3530 - return 1; // Any older version that we don't support
48.3531 - })();
48.3532 -
48.3533 - function ensureHasReferencedJQueryTemplates() {
48.3534 - if (jQueryTmplVersion < 2)
48.3535 - throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
48.3536 - }
48.3537 -
48.3538 - function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
48.3539 - return jQuery['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
48.3540 - }
48.3541 -
48.3542 - this['renderTemplateSource'] = function(templateSource, bindingContext, options) {
48.3543 - options = options || {};
48.3544 - ensureHasReferencedJQueryTemplates();
48.3545 -
48.3546 - // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
48.3547 - var precompiled = templateSource['data']('precompiled');
48.3548 - if (!precompiled) {
48.3549 - var templateText = templateSource['text']() || "";
48.3550 - // Wrap in "with($whatever.koBindingContext) { ... }"
48.3551 - templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";
48.3552 -
48.3553 - precompiled = jQuery['template'](null, templateText);
48.3554 - templateSource['data']('precompiled', precompiled);
48.3555 - }
48.3556 -
48.3557 - var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
48.3558 - var jQueryTemplateOptions = jQuery['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
48.3559 -
48.3560 - var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
48.3561 - resultNodes['appendTo'](document.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
48.3562 -
48.3563 - jQuery['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
48.3564 - return resultNodes;
48.3565 - };
48.3566 -
48.3567 - this['createJavaScriptEvaluatorBlock'] = function(script) {
48.3568 - return "{{ko_code ((function() { return " + script + " })()) }}";
48.3569 - };
48.3570 -
48.3571 - this['addTemplate'] = function(templateName, templateMarkup) {
48.3572 - document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "</script>");
48.3573 - };
48.3574 -
48.3575 - if (jQueryTmplVersion > 0) {
48.3576 - jQuery['tmpl']['tag']['ko_code'] = {
48.3577 - open: "__.push($1 || '');"
48.3578 - };
48.3579 - jQuery['tmpl']['tag']['ko_with'] = {
48.3580 - open: "with($1) {",
48.3581 - close: "} "
48.3582 - };
48.3583 - }
48.3584 - };
48.3585 -
48.3586 - ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
48.3587 -
48.3588 - // Use this one by default *only if jquery.tmpl is referenced*
48.3589 - var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
48.3590 - if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
48.3591 - ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);
48.3592 -
48.3593 - ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
48.3594 -})();
48.3595 -});
48.3596 -})(window,document,navigator,window["jQuery"]);
48.3597 -})();
48.3598 \ No newline at end of file
49.1 --- a/ko-fx/src/test/java/org/netbeans/html/kofx/DynamicHTTP.java Wed Jan 08 13:18:34 2014 +0100
49.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
49.3 @@ -1,259 +0,0 @@
49.4 -/**
49.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
49.6 - *
49.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
49.8 - *
49.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
49.10 - * Other names may be trademarks of their respective owners.
49.11 - *
49.12 - * The contents of this file are subject to the terms of either the GNU
49.13 - * General Public License Version 2 only ("GPL") or the Common
49.14 - * Development and Distribution License("CDDL") (collectively, the
49.15 - * "License"). You may not use this file except in compliance with the
49.16 - * License. You can obtain a copy of the License at
49.17 - * http://www.netbeans.org/cddl-gplv2.html
49.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
49.19 - * specific language governing permissions and limitations under the
49.20 - * License. When distributing the software, include this License Header
49.21 - * Notice in each file and include the License file at
49.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
49.23 - * particular file as subject to the "Classpath" exception as provided
49.24 - * by Oracle in the GPL Version 2 section of the License file that
49.25 - * accompanied this code. If applicable, add the following below the
49.26 - * License Header, with the fields enclosed by brackets [] replaced by
49.27 - * your own identifying information:
49.28 - * "Portions Copyrighted [year] [name of copyright owner]"
49.29 - *
49.30 - * Contributor(s):
49.31 - *
49.32 - * The Original Software is NetBeans. The Initial Developer of the Original
49.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
49.34 - *
49.35 - * If you wish your version of this file to be governed by only the CDDL
49.36 - * or only the GPL Version 2, indicate your decision by adding
49.37 - * "[Contributor] elects to include this software in this distribution
49.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
49.39 - * single choice of license, a recipient has the option to distribute
49.40 - * your version of this file under either the CDDL, the GPL Version 2 or
49.41 - * to extend the choice of license to its licensees as provided above.
49.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
49.43 - * Version 2 license, then the option applies only if the new code is
49.44 - * made subject to such option by the copyright holder.
49.45 - */
49.46 -package org.netbeans.html.kofx;
49.47 -
49.48 -import java.io.ByteArrayInputStream;
49.49 -import java.io.ByteArrayOutputStream;
49.50 -import java.io.IOException;
49.51 -import java.io.InputStream;
49.52 -import java.io.OutputStream;
49.53 -import java.io.Reader;
49.54 -import java.net.URI;
49.55 -import java.net.URISyntaxException;
49.56 -import java.util.ArrayList;
49.57 -import java.util.List;
49.58 -import java.util.logging.Level;
49.59 -import java.util.logging.Logger;
49.60 -import org.glassfish.grizzly.PortRange;
49.61 -import org.glassfish.grizzly.http.server.HttpHandler;
49.62 -import org.glassfish.grizzly.http.server.HttpServer;
49.63 -import org.glassfish.grizzly.http.server.NetworkListener;
49.64 -import org.glassfish.grizzly.http.server.Request;
49.65 -import org.glassfish.grizzly.http.server.Response;
49.66 -import org.glassfish.grizzly.http.server.ServerConfiguration;
49.67 -import org.glassfish.grizzly.websockets.WebSocket;
49.68 -import org.glassfish.grizzly.websockets.WebSocketAddOn;
49.69 -import org.glassfish.grizzly.websockets.WebSocketApplication;
49.70 -import org.glassfish.grizzly.websockets.WebSocketEngine;
49.71 -
49.72 -/**
49.73 - *
49.74 - * @author Jaroslav Tulach <jtulach@netbeans.org>
49.75 - */
49.76 -final class DynamicHTTP extends HttpHandler {
49.77 - private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
49.78 - private static int resourcesCount;
49.79 - private static List<Resource> resources;
49.80 - private static ServerConfiguration conf;
49.81 - private static HttpServer server;
49.82 -
49.83 - private DynamicHTTP() {
49.84 - }
49.85 -
49.86 - static URI initServer() throws Exception {
49.87 - server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
49.88 - final WebSocketAddOn addon = new WebSocketAddOn();
49.89 - for (NetworkListener listener : server.getListeners()) {
49.90 - listener.registerAddOn(addon);
49.91 - }
49.92 - resources = new ArrayList<Resource>();
49.93 -
49.94 - conf = server.getServerConfiguration();
49.95 - final DynamicHTTP dh = new DynamicHTTP();
49.96 -
49.97 - conf.addHttpHandler(dh, "/");
49.98 -
49.99 - server.start();
49.100 -
49.101 - return pageURL("http", server, "/test.html");
49.102 - }
49.103 -
49.104 - @Override
49.105 - public void service(Request request, Response response) throws Exception {
49.106 - if ("/test.html".equals(request.getRequestURI())) {
49.107 - response.setContentType("text/html");
49.108 - final InputStream is = DynamicHTTP.class.getResourceAsStream("test.html");
49.109 - copyStream(is, response.getOutputStream(), null);
49.110 - return;
49.111 - }
49.112 - if ("/dynamic".equals(request.getRequestURI())) {
49.113 - String mimeType = request.getParameter("mimeType");
49.114 - List<String> params = new ArrayList<String>();
49.115 - boolean webSocket = false;
49.116 - for (int i = 0;; i++) {
49.117 - String p = request.getParameter("param" + i);
49.118 - if (p == null) {
49.119 - break;
49.120 - }
49.121 - if ("protocol:ws".equals(p)) {
49.122 - webSocket = true;
49.123 - continue;
49.124 - }
49.125 - params.add(p);
49.126 - }
49.127 - final String cnt = request.getParameter("content");
49.128 - String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
49.129 - ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
49.130 - URI url;
49.131 - final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
49.132 - if (webSocket) {
49.133 - url = registerWebSocket(res);
49.134 - } else {
49.135 - url = registerResource(res);
49.136 - }
49.137 - response.getWriter().write(url.toString());
49.138 - response.getWriter().write("\n");
49.139 - return;
49.140 - }
49.141 -
49.142 - for (Resource r : resources) {
49.143 - if (r.httpPath.equals(request.getRequestURI())) {
49.144 - response.setContentType(r.httpType);
49.145 - r.httpContent.reset();
49.146 - String[] params = null;
49.147 - if (r.parameters.length != 0) {
49.148 - params = new String[r.parameters.length];
49.149 - for (int i = 0; i < r.parameters.length; i++) {
49.150 - params[i] = request.getParameter(r.parameters[i]);
49.151 - if (params[i] == null) {
49.152 - if ("http.method".equals(r.parameters[i])) {
49.153 - params[i] = request.getMethod().toString();
49.154 - } else if ("http.requestBody".equals(r.parameters[i])) {
49.155 - Reader rdr = request.getReader();
49.156 - StringBuilder sb = new StringBuilder();
49.157 - for (;;) {
49.158 - int ch = rdr.read();
49.159 - if (ch == -1) {
49.160 - break;
49.161 - }
49.162 - sb.append((char) ch);
49.163 - }
49.164 - params[i] = sb.toString();
49.165 - }
49.166 - }
49.167 - if (params[i] == null) {
49.168 - params[i] = "null";
49.169 - }
49.170 - }
49.171 - }
49.172 -
49.173 - copyStream(r.httpContent, response.getOutputStream(), null, params);
49.174 - }
49.175 - }
49.176 - }
49.177 -
49.178 - private URI registerWebSocket(Resource r) {
49.179 - WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
49.180 - return pageURL("ws", server, r.httpPath);
49.181 - }
49.182 -
49.183 - private URI registerResource(Resource r) {
49.184 - if (!resources.contains(r)) {
49.185 - resources.add(r);
49.186 - conf.addHttpHandler(this, r.httpPath);
49.187 - }
49.188 - return pageURL("http", server, r.httpPath);
49.189 - }
49.190 -
49.191 - private static URI pageURL(String proto, HttpServer server, final String page) {
49.192 - NetworkListener listener = server.getListeners().iterator().next();
49.193 - int port = listener.getPort();
49.194 - try {
49.195 - return new URI(proto + "://localhost:" + port + page);
49.196 - } catch (URISyntaxException ex) {
49.197 - throw new IllegalStateException(ex);
49.198 - }
49.199 - }
49.200 -
49.201 - static final class Resource {
49.202 -
49.203 - final InputStream httpContent;
49.204 - final String httpType;
49.205 - final String httpPath;
49.206 - final String[] parameters;
49.207 -
49.208 - Resource(InputStream httpContent, String httpType, String httpPath,
49.209 - String[] parameters) {
49.210 - httpContent.mark(Integer.MAX_VALUE);
49.211 - this.httpContent = httpContent;
49.212 - this.httpType = httpType;
49.213 - this.httpPath = httpPath;
49.214 - this.parameters = parameters;
49.215 - }
49.216 - }
49.217 -
49.218 - static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
49.219 - for (;;) {
49.220 - int ch = is.read();
49.221 - if (ch == -1) {
49.222 - break;
49.223 - }
49.224 - if (ch == '$' && params.length > 0) {
49.225 - int cnt = is.read() - '0';
49.226 - if (baseURL != null && cnt == 'U' - '0') {
49.227 - os.write(baseURL.getBytes("UTF-8"));
49.228 - } else {
49.229 - if (cnt >= 0 && cnt < params.length) {
49.230 - os.write(params[cnt].getBytes("UTF-8"));
49.231 - } else {
49.232 - os.write('$');
49.233 - os.write(cnt + '0');
49.234 - }
49.235 - }
49.236 - } else {
49.237 - os.write(ch);
49.238 - }
49.239 - }
49.240 - }
49.241 -
49.242 - private static class WS extends WebSocketApplication {
49.243 - private final Resource r;
49.244 -
49.245 - private WS(Resource r) {
49.246 - this.r = r;
49.247 - }
49.248 -
49.249 - @Override
49.250 - public void onMessage(WebSocket socket, String text) {
49.251 - try {
49.252 - r.httpContent.reset();
49.253 - ByteArrayOutputStream out = new ByteArrayOutputStream();
49.254 - copyStream(r.httpContent, out, null, text);
49.255 - String s = new String(out.toByteArray(), "UTF-8");
49.256 - socket.send(s);
49.257 - } catch (IOException ex) {
49.258 - LOG.log(Level.WARNING, "Error processing message " + text, ex);
49.259 - }
49.260 - }
49.261 - }
49.262 -}
50.1 --- a/ko-fx/src/test/java/org/netbeans/html/kofx/KOFx.java Wed Jan 08 13:18:34 2014 +0100
50.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
50.3 @@ -1,118 +0,0 @@
50.4 -/**
50.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
50.6 - *
50.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
50.8 - *
50.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
50.10 - * Other names may be trademarks of their respective owners.
50.11 - *
50.12 - * The contents of this file are subject to the terms of either the GNU
50.13 - * General Public License Version 2 only ("GPL") or the Common
50.14 - * Development and Distribution License("CDDL") (collectively, the
50.15 - * "License"). You may not use this file except in compliance with the
50.16 - * License. You can obtain a copy of the License at
50.17 - * http://www.netbeans.org/cddl-gplv2.html
50.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
50.19 - * specific language governing permissions and limitations under the
50.20 - * License. When distributing the software, include this License Header
50.21 - * Notice in each file and include the License file at
50.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
50.23 - * particular file as subject to the "Classpath" exception as provided
50.24 - * by Oracle in the GPL Version 2 section of the License file that
50.25 - * accompanied this code. If applicable, add the following below the
50.26 - * License Header, with the fields enclosed by brackets [] replaced by
50.27 - * your own identifying information:
50.28 - * "Portions Copyrighted [year] [name of copyright owner]"
50.29 - *
50.30 - * Contributor(s):
50.31 - *
50.32 - * The Original Software is NetBeans. The Initial Developer of the Original
50.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
50.34 - *
50.35 - * If you wish your version of this file to be governed by only the CDDL
50.36 - * or only the GPL Version 2, indicate your decision by adding
50.37 - * "[Contributor] elects to include this software in this distribution
50.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
50.39 - * single choice of license, a recipient has the option to distribute
50.40 - * your version of this file under either the CDDL, the GPL Version 2 or
50.41 - * to extend the choice of license to its licensees as provided above.
50.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
50.43 - * Version 2 license, then the option applies only if the new code is
50.44 - * made subject to such option by the copyright holder.
50.45 - */
50.46 -package org.netbeans.html.kofx;
50.47 -
50.48 -import java.io.Closeable;
50.49 -import java.lang.reflect.InvocationTargetException;
50.50 -import java.lang.reflect.Method;
50.51 -import javafx.application.Platform;
50.52 -import org.apidesign.html.boot.spi.Fn;
50.53 -import org.testng.ITest;
50.54 -import org.testng.annotations.Test;
50.55 -
50.56 -/**
50.57 - *
50.58 - * @author Jaroslav Tulach <jtulach@netbeans.org>
50.59 - */
50.60 -public final class KOFx implements ITest, Runnable {
50.61 - private final Fn.Presenter p;
50.62 - private final Method m;
50.63 - private Object result;
50.64 - private Object inst;
50.65 - private int count;
50.66 -
50.67 - KOFx(Fn.Presenter p, Method m) {
50.68 - this.p = p;
50.69 - this.m = m;
50.70 - }
50.71 -
50.72 - @Override
50.73 - public String getTestName() {
50.74 - return m.getName();
50.75 - }
50.76 -
50.77 - @Test
50.78 - public synchronized void executeTest() throws Exception {
50.79 - if (result == null) {
50.80 - Platform.runLater(this);
50.81 - wait();
50.82 - }
50.83 - if (result instanceof Exception) {
50.84 - throw (Exception)result;
50.85 - }
50.86 - if (result instanceof Error) {
50.87 - throw (Error)result;
50.88 - }
50.89 - }
50.90 -
50.91 - @Override
50.92 - public synchronized void run() {
50.93 - boolean notify = true;
50.94 - try (Closeable a = Fn.activate(p)) {
50.95 - if (inst == null) {
50.96 - inst = m.getDeclaringClass().newInstance();
50.97 - }
50.98 - result = m.invoke(inst);
50.99 - if (result == null) {
50.100 - result = this;
50.101 - }
50.102 - } catch (InvocationTargetException ex) {
50.103 - Throwable r = ex.getTargetException();
50.104 - if (r instanceof InterruptedException) {
50.105 - if (count++ < 10000) {
50.106 - notify = false;
50.107 - Platform.runLater(this);
50.108 - return;
50.109 - }
50.110 - }
50.111 - result = r;
50.112 - } catch (Exception ex) {
50.113 - result = ex;
50.114 - } finally {
50.115 - if (notify) {
50.116 - notifyAll();
50.117 - }
50.118 - }
50.119 - }
50.120 -
50.121 -}
51.1 --- a/ko-fx/src/test/java/org/netbeans/html/kofx/KnockoutFXTest.java Wed Jan 08 13:18:34 2014 +0100
51.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
51.3 @@ -1,229 +0,0 @@
51.4 -/**
51.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
51.6 - *
51.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
51.8 - *
51.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
51.10 - * Other names may be trademarks of their respective owners.
51.11 - *
51.12 - * The contents of this file are subject to the terms of either the GNU
51.13 - * General Public License Version 2 only ("GPL") or the Common
51.14 - * Development and Distribution License("CDDL") (collectively, the
51.15 - * "License"). You may not use this file except in compliance with the
51.16 - * License. You can obtain a copy of the License at
51.17 - * http://www.netbeans.org/cddl-gplv2.html
51.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
51.19 - * specific language governing permissions and limitations under the
51.20 - * License. When distributing the software, include this License Header
51.21 - * Notice in each file and include the License file at
51.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
51.23 - * particular file as subject to the "Classpath" exception as provided
51.24 - * by Oracle in the GPL Version 2 section of the License file that
51.25 - * accompanied this code. If applicable, add the following below the
51.26 - * License Header, with the fields enclosed by brackets [] replaced by
51.27 - * your own identifying information:
51.28 - * "Portions Copyrighted [year] [name of copyright owner]"
51.29 - *
51.30 - * Contributor(s):
51.31 - *
51.32 - * The Original Software is NetBeans. The Initial Developer of the Original
51.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
51.34 - *
51.35 - * If you wish your version of this file to be governed by only the CDDL
51.36 - * or only the GPL Version 2, indicate your decision by adding
51.37 - * "[Contributor] elects to include this software in this distribution
51.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
51.39 - * single choice of license, a recipient has the option to distribute
51.40 - * your version of this file under either the CDDL, the GPL Version 2 or
51.41 - * to extend the choice of license to its licensees as provided above.
51.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
51.43 - * Version 2 license, then the option applies only if the new code is
51.44 - * made subject to such option by the copyright holder.
51.45 - */
51.46 -package org.netbeans.html.kofx;
51.47 -
51.48 -import java.io.BufferedReader;
51.49 -import java.io.IOException;
51.50 -import java.io.InputStreamReader;
51.51 -import java.lang.annotation.Annotation;
51.52 -import java.lang.reflect.Method;
51.53 -import java.net.URI;
51.54 -import java.net.URISyntaxException;
51.55 -import java.net.URL;
51.56 -import java.net.URLConnection;
51.57 -import java.util.ArrayList;
51.58 -import java.util.List;
51.59 -import java.util.Map;
51.60 -import java.util.concurrent.Executors;
51.61 -import net.java.html.BrwsrCtx;
51.62 -import net.java.html.boot.BrowserBuilder;
51.63 -import net.java.html.js.JavaScriptBody;
51.64 -import org.netbeans.html.boot.impl.FnContext;
51.65 -import org.apidesign.html.boot.spi.Fn;
51.66 -import org.apidesign.html.context.spi.Contexts;
51.67 -import org.apidesign.html.json.spi.Technology;
51.68 -import org.apidesign.html.json.spi.Transfer;
51.69 -import org.apidesign.html.json.spi.WSTransfer;
51.70 -import org.apidesign.html.json.tck.KOTest;
51.71 -import org.apidesign.html.json.tck.KnockoutTCK;
51.72 -import org.json.JSONException;
51.73 -import org.json.JSONObject;
51.74 -import org.openide.util.lookup.ServiceProvider;
51.75 -import org.testng.annotations.Factory;
51.76 -import static org.testng.Assert.*;
51.77 -
51.78 -/**
51.79 - *
51.80 - * @author Jaroslav Tulach <jtulach@netbeans.org>
51.81 - */
51.82 -@ServiceProvider(service = KnockoutTCK.class)
51.83 -public final class KnockoutFXTest extends KnockoutTCK {
51.84 - private static Class<?> browserClass;
51.85 - private static Fn.Presenter browserContext;
51.86 -
51.87 - public KnockoutFXTest() {
51.88 - }
51.89 -
51.90 - @Factory public static Object[] compatibilityTests() throws Exception {
51.91 - Class[] arr = testClasses();
51.92 - for (int i = 0; i < arr.length; i++) {
51.93 - assertEquals(
51.94 - arr[i].getClassLoader(),
51.95 - KnockoutFXTest.class.getClassLoader(),
51.96 - "All classes loaded by the same classloader"
51.97 - );
51.98 - }
51.99 -
51.100 - URI uri = DynamicHTTP.initServer();
51.101 -
51.102 - final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutFXTest.class).
51.103 - loadPage(uri.toString()).
51.104 - invoke("initialized");
51.105 -
51.106 - Executors.newSingleThreadExecutor().submit(new Runnable() {
51.107 - @Override
51.108 - public void run() {
51.109 - bb.showAndWait();
51.110 - }
51.111 - });
51.112 -
51.113 - ClassLoader l = getClassLoader();
51.114 - List<Object> res = new ArrayList<Object>();
51.115 - for (int i = 0; i < arr.length; i++) {
51.116 - Class<?> c = Class.forName(arr[i].getName(), true, l);
51.117 - seekKOTests(c, res);
51.118 - }
51.119 - Class<?> c = Class.forName(LessCallbacksCheck.class.getName(), true, l);
51.120 - seekKOTests(c, res);
51.121 - return res.toArray();
51.122 - }
51.123 -
51.124 - private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
51.125 - Class<? extends Annotation> koTest =
51.126 - c.getClassLoader().loadClass(KOTest.class.getName()).
51.127 - asSubclass(Annotation.class);
51.128 - for (Method m : c.getMethods()) {
51.129 - if (m.getAnnotation(koTest) != null) {
51.130 - res.add(new KOFx(browserContext, m));
51.131 - }
51.132 - }
51.133 - }
51.134 -
51.135 - static synchronized ClassLoader getClassLoader() throws InterruptedException {
51.136 - while (browserClass == null) {
51.137 - KnockoutFXTest.class.wait();
51.138 - }
51.139 - return browserClass.getClassLoader();
51.140 - }
51.141 -
51.142 - public static synchronized void initialized(Class<?> browserCls) throws Exception {
51.143 - browserClass = browserCls;
51.144 - browserContext = Fn.activePresenter();
51.145 - KnockoutFXTest.class.notifyAll();
51.146 - }
51.147 -
51.148 - public static void initialized() throws Exception {
51.149 - Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(KnockoutFXTest.class.getName());
51.150 - Method m = classpathClass.getMethod("initialized", Class.class);
51.151 - m.invoke(null, KnockoutFXTest.class);
51.152 - browserContext = Fn.activePresenter();
51.153 - }
51.154 -
51.155 - @Override
51.156 - public BrwsrCtx createContext() {
51.157 - FXContext fx = new FXContext(browserContext);
51.158 - Contexts.Builder cb = Contexts.newBuilder().
51.159 - register(Technology.class, fx, 10).
51.160 - register(Transfer.class, fx, 10);
51.161 - if (fx.areWebSocketsSupported()) {
51.162 - cb.register(WSTransfer.class, fx, 10);
51.163 - }
51.164 - return cb.build();
51.165 - }
51.166 -
51.167 - @Override
51.168 - public Object createJSON(Map<String, Object> values) {
51.169 - JSONObject json = new JSONObject();
51.170 - for (Map.Entry<String, Object> entry : values.entrySet()) {
51.171 - try {
51.172 - json.put(entry.getKey(), entry.getValue());
51.173 - } catch (JSONException ex) {
51.174 - throw new IllegalStateException(ex);
51.175 - }
51.176 - }
51.177 - return json;
51.178 - }
51.179 -
51.180 - @Override
51.181 - @JavaScriptBody(args = { "s", "args" }, body = ""
51.182 - + "var f = new Function(s); "
51.183 - + "return f.apply(null, args);"
51.184 - )
51.185 - public native Object executeScript(String script, Object[] arguments);
51.186 -
51.187 - @JavaScriptBody(args = { }, body =
51.188 - "var h;"
51.189 - + "if (!!window && !!window.location && !!window.location.href)\n"
51.190 - + " h = window.location.href;\n"
51.191 - + "else "
51.192 - + " h = null;"
51.193 - + "return h;\n"
51.194 - )
51.195 - private static native String findBaseURL();
51.196 -
51.197 - @Override
51.198 - public URI prepareURL(String content, String mimeType, String[] parameters) {
51.199 - try {
51.200 - final URL baseURL = new URL(findBaseURL());
51.201 - StringBuilder sb = new StringBuilder();
51.202 - sb.append("/dynamic?mimeType=").append(mimeType);
51.203 - for (int i = 0; i < parameters.length; i++) {
51.204 - sb.append("¶m" + i).append("=").append(parameters[i]);
51.205 - }
51.206 - String mangle = content.replace("\n", "%0a")
51.207 - .replace("\"", "\\\"").replace(" ", "%20");
51.208 - sb.append("&content=").append(mangle);
51.209 -
51.210 - URL query = new URL(baseURL, sb.toString());
51.211 - URLConnection c = query.openConnection();
51.212 - BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
51.213 - URI connectTo = new URI(br.readLine());
51.214 - return connectTo;
51.215 - } catch (IOException ex) {
51.216 - throw new IllegalStateException(ex);
51.217 - } catch (URISyntaxException ex) {
51.218 - throw new IllegalStateException(ex);
51.219 - }
51.220 - }
51.221 -
51.222 - @Override
51.223 - public boolean canFailWebSocketTest() {
51.224 - try {
51.225 - Class.forName("java.util.function.Function");
51.226 - return false;
51.227 - } catch (ClassNotFoundException ex) {
51.228 - // running on JDK7, FX WebView WebSocket impl does not work
51.229 - return true;
51.230 - }
51.231 - }
51.232 -}
52.1 --- a/ko-fx/src/test/java/org/netbeans/html/kofx/LessCallbacksCheck.java Wed Jan 08 13:18:34 2014 +0100
52.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
52.3 @@ -1,82 +0,0 @@
52.4 -/**
52.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
52.6 - *
52.7 - * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
52.8 - *
52.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
52.10 - * Other names may be trademarks of their respective owners.
52.11 - *
52.12 - * The contents of this file are subject to the terms of either the GNU
52.13 - * General Public License Version 2 only ("GPL") or the Common
52.14 - * Development and Distribution License("CDDL") (collectively, the
52.15 - * "License"). You may not use this file except in compliance with the
52.16 - * License. You can obtain a copy of the License at
52.17 - * http://www.netbeans.org/cddl-gplv2.html
52.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
52.19 - * specific language governing permissions and limitations under the
52.20 - * License. When distributing the software, include this License Header
52.21 - * Notice in each file and include the License file at
52.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
52.23 - * particular file as subject to the "Classpath" exception as provided
52.24 - * by Oracle in the GPL Version 2 section of the License file that
52.25 - * accompanied this code. If applicable, add the following below the
52.26 - * License Header, with the fields enclosed by brackets [] replaced by
52.27 - * your own identifying information:
52.28 - * "Portions Copyrighted [year] [name of copyright owner]"
52.29 - *
52.30 - * Contributor(s):
52.31 - *
52.32 - * The Original Software is NetBeans. The Initial Developer of the Original
52.33 - * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
52.34 - *
52.35 - * If you wish your version of this file to be governed by only the CDDL
52.36 - * or only the GPL Version 2, indicate your decision by adding
52.37 - * "[Contributor] elects to include this software in this distribution
52.38 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
52.39 - * single choice of license, a recipient has the option to distribute
52.40 - * your version of this file under either the CDDL, the GPL Version 2 or
52.41 - * to extend the choice of license to its licensees as provided above.
52.42 - * However, if you add GPL Version 2 code and therefore, elected the GPL
52.43 - * Version 2 license, then the option applies only if the new code is
52.44 - * made subject to such option by the copyright holder.
52.45 - */
52.46 -package org.netbeans.html.kofx;
52.47 -
52.48 -import java.io.PrintWriter;
52.49 -import java.io.StringWriter;
52.50 -import net.java.html.json.ComputedProperty;
52.51 -import net.java.html.json.Model;
52.52 -import net.java.html.json.Property;
52.53 -import org.apidesign.html.json.tck.KOTest;
52.54 -
52.55 -/**
52.56 - *
52.57 - * @author Jaroslav Tulach <jtulach@netbeans.org>
52.58 - */
52.59 -@Model(className = "LessCalls", properties = {
52.60 - @Property(name = "value", type = int.class)
52.61 -})
52.62 -public class LessCallbacksCheck {
52.63 - private static StringWriter sw;
52.64 -
52.65 - @ComputedProperty static int plusOne(int value) {
52.66 - if (sw == null) {
52.67 - sw = new StringWriter();
52.68 - }
52.69 - new Exception("Who calls me?").printStackTrace(
52.70 - new PrintWriter(sw)
52.71 - );
52.72 - return value + 1;
52.73 - }
52.74 -
52.75 - @KOTest public void dontCallForInitialValueBackToJavaVM() {
52.76 - LessCalls m = new LessCalls(10).applyBindings();
52.77 - assert m.getPlusOne() == 11 : "Expecting 11: " + m.getPlusOne();
52.78 -
52.79 - assert sw != null : "StringWriter should be initialized: " + sw;
52.80 -
52.81 - if (sw.toString().contains("$JsCallbacks$")) {
52.82 - assert false : "Don't call for initial value via JsCallbacks:\n" + sw;
52.83 - }
52.84 - }
52.85 -}
53.1 --- a/ko-fx/src/test/resources/org/netbeans/html/kofx/test.html Wed Jan 08 13:18:34 2014 +0100
53.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
53.3 @@ -1,56 +0,0 @@
53.4 -<!--
53.5 -
53.6 - DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
53.7 -
53.8 - Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
53.9 -
53.10 - Oracle and Java are registered trademarks of Oracle and/or its affiliates.
53.11 - Other names may be trademarks of their respective owners.
53.12 -
53.13 - The contents of this file are subject to the terms of either the GNU
53.14 - General Public License Version 2 only ("GPL") or the Common
53.15 - Development and Distribution License("CDDL") (collectively, the
53.16 - "License"). You may not use this file except in compliance with the
53.17 - License. You can obtain a copy of the License at
53.18 - http://www.netbeans.org/cddl-gplv2.html
53.19 - or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
53.20 - specific language governing permissions and limitations under the
53.21 - License. When distributing the software, include this License Header
53.22 - Notice in each file and include the License file at
53.23 - nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
53.24 - particular file as subject to the "Classpath" exception as provided
53.25 - by Oracle in the GPL Version 2 section of the License file that
53.26 - accompanied this code. If applicable, add the following below the
53.27 - License Header, with the fields enclosed by brackets [] replaced by
53.28 - your own identifying information:
53.29 - "Portions Copyrighted [year] [name of copyright owner]"
53.30 -
53.31 - Contributor(s):
53.32 -
53.33 - The Original Software is NetBeans. The Initial Developer of the Original
53.34 - Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
53.35 -
53.36 - If you wish your version of this file to be governed by only the CDDL
53.37 - or only the GPL Version 2, indicate your decision by adding
53.38 - "[Contributor] elects to include this software in this distribution
53.39 - under the [CDDL or GPL Version 2] license." If you do not indicate a
53.40 - single choice of license, a recipient has the option to distribute
53.41 - your version of this file under either the CDDL, the GPL Version 2 or
53.42 - to extend the choice of license to its licensees as provided above.
53.43 - However, if you add GPL Version 2 code and therefore, elected the GPL
53.44 - Version 2 license, then the option applies only if the new code is
53.45 - made subject to such option by the copyright holder.
53.46 -
53.47 --->
53.48 -<!DOCTYPE html>
53.49 -<html>
53.50 - <head>
53.51 - <title>Knockout.fx Execution Harness</title>
53.52 - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
53.53 - <meta name="viewport" content="width=device-width">
53.54 - </head>
53.55 - <body>
53.56 - <h1>Knockout.fx Execution Harness</h1>
53.57 - </body>
53.58 - <script></script>
53.59 -</html>
54.1 --- a/ko-osgi-test/pom.xml Wed Jan 08 13:18:34 2014 +0100
54.2 +++ b/ko-osgi-test/pom.xml Tue Jan 14 14:18:50 2014 +0100
54.3 @@ -4,7 +4,7 @@
54.4 <parent>
54.5 <groupId>org.netbeans.html</groupId>
54.6 <artifactId>pom</artifactId>
54.7 - <version>0.7-SNAPSHOT</version>
54.8 + <version>0.8-SNAPSHOT</version>
54.9 </parent>
54.10 <name>KO Tests in an OSGi Container</name>
54.11 <artifactId>ko-osgi-test</artifactId>
54.12 @@ -87,7 +87,7 @@
54.13 </dependency>
54.14 <dependency>
54.15 <groupId>${project.groupId}</groupId>
54.16 - <artifactId>ko-fx</artifactId>
54.17 + <artifactId>ko4j</artifactId>
54.18 <version>${project.version}</version>
54.19 </dependency>
54.20 <dependency>
54.21 @@ -99,20 +99,20 @@
54.22 <dependency>
54.23 <groupId>org.glassfish.grizzly</groupId>
54.24 <artifactId>grizzly-http-server</artifactId>
54.25 - <version>2.3.3</version>
54.26 + <version>${grizzly.version}</version>
54.27 <scope>test</scope>
54.28 </dependency>
54.29 <dependency>
54.30 <groupId>org.glassfish.grizzly</groupId>
54.31 <artifactId>grizzly-websockets-server</artifactId>
54.32 - <version>2.3.3</version>
54.33 + <version>${grizzly.version}</version>
54.34 <scope>test</scope>
54.35 <type>jar</type>
54.36 </dependency>
54.37 <dependency>
54.38 <groupId>org.glassfish.grizzly</groupId>
54.39 <artifactId>grizzly-http-servlet</artifactId>
54.40 - <version>2.3.3</version>
54.41 + <version>${grizzly.version}</version>
54.42 <scope>test</scope>
54.43 </dependency>
54.44 <dependency>
55.1 --- a/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java Wed Jan 08 13:18:34 2014 +0100
55.2 +++ b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java Tue Jan 14 14:18:50 2014 +0100
55.3 @@ -45,6 +45,7 @@
55.4 import java.io.BufferedReader;
55.5 import java.io.IOException;
55.6 import java.io.InputStreamReader;
55.7 +import java.lang.reflect.Constructor;
55.8 import java.lang.reflect.Method;
55.9 import java.net.URI;
55.10 import java.net.URISyntaxException;
55.11 @@ -134,10 +135,12 @@
55.12 public BrwsrCtx createContext() {
55.13 try {
55.14 Class<?> fxCls = loadOSGiClass(
55.15 - "org.netbeans.html.kofx.FXContext",
55.16 + "org.netbeans.html.ko4j.FXContext",
55.17 FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class).getBundleContext()
55.18 );
55.19 - Object fx = fxCls.getConstructor(Fn.Presenter.class).newInstance(browserContext);
55.20 + final Constructor<?> cnstr = fxCls.getConstructor(Fn.Presenter.class);
55.21 + cnstr.setAccessible(true);
55.22 + Object fx = cnstr.newInstance(browserContext);
55.23 Contexts.Builder cb = Contexts.newBuilder().
55.24 register(Technology.class, (Technology)fx, 10).
55.25 register(Transfer.class, (Transfer)fx, 10);
56.1 --- a/ko-ws-tyrus/pom.xml Wed Jan 08 13:18:34 2014 +0100
56.2 +++ b/ko-ws-tyrus/pom.xml Tue Jan 14 14:18:50 2014 +0100
56.3 @@ -4,11 +4,11 @@
56.4 <parent>
56.5 <groupId>org.netbeans.html</groupId>
56.6 <artifactId>pom</artifactId>
56.7 - <version>0.7-SNAPSHOT</version>
56.8 + <version>0.8-SNAPSHOT</version>
56.9 </parent>
56.10 <groupId>org.netbeans.html</groupId>
56.11 <artifactId>ko-ws-tyrus</artifactId>
56.12 - <version>0.7-SNAPSHOT</version>
56.13 + <version>0.8-SNAPSHOT</version>
56.14 <packaging>bundle</packaging>
56.15 <name>Tyrus Based WebSockets</name>
56.16 <url>http://maven.apache.org</url>
56.17 @@ -77,13 +77,13 @@
56.18 <dependency>
56.19 <groupId>org.glassfish.tyrus</groupId>
56.20 <artifactId>tyrus-client</artifactId>
56.21 - <version>1.2.1</version>
56.22 + <version>1.3.1</version>
56.23 <scope>runtime</scope>
56.24 </dependency>
56.25 <dependency>
56.26 <groupId>org.glassfish.tyrus</groupId>
56.27 - <artifactId>tyrus-container-grizzly</artifactId>
56.28 - <version>1.2.1</version>
56.29 + <artifactId>tyrus-container-grizzly-client</artifactId>
56.30 + <version>1.3.1</version>
56.31 <scope>runtime</scope>
56.32 </dependency>
56.33
56.34 @@ -94,7 +94,7 @@
56.35 <groupId>${project.groupId}</groupId>
56.36 <artifactId>net.java.html.boot</artifactId>
56.37 <version>${project.version}</version>
56.38 - <scope>test</scope>
56.39 + <scope>provided</scope>
56.40 <type>jar</type>
56.41 </dependency>
56.42 <dependency>
56.43 @@ -106,7 +106,7 @@
56.44 </dependency>
56.45 <dependency>
56.46 <groupId>${project.groupId}</groupId>
56.47 - <artifactId>ko-fx</artifactId>
56.48 + <artifactId>ko4j</artifactId>
56.49 <version>${project.version}</version>
56.50 <scope>test</scope>
56.51 <type>jar</type>
56.52 @@ -114,14 +114,14 @@
56.53 <dependency>
56.54 <groupId>org.glassfish.grizzly</groupId>
56.55 <artifactId>grizzly-http-server-core</artifactId>
56.56 - <version>2.3.3</version>
56.57 + <version>${grizzly.version}</version>
56.58 <scope>test</scope>
56.59 <type>jar</type>
56.60 </dependency>
56.61 <dependency>
56.62 <groupId>org.glassfish.grizzly</groupId>
56.63 <artifactId>grizzly-websockets-server</artifactId>
56.64 - <version>2.3.3</version>
56.65 + <version>${grizzly.version}</version>
56.66 <scope>test</scope>
56.67 <type>jar</type>
56.68 </dependency>
56.69 @@ -134,13 +134,13 @@
56.70 <dependency>
56.71 <groupId>org.glassfish.grizzly</groupId>
56.72 <artifactId>grizzly-http-server</artifactId>
56.73 - <version>2.3.3</version>
56.74 + <version>${grizzly.version}</version>
56.75 <scope>test</scope>
56.76 </dependency>
56.77 <dependency>
56.78 <groupId>org.glassfish.grizzly</groupId>
56.79 <artifactId>grizzly-http-servlet</artifactId>
56.80 - <version>2.3.3</version>
56.81 + <version>${grizzly.version}</version>
56.82 <scope>test</scope>
56.83 </dependency>
56.84 <dependency>
57.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
57.2 +++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java Tue Jan 14 14:18:50 2014 +0100
57.3 @@ -0,0 +1,248 @@
57.4 +/**
57.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
57.6 + *
57.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
57.8 + *
57.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
57.10 + * Other names may be trademarks of their respective owners.
57.11 + *
57.12 + * The contents of this file are subject to the terms of either the GNU
57.13 + * General Public License Version 2 only ("GPL") or the Common
57.14 + * Development and Distribution License("CDDL") (collectively, the
57.15 + * "License"). You may not use this file except in compliance with the
57.16 + * License. You can obtain a copy of the License at
57.17 + * http://www.netbeans.org/cddl-gplv2.html
57.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
57.19 + * specific language governing permissions and limitations under the
57.20 + * License. When distributing the software, include this License Header
57.21 + * Notice in each file and include the License file at
57.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
57.23 + * particular file as subject to the "Classpath" exception as provided
57.24 + * by Oracle in the GPL Version 2 section of the License file that
57.25 + * accompanied this code. If applicable, add the following below the
57.26 + * License Header, with the fields enclosed by brackets [] replaced by
57.27 + * your own identifying information:
57.28 + * "Portions Copyrighted [year] [name of copyright owner]"
57.29 + *
57.30 + * Contributor(s):
57.31 + *
57.32 + * The Original Software is NetBeans. The Initial Developer of the Original
57.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
57.34 + *
57.35 + * If you wish your version of this file to be governed by only the CDDL
57.36 + * or only the GPL Version 2, indicate your decision by adding
57.37 + * "[Contributor] elects to include this software in this distribution
57.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
57.39 + * single choice of license, a recipient has the option to distribute
57.40 + * your version of this file under either the CDDL, the GPL Version 2 or
57.41 + * to extend the choice of license to its licensees as provided above.
57.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
57.43 + * Version 2 license, then the option applies only if the new code is
57.44 + * made subject to such option by the copyright holder.
57.45 + */
57.46 +package org.netbeans.html.wstyrus;
57.47 +
57.48 +import java.io.IOException;
57.49 +import java.io.InputStream;
57.50 +import java.io.InputStreamReader;
57.51 +import java.io.OutputStream;
57.52 +import java.io.PushbackInputStream;
57.53 +import java.io.Reader;
57.54 +import java.net.HttpURLConnection;
57.55 +import java.net.MalformedURLException;
57.56 +import java.net.URL;
57.57 +import java.net.URLConnection;
57.58 +import java.util.Iterator;
57.59 +import java.util.concurrent.Executor;
57.60 +import java.util.concurrent.Executors;
57.61 +import java.util.concurrent.ThreadFactory;
57.62 +import java.util.logging.Level;
57.63 +import java.util.logging.Logger;
57.64 +import net.java.html.js.JavaScriptBody;
57.65 +import org.apidesign.html.json.spi.JSONCall;
57.66 +import org.json.JSONArray;
57.67 +import org.json.JSONException;
57.68 +import org.json.JSONObject;
57.69 +import org.json.JSONTokener;
57.70 +
57.71 +/** This is an implementation package - just
57.72 + * include its JAR on classpath and use official {@link Context} API
57.73 + * to access the functionality.
57.74 + *
57.75 + * @author Jaroslav Tulach <jtulach@netbeans.org>
57.76 + */
57.77 +final class LoadJSON implements Runnable {
57.78 + private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName());
57.79 + private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
57.80 + @Override
57.81 + public Thread newThread(Runnable runnable) {
57.82 + Thread thread = Executors.defaultThreadFactory().newThread(runnable);
57.83 + thread.setDaemon(true);
57.84 + return thread;
57.85 + }
57.86 + });
57.87 +
57.88 + private final JSONCall call;
57.89 + private final URL base;
57.90 +
57.91 +
57.92 + private LoadJSON(JSONCall call) {
57.93 + this.call = call;
57.94 + this.base = null;
57.95 + }
57.96 +
57.97 + public static void loadJSON(JSONCall call) {
57.98 + assert !"WebSocket".equals(call.getMethod());
57.99 + REQ.execute(new LoadJSON((call)));
57.100 + }
57.101 +
57.102 + @Override
57.103 + public void run() {
57.104 + final String url;
57.105 + Throwable error = null;
57.106 + Object json = null;
57.107 +
57.108 + if (call.isJSONP()) {
57.109 + url = call.composeURL("dummy");
57.110 + } else {
57.111 + url = call.composeURL(null);
57.112 + }
57.113 + try {
57.114 + final URL u = new URL(base, url.replace(" ", "%20"));
57.115 + URLConnection conn = u.openConnection();
57.116 + if (conn instanceof HttpURLConnection) {
57.117 + HttpURLConnection huc = (HttpURLConnection) conn;
57.118 + if (call.getMethod() != null) {
57.119 + huc.setRequestMethod(call.getMethod());
57.120 + }
57.121 + if (call.isDoOutput()) {
57.122 + huc.setDoOutput(true);
57.123 + final OutputStream os = huc.getOutputStream();
57.124 + call.writeData(os);
57.125 + os.flush();
57.126 + }
57.127 + }
57.128 + final PushbackInputStream is = new PushbackInputStream(
57.129 + conn.getInputStream(), 1
57.130 + );
57.131 + boolean array = false;
57.132 + boolean string = false;
57.133 + if (call.isJSONP()) {
57.134 + for (;;) {
57.135 + int ch = is.read();
57.136 + if (ch == -1) {
57.137 + break;
57.138 + }
57.139 + if (ch == '[') {
57.140 + is.unread(ch);
57.141 + array = true;
57.142 + break;
57.143 + }
57.144 + if (ch == '{') {
57.145 + is.unread(ch);
57.146 + break;
57.147 + }
57.148 + }
57.149 + } else {
57.150 + int ch = is.read();
57.151 + if (ch == -1) {
57.152 + string = true;
57.153 + } else {
57.154 + array = ch == '[';
57.155 + is.unread(ch);
57.156 + if (!array && ch != '{') {
57.157 + string = true;
57.158 + }
57.159 + }
57.160 + }
57.161 + try {
57.162 + if (string) {
57.163 + throw new JSONException("");
57.164 + }
57.165 + Reader r = new InputStreamReader(is, "UTF-8");
57.166 +
57.167 + JSONTokener tok = new JSONTokener(r);
57.168 + Object obj;
57.169 + obj = array ? new JSONArray(tok) : new JSONObject(tok);
57.170 + json = convertToArray(obj);
57.171 + } catch (JSONException ex) {
57.172 + Reader r = new InputStreamReader(is, "UTF-8");
57.173 + StringBuilder sb = new StringBuilder();
57.174 + for (;;) {
57.175 + int ch = r.read();
57.176 + if (ch == -1) {
57.177 + break;
57.178 + }
57.179 + sb.append((char)ch);
57.180 + }
57.181 + json = sb.toString();
57.182 + }
57.183 + } catch (IOException ex) {
57.184 + error = ex;
57.185 + } finally {
57.186 + if (error != null) {
57.187 + call.notifyError(error);
57.188 + } else {
57.189 + call.notifySuccess(json);
57.190 + }
57.191 + }
57.192 + }
57.193 +
57.194 + static Object convertToArray(Object o) throws JSONException {
57.195 + if (o instanceof JSONArray) {
57.196 + JSONArray ja = (JSONArray)o;
57.197 + Object[] arr = new Object[ja.length()];
57.198 + for (int i = 0; i < arr.length; i++) {
57.199 + arr[i] = convertToArray(ja.get(i));
57.200 + }
57.201 + return arr;
57.202 + } else if (o instanceof JSONObject) {
57.203 + JSONObject obj = (JSONObject)o;
57.204 + Iterator it = obj.keys();
57.205 + while (it.hasNext()) {
57.206 + String key = (String)it.next();
57.207 + obj.put(key, convertToArray(obj.get(key)));
57.208 + }
57.209 + return obj;
57.210 + } else {
57.211 + return o;
57.212 + }
57.213 + }
57.214 +
57.215 + public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
57.216 + if (jsonObject instanceof JSONObject) {
57.217 + JSONObject obj = (JSONObject)jsonObject;
57.218 + for (int i = 0; i < props.length; i++) {
57.219 + try {
57.220 + values[i] = obj.has(props[i]) ? obj.get(props[i]) : null;
57.221 + } catch (JSONException ex) {
57.222 + LoadJSON.LOG.log(Level.SEVERE, "Can't read " + props[i] + " from " + jsonObject, ex);
57.223 + }
57.224 + }
57.225 + return;
57.226 + }
57.227 + for (int i = 0; i < props.length; i++) {
57.228 + values[i] = getProperty(jsonObject, props[i]);
57.229 + }
57.230 + }
57.231 +
57.232 + @JavaScriptBody(args = {"object", "property"},
57.233 + body
57.234 + = "if (property === null) return object;\n"
57.235 + + "if (object === null) return null;\n"
57.236 + + "var p = object[property]; return p ? p : null;"
57.237 + )
57.238 + private static Object getProperty(Object object, String property) {
57.239 + return null;
57.240 + }
57.241 +
57.242 + public static Object parse(InputStream is) throws IOException {
57.243 + try {
57.244 + InputStreamReader r = new InputStreamReader(is, "UTF-8");
57.245 + JSONTokener t = new JSONTokener(r);
57.246 + return new JSONObject(t);
57.247 + } catch (JSONException ex) {
57.248 + throw new IOException(ex);
57.249 + }
57.250 + }
57.251 +}
58.1 --- a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java Wed Jan 08 13:18:34 2014 +0100
58.2 +++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java Tue Jan 14 14:18:50 2014 +0100
58.3 @@ -43,6 +43,7 @@
58.4 package org.netbeans.html.wstyrus;
58.5
58.6 import java.io.IOException;
58.7 +import java.io.InputStream;
58.8 import java.net.URI;
58.9 import java.net.URISyntaxException;
58.10 import java.util.Iterator;
58.11 @@ -58,6 +59,8 @@
58.12 import net.java.html.json.OnReceive;
58.13 import org.apidesign.html.context.spi.Contexts;
58.14 import org.apidesign.html.json.spi.JSONCall;
58.15 +import org.apidesign.html.json.spi.Technology;
58.16 +import org.apidesign.html.json.spi.Transfer;
58.17 import org.apidesign.html.json.spi.WSTransfer;
58.18 import org.netbeans.html.wstyrus.TyrusContext.Comm;
58.19 import org.json.JSONArray;
58.20 @@ -82,12 +85,14 @@
58.21 * @author Jaroslav Tulach <jtulach@netbeans.org>
58.22 */
58.23 @ServiceProvider(service = Contexts.Provider.class)
58.24 -public final class TyrusContext implements Contexts.Provider, WSTransfer<Comm> {
58.25 +public final class TyrusContext
58.26 +implements Contexts.Provider, WSTransfer<Comm>, Transfer {
58.27 @Override
58.28 public void fillContext(Contexts.Builder context, Class<?> requestor) {
58.29 // default WebSocket transfer implementation is registered
58.30 // in ko-fx module with 100, provide this one as a fallback only
58.31 context.register(WSTransfer.class, this, 1000);
58.32 + context.register(Transfer.class, this, 1000);
58.33 }
58.34
58.35 @Override
58.36 @@ -115,6 +120,21 @@
58.37 socket.callback.notifyError(ex);
58.38 }
58.39 }
58.40 +
58.41 + @Override
58.42 + public void extract(Object obj, String[] props, Object[] values) {
58.43 + LoadJSON.extractJSON(obj, props, values);
58.44 + }
58.45 +
58.46 + @Override
58.47 + public Object toJSON(InputStream is) throws IOException {
58.48 + return LoadJSON.parse(is);
58.49 + }
58.50 +
58.51 + @Override
58.52 + public void loadJSON(JSONCall call) {
58.53 + LoadJSON.loadJSON(call);
58.54 + }
58.55
58.56 /** Implementation class in an implementation. Represents a {@link ClientEndpoint} of the
58.57 * WebSocket channel. You are unlikely to get on hold of it.
59.1 --- a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java Wed Jan 08 13:18:34 2014 +0100
59.2 +++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java Tue Jan 14 14:18:50 2014 +0100
59.3 @@ -58,7 +58,6 @@
59.4 import net.java.html.BrwsrCtx;
59.5 import net.java.html.boot.BrowserBuilder;
59.6 import net.java.html.js.JavaScriptBody;
59.7 -import org.netbeans.html.boot.impl.FnContext;
59.8 import org.apidesign.html.boot.spi.Fn;
59.9 import org.apidesign.html.context.spi.Contexts;
59.10 import org.apidesign.html.json.spi.Technology;
59.11 @@ -68,10 +67,11 @@
59.12 import org.apidesign.html.json.tck.KnockoutTCK;
59.13 import org.json.JSONException;
59.14 import org.json.JSONObject;
59.15 -import org.netbeans.html.kofx.FXContext;
59.16 +import org.netbeans.html.boot.impl.FnContext;
59.17 +import org.netbeans.html.ko4j.KO4J;
59.18 import org.openide.util.lookup.ServiceProvider;
59.19 +import static org.testng.Assert.*;
59.20 import org.testng.annotations.Factory;
59.21 -import static org.testng.Assert.*;
59.22
59.23 /**
59.24 *
59.25 @@ -146,11 +146,11 @@
59.26
59.27 @Override
59.28 public BrwsrCtx createContext() {
59.29 - FXContext fx = new FXContext(browserContext);
59.30 + KO4J ko = new KO4J(browserContext);
59.31 TyrusContext tc = new TyrusContext();
59.32 Contexts.Builder cb = Contexts.newBuilder().
59.33 - register(Technology.class, fx, 10).
59.34 - register(Transfer.class, fx, 10).
59.35 + register(Technology.class, ko.knockout(), 10).
59.36 + register(Transfer.class, tc, 10).
59.37 register(WSTransfer.class, tc, 10);
59.38 return cb.build();
59.39 }
60.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
60.2 +++ b/ko4j/pom.xml Tue Jan 14 14:18:50 2014 +0100
60.3 @@ -0,0 +1,104 @@
60.4 +<?xml version="1.0"?>
60.5 +<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">
60.6 + <modelVersion>4.0.0</modelVersion>
60.7 + <parent>
60.8 + <groupId>org.netbeans.html</groupId>
60.9 + <artifactId>pom</artifactId>
60.10 + <version>0.8-SNAPSHOT</version>
60.11 + </parent>
60.12 + <groupId>org.netbeans.html</groupId>
60.13 + <artifactId>ko4j</artifactId>
60.14 + <version>0.8-SNAPSHOT</version>
60.15 + <packaging>bundle</packaging>
60.16 + <name>Knockout.js for Java</name>
60.17 + <url>http://maven.apache.org</url>
60.18 + <properties>
60.19 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
60.20 + <bundleSymbolicName>org.netbeans.html.ko4j</bundleSymbolicName>
60.21 + </properties>
60.22 + <build>
60.23 + <plugins>
60.24 + <plugin>
60.25 + <groupId>org.apache.felix</groupId>
60.26 + <artifactId>maven-bundle-plugin</artifactId>
60.27 + </plugin>
60.28 + <plugin>
60.29 + <groupId>org.apache.maven.plugins</groupId>
60.30 + <artifactId>maven-javadoc-plugin</artifactId>
60.31 + <configuration>
60.32 + <skip>false</skip>
60.33 + </configuration>
60.34 + </plugin>
60.35 + <plugin>
60.36 + <groupId>org.apache.maven.plugins</groupId>
60.37 + <artifactId>maven-compiler-plugin</artifactId>
60.38 + <version>2.3.2</version>
60.39 + <configuration>
60.40 + <source>1.7</source>
60.41 + <target>1.7</target>
60.42 + </configuration>
60.43 + </plugin>
60.44 + </plugins>
60.45 + </build>
60.46 + <dependencies>
60.47 + <dependency>
60.48 + <groupId>org.netbeans.html</groupId>
60.49 + <artifactId>net.java.html.json</artifactId>
60.50 + <version>${project.version}</version>
60.51 + </dependency>
60.52 + <dependency>
60.53 + <groupId>org.testng</groupId>
60.54 + <artifactId>testng</artifactId>
60.55 + <scope>test</scope>
60.56 + </dependency>
60.57 + <dependency>
60.58 + <groupId>${project.groupId}</groupId>
60.59 + <artifactId>net.java.html.json.tck</artifactId>
60.60 + <version>${project.version}</version>
60.61 + <scope>test</scope>
60.62 + </dependency>
60.63 + <dependency>
60.64 + <groupId>org.netbeans.api</groupId>
60.65 + <artifactId>org-openide-util-lookup</artifactId>
60.66 + <scope>provided</scope>
60.67 + </dependency>
60.68 + <dependency>
60.69 + <groupId>org.netbeans.html</groupId>
60.70 + <artifactId>net.java.html.boot</artifactId>
60.71 + <version>${project.version}</version>
60.72 + <type>jar</type>
60.73 + </dependency>
60.74 + <dependency>
60.75 + <groupId>${project.groupId}</groupId>
60.76 + <artifactId>net.java.html.boot.fx</artifactId>
60.77 + <version>${project.version}</version>
60.78 + <scope>test</scope>
60.79 + </dependency>
60.80 + <dependency>
60.81 + <groupId>org.glassfish.grizzly</groupId>
60.82 + <artifactId>grizzly-http-server</artifactId>
60.83 + <version>${grizzly.version}</version>
60.84 + <scope>test</scope>
60.85 + </dependency>
60.86 + <dependency>
60.87 + <groupId>org.glassfish.grizzly</groupId>
60.88 + <artifactId>grizzly-websockets-server</artifactId>
60.89 + <version>${grizzly.version}</version>
60.90 + <scope>test</scope>
60.91 + <type>jar</type>
60.92 + </dependency>
60.93 + <dependency>
60.94 + <groupId>org.glassfish.grizzly</groupId>
60.95 + <artifactId>grizzly-http-servlet</artifactId>
60.96 + <version>${grizzly.version}</version>
60.97 + <scope>test</scope>
60.98 + </dependency>
60.99 + <dependency>
60.100 + <groupId>javax.servlet</groupId>
60.101 + <artifactId>javax.servlet-api</artifactId>
60.102 + <scope>test</scope>
60.103 + <version>3.1.0</version>
60.104 + </dependency>
60.105 + </dependencies>
60.106 + <description>Binds net.java.html.json APIs together with knockout.js</description>
60.107 +</project>
61.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
61.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java Tue Jan 14 14:18:50 2014 +0100
61.3 @@ -0,0 +1,262 @@
61.4 +/**
61.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
61.6 + *
61.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
61.8 + *
61.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
61.10 + * Other names may be trademarks of their respective owners.
61.11 + *
61.12 + * The contents of this file are subject to the terms of either the GNU
61.13 + * General Public License Version 2 only ("GPL") or the Common
61.14 + * Development and Distribution License("CDDL") (collectively, the
61.15 + * "License"). You may not use this file except in compliance with the
61.16 + * License. You can obtain a copy of the License at
61.17 + * http://www.netbeans.org/cddl-gplv2.html
61.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
61.19 + * specific language governing permissions and limitations under the
61.20 + * License. When distributing the software, include this License Header
61.21 + * Notice in each file and include the License file at
61.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
61.23 + * particular file as subject to the "Classpath" exception as provided
61.24 + * by Oracle in the GPL Version 2 section of the License file that
61.25 + * accompanied this code. If applicable, add the following below the
61.26 + * License Header, with the fields enclosed by brackets [] replaced by
61.27 + * your own identifying information:
61.28 + * "Portions Copyrighted [year] [name of copyright owner]"
61.29 + *
61.30 + * Contributor(s):
61.31 + *
61.32 + * The Original Software is NetBeans. The Initial Developer of the Original
61.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
61.34 + *
61.35 + * If you wish your version of this file to be governed by only the CDDL
61.36 + * or only the GPL Version 2, indicate your decision by adding
61.37 + * "[Contributor] elects to include this software in this distribution
61.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
61.39 + * single choice of license, a recipient has the option to distribute
61.40 + * your version of this file under either the CDDL, the GPL Version 2 or
61.41 + * to extend the choice of license to its licensees as provided above.
61.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
61.43 + * Version 2 license, then the option applies only if the new code is
61.44 + * made subject to such option by the copyright holder.
61.45 + */
61.46 +package org.netbeans.html.ko4j;
61.47 +
61.48 +import java.io.ByteArrayOutputStream;
61.49 +import java.io.Closeable;
61.50 +import java.io.IOException;
61.51 +import java.io.InputStream;
61.52 +import java.io.InputStreamReader;
61.53 +import java.io.Reader;
61.54 +import java.net.URL;
61.55 +import java.util.concurrent.Executor;
61.56 +import java.util.logging.Logger;
61.57 +import net.java.html.js.JavaScriptBody;
61.58 +import org.apidesign.html.boot.spi.Fn;
61.59 +import org.apidesign.html.json.spi.FunctionBinding;
61.60 +import org.apidesign.html.json.spi.JSONCall;
61.61 +import org.apidesign.html.json.spi.PropertyBinding;
61.62 +import org.apidesign.html.json.spi.Technology;
61.63 +import org.apidesign.html.json.spi.Transfer;
61.64 +import org.apidesign.html.json.spi.WSTransfer;
61.65 +
61.66 +/** This is an implementation package - just
61.67 + * include its JAR on classpath and use official {@link Context} API
61.68 + * to access the functionality.
61.69 + * <p>
61.70 + *
61.71 + * @author Jaroslav Tulach <jtulach@netbeans.org>
61.72 + */
61.73 +final class FXContext
61.74 +implements Technology.BatchInit<Object>, Transfer, WSTransfer<LoadWS> {
61.75 + static final Logger LOG = Logger.getLogger(FXContext.class.getName());
61.76 + private static Boolean javaScriptEnabled;
61.77 + private final Fn.Presenter browserContext;
61.78 +
61.79 + public FXContext(Fn.Presenter browserContext) {
61.80 + this.browserContext = browserContext;
61.81 + }
61.82 +
61.83 + @JavaScriptBody(args = {}, body = "if (window) return true; else return false;")
61.84 + private static boolean isJavaScriptEnabledJs() {
61.85 + return false;
61.86 + }
61.87 +
61.88 + static boolean isJavaScriptEnabled() {
61.89 + if (javaScriptEnabled != null) {
61.90 + return javaScriptEnabled;
61.91 + }
61.92 + if (!(javaScriptEnabled = isJavaScriptEnabledJs())) {
61.93 + Closeable c = Fn.activate(new TrueFn());
61.94 + try {
61.95 + javaScriptEnabled = isJavaScriptEnabledJs();
61.96 + } finally {
61.97 + try {
61.98 + c.close();
61.99 + } catch (IOException ex) {
61.100 + // cannot happen
61.101 + }
61.102 + }
61.103 + }
61.104 + return javaScriptEnabled;
61.105 + }
61.106 +
61.107 + final boolean areWebSocketsSupported() {
61.108 + return LoadWS.isSupported();
61.109 + }
61.110 +
61.111 +
61.112 + @Override
61.113 + public Object wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr) {
61.114 + String[] propNames = new String[propArr.length];
61.115 + boolean[] propReadOnly = new boolean[propArr.length];
61.116 + Object[] propValues = new Object[propArr.length];
61.117 + for (int i = 0; i < propNames.length; i++) {
61.118 + propNames[i] = propArr[i].getPropertyName();
61.119 + propReadOnly[i] = propArr[i].isReadOnly();
61.120 + propValues[i] = propArr[i].getValue();
61.121 + }
61.122 + String[] funcNames = new String[funcArr.length];
61.123 + for (int i = 0; i < funcNames.length; i++) {
61.124 + funcNames[i] = funcArr[i].getFunctionName();
61.125 + }
61.126 + Object ret = Knockout.wrapModel(model,
61.127 + propNames, propReadOnly, propValues, propArr,
61.128 + funcNames, funcArr
61.129 + );
61.130 + return ret;
61.131 + }
61.132 +
61.133 + @Override
61.134 + public Object wrapModel(Object model) {
61.135 + throw new UnsupportedOperationException();
61.136 + }
61.137 +
61.138 + @Override
61.139 + public void bind(PropertyBinding b, Object model, Object data) {
61.140 + throw new UnsupportedOperationException();
61.141 + }
61.142 +
61.143 + @Override
61.144 + public void valueHasMutated(Object data, String propertyName) {
61.145 + Knockout.valueHasMutated(data, propertyName);
61.146 + }
61.147 +
61.148 + @Override
61.149 + public void expose(FunctionBinding fb, Object model, Object d) {
61.150 + throw new UnsupportedOperationException();
61.151 + }
61.152 +
61.153 + @Override
61.154 + public void applyBindings(Object data) {
61.155 + Knockout.applyBindings(data);
61.156 + }
61.157 +
61.158 + @Override
61.159 + public Object wrapArray(Object[] arr) {
61.160 + return arr;
61.161 + }
61.162 +
61.163 + @Override
61.164 + public void extract(Object obj, String[] props, Object[] values) {
61.165 + LoadJSON.extractJSON(obj, props, values);
61.166 + }
61.167 +
61.168 + @Override
61.169 + public void loadJSON(final JSONCall call) {
61.170 + if (call.isJSONP()) {
61.171 + String me = LoadJSON.createJSONP(call);
61.172 + LoadJSON.loadJSONP(call.composeURL(me), me);
61.173 + } else {
61.174 + String data = null;
61.175 + if (call.isDoOutput()) {
61.176 + try {
61.177 + ByteArrayOutputStream bos = new ByteArrayOutputStream();
61.178 + call.writeData(bos);
61.179 + data = new String(bos.toByteArray(), "UTF-8");
61.180 + } catch (IOException ex) {
61.181 + call.notifyError(ex);
61.182 + }
61.183 + }
61.184 + LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data);
61.185 + }
61.186 + }
61.187 +
61.188 + @Override
61.189 + public <M> M toModel(Class<M> modelClass, Object data) {
61.190 + return modelClass.cast(Knockout.toModel(data));
61.191 + }
61.192 +
61.193 + @Override
61.194 + public Object toJSON(InputStream is) throws IOException {
61.195 + StringBuilder sb = new StringBuilder();
61.196 + InputStreamReader r = new InputStreamReader(is);
61.197 + for (;;) {
61.198 + int ch = r.read();
61.199 + if (ch == -1) {
61.200 + break;
61.201 + }
61.202 + sb.append((char)ch);
61.203 + }
61.204 + return LoadJSON.parse(sb.toString());
61.205 + }
61.206 +
61.207 + @Override
61.208 + public void runSafe(final Runnable r) {
61.209 + class Wrap implements Runnable {
61.210 + @Override public void run() {
61.211 + Closeable c = Fn.activate(browserContext);
61.212 + try {
61.213 + r.run();
61.214 + } finally {
61.215 + try {
61.216 + c.close();
61.217 + } catch (IOException ex) {
61.218 + // cannot be thrown
61.219 + }
61.220 + }
61.221 + }
61.222 + }
61.223 + Wrap w = new Wrap();
61.224 + if (browserContext instanceof Executor) {
61.225 + ((Executor)browserContext).execute(w);
61.226 + } else {
61.227 + w.run();
61.228 + }
61.229 + }
61.230 +
61.231 + @Override
61.232 + public LoadWS open(String url, JSONCall onReply) {
61.233 + return new LoadWS(onReply, url);
61.234 + }
61.235 +
61.236 + @Override
61.237 + public void send(LoadWS socket, JSONCall data) {
61.238 + socket.send(data);
61.239 + }
61.240 +
61.241 + @Override
61.242 + public void close(LoadWS socket) {
61.243 + socket.close();
61.244 + }
61.245 +
61.246 + private static final class TrueFn extends Fn implements Fn.Presenter {
61.247 + @Override
61.248 + public Object invoke(Object thiz, Object... args) throws Exception {
61.249 + return true;
61.250 + }
61.251 +
61.252 + @Override
61.253 + public Fn defineFn(String code, String... names) {
61.254 + return this;
61.255 + }
61.256 +
61.257 + @Override
61.258 + public void displayPage(URL page, Runnable onPageLoad) {
61.259 + }
61.260 +
61.261 + @Override
61.262 + public void loadScript(Reader code) throws Exception {
61.263 + }
61.264 + } // end of TrueFn
61.265 +}
62.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
62.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java Tue Jan 14 14:18:50 2014 +0100
62.3 @@ -0,0 +1,127 @@
62.4 +/**
62.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
62.6 + *
62.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
62.8 + *
62.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
62.10 + * Other names may be trademarks of their respective owners.
62.11 + *
62.12 + * The contents of this file are subject to the terms of either the GNU
62.13 + * General Public License Version 2 only ("GPL") or the Common
62.14 + * Development and Distribution License("CDDL") (collectively, the
62.15 + * "License"). You may not use this file except in compliance with the
62.16 + * License. You can obtain a copy of the License at
62.17 + * http://www.netbeans.org/cddl-gplv2.html
62.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
62.19 + * specific language governing permissions and limitations under the
62.20 + * License. When distributing the software, include this License Header
62.21 + * Notice in each file and include the License file at
62.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
62.23 + * particular file as subject to the "Classpath" exception as provided
62.24 + * by Oracle in the GPL Version 2 section of the License file that
62.25 + * accompanied this code. If applicable, add the following below the
62.26 + * License Header, with the fields enclosed by brackets [] replaced by
62.27 + * your own identifying information:
62.28 + * "Portions Copyrighted [year] [name of copyright owner]"
62.29 + *
62.30 + * Contributor(s):
62.31 + *
62.32 + * The Original Software is NetBeans. The Initial Developer of the Original
62.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
62.34 + *
62.35 + * If you wish your version of this file to be governed by only the CDDL
62.36 + * or only the GPL Version 2, indicate your decision by adding
62.37 + * "[Contributor] elects to include this software in this distribution
62.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
62.39 + * single choice of license, a recipient has the option to distribute
62.40 + * your version of this file under either the CDDL, the GPL Version 2 or
62.41 + * to extend the choice of license to its licensees as provided above.
62.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
62.43 + * Version 2 license, then the option applies only if the new code is
62.44 + * made subject to such option by the copyright holder.
62.45 + */
62.46 +package org.netbeans.html.ko4j;
62.47 +
62.48 +import net.java.html.json.Model;
62.49 +import org.apidesign.html.boot.spi.Fn;
62.50 +import org.apidesign.html.context.spi.Contexts;
62.51 +import org.apidesign.html.json.spi.Technology;
62.52 +import org.apidesign.html.json.spi.Transfer;
62.53 +import org.apidesign.html.json.spi.WSTransfer;
62.54 +import org.openide.util.lookup.ServiceProvider;
62.55 +
62.56 +/** Support for <a href="http://knockoutjs.com">knockout.js</a>
62.57 + * and its Java binding via {@link Model model classes}.
62.58 + * Registers {@link ContextProvider}, so {@link ServiceLoader} can find it.
62.59 + *
62.60 + * @author Jaroslav Tulach <jtulach@netbeans.org>
62.61 + * @since 0.7
62.62 + */
62.63 +@ServiceProvider(service = Contexts.Provider.class)
62.64 +public final class KO4J implements Contexts.Provider {
62.65 + private final Fn.Presenter presenter;
62.66 + private FXContext c;
62.67 +
62.68 + public KO4J() {
62.69 + this(null);
62.70 + }
62.71 +
62.72 + public KO4J(Fn.Presenter presenter) {
62.73 + this.presenter = presenter;
62.74 + }
62.75 +
62.76 + private FXContext getKO() {
62.77 + if (c == null) {
62.78 + c = new FXContext(presenter == null ? Fn.activePresenter() : presenter);
62.79 + }
62.80 + return c;
62.81 + }
62.82 +
62.83 + /** Return instance of the knockout.js for Java technology.
62.84 + * @return non-null instance
62.85 + */
62.86 + public Technology knockout() {
62.87 + return getKO();
62.88 + }
62.89 +
62.90 + /** Browser based implementation of transfer interface. Uses
62.91 + * browser method to convert string to JSON.
62.92 + *
62.93 + * @return non-null instance
62.94 + */
62.95 + public Transfer transfer() {
62.96 + return getKO();
62.97 + }
62.98 +
62.99 + /** Returns browser based implementation of websocket transfer.
62.100 + * If available (for example JavaFX WebView on JDK7 does not have
62.101 + * this implementation).
62.102 + *
62.103 + * @return an instance or <code>null</code>, if there is no
62.104 + * <code>WebSocket</code> object in the browser
62.105 + */
62.106 + public WSTransfer<?> websockets() {
62.107 + return getKO().areWebSocketsSupported() ? getKO() : null;
62.108 + }
62.109 +
62.110 + /** Registers technologies at position 100:
62.111 + * <ul>
62.112 + * <li>{@link #knockout()}</li>
62.113 + * <li>{@link #transfer()}</li>
62.114 + * <li>{@link #websockets()()} - if browser supports web sockets</li>
62.115 + * </ul>
62.116 + * @param context the context to register to
62.117 + * @param requestor the class requesting the registration
62.118 + */
62.119 + @Override
62.120 + public void fillContext(Contexts.Builder context, Class<?> requestor) {
62.121 + if (FXContext.isJavaScriptEnabled()) {
62.122 + context.register(Technology.class, knockout(), 100);
62.123 + context.register(Transfer.class, transfer(), 100);
62.124 + if (c.areWebSocketsSupported()) {
62.125 + context.register(WSTransfer.class, websockets(), 100);
62.126 + }
62.127 + }
62.128 + }
62.129 +
62.130 +}
63.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
63.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java Tue Jan 14 14:18:50 2014 +0100
63.3 @@ -0,0 +1,134 @@
63.4 +/**
63.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
63.6 + *
63.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
63.8 + *
63.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
63.10 + * Other names may be trademarks of their respective owners.
63.11 + *
63.12 + * The contents of this file are subject to the terms of either the GNU
63.13 + * General Public License Version 2 only ("GPL") or the Common
63.14 + * Development and Distribution License("CDDL") (collectively, the
63.15 + * "License"). You may not use this file except in compliance with the
63.16 + * License. You can obtain a copy of the License at
63.17 + * http://www.netbeans.org/cddl-gplv2.html
63.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
63.19 + * specific language governing permissions and limitations under the
63.20 + * License. When distributing the software, include this License Header
63.21 + * Notice in each file and include the License file at
63.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
63.23 + * particular file as subject to the "Classpath" exception as provided
63.24 + * by Oracle in the GPL Version 2 section of the License file that
63.25 + * accompanied this code. If applicable, add the following below the
63.26 + * License Header, with the fields enclosed by brackets [] replaced by
63.27 + * your own identifying information:
63.28 + * "Portions Copyrighted [year] [name of copyright owner]"
63.29 + *
63.30 + * Contributor(s):
63.31 + *
63.32 + * The Original Software is NetBeans. The Initial Developer of the Original
63.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
63.34 + *
63.35 + * If you wish your version of this file to be governed by only the CDDL
63.36 + * or only the GPL Version 2, indicate your decision by adding
63.37 + * "[Contributor] elects to include this software in this distribution
63.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
63.39 + * single choice of license, a recipient has the option to distribute
63.40 + * your version of this file under either the CDDL, the GPL Version 2 or
63.41 + * to extend the choice of license to its licensees as provided above.
63.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
63.43 + * Version 2 license, then the option applies only if the new code is
63.44 + * made subject to such option by the copyright holder.
63.45 + */
63.46 +package org.netbeans.html.ko4j;
63.47 +
63.48 +import net.java.html.js.JavaScriptBody;
63.49 +import net.java.html.js.JavaScriptResource;
63.50 +import net.java.html.json.Model;
63.51 +import org.apidesign.html.json.spi.FunctionBinding;
63.52 +import org.apidesign.html.json.spi.PropertyBinding;
63.53 +
63.54 +/** This is an implementation package - just
63.55 + * include its JAR on classpath and use official {@link Context} API
63.56 + * to access the functionality.
63.57 + * <p>
63.58 + * Provides binding between {@link Model models} and knockout.js running
63.59 + * inside a JavaFX WebView.
63.60 + *
63.61 + * @author Jaroslav Tulach <jtulach@netbeans.org>
63.62 + */
63.63 +@JavaScriptResource("knockout-2.2.1.js")
63.64 +final class Knockout {
63.65 + static {
63.66 + loadKnockout();
63.67 + }
63.68 +
63.69 + @JavaScriptBody(args = { }, body = "")
63.70 + private static native void loadKnockout();
63.71 +
63.72 + @JavaScriptBody(args = { "model", "prop" }, body =
63.73 + "if (model) {\n"
63.74 + + " var koProp = model[prop];\n"
63.75 + + " if (koProp && koProp['valueHasMutated']) {\n"
63.76 + + " koProp['valueHasMutated']();\n"
63.77 + + " }\n"
63.78 + + "}\n"
63.79 + )
63.80 + public native static void valueHasMutated(Object model, String prop);
63.81 +
63.82 + @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);\n")
63.83 + native static void applyBindings(Object bindings);
63.84 +
63.85 + @JavaScriptBody(
63.86 + javacall = true,
63.87 + args = {"model", "propNames", "propReadOnly", "propValues", "propArr", "funcNames", "funcArr"},
63.88 + body
63.89 + = "var ret = {};\n"
63.90 + + "ret['ko-fx.model'] = model;\n"
63.91 + + "function koComputed(name, readOnly, value, prop) {\n"
63.92 + + " function realGetter() {\n"
63.93 + + " try {\n"
63.94 + + " var v = prop.@org.apidesign.html.json.spi.PropertyBinding::getValue()();\n"
63.95 + + " return v;\n"
63.96 + + " } catch (e) {\n"
63.97 + + " alert(\"Cannot call getValue on \" + model + \" prop: \" + name + \" error: \" + e);\n"
63.98 + + " }\n"
63.99 + + " }\n"
63.100 + + " var activeGetter = function() { return value; };\n"
63.101 + + " var bnd = {\n"
63.102 + + " read: function() {\n"
63.103 + + " var r = activeGetter();\n"
63.104 + + " activeGetter = realGetter;\n"
63.105 + + " return r == null ? null : r.valueOf();\n"
63.106 + + " },\n"
63.107 + + " owner: ret\n"
63.108 + + " };\n"
63.109 + + " if (!readOnly) {\n"
63.110 + + " bnd.write = function(val) {\n"
63.111 + + " prop.@org.apidesign.html.json.spi.PropertyBinding::setValue(Ljava/lang/Object;)(val);\n"
63.112 + + " };\n"
63.113 + + " };\n"
63.114 + + " ret[name] = ko.computed(bnd);\n"
63.115 + + "}\n"
63.116 + + "for (var i = 0; i < propNames.length; i++) {\n"
63.117 + + " koComputed(propNames[i], propReadOnly[i], propValues[i], propArr[i]);\n"
63.118 + + "}\n"
63.119 + + "function koExpose(name, func) {\n"
63.120 + + " ret[name] = function(data, ev) {\n"
63.121 + + " func.@org.apidesign.html.json.spi.FunctionBinding::call(Ljava/lang/Object;Ljava/lang/Object;)(data, ev);\n"
63.122 + + " };\n"
63.123 + + "}\n"
63.124 + + "for (var i = 0; i < funcNames.length; i++) {\n"
63.125 + + " koExpose(funcNames[i], funcArr[i]);\n"
63.126 + + "}\n"
63.127 + + "return ret;\n"
63.128 + )
63.129 + static native Object wrapModel(
63.130 + Object model,
63.131 + String[] propNames, boolean[] propReadOnly, Object propValues, PropertyBinding[] propArr,
63.132 + String[] funcNames, FunctionBinding[] funcArr
63.133 + );
63.134 +
63.135 + @JavaScriptBody(args = { "o" }, body = "return o['ko-fx.model'] ? o['ko-fx.model'] : o;")
63.136 + static native Object toModel(Object wrapper);
63.137 +}
64.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
64.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java Tue Jan 14 14:18:50 2014 +0100
64.3 @@ -0,0 +1,208 @@
64.4 +/**
64.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
64.6 + *
64.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
64.8 + *
64.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
64.10 + * Other names may be trademarks of their respective owners.
64.11 + *
64.12 + * The contents of this file are subject to the terms of either the GNU
64.13 + * General Public License Version 2 only ("GPL") or the Common
64.14 + * Development and Distribution License("CDDL") (collectively, the
64.15 + * "License"). You may not use this file except in compliance with the
64.16 + * License. You can obtain a copy of the License at
64.17 + * http://www.netbeans.org/cddl-gplv2.html
64.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
64.19 + * specific language governing permissions and limitations under the
64.20 + * License. When distributing the software, include this License Header
64.21 + * Notice in each file and include the License file at
64.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
64.23 + * particular file as subject to the "Classpath" exception as provided
64.24 + * by Oracle in the GPL Version 2 section of the License file that
64.25 + * accompanied this code. If applicable, add the following below the
64.26 + * License Header, with the fields enclosed by brackets [] replaced by
64.27 + * your own identifying information:
64.28 + * "Portions Copyrighted [year] [name of copyright owner]"
64.29 + *
64.30 + * Contributor(s):
64.31 + *
64.32 + * The Original Software is NetBeans. The Initial Developer of the Original
64.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
64.34 + *
64.35 + * If you wish your version of this file to be governed by only the CDDL
64.36 + * or only the GPL Version 2, indicate your decision by adding
64.37 + * "[Contributor] elects to include this software in this distribution
64.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
64.39 + * single choice of license, a recipient has the option to distribute
64.40 + * your version of this file under either the CDDL, the GPL Version 2 or
64.41 + * to extend the choice of license to its licensees as provided above.
64.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
64.43 + * Version 2 license, then the option applies only if the new code is
64.44 + * made subject to such option by the copyright holder.
64.45 + */
64.46 +package org.netbeans.html.ko4j;
64.47 +
64.48 +import java.io.ByteArrayOutputStream;
64.49 +import java.io.IOException;
64.50 +import java.io.InputStream;
64.51 +import java.io.InputStreamReader;
64.52 +import net.java.html.js.JavaScriptBody;
64.53 +import org.apidesign.html.json.spi.JSONCall;
64.54 +import org.apidesign.html.json.spi.Transfer;
64.55 +import org.apidesign.html.json.spi.WSTransfer;
64.56 +
64.57 +/**
64.58 + *
64.59 + * @author Jaroslav Tulach <jtulach@netbeans.org>
64.60 + */
64.61 +final class LoadJSON implements Transfer, WSTransfer<LoadWS> {
64.62 + private LoadJSON() {}
64.63 +
64.64 + @Override
64.65 + public void extract(Object obj, String[] props, Object[] values) {
64.66 + extractJSON(obj, props, values);
64.67 + }
64.68 +
64.69 + @Override
64.70 + public void loadJSON(final JSONCall call) {
64.71 + if (call.isJSONP()) {
64.72 + String me = createJSONP(call);
64.73 + loadJSONP(call.composeURL(me), me);
64.74 + } else {
64.75 + String data = null;
64.76 + if (call.isDoOutput()) {
64.77 + try {
64.78 + ByteArrayOutputStream bos = new ByteArrayOutputStream();
64.79 + call.writeData(bos);
64.80 + data = new String(bos.toByteArray(), "UTF-8");
64.81 + } catch (IOException ex) {
64.82 + call.notifyError(ex);
64.83 + }
64.84 + }
64.85 + loadJSON(call.composeURL(null), call, call.getMethod(), data);
64.86 + }
64.87 + }
64.88 +
64.89 + @Override
64.90 + public Object toJSON(InputStream is) throws IOException {
64.91 + StringBuilder sb = new StringBuilder();
64.92 + InputStreamReader r = new InputStreamReader(is);
64.93 + for (;;) {
64.94 + int ch = r.read();
64.95 + if (ch == -1) {
64.96 + break;
64.97 + }
64.98 + sb.append((char)ch);
64.99 + }
64.100 + return parse(sb.toString());
64.101 + }
64.102 +
64.103 + @Override
64.104 + public LoadWS open(String url, JSONCall callback) {
64.105 + return new LoadWS(callback, url);
64.106 + }
64.107 +
64.108 + @Override
64.109 + public void send(LoadWS socket, JSONCall data) {
64.110 + socket.send(data);
64.111 + }
64.112 +
64.113 + @Override
64.114 + public void close(LoadWS socket) {
64.115 + socket.close();
64.116 + }
64.117 +
64.118 + //
64.119 + // implementations
64.120 + //
64.121 +
64.122 + @JavaScriptBody(args = {"object", "property"},
64.123 + body
64.124 + = "if (property === null) return object;\n"
64.125 + + "if (object === null) return null;\n"
64.126 + + "var p = object[property]; return p ? p : null;"
64.127 + )
64.128 + private static Object getProperty(Object object, String property) {
64.129 + return null;
64.130 + }
64.131 +
64.132 + static String createJSONP(JSONCall whenDone) {
64.133 + int h = whenDone.hashCode();
64.134 + String name;
64.135 + for (;;) {
64.136 + name = "jsonp" + Integer.toHexString(h);
64.137 + if (defineIfUnused(name, whenDone)) {
64.138 + return name;
64.139 + }
64.140 + h++;
64.141 + }
64.142 + }
64.143 +
64.144 + @JavaScriptBody(args = {"name", "done"}, javacall = true, body
64.145 + = "if (window[name]) return false;\n "
64.146 + + "window[name] = function(data) {\n "
64.147 + + " delete window[name];\n"
64.148 + + " var el = window.document.getElementById(name);\n"
64.149 + + " el.parentNode.removeChild(el);\n"
64.150 + + " done.@org.apidesign.html.json.spi.JSONCall::notifySuccess(Ljava/lang/Object;)(data);\n"
64.151 + + "};\n"
64.152 + + "return true;\n"
64.153 + )
64.154 + private static boolean defineIfUnused(String name, JSONCall done) {
64.155 + return true;
64.156 + }
64.157 +
64.158 + @JavaScriptBody(args = {"s"}, body = "return eval('(' + s + ')');")
64.159 + static Object parse(String s) {
64.160 + return s;
64.161 + }
64.162 +
64.163 + @JavaScriptBody(args = {"url", "done", "method", "data"}, javacall = true, body = ""
64.164 + + "var request = new XMLHttpRequest();\n"
64.165 + + "if (!method) method = 'GET';\n"
64.166 + + "request.open(method, url, true);\n"
64.167 + + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
64.168 + + "request.onreadystatechange = function() {\n"
64.169 + + " if (this.readyState!==4) return;\n"
64.170 + + " try {\n"
64.171 + + " var r = this.response;\n"
64.172 + + " try { r = eval('(' + this.response + ')'); } catch (ignore) { }"
64.173 + + " done.@org.apidesign.html.json.spi.JSONCall::notifySuccess(Ljava/lang/Object;)(r);\n"
64.174 + + " } catch (error) {;\n"
64.175 + + " @org.netbeans.html.ko4j.LoadJSON::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, this.response);\n"
64.176 + + " }\n"
64.177 + + "};\n"
64.178 + + "request.onerror = function (e) {console.log('eeeor:' + Object.getOwnPropertyNames(e));\n"
64.179 + + " @org.netbeans.html.ko4j.LoadJSON::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, e);\n"
64.180 + + "}\n"
64.181 + + "if (data) request.send(data);"
64.182 + + "else request.send();"
64.183 + )
64.184 + static void loadJSON(
64.185 + String url, JSONCall done, String method, String data
64.186 + ) {
64.187 + }
64.188 +
64.189 + static void notifyError(Object done, Object msg) {
64.190 + ((JSONCall)done).notifyError(new Exception(msg.toString()));
64.191 + }
64.192 +
64.193 + @JavaScriptBody(args = {"url", "jsonp"}, body
64.194 + = "var scrpt = window.document.createElement('script');\n "
64.195 + + "scrpt.setAttribute('src', url);\n "
64.196 + + "scrpt.setAttribute('id', jsonp);\n "
64.197 + + "scrpt.setAttribute('type', 'text/javascript');\n "
64.198 + + "var body = document.getElementsByTagName('body')[0];\n "
64.199 + + "body.appendChild(scrpt);\n"
64.200 + )
64.201 + static void loadJSONP(String url, String jsonp) {
64.202 +
64.203 + }
64.204 +
64.205 + static void extractJSON(Object jsonObject, String[] props, Object[] values) {
64.206 + for (int i = 0; i < props.length; i++) {
64.207 + values[i] = getProperty(jsonObject, props[i]);
64.208 + }
64.209 + }
64.210 +
64.211 +}
65.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
65.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadWS.java Tue Jan 14 14:18:50 2014 +0100
65.3 @@ -0,0 +1,151 @@
65.4 +/**
65.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
65.6 + *
65.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
65.8 + *
65.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
65.10 + * Other names may be trademarks of their respective owners.
65.11 + *
65.12 + * The contents of this file are subject to the terms of either the GNU
65.13 + * General Public License Version 2 only ("GPL") or the Common
65.14 + * Development and Distribution License("CDDL") (collectively, the
65.15 + * "License"). You may not use this file except in compliance with the
65.16 + * License. You can obtain a copy of the License at
65.17 + * http://www.netbeans.org/cddl-gplv2.html
65.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
65.19 + * specific language governing permissions and limitations under the
65.20 + * License. When distributing the software, include this License Header
65.21 + * Notice in each file and include the License file at
65.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
65.23 + * particular file as subject to the "Classpath" exception as provided
65.24 + * by Oracle in the GPL Version 2 section of the License file that
65.25 + * accompanied this code. If applicable, add the following below the
65.26 + * License Header, with the fields enclosed by brackets [] replaced by
65.27 + * your own identifying information:
65.28 + * "Portions Copyrighted [year] [name of copyright owner]"
65.29 + *
65.30 + * Contributor(s):
65.31 + *
65.32 + * The Original Software is NetBeans. The Initial Developer of the Original
65.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
65.34 + *
65.35 + * If you wish your version of this file to be governed by only the CDDL
65.36 + * or only the GPL Version 2, indicate your decision by adding
65.37 + * "[Contributor] elects to include this software in this distribution
65.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
65.39 + * single choice of license, a recipient has the option to distribute
65.40 + * your version of this file under either the CDDL, the GPL Version 2 or
65.41 + * to extend the choice of license to its licensees as provided above.
65.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
65.43 + * Version 2 license, then the option applies only if the new code is
65.44 + * made subject to such option by the copyright holder.
65.45 + */
65.46 +package org.netbeans.html.ko4j;
65.47 +
65.48 +import net.java.html.js.JavaScriptBody;
65.49 +import org.apidesign.html.json.spi.JSONCall;
65.50 +
65.51 +/** Communication with WebSockets via browser's WebSocket object.
65.52 + *
65.53 + * @author Jaroslav Tulach <jtulach@netbeans.org>
65.54 + */
65.55 +final class LoadWS {
65.56 + private static final boolean SUPPORTED = isWebSocket();
65.57 + private final Object ws;
65.58 + private final JSONCall call;
65.59 + LoadWS(JSONCall first, String url) {
65.60 + call = first;
65.61 + ws = initWebSocket(this, url);
65.62 + if (ws == null) {
65.63 + first.notifyError(new IllegalArgumentException("Wrong URL: " + url));
65.64 + }
65.65 + }
65.66 +
65.67 + static boolean isSupported() {
65.68 + return SUPPORTED;
65.69 + }
65.70 +
65.71 + void send(JSONCall call) {
65.72 + push(call);
65.73 + }
65.74 +
65.75 + private synchronized void push(JSONCall call) {
65.76 + send(ws, call.getMessage());
65.77 + }
65.78 +
65.79 + void onOpen(Object ev) {
65.80 + if (!call.isDoOutput()) {
65.81 + call.notifySuccess(null);
65.82 + }
65.83 + }
65.84 +
65.85 +
65.86 + @JavaScriptBody(args = { "data" }, body = "try {\n"
65.87 + + " return eval('(' + data + ')');\n"
65.88 + + " } catch (error) {;\n"
65.89 + + " return data;\n"
65.90 + + " }\n"
65.91 + )
65.92 + private static native Object toJSON(String data);
65.93 +
65.94 + void onMessage(Object ev, String data) {
65.95 + Object json = toJSON(data);
65.96 + call.notifySuccess(json);
65.97 + }
65.98 +
65.99 + void onError(Object ev) {
65.100 + call.notifyError(new Exception(ev.toString()));
65.101 + }
65.102 +
65.103 + void onClose(boolean wasClean, int code, String reason) {
65.104 + call.notifyError(null);
65.105 + }
65.106 +
65.107 + @JavaScriptBody(args = {}, body = "if (window.WebSocket) return true; else return false;")
65.108 + private static boolean isWebSocket() {
65.109 + return false;
65.110 + }
65.111 +
65.112 + @JavaScriptBody(args = { "back", "url" }, javacall = true, body = ""
65.113 + + "if (window.WebSocket) {\n"
65.114 + + " try {\n"
65.115 + + " var ws = new window.WebSocket(url);\n"
65.116 + + " ws.onopen = function(ev) {\n"
65.117 + + " back.@org.netbeans.html.ko4j.LoadWS::onOpen(Ljava/lang/Object;)(ev);\n"
65.118 + + " };\n"
65.119 + + " ws.onmessage = function(ev) {\n"
65.120 + + " back.@org.netbeans.html.ko4j.LoadWS::onMessage(Ljava/lang/Object;Ljava/lang/String;)(ev, ev.data);\n"
65.121 + + " };\n"
65.122 + + " ws.onerror = function(ev) {\n"
65.123 + + " back.@org.netbeans.html.ko4j.LoadWS::onError(Ljava/lang/Object;)(ev);\n"
65.124 + + " };\n"
65.125 + + " ws.onclose = function(ev) {\n"
65.126 + + " back.@org.netbeans.html.ko4j.LoadWS::onClose(ZILjava/lang/String;)(ev.wasClean, ev.code, ev.reason);\n"
65.127 + + " };\n"
65.128 + + " return ws;\n"
65.129 + + " } catch (ex) {\n"
65.130 + + " return null;\n"
65.131 + + " }\n"
65.132 + + "} else {\n"
65.133 + + " return null;\n"
65.134 + + "}\n"
65.135 + )
65.136 + private static Object initWebSocket(Object back, String url) {
65.137 + return null;
65.138 + }
65.139 +
65.140 +
65.141 + @JavaScriptBody(args = { "ws", "msg" }, body = ""
65.142 + + "ws.send(msg);"
65.143 + )
65.144 + private void send(Object ws, String msg) {
65.145 + }
65.146 +
65.147 + @JavaScriptBody(args = { "ws" }, body = "ws.close();")
65.148 + private static void close(Object ws) {
65.149 + }
65.150 +
65.151 + void close() {
65.152 + close(ws);
65.153 + }
65.154 +}
66.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
66.2 +++ b/ko4j/src/main/resources/org/netbeans/html/ko4j/knockout-2.2.1.js Tue Jan 14 14:18:50 2014 +0100
66.3 @@ -0,0 +1,3594 @@
66.4 +// Knockout JavaScript library v2.2.1
66.5 +// (c) Steven Sanderson - http://knockoutjs.com/
66.6 +// License: MIT (http://www.opensource.org/licenses/mit-license.php)
66.7 +
66.8 +(function(){
66.9 +var DEBUG=true;
66.10 +(function(window,document,navigator,jQuery,undefined){
66.11 +!function(factory) {
66.12 + // Support three module loading scenarios
66.13 + if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
66.14 + // [1] CommonJS/Node.js
66.15 + var target = module['exports'] || exports; // module.exports is for Node.js
66.16 + factory(target);
66.17 + } else if (typeof define === 'function' && define['amd']) {
66.18 + // [2] AMD anonymous module
66.19 + define(['exports'], factory);
66.20 + } else {
66.21 + // [3] No module loader (plain <script> tag) - put directly in global namespace
66.22 + factory(window['ko'] = {});
66.23 + }
66.24 +}(function(koExports){
66.25 +// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
66.26 +// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
66.27 +var ko = typeof koExports !== 'undefined' ? koExports : {};
66.28 +// Google Closure Compiler helpers (used only to make the minified file smaller)
66.29 +ko.exportSymbol = function(koPath, object) {
66.30 + var tokens = koPath.split(".");
66.31 +
66.32 + // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
66.33 + // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
66.34 + var target = ko;
66.35 +
66.36 + for (var i = 0; i < tokens.length - 1; i++)
66.37 + target = target[tokens[i]];
66.38 + target[tokens[tokens.length - 1]] = object;
66.39 +};
66.40 +ko.exportProperty = function(owner, publicName, object) {
66.41 + owner[publicName] = object;
66.42 +};
66.43 +ko.version = "2.2.1";
66.44 +
66.45 +ko.exportSymbol('version', ko.version);
66.46 +ko.utils = new (function () {
66.47 + var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
66.48 +
66.49 + // Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
66.50 + var knownEvents = {}, knownEventTypesByEventName = {};
66.51 + var keyEventTypeName = /Firefox\/2/i.test(navigator.userAgent) ? 'KeyboardEvent' : 'UIEvents';
66.52 + knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
66.53 + knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
66.54 + for (var eventType in knownEvents) {
66.55 + var knownEventsForType = knownEvents[eventType];
66.56 + if (knownEventsForType.length) {
66.57 + for (var i = 0, j = knownEventsForType.length; i < j; i++)
66.58 + knownEventTypesByEventName[knownEventsForType[i]] = eventType;
66.59 + }
66.60 + }
66.61 + var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
66.62 +
66.63 + // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
66.64 + // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
66.65 + // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
66.66 + // If there is a future need to detect specific versions of IE10+, we will amend this.
66.67 + var ieVersion = (function() {
66.68 + var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
66.69 +
66.70 + // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
66.71 + while (
66.72 + div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
66.73 + iElems[0]
66.74 + );
66.75 + return version > 4 ? version : undefined;
66.76 + }());
66.77 + var isIe6 = ieVersion === 6,
66.78 + isIe7 = ieVersion === 7;
66.79 +
66.80 + function isClickOnCheckableElement(element, eventType) {
66.81 + if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
66.82 + if (eventType.toLowerCase() != "click") return false;
66.83 + var inputType = element.type;
66.84 + return (inputType == "checkbox") || (inputType == "radio");
66.85 + }
66.86 +
66.87 + return {
66.88 + fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
66.89 +
66.90 + arrayForEach: function (array, action) {
66.91 + for (var i = 0, j = array.length; i < j; i++)
66.92 + action(array[i]);
66.93 + },
66.94 +
66.95 + arrayIndexOf: function (array, item) {
66.96 + if (typeof Array.prototype.indexOf == "function")
66.97 + return Array.prototype.indexOf.call(array, item);
66.98 + for (var i = 0, j = array.length; i < j; i++)
66.99 + if (array[i] === item)
66.100 + return i;
66.101 + return -1;
66.102 + },
66.103 +
66.104 + arrayFirst: function (array, predicate, predicateOwner) {
66.105 + for (var i = 0, j = array.length; i < j; i++)
66.106 + if (predicate.call(predicateOwner, array[i]))
66.107 + return array[i];
66.108 + return null;
66.109 + },
66.110 +
66.111 + arrayRemoveItem: function (array, itemToRemove) {
66.112 + var index = ko.utils.arrayIndexOf(array, itemToRemove);
66.113 + if (index >= 0)
66.114 + array.splice(index, 1);
66.115 + },
66.116 +
66.117 + arrayGetDistinctValues: function (array) {
66.118 + array = array || [];
66.119 + var result = [];
66.120 + for (var i = 0, j = array.length; i < j; i++) {
66.121 + if (ko.utils.arrayIndexOf(result, array[i]) < 0)
66.122 + result.push(array[i]);
66.123 + }
66.124 + return result;
66.125 + },
66.126 +
66.127 + arrayMap: function (array, mapping) {
66.128 + array = array || [];
66.129 + var result = [];
66.130 + for (var i = 0, j = array.length; i < j; i++)
66.131 + result.push(mapping(array[i]));
66.132 + return result;
66.133 + },
66.134 +
66.135 + arrayFilter: function (array, predicate) {
66.136 + array = array || [];
66.137 + var result = [];
66.138 + for (var i = 0, j = array.length; i < j; i++)
66.139 + if (predicate(array[i]))
66.140 + result.push(array[i]);
66.141 + return result;
66.142 + },
66.143 +
66.144 + arrayPushAll: function (array, valuesToPush) {
66.145 + if (valuesToPush instanceof Array)
66.146 + array.push.apply(array, valuesToPush);
66.147 + else
66.148 + for (var i = 0, j = valuesToPush.length; i < j; i++)
66.149 + array.push(valuesToPush[i]);
66.150 + return array;
66.151 + },
66.152 +
66.153 + extend: function (target, source) {
66.154 + if (source) {
66.155 + for(var prop in source) {
66.156 + if(source.hasOwnProperty(prop)) {
66.157 + target[prop] = source[prop];
66.158 + }
66.159 + }
66.160 + }
66.161 + return target;
66.162 + },
66.163 +
66.164 + emptyDomNode: function (domNode) {
66.165 + while (domNode.firstChild) {
66.166 + ko.removeNode(domNode.firstChild);
66.167 + }
66.168 + },
66.169 +
66.170 + moveCleanedNodesToContainerElement: function(nodes) {
66.171 + // Ensure it's a real array, as we're about to reparent the nodes and
66.172 + // we don't want the underlying collection to change while we're doing that.
66.173 + var nodesArray = ko.utils.makeArray(nodes);
66.174 +
66.175 + var container = document.createElement('div');
66.176 + for (var i = 0, j = nodesArray.length; i < j; i++) {
66.177 + container.appendChild(ko.cleanNode(nodesArray[i]));
66.178 + }
66.179 + return container;
66.180 + },
66.181 +
66.182 + cloneNodes: function (nodesArray, shouldCleanNodes) {
66.183 + for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
66.184 + var clonedNode = nodesArray[i].cloneNode(true);
66.185 + newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
66.186 + }
66.187 + return newNodesArray;
66.188 + },
66.189 +
66.190 + setDomNodeChildren: function (domNode, childNodes) {
66.191 + ko.utils.emptyDomNode(domNode);
66.192 + if (childNodes) {
66.193 + for (var i = 0, j = childNodes.length; i < j; i++)
66.194 + domNode.appendChild(childNodes[i]);
66.195 + }
66.196 + },
66.197 +
66.198 + replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
66.199 + var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
66.200 + if (nodesToReplaceArray.length > 0) {
66.201 + var insertionPoint = nodesToReplaceArray[0];
66.202 + var parent = insertionPoint.parentNode;
66.203 + for (var i = 0, j = newNodesArray.length; i < j; i++)
66.204 + parent.insertBefore(newNodesArray[i], insertionPoint);
66.205 + for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
66.206 + ko.removeNode(nodesToReplaceArray[i]);
66.207 + }
66.208 + }
66.209 + },
66.210 +
66.211 + setOptionNodeSelectionState: function (optionNode, isSelected) {
66.212 + // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
66.213 + if (ieVersion < 7)
66.214 + optionNode.setAttribute("selected", isSelected);
66.215 + else
66.216 + optionNode.selected = isSelected;
66.217 + },
66.218 +
66.219 + stringTrim: function (string) {
66.220 + return (string || "").replace(stringTrimRegex, "");
66.221 + },
66.222 +
66.223 + stringTokenize: function (string, delimiter) {
66.224 + var result = [];
66.225 + var tokens = (string || "").split(delimiter);
66.226 + for (var i = 0, j = tokens.length; i < j; i++) {
66.227 + var trimmed = ko.utils.stringTrim(tokens[i]);
66.228 + if (trimmed !== "")
66.229 + result.push(trimmed);
66.230 + }
66.231 + return result;
66.232 + },
66.233 +
66.234 + stringStartsWith: function (string, startsWith) {
66.235 + string = string || "";
66.236 + if (startsWith.length > string.length)
66.237 + return false;
66.238 + return string.substring(0, startsWith.length) === startsWith;
66.239 + },
66.240 +
66.241 + domNodeIsContainedBy: function (node, containedByNode) {
66.242 + if (containedByNode.compareDocumentPosition)
66.243 + return (containedByNode.compareDocumentPosition(node) & 16) == 16;
66.244 + while (node != null) {
66.245 + if (node == containedByNode)
66.246 + return true;
66.247 + node = node.parentNode;
66.248 + }
66.249 + return false;
66.250 + },
66.251 +
66.252 + domNodeIsAttachedToDocument: function (node) {
66.253 + return ko.utils.domNodeIsContainedBy(node, node.ownerDocument);
66.254 + },
66.255 +
66.256 + tagNameLower: function(element) {
66.257 + // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
66.258 + // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
66.259 + // we don't need to do the .toLowerCase() as it will always be lower case anyway.
66.260 + return element && element.tagName && element.tagName.toLowerCase();
66.261 + },
66.262 +
66.263 + registerEventHandler: function (element, eventType, handler) {
66.264 + var mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
66.265 + if (!mustUseAttachEvent && typeof jQuery != "undefined") {
66.266 + if (isClickOnCheckableElement(element, eventType)) {
66.267 + // For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
66.268 + // it toggles the element checked state *after* the click event handlers run, whereas native
66.269 + // click events toggle the checked state *before* the event handler.
66.270 + // Fix this by intecepting the handler and applying the correct checkedness before it runs.
66.271 + var originalHandler = handler;
66.272 + handler = function(event, eventData) {
66.273 + var jQuerySuppliedCheckedState = this.checked;
66.274 + if (eventData)
66.275 + this.checked = eventData.checkedStateBeforeEvent !== true;
66.276 + originalHandler.call(this, event);
66.277 + this.checked = jQuerySuppliedCheckedState; // Restore the state jQuery applied
66.278 + };
66.279 + }
66.280 + jQuery(element)['bind'](eventType, handler);
66.281 + } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
66.282 + element.addEventListener(eventType, handler, false);
66.283 + else if (typeof element.attachEvent != "undefined")
66.284 + element.attachEvent("on" + eventType, function (event) {
66.285 + handler.call(element, event);
66.286 + });
66.287 + else
66.288 + throw new Error("Browser doesn't support addEventListener or attachEvent");
66.289 + },
66.290 +
66.291 + triggerEvent: function (element, eventType) {
66.292 + if (!(element && element.nodeType))
66.293 + throw new Error("element must be a DOM node when calling triggerEvent");
66.294 +
66.295 + if (typeof jQuery != "undefined") {
66.296 + var eventData = [];
66.297 + if (isClickOnCheckableElement(element, eventType)) {
66.298 + // Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
66.299 + eventData.push({ checkedStateBeforeEvent: element.checked });
66.300 + }
66.301 + jQuery(element)['trigger'](eventType, eventData);
66.302 + } else if (typeof document.createEvent == "function") {
66.303 + if (typeof element.dispatchEvent == "function") {
66.304 + var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
66.305 + var event = document.createEvent(eventCategory);
66.306 + event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
66.307 + element.dispatchEvent(event);
66.308 + }
66.309 + else
66.310 + throw new Error("The supplied element doesn't support dispatchEvent");
66.311 + } else if (typeof element.fireEvent != "undefined") {
66.312 + // Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
66.313 + // so to make it consistent, we'll do it manually here
66.314 + if (isClickOnCheckableElement(element, eventType))
66.315 + element.checked = element.checked !== true;
66.316 + element.fireEvent("on" + eventType);
66.317 + }
66.318 + else
66.319 + throw new Error("Browser doesn't support triggering events");
66.320 + },
66.321 +
66.322 + unwrapObservable: function (value) {
66.323 + return ko.isObservable(value) ? value() : value;
66.324 + },
66.325 +
66.326 + peekObservable: function (value) {
66.327 + return ko.isObservable(value) ? value.peek() : value;
66.328 + },
66.329 +
66.330 + toggleDomNodeCssClass: function (node, classNames, shouldHaveClass) {
66.331 + if (classNames) {
66.332 + var cssClassNameRegex = /[\w-]+/g,
66.333 + currentClassNames = node.className.match(cssClassNameRegex) || [];
66.334 + ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
66.335 + var indexOfClass = ko.utils.arrayIndexOf(currentClassNames, className);
66.336 + if (indexOfClass >= 0) {
66.337 + if (!shouldHaveClass)
66.338 + currentClassNames.splice(indexOfClass, 1);
66.339 + } else {
66.340 + if (shouldHaveClass)
66.341 + currentClassNames.push(className);
66.342 + }
66.343 + });
66.344 + node.className = currentClassNames.join(" ");
66.345 + }
66.346 + },
66.347 +
66.348 + setTextContent: function(element, textContent) {
66.349 + var value = ko.utils.unwrapObservable(textContent);
66.350 + if ((value === null) || (value === undefined))
66.351 + value = "";
66.352 +
66.353 + if (element.nodeType === 3) {
66.354 + element.data = value;
66.355 + } else {
66.356 + // We need there to be exactly one child: a text node.
66.357 + // If there are no children, more than one, or if it's not a text node,
66.358 + // we'll clear everything and create a single text node.
66.359 + var innerTextNode = ko.virtualElements.firstChild(element);
66.360 + if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
66.361 + ko.virtualElements.setDomNodeChildren(element, [document.createTextNode(value)]);
66.362 + } else {
66.363 + innerTextNode.data = value;
66.364 + }
66.365 +
66.366 + ko.utils.forceRefresh(element);
66.367 + }
66.368 + },
66.369 +
66.370 + setElementName: function(element, name) {
66.371 + element.name = name;
66.372 +
66.373 + // Workaround IE 6/7 issue
66.374 + // - https://github.com/SteveSanderson/knockout/issues/197
66.375 + // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
66.376 + if (ieVersion <= 7) {
66.377 + try {
66.378 + element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
66.379 + }
66.380 + catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
66.381 + }
66.382 + },
66.383 +
66.384 + forceRefresh: function(node) {
66.385 + // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
66.386 + if (ieVersion >= 9) {
66.387 + // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
66.388 + var elem = node.nodeType == 1 ? node : node.parentNode;
66.389 + if (elem.style)
66.390 + elem.style.zoom = elem.style.zoom;
66.391 + }
66.392 + },
66.393 +
66.394 + ensureSelectElementIsRenderedCorrectly: function(selectElement) {
66.395 + // Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width.
66.396 + // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
66.397 + if (ieVersion >= 9) {
66.398 + var originalWidth = selectElement.style.width;
66.399 + selectElement.style.width = 0;
66.400 + selectElement.style.width = originalWidth;
66.401 + }
66.402 + },
66.403 +
66.404 + range: function (min, max) {
66.405 + min = ko.utils.unwrapObservable(min);
66.406 + max = ko.utils.unwrapObservable(max);
66.407 + var result = [];
66.408 + for (var i = min; i <= max; i++)
66.409 + result.push(i);
66.410 + return result;
66.411 + },
66.412 +
66.413 + makeArray: function(arrayLikeObject) {
66.414 + var result = [];
66.415 + for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
66.416 + result.push(arrayLikeObject[i]);
66.417 + };
66.418 + return result;
66.419 + },
66.420 +
66.421 + isIe6 : isIe6,
66.422 + isIe7 : isIe7,
66.423 + ieVersion : ieVersion,
66.424 +
66.425 + getFormFields: function(form, fieldName) {
66.426 + var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
66.427 + var isMatchingField = (typeof fieldName == 'string')
66.428 + ? function(field) { return field.name === fieldName }
66.429 + : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
66.430 + var matches = [];
66.431 + for (var i = fields.length - 1; i >= 0; i--) {
66.432 + if (isMatchingField(fields[i]))
66.433 + matches.push(fields[i]);
66.434 + };
66.435 + return matches;
66.436 + },
66.437 +
66.438 + parseJson: function (jsonString) {
66.439 + if (typeof jsonString == "string") {
66.440 + jsonString = ko.utils.stringTrim(jsonString);
66.441 + if (jsonString) {
66.442 + if (window.JSON && window.JSON.parse) // Use native parsing where available
66.443 + return window.JSON.parse(jsonString);
66.444 + return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
66.445 + }
66.446 + }
66.447 + return null;
66.448 + },
66.449 +
66.450 + stringifyJson: function (data, replacer, space) { // replacer and space are optional
66.451 + if ((typeof JSON == "undefined") || (typeof JSON.stringify == "undefined"))
66.452 + throw new Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
66.453 + return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
66.454 + },
66.455 +
66.456 + postJson: function (urlOrForm, data, options) {
66.457 + options = options || {};
66.458 + var params = options['params'] || {};
66.459 + var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
66.460 + var url = urlOrForm;
66.461 +
66.462 + // If we were given a form, use its 'action' URL and pick out any requested field values
66.463 + if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
66.464 + var originalForm = urlOrForm;
66.465 + url = originalForm.action;
66.466 + for (var i = includeFields.length - 1; i >= 0; i--) {
66.467 + var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
66.468 + for (var j = fields.length - 1; j >= 0; j--)
66.469 + params[fields[j].name] = fields[j].value;
66.470 + }
66.471 + }
66.472 +
66.473 + data = ko.utils.unwrapObservable(data);
66.474 + var form = document.createElement("form");
66.475 + form.style.display = "none";
66.476 + form.action = url;
66.477 + form.method = "post";
66.478 + for (var key in data) {
66.479 + var input = document.createElement("input");
66.480 + input.name = key;
66.481 + input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
66.482 + form.appendChild(input);
66.483 + }
66.484 + for (var key in params) {
66.485 + var input = document.createElement("input");
66.486 + input.name = key;
66.487 + input.value = params[key];
66.488 + form.appendChild(input);
66.489 + }
66.490 + document.body.appendChild(form);
66.491 + options['submitter'] ? options['submitter'](form) : form.submit();
66.492 + setTimeout(function () { form.parentNode.removeChild(form); }, 0);
66.493 + }
66.494 + }
66.495 +})();
66.496 +
66.497 +ko.exportSymbol('utils', ko.utils);
66.498 +ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
66.499 +ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
66.500 +ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
66.501 +ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
66.502 +ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
66.503 +ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
66.504 +ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
66.505 +ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
66.506 +ko.exportSymbol('utils.extend', ko.utils.extend);
66.507 +ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
66.508 +ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
66.509 +ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
66.510 +ko.exportSymbol('utils.postJson', ko.utils.postJson);
66.511 +ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
66.512 +ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
66.513 +ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
66.514 +ko.exportSymbol('utils.range', ko.utils.range);
66.515 +ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
66.516 +ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
66.517 +ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
66.518 +
66.519 +if (!Function.prototype['bind']) {
66.520 + // Function.prototype.bind is a standard part of ECMAScript 5th Edition (December 2009, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)
66.521 + // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
66.522 + Function.prototype['bind'] = function (object) {
66.523 + var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
66.524 + return function () {
66.525 + return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
66.526 + };
66.527 + };
66.528 +}
66.529 +
66.530 +ko.utils.domData = new (function () {
66.531 + var uniqueId = 0;
66.532 + var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
66.533 + var dataStore = {};
66.534 + return {
66.535 + get: function (node, key) {
66.536 + var allDataForNode = ko.utils.domData.getAll(node, false);
66.537 + return allDataForNode === undefined ? undefined : allDataForNode[key];
66.538 + },
66.539 + set: function (node, key, value) {
66.540 + if (value === undefined) {
66.541 + // Make sure we don't actually create a new domData key if we are actually deleting a value
66.542 + if (ko.utils.domData.getAll(node, false) === undefined)
66.543 + return;
66.544 + }
66.545 + var allDataForNode = ko.utils.domData.getAll(node, true);
66.546 + allDataForNode[key] = value;
66.547 + },
66.548 + getAll: function (node, createIfNotFound) {
66.549 + var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
66.550 + var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
66.551 + if (!hasExistingDataStore) {
66.552 + if (!createIfNotFound)
66.553 + return undefined;
66.554 + dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
66.555 + dataStore[dataStoreKey] = {};
66.556 + }
66.557 + return dataStore[dataStoreKey];
66.558 + },
66.559 + clear: function (node) {
66.560 + var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
66.561 + if (dataStoreKey) {
66.562 + delete dataStore[dataStoreKey];
66.563 + node[dataStoreKeyExpandoPropertyName] = null;
66.564 + return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
66.565 + }
66.566 + return false;
66.567 + }
66.568 + }
66.569 +})();
66.570 +
66.571 +ko.exportSymbol('utils.domData', ko.utils.domData);
66.572 +ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully
66.573 +
66.574 +ko.utils.domNodeDisposal = new (function () {
66.575 + var domDataKey = "__ko_domNodeDisposal__" + (new Date).getTime();
66.576 + var cleanableNodeTypes = { 1: true, 8: true, 9: true }; // Element, Comment, Document
66.577 + var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
66.578 +
66.579 + function getDisposeCallbacksCollection(node, createIfNotFound) {
66.580 + var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
66.581 + if ((allDisposeCallbacks === undefined) && createIfNotFound) {
66.582 + allDisposeCallbacks = [];
66.583 + ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
66.584 + }
66.585 + return allDisposeCallbacks;
66.586 + }
66.587 + function destroyCallbacksCollection(node) {
66.588 + ko.utils.domData.set(node, domDataKey, undefined);
66.589 + }
66.590 +
66.591 + function cleanSingleNode(node) {
66.592 + // Run all the dispose callbacks
66.593 + var callbacks = getDisposeCallbacksCollection(node, false);
66.594 + if (callbacks) {
66.595 + callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
66.596 + for (var i = 0; i < callbacks.length; i++)
66.597 + callbacks[i](node);
66.598 + }
66.599 +
66.600 + // Also erase the DOM data
66.601 + ko.utils.domData.clear(node);
66.602 +
66.603 + // Special support for jQuery here because it's so commonly used.
66.604 + // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
66.605 + // so notify it to tear down any resources associated with the node & descendants here.
66.606 + if ((typeof jQuery == "function") && (typeof jQuery['cleanData'] == "function"))
66.607 + jQuery['cleanData']([node]);
66.608 +
66.609 + // Also clear any immediate-child comment nodes, as these wouldn't have been found by
66.610 + // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
66.611 + if (cleanableNodeTypesWithDescendants[node.nodeType])
66.612 + cleanImmediateCommentTypeChildren(node);
66.613 + }
66.614 +
66.615 + function cleanImmediateCommentTypeChildren(nodeWithChildren) {
66.616 + var child, nextChild = nodeWithChildren.firstChild;
66.617 + while (child = nextChild) {
66.618 + nextChild = child.nextSibling;
66.619 + if (child.nodeType === 8)
66.620 + cleanSingleNode(child);
66.621 + }
66.622 + }
66.623 +
66.624 + return {
66.625 + addDisposeCallback : function(node, callback) {
66.626 + if (typeof callback != "function")
66.627 + throw new Error("Callback must be a function");
66.628 + getDisposeCallbacksCollection(node, true).push(callback);
66.629 + },
66.630 +
66.631 + removeDisposeCallback : function(node, callback) {
66.632 + var callbacksCollection = getDisposeCallbacksCollection(node, false);
66.633 + if (callbacksCollection) {
66.634 + ko.utils.arrayRemoveItem(callbacksCollection, callback);
66.635 + if (callbacksCollection.length == 0)
66.636 + destroyCallbacksCollection(node);
66.637 + }
66.638 + },
66.639 +
66.640 + cleanNode : function(node) {
66.641 + // First clean this node, where applicable
66.642 + if (cleanableNodeTypes[node.nodeType]) {
66.643 + cleanSingleNode(node);
66.644 +
66.645 + // ... then its descendants, where applicable
66.646 + if (cleanableNodeTypesWithDescendants[node.nodeType]) {
66.647 + // Clone the descendants list in case it changes during iteration
66.648 + var descendants = [];
66.649 + ko.utils.arrayPushAll(descendants, node.getElementsByTagName("*"));
66.650 + for (var i = 0, j = descendants.length; i < j; i++)
66.651 + cleanSingleNode(descendants[i]);
66.652 + }
66.653 + }
66.654 + return node;
66.655 + },
66.656 +
66.657 + removeNode : function(node) {
66.658 + ko.cleanNode(node);
66.659 + if (node.parentNode)
66.660 + node.parentNode.removeChild(node);
66.661 + }
66.662 + }
66.663 +})();
66.664 +ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
66.665 +ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
66.666 +ko.exportSymbol('cleanNode', ko.cleanNode);
66.667 +ko.exportSymbol('removeNode', ko.removeNode);
66.668 +ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
66.669 +ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
66.670 +ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
66.671 +(function () {
66.672 + var leadingCommentRegex = /^(\s*)<!--(.*?)-->/;
66.673 +
66.674 + function simpleHtmlParse(html) {
66.675 + // Based on jQuery's "clean" function, but only accounting for table-related elements.
66.676 + // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
66.677 +
66.678 + // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
66.679 + // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
66.680 + // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
66.681 + // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
66.682 +
66.683 + // Trim whitespace, otherwise indexOf won't work as expected
66.684 + var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
66.685 +
66.686 + // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
66.687 + var wrap = tags.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] ||
66.688 + !tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] ||
66.689 + (!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
66.690 + /* anything else */ [0, "", ""];
66.691 +
66.692 + // Go to html and back, then peel off extra wrappers
66.693 + // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
66.694 + var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
66.695 + if (typeof window['innerShiv'] == "function") {
66.696 + div.appendChild(window['innerShiv'](markup));
66.697 + } else {
66.698 + div.innerHTML = markup;
66.699 + }
66.700 +
66.701 + // Move to the right depth
66.702 + while (wrap[0]--)
66.703 + div = div.lastChild;
66.704 +
66.705 + return ko.utils.makeArray(div.lastChild.childNodes);
66.706 + }
66.707 +
66.708 + function jQueryHtmlParse(html) {
66.709 + // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
66.710 + if (jQuery['parseHTML']) {
66.711 + return jQuery['parseHTML'](html);
66.712 + } else {
66.713 + // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
66.714 + var elems = jQuery['clean']([html]);
66.715 +
66.716 + // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
66.717 + // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
66.718 + // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
66.719 + if (elems && elems[0]) {
66.720 + // Find the top-most parent element that's a direct child of a document fragment
66.721 + var elem = elems[0];
66.722 + while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
66.723 + elem = elem.parentNode;
66.724 + // ... then detach it
66.725 + if (elem.parentNode)
66.726 + elem.parentNode.removeChild(elem);
66.727 + }
66.728 +
66.729 + return elems;
66.730 + }
66.731 + }
66.732 +
66.733 + ko.utils.parseHtmlFragment = function(html) {
66.734 + return typeof jQuery != 'undefined' ? jQueryHtmlParse(html) // As below, benefit from jQuery's optimisations where possible
66.735 + : simpleHtmlParse(html); // ... otherwise, this simple logic will do in most common cases.
66.736 + };
66.737 +
66.738 + ko.utils.setHtml = function(node, html) {
66.739 + ko.utils.emptyDomNode(node);
66.740 +
66.741 + // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
66.742 + html = ko.utils.unwrapObservable(html);
66.743 +
66.744 + if ((html !== null) && (html !== undefined)) {
66.745 + if (typeof html != 'string')
66.746 + html = html.toString();
66.747 +
66.748 + // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
66.749 + // for example <tr> elements which are not normally allowed to exist on their own.
66.750 + // If you've referenced jQuery we'll use that rather than duplicating its code.
66.751 + if (typeof jQuery != 'undefined') {
66.752 + jQuery(node)['html'](html);
66.753 + } else {
66.754 + // ... otherwise, use KO's own parsing logic.
66.755 + var parsedNodes = ko.utils.parseHtmlFragment(html);
66.756 + for (var i = 0; i < parsedNodes.length; i++)
66.757 + node.appendChild(parsedNodes[i]);
66.758 + }
66.759 + }
66.760 + };
66.761 +})();
66.762 +
66.763 +ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
66.764 +ko.exportSymbol('utils.setHtml', ko.utils.setHtml);
66.765 +
66.766 +ko.memoization = (function () {
66.767 + var memos = {};
66.768 +
66.769 + function randomMax8HexChars() {
66.770 + return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
66.771 + }
66.772 + function generateRandomId() {
66.773 + return randomMax8HexChars() + randomMax8HexChars();
66.774 + }
66.775 + function findMemoNodes(rootNode, appendToArray) {
66.776 + if (!rootNode)
66.777 + return;
66.778 + if (rootNode.nodeType == 8) {
66.779 + var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
66.780 + if (memoId != null)
66.781 + appendToArray.push({ domNode: rootNode, memoId: memoId });
66.782 + } else if (rootNode.nodeType == 1) {
66.783 + for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
66.784 + findMemoNodes(childNodes[i], appendToArray);
66.785 + }
66.786 + }
66.787 +
66.788 + return {
66.789 + memoize: function (callback) {
66.790 + if (typeof callback != "function")
66.791 + throw new Error("You can only pass a function to ko.memoization.memoize()");
66.792 + var memoId = generateRandomId();
66.793 + memos[memoId] = callback;
66.794 + return "<!--[ko_memo:" + memoId + "]-->";
66.795 + },
66.796 +
66.797 + unmemoize: function (memoId, callbackParams) {
66.798 + var callback = memos[memoId];
66.799 + if (callback === undefined)
66.800 + throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
66.801 + try {
66.802 + callback.apply(null, callbackParams || []);
66.803 + return true;
66.804 + }
66.805 + finally { delete memos[memoId]; }
66.806 + },
66.807 +
66.808 + unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
66.809 + var memos = [];
66.810 + findMemoNodes(domNode, memos);
66.811 + for (var i = 0, j = memos.length; i < j; i++) {
66.812 + var node = memos[i].domNode;
66.813 + var combinedParams = [node];
66.814 + if (extraCallbackParamsArray)
66.815 + ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
66.816 + ko.memoization.unmemoize(memos[i].memoId, combinedParams);
66.817 + node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
66.818 + if (node.parentNode)
66.819 + node.parentNode.removeChild(node); // If possible, erase it totally (not always possible - someone else might just hold a reference to it then call unmemoizeDomNodeAndDescendants again)
66.820 + }
66.821 + },
66.822 +
66.823 + parseMemoText: function (memoText) {
66.824 + var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
66.825 + return match ? match[1] : null;
66.826 + }
66.827 + };
66.828 +})();
66.829 +
66.830 +ko.exportSymbol('memoization', ko.memoization);
66.831 +ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
66.832 +ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
66.833 +ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
66.834 +ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
66.835 +ko.extenders = {
66.836 + 'throttle': function(target, timeout) {
66.837 + // Throttling means two things:
66.838 +
66.839 + // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
66.840 + // notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
66.841 + target['throttleEvaluation'] = timeout;
66.842 +
66.843 + // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
66.844 + // so the target cannot change value synchronously or faster than a certain rate
66.845 + var writeTimeoutInstance = null;
66.846 + return ko.dependentObservable({
66.847 + 'read': target,
66.848 + 'write': function(value) {
66.849 + clearTimeout(writeTimeoutInstance);
66.850 + writeTimeoutInstance = setTimeout(function() {
66.851 + target(value);
66.852 + }, timeout);
66.853 + }
66.854 + });
66.855 + },
66.856 +
66.857 + 'notify': function(target, notifyWhen) {
66.858 + target["equalityComparer"] = notifyWhen == "always"
66.859 + ? function() { return false } // Treat all values as not equal
66.860 + : ko.observable["fn"]["equalityComparer"];
66.861 + return target;
66.862 + }
66.863 +};
66.864 +
66.865 +function applyExtenders(requestedExtenders) {
66.866 + var target = this;
66.867 + if (requestedExtenders) {
66.868 + for (var key in requestedExtenders) {
66.869 + var extenderHandler = ko.extenders[key];
66.870 + if (typeof extenderHandler == 'function') {
66.871 + target = extenderHandler(target, requestedExtenders[key]);
66.872 + }
66.873 + }
66.874 + }
66.875 + return target;
66.876 +}
66.877 +
66.878 +ko.exportSymbol('extenders', ko.extenders);
66.879 +
66.880 +ko.subscription = function (target, callback, disposeCallback) {
66.881 + this.target = target;
66.882 + this.callback = callback;
66.883 + this.disposeCallback = disposeCallback;
66.884 + ko.exportProperty(this, 'dispose', this.dispose);
66.885 +};
66.886 +ko.subscription.prototype.dispose = function () {
66.887 + this.isDisposed = true;
66.888 + this.disposeCallback();
66.889 +};
66.890 +
66.891 +ko.subscribable = function () {
66.892 + this._subscriptions = {};
66.893 +
66.894 + ko.utils.extend(this, ko.subscribable['fn']);
66.895 + ko.exportProperty(this, 'subscribe', this.subscribe);
66.896 + ko.exportProperty(this, 'extend', this.extend);
66.897 + ko.exportProperty(this, 'getSubscriptionsCount', this.getSubscriptionsCount);
66.898 +}
66.899 +
66.900 +var defaultEvent = "change";
66.901 +
66.902 +ko.subscribable['fn'] = {
66.903 + subscribe: function (callback, callbackTarget, event) {
66.904 + event = event || defaultEvent;
66.905 + var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
66.906 +
66.907 + var subscription = new ko.subscription(this, boundCallback, function () {
66.908 + ko.utils.arrayRemoveItem(this._subscriptions[event], subscription);
66.909 + }.bind(this));
66.910 +
66.911 + if (!this._subscriptions[event])
66.912 + this._subscriptions[event] = [];
66.913 + this._subscriptions[event].push(subscription);
66.914 + return subscription;
66.915 + },
66.916 +
66.917 + "notifySubscribers": function (valueToNotify, event) {
66.918 + event = event || defaultEvent;
66.919 + if (this._subscriptions[event]) {
66.920 + ko.dependencyDetection.ignore(function() {
66.921 + ko.utils.arrayForEach(this._subscriptions[event].slice(0), function (subscription) {
66.922 + // In case a subscription was disposed during the arrayForEach cycle, check
66.923 + // for isDisposed on each subscription before invoking its callback
66.924 + if (subscription && (subscription.isDisposed !== true))
66.925 + subscription.callback(valueToNotify);
66.926 + });
66.927 + }, this);
66.928 + }
66.929 + },
66.930 +
66.931 + getSubscriptionsCount: function () {
66.932 + var total = 0;
66.933 + for (var eventName in this._subscriptions) {
66.934 + if (this._subscriptions.hasOwnProperty(eventName))
66.935 + total += this._subscriptions[eventName].length;
66.936 + }
66.937 + return total;
66.938 + },
66.939 +
66.940 + extend: applyExtenders
66.941 +};
66.942 +
66.943 +
66.944 +ko.isSubscribable = function (instance) {
66.945 + return typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
66.946 +};
66.947 +
66.948 +ko.exportSymbol('subscribable', ko.subscribable);
66.949 +ko.exportSymbol('isSubscribable', ko.isSubscribable);
66.950 +
66.951 +ko.dependencyDetection = (function () {
66.952 + var _frames = [];
66.953 +
66.954 + return {
66.955 + begin: function (callback) {
66.956 + _frames.push({ callback: callback, distinctDependencies:[] });
66.957 + },
66.958 +
66.959 + end: function () {
66.960 + _frames.pop();
66.961 + },
66.962 +
66.963 + registerDependency: function (subscribable) {
66.964 + if (!ko.isSubscribable(subscribable))
66.965 + throw new Error("Only subscribable things can act as dependencies");
66.966 + if (_frames.length > 0) {
66.967 + var topFrame = _frames[_frames.length - 1];
66.968 + if (!topFrame || ko.utils.arrayIndexOf(topFrame.distinctDependencies, subscribable) >= 0)
66.969 + return;
66.970 + topFrame.distinctDependencies.push(subscribable);
66.971 + topFrame.callback(subscribable);
66.972 + }
66.973 + },
66.974 +
66.975 + ignore: function(callback, callbackTarget, callbackArgs) {
66.976 + try {
66.977 + _frames.push(null);
66.978 + return callback.apply(callbackTarget, callbackArgs || []);
66.979 + } finally {
66.980 + _frames.pop();
66.981 + }
66.982 + }
66.983 + };
66.984 +})();
66.985 +var primitiveTypes = { 'undefined':true, 'boolean':true, 'number':true, 'string':true };
66.986 +
66.987 +ko.observable = function (initialValue) {
66.988 + var _latestValue = initialValue;
66.989 +
66.990 + function observable() {
66.991 + if (arguments.length > 0) {
66.992 + // Write
66.993 +
66.994 + // Ignore writes if the value hasn't changed
66.995 + if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
66.996 + observable.valueWillMutate();
66.997 + _latestValue = arguments[0];
66.998 + if (DEBUG) observable._latestValue = _latestValue;
66.999 + observable.valueHasMutated();
66.1000 + }
66.1001 + return this; // Permits chained assignments
66.1002 + }
66.1003 + else {
66.1004 + // Read
66.1005 + ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
66.1006 + return _latestValue;
66.1007 + }
66.1008 + }
66.1009 + if (DEBUG) observable._latestValue = _latestValue;
66.1010 + ko.subscribable.call(observable);
66.1011 + observable.peek = function() { return _latestValue };
66.1012 + observable.valueHasMutated = function () { observable["notifySubscribers"](_latestValue); }
66.1013 + observable.valueWillMutate = function () { observable["notifySubscribers"](_latestValue, "beforeChange"); }
66.1014 + ko.utils.extend(observable, ko.observable['fn']);
66.1015 +
66.1016 + ko.exportProperty(observable, 'peek', observable.peek);
66.1017 + ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
66.1018 + ko.exportProperty(observable, "valueWillMutate", observable.valueWillMutate);
66.1019 +
66.1020 + return observable;
66.1021 +}
66.1022 +
66.1023 +ko.observable['fn'] = {
66.1024 + "equalityComparer": function valuesArePrimitiveAndEqual(a, b) {
66.1025 + var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
66.1026 + return oldValueIsPrimitive ? (a === b) : false;
66.1027 + }
66.1028 +};
66.1029 +
66.1030 +var protoProperty = ko.observable.protoProperty = "__ko_proto__";
66.1031 +ko.observable['fn'][protoProperty] = ko.observable;
66.1032 +
66.1033 +ko.hasPrototype = function(instance, prototype) {
66.1034 + if ((instance === null) || (instance === undefined) || (instance[protoProperty] === undefined)) return false;
66.1035 + if (instance[protoProperty] === prototype) return true;
66.1036 + return ko.hasPrototype(instance[protoProperty], prototype); // Walk the prototype chain
66.1037 +};
66.1038 +
66.1039 +ko.isObservable = function (instance) {
66.1040 + return ko.hasPrototype(instance, ko.observable);
66.1041 +}
66.1042 +ko.isWriteableObservable = function (instance) {
66.1043 + // Observable
66.1044 + if ((typeof instance == "function") && instance[protoProperty] === ko.observable)
66.1045 + return true;
66.1046 + // Writeable dependent observable
66.1047 + if ((typeof instance == "function") && (instance[protoProperty] === ko.dependentObservable) && (instance.hasWriteFunction))
66.1048 + return true;
66.1049 + // Anything else
66.1050 + return false;
66.1051 +}
66.1052 +
66.1053 +
66.1054 +ko.exportSymbol('observable', ko.observable);
66.1055 +ko.exportSymbol('isObservable', ko.isObservable);
66.1056 +ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
66.1057 +ko.observableArray = function (initialValues) {
66.1058 + if (arguments.length == 0) {
66.1059 + // Zero-parameter constructor initializes to empty array
66.1060 + initialValues = [];
66.1061 + }
66.1062 + if ((initialValues !== null) && (initialValues !== undefined) && !('length' in initialValues))
66.1063 + throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
66.1064 +
66.1065 + var result = ko.observable(initialValues);
66.1066 + ko.utils.extend(result, ko.observableArray['fn']);
66.1067 + return result;
66.1068 +}
66.1069 +
66.1070 +ko.observableArray['fn'] = {
66.1071 + 'remove': function (valueOrPredicate) {
66.1072 + var underlyingArray = this.peek();
66.1073 + var removedValues = [];
66.1074 + var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
66.1075 + for (var i = 0; i < underlyingArray.length; i++) {
66.1076 + var value = underlyingArray[i];
66.1077 + if (predicate(value)) {
66.1078 + if (removedValues.length === 0) {
66.1079 + this.valueWillMutate();
66.1080 + }
66.1081 + removedValues.push(value);
66.1082 + underlyingArray.splice(i, 1);
66.1083 + i--;
66.1084 + }
66.1085 + }
66.1086 + if (removedValues.length) {
66.1087 + this.valueHasMutated();
66.1088 + }
66.1089 + return removedValues;
66.1090 + },
66.1091 +
66.1092 + 'removeAll': function (arrayOfValues) {
66.1093 + // If you passed zero args, we remove everything
66.1094 + if (arrayOfValues === undefined) {
66.1095 + var underlyingArray = this.peek();
66.1096 + var allValues = underlyingArray.slice(0);
66.1097 + this.valueWillMutate();
66.1098 + underlyingArray.splice(0, underlyingArray.length);
66.1099 + this.valueHasMutated();
66.1100 + return allValues;
66.1101 + }
66.1102 + // If you passed an arg, we interpret it as an array of entries to remove
66.1103 + if (!arrayOfValues)
66.1104 + return [];
66.1105 + return this['remove'](function (value) {
66.1106 + return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
66.1107 + });
66.1108 + },
66.1109 +
66.1110 + 'destroy': function (valueOrPredicate) {
66.1111 + var underlyingArray = this.peek();
66.1112 + var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
66.1113 + this.valueWillMutate();
66.1114 + for (var i = underlyingArray.length - 1; i >= 0; i--) {
66.1115 + var value = underlyingArray[i];
66.1116 + if (predicate(value))
66.1117 + underlyingArray[i]["_destroy"] = true;
66.1118 + }
66.1119 + this.valueHasMutated();
66.1120 + },
66.1121 +
66.1122 + 'destroyAll': function (arrayOfValues) {
66.1123 + // If you passed zero args, we destroy everything
66.1124 + if (arrayOfValues === undefined)
66.1125 + return this['destroy'](function() { return true });
66.1126 +
66.1127 + // If you passed an arg, we interpret it as an array of entries to destroy
66.1128 + if (!arrayOfValues)
66.1129 + return [];
66.1130 + return this['destroy'](function (value) {
66.1131 + return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
66.1132 + });
66.1133 + },
66.1134 +
66.1135 + 'indexOf': function (item) {
66.1136 + var underlyingArray = this();
66.1137 + return ko.utils.arrayIndexOf(underlyingArray, item);
66.1138 + },
66.1139 +
66.1140 + 'replace': function(oldItem, newItem) {
66.1141 + var index = this['indexOf'](oldItem);
66.1142 + if (index >= 0) {
66.1143 + this.valueWillMutate();
66.1144 + this.peek()[index] = newItem;
66.1145 + this.valueHasMutated();
66.1146 + }
66.1147 + }
66.1148 +}
66.1149 +
66.1150 +// Populate ko.observableArray.fn with read/write functions from native arrays
66.1151 +// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
66.1152 +// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
66.1153 +ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
66.1154 + ko.observableArray['fn'][methodName] = function () {
66.1155 + // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
66.1156 + // (for consistency with mutating regular observables)
66.1157 + var underlyingArray = this.peek();
66.1158 + this.valueWillMutate();
66.1159 + var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
66.1160 + this.valueHasMutated();
66.1161 + return methodCallResult;
66.1162 + };
66.1163 +});
66.1164 +
66.1165 +// Populate ko.observableArray.fn with read-only functions from native arrays
66.1166 +ko.utils.arrayForEach(["slice"], function (methodName) {
66.1167 + ko.observableArray['fn'][methodName] = function () {
66.1168 + var underlyingArray = this();
66.1169 + return underlyingArray[methodName].apply(underlyingArray, arguments);
66.1170 + };
66.1171 +});
66.1172 +
66.1173 +ko.exportSymbol('observableArray', ko.observableArray);
66.1174 +ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
66.1175 + var _latestValue,
66.1176 + _hasBeenEvaluated = false,
66.1177 + _isBeingEvaluated = false,
66.1178 + readFunction = evaluatorFunctionOrOptions;
66.1179 +
66.1180 + if (readFunction && typeof readFunction == "object") {
66.1181 + // Single-parameter syntax - everything is on this "options" param
66.1182 + options = readFunction;
66.1183 + readFunction = options["read"];
66.1184 + } else {
66.1185 + // Multi-parameter syntax - construct the options according to the params passed
66.1186 + options = options || {};
66.1187 + if (!readFunction)
66.1188 + readFunction = options["read"];
66.1189 + }
66.1190 + if (typeof readFunction != "function")
66.1191 + throw new Error("Pass a function that returns the value of the ko.computed");
66.1192 +
66.1193 + function addSubscriptionToDependency(subscribable) {
66.1194 + _subscriptionsToDependencies.push(subscribable.subscribe(evaluatePossiblyAsync));
66.1195 + }
66.1196 +
66.1197 + function disposeAllSubscriptionsToDependencies() {
66.1198 + ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
66.1199 + subscription.dispose();
66.1200 + });
66.1201 + _subscriptionsToDependencies = [];
66.1202 + }
66.1203 +
66.1204 + function evaluatePossiblyAsync() {
66.1205 + var throttleEvaluationTimeout = dependentObservable['throttleEvaluation'];
66.1206 + if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
66.1207 + clearTimeout(evaluationTimeoutInstance);
66.1208 + evaluationTimeoutInstance = setTimeout(evaluateImmediate, throttleEvaluationTimeout);
66.1209 + } else
66.1210 + evaluateImmediate();
66.1211 + }
66.1212 +
66.1213 + function evaluateImmediate() {
66.1214 + if (_isBeingEvaluated) {
66.1215 + // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
66.1216 + // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
66.1217 + // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
66.1218 + // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
66.1219 + return;
66.1220 + }
66.1221 +
66.1222 + // Don't dispose on first evaluation, because the "disposeWhen" callback might
66.1223 + // e.g., dispose when the associated DOM element isn't in the doc, and it's not
66.1224 + // going to be in the doc until *after* the first evaluation
66.1225 + if (_hasBeenEvaluated && disposeWhen()) {
66.1226 + dispose();
66.1227 + return;
66.1228 + }
66.1229 +
66.1230 + _isBeingEvaluated = true;
66.1231 + try {
66.1232 + // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
66.1233 + // Then, during evaluation, we cross off any that are in fact still being used.
66.1234 + var disposalCandidates = ko.utils.arrayMap(_subscriptionsToDependencies, function(item) {return item.target;});
66.1235 +
66.1236 + ko.dependencyDetection.begin(function(subscribable) {
66.1237 + var inOld;
66.1238 + if ((inOld = ko.utils.arrayIndexOf(disposalCandidates, subscribable)) >= 0)
66.1239 + disposalCandidates[inOld] = undefined; // Don't want to dispose this subscription, as it's still being used
66.1240 + else
66.1241 + addSubscriptionToDependency(subscribable); // Brand new subscription - add it
66.1242 + });
66.1243 +
66.1244 + var newValue = readFunction.call(evaluatorFunctionTarget);
66.1245 +
66.1246 + // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
66.1247 + for (var i = disposalCandidates.length - 1; i >= 0; i--) {
66.1248 + if (disposalCandidates[i])
66.1249 + _subscriptionsToDependencies.splice(i, 1)[0].dispose();
66.1250 + }
66.1251 + _hasBeenEvaluated = true;
66.1252 +
66.1253 + dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
66.1254 + _latestValue = newValue;
66.1255 + if (DEBUG) dependentObservable._latestValue = _latestValue;
66.1256 + } finally {
66.1257 + ko.dependencyDetection.end();
66.1258 + }
66.1259 +
66.1260 + dependentObservable["notifySubscribers"](_latestValue);
66.1261 + _isBeingEvaluated = false;
66.1262 + if (!_subscriptionsToDependencies.length)
66.1263 + dispose();
66.1264 + }
66.1265 +
66.1266 + function dependentObservable() {
66.1267 + if (arguments.length > 0) {
66.1268 + if (typeof writeFunction === "function") {
66.1269 + // Writing a value
66.1270 + writeFunction.apply(evaluatorFunctionTarget, arguments);
66.1271 + } else {
66.1272 + throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
66.1273 + }
66.1274 + return this; // Permits chained assignments
66.1275 + } else {
66.1276 + // Reading the value
66.1277 + if (!_hasBeenEvaluated)
66.1278 + evaluateImmediate();
66.1279 + ko.dependencyDetection.registerDependency(dependentObservable);
66.1280 + return _latestValue;
66.1281 + }
66.1282 + }
66.1283 +
66.1284 + function peek() {
66.1285 + if (!_hasBeenEvaluated)
66.1286 + evaluateImmediate();
66.1287 + return _latestValue;
66.1288 + }
66.1289 +
66.1290 + function isActive() {
66.1291 + return !_hasBeenEvaluated || _subscriptionsToDependencies.length > 0;
66.1292 + }
66.1293 +
66.1294 + // By here, "options" is always non-null
66.1295 + var writeFunction = options["write"],
66.1296 + disposeWhenNodeIsRemoved = options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
66.1297 + disposeWhen = options["disposeWhen"] || options.disposeWhen || function() { return false; },
66.1298 + dispose = disposeAllSubscriptionsToDependencies,
66.1299 + _subscriptionsToDependencies = [],
66.1300 + evaluationTimeoutInstance = null;
66.1301 +
66.1302 + if (!evaluatorFunctionTarget)
66.1303 + evaluatorFunctionTarget = options["owner"];
66.1304 +
66.1305 + dependentObservable.peek = peek;
66.1306 + dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; };
66.1307 + dependentObservable.hasWriteFunction = typeof options["write"] === "function";
66.1308 + dependentObservable.dispose = function () { dispose(); };
66.1309 + dependentObservable.isActive = isActive;
66.1310 + dependentObservable.valueHasMutated = function() {
66.1311 + _hasBeenEvaluated = false;
66.1312 + evaluateImmediate();
66.1313 + };
66.1314 +
66.1315 + ko.subscribable.call(dependentObservable);
66.1316 + ko.utils.extend(dependentObservable, ko.dependentObservable['fn']);
66.1317 +
66.1318 + ko.exportProperty(dependentObservable, 'peek', dependentObservable.peek);
66.1319 + ko.exportProperty(dependentObservable, 'dispose', dependentObservable.dispose);
66.1320 + ko.exportProperty(dependentObservable, 'isActive', dependentObservable.isActive);
66.1321 + ko.exportProperty(dependentObservable, 'getDependenciesCount', dependentObservable.getDependenciesCount);
66.1322 +
66.1323 + // Evaluate, unless deferEvaluation is true
66.1324 + if (options['deferEvaluation'] !== true)
66.1325 + evaluateImmediate();
66.1326 +
66.1327 + // Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values.
66.1328 + // But skip if isActive is false (there will never be any dependencies to dispose).
66.1329 + // (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
66.1330 + // plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
66.1331 + if (disposeWhenNodeIsRemoved && isActive()) {
66.1332 + dispose = function() {
66.1333 + ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, arguments.callee);
66.1334 + disposeAllSubscriptionsToDependencies();
66.1335 + };
66.1336 + ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, dispose);
66.1337 + var existingDisposeWhenFunction = disposeWhen;
66.1338 + disposeWhen = function () {
66.1339 + return !ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) || existingDisposeWhenFunction();
66.1340 + }
66.1341 + }
66.1342 +
66.1343 + return dependentObservable;
66.1344 +};
66.1345 +
66.1346 +ko.isComputed = function(instance) {
66.1347 + return ko.hasPrototype(instance, ko.dependentObservable);
66.1348 +};
66.1349 +
66.1350 +var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
66.1351 +ko.dependentObservable[protoProp] = ko.observable;
66.1352 +
66.1353 +ko.dependentObservable['fn'] = {};
66.1354 +ko.dependentObservable['fn'][protoProp] = ko.dependentObservable;
66.1355 +
66.1356 +ko.exportSymbol('dependentObservable', ko.dependentObservable);
66.1357 +ko.exportSymbol('computed', ko.dependentObservable); // Make "ko.computed" an alias for "ko.dependentObservable"
66.1358 +ko.exportSymbol('isComputed', ko.isComputed);
66.1359 +
66.1360 +(function() {
66.1361 + var maxNestedObservableDepth = 10; // Escape the (unlikely) pathalogical case where an observable's current value is itself (or similar reference cycle)
66.1362 +
66.1363 + ko.toJS = function(rootObject) {
66.1364 + if (arguments.length == 0)
66.1365 + throw new Error("When calling ko.toJS, pass the object you want to convert.");
66.1366 +
66.1367 + // We just unwrap everything at every level in the object graph
66.1368 + return mapJsObjectGraph(rootObject, function(valueToMap) {
66.1369 + // Loop because an observable's value might in turn be another observable wrapper
66.1370 + for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
66.1371 + valueToMap = valueToMap();
66.1372 + return valueToMap;
66.1373 + });
66.1374 + };
66.1375 +
66.1376 + ko.toJSON = function(rootObject, replacer, space) { // replacer and space are optional
66.1377 + var plainJavaScriptObject = ko.toJS(rootObject);
66.1378 + return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
66.1379 + };
66.1380 +
66.1381 + function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
66.1382 + visitedObjects = visitedObjects || new objectLookup();
66.1383 +
66.1384 + rootObject = mapInputCallback(rootObject);
66.1385 + var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof Date));
66.1386 + if (!canHaveProperties)
66.1387 + return rootObject;
66.1388 +
66.1389 + var outputProperties = rootObject instanceof Array ? [] : {};
66.1390 + visitedObjects.save(rootObject, outputProperties);
66.1391 +
66.1392 + visitPropertiesOrArrayEntries(rootObject, function(indexer) {
66.1393 + var propertyValue = mapInputCallback(rootObject[indexer]);
66.1394 +
66.1395 + switch (typeof propertyValue) {
66.1396 + case "boolean":
66.1397 + case "number":
66.1398 + case "string":
66.1399 + case "function":
66.1400 + outputProperties[indexer] = propertyValue;
66.1401 + break;
66.1402 + case "object":
66.1403 + case "undefined":
66.1404 + var previouslyMappedValue = visitedObjects.get(propertyValue);
66.1405 + outputProperties[indexer] = (previouslyMappedValue !== undefined)
66.1406 + ? previouslyMappedValue
66.1407 + : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
66.1408 + break;
66.1409 + }
66.1410 + });
66.1411 +
66.1412 + return outputProperties;
66.1413 + }
66.1414 +
66.1415 + function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
66.1416 + if (rootObject instanceof Array) {
66.1417 + for (var i = 0; i < rootObject.length; i++)
66.1418 + visitorCallback(i);
66.1419 +
66.1420 + // For arrays, also respect toJSON property for custom mappings (fixes #278)
66.1421 + if (typeof rootObject['toJSON'] == 'function')
66.1422 + visitorCallback('toJSON');
66.1423 + } else {
66.1424 + for (var propertyName in rootObject)
66.1425 + visitorCallback(propertyName);
66.1426 + }
66.1427 + };
66.1428 +
66.1429 + function objectLookup() {
66.1430 + var keys = [];
66.1431 + var values = [];
66.1432 + this.save = function(key, value) {
66.1433 + var existingIndex = ko.utils.arrayIndexOf(keys, key);
66.1434 + if (existingIndex >= 0)
66.1435 + values[existingIndex] = value;
66.1436 + else {
66.1437 + keys.push(key);
66.1438 + values.push(value);
66.1439 + }
66.1440 + };
66.1441 + this.get = function(key) {
66.1442 + var existingIndex = ko.utils.arrayIndexOf(keys, key);
66.1443 + return (existingIndex >= 0) ? values[existingIndex] : undefined;
66.1444 + };
66.1445 + };
66.1446 +})();
66.1447 +
66.1448 +ko.exportSymbol('toJS', ko.toJS);
66.1449 +ko.exportSymbol('toJSON', ko.toJSON);
66.1450 +(function () {
66.1451 + var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';
66.1452 +
66.1453 + // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
66.1454 + // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
66.1455 + // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
66.1456 + ko.selectExtensions = {
66.1457 + readValue : function(element) {
66.1458 + switch (ko.utils.tagNameLower(element)) {
66.1459 + case 'option':
66.1460 + if (element[hasDomDataExpandoProperty] === true)
66.1461 + return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
66.1462 + return ko.utils.ieVersion <= 7
66.1463 + ? (element.getAttributeNode('value').specified ? element.value : element.text)
66.1464 + : element.value;
66.1465 + case 'select':
66.1466 + return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
66.1467 + default:
66.1468 + return element.value;
66.1469 + }
66.1470 + },
66.1471 +
66.1472 + writeValue: function(element, value) {
66.1473 + switch (ko.utils.tagNameLower(element)) {
66.1474 + case 'option':
66.1475 + switch(typeof value) {
66.1476 + case "string":
66.1477 + ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
66.1478 + if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
66.1479 + delete element[hasDomDataExpandoProperty];
66.1480 + }
66.1481 + element.value = value;
66.1482 + break;
66.1483 + default:
66.1484 + // Store arbitrary object using DomData
66.1485 + ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
66.1486 + element[hasDomDataExpandoProperty] = true;
66.1487 +
66.1488 + // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
66.1489 + element.value = typeof value === "number" ? value : "";
66.1490 + break;
66.1491 + }
66.1492 + break;
66.1493 + case 'select':
66.1494 + for (var i = element.options.length - 1; i >= 0; i--) {
66.1495 + if (ko.selectExtensions.readValue(element.options[i]) == value) {
66.1496 + element.selectedIndex = i;
66.1497 + break;
66.1498 + }
66.1499 + }
66.1500 + break;
66.1501 + default:
66.1502 + if ((value === null) || (value === undefined))
66.1503 + value = "";
66.1504 + element.value = value;
66.1505 + break;
66.1506 + }
66.1507 + }
66.1508 + };
66.1509 +})();
66.1510 +
66.1511 +ko.exportSymbol('selectExtensions', ko.selectExtensions);
66.1512 +ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
66.1513 +ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
66.1514 +ko.expressionRewriting = (function () {
66.1515 + var restoreCapturedTokensRegex = /\@ko_token_(\d+)\@/g;
66.1516 + var javaScriptReservedWords = ["true", "false"];
66.1517 +
66.1518 + // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
66.1519 + // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
66.1520 + var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;
66.1521 +
66.1522 + function restoreTokens(string, tokens) {
66.1523 + var prevValue = null;
66.1524 + while (string != prevValue) { // Keep restoring tokens until it no longer makes a difference (they may be nested)
66.1525 + prevValue = string;
66.1526 + string = string.replace(restoreCapturedTokensRegex, function (match, tokenIndex) {
66.1527 + return tokens[tokenIndex];
66.1528 + });
66.1529 + }
66.1530 + return string;
66.1531 + }
66.1532 +
66.1533 + function getWriteableValue(expression) {
66.1534 + if (ko.utils.arrayIndexOf(javaScriptReservedWords, ko.utils.stringTrim(expression).toLowerCase()) >= 0)
66.1535 + return false;
66.1536 + var match = expression.match(javaScriptAssignmentTarget);
66.1537 + return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
66.1538 + }
66.1539 +
66.1540 + function ensureQuoted(key) {
66.1541 + var trimmedKey = ko.utils.stringTrim(key);
66.1542 + switch (trimmedKey.length && trimmedKey.charAt(0)) {
66.1543 + case "'":
66.1544 + case '"':
66.1545 + return key;
66.1546 + default:
66.1547 + return "'" + trimmedKey + "'";
66.1548 + }
66.1549 + }
66.1550 +
66.1551 + return {
66.1552 + bindingRewriteValidators: [],
66.1553 +
66.1554 + parseObjectLiteral: function(objectLiteralString) {
66.1555 + // A full tokeniser+lexer would add too much weight to this library, so here's a simple parser
66.1556 + // that is sufficient just to split an object literal string into a set of top-level key-value pairs
66.1557 +
66.1558 + var str = ko.utils.stringTrim(objectLiteralString);
66.1559 + if (str.length < 3)
66.1560 + return [];
66.1561 + if (str.charAt(0) === "{")// Ignore any braces surrounding the whole object literal
66.1562 + str = str.substring(1, str.length - 1);
66.1563 +
66.1564 + // Pull out any string literals and regex literals
66.1565 + var tokens = [];
66.1566 + var tokenStart = null, tokenEndChar;
66.1567 + for (var position = 0; position < str.length; position++) {
66.1568 + var c = str.charAt(position);
66.1569 + if (tokenStart === null) {
66.1570 + switch (c) {
66.1571 + case '"':
66.1572 + case "'":
66.1573 + case "/":
66.1574 + tokenStart = position;
66.1575 + tokenEndChar = c;
66.1576 + break;
66.1577 + }
66.1578 + } else if ((c == tokenEndChar) && (str.charAt(position - 1) !== "\\")) {
66.1579 + var token = str.substring(tokenStart, position + 1);
66.1580 + tokens.push(token);
66.1581 + var replacement = "@ko_token_" + (tokens.length - 1) + "@";
66.1582 + str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
66.1583 + position -= (token.length - replacement.length);
66.1584 + tokenStart = null;
66.1585 + }
66.1586 + }
66.1587 +
66.1588 + // Next pull out balanced paren, brace, and bracket blocks
66.1589 + tokenStart = null;
66.1590 + tokenEndChar = null;
66.1591 + var tokenDepth = 0, tokenStartChar = null;
66.1592 + for (var position = 0; position < str.length; position++) {
66.1593 + var c = str.charAt(position);
66.1594 + if (tokenStart === null) {
66.1595 + switch (c) {
66.1596 + case "{": tokenStart = position; tokenStartChar = c;
66.1597 + tokenEndChar = "}";
66.1598 + break;
66.1599 + case "(": tokenStart = position; tokenStartChar = c;
66.1600 + tokenEndChar = ")";
66.1601 + break;
66.1602 + case "[": tokenStart = position; tokenStartChar = c;
66.1603 + tokenEndChar = "]";
66.1604 + break;
66.1605 + }
66.1606 + }
66.1607 +
66.1608 + if (c === tokenStartChar)
66.1609 + tokenDepth++;
66.1610 + else if (c === tokenEndChar) {
66.1611 + tokenDepth--;
66.1612 + if (tokenDepth === 0) {
66.1613 + var token = str.substring(tokenStart, position + 1);
66.1614 + tokens.push(token);
66.1615 + var replacement = "@ko_token_" + (tokens.length - 1) + "@";
66.1616 + str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
66.1617 + position -= (token.length - replacement.length);
66.1618 + tokenStart = null;
66.1619 + }
66.1620 + }
66.1621 + }
66.1622 +
66.1623 + // Now we can safely split on commas to get the key/value pairs
66.1624 + var result = [];
66.1625 + var keyValuePairs = str.split(",");
66.1626 + for (var i = 0, j = keyValuePairs.length; i < j; i++) {
66.1627 + var pair = keyValuePairs[i];
66.1628 + var colonPos = pair.indexOf(":");
66.1629 + if ((colonPos > 0) && (colonPos < pair.length - 1)) {
66.1630 + var key = pair.substring(0, colonPos);
66.1631 + var value = pair.substring(colonPos + 1);
66.1632 + result.push({ 'key': restoreTokens(key, tokens), 'value': restoreTokens(value, tokens) });
66.1633 + } else {
66.1634 + result.push({ 'unknown': restoreTokens(pair, tokens) });
66.1635 + }
66.1636 + }
66.1637 + return result;
66.1638 + },
66.1639 +
66.1640 + preProcessBindings: function (objectLiteralStringOrKeyValueArray) {
66.1641 + var keyValueArray = typeof objectLiteralStringOrKeyValueArray === "string"
66.1642 + ? ko.expressionRewriting.parseObjectLiteral(objectLiteralStringOrKeyValueArray)
66.1643 + : objectLiteralStringOrKeyValueArray;
66.1644 + var resultStrings = [], propertyAccessorResultStrings = [];
66.1645 +
66.1646 + var keyValueEntry;
66.1647 + for (var i = 0; keyValueEntry = keyValueArray[i]; i++) {
66.1648 + if (resultStrings.length > 0)
66.1649 + resultStrings.push(",");
66.1650 +
66.1651 + if (keyValueEntry['key']) {
66.1652 + var quotedKey = ensureQuoted(keyValueEntry['key']), val = keyValueEntry['value'];
66.1653 + resultStrings.push(quotedKey);
66.1654 + resultStrings.push(":");
66.1655 + resultStrings.push(val);
66.1656 +
66.1657 + if (val = getWriteableValue(ko.utils.stringTrim(val))) {
66.1658 + if (propertyAccessorResultStrings.length > 0)
66.1659 + propertyAccessorResultStrings.push(", ");
66.1660 + propertyAccessorResultStrings.push(quotedKey + " : function(__ko_value) { " + val + " = __ko_value; }");
66.1661 + }
66.1662 + } else if (keyValueEntry['unknown']) {
66.1663 + resultStrings.push(keyValueEntry['unknown']);
66.1664 + }
66.1665 + }
66.1666 +
66.1667 + var combinedResult = resultStrings.join("");
66.1668 + if (propertyAccessorResultStrings.length > 0) {
66.1669 + var allPropertyAccessors = propertyAccessorResultStrings.join("");
66.1670 + combinedResult = combinedResult + ", '_ko_property_writers' : { " + allPropertyAccessors + " } ";
66.1671 + }
66.1672 +
66.1673 + return combinedResult;
66.1674 + },
66.1675 +
66.1676 + keyValueArrayContainsKey: function(keyValueArray, key) {
66.1677 + for (var i = 0; i < keyValueArray.length; i++)
66.1678 + if (ko.utils.stringTrim(keyValueArray[i]['key']) == key)
66.1679 + return true;
66.1680 + return false;
66.1681 + },
66.1682 +
66.1683 + // Internal, private KO utility for updating model properties from within bindings
66.1684 + // property: If the property being updated is (or might be) an observable, pass it here
66.1685 + // If it turns out to be a writable observable, it will be written to directly
66.1686 + // allBindingsAccessor: All bindings in the current execution context.
66.1687 + // This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
66.1688 + // key: The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
66.1689 + // value: The value to be written
66.1690 + // checkIfDifferent: If true, and if the property being written is a writable observable, the value will only be written if
66.1691 + // it is !== existing value on that writable observable
66.1692 + writeValueToProperty: function(property, allBindingsAccessor, key, value, checkIfDifferent) {
66.1693 + if (!property || !ko.isWriteableObservable(property)) {
66.1694 + var propWriters = allBindingsAccessor()['_ko_property_writers'];
66.1695 + if (propWriters && propWriters[key])
66.1696 + propWriters[key](value);
66.1697 + } else if (!checkIfDifferent || property.peek() !== value) {
66.1698 + property(value);
66.1699 + }
66.1700 + }
66.1701 + };
66.1702 +})();
66.1703 +
66.1704 +ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
66.1705 +ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
66.1706 +ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
66.1707 +ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);
66.1708 +
66.1709 +// For backward compatibility, define the following aliases. (Previously, these function names were misleading because
66.1710 +// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
66.1711 +ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
66.1712 +ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);(function() {
66.1713 + // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
66.1714 + // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
66.1715 + // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
66.1716 + // of that virtual hierarchy
66.1717 + //
66.1718 + // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
66.1719 + // without having to scatter special cases all over the binding and templating code.
66.1720 +
66.1721 + // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
66.1722 + // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
66.1723 + // So, use node.text where available, and node.nodeValue elsewhere
66.1724 + var commentNodesHaveTextProperty = document.createComment("test").text === "<!--test-->";
66.1725 +
66.1726 + var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*-->$/ : /^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/;
66.1727 + var endCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
66.1728 + var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
66.1729 +
66.1730 + function isStartComment(node) {
66.1731 + return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
66.1732 + }
66.1733 +
66.1734 + function isEndComment(node) {
66.1735 + return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(endCommentRegex);
66.1736 + }
66.1737 +
66.1738 + function getVirtualChildren(startComment, allowUnbalanced) {
66.1739 + var currentNode = startComment;
66.1740 + var depth = 1;
66.1741 + var children = [];
66.1742 + while (currentNode = currentNode.nextSibling) {
66.1743 + if (isEndComment(currentNode)) {
66.1744 + depth--;
66.1745 + if (depth === 0)
66.1746 + return children;
66.1747 + }
66.1748 +
66.1749 + children.push(currentNode);
66.1750 +
66.1751 + if (isStartComment(currentNode))
66.1752 + depth++;
66.1753 + }
66.1754 + if (!allowUnbalanced)
66.1755 + throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
66.1756 + return null;
66.1757 + }
66.1758 +
66.1759 + function getMatchingEndComment(startComment, allowUnbalanced) {
66.1760 + var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
66.1761 + if (allVirtualChildren) {
66.1762 + if (allVirtualChildren.length > 0)
66.1763 + return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
66.1764 + return startComment.nextSibling;
66.1765 + } else
66.1766 + return null; // Must have no matching end comment, and allowUnbalanced is true
66.1767 + }
66.1768 +
66.1769 + function getUnbalancedChildTags(node) {
66.1770 + // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
66.1771 + // from <div>OK</div><!-- /ko --><!-- /ko -->, returns: <!-- /ko --><!-- /ko -->
66.1772 + var childNode = node.firstChild, captureRemaining = null;
66.1773 + if (childNode) {
66.1774 + do {
66.1775 + if (captureRemaining) // We already hit an unbalanced node and are now just scooping up all subsequent nodes
66.1776 + captureRemaining.push(childNode);
66.1777 + else if (isStartComment(childNode)) {
66.1778 + var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
66.1779 + if (matchingEndComment) // It's a balanced tag, so skip immediately to the end of this virtual set
66.1780 + childNode = matchingEndComment;
66.1781 + else
66.1782 + captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
66.1783 + } else if (isEndComment(childNode)) {
66.1784 + captureRemaining = [childNode]; // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
66.1785 + }
66.1786 + } while (childNode = childNode.nextSibling);
66.1787 + }
66.1788 + return captureRemaining;
66.1789 + }
66.1790 +
66.1791 + ko.virtualElements = {
66.1792 + allowedBindings: {},
66.1793 +
66.1794 + childNodes: function(node) {
66.1795 + return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
66.1796 + },
66.1797 +
66.1798 + emptyNode: function(node) {
66.1799 + if (!isStartComment(node))
66.1800 + ko.utils.emptyDomNode(node);
66.1801 + else {
66.1802 + var virtualChildren = ko.virtualElements.childNodes(node);
66.1803 + for (var i = 0, j = virtualChildren.length; i < j; i++)
66.1804 + ko.removeNode(virtualChildren[i]);
66.1805 + }
66.1806 + },
66.1807 +
66.1808 + setDomNodeChildren: function(node, childNodes) {
66.1809 + if (!isStartComment(node))
66.1810 + ko.utils.setDomNodeChildren(node, childNodes);
66.1811 + else {
66.1812 + ko.virtualElements.emptyNode(node);
66.1813 + var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
66.1814 + for (var i = 0, j = childNodes.length; i < j; i++)
66.1815 + endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
66.1816 + }
66.1817 + },
66.1818 +
66.1819 + prepend: function(containerNode, nodeToPrepend) {
66.1820 + if (!isStartComment(containerNode)) {
66.1821 + if (containerNode.firstChild)
66.1822 + containerNode.insertBefore(nodeToPrepend, containerNode.firstChild);
66.1823 + else
66.1824 + containerNode.appendChild(nodeToPrepend);
66.1825 + } else {
66.1826 + // Start comments must always have a parent and at least one following sibling (the end comment)
66.1827 + containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
66.1828 + }
66.1829 + },
66.1830 +
66.1831 + insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
66.1832 + if (!insertAfterNode) {
66.1833 + ko.virtualElements.prepend(containerNode, nodeToInsert);
66.1834 + } else if (!isStartComment(containerNode)) {
66.1835 + // Insert after insertion point
66.1836 + if (insertAfterNode.nextSibling)
66.1837 + containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
66.1838 + else
66.1839 + containerNode.appendChild(nodeToInsert);
66.1840 + } else {
66.1841 + // Children of start comments must always have a parent and at least one following sibling (the end comment)
66.1842 + containerNode.parentNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
66.1843 + }
66.1844 + },
66.1845 +
66.1846 + firstChild: function(node) {
66.1847 + if (!isStartComment(node))
66.1848 + return node.firstChild;
66.1849 + if (!node.nextSibling || isEndComment(node.nextSibling))
66.1850 + return null;
66.1851 + return node.nextSibling;
66.1852 + },
66.1853 +
66.1854 + nextSibling: function(node) {
66.1855 + if (isStartComment(node))
66.1856 + node = getMatchingEndComment(node);
66.1857 + if (node.nextSibling && isEndComment(node.nextSibling))
66.1858 + return null;
66.1859 + return node.nextSibling;
66.1860 + },
66.1861 +
66.1862 + virtualNodeBindingValue: function(node) {
66.1863 + var regexMatch = isStartComment(node);
66.1864 + return regexMatch ? regexMatch[1] : null;
66.1865 + },
66.1866 +
66.1867 + normaliseVirtualElementDomStructure: function(elementVerified) {
66.1868 + // Workaround for https://github.com/SteveSanderson/knockout/issues/155
66.1869 + // (IE <= 8 or IE 9 quirks mode parses your HTML weirdly, treating closing </li> tags as if they don't exist, thereby moving comment nodes
66.1870 + // that are direct descendants of <ul> into the preceding <li>)
66.1871 + if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
66.1872 + return;
66.1873 +
66.1874 + // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
66.1875 + // must be intended to appear *after* that child, so move them there.
66.1876 + var childNode = elementVerified.firstChild;
66.1877 + if (childNode) {
66.1878 + do {
66.1879 + if (childNode.nodeType === 1) {
66.1880 + var unbalancedTags = getUnbalancedChildTags(childNode);
66.1881 + if (unbalancedTags) {
66.1882 + // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
66.1883 + var nodeToInsertBefore = childNode.nextSibling;
66.1884 + for (var i = 0; i < unbalancedTags.length; i++) {
66.1885 + if (nodeToInsertBefore)
66.1886 + elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
66.1887 + else
66.1888 + elementVerified.appendChild(unbalancedTags[i]);
66.1889 + }
66.1890 + }
66.1891 + }
66.1892 + } while (childNode = childNode.nextSibling);
66.1893 + }
66.1894 + }
66.1895 + };
66.1896 +})();
66.1897 +ko.exportSymbol('virtualElements', ko.virtualElements);
66.1898 +ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
66.1899 +ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
66.1900 +//ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild); // firstChild is not minified
66.1901 +ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
66.1902 +//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling); // nextSibling is not minified
66.1903 +ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
66.1904 +ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
66.1905 +(function() {
66.1906 + var defaultBindingAttributeName = "data-bind";
66.1907 +
66.1908 + ko.bindingProvider = function() {
66.1909 + this.bindingCache = {};
66.1910 + };
66.1911 +
66.1912 + ko.utils.extend(ko.bindingProvider.prototype, {
66.1913 + 'nodeHasBindings': function(node) {
66.1914 + switch (node.nodeType) {
66.1915 + case 1: return node.getAttribute(defaultBindingAttributeName) != null; // Element
66.1916 + case 8: return ko.virtualElements.virtualNodeBindingValue(node) != null; // Comment node
66.1917 + default: return false;
66.1918 + }
66.1919 + },
66.1920 +
66.1921 + 'getBindings': function(node, bindingContext) {
66.1922 + var bindingsString = this['getBindingsString'](node, bindingContext);
66.1923 + return bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
66.1924 + },
66.1925 +
66.1926 + // The following function is only used internally by this default provider.
66.1927 + // It's not part of the interface definition for a general binding provider.
66.1928 + 'getBindingsString': function(node, bindingContext) {
66.1929 + switch (node.nodeType) {
66.1930 + case 1: return node.getAttribute(defaultBindingAttributeName); // Element
66.1931 + case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
66.1932 + default: return null;
66.1933 + }
66.1934 + },
66.1935 +
66.1936 + // The following function is only used internally by this default provider.
66.1937 + // It's not part of the interface definition for a general binding provider.
66.1938 + 'parseBindingsString': function(bindingsString, bindingContext, node) {
66.1939 + try {
66.1940 + var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache);
66.1941 + return bindingFunction(bindingContext, node);
66.1942 + } catch (ex) {
66.1943 + throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
66.1944 + }
66.1945 + }
66.1946 + });
66.1947 +
66.1948 + ko.bindingProvider['instance'] = new ko.bindingProvider();
66.1949 +
66.1950 + function createBindingsStringEvaluatorViaCache(bindingsString, cache) {
66.1951 + var cacheKey = bindingsString;
66.1952 + return cache[cacheKey]
66.1953 + || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString));
66.1954 + }
66.1955 +
66.1956 + function createBindingsStringEvaluator(bindingsString) {
66.1957 + // Build the source for a function that evaluates "expression"
66.1958 + // For each scope variable, add an extra level of "with" nesting
66.1959 + // Example result: with(sc1) { with(sc0) { return (expression) } }
66.1960 + var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString),
66.1961 + functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
66.1962 + return new Function("$context", "$element", functionBody);
66.1963 + }
66.1964 +})();
66.1965 +
66.1966 +ko.exportSymbol('bindingProvider', ko.bindingProvider);
66.1967 +(function () {
66.1968 + ko.bindingHandlers = {};
66.1969 +
66.1970 + ko.bindingContext = function(dataItem, parentBindingContext, dataItemAlias) {
66.1971 + if (parentBindingContext) {
66.1972 + ko.utils.extend(this, parentBindingContext); // Inherit $root and any custom properties
66.1973 + this['$parentContext'] = parentBindingContext;
66.1974 + this['$parent'] = parentBindingContext['$data'];
66.1975 + this['$parents'] = (parentBindingContext['$parents'] || []).slice(0);
66.1976 + this['$parents'].unshift(this['$parent']);
66.1977 + } else {
66.1978 + this['$parents'] = [];
66.1979 + this['$root'] = dataItem;
66.1980 + // Export 'ko' in the binding context so it will be available in bindings and templates
66.1981 + // even if 'ko' isn't exported as a global, such as when using an AMD loader.
66.1982 + // See https://github.com/SteveSanderson/knockout/issues/490
66.1983 + this['ko'] = ko;
66.1984 + }
66.1985 + this['$data'] = dataItem;
66.1986 + if (dataItemAlias)
66.1987 + this[dataItemAlias] = dataItem;
66.1988 + }
66.1989 + ko.bindingContext.prototype['createChildContext'] = function (dataItem, dataItemAlias) {
66.1990 + return new ko.bindingContext(dataItem, this, dataItemAlias);
66.1991 + };
66.1992 + ko.bindingContext.prototype['extend'] = function(properties) {
66.1993 + var clone = ko.utils.extend(new ko.bindingContext(), this);
66.1994 + return ko.utils.extend(clone, properties);
66.1995 + };
66.1996 +
66.1997 + function validateThatBindingIsAllowedForVirtualElements(bindingName) {
66.1998 + var validator = ko.virtualElements.allowedBindings[bindingName];
66.1999 + if (!validator)
66.2000 + throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
66.2001 + }
66.2002 +
66.2003 + function applyBindingsToDescendantsInternal (viewModel, elementOrVirtualElement, bindingContextsMayDifferFromDomParentElement) {
66.2004 + var currentChild, nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
66.2005 + while (currentChild = nextInQueue) {
66.2006 + // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
66.2007 + nextInQueue = ko.virtualElements.nextSibling(currentChild);
66.2008 + applyBindingsToNodeAndDescendantsInternal(viewModel, currentChild, bindingContextsMayDifferFromDomParentElement);
66.2009 + }
66.2010 + }
66.2011 +
66.2012 + function applyBindingsToNodeAndDescendantsInternal (viewModel, nodeVerified, bindingContextMayDifferFromDomParentElement) {
66.2013 + var shouldBindDescendants = true;
66.2014 +
66.2015 + // Perf optimisation: Apply bindings only if...
66.2016 + // (1) We need to store the binding context on this node (because it may differ from the DOM parent node's binding context)
66.2017 + // Note that we can't store binding contexts on non-elements (e.g., text nodes), as IE doesn't allow expando properties for those
66.2018 + // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
66.2019 + var isElement = (nodeVerified.nodeType === 1);
66.2020 + if (isElement) // Workaround IE <= 8 HTML parsing weirdness
66.2021 + ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);
66.2022 +
66.2023 + var shouldApplyBindings = (isElement && bindingContextMayDifferFromDomParentElement) // Case (1)
66.2024 + || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified); // Case (2)
66.2025 + if (shouldApplyBindings)
66.2026 + shouldBindDescendants = applyBindingsToNodeInternal(nodeVerified, null, viewModel, bindingContextMayDifferFromDomParentElement).shouldBindDescendants;
66.2027 +
66.2028 + if (shouldBindDescendants) {
66.2029 + // We're recursing automatically into (real or virtual) child nodes without changing binding contexts. So,
66.2030 + // * For children of a *real* element, the binding context is certainly the same as on their DOM .parentNode,
66.2031 + // hence bindingContextsMayDifferFromDomParentElement is false
66.2032 + // * For children of a *virtual* element, we can't be sure. Evaluating .parentNode on those children may
66.2033 + // skip over any number of intermediate virtual elements, any of which might define a custom binding context,
66.2034 + // hence bindingContextsMayDifferFromDomParentElement is true
66.2035 + applyBindingsToDescendantsInternal(viewModel, nodeVerified, /* bindingContextsMayDifferFromDomParentElement: */ !isElement);
66.2036 + }
66.2037 + }
66.2038 +
66.2039 + function applyBindingsToNodeInternal (node, bindings, viewModelOrBindingContext, bindingContextMayDifferFromDomParentElement) {
66.2040 + // Need to be sure that inits are only run once, and updates never run until all the inits have been run
66.2041 + var initPhase = 0; // 0 = before all inits, 1 = during inits, 2 = after all inits
66.2042 +
66.2043 + // Each time the dependentObservable is evaluated (after data changes),
66.2044 + // the binding attribute is reparsed so that it can pick out the correct
66.2045 + // model properties in the context of the changed data.
66.2046 + // DOM event callbacks need to be able to access this changed data,
66.2047 + // so we need a single parsedBindings variable (shared by all callbacks
66.2048 + // associated with this node's bindings) that all the closures can access.
66.2049 + var parsedBindings;
66.2050 + function makeValueAccessor(bindingKey) {
66.2051 + return function () { return parsedBindings[bindingKey] }
66.2052 + }
66.2053 + function parsedBindingsAccessor() {
66.2054 + return parsedBindings;
66.2055 + }
66.2056 +
66.2057 + var bindingHandlerThatControlsDescendantBindings;
66.2058 + ko.dependentObservable(
66.2059 + function () {
66.2060 + // Ensure we have a nonnull binding context to work with
66.2061 + var bindingContextInstance = viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
66.2062 + ? viewModelOrBindingContext
66.2063 + : new ko.bindingContext(ko.utils.unwrapObservable(viewModelOrBindingContext));
66.2064 + var viewModel = bindingContextInstance['$data'];
66.2065 +
66.2066 + // Optimization: Don't store the binding context on this node if it's definitely the same as on node.parentNode, because
66.2067 + // we can easily recover it just by scanning up the node's ancestors in the DOM
66.2068 + // (note: here, parent node means "real DOM parent" not "virtual parent", as there's no O(1) way to find the virtual parent)
66.2069 + if (bindingContextMayDifferFromDomParentElement)
66.2070 + ko.storedBindingContextForNode(node, bindingContextInstance);
66.2071 +
66.2072 + // Use evaluatedBindings if given, otherwise fall back on asking the bindings provider to give us some bindings
66.2073 + var evaluatedBindings = (typeof bindings == "function") ? bindings(bindingContextInstance, node) : bindings;
66.2074 + parsedBindings = evaluatedBindings || ko.bindingProvider['instance']['getBindings'](node, bindingContextInstance);
66.2075 +
66.2076 + if (parsedBindings) {
66.2077 + // First run all the inits, so bindings can register for notification on changes
66.2078 + if (initPhase === 0) {
66.2079 + initPhase = 1;
66.2080 + for (var bindingKey in parsedBindings) {
66.2081 + var binding = ko.bindingHandlers[bindingKey];
66.2082 + if (binding && node.nodeType === 8)
66.2083 + validateThatBindingIsAllowedForVirtualElements(bindingKey);
66.2084 +
66.2085 + if (binding && typeof binding["init"] == "function") {
66.2086 + var handlerInitFn = binding["init"];
66.2087 + var initResult = handlerInitFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
66.2088 +
66.2089 + // If this binding handler claims to control descendant bindings, make a note of this
66.2090 + if (initResult && initResult['controlsDescendantBindings']) {
66.2091 + if (bindingHandlerThatControlsDescendantBindings !== undefined)
66.2092 + throw new Error("Multiple bindings (" + bindingHandlerThatControlsDescendantBindings + " and " + bindingKey + ") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
66.2093 + bindingHandlerThatControlsDescendantBindings = bindingKey;
66.2094 + }
66.2095 + }
66.2096 + }
66.2097 + initPhase = 2;
66.2098 + }
66.2099 +
66.2100 + // ... then run all the updates, which might trigger changes even on the first evaluation
66.2101 + if (initPhase === 2) {
66.2102 + for (var bindingKey in parsedBindings) {
66.2103 + var binding = ko.bindingHandlers[bindingKey];
66.2104 + if (binding && typeof binding["update"] == "function") {
66.2105 + var handlerUpdateFn = binding["update"];
66.2106 + handlerUpdateFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
66.2107 + }
66.2108 + }
66.2109 + }
66.2110 + }
66.2111 + },
66.2112 + null,
66.2113 + { disposeWhenNodeIsRemoved : node }
66.2114 + );
66.2115 +
66.2116 + return {
66.2117 + shouldBindDescendants: bindingHandlerThatControlsDescendantBindings === undefined
66.2118 + };
66.2119 + };
66.2120 +
66.2121 + var storedBindingContextDomDataKey = "__ko_bindingContext__";
66.2122 + ko.storedBindingContextForNode = function (node, bindingContext) {
66.2123 + if (arguments.length == 2)
66.2124 + ko.utils.domData.set(node, storedBindingContextDomDataKey, bindingContext);
66.2125 + else
66.2126 + return ko.utils.domData.get(node, storedBindingContextDomDataKey);
66.2127 + }
66.2128 +
66.2129 + ko.applyBindingsToNode = function (node, bindings, viewModel) {
66.2130 + if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
66.2131 + ko.virtualElements.normaliseVirtualElementDomStructure(node);
66.2132 + return applyBindingsToNodeInternal(node, bindings, viewModel, true);
66.2133 + };
66.2134 +
66.2135 + ko.applyBindingsToDescendants = function(viewModel, rootNode) {
66.2136 + if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
66.2137 + applyBindingsToDescendantsInternal(viewModel, rootNode, true);
66.2138 + };
66.2139 +
66.2140 + ko.applyBindings = function (viewModel, rootNode) {
66.2141 + if (rootNode && (rootNode.nodeType !== 1) && (rootNode.nodeType !== 8))
66.2142 + throw new Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
66.2143 + rootNode = rootNode || window.document.body; // Make "rootNode" parameter optional
66.2144 +
66.2145 + applyBindingsToNodeAndDescendantsInternal(viewModel, rootNode, true);
66.2146 + };
66.2147 +
66.2148 + // Retrieving binding context from arbitrary nodes
66.2149 + ko.contextFor = function(node) {
66.2150 + // We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)
66.2151 + switch (node.nodeType) {
66.2152 + case 1:
66.2153 + case 8:
66.2154 + var context = ko.storedBindingContextForNode(node);
66.2155 + if (context) return context;
66.2156 + if (node.parentNode) return ko.contextFor(node.parentNode);
66.2157 + break;
66.2158 + }
66.2159 + return undefined;
66.2160 + };
66.2161 + ko.dataFor = function(node) {
66.2162 + var context = ko.contextFor(node);
66.2163 + return context ? context['$data'] : undefined;
66.2164 + };
66.2165 +
66.2166 + ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
66.2167 + ko.exportSymbol('applyBindings', ko.applyBindings);
66.2168 + ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
66.2169 + ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
66.2170 + ko.exportSymbol('contextFor', ko.contextFor);
66.2171 + ko.exportSymbol('dataFor', ko.dataFor);
66.2172 +})();
66.2173 +var attrHtmlToJavascriptMap = { 'class': 'className', 'for': 'htmlFor' };
66.2174 +ko.bindingHandlers['attr'] = {
66.2175 + 'update': function(element, valueAccessor, allBindingsAccessor) {
66.2176 + var value = ko.utils.unwrapObservable(valueAccessor()) || {};
66.2177 + for (var attrName in value) {
66.2178 + if (typeof attrName == "string") {
66.2179 + var attrValue = ko.utils.unwrapObservable(value[attrName]);
66.2180 +
66.2181 + // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
66.2182 + // when someProp is a "no value"-like value (strictly null, false, or undefined)
66.2183 + // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
66.2184 + var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
66.2185 + if (toRemove)
66.2186 + element.removeAttribute(attrName);
66.2187 +
66.2188 + // In IE <= 7 and IE8 Quirks Mode, you have to use the Javascript property name instead of the
66.2189 + // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
66.2190 + // but instead of figuring out the mode, we'll just set the attribute through the Javascript
66.2191 + // property for IE <= 8.
66.2192 + if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavascriptMap) {
66.2193 + attrName = attrHtmlToJavascriptMap[attrName];
66.2194 + if (toRemove)
66.2195 + element.removeAttribute(attrName);
66.2196 + else
66.2197 + element[attrName] = attrValue;
66.2198 + } else if (!toRemove) {
66.2199 + try {
66.2200 + element.setAttribute(attrName, attrValue.toString());
66.2201 + } catch (err) {
66.2202 + // ignore for now
66.2203 + if (console) {
66.2204 + console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err);
66.2205 + }
66.2206 + }
66.2207 + }
66.2208 +
66.2209 + // Treat "name" specially - although you can think of it as an attribute, it also needs
66.2210 + // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
66.2211 + // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
66.2212 + // entirely, and there's no strong reason to allow for such casing in HTML.
66.2213 + if (attrName === "name") {
66.2214 + ko.utils.setElementName(element, toRemove ? "" : attrValue.toString());
66.2215 + }
66.2216 + }
66.2217 + }
66.2218 + }
66.2219 +};
66.2220 +ko.bindingHandlers['checked'] = {
66.2221 + 'init': function (element, valueAccessor, allBindingsAccessor) {
66.2222 + var updateHandler = function() {
66.2223 + var valueToWrite;
66.2224 + if (element.type == "checkbox") {
66.2225 + valueToWrite = element.checked;
66.2226 + } else if ((element.type == "radio") && (element.checked)) {
66.2227 + valueToWrite = element.value;
66.2228 + } else {
66.2229 + return; // "checked" binding only responds to checkboxes and selected radio buttons
66.2230 + }
66.2231 +
66.2232 + var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue);
66.2233 + if ((element.type == "checkbox") && (unwrappedValue instanceof Array)) {
66.2234 + // For checkboxes bound to an array, we add/remove the checkbox value to that array
66.2235 + // This works for both observable and non-observable arrays
66.2236 + var existingEntryIndex = ko.utils.arrayIndexOf(unwrappedValue, element.value);
66.2237 + if (element.checked && (existingEntryIndex < 0))
66.2238 + modelValue.push(element.value);
66.2239 + else if ((!element.checked) && (existingEntryIndex >= 0))
66.2240 + modelValue.splice(existingEntryIndex, 1);
66.2241 + } else {
66.2242 + ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
66.2243 + }
66.2244 + };
66.2245 + ko.utils.registerEventHandler(element, "click", updateHandler);
66.2246 +
66.2247 + // IE 6 won't allow radio buttons to be selected unless they have a name
66.2248 + if ((element.type == "radio") && !element.name)
66.2249 + ko.bindingHandlers['uniqueName']['init'](element, function() { return true });
66.2250 + },
66.2251 + 'update': function (element, valueAccessor) {
66.2252 + var value = ko.utils.unwrapObservable(valueAccessor());
66.2253 +
66.2254 + if (element.type == "checkbox") {
66.2255 + if (value instanceof Array) {
66.2256 + // When bound to an array, the checkbox being checked represents its value being present in that array
66.2257 + element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
66.2258 + } else {
66.2259 + // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
66.2260 + element.checked = value;
66.2261 + }
66.2262 + } else if (element.type == "radio") {
66.2263 + element.checked = (element.value == value);
66.2264 + }
66.2265 + }
66.2266 +};
66.2267 +var classesWrittenByBindingKey = '__ko__cssValue';
66.2268 +ko.bindingHandlers['css'] = {
66.2269 + 'update': function (element, valueAccessor) {
66.2270 + var value = ko.utils.unwrapObservable(valueAccessor());
66.2271 + if (typeof value == "object") {
66.2272 + for (var className in value) {
66.2273 + var shouldHaveClass = ko.utils.unwrapObservable(value[className]);
66.2274 + ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
66.2275 + }
66.2276 + } else {
66.2277 + value = String(value || ''); // Make sure we don't try to store or set a non-string value
66.2278 + ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
66.2279 + element[classesWrittenByBindingKey] = value;
66.2280 + ko.utils.toggleDomNodeCssClass(element, value, true);
66.2281 + }
66.2282 + }
66.2283 +};
66.2284 +ko.bindingHandlers['enable'] = {
66.2285 + 'update': function (element, valueAccessor) {
66.2286 + var value = ko.utils.unwrapObservable(valueAccessor());
66.2287 + if (value && element.disabled)
66.2288 + element.removeAttribute("disabled");
66.2289 + else if ((!value) && (!element.disabled))
66.2290 + element.disabled = true;
66.2291 + }
66.2292 +};
66.2293 +
66.2294 +ko.bindingHandlers['disable'] = {
66.2295 + 'update': function (element, valueAccessor) {
66.2296 + ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
66.2297 + }
66.2298 +};
66.2299 +// For certain common events (currently just 'click'), allow a simplified data-binding syntax
66.2300 +// e.g. click:handler instead of the usual full-length event:{click:handler}
66.2301 +function makeEventHandlerShortcut(eventName) {
66.2302 + ko.bindingHandlers[eventName] = {
66.2303 + 'init': function(element, valueAccessor, allBindingsAccessor, viewModel) {
66.2304 + var newValueAccessor = function () {
66.2305 + var result = {};
66.2306 + result[eventName] = valueAccessor();
66.2307 + return result;
66.2308 + };
66.2309 + return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindingsAccessor, viewModel);
66.2310 + }
66.2311 + }
66.2312 +}
66.2313 +
66.2314 +ko.bindingHandlers['event'] = {
66.2315 + 'init' : function (element, valueAccessor, allBindingsAccessor, viewModel) {
66.2316 + var eventsToHandle = valueAccessor() || {};
66.2317 + for(var eventNameOutsideClosure in eventsToHandle) {
66.2318 + (function() {
66.2319 + var eventName = eventNameOutsideClosure; // Separate variable to be captured by event handler closure
66.2320 + if (typeof eventName == "string") {
66.2321 + ko.utils.registerEventHandler(element, eventName, function (event) {
66.2322 + var handlerReturnValue;
66.2323 + var handlerFunction = valueAccessor()[eventName];
66.2324 + if (!handlerFunction)
66.2325 + return;
66.2326 + var allBindings = allBindingsAccessor();
66.2327 +
66.2328 + try {
66.2329 + // Take all the event args, and prefix with the viewmodel
66.2330 + var argsForHandler = ko.utils.makeArray(arguments);
66.2331 + argsForHandler.unshift(viewModel);
66.2332 + handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
66.2333 + } finally {
66.2334 + if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
66.2335 + if (event.preventDefault)
66.2336 + event.preventDefault();
66.2337 + else
66.2338 + event.returnValue = false;
66.2339 + }
66.2340 + }
66.2341 +
66.2342 + var bubble = allBindings[eventName + 'Bubble'] !== false;
66.2343 + if (!bubble) {
66.2344 + event.cancelBubble = true;
66.2345 + if (event.stopPropagation)
66.2346 + event.stopPropagation();
66.2347 + }
66.2348 + });
66.2349 + }
66.2350 + })();
66.2351 + }
66.2352 + }
66.2353 +};
66.2354 +// "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
66.2355 +// "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
66.2356 +ko.bindingHandlers['foreach'] = {
66.2357 + makeTemplateValueAccessor: function(valueAccessor) {
66.2358 + return function() {
66.2359 + var modelValue = valueAccessor(),
66.2360 + unwrappedValue = ko.utils.peekObservable(modelValue); // Unwrap without setting a dependency here
66.2361 +
66.2362 + // If unwrappedValue is the array, pass in the wrapped value on its own
66.2363 + // The value will be unwrapped and tracked within the template binding
66.2364 + // (See https://github.com/SteveSanderson/knockout/issues/523)
66.2365 + if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
66.2366 + return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };
66.2367 +
66.2368 + // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
66.2369 + ko.utils.unwrapObservable(modelValue);
66.2370 + return {
66.2371 + 'foreach': unwrappedValue['data'],
66.2372 + 'as': unwrappedValue['as'],
66.2373 + 'includeDestroyed': unwrappedValue['includeDestroyed'],
66.2374 + 'afterAdd': unwrappedValue['afterAdd'],
66.2375 + 'beforeRemove': unwrappedValue['beforeRemove'],
66.2376 + 'afterRender': unwrappedValue['afterRender'],
66.2377 + 'beforeMove': unwrappedValue['beforeMove'],
66.2378 + 'afterMove': unwrappedValue['afterMove'],
66.2379 + 'templateEngine': ko.nativeTemplateEngine.instance
66.2380 + };
66.2381 + };
66.2382 + },
66.2383 + 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
66.2384 + return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
66.2385 + },
66.2386 + 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
66.2387 + return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindingsAccessor, viewModel, bindingContext);
66.2388 + }
66.2389 +};
66.2390 +ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
66.2391 +ko.virtualElements.allowedBindings['foreach'] = true;
66.2392 +var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
66.2393 +ko.bindingHandlers['hasfocus'] = {
66.2394 + 'init': function(element, valueAccessor, allBindingsAccessor) {
66.2395 + var handleElementFocusChange = function(isFocused) {
66.2396 + // Where possible, ignore which event was raised and determine focus state using activeElement,
66.2397 + // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
66.2398 + // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
66.2399 + // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
66.2400 + // from calling 'blur()' on the element when it loses focus.
66.2401 + // Discussion at https://github.com/SteveSanderson/knockout/pull/352
66.2402 + element[hasfocusUpdatingProperty] = true;
66.2403 + var ownerDoc = element.ownerDocument;
66.2404 + if ("activeElement" in ownerDoc) {
66.2405 + isFocused = (ownerDoc.activeElement === element);
66.2406 + }
66.2407 + var modelValue = valueAccessor();
66.2408 + ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'hasfocus', isFocused, true);
66.2409 + element[hasfocusUpdatingProperty] = false;
66.2410 + };
66.2411 + var handleElementFocusIn = handleElementFocusChange.bind(null, true);
66.2412 + var handleElementFocusOut = handleElementFocusChange.bind(null, false);
66.2413 +
66.2414 + ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
66.2415 + ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
66.2416 + ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
66.2417 + ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
66.2418 + },
66.2419 + 'update': function(element, valueAccessor) {
66.2420 + var value = ko.utils.unwrapObservable(valueAccessor());
66.2421 + if (!element[hasfocusUpdatingProperty]) {
66.2422 + value ? element.focus() : element.blur();
66.2423 + ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]); // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
66.2424 + }
66.2425 + }
66.2426 +};
66.2427 +ko.bindingHandlers['html'] = {
66.2428 + 'init': function() {
66.2429 + // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
66.2430 + return { 'controlsDescendantBindings': true };
66.2431 + },
66.2432 + 'update': function (element, valueAccessor) {
66.2433 + // setHtml will unwrap the value if needed
66.2434 + ko.utils.setHtml(element, valueAccessor());
66.2435 + }
66.2436 +};
66.2437 +var withIfDomDataKey = '__ko_withIfBindingData';
66.2438 +// Makes a binding like with or if
66.2439 +function makeWithIfBinding(bindingKey, isWith, isNot, makeContextCallback) {
66.2440 + ko.bindingHandlers[bindingKey] = {
66.2441 + 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
66.2442 + ko.utils.domData.set(element, withIfDomDataKey, {});
66.2443 + return { 'controlsDescendantBindings': true };
66.2444 + },
66.2445 + 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
66.2446 + var withIfData = ko.utils.domData.get(element, withIfDomDataKey),
66.2447 + dataValue = ko.utils.unwrapObservable(valueAccessor()),
66.2448 + shouldDisplay = !isNot !== !dataValue, // equivalent to isNot ? !dataValue : !!dataValue
66.2449 + isFirstRender = !withIfData.savedNodes,
66.2450 + needsRefresh = isFirstRender || isWith || (shouldDisplay !== withIfData.didDisplayOnLastUpdate);
66.2451 +
66.2452 + if (needsRefresh) {
66.2453 + if (isFirstRender) {
66.2454 + withIfData.savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
66.2455 + }
66.2456 +
66.2457 + if (shouldDisplay) {
66.2458 + if (!isFirstRender) {
66.2459 + ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(withIfData.savedNodes));
66.2460 + }
66.2461 + ko.applyBindingsToDescendants(makeContextCallback ? makeContextCallback(bindingContext, dataValue) : bindingContext, element);
66.2462 + } else {
66.2463 + ko.virtualElements.emptyNode(element);
66.2464 + }
66.2465 +
66.2466 + withIfData.didDisplayOnLastUpdate = shouldDisplay;
66.2467 + }
66.2468 + }
66.2469 + };
66.2470 + ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
66.2471 + ko.virtualElements.allowedBindings[bindingKey] = true;
66.2472 +}
66.2473 +
66.2474 +// Construct the actual binding handlers
66.2475 +makeWithIfBinding('if');
66.2476 +makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
66.2477 +makeWithIfBinding('with', true /* isWith */, false /* isNot */,
66.2478 + function(bindingContext, dataValue) {
66.2479 + return bindingContext['createChildContext'](dataValue);
66.2480 + }
66.2481 +);
66.2482 +function ensureDropdownSelectionIsConsistentWithModelValue(element, modelValue, preferModelValue) {
66.2483 + if (preferModelValue) {
66.2484 + if (modelValue !== ko.selectExtensions.readValue(element))
66.2485 + ko.selectExtensions.writeValue(element, modelValue);
66.2486 + }
66.2487 +
66.2488 + // No matter which direction we're syncing in, we want the end result to be equality between dropdown value and model value.
66.2489 + // If they aren't equal, either we prefer the dropdown value, or the model value couldn't be represented, so either way,
66.2490 + // change the model value to match the dropdown.
66.2491 + if (modelValue !== ko.selectExtensions.readValue(element))
66.2492 + ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
66.2493 +};
66.2494 +
66.2495 +ko.bindingHandlers['options'] = {
66.2496 + 'update': function (element, valueAccessor, allBindingsAccessor) {
66.2497 + if (ko.utils.tagNameLower(element) !== "select")
66.2498 + throw new Error("options binding applies only to SELECT elements");
66.2499 +
66.2500 + var selectWasPreviouslyEmpty = element.length == 0;
66.2501 + var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
66.2502 + return node.tagName && (ko.utils.tagNameLower(node) === "option") && node.selected;
66.2503 + }), function (node) {
66.2504 + return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
66.2505 + });
66.2506 + var previousScrollTop = element.scrollTop;
66.2507 +
66.2508 + var value = ko.utils.unwrapObservable(valueAccessor());
66.2509 + var selectedValue = element.value;
66.2510 +
66.2511 + // Remove all existing <option>s.
66.2512 + // Need to use .remove() rather than .removeChild() for <option>s otherwise IE behaves oddly (https://github.com/SteveSanderson/knockout/issues/134)
66.2513 + while (element.length > 0) {
66.2514 + ko.cleanNode(element.options[0]);
66.2515 + element.remove(0);
66.2516 + }
66.2517 +
66.2518 + if (value) {
66.2519 + var allBindings = allBindingsAccessor(),
66.2520 + includeDestroyed = allBindings['optionsIncludeDestroyed'];
66.2521 +
66.2522 + if (typeof value.length != "number")
66.2523 + value = [value];
66.2524 + if (allBindings['optionsCaption']) {
66.2525 + var option = document.createElement("option");
66.2526 + ko.utils.setHtml(option, allBindings['optionsCaption']);
66.2527 + ko.selectExtensions.writeValue(option, undefined);
66.2528 + element.appendChild(option);
66.2529 + }
66.2530 +
66.2531 + for (var i = 0, j = value.length; i < j; i++) {
66.2532 + // Skip destroyed items
66.2533 + var arrayEntry = value[i];
66.2534 + if (arrayEntry && arrayEntry['_destroy'] && !includeDestroyed)
66.2535 + continue;
66.2536 +
66.2537 + var option = document.createElement("option");
66.2538 +
66.2539 + function applyToObject(object, predicate, defaultValue) {
66.2540 + var predicateType = typeof predicate;
66.2541 + if (predicateType == "function") // Given a function; run it against the data value
66.2542 + return predicate(object);
66.2543 + else if (predicateType == "string") // Given a string; treat it as a property name on the data value
66.2544 + return object[predicate];
66.2545 + else // Given no optionsText arg; use the data value itself
66.2546 + return defaultValue;
66.2547 + }
66.2548 +
66.2549 + // Apply a value to the option element
66.2550 + var optionValue = applyToObject(arrayEntry, allBindings['optionsValue'], arrayEntry);
66.2551 + ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));
66.2552 +
66.2553 + // Apply some text to the option element
66.2554 + var optionText = applyToObject(arrayEntry, allBindings['optionsText'], optionValue);
66.2555 + ko.utils.setTextContent(option, optionText);
66.2556 +
66.2557 + element.appendChild(option);
66.2558 + }
66.2559 +
66.2560 + // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
66.2561 + // That's why we first added them without selection. Now it's time to set the selection.
66.2562 + var newOptions = element.getElementsByTagName("option");
66.2563 + var countSelectionsRetained = 0;
66.2564 + for (var i = 0, j = newOptions.length; i < j; i++) {
66.2565 + if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
66.2566 + ko.utils.setOptionNodeSelectionState(newOptions[i], true);
66.2567 + countSelectionsRetained++;
66.2568 + }
66.2569 + }
66.2570 +
66.2571 + element.scrollTop = previousScrollTop;
66.2572 +
66.2573 + if (selectWasPreviouslyEmpty && ('value' in allBindings)) {
66.2574 + // Ensure consistency between model value and selected option.
66.2575 + // If the dropdown is being populated for the first time here (or was otherwise previously empty),
66.2576 + // the dropdown selection state is meaningless, so we preserve the model value.
66.2577 + ensureDropdownSelectionIsConsistentWithModelValue(element, ko.utils.peekObservable(allBindings['value']), /* preferModelValue */ true);
66.2578 + }
66.2579 +
66.2580 + // Workaround for IE9 bug
66.2581 + ko.utils.ensureSelectElementIsRenderedCorrectly(element);
66.2582 + }
66.2583 + }
66.2584 +};
66.2585 +ko.bindingHandlers['options'].optionValueDomDataKey = '__ko.optionValueDomData__';
66.2586 +ko.bindingHandlers['selectedOptions'] = {
66.2587 + 'init': function (element, valueAccessor, allBindingsAccessor) {
66.2588 + ko.utils.registerEventHandler(element, "change", function () {
66.2589 + var value = valueAccessor(), valueToWrite = [];
66.2590 + ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
66.2591 + if (node.selected)
66.2592 + valueToWrite.push(ko.selectExtensions.readValue(node));
66.2593 + });
66.2594 + ko.expressionRewriting.writeValueToProperty(value, allBindingsAccessor, 'value', valueToWrite);
66.2595 + });
66.2596 + },
66.2597 + 'update': function (element, valueAccessor) {
66.2598 + if (ko.utils.tagNameLower(element) != "select")
66.2599 + throw new Error("values binding applies only to SELECT elements");
66.2600 +
66.2601 + var newValue = ko.utils.unwrapObservable(valueAccessor());
66.2602 + if (newValue && typeof newValue.length == "number") {
66.2603 + ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
66.2604 + var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
66.2605 + ko.utils.setOptionNodeSelectionState(node, isSelected);
66.2606 + });
66.2607 + }
66.2608 + }
66.2609 +};
66.2610 +ko.bindingHandlers['style'] = {
66.2611 + 'update': function (element, valueAccessor) {
66.2612 + var value = ko.utils.unwrapObservable(valueAccessor() || {});
66.2613 + for (var styleName in value) {
66.2614 + if (typeof styleName == "string") {
66.2615 + var styleValue = ko.utils.unwrapObservable(value[styleName]);
66.2616 + element.style[styleName] = styleValue || ""; // Empty string removes the value, whereas null/undefined have no effect
66.2617 + }
66.2618 + }
66.2619 + }
66.2620 +};
66.2621 +ko.bindingHandlers['submit'] = {
66.2622 + 'init': function (element, valueAccessor, allBindingsAccessor, viewModel) {
66.2623 + if (typeof valueAccessor() != "function")
66.2624 + throw new Error("The value for a submit binding must be a function");
66.2625 + ko.utils.registerEventHandler(element, "submit", function (event) {
66.2626 + var handlerReturnValue;
66.2627 + var value = valueAccessor();
66.2628 + try { handlerReturnValue = value.call(viewModel, element); }
66.2629 + finally {
66.2630 + if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
66.2631 + if (event.preventDefault)
66.2632 + event.preventDefault();
66.2633 + else
66.2634 + event.returnValue = false;
66.2635 + }
66.2636 + }
66.2637 + });
66.2638 + }
66.2639 +};
66.2640 +ko.bindingHandlers['text'] = {
66.2641 + 'update': function (element, valueAccessor) {
66.2642 + ko.utils.setTextContent(element, valueAccessor());
66.2643 + }
66.2644 +};
66.2645 +ko.virtualElements.allowedBindings['text'] = true;
66.2646 +ko.bindingHandlers['uniqueName'] = {
66.2647 + 'init': function (element, valueAccessor) {
66.2648 + if (valueAccessor()) {
66.2649 + var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
66.2650 + ko.utils.setElementName(element, name);
66.2651 + }
66.2652 + }
66.2653 +};
66.2654 +ko.bindingHandlers['uniqueName'].currentIndex = 0;
66.2655 +ko.bindingHandlers['value'] = {
66.2656 + 'init': function (element, valueAccessor, allBindingsAccessor) {
66.2657 + // Always catch "change" event; possibly other events too if asked
66.2658 + var eventsToCatch = ["change"];
66.2659 + var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
66.2660 + var propertyChangedFired = false;
66.2661 + if (requestedEventsToCatch) {
66.2662 + if (typeof requestedEventsToCatch == "string") // Allow both individual event names, and arrays of event names
66.2663 + requestedEventsToCatch = [requestedEventsToCatch];
66.2664 + ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
66.2665 + eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
66.2666 + }
66.2667 +
66.2668 + var valueUpdateHandler = function() {
66.2669 + propertyChangedFired = false;
66.2670 + var modelValue = valueAccessor();
66.2671 + var elementValue = ko.selectExtensions.readValue(element);
66.2672 + ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'value', elementValue);
66.2673 + }
66.2674 +
66.2675 + // Workaround for https://github.com/SteveSanderson/knockout/issues/122
66.2676 + // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
66.2677 + var ieAutoCompleteHackNeeded = ko.utils.ieVersion && element.tagName.toLowerCase() == "input" && element.type == "text"
66.2678 + && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
66.2679 + if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
66.2680 + ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
66.2681 + ko.utils.registerEventHandler(element, "blur", function() {
66.2682 + if (propertyChangedFired) {
66.2683 + valueUpdateHandler();
66.2684 + }
66.2685 + });
66.2686 + }
66.2687 +
66.2688 + ko.utils.arrayForEach(eventsToCatch, function(eventName) {
66.2689 + // The syntax "after<eventname>" means "run the handler asynchronously after the event"
66.2690 + // This is useful, for example, to catch "keydown" events after the browser has updated the control
66.2691 + // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
66.2692 + var handler = valueUpdateHandler;
66.2693 + if (ko.utils.stringStartsWith(eventName, "after")) {
66.2694 + handler = function() { setTimeout(valueUpdateHandler, 0) };
66.2695 + eventName = eventName.substring("after".length);
66.2696 + }
66.2697 + ko.utils.registerEventHandler(element, eventName, handler);
66.2698 + });
66.2699 + },
66.2700 + 'update': function (element, valueAccessor) {
66.2701 + var valueIsSelectOption = ko.utils.tagNameLower(element) === "select";
66.2702 + var newValue = ko.utils.unwrapObservable(valueAccessor());
66.2703 + var elementValue = ko.selectExtensions.readValue(element);
66.2704 + var valueHasChanged = (newValue != elementValue);
66.2705 +
66.2706 + // JavaScript's 0 == "" behavious is unfortunate here as it prevents writing 0 to an empty text box (loose equality suggests the values are the same).
66.2707 + // We don't want to do a strict equality comparison as that is more confusing for developers in certain cases, so we specifically special case 0 != "" here.
66.2708 + if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
66.2709 + valueHasChanged = true;
66.2710 +
66.2711 + if (valueHasChanged) {
66.2712 + var applyValueAction = function () { ko.selectExtensions.writeValue(element, newValue); };
66.2713 + applyValueAction();
66.2714 +
66.2715 + // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
66.2716 + // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
66.2717 + // to apply the value as well.
66.2718 + var alsoApplyAsynchronously = valueIsSelectOption;
66.2719 + if (alsoApplyAsynchronously)
66.2720 + setTimeout(applyValueAction, 0);
66.2721 + }
66.2722 +
66.2723 + // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
66.2724 + // because you're not allowed to have a model value that disagrees with a visible UI selection.
66.2725 + if (valueIsSelectOption && (element.length > 0))
66.2726 + ensureDropdownSelectionIsConsistentWithModelValue(element, newValue, /* preferModelValue */ false);
66.2727 + }
66.2728 +};
66.2729 +ko.bindingHandlers['visible'] = {
66.2730 + 'update': function (element, valueAccessor) {
66.2731 + var value = ko.utils.unwrapObservable(valueAccessor());
66.2732 + var isCurrentlyVisible = !(element.style.display == "none");
66.2733 + if (value && !isCurrentlyVisible)
66.2734 + element.style.display = "";
66.2735 + else if ((!value) && isCurrentlyVisible)
66.2736 + element.style.display = "none";
66.2737 + }
66.2738 +};
66.2739 +// 'click' is just a shorthand for the usual full-length event:{click:handler}
66.2740 +makeEventHandlerShortcut('click');
66.2741 +// If you want to make a custom template engine,
66.2742 +//
66.2743 +// [1] Inherit from this class (like ko.nativeTemplateEngine does)
66.2744 +// [2] Override 'renderTemplateSource', supplying a function with this signature:
66.2745 +//
66.2746 +// function (templateSource, bindingContext, options) {
66.2747 +// // - templateSource.text() is the text of the template you should render
66.2748 +// // - bindingContext.$data is the data you should pass into the template
66.2749 +// // - you might also want to make bindingContext.$parent, bindingContext.$parents,
66.2750 +// // and bindingContext.$root available in the template too
66.2751 +// // - options gives you access to any other properties set on "data-bind: { template: options }"
66.2752 +// //
66.2753 +// // Return value: an array of DOM nodes
66.2754 +// }
66.2755 +//
66.2756 +// [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
66.2757 +//
66.2758 +// function (script) {
66.2759 +// // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
66.2760 +// // For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
66.2761 +// }
66.2762 +//
66.2763 +// This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
66.2764 +// If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
66.2765 +// and then you don't need to override 'createJavaScriptEvaluatorBlock'.
66.2766 +
66.2767 +ko.templateEngine = function () { };
66.2768 +
66.2769 +ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
66.2770 + throw new Error("Override renderTemplateSource");
66.2771 +};
66.2772 +
66.2773 +ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
66.2774 + throw new Error("Override createJavaScriptEvaluatorBlock");
66.2775 +};
66.2776 +
66.2777 +ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {
66.2778 + // Named template
66.2779 + if (typeof template == "string") {
66.2780 + templateDocument = templateDocument || document;
66.2781 + var elem = templateDocument.getElementById(template);
66.2782 + if (!elem)
66.2783 + throw new Error("Cannot find template with ID " + template);
66.2784 + return new ko.templateSources.domElement(elem);
66.2785 + } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
66.2786 + // Anonymous template
66.2787 + return new ko.templateSources.anonymousTemplate(template);
66.2788 + } else
66.2789 + throw new Error("Unknown template type: " + template);
66.2790 +};
66.2791 +
66.2792 +ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
66.2793 + var templateSource = this['makeTemplateSource'](template, templateDocument);
66.2794 + return this['renderTemplateSource'](templateSource, bindingContext, options);
66.2795 +};
66.2796 +
66.2797 +ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
66.2798 + // Skip rewriting if requested
66.2799 + if (this['allowTemplateRewriting'] === false)
66.2800 + return true;
66.2801 + return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
66.2802 +};
66.2803 +
66.2804 +ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
66.2805 + var templateSource = this['makeTemplateSource'](template, templateDocument);
66.2806 + var rewritten = rewriterCallback(templateSource['text']());
66.2807 + templateSource['text'](rewritten);
66.2808 + templateSource['data']("isRewritten", true);
66.2809 +};
66.2810 +
66.2811 +ko.exportSymbol('templateEngine', ko.templateEngine);
66.2812 +
66.2813 +ko.templateRewriting = (function () {
66.2814 + var memoizeDataBindingAttributeSyntaxRegex = /(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi;
66.2815 + var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;
66.2816 +
66.2817 + function validateDataBindValuesForRewriting(keyValueArray) {
66.2818 + var allValidators = ko.expressionRewriting.bindingRewriteValidators;
66.2819 + for (var i = 0; i < keyValueArray.length; i++) {
66.2820 + var key = keyValueArray[i]['key'];
66.2821 + if (allValidators.hasOwnProperty(key)) {
66.2822 + var validator = allValidators[key];
66.2823 +
66.2824 + if (typeof validator === "function") {
66.2825 + var possibleErrorMessage = validator(keyValueArray[i]['value']);
66.2826 + if (possibleErrorMessage)
66.2827 + throw new Error(possibleErrorMessage);
66.2828 + } else if (!validator) {
66.2829 + throw new Error("This template engine does not support the '" + key + "' binding within its templates");
66.2830 + }
66.2831 + }
66.2832 + }
66.2833 + }
66.2834 +
66.2835 + function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, templateEngine) {
66.2836 + var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
66.2837 + validateDataBindValuesForRewriting(dataBindKeyValueArray);
66.2838 + var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray);
66.2839 +
66.2840 + // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
66.2841 + // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
66.2842 + // extra indirection.
66.2843 + var applyBindingsToNextSiblingScript =
66.2844 + "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()})";
66.2845 + return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
66.2846 + }
66.2847 +
66.2848 + return {
66.2849 + ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
66.2850 + if (!templateEngine['isTemplateRewritten'](template, templateDocument))
66.2851 + templateEngine['rewriteTemplate'](template, function (htmlString) {
66.2852 + return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
66.2853 + }, templateDocument);
66.2854 + },
66.2855 +
66.2856 + memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
66.2857 + return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
66.2858 + return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[6], /* tagToRetain: */ arguments[1], templateEngine);
66.2859 + }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {
66.2860 + return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", templateEngine);
66.2861 + });
66.2862 + },
66.2863 +
66.2864 + applyMemoizedBindingsToNextSibling: function (bindings) {
66.2865 + return ko.memoization.memoize(function (domNode, bindingContext) {
66.2866 + if (domNode.nextSibling)
66.2867 + ko.applyBindingsToNode(domNode.nextSibling, bindings, bindingContext);
66.2868 + });
66.2869 + }
66.2870 + }
66.2871 +})();
66.2872 +
66.2873 +
66.2874 +// Exported only because it has to be referenced by string lookup from within rewritten template
66.2875 +ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
66.2876 +(function() {
66.2877 + // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
66.2878 + // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
66.2879 + //
66.2880 + // Two are provided by default:
66.2881 + // 1. ko.templateSources.domElement - reads/writes the text content of an arbitrary DOM element
66.2882 + // 2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
66.2883 + // without reading/writing the actual element text content, since it will be overwritten
66.2884 + // with the rendered template output.
66.2885 + // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
66.2886 + // Template sources need to have the following functions:
66.2887 + // text() - returns the template text from your storage location
66.2888 + // text(value) - writes the supplied template text to your storage location
66.2889 + // data(key) - reads values stored using data(key, value) - see below
66.2890 + // data(key, value) - associates "value" with this template and the key "key". Is used to store information like "isRewritten".
66.2891 + //
66.2892 + // Optionally, template sources can also have the following functions:
66.2893 + // nodes() - returns a DOM element containing the nodes of this template, where available
66.2894 + // nodes(value) - writes the given DOM element to your storage location
66.2895 + // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
66.2896 + // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
66.2897 + //
66.2898 + // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
66.2899 + // using and overriding "makeTemplateSource" to return an instance of your custom template source.
66.2900 +
66.2901 + ko.templateSources = {};
66.2902 +
66.2903 + // ---- ko.templateSources.domElement -----
66.2904 +
66.2905 + ko.templateSources.domElement = function(element) {
66.2906 + this.domElement = element;
66.2907 + }
66.2908 +
66.2909 + ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
66.2910 + var tagNameLower = ko.utils.tagNameLower(this.domElement),
66.2911 + elemContentsProperty = tagNameLower === "script" ? "text"
66.2912 + : tagNameLower === "textarea" ? "value"
66.2913 + : "innerHTML";
66.2914 +
66.2915 + if (arguments.length == 0) {
66.2916 + return this.domElement[elemContentsProperty];
66.2917 + } else {
66.2918 + var valueToWrite = arguments[0];
66.2919 + if (elemContentsProperty === "innerHTML")
66.2920 + ko.utils.setHtml(this.domElement, valueToWrite);
66.2921 + else
66.2922 + this.domElement[elemContentsProperty] = valueToWrite;
66.2923 + }
66.2924 + };
66.2925 +
66.2926 + ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {
66.2927 + if (arguments.length === 1) {
66.2928 + return ko.utils.domData.get(this.domElement, "templateSourceData_" + key);
66.2929 + } else {
66.2930 + ko.utils.domData.set(this.domElement, "templateSourceData_" + key, arguments[1]);
66.2931 + }
66.2932 + };
66.2933 +
66.2934 + // ---- ko.templateSources.anonymousTemplate -----
66.2935 + // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
66.2936 + // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
66.2937 + // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.
66.2938 +
66.2939 + var anonymousTemplatesDomDataKey = "__ko_anon_template__";
66.2940 + ko.templateSources.anonymousTemplate = function(element) {
66.2941 + this.domElement = element;
66.2942 + }
66.2943 + ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
66.2944 + ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
66.2945 + if (arguments.length == 0) {
66.2946 + var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
66.2947 + if (templateData.textData === undefined && templateData.containerData)
66.2948 + templateData.textData = templateData.containerData.innerHTML;
66.2949 + return templateData.textData;
66.2950 + } else {
66.2951 + var valueToWrite = arguments[0];
66.2952 + ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {textData: valueToWrite});
66.2953 + }
66.2954 + };
66.2955 + ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
66.2956 + if (arguments.length == 0) {
66.2957 + var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
66.2958 + return templateData.containerData;
66.2959 + } else {
66.2960 + var valueToWrite = arguments[0];
66.2961 + ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {containerData: valueToWrite});
66.2962 + }
66.2963 + };
66.2964 +
66.2965 + ko.exportSymbol('templateSources', ko.templateSources);
66.2966 + ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
66.2967 + ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
66.2968 +})();
66.2969 +(function () {
66.2970 + var _templateEngine;
66.2971 + ko.setTemplateEngine = function (templateEngine) {
66.2972 + if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
66.2973 + throw new Error("templateEngine must inherit from ko.templateEngine");
66.2974 + _templateEngine = templateEngine;
66.2975 + }
66.2976 +
66.2977 + function invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, action) {
66.2978 + var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
66.2979 + while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
66.2980 + nextInQueue = ko.virtualElements.nextSibling(node);
66.2981 + if (node.nodeType === 1 || node.nodeType === 8)
66.2982 + action(node);
66.2983 + }
66.2984 + }
66.2985 +
66.2986 + function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
66.2987 + // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
66.2988 + // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
66.2989 + // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
66.2990 + // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
66.2991 + // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)
66.2992 +
66.2993 + if (continuousNodeArray.length) {
66.2994 + var firstNode = continuousNodeArray[0], lastNode = continuousNodeArray[continuousNodeArray.length - 1];
66.2995 +
66.2996 + // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
66.2997 + // whereas a regular applyBindings won't introduce new memoized nodes
66.2998 + invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
66.2999 + ko.applyBindings(bindingContext, node);
66.3000 + });
66.3001 + invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
66.3002 + ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
66.3003 + });
66.3004 + }
66.3005 + }
66.3006 +
66.3007 + function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
66.3008 + return nodeOrNodeArray.nodeType ? nodeOrNodeArray
66.3009 + : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
66.3010 + : null;
66.3011 + }
66.3012 +
66.3013 + function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
66.3014 + options = options || {};
66.3015 + var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
66.3016 + var templateDocument = firstTargetNode && firstTargetNode.ownerDocument;
66.3017 + var templateEngineToUse = (options['templateEngine'] || _templateEngine);
66.3018 + ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
66.3019 + var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);
66.3020 +
66.3021 + // Loosely check result is an array of DOM nodes
66.3022 + if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
66.3023 + throw new Error("Template engine must return an array of DOM nodes");
66.3024 +
66.3025 + var haveAddedNodesToParent = false;
66.3026 + switch (renderMode) {
66.3027 + case "replaceChildren":
66.3028 + ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
66.3029 + haveAddedNodesToParent = true;
66.3030 + break;
66.3031 + case "replaceNode":
66.3032 + ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
66.3033 + haveAddedNodesToParent = true;
66.3034 + break;
66.3035 + case "ignoreTargetNode": break;
66.3036 + default:
66.3037 + throw new Error("Unknown renderMode: " + renderMode);
66.3038 + }
66.3039 +
66.3040 + if (haveAddedNodesToParent) {
66.3041 + activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
66.3042 + if (options['afterRender'])
66.3043 + ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext['$data']]);
66.3044 + }
66.3045 +
66.3046 + return renderedNodesArray;
66.3047 + }
66.3048 +
66.3049 + ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
66.3050 + options = options || {};
66.3051 + if ((options['templateEngine'] || _templateEngine) == undefined)
66.3052 + throw new Error("Set a template engine before calling renderTemplate");
66.3053 + renderMode = renderMode || "replaceChildren";
66.3054 +
66.3055 + if (targetNodeOrNodeArray) {
66.3056 + var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
66.3057 +
66.3058 + var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
66.3059 + var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;
66.3060 +
66.3061 + return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
66.3062 + function () {
66.3063 + // Ensure we've got a proper binding context to work with
66.3064 + var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
66.3065 + ? dataOrBindingContext
66.3066 + : new ko.bindingContext(ko.utils.unwrapObservable(dataOrBindingContext));
66.3067 +
66.3068 + // Support selecting template as a function of the data being rendered
66.3069 + var templateName = typeof(template) == 'function' ? template(bindingContext['$data'], bindingContext) : template;
66.3070 +
66.3071 + var renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);
66.3072 + if (renderMode == "replaceNode") {
66.3073 + targetNodeOrNodeArray = renderedNodesArray;
66.3074 + firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
66.3075 + }
66.3076 + },
66.3077 + null,
66.3078 + { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
66.3079 + );
66.3080 + } else {
66.3081 + // We don't yet have a DOM node to evaluate, so use a memo and render the template later when there is a DOM node
66.3082 + return ko.memoization.memoize(function (domNode) {
66.3083 + ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
66.3084 + });
66.3085 + }
66.3086 + };
66.3087 +
66.3088 + ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
66.3089 + // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
66.3090 + // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
66.3091 + var arrayItemContext;
66.3092 +
66.3093 + // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
66.3094 + var executeTemplateForArrayItem = function (arrayValue, index) {
66.3095 + // Support selecting template as a function of the data being rendered
66.3096 + arrayItemContext = parentBindingContext['createChildContext'](ko.utils.unwrapObservable(arrayValue), options['as']);
66.3097 + arrayItemContext['$index'] = index;
66.3098 + var templateName = typeof(template) == 'function' ? template(arrayValue, arrayItemContext) : template;
66.3099 + return executeTemplate(null, "ignoreTargetNode", templateName, arrayItemContext, options);
66.3100 + }
66.3101 +
66.3102 + // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
66.3103 + var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {
66.3104 + activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
66.3105 + if (options['afterRender'])
66.3106 + options['afterRender'](addedNodesArray, arrayValue);
66.3107 + };
66.3108 +
66.3109 + return ko.dependentObservable(function () {
66.3110 + var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
66.3111 + if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
66.3112 + unwrappedArray = [unwrappedArray];
66.3113 +
66.3114 + // Filter out any entries marked as destroyed
66.3115 + var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
66.3116 + return options['includeDestroyed'] || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
66.3117 + });
66.3118 +
66.3119 + // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
66.3120 + // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
66.3121 + ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, filteredArray, executeTemplateForArrayItem, options, activateBindingsCallback]);
66.3122 +
66.3123 + }, null, { disposeWhenNodeIsRemoved: targetNode });
66.3124 + };
66.3125 +
66.3126 + var templateComputedDomDataKey = '__ko__templateComputedDomDataKey__';
66.3127 + function disposeOldComputedAndStoreNewOne(element, newComputed) {
66.3128 + var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
66.3129 + if (oldComputed && (typeof(oldComputed.dispose) == 'function'))
66.3130 + oldComputed.dispose();
66.3131 + ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && newComputed.isActive()) ? newComputed : undefined);
66.3132 + }
66.3133 +
66.3134 + ko.bindingHandlers['template'] = {
66.3135 + 'init': function(element, valueAccessor) {
66.3136 + // Support anonymous templates
66.3137 + var bindingValue = ko.utils.unwrapObservable(valueAccessor());
66.3138 + if ((typeof bindingValue != "string") && (!bindingValue['name']) && (element.nodeType == 1 || element.nodeType == 8)) {
66.3139 + // It's an anonymous template - store the element contents, then clear the element
66.3140 + var templateNodes = element.nodeType == 1 ? element.childNodes : ko.virtualElements.childNodes(element),
66.3141 + container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
66.3142 + new ko.templateSources.anonymousTemplate(element)['nodes'](container);
66.3143 + }
66.3144 + return { 'controlsDescendantBindings': true };
66.3145 + },
66.3146 + 'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
66.3147 + var templateName = ko.utils.unwrapObservable(valueAccessor()),
66.3148 + options = {},
66.3149 + shouldDisplay = true,
66.3150 + dataValue,
66.3151 + templateComputed = null;
66.3152 +
66.3153 + if (typeof templateName != "string") {
66.3154 + options = templateName;
66.3155 + templateName = options['name'];
66.3156 +
66.3157 + // Support "if"/"ifnot" conditions
66.3158 + if ('if' in options)
66.3159 + shouldDisplay = ko.utils.unwrapObservable(options['if']);
66.3160 + if (shouldDisplay && 'ifnot' in options)
66.3161 + shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);
66.3162 +
66.3163 + dataValue = ko.utils.unwrapObservable(options['data']);
66.3164 + }
66.3165 +
66.3166 + if ('foreach' in options) {
66.3167 + // Render once for each data point (treating data set as empty if shouldDisplay==false)
66.3168 + var dataArray = (shouldDisplay && options['foreach']) || [];
66.3169 + templateComputed = ko.renderTemplateForEach(templateName || element, dataArray, options, element, bindingContext);
66.3170 + } else if (!shouldDisplay) {
66.3171 + ko.virtualElements.emptyNode(element);
66.3172 + } else {
66.3173 + // Render once for this single data point (or use the viewModel if no data was provided)
66.3174 + var innerBindingContext = ('data' in options) ?
66.3175 + bindingContext['createChildContext'](dataValue, options['as']) : // Given an explitit 'data' value, we create a child binding context for it
66.3176 + bindingContext; // Given no explicit 'data' value, we retain the same binding context
66.3177 + templateComputed = ko.renderTemplate(templateName || element, innerBindingContext, options, element);
66.3178 + }
66.3179 +
66.3180 + // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
66.3181 + disposeOldComputedAndStoreNewOne(element, templateComputed);
66.3182 + }
66.3183 + };
66.3184 +
66.3185 + // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
66.3186 + ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {
66.3187 + var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);
66.3188 +
66.3189 + if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
66.3190 + return null; // It looks like a string literal, not an object literal, so treat it as a named template (which is allowed for rewriting)
66.3191 +
66.3192 + if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
66.3193 + return null; // Named templates can be rewritten, so return "no error"
66.3194 + return "This template engine does not support anonymous templates nested within its templates";
66.3195 + };
66.3196 +
66.3197 + ko.virtualElements.allowedBindings['template'] = true;
66.3198 +})();
66.3199 +
66.3200 +ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
66.3201 +ko.exportSymbol('renderTemplate', ko.renderTemplate);
66.3202 +
66.3203 +ko.utils.compareArrays = (function () {
66.3204 + var statusNotInOld = 'added', statusNotInNew = 'deleted';
66.3205 +
66.3206 + // Simple calculation based on Levenshtein distance.
66.3207 + function compareArrays(oldArray, newArray, dontLimitMoves) {
66.3208 + oldArray = oldArray || [];
66.3209 + newArray = newArray || [];
66.3210 +
66.3211 + if (oldArray.length <= newArray.length)
66.3212 + return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, dontLimitMoves);
66.3213 + else
66.3214 + return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, dontLimitMoves);
66.3215 + }
66.3216 +
66.3217 + function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, dontLimitMoves) {
66.3218 + var myMin = Math.min,
66.3219 + myMax = Math.max,
66.3220 + editDistanceMatrix = [],
66.3221 + smlIndex, smlIndexMax = smlArray.length,
66.3222 + bigIndex, bigIndexMax = bigArray.length,
66.3223 + compareRange = (bigIndexMax - smlIndexMax) || 1,
66.3224 + maxDistance = smlIndexMax + bigIndexMax + 1,
66.3225 + thisRow, lastRow,
66.3226 + bigIndexMaxForRow, bigIndexMinForRow;
66.3227 +
66.3228 + for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
66.3229 + lastRow = thisRow;
66.3230 + editDistanceMatrix.push(thisRow = []);
66.3231 + bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
66.3232 + bigIndexMinForRow = myMax(0, smlIndex - 1);
66.3233 + for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
66.3234 + if (!bigIndex)
66.3235 + thisRow[bigIndex] = smlIndex + 1;
66.3236 + else if (!smlIndex) // Top row - transform empty array into new array via additions
66.3237 + thisRow[bigIndex] = bigIndex + 1;
66.3238 + else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
66.3239 + thisRow[bigIndex] = lastRow[bigIndex - 1]; // copy value (no edit)
66.3240 + else {
66.3241 + var northDistance = lastRow[bigIndex] || maxDistance; // not in big (deletion)
66.3242 + var westDistance = thisRow[bigIndex - 1] || maxDistance; // not in small (addition)
66.3243 + thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
66.3244 + }
66.3245 + }
66.3246 + }
66.3247 +
66.3248 + var editScript = [], meMinusOne, notInSml = [], notInBig = [];
66.3249 + for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
66.3250 + meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
66.3251 + if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
66.3252 + notInSml.push(editScript[editScript.length] = { // added
66.3253 + 'status': statusNotInSml,
66.3254 + 'value': bigArray[--bigIndex],
66.3255 + 'index': bigIndex });
66.3256 + } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
66.3257 + notInBig.push(editScript[editScript.length] = { // deleted
66.3258 + 'status': statusNotInBig,
66.3259 + 'value': smlArray[--smlIndex],
66.3260 + 'index': smlIndex });
66.3261 + } else {
66.3262 + editScript.push({
66.3263 + 'status': "retained",
66.3264 + 'value': bigArray[--bigIndex] });
66.3265 + --smlIndex;
66.3266 + }
66.3267 + }
66.3268 +
66.3269 + if (notInSml.length && notInBig.length) {
66.3270 + // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
66.3271 + // smlIndexMax keeps the time complexity of this algorithm linear.
66.3272 + var limitFailedCompares = smlIndexMax * 10, failedCompares,
66.3273 + a, d, notInSmlItem, notInBigItem;
66.3274 + // Go through the items that have been added and deleted and try to find matches between them.
66.3275 + for (failedCompares = a = 0; (dontLimitMoves || failedCompares < limitFailedCompares) && (notInSmlItem = notInSml[a]); a++) {
66.3276 + for (d = 0; notInBigItem = notInBig[d]; d++) {
66.3277 + if (notInSmlItem['value'] === notInBigItem['value']) {
66.3278 + notInSmlItem['moved'] = notInBigItem['index'];
66.3279 + notInBigItem['moved'] = notInSmlItem['index'];
66.3280 + notInBig.splice(d,1); // This item is marked as moved; so remove it from notInBig list
66.3281 + failedCompares = d = 0; // Reset failed compares count because we're checking for consecutive failures
66.3282 + break;
66.3283 + }
66.3284 + }
66.3285 + failedCompares += d;
66.3286 + }
66.3287 + }
66.3288 + return editScript.reverse();
66.3289 + }
66.3290 +
66.3291 + return compareArrays;
66.3292 +})();
66.3293 +
66.3294 +ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
66.3295 +
66.3296 +(function () {
66.3297 + // Objective:
66.3298 + // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
66.3299 + // map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
66.3300 + // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
66.3301 + // so that its children is again the concatenation of the mappings of the array elements, but don't re-map any array elements that we
66.3302 + // previously mapped - retain those nodes, and just insert/delete other ones
66.3303 +
66.3304 + // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
66.3305 + // You can use this, for example, to activate bindings on those nodes.
66.3306 +
66.3307 + function fixUpNodesToBeMovedOrRemoved(contiguousNodeArray) {
66.3308 + // Before moving, deleting, or replacing a set of nodes that were previously outputted by the "map" function, we have to reconcile
66.3309 + // them against what is in the DOM right now. It may be that some of the nodes have already been removed from the document,
66.3310 + // or that new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
66.3311 + // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
66.3312 + // So, this function translates the old "map" output array into its best guess of what set of current DOM nodes should be removed.
66.3313 + //
66.3314 + // Rules:
66.3315 + // [A] Any leading nodes that aren't in the document any more should be ignored
66.3316 + // These most likely correspond to memoization nodes that were already removed during binding
66.3317 + // See https://github.com/SteveSanderson/knockout/pull/440
66.3318 + // [B] We want to output a contiguous series of nodes that are still in the document. So, ignore any nodes that
66.3319 + // have already been removed, and include any nodes that have been inserted among the previous collection
66.3320 +
66.3321 + // Rule [A]
66.3322 + while (contiguousNodeArray.length && !ko.utils.domNodeIsAttachedToDocument(contiguousNodeArray[0]))
66.3323 + contiguousNodeArray.splice(0, 1);
66.3324 +
66.3325 + // Rule [B]
66.3326 + if (contiguousNodeArray.length > 1) {
66.3327 + // Build up the actual new contiguous node set
66.3328 + var current = contiguousNodeArray[0], last = contiguousNodeArray[contiguousNodeArray.length - 1], newContiguousSet = [current];
66.3329 + while (current !== last) {
66.3330 + current = current.nextSibling;
66.3331 + if (!current) // Won't happen, except if the developer has manually removed some DOM elements (then we're in an undefined scenario)
66.3332 + return;
66.3333 + newContiguousSet.push(current);
66.3334 + }
66.3335 +
66.3336 + // ... then mutate the input array to match this.
66.3337 + // (The following line replaces the contents of contiguousNodeArray with newContiguousSet)
66.3338 + Array.prototype.splice.apply(contiguousNodeArray, [0, contiguousNodeArray.length].concat(newContiguousSet));
66.3339 + }
66.3340 + return contiguousNodeArray;
66.3341 + }
66.3342 +
66.3343 + function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
66.3344 + // Map this array value inside a dependentObservable so we re-map when any dependency changes
66.3345 + var mappedNodes = [];
66.3346 + var dependentObservable = ko.dependentObservable(function() {
66.3347 + var newMappedNodes = mapping(valueToMap, index) || [];
66.3348 +
66.3349 + // On subsequent evaluations, just replace the previously-inserted DOM nodes
66.3350 + if (mappedNodes.length > 0) {
66.3351 + ko.utils.replaceDomNodes(fixUpNodesToBeMovedOrRemoved(mappedNodes), newMappedNodes);
66.3352 + if (callbackAfterAddingNodes)
66.3353 + ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
66.3354 + }
66.3355 +
66.3356 + // Replace the contents of the mappedNodes array, thereby updating the record
66.3357 + // of which nodes would be deleted if valueToMap was itself later removed
66.3358 + mappedNodes.splice(0, mappedNodes.length);
66.3359 + ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
66.3360 + }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return (mappedNodes.length == 0) || !ko.utils.domNodeIsAttachedToDocument(mappedNodes[0]) } });
66.3361 + return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
66.3362 + }
66.3363 +
66.3364 + var lastMappingResultDomDataKey = "setDomNodeChildrenFromArrayMapping_lastMappingResult";
66.3365 +
66.3366 + ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes) {
66.3367 + // Compare the provided array against the previous one
66.3368 + array = array || [];
66.3369 + options = options || {};
66.3370 + var isFirstExecution = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) === undefined;
66.3371 + var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) || [];
66.3372 + var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; });
66.3373 + var editScript = ko.utils.compareArrays(lastArray, array);
66.3374 +
66.3375 + // Build the new mapping result
66.3376 + var newMappingResult = [];
66.3377 + var lastMappingResultIndex = 0;
66.3378 + var newMappingResultIndex = 0;
66.3379 +
66.3380 + var nodesToDelete = [];
66.3381 + var itemsToProcess = [];
66.3382 + var itemsForBeforeRemoveCallbacks = [];
66.3383 + var itemsForMoveCallbacks = [];
66.3384 + var itemsForAfterAddCallbacks = [];
66.3385 + var mapData;
66.3386 +
66.3387 + function itemMovedOrRetained(editScriptIndex, oldPosition) {
66.3388 + mapData = lastMappingResult[oldPosition];
66.3389 + if (newMappingResultIndex !== oldPosition)
66.3390 + itemsForMoveCallbacks[editScriptIndex] = mapData;
66.3391 + // Since updating the index might change the nodes, do so before calling fixUpNodesToBeMovedOrRemoved
66.3392 + mapData.indexObservable(newMappingResultIndex++);
66.3393 + fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes);
66.3394 + newMappingResult.push(mapData);
66.3395 + itemsToProcess.push(mapData);
66.3396 + }
66.3397 +
66.3398 + function callCallback(callback, items) {
66.3399 + if (callback) {
66.3400 + for (var i = 0, n = items.length; i < n; i++) {
66.3401 + if (items[i]) {
66.3402 + ko.utils.arrayForEach(items[i].mappedNodes, function(node) {
66.3403 + callback(node, i, items[i].arrayEntry);
66.3404 + });
66.3405 + }
66.3406 + }
66.3407 + }
66.3408 + }
66.3409 +
66.3410 + for (var i = 0, editScriptItem, movedIndex; editScriptItem = editScript[i]; i++) {
66.3411 + movedIndex = editScriptItem['moved'];
66.3412 + switch (editScriptItem['status']) {
66.3413 + case "deleted":
66.3414 + if (movedIndex === undefined) {
66.3415 + mapData = lastMappingResult[lastMappingResultIndex];
66.3416 +
66.3417 + // Stop tracking changes to the mapping for these nodes
66.3418 + if (mapData.dependentObservable)
66.3419 + mapData.dependentObservable.dispose();
66.3420 +
66.3421 + // Queue these nodes for later removal
66.3422 + nodesToDelete.push.apply(nodesToDelete, fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes));
66.3423 + if (options['beforeRemove']) {
66.3424 + itemsForBeforeRemoveCallbacks[i] = mapData;
66.3425 + itemsToProcess.push(mapData);
66.3426 + }
66.3427 + }
66.3428 + lastMappingResultIndex++;
66.3429 + break;
66.3430 +
66.3431 + case "retained":
66.3432 + itemMovedOrRetained(i, lastMappingResultIndex++);
66.3433 + break;
66.3434 +
66.3435 + case "added":
66.3436 + if (movedIndex !== undefined) {
66.3437 + itemMovedOrRetained(i, movedIndex);
66.3438 + } else {
66.3439 + mapData = { arrayEntry: editScriptItem['value'], indexObservable: ko.observable(newMappingResultIndex++) };
66.3440 + newMappingResult.push(mapData);
66.3441 + itemsToProcess.push(mapData);
66.3442 + if (!isFirstExecution)
66.3443 + itemsForAfterAddCallbacks[i] = mapData;
66.3444 + }
66.3445 + break;
66.3446 + }
66.3447 + }
66.3448 +
66.3449 + // Call beforeMove first before any changes have been made to the DOM
66.3450 + callCallback(options['beforeMove'], itemsForMoveCallbacks);
66.3451 +
66.3452 + // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
66.3453 + ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);
66.3454 +
66.3455 + // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
66.3456 + for (var i = 0, nextNode = ko.virtualElements.firstChild(domNode), lastNode, node; mapData = itemsToProcess[i]; i++) {
66.3457 + // Get nodes for newly added items
66.3458 + if (!mapData.mappedNodes)
66.3459 + ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));
66.3460 +
66.3461 + // Put nodes in the right place if they aren't there already
66.3462 + for (var j = 0; node = mapData.mappedNodes[j]; nextNode = node.nextSibling, lastNode = node, j++) {
66.3463 + if (node !== nextNode)
66.3464 + ko.virtualElements.insertAfter(domNode, node, lastNode);
66.3465 + }
66.3466 +
66.3467 + // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
66.3468 + if (!mapData.initialized && callbackAfterAddingNodes) {
66.3469 + callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
66.3470 + mapData.initialized = true;
66.3471 + }
66.3472 + }
66.3473 +
66.3474 + // If there's a beforeRemove callback, call it after reordering.
66.3475 + // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
66.3476 + // some sort of animation, which is why we first reorder the nodes that will be removed. If the
66.3477 + // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
66.3478 + // Perhaps we'll make that change in the future if this scenario becomes more common.
66.3479 + callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);
66.3480 +
66.3481 + // Finally call afterMove and afterAdd callbacks
66.3482 + callCallback(options['afterMove'], itemsForMoveCallbacks);
66.3483 + callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
66.3484 +
66.3485 + // Store a copy of the array items we just considered so we can difference it next time
66.3486 + ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
66.3487 + }
66.3488 +})();
66.3489 +
66.3490 +ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
66.3491 +ko.nativeTemplateEngine = function () {
66.3492 + this['allowTemplateRewriting'] = false;
66.3493 +}
66.3494 +
66.3495 +ko.nativeTemplateEngine.prototype = new ko.templateEngine();
66.3496 +ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
66.3497 + var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
66.3498 + templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
66.3499 + templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
66.3500 +
66.3501 + if (templateNodes) {
66.3502 + return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
66.3503 + } else {
66.3504 + var templateText = templateSource['text']();
66.3505 + return ko.utils.parseHtmlFragment(templateText);
66.3506 + }
66.3507 +};
66.3508 +
66.3509 +ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
66.3510 +ko.setTemplateEngine(ko.nativeTemplateEngine.instance);
66.3511 +
66.3512 +ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
66.3513 +(function() {
66.3514 + ko.jqueryTmplTemplateEngine = function () {
66.3515 + // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
66.3516 + // doesn't expose a version number, so we have to infer it.
66.3517 + // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
66.3518 + // which KO internally refers to as version "2", so older versions are no longer detected.
66.3519 + var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
66.3520 + if ((typeof(jQuery) == "undefined") || !(jQuery['tmpl']))
66.3521 + return 0;
66.3522 + // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
66.3523 + try {
66.3524 + if (jQuery['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
66.3525 + // Since 1.0.0pre, custom tags should append markup to an array called "__"
66.3526 + return 2; // Final version of jquery.tmpl
66.3527 + }
66.3528 + } catch(ex) { /* Apparently not the version we were looking for */ }
66.3529 +
66.3530 + return 1; // Any older version that we don't support
66.3531 + })();
66.3532 +
66.3533 + function ensureHasReferencedJQueryTemplates() {
66.3534 + if (jQueryTmplVersion < 2)
66.3535 + throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
66.3536 + }
66.3537 +
66.3538 + function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
66.3539 + return jQuery['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
66.3540 + }
66.3541 +
66.3542 + this['renderTemplateSource'] = function(templateSource, bindingContext, options) {
66.3543 + options = options || {};
66.3544 + ensureHasReferencedJQueryTemplates();
66.3545 +
66.3546 + // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
66.3547 + var precompiled = templateSource['data']('precompiled');
66.3548 + if (!precompiled) {
66.3549 + var templateText = templateSource['text']() || "";
66.3550 + // Wrap in "with($whatever.koBindingContext) { ... }"
66.3551 + templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";
66.3552 +
66.3553 + precompiled = jQuery['template'](null, templateText);
66.3554 + templateSource['data']('precompiled', precompiled);
66.3555 + }
66.3556 +
66.3557 + var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
66.3558 + var jQueryTemplateOptions = jQuery['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
66.3559 +
66.3560 + var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
66.3561 + resultNodes['appendTo'](document.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
66.3562 +
66.3563 + jQuery['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
66.3564 + return resultNodes;
66.3565 + };
66.3566 +
66.3567 + this['createJavaScriptEvaluatorBlock'] = function(script) {
66.3568 + return "{{ko_code ((function() { return " + script + " })()) }}";
66.3569 + };
66.3570 +
66.3571 + this['addTemplate'] = function(templateName, templateMarkup) {
66.3572 + document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "</script>");
66.3573 + };
66.3574 +
66.3575 + if (jQueryTmplVersion > 0) {
66.3576 + jQuery['tmpl']['tag']['ko_code'] = {
66.3577 + open: "__.push($1 || '');"
66.3578 + };
66.3579 + jQuery['tmpl']['tag']['ko_with'] = {
66.3580 + open: "with($1) {",
66.3581 + close: "} "
66.3582 + };
66.3583 + }
66.3584 + };
66.3585 +
66.3586 + ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
66.3587 +
66.3588 + // Use this one by default *only if jquery.tmpl is referenced*
66.3589 + var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
66.3590 + if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
66.3591 + ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);
66.3592 +
66.3593 + ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
66.3594 +})();
66.3595 +});
66.3596 +})(window,document,navigator,window["jQuery"]);
66.3597 +})();
66.3598 \ No newline at end of file
67.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
67.2 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java Tue Jan 14 14:18:50 2014 +0100
67.3 @@ -0,0 +1,259 @@
67.4 +/**
67.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
67.6 + *
67.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
67.8 + *
67.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
67.10 + * Other names may be trademarks of their respective owners.
67.11 + *
67.12 + * The contents of this file are subject to the terms of either the GNU
67.13 + * General Public License Version 2 only ("GPL") or the Common
67.14 + * Development and Distribution License("CDDL") (collectively, the
67.15 + * "License"). You may not use this file except in compliance with the
67.16 + * License. You can obtain a copy of the License at
67.17 + * http://www.netbeans.org/cddl-gplv2.html
67.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
67.19 + * specific language governing permissions and limitations under the
67.20 + * License. When distributing the software, include this License Header
67.21 + * Notice in each file and include the License file at
67.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
67.23 + * particular file as subject to the "Classpath" exception as provided
67.24 + * by Oracle in the GPL Version 2 section of the License file that
67.25 + * accompanied this code. If applicable, add the following below the
67.26 + * License Header, with the fields enclosed by brackets [] replaced by
67.27 + * your own identifying information:
67.28 + * "Portions Copyrighted [year] [name of copyright owner]"
67.29 + *
67.30 + * Contributor(s):
67.31 + *
67.32 + * The Original Software is NetBeans. The Initial Developer of the Original
67.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
67.34 + *
67.35 + * If you wish your version of this file to be governed by only the CDDL
67.36 + * or only the GPL Version 2, indicate your decision by adding
67.37 + * "[Contributor] elects to include this software in this distribution
67.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
67.39 + * single choice of license, a recipient has the option to distribute
67.40 + * your version of this file under either the CDDL, the GPL Version 2 or
67.41 + * to extend the choice of license to its licensees as provided above.
67.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
67.43 + * Version 2 license, then the option applies only if the new code is
67.44 + * made subject to such option by the copyright holder.
67.45 + */
67.46 +package org.netbeans.html.ko4j;
67.47 +
67.48 +import java.io.ByteArrayInputStream;
67.49 +import java.io.ByteArrayOutputStream;
67.50 +import java.io.IOException;
67.51 +import java.io.InputStream;
67.52 +import java.io.OutputStream;
67.53 +import java.io.Reader;
67.54 +import java.net.URI;
67.55 +import java.net.URISyntaxException;
67.56 +import java.util.ArrayList;
67.57 +import java.util.List;
67.58 +import java.util.logging.Level;
67.59 +import java.util.logging.Logger;
67.60 +import org.glassfish.grizzly.PortRange;
67.61 +import org.glassfish.grizzly.http.server.HttpHandler;
67.62 +import org.glassfish.grizzly.http.server.HttpServer;
67.63 +import org.glassfish.grizzly.http.server.NetworkListener;
67.64 +import org.glassfish.grizzly.http.server.Request;
67.65 +import org.glassfish.grizzly.http.server.Response;
67.66 +import org.glassfish.grizzly.http.server.ServerConfiguration;
67.67 +import org.glassfish.grizzly.websockets.WebSocket;
67.68 +import org.glassfish.grizzly.websockets.WebSocketAddOn;
67.69 +import org.glassfish.grizzly.websockets.WebSocketApplication;
67.70 +import org.glassfish.grizzly.websockets.WebSocketEngine;
67.71 +
67.72 +/**
67.73 + *
67.74 + * @author Jaroslav Tulach <jtulach@netbeans.org>
67.75 + */
67.76 +final class DynamicHTTP extends HttpHandler {
67.77 + private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
67.78 + private static int resourcesCount;
67.79 + private static List<Resource> resources;
67.80 + private static ServerConfiguration conf;
67.81 + private static HttpServer server;
67.82 +
67.83 + private DynamicHTTP() {
67.84 + }
67.85 +
67.86 + static URI initServer() throws Exception {
67.87 + server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
67.88 + final WebSocketAddOn addon = new WebSocketAddOn();
67.89 + for (NetworkListener listener : server.getListeners()) {
67.90 + listener.registerAddOn(addon);
67.91 + }
67.92 + resources = new ArrayList<Resource>();
67.93 +
67.94 + conf = server.getServerConfiguration();
67.95 + final DynamicHTTP dh = new DynamicHTTP();
67.96 +
67.97 + conf.addHttpHandler(dh, "/");
67.98 +
67.99 + server.start();
67.100 +
67.101 + return pageURL("http", server, "/test.html");
67.102 + }
67.103 +
67.104 + @Override
67.105 + public void service(Request request, Response response) throws Exception {
67.106 + if ("/test.html".equals(request.getRequestURI())) {
67.107 + response.setContentType("text/html");
67.108 + final InputStream is = DynamicHTTP.class.getResourceAsStream("test.html");
67.109 + copyStream(is, response.getOutputStream(), null);
67.110 + return;
67.111 + }
67.112 + if ("/dynamic".equals(request.getRequestURI())) {
67.113 + String mimeType = request.getParameter("mimeType");
67.114 + List<String> params = new ArrayList<String>();
67.115 + boolean webSocket = false;
67.116 + for (int i = 0;; i++) {
67.117 + String p = request.getParameter("param" + i);
67.118 + if (p == null) {
67.119 + break;
67.120 + }
67.121 + if ("protocol:ws".equals(p)) {
67.122 + webSocket = true;
67.123 + continue;
67.124 + }
67.125 + params.add(p);
67.126 + }
67.127 + final String cnt = request.getParameter("content");
67.128 + String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
67.129 + ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
67.130 + URI url;
67.131 + final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
67.132 + if (webSocket) {
67.133 + url = registerWebSocket(res);
67.134 + } else {
67.135 + url = registerResource(res);
67.136 + }
67.137 + response.getWriter().write(url.toString());
67.138 + response.getWriter().write("\n");
67.139 + return;
67.140 + }
67.141 +
67.142 + for (Resource r : resources) {
67.143 + if (r.httpPath.equals(request.getRequestURI())) {
67.144 + response.setContentType(r.httpType);
67.145 + r.httpContent.reset();
67.146 + String[] params = null;
67.147 + if (r.parameters.length != 0) {
67.148 + params = new String[r.parameters.length];
67.149 + for (int i = 0; i < r.parameters.length; i++) {
67.150 + params[i] = request.getParameter(r.parameters[i]);
67.151 + if (params[i] == null) {
67.152 + if ("http.method".equals(r.parameters[i])) {
67.153 + params[i] = request.getMethod().toString();
67.154 + } else if ("http.requestBody".equals(r.parameters[i])) {
67.155 + Reader rdr = request.getReader();
67.156 + StringBuilder sb = new StringBuilder();
67.157 + for (;;) {
67.158 + int ch = rdr.read();
67.159 + if (ch == -1) {
67.160 + break;
67.161 + }
67.162 + sb.append((char) ch);
67.163 + }
67.164 + params[i] = sb.toString();
67.165 + }
67.166 + }
67.167 + if (params[i] == null) {
67.168 + params[i] = "null";
67.169 + }
67.170 + }
67.171 + }
67.172 +
67.173 + copyStream(r.httpContent, response.getOutputStream(), null, params);
67.174 + }
67.175 + }
67.176 + }
67.177 +
67.178 + private URI registerWebSocket(Resource r) {
67.179 + WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
67.180 + return pageURL("ws", server, r.httpPath);
67.181 + }
67.182 +
67.183 + private URI registerResource(Resource r) {
67.184 + if (!resources.contains(r)) {
67.185 + resources.add(r);
67.186 + conf.addHttpHandler(this, r.httpPath);
67.187 + }
67.188 + return pageURL("http", server, r.httpPath);
67.189 + }
67.190 +
67.191 + private static URI pageURL(String proto, HttpServer server, final String page) {
67.192 + NetworkListener listener = server.getListeners().iterator().next();
67.193 + int port = listener.getPort();
67.194 + try {
67.195 + return new URI(proto + "://localhost:" + port + page);
67.196 + } catch (URISyntaxException ex) {
67.197 + throw new IllegalStateException(ex);
67.198 + }
67.199 + }
67.200 +
67.201 + static final class Resource {
67.202 +
67.203 + final InputStream httpContent;
67.204 + final String httpType;
67.205 + final String httpPath;
67.206 + final String[] parameters;
67.207 +
67.208 + Resource(InputStream httpContent, String httpType, String httpPath,
67.209 + String[] parameters) {
67.210 + httpContent.mark(Integer.MAX_VALUE);
67.211 + this.httpContent = httpContent;
67.212 + this.httpType = httpType;
67.213 + this.httpPath = httpPath;
67.214 + this.parameters = parameters;
67.215 + }
67.216 + }
67.217 +
67.218 + static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
67.219 + for (;;) {
67.220 + int ch = is.read();
67.221 + if (ch == -1) {
67.222 + break;
67.223 + }
67.224 + if (ch == '$' && params.length > 0) {
67.225 + int cnt = is.read() - '0';
67.226 + if (baseURL != null && cnt == 'U' - '0') {
67.227 + os.write(baseURL.getBytes("UTF-8"));
67.228 + } else {
67.229 + if (cnt >= 0 && cnt < params.length) {
67.230 + os.write(params[cnt].getBytes("UTF-8"));
67.231 + } else {
67.232 + os.write('$');
67.233 + os.write(cnt + '0');
67.234 + }
67.235 + }
67.236 + } else {
67.237 + os.write(ch);
67.238 + }
67.239 + }
67.240 + }
67.241 +
67.242 + private static class WS extends WebSocketApplication {
67.243 + private final Resource r;
67.244 +
67.245 + private WS(Resource r) {
67.246 + this.r = r;
67.247 + }
67.248 +
67.249 + @Override
67.250 + public void onMessage(WebSocket socket, String text) {
67.251 + try {
67.252 + r.httpContent.reset();
67.253 + ByteArrayOutputStream out = new ByteArrayOutputStream();
67.254 + copyStream(r.httpContent, out, null, text);
67.255 + String s = new String(out.toByteArray(), "UTF-8");
67.256 + socket.send(s);
67.257 + } catch (IOException ex) {
67.258 + LOG.log(Level.WARNING, "Error processing message " + text, ex);
67.259 + }
67.260 + }
67.261 + }
67.262 +}
68.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
68.2 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/KOFx.java Tue Jan 14 14:18:50 2014 +0100
68.3 @@ -0,0 +1,118 @@
68.4 +/**
68.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
68.6 + *
68.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
68.8 + *
68.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
68.10 + * Other names may be trademarks of their respective owners.
68.11 + *
68.12 + * The contents of this file are subject to the terms of either the GNU
68.13 + * General Public License Version 2 only ("GPL") or the Common
68.14 + * Development and Distribution License("CDDL") (collectively, the
68.15 + * "License"). You may not use this file except in compliance with the
68.16 + * License. You can obtain a copy of the License at
68.17 + * http://www.netbeans.org/cddl-gplv2.html
68.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
68.19 + * specific language governing permissions and limitations under the
68.20 + * License. When distributing the software, include this License Header
68.21 + * Notice in each file and include the License file at
68.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
68.23 + * particular file as subject to the "Classpath" exception as provided
68.24 + * by Oracle in the GPL Version 2 section of the License file that
68.25 + * accompanied this code. If applicable, add the following below the
68.26 + * License Header, with the fields enclosed by brackets [] replaced by
68.27 + * your own identifying information:
68.28 + * "Portions Copyrighted [year] [name of copyright owner]"
68.29 + *
68.30 + * Contributor(s):
68.31 + *
68.32 + * The Original Software is NetBeans. The Initial Developer of the Original
68.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
68.34 + *
68.35 + * If you wish your version of this file to be governed by only the CDDL
68.36 + * or only the GPL Version 2, indicate your decision by adding
68.37 + * "[Contributor] elects to include this software in this distribution
68.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
68.39 + * single choice of license, a recipient has the option to distribute
68.40 + * your version of this file under either the CDDL, the GPL Version 2 or
68.41 + * to extend the choice of license to its licensees as provided above.
68.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
68.43 + * Version 2 license, then the option applies only if the new code is
68.44 + * made subject to such option by the copyright holder.
68.45 + */
68.46 +package org.netbeans.html.ko4j;
68.47 +
68.48 +import java.io.Closeable;
68.49 +import java.lang.reflect.InvocationTargetException;
68.50 +import java.lang.reflect.Method;
68.51 +import javafx.application.Platform;
68.52 +import org.apidesign.html.boot.spi.Fn;
68.53 +import org.testng.ITest;
68.54 +import org.testng.annotations.Test;
68.55 +
68.56 +/**
68.57 + *
68.58 + * @author Jaroslav Tulach <jtulach@netbeans.org>
68.59 + */
68.60 +public final class KOFx implements ITest, Runnable {
68.61 + private final Fn.Presenter p;
68.62 + private final Method m;
68.63 + private Object result;
68.64 + private Object inst;
68.65 + private int count;
68.66 +
68.67 + KOFx(Fn.Presenter p, Method m) {
68.68 + this.p = p;
68.69 + this.m = m;
68.70 + }
68.71 +
68.72 + @Override
68.73 + public String getTestName() {
68.74 + return m.getName();
68.75 + }
68.76 +
68.77 + @Test
68.78 + public synchronized void executeTest() throws Exception {
68.79 + if (result == null) {
68.80 + Platform.runLater(this);
68.81 + wait();
68.82 + }
68.83 + if (result instanceof Exception) {
68.84 + throw (Exception)result;
68.85 + }
68.86 + if (result instanceof Error) {
68.87 + throw (Error)result;
68.88 + }
68.89 + }
68.90 +
68.91 + @Override
68.92 + public synchronized void run() {
68.93 + boolean notify = true;
68.94 + try (Closeable a = Fn.activate(p)) {
68.95 + if (inst == null) {
68.96 + inst = m.getDeclaringClass().newInstance();
68.97 + }
68.98 + result = m.invoke(inst);
68.99 + if (result == null) {
68.100 + result = this;
68.101 + }
68.102 + } catch (InvocationTargetException ex) {
68.103 + Throwable r = ex.getTargetException();
68.104 + if (r instanceof InterruptedException) {
68.105 + if (count++ < 10000) {
68.106 + notify = false;
68.107 + Platform.runLater(this);
68.108 + return;
68.109 + }
68.110 + }
68.111 + result = r;
68.112 + } catch (Exception ex) {
68.113 + result = ex;
68.114 + } finally {
68.115 + if (notify) {
68.116 + notifyAll();
68.117 + }
68.118 + }
68.119 + }
68.120 +
68.121 +}
69.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
69.2 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java Tue Jan 14 14:18:50 2014 +0100
69.3 @@ -0,0 +1,228 @@
69.4 +/**
69.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
69.6 + *
69.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
69.8 + *
69.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
69.10 + * Other names may be trademarks of their respective owners.
69.11 + *
69.12 + * The contents of this file are subject to the terms of either the GNU
69.13 + * General Public License Version 2 only ("GPL") or the Common
69.14 + * Development and Distribution License("CDDL") (collectively, the
69.15 + * "License"). You may not use this file except in compliance with the
69.16 + * License. You can obtain a copy of the License at
69.17 + * http://www.netbeans.org/cddl-gplv2.html
69.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
69.19 + * specific language governing permissions and limitations under the
69.20 + * License. When distributing the software, include this License Header
69.21 + * Notice in each file and include the License file at
69.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
69.23 + * particular file as subject to the "Classpath" exception as provided
69.24 + * by Oracle in the GPL Version 2 section of the License file that
69.25 + * accompanied this code. If applicable, add the following below the
69.26 + * License Header, with the fields enclosed by brackets [] replaced by
69.27 + * your own identifying information:
69.28 + * "Portions Copyrighted [year] [name of copyright owner]"
69.29 + *
69.30 + * Contributor(s):
69.31 + *
69.32 + * The Original Software is NetBeans. The Initial Developer of the Original
69.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
69.34 + *
69.35 + * If you wish your version of this file to be governed by only the CDDL
69.36 + * or only the GPL Version 2, indicate your decision by adding
69.37 + * "[Contributor] elects to include this software in this distribution
69.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
69.39 + * single choice of license, a recipient has the option to distribute
69.40 + * your version of this file under either the CDDL, the GPL Version 2 or
69.41 + * to extend the choice of license to its licensees as provided above.
69.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
69.43 + * Version 2 license, then the option applies only if the new code is
69.44 + * made subject to such option by the copyright holder.
69.45 + */
69.46 +package org.netbeans.html.ko4j;
69.47 +
69.48 +import java.io.BufferedReader;
69.49 +import java.io.IOException;
69.50 +import java.io.InputStreamReader;
69.51 +import java.lang.annotation.Annotation;
69.52 +import java.lang.reflect.Method;
69.53 +import java.net.URI;
69.54 +import java.net.URISyntaxException;
69.55 +import java.net.URL;
69.56 +import java.net.URLConnection;
69.57 +import java.util.ArrayList;
69.58 +import java.util.List;
69.59 +import java.util.Map;
69.60 +import java.util.concurrent.Executors;
69.61 +import net.java.html.BrwsrCtx;
69.62 +import net.java.html.boot.BrowserBuilder;
69.63 +import net.java.html.js.JavaScriptBody;
69.64 +import org.netbeans.html.boot.impl.FnContext;
69.65 +import org.apidesign.html.boot.spi.Fn;
69.66 +import org.apidesign.html.context.spi.Contexts;
69.67 +import org.apidesign.html.json.spi.Technology;
69.68 +import org.apidesign.html.json.spi.Transfer;
69.69 +import org.apidesign.html.json.spi.WSTransfer;
69.70 +import org.apidesign.html.json.tck.KOTest;
69.71 +import org.apidesign.html.json.tck.KnockoutTCK;
69.72 +import org.openide.util.lookup.ServiceProvider;
69.73 +import org.testng.annotations.Factory;
69.74 +import static org.testng.Assert.*;
69.75 +
69.76 +/**
69.77 + *
69.78 + * @author Jaroslav Tulach <jtulach@netbeans.org>
69.79 + */
69.80 +@ServiceProvider(service = KnockoutTCK.class)
69.81 +public final class KnockoutFXTest extends KnockoutTCK {
69.82 + private static Class<?> browserClass;
69.83 + private static Fn.Presenter browserContext;
69.84 +
69.85 + public KnockoutFXTest() {
69.86 + }
69.87 +
69.88 + @Factory public static Object[] compatibilityTests() throws Exception {
69.89 + Class[] arr = testClasses();
69.90 + for (int i = 0; i < arr.length; i++) {
69.91 + assertEquals(
69.92 + arr[i].getClassLoader(),
69.93 + KnockoutFXTest.class.getClassLoader(),
69.94 + "All classes loaded by the same classloader"
69.95 + );
69.96 + }
69.97 +
69.98 + URI uri = DynamicHTTP.initServer();
69.99 +
69.100 + final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutFXTest.class).
69.101 + loadPage(uri.toString()).
69.102 + invoke("initialized");
69.103 +
69.104 + Executors.newSingleThreadExecutor().submit(new Runnable() {
69.105 + @Override
69.106 + public void run() {
69.107 + bb.showAndWait();
69.108 + }
69.109 + });
69.110 +
69.111 + ClassLoader l = getClassLoader();
69.112 + List<Object> res = new ArrayList<Object>();
69.113 + for (int i = 0; i < arr.length; i++) {
69.114 + Class<?> c = Class.forName(arr[i].getName(), true, l);
69.115 + seekKOTests(c, res);
69.116 + }
69.117 + Class<?> c = Class.forName(LessCallbacksCheck.class.getName(), true, l);
69.118 + seekKOTests(c, res);
69.119 + return res.toArray();
69.120 + }
69.121 +
69.122 + private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
69.123 + Class<? extends Annotation> koTest =
69.124 + c.getClassLoader().loadClass(KOTest.class.getName()).
69.125 + asSubclass(Annotation.class);
69.126 + for (Method m : c.getMethods()) {
69.127 + if (m.getAnnotation(koTest) != null) {
69.128 + res.add(new KOFx(browserContext, m));
69.129 + }
69.130 + }
69.131 + }
69.132 +
69.133 + static synchronized ClassLoader getClassLoader() throws InterruptedException {
69.134 + while (browserClass == null) {
69.135 + KnockoutFXTest.class.wait();
69.136 + }
69.137 + return browserClass.getClassLoader();
69.138 + }
69.139 +
69.140 + public static synchronized void initialized(Class<?> browserCls) throws Exception {
69.141 + browserClass = browserCls;
69.142 + browserContext = Fn.activePresenter();
69.143 + KnockoutFXTest.class.notifyAll();
69.144 + }
69.145 +
69.146 + public static void initialized() throws Exception {
69.147 + Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(KnockoutFXTest.class.getName());
69.148 + Method m = classpathClass.getMethod("initialized", Class.class);
69.149 + m.invoke(null, KnockoutFXTest.class);
69.150 + browserContext = Fn.activePresenter();
69.151 + }
69.152 +
69.153 + @Override
69.154 + public BrwsrCtx createContext() {
69.155 + FXContext fx = new FXContext(browserContext);
69.156 + Contexts.Builder cb = Contexts.newBuilder().
69.157 + register(Technology.class, fx, 10).
69.158 + register(Transfer.class, fx, 10);
69.159 + if (fx.areWebSocketsSupported()) {
69.160 + cb.register(WSTransfer.class, fx, 10);
69.161 + }
69.162 + return cb.build();
69.163 + }
69.164 +
69.165 + @Override
69.166 + public Object createJSON(Map<String, Object> values) {
69.167 + Object json = createJSON();
69.168 + for (Map.Entry<String, Object> entry : values.entrySet()) {
69.169 + setProperty(json, entry.getKey(), entry.getValue());
69.170 + }
69.171 + return json;
69.172 + }
69.173 +
69.174 + @JavaScriptBody(args = {}, body = "return new Object();")
69.175 + private static native Object createJSON();
69.176 + @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
69.177 + private static native void setProperty(Object json, String key, Object value);
69.178 +
69.179 + @Override
69.180 + @JavaScriptBody(args = { "s", "args" }, body = ""
69.181 + + "var f = new Function(s); "
69.182 + + "return f.apply(null, args);"
69.183 + )
69.184 + public native Object executeScript(String script, Object[] arguments);
69.185 +
69.186 + @JavaScriptBody(args = { }, body =
69.187 + "var h;"
69.188 + + "if (!!window && !!window.location && !!window.location.href)\n"
69.189 + + " h = window.location.href;\n"
69.190 + + "else "
69.191 + + " h = null;"
69.192 + + "return h;\n"
69.193 + )
69.194 + private static native String findBaseURL();
69.195 +
69.196 + @Override
69.197 + public URI prepareURL(String content, String mimeType, String[] parameters) {
69.198 + try {
69.199 + final URL baseURL = new URL(findBaseURL());
69.200 + StringBuilder sb = new StringBuilder();
69.201 + sb.append("/dynamic?mimeType=").append(mimeType);
69.202 + for (int i = 0; i < parameters.length; i++) {
69.203 + sb.append("¶m" + i).append("=").append(parameters[i]);
69.204 + }
69.205 + String mangle = content.replace("\n", "%0a")
69.206 + .replace("\"", "\\\"").replace(" ", "%20");
69.207 + sb.append("&content=").append(mangle);
69.208 +
69.209 + URL query = new URL(baseURL, sb.toString());
69.210 + URLConnection c = query.openConnection();
69.211 + BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
69.212 + URI connectTo = new URI(br.readLine());
69.213 + return connectTo;
69.214 + } catch (IOException ex) {
69.215 + throw new IllegalStateException(ex);
69.216 + } catch (URISyntaxException ex) {
69.217 + throw new IllegalStateException(ex);
69.218 + }
69.219 + }
69.220 +
69.221 + @Override
69.222 + public boolean canFailWebSocketTest() {
69.223 + try {
69.224 + Class.forName("java.util.function.Function");
69.225 + return false;
69.226 + } catch (ClassNotFoundException ex) {
69.227 + // running on JDK7, FX WebView WebSocket impl does not work
69.228 + return true;
69.229 + }
69.230 + }
69.231 +}
70.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
70.2 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/LessCallbacksCheck.java Tue Jan 14 14:18:50 2014 +0100
70.3 @@ -0,0 +1,82 @@
70.4 +/**
70.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
70.6 + *
70.7 + * Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
70.8 + *
70.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
70.10 + * Other names may be trademarks of their respective owners.
70.11 + *
70.12 + * The contents of this file are subject to the terms of either the GNU
70.13 + * General Public License Version 2 only ("GPL") or the Common
70.14 + * Development and Distribution License("CDDL") (collectively, the
70.15 + * "License"). You may not use this file except in compliance with the
70.16 + * License. You can obtain a copy of the License at
70.17 + * http://www.netbeans.org/cddl-gplv2.html
70.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
70.19 + * specific language governing permissions and limitations under the
70.20 + * License. When distributing the software, include this License Header
70.21 + * Notice in each file and include the License file at
70.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
70.23 + * particular file as subject to the "Classpath" exception as provided
70.24 + * by Oracle in the GPL Version 2 section of the License file that
70.25 + * accompanied this code. If applicable, add the following below the
70.26 + * License Header, with the fields enclosed by brackets [] replaced by
70.27 + * your own identifying information:
70.28 + * "Portions Copyrighted [year] [name of copyright owner]"
70.29 + *
70.30 + * Contributor(s):
70.31 + *
70.32 + * The Original Software is NetBeans. The Initial Developer of the Original
70.33 + * Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
70.34 + *
70.35 + * If you wish your version of this file to be governed by only the CDDL
70.36 + * or only the GPL Version 2, indicate your decision by adding
70.37 + * "[Contributor] elects to include this software in this distribution
70.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
70.39 + * single choice of license, a recipient has the option to distribute
70.40 + * your version of this file under either the CDDL, the GPL Version 2 or
70.41 + * to extend the choice of license to its licensees as provided above.
70.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
70.43 + * Version 2 license, then the option applies only if the new code is
70.44 + * made subject to such option by the copyright holder.
70.45 + */
70.46 +package org.netbeans.html.ko4j;
70.47 +
70.48 +import java.io.PrintWriter;
70.49 +import java.io.StringWriter;
70.50 +import net.java.html.json.ComputedProperty;
70.51 +import net.java.html.json.Model;
70.52 +import net.java.html.json.Property;
70.53 +import org.apidesign.html.json.tck.KOTest;
70.54 +
70.55 +/**
70.56 + *
70.57 + * @author Jaroslav Tulach <jtulach@netbeans.org>
70.58 + */
70.59 +@Model(className = "LessCalls", properties = {
70.60 + @Property(name = "value", type = int.class)
70.61 +})
70.62 +public class LessCallbacksCheck {
70.63 + private static StringWriter sw;
70.64 +
70.65 + @ComputedProperty static int plusOne(int value) {
70.66 + if (sw == null) {
70.67 + sw = new StringWriter();
70.68 + }
70.69 + new Exception("Who calls me?").printStackTrace(
70.70 + new PrintWriter(sw)
70.71 + );
70.72 + return value + 1;
70.73 + }
70.74 +
70.75 + @KOTest public void dontCallForInitialValueBackToJavaVM() {
70.76 + LessCalls m = new LessCalls(10).applyBindings();
70.77 + assert m.getPlusOne() == 11 : "Expecting 11: " + m.getPlusOne();
70.78 +
70.79 + assert sw != null : "StringWriter should be initialized: " + sw;
70.80 +
70.81 + if (sw.toString().contains("$JsCallbacks$")) {
70.82 + assert false : "Don't call for initial value via JsCallbacks:\n" + sw;
70.83 + }
70.84 + }
70.85 +}
71.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
71.2 +++ b/ko4j/src/test/resources/org/netbeans/html/ko4j/test.html Tue Jan 14 14:18:50 2014 +0100
71.3 @@ -0,0 +1,56 @@
71.4 +<!--
71.5 +
71.6 + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
71.7 +
71.8 + Copyright 2013-2013 Oracle and/or its affiliates. All rights reserved.
71.9 +
71.10 + Oracle and Java are registered trademarks of Oracle and/or its affiliates.
71.11 + Other names may be trademarks of their respective owners.
71.12 +
71.13 + The contents of this file are subject to the terms of either the GNU
71.14 + General Public License Version 2 only ("GPL") or the Common
71.15 + Development and Distribution License("CDDL") (collectively, the
71.16 + "License"). You may not use this file except in compliance with the
71.17 + License. You can obtain a copy of the License at
71.18 + http://www.netbeans.org/cddl-gplv2.html
71.19 + or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
71.20 + specific language governing permissions and limitations under the
71.21 + License. When distributing the software, include this License Header
71.22 + Notice in each file and include the License file at
71.23 + nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
71.24 + particular file as subject to the "Classpath" exception as provided
71.25 + by Oracle in the GPL Version 2 section of the License file that
71.26 + accompanied this code. If applicable, add the following below the
71.27 + License Header, with the fields enclosed by brackets [] replaced by
71.28 + your own identifying information:
71.29 + "Portions Copyrighted [year] [name of copyright owner]"
71.30 +
71.31 + Contributor(s):
71.32 +
71.33 + The Original Software is NetBeans. The Initial Developer of the Original
71.34 + Software is Oracle. Portions Copyright 2013-2013 Oracle. All Rights Reserved.
71.35 +
71.36 + If you wish your version of this file to be governed by only the CDDL
71.37 + or only the GPL Version 2, indicate your decision by adding
71.38 + "[Contributor] elects to include this software in this distribution
71.39 + under the [CDDL or GPL Version 2] license." If you do not indicate a
71.40 + single choice of license, a recipient has the option to distribute
71.41 + your version of this file under either the CDDL, the GPL Version 2 or
71.42 + to extend the choice of license to its licensees as provided above.
71.43 + However, if you add GPL Version 2 code and therefore, elected the GPL
71.44 + Version 2 license, then the option applies only if the new code is
71.45 + made subject to such option by the copyright holder.
71.46 +
71.47 +-->
71.48 +<!DOCTYPE html>
71.49 +<html>
71.50 + <head>
71.51 + <title>Knockout.fx Execution Harness</title>
71.52 + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
71.53 + <meta name="viewport" content="width=device-width">
71.54 + </head>
71.55 + <body>
71.56 + <h1>Knockout.fx Execution Harness</h1>
71.57 + </body>
71.58 + <script></script>
71.59 +</html>
72.1 --- a/pom.xml Wed Jan 08 13:18:34 2014 +0100
72.2 +++ b/pom.xml Tue Jan 14 14:18:50 2014 +0100
72.3 @@ -3,7 +3,7 @@
72.4 <modelVersion>4.0.0</modelVersion>
72.5 <groupId>org.netbeans.html</groupId>
72.6 <artifactId>pom</artifactId>
72.7 - <version>0.7-SNAPSHOT</version>
72.8 + <version>0.8-SNAPSHOT</version>
72.9 <packaging>pom</packaging>
72.10 <name>HTML APIs via Java</name>
72.11 <parent>
72.12 @@ -14,8 +14,9 @@
72.13 <properties>
72.14 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
72.15 <netbeans.version>RELEASE73</netbeans.version>
72.16 + <grizzly.version>2.3.8</grizzly.version>
72.17 <license>COPYING</license>
72.18 - <publicPackages></publicPackages>
72.19 + <publicPackages />
72.20 <bundleSymbolicName>${project.artifactId}</bundleSymbolicName>
72.21 </properties>
72.22 <modules>
72.23 @@ -23,7 +24,7 @@
72.24 <module>json-tck</module>
72.25 <module>ko-archetype</module>
72.26 <module>ko-archetype-test</module>
72.27 - <module>ko-fx</module>
72.28 + <module>ko4j</module>
72.29 <module>sound</module>
72.30 <module>context</module>
72.31 <module>boot</module>
72.32 @@ -46,9 +47,9 @@
72.33 <url>http://netbeans.org</url>
72.34 </organization>
72.35 <scm>
72.36 - <connection>scm:hg:https://hg.apidesign.org/hg/html~html4j</connection>
72.37 - <developerConnection>scm:hg:https://hg.apidesign.org/hg/html~html4j</developerConnection>
72.38 - <url>https://hg.apidesign.org/hg/html~html4j</url>
72.39 + <connection>scm:hg:https://hg.netbeans.org/html4j</connection>
72.40 + <developerConnection>scm:hg:https://hg.netbeans.org/html4j</developerConnection>
72.41 + <url>https://hg.netbeans.org/html4j</url>
72.42 <tag>default</tag>
72.43 </scm>
72.44 <repositories>
72.45 @@ -95,7 +96,7 @@
72.46 <exclude>*</exclude>
72.47 <exclude>.*/**</exclude>
72.48 <exclude>ko-archetype/src/main/resources/**</exclude>
72.49 - <exclude>ko-fx/src/main/resources/org/netbeans/html/kofx/knockout*.js</exclude>
72.50 + <exclude>ko4j/src/main/resources/org/netbeans/html/ko4j/knockout*.js</exclude>
72.51 </excludes>
72.52 </configuration>
72.53 </plugin>
72.54 @@ -305,4 +306,4 @@
72.55 </properties>
72.56 </profile>
72.57 </profiles>
72.58 -</project>
72.59 \ No newline at end of file
72.60 +</project>
73.1 --- a/sound/pom.xml Wed Jan 08 13:18:34 2014 +0100
73.2 +++ b/sound/pom.xml Tue Jan 14 14:18:50 2014 +0100
73.3 @@ -4,11 +4,11 @@
73.4 <parent>
73.5 <groupId>org.netbeans.html</groupId>
73.6 <artifactId>pom</artifactId>
73.7 - <version>0.7-SNAPSHOT</version>
73.8 + <version>0.8-SNAPSHOT</version>
73.9 </parent>
73.10 <groupId>org.netbeans.html</groupId>
73.11 <artifactId>net.java.html.sound</artifactId>
73.12 - <version>0.7-SNAPSHOT</version>
73.13 + <version>0.8-SNAPSHOT</version>
73.14 <packaging>bundle</packaging>
73.15 <name>Sound API via HTML</name>
73.16 <url>http://maven.apache.org</url>
73.17 @@ -33,7 +33,7 @@
73.18 <dependency>
73.19 <groupId>org.netbeans.html</groupId>
73.20 <artifactId>net.java.html.boot</artifactId>
73.21 - <version>0.7-SNAPSHOT</version>
73.22 + <version>0.8-SNAPSHOT</version>
73.23 <type>jar</type>
73.24 </dependency>
73.25 <dependency>