1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/ko/archetype-test/pom.xml Sat Sep 07 18:28:09 2013 +0200
1.3 @@ -0,0 +1,48 @@
1.4 +<?xml version="1.0"?>
1.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">
1.6 + <modelVersion>4.0.0</modelVersion>
1.7 + <parent>
1.8 + <groupId>org.apidesign.bck2brwsr</groupId>
1.9 + <artifactId>ko</artifactId>
1.10 + <version>0.8-SNAPSHOT</version>
1.11 + </parent>
1.12 + <groupId>org.apidesign.bck2brwsr</groupId>
1.13 + <artifactId>ko-archetype-test</artifactId>
1.14 + <version>0.8-SNAPSHOT</version>
1.15 + <name>Knockout Bck2Brwsr Archetype Test</name>
1.16 + <url>http://maven.apache.org</url>
1.17 + <description>Verifies the Knockout & net.java.html.json archetype behaves properly.</description>
1.18 + <properties>
1.19 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1.20 + </properties>
1.21 + <dependencies>
1.22 + <dependency>
1.23 + <groupId>${project.groupId}</groupId>
1.24 + <artifactId>knockout4j-archetype</artifactId>
1.25 + <version>${project.version}</version>
1.26 + </dependency>
1.27 + <dependency>
1.28 + <groupId>org.testng</groupId>
1.29 + <artifactId>testng</artifactId>
1.30 + <scope>test</scope>
1.31 + </dependency>
1.32 + <dependency>
1.33 + <groupId>org.apache.maven.shared</groupId>
1.34 + <artifactId>maven-verifier</artifactId>
1.35 + <version>1.4</version>
1.36 + <scope>test</scope>
1.37 + </dependency>
1.38 + <dependency>
1.39 + <groupId>${project.groupId}</groupId>
1.40 + <artifactId>ko-fx</artifactId>
1.41 + <version>${project.version}</version>
1.42 + <scope>provided</scope>
1.43 + </dependency>
1.44 + <dependency>
1.45 + <groupId>${project.groupId}</groupId>
1.46 + <artifactId>ko-bck2brwsr</artifactId>
1.47 + <version>${project.version}</version>
1.48 + <scope>provided</scope>
1.49 + </dependency>
1.50 + </dependencies>
1.51 +</project>
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/ko/archetype-test/src/test/java/org/apidesign/bck2brwsr/ko/archetype/test/ArchetypeVersionTest.java Sat Sep 07 18:28:09 2013 +0200
2.3 @@ -0,0 +1,138 @@
2.4 +/**
2.5 + * Back 2 Browser Bytecode Translator
2.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
2.7 + *
2.8 + * This program is free software: you can redistribute it and/or modify
2.9 + * it under the terms of the GNU General Public License as published by
2.10 + * the Free Software Foundation, version 2 of the License.
2.11 + *
2.12 + * This program is distributed in the hope that it will be useful,
2.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
2.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2.15 + * GNU General Public License for more details.
2.16 + *
2.17 + * You should have received a copy of the GNU General Public License
2.18 + * along with this program. Look for COPYING file in the top folder.
2.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
2.20 + */
2.21 +package org.apidesign.bck2brwsr.ko.archetype.test;
2.22 +
2.23 +import java.io.IOException;
2.24 +import java.net.URL;
2.25 +import javax.xml.XMLConstants;
2.26 +import javax.xml.parsers.DocumentBuilderFactory;
2.27 +import javax.xml.parsers.ParserConfigurationException;
2.28 +import javax.xml.xpath.XPathConstants;
2.29 +import javax.xml.xpath.XPathExpression;
2.30 +import javax.xml.xpath.XPathExpressionException;
2.31 +import javax.xml.xpath.XPathFactory;
2.32 +import javax.xml.xpath.XPathFactoryConfigurationException;
2.33 +import org.testng.annotations.Test;
2.34 +import static org.testng.Assert.*;
2.35 +import org.testng.annotations.BeforeClass;
2.36 +import org.w3c.dom.Document;
2.37 +import org.w3c.dom.NodeList;
2.38 +import org.xml.sax.SAXException;
2.39 +
2.40 +/**
2.41 + *
2.42 + * @author Jaroslav Tulach <jtulach@netbeans.org>
2.43 + */
2.44 +public class ArchetypeVersionTest {
2.45 + private String version;
2.46 +
2.47 + public ArchetypeVersionTest() {
2.48 + }
2.49 +
2.50 + @BeforeClass public void readCurrentVersion() throws Exception {
2.51 + version = findCurrentVersion();
2.52 + assertFalse(version.isEmpty(), "There should be some version string");
2.53 + }
2.54 +
2.55 +
2.56 + @Test public void testComparePomDepsVersions() throws Exception {
2.57 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
2.58 + URL r = l.getResource("archetype-resources/pom.xml");
2.59 + assertNotNull(r, "Archetype pom found");
2.60 +
2.61 + final XPathFactory fact = XPathFactory.newInstance();
2.62 + XPathExpression xp2 = fact.newXPath().compile(
2.63 + "//properties/net.java.html.version/text()"
2.64 + );
2.65 +
2.66 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
2.67 + String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
2.68 +
2.69 + int snapshot = arch.indexOf("-SNAPSHOT");
2.70 + assertEquals(snapshot, -1, "Don't depend on snapshots: " + arch);
2.71 +
2.72 + assertTrue(arch.matches("[0-9\\.]+"), "net.java.html.json version seems valid: " + arch);
2.73 + }
2.74 +
2.75 + @Test public void testCheckLauncher() throws Exception {
2.76 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
2.77 + URL r = l.getResource("archetype-resources/pom.xml");
2.78 + assertNotNull(r, "Archetype pom found");
2.79 +
2.80 + final XPathFactory fact = XPathFactory.newInstance();
2.81 + XPathExpression xp2 = fact.newXPath().compile(
2.82 + "//properties/bck2brwsr.launcher.version/text()"
2.83 + );
2.84 +
2.85 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
2.86 + String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
2.87 +
2.88 + assertEquals(arch, version, "launcher dependency is on more recent version");
2.89 + }
2.90 +
2.91 + @Test public void testCheckBck2Brwsr() throws Exception {
2.92 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
2.93 + URL r = l.getResource("archetype-resources/pom.xml");
2.94 + assertNotNull(r, "Archetype pom found");
2.95 +
2.96 + final XPathFactory fact = XPathFactory.newInstance();
2.97 + XPathExpression xp2 = fact.newXPath().compile(
2.98 + "//properties/bck2brwsr.version/text()"
2.99 + );
2.100 +
2.101 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
2.102 + String arch = (String) xp2.evaluate(dom, XPathConstants.STRING);
2.103 +
2.104 + assertEquals(arch, version, "bck2brwsr dependency is on more recent version");
2.105 + }
2.106 +
2.107 + @Test public void testNbActions() throws Exception {
2.108 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
2.109 + URL r = l.getResource("archetype-resources/nbactions.xml");
2.110 + assertNotNull(r, "Archetype nb file found");
2.111 +
2.112 + final XPathFactory fact = XPathFactory.newInstance();
2.113 + XPathExpression xp2 = fact.newXPath().compile(
2.114 + "//goal/text()"
2.115 + );
2.116 +
2.117 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(r.openStream());
2.118 + NodeList goals = (NodeList) xp2.evaluate(dom, XPathConstants.NODESET);
2.119 +
2.120 + for (int i = 0; i < goals.getLength(); i++) {
2.121 + String s = goals.item(i).getTextContent();
2.122 + if (s.contains("apidesign")) {
2.123 + assertFalse(s.matches(".*apidesign.*[0-9].*"), "No numbers: " + s);
2.124 + }
2.125 + }
2.126 + }
2.127 +
2.128 + static String findCurrentVersion() throws XPathExpressionException, IOException, ParserConfigurationException, SAXException, XPathFactoryConfigurationException {
2.129 + final ClassLoader l = ArchetypeVersionTest.class.getClassLoader();
2.130 + URL u = l.getResource("META-INF/maven/org.apidesign.bck2brwsr/knockout4j-archetype/pom.xml");
2.131 + assertNotNull(u, "Own pom found: " + System.getProperty("java.class.path"));
2.132 +
2.133 + final XPathFactory fact = XPathFactory.newInstance();
2.134 + fact.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
2.135 +
2.136 + XPathExpression xp = fact.newXPath().compile("project/version/text()");
2.137 +
2.138 + Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(u.openStream());
2.139 + return xp.evaluate(dom);
2.140 + }
2.141 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/ko/archetype-test/src/test/java/org/apidesign/bck2brwsr/ko/archetype/test/VerifyArchetypeTest.java Sat Sep 07 18:28:09 2013 +0200
3.3 @@ -0,0 +1,116 @@
3.4 +/**
3.5 + * Back 2 Browser Bytecode Translator
3.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
3.7 + *
3.8 + * This program is free software: you can redistribute it and/or modify
3.9 + * it under the terms of the GNU General Public License as published by
3.10 + * the Free Software Foundation, version 2 of the License.
3.11 + *
3.12 + * This program is distributed in the hope that it will be useful,
3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.15 + * GNU General Public License for more details.
3.16 + *
3.17 + * You should have received a copy of the GNU General Public License
3.18 + * along with this program. Look for COPYING file in the top folder.
3.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
3.20 + */
3.21 +package org.apidesign.bck2brwsr.ko.archetype.test;
3.22 +
3.23 +import java.io.File;
3.24 +import java.util.Properties;
3.25 +import java.util.zip.ZipFile;
3.26 +import org.apache.maven.it.Verifier;
3.27 +import org.testng.annotations.Test;
3.28 +import static org.testng.Assert.*;
3.29 +
3.30 +/**
3.31 + *
3.32 + * @author Jaroslav Tulach <jtulach@netbeans.org>
3.33 + */
3.34 +public class VerifyArchetypeTest {
3.35 + @Test public void fxBrwsrCompiles() throws Exception {
3.36 + final File dir = new File("target/tests/fxcompile/").getAbsoluteFile();
3.37 + generateFromArchetype(dir);
3.38 +
3.39 + File created = new File(dir, "o-a-test");
3.40 + assertTrue(created.isDirectory(), "Project created");
3.41 + assertTrue(new File(created, "pom.xml").isFile(), "Pom file is in there");
3.42 +
3.43 + Verifier v = new Verifier(created.getAbsolutePath());
3.44 + v.executeGoal("verify");
3.45 +
3.46 + v.verifyErrorFreeLog();
3.47 +
3.48 + for (String l : v.loadFile(v.getBasedir(), v.getLogFileName(), false)) {
3.49 + if (l.contains("j2js")) {
3.50 + fail("No pre-compilaton:\n" + l);
3.51 + }
3.52 + }
3.53 +
3.54 + v.verifyTextInLog("org.apidesign.bck2brwsr.launcher.FXBrwsrLauncher");
3.55 + v.verifyTextInLog("fxcompile/o-a-test/target/o-a-test-1.0-SNAPSHOT-fxbrwsr.zip");
3.56 + }
3.57 +
3.58 + @Test public void bck2BrwsrCompiles() throws Exception {
3.59 + final File dir = new File("target/tests/b2bcompile/").getAbsoluteFile();
3.60 + generateFromArchetype(dir);
3.61 +
3.62 + File created = new File(dir, "o-a-test");
3.63 + assertTrue(created.isDirectory(), "Project created");
3.64 + assertTrue(new File(created, "pom.xml").isFile(), "Pom file is in there");
3.65 +
3.66 + Verifier v = new Verifier(created.getAbsolutePath());
3.67 + Properties sysProp = v.getSystemProperties();
3.68 + if (Boolean.getBoolean("java.awt.headless")) {
3.69 + sysProp.put("java.awt.headless", "true");
3.70 + }
3.71 + v.addCliOption("-Pbck2brwsr");
3.72 + v.executeGoal("verify");
3.73 +
3.74 + v.verifyErrorFreeLog();
3.75 +
3.76 + // does pre-compilation to JavaScript
3.77 + v.verifyTextInLog("j2js");
3.78 + // uses Bck2BrwsrLauncher
3.79 + v.verifyTextInLog("BaseHTTPLauncher showBrwsr");
3.80 + // building zip:
3.81 + v.verifyTextInLog("b2bcompile/o-a-test/target/o-a-test-1.0-SNAPSHOT-bck2brwsr.zip");
3.82 +
3.83 + for (String l : v.loadFile(v.getBasedir(), v.getLogFileName(), false)) {
3.84 + if (l.contains("fxbrwsr")) {
3.85 + fail("No fxbrwsr:\n" + l);
3.86 + }
3.87 + }
3.88 +
3.89 + File zip = new File(new File(created, "target"), "o-a-test-1.0-SNAPSHOT-bck2brwsr.zip");
3.90 + assertTrue(zip.isFile(), "Zip file with website was created");
3.91 +
3.92 + ZipFile zf = new ZipFile(zip);
3.93 + assertNotNull(zf.getEntry("public_html/index.html"), "index.html found");
3.94 + assertNotNull(zf.getEntry("public_html/twitterExample.css"), "css file found");
3.95 +
3.96 + }
3.97 +
3.98 + private Verifier generateFromArchetype(final File dir, String... params) throws Exception {
3.99 + Verifier v = new Verifier(dir.getAbsolutePath());
3.100 + v.setAutoclean(false);
3.101 + v.setLogFileName("generate.log");
3.102 + v.deleteDirectory("");
3.103 + dir.mkdirs();
3.104 + Properties sysProp = v.getSystemProperties();
3.105 + sysProp.put("groupId", "org.apidesign.test");
3.106 + sysProp.put("artifactId", "o-a-test");
3.107 + sysProp.put("package", "org.apidesign.test.oat");
3.108 + sysProp.put("archetypeGroupId", "org.apidesign.bck2brwsr");
3.109 + sysProp.put("archetypeArtifactId", "knockout4j-archetype");
3.110 + sysProp.put("archetypeVersion", ArchetypeVersionTest.findCurrentVersion());
3.111 +
3.112 + for (String p : params) {
3.113 + v.addCliOption(p);
3.114 + }
3.115 + v.executeGoal("archetype:generate");
3.116 + v.verifyErrorFreeLog();
3.117 + return v;
3.118 + }
3.119 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/ko/archetype/pom.xml Sat Sep 07 18:28:09 2013 +0200
4.3 @@ -0,0 +1,58 @@
4.4 +<?xml version="1.0" encoding="UTF-8"?>
4.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">
4.6 + <modelVersion>4.0.0</modelVersion>
4.7 + <parent>
4.8 + <artifactId>ko</artifactId>
4.9 + <groupId>org.apidesign.bck2brwsr</groupId>
4.10 + <version>0.8-SNAPSHOT</version>
4.11 + </parent>
4.12 + <groupId>org.apidesign.bck2brwsr</groupId>
4.13 + <artifactId>knockout4j-archetype</artifactId>
4.14 + <version>0.8-SNAPSHOT</version>
4.15 + <packaging>jar</packaging>
4.16 + <name>Knockout Bck2Brwsr Maven Archetype</name>
4.17 + <description>
4.18 + HTML page with Knockout.js bindings driven by application model
4.19 + written in Java. Use your favorite language to code. Use
4.20 + HTML as a lightweight rendering toolkit. Deploy using JavaFX or
4.21 + bck2brwsr virtual machine.
4.22 + </description>
4.23 + <build>
4.24 + <resources>
4.25 + <resource>
4.26 + <directory>src/main/resources</directory>
4.27 + <filtering>true</filtering>
4.28 + <includes>
4.29 + <include>**/pom.xml</include>
4.30 + </includes>
4.31 + </resource>
4.32 + <resource>
4.33 + <directory>src/main/resources</directory>
4.34 + <filtering>false</filtering>
4.35 + <excludes>
4.36 + <exclude>**/pom.xml</exclude>
4.37 + </excludes>
4.38 + </resource>
4.39 + </resources>
4.40 + <plugins>
4.41 + <plugin>
4.42 + <groupId>org.apache.maven.plugins</groupId>
4.43 + <artifactId>maven-compiler-plugin</artifactId>
4.44 + <version>2.3.2</version>
4.45 + <configuration>
4.46 + <source>1.6</source>
4.47 + <target>1.6</target>
4.48 + </configuration>
4.49 + </plugin>
4.50 + <plugin>
4.51 + <groupId>org.apache.maven.plugins</groupId>
4.52 + <artifactId>maven-resources-plugin</artifactId>
4.53 + <version>2.6</version>
4.54 + <configuration>
4.55 + <escapeString>\</escapeString>
4.56 + <target>1.6</target>
4.57 + </configuration>
4.58 + </plugin>
4.59 + </plugins>
4.60 + </build>
4.61 +</project>
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/ko/archetype/src/main/java/org/apidesign/bck2brwsr/ko/archetype/package-info.java Sat Sep 07 18:28:09 2013 +0200
5.3 @@ -0,0 +1,18 @@
5.4 +/**
5.5 + * Back 2 Browser Bytecode Translator
5.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5.7 + *
5.8 + * This program is free software: you can redistribute it and/or modify
5.9 + * it under the terms of the GNU General Public License as published by
5.10 + * the Free Software Foundation, version 2 of the License.
5.11 + *
5.12 + * This program is distributed in the hope that it will be useful,
5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5.15 + * GNU General Public License for more details.
5.16 + *
5.17 + * You should have received a copy of the GNU General Public License
5.18 + * along with this program. Look for COPYING file in the top folder.
5.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
5.20 + */
5.21 +package org.apidesign.bck2brwsr.ko.archetype;
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/ko/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml Sat Sep 07 18:28:09 2013 +0200
6.3 @@ -0,0 +1,63 @@
6.4 +<?xml version="1.0" encoding="UTF-8"?>
6.5 +<!--
6.6 +
6.7 + Back 2 Browser Bytecode Translator
6.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
6.9 +
6.10 + This program is free software: you can redistribute it and/or modify
6.11 + it under the terms of the GNU General Public License as published by
6.12 + the Free Software Foundation, version 2 of the License.
6.13 +
6.14 + This program is distributed in the hope that it will be useful,
6.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
6.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.17 + GNU General Public License for more details.
6.18 +
6.19 + You should have received a copy of the GNU General Public License
6.20 + along with this program. Look for COPYING file in the top folder.
6.21 + If not, see http://opensource.org/licenses/GPL-2.0.
6.22 +
6.23 +-->
6.24 +<archetype-descriptor name="FX/Bck2Brwsr Example">
6.25 + <fileSets>
6.26 + <fileSet filtered="true" packaged="true">
6.27 + <directory>src/main/java</directory>
6.28 + <includes>
6.29 + <include>**/*.java</include>
6.30 + </includes>
6.31 + </fileSet>
6.32 + <fileSet filtered="true" packaged="true">
6.33 + <directory>src/main/resources</directory>
6.34 + <includes>
6.35 + <include>**/*.xhtml</include>
6.36 + <include>**/*.html</include>
6.37 + <include>**/*.css</include>
6.38 + </includes>
6.39 + </fileSet>
6.40 + <fileSet filtered="true" packaged="true">
6.41 + <directory>src/test/java</directory>
6.42 + <includes>
6.43 + <include>**/*Test.java</include>
6.44 + </includes>
6.45 + </fileSet>
6.46 + <fileSet filtered="true" packaged="false">
6.47 + <directory>src/main/assembly</directory>
6.48 + <includes>
6.49 + <include>**/*.xml</include>
6.50 + </includes>
6.51 + </fileSet>
6.52 + <fileSet filtered="false" packaged="false">
6.53 + <directory></directory>
6.54 + <includes>
6.55 + <include>nbactions*.xml</include>
6.56 + </includes>
6.57 + </fileSet>
6.58 + <fileSet filtered="true" packaged="false">
6.59 + <directory>assembly</directory>
6.60 + <includes>
6.61 + <include>fxbrwsr-assembly.xml</include>
6.62 + <include>bck2brwsr-assembly.xml</include>
6.63 + </includes>
6.64 + </fileSet>
6.65 + </fileSets>
6.66 +</archetype-descriptor>
6.67 \ No newline at end of file
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/ko/archetype/src/main/resources/archetype-resources/nbactions-bck2brwsr.xml Sat Sep 07 18:28:09 2013 +0200
7.3 @@ -0,0 +1,14 @@
7.4 +<?xml version="1.0" encoding="UTF-8"?>
7.5 +<actions>
7.6 + <action>
7.7 + <actionName>run</actionName>
7.8 + <goals>
7.9 + <goal>package</goal>
7.10 + <goal>bck2brwsr:brwsr</goal>
7.11 + </goals>
7.12 + <properties>
7.13 + <skipTests>true</skipTests>
7.14 + <bck2brwsr.obfuscationlevel>NONE</bck2brwsr.obfuscationlevel>
7.15 + </properties>
7.16 + </action>
7.17 +</actions>
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/ko/archetype/src/main/resources/archetype-resources/nbactions-fxbrwsr.xml Sat Sep 07 18:28:09 2013 +0200
8.3 @@ -0,0 +1,20 @@
8.4 +<?xml version="1.0" encoding="UTF-8"?>
8.5 +<actions>
8.6 + <action>
8.7 + <actionName>run</actionName>
8.8 + <goals>
8.9 + <goal>process-classes</goal>
8.10 + <goal>bck2brwsr:brwsr</goal>
8.11 + </goals>
8.12 + </action>
8.13 + <action>
8.14 + <actionName>debug</actionName>
8.15 + <goals>
8.16 + <goal>process-classes</goal>
8.17 + <goal>bck2brwsr:brwsr</goal>
8.18 + </goals>
8.19 + <properties>
8.20 + <jpda.listen>maven</jpda.listen>
8.21 + </properties>
8.22 + </action>
8.23 +</actions>
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/ko/archetype/src/main/resources/archetype-resources/nbactions.xml Sat Sep 07 18:28:09 2013 +0200
9.3 @@ -0,0 +1,20 @@
9.4 +<?xml version="1.0" encoding="UTF-8"?>
9.5 +<actions>
9.6 + <action>
9.7 + <actionName>run</actionName>
9.8 + <goals>
9.9 + <goal>process-classes</goal>
9.10 + <goal>bck2brwsr:brwsr</goal>
9.11 + </goals>
9.12 + </action>
9.13 + <action>
9.14 + <actionName>debug</actionName>
9.15 + <goals>
9.16 + <goal>process-classes</goal>
9.17 + <goal>bck2brwsr:brwsr</goal>
9.18 + </goals>
9.19 + <properties>
9.20 + <jpda.listen>maven</jpda.listen>
9.21 + </properties>
9.22 + </action>
9.23 +</actions>
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/ko/archetype/src/main/resources/archetype-resources/pom.xml Sat Sep 07 18:28:09 2013 +0200
10.3 @@ -0,0 +1,266 @@
10.4 +<?xml version="1.0"?>
10.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10.6 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
10.7 + <modelVersion>4.0.0</modelVersion>
10.8 +
10.9 + <groupId>\${groupId}</groupId>
10.10 + <artifactId>\${artifactId}</artifactId>
10.11 + <version>\${version}</version>
10.12 + <packaging>jar</packaging>
10.13 +
10.14 + <name>\${artifactId}</name>
10.15 +
10.16 + <repositories>
10.17 + <repository>
10.18 + <id>java.net</id>
10.19 + <name>Java.net</name>
10.20 + <url>https://maven.java.net/content/repositories/releases/</url>
10.21 + <snapshots>
10.22 + <enabled>true</enabled>
10.23 + </snapshots>
10.24 + </repository>
10.25 + <repository>
10.26 + <id>netbeans</id>
10.27 + <name>NetBeans</name>
10.28 + <url>http://bits.netbeans.org/maven2/</url>
10.29 + </repository>
10.30 + </repositories>
10.31 + <pluginRepositories>
10.32 + <pluginRepository>
10.33 + <id>java.net</id>
10.34 + <name>Java.net</name>
10.35 + <url>https://maven.java.net/content/repositories/releases/</url>
10.36 + <snapshots>
10.37 + <enabled>true</enabled>
10.38 + </snapshots>
10.39 + </pluginRepository>
10.40 + </pluginRepositories>
10.41 +
10.42 + <properties>
10.43 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
10.44 + <net.java.html.version>${net.java.html.version}</net.java.html.version>
10.45 + <bck2brwsr.version>${project.version}</bck2brwsr.version>
10.46 + <bck2brwsr.launcher.version>${project.version}</bck2brwsr.launcher.version>
10.47 + <bck2brwsr.obfuscationlevel>MINIMAL</bck2brwsr.obfuscationlevel>
10.48 + <brwsr.startpage>\${package.replace('.','/')}/index.html</brwsr.startpage>
10.49 + </properties>
10.50 + <build>
10.51 + <plugins>
10.52 + <plugin>
10.53 + <groupId>org.apidesign.bck2brwsr</groupId>
10.54 + <artifactId>bck2brwsr-maven-plugin</artifactId>
10.55 + <version>\${bck2brwsr.launcher.version}</version>
10.56 + <executions>
10.57 + <execution>
10.58 + <goals>
10.59 + <goal>brwsr</goal>
10.60 + </goals>
10.61 + </execution>
10.62 + </executions>
10.63 + <configuration>
10.64 + <startpage>\${brwsr.startpage}</startpage>
10.65 + <launcher>\${brwsr}</launcher>
10.66 + </configuration>
10.67 + </plugin>
10.68 + <plugin>
10.69 + <groupId>org.apache.maven.plugins</groupId>
10.70 + <artifactId>maven-compiler-plugin</artifactId>
10.71 + <version>2.3.2</version>
10.72 + <configuration>
10.73 + <source>1.7</source>
10.74 + <target>1.7</target>
10.75 + </configuration>
10.76 + </plugin>
10.77 + <plugin>
10.78 + <groupId>org.apache.maven.plugins</groupId>
10.79 + <artifactId>maven-surefire-plugin</artifactId>
10.80 + <version>2.14.1</version>
10.81 + <configuration>
10.82 + <systemPropertyVariables>
10.83 + <vmtest.brwsrs>\${brwsr}</vmtest.brwsrs>
10.84 + </systemPropertyVariables>
10.85 + </configuration>
10.86 + </plugin>
10.87 + <plugin>
10.88 + <groupId>org.apache.maven.plugins</groupId>
10.89 + <artifactId>maven-jar-plugin</artifactId>
10.90 + <version>2.4</version>
10.91 + <configuration>
10.92 + <archive>
10.93 + <manifest>
10.94 + <addClasspath>true</addClasspath>
10.95 + <classpathPrefix>lib/</classpathPrefix>
10.96 + </manifest>
10.97 + </archive>
10.98 + </configuration>
10.99 + </plugin>
10.100 + <plugin>
10.101 + <groupId>org.apache.maven.plugins</groupId>
10.102 + <artifactId>maven-deploy-plugin</artifactId>
10.103 + <version>2.7</version>
10.104 + <configuration>
10.105 + <skip>true</skip>
10.106 + </configuration>
10.107 + </plugin>
10.108 + </plugins>
10.109 + </build>
10.110 +
10.111 + <dependencies>
10.112 + <dependency>
10.113 + <groupId>org.testng</groupId>
10.114 + <artifactId>testng</artifactId>
10.115 + <version>6.5.2</version>
10.116 + <scope>test</scope>
10.117 + </dependency>
10.118 + <dependency>
10.119 + <groupId>org.apidesign.bck2brwsr</groupId>
10.120 + <artifactId>vmtest</artifactId>
10.121 + <version>\${bck2brwsr.version}</version>
10.122 + <scope>test</scope>
10.123 + </dependency>
10.124 + <dependency>
10.125 + <groupId>org.apidesign.html</groupId>
10.126 + <artifactId>net.java.html.json</artifactId>
10.127 + <version>\${net.java.html.version}</version>
10.128 + <type>jar</type>
10.129 + </dependency>
10.130 + </dependencies>
10.131 + <profiles>
10.132 + <profile>
10.133 + <id>fxbrwsr</id>
10.134 + <activation>
10.135 + <activeByDefault>true</activeByDefault>
10.136 + </activation>
10.137 + <properties>
10.138 + <brwsr>fxbrwsr</brwsr>
10.139 + </properties>
10.140 + <build>
10.141 + <plugins>
10.142 + <plugin>
10.143 + <groupId>org.apache.maven.plugins</groupId>
10.144 + <artifactId>maven-jar-plugin</artifactId>
10.145 + <version>2.4</version>
10.146 + <configuration>
10.147 + <archive>
10.148 + <manifest>
10.149 + <mainClass>org.apidesign.bck2brwsr.launcher.FXBrwsrLauncher</mainClass>
10.150 + <addClasspath>true</addClasspath>
10.151 + <classpathPrefix>lib/</classpathPrefix>
10.152 + </manifest>
10.153 + <manifestEntries>
10.154 + <StartPage>\${brwsr.startpage}</StartPage>
10.155 + </manifestEntries>
10.156 + </archive>
10.157 + </configuration>
10.158 + </plugin>
10.159 + <plugin>
10.160 + <artifactId>maven-assembly-plugin</artifactId>
10.161 + <version>2.4</version>
10.162 + <executions>
10.163 + <execution>
10.164 + <id>distro-assembly</id>
10.165 + <phase>package</phase>
10.166 + <goals>
10.167 + <goal>single</goal>
10.168 + </goals>
10.169 + <configuration>
10.170 + <descriptors>
10.171 + <descriptor>src/main/assembly/fxbrwsr.xml</descriptor>
10.172 + </descriptors>
10.173 + </configuration>
10.174 + </execution>
10.175 + </executions>
10.176 + </plugin>
10.177 + </plugins>
10.178 + </build>
10.179 + <dependencies>
10.180 + <dependency>
10.181 + <groupId>org.apidesign.html</groupId>
10.182 + <artifactId>ko-fx</artifactId>
10.183 + <version>\${net.java.html.version}</version>
10.184 + </dependency>
10.185 + <dependency>
10.186 + <groupId>org.apidesign.bck2brwsr</groupId>
10.187 + <artifactId>launcher.fx</artifactId>
10.188 + <version>\${bck2brwsr.launcher.version}</version>
10.189 + <scope>runtime</scope>
10.190 + </dependency>
10.191 + </dependencies>
10.192 + </profile>
10.193 + <profile>
10.194 + <id>bck2brwsr</id>
10.195 + <activation>
10.196 + <property>
10.197 + <name>brwsr</name>
10.198 + <value>bck2brwsr</value>
10.199 + </property>
10.200 + </activation>
10.201 + <build>
10.202 + <plugins>
10.203 + <plugin>
10.204 + <groupId>org.apidesign.bck2brwsr</groupId>
10.205 + <artifactId>bck2brwsr-maven-plugin</artifactId>
10.206 + <executions>
10.207 + <execution>
10.208 + <goals>
10.209 + <goal>j2js</goal>
10.210 + </goals>
10.211 + </execution>
10.212 + </executions>
10.213 + <configuration>
10.214 + <javascript>\${project.build.directory}/bck2brwsr.js</javascript>
10.215 + <obfuscation>\${bck2brwsr.obfuscationlevel}</obfuscation>
10.216 + </configuration>
10.217 + </plugin>
10.218 + <plugin>
10.219 + <groupId>org.apache.maven.plugins</groupId>
10.220 + <artifactId>maven-compiler-plugin</artifactId>
10.221 + <configuration>
10.222 + <compilerArguments>
10.223 + <bootclasspath>netbeans.ignore.jdk.bootclasspath</bootclasspath>
10.224 + </compilerArguments>
10.225 + </configuration>
10.226 + </plugin>
10.227 + <plugin>
10.228 + <artifactId>maven-assembly-plugin</artifactId>
10.229 + <version>2.4</version>
10.230 + <executions>
10.231 + <execution>
10.232 + <id>distro-assembly</id>
10.233 + <phase>package</phase>
10.234 + <goals>
10.235 + <goal>single</goal>
10.236 + </goals>
10.237 + <configuration>
10.238 + <descriptors>
10.239 + <descriptor>src/main/assembly/bck2brwsr.xml</descriptor>
10.240 + </descriptors>
10.241 + </configuration>
10.242 + </execution>
10.243 + </executions>
10.244 + </plugin>
10.245 + </plugins>
10.246 + </build>
10.247 + <dependencies>
10.248 + <dependency>
10.249 + <groupId>org.apidesign.bck2brwsr</groupId>
10.250 + <artifactId>emul</artifactId>
10.251 + <version>\${bck2brwsr.version}</version>
10.252 + <classifier>rt</classifier>
10.253 + </dependency>
10.254 + <dependency>
10.255 + <groupId>org.apidesign.bck2brwsr</groupId>
10.256 + <artifactId>ko-bck2brwsr</artifactId>
10.257 + <version>\${bck2brwsr.version}</version>
10.258 + <scope>runtime</scope>
10.259 + </dependency>
10.260 + <dependency>
10.261 + <groupId>org.apidesign.bck2brwsr</groupId>
10.262 + <artifactId>launcher.http</artifactId>
10.263 + <version>\${bck2brwsr.launcher.version}</version>
10.264 + <scope>test</scope>
10.265 + </dependency>
10.266 + </dependencies>
10.267 + </profile>
10.268 + </profiles>
10.269 +</project>
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/bck2brwsr.xml Sat Sep 07 18:28:09 2013 +0200
11.3 @@ -0,0 +1,43 @@
11.4 +<?xml version="1.0"?>
11.5 +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
11.6 + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
11.7 +
11.8 + <id>bck2brwsr</id>
11.9 + <formats>
11.10 + <format>zip</format>
11.11 + </formats>
11.12 + <baseDirectory>public_html</baseDirectory>
11.13 + <dependencySets>
11.14 + <dependencySet>
11.15 + <useProjectArtifact>false</useProjectArtifact>
11.16 + <scope>runtime</scope>
11.17 + <outputDirectory>lib</outputDirectory>
11.18 + <includes>
11.19 + <include>*:jar</include>
11.20 + <include>*:rt</include>
11.21 + </includes>
11.22 + </dependencySet>
11.23 + </dependencySets>
11.24 + <fileSets>
11.25 + <fileSet>
11.26 + <directory>${project.build.directory}/classes/${package.replace('.','/')}/</directory>
11.27 + <includes>
11.28 + <include>**/*</include>
11.29 + </includes>
11.30 + <excludes>
11.31 + <exclude>**/*.class</exclude>
11.32 + </excludes>
11.33 + <outputDirectory>/</outputDirectory>
11.34 + </fileSet>
11.35 + </fileSets>
11.36 + <files>
11.37 + <file>
11.38 + <source>${project.build.directory}/${project.build.finalName}.jar</source>
11.39 + <outputDirectory>/</outputDirectory>
11.40 + </file>
11.41 + <file>
11.42 + <source>${project.build.directory}/bck2brwsr.js</source>
11.43 + <outputDirectory>/</outputDirectory>
11.44 + </file>
11.45 + </files>
11.46 +</assembly>
11.47 \ No newline at end of file
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/assembly/fxbrwsr.xml Sat Sep 07 18:28:09 2013 +0200
12.3 @@ -0,0 +1,23 @@
12.4 +<?xml version="1.0"?>
12.5 +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
12.6 + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
12.7 +
12.8 + <id>fxbrwsr</id>
12.9 + <formats>
12.10 + <format>zip</format>
12.11 + </formats>
12.12 + <baseDirectory>${project.build.finalName}-fxbrwsr</baseDirectory>
12.13 + <dependencySets>
12.14 + <dependencySet>
12.15 + <useProjectArtifact>false</useProjectArtifact>
12.16 + <scope>runtime</scope>
12.17 + <outputDirectory>lib</outputDirectory>
12.18 + </dependencySet>
12.19 + </dependencySets>
12.20 + <files>
12.21 + <file>
12.22 + <source>${project.build.directory}/${project.build.finalName}.jar</source>
12.23 + <outputDirectory>/</outputDirectory>
12.24 + </file>
12.25 + </files>
12.26 +</assembly>
12.27 \ No newline at end of file
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/java/TwitterClient.java Sat Sep 07 18:28:09 2013 +0200
13.3 @@ -0,0 +1,178 @@
13.4 +package ${package};
13.5 +
13.6 +import java.util.Arrays;
13.7 +import java.util.List;
13.8 +import net.java.html.json.ComputedProperty;
13.9 +import net.java.html.json.Function;
13.10 +import net.java.html.json.Model;
13.11 +import net.java.html.json.OnPropertyChange;
13.12 +import net.java.html.json.OnReceive;
13.13 +import net.java.html.json.Property;
13.14 +
13.15 +@Model(className="TwitterModel", properties={
13.16 + @Property(name="savedLists", type=Tweeters.class, array = true),
13.17 + @Property(name="activeTweetersName", type=String.class),
13.18 + @Property(name="activeTweeters", type=String.class, array = true),
13.19 + @Property(name="userNameToAdd", type=String.class),
13.20 + @Property(name="loading", type=boolean.class),
13.21 + @Property(name="currentTweets", type=Tweet.class, array = true)
13.22 +})
13.23 +public class TwitterClient {
13.24 + @Model(className = "Tweeters", properties = {
13.25 + @Property(name="name", type = String.class),
13.26 + @Property(name="userNames", type = String.class, array = true)
13.27 + })
13.28 + static class Twttrs {
13.29 + }
13.30 + @Model(className = "Tweet", properties = {
13.31 + @Property(name = "from_user", type = String.class),
13.32 + @Property(name = "from_user_id", type = int.class),
13.33 + @Property(name = "profile_image_url", type = String.class),
13.34 + @Property(name = "text", type = String.class),
13.35 + @Property(name = "created_at", type = String.class),
13.36 + })
13.37 + static final class Twt {
13.38 + @ComputedProperty static String html(String text) {
13.39 + StringBuilder sb = new StringBuilder(320);
13.40 + for (int pos = 0;;) {
13.41 + int http = text.indexOf("http", pos);
13.42 + if (http == -1) {
13.43 + sb.append(text.substring(pos));
13.44 + return sb.toString();
13.45 + }
13.46 + int spc = text.indexOf(' ', http);
13.47 + if (spc == -1) {
13.48 + spc = text.length();
13.49 + }
13.50 + sb.append(text.substring(pos, http));
13.51 + String url = text.substring(http, spc);
13.52 + sb.append("<a href='").append(url).append("'>").append(url).append("</a>");
13.53 + pos = spc;
13.54 + }
13.55 + }
13.56 +
13.57 + @ComputedProperty static String userUrl(String from_user) {
13.58 + return "http://twitter.com/" + from_user;
13.59 + }
13.60 + }
13.61 + @Model(className = "TwitterQuery", properties = {
13.62 + @Property(array = true, name = "results", type = Twt.class)
13.63 + })
13.64 + public static final class TwttrQr {
13.65 + }
13.66 +
13.67 + @OnReceive(url="{root}/search.json?{query}&callback={me}", jsonp="me")
13.68 + static void queryTweets(TwitterModel page, TwitterQuery q) {
13.69 + page.getCurrentTweets().clear();
13.70 + page.getCurrentTweets().addAll(q.getResults());
13.71 + page.setLoading(false);
13.72 + }
13.73 +
13.74 + @OnPropertyChange("activeTweetersName")
13.75 + static void changeTweetersList(TwitterModel model) {
13.76 + Tweeters people = findByName(model.getSavedLists(), model.getActiveTweetersName());
13.77 + model.getActiveTweeters().clear();
13.78 + model.getActiveTweeters().addAll(people.getUserNames());
13.79 + }
13.80 +
13.81 + @OnPropertyChange({ "activeTweeters", "activeTweetersCount" })
13.82 + static void refreshTweets(TwitterModel model) {
13.83 + StringBuilder sb = new StringBuilder();
13.84 + sb.append("rpp=25&q=");
13.85 + String sep = "";
13.86 + for (String p : model.getActiveTweeters()) {
13.87 + sb.append(sep);
13.88 + sb.append("from:");
13.89 + sb.append(p);
13.90 + sep = " OR ";
13.91 + }
13.92 + model.setLoading(true);
13.93 + model.queryTweets("http://search.twitter.com", sb.toString());
13.94 + }
13.95 +
13.96 + static {
13.97 + final TwitterModel model = new TwitterModel();
13.98 + final List<Tweeters> svdLst = model.getSavedLists();
13.99 + svdLst.add(newTweeters("API Design", "JaroslavTulach"));
13.100 + svdLst.add(newTweeters("Celebrities", "JohnCleese", "MCHammer", "StephenFry", "algore", "StevenSanderson"));
13.101 + svdLst.add(newTweeters("Microsoft people", "BillGates", "shanselman", "ScottGu"));
13.102 + svdLst.add(newTweeters("NetBeans", "GeertjanW","monacotoni", "NetBeans", "petrjiricka"));
13.103 + svdLst.add(newTweeters("Tech pundits", "Scobleizer", "LeoLaporte", "techcrunch", "BoingBoing", "timoreilly", "codinghorror"));
13.104 +
13.105 + model.setActiveTweetersName("NetBeans");
13.106 +
13.107 + model.applyBindings();
13.108 + }
13.109 +
13.110 + @ComputedProperty
13.111 + static boolean hasUnsavedChanges(List<String> activeTweeters, List<Tweeters> savedLists, String activeTweetersName) {
13.112 + Tweeters tw = findByName(savedLists, activeTweetersName);
13.113 + if (activeTweeters == null) {
13.114 + return false;
13.115 + }
13.116 + return !tw.getUserNames().equals(activeTweeters);
13.117 + }
13.118 +
13.119 + @ComputedProperty
13.120 + static int activeTweetersCount(List<String> activeTweeters) {
13.121 + return activeTweeters.size();
13.122 + }
13.123 +
13.124 + @ComputedProperty
13.125 + static boolean userNameToAddIsValid(
13.126 + String userNameToAdd, String activeTweetersName, List<Tweeters> savedLists, List<String> activeTweeters
13.127 + ) {
13.128 + return userNameToAdd != null &&
13.129 + userNameToAdd.matches("[a-zA-Z0-9_]{1,15}") &&
13.130 + !activeTweeters.contains(userNameToAdd);
13.131 + }
13.132 +
13.133 + @Function
13.134 + static void deleteList(TwitterModel model) {
13.135 + final List<Tweeters> sl = model.getSavedLists();
13.136 + sl.remove(findByName(sl, model.getActiveTweetersName()));
13.137 + if (sl.isEmpty()) {
13.138 + final Tweeters t = new Tweeters();
13.139 + t.setName("New");
13.140 + sl.add(t);
13.141 + }
13.142 + model.setActiveTweetersName(sl.get(0).getName());
13.143 + }
13.144 +
13.145 + @Function
13.146 + static void saveChanges(TwitterModel model) {
13.147 + Tweeters t = findByName(model.getSavedLists(), model.getActiveTweetersName());
13.148 + int indx = model.getSavedLists().indexOf(t);
13.149 + if (indx != -1) {
13.150 + t.setName(model.getActiveTweetersName());
13.151 + t.getUserNames().clear();
13.152 + t.getUserNames().addAll(model.getActiveTweeters());
13.153 + }
13.154 + }
13.155 +
13.156 + @Function
13.157 + static void addUser(TwitterModel model) {
13.158 + String n = model.getUserNameToAdd();
13.159 + model.getActiveTweeters().add(n);
13.160 + }
13.161 + @Function
13.162 + static void removeUser(String data, TwitterModel model) {
13.163 + model.getActiveTweeters().remove(data);
13.164 + }
13.165 +
13.166 + private static Tweeters findByName(List<Tweeters> list, String name) {
13.167 + for (Tweeters l : list) {
13.168 + if (l.getName() != null && l.getName().equals(name)) {
13.169 + return l;
13.170 + }
13.171 + }
13.172 + return list.isEmpty() ? new Tweeters() : list.get(0);
13.173 + }
13.174 +
13.175 + private static Tweeters newTweeters(String listName, String... userNames) {
13.176 + Tweeters t = new Tweeters();
13.177 + t.setName(listName);
13.178 + t.getUserNames().addAll(Arrays.asList(userNames));
13.179 + return t;
13.180 + }
13.181 +}
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/resources/index.html Sat Sep 07 18:28:09 2013 +0200
14.3 @@ -0,0 +1,90 @@
14.4 +<?xml version="1.0" encoding="UTF-8"?>
14.5 +
14.6 +<!--
14.7 + Copied from knockout.js Twitter example:
14.8 + http://knockoutjs.com/examples/twitter.html
14.9 +-->
14.10 +
14.11 +<!DOCTYPE html>
14.12 +<html xmlns="http://www.w3.org/1999/xhtml">
14.13 + <head>
14.14 + <title>Bck2Brwsr's Twitter</title>
14.15 + </head>
14.16 + <body>
14.17 + <link href='twitterExample.css' rel='Stylesheet' ></link>
14.18 +
14.19 + <style type='text/css'>
14.20 + .liveExample select { height: 1.7em; }
14.21 + .liveExample button { height: 2em; }
14.22 + </style>
14.23 +
14.24 +
14.25 + <h2>Bck2Brwsr's Twitter</h2>
14.26 +
14.27 + <p>
14.28 + This code is based on original
14.29 + <a href="http://knockoutjs.com/examples/twitter.html">knockout.js
14.30 + Twitter example</a> and
14.31 + uses almost unmodified HTML page. It just changes the model. The model
14.32 + is written in Java language with the help of
14.33 + <a href="http://bck2brwsr.apidesign.org/javadoc/net.java.html.json/">
14.34 + Knockout/Java binding library
14.35 + </a>. The Java source code has about 180 lines and seems more
14.36 + dense and shorter than the original JavaScript model.
14.37 + </p>
14.38 + <p>
14.39 + The project has two profiles. Either it executes in real Java virtual
14.40 + machine and renders using JavaFX's WebView (use <code>fxbrwsr</code> profile
14.41 + - the default). It can also run directly in a browser via
14.42 + <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a> virtual machine
14.43 + (use <code>bck2brwsr</code> profile).
14.44 + </p>
14.45 +
14.46 + <div class='liveExample'>
14.47 + <div class='configuration'>
14.48 + <div class='listChooser'>
14.49 + <button data-bind='click: deleteList, enable: activeTweetersName'>Delete</button>
14.50 + <button data-bind='click: saveChanges, enable: hasUnsavedChanges'>Save</button>
14.51 + <select data-bind='options: savedLists, optionsValue: "name", value: activeTweetersName'> </select>
14.52 + </div>
14.53 +
14.54 + <p>Currently viewing <span data-bind='text: activeTweetersCount'> </span> user(s):</p>
14.55 + <div class='currentUsers' >
14.56 + <ul data-bind='foreach: activeTweeters'>
14.57 + <li>
14.58 + <button data-bind='click: $root.removeUser'>Remove</button>
14.59 + <div data-bind='text: $data'> </div>
14.60 + </li>
14.61 + </ul>
14.62 + </div>
14.63 +
14.64 + <form data-bind='submit: addUser'>
14.65 + <label>Add user:</label>
14.66 + <input data-bind='value: userNameToAdd, valueUpdate: "keyup", css: { invalid: !userNameToAddIsValid() }' />
14.67 + <button data-bind='enable: userNameToAddIsValid' type='submit'>Add</button>
14.68 + </form>
14.69 + </div>
14.70 + <div class='tweets'>
14.71 + <div class='loadingIndicator' data-bind="visible: loading">Loading...</div>
14.72 + <table data-bind='foreach: currentTweets' width='100%'>
14.73 + <tr>
14.74 + <td><img data-bind='attr: { src: profile_image_url }' /></td>
14.75 + <td>
14.76 + <a class='twitterUser' data-bind='attr: { href: userUrl }, text: from_user'> </a>
14.77 + <span data-bind='html: html'> </span>
14.78 + <div class='tweetInfo' data-bind='text: created_at'> </div>
14.79 + </td>
14.80 + </tr>
14.81 + </table>
14.82 + </div>
14.83 + </div>
14.84 +
14.85 + <script src="bck2brwsr.js"></script>
14.86 + <script type="text/javascript">
14.87 + var vm = bck2brwsr('${artifactId}-${version}.jar');
14.88 + vm.loadClass('${package}.TwitterClient');
14.89 + </script>
14.90 +
14.91 +
14.92 + </body>
14.93 +</html>
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
15.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/main/resources/twitterExample.css Sat Sep 07 18:28:09 2013 +0200
15.3 @@ -0,0 +1,32 @@
15.4 +/*
15.5 + Copied from knockout.js Twitter example:
15.6 + http://knockoutjs.com/examples/twitter.html
15.7 +*/
15.8 +
15.9 +.configuration, .tweets, .tweets td { font-family: Verdana; font-size: 13px; }
15.10 +.configuration { background-color: #DEDEDE; border: 2px solid gray; float:left; height: 40em; width: 40%; padding: 0.5em; border-right-width:0; }
15.11 +.tweets { width: 55%; border: 2px solid gray; height: 40em; overflow: scroll; overflow-x: hidden; background-color: Black; color: White; padding: 0.5em; position: relative; }
15.12 +.tweets table { border-width: 0;}
15.13 +.tweets tr { vertical-align: top; }
15.14 +.tweets td { padding: 0.4em 0.3em 1em 0.4em; border-width: 0; }
15.15 +.tweets img { width: 4em; }
15.16 +.tweetInfo { color: Gray; font-size: 0.9em; }
15.17 +.twitterUser { color: #77AAFF; text-decoration: none; font-size: 1.1em; font-weight: bold; }
15.18 +input.invalid { border: 1px solid red !important; background-color: #FFAAAA !important; }
15.19 +
15.20 +.listChooser select, .listChooser button { vertical-align:top; }
15.21 +.listChooser select { width: 60%; font-size:1.2em; height:1.4em; }
15.22 +.listChooser button { width: 19%; height:1.68em; float:right; }
15.23 +
15.24 +.currentUsers { height: 28em; overflow-y: auto; overflow-x: hidden; }
15.25 +.currentUsers button { float: right; height: 2.5em; margin: 0.1em; padding-left: 1em; padding-right: 1em; }
15.26 +.currentUsers ul, .configuration li { list-style: none; margin: 0; padding: 0 }
15.27 +.currentUsers li { height: 2.4em; font-size: 1.2em; background-color: #A7D0E3; border: 1px solid gray; margin-bottom: 0.3em; -webkit-border-radius: 5px; -moz-border-radius: 5px; -webkit-box-shadow: 0 0.2em 0.5em gray; -moz-box-shadow: 0 0.2em 0.5em gray; }
15.28 +.currentUsers li div { padding: 0.6em; }
15.29 +.currentUsers li:hover { background-color: #EEC; }
15.30 +
15.31 +.configuration form label { width: 25%; display: inline-block; text-align:right; overflow: hidden; }
15.32 +.configuration form input { width:40%; font-size: 1.3em; border:1px solid silver; background-color: White; padding: 0.1em; }
15.33 +.configuration form button { width: 20%; margin-left: 0.3em; height: 2em; }
15.34 +
15.35 +.loadingIndicator { position: absolute; top: 0.1em; left: 0.1em; font: 0.8em Arial; background-color: #229; color: White; padding: 0.2em 0.5em 0.2em 0.5em; }
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
16.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/test/java/IntegrationTest.java Sat Sep 07 18:28:09 2013 +0200
16.3 @@ -0,0 +1,31 @@
16.4 +package ${package};
16.5 +
16.6 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
16.7 +import org.apidesign.bck2brwsr.vmtest.HtmlFragment;
16.8 +import org.apidesign.bck2brwsr.vmtest.VMTest;
16.9 +import org.testng.annotations.Factory;
16.10 +
16.11 +/** Sometimes it is useful to run tests inside of the real browser.
16.12 + * To do that just annotate your method with {@link org.apidesign.bck2brwsr.vmtest.BrwsrTest}
16.13 + * and that is it. If your code references elements on the HTML page,
16.14 + * you can pass in an {@link org.apidesign.bck2brwsr.vmtest.HtmlFragment} which
16.15 + * will be made available on the page before your test starts.
16.16 + */
16.17 +public class IntegrationTest {
16.18 +
16.19 + /** Write to testing code here. Use <code>assert</code> (but not TestNG's
16.20 + * Assert, as TestNG is not compiled with target 1.6 yet).
16.21 + */
16.22 + @HtmlFragment(
16.23 + "<h1>Put this snippet on the HTML page</h1>\n"
16.24 + )
16.25 + @BrwsrTest
16.26 + public void runThisTestInABrowser() {
16.27 + }
16.28 +
16.29 + @Factory
16.30 + public static Object[] create() {
16.31 + return VMTest.create(IntegrationTest.class);
16.32 + }
16.33 +
16.34 +}
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
17.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/test/java/TwitterClientTest.java Sat Sep 07 18:28:09 2013 +0200
17.3 @@ -0,0 +1,50 @@
17.4 +package ${package};
17.5 +
17.6 +import java.util.List;
17.7 +import net.java.html.BrwsrCtx;
17.8 +import net.java.html.json.Models;
17.9 +import static org.testng.Assert.*;
17.10 +import org.testng.annotations.BeforeMethod;
17.11 +import org.testng.annotations.Test;
17.12 +
17.13 +/** We can unit test the TwitterModel smoothly.
17.14 + */
17.15 +public class TwitterClientTest {
17.16 + private TwitterModel model;
17.17 +
17.18 +
17.19 + @BeforeMethod
17.20 + public void initModel() {
17.21 + model = Models.bind(new TwitterModel(), BrwsrCtx.EMPTY);
17.22 + }
17.23 +
17.24 + @Test public void testIsValidToAdd() {
17.25 + model.setUserNameToAdd("Joe");
17.26 + Tweeters t = Models.bind(new Tweeters(), BrwsrCtx.EMPTY);
17.27 + t.setName("test");
17.28 + model.getSavedLists().add(t);
17.29 + model.setActiveTweetersName("test");
17.30 +
17.31 + assertTrue(model.isUserNameToAddIsValid(), "Joe is OK");
17.32 + TwitterClient.addUser(model);
17.33 + assertFalse(model.isUserNameToAddIsValid(), "Can't add Joe for the 2nd time");
17.34 + assertEquals(t.getUserNames().size(), 0, "Original tweeters list remains empty");
17.35 +
17.36 + List<String> mod = model.getActiveTweeters();
17.37 + assertTrue(model.isHasUnsavedChanges(), "We have modifications");
17.38 + assertEquals(mod.size(), 1, "One element in the list");
17.39 + assertEquals(mod.get(0), "Joe", "Its name is Joe");
17.40 +
17.41 + assertSame(model.getActiveTweeters(), mod, "Editing list is the modified one");
17.42 +
17.43 + TwitterClient.saveChanges(model);
17.44 + assertFalse(model.isHasUnsavedChanges(), "Does not have anything to save");
17.45 +
17.46 + assertSame(model.getActiveTweeters(), mod, "Still editing the old modified one");
17.47 + }
17.48 +
17.49 + @Test public void httpAtTheEnd() {
17.50 + String res = TwitterClient.Twt.html("Ahoj http://kuk");
17.51 + assertEquals(res, "Ahoj <a href='http://kuk'>http://kuk</a>");
17.52 + }
17.53 +}
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
18.2 +++ b/ko/archetype/src/main/resources/archetype-resources/src/test/java/TwitterProtocolTest.java Sat Sep 07 18:28:09 2013 +0200
18.3 @@ -0,0 +1,73 @@
18.4 +package ${package};
18.5 +
18.6 +import org.apidesign.bck2brwsr.vmtest.BrwsrTest;
18.7 +import org.apidesign.bck2brwsr.vmtest.Http;
18.8 +import org.apidesign.bck2brwsr.vmtest.VMTest;
18.9 +import org.testng.annotations.Factory;
18.10 +
18.11 +public class TwitterProtocolTest {
18.12 + private TwitterModel page;
18.13 + @Http(@Http.Resource(
18.14 + path = "/search.json",
18.15 + mimeType = "application/json",
18.16 + parameters = {"callback"},
18.17 + content = "$0({\"completed_in\":0.04,\"max_id\":320055706885689344,\"max_id_str\""
18.18 + + ":\"320055706885689344\",\"page\":1,\"query\":\"from%3AJaroslavTulach\",\"refresh_url\":"
18.19 + + "\"?since_id=320055706885689344&q=from%3AJaroslavTulach\","
18.20 + + "\"results\":[{\"created_at\":\"Fri, 05 Apr 2013 06:10:01 +0000\","
18.21 + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
18.22 + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":320055706885689344,"
18.23 + + "\"id_str\":\"320055706885689344\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
18.24 + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
18.25 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
18.26 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
18.27 + + "\"@tom_enebo Amzng! Not that I would like #ruby, but I am really glad you guys stabilized the plugin + "
18.28 + + "made it work in #netbeans 7.3! Gd wrk.\",\"to_user\":\"tom_enebo\",\"to_user_id\":14498747,"
18.29 + + "\"to_user_id_str\":\"14498747\",\"to_user_name\":\"tom_enebo\",\"in_reply_to_status_id\":319832359509839872,"
18.30 + + "\"in_reply_to_status_id_str\":\"319832359509839872\"},{\"created_at\":\"Thu, 04 Apr 2013 07:33:06 +0000\","
18.31 + + "\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,\"from_user_id_str\":"
18.32 + + "\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,\"id\":319714227088678913,"
18.33 + + "\"id_str\":\"319714227088678913\",\"iso_language_code\":\"en\",\"metadata\":{\"result_type\":"
18.34 + + "\"recent\"},\"profile_image_url\":\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
18.35 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
18.36 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
18.37 + + "\"RT @drkrab: At #erlangfactory @joerl: Frameworks grow in complexity until nobody can use them.\"},"
18.38 + + "{\"created_at\":\"Tue, 02 Apr 2013 07:44:34 +0000\",\"from_user\":\"JaroslavTulach\","
18.39 + + "\"from_user_id\":420944648,\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\","
18.40 + + "\"geo\":null,\"id\":318992336145248256,\"id_str\":\"318992336145248256\",\"iso_language_code\":\"en\","
18.41 + + "\"metadata\":{\"result_type\":\"recent\"},\"profile_image_url\":"
18.42 + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
18.43 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
18.44 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
18.45 + + "\"Twitter renamed to twttr http:\\/\\/t.co\\/tqaN4T1xlZ - good, I don't have to rename #bck2brwsr!\"},"
18.46 + + "{\"created_at\":\"Sun, 31 Mar 2013 03:52:04 +0000\",\"from_user\":\"JaroslavTulach\",\"from_user_id\":420944648,"
18.47 + + "\"from_user_id_str\":\"420944648\",\"from_user_name\":\"Jaroslav Tulach\",\"geo\":null,"
18.48 + + "\"id\":318209051223789568,\"id_str\":\"318209051223789568\",\"iso_language_code\":\"en\",\"metadata\":"
18.49 + + "{\"result_type\":\"recent\"},\"profile_image_url\":"
18.50 + + "\"http:\\/\\/a0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
18.51 + + "\"profile_image_url_https\":\"https:\\/\\/si0.twimg.com\\/profile_images\\/1656828312\\/jst_normal.gif\","
18.52 + + "\"source\":\"<a href="http:\\/\\/twitter.com\\/">web<\\/a>\",\"text\":"
18.53 + + "\"Math proofs without words. Ingenious: http:\\/\\/t.co\\/sz7yVbfpGw\"}],\"results_per_page\":100,"
18.54 + + "\"since_id\":0,\"since_id_str\":\"0\"})"
18.55 + ))
18.56 + @BrwsrTest public void readFromTwttr() throws InterruptedException {
18.57 + if (page == null) {
18.58 + page = new TwitterModel();
18.59 + page.applyBindings();
18.60 + page.queryTweets("", "q=xyz");
18.61 + }
18.62 +
18.63 + if (page.getCurrentTweets().isEmpty()) {
18.64 + throw new InterruptedException();
18.65 + }
18.66 +
18.67 + assert 4 == page.getCurrentTweets().size() : "Four tweets: " + page.getCurrentTweets();
18.68 +
18.69 + String firstDate = page.getCurrentTweets().get(0).getCreated_at();
18.70 + assert "Fri, 05 Apr 2013 06:10:01 +0000".equals(firstDate) : "Date is OK: " + firstDate;
18.71 + }
18.72 +
18.73 + @Factory public static Object[] create() {
18.74 + return VMTest.create(TwitterProtocolTest.class);
18.75 + }
18.76 +}
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
19.2 +++ b/ko/bck2brwsr/pom.xml Sat Sep 07 18:28:09 2013 +0200
19.3 @@ -0,0 +1,102 @@
19.4 +<?xml version="1.0"?>
19.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">
19.6 + <modelVersion>4.0.0</modelVersion>
19.7 + <parent>
19.8 + <groupId>org.apidesign.bck2brwsr</groupId>
19.9 + <artifactId>ko</artifactId>
19.10 + <version>0.8-SNAPSHOT</version>
19.11 + </parent>
19.12 + <groupId>org.apidesign.bck2brwsr</groupId>
19.13 + <artifactId>ko-bck2brwsr</artifactId>
19.14 + <version>0.8-SNAPSHOT</version>
19.15 + <name>Knockout.b2b</name>
19.16 + <url>http://maven.apache.org</url>
19.17 + <build>
19.18 + <plugins>
19.19 + <plugin>
19.20 + <groupId>org.apache.maven.plugins</groupId>
19.21 + <artifactId>maven-compiler-plugin</artifactId>
19.22 + <version>2.3.2</version>
19.23 + <configuration>
19.24 + <source>1.7</source>
19.25 + <target>1.7</target>
19.26 + </configuration>
19.27 + </plugin>
19.28 + <plugin>
19.29 + <groupId>org.apache.maven.plugins</groupId>
19.30 + <artifactId>maven-javadoc-plugin</artifactId>
19.31 + <configuration>
19.32 + <skip>false</skip>
19.33 + </configuration>
19.34 + </plugin>
19.35 + </plugins>
19.36 + </build>
19.37 + <dependencies>
19.38 + <dependency>
19.39 + <groupId>org.testng</groupId>
19.40 + <artifactId>testng</artifactId>
19.41 + <scope>test</scope>
19.42 + <exclusions>
19.43 + <exclusion>
19.44 + <artifactId>junit</artifactId>
19.45 + <groupId>junit</groupId>
19.46 + </exclusion>
19.47 + </exclusions>
19.48 + </dependency>
19.49 + <dependency>
19.50 + <groupId>org.netbeans.api</groupId>
19.51 + <artifactId>org-openide-util-lookup</artifactId>
19.52 + <scope>provided</scope>
19.53 + </dependency>
19.54 + <dependency>
19.55 + <groupId>org.apidesign.bck2brwsr</groupId>
19.56 + <artifactId>emul</artifactId>
19.57 + <version>${project.version}</version>
19.58 + <classifier>rt</classifier>
19.59 + <type>jar</type>
19.60 + <scope>compile</scope>
19.61 + </dependency>
19.62 + <dependency>
19.63 + <groupId>org.apidesign.bck2brwsr</groupId>
19.64 + <artifactId>vm4brwsr</artifactId>
19.65 + <version>${project.version}</version>
19.66 + <type>jar</type>
19.67 + <scope>test</scope>
19.68 + </dependency>
19.69 + <dependency>
19.70 + <groupId>org.apidesign.bck2brwsr</groupId>
19.71 + <artifactId>vmtest</artifactId>
19.72 + <version>${project.version}</version>
19.73 + <scope>test</scope>
19.74 + </dependency>
19.75 + <dependency>
19.76 + <groupId>org.apidesign.bck2brwsr</groupId>
19.77 + <artifactId>launcher.http</artifactId>
19.78 + <version>${project.version}</version>
19.79 + <scope>test</scope>
19.80 + </dependency>
19.81 + <dependency>
19.82 + <groupId>org.apidesign.html</groupId>
19.83 + <artifactId>net.java.html.json</artifactId>
19.84 + <version>${net.java.html.version}</version>
19.85 + </dependency>
19.86 + <dependency>
19.87 + <groupId>org.apidesign.html</groupId>
19.88 + <artifactId>net.java.html.json.tck</artifactId>
19.89 + <version>${net.java.html.version}</version>
19.90 + <scope>test</scope>
19.91 + </dependency>
19.92 + <dependency>
19.93 + <groupId>org.apidesign.bck2brwsr</groupId>
19.94 + <artifactId>core</artifactId>
19.95 + <version>${project.version}</version>
19.96 + <type>jar</type>
19.97 + </dependency>
19.98 + <dependency>
19.99 + <groupId>org.apidesign.html</groupId>
19.100 + <artifactId>net.java.html.boot</artifactId>
19.101 + <version>0.5</version>
19.102 + <type>jar</type>
19.103 + </dependency>
19.104 + </dependencies>
19.105 +</project>
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
20.2 +++ b/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/BrwsrCtxImpl.java Sat Sep 07 18:28:09 2013 +0200
20.3 @@ -0,0 +1,166 @@
20.4 +/**
20.5 + * Back 2 Browser Bytecode Translator
20.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
20.7 + *
20.8 + * This program is free software: you can redistribute it and/or modify
20.9 + * it under the terms of the GNU General Public License as published by
20.10 + * the Free Software Foundation, version 2 of the License.
20.11 + *
20.12 + * This program is distributed in the hope that it will be useful,
20.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
20.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20.15 + * GNU General Public License for more details.
20.16 + *
20.17 + * You should have received a copy of the GNU General Public License
20.18 + * along with this program. Look for COPYING file in the top folder.
20.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
20.20 + */
20.21 +package org.apidesign.bck2brwsr.ko2brwsr;
20.22 +
20.23 +import java.io.ByteArrayOutputStream;
20.24 +import java.io.IOException;
20.25 +import java.io.InputStream;
20.26 +import java.io.InputStreamReader;
20.27 +import org.apidesign.html.json.spi.FunctionBinding;
20.28 +import org.apidesign.html.json.spi.JSONCall;
20.29 +import org.apidesign.html.json.spi.PropertyBinding;
20.30 +import org.apidesign.html.json.spi.Technology;
20.31 +import org.apidesign.html.json.spi.Transfer;
20.32 +import org.apidesign.html.json.spi.WSTransfer;
20.33 +
20.34 +/**
20.35 + *
20.36 + * @author Jaroslav Tulach <jtulach@netbeans.org>
20.37 + */
20.38 +final class BrwsrCtxImpl implements Technology<Object>, Transfer, WSTransfer<LoadWS> {
20.39 + private BrwsrCtxImpl() {}
20.40 +
20.41 + public static final BrwsrCtxImpl DEFAULT = new BrwsrCtxImpl();
20.42 +
20.43 + @Override
20.44 + public void extract(Object obj, String[] props, Object[] values) {
20.45 + ConvertTypes.extractJSON(obj, props, values);
20.46 + }
20.47 +
20.48 + @Override
20.49 + public void loadJSON(final JSONCall call) {
20.50 + class R implements Runnable {
20.51 + final boolean success;
20.52 +
20.53 + public R(boolean success) {
20.54 + this.success = success;
20.55 + }
20.56 +
20.57 + Object[] arr = { null };
20.58 + @Override
20.59 + public void run() {
20.60 + if (success) {
20.61 + call.notifySuccess(arr[0]);
20.62 + } else {
20.63 + Throwable t;
20.64 + if (arr[0] instanceof Throwable) {
20.65 + t = (Throwable) arr[0];
20.66 + } else {
20.67 + if (arr[0] == null) {
20.68 + t = new IOException();
20.69 + } else {
20.70 + t = new IOException(arr[0].toString());
20.71 + }
20.72 + }
20.73 + call.notifyError(t);
20.74 + }
20.75 + }
20.76 + }
20.77 + R success = new R(true);
20.78 + R failure = new R(false);
20.79 + if (call.isJSONP()) {
20.80 + String me = ConvertTypes.createJSONP(success.arr, success);
20.81 + ConvertTypes.loadJSONP(call.composeURL(me), me);
20.82 + } else {
20.83 + String data = null;
20.84 + if (call.isDoOutput()) {
20.85 + try {
20.86 + ByteArrayOutputStream bos = new ByteArrayOutputStream();
20.87 + call.writeData(bos);
20.88 + data = new String(bos.toByteArray(), "UTF-8");
20.89 + } catch (IOException ex) {
20.90 + call.notifyError(ex);
20.91 + }
20.92 + }
20.93 + ConvertTypes.loadJSON(call.composeURL(null), success.arr, success, failure, call.getMethod(), data);
20.94 + }
20.95 + }
20.96 +
20.97 + @Override
20.98 + public Object wrapModel(Object model) {
20.99 + return model;
20.100 + }
20.101 +
20.102 + @Override
20.103 + public void bind(PropertyBinding b, Object model, Object data) {
20.104 + Knockout.bind(data, b, b.getPropertyName(),
20.105 + "getValue__Ljava_lang_Object_2",
20.106 + b.isReadOnly() ? null : "setValue__VLjava_lang_Object_2",
20.107 + false, false
20.108 + );
20.109 + }
20.110 +
20.111 + @Override
20.112 + public void valueHasMutated(Object data, String propertyName) {
20.113 + Knockout.valueHasMutated(data, propertyName);
20.114 + }
20.115 +
20.116 + @Override
20.117 + public void expose(FunctionBinding fb, Object model, Object d) {
20.118 + Knockout.expose(d, fb, fb.getFunctionName(), "call__VLjava_lang_Object_2Ljava_lang_Object_2");
20.119 + }
20.120 +
20.121 + @Override
20.122 + public void applyBindings(Object data) {
20.123 + Knockout.applyBindings(data);
20.124 + }
20.125 +
20.126 + @Override
20.127 + public Object wrapArray(Object[] arr) {
20.128 + return arr;
20.129 + }
20.130 +
20.131 + @Override
20.132 + public <M> M toModel(Class<M> modelClass, Object data) {
20.133 + return modelClass.cast(data);
20.134 + }
20.135 +
20.136 + @Override
20.137 + public Object toJSON(InputStream is) throws IOException {
20.138 + StringBuilder sb = new StringBuilder();
20.139 + InputStreamReader r = new InputStreamReader(is);
20.140 + for (;;) {
20.141 + int ch = r.read();
20.142 + if (ch == -1) {
20.143 + break;
20.144 + }
20.145 + sb.append((char)ch);
20.146 + }
20.147 + return ConvertTypes.parse(sb.toString());
20.148 + }
20.149 +
20.150 + @Override
20.151 + public void runSafe(Runnable r) {
20.152 + r.run();
20.153 + }
20.154 +
20.155 + @Override
20.156 + public LoadWS open(String url, JSONCall callback) {
20.157 + return new LoadWS(callback, url);
20.158 + }
20.159 +
20.160 + @Override
20.161 + public void send(LoadWS socket, JSONCall data) {
20.162 + socket.send(data);
20.163 + }
20.164 +
20.165 + @Override
20.166 + public void close(LoadWS socket) {
20.167 + socket.close();
20.168 + }
20.169 +}
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
21.2 +++ b/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/BrwsrCtxPrvdr.java Sat Sep 07 18:28:09 2013 +0200
21.3 @@ -0,0 +1,51 @@
21.4 +/**
21.5 + * Back 2 Browser Bytecode Translator
21.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
21.7 + *
21.8 + * This program is free software: you can redistribute it and/or modify
21.9 + * it under the terms of the GNU General Public License as published by
21.10 + * the Free Software Foundation, version 2 of the License.
21.11 + *
21.12 + * This program is distributed in the hope that it will be useful,
21.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
21.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21.15 + * GNU General Public License for more details.
21.16 + *
21.17 + * You should have received a copy of the GNU General Public License
21.18 + * along with this program. Look for COPYING file in the top folder.
21.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
21.20 + */
21.21 +package org.apidesign.bck2brwsr.ko2brwsr;
21.22 +
21.23 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
21.24 +import org.apidesign.html.context.spi.Contexts;
21.25 +import org.apidesign.html.json.spi.Technology;
21.26 +import org.apidesign.html.json.spi.Transfer;
21.27 +import org.openide.util.lookup.ServiceProvider;
21.28 +
21.29 +/** This is an implementation package - just
21.30 + * include its JAR on classpath and use official {@link Context} API
21.31 + * to access the functionality.
21.32 + * <p>
21.33 + * Provides binding between models and <a href="http://bck2brwsr.apidesign.org">
21.34 + * Bck2Brwsr</a> VM.
21.35 + * Registers {@link ContextProvider}, so {@link ServiceLoader} can find it.
21.36 + *
21.37 + * @author Jaroslav Tulach <jtulach@netbeans.org>
21.38 + */
21.39 +@ServiceProvider(service = Contexts.Provider.class)
21.40 +public final class BrwsrCtxPrvdr implements Contexts.Provider {
21.41 +
21.42 + @Override
21.43 + public void fillContext(Contexts.Builder context, Class<?> requestor) {
21.44 + if (bck2BrwsrVM()) {
21.45 + context.register(Technology.class, BrwsrCtxImpl.DEFAULT, 50).
21.46 + register(Transfer.class, BrwsrCtxImpl.DEFAULT, 50);
21.47 + }
21.48 + }
21.49 +
21.50 + @JavaScriptBody(args = { }, body = "return true;")
21.51 + private static boolean bck2BrwsrVM() {
21.52 + return false;
21.53 + }
21.54 +}
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
22.2 +++ b/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/ConvertTypes.java Sat Sep 07 18:28:09 2013 +0200
22.3 @@ -0,0 +1,157 @@
22.4 +/**
22.5 + * Back 2 Browser Bytecode Translator
22.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
22.7 + *
22.8 + * This program is free software: you can redistribute it and/or modify
22.9 + * it under the terms of the GNU General Public License as published by
22.10 + * the Free Software Foundation, version 2 of the License.
22.11 + *
22.12 + * This program is distributed in the hope that it will be useful,
22.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
22.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22.15 + * GNU General Public License for more details.
22.16 + *
22.17 + * You should have received a copy of the GNU General Public License
22.18 + * along with this program. Look for COPYING file in the top folder.
22.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
22.20 + */
22.21 +package org.apidesign.bck2brwsr.ko2brwsr;
22.22 +
22.23 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
22.24 +
22.25 +/**
22.26 + *
22.27 + * @author Jaroslav Tulach <jtulach@netbeans.org>
22.28 + */
22.29 +final class ConvertTypes {
22.30 + ConvertTypes() {
22.31 + }
22.32 +
22.33 + public static String toString(Object object, String property) {
22.34 + Object ret = getProperty(object, property);
22.35 + return ret == null ? null : ret.toString();
22.36 + }
22.37 +
22.38 + public static double toDouble(Object object, String property) {
22.39 + Object ret = getProperty(object, property);
22.40 + return ret instanceof Number ? ((Number)ret).doubleValue() : Double.NaN;
22.41 + }
22.42 +
22.43 + public static int toInt(Object object, String property) {
22.44 + Object ret = getProperty(object, property);
22.45 + return ret instanceof Number ? ((Number)ret).intValue() : Integer.MIN_VALUE;
22.46 + }
22.47 +
22.48 + public static <T> T toModel(Class<T> modelClass, Object object, String property) {
22.49 + Object ret = getProperty(object, property);
22.50 + if (ret == null || modelClass.isInstance(ret)) {
22.51 + return modelClass.cast(ret);
22.52 + }
22.53 + throw new IllegalStateException("Value " + ret + " is not of type " + modelClass);
22.54 + }
22.55 +
22.56 + public static String toJSON(Object value) {
22.57 + if (value == null) {
22.58 + return "null";
22.59 + }
22.60 + if (value instanceof Enum) {
22.61 + value = value.toString();
22.62 + }
22.63 + if (value instanceof String) {
22.64 + return '"' +
22.65 + ((String)value).
22.66 + replace("\"", "\\\"").
22.67 + replace("\n", "\\n").
22.68 + replace("\r", "\\r").
22.69 + replace("\t", "\\t")
22.70 + + '"';
22.71 + }
22.72 + return value.toString();
22.73 + }
22.74 +
22.75 + @JavaScriptBody(args = { "object", "property" },
22.76 + body =
22.77 + "if (property === null) return object;\n"
22.78 + + "if (object === null) return null;\n"
22.79 + + "var p = object[property]; return p ? p : null;"
22.80 + )
22.81 + private static Object getProperty(Object object, String property) {
22.82 + return null;
22.83 + }
22.84 +
22.85 + public static String createJSONP(Object[] jsonResult, Runnable whenDone) {
22.86 + int h = whenDone.hashCode();
22.87 + String name;
22.88 + for (;;) {
22.89 + name = "jsonp" + Integer.toHexString(h);
22.90 + if (defineIfUnused(name, jsonResult, whenDone)) {
22.91 + return name;
22.92 + }
22.93 + h++;
22.94 + }
22.95 + }
22.96 +
22.97 + @JavaScriptBody(args = { "name", "arr", "run" }, body =
22.98 + "if (window[name]) return false;\n "
22.99 + + "window[name] = function(data) {\n "
22.100 + + " delete window[name];\n"
22.101 + + " var el = window.document.getElementById(name);\n"
22.102 + + " el.parentNode.removeChild(el);\n"
22.103 + + " arr[0] = data;\n"
22.104 + + " run.run__V();\n"
22.105 + + "};\n"
22.106 + + "return true;\n"
22.107 + )
22.108 + private static boolean defineIfUnused(String name, Object[] arr, Runnable run) {
22.109 + return true;
22.110 + }
22.111 +
22.112 + @JavaScriptBody(args = { "s" }, body = "return eval('(' + s + ')');")
22.113 + static Object parse(String s) {
22.114 + return s;
22.115 + }
22.116 +
22.117 + @JavaScriptBody(args = { "url", "arr", "callback", "onError", "method", "data" }, body = ""
22.118 + + "var request = new XMLHttpRequest();\n"
22.119 + + "if (!method) method = 'GET';\n"
22.120 + + "request.open(method, url, true);\n"
22.121 + + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
22.122 + + "request.onreadystatechange = function() {\n"
22.123 + + " if (this.readyState!==4) return;\n"
22.124 + + " try {\n"
22.125 + + " arr[0] = eval('(' + this.response + ')');\n"
22.126 + + " } catch (error) {;\n"
22.127 + + " arr[0] = this.response;\n"
22.128 + + " }\n"
22.129 + + " callback.run__V();\n"
22.130 + + "};\n"
22.131 + + "request.onerror = function (e) {\n"
22.132 + + " arr[0] = e; onError.run__V();\n"
22.133 + + "}\n"
22.134 + + "if (data) request.send(data);"
22.135 + + "else request.send();"
22.136 + )
22.137 + static void loadJSON(
22.138 + String url, Object[] jsonResult, Runnable whenDone, Runnable whenErr, String method, String data
22.139 + ) {
22.140 + }
22.141 +
22.142 + @JavaScriptBody(args = { "url", "jsonp" }, body =
22.143 + "var scrpt = window.document.createElement('script');\n "
22.144 + + "scrpt.setAttribute('src', url);\n "
22.145 + + "scrpt.setAttribute('id', jsonp);\n "
22.146 + + "scrpt.setAttribute('type', 'text/javascript');\n "
22.147 + + "var body = document.getElementsByTagName('body')[0];\n "
22.148 + + "body.appendChild(scrpt);\n"
22.149 + )
22.150 + static void loadJSONP(String url, String jsonp) {
22.151 +
22.152 + }
22.153 +
22.154 + public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
22.155 + for (int i = 0; i < props.length; i++) {
22.156 + values[i] = getProperty(jsonObject, props[i]);
22.157 + }
22.158 + }
22.159 +
22.160 +}
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
23.2 +++ b/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/Knockout.java Sat Sep 07 18:28:09 2013 +0200
23.3 @@ -0,0 +1,131 @@
23.4 +/**
23.5 + * Back 2 Browser Bytecode Translator
23.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
23.7 + *
23.8 + * This program is free software: you can redistribute it and/or modify
23.9 + * it under the terms of the GNU General Public License as published by
23.10 + * the Free Software Foundation, version 2 of the License.
23.11 + *
23.12 + * This program is distributed in the hope that it will be useful,
23.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
23.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23.15 + * GNU General Public License for more details.
23.16 + *
23.17 + * You should have received a copy of the GNU General Public License
23.18 + * along with this program. Look for COPYING file in the top folder.
23.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
23.20 + */
23.21 +package org.apidesign.bck2brwsr.ko2brwsr;
23.22 +
23.23 +import java.lang.reflect.Method;
23.24 +import java.util.List;
23.25 +import org.apidesign.bck2brwsr.core.ExtraJavaScript;
23.26 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
23.27 +
23.28 +/** Provides binding between models and bck2brwsr VM.
23.29 + *
23.30 + * @author Jaroslav Tulach <jtulach@netbeans.org>
23.31 + */
23.32 +@ExtraJavaScript(resource = "/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js")
23.33 +final class Knockout {
23.34 + /** used by tests */
23.35 + static Knockout next;
23.36 + private final Object model;
23.37 +
23.38 + Knockout(Object model) {
23.39 + this.model = model == null ? this : model;
23.40 + }
23.41 +
23.42 + public static <M> Knockout applyBindings(
23.43 + Object model, String[] propsGettersAndSetters,
23.44 + String[] methodsAndSignatures
23.45 + ) {
23.46 + applyImpl(propsGettersAndSetters, model.getClass(), model, model, methodsAndSignatures);
23.47 + return new Knockout(model);
23.48 + }
23.49 + public static <M> Knockout applyBindings(
23.50 + Class<M> modelClass, M model, String[] propsGettersAndSetters,
23.51 + String[] methodsAndSignatures
23.52 + ) {
23.53 + Knockout bindings = next;
23.54 + next = null;
23.55 + if (bindings == null) {
23.56 + bindings = new Knockout(null);
23.57 + }
23.58 + applyImpl(propsGettersAndSetters, modelClass, bindings, model, methodsAndSignatures);
23.59 + applyBindings(bindings);
23.60 + return bindings;
23.61 + }
23.62 +
23.63 + public void valueHasMutated(String prop) {
23.64 + valueHasMutated(model, prop);
23.65 + }
23.66 + @JavaScriptBody(args = { "self", "prop" }, body =
23.67 + "var p = self[prop]; if (p) p.valueHasMutated();"
23.68 + )
23.69 + public static void valueHasMutated(Object self, String prop) {
23.70 + }
23.71 +
23.72 +
23.73 + @JavaScriptBody(args = { "id", "ev" }, body = "ko.utils.triggerEvent(window.document.getElementById(id), ev.substring(2));")
23.74 + public static void triggerEvent(String id, String ev) {
23.75 + }
23.76 +
23.77 + @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive", "array" }, body =
23.78 + "var bnd = {\n"
23.79 + + " 'read': function() {\n"
23.80 + + " var v = model[getter]();\n"
23.81 + + " if (array) v = v.koArray(); else if (v !== null) v = v.valueOf();\n"
23.82 + + " return v;\n"
23.83 + + " },\n"
23.84 + + " 'owner': bindings\n"
23.85 + + "};\n"
23.86 + + "if (setter != null) {\n"
23.87 + + " bnd['write'] = function(val) {\n"
23.88 + + " var v = val === null ? null : val.valueOf();"
23.89 + + " model[setter](v);\n"
23.90 + + " };\n"
23.91 + + "}\n"
23.92 + + "bindings[prop] = ko['computed'](bnd);"
23.93 + )
23.94 + static void bind(
23.95 + Object bindings, Object model, String prop, String getter, String setter, boolean primitive, boolean array
23.96 + ) {
23.97 + }
23.98 +
23.99 + @JavaScriptBody(args = { "bindings", "model", "prop", "sig" }, body =
23.100 + "bindings[prop] = function(data, ev) { model[sig](data, ev); };"
23.101 + )
23.102 + static void expose(
23.103 + Object bindings, Object model, String prop, String sig
23.104 + ) {
23.105 + }
23.106 +
23.107 + @JavaScriptBody(args = { "bindings" }, body = "ko.applyBindings(bindings);")
23.108 + static void applyBindings(Object bindings) {}
23.109 +
23.110 + private static void applyImpl(
23.111 + String[] propsGettersAndSetters,
23.112 + Class<?> modelClass,
23.113 + Object bindings,
23.114 + Object model,
23.115 + String[] methodsAndSignatures
23.116 + ) throws IllegalStateException, SecurityException {
23.117 + for (int i = 0; i < propsGettersAndSetters.length; i += 4) {
23.118 + try {
23.119 + Method getter = modelClass.getMethod(propsGettersAndSetters[i + 3]);
23.120 + bind(bindings, model, propsGettersAndSetters[i],
23.121 + propsGettersAndSetters[i + 1],
23.122 + propsGettersAndSetters[i + 2],
23.123 + getter.getReturnType().isPrimitive(),
23.124 + List.class.isAssignableFrom(getter.getReturnType()));
23.125 + } catch (NoSuchMethodException ex) {
23.126 + throw new IllegalStateException(ex.getMessage());
23.127 + }
23.128 + }
23.129 + for (int i = 0; i < methodsAndSignatures.length; i += 2) {
23.130 + expose(
23.131 + bindings, model, methodsAndSignatures[i], methodsAndSignatures[i + 1]);
23.132 + }
23.133 + }
23.134 +}
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
24.2 +++ b/ko/bck2brwsr/src/main/java/org/apidesign/bck2brwsr/ko2brwsr/LoadWS.java Sat Sep 07 18:28:09 2013 +0200
24.3 @@ -0,0 +1,126 @@
24.4 +/**
24.5 + * Back 2 Browser Bytecode Translator
24.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
24.7 + *
24.8 + * This program is free software: you can redistribute it and/or modify
24.9 + * it under the terms of the GNU General Public License as published by
24.10 + * the Free Software Foundation, version 2 of the License.
24.11 + *
24.12 + * This program is distributed in the hope that it will be useful,
24.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
24.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24.15 + * GNU General Public License for more details.
24.16 + *
24.17 + * You should have received a copy of the GNU General Public License
24.18 + * along with this program. Look for COPYING file in the top folder.
24.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
24.20 + */
24.21 +package org.apidesign.bck2brwsr.ko2brwsr;
24.22 +
24.23 +import net.java.html.js.JavaScriptBody;
24.24 +import org.apidesign.html.json.spi.JSONCall;
24.25 +
24.26 +/** Communication with WebSockets for WebView 1.8.
24.27 + *
24.28 + * @author Jaroslav Tulach <jtulach@netbeans.org>
24.29 + */
24.30 +final class LoadWS {
24.31 + private static final boolean SUPPORTED = isWebSocket();
24.32 + private final Object ws;
24.33 + private final JSONCall call;
24.34 + LoadWS(JSONCall first, String url) {
24.35 + call = first;
24.36 + ws = initWebSocket(this, url);
24.37 + if (ws == null) {
24.38 + first.notifyError(new IllegalArgumentException("Wrong URL: " + url));
24.39 + }
24.40 + }
24.41 +
24.42 + static boolean isSupported() {
24.43 + return SUPPORTED;
24.44 + }
24.45 +
24.46 + void send(JSONCall call) {
24.47 + push(call);
24.48 + }
24.49 +
24.50 + private synchronized void push(JSONCall call) {
24.51 + send(ws, call.getMessage());
24.52 + }
24.53 +
24.54 + void onOpen(Object ev) {
24.55 + if (!call.isDoOutput()) {
24.56 + call.notifySuccess(null);
24.57 + }
24.58 + }
24.59 +
24.60 +
24.61 + @JavaScriptBody(args = { "data" }, body = "try {\n"
24.62 + + " return eval('(' + data + ')');\n"
24.63 + + " } catch (error) {;\n"
24.64 + + " return data;\n"
24.65 + + " }\n"
24.66 + )
24.67 + private static native Object toJSON(String data);
24.68 +
24.69 + void onMessage(Object ev, String data) {
24.70 + Object json = toJSON(data);
24.71 + call.notifySuccess(json);
24.72 + }
24.73 +
24.74 + void onError(Object ev) {
24.75 + call.notifyError(new Exception(ev.toString()));
24.76 + }
24.77 +
24.78 + void onClose(boolean wasClean, int code, String reason) {
24.79 + call.notifyError(null);
24.80 + }
24.81 +
24.82 + @JavaScriptBody(args = {}, body = "if (window.WebSocket) return true; else return false;")
24.83 + private static boolean isWebSocket() {
24.84 + return false;
24.85 + }
24.86 +
24.87 + @JavaScriptBody(args = { "back", "url" }, javacall = true, body = ""
24.88 + + "if (window.WebSocket) {\n"
24.89 + + " try {\n"
24.90 + + " var ws = new window.WebSocket(url);\n"
24.91 + + " ws.onopen = function(ev) {\n"
24.92 + + " back.@org.apidesign.bck2brwsr.ko2brwsr.LoadWS::onOpen(Ljava/lang/Object;)(ev);\n"
24.93 + + " };\n"
24.94 + + " ws.onmessage = function(ev) {\n"
24.95 + + " back.@org.apidesign.bck2brwsr.ko2brwsr.LoadWS::onMessage(Ljava/lang/Object;Ljava/lang/String;)(ev, ev.data);\n"
24.96 + + " };\n"
24.97 + + " ws.onerror = function(ev) {\n"
24.98 + + " back.@org.apidesign.bck2brwsr.ko2brwsr.LoadWS::onError(Ljava/lang/Object;)(ev);\n"
24.99 + + " };\n"
24.100 + + " ws.onclose = function(ev) {\n"
24.101 + + " back.@org.apidesign.bck2brwsr.ko2brwsr.LoadWS::onClose(ZILjava/lang/String;)(ev.wasClean, ev.code, ev.reason);\n"
24.102 + + " };\n"
24.103 + + " return ws;\n"
24.104 + + " } catch (ex) {\n"
24.105 + + " return null;\n"
24.106 + + " }\n"
24.107 + + "} else {\n"
24.108 + + " return null;\n"
24.109 + + "}\n"
24.110 + )
24.111 + private static Object initWebSocket(Object back, String url) {
24.112 + return null;
24.113 + }
24.114 +
24.115 +
24.116 + @JavaScriptBody(args = { "ws", "msg" }, body = ""
24.117 + + "ws.send(msg);"
24.118 + )
24.119 + private void send(Object ws, String msg) {
24.120 + }
24.121 +
24.122 + @JavaScriptBody(args = { "ws" }, body = "ws.close();")
24.123 + private static void close(Object ws) {
24.124 + }
24.125 +
24.126 + void close() {
24.127 + close(ws);
24.128 + }
24.129 +}
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
25.2 +++ b/ko/bck2brwsr/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout-2.2.1.js Sat Sep 07 18:28:09 2013 +0200
25.3 @@ -0,0 +1,3614 @@
25.4 +/*
25.5 + * HTML via Java(tm) Language Bindings
25.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
25.7 + *
25.8 + * This program is free software: you can redistribute it and/or modify
25.9 + * it under the terms of the GNU General Public License as published by
25.10 + * the Free Software Foundation, version 2 of the License.
25.11 + *
25.12 + * This program is distributed in the hope that it will be useful,
25.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
25.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25.15 + * GNU General Public License for more details. apidesign.org
25.16 + * designates this particular file as subject to the
25.17 + * "Classpath" exception as provided by apidesign.org
25.18 + * in the License file that accompanied this code.
25.19 + *
25.20 + * You should have received a copy of the GNU General Public License
25.21 + * along with this program. Look for COPYING file in the top folder.
25.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
25.23 + */
25.24 +// Knockout JavaScript library v2.2.1
25.25 +// (c) Steven Sanderson - http://knockoutjs.com/
25.26 +// License: MIT (http://www.opensource.org/licenses/mit-license.php)
25.27 +
25.28 +(function(){
25.29 +var DEBUG=true;
25.30 +(function(window,document,navigator,jQuery,undefined){
25.31 +!function(factory) {
25.32 + // Support three module loading scenarios
25.33 + if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
25.34 + // [1] CommonJS/Node.js
25.35 + var target = module['exports'] || exports; // module.exports is for Node.js
25.36 + factory(target);
25.37 + } else if (typeof define === 'function' && define['amd']) {
25.38 + // [2] AMD anonymous module
25.39 + define(['exports'], factory);
25.40 + } else {
25.41 + // [3] No module loader (plain <script> tag) - put directly in global namespace
25.42 + factory(window['ko'] = {});
25.43 + }
25.44 +}(function(koExports){
25.45 +// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
25.46 +// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
25.47 +var ko = typeof koExports !== 'undefined' ? koExports : {};
25.48 +// Google Closure Compiler helpers (used only to make the minified file smaller)
25.49 +ko.exportSymbol = function(koPath, object) {
25.50 + var tokens = koPath.split(".");
25.51 +
25.52 + // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
25.53 + // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
25.54 + var target = ko;
25.55 +
25.56 + for (var i = 0; i < tokens.length - 1; i++)
25.57 + target = target[tokens[i]];
25.58 + target[tokens[tokens.length - 1]] = object;
25.59 +};
25.60 +ko.exportProperty = function(owner, publicName, object) {
25.61 + owner[publicName] = object;
25.62 +};
25.63 +ko.version = "2.2.1";
25.64 +
25.65 +ko.exportSymbol('version', ko.version);
25.66 +ko.utils = new (function () {
25.67 + var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
25.68 +
25.69 + // 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)
25.70 + var knownEvents = {}, knownEventTypesByEventName = {};
25.71 + var keyEventTypeName = /Firefox\/2/i.test(navigator.userAgent) ? 'KeyboardEvent' : 'UIEvents';
25.72 + knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
25.73 + knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
25.74 + for (var eventType in knownEvents) {
25.75 + var knownEventsForType = knownEvents[eventType];
25.76 + if (knownEventsForType.length) {
25.77 + for (var i = 0, j = knownEventsForType.length; i < j; i++)
25.78 + knownEventTypesByEventName[knownEventsForType[i]] = eventType;
25.79 + }
25.80 + }
25.81 + var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
25.82 +
25.83 + // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
25.84 + // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
25.85 + // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
25.86 + // If there is a future need to detect specific versions of IE10+, we will amend this.
25.87 + var ieVersion = (function() {
25.88 + var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
25.89 +
25.90 + // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
25.91 + while (
25.92 + div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
25.93 + iElems[0]
25.94 + );
25.95 + return version > 4 ? version : undefined;
25.96 + }());
25.97 + var isIe6 = ieVersion === 6,
25.98 + isIe7 = ieVersion === 7;
25.99 +
25.100 + function isClickOnCheckableElement(element, eventType) {
25.101 + if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
25.102 + if (eventType.toLowerCase() != "click") return false;
25.103 + var inputType = element.type;
25.104 + return (inputType == "checkbox") || (inputType == "radio");
25.105 + }
25.106 +
25.107 + return {
25.108 + fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
25.109 +
25.110 + arrayForEach: function (array, action) {
25.111 + for (var i = 0, j = array.length; i < j; i++)
25.112 + action(array[i]);
25.113 + },
25.114 +
25.115 + arrayIndexOf: function (array, item) {
25.116 + if (typeof Array.prototype.indexOf == "function")
25.117 + return Array.prototype.indexOf.call(array, item);
25.118 + for (var i = 0, j = array.length; i < j; i++)
25.119 + if (array[i] === item)
25.120 + return i;
25.121 + return -1;
25.122 + },
25.123 +
25.124 + arrayFirst: function (array, predicate, predicateOwner) {
25.125 + for (var i = 0, j = array.length; i < j; i++)
25.126 + if (predicate.call(predicateOwner, array[i]))
25.127 + return array[i];
25.128 + return null;
25.129 + },
25.130 +
25.131 + arrayRemoveItem: function (array, itemToRemove) {
25.132 + var index = ko.utils.arrayIndexOf(array, itemToRemove);
25.133 + if (index >= 0)
25.134 + array.splice(index, 1);
25.135 + },
25.136 +
25.137 + arrayGetDistinctValues: function (array) {
25.138 + array = array || [];
25.139 + var result = [];
25.140 + for (var i = 0, j = array.length; i < j; i++) {
25.141 + if (ko.utils.arrayIndexOf(result, array[i]) < 0)
25.142 + result.push(array[i]);
25.143 + }
25.144 + return result;
25.145 + },
25.146 +
25.147 + arrayMap: function (array, mapping) {
25.148 + array = array || [];
25.149 + var result = [];
25.150 + for (var i = 0, j = array.length; i < j; i++)
25.151 + result.push(mapping(array[i]));
25.152 + return result;
25.153 + },
25.154 +
25.155 + arrayFilter: function (array, predicate) {
25.156 + array = array || [];
25.157 + var result = [];
25.158 + for (var i = 0, j = array.length; i < j; i++)
25.159 + if (predicate(array[i]))
25.160 + result.push(array[i]);
25.161 + return result;
25.162 + },
25.163 +
25.164 + arrayPushAll: function (array, valuesToPush) {
25.165 + if (valuesToPush instanceof Array)
25.166 + array.push.apply(array, valuesToPush);
25.167 + else
25.168 + for (var i = 0, j = valuesToPush.length; i < j; i++)
25.169 + array.push(valuesToPush[i]);
25.170 + return array;
25.171 + },
25.172 +
25.173 + extend: function (target, source) {
25.174 + if (source) {
25.175 + for(var prop in source) {
25.176 + if(source.hasOwnProperty(prop)) {
25.177 + target[prop] = source[prop];
25.178 + }
25.179 + }
25.180 + }
25.181 + return target;
25.182 + },
25.183 +
25.184 + emptyDomNode: function (domNode) {
25.185 + while (domNode.firstChild) {
25.186 + ko.removeNode(domNode.firstChild);
25.187 + }
25.188 + },
25.189 +
25.190 + moveCleanedNodesToContainerElement: function(nodes) {
25.191 + // Ensure it's a real array, as we're about to reparent the nodes and
25.192 + // we don't want the underlying collection to change while we're doing that.
25.193 + var nodesArray = ko.utils.makeArray(nodes);
25.194 +
25.195 + var container = document.createElement('div');
25.196 + for (var i = 0, j = nodesArray.length; i < j; i++) {
25.197 + container.appendChild(ko.cleanNode(nodesArray[i]));
25.198 + }
25.199 + return container;
25.200 + },
25.201 +
25.202 + cloneNodes: function (nodesArray, shouldCleanNodes) {
25.203 + for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
25.204 + var clonedNode = nodesArray[i].cloneNode(true);
25.205 + newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
25.206 + }
25.207 + return newNodesArray;
25.208 + },
25.209 +
25.210 + setDomNodeChildren: function (domNode, childNodes) {
25.211 + ko.utils.emptyDomNode(domNode);
25.212 + if (childNodes) {
25.213 + for (var i = 0, j = childNodes.length; i < j; i++)
25.214 + domNode.appendChild(childNodes[i]);
25.215 + }
25.216 + },
25.217 +
25.218 + replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
25.219 + var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
25.220 + if (nodesToReplaceArray.length > 0) {
25.221 + var insertionPoint = nodesToReplaceArray[0];
25.222 + var parent = insertionPoint.parentNode;
25.223 + for (var i = 0, j = newNodesArray.length; i < j; i++)
25.224 + parent.insertBefore(newNodesArray[i], insertionPoint);
25.225 + for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
25.226 + ko.removeNode(nodesToReplaceArray[i]);
25.227 + }
25.228 + }
25.229 + },
25.230 +
25.231 + setOptionNodeSelectionState: function (optionNode, isSelected) {
25.232 + // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
25.233 + if (ieVersion < 7)
25.234 + optionNode.setAttribute("selected", isSelected);
25.235 + else
25.236 + optionNode.selected = isSelected;
25.237 + },
25.238 +
25.239 + stringTrim: function (string) {
25.240 + return (string || "").replace(stringTrimRegex, "");
25.241 + },
25.242 +
25.243 + stringTokenize: function (string, delimiter) {
25.244 + var result = [];
25.245 + var tokens = (string || "").split(delimiter);
25.246 + for (var i = 0, j = tokens.length; i < j; i++) {
25.247 + var trimmed = ko.utils.stringTrim(tokens[i]);
25.248 + if (trimmed !== "")
25.249 + result.push(trimmed);
25.250 + }
25.251 + return result;
25.252 + },
25.253 +
25.254 + stringStartsWith: function (string, startsWith) {
25.255 + string = string || "";
25.256 + if (startsWith.length > string.length)
25.257 + return false;
25.258 + return string.substring(0, startsWith.length) === startsWith;
25.259 + },
25.260 +
25.261 + domNodeIsContainedBy: function (node, containedByNode) {
25.262 + if (containedByNode.compareDocumentPosition)
25.263 + return (containedByNode.compareDocumentPosition(node) & 16) == 16;
25.264 + while (node != null) {
25.265 + if (node == containedByNode)
25.266 + return true;
25.267 + node = node.parentNode;
25.268 + }
25.269 + return false;
25.270 + },
25.271 +
25.272 + domNodeIsAttachedToDocument: function (node) {
25.273 + return ko.utils.domNodeIsContainedBy(node, node.ownerDocument);
25.274 + },
25.275 +
25.276 + tagNameLower: function(element) {
25.277 + // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
25.278 + // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
25.279 + // we don't need to do the .toLowerCase() as it will always be lower case anyway.
25.280 + return element && element.tagName && element.tagName.toLowerCase();
25.281 + },
25.282 +
25.283 + registerEventHandler: function (element, eventType, handler) {
25.284 + var mustUseAttachEvent = ieVersion && eventsThatMustBeRegisteredUsingAttachEvent[eventType];
25.285 + if (!mustUseAttachEvent && typeof jQuery != "undefined") {
25.286 + if (isClickOnCheckableElement(element, eventType)) {
25.287 + // For click events on checkboxes, jQuery interferes with the event handling in an awkward way:
25.288 + // it toggles the element checked state *after* the click event handlers run, whereas native
25.289 + // click events toggle the checked state *before* the event handler.
25.290 + // Fix this by intecepting the handler and applying the correct checkedness before it runs.
25.291 + var originalHandler = handler;
25.292 + handler = function(event, eventData) {
25.293 + var jQuerySuppliedCheckedState = this.checked;
25.294 + if (eventData)
25.295 + this.checked = eventData.checkedStateBeforeEvent !== true;
25.296 + originalHandler.call(this, event);
25.297 + this.checked = jQuerySuppliedCheckedState; // Restore the state jQuery applied
25.298 + };
25.299 + }
25.300 + jQuery(element)['bind'](eventType, handler);
25.301 + } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
25.302 + element.addEventListener(eventType, handler, false);
25.303 + else if (typeof element.attachEvent != "undefined")
25.304 + element.attachEvent("on" + eventType, function (event) {
25.305 + handler.call(element, event);
25.306 + });
25.307 + else
25.308 + throw new Error("Browser doesn't support addEventListener or attachEvent");
25.309 + },
25.310 +
25.311 + triggerEvent: function (element, eventType) {
25.312 + if (!(element && element.nodeType))
25.313 + throw new Error("element must be a DOM node when calling triggerEvent");
25.314 +
25.315 + if (typeof jQuery != "undefined") {
25.316 + var eventData = [];
25.317 + if (isClickOnCheckableElement(element, eventType)) {
25.318 + // Work around the jQuery "click events on checkboxes" issue described above by storing the original checked state before triggering the handler
25.319 + eventData.push({ checkedStateBeforeEvent: element.checked });
25.320 + }
25.321 + jQuery(element)['trigger'](eventType, eventData);
25.322 + } else if (typeof document.createEvent == "function") {
25.323 + if (typeof element.dispatchEvent == "function") {
25.324 + var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
25.325 + var event = document.createEvent(eventCategory);
25.326 + event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
25.327 + element.dispatchEvent(event);
25.328 + }
25.329 + else
25.330 + throw new Error("The supplied element doesn't support dispatchEvent");
25.331 + } else if (typeof element.fireEvent != "undefined") {
25.332 + // Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
25.333 + // so to make it consistent, we'll do it manually here
25.334 + if (isClickOnCheckableElement(element, eventType))
25.335 + element.checked = element.checked !== true;
25.336 + element.fireEvent("on" + eventType);
25.337 + }
25.338 + else
25.339 + throw new Error("Browser doesn't support triggering events");
25.340 + },
25.341 +
25.342 + unwrapObservable: function (value) {
25.343 + return ko.isObservable(value) ? value() : value;
25.344 + },
25.345 +
25.346 + peekObservable: function (value) {
25.347 + return ko.isObservable(value) ? value.peek() : value;
25.348 + },
25.349 +
25.350 + toggleDomNodeCssClass: function (node, classNames, shouldHaveClass) {
25.351 + if (classNames) {
25.352 + var cssClassNameRegex = /[\w-]+/g,
25.353 + currentClassNames = node.className.match(cssClassNameRegex) || [];
25.354 + ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
25.355 + var indexOfClass = ko.utils.arrayIndexOf(currentClassNames, className);
25.356 + if (indexOfClass >= 0) {
25.357 + if (!shouldHaveClass)
25.358 + currentClassNames.splice(indexOfClass, 1);
25.359 + } else {
25.360 + if (shouldHaveClass)
25.361 + currentClassNames.push(className);
25.362 + }
25.363 + });
25.364 + node.className = currentClassNames.join(" ");
25.365 + }
25.366 + },
25.367 +
25.368 + setTextContent: function(element, textContent) {
25.369 + var value = ko.utils.unwrapObservable(textContent);
25.370 + if ((value === null) || (value === undefined))
25.371 + value = "";
25.372 +
25.373 + if (element.nodeType === 3) {
25.374 + element.data = value;
25.375 + } else {
25.376 + // We need there to be exactly one child: a text node.
25.377 + // If there are no children, more than one, or if it's not a text node,
25.378 + // we'll clear everything and create a single text node.
25.379 + var innerTextNode = ko.virtualElements.firstChild(element);
25.380 + if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
25.381 + ko.virtualElements.setDomNodeChildren(element, [document.createTextNode(value)]);
25.382 + } else {
25.383 + innerTextNode.data = value;
25.384 + }
25.385 +
25.386 + ko.utils.forceRefresh(element);
25.387 + }
25.388 + },
25.389 +
25.390 + setElementName: function(element, name) {
25.391 + element.name = name;
25.392 +
25.393 + // Workaround IE 6/7 issue
25.394 + // - https://github.com/SteveSanderson/knockout/issues/197
25.395 + // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
25.396 + if (ieVersion <= 7) {
25.397 + try {
25.398 + element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false);
25.399 + }
25.400 + catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
25.401 + }
25.402 + },
25.403 +
25.404 + forceRefresh: function(node) {
25.405 + // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
25.406 + if (ieVersion >= 9) {
25.407 + // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
25.408 + var elem = node.nodeType == 1 ? node : node.parentNode;
25.409 + if (elem.style)
25.410 + elem.style.zoom = elem.style.zoom;
25.411 + }
25.412 + },
25.413 +
25.414 + ensureSelectElementIsRenderedCorrectly: function(selectElement) {
25.415 + // 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.
25.416 + // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
25.417 + if (ieVersion >= 9) {
25.418 + var originalWidth = selectElement.style.width;
25.419 + selectElement.style.width = 0;
25.420 + selectElement.style.width = originalWidth;
25.421 + }
25.422 + },
25.423 +
25.424 + range: function (min, max) {
25.425 + min = ko.utils.unwrapObservable(min);
25.426 + max = ko.utils.unwrapObservable(max);
25.427 + var result = [];
25.428 + for (var i = min; i <= max; i++)
25.429 + result.push(i);
25.430 + return result;
25.431 + },
25.432 +
25.433 + makeArray: function(arrayLikeObject) {
25.434 + var result = [];
25.435 + for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
25.436 + result.push(arrayLikeObject[i]);
25.437 + };
25.438 + return result;
25.439 + },
25.440 +
25.441 + isIe6 : isIe6,
25.442 + isIe7 : isIe7,
25.443 + ieVersion : ieVersion,
25.444 +
25.445 + getFormFields: function(form, fieldName) {
25.446 + var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
25.447 + var isMatchingField = (typeof fieldName == 'string')
25.448 + ? function(field) { return field.name === fieldName }
25.449 + : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
25.450 + var matches = [];
25.451 + for (var i = fields.length - 1; i >= 0; i--) {
25.452 + if (isMatchingField(fields[i]))
25.453 + matches.push(fields[i]);
25.454 + };
25.455 + return matches;
25.456 + },
25.457 +
25.458 + parseJson: function (jsonString) {
25.459 + if (typeof jsonString == "string") {
25.460 + jsonString = ko.utils.stringTrim(jsonString);
25.461 + if (jsonString) {
25.462 + if (window.JSON && window.JSON.parse) // Use native parsing where available
25.463 + return window.JSON.parse(jsonString);
25.464 + return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
25.465 + }
25.466 + }
25.467 + return null;
25.468 + },
25.469 +
25.470 + stringifyJson: function (data, replacer, space) { // replacer and space are optional
25.471 + if ((typeof JSON == "undefined") || (typeof JSON.stringify == "undefined"))
25.472 + 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");
25.473 + return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
25.474 + },
25.475 +
25.476 + postJson: function (urlOrForm, data, options) {
25.477 + options = options || {};
25.478 + var params = options['params'] || {};
25.479 + var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
25.480 + var url = urlOrForm;
25.481 +
25.482 + // If we were given a form, use its 'action' URL and pick out any requested field values
25.483 + if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
25.484 + var originalForm = urlOrForm;
25.485 + url = originalForm.action;
25.486 + for (var i = includeFields.length - 1; i >= 0; i--) {
25.487 + var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
25.488 + for (var j = fields.length - 1; j >= 0; j--)
25.489 + params[fields[j].name] = fields[j].value;
25.490 + }
25.491 + }
25.492 +
25.493 + data = ko.utils.unwrapObservable(data);
25.494 + var form = document.createElement("form");
25.495 + form.style.display = "none";
25.496 + form.action = url;
25.497 + form.method = "post";
25.498 + for (var key in data) {
25.499 + var input = document.createElement("input");
25.500 + input.name = key;
25.501 + input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
25.502 + form.appendChild(input);
25.503 + }
25.504 + for (var key in params) {
25.505 + var input = document.createElement("input");
25.506 + input.name = key;
25.507 + input.value = params[key];
25.508 + form.appendChild(input);
25.509 + }
25.510 + document.body.appendChild(form);
25.511 + options['submitter'] ? options['submitter'](form) : form.submit();
25.512 + setTimeout(function () { form.parentNode.removeChild(form); }, 0);
25.513 + }
25.514 + }
25.515 +})();
25.516 +
25.517 +ko.exportSymbol('utils', ko.utils);
25.518 +ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
25.519 +ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
25.520 +ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
25.521 +ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
25.522 +ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
25.523 +ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
25.524 +ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
25.525 +ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
25.526 +ko.exportSymbol('utils.extend', ko.utils.extend);
25.527 +ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
25.528 +ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
25.529 +ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
25.530 +ko.exportSymbol('utils.postJson', ko.utils.postJson);
25.531 +ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
25.532 +ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
25.533 +ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
25.534 +ko.exportSymbol('utils.range', ko.utils.range);
25.535 +ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
25.536 +ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
25.537 +ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
25.538 +
25.539 +if (!Function.prototype['bind']) {
25.540 + // 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)
25.541 + // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
25.542 + Function.prototype['bind'] = function (object) {
25.543 + var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
25.544 + return function () {
25.545 + return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
25.546 + };
25.547 + };
25.548 +}
25.549 +
25.550 +ko.utils.domData = new (function () {
25.551 + var uniqueId = 0;
25.552 + var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
25.553 + var dataStore = {};
25.554 + return {
25.555 + get: function (node, key) {
25.556 + var allDataForNode = ko.utils.domData.getAll(node, false);
25.557 + return allDataForNode === undefined ? undefined : allDataForNode[key];
25.558 + },
25.559 + set: function (node, key, value) {
25.560 + if (value === undefined) {
25.561 + // Make sure we don't actually create a new domData key if we are actually deleting a value
25.562 + if (ko.utils.domData.getAll(node, false) === undefined)
25.563 + return;
25.564 + }
25.565 + var allDataForNode = ko.utils.domData.getAll(node, true);
25.566 + allDataForNode[key] = value;
25.567 + },
25.568 + getAll: function (node, createIfNotFound) {
25.569 + var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
25.570 + var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
25.571 + if (!hasExistingDataStore) {
25.572 + if (!createIfNotFound)
25.573 + return undefined;
25.574 + dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
25.575 + dataStore[dataStoreKey] = {};
25.576 + }
25.577 + return dataStore[dataStoreKey];
25.578 + },
25.579 + clear: function (node) {
25.580 + var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
25.581 + if (dataStoreKey) {
25.582 + delete dataStore[dataStoreKey];
25.583 + node[dataStoreKeyExpandoPropertyName] = null;
25.584 + return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
25.585 + }
25.586 + return false;
25.587 + }
25.588 + }
25.589 +})();
25.590 +
25.591 +ko.exportSymbol('utils.domData', ko.utils.domData);
25.592 +ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully
25.593 +
25.594 +ko.utils.domNodeDisposal = new (function () {
25.595 + var domDataKey = "__ko_domNodeDisposal__" + (new Date).getTime();
25.596 + var cleanableNodeTypes = { 1: true, 8: true, 9: true }; // Element, Comment, Document
25.597 + var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
25.598 +
25.599 + function getDisposeCallbacksCollection(node, createIfNotFound) {
25.600 + var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
25.601 + if ((allDisposeCallbacks === undefined) && createIfNotFound) {
25.602 + allDisposeCallbacks = [];
25.603 + ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
25.604 + }
25.605 + return allDisposeCallbacks;
25.606 + }
25.607 + function destroyCallbacksCollection(node) {
25.608 + ko.utils.domData.set(node, domDataKey, undefined);
25.609 + }
25.610 +
25.611 + function cleanSingleNode(node) {
25.612 + // Run all the dispose callbacks
25.613 + var callbacks = getDisposeCallbacksCollection(node, false);
25.614 + if (callbacks) {
25.615 + callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
25.616 + for (var i = 0; i < callbacks.length; i++)
25.617 + callbacks[i](node);
25.618 + }
25.619 +
25.620 + // Also erase the DOM data
25.621 + ko.utils.domData.clear(node);
25.622 +
25.623 + // Special support for jQuery here because it's so commonly used.
25.624 + // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
25.625 + // so notify it to tear down any resources associated with the node & descendants here.
25.626 + if ((typeof jQuery == "function") && (typeof jQuery['cleanData'] == "function"))
25.627 + jQuery['cleanData']([node]);
25.628 +
25.629 + // Also clear any immediate-child comment nodes, as these wouldn't have been found by
25.630 + // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
25.631 + if (cleanableNodeTypesWithDescendants[node.nodeType])
25.632 + cleanImmediateCommentTypeChildren(node);
25.633 + }
25.634 +
25.635 + function cleanImmediateCommentTypeChildren(nodeWithChildren) {
25.636 + var child, nextChild = nodeWithChildren.firstChild;
25.637 + while (child = nextChild) {
25.638 + nextChild = child.nextSibling;
25.639 + if (child.nodeType === 8)
25.640 + cleanSingleNode(child);
25.641 + }
25.642 + }
25.643 +
25.644 + return {
25.645 + addDisposeCallback : function(node, callback) {
25.646 + if (typeof callback != "function")
25.647 + throw new Error("Callback must be a function");
25.648 + getDisposeCallbacksCollection(node, true).push(callback);
25.649 + },
25.650 +
25.651 + removeDisposeCallback : function(node, callback) {
25.652 + var callbacksCollection = getDisposeCallbacksCollection(node, false);
25.653 + if (callbacksCollection) {
25.654 + ko.utils.arrayRemoveItem(callbacksCollection, callback);
25.655 + if (callbacksCollection.length == 0)
25.656 + destroyCallbacksCollection(node);
25.657 + }
25.658 + },
25.659 +
25.660 + cleanNode : function(node) {
25.661 + // First clean this node, where applicable
25.662 + if (cleanableNodeTypes[node.nodeType]) {
25.663 + cleanSingleNode(node);
25.664 +
25.665 + // ... then its descendants, where applicable
25.666 + if (cleanableNodeTypesWithDescendants[node.nodeType]) {
25.667 + // Clone the descendants list in case it changes during iteration
25.668 + var descendants = [];
25.669 + ko.utils.arrayPushAll(descendants, node.getElementsByTagName("*"));
25.670 + for (var i = 0, j = descendants.length; i < j; i++)
25.671 + cleanSingleNode(descendants[i]);
25.672 + }
25.673 + }
25.674 + return node;
25.675 + },
25.676 +
25.677 + removeNode : function(node) {
25.678 + ko.cleanNode(node);
25.679 + if (node.parentNode)
25.680 + node.parentNode.removeChild(node);
25.681 + }
25.682 + }
25.683 +})();
25.684 +ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
25.685 +ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
25.686 +ko.exportSymbol('cleanNode', ko.cleanNode);
25.687 +ko.exportSymbol('removeNode', ko.removeNode);
25.688 +ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
25.689 +ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
25.690 +ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
25.691 +(function () {
25.692 + var leadingCommentRegex = /^(\s*)<!--(.*?)-->/;
25.693 +
25.694 + function simpleHtmlParse(html) {
25.695 + // Based on jQuery's "clean" function, but only accounting for table-related elements.
25.696 + // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
25.697 +
25.698 + // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
25.699 + // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
25.700 + // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
25.701 + // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
25.702 +
25.703 + // Trim whitespace, otherwise indexOf won't work as expected
25.704 + var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
25.705 +
25.706 + // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
25.707 + var wrap = tags.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] ||
25.708 + !tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] ||
25.709 + (!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
25.710 + /* anything else */ [0, "", ""];
25.711 +
25.712 + // Go to html and back, then peel off extra wrappers
25.713 + // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
25.714 + var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
25.715 + if (typeof window['innerShiv'] == "function") {
25.716 + div.appendChild(window['innerShiv'](markup));
25.717 + } else {
25.718 + div.innerHTML = markup;
25.719 + }
25.720 +
25.721 + // Move to the right depth
25.722 + while (wrap[0]--)
25.723 + div = div.lastChild;
25.724 +
25.725 + return ko.utils.makeArray(div.lastChild.childNodes);
25.726 + }
25.727 +
25.728 + function jQueryHtmlParse(html) {
25.729 + // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
25.730 + if (jQuery['parseHTML']) {
25.731 + return jQuery['parseHTML'](html);
25.732 + } else {
25.733 + // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
25.734 + var elems = jQuery['clean']([html]);
25.735 +
25.736 + // 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.
25.737 + // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
25.738 + // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
25.739 + if (elems && elems[0]) {
25.740 + // Find the top-most parent element that's a direct child of a document fragment
25.741 + var elem = elems[0];
25.742 + while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
25.743 + elem = elem.parentNode;
25.744 + // ... then detach it
25.745 + if (elem.parentNode)
25.746 + elem.parentNode.removeChild(elem);
25.747 + }
25.748 +
25.749 + return elems;
25.750 + }
25.751 + }
25.752 +
25.753 + ko.utils.parseHtmlFragment = function(html) {
25.754 + return typeof jQuery != 'undefined' ? jQueryHtmlParse(html) // As below, benefit from jQuery's optimisations where possible
25.755 + : simpleHtmlParse(html); // ... otherwise, this simple logic will do in most common cases.
25.756 + };
25.757 +
25.758 + ko.utils.setHtml = function(node, html) {
25.759 + ko.utils.emptyDomNode(node);
25.760 +
25.761 + // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
25.762 + html = ko.utils.unwrapObservable(html);
25.763 +
25.764 + if ((html !== null) && (html !== undefined)) {
25.765 + if (typeof html != 'string')
25.766 + html = html.toString();
25.767 +
25.768 + // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
25.769 + // for example <tr> elements which are not normally allowed to exist on their own.
25.770 + // If you've referenced jQuery we'll use that rather than duplicating its code.
25.771 + if (typeof jQuery != 'undefined') {
25.772 + jQuery(node)['html'](html);
25.773 + } else {
25.774 + // ... otherwise, use KO's own parsing logic.
25.775 + var parsedNodes = ko.utils.parseHtmlFragment(html);
25.776 + for (var i = 0; i < parsedNodes.length; i++)
25.777 + node.appendChild(parsedNodes[i]);
25.778 + }
25.779 + }
25.780 + };
25.781 +})();
25.782 +
25.783 +ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
25.784 +ko.exportSymbol('utils.setHtml', ko.utils.setHtml);
25.785 +
25.786 +ko.memoization = (function () {
25.787 + var memos = {};
25.788 +
25.789 + function randomMax8HexChars() {
25.790 + return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
25.791 + }
25.792 + function generateRandomId() {
25.793 + return randomMax8HexChars() + randomMax8HexChars();
25.794 + }
25.795 + function findMemoNodes(rootNode, appendToArray) {
25.796 + if (!rootNode)
25.797 + return;
25.798 + if (rootNode.nodeType == 8) {
25.799 + var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
25.800 + if (memoId != null)
25.801 + appendToArray.push({ domNode: rootNode, memoId: memoId });
25.802 + } else if (rootNode.nodeType == 1) {
25.803 + for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
25.804 + findMemoNodes(childNodes[i], appendToArray);
25.805 + }
25.806 + }
25.807 +
25.808 + return {
25.809 + memoize: function (callback) {
25.810 + if (typeof callback != "function")
25.811 + throw new Error("You can only pass a function to ko.memoization.memoize()");
25.812 + var memoId = generateRandomId();
25.813 + memos[memoId] = callback;
25.814 + return "<!--[ko_memo:" + memoId + "]-->";
25.815 + },
25.816 +
25.817 + unmemoize: function (memoId, callbackParams) {
25.818 + var callback = memos[memoId];
25.819 + if (callback === undefined)
25.820 + throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
25.821 + try {
25.822 + callback.apply(null, callbackParams || []);
25.823 + return true;
25.824 + }
25.825 + finally { delete memos[memoId]; }
25.826 + },
25.827 +
25.828 + unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
25.829 + var memos = [];
25.830 + findMemoNodes(domNode, memos);
25.831 + for (var i = 0, j = memos.length; i < j; i++) {
25.832 + var node = memos[i].domNode;
25.833 + var combinedParams = [node];
25.834 + if (extraCallbackParamsArray)
25.835 + ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
25.836 + ko.memoization.unmemoize(memos[i].memoId, combinedParams);
25.837 + node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
25.838 + if (node.parentNode)
25.839 + 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)
25.840 + }
25.841 + },
25.842 +
25.843 + parseMemoText: function (memoText) {
25.844 + var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
25.845 + return match ? match[1] : null;
25.846 + }
25.847 + };
25.848 +})();
25.849 +
25.850 +ko.exportSymbol('memoization', ko.memoization);
25.851 +ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
25.852 +ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
25.853 +ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
25.854 +ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
25.855 +ko.extenders = {
25.856 + 'throttle': function(target, timeout) {
25.857 + // Throttling means two things:
25.858 +
25.859 + // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
25.860 + // notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
25.861 + target['throttleEvaluation'] = timeout;
25.862 +
25.863 + // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
25.864 + // so the target cannot change value synchronously or faster than a certain rate
25.865 + var writeTimeoutInstance = null;
25.866 + return ko.dependentObservable({
25.867 + 'read': target,
25.868 + 'write': function(value) {
25.869 + clearTimeout(writeTimeoutInstance);
25.870 + writeTimeoutInstance = setTimeout(function() {
25.871 + target(value);
25.872 + }, timeout);
25.873 + }
25.874 + });
25.875 + },
25.876 +
25.877 + 'notify': function(target, notifyWhen) {
25.878 + target["equalityComparer"] = notifyWhen == "always"
25.879 + ? function() { return false } // Treat all values as not equal
25.880 + : ko.observable["fn"]["equalityComparer"];
25.881 + return target;
25.882 + }
25.883 +};
25.884 +
25.885 +function applyExtenders(requestedExtenders) {
25.886 + var target = this;
25.887 + if (requestedExtenders) {
25.888 + for (var key in requestedExtenders) {
25.889 + var extenderHandler = ko.extenders[key];
25.890 + if (typeof extenderHandler == 'function') {
25.891 + target = extenderHandler(target, requestedExtenders[key]);
25.892 + }
25.893 + }
25.894 + }
25.895 + return target;
25.896 +}
25.897 +
25.898 +ko.exportSymbol('extenders', ko.extenders);
25.899 +
25.900 +ko.subscription = function (target, callback, disposeCallback) {
25.901 + this.target = target;
25.902 + this.callback = callback;
25.903 + this.disposeCallback = disposeCallback;
25.904 + ko.exportProperty(this, 'dispose', this.dispose);
25.905 +};
25.906 +ko.subscription.prototype.dispose = function () {
25.907 + this.isDisposed = true;
25.908 + this.disposeCallback();
25.909 +};
25.910 +
25.911 +ko.subscribable = function () {
25.912 + this._subscriptions = {};
25.913 +
25.914 + ko.utils.extend(this, ko.subscribable['fn']);
25.915 + ko.exportProperty(this, 'subscribe', this.subscribe);
25.916 + ko.exportProperty(this, 'extend', this.extend);
25.917 + ko.exportProperty(this, 'getSubscriptionsCount', this.getSubscriptionsCount);
25.918 +}
25.919 +
25.920 +var defaultEvent = "change";
25.921 +
25.922 +ko.subscribable['fn'] = {
25.923 + subscribe: function (callback, callbackTarget, event) {
25.924 + event = event || defaultEvent;
25.925 + var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
25.926 +
25.927 + var subscription = new ko.subscription(this, boundCallback, function () {
25.928 + ko.utils.arrayRemoveItem(this._subscriptions[event], subscription);
25.929 + }.bind(this));
25.930 +
25.931 + if (!this._subscriptions[event])
25.932 + this._subscriptions[event] = [];
25.933 + this._subscriptions[event].push(subscription);
25.934 + return subscription;
25.935 + },
25.936 +
25.937 + "notifySubscribers": function (valueToNotify, event) {
25.938 + event = event || defaultEvent;
25.939 + if (this._subscriptions[event]) {
25.940 + ko.dependencyDetection.ignore(function() {
25.941 + ko.utils.arrayForEach(this._subscriptions[event].slice(0), function (subscription) {
25.942 + // In case a subscription was disposed during the arrayForEach cycle, check
25.943 + // for isDisposed on each subscription before invoking its callback
25.944 + if (subscription && (subscription.isDisposed !== true))
25.945 + subscription.callback(valueToNotify);
25.946 + });
25.947 + }, this);
25.948 + }
25.949 + },
25.950 +
25.951 + getSubscriptionsCount: function () {
25.952 + var total = 0;
25.953 + for (var eventName in this._subscriptions) {
25.954 + if (this._subscriptions.hasOwnProperty(eventName))
25.955 + total += this._subscriptions[eventName].length;
25.956 + }
25.957 + return total;
25.958 + },
25.959 +
25.960 + extend: applyExtenders
25.961 +};
25.962 +
25.963 +
25.964 +ko.isSubscribable = function (instance) {
25.965 + return typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
25.966 +};
25.967 +
25.968 +ko.exportSymbol('subscribable', ko.subscribable);
25.969 +ko.exportSymbol('isSubscribable', ko.isSubscribable);
25.970 +
25.971 +ko.dependencyDetection = (function () {
25.972 + var _frames = [];
25.973 +
25.974 + return {
25.975 + begin: function (callback) {
25.976 + _frames.push({ callback: callback, distinctDependencies:[] });
25.977 + },
25.978 +
25.979 + end: function () {
25.980 + _frames.pop();
25.981 + },
25.982 +
25.983 + registerDependency: function (subscribable) {
25.984 + if (!ko.isSubscribable(subscribable))
25.985 + throw new Error("Only subscribable things can act as dependencies");
25.986 + if (_frames.length > 0) {
25.987 + var topFrame = _frames[_frames.length - 1];
25.988 + if (!topFrame || ko.utils.arrayIndexOf(topFrame.distinctDependencies, subscribable) >= 0)
25.989 + return;
25.990 + topFrame.distinctDependencies.push(subscribable);
25.991 + topFrame.callback(subscribable);
25.992 + }
25.993 + },
25.994 +
25.995 + ignore: function(callback, callbackTarget, callbackArgs) {
25.996 + try {
25.997 + _frames.push(null);
25.998 + return callback.apply(callbackTarget, callbackArgs || []);
25.999 + } finally {
25.1000 + _frames.pop();
25.1001 + }
25.1002 + }
25.1003 + };
25.1004 +})();
25.1005 +var primitiveTypes = { 'undefined':true, 'boolean':true, 'number':true, 'string':true };
25.1006 +
25.1007 +ko.observable = function (initialValue) {
25.1008 + var _latestValue = initialValue;
25.1009 +
25.1010 + function observable() {
25.1011 + if (arguments.length > 0) {
25.1012 + // Write
25.1013 +
25.1014 + // Ignore writes if the value hasn't changed
25.1015 + if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {
25.1016 + observable.valueWillMutate();
25.1017 + _latestValue = arguments[0];
25.1018 + if (DEBUG) observable._latestValue = _latestValue;
25.1019 + observable.valueHasMutated();
25.1020 + }
25.1021 + return this; // Permits chained assignments
25.1022 + }
25.1023 + else {
25.1024 + // Read
25.1025 + ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
25.1026 + return _latestValue;
25.1027 + }
25.1028 + }
25.1029 + if (DEBUG) observable._latestValue = _latestValue;
25.1030 + ko.subscribable.call(observable);
25.1031 + observable.peek = function() { return _latestValue };
25.1032 + observable.valueHasMutated = function () { observable["notifySubscribers"](_latestValue); }
25.1033 + observable.valueWillMutate = function () { observable["notifySubscribers"](_latestValue, "beforeChange"); }
25.1034 + ko.utils.extend(observable, ko.observable['fn']);
25.1035 +
25.1036 + ko.exportProperty(observable, 'peek', observable.peek);
25.1037 + ko.exportProperty(observable, "valueHasMutated", observable.valueHasMutated);
25.1038 + ko.exportProperty(observable, "valueWillMutate", observable.valueWillMutate);
25.1039 +
25.1040 + return observable;
25.1041 +}
25.1042 +
25.1043 +ko.observable['fn'] = {
25.1044 + "equalityComparer": function valuesArePrimitiveAndEqual(a, b) {
25.1045 + var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
25.1046 + return oldValueIsPrimitive ? (a === b) : false;
25.1047 + }
25.1048 +};
25.1049 +
25.1050 +var protoProperty = ko.observable.protoProperty = "__ko_proto__";
25.1051 +ko.observable['fn'][protoProperty] = ko.observable;
25.1052 +
25.1053 +ko.hasPrototype = function(instance, prototype) {
25.1054 + if ((instance === null) || (instance === undefined) || (instance[protoProperty] === undefined)) return false;
25.1055 + if (instance[protoProperty] === prototype) return true;
25.1056 + return ko.hasPrototype(instance[protoProperty], prototype); // Walk the prototype chain
25.1057 +};
25.1058 +
25.1059 +ko.isObservable = function (instance) {
25.1060 + return ko.hasPrototype(instance, ko.observable);
25.1061 +}
25.1062 +ko.isWriteableObservable = function (instance) {
25.1063 + // Observable
25.1064 + if ((typeof instance == "function") && instance[protoProperty] === ko.observable)
25.1065 + return true;
25.1066 + // Writeable dependent observable
25.1067 + if ((typeof instance == "function") && (instance[protoProperty] === ko.dependentObservable) && (instance.hasWriteFunction))
25.1068 + return true;
25.1069 + // Anything else
25.1070 + return false;
25.1071 +}
25.1072 +
25.1073 +
25.1074 +ko.exportSymbol('observable', ko.observable);
25.1075 +ko.exportSymbol('isObservable', ko.isObservable);
25.1076 +ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
25.1077 +ko.observableArray = function (initialValues) {
25.1078 + if (arguments.length == 0) {
25.1079 + // Zero-parameter constructor initializes to empty array
25.1080 + initialValues = [];
25.1081 + }
25.1082 + if ((initialValues !== null) && (initialValues !== undefined) && !('length' in initialValues))
25.1083 + throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");
25.1084 +
25.1085 + var result = ko.observable(initialValues);
25.1086 + ko.utils.extend(result, ko.observableArray['fn']);
25.1087 + return result;
25.1088 +}
25.1089 +
25.1090 +ko.observableArray['fn'] = {
25.1091 + 'remove': function (valueOrPredicate) {
25.1092 + var underlyingArray = this.peek();
25.1093 + var removedValues = [];
25.1094 + var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
25.1095 + for (var i = 0; i < underlyingArray.length; i++) {
25.1096 + var value = underlyingArray[i];
25.1097 + if (predicate(value)) {
25.1098 + if (removedValues.length === 0) {
25.1099 + this.valueWillMutate();
25.1100 + }
25.1101 + removedValues.push(value);
25.1102 + underlyingArray.splice(i, 1);
25.1103 + i--;
25.1104 + }
25.1105 + }
25.1106 + if (removedValues.length) {
25.1107 + this.valueHasMutated();
25.1108 + }
25.1109 + return removedValues;
25.1110 + },
25.1111 +
25.1112 + 'removeAll': function (arrayOfValues) {
25.1113 + // If you passed zero args, we remove everything
25.1114 + if (arrayOfValues === undefined) {
25.1115 + var underlyingArray = this.peek();
25.1116 + var allValues = underlyingArray.slice(0);
25.1117 + this.valueWillMutate();
25.1118 + underlyingArray.splice(0, underlyingArray.length);
25.1119 + this.valueHasMutated();
25.1120 + return allValues;
25.1121 + }
25.1122 + // If you passed an arg, we interpret it as an array of entries to remove
25.1123 + if (!arrayOfValues)
25.1124 + return [];
25.1125 + return this['remove'](function (value) {
25.1126 + return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
25.1127 + });
25.1128 + },
25.1129 +
25.1130 + 'destroy': function (valueOrPredicate) {
25.1131 + var underlyingArray = this.peek();
25.1132 + var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
25.1133 + this.valueWillMutate();
25.1134 + for (var i = underlyingArray.length - 1; i >= 0; i--) {
25.1135 + var value = underlyingArray[i];
25.1136 + if (predicate(value))
25.1137 + underlyingArray[i]["_destroy"] = true;
25.1138 + }
25.1139 + this.valueHasMutated();
25.1140 + },
25.1141 +
25.1142 + 'destroyAll': function (arrayOfValues) {
25.1143 + // If you passed zero args, we destroy everything
25.1144 + if (arrayOfValues === undefined)
25.1145 + return this['destroy'](function() { return true });
25.1146 +
25.1147 + // If you passed an arg, we interpret it as an array of entries to destroy
25.1148 + if (!arrayOfValues)
25.1149 + return [];
25.1150 + return this['destroy'](function (value) {
25.1151 + return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
25.1152 + });
25.1153 + },
25.1154 +
25.1155 + 'indexOf': function (item) {
25.1156 + var underlyingArray = this();
25.1157 + return ko.utils.arrayIndexOf(underlyingArray, item);
25.1158 + },
25.1159 +
25.1160 + 'replace': function(oldItem, newItem) {
25.1161 + var index = this['indexOf'](oldItem);
25.1162 + if (index >= 0) {
25.1163 + this.valueWillMutate();
25.1164 + this.peek()[index] = newItem;
25.1165 + this.valueHasMutated();
25.1166 + }
25.1167 + }
25.1168 +}
25.1169 +
25.1170 +// Populate ko.observableArray.fn with read/write functions from native arrays
25.1171 +// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
25.1172 +// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
25.1173 +ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
25.1174 + ko.observableArray['fn'][methodName] = function () {
25.1175 + // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
25.1176 + // (for consistency with mutating regular observables)
25.1177 + var underlyingArray = this.peek();
25.1178 + this.valueWillMutate();
25.1179 + var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
25.1180 + this.valueHasMutated();
25.1181 + return methodCallResult;
25.1182 + };
25.1183 +});
25.1184 +
25.1185 +// Populate ko.observableArray.fn with read-only functions from native arrays
25.1186 +ko.utils.arrayForEach(["slice"], function (methodName) {
25.1187 + ko.observableArray['fn'][methodName] = function () {
25.1188 + var underlyingArray = this();
25.1189 + return underlyingArray[methodName].apply(underlyingArray, arguments);
25.1190 + };
25.1191 +});
25.1192 +
25.1193 +ko.exportSymbol('observableArray', ko.observableArray);
25.1194 +ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
25.1195 + var _latestValue,
25.1196 + _hasBeenEvaluated = false,
25.1197 + _isBeingEvaluated = false,
25.1198 + readFunction = evaluatorFunctionOrOptions;
25.1199 +
25.1200 + if (readFunction && typeof readFunction == "object") {
25.1201 + // Single-parameter syntax - everything is on this "options" param
25.1202 + options = readFunction;
25.1203 + readFunction = options["read"];
25.1204 + } else {
25.1205 + // Multi-parameter syntax - construct the options according to the params passed
25.1206 + options = options || {};
25.1207 + if (!readFunction)
25.1208 + readFunction = options["read"];
25.1209 + }
25.1210 + if (typeof readFunction != "function")
25.1211 + throw new Error("Pass a function that returns the value of the ko.computed");
25.1212 +
25.1213 + function addSubscriptionToDependency(subscribable) {
25.1214 + _subscriptionsToDependencies.push(subscribable.subscribe(evaluatePossiblyAsync));
25.1215 + }
25.1216 +
25.1217 + function disposeAllSubscriptionsToDependencies() {
25.1218 + ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
25.1219 + subscription.dispose();
25.1220 + });
25.1221 + _subscriptionsToDependencies = [];
25.1222 + }
25.1223 +
25.1224 + function evaluatePossiblyAsync() {
25.1225 + var throttleEvaluationTimeout = dependentObservable['throttleEvaluation'];
25.1226 + if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
25.1227 + clearTimeout(evaluationTimeoutInstance);
25.1228 + evaluationTimeoutInstance = setTimeout(evaluateImmediate, throttleEvaluationTimeout);
25.1229 + } else
25.1230 + evaluateImmediate();
25.1231 + }
25.1232 +
25.1233 + function evaluateImmediate() {
25.1234 + if (_isBeingEvaluated) {
25.1235 + // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
25.1236 + // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
25.1237 + // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
25.1238 + // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
25.1239 + return;
25.1240 + }
25.1241 +
25.1242 + // Don't dispose on first evaluation, because the "disposeWhen" callback might
25.1243 + // e.g., dispose when the associated DOM element isn't in the doc, and it's not
25.1244 + // going to be in the doc until *after* the first evaluation
25.1245 + if (_hasBeenEvaluated && disposeWhen()) {
25.1246 + dispose();
25.1247 + return;
25.1248 + }
25.1249 +
25.1250 + _isBeingEvaluated = true;
25.1251 + try {
25.1252 + // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
25.1253 + // Then, during evaluation, we cross off any that are in fact still being used.
25.1254 + var disposalCandidates = ko.utils.arrayMap(_subscriptionsToDependencies, function(item) {return item.target;});
25.1255 +
25.1256 + ko.dependencyDetection.begin(function(subscribable) {
25.1257 + var inOld;
25.1258 + if ((inOld = ko.utils.arrayIndexOf(disposalCandidates, subscribable)) >= 0)
25.1259 + disposalCandidates[inOld] = undefined; // Don't want to dispose this subscription, as it's still being used
25.1260 + else
25.1261 + addSubscriptionToDependency(subscribable); // Brand new subscription - add it
25.1262 + });
25.1263 +
25.1264 + var newValue = readFunction.call(evaluatorFunctionTarget);
25.1265 +
25.1266 + // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
25.1267 + for (var i = disposalCandidates.length - 1; i >= 0; i--) {
25.1268 + if (disposalCandidates[i])
25.1269 + _subscriptionsToDependencies.splice(i, 1)[0].dispose();
25.1270 + }
25.1271 + _hasBeenEvaluated = true;
25.1272 +
25.1273 + dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
25.1274 + _latestValue = newValue;
25.1275 + if (DEBUG) dependentObservable._latestValue = _latestValue;
25.1276 + } finally {
25.1277 + ko.dependencyDetection.end();
25.1278 + }
25.1279 +
25.1280 + dependentObservable["notifySubscribers"](_latestValue);
25.1281 + _isBeingEvaluated = false;
25.1282 + if (!_subscriptionsToDependencies.length)
25.1283 + dispose();
25.1284 + }
25.1285 +
25.1286 + function dependentObservable() {
25.1287 + if (arguments.length > 0) {
25.1288 + if (typeof writeFunction === "function") {
25.1289 + // Writing a value
25.1290 + writeFunction.apply(evaluatorFunctionTarget, arguments);
25.1291 + } else {
25.1292 + 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.");
25.1293 + }
25.1294 + return this; // Permits chained assignments
25.1295 + } else {
25.1296 + // Reading the value
25.1297 + if (!_hasBeenEvaluated)
25.1298 + evaluateImmediate();
25.1299 + ko.dependencyDetection.registerDependency(dependentObservable);
25.1300 + return _latestValue;
25.1301 + }
25.1302 + }
25.1303 +
25.1304 + function peek() {
25.1305 + if (!_hasBeenEvaluated)
25.1306 + evaluateImmediate();
25.1307 + return _latestValue;
25.1308 + }
25.1309 +
25.1310 + function isActive() {
25.1311 + return !_hasBeenEvaluated || _subscriptionsToDependencies.length > 0;
25.1312 + }
25.1313 +
25.1314 + // By here, "options" is always non-null
25.1315 + var writeFunction = options["write"],
25.1316 + disposeWhenNodeIsRemoved = options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
25.1317 + disposeWhen = options["disposeWhen"] || options.disposeWhen || function() { return false; },
25.1318 + dispose = disposeAllSubscriptionsToDependencies,
25.1319 + _subscriptionsToDependencies = [],
25.1320 + evaluationTimeoutInstance = null;
25.1321 +
25.1322 + if (!evaluatorFunctionTarget)
25.1323 + evaluatorFunctionTarget = options["owner"];
25.1324 +
25.1325 + dependentObservable.peek = peek;
25.1326 + dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; };
25.1327 + dependentObservable.hasWriteFunction = typeof options["write"] === "function";
25.1328 + dependentObservable.dispose = function () { dispose(); };
25.1329 + dependentObservable.isActive = isActive;
25.1330 + dependentObservable.valueHasMutated = function() {
25.1331 + _hasBeenEvaluated = false;
25.1332 + evaluateImmediate();
25.1333 + };
25.1334 +
25.1335 + ko.subscribable.call(dependentObservable);
25.1336 + ko.utils.extend(dependentObservable, ko.dependentObservable['fn']);
25.1337 +
25.1338 + ko.exportProperty(dependentObservable, 'peek', dependentObservable.peek);
25.1339 + ko.exportProperty(dependentObservable, 'dispose', dependentObservable.dispose);
25.1340 + ko.exportProperty(dependentObservable, 'isActive', dependentObservable.isActive);
25.1341 + ko.exportProperty(dependentObservable, 'getDependenciesCount', dependentObservable.getDependenciesCount);
25.1342 +
25.1343 + // Evaluate, unless deferEvaluation is true
25.1344 + if (options['deferEvaluation'] !== true)
25.1345 + evaluateImmediate();
25.1346 +
25.1347 + // Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values.
25.1348 + // But skip if isActive is false (there will never be any dependencies to dispose).
25.1349 + // (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
25.1350 + // plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
25.1351 + if (disposeWhenNodeIsRemoved && isActive()) {
25.1352 + dispose = function() {
25.1353 + ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, arguments.callee);
25.1354 + disposeAllSubscriptionsToDependencies();
25.1355 + };
25.1356 + ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, dispose);
25.1357 + var existingDisposeWhenFunction = disposeWhen;
25.1358 + disposeWhen = function () {
25.1359 + return !ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) || existingDisposeWhenFunction();
25.1360 + }
25.1361 + }
25.1362 +
25.1363 + return dependentObservable;
25.1364 +};
25.1365 +
25.1366 +ko.isComputed = function(instance) {
25.1367 + return ko.hasPrototype(instance, ko.dependentObservable);
25.1368 +};
25.1369 +
25.1370 +var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
25.1371 +ko.dependentObservable[protoProp] = ko.observable;
25.1372 +
25.1373 +ko.dependentObservable['fn'] = {};
25.1374 +ko.dependentObservable['fn'][protoProp] = ko.dependentObservable;
25.1375 +
25.1376 +ko.exportSymbol('dependentObservable', ko.dependentObservable);
25.1377 +ko.exportSymbol('computed', ko.dependentObservable); // Make "ko.computed" an alias for "ko.dependentObservable"
25.1378 +ko.exportSymbol('isComputed', ko.isComputed);
25.1379 +
25.1380 +(function() {
25.1381 + var maxNestedObservableDepth = 10; // Escape the (unlikely) pathalogical case where an observable's current value is itself (or similar reference cycle)
25.1382 +
25.1383 + ko.toJS = function(rootObject) {
25.1384 + if (arguments.length == 0)
25.1385 + throw new Error("When calling ko.toJS, pass the object you want to convert.");
25.1386 +
25.1387 + // We just unwrap everything at every level in the object graph
25.1388 + return mapJsObjectGraph(rootObject, function(valueToMap) {
25.1389 + // Loop because an observable's value might in turn be another observable wrapper
25.1390 + for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
25.1391 + valueToMap = valueToMap();
25.1392 + return valueToMap;
25.1393 + });
25.1394 + };
25.1395 +
25.1396 + ko.toJSON = function(rootObject, replacer, space) { // replacer and space are optional
25.1397 + var plainJavaScriptObject = ko.toJS(rootObject);
25.1398 + return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
25.1399 + };
25.1400 +
25.1401 + function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
25.1402 + visitedObjects = visitedObjects || new objectLookup();
25.1403 +
25.1404 + rootObject = mapInputCallback(rootObject);
25.1405 + var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof Date));
25.1406 + if (!canHaveProperties)
25.1407 + return rootObject;
25.1408 +
25.1409 + var outputProperties = rootObject instanceof Array ? [] : {};
25.1410 + visitedObjects.save(rootObject, outputProperties);
25.1411 +
25.1412 + visitPropertiesOrArrayEntries(rootObject, function(indexer) {
25.1413 + var propertyValue = mapInputCallback(rootObject[indexer]);
25.1414 +
25.1415 + switch (typeof propertyValue) {
25.1416 + case "boolean":
25.1417 + case "number":
25.1418 + case "string":
25.1419 + case "function":
25.1420 + outputProperties[indexer] = propertyValue;
25.1421 + break;
25.1422 + case "object":
25.1423 + case "undefined":
25.1424 + var previouslyMappedValue = visitedObjects.get(propertyValue);
25.1425 + outputProperties[indexer] = (previouslyMappedValue !== undefined)
25.1426 + ? previouslyMappedValue
25.1427 + : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
25.1428 + break;
25.1429 + }
25.1430 + });
25.1431 +
25.1432 + return outputProperties;
25.1433 + }
25.1434 +
25.1435 + function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
25.1436 + if (rootObject instanceof Array) {
25.1437 + for (var i = 0; i < rootObject.length; i++)
25.1438 + visitorCallback(i);
25.1439 +
25.1440 + // For arrays, also respect toJSON property for custom mappings (fixes #278)
25.1441 + if (typeof rootObject['toJSON'] == 'function')
25.1442 + visitorCallback('toJSON');
25.1443 + } else {
25.1444 + for (var propertyName in rootObject)
25.1445 + visitorCallback(propertyName);
25.1446 + }
25.1447 + };
25.1448 +
25.1449 + function objectLookup() {
25.1450 + var keys = [];
25.1451 + var values = [];
25.1452 + this.save = function(key, value) {
25.1453 + var existingIndex = ko.utils.arrayIndexOf(keys, key);
25.1454 + if (existingIndex >= 0)
25.1455 + values[existingIndex] = value;
25.1456 + else {
25.1457 + keys.push(key);
25.1458 + values.push(value);
25.1459 + }
25.1460 + };
25.1461 + this.get = function(key) {
25.1462 + var existingIndex = ko.utils.arrayIndexOf(keys, key);
25.1463 + return (existingIndex >= 0) ? values[existingIndex] : undefined;
25.1464 + };
25.1465 + };
25.1466 +})();
25.1467 +
25.1468 +ko.exportSymbol('toJS', ko.toJS);
25.1469 +ko.exportSymbol('toJSON', ko.toJSON);
25.1470 +(function () {
25.1471 + var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';
25.1472 +
25.1473 + // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
25.1474 + // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
25.1475 + // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
25.1476 + ko.selectExtensions = {
25.1477 + readValue : function(element) {
25.1478 + switch (ko.utils.tagNameLower(element)) {
25.1479 + case 'option':
25.1480 + if (element[hasDomDataExpandoProperty] === true)
25.1481 + return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
25.1482 + return ko.utils.ieVersion <= 7
25.1483 + ? (element.getAttributeNode('value').specified ? element.value : element.text)
25.1484 + : element.value;
25.1485 + case 'select':
25.1486 + return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
25.1487 + default:
25.1488 + return element.value;
25.1489 + }
25.1490 + },
25.1491 +
25.1492 + writeValue: function(element, value) {
25.1493 + switch (ko.utils.tagNameLower(element)) {
25.1494 + case 'option':
25.1495 + switch(typeof value) {
25.1496 + case "string":
25.1497 + ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
25.1498 + if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
25.1499 + delete element[hasDomDataExpandoProperty];
25.1500 + }
25.1501 + element.value = value;
25.1502 + break;
25.1503 + default:
25.1504 + // Store arbitrary object using DomData
25.1505 + ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
25.1506 + element[hasDomDataExpandoProperty] = true;
25.1507 +
25.1508 + // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
25.1509 + element.value = typeof value === "number" ? value : "";
25.1510 + break;
25.1511 + }
25.1512 + break;
25.1513 + case 'select':
25.1514 + for (var i = element.options.length - 1; i >= 0; i--) {
25.1515 + if (ko.selectExtensions.readValue(element.options[i]) == value) {
25.1516 + element.selectedIndex = i;
25.1517 + break;
25.1518 + }
25.1519 + }
25.1520 + break;
25.1521 + default:
25.1522 + if ((value === null) || (value === undefined))
25.1523 + value = "";
25.1524 + element.value = value;
25.1525 + break;
25.1526 + }
25.1527 + }
25.1528 + };
25.1529 +})();
25.1530 +
25.1531 +ko.exportSymbol('selectExtensions', ko.selectExtensions);
25.1532 +ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
25.1533 +ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
25.1534 +ko.expressionRewriting = (function () {
25.1535 + var restoreCapturedTokensRegex = /\@ko_token_(\d+)\@/g;
25.1536 + var javaScriptReservedWords = ["true", "false"];
25.1537 +
25.1538 + // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
25.1539 + // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
25.1540 + var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;
25.1541 +
25.1542 + function restoreTokens(string, tokens) {
25.1543 + var prevValue = null;
25.1544 + while (string != prevValue) { // Keep restoring tokens until it no longer makes a difference (they may be nested)
25.1545 + prevValue = string;
25.1546 + string = string.replace(restoreCapturedTokensRegex, function (match, tokenIndex) {
25.1547 + return tokens[tokenIndex];
25.1548 + });
25.1549 + }
25.1550 + return string;
25.1551 + }
25.1552 +
25.1553 + function getWriteableValue(expression) {
25.1554 + if (ko.utils.arrayIndexOf(javaScriptReservedWords, ko.utils.stringTrim(expression).toLowerCase()) >= 0)
25.1555 + return false;
25.1556 + var match = expression.match(javaScriptAssignmentTarget);
25.1557 + return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
25.1558 + }
25.1559 +
25.1560 + function ensureQuoted(key) {
25.1561 + var trimmedKey = ko.utils.stringTrim(key);
25.1562 + switch (trimmedKey.length && trimmedKey.charAt(0)) {
25.1563 + case "'":
25.1564 + case '"':
25.1565 + return key;
25.1566 + default:
25.1567 + return "'" + trimmedKey + "'";
25.1568 + }
25.1569 + }
25.1570 +
25.1571 + return {
25.1572 + bindingRewriteValidators: [],
25.1573 +
25.1574 + parseObjectLiteral: function(objectLiteralString) {
25.1575 + // A full tokeniser+lexer would add too much weight to this library, so here's a simple parser
25.1576 + // that is sufficient just to split an object literal string into a set of top-level key-value pairs
25.1577 +
25.1578 + var str = ko.utils.stringTrim(objectLiteralString);
25.1579 + if (str.length < 3)
25.1580 + return [];
25.1581 + if (str.charAt(0) === "{")// Ignore any braces surrounding the whole object literal
25.1582 + str = str.substring(1, str.length - 1);
25.1583 +
25.1584 + // Pull out any string literals and regex literals
25.1585 + var tokens = [];
25.1586 + var tokenStart = null, tokenEndChar;
25.1587 + for (var position = 0; position < str.length; position++) {
25.1588 + var c = str.charAt(position);
25.1589 + if (tokenStart === null) {
25.1590 + switch (c) {
25.1591 + case '"':
25.1592 + case "'":
25.1593 + case "/":
25.1594 + tokenStart = position;
25.1595 + tokenEndChar = c;
25.1596 + break;
25.1597 + }
25.1598 + } else if ((c == tokenEndChar) && (str.charAt(position - 1) !== "\\")) {
25.1599 + var token = str.substring(tokenStart, position + 1);
25.1600 + tokens.push(token);
25.1601 + var replacement = "@ko_token_" + (tokens.length - 1) + "@";
25.1602 + str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
25.1603 + position -= (token.length - replacement.length);
25.1604 + tokenStart = null;
25.1605 + }
25.1606 + }
25.1607 +
25.1608 + // Next pull out balanced paren, brace, and bracket blocks
25.1609 + tokenStart = null;
25.1610 + tokenEndChar = null;
25.1611 + var tokenDepth = 0, tokenStartChar = null;
25.1612 + for (var position = 0; position < str.length; position++) {
25.1613 + var c = str.charAt(position);
25.1614 + if (tokenStart === null) {
25.1615 + switch (c) {
25.1616 + case "{": tokenStart = position; tokenStartChar = c;
25.1617 + tokenEndChar = "}";
25.1618 + break;
25.1619 + case "(": tokenStart = position; tokenStartChar = c;
25.1620 + tokenEndChar = ")";
25.1621 + break;
25.1622 + case "[": tokenStart = position; tokenStartChar = c;
25.1623 + tokenEndChar = "]";
25.1624 + break;
25.1625 + }
25.1626 + }
25.1627 +
25.1628 + if (c === tokenStartChar)
25.1629 + tokenDepth++;
25.1630 + else if (c === tokenEndChar) {
25.1631 + tokenDepth--;
25.1632 + if (tokenDepth === 0) {
25.1633 + var token = str.substring(tokenStart, position + 1);
25.1634 + tokens.push(token);
25.1635 + var replacement = "@ko_token_" + (tokens.length - 1) + "@";
25.1636 + str = str.substring(0, tokenStart) + replacement + str.substring(position + 1);
25.1637 + position -= (token.length - replacement.length);
25.1638 + tokenStart = null;
25.1639 + }
25.1640 + }
25.1641 + }
25.1642 +
25.1643 + // Now we can safely split on commas to get the key/value pairs
25.1644 + var result = [];
25.1645 + var keyValuePairs = str.split(",");
25.1646 + for (var i = 0, j = keyValuePairs.length; i < j; i++) {
25.1647 + var pair = keyValuePairs[i];
25.1648 + var colonPos = pair.indexOf(":");
25.1649 + if ((colonPos > 0) && (colonPos < pair.length - 1)) {
25.1650 + var key = pair.substring(0, colonPos);
25.1651 + var value = pair.substring(colonPos + 1);
25.1652 + result.push({ 'key': restoreTokens(key, tokens), 'value': restoreTokens(value, tokens) });
25.1653 + } else {
25.1654 + result.push({ 'unknown': restoreTokens(pair, tokens) });
25.1655 + }
25.1656 + }
25.1657 + return result;
25.1658 + },
25.1659 +
25.1660 + preProcessBindings: function (objectLiteralStringOrKeyValueArray) {
25.1661 + var keyValueArray = typeof objectLiteralStringOrKeyValueArray === "string"
25.1662 + ? ko.expressionRewriting.parseObjectLiteral(objectLiteralStringOrKeyValueArray)
25.1663 + : objectLiteralStringOrKeyValueArray;
25.1664 + var resultStrings = [], propertyAccessorResultStrings = [];
25.1665 +
25.1666 + var keyValueEntry;
25.1667 + for (var i = 0; keyValueEntry = keyValueArray[i]; i++) {
25.1668 + if (resultStrings.length > 0)
25.1669 + resultStrings.push(",");
25.1670 +
25.1671 + if (keyValueEntry['key']) {
25.1672 + var quotedKey = ensureQuoted(keyValueEntry['key']), val = keyValueEntry['value'];
25.1673 + resultStrings.push(quotedKey);
25.1674 + resultStrings.push(":");
25.1675 + resultStrings.push(val);
25.1676 +
25.1677 + if (val = getWriteableValue(ko.utils.stringTrim(val))) {
25.1678 + if (propertyAccessorResultStrings.length > 0)
25.1679 + propertyAccessorResultStrings.push(", ");
25.1680 + propertyAccessorResultStrings.push(quotedKey + " : function(__ko_value) { " + val + " = __ko_value; }");
25.1681 + }
25.1682 + } else if (keyValueEntry['unknown']) {
25.1683 + resultStrings.push(keyValueEntry['unknown']);
25.1684 + }
25.1685 + }
25.1686 +
25.1687 + var combinedResult = resultStrings.join("");
25.1688 + if (propertyAccessorResultStrings.length > 0) {
25.1689 + var allPropertyAccessors = propertyAccessorResultStrings.join("");
25.1690 + combinedResult = combinedResult + ", '_ko_property_writers' : { " + allPropertyAccessors + " } ";
25.1691 + }
25.1692 +
25.1693 + return combinedResult;
25.1694 + },
25.1695 +
25.1696 + keyValueArrayContainsKey: function(keyValueArray, key) {
25.1697 + for (var i = 0; i < keyValueArray.length; i++)
25.1698 + if (ko.utils.stringTrim(keyValueArray[i]['key']) == key)
25.1699 + return true;
25.1700 + return false;
25.1701 + },
25.1702 +
25.1703 + // Internal, private KO utility for updating model properties from within bindings
25.1704 + // property: If the property being updated is (or might be) an observable, pass it here
25.1705 + // If it turns out to be a writable observable, it will be written to directly
25.1706 + // allBindingsAccessor: All bindings in the current execution context.
25.1707 + // This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
25.1708 + // key: The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
25.1709 + // value: The value to be written
25.1710 + // checkIfDifferent: If true, and if the property being written is a writable observable, the value will only be written if
25.1711 + // it is !== existing value on that writable observable
25.1712 + writeValueToProperty: function(property, allBindingsAccessor, key, value, checkIfDifferent) {
25.1713 + if (!property || !ko.isWriteableObservable(property)) {
25.1714 + var propWriters = allBindingsAccessor()['_ko_property_writers'];
25.1715 + if (propWriters && propWriters[key])
25.1716 + propWriters[key](value);
25.1717 + } else if (!checkIfDifferent || property.peek() !== value) {
25.1718 + property(value);
25.1719 + }
25.1720 + }
25.1721 + };
25.1722 +})();
25.1723 +
25.1724 +ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
25.1725 +ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
25.1726 +ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
25.1727 +ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);
25.1728 +
25.1729 +// For backward compatibility, define the following aliases. (Previously, these function names were misleading because
25.1730 +// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
25.1731 +ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
25.1732 +ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);(function() {
25.1733 + // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
25.1734 + // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
25.1735 + // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
25.1736 + // of that virtual hierarchy
25.1737 + //
25.1738 + // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
25.1739 + // without having to scatter special cases all over the binding and templating code.
25.1740 +
25.1741 + // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
25.1742 + // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
25.1743 + // So, use node.text where available, and node.nodeValue elsewhere
25.1744 + var commentNodesHaveTextProperty = document.createComment("test").text === "<!--test-->";
25.1745 +
25.1746 + var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*-->$/ : /^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/;
25.1747 + var endCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
25.1748 + var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
25.1749 +
25.1750 + function isStartComment(node) {
25.1751 + return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
25.1752 + }
25.1753 +
25.1754 + function isEndComment(node) {
25.1755 + return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(endCommentRegex);
25.1756 + }
25.1757 +
25.1758 + function getVirtualChildren(startComment, allowUnbalanced) {
25.1759 + var currentNode = startComment;
25.1760 + var depth = 1;
25.1761 + var children = [];
25.1762 + while (currentNode = currentNode.nextSibling) {
25.1763 + if (isEndComment(currentNode)) {
25.1764 + depth--;
25.1765 + if (depth === 0)
25.1766 + return children;
25.1767 + }
25.1768 +
25.1769 + children.push(currentNode);
25.1770 +
25.1771 + if (isStartComment(currentNode))
25.1772 + depth++;
25.1773 + }
25.1774 + if (!allowUnbalanced)
25.1775 + throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
25.1776 + return null;
25.1777 + }
25.1778 +
25.1779 + function getMatchingEndComment(startComment, allowUnbalanced) {
25.1780 + var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
25.1781 + if (allVirtualChildren) {
25.1782 + if (allVirtualChildren.length > 0)
25.1783 + return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
25.1784 + return startComment.nextSibling;
25.1785 + } else
25.1786 + return null; // Must have no matching end comment, and allowUnbalanced is true
25.1787 + }
25.1788 +
25.1789 + function getUnbalancedChildTags(node) {
25.1790 + // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
25.1791 + // from <div>OK</div><!-- /ko --><!-- /ko -->, returns: <!-- /ko --><!-- /ko -->
25.1792 + var childNode = node.firstChild, captureRemaining = null;
25.1793 + if (childNode) {
25.1794 + do {
25.1795 + if (captureRemaining) // We already hit an unbalanced node and are now just scooping up all subsequent nodes
25.1796 + captureRemaining.push(childNode);
25.1797 + else if (isStartComment(childNode)) {
25.1798 + var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
25.1799 + if (matchingEndComment) // It's a balanced tag, so skip immediately to the end of this virtual set
25.1800 + childNode = matchingEndComment;
25.1801 + else
25.1802 + captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
25.1803 + } else if (isEndComment(childNode)) {
25.1804 + captureRemaining = [childNode]; // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
25.1805 + }
25.1806 + } while (childNode = childNode.nextSibling);
25.1807 + }
25.1808 + return captureRemaining;
25.1809 + }
25.1810 +
25.1811 + ko.virtualElements = {
25.1812 + allowedBindings: {},
25.1813 +
25.1814 + childNodes: function(node) {
25.1815 + return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
25.1816 + },
25.1817 +
25.1818 + emptyNode: function(node) {
25.1819 + if (!isStartComment(node))
25.1820 + ko.utils.emptyDomNode(node);
25.1821 + else {
25.1822 + var virtualChildren = ko.virtualElements.childNodes(node);
25.1823 + for (var i = 0, j = virtualChildren.length; i < j; i++)
25.1824 + ko.removeNode(virtualChildren[i]);
25.1825 + }
25.1826 + },
25.1827 +
25.1828 + setDomNodeChildren: function(node, childNodes) {
25.1829 + if (!isStartComment(node))
25.1830 + ko.utils.setDomNodeChildren(node, childNodes);
25.1831 + else {
25.1832 + ko.virtualElements.emptyNode(node);
25.1833 + var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
25.1834 + for (var i = 0, j = childNodes.length; i < j; i++)
25.1835 + endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
25.1836 + }
25.1837 + },
25.1838 +
25.1839 + prepend: function(containerNode, nodeToPrepend) {
25.1840 + if (!isStartComment(containerNode)) {
25.1841 + if (containerNode.firstChild)
25.1842 + containerNode.insertBefore(nodeToPrepend, containerNode.firstChild);
25.1843 + else
25.1844 + containerNode.appendChild(nodeToPrepend);
25.1845 + } else {
25.1846 + // Start comments must always have a parent and at least one following sibling (the end comment)
25.1847 + containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
25.1848 + }
25.1849 + },
25.1850 +
25.1851 + insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
25.1852 + if (!insertAfterNode) {
25.1853 + ko.virtualElements.prepend(containerNode, nodeToInsert);
25.1854 + } else if (!isStartComment(containerNode)) {
25.1855 + // Insert after insertion point
25.1856 + if (insertAfterNode.nextSibling)
25.1857 + containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
25.1858 + else
25.1859 + containerNode.appendChild(nodeToInsert);
25.1860 + } else {
25.1861 + // Children of start comments must always have a parent and at least one following sibling (the end comment)
25.1862 + containerNode.parentNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
25.1863 + }
25.1864 + },
25.1865 +
25.1866 + firstChild: function(node) {
25.1867 + if (!isStartComment(node))
25.1868 + return node.firstChild;
25.1869 + if (!node.nextSibling || isEndComment(node.nextSibling))
25.1870 + return null;
25.1871 + return node.nextSibling;
25.1872 + },
25.1873 +
25.1874 + nextSibling: function(node) {
25.1875 + if (isStartComment(node))
25.1876 + node = getMatchingEndComment(node);
25.1877 + if (node.nextSibling && isEndComment(node.nextSibling))
25.1878 + return null;
25.1879 + return node.nextSibling;
25.1880 + },
25.1881 +
25.1882 + virtualNodeBindingValue: function(node) {
25.1883 + var regexMatch = isStartComment(node);
25.1884 + return regexMatch ? regexMatch[1] : null;
25.1885 + },
25.1886 +
25.1887 + normaliseVirtualElementDomStructure: function(elementVerified) {
25.1888 + // Workaround for https://github.com/SteveSanderson/knockout/issues/155
25.1889 + // (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
25.1890 + // that are direct descendants of <ul> into the preceding <li>)
25.1891 + if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
25.1892 + return;
25.1893 +
25.1894 + // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
25.1895 + // must be intended to appear *after* that child, so move them there.
25.1896 + var childNode = elementVerified.firstChild;
25.1897 + if (childNode) {
25.1898 + do {
25.1899 + if (childNode.nodeType === 1) {
25.1900 + var unbalancedTags = getUnbalancedChildTags(childNode);
25.1901 + if (unbalancedTags) {
25.1902 + // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
25.1903 + var nodeToInsertBefore = childNode.nextSibling;
25.1904 + for (var i = 0; i < unbalancedTags.length; i++) {
25.1905 + if (nodeToInsertBefore)
25.1906 + elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
25.1907 + else
25.1908 + elementVerified.appendChild(unbalancedTags[i]);
25.1909 + }
25.1910 + }
25.1911 + }
25.1912 + } while (childNode = childNode.nextSibling);
25.1913 + }
25.1914 + }
25.1915 + };
25.1916 +})();
25.1917 +ko.exportSymbol('virtualElements', ko.virtualElements);
25.1918 +ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
25.1919 +ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
25.1920 +//ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild); // firstChild is not minified
25.1921 +ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
25.1922 +//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling); // nextSibling is not minified
25.1923 +ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
25.1924 +ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
25.1925 +(function() {
25.1926 + var defaultBindingAttributeName = "data-bind";
25.1927 +
25.1928 + ko.bindingProvider = function() {
25.1929 + this.bindingCache = {};
25.1930 + };
25.1931 +
25.1932 + ko.utils.extend(ko.bindingProvider.prototype, {
25.1933 + 'nodeHasBindings': function(node) {
25.1934 + switch (node.nodeType) {
25.1935 + case 1: return node.getAttribute(defaultBindingAttributeName) != null; // Element
25.1936 + case 8: return ko.virtualElements.virtualNodeBindingValue(node) != null; // Comment node
25.1937 + default: return false;
25.1938 + }
25.1939 + },
25.1940 +
25.1941 + 'getBindings': function(node, bindingContext) {
25.1942 + var bindingsString = this['getBindingsString'](node, bindingContext);
25.1943 + return bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
25.1944 + },
25.1945 +
25.1946 + // The following function is only used internally by this default provider.
25.1947 + // It's not part of the interface definition for a general binding provider.
25.1948 + 'getBindingsString': function(node, bindingContext) {
25.1949 + switch (node.nodeType) {
25.1950 + case 1: return node.getAttribute(defaultBindingAttributeName); // Element
25.1951 + case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
25.1952 + default: return null;
25.1953 + }
25.1954 + },
25.1955 +
25.1956 + // The following function is only used internally by this default provider.
25.1957 + // It's not part of the interface definition for a general binding provider.
25.1958 + 'parseBindingsString': function(bindingsString, bindingContext, node) {
25.1959 + try {
25.1960 + var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache);
25.1961 + return bindingFunction(bindingContext, node);
25.1962 + } catch (ex) {
25.1963 + throw new Error("Unable to parse bindings.\nMessage: " + ex + ";\nBindings value: " + bindingsString);
25.1964 + }
25.1965 + }
25.1966 + });
25.1967 +
25.1968 + ko.bindingProvider['instance'] = new ko.bindingProvider();
25.1969 +
25.1970 + function createBindingsStringEvaluatorViaCache(bindingsString, cache) {
25.1971 + var cacheKey = bindingsString;
25.1972 + return cache[cacheKey]
25.1973 + || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString));
25.1974 + }
25.1975 +
25.1976 + function createBindingsStringEvaluator(bindingsString) {
25.1977 + // Build the source for a function that evaluates "expression"
25.1978 + // For each scope variable, add an extra level of "with" nesting
25.1979 + // Example result: with(sc1) { with(sc0) { return (expression) } }
25.1980 + var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString),
25.1981 + functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
25.1982 + return new Function("$context", "$element", functionBody);
25.1983 + }
25.1984 +})();
25.1985 +
25.1986 +ko.exportSymbol('bindingProvider', ko.bindingProvider);
25.1987 +(function () {
25.1988 + ko.bindingHandlers = {};
25.1989 +
25.1990 + ko.bindingContext = function(dataItem, parentBindingContext, dataItemAlias) {
25.1991 + if (parentBindingContext) {
25.1992 + ko.utils.extend(this, parentBindingContext); // Inherit $root and any custom properties
25.1993 + this['$parentContext'] = parentBindingContext;
25.1994 + this['$parent'] = parentBindingContext['$data'];
25.1995 + this['$parents'] = (parentBindingContext['$parents'] || []).slice(0);
25.1996 + this['$parents'].unshift(this['$parent']);
25.1997 + } else {
25.1998 + this['$parents'] = [];
25.1999 + this['$root'] = dataItem;
25.2000 + // Export 'ko' in the binding context so it will be available in bindings and templates
25.2001 + // even if 'ko' isn't exported as a global, such as when using an AMD loader.
25.2002 + // See https://github.com/SteveSanderson/knockout/issues/490
25.2003 + this['ko'] = ko;
25.2004 + }
25.2005 + this['$data'] = dataItem;
25.2006 + if (dataItemAlias)
25.2007 + this[dataItemAlias] = dataItem;
25.2008 + }
25.2009 + ko.bindingContext.prototype['createChildContext'] = function (dataItem, dataItemAlias) {
25.2010 + return new ko.bindingContext(dataItem, this, dataItemAlias);
25.2011 + };
25.2012 + ko.bindingContext.prototype['extend'] = function(properties) {
25.2013 + var clone = ko.utils.extend(new ko.bindingContext(), this);
25.2014 + return ko.utils.extend(clone, properties);
25.2015 + };
25.2016 +
25.2017 + function validateThatBindingIsAllowedForVirtualElements(bindingName) {
25.2018 + var validator = ko.virtualElements.allowedBindings[bindingName];
25.2019 + if (!validator)
25.2020 + throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
25.2021 + }
25.2022 +
25.2023 + function applyBindingsToDescendantsInternal (viewModel, elementOrVirtualElement, bindingContextsMayDifferFromDomParentElement) {
25.2024 + var currentChild, nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
25.2025 + while (currentChild = nextInQueue) {
25.2026 + // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
25.2027 + nextInQueue = ko.virtualElements.nextSibling(currentChild);
25.2028 + applyBindingsToNodeAndDescendantsInternal(viewModel, currentChild, bindingContextsMayDifferFromDomParentElement);
25.2029 + }
25.2030 + }
25.2031 +
25.2032 + function applyBindingsToNodeAndDescendantsInternal (viewModel, nodeVerified, bindingContextMayDifferFromDomParentElement) {
25.2033 + var shouldBindDescendants = true;
25.2034 +
25.2035 + // Perf optimisation: Apply bindings only if...
25.2036 + // (1) We need to store the binding context on this node (because it may differ from the DOM parent node's binding context)
25.2037 + // Note that we can't store binding contexts on non-elements (e.g., text nodes), as IE doesn't allow expando properties for those
25.2038 + // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
25.2039 + var isElement = (nodeVerified.nodeType === 1);
25.2040 + if (isElement) // Workaround IE <= 8 HTML parsing weirdness
25.2041 + ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);
25.2042 +
25.2043 + var shouldApplyBindings = (isElement && bindingContextMayDifferFromDomParentElement) // Case (1)
25.2044 + || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified); // Case (2)
25.2045 + if (shouldApplyBindings)
25.2046 + shouldBindDescendants = applyBindingsToNodeInternal(nodeVerified, null, viewModel, bindingContextMayDifferFromDomParentElement).shouldBindDescendants;
25.2047 +
25.2048 + if (shouldBindDescendants) {
25.2049 + // We're recursing automatically into (real or virtual) child nodes without changing binding contexts. So,
25.2050 + // * For children of a *real* element, the binding context is certainly the same as on their DOM .parentNode,
25.2051 + // hence bindingContextsMayDifferFromDomParentElement is false
25.2052 + // * For children of a *virtual* element, we can't be sure. Evaluating .parentNode on those children may
25.2053 + // skip over any number of intermediate virtual elements, any of which might define a custom binding context,
25.2054 + // hence bindingContextsMayDifferFromDomParentElement is true
25.2055 + applyBindingsToDescendantsInternal(viewModel, nodeVerified, /* bindingContextsMayDifferFromDomParentElement: */ !isElement);
25.2056 + }
25.2057 + }
25.2058 +
25.2059 + function applyBindingsToNodeInternal (node, bindings, viewModelOrBindingContext, bindingContextMayDifferFromDomParentElement) {
25.2060 + // Need to be sure that inits are only run once, and updates never run until all the inits have been run
25.2061 + var initPhase = 0; // 0 = before all inits, 1 = during inits, 2 = after all inits
25.2062 +
25.2063 + // Each time the dependentObservable is evaluated (after data changes),
25.2064 + // the binding attribute is reparsed so that it can pick out the correct
25.2065 + // model properties in the context of the changed data.
25.2066 + // DOM event callbacks need to be able to access this changed data,
25.2067 + // so we need a single parsedBindings variable (shared by all callbacks
25.2068 + // associated with this node's bindings) that all the closures can access.
25.2069 + var parsedBindings;
25.2070 + function makeValueAccessor(bindingKey) {
25.2071 + return function () { return parsedBindings[bindingKey] }
25.2072 + }
25.2073 + function parsedBindingsAccessor() {
25.2074 + return parsedBindings;
25.2075 + }
25.2076 +
25.2077 + var bindingHandlerThatControlsDescendantBindings;
25.2078 + ko.dependentObservable(
25.2079 + function () {
25.2080 + // Ensure we have a nonnull binding context to work with
25.2081 + var bindingContextInstance = viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
25.2082 + ? viewModelOrBindingContext
25.2083 + : new ko.bindingContext(ko.utils.unwrapObservable(viewModelOrBindingContext));
25.2084 + var viewModel = bindingContextInstance['$data'];
25.2085 +
25.2086 + // Optimization: Don't store the binding context on this node if it's definitely the same as on node.parentNode, because
25.2087 + // we can easily recover it just by scanning up the node's ancestors in the DOM
25.2088 + // (note: here, parent node means "real DOM parent" not "virtual parent", as there's no O(1) way to find the virtual parent)
25.2089 + if (bindingContextMayDifferFromDomParentElement)
25.2090 + ko.storedBindingContextForNode(node, bindingContextInstance);
25.2091 +
25.2092 + // Use evaluatedBindings if given, otherwise fall back on asking the bindings provider to give us some bindings
25.2093 + var evaluatedBindings = (typeof bindings == "function") ? bindings(bindingContextInstance, node) : bindings;
25.2094 + parsedBindings = evaluatedBindings || ko.bindingProvider['instance']['getBindings'](node, bindingContextInstance);
25.2095 +
25.2096 + if (parsedBindings) {
25.2097 + // First run all the inits, so bindings can register for notification on changes
25.2098 + if (initPhase === 0) {
25.2099 + initPhase = 1;
25.2100 + for (var bindingKey in parsedBindings) {
25.2101 + var binding = ko.bindingHandlers[bindingKey];
25.2102 + if (binding && node.nodeType === 8)
25.2103 + validateThatBindingIsAllowedForVirtualElements(bindingKey);
25.2104 +
25.2105 + if (binding && typeof binding["init"] == "function") {
25.2106 + var handlerInitFn = binding["init"];
25.2107 + var initResult = handlerInitFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
25.2108 +
25.2109 + // If this binding handler claims to control descendant bindings, make a note of this
25.2110 + if (initResult && initResult['controlsDescendantBindings']) {
25.2111 + if (bindingHandlerThatControlsDescendantBindings !== undefined)
25.2112 + 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.");
25.2113 + bindingHandlerThatControlsDescendantBindings = bindingKey;
25.2114 + }
25.2115 + }
25.2116 + }
25.2117 + initPhase = 2;
25.2118 + }
25.2119 +
25.2120 + // ... then run all the updates, which might trigger changes even on the first evaluation
25.2121 + if (initPhase === 2) {
25.2122 + for (var bindingKey in parsedBindings) {
25.2123 + var binding = ko.bindingHandlers[bindingKey];
25.2124 + if (binding && typeof binding["update"] == "function") {
25.2125 + var handlerUpdateFn = binding["update"];
25.2126 + handlerUpdateFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);
25.2127 + }
25.2128 + }
25.2129 + }
25.2130 + }
25.2131 + },
25.2132 + null,
25.2133 + { disposeWhenNodeIsRemoved : node }
25.2134 + );
25.2135 +
25.2136 + return {
25.2137 + shouldBindDescendants: bindingHandlerThatControlsDescendantBindings === undefined
25.2138 + };
25.2139 + };
25.2140 +
25.2141 + var storedBindingContextDomDataKey = "__ko_bindingContext__";
25.2142 + ko.storedBindingContextForNode = function (node, bindingContext) {
25.2143 + if (arguments.length == 2)
25.2144 + ko.utils.domData.set(node, storedBindingContextDomDataKey, bindingContext);
25.2145 + else
25.2146 + return ko.utils.domData.get(node, storedBindingContextDomDataKey);
25.2147 + }
25.2148 +
25.2149 + ko.applyBindingsToNode = function (node, bindings, viewModel) {
25.2150 + if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
25.2151 + ko.virtualElements.normaliseVirtualElementDomStructure(node);
25.2152 + return applyBindingsToNodeInternal(node, bindings, viewModel, true);
25.2153 + };
25.2154 +
25.2155 + ko.applyBindingsToDescendants = function(viewModel, rootNode) {
25.2156 + if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
25.2157 + applyBindingsToDescendantsInternal(viewModel, rootNode, true);
25.2158 + };
25.2159 +
25.2160 + ko.applyBindings = function (viewModel, rootNode) {
25.2161 + if (rootNode && (rootNode.nodeType !== 1) && (rootNode.nodeType !== 8))
25.2162 + throw new Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
25.2163 + rootNode = rootNode || window.document.body; // Make "rootNode" parameter optional
25.2164 +
25.2165 + applyBindingsToNodeAndDescendantsInternal(viewModel, rootNode, true);
25.2166 + };
25.2167 +
25.2168 + // Retrieving binding context from arbitrary nodes
25.2169 + ko.contextFor = function(node) {
25.2170 + // We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)
25.2171 + switch (node.nodeType) {
25.2172 + case 1:
25.2173 + case 8:
25.2174 + var context = ko.storedBindingContextForNode(node);
25.2175 + if (context) return context;
25.2176 + if (node.parentNode) return ko.contextFor(node.parentNode);
25.2177 + break;
25.2178 + }
25.2179 + return undefined;
25.2180 + };
25.2181 + ko.dataFor = function(node) {
25.2182 + var context = ko.contextFor(node);
25.2183 + return context ? context['$data'] : undefined;
25.2184 + };
25.2185 +
25.2186 + ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
25.2187 + ko.exportSymbol('applyBindings', ko.applyBindings);
25.2188 + ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
25.2189 + ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
25.2190 + ko.exportSymbol('contextFor', ko.contextFor);
25.2191 + ko.exportSymbol('dataFor', ko.dataFor);
25.2192 +})();
25.2193 +var attrHtmlToJavascriptMap = { 'class': 'className', 'for': 'htmlFor' };
25.2194 +ko.bindingHandlers['attr'] = {
25.2195 + 'update': function(element, valueAccessor, allBindingsAccessor) {
25.2196 + var value = ko.utils.unwrapObservable(valueAccessor()) || {};
25.2197 + for (var attrName in value) {
25.2198 + if (typeof attrName == "string") {
25.2199 + var attrValue = ko.utils.unwrapObservable(value[attrName]);
25.2200 +
25.2201 + // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
25.2202 + // when someProp is a "no value"-like value (strictly null, false, or undefined)
25.2203 + // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
25.2204 + var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
25.2205 + if (toRemove)
25.2206 + element.removeAttribute(attrName);
25.2207 +
25.2208 + // In IE <= 7 and IE8 Quirks Mode, you have to use the Javascript property name instead of the
25.2209 + // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
25.2210 + // but instead of figuring out the mode, we'll just set the attribute through the Javascript
25.2211 + // property for IE <= 8.
25.2212 + if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavascriptMap) {
25.2213 + attrName = attrHtmlToJavascriptMap[attrName];
25.2214 + if (toRemove)
25.2215 + element.removeAttribute(attrName);
25.2216 + else
25.2217 + element[attrName] = attrValue;
25.2218 + } else if (!toRemove) {
25.2219 + try {
25.2220 + element.setAttribute(attrName, attrValue.toString());
25.2221 + } catch (err) {
25.2222 + // ignore for now
25.2223 + if (console) {
25.2224 + console.log("Can't set attribute " + attrName + " to " + attrValue + " error: " + err);
25.2225 + }
25.2226 + }
25.2227 + }
25.2228 +
25.2229 + // Treat "name" specially - although you can think of it as an attribute, it also needs
25.2230 + // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
25.2231 + // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
25.2232 + // entirely, and there's no strong reason to allow for such casing in HTML.
25.2233 + if (attrName === "name") {
25.2234 + ko.utils.setElementName(element, toRemove ? "" : attrValue.toString());
25.2235 + }
25.2236 + }
25.2237 + }
25.2238 + }
25.2239 +};
25.2240 +ko.bindingHandlers['checked'] = {
25.2241 + 'init': function (element, valueAccessor, allBindingsAccessor) {
25.2242 + var updateHandler = function() {
25.2243 + var valueToWrite;
25.2244 + if (element.type == "checkbox") {
25.2245 + valueToWrite = element.checked;
25.2246 + } else if ((element.type == "radio") && (element.checked)) {
25.2247 + valueToWrite = element.value;
25.2248 + } else {
25.2249 + return; // "checked" binding only responds to checkboxes and selected radio buttons
25.2250 + }
25.2251 +
25.2252 + var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue);
25.2253 + if ((element.type == "checkbox") && (unwrappedValue instanceof Array)) {
25.2254 + // For checkboxes bound to an array, we add/remove the checkbox value to that array
25.2255 + // This works for both observable and non-observable arrays
25.2256 + var existingEntryIndex = ko.utils.arrayIndexOf(unwrappedValue, element.value);
25.2257 + if (element.checked && (existingEntryIndex < 0))
25.2258 + modelValue.push(element.value);
25.2259 + else if ((!element.checked) && (existingEntryIndex >= 0))
25.2260 + modelValue.splice(existingEntryIndex, 1);
25.2261 + } else {
25.2262 + ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
25.2263 + }
25.2264 + };
25.2265 + ko.utils.registerEventHandler(element, "click", updateHandler);
25.2266 +
25.2267 + // IE 6 won't allow radio buttons to be selected unless they have a name
25.2268 + if ((element.type == "radio") && !element.name)
25.2269 + ko.bindingHandlers['uniqueName']['init'](element, function() { return true });
25.2270 + },
25.2271 + 'update': function (element, valueAccessor) {
25.2272 + var value = ko.utils.unwrapObservable(valueAccessor());
25.2273 +
25.2274 + if (element.type == "checkbox") {
25.2275 + if (value instanceof Array) {
25.2276 + // When bound to an array, the checkbox being checked represents its value being present in that array
25.2277 + element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
25.2278 + } else {
25.2279 + // When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
25.2280 + element.checked = value;
25.2281 + }
25.2282 + } else if (element.type == "radio") {
25.2283 + element.checked = (element.value == value);
25.2284 + }
25.2285 + }
25.2286 +};
25.2287 +var classesWrittenByBindingKey = '__ko__cssValue';
25.2288 +ko.bindingHandlers['css'] = {
25.2289 + 'update': function (element, valueAccessor) {
25.2290 + var value = ko.utils.unwrapObservable(valueAccessor());
25.2291 + if (typeof value == "object") {
25.2292 + for (var className in value) {
25.2293 + var shouldHaveClass = ko.utils.unwrapObservable(value[className]);
25.2294 + ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
25.2295 + }
25.2296 + } else {
25.2297 + value = String(value || ''); // Make sure we don't try to store or set a non-string value
25.2298 + ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
25.2299 + element[classesWrittenByBindingKey] = value;
25.2300 + ko.utils.toggleDomNodeCssClass(element, value, true);
25.2301 + }
25.2302 + }
25.2303 +};
25.2304 +ko.bindingHandlers['enable'] = {
25.2305 + 'update': function (element, valueAccessor) {
25.2306 + var value = ko.utils.unwrapObservable(valueAccessor());
25.2307 + if (value && element.disabled)
25.2308 + element.removeAttribute("disabled");
25.2309 + else if ((!value) && (!element.disabled))
25.2310 + element.disabled = true;
25.2311 + }
25.2312 +};
25.2313 +
25.2314 +ko.bindingHandlers['disable'] = {
25.2315 + 'update': function (element, valueAccessor) {
25.2316 + ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
25.2317 + }
25.2318 +};
25.2319 +// For certain common events (currently just 'click'), allow a simplified data-binding syntax
25.2320 +// e.g. click:handler instead of the usual full-length event:{click:handler}
25.2321 +function makeEventHandlerShortcut(eventName) {
25.2322 + ko.bindingHandlers[eventName] = {
25.2323 + 'init': function(element, valueAccessor, allBindingsAccessor, viewModel) {
25.2324 + var newValueAccessor = function () {
25.2325 + var result = {};
25.2326 + result[eventName] = valueAccessor();
25.2327 + return result;
25.2328 + };
25.2329 + return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindingsAccessor, viewModel);
25.2330 + }
25.2331 + }
25.2332 +}
25.2333 +
25.2334 +ko.bindingHandlers['event'] = {
25.2335 + 'init' : function (element, valueAccessor, allBindingsAccessor, viewModel) {
25.2336 + var eventsToHandle = valueAccessor() || {};
25.2337 + for(var eventNameOutsideClosure in eventsToHandle) {
25.2338 + (function() {
25.2339 + var eventName = eventNameOutsideClosure; // Separate variable to be captured by event handler closure
25.2340 + if (typeof eventName == "string") {
25.2341 + ko.utils.registerEventHandler(element, eventName, function (event) {
25.2342 + var handlerReturnValue;
25.2343 + var handlerFunction = valueAccessor()[eventName];
25.2344 + if (!handlerFunction)
25.2345 + return;
25.2346 + var allBindings = allBindingsAccessor();
25.2347 +
25.2348 + try {
25.2349 + // Take all the event args, and prefix with the viewmodel
25.2350 + var argsForHandler = ko.utils.makeArray(arguments);
25.2351 + argsForHandler.unshift(viewModel);
25.2352 + handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
25.2353 + } finally {
25.2354 + if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
25.2355 + if (event.preventDefault)
25.2356 + event.preventDefault();
25.2357 + else
25.2358 + event.returnValue = false;
25.2359 + }
25.2360 + }
25.2361 +
25.2362 + var bubble = allBindings[eventName + 'Bubble'] !== false;
25.2363 + if (!bubble) {
25.2364 + event.cancelBubble = true;
25.2365 + if (event.stopPropagation)
25.2366 + event.stopPropagation();
25.2367 + }
25.2368 + });
25.2369 + }
25.2370 + })();
25.2371 + }
25.2372 + }
25.2373 +};
25.2374 +// "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
25.2375 +// "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
25.2376 +ko.bindingHandlers['foreach'] = {
25.2377 + makeTemplateValueAccessor: function(valueAccessor) {
25.2378 + return function() {
25.2379 + var modelValue = valueAccessor(),
25.2380 + unwrappedValue = ko.utils.peekObservable(modelValue); // Unwrap without setting a dependency here
25.2381 +
25.2382 + // If unwrappedValue is the array, pass in the wrapped value on its own
25.2383 + // The value will be unwrapped and tracked within the template binding
25.2384 + // (See https://github.com/SteveSanderson/knockout/issues/523)
25.2385 + if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
25.2386 + return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };
25.2387 +
25.2388 + // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
25.2389 + ko.utils.unwrapObservable(modelValue);
25.2390 + return {
25.2391 + 'foreach': unwrappedValue['data'],
25.2392 + 'as': unwrappedValue['as'],
25.2393 + 'includeDestroyed': unwrappedValue['includeDestroyed'],
25.2394 + 'afterAdd': unwrappedValue['afterAdd'],
25.2395 + 'beforeRemove': unwrappedValue['beforeRemove'],
25.2396 + 'afterRender': unwrappedValue['afterRender'],
25.2397 + 'beforeMove': unwrappedValue['beforeMove'],
25.2398 + 'afterMove': unwrappedValue['afterMove'],
25.2399 + 'templateEngine': ko.nativeTemplateEngine.instance
25.2400 + };
25.2401 + };
25.2402 + },
25.2403 + 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
25.2404 + return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
25.2405 + },
25.2406 + 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
25.2407 + return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindingsAccessor, viewModel, bindingContext);
25.2408 + }
25.2409 +};
25.2410 +ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
25.2411 +ko.virtualElements.allowedBindings['foreach'] = true;
25.2412 +var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
25.2413 +ko.bindingHandlers['hasfocus'] = {
25.2414 + 'init': function(element, valueAccessor, allBindingsAccessor) {
25.2415 + var handleElementFocusChange = function(isFocused) {
25.2416 + // Where possible, ignore which event was raised and determine focus state using activeElement,
25.2417 + // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
25.2418 + // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
25.2419 + // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
25.2420 + // from calling 'blur()' on the element when it loses focus.
25.2421 + // Discussion at https://github.com/SteveSanderson/knockout/pull/352
25.2422 + element[hasfocusUpdatingProperty] = true;
25.2423 + var ownerDoc = element.ownerDocument;
25.2424 + if ("activeElement" in ownerDoc) {
25.2425 + isFocused = (ownerDoc.activeElement === element);
25.2426 + }
25.2427 + var modelValue = valueAccessor();
25.2428 + ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'hasfocus', isFocused, true);
25.2429 + element[hasfocusUpdatingProperty] = false;
25.2430 + };
25.2431 + var handleElementFocusIn = handleElementFocusChange.bind(null, true);
25.2432 + var handleElementFocusOut = handleElementFocusChange.bind(null, false);
25.2433 +
25.2434 + ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
25.2435 + ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
25.2436 + ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
25.2437 + ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
25.2438 + },
25.2439 + 'update': function(element, valueAccessor) {
25.2440 + var value = ko.utils.unwrapObservable(valueAccessor());
25.2441 + if (!element[hasfocusUpdatingProperty]) {
25.2442 + value ? element.focus() : element.blur();
25.2443 + ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]); // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
25.2444 + }
25.2445 + }
25.2446 +};
25.2447 +ko.bindingHandlers['html'] = {
25.2448 + 'init': function() {
25.2449 + // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
25.2450 + return { 'controlsDescendantBindings': true };
25.2451 + },
25.2452 + 'update': function (element, valueAccessor) {
25.2453 + // setHtml will unwrap the value if needed
25.2454 + ko.utils.setHtml(element, valueAccessor());
25.2455 + }
25.2456 +};
25.2457 +var withIfDomDataKey = '__ko_withIfBindingData';
25.2458 +// Makes a binding like with or if
25.2459 +function makeWithIfBinding(bindingKey, isWith, isNot, makeContextCallback) {
25.2460 + ko.bindingHandlers[bindingKey] = {
25.2461 + 'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
25.2462 + ko.utils.domData.set(element, withIfDomDataKey, {});
25.2463 + return { 'controlsDescendantBindings': true };
25.2464 + },
25.2465 + 'update': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
25.2466 + var withIfData = ko.utils.domData.get(element, withIfDomDataKey),
25.2467 + dataValue = ko.utils.unwrapObservable(valueAccessor()),
25.2468 + shouldDisplay = !isNot !== !dataValue, // equivalent to isNot ? !dataValue : !!dataValue
25.2469 + isFirstRender = !withIfData.savedNodes,
25.2470 + needsRefresh = isFirstRender || isWith || (shouldDisplay !== withIfData.didDisplayOnLastUpdate);
25.2471 +
25.2472 + if (needsRefresh) {
25.2473 + if (isFirstRender) {
25.2474 + withIfData.savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
25.2475 + }
25.2476 +
25.2477 + if (shouldDisplay) {
25.2478 + if (!isFirstRender) {
25.2479 + ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(withIfData.savedNodes));
25.2480 + }
25.2481 + ko.applyBindingsToDescendants(makeContextCallback ? makeContextCallback(bindingContext, dataValue) : bindingContext, element);
25.2482 + } else {
25.2483 + ko.virtualElements.emptyNode(element);
25.2484 + }
25.2485 +
25.2486 + withIfData.didDisplayOnLastUpdate = shouldDisplay;
25.2487 + }
25.2488 + }
25.2489 + };
25.2490 + ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
25.2491 + ko.virtualElements.allowedBindings[bindingKey] = true;
25.2492 +}
25.2493 +
25.2494 +// Construct the actual binding handlers
25.2495 +makeWithIfBinding('if');
25.2496 +makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
25.2497 +makeWithIfBinding('with', true /* isWith */, false /* isNot */,
25.2498 + function(bindingContext, dataValue) {
25.2499 + return bindingContext['createChildContext'](dataValue);
25.2500 + }
25.2501 +);
25.2502 +function ensureDropdownSelectionIsConsistentWithModelValue(element, modelValue, preferModelValue) {
25.2503 + if (preferModelValue) {
25.2504 + if (modelValue !== ko.selectExtensions.readValue(element))
25.2505 + ko.selectExtensions.writeValue(element, modelValue);
25.2506 + }
25.2507 +
25.2508 + // No matter which direction we're syncing in, we want the end result to be equality between dropdown value and model value.
25.2509 + // If they aren't equal, either we prefer the dropdown value, or the model value couldn't be represented, so either way,
25.2510 + // change the model value to match the dropdown.
25.2511 + if (modelValue !== ko.selectExtensions.readValue(element))
25.2512 + ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
25.2513 +};
25.2514 +
25.2515 +ko.bindingHandlers['options'] = {
25.2516 + 'update': function (element, valueAccessor, allBindingsAccessor) {
25.2517 + if (ko.utils.tagNameLower(element) !== "select")
25.2518 + throw new Error("options binding applies only to SELECT elements");
25.2519 +
25.2520 + var selectWasPreviouslyEmpty = element.length == 0;
25.2521 + var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
25.2522 + return node.tagName && (ko.utils.tagNameLower(node) === "option") && node.selected;
25.2523 + }), function (node) {
25.2524 + return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
25.2525 + });
25.2526 + var previousScrollTop = element.scrollTop;
25.2527 +
25.2528 + var value = ko.utils.unwrapObservable(valueAccessor());
25.2529 + var selectedValue = element.value;
25.2530 +
25.2531 + // Remove all existing <option>s.
25.2532 + // Need to use .remove() rather than .removeChild() for <option>s otherwise IE behaves oddly (https://github.com/SteveSanderson/knockout/issues/134)
25.2533 + while (element.length > 0) {
25.2534 + ko.cleanNode(element.options[0]);
25.2535 + element.remove(0);
25.2536 + }
25.2537 +
25.2538 + if (value) {
25.2539 + var allBindings = allBindingsAccessor(),
25.2540 + includeDestroyed = allBindings['optionsIncludeDestroyed'];
25.2541 +
25.2542 + if (typeof value.length != "number")
25.2543 + value = [value];
25.2544 + if (allBindings['optionsCaption']) {
25.2545 + var option = document.createElement("option");
25.2546 + ko.utils.setHtml(option, allBindings['optionsCaption']);
25.2547 + ko.selectExtensions.writeValue(option, undefined);
25.2548 + element.appendChild(option);
25.2549 + }
25.2550 +
25.2551 + for (var i = 0, j = value.length; i < j; i++) {
25.2552 + // Skip destroyed items
25.2553 + var arrayEntry = value[i];
25.2554 + if (arrayEntry && arrayEntry['_destroy'] && !includeDestroyed)
25.2555 + continue;
25.2556 +
25.2557 + var option = document.createElement("option");
25.2558 +
25.2559 + function applyToObject(object, predicate, defaultValue) {
25.2560 + var predicateType = typeof predicate;
25.2561 + if (predicateType == "function") // Given a function; run it against the data value
25.2562 + return predicate(object);
25.2563 + else if (predicateType == "string") // Given a string; treat it as a property name on the data value
25.2564 + return object[predicate];
25.2565 + else // Given no optionsText arg; use the data value itself
25.2566 + return defaultValue;
25.2567 + }
25.2568 +
25.2569 + // Apply a value to the option element
25.2570 + var optionValue = applyToObject(arrayEntry, allBindings['optionsValue'], arrayEntry);
25.2571 + ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));
25.2572 +
25.2573 + // Apply some text to the option element
25.2574 + var optionText = applyToObject(arrayEntry, allBindings['optionsText'], optionValue);
25.2575 + ko.utils.setTextContent(option, optionText);
25.2576 +
25.2577 + element.appendChild(option);
25.2578 + }
25.2579 +
25.2580 + // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
25.2581 + // That's why we first added them without selection. Now it's time to set the selection.
25.2582 + var newOptions = element.getElementsByTagName("option");
25.2583 + var countSelectionsRetained = 0;
25.2584 + for (var i = 0, j = newOptions.length; i < j; i++) {
25.2585 + if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
25.2586 + ko.utils.setOptionNodeSelectionState(newOptions[i], true);
25.2587 + countSelectionsRetained++;
25.2588 + }
25.2589 + }
25.2590 +
25.2591 + element.scrollTop = previousScrollTop;
25.2592 +
25.2593 + if (selectWasPreviouslyEmpty && ('value' in allBindings)) {
25.2594 + // Ensure consistency between model value and selected option.
25.2595 + // If the dropdown is being populated for the first time here (or was otherwise previously empty),
25.2596 + // the dropdown selection state is meaningless, so we preserve the model value.
25.2597 + ensureDropdownSelectionIsConsistentWithModelValue(element, ko.utils.peekObservable(allBindings['value']), /* preferModelValue */ true);
25.2598 + }
25.2599 +
25.2600 + // Workaround for IE9 bug
25.2601 + ko.utils.ensureSelectElementIsRenderedCorrectly(element);
25.2602 + }
25.2603 + }
25.2604 +};
25.2605 +ko.bindingHandlers['options'].optionValueDomDataKey = '__ko.optionValueDomData__';
25.2606 +ko.bindingHandlers['selectedOptions'] = {
25.2607 + 'init': function (element, valueAccessor, allBindingsAccessor) {
25.2608 + ko.utils.registerEventHandler(element, "change", function () {
25.2609 + var value = valueAccessor(), valueToWrite = [];
25.2610 + ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
25.2611 + if (node.selected)
25.2612 + valueToWrite.push(ko.selectExtensions.readValue(node));
25.2613 + });
25.2614 + ko.expressionRewriting.writeValueToProperty(value, allBindingsAccessor, 'value', valueToWrite);
25.2615 + });
25.2616 + },
25.2617 + 'update': function (element, valueAccessor) {
25.2618 + if (ko.utils.tagNameLower(element) != "select")
25.2619 + throw new Error("values binding applies only to SELECT elements");
25.2620 +
25.2621 + var newValue = ko.utils.unwrapObservable(valueAccessor());
25.2622 + if (newValue && typeof newValue.length == "number") {
25.2623 + ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
25.2624 + var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
25.2625 + ko.utils.setOptionNodeSelectionState(node, isSelected);
25.2626 + });
25.2627 + }
25.2628 + }
25.2629 +};
25.2630 +ko.bindingHandlers['style'] = {
25.2631 + 'update': function (element, valueAccessor) {
25.2632 + var value = ko.utils.unwrapObservable(valueAccessor() || {});
25.2633 + for (var styleName in value) {
25.2634 + if (typeof styleName == "string") {
25.2635 + var styleValue = ko.utils.unwrapObservable(value[styleName]);
25.2636 + element.style[styleName] = styleValue || ""; // Empty string removes the value, whereas null/undefined have no effect
25.2637 + }
25.2638 + }
25.2639 + }
25.2640 +};
25.2641 +ko.bindingHandlers['submit'] = {
25.2642 + 'init': function (element, valueAccessor, allBindingsAccessor, viewModel) {
25.2643 + if (typeof valueAccessor() != "function")
25.2644 + throw new Error("The value for a submit binding must be a function");
25.2645 + ko.utils.registerEventHandler(element, "submit", function (event) {
25.2646 + var handlerReturnValue;
25.2647 + var value = valueAccessor();
25.2648 + try { handlerReturnValue = value.call(viewModel, element); }
25.2649 + finally {
25.2650 + if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
25.2651 + if (event.preventDefault)
25.2652 + event.preventDefault();
25.2653 + else
25.2654 + event.returnValue = false;
25.2655 + }
25.2656 + }
25.2657 + });
25.2658 + }
25.2659 +};
25.2660 +ko.bindingHandlers['text'] = {
25.2661 + 'update': function (element, valueAccessor) {
25.2662 + ko.utils.setTextContent(element, valueAccessor());
25.2663 + }
25.2664 +};
25.2665 +ko.virtualElements.allowedBindings['text'] = true;
25.2666 +ko.bindingHandlers['uniqueName'] = {
25.2667 + 'init': function (element, valueAccessor) {
25.2668 + if (valueAccessor()) {
25.2669 + var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
25.2670 + ko.utils.setElementName(element, name);
25.2671 + }
25.2672 + }
25.2673 +};
25.2674 +ko.bindingHandlers['uniqueName'].currentIndex = 0;
25.2675 +ko.bindingHandlers['value'] = {
25.2676 + 'init': function (element, valueAccessor, allBindingsAccessor) {
25.2677 + // Always catch "change" event; possibly other events too if asked
25.2678 + var eventsToCatch = ["change"];
25.2679 + var requestedEventsToCatch = allBindingsAccessor()["valueUpdate"];
25.2680 + var propertyChangedFired = false;
25.2681 + if (requestedEventsToCatch) {
25.2682 + if (typeof requestedEventsToCatch == "string") // Allow both individual event names, and arrays of event names
25.2683 + requestedEventsToCatch = [requestedEventsToCatch];
25.2684 + ko.utils.arrayPushAll(eventsToCatch, requestedEventsToCatch);
25.2685 + eventsToCatch = ko.utils.arrayGetDistinctValues(eventsToCatch);
25.2686 + }
25.2687 +
25.2688 + var valueUpdateHandler = function() {
25.2689 + propertyChangedFired = false;
25.2690 + var modelValue = valueAccessor();
25.2691 + var elementValue = ko.selectExtensions.readValue(element);
25.2692 + ko.expressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'value', elementValue);
25.2693 + }
25.2694 +
25.2695 + // Workaround for https://github.com/SteveSanderson/knockout/issues/122
25.2696 + // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
25.2697 + var ieAutoCompleteHackNeeded = ko.utils.ieVersion && element.tagName.toLowerCase() == "input" && element.type == "text"
25.2698 + && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
25.2699 + if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
25.2700 + ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
25.2701 + ko.utils.registerEventHandler(element, "blur", function() {
25.2702 + if (propertyChangedFired) {
25.2703 + valueUpdateHandler();
25.2704 + }
25.2705 + });
25.2706 + }
25.2707 +
25.2708 + ko.utils.arrayForEach(eventsToCatch, function(eventName) {
25.2709 + // The syntax "after<eventname>" means "run the handler asynchronously after the event"
25.2710 + // This is useful, for example, to catch "keydown" events after the browser has updated the control
25.2711 + // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
25.2712 + var handler = valueUpdateHandler;
25.2713 + if (ko.utils.stringStartsWith(eventName, "after")) {
25.2714 + handler = function() { setTimeout(valueUpdateHandler, 0) };
25.2715 + eventName = eventName.substring("after".length);
25.2716 + }
25.2717 + ko.utils.registerEventHandler(element, eventName, handler);
25.2718 + });
25.2719 + },
25.2720 + 'update': function (element, valueAccessor) {
25.2721 + var valueIsSelectOption = ko.utils.tagNameLower(element) === "select";
25.2722 + var newValue = ko.utils.unwrapObservable(valueAccessor());
25.2723 + var elementValue = ko.selectExtensions.readValue(element);
25.2724 + var valueHasChanged = (newValue != elementValue);
25.2725 +
25.2726 + // 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).
25.2727 + // 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.
25.2728 + if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0"))
25.2729 + valueHasChanged = true;
25.2730 +
25.2731 + if (valueHasChanged) {
25.2732 + var applyValueAction = function () { ko.selectExtensions.writeValue(element, newValue); };
25.2733 + applyValueAction();
25.2734 +
25.2735 + // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
25.2736 + // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
25.2737 + // to apply the value as well.
25.2738 + var alsoApplyAsynchronously = valueIsSelectOption;
25.2739 + if (alsoApplyAsynchronously)
25.2740 + setTimeout(applyValueAction, 0);
25.2741 + }
25.2742 +
25.2743 + // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
25.2744 + // because you're not allowed to have a model value that disagrees with a visible UI selection.
25.2745 + if (valueIsSelectOption && (element.length > 0))
25.2746 + ensureDropdownSelectionIsConsistentWithModelValue(element, newValue, /* preferModelValue */ false);
25.2747 + }
25.2748 +};
25.2749 +ko.bindingHandlers['visible'] = {
25.2750 + 'update': function (element, valueAccessor) {
25.2751 + var value = ko.utils.unwrapObservable(valueAccessor());
25.2752 + var isCurrentlyVisible = !(element.style.display == "none");
25.2753 + if (value && !isCurrentlyVisible)
25.2754 + element.style.display = "";
25.2755 + else if ((!value) && isCurrentlyVisible)
25.2756 + element.style.display = "none";
25.2757 + }
25.2758 +};
25.2759 +// 'click' is just a shorthand for the usual full-length event:{click:handler}
25.2760 +makeEventHandlerShortcut('click');
25.2761 +// If you want to make a custom template engine,
25.2762 +//
25.2763 +// [1] Inherit from this class (like ko.nativeTemplateEngine does)
25.2764 +// [2] Override 'renderTemplateSource', supplying a function with this signature:
25.2765 +//
25.2766 +// function (templateSource, bindingContext, options) {
25.2767 +// // - templateSource.text() is the text of the template you should render
25.2768 +// // - bindingContext.$data is the data you should pass into the template
25.2769 +// // - you might also want to make bindingContext.$parent, bindingContext.$parents,
25.2770 +// // and bindingContext.$root available in the template too
25.2771 +// // - options gives you access to any other properties set on "data-bind: { template: options }"
25.2772 +// //
25.2773 +// // Return value: an array of DOM nodes
25.2774 +// }
25.2775 +//
25.2776 +// [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
25.2777 +//
25.2778 +// function (script) {
25.2779 +// // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
25.2780 +// // For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
25.2781 +// }
25.2782 +//
25.2783 +// This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
25.2784 +// If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
25.2785 +// and then you don't need to override 'createJavaScriptEvaluatorBlock'.
25.2786 +
25.2787 +ko.templateEngine = function () { };
25.2788 +
25.2789 +ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
25.2790 + throw new Error("Override renderTemplateSource");
25.2791 +};
25.2792 +
25.2793 +ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
25.2794 + throw new Error("Override createJavaScriptEvaluatorBlock");
25.2795 +};
25.2796 +
25.2797 +ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {
25.2798 + // Named template
25.2799 + if (typeof template == "string") {
25.2800 + templateDocument = templateDocument || document;
25.2801 + var elem = templateDocument.getElementById(template);
25.2802 + if (!elem)
25.2803 + throw new Error("Cannot find template with ID " + template);
25.2804 + return new ko.templateSources.domElement(elem);
25.2805 + } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
25.2806 + // Anonymous template
25.2807 + return new ko.templateSources.anonymousTemplate(template);
25.2808 + } else
25.2809 + throw new Error("Unknown template type: " + template);
25.2810 +};
25.2811 +
25.2812 +ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
25.2813 + var templateSource = this['makeTemplateSource'](template, templateDocument);
25.2814 + return this['renderTemplateSource'](templateSource, bindingContext, options);
25.2815 +};
25.2816 +
25.2817 +ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
25.2818 + // Skip rewriting if requested
25.2819 + if (this['allowTemplateRewriting'] === false)
25.2820 + return true;
25.2821 + return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
25.2822 +};
25.2823 +
25.2824 +ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
25.2825 + var templateSource = this['makeTemplateSource'](template, templateDocument);
25.2826 + var rewritten = rewriterCallback(templateSource['text']());
25.2827 + templateSource['text'](rewritten);
25.2828 + templateSource['data']("isRewritten", true);
25.2829 +};
25.2830 +
25.2831 +ko.exportSymbol('templateEngine', ko.templateEngine);
25.2832 +
25.2833 +ko.templateRewriting = (function () {
25.2834 + var memoizeDataBindingAttributeSyntaxRegex = /(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi;
25.2835 + var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;
25.2836 +
25.2837 + function validateDataBindValuesForRewriting(keyValueArray) {
25.2838 + var allValidators = ko.expressionRewriting.bindingRewriteValidators;
25.2839 + for (var i = 0; i < keyValueArray.length; i++) {
25.2840 + var key = keyValueArray[i]['key'];
25.2841 + if (allValidators.hasOwnProperty(key)) {
25.2842 + var validator = allValidators[key];
25.2843 +
25.2844 + if (typeof validator === "function") {
25.2845 + var possibleErrorMessage = validator(keyValueArray[i]['value']);
25.2846 + if (possibleErrorMessage)
25.2847 + throw new Error(possibleErrorMessage);
25.2848 + } else if (!validator) {
25.2849 + throw new Error("This template engine does not support the '" + key + "' binding within its templates");
25.2850 + }
25.2851 + }
25.2852 + }
25.2853 + }
25.2854 +
25.2855 + function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, templateEngine) {
25.2856 + var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
25.2857 + validateDataBindValuesForRewriting(dataBindKeyValueArray);
25.2858 + var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray);
25.2859 +
25.2860 + // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
25.2861 + // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
25.2862 + // extra indirection.
25.2863 + var applyBindingsToNextSiblingScript =
25.2864 + "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()})";
25.2865 + return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
25.2866 + }
25.2867 +
25.2868 + return {
25.2869 + ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
25.2870 + if (!templateEngine['isTemplateRewritten'](template, templateDocument))
25.2871 + templateEngine['rewriteTemplate'](template, function (htmlString) {
25.2872 + return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
25.2873 + }, templateDocument);
25.2874 + },
25.2875 +
25.2876 + memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
25.2877 + return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
25.2878 + return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[6], /* tagToRetain: */ arguments[1], templateEngine);
25.2879 + }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {
25.2880 + return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", templateEngine);
25.2881 + });
25.2882 + },
25.2883 +
25.2884 + applyMemoizedBindingsToNextSibling: function (bindings) {
25.2885 + return ko.memoization.memoize(function (domNode, bindingContext) {
25.2886 + if (domNode.nextSibling)
25.2887 + ko.applyBindingsToNode(domNode.nextSibling, bindings, bindingContext);
25.2888 + });
25.2889 + }
25.2890 + }
25.2891 +})();
25.2892 +
25.2893 +
25.2894 +// Exported only because it has to be referenced by string lookup from within rewritten template
25.2895 +ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
25.2896 +(function() {
25.2897 + // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
25.2898 + // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
25.2899 + //
25.2900 + // Two are provided by default:
25.2901 + // 1. ko.templateSources.domElement - reads/writes the text content of an arbitrary DOM element
25.2902 + // 2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
25.2903 + // without reading/writing the actual element text content, since it will be overwritten
25.2904 + // with the rendered template output.
25.2905 + // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
25.2906 + // Template sources need to have the following functions:
25.2907 + // text() - returns the template text from your storage location
25.2908 + // text(value) - writes the supplied template text to your storage location
25.2909 + // data(key) - reads values stored using data(key, value) - see below
25.2910 + // data(key, value) - associates "value" with this template and the key "key". Is used to store information like "isRewritten".
25.2911 + //
25.2912 + // Optionally, template sources can also have the following functions:
25.2913 + // nodes() - returns a DOM element containing the nodes of this template, where available
25.2914 + // nodes(value) - writes the given DOM element to your storage location
25.2915 + // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
25.2916 + // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
25.2917 + //
25.2918 + // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
25.2919 + // using and overriding "makeTemplateSource" to return an instance of your custom template source.
25.2920 +
25.2921 + ko.templateSources = {};
25.2922 +
25.2923 + // ---- ko.templateSources.domElement -----
25.2924 +
25.2925 + ko.templateSources.domElement = function(element) {
25.2926 + this.domElement = element;
25.2927 + }
25.2928 +
25.2929 + ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
25.2930 + var tagNameLower = ko.utils.tagNameLower(this.domElement),
25.2931 + elemContentsProperty = tagNameLower === "script" ? "text"
25.2932 + : tagNameLower === "textarea" ? "value"
25.2933 + : "innerHTML";
25.2934 +
25.2935 + if (arguments.length == 0) {
25.2936 + return this.domElement[elemContentsProperty];
25.2937 + } else {
25.2938 + var valueToWrite = arguments[0];
25.2939 + if (elemContentsProperty === "innerHTML")
25.2940 + ko.utils.setHtml(this.domElement, valueToWrite);
25.2941 + else
25.2942 + this.domElement[elemContentsProperty] = valueToWrite;
25.2943 + }
25.2944 + };
25.2945 +
25.2946 + ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {
25.2947 + if (arguments.length === 1) {
25.2948 + return ko.utils.domData.get(this.domElement, "templateSourceData_" + key);
25.2949 + } else {
25.2950 + ko.utils.domData.set(this.domElement, "templateSourceData_" + key, arguments[1]);
25.2951 + }
25.2952 + };
25.2953 +
25.2954 + // ---- ko.templateSources.anonymousTemplate -----
25.2955 + // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
25.2956 + // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
25.2957 + // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.
25.2958 +
25.2959 + var anonymousTemplatesDomDataKey = "__ko_anon_template__";
25.2960 + ko.templateSources.anonymousTemplate = function(element) {
25.2961 + this.domElement = element;
25.2962 + }
25.2963 + ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
25.2964 + ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
25.2965 + if (arguments.length == 0) {
25.2966 + var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
25.2967 + if (templateData.textData === undefined && templateData.containerData)
25.2968 + templateData.textData = templateData.containerData.innerHTML;
25.2969 + return templateData.textData;
25.2970 + } else {
25.2971 + var valueToWrite = arguments[0];
25.2972 + ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {textData: valueToWrite});
25.2973 + }
25.2974 + };
25.2975 + ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
25.2976 + if (arguments.length == 0) {
25.2977 + var templateData = ko.utils.domData.get(this.domElement, anonymousTemplatesDomDataKey) || {};
25.2978 + return templateData.containerData;
25.2979 + } else {
25.2980 + var valueToWrite = arguments[0];
25.2981 + ko.utils.domData.set(this.domElement, anonymousTemplatesDomDataKey, {containerData: valueToWrite});
25.2982 + }
25.2983 + };
25.2984 +
25.2985 + ko.exportSymbol('templateSources', ko.templateSources);
25.2986 + ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
25.2987 + ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
25.2988 +})();
25.2989 +(function () {
25.2990 + var _templateEngine;
25.2991 + ko.setTemplateEngine = function (templateEngine) {
25.2992 + if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
25.2993 + throw new Error("templateEngine must inherit from ko.templateEngine");
25.2994 + _templateEngine = templateEngine;
25.2995 + }
25.2996 +
25.2997 + function invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, action) {
25.2998 + var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
25.2999 + while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
25.3000 + nextInQueue = ko.virtualElements.nextSibling(node);
25.3001 + if (node.nodeType === 1 || node.nodeType === 8)
25.3002 + action(node);
25.3003 + }
25.3004 + }
25.3005 +
25.3006 + function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
25.3007 + // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
25.3008 + // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
25.3009 + // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
25.3010 + // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
25.3011 + // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)
25.3012 +
25.3013 + if (continuousNodeArray.length) {
25.3014 + var firstNode = continuousNodeArray[0], lastNode = continuousNodeArray[continuousNodeArray.length - 1];
25.3015 +
25.3016 + // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
25.3017 + // whereas a regular applyBindings won't introduce new memoized nodes
25.3018 + invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
25.3019 + ko.applyBindings(bindingContext, node);
25.3020 + });
25.3021 + invokeForEachNodeOrCommentInContinuousRange(firstNode, lastNode, function(node) {
25.3022 + ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
25.3023 + });
25.3024 + }
25.3025 + }
25.3026 +
25.3027 + function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
25.3028 + return nodeOrNodeArray.nodeType ? nodeOrNodeArray
25.3029 + : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
25.3030 + : null;
25.3031 + }
25.3032 +
25.3033 + function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
25.3034 + options = options || {};
25.3035 + var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
25.3036 + var templateDocument = firstTargetNode && firstTargetNode.ownerDocument;
25.3037 + var templateEngineToUse = (options['templateEngine'] || _templateEngine);
25.3038 + ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
25.3039 + var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);
25.3040 +
25.3041 + // Loosely check result is an array of DOM nodes
25.3042 + if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
25.3043 + throw new Error("Template engine must return an array of DOM nodes");
25.3044 +
25.3045 + var haveAddedNodesToParent = false;
25.3046 + switch (renderMode) {
25.3047 + case "replaceChildren":
25.3048 + ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
25.3049 + haveAddedNodesToParent = true;
25.3050 + break;
25.3051 + case "replaceNode":
25.3052 + ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
25.3053 + haveAddedNodesToParent = true;
25.3054 + break;
25.3055 + case "ignoreTargetNode": break;
25.3056 + default:
25.3057 + throw new Error("Unknown renderMode: " + renderMode);
25.3058 + }
25.3059 +
25.3060 + if (haveAddedNodesToParent) {
25.3061 + activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
25.3062 + if (options['afterRender'])
25.3063 + ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext['$data']]);
25.3064 + }
25.3065 +
25.3066 + return renderedNodesArray;
25.3067 + }
25.3068 +
25.3069 + ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
25.3070 + options = options || {};
25.3071 + if ((options['templateEngine'] || _templateEngine) == undefined)
25.3072 + throw new Error("Set a template engine before calling renderTemplate");
25.3073 + renderMode = renderMode || "replaceChildren";
25.3074 +
25.3075 + if (targetNodeOrNodeArray) {
25.3076 + var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
25.3077 +
25.3078 + var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
25.3079 + var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;
25.3080 +
25.3081 + return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
25.3082 + function () {
25.3083 + // Ensure we've got a proper binding context to work with
25.3084 + var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
25.3085 + ? dataOrBindingContext
25.3086 + : new ko.bindingContext(ko.utils.unwrapObservable(dataOrBindingContext));
25.3087 +
25.3088 + // Support selecting template as a function of the data being rendered
25.3089 + var templateName = typeof(template) == 'function' ? template(bindingContext['$data'], bindingContext) : template;
25.3090 +
25.3091 + var renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);
25.3092 + if (renderMode == "replaceNode") {
25.3093 + targetNodeOrNodeArray = renderedNodesArray;
25.3094 + firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
25.3095 + }
25.3096 + },
25.3097 + null,
25.3098 + { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
25.3099 + );
25.3100 + } else {
25.3101 + // 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
25.3102 + return ko.memoization.memoize(function (domNode) {
25.3103 + ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
25.3104 + });
25.3105 + }
25.3106 + };
25.3107 +
25.3108 + ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
25.3109 + // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
25.3110 + // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
25.3111 + var arrayItemContext;
25.3112 +
25.3113 + // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
25.3114 + var executeTemplateForArrayItem = function (arrayValue, index) {
25.3115 + // Support selecting template as a function of the data being rendered
25.3116 + arrayItemContext = parentBindingContext['createChildContext'](ko.utils.unwrapObservable(arrayValue), options['as']);
25.3117 + arrayItemContext['$index'] = index;
25.3118 + var templateName = typeof(template) == 'function' ? template(arrayValue, arrayItemContext) : template;
25.3119 + return executeTemplate(null, "ignoreTargetNode", templateName, arrayItemContext, options);
25.3120 + }
25.3121 +
25.3122 + // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
25.3123 + var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {
25.3124 + activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
25.3125 + if (options['afterRender'])
25.3126 + options['afterRender'](addedNodesArray, arrayValue);
25.3127 + };
25.3128 +
25.3129 + return ko.dependentObservable(function () {
25.3130 + var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
25.3131 + if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
25.3132 + unwrappedArray = [unwrappedArray];
25.3133 +
25.3134 + // Filter out any entries marked as destroyed
25.3135 + var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
25.3136 + return options['includeDestroyed'] || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
25.3137 + });
25.3138 +
25.3139 + // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
25.3140 + // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
25.3141 + ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, filteredArray, executeTemplateForArrayItem, options, activateBindingsCallback]);
25.3142 +
25.3143 + }, null, { disposeWhenNodeIsRemoved: targetNode });
25.3144 + };
25.3145 +
25.3146 + var templateComputedDomDataKey = '__ko__templateComputedDomDataKey__';
25.3147 + function disposeOldComputedAndStoreNewOne(element, newComputed) {
25.3148 + var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
25.3149 + if (oldComputed && (typeof(oldComputed.dispose) == 'function'))
25.3150 + oldComputed.dispose();
25.3151 + ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && newComputed.isActive()) ? newComputed : undefined);
25.3152 + }
25.3153 +
25.3154 + ko.bindingHandlers['template'] = {
25.3155 + 'init': function(element, valueAccessor) {
25.3156 + // Support anonymous templates
25.3157 + var bindingValue = ko.utils.unwrapObservable(valueAccessor());
25.3158 + if ((typeof bindingValue != "string") && (!bindingValue['name']) && (element.nodeType == 1 || element.nodeType == 8)) {
25.3159 + // It's an anonymous template - store the element contents, then clear the element
25.3160 + var templateNodes = element.nodeType == 1 ? element.childNodes : ko.virtualElements.childNodes(element),
25.3161 + container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
25.3162 + new ko.templateSources.anonymousTemplate(element)['nodes'](container);
25.3163 + }
25.3164 + return { 'controlsDescendantBindings': true };
25.3165 + },
25.3166 + 'update': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
25.3167 + var templateName = ko.utils.unwrapObservable(valueAccessor()),
25.3168 + options = {},
25.3169 + shouldDisplay = true,
25.3170 + dataValue,
25.3171 + templateComputed = null;
25.3172 +
25.3173 + if (typeof templateName != "string") {
25.3174 + options = templateName;
25.3175 + templateName = options['name'];
25.3176 +
25.3177 + // Support "if"/"ifnot" conditions
25.3178 + if ('if' in options)
25.3179 + shouldDisplay = ko.utils.unwrapObservable(options['if']);
25.3180 + if (shouldDisplay && 'ifnot' in options)
25.3181 + shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);
25.3182 +
25.3183 + dataValue = ko.utils.unwrapObservable(options['data']);
25.3184 + }
25.3185 +
25.3186 + if ('foreach' in options) {
25.3187 + // Render once for each data point (treating data set as empty if shouldDisplay==false)
25.3188 + var dataArray = (shouldDisplay && options['foreach']) || [];
25.3189 + templateComputed = ko.renderTemplateForEach(templateName || element, dataArray, options, element, bindingContext);
25.3190 + } else if (!shouldDisplay) {
25.3191 + ko.virtualElements.emptyNode(element);
25.3192 + } else {
25.3193 + // Render once for this single data point (or use the viewModel if no data was provided)
25.3194 + var innerBindingContext = ('data' in options) ?
25.3195 + bindingContext['createChildContext'](dataValue, options['as']) : // Given an explitit 'data' value, we create a child binding context for it
25.3196 + bindingContext; // Given no explicit 'data' value, we retain the same binding context
25.3197 + templateComputed = ko.renderTemplate(templateName || element, innerBindingContext, options, element);
25.3198 + }
25.3199 +
25.3200 + // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
25.3201 + disposeOldComputedAndStoreNewOne(element, templateComputed);
25.3202 + }
25.3203 + };
25.3204 +
25.3205 + // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
25.3206 + ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {
25.3207 + var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);
25.3208 +
25.3209 + if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
25.3210 + return null; // It looks like a string literal, not an object literal, so treat it as a named template (which is allowed for rewriting)
25.3211 +
25.3212 + if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
25.3213 + return null; // Named templates can be rewritten, so return "no error"
25.3214 + return "This template engine does not support anonymous templates nested within its templates";
25.3215 + };
25.3216 +
25.3217 + ko.virtualElements.allowedBindings['template'] = true;
25.3218 +})();
25.3219 +
25.3220 +ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
25.3221 +ko.exportSymbol('renderTemplate', ko.renderTemplate);
25.3222 +
25.3223 +ko.utils.compareArrays = (function () {
25.3224 + var statusNotInOld = 'added', statusNotInNew = 'deleted';
25.3225 +
25.3226 + // Simple calculation based on Levenshtein distance.
25.3227 + function compareArrays(oldArray, newArray, dontLimitMoves) {
25.3228 + oldArray = oldArray || [];
25.3229 + newArray = newArray || [];
25.3230 +
25.3231 + if (oldArray.length <= newArray.length)
25.3232 + return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, dontLimitMoves);
25.3233 + else
25.3234 + return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, dontLimitMoves);
25.3235 + }
25.3236 +
25.3237 + function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, dontLimitMoves) {
25.3238 + var myMin = Math.min,
25.3239 + myMax = Math.max,
25.3240 + editDistanceMatrix = [],
25.3241 + smlIndex, smlIndexMax = smlArray.length,
25.3242 + bigIndex, bigIndexMax = bigArray.length,
25.3243 + compareRange = (bigIndexMax - smlIndexMax) || 1,
25.3244 + maxDistance = smlIndexMax + bigIndexMax + 1,
25.3245 + thisRow, lastRow,
25.3246 + bigIndexMaxForRow, bigIndexMinForRow;
25.3247 +
25.3248 + for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
25.3249 + lastRow = thisRow;
25.3250 + editDistanceMatrix.push(thisRow = []);
25.3251 + bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
25.3252 + bigIndexMinForRow = myMax(0, smlIndex - 1);
25.3253 + for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
25.3254 + if (!bigIndex)
25.3255 + thisRow[bigIndex] = smlIndex + 1;
25.3256 + else if (!smlIndex) // Top row - transform empty array into new array via additions
25.3257 + thisRow[bigIndex] = bigIndex + 1;
25.3258 + else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
25.3259 + thisRow[bigIndex] = lastRow[bigIndex - 1]; // copy value (no edit)
25.3260 + else {
25.3261 + var northDistance = lastRow[bigIndex] || maxDistance; // not in big (deletion)
25.3262 + var westDistance = thisRow[bigIndex - 1] || maxDistance; // not in small (addition)
25.3263 + thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
25.3264 + }
25.3265 + }
25.3266 + }
25.3267 +
25.3268 + var editScript = [], meMinusOne, notInSml = [], notInBig = [];
25.3269 + for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
25.3270 + meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
25.3271 + if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
25.3272 + notInSml.push(editScript[editScript.length] = { // added
25.3273 + 'status': statusNotInSml,
25.3274 + 'value': bigArray[--bigIndex],
25.3275 + 'index': bigIndex });
25.3276 + } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
25.3277 + notInBig.push(editScript[editScript.length] = { // deleted
25.3278 + 'status': statusNotInBig,
25.3279 + 'value': smlArray[--smlIndex],
25.3280 + 'index': smlIndex });
25.3281 + } else {
25.3282 + editScript.push({
25.3283 + 'status': "retained",
25.3284 + 'value': bigArray[--bigIndex] });
25.3285 + --smlIndex;
25.3286 + }
25.3287 + }
25.3288 +
25.3289 + if (notInSml.length && notInBig.length) {
25.3290 + // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
25.3291 + // smlIndexMax keeps the time complexity of this algorithm linear.
25.3292 + var limitFailedCompares = smlIndexMax * 10, failedCompares,
25.3293 + a, d, notInSmlItem, notInBigItem;
25.3294 + // Go through the items that have been added and deleted and try to find matches between them.
25.3295 + for (failedCompares = a = 0; (dontLimitMoves || failedCompares < limitFailedCompares) && (notInSmlItem = notInSml[a]); a++) {
25.3296 + for (d = 0; notInBigItem = notInBig[d]; d++) {
25.3297 + if (notInSmlItem['value'] === notInBigItem['value']) {
25.3298 + notInSmlItem['moved'] = notInBigItem['index'];
25.3299 + notInBigItem['moved'] = notInSmlItem['index'];
25.3300 + notInBig.splice(d,1); // This item is marked as moved; so remove it from notInBig list
25.3301 + failedCompares = d = 0; // Reset failed compares count because we're checking for consecutive failures
25.3302 + break;
25.3303 + }
25.3304 + }
25.3305 + failedCompares += d;
25.3306 + }
25.3307 + }
25.3308 + return editScript.reverse();
25.3309 + }
25.3310 +
25.3311 + return compareArrays;
25.3312 +})();
25.3313 +
25.3314 +ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
25.3315 +
25.3316 +(function () {
25.3317 + // Objective:
25.3318 + // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
25.3319 + // map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
25.3320 + // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
25.3321 + // 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
25.3322 + // previously mapped - retain those nodes, and just insert/delete other ones
25.3323 +
25.3324 + // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
25.3325 + // You can use this, for example, to activate bindings on those nodes.
25.3326 +
25.3327 + function fixUpNodesToBeMovedOrRemoved(contiguousNodeArray) {
25.3328 + // Before moving, deleting, or replacing a set of nodes that were previously outputted by the "map" function, we have to reconcile
25.3329 + // them against what is in the DOM right now. It may be that some of the nodes have already been removed from the document,
25.3330 + // or that new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
25.3331 + // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
25.3332 + // So, this function translates the old "map" output array into its best guess of what set of current DOM nodes should be removed.
25.3333 + //
25.3334 + // Rules:
25.3335 + // [A] Any leading nodes that aren't in the document any more should be ignored
25.3336 + // These most likely correspond to memoization nodes that were already removed during binding
25.3337 + // See https://github.com/SteveSanderson/knockout/pull/440
25.3338 + // [B] We want to output a contiguous series of nodes that are still in the document. So, ignore any nodes that
25.3339 + // have already been removed, and include any nodes that have been inserted among the previous collection
25.3340 +
25.3341 + // Rule [A]
25.3342 + while (contiguousNodeArray.length && !ko.utils.domNodeIsAttachedToDocument(contiguousNodeArray[0]))
25.3343 + contiguousNodeArray.splice(0, 1);
25.3344 +
25.3345 + // Rule [B]
25.3346 + if (contiguousNodeArray.length > 1) {
25.3347 + // Build up the actual new contiguous node set
25.3348 + var current = contiguousNodeArray[0], last = contiguousNodeArray[contiguousNodeArray.length - 1], newContiguousSet = [current];
25.3349 + while (current !== last) {
25.3350 + current = current.nextSibling;
25.3351 + if (!current) // Won't happen, except if the developer has manually removed some DOM elements (then we're in an undefined scenario)
25.3352 + return;
25.3353 + newContiguousSet.push(current);
25.3354 + }
25.3355 +
25.3356 + // ... then mutate the input array to match this.
25.3357 + // (The following line replaces the contents of contiguousNodeArray with newContiguousSet)
25.3358 + Array.prototype.splice.apply(contiguousNodeArray, [0, contiguousNodeArray.length].concat(newContiguousSet));
25.3359 + }
25.3360 + return contiguousNodeArray;
25.3361 + }
25.3362 +
25.3363 + function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
25.3364 + // Map this array value inside a dependentObservable so we re-map when any dependency changes
25.3365 + var mappedNodes = [];
25.3366 + var dependentObservable = ko.dependentObservable(function() {
25.3367 + var newMappedNodes = mapping(valueToMap, index) || [];
25.3368 +
25.3369 + // On subsequent evaluations, just replace the previously-inserted DOM nodes
25.3370 + if (mappedNodes.length > 0) {
25.3371 + ko.utils.replaceDomNodes(fixUpNodesToBeMovedOrRemoved(mappedNodes), newMappedNodes);
25.3372 + if (callbackAfterAddingNodes)
25.3373 + ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
25.3374 + }
25.3375 +
25.3376 + // Replace the contents of the mappedNodes array, thereby updating the record
25.3377 + // of which nodes would be deleted if valueToMap was itself later removed
25.3378 + mappedNodes.splice(0, mappedNodes.length);
25.3379 + ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
25.3380 + }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return (mappedNodes.length == 0) || !ko.utils.domNodeIsAttachedToDocument(mappedNodes[0]) } });
25.3381 + return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
25.3382 + }
25.3383 +
25.3384 + var lastMappingResultDomDataKey = "setDomNodeChildrenFromArrayMapping_lastMappingResult";
25.3385 +
25.3386 + ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes) {
25.3387 + // Compare the provided array against the previous one
25.3388 + array = array || [];
25.3389 + options = options || {};
25.3390 + var isFirstExecution = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) === undefined;
25.3391 + var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey) || [];
25.3392 + var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; });
25.3393 + var editScript = ko.utils.compareArrays(lastArray, array);
25.3394 +
25.3395 + // Build the new mapping result
25.3396 + var newMappingResult = [];
25.3397 + var lastMappingResultIndex = 0;
25.3398 + var newMappingResultIndex = 0;
25.3399 +
25.3400 + var nodesToDelete = [];
25.3401 + var itemsToProcess = [];
25.3402 + var itemsForBeforeRemoveCallbacks = [];
25.3403 + var itemsForMoveCallbacks = [];
25.3404 + var itemsForAfterAddCallbacks = [];
25.3405 + var mapData;
25.3406 +
25.3407 + function itemMovedOrRetained(editScriptIndex, oldPosition) {
25.3408 + mapData = lastMappingResult[oldPosition];
25.3409 + if (newMappingResultIndex !== oldPosition)
25.3410 + itemsForMoveCallbacks[editScriptIndex] = mapData;
25.3411 + // Since updating the index might change the nodes, do so before calling fixUpNodesToBeMovedOrRemoved
25.3412 + mapData.indexObservable(newMappingResultIndex++);
25.3413 + fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes);
25.3414 + newMappingResult.push(mapData);
25.3415 + itemsToProcess.push(mapData);
25.3416 + }
25.3417 +
25.3418 + function callCallback(callback, items) {
25.3419 + if (callback) {
25.3420 + for (var i = 0, n = items.length; i < n; i++) {
25.3421 + if (items[i]) {
25.3422 + ko.utils.arrayForEach(items[i].mappedNodes, function(node) {
25.3423 + callback(node, i, items[i].arrayEntry);
25.3424 + });
25.3425 + }
25.3426 + }
25.3427 + }
25.3428 + }
25.3429 +
25.3430 + for (var i = 0, editScriptItem, movedIndex; editScriptItem = editScript[i]; i++) {
25.3431 + movedIndex = editScriptItem['moved'];
25.3432 + switch (editScriptItem['status']) {
25.3433 + case "deleted":
25.3434 + if (movedIndex === undefined) {
25.3435 + mapData = lastMappingResult[lastMappingResultIndex];
25.3436 +
25.3437 + // Stop tracking changes to the mapping for these nodes
25.3438 + if (mapData.dependentObservable)
25.3439 + mapData.dependentObservable.dispose();
25.3440 +
25.3441 + // Queue these nodes for later removal
25.3442 + nodesToDelete.push.apply(nodesToDelete, fixUpNodesToBeMovedOrRemoved(mapData.mappedNodes));
25.3443 + if (options['beforeRemove']) {
25.3444 + itemsForBeforeRemoveCallbacks[i] = mapData;
25.3445 + itemsToProcess.push(mapData);
25.3446 + }
25.3447 + }
25.3448 + lastMappingResultIndex++;
25.3449 + break;
25.3450 +
25.3451 + case "retained":
25.3452 + itemMovedOrRetained(i, lastMappingResultIndex++);
25.3453 + break;
25.3454 +
25.3455 + case "added":
25.3456 + if (movedIndex !== undefined) {
25.3457 + itemMovedOrRetained(i, movedIndex);
25.3458 + } else {
25.3459 + mapData = { arrayEntry: editScriptItem['value'], indexObservable: ko.observable(newMappingResultIndex++) };
25.3460 + newMappingResult.push(mapData);
25.3461 + itemsToProcess.push(mapData);
25.3462 + if (!isFirstExecution)
25.3463 + itemsForAfterAddCallbacks[i] = mapData;
25.3464 + }
25.3465 + break;
25.3466 + }
25.3467 + }
25.3468 +
25.3469 + // Call beforeMove first before any changes have been made to the DOM
25.3470 + callCallback(options['beforeMove'], itemsForMoveCallbacks);
25.3471 +
25.3472 + // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
25.3473 + ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);
25.3474 +
25.3475 + // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
25.3476 + for (var i = 0, nextNode = ko.virtualElements.firstChild(domNode), lastNode, node; mapData = itemsToProcess[i]; i++) {
25.3477 + // Get nodes for newly added items
25.3478 + if (!mapData.mappedNodes)
25.3479 + ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));
25.3480 +
25.3481 + // Put nodes in the right place if they aren't there already
25.3482 + for (var j = 0; node = mapData.mappedNodes[j]; nextNode = node.nextSibling, lastNode = node, j++) {
25.3483 + if (node !== nextNode)
25.3484 + ko.virtualElements.insertAfter(domNode, node, lastNode);
25.3485 + }
25.3486 +
25.3487 + // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
25.3488 + if (!mapData.initialized && callbackAfterAddingNodes) {
25.3489 + callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
25.3490 + mapData.initialized = true;
25.3491 + }
25.3492 + }
25.3493 +
25.3494 + // If there's a beforeRemove callback, call it after reordering.
25.3495 + // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
25.3496 + // some sort of animation, which is why we first reorder the nodes that will be removed. If the
25.3497 + // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
25.3498 + // Perhaps we'll make that change in the future if this scenario becomes more common.
25.3499 + callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);
25.3500 +
25.3501 + // Finally call afterMove and afterAdd callbacks
25.3502 + callCallback(options['afterMove'], itemsForMoveCallbacks);
25.3503 + callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
25.3504 +
25.3505 + // Store a copy of the array items we just considered so we can difference it next time
25.3506 + ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);
25.3507 + }
25.3508 +})();
25.3509 +
25.3510 +ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
25.3511 +ko.nativeTemplateEngine = function () {
25.3512 + this['allowTemplateRewriting'] = false;
25.3513 +}
25.3514 +
25.3515 +ko.nativeTemplateEngine.prototype = new ko.templateEngine();
25.3516 +ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
25.3517 + var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
25.3518 + templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
25.3519 + templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
25.3520 +
25.3521 + if (templateNodes) {
25.3522 + return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
25.3523 + } else {
25.3524 + var templateText = templateSource['text']();
25.3525 + return ko.utils.parseHtmlFragment(templateText);
25.3526 + }
25.3527 +};
25.3528 +
25.3529 +ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
25.3530 +ko.setTemplateEngine(ko.nativeTemplateEngine.instance);
25.3531 +
25.3532 +ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
25.3533 +(function() {
25.3534 + ko.jqueryTmplTemplateEngine = function () {
25.3535 + // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
25.3536 + // doesn't expose a version number, so we have to infer it.
25.3537 + // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
25.3538 + // which KO internally refers to as version "2", so older versions are no longer detected.
25.3539 + var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
25.3540 + if ((typeof(jQuery) == "undefined") || !(jQuery['tmpl']))
25.3541 + return 0;
25.3542 + // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
25.3543 + try {
25.3544 + if (jQuery['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
25.3545 + // Since 1.0.0pre, custom tags should append markup to an array called "__"
25.3546 + return 2; // Final version of jquery.tmpl
25.3547 + }
25.3548 + } catch(ex) { /* Apparently not the version we were looking for */ }
25.3549 +
25.3550 + return 1; // Any older version that we don't support
25.3551 + })();
25.3552 +
25.3553 + function ensureHasReferencedJQueryTemplates() {
25.3554 + if (jQueryTmplVersion < 2)
25.3555 + throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
25.3556 + }
25.3557 +
25.3558 + function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
25.3559 + return jQuery['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
25.3560 + }
25.3561 +
25.3562 + this['renderTemplateSource'] = function(templateSource, bindingContext, options) {
25.3563 + options = options || {};
25.3564 + ensureHasReferencedJQueryTemplates();
25.3565 +
25.3566 + // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
25.3567 + var precompiled = templateSource['data']('precompiled');
25.3568 + if (!precompiled) {
25.3569 + var templateText = templateSource['text']() || "";
25.3570 + // Wrap in "with($whatever.koBindingContext) { ... }"
25.3571 + templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";
25.3572 +
25.3573 + precompiled = jQuery['template'](null, templateText);
25.3574 + templateSource['data']('precompiled', precompiled);
25.3575 + }
25.3576 +
25.3577 + var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
25.3578 + var jQueryTemplateOptions = jQuery['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
25.3579 +
25.3580 + var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
25.3581 + resultNodes['appendTo'](document.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
25.3582 +
25.3583 + jQuery['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
25.3584 + return resultNodes;
25.3585 + };
25.3586 +
25.3587 + this['createJavaScriptEvaluatorBlock'] = function(script) {
25.3588 + return "{{ko_code ((function() { return " + script + " })()) }}";
25.3589 + };
25.3590 +
25.3591 + this['addTemplate'] = function(templateName, templateMarkup) {
25.3592 + document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "</script>");
25.3593 + };
25.3594 +
25.3595 + if (jQueryTmplVersion > 0) {
25.3596 + jQuery['tmpl']['tag']['ko_code'] = {
25.3597 + open: "__.push($1 || '');"
25.3598 + };
25.3599 + jQuery['tmpl']['tag']['ko_with'] = {
25.3600 + open: "with($1) {",
25.3601 + close: "} "
25.3602 + };
25.3603 + }
25.3604 + };
25.3605 +
25.3606 + ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
25.3607 +
25.3608 + // Use this one by default *only if jquery.tmpl is referenced*
25.3609 + var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
25.3610 + if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
25.3611 + ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);
25.3612 +
25.3613 + ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
25.3614 +})();
25.3615 +});
25.3616 +})(window,document,navigator,window["jQuery"]);
25.3617 +})();
25.3618 \ No newline at end of file
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
26.2 +++ b/ko/bck2brwsr/src/test/java/org/apidesign/bck2brwsr/ko2brwsr/Bck2BrwsrKnockoutTest.java Sat Sep 07 18:28:09 2013 +0200
26.3 @@ -0,0 +1,120 @@
26.4 +/**
26.5 + * Back 2 Browser Bytecode Translator
26.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
26.7 + *
26.8 + * This program is free software: you can redistribute it and/or modify
26.9 + * it under the terms of the GNU General Public License as published by
26.10 + * the Free Software Foundation, version 2 of the License.
26.11 + *
26.12 + * This program is distributed in the hope that it will be useful,
26.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
26.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26.15 + * GNU General Public License for more details.
26.16 + *
26.17 + * You should have received a copy of the GNU General Public License
26.18 + * along with this program. Look for COPYING file in the top folder.
26.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
26.20 + */
26.21 +package org.apidesign.bck2brwsr.ko2brwsr;
26.22 +
26.23 +import java.io.IOException;
26.24 +import java.net.URI;
26.25 +import java.net.URISyntaxException;
26.26 +import java.net.URL;
26.27 +import java.util.Map;
26.28 +import net.java.html.BrwsrCtx;
26.29 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
26.30 +import org.apidesign.bck2brwsr.vmtest.VMTest;
26.31 +import org.apidesign.html.context.spi.Contexts;
26.32 +import org.apidesign.html.json.spi.Technology;
26.33 +import org.apidesign.html.json.spi.Transfer;
26.34 +import org.apidesign.html.json.spi.WSTransfer;
26.35 +import org.apidesign.html.json.tck.KOTest;
26.36 +import org.apidesign.html.json.tck.KnockoutTCK;
26.37 +import org.openide.util.lookup.ServiceProvider;
26.38 +import org.testng.annotations.Factory;
26.39 +
26.40 +/**
26.41 + *
26.42 + * @author Jaroslav Tulach <jtulach@netbeans.org>
26.43 + */
26.44 +@ServiceProvider(service = KnockoutTCK.class)
26.45 +public final class Bck2BrwsrKnockoutTest extends KnockoutTCK {
26.46 + @Factory public static Object[] create() {
26.47 + return VMTest.newTests().
26.48 + withClasses(testClasses()).
26.49 + withLaunchers("bck2brwsr").
26.50 + withTestAnnotation(KOTest.class).
26.51 + build();
26.52 + }
26.53 +
26.54 + @Override
26.55 + public BrwsrCtx createContext() {
26.56 + return Contexts.newBuilder().
26.57 + register(Transfer.class, BrwsrCtxImpl.DEFAULT, 9).
26.58 + register(WSTransfer.class, BrwsrCtxImpl.DEFAULT, 9).
26.59 + register(Technology.class, BrwsrCtxImpl.DEFAULT, 9).build();
26.60 + }
26.61 +
26.62 +
26.63 +
26.64 + @Override
26.65 + public Object createJSON(Map<String, Object> values) {
26.66 + Object json = createJSON();
26.67 +
26.68 + for (Map.Entry<String, Object> entry : values.entrySet()) {
26.69 + putValue(json, entry.getKey(), entry.getValue());
26.70 + }
26.71 + return json;
26.72 + }
26.73 +
26.74 + @JavaScriptBody(args = {}, body = "return new Object();")
26.75 + private static native Object createJSON();
26.76 +
26.77 + @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
26.78 + private static native void putValue(Object json, String key, Object value);
26.79 +
26.80 + @Override
26.81 + public Object executeScript(String script, Object[] arguments) {
26.82 + return execScript(script, arguments);
26.83 + }
26.84 +
26.85 + @JavaScriptBody(args = { "s", "args" }, body =
26.86 + "var f = new Function(s); return f.apply(null, args);"
26.87 + )
26.88 + private static native Object execScript(String s, Object[] arguments);
26.89 +
26.90 + @JavaScriptBody(args = { }, body =
26.91 + "var h;"
26.92 + + "if (!!window && !!window.location && !!window.location.href)\n"
26.93 + + " h = window.location.href;\n"
26.94 + + "else "
26.95 + + " h = null;"
26.96 + + "return h;\n"
26.97 + )
26.98 + private static native String findBaseURL();
26.99 +
26.100 + @Override
26.101 + public URI prepareURL(String content, String mimeType, String[] parameters) {
26.102 + try {
26.103 + final URL baseURL = new URL(findBaseURL());
26.104 + StringBuilder sb = new StringBuilder();
26.105 + sb.append("/dynamic?mimeType=").append(mimeType);
26.106 + for (int i = 0; i < parameters.length; i++) {
26.107 + sb.append("¶m" + i).append("=").append(parameters[i]);
26.108 + }
26.109 + String mangle = content.replace("\n", "%0a")
26.110 + .replace("\"", "\\\"").replace(" ", "%20");
26.111 + sb.append("&content=").append(mangle);
26.112 +
26.113 + URL query = new URL(baseURL, sb.toString());
26.114 + String uri = (String) query.getContent(new Class[] { String.class });
26.115 + URI connectTo = new URI(uri.trim());
26.116 + return connectTo;
26.117 + } catch (IOException ex) {
26.118 + throw new IllegalStateException(ex);
26.119 + } catch (URISyntaxException ex) {
26.120 + throw new IllegalStateException(ex);
26.121 + }
26.122 + }
26.123 +}
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
27.2 +++ b/ko/fx/pom.xml Sat Sep 07 18:28:09 2013 +0200
27.3 @@ -0,0 +1,120 @@
27.4 +<?xml version="1.0"?>
27.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">
27.6 + <modelVersion>4.0.0</modelVersion>
27.7 + <parent>
27.8 + <groupId>org.apidesign.bck2brwsr</groupId>
27.9 + <artifactId>ko</artifactId>
27.10 + <version>0.8-SNAPSHOT</version>
27.11 + </parent>
27.12 + <groupId>org.apidesign.bck2brwsr</groupId>
27.13 + <artifactId>ko-fx</artifactId>
27.14 + <version>0.8-SNAPSHOT</version>
27.15 + <name>Knockout.fx in Brwsr</name>
27.16 + <url>http://maven.apache.org</url>
27.17 + <properties>
27.18 + <jfxrt.jar>${java.home}/lib/jfxrt.jar</jfxrt.jar>
27.19 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
27.20 + </properties>
27.21 + <build>
27.22 + <plugins>
27.23 + <plugin>
27.24 + <groupId>org.apache.maven.plugins</groupId>
27.25 + <artifactId>maven-javadoc-plugin</artifactId>
27.26 + <configuration>
27.27 + <skip>false</skip>
27.28 + </configuration>
27.29 + </plugin>
27.30 + </plugins>
27.31 + </build>
27.32 + <dependencies>
27.33 + <dependency>
27.34 + <groupId>com.oracle</groupId>
27.35 + <artifactId>javafx</artifactId>
27.36 + <version>2.2</version>
27.37 + <scope>system</scope>
27.38 + <systemPath>${jfxrt.jar}</systemPath>
27.39 + </dependency>
27.40 + <dependency>
27.41 + <groupId>org.json</groupId>
27.42 + <artifactId>json</artifactId>
27.43 + <version>20090211</version>
27.44 + <type>jar</type>
27.45 + </dependency>
27.46 + <dependency>
27.47 + <groupId>org.apidesign.html</groupId>
27.48 + <artifactId>net.java.html.json</artifactId>
27.49 + <version>${net.java.html.version}</version>
27.50 + </dependency>
27.51 + <dependency>
27.52 + <groupId>org.testng</groupId>
27.53 + <artifactId>testng</artifactId>
27.54 + <scope>test</scope>
27.55 + </dependency>
27.56 + <dependency>
27.57 + <groupId>org.apidesign.html</groupId>
27.58 + <artifactId>net.java.html.json.tck</artifactId>
27.59 + <version>${net.java.html.version}</version>
27.60 + <scope>test</scope>
27.61 + </dependency>
27.62 + <dependency>
27.63 + <groupId>org.netbeans.api</groupId>
27.64 + <artifactId>org-openide-util</artifactId>
27.65 + <scope>provided</scope>
27.66 + </dependency>
27.67 + <dependency>
27.68 + <groupId>org.apidesign.bck2brwsr</groupId>
27.69 + <artifactId>launcher.fx</artifactId>
27.70 + <version>${project.version}</version>
27.71 + <scope>test</scope>
27.72 + </dependency>
27.73 + <dependency>
27.74 + <groupId>org.apidesign.html</groupId>
27.75 + <artifactId>net.java.html.boot</artifactId>
27.76 + <version>${net.java.html.version}</version>
27.77 + <type>jar</type>
27.78 + </dependency>
27.79 + <dependency>
27.80 + <groupId>org.apidesign.html</groupId>
27.81 + <artifactId>ko-fx</artifactId>
27.82 + <version>${net.java.html.version}</version>
27.83 + <type>jar</type>
27.84 + </dependency>
27.85 + <dependency>
27.86 + <groupId>org.apidesign.bck2brwsr</groupId>
27.87 + <artifactId>vmtest</artifactId>
27.88 + <version>${project.version}</version>
27.89 + <scope>test</scope>
27.90 + <type>jar</type>
27.91 + </dependency>
27.92 + <dependency>
27.93 + <groupId>org.apidesign.html</groupId>
27.94 + <artifactId>ko-ws-tyrus</artifactId>
27.95 + <version>${net.java.html.version}</version>
27.96 + <scope>test</scope>
27.97 + </dependency>
27.98 + </dependencies>
27.99 + <profiles>
27.100 + <profile>
27.101 + <id>jdk8</id>
27.102 + <activation>
27.103 + <file>
27.104 + <exists>${java.home}/lib/ext/jfxrt.jar</exists>
27.105 + </file>
27.106 + </activation>
27.107 + <properties>
27.108 + <jfxrt.jar>${java.home}/lib/ext/jfxrt.jar</jfxrt.jar>
27.109 + </properties>
27.110 + </profile>
27.111 + <profile>
27.112 + <id>jdk7</id>
27.113 + <activation>
27.114 + <file>
27.115 + <exists>${java.home}/lib/jfxrt.jar</exists>
27.116 + </file>
27.117 + </activation>
27.118 + <properties>
27.119 + <jfxrt.jar>${java.home}/lib/jfxrt.jar</jfxrt.jar>
27.120 + </properties>
27.121 + </profile>
27.122 + </profiles>
27.123 +</project>
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
28.2 +++ b/ko/fx/src/test/java/org/apidesign/bck2brwsr/kofx/KnockoutFXTest.java Sat Sep 07 18:28:09 2013 +0200
28.3 @@ -0,0 +1,132 @@
28.4 +/**
28.5 + * Back 2 Browser Bytecode Translator
28.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
28.7 + *
28.8 + * This program is free software: you can redistribute it and/or modify
28.9 + * it under the terms of the GNU General Public License as published by
28.10 + * the Free Software Foundation, version 2 of the License.
28.11 + *
28.12 + * This program is distributed in the hope that it will be useful,
28.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
28.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28.15 + * GNU General Public License for more details.
28.16 + *
28.17 + * You should have received a copy of the GNU General Public License
28.18 + * along with this program. Look for COPYING file in the top folder.
28.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
28.20 + */
28.21 +package org.apidesign.bck2brwsr.kofx;
28.22 +
28.23 +import java.io.BufferedReader;
28.24 +import java.io.IOException;
28.25 +import java.io.InputStreamReader;
28.26 +import java.net.URI;
28.27 +import java.net.URISyntaxException;
28.28 +import java.net.URL;
28.29 +import java.net.URLConnection;
28.30 +import java.util.Map;
28.31 +import net.java.html.BrwsrCtx;
28.32 +import net.java.html.js.JavaScriptBody;
28.33 +import org.apidesign.bck2brwsr.vmtest.VMTest;
28.34 +import org.apidesign.html.context.spi.Contexts;
28.35 +import org.apidesign.html.json.spi.Technology;
28.36 +import org.apidesign.html.json.spi.Transfer;
28.37 +import org.apidesign.html.json.spi.WSTransfer;
28.38 +import org.apidesign.html.json.tck.KOTest;
28.39 +import org.apidesign.html.json.tck.KnockoutTCK;
28.40 +import org.apidesign.html.kofx.FXContext;
28.41 +import org.apidesign.html.wstyrus.TyrusContext;
28.42 +import org.json.JSONException;
28.43 +import org.json.JSONObject;
28.44 +import org.openide.util.lookup.ServiceProvider;
28.45 +import org.testng.annotations.Factory;
28.46 +
28.47 +/**
28.48 + *
28.49 + * @author Jaroslav Tulach <jtulach@netbeans.org>
28.50 + */
28.51 +@ServiceProvider(service = KnockoutTCK.class)
28.52 +public final class KnockoutFXTest extends KnockoutTCK {
28.53 + public KnockoutFXTest() {
28.54 + }
28.55 +
28.56 + @Factory public static Object[] compatibilityTests() {
28.57 + return VMTest.newTests().
28.58 + withClasses(testClasses()).
28.59 + withTestAnnotation(KOTest.class).
28.60 + withLaunchers("fxbrwsr").build();
28.61 + }
28.62 +
28.63 + @Override
28.64 + public BrwsrCtx createContext() {
28.65 + FXContext fx = new FXContext();
28.66 + TyrusContext tc = new TyrusContext();
28.67 + Contexts.Builder b = Contexts.newBuilder().
28.68 + register(Technology.class, fx, 10).
28.69 + register(Transfer.class, fx, 10);
28.70 + try {
28.71 + Class.forName("java.util.function.Function");
28.72 + // prefer WebView's WebSockets on JDK8
28.73 + b.register(WSTransfer.class, fx, 10);
28.74 + } catch (ClassNotFoundException ex) {
28.75 + // ok, JDK7 needs tyrus
28.76 + b.register(WSTransfer.class, tc, 20);
28.77 + }
28.78 + return b.build();
28.79 + }
28.80 +
28.81 + @Override
28.82 + public Object createJSON(Map<String, Object> values) {
28.83 + JSONObject json = new JSONObject();
28.84 + for (Map.Entry<String, Object> entry : values.entrySet()) {
28.85 + try {
28.86 + json.put(entry.getKey(), entry.getValue());
28.87 + } catch (JSONException ex) {
28.88 + throw new IllegalStateException(ex);
28.89 + }
28.90 + }
28.91 + return json;
28.92 + }
28.93 +
28.94 + @Override
28.95 + @JavaScriptBody(args = { "s", "args" }, body = ""
28.96 + + "var f = new Function(s); "
28.97 + + "return f.apply(null, args);"
28.98 + )
28.99 + public native Object executeScript(String script, Object[] arguments);
28.100 +
28.101 + @JavaScriptBody(args = { }, body =
28.102 + "var h;"
28.103 + + "if (!!window && !!window.location && !!window.location.href)\n"
28.104 + + " h = window.location.href;\n"
28.105 + + "else "
28.106 + + " h = null;"
28.107 + + "return h;\n"
28.108 + )
28.109 + private static native String findBaseURL();
28.110 +
28.111 + @Override
28.112 + public URI prepareURL(String content, String mimeType, String[] parameters) {
28.113 + try {
28.114 + final URL baseURL = new URL(findBaseURL());
28.115 + StringBuilder sb = new StringBuilder();
28.116 + sb.append("/dynamic?mimeType=").append(mimeType);
28.117 + for (int i = 0; i < parameters.length; i++) {
28.118 + sb.append("¶m" + i).append("=").append(parameters[i]);
28.119 + }
28.120 + String mangle = content.replace("\n", "%0a")
28.121 + .replace("\"", "\\\"").replace(" ", "%20");
28.122 + sb.append("&content=").append(mangle);
28.123 +
28.124 + URL query = new URL(baseURL, sb.toString());
28.125 + URLConnection c = query.openConnection();
28.126 + BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
28.127 + URI connectTo = new URI(br.readLine());
28.128 + return connectTo;
28.129 + } catch (IOException ex) {
28.130 + throw new IllegalStateException(ex);
28.131 + } catch (URISyntaxException ex) {
28.132 + throw new IllegalStateException(ex);
28.133 + }
28.134 + }
28.135 +}
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
29.2 +++ b/ko/pom.xml Sat Sep 07 18:28:09 2013 +0200
29.3 @@ -0,0 +1,20 @@
29.4 +<?xml version="1.0" encoding="UTF-8"?>
29.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">
29.6 + <modelVersion>4.0.0</modelVersion>
29.7 + <groupId>org.apidesign.bck2brwsr</groupId>
29.8 + <artifactId>ko</artifactId>
29.9 + <version>0.8-SNAPSHOT</version>
29.10 + <packaging>pom</packaging>
29.11 + <name>Bck2Brwsr Knockout Support</name>
29.12 + <parent>
29.13 + <groupId>org.apidesign</groupId>
29.14 + <artifactId>bck2brwsr</artifactId>
29.15 + <version>0.8-SNAPSHOT</version>
29.16 + </parent>
29.17 + <modules>
29.18 + <module>archetype</module>
29.19 + <module>archetype-test</module>
29.20 + <module>bck2brwsr</module>
29.21 + <module>fx</module>
29.22 + </modules>
29.23 +</project>
30.1 --- a/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Sat Sep 07 18:25:09 2013 +0200
30.2 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Sat Sep 07 18:28:09 2013 +0200
30.3 @@ -19,6 +19,7 @@
30.4
30.5 import java.io.IOException;
30.6 import java.io.InputStream;
30.7 +import java.net.URI;
30.8 import java.util.ArrayList;
30.9 import java.util.List;
30.10 import java.util.concurrent.CountDownLatch;
30.11 @@ -95,7 +96,6 @@
30.12 wait.countDown();
30.13 }
30.14
30.15 -
30.16 static final class Resource {
30.17 final InputStream httpContent;
30.18 final String httpType;
31.1 --- a/launcher/fx/pom.xml Sat Sep 07 18:25:09 2013 +0200
31.2 +++ b/launcher/fx/pom.xml Sat Sep 07 18:28:09 2013 +0200
31.3 @@ -18,8 +18,8 @@
31.4 <artifactId>maven-compiler-plugin</artifactId>
31.5 <version>2.3.2</version>
31.6 <configuration>
31.7 - <source>1.7</source>
31.8 - <target>1.7</target>
31.9 + <source>1.6</source>
31.10 + <target>1.6</target>
31.11 </configuration>
31.12 </plugin>
31.13 <plugin>
31.14 @@ -54,5 +54,47 @@
31.15 <scope>system</scope>
31.16 <systemPath>${jfxrt.jar}</systemPath>
31.17 </dependency>
31.18 + <dependency>
31.19 + <groupId>org.testng</groupId>
31.20 + <artifactId>testng</artifactId>
31.21 + <scope>test</scope>
31.22 + </dependency>
31.23 + <dependency>
31.24 + <groupId>org.netbeans.modules</groupId>
31.25 + <artifactId>org-netbeans-bootstrap</artifactId>
31.26 + <version>RELEASE73</version>
31.27 + </dependency>
31.28 + <dependency>
31.29 + <groupId>${project.groupId}</groupId>
31.30 + <artifactId>core</artifactId>
31.31 + <version>${project.version}</version>
31.32 + <scope>compile</scope>
31.33 + </dependency>
31.34 + <dependency>
31.35 + <groupId>org.ow2.asm</groupId>
31.36 + <artifactId>asm</artifactId>
31.37 + <version>4.1</version>
31.38 + </dependency>
31.39 + <dependency>
31.40 + <groupId>org.apidesign.html</groupId>
31.41 + <artifactId>net.java.html.boot</artifactId>
31.42 + <version>${net.java.html.version}</version>
31.43 + </dependency>
31.44 + <dependency>
31.45 + <groupId>org.glassfish.grizzly</groupId>
31.46 + <artifactId>grizzly-websockets-server</artifactId>
31.47 + <version>${grizzly.version}</version>
31.48 + <type>jar</type>
31.49 + </dependency>
31.50 + <dependency>
31.51 + <groupId>org.glassfish.grizzly</groupId>
31.52 + <artifactId>grizzly-http-servlet</artifactId>
31.53 + <version>${grizzly.version}</version>
31.54 + </dependency>
31.55 + <dependency>
31.56 + <groupId>javax.servlet</groupId>
31.57 + <artifactId>javax.servlet-api</artifactId>
31.58 + <version>3.1.0</version>
31.59 + </dependency>
31.60 </dependencies>
31.61 </project>
32.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Sat Sep 07 18:25:09 2013 +0200
32.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Sat Sep 07 18:28:09 2013 +0200
32.3 @@ -17,6 +17,8 @@
32.4 */
32.5 package org.apidesign.bck2brwsr.launcher;
32.6
32.7 +import java.io.ByteArrayInputStream;
32.8 +import java.io.ByteArrayOutputStream;
32.9 import java.io.Closeable;
32.10 import java.io.File;
32.11 import java.io.IOException;
32.12 @@ -52,6 +54,11 @@
32.13 import org.glassfish.grizzly.http.server.ServerConfiguration;
32.14 import org.glassfish.grizzly.http.util.HttpStatus;
32.15 import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
32.16 +import org.glassfish.grizzly.websockets.WebSocket;
32.17 +import org.glassfish.grizzly.websockets.WebSocketAddOn;
32.18 +import org.glassfish.grizzly.websockets.WebSocketApplication;
32.19 +import org.glassfish.grizzly.websockets.WebSocketEngine;
32.20 +import org.openide.util.Exceptions;
32.21
32.22 /**
32.23 * Lightweight server to launch Bck2Brwsr applications and tests.
32.24 @@ -61,8 +68,8 @@
32.25 abstract class BaseHTTPLauncher extends Launcher implements Closeable, Callable<HttpServer> {
32.26 static final Logger LOG = Logger.getLogger(BaseHTTPLauncher.class.getName());
32.27 private static final InvocationContext END = new InvocationContext(null, null, null);
32.28 - private final Set<ClassLoader> loaders = new LinkedHashSet<>();
32.29 - private final BlockingQueue<InvocationContext> methods = new LinkedBlockingQueue<>();
32.30 + private final Set<ClassLoader> loaders = new LinkedHashSet<ClassLoader>();
32.31 + private final BlockingQueue<InvocationContext> methods = new LinkedBlockingQueue<InvocationContext>();
32.32 private long timeOut;
32.33 private final Res resources = new Res();
32.34 private final String cmd;
32.35 @@ -112,7 +119,7 @@
32.36 server = s;
32.37 try {
32.38 launchServerAndBrwsr(s, simpleName);
32.39 - } catch (URISyntaxException | InterruptedException ex) {
32.40 + } catch (Exception ex) {
32.41 throw new IOException(ex);
32.42 }
32.43 }
32.44 @@ -124,7 +131,7 @@
32.45 HttpServer s = initServer(dir.getPath(), false);
32.46 try {
32.47 launchServerAndBrwsr(s, startpage);
32.48 - } catch (URISyntaxException | InterruptedException ex) {
32.49 + } catch (Exception ex) {
32.50 throw new IOException(ex);
32.51 }
32.52 }
32.53 @@ -150,25 +157,29 @@
32.54
32.55 private HttpServer initServer(String path, boolean addClasses) throws IOException {
32.56 HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535));
32.57 -
32.58 + /*
32.59 ThreadPoolConfig fewThreads = ThreadPoolConfig.defaultConfig().copy().
32.60 setPoolName("Fx/Bck2 Brwsr").
32.61 - setCorePoolSize(1).
32.62 + setCorePoolSize(3).
32.63 setMaxPoolSize(5);
32.64 ThreadPoolConfig oneKernel = ThreadPoolConfig.defaultConfig().copy().
32.65 setPoolName("Kernel Fx/Bck2").
32.66 - setCorePoolSize(1).
32.67 + setCorePoolSize(3).
32.68 setMaxPoolSize(3);
32.69 for (NetworkListener nl : s.getListeners()) {
32.70 nl.getTransport().setWorkerThreadPoolConfig(fewThreads);
32.71 nl.getTransport().setKernelThreadPoolConfig(oneKernel);
32.72 }
32.73 -
32.74 + */
32.75 final ServerConfiguration conf = s.getServerConfiguration();
32.76 if (addClasses) {
32.77 conf.addHttpHandler(new VM(), "/bck2brwsr.js");
32.78 conf.addHttpHandler(new Classes(resources), "/classes/");
32.79 }
32.80 + final WebSocketAddOn addon = new WebSocketAddOn();
32.81 + for (NetworkListener listener : s.getListeners()) {
32.82 + listener.registerAddOn(addon);
32.83 + }
32.84 return s;
32.85 }
32.86
32.87 @@ -179,22 +190,56 @@
32.88
32.89 class DynamicResourceHandler extends HttpHandler {
32.90 private final InvocationContext ic;
32.91 + private int resourcesCount;
32.92 + DynamicResourceHandler delegate;
32.93 public DynamicResourceHandler(InvocationContext ic) {
32.94 - if (ic == null || ic.resources.isEmpty()) {
32.95 - throw new NullPointerException();
32.96 - }
32.97 this.ic = ic;
32.98 for (Resource r : ic.resources) {
32.99 conf.addHttpHandler(this, r.httpPath);
32.100 }
32.101 }
32.102
32.103 - public void close() {
32.104 + public void close(DynamicResourceHandler del) {
32.105 conf.removeHttpHandler(this);
32.106 + delegate = del;
32.107 }
32.108
32.109 @Override
32.110 public void service(Request request, Response response) throws Exception {
32.111 + if (delegate != null) {
32.112 + delegate.service(request, response);
32.113 + return;
32.114 + }
32.115 +
32.116 + if ("/dynamic".equals(request.getRequestURI())) {
32.117 + boolean webSocket = false;
32.118 + String mimeType = request.getParameter("mimeType");
32.119 + List<String> params = new ArrayList<String>();
32.120 + for (int i = 0; ; i++) {
32.121 + String p = request.getParameter("param" + i);
32.122 + if (p == null) {
32.123 + break;
32.124 + }
32.125 + params.add(p);
32.126 + if ("protocol:ws".equals(p)) {
32.127 + webSocket = true;
32.128 + continue;
32.129 + } }
32.130 + final String cnt = request.getParameter("content");
32.131 + String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
32.132 + ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
32.133 + URI url;
32.134 + final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
32.135 + if (webSocket) {
32.136 + url = registerWebSocket(res);
32.137 + } else {
32.138 + url = registerResource(res);
32.139 + }
32.140 + response.getWriter().write(url.toString());
32.141 + response.getWriter().write("\n");
32.142 + return;
32.143 + }
32.144 +
32.145 for (Resource r : ic.resources) {
32.146 if (r.httpPath.equals(request.getRequestURI())) {
32.147 LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI());
32.148 @@ -231,13 +276,26 @@
32.149 }
32.150 }
32.151 }
32.152 +
32.153 + private URI registerWebSocket(Resource r) {
32.154 + WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
32.155 + return pageURL("ws", server, r.httpPath);
32.156 + }
32.157 +
32.158 + private URI registerResource(Resource r) {
32.159 + if (!ic.resources.contains(r)) {
32.160 + ic.resources.add(r);
32.161 + conf.addHttpHandler(this, r.httpPath);
32.162 + }
32.163 + return pageURL("http", server, r.httpPath);
32.164 + }
32.165 }
32.166
32.167 conf.addHttpHandler(new Page(resources, harnessResource()), "/execute");
32.168
32.169 conf.addHttpHandler(new HttpHandler() {
32.170 int cnt;
32.171 - List<InvocationContext> cases = new ArrayList<>();
32.172 + List<InvocationContext> cases = new ArrayList<InvocationContext>();
32.173 DynamicResourceHandler prev;
32.174 @Override
32.175 public void service(Request request, Response response) throws Exception {
32.176 @@ -269,11 +327,6 @@
32.177 }
32.178 }
32.179
32.180 - if (prev != null) {
32.181 - prev.close();
32.182 - prev = null;
32.183 - }
32.184 -
32.185 if (mi == null) {
32.186 mi = methods.take();
32.187 caseNmbr = cnt++;
32.188 @@ -285,10 +338,12 @@
32.189 LOG.log(Level.INFO, "End of data reached. Exiting.");
32.190 return;
32.191 }
32.192 -
32.193 - if (!mi.resources.isEmpty()) {
32.194 - prev = new DynamicResourceHandler(mi);
32.195 + final DynamicResourceHandler newRH = new DynamicResourceHandler(mi);
32.196 + if (prev != null) {
32.197 + prev.close(newRH);
32.198 }
32.199 + prev = newRH;
32.200 + conf.addHttpHandler(prev, "/dynamic");
32.201
32.202 cases.add(mi);
32.203 final String cn = mi.clazz.getName();
32.204 @@ -381,10 +436,7 @@
32.205
32.206 private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException {
32.207 server.start();
32.208 - NetworkListener listener = server.getListeners().iterator().next();
32.209 - int port = listener.getPort();
32.210 -
32.211 - URI uri = new URI("http://localhost:" + port + page);
32.212 + URI uri = pageURL("http", server, page);
32.213 return showBrwsr(uri);
32.214 }
32.215 private static String toUTF8(String value) throws UnsupportedEncodingException {
32.216 @@ -496,6 +548,16 @@
32.217 abstract void generateBck2BrwsrJS(StringBuilder sb, Res loader) throws IOException;
32.218 abstract String harnessResource();
32.219
32.220 + private static URI pageURL(String protocol, HttpServer server, final String page) {
32.221 + NetworkListener listener = server.getListeners().iterator().next();
32.222 + int port = listener.getPort();
32.223 + try {
32.224 + return new URI(protocol + "://localhost:" + port + page);
32.225 + } catch (URISyntaxException ex) {
32.226 + throw new IllegalStateException(ex);
32.227 + }
32.228 + }
32.229 +
32.230 class Res {
32.231 public InputStream get(String resource) throws IOException {
32.232 URL u = null;
32.233 @@ -547,7 +609,8 @@
32.234 replace = args;
32.235 }
32.236 OutputStream os = response.getOutputStream();
32.237 - try (InputStream is = res.get(r)) {
32.238 + try {
32.239 + InputStream is = res.get(r);
32.240 copyStream(is, os, request.getRequestURL().toString(), replace);
32.241 } catch (IOException ex) {
32.242 response.setDetailMessage(ex.getLocalizedMessage());
32.243 @@ -603,7 +666,9 @@
32.244 if (res.startsWith("/")) {
32.245 res = res.substring(1);
32.246 }
32.247 - try (InputStream is = loader.get(res)) {
32.248 + InputStream is = null;
32.249 + try {
32.250 + is = loader.get(res);
32.251 response.setContentType("text/javascript");
32.252 Writer w = response.getWriter();
32.253 w.append("[");
32.254 @@ -628,7 +693,32 @@
32.255 response.setStatus(HttpStatus.NOT_FOUND_404);
32.256 response.setError();
32.257 response.setDetailMessage(ex.getMessage());
32.258 + } finally {
32.259 + if (is != null) {
32.260 + is.close();
32.261 + }
32.262 }
32.263 }
32.264 }
32.265 -}
32.266 + private static class WS extends WebSocketApplication {
32.267 +
32.268 + private final Resource r;
32.269 +
32.270 + private WS(Resource r) {
32.271 + this.r = r;
32.272 + }
32.273 +
32.274 + @Override
32.275 + public void onMessage(WebSocket socket, String text) {
32.276 + try {
32.277 + r.httpContent.reset();
32.278 + ByteArrayOutputStream out = new ByteArrayOutputStream();
32.279 + copyStream(r.httpContent, out, null, text);
32.280 + String s = new String(out.toByteArray(), "UTF-8");
32.281 + socket.send(s);
32.282 + } catch (IOException ex) {
32.283 + Exceptions.printStackTrace(ex);
32.284 + }
32.285 + }
32.286 +
32.287 + }}
33.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java Sat Sep 07 18:25:09 2013 +0200
33.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java Sat Sep 07 18:28:09 2013 +0200
33.3 @@ -24,6 +24,7 @@
33.4 import java.net.URI;
33.5 import java.net.URL;
33.6 import java.net.URLClassLoader;
33.7 +import java.util.ArrayList;
33.8 import java.util.Enumeration;
33.9 import java.util.List;
33.10
33.11 @@ -67,7 +68,17 @@
33.12 public void run() {
33.13 LOG.log(Level.INFO, "In FX thread. Launching!");
33.14 try {
33.15 - FXBrwsr.launch(FXBrwsr.class, url.toString());
33.16 + List<String> params = new ArrayList<String>();
33.17 + params.add(url.toString());
33.18 + if (isDebugged()) {
33.19 + params.add("--toolbar=true");
33.20 + params.add("--firebug=true");
33.21 + String ud = System.getProperty("netbeans.user");
33.22 + if (ud != null) {
33.23 + params.add("--userdir=" + ud);
33.24 + }
33.25 + }
33.26 + FXBrwsr.launch(FXBrwsr.class, params.toArray(new String[params.size()]));
33.27 LOG.log(Level.INFO, "Launcher is back. Closing");
33.28 close();
33.29 System.exit(0);
33.30 @@ -87,17 +98,6 @@
33.31 sb.append("(function() {\n"
33.32 + " var impl = this.bck2brwsr;\n"
33.33 + " this.bck2brwsr = function() { return impl; };\n");
33.34 - if (isDebugged()) {
33.35 - sb.append("var scr = window.document.createElement('script');\n");
33.36 - sb.append("scr.type = 'text/javascript';\n");
33.37 - sb.append("scr.src = 'https://getfirebug.com/firebug-lite.js';\n");
33.38 - sb.append("scr.text = '{ startOpened: true }';\n");
33.39 - sb.append("var head = window.document.getElementsByTagName('head')[0];");
33.40 - sb.append("head.appendChild(scr);\n");
33.41 - sb.append("var html = window.document.getElementsByTagName('html')[0];");
33.42 - sb.append("html.debug = true;\n");
33.43 - }
33.44 -
33.45 sb.append("})(window);\n");
33.46 JVMBridge.onBck2BrwsrLoad();
33.47 }
33.48 @@ -129,8 +129,12 @@
33.49 while (en.hasMoreElements()) {
33.50 URL url = en.nextElement();
33.51 Manifest mf;
33.52 - try (InputStream is = url.openStream()) {
33.53 + InputStream is = null;
33.54 + try {
33.55 + is = url.openStream();
33.56 mf = new Manifest(is);
33.57 + } finally {
33.58 + if (is != null) is.close();
33.59 }
33.60 String sp = mf.getMainAttributes().getValue("StartPage");
33.61 if (sp != null) {
34.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
34.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/BrowserToolbar.java Sat Sep 07 18:28:09 2013 +0200
34.3 @@ -0,0 +1,397 @@
34.4 +/**
34.5 + * Back 2 Browser Bytecode Translator
34.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
34.7 + *
34.8 + * This program is free software: you can redistribute it and/or modify
34.9 + * it under the terms of the GNU General Public License as published by
34.10 + * the Free Software Foundation, version 2 of the License.
34.11 + *
34.12 + * This program is distributed in the hope that it will be useful,
34.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
34.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34.15 + * GNU General Public License for more details.
34.16 + *
34.17 + * You should have received a copy of the GNU General Public License
34.18 + * along with this program. Look for COPYING file in the top folder.
34.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
34.20 + */
34.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
34.22 +
34.23 +import java.util.ArrayList;
34.24 +import java.util.List;
34.25 +import javafx.beans.InvalidationListener;
34.26 +import javafx.beans.Observable;
34.27 +import javafx.beans.value.ChangeListener;
34.28 +import javafx.beans.value.ObservableValue;
34.29 +import javafx.collections.FXCollections;
34.30 +import javafx.scene.control.ComboBox;
34.31 +import javafx.scene.control.ScrollPane;
34.32 +import javafx.scene.control.Separator;
34.33 +import javafx.scene.control.Toggle;
34.34 +import javafx.scene.control.ToggleButton;
34.35 +import javafx.scene.control.ToggleGroup;
34.36 +import javafx.scene.control.ToolBar;
34.37 +import javafx.scene.control.Tooltip;
34.38 +import javafx.scene.image.Image;
34.39 +import javafx.scene.image.ImageView;
34.40 +import javafx.scene.layout.Pane;
34.41 +import javafx.scene.web.WebEngine;
34.42 +import javafx.scene.web.WebView;
34.43 +
34.44 +final class BrowserToolbar extends ToolBar {
34.45 + private final ArrayList<ResizeBtn> resizeButtons;
34.46 + private final WebView webView;
34.47 + private final Pane container;
34.48 + private final ToggleGroup resizeGroup = new ToggleGroup();
34.49 + private final ComboBox<String> comboZoom = new ComboBox<String>();
34.50 +
34.51 + BrowserToolbar(WebView webView, Pane container, boolean useFirebug, final WebDebug webDebug) {
34.52 + this.webView = webView;
34.53 + this.container = container;
34.54 +
34.55 + List<ResizeOption> options = ResizeOption.loadAll();
34.56 + options.add( 0, ResizeOption.SIZE_TO_FIT );
34.57 + resizeButtons = new ArrayList<ResizeBtn>( options.size() );
34.58 +
34.59 + for( ResizeOption ro : options ) {
34.60 + ResizeBtn button = new ResizeBtn(ro);
34.61 + resizeButtons.add( button );
34.62 + resizeGroup.getToggles().add( button );
34.63 + getItems().add( button );
34.64 + }
34.65 + resizeButtons.get( 0 ).setSelected( true );
34.66 + resizeGroup.selectedToggleProperty().addListener( new InvalidationListener() {
34.67 +
34.68 + @Override
34.69 + public void invalidated( Observable o ) {
34.70 + resize();
34.71 + }
34.72 + });
34.73 +
34.74 + getItems().add( new Separator() );
34.75 +
34.76 + getItems().add( comboZoom );
34.77 + ArrayList<String> zoomModel = new ArrayList<String>( 6 );
34.78 + zoomModel.add( "200%" ); //NOI18N
34.79 + zoomModel.add( "150%" ); //NOI18N
34.80 + zoomModel.add( "100%" ); //NOI18N
34.81 + zoomModel.add( "75%" ); //NOI18N
34.82 + zoomModel.add( "50%" ); //NOI18N
34.83 + comboZoom.setItems( FXCollections.observableList( zoomModel ) );
34.84 + comboZoom.setEditable( true );
34.85 + comboZoom.setValue( "100%" ); //NOI18N
34.86 + comboZoom.valueProperty().addListener( new ChangeListener<String>() {
34.87 +
34.88 + @Override
34.89 + public void changed( ObservableValue<? extends String> ov, String t, String t1 ) {
34.90 + String newZoom = zoom( t1 );
34.91 + comboZoom.setValue( newZoom );
34.92 + }
34.93 + });
34.94 +
34.95 + if (useFirebug) {
34.96 + getItems().add(new Separator());
34.97 +
34.98 + final ToggleButton firebug = new ToggleButton(null, new ImageView(
34.99 + new Image(BrowserToolbar.class.getResourceAsStream("firebug.png"))
34.100 + ));
34.101 + firebug.setTooltip(new Tooltip("Show/Hide firebug"));
34.102 + firebug.selectedProperty().addListener(new InvalidationListener() {
34.103 + @Override
34.104 + public void invalidated(Observable o) {
34.105 + toggleFireBug(firebug.isSelected());
34.106 + }
34.107 + });
34.108 + getItems().add(firebug);
34.109 + }
34.110 +
34.111 + if (webDebug != null) {
34.112 + final ToggleButton btnSelMode = new ToggleButton(null, new ImageView(
34.113 + new Image(BrowserToolbar.class.getResourceAsStream("selectionMode.png"))));
34.114 + btnSelMode.setTooltip(new Tooltip("Toggle selection mode"));
34.115 + btnSelMode.selectedProperty().addListener(new InvalidationListener() {
34.116 + @Override
34.117 + public void invalidated(Observable o) {
34.118 + toggleSelectionMode(webDebug, btnSelMode.isSelected());
34.119 + }
34.120 + });
34.121 + getItems().add(btnSelMode);
34.122 + }
34.123 + }
34.124 +
34.125 + private String zoom( String zoomFactor ) {
34.126 + if( zoomFactor.trim().isEmpty() )
34.127 + return null;
34.128 +
34.129 + try {
34.130 + zoomFactor = zoomFactor.replaceAll( "\\%", ""); //NOI18N
34.131 + zoomFactor = zoomFactor.trim();
34.132 + double zoom = Double.parseDouble( zoomFactor );
34.133 + zoom = Math.abs( zoom )/100;
34.134 + if( zoom <= 0.0 )
34.135 + return null;
34.136 + webView.impl_setScale( zoom );
34.137 + return (int)(100*zoom) + "%"; //NOI18N
34.138 + } catch( NumberFormatException nfe ) {
34.139 + //ignore
34.140 + }
34.141 + return null;
34.142 + }
34.143 +
34.144 + private void resize() {
34.145 + Toggle selection = resizeGroup.getSelectedToggle();
34.146 + if( selection instanceof ResizeBtn ) {
34.147 + ResizeOption ro = ((ResizeBtn)selection).getResizeOption();
34.148 + if( ro == ResizeOption.SIZE_TO_FIT ) {
34.149 + _autofit();
34.150 + } else {
34.151 + _resize( ro.getWidth(), ro.getHeight() );
34.152 + }
34.153 + }
34.154 +
34.155 + }
34.156 +
34.157 + private void _resize( final double width, final double height ) {
34.158 + ScrollPane scroll;
34.159 + if( !(container.getChildren().get( 0) instanceof ScrollPane) ) {
34.160 + scroll = new ScrollPane();
34.161 + scroll.setContent( webView );
34.162 + container.getChildren().clear();
34.163 + container.getChildren().add( scroll );
34.164 + } else {
34.165 + scroll = ( ScrollPane ) container.getChildren().get( 0 );
34.166 + }
34.167 + scroll.setPrefViewportWidth( width );
34.168 + scroll.setPrefViewportHeight(height );
34.169 + webView.setMaxWidth( width );
34.170 + webView.setMaxHeight( height );
34.171 + webView.setMinWidth( width );
34.172 + webView.setMinHeight( height );
34.173 + }
34.174 +
34.175 + private void _autofit() {
34.176 + if( container.getChildren().get( 0) instanceof ScrollPane ) {
34.177 + container.getChildren().clear();
34.178 + container.getChildren().add( webView );
34.179 + }
34.180 + webView.setMaxWidth( Integer.MAX_VALUE );
34.181 + webView.setMaxHeight( Integer.MAX_VALUE );
34.182 + webView.setMinWidth( -1 );
34.183 + webView.setMinHeight( -1 );
34.184 + webView.autosize();
34.185 + }
34.186 +
34.187 + private void toggleSelectionMode(WebDebug dbg, boolean selMode) {
34.188 + // "inspect"
34.189 + dbg.call("{\"message\":\"selection_mode\",\"selectionMode\":" + selMode + "}");
34.190 + }
34.191 +
34.192 + final void toggleFireBug(boolean enable) {
34.193 + WebEngine eng = webView.getEngine();
34.194 + Object installed = eng.executeScript("window.Firebug");
34.195 + if ("undefined".equals(installed)) {
34.196 + StringBuilder sb = new StringBuilder();
34.197 + sb.append("var scr = window.document.createElement('script');\n");
34.198 + sb.append("scr.type = 'text/javascript';\n");
34.199 + sb.append("scr.src = 'https://getfirebug.com/firebug-lite.js';\n");
34.200 + sb.append("scr.text = '{ startOpened: true }';\n");
34.201 + sb.append("var head = window.document.getElementsByTagName('head')[0];");
34.202 + sb.append("head.appendChild(scr);\n");
34.203 + sb.append("var html = window.document.getElementsByTagName('html')[0];");
34.204 + sb.append("html.debug = true;\n");
34.205 + eng.executeScript(sb.toString());
34.206 + } else {
34.207 + if (enable) {
34.208 + eng.executeScript("Firebug.chrome.open()");
34.209 + } else {
34.210 + eng.executeScript("Firebug.chrome.close()");
34.211 + }
34.212 + }
34.213 + }
34.214 +
34.215 + /**
34.216 + * Button to resize the browser window.
34.217 + * Taken from NetBeans. Kept GPLwithCPEx license.
34.218 + * Portions Copyrighted 2012 Sun Microsystems, Inc.
34.219 + *
34.220 + * @author S. Aubrecht
34.221 + */
34.222 + static final class ResizeBtn extends ToggleButton {
34.223 +
34.224 + private final ResizeOption resizeOption;
34.225 +
34.226 + ResizeBtn(ResizeOption resizeOption) {
34.227 + super(null, new ImageView(toImage(resizeOption)));
34.228 + this.resizeOption = resizeOption;
34.229 + setTooltip(new Tooltip(resizeOption.getToolTip()));
34.230 + }
34.231 +
34.232 + ResizeOption getResizeOption() {
34.233 + return resizeOption;
34.234 + }
34.235 +
34.236 + static Image toImage(ResizeOption ro) {
34.237 + if (ro == ResizeOption.SIZE_TO_FIT) {
34.238 + return ResizeOption.Type.CUSTOM.getImage();
34.239 + }
34.240 + return ro.getType().getImage();
34.241 + }
34.242 + }
34.243 +
34.244 + /**
34.245 + * Immutable value class describing a single button to resize web browser window.
34.246 + * Taken from NetBeans. Kept GPLwithCPEx license.
34.247 + * Portions Copyrighted 2012 Sun Microsystems, Inc.
34.248 + *
34.249 + * @author S. Aubrecht
34.250 + */
34.251 + static final class ResizeOption {
34.252 +
34.253 + private final Type type;
34.254 + private final String displayName;
34.255 + private final int width;
34.256 + private final int height;
34.257 + private final boolean isDefault;
34.258 +
34.259 + enum Type {
34.260 + DESKTOP("desktop.png"),
34.261 + TABLET_PORTRAIT("tabletPortrait.png"),
34.262 + TABLET_LANDSCAPE("tabletLandscape.png"),
34.263 + SMARTPHONE_PORTRAIT("handheldPortrait.png"),
34.264 + SMARTPHONE_LANDSCAPE("handheldLandscape.png"),
34.265 + WIDESCREEN("widescreen.png"),
34.266 + NETBOOK("netbook.png"),
34.267 + CUSTOM("sizeToFit.png");
34.268 +
34.269 +
34.270 + private final String resource;
34.271 +
34.272 + private Type(String r) {
34.273 + resource = r;
34.274 + }
34.275 +
34.276 + public Image getImage() {
34.277 + return new Image(Type.class.getResourceAsStream(resource));
34.278 + }
34.279 + }
34.280 +
34.281 + private ResizeOption(Type type, String displayName, int width, int height, boolean showInToolbar, boolean isDefault) {
34.282 + super();
34.283 + this.type = type;
34.284 + this.displayName = displayName;
34.285 + this.width = width;
34.286 + this.height = height;
34.287 + this.isDefault = isDefault;
34.288 + }
34.289 +
34.290 + static List<ResizeOption> loadAll() {
34.291 + List<ResizeOption> res = new ArrayList<ResizeOption>(10);
34.292 + res.add(ResizeOption.create(ResizeOption.Type.DESKTOP, "Desktop", 1280, 1024, true, true));
34.293 + res.add(ResizeOption.create(ResizeOption.Type.TABLET_LANDSCAPE, "Tablet Landscape", 1024, 768, true, true));
34.294 + res.add(ResizeOption.create(ResizeOption.Type.TABLET_PORTRAIT, "Tablet Portrait", 768, 1024, true, true));
34.295 + res.add(ResizeOption.create(ResizeOption.Type.SMARTPHONE_LANDSCAPE, "Smartphone Landscape", 480, 320, true, true));
34.296 + res.add(ResizeOption.create(ResizeOption.Type.SMARTPHONE_PORTRAIT, "Smartphone Portrait", 320, 480, true, true));
34.297 + res.add(ResizeOption.create(ResizeOption.Type.WIDESCREEN, "Widescreen", 1680, 1050, false, true));
34.298 + res.add(ResizeOption.create(ResizeOption.Type.NETBOOK, "Netbook", 1024, 600, false, true));
34.299 + return res;
34.300 + }
34.301 +
34.302 + /**
34.303 + * Creates a new instance.
34.304 + * @param type
34.305 + * @param displayName Display name to show in tooltip, cannot be empty.
34.306 + * @param width Screen width
34.307 + * @param height Screen height
34.308 + * @param showInToolbar True to show in web developer toolbar.
34.309 + * @param isDefault True if this is a predefined option that cannot be removed.
34.310 + * @return New instance.
34.311 + */
34.312 + public static ResizeOption create(Type type, String displayName, int width, int height, boolean showInToolbar, boolean isDefault) {
34.313 + if (width <= 0 || height <= 0) {
34.314 + throw new IllegalArgumentException("Invalid screen dimensions: " + width + " x " + height); //NOI18N
34.315 + }
34.316 + return new ResizeOption(type, displayName, width, height, showInToolbar, isDefault);
34.317 + }
34.318 + /**
34.319 + * An extra option to size the browser content to fit its window.
34.320 + */
34.321 + public static final ResizeOption SIZE_TO_FIT = new ResizeOption(Type.CUSTOM, "Size To Fit", -1, -1, true, true);
34.322 +
34.323 + public String getDisplayName() {
34.324 + return displayName;
34.325 + }
34.326 +
34.327 + public Type getType() {
34.328 + return type;
34.329 + }
34.330 +
34.331 + public int getWidth() {
34.332 + return width;
34.333 + }
34.334 +
34.335 + public int getHeight() {
34.336 + return height;
34.337 + }
34.338 +
34.339 + public boolean isDefault() {
34.340 + return isDefault;
34.341 + }
34.342 +
34.343 + @Override
34.344 + public String toString() {
34.345 + return displayName;
34.346 + }
34.347 +
34.348 + public String getToolTip() {
34.349 + if (width < 0 || height < 0) {
34.350 + return displayName;
34.351 + }
34.352 + StringBuilder sb = new StringBuilder();
34.353 + sb.append(width);
34.354 + sb.append(" x "); //NOI18N
34.355 + sb.append(height);
34.356 + sb.append(" ("); //NOI18N
34.357 + sb.append(displayName);
34.358 + sb.append(')'); //NOI18N
34.359 + return sb.toString();
34.360 + }
34.361 +
34.362 + @Override
34.363 + public boolean equals(Object obj) {
34.364 + if (obj == null) {
34.365 + return false;
34.366 + }
34.367 + if (getClass() != obj.getClass()) {
34.368 + return false;
34.369 + }
34.370 + final ResizeOption other = (ResizeOption) obj;
34.371 + if (this.type != other.type) {
34.372 + return false;
34.373 + }
34.374 + if ((this.displayName == null) ? (other.displayName != null) : !this.displayName.equals(other.displayName)) {
34.375 + return false;
34.376 + }
34.377 + if (this.width != other.width) {
34.378 + return false;
34.379 + }
34.380 + if (this.height != other.height) {
34.381 + return false;
34.382 + }
34.383 + if (this.isDefault != other.isDefault) {
34.384 + return false;
34.385 + }
34.386 + return true;
34.387 + }
34.388 +
34.389 + @Override
34.390 + public int hashCode() {
34.391 + int hash = 7;
34.392 + hash = 11 * hash + (this.type != null ? this.type.hashCode() : 0);
34.393 + hash = 11 * hash + (this.displayName != null ? this.displayName.hashCode() : 0);
34.394 + hash = 11 * hash + this.width;
34.395 + hash = 11 * hash + this.height;
34.396 + hash = 11 * hash + (this.isDefault ? 1 : 0);
34.397 + return hash;
34.398 + }
34.399 + }
34.400 +}
35.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java Sat Sep 07 18:25:09 2013 +0200
35.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java Sat Sep 07 18:28:09 2013 +0200
35.3 @@ -25,8 +25,8 @@
35.4 import java.lang.reflect.Modifier;
35.5 import java.net.URL;
35.6 import java.util.Enumeration;
35.7 -import javafx.scene.web.WebEngine;
35.8 import netscape.javascript.JSObject;
35.9 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
35.10
35.11 /**
35.12 *
35.13 @@ -35,17 +35,15 @@
35.14 public final class Console {
35.15 public Console() {
35.16 }
35.17 -
35.18 - private static Object getAttr(Object elem, String attr) {
35.19 - return InvokeJS.CObject.call("getAttr", elem, attr);
35.20 - }
35.21
35.22 - private static void setAttr(String id, String attr, Object value) {
35.23 - InvokeJS.CObject.call("setAttrId", id, attr, value);
35.24 - }
35.25 - private static void setAttr(Object id, String attr, Object value) {
35.26 - InvokeJS.CObject.call("setAttr", id, attr, value);
35.27 - }
35.28 + @JavaScriptBody(args = { "elem", "attr" }, body = "return elem[attr].toString();")
35.29 + private static native Object getAttr(Object elem, String attr);
35.30 +
35.31 + @JavaScriptBody(args = { "id", "attr", "value" }, body = "window.document.getElementById(id)[attr] = value;")
35.32 + private static native void setAttr(String id, String attr, Object value);
35.33 +
35.34 + @JavaScriptBody(args = { "elem", "attr", "value" }, body = "elem[attr] = value;")
35.35 + private static native void setAttr(Object id, String attr, Object value);
35.36
35.37 private static void closeWindow() {}
35.38
35.39 @@ -78,7 +76,7 @@
35.40 textArea = null;
35.41 }
35.42
35.43 - private static final String BEGIN_TEST =
35.44 + @JavaScriptBody(args = { "test", "c", "arr" }, body =
35.45 "var ul = window.document.getElementById('bck2brwsr.result');\n"
35.46 + "var li = window.document.createElement('li');\n"
35.47 + "var span = window.document.createElement('span');"
35.48 @@ -103,27 +101,24 @@
35.49 + "p.appendChild(pre);\n"
35.50 + "ul.appendChild(li);\n"
35.51 + "arr[0] = pre;\n"
35.52 - + "arr[1] = status;\n";
35.53 -
35.54 - private static void beginTest(String test, Case c, Object[] arr) {
35.55 - InvokeJS.CObject.call("beginTest", test, c, arr);
35.56 - }
35.57 + + "arr[1] = status;\n"
35.58 + )
35.59 + private static native void beginTest(String test, Case c, Object[] arr);
35.60
35.61 - private static final String LOAD_TEXT =
35.62 + @JavaScriptBody(args = { "url", "callback", "arr" }, body =
35.63 "var request = new XMLHttpRequest();\n"
35.64 + "request.open('GET', url, true);\n"
35.65 + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n"
35.66 + "request.onreadystatechange = function() {\n"
35.67 + " if (this.readyState!==4) return;\n"
35.68 - + " try {"
35.69 + + " try {\n"
35.70 + " arr[0] = this.responseText;\n"
35.71 - + " callback.run__V();\n"
35.72 - + " } catch (e) { alert(e); }"
35.73 - + "};"
35.74 - + "request.send();";
35.75 - private static void loadText(String url, Runnable callback, String[] arr) throws IOException {
35.76 - InvokeJS.CObject.call("loadText", url, new Run(callback), arr);
35.77 - }
35.78 + + " callback.run();\n"
35.79 + + " } catch (e) { alert(e); }\n"
35.80 + + "};\n"
35.81 + + "request.send();\n"
35.82 + )
35.83 + private static native void loadText(String url, Runnable callback, String[] arr) throws IOException;
35.84
35.85 public static void runHarness(String url) throws IOException {
35.86 new Console().harness(url);
35.87 @@ -142,15 +137,16 @@
35.88
35.89 private Request(String url) throws IOException {
35.90 this.url = url;
35.91 - loadText(url, this, arr);
35.92 + loadText(url, new Run(this), arr);
35.93 }
35.94 private Request(String url, String u) throws IOException {
35.95 this.url = url;
35.96 - loadText(u, this, arr);
35.97 + loadText(u, new Run(this), arr);
35.98 }
35.99
35.100 @Override
35.101 public void run() {
35.102 + Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
35.103 try {
35.104 if (c == null) {
35.105 String data = arr[0];
35.106 @@ -181,7 +177,7 @@
35.107 } catch (Exception ex) {
35.108 if (ex instanceof InterruptedException) {
35.109 log("Re-scheduling in 100ms");
35.110 - schedule(this, 100);
35.111 + schedule(new Run(this), 100);
35.112 return;
35.113 }
35.114 log(ex.getClass().getName() + ":" + ex.getMessage());
35.115 @@ -232,7 +228,9 @@
35.116 if (u == null) {
35.117 throw new IOException("Can't find " + name);
35.118 }
35.119 - try (InputStream is = u.openStream()) {
35.120 + InputStream is = null;
35.121 + try {
35.122 + is = u.openStream();
35.123 byte[] arr;
35.124 arr = new byte[is.available()];
35.125 int offset = 0;
35.126 @@ -244,15 +242,16 @@
35.127 offset += len;
35.128 }
35.129 return arr;
35.130 + } finally {
35.131 + if (is != null) is.close();
35.132 }
35.133 }
35.134
35.135 private static void turnAssetionStatusOn() {
35.136 }
35.137
35.138 - private static Object schedule(Runnable r, int time) {
35.139 - return InvokeJS.CObject.call("schedule", new Run(r), time);
35.140 - }
35.141 + @JavaScriptBody(args = { "r", "time" }, body = "return window.setTimeout(function() { r.run(); }, time);")
35.142 + private static native Object schedule(Runnable r, int time);
35.143
35.144 private static final class Case {
35.145 private final Object data;
35.146 @@ -348,48 +347,16 @@
35.147 }
35.148 return res;
35.149 }
35.150 -
35.151 - private static Object toJSON(String s) {
35.152 - return InvokeJS.CObject.call("toJSON", s);
35.153 - }
35.154 +
35.155 + @JavaScriptBody(args = { "s" }, body = "return eval('(' + s + ')');")
35.156 + private static native Object toJSON(String s);
35.157
35.158 private static Object value(String p, Object d) {
35.159 return ((JSObject)d).getMember(p);
35.160 }
35.161 }
35.162
35.163 - private static String safe(String txt) {
35.164 - return "try {" + txt + "} catch (err) { alert(err); }";
35.165 - }
35.166 -
35.167 static {
35.168 turnAssetionStatusOn();
35.169 }
35.170 -
35.171 - private static final class InvokeJS {
35.172 - static final JSObject CObject = initJS();
35.173 -
35.174 - private static JSObject initJS() {
35.175 - WebEngine web = (WebEngine) System.getProperties().get("webEngine");
35.176 - return (JSObject) web.executeScript("(function() {"
35.177 - + "var CObject = {};"
35.178 -
35.179 - + "CObject.getAttr = function(elem, attr) { return elem[attr].toString(); };"
35.180 -
35.181 - + "CObject.setAttrId = function(id, attr, value) { window.document.getElementById(id)[attr] = value; };"
35.182 - + "CObject.setAttr = function(elem, attr, value) { elem[attr] = value; };"
35.183 -
35.184 - + "CObject.beginTest = function(test, c, arr) {" + safe(BEGIN_TEST) + "};"
35.185 -
35.186 - + "CObject.loadText = function(url, callback, arr) {" + safe(LOAD_TEXT.replace("run__V", "run")) + "};"
35.187 -
35.188 - + "CObject.schedule = function(r, time) { return window.setTimeout(function() { r.run(); }, time); };"
35.189 -
35.190 - + "CObject.toJSON = function(s) { return eval('(' + s + ')'); };"
35.191 -
35.192 - + "return CObject;"
35.193 - + "})(this)");
35.194 - }
35.195 - }
35.196 -
35.197 }
36.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java Sat Sep 07 18:25:09 2013 +0200
36.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java Sat Sep 07 18:28:09 2013 +0200
36.3 @@ -27,17 +27,13 @@
36.4 import javafx.beans.value.ObservableValue;
36.5 import javafx.event.ActionEvent;
36.6 import javafx.event.EventHandler;
36.7 -import javafx.geometry.HPos;
36.8 import javafx.geometry.Insets;
36.9 import javafx.geometry.Pos;
36.10 -import javafx.geometry.VPos;
36.11 -import javafx.scene.Node;
36.12 import javafx.scene.Scene;
36.13 import javafx.scene.control.Button;
36.14 -import javafx.scene.layout.ColumnConstraints;
36.15 -import javafx.scene.layout.GridPane;
36.16 -import javafx.scene.layout.Pane;
36.17 -import javafx.scene.layout.Priority;
36.18 +import javafx.scene.control.ToolBar;
36.19 +import javafx.scene.layout.BorderPane;
36.20 +import javafx.scene.layout.HBox;
36.21 import javafx.scene.layout.VBox;
36.22 import javafx.scene.text.Text;
36.23 import javafx.scene.web.WebEngine;
36.24 @@ -55,30 +51,52 @@
36.25 */
36.26 public class FXBrwsr extends Application {
36.27 private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName());
36.28 -
36.29 +
36.30 @Override
36.31 public void start(Stage primaryStage) throws Exception {
36.32 - Pane root = new WebViewPane(getParameters().getUnnamed());
36.33 - primaryStage.setScene(new Scene(root, 1024, 768));
36.34 - LOG.info("Showing the stage");
36.35 + WebView view = new WebView();
36.36 + final String nbUserDir = this.getParameters().getNamed().get("userdir"); // NOI18N
36.37 + WebController wc = new WebController(view, nbUserDir, getParameters().getUnnamed());
36.38 +
36.39 + final VBox vbox = new VBox();
36.40 + vbox.setAlignment( Pos.CENTER );
36.41 + vbox.setStyle( "-fx-background-color: #808080;");
36.42 +
36.43 +
36.44 + HBox hbox = new HBox();
36.45 + hbox.setStyle( "-fx-background-color: #808080;");
36.46 + hbox.setAlignment(Pos.CENTER);
36.47 + hbox.getChildren().add(vbox);
36.48 + vbox.getChildren().add(view);
36.49 +
36.50 + BorderPane root = new BorderPane();
36.51 + final boolean showToolbar = "true".equals(this.getParameters().getNamed().get("toolbar")); // NOI18N
36.52 + final boolean useFirebug = "true".equals(this.getParameters().getNamed().get("firebug")); // NOI18N
36.53 + if (showToolbar) {
36.54 + final ToolBar toolbar = new BrowserToolbar(view, vbox, useFirebug, wc.dbg);
36.55 + root.setTop( toolbar );
36.56 + }
36.57 + root.setCenter(hbox);
36.58 +
36.59 + Scene scene = new Scene(root, 800, 600);
36.60 +
36.61 + primaryStage.setTitle( "Device Emulator" );
36.62 + primaryStage.setScene( scene );
36.63 primaryStage.show();
36.64 - LOG.log(Level.INFO, "State shown: {0}", primaryStage.isShowing());
36.65 }
36.66
36.67 /**
36.68 * Create a resizable WebView pane
36.69 */
36.70 - private class WebViewPane extends Pane {
36.71 - private final JVMBridge bridge = new JVMBridge();
36.72 + private static class WebController {
36.73 + private final JVMBridge bridge;
36.74 + private final WebDebug dbg;
36.75 + private final String ud;
36.76
36.77 - public WebViewPane(List<String> params) {
36.78 + public WebController(WebView view, String ud, List<String> params) {
36.79 + this.bridge = new JVMBridge(view.getEngine());
36.80 + this.ud = ud;
36.81 LOG.log(Level.INFO, "Initializing WebView with {0}", params);
36.82 - VBox.setVgrow(this, Priority.ALWAYS);
36.83 - setMaxWidth(Double.MAX_VALUE);
36.84 - setMaxHeight(Double.MAX_VALUE);
36.85 - WebView view = new WebView();
36.86 - view.setMinSize(500, 400);
36.87 - view.setPrefSize(500, 400);
36.88 final WebEngine eng = view.getEngine();
36.89 try {
36.90 JVMBridge.addBck2BrwsrLoad(new InitBck2Brwsr(eng));
36.91 @@ -120,13 +138,15 @@
36.92 dialogStage.showAndWait();
36.93 }
36.94 });
36.95 - GridPane grid = new GridPane();
36.96 - grid.setVgap(5);
36.97 - grid.setHgap(5);
36.98 - GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS);
36.99 - grid.getColumnConstraints().addAll(new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true), new ColumnConstraints(40, 40, 40, Priority.NEVER, HPos.CENTER, true));
36.100 - grid.getChildren().addAll(view);
36.101 - getChildren().add(grid);
36.102 + WebDebug wd = null;
36.103 + try {
36.104 + if (ud != null) {
36.105 + wd = WebDebug.create(eng.impl_getDebugger(), ud);
36.106 + }
36.107 + } catch (Exception ex) {
36.108 + LOG.log(Level.WARNING, null, ex);
36.109 + }
36.110 + this.dbg = wd;
36.111 }
36.112
36.113 boolean initBck2Brwsr(WebEngine webEngine) {
36.114 @@ -134,28 +154,12 @@
36.115 LOG.log(Level.FINE, "window: {0}", jsobj);
36.116 Object prev = jsobj.getMember("bck2brwsr");
36.117 if ("undefined".equals(prev)) {
36.118 - System.getProperties().put("webEngine", webEngine);
36.119 jsobj.setMember("bck2brwsr", bridge);
36.120 return true;
36.121 }
36.122 return false;
36.123 }
36.124
36.125 - @Override
36.126 - protected void layoutChildren() {
36.127 - List<Node> managed = getManagedChildren();
36.128 - double width = getWidth();
36.129 - double height = getHeight();
36.130 - double top = getInsets().getTop();
36.131 - double right = getInsets().getRight();
36.132 - double left = getInsets().getLeft();
36.133 - double bottom = getInsets().getBottom();
36.134 - for (int i = 0; i < managed.size(); i++) {
36.135 - Node child = managed.get(i);
36.136 - layoutInArea(child, left, top, width - left - right, height - top - bottom, 0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER);
36.137 - }
36.138 - }
36.139 -
36.140 private class InitBck2Brwsr implements ChangeListener<Void>, Runnable {
36.141 private final WebEngine eng;
36.142
37.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java Sat Sep 07 18:25:09 2013 +0200
37.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java Sat Sep 07 18:28:09 2013 +0200
37.3 @@ -17,16 +17,38 @@
37.4 */
37.5 package org.apidesign.bck2brwsr.launcher.fximpl;
37.6
37.7 +import java.io.BufferedReader;
37.8 +import java.io.Reader;
37.9 +import org.apidesign.html.boot.spi.Fn;
37.10 +import java.net.URL;
37.11 +import java.util.ArrayList;
37.12 +import java.util.Arrays;
37.13 +import java.util.Collection;
37.14 +import java.util.List;
37.15 import java.util.TooManyListenersException;
37.16 import javafx.beans.value.ChangeListener;
37.17 +import javafx.scene.web.WebEngine;
37.18 +import netscape.javascript.JSObject;
37.19 +import org.apidesign.html.boot.impl.FindResources;
37.20 +import org.apidesign.html.boot.impl.FnUtils;
37.21
37.22 /**
37.23 *
37.24 * @author Jaroslav Tulach <jtulach@netbeans.org>
37.25 */
37.26 public final class JVMBridge {
37.27 + private final WebEngine engine;
37.28 + private final ClassLoader cl;
37.29 +
37.30 private static ClassLoader[] ldrs;
37.31 private static ChangeListener<Void> onBck2BrwsrLoad;
37.32 +
37.33 + JVMBridge(WebEngine eng) {
37.34 + this.engine = eng;
37.35 + final ClassLoader p = JVMBridge.class.getClassLoader().getParent();
37.36 + WebClassLoader wcl = new WebClassLoader();
37.37 + this.cl = FnUtils.newLoader(wcl, wcl, p);
37.38 + }
37.39
37.40 public static void registerClassLoaders(ClassLoader[] loaders) {
37.41 ldrs = loaders.clone();
37.42 @@ -47,18 +69,81 @@
37.43 }
37.44
37.45 public Class<?> loadClass(String name) throws ClassNotFoundException {
37.46 - System.err.println("trying to load " + name);
37.47 - ClassNotFoundException ex = null;
37.48 - if (ldrs != null) for (ClassLoader l : ldrs) {
37.49 - try {
37.50 - return Class.forName(name, true, l);
37.51 - } catch (ClassNotFoundException ex2) {
37.52 - ex = ex2;
37.53 + return Class.forName(name, true, cl);
37.54 + }
37.55 +